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