/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "gm.h" #include "SkDebugCanvas.h" #include "SkPictureFlat.h" #include "SkPictureRecorder.h" #define WARN(msg) \ SkDebugf("%s:%d: %s\n", __FILE__, __LINE__, msg); // Do the commands in 'input' match the supplied pattern? Note: this is a pretty // heavy-weight operation since we are drawing the picture into a debug canvas // to extract the commands. static bool check_pattern(SkPicture& input, const SkTDArray<DrawType> &pattern) { SkDebugCanvas debugCanvas(input.width(), input.height()); debugCanvas.setBounds(input.width(), input.height()); input.draw(&debugCanvas); if (pattern.count() != debugCanvas.getSize()) { return false; } for (int i = 0; i < pattern.count(); ++i) { if (pattern[i] != debugCanvas.getDrawCommandAt(i)->getType()) { return false; } } return true; } // construct the pattern removed by the SkPictureRecord::remove_save_layer1 // optimization, i.e.: // SAVE_LAYER // DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT // RESTORE // // saveLayerHasPaint - control if the saveLayer has a paint (the optimization // takes a different path if this is false) // dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization // takes a different path if this is false) // colorsMatch - control if the saveLayer and dbmr2r paint colors // match (the optimization will fail if they do not) static SkPicture* create_save_layer_opt_1(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard, bool saveLayerHasPaint, bool dbmr2rHasPaint, bool colorsMatch) { // Create the pattern that should trigger the optimization preOptPattern->setCount(5); (*preOptPattern)[0] = SAVE; (*preOptPattern)[1] = SAVE_LAYER; (*preOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT; (*preOptPattern)[3] = RESTORE; (*preOptPattern)[4] = RESTORE; if (colorsMatch) { // Create the pattern that should appear after the optimization postOptPattern->setCount(5); (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw (*postOptPattern)[1] = SAVE; (*postOptPattern)[2] = DRAW_BITMAP_RECT_TO_RECT; (*postOptPattern)[3] = RESTORE; (*postOptPattern)[4] = RESTORE; } else { // Create the pattern that appears if the optimization doesn't fire postOptPattern->setCount(7); (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw (*postOptPattern)[1] = SAVE; (*postOptPattern)[2] = SAVE_LAYER; (*postOptPattern)[3] = DRAW_BITMAP_RECT_TO_RECT; (*postOptPattern)[4] = RESTORE; (*postOptPattern)[5] = RESTORE; (*postOptPattern)[6] = RESTORE; } SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); // have to disable the optimizations while generating the picture recorder.internalOnly_EnableOpts(false); SkPaint saveLayerPaint; saveLayerPaint.setColor(0xCC000000); // saveLayer's 'bounds' parameter must be NULL for this optimization if (saveLayerHasPaint) { canvas->saveLayer(NULL, &saveLayerPaint); } else { canvas->saveLayer(NULL, NULL); } SkRect rect = { 10, 10, 90, 90 }; // The dbmr2r's paint must be opaque SkPaint dbmr2rPaint; if (colorsMatch) { dbmr2rPaint.setColor(0xFF000000); } else { dbmr2rPaint.setColor(0xFFFF0000); } if (dbmr2rHasPaint) { canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint); } else { canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL); } canvas->restore(); return recorder.endRecording(); } // straight-ahead version that is seen in the skps static SkPicture* create_save_layer_opt_1_v1(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, true, // saveLayer has a paint true, // dbmr2r has a paint true); // and the colors match } // alternate version that should still succeed static SkPicture* create_save_layer_opt_1_v2(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, false, // saveLayer doesn't have a paint! true, // dbmr2r has a paint true); // color matching not really applicable } // alternate version that should still succeed static SkPicture* create_save_layer_opt_1_v3(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, true, // saveLayer has a paint false, // dbmr2r doesn't have a paint! true); // color matching not really applicable } // version in which the optimization fails b.c. the colors don't match static SkPicture* create_save_layer_opt_1_v4(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_1(preOptPattern, postOptPattern, checkerBoard, true, // saveLayer has a paint true, // dbmr2r has a paint false); // and the colors don't match! } // construct the pattern removed by the SkPictureRecord::remove_save_layer2 // optimization, i.e.: // SAVE_LAYER (with NULL == bounds) // SAVE // CLIP_RECT // DRAW_BITMAP|DRAW_BITMAP_MATRIX|DRAW_BITMAP_NINE|DRAW_BITMAP_RECT_TO_RECT // RESTORE // RESTORE // // saveLayerHasPaint - control if the saveLayer has a paint (the optimization // takes a different path if this is false) // dbmr2rHasPaint - control if the dbmr2r has a paint (the optimization // takes a different path if this is false) // colorsMatch - control if the saveLayer and dbmr2r paint colors // match (the optimization will fail if they do not) static SkPicture* create_save_layer_opt_2(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard, bool saveLayerHasPaint, bool dbmr2rHasPaint, bool colorsMatch) { // Create the pattern that should trigger the optimization preOptPattern->setCount(8); (*preOptPattern)[0] = SAVE; (*preOptPattern)[1] = SAVE_LAYER; (*preOptPattern)[2] = SAVE; (*preOptPattern)[3] = CLIP_RECT; (*preOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; (*preOptPattern)[5] = RESTORE; (*preOptPattern)[6] = RESTORE; (*preOptPattern)[7] = RESTORE; if (colorsMatch) { // Create the pattern that should appear after the optimization postOptPattern->setCount(8); (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw (*postOptPattern)[1] = SAVE; (*postOptPattern)[2] = SAVE; (*postOptPattern)[3] = CLIP_RECT; (*postOptPattern)[4] = DRAW_BITMAP_RECT_TO_RECT; (*postOptPattern)[5] = RESTORE; (*postOptPattern)[6] = RESTORE; (*postOptPattern)[7] = RESTORE; } else { // Create the pattern that appears if the optimization doesn't fire postOptPattern->setCount(10); (*postOptPattern)[0] = SAVE; // extra save/restore added by extra draw (*postOptPattern)[1] = SAVE; (*postOptPattern)[2] = SAVE_LAYER; (*postOptPattern)[3] = SAVE; (*postOptPattern)[4] = CLIP_RECT; (*postOptPattern)[5] = DRAW_BITMAP_RECT_TO_RECT; (*postOptPattern)[6] = RESTORE; (*postOptPattern)[7] = RESTORE; (*postOptPattern)[8] = RESTORE; (*postOptPattern)[9] = RESTORE; } SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(100, 100, NULL, 0); // have to disable the optimizations while generating the picture recorder.internalOnly_EnableOpts(false); SkPaint saveLayerPaint; saveLayerPaint.setColor(0xCC000000); // saveLayer's 'bounds' parameter must be NULL for this optimization if (saveLayerHasPaint) { canvas->saveLayer(NULL, &saveLayerPaint); } else { canvas->saveLayer(NULL, NULL); } canvas->save(); SkRect rect = { 10, 10, 90, 90 }; canvas->clipRect(rect); // The dbmr2r's paint must be opaque SkPaint dbmr2rPaint; if (colorsMatch) { dbmr2rPaint.setColor(0xFF000000); } else { dbmr2rPaint.setColor(0xFFFF0000); } if (dbmr2rHasPaint) { canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, &dbmr2rPaint); } else { canvas->drawBitmapRectToRect(checkerBoard, NULL, rect, NULL); } canvas->restore(); canvas->restore(); return recorder.endRecording(); } // straight-ahead version that is seen in the skps static SkPicture* create_save_layer_opt_2_v1(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, true, // saveLayer has a paint true, // dbmr2r has a paint true); // and the colors match } // alternate version that should still succeed static SkPicture* create_save_layer_opt_2_v2(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, false, // saveLayer doesn't have a paint! true, // dbmr2r has a paint true); // color matching not really applicable } // alternate version that should still succeed static SkPicture* create_save_layer_opt_2_v3(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, true, // saveLayer has a paint false, // dbmr2r doesn't have a paint! true); // color matching not really applicable } // version in which the optimization fails b.c. the colors don't match static SkPicture* create_save_layer_opt_2_v4(SkTDArray<DrawType>* preOptPattern, SkTDArray<DrawType>* postOptPattern, const SkBitmap& checkerBoard) { return create_save_layer_opt_2(preOptPattern, postOptPattern, checkerBoard, true, // saveLayer has a paint true, // dbmr2r has a paint false); // and the colors don't match! } // As our .skp optimizations get folded into the captured skps our code will // no longer be locally exercised. This GM manually constructs the patterns // our optimizations will remove to test them. It acts as both a GM and a unit // test class OptimizationsGM : public skiagm::GM { public: OptimizationsGM() { this->makeCheckerboard(); } static const int kWidth = 800; static const int kHeight = 800; protected: uint32_t onGetFlags() const SK_OVERRIDE { // One optimization changes the color drawn slightly in a 565 target. // We've decided it's innocuous, so we disable this GM when targeting 565. // Revisit this if we get finer-grained control: it'd be nice to keep drawing directly. // For more, see skia:1994. return skiagm::GM::kSkip565_Flag; } SkString onShortName() { return SkString("optimizations"); } SkISize onISize() { return SkISize::Make(kWidth, kHeight); } typedef SkPicture* (*PFCreateOpt)(SkTDArray<DrawType> *preOptPattern, SkTDArray<DrawType> *postOptPattern, const SkBitmap& checkerBoard); virtual void onDraw(SkCanvas* canvas) { PFCreateOpt gOpts[] = { create_save_layer_opt_1_v1, create_save_layer_opt_1_v2, create_save_layer_opt_1_v3, create_save_layer_opt_1_v4, create_save_layer_opt_2_v1, create_save_layer_opt_2_v2, create_save_layer_opt_2_v3, create_save_layer_opt_2_v4, }; SkTDArray<DrawType> prePattern, postPattern; int xPos = 0, yPos = 0; for (size_t i = 0; i < SK_ARRAY_COUNT(gOpts); ++i) { SkAutoTUnref<SkPicture> pre((*gOpts[i])(&prePattern, &postPattern, fCheckerboard)); if (!(check_pattern(*pre, prePattern))) { WARN("Pre optimization pattern mismatch"); SkASSERT(0); } canvas->save(); canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos)); pre->draw(canvas); xPos += pre->width(); canvas->restore(); // re-render the 'pre' picture and thus 'apply' the optimization SkPictureRecorder recorder; SkCanvas* recordCanvas = recorder.beginRecording(pre->width(), pre->height(), NULL, 0); pre->draw(recordCanvas); SkAutoTUnref<SkPicture> post(recorder.endRecording()); if (!(check_pattern(*post, postPattern))) { WARN("Post optimization pattern mismatch"); SkASSERT(0); } canvas->save(); canvas->translate(SkIntToScalar(xPos), SkIntToScalar(yPos)); post->draw(canvas); xPos += post->width(); canvas->restore(); if (xPos >= kWidth) { // start a new line xPos = 0; yPos += post->height(); } // TODO: we could also render the pre and post pictures to bitmaps // and manually compare them in this method } } private: void makeCheckerboard() { static const unsigned int kCheckerboardWidth = 16; static const unsigned int kCheckerboardHeight = 16; fCheckerboard.allocN32Pixels(kCheckerboardWidth, kCheckerboardHeight); for (unsigned int y = 0; y < kCheckerboardHeight; y += 2) { SkPMColor* scanline = fCheckerboard.getAddr32(0, y); for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) { *scanline++ = 0xFFFFFFFF; *scanline++ = 0xFF000000; } scanline = fCheckerboard.getAddr32(0, y + 1); for (unsigned int x = 0; x < kCheckerboardWidth; x += 2) { *scanline++ = 0xFF000000; *scanline++ = 0xFFFFFFFF; } } } SkBitmap fCheckerboard; typedef skiagm::GM INHERITED; }; ////////////////////////////////////////////////////////////////////////////// DEF_GM( return new OptimizationsGM; )