| /* |
| * 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; |
| } |
| |
| SkPicture* result = new SkPicture; |
| |
| // have to disable the optimizations while generating the picture |
| SkCanvas* canvas = result->beginRecording(100, 100, |
| SkPicture::kDisableRecordOptimizations_RecordingFlag); |
| |
| 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(); |
| |
| result->endRecording(); |
| |
| return result; |
| } |
| |
| // 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; |
| } |
| |
| SkPicture* result = new SkPicture; |
| |
| // have to disable the optimizations while generating the picture |
| SkCanvas* canvas = result->beginRecording(100, 100, |
| SkPicture::kDisableRecordOptimizations_RecordingFlag); |
| |
| 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(); |
| |
| result->endRecording(); |
| |
| return result; |
| } |
| |
| // 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: |
| 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 |
| SkAutoTUnref<SkPicture> post(new SkPicture); |
| |
| SkCanvas* recordCanvas = post->beginRecording(pre->width(), pre->height()); |
| |
| pre->draw(recordCanvas); |
| |
| post->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.setConfig(SkBitmap::kARGB_8888_Config, |
| kCheckerboardWidth, kCheckerboardHeight); |
| fCheckerboard.allocPixels(); |
| SkAutoLockPixels lock(fCheckerboard); |
| 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; ) |