231 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			231 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright 2014 Google Inc.
 | |
|  *
 | |
|  * Use of this source code is governed by a BSD-style license that can be
 | |
|  * found in the LICENSE file.
 | |
|  */
 | |
| 
 | |
| #include "gm/gm.h"
 | |
| #include "include/core/SkBitmap.h"
 | |
| #include "include/core/SkCanvas.h"
 | |
| #include "include/core/SkColor.h"
 | |
| #include "include/core/SkColorFilter.h"
 | |
| #include "include/core/SkImageFilter.h"
 | |
| #include "include/core/SkPaint.h"
 | |
| #include "include/core/SkPicture.h"
 | |
| #include "include/core/SkPictureRecorder.h"
 | |
| #include "include/core/SkRect.h"
 | |
| #include "include/core/SkRefCnt.h"
 | |
| #include "include/core/SkScalar.h"
 | |
| #include "include/core/SkTypes.h"
 | |
| #include "include/effects/SkImageFilters.h"
 | |
| #include "include/effects/SkTableColorFilter.h"
 | |
| #include "include/gpu/GrDirectContext.h"
 | |
| 
 | |
| constexpr int kTestRectSize = 50;
 | |
| constexpr int kDetectorGreenValue = 50;
 | |
| 
 | |
| // Below are few functions to install "detector" color filters. The filter is there to assert that
 | |
| // the color value it sees is the expected. It will trigger only with kDetectorGreenValue, and
 | |
| // turn that value into full green. The idea is that if an optimization incorrectly changes
 | |
| // kDetectorGreenValue and then the incorrect value is observable by some part of the drawing
 | |
| // pipeline, that pixel will remain empty.
 | |
| 
 | |
| static sk_sp<SkColorFilter> make_detector_color_filter() {
 | |
|     uint8_t tableA[256] = { 0, };
 | |
|     uint8_t tableR[256] = { 0, };
 | |
|     uint8_t tableG[256] = { 0, };
 | |
|     uint8_t tableB[256] = { 0, };
 | |
|     tableA[255] = 255;
 | |
|     tableG[kDetectorGreenValue] = 255;
 | |
|     return SkTableColorFilter::MakeARGB(tableA, tableR, tableG, tableB);
 | |
| }
 | |
| 
 | |
| // This detector detects that color filter phase of the pixel pipeline receives the correct value.
 | |
| static void install_detector_color_filter(SkPaint* drawPaint) {
 | |
|     drawPaint->setColorFilter(make_detector_color_filter());
 | |
| }
 | |
| 
 | |
| // This detector detects that image filter phase of the pixel pipeline receives the correct value.
 | |
| static void install_detector_image_filter(SkPaint* drawPaint) {
 | |
|     drawPaint->setImageFilter(SkImageFilters::ColorFilter(
 | |
|             make_detector_color_filter(), drawPaint->refImageFilter()));
 | |
| }
 | |
| 
 | |
| static void no_detector_install(SkPaint*) {
 | |
| }
 | |
| 
 | |
| typedef void(*InstallDetectorFunc)(SkPaint*);
 | |
| 
 | |
| 
 | |
| // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
 | |
| // inner draw. Since we know that folding will happen to the inner draw, install a detector
 | |
| // to make sure that optimization does not change anything observable.
 | |
| static void draw_save_layer_draw_rect_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
 | |
|                                                        InstallDetectorFunc installDetector) {
 | |
|     SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
 | |
|     SkPaint layerPaint;
 | |
|     layerPaint.setColor(SkColorSetARGB(128, 0, 0, 0));
 | |
|     canvas->saveLayer(&targetRect, &layerPaint);
 | |
|         SkPaint drawPaint;
 | |
|         drawPaint.setColor(shapeColor);
 | |
|         installDetector(&drawPaint);
 | |
|         canvas->drawRect(targetRect, drawPaint);
 | |
|     canvas->restore();
 | |
| }
 | |
| 
 | |
| // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
 | |
| // inner draw. A variant where the draw is not uniform color.
 | |
| static void draw_save_layer_draw_bitmap_restore_sequence(SkCanvas* canvas, SkColor shapeColor,
 | |
|                                                          InstallDetectorFunc installDetector) {
 | |
|     SkBitmap bitmap;
 | |
|     bitmap.allocN32Pixels(kTestRectSize, kTestRectSize);
 | |
|     bitmap.eraseColor(shapeColor);
 | |
|     {
 | |
|         // Make the bitmap non-uniform color, so that it can not be optimized as uniform drawRect.
 | |
|         SkCanvas bitmapCanvas(bitmap);
 | |
|         SkPaint p;
 | |
|         p.setColor(SK_ColorWHITE);
 | |
|         SkASSERT(shapeColor != SK_ColorWHITE);
 | |
|         bitmapCanvas.drawRect(SkRect::MakeWH(SkIntToScalar(7), SkIntToScalar(7)), p);
 | |
|     }
 | |
| 
 | |
|     SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
 | |
|     SkPaint layerPaint;
 | |
|     layerPaint.setColor(SkColorSetARGB(129, 0, 0, 0));
 | |
|     canvas->saveLayer(&targetRect, &layerPaint);
 | |
|         SkPaint drawPaint;
 | |
|         installDetector(&drawPaint);
 | |
|         canvas->drawImage(bitmap.asImage(), 0, 0, SkSamplingOptions(), &drawPaint);
 | |
|     canvas->restore();
 | |
| }
 | |
| 
 | |
| // Draws an pattern that can be optimized by alpha folding outer savelayer alpha value to
 | |
| // inner savelayer. We know that alpha folding happens to inner savelayer, so add detector there.
 | |
| static void draw_svg_opacity_and_filter_layer_sequence(SkCanvas* canvas, SkColor shapeColor,
 | |
|                                                        InstallDetectorFunc installDetector) {
 | |
| 
 | |
|     SkRect targetRect(SkRect::MakeWH(SkIntToScalar(kTestRectSize), SkIntToScalar(kTestRectSize)));
 | |
|     sk_sp<SkPicture> shape;
 | |
|     {
 | |
|         SkPictureRecorder recorder;
 | |
|         SkCanvas* recordCanvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2),
 | |
|                                                          SkIntToScalar(kTestRectSize + 2));
 | |
|         SkPaint shapePaint;
 | |
|         shapePaint.setColor(shapeColor);
 | |
|         recordCanvas->drawRect(targetRect, shapePaint);
 | |
|         shape = recorder.finishRecordingAsPicture();
 | |
|     }
 | |
| 
 | |
|     SkPaint layerPaint;
 | |
|     layerPaint.setColor(SkColorSetARGB(130, 0, 0, 0));
 | |
|     canvas->saveLayer(&targetRect, &layerPaint);
 | |
|         canvas->save();
 | |
|             canvas->clipRect(targetRect);
 | |
|             SkPaint drawPaint;
 | |
|             drawPaint.setImageFilter(SkImageFilters::Picture(shape));
 | |
|             installDetector(&drawPaint);
 | |
|             canvas->saveLayer(&targetRect, &drawPaint);
 | |
|             canvas->restore();
 | |
|         canvas->restore();
 | |
|     canvas->restore();
 | |
| }
 | |
| 
 | |
| // Draws two columns of rectangles. The test is correct when:
 | |
| //  - Left and right columns always identical
 | |
| //  - First 3 rows are green, with a white dent in the middle row
 | |
| //  - Next 6 rows are green, with a grey dent in the middle row
 | |
| //    (the grey dent is from the color filter removing everything but the "good" green, see below)
 | |
| //  - Last 6 rows are grey
 | |
| DEF_SIMPLE_GM(recordopts, canvas, (kTestRectSize+1)*2, (kTestRectSize+1)*15) {
 | |
|     auto direct = GrAsDirectContext(canvas->recordingContext());
 | |
|     canvas->clear(SK_ColorTRANSPARENT);
 | |
| 
 | |
|     typedef void (*TestVariantSequence)(SkCanvas*, SkColor, InstallDetectorFunc);
 | |
|     TestVariantSequence funcs[] = {
 | |
|         draw_save_layer_draw_rect_restore_sequence,
 | |
|         draw_save_layer_draw_bitmap_restore_sequence,
 | |
|         draw_svg_opacity_and_filter_layer_sequence,
 | |
|     };
 | |
| 
 | |
|     // Draw layer-related sequences that can be optimized by folding the opacity layer alpha to
 | |
|     // the inner draw operation. This tries to trigger the optimization, and relies on gm diffs
 | |
|     // to keep the color value correct over time.
 | |
| 
 | |
|     // Draws two green rects side by side: one is without the optimization, the other is with
 | |
|     // the optimization applied.
 | |
| 
 | |
|     SkColor shapeColor = SkColorSetARGB(255, 0, 255, 0);
 | |
|     for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
 | |
|         canvas->save();
 | |
| 
 | |
|         TestVariantSequence drawTestSequence = funcs[k];
 | |
|         drawTestSequence(canvas, shapeColor, no_detector_install);
 | |
|         if (direct) {
 | |
|             direct->flushAndSubmit();
 | |
|         }
 | |
|         canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
 | |
|         {
 | |
|             SkPictureRecorder recorder;
 | |
|             drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
 | |
|                                                      SkIntToScalar(kTestRectSize)),
 | |
|                              shapeColor, no_detector_install);
 | |
|             recorder.finishRecordingAsPicture()->playback(canvas);
 | |
|             if (direct) {
 | |
|                 direct->flushAndSubmit();
 | |
|             }
 | |
|         }
 | |
|         canvas->restore();
 | |
|         canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
 | |
|     }
 | |
| 
 | |
|     // Draw the same layer related sequences, but manipulate the sequences so that the result is
 | |
|     // incorrect if the alpha is folded or folded incorrectly. These test the observable state
 | |
|     // throughout the pixel pipeline, and thus may turn off the optimizations (this is why we
 | |
|     // trigger the optimizations above).
 | |
| 
 | |
|     // Draws two green rects side by side: one is without the optimization, the other is with
 | |
|     // the possibility that optimization is applied.
 | |
|     // At the end, draws the same patterns in translucent black. This tests that the detectors
 | |
|     // work, eg. that if the value the detector sees is wrong, the resulting image shows this.
 | |
|     SkColor shapeColors[] = {
 | |
|         SkColorSetARGB(255, 0, kDetectorGreenValue, 0),
 | |
|         SkColorSetARGB(255, 0, (kDetectorGreenValue + 1), 0) // This tests that detectors work.
 | |
|     };
 | |
| 
 | |
|     InstallDetectorFunc detectorInstallFuncs[] = {
 | |
|         install_detector_image_filter,
 | |
|         install_detector_color_filter
 | |
|     };
 | |
| 
 | |
|     for (size_t i = 0; i < SK_ARRAY_COUNT(shapeColors); ++i) {
 | |
|         shapeColor = shapeColors[i];
 | |
|         for (size_t j = 0; j < SK_ARRAY_COUNT(detectorInstallFuncs); ++j) {
 | |
|             InstallDetectorFunc detectorInstallFunc = detectorInstallFuncs[j];
 | |
|             for (size_t k = 0; k < SK_ARRAY_COUNT(funcs); ++k) {
 | |
|                 TestVariantSequence drawTestSequence = funcs[k];
 | |
|                 canvas->save();
 | |
|                 drawTestSequence(canvas, shapeColor, detectorInstallFunc);
 | |
|                 if (direct) {
 | |
|                     direct->flushAndSubmit();
 | |
|                 }
 | |
|                 canvas->translate(SkIntToScalar(kTestRectSize + 1), SkIntToScalar(0));
 | |
|                 {
 | |
|                     SkPictureRecorder recorder;
 | |
|                     drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
 | |
|                                                              SkIntToScalar(kTestRectSize)),
 | |
|                                      shapeColor, detectorInstallFunc);
 | |
|                     recorder.finishRecordingAsPicture()->playback(canvas);
 | |
|                     if (direct) {
 | |
|                         direct->flushAndSubmit();
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 canvas->restore();
 | |
|                 canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize + 1));
 | |
|             }
 | |
| 
 | |
|         }
 | |
|     }
 | |
| }
 |