Home | History | Annotate | Download | only in gm
      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