1 /* 2 * Copyright 2014 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #include "gm.h" 9 #include "SkCanvas.h" 10 #include "SkPath.h" 11 #include "SkPictureRecorder.h" 12 #include "SkTableColorFilter.h" 13 #include "SkColorFilterImageFilter.h" 14 #include "SkPictureImageFilter.h" 15 16 constexpr int kTestRectSize = 50; 17 constexpr int kDetectorGreenValue = 50; 18 19 // Below are few functions to install "detector" color filters. The filter is there to assert that 20 // the color value it sees is the expected. It will trigger only with kDetectorGreenValue, and 21 // turn that value into full green. The idea is that if an optimization incorrectly changes 22 // kDetectorGreenValue and then the incorrect value is observable by some part of the drawing 23 // pipeline, that pixel will remain empty. 24 25 static sk_sp<SkColorFilter> make_detector_color_filter() { 26 uint8_t tableA[256] = { 0, }; 27 uint8_t tableR[256] = { 0, }; 28 uint8_t tableG[256] = { 0, }; 29 uint8_t tableB[256] = { 0, }; 30 tableA[255] = 255; 31 tableG[kDetectorGreenValue] = 255; 32 return SkTableColorFilter::MakeARGB(tableA, tableR, tableG, tableB); 33 } 34 35 // This detector detects that color filter phase of the pixel pipeline receives the correct value. 36 static void install_detector_color_filter(SkPaint* drawPaint) { 37 drawPaint->setColorFilter(make_detector_color_filter()); 38 } 39 40 // This detector detects that image filter phase of the pixel pipeline receives the correct value. 41 static void install_detector_image_filter(SkPaint* drawPaint) { 42 drawPaint->setImageFilter(SkColorFilterImageFilter::Make(make_detector_color_filter(), 43 drawPaint->refImageFilter())); 44 } 45 46 static void no_detector_install(SkPaint*) { 47 } 48 49 typedef void(*InstallDetectorFunc)(SkPaint*); 50 51 52 // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to 53 // inner draw. Since we know that folding will happen to the inner draw, install a detector 54 // to make sure that optimization does not change anything observable. 55 static void draw_save_layer_draw_rect_restore_sequence(SkCanvas* canvas, SkColor shapeColor, 56 InstallDetectorFunc installDetector) { 57 SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize))); 58 SkPaint layerPaint; 59 layerPaint.setColor(SkColorSetARGB(128, 0, 0, 0)); 60 canvas->saveLayer(&targetRect, &layerPaint); 61 SkPaint drawPaint; 62 drawPaint.setColor(shapeColor); 63 installDetector(&drawPaint); 64 canvas->drawRect(targetRect, drawPaint); 65 canvas->restore(); 66 } 67 68 // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to 69 // inner draw. A variant where the draw is not uniform color. 70 static void draw_save_layer_draw_bitmap_restore_sequence(SkCanvas* canvas, SkColor shapeColor, 71 InstallDetectorFunc installDetector) { 72 SkBitmap bitmap; 73 bitmap.allocN32Pixels(kTestRectSize, kTestRectSize); 74 bitmap.eraseColor(shapeColor); 75 { 76 // Make the bitmap non-uniform color, so that it can not be optimized as uniform drawRect. 77 SkCanvas canvas(bitmap); 78 SkPaint p; 79 p.setColor(SK_ColorWHITE); 80 SkASSERT(shapeColor != SK_ColorWHITE); 81 canvas.drawRect(SkRect::MakeWH(SkIntToScalar(7), SkIntToScalar(7)), p); 82 canvas.flush(); 83 } 84 85 SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize))); 86 SkPaint layerPaint; 87 layerPaint.setColor(SkColorSetARGB(129, 0, 0, 0)); 88 canvas->saveLayer(&targetRect, &layerPaint); 89 SkPaint drawPaint; 90 installDetector(&drawPaint); 91 canvas->drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &drawPaint); 92 canvas->restore(); 93 } 94 95 // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to 96 // inner savelayer. We know that alpha folding happens to inner savelayer, so add detector there. 97 static void draw_svg_opacity_and_filter_layer_sequence(SkCanvas* canvas, SkColor shapeColor, 98 InstallDetectorFunc installDetector) { 99 100 SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize))); 101 sk_sp<SkPicture> shape; 102 { 103 SkPictureRecorder recorder; 104 SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2), 105 SkIntToScalar(kTestRectSize + 2)); 106 SkPaint shapePaint; 107 shapePaint.setColor(shapeColor); 108 canvas->drawRect(targetRect, shapePaint); 109 shape = recorder.finishRecordingAsPicture(); 110 } 111 112 SkPaint layerPaint; 113 layerPaint.setColor(SkColorSetARGB(130, 0, 0, 0)); 114 canvas->saveLayer(&targetRect, &layerPaint); 115 canvas->save(); 116 canvas->clipRect(targetRect); 117 SkPaint drawPaint; 118 drawPaint.setImageFilter(SkPictureImageFilter::Make(shape)); 119 installDetector(&drawPaint); 120 canvas->saveLayer(&targetRect, &drawPaint); 121 canvas->restore(); 122 canvas->restore(); 123 canvas->restore(); 124 } 125 126 // Draws two columns of rectangles. The test is correct when: 127 // - Left and right columns always identical 128 // - First 3 rows are green, with a white dent in the middle row 129 // - Next 6 rows are green, with a grey dent in the middle row 130 // (the grey dent is from the color filter removing everything but the "good" green, see below) 131 // - Last 6 rows are grey 132 DEF_SIMPLE_GM(recordopts, canvas, (kTestRectSize+1)*2, (kTestRectSize+1)*15) { 133 canvas->clear(SK_ColorTRANSPARENT); 134 135 typedef void (*TestVariantSequence)(SkCanvas*, SkColor, InstallDetectorFunc); 136 TestVariantSequence funcs[] = { 137 draw_save_layer_draw_rect_restore_sequence, 138 draw_save_layer_draw_bitmap_restore_sequence, 139 draw_svg_opacity_and_filter_layer_sequence, 140 }; 141 142 // Draw layer-related sequences that can be optimized by folding the opacity layer alpha to 143 // the inner draw operation. This tries to trigger the optimization, and relies on gm diffs 144 // to keep the color value correct over time. 145 146 // Draws two green rects side by side: one is without the optimization, the other is with 147 // the optimization applied. 148 149 SkColor shapeColor = SkColorSetARGB(255, 0, 255, 0); 150 for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) { 151 canvas->save(); 152 153 TestVariantSequence drawTestSequence = funcs[k]; 154 drawTestSequence(canvas, shapeColor, no_detector_install); 155 canvas->flush(); 156 canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0)); 157 { 158 SkPictureRecorder recorder; 159 drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize), 160 SkIntToScalar(kTestRectSize)), 161 shapeColor, no_detector_install); 162 recorder.finishRecordingAsPicture()->playback(canvas); 163 canvas->flush(); 164 } 165 canvas->restore(); 166 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1)); 167 } 168 169 // Draw the same layer related sequences, but manipulate the sequences so that the result is 170 // incorrect if the alpha is folded or folded incorrectly. These test the observable state 171 // throughout the pixel pipeline, and thus may turn off the optimizations (this is why we 172 // trigger the optimizations above). 173 174 // Draws two green rects side by side: one is without the optimization, the other is with 175 // the possibility that optimization is applied. 176 // At the end, draws the same patterns in translucent black. This tests that the detectors 177 // work, eg. that if the value the detector sees is wrong, the resulting image shows this. 178 SkColor shapeColors[] = { 179 SkColorSetARGB(255, 0, kDetectorGreenValue, 0), 180 SkColorSetARGB(255, 0, (kDetectorGreenValue + 1), 0) // This tests that detectors work. 181 }; 182 183 InstallDetectorFunc detectorInstallFuncs[] = { 184 install_detector_image_filter, 185 install_detector_color_filter 186 }; 187 188 for (size_t i = 0; i < SK_ARRAY_COUNT(shapeColors); ++i) { 189 shapeColor = shapeColors[i]; 190 for (size_t j = 0; j < SK_ARRAY_COUNT(detectorInstallFuncs); ++j) { 191 InstallDetectorFunc detectorInstallFunc = detectorInstallFuncs[j]; 192 for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) { 193 TestVariantSequence drawTestSequence = funcs[k]; 194 canvas->save(); 195 drawTestSequence(canvas, shapeColor, detectorInstallFunc); 196 canvas->flush(); 197 canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0)); 198 { 199 SkPictureRecorder recorder; 200 drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize), 201 SkIntToScalar(kTestRectSize)), 202 shapeColor, detectorInstallFunc); 203 recorder.finishRecordingAsPicture()->playback(canvas); 204 canvas->flush(); 205 } 206 207 canvas->restore(); 208 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1)); 209 } 210 211 } 212 } 213 } 214