aboutsummaryrefslogtreecommitdiffhomepage
path: root/gm/recordopts.cpp
diff options
context:
space:
mode:
authorGravatar kkinnunen <kkinnunen@nvidia.com>2015-01-26 00:14:26 -0800
committerGravatar Commit bot <commit-bot@chromium.org>2015-01-26 00:14:26 -0800
commitdc0f408a961b6ce4e7631e06031bbfe1d8d7bcec (patch)
tree0c039bb8fd3ea59d89a3d5fd088af5404f1dca72 /gm/recordopts.cpp
parent5c1dad7c751b7fa5a6291a15af3cfabb06808cba (diff)
Fold alpha to the inner savelayer in savelayer-savelayer-restore patterns
Fold alpha to the inner savelayer in savelayer-savelayer-restore patterns such as this: SaveLayer (non-opaque) Save ClipRect SaveLayer Restore Restore Restore Current blink generates these for example for SVG content such as this: <path style="opacity:0.5 filter:url(#blur_filter)"/> The outer save layer is due to the opacity and the inner one is due to blur filter being implemented with picture image filter. Reduces layers in desk_carsvg.skp testcase from 115 to 78. BUG=skia:3119 Review URL: https://codereview.chromium.org/835973005
Diffstat (limited to 'gm/recordopts.cpp')
-rw-r--r--gm/recordopts.cpp218
1 files changed, 218 insertions, 0 deletions
diff --git a/gm/recordopts.cpp b/gm/recordopts.cpp
new file mode 100644
index 0000000000..08202111f5
--- /dev/null
+++ b/gm/recordopts.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+#include "SkPictureRecorder.h"
+#include "SkTableColorFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkPictureImageFilter.h"
+
+static const int kTestRectSize = 50;
+static const 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 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::CreateARGB(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())->unref();
+}
+
+// This detector detects that image filter phase of the pixel pipeline receives the correct value.
+static void install_detector_image_filter(SkPaint* drawPaint) {
+ SkAutoTUnref<SkColorFilter> colorFilter(make_detector_color_filter());
+ SkImageFilter* imageFilter =
+ SkColorFilterImageFilter::Create(colorFilter, drawPaint->getImageFilter());
+ drawPaint->setImageFilter(imageFilter)->unref();
+}
+
+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 canvas(bitmap);
+ SkPaint p;
+ p.setColor(SK_ColorWHITE);
+ SkASSERT(shapeColor != SK_ColorWHITE);
+ canvas.drawRect(SkRect::MakeWH(SkIntToScalar(7), SkIntToScalar(7)), p);
+ canvas.flush();
+ }
+
+ 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->drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0), &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)));
+ SkAutoTUnref<SkPicture> shape;
+ {
+ SkPictureRecorder recorder;
+ SkCanvas* canvas = recorder.beginRecording(SkIntToScalar(kTestRectSize + 2),
+ SkIntToScalar(kTestRectSize + 2));
+ SkPaint shapePaint;
+ shapePaint.setColor(shapeColor);
+ canvas->drawRect(targetRect, shapePaint);
+ shape.reset(recorder.endRecordingAsPicture());
+ }
+
+ SkPaint layerPaint;
+ layerPaint.setColor(SkColorSetARGB(130, 0, 0, 0));
+ canvas->saveLayer(&targetRect, &layerPaint);
+ canvas->save();
+ canvas->clipRect(targetRect);
+ SkPaint drawPaint;
+ drawPaint.setImageFilter(SkPictureImageFilter::Create(shape))->unref();
+ 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) {
+ 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);
+ canvas->flush();
+ canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
+ {
+ SkPictureRecorder recorder;
+ drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
+ SkIntToScalar(kTestRectSize)),
+ shapeColor, no_detector_install);
+ SkAutoTUnref<SkPicture> optimizedPicture(recorder.endRecordingAsPicture());
+ optimizedPicture->playback(canvas);
+ canvas->flush();
+ }
+ 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(shapeColors); ++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);
+ canvas->flush();
+ canvas->translate(SkIntToScalar(kTestRectSize) + SkIntToScalar(1), SkIntToScalar(0));
+ {
+ SkPictureRecorder recorder;
+ drawTestSequence(recorder.beginRecording(SkIntToScalar(kTestRectSize),
+ SkIntToScalar(kTestRectSize)),
+ shapeColor, detectorInstallFunc);
+ SkAutoTUnref<SkPicture> optimizedPicture(recorder.endRecordingAsPicture());
+ optimizedPicture->playback(canvas);
+ canvas->flush();
+ }
+
+ canvas->restore();
+ canvas->translate(SkIntToScalar(0), SkIntToScalar(kTestRectSize) + SkIntToScalar(1));
+ }
+
+ }
+ }
+}
+