|  | /* | 
|  | * 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" | 
|  |  | 
|  | #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); | 
|  | // 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); | 
|  | // 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()); | 
|  |  | 
|  | 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; ) |