2020-10-14 19:59:02 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2019 Google LLC
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "samplecode/Sample.h"
|
|
|
|
|
2020-12-23 15:11:33 +00:00
|
|
|
#include "include/core/SkBitmap.h"
|
2020-10-14 19:59:02 +00:00
|
|
|
#include "include/core/SkCanvas.h"
|
|
|
|
#include "include/core/SkColor.h"
|
|
|
|
#include "include/core/SkFont.h"
|
|
|
|
#include "include/core/SkPaint.h"
|
|
|
|
#include "include/core/SkPath.h"
|
|
|
|
#include "include/core/SkPoint.h"
|
|
|
|
#include "include/core/SkRect.h"
|
|
|
|
#include "include/effects/SkDashPathEffect.h"
|
2020-10-16 14:38:31 +00:00
|
|
|
#include "include/effects/SkGradientShader.h"
|
2020-10-14 19:59:02 +00:00
|
|
|
#include "include/effects/SkImageFilters.h"
|
|
|
|
|
|
|
|
#include "src/core/SkImageFilterTypes.h"
|
|
|
|
#include "src/core/SkImageFilter_Base.h"
|
2020-10-16 14:38:31 +00:00
|
|
|
#include "src/core/SkMatrixPriv.h"
|
2020-10-14 19:59:02 +00:00
|
|
|
|
|
|
|
#include "tools/ToolUtils.h"
|
|
|
|
|
|
|
|
static constexpr float kLineHeight = 16.f;
|
|
|
|
static constexpr float kLineInset = 8.f;
|
|
|
|
|
|
|
|
static float print_size(SkCanvas* canvas, const char* prefix, const SkIRect& rect,
|
|
|
|
float x, float y, const SkFont& font, const SkPaint& paint) {
|
|
|
|
canvas->drawString(prefix, x, y, font, paint);
|
|
|
|
y += kLineHeight;
|
|
|
|
SkString sz;
|
|
|
|
sz.appendf("%d x %d", rect.width(), rect.height());
|
|
|
|
canvas->drawString(sz, x, y, font, paint);
|
|
|
|
return y + kLineHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
static float print_info(SkCanvas* canvas,
|
|
|
|
const SkIRect& layerContentBounds,
|
|
|
|
const SkIRect& outputBounds,
|
|
|
|
const SkIRect& hintedOutputBounds,
|
|
|
|
const SkIRect& unhintedLayerBounds) {
|
|
|
|
SkFont font(nullptr, 12);
|
|
|
|
SkPaint text;
|
|
|
|
text.setAntiAlias(true);
|
|
|
|
|
|
|
|
float y = kLineHeight;
|
|
|
|
|
|
|
|
text.setColor(SK_ColorRED);
|
|
|
|
y = print_size(canvas, "Content (in layer)", layerContentBounds, kLineInset, y, font, text);
|
|
|
|
text.setColor(SK_ColorDKGRAY);
|
|
|
|
y = print_size(canvas, "Target (in device)", outputBounds, kLineInset, y, font, text);
|
|
|
|
text.setColor(SK_ColorBLUE);
|
|
|
|
y = print_size(canvas, "Output (w/ hint)", hintedOutputBounds, kLineInset, y, font, text);
|
|
|
|
text.setColor(SK_ColorGREEN);
|
|
|
|
y = print_size(canvas, "Input (w/ no hint)", unhintedLayerBounds, kLineInset, y, font, text);
|
|
|
|
|
|
|
|
return y;
|
|
|
|
}
|
|
|
|
|
2020-10-16 14:38:31 +00:00
|
|
|
static void print_label(SkCanvas* canvas, float x, float y, float value) {
|
|
|
|
SkFont font(nullptr, 12);
|
|
|
|
SkPaint text;
|
|
|
|
text.setAntiAlias(true);
|
|
|
|
|
|
|
|
SkString label;
|
|
|
|
label.printf("%.3f", value);
|
|
|
|
|
|
|
|
canvas->drawString(label, x, y + kLineHeight / 2.f, font, text);
|
|
|
|
}
|
|
|
|
|
2020-10-14 19:59:02 +00:00
|
|
|
static SkPaint line_paint(SkColor color, bool dashed = false) {
|
|
|
|
SkPaint paint;
|
|
|
|
paint.setColor(color);
|
|
|
|
paint.setStrokeWidth(0.f);
|
|
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
|
|
paint.setAntiAlias(true);
|
|
|
|
if (dashed) {
|
|
|
|
SkScalar dash[2] = {10.f, 10.f};
|
|
|
|
paint.setPathEffect(SkDashPathEffect::Make(dash, 2, 0.f));
|
|
|
|
}
|
|
|
|
return paint;
|
|
|
|
}
|
|
|
|
|
|
|
|
static SkPath create_axis_path(const SkRect& rect, float axisSpace) {
|
|
|
|
SkPath localSpace;
|
|
|
|
for (float y = rect.fTop + axisSpace; y <= rect.fBottom; y += axisSpace) {
|
|
|
|
localSpace.moveTo(rect.fLeft, y);
|
|
|
|
localSpace.lineTo(rect.fRight, y);
|
|
|
|
}
|
|
|
|
for (float x = rect.fLeft + axisSpace; x <= rect.fRight; x += axisSpace) {
|
|
|
|
localSpace.moveTo(x, rect.fTop);
|
|
|
|
localSpace.lineTo(x, rect.fBottom);
|
|
|
|
}
|
|
|
|
return localSpace;
|
|
|
|
}
|
|
|
|
|
2020-10-16 14:38:31 +00:00
|
|
|
static const SkColor4f kScaleGradientColors[] =
|
|
|
|
{ { 0.05f, 0.0f, 6.f, 1.f }, // Severe downscaling, s < 1/8, log(s) < -3
|
|
|
|
{ 0.6f, 0.6f, 0.8f, 0.6f }, // Okay downscaling, s < 1/2, log(s) < -1
|
|
|
|
{ 1.f, 1.f, 1.f, 0.2f }, // No scaling, s = 1, log(s) = 0
|
|
|
|
{ 0.95f, 0.6f, 0.5f, 0.6f }, // Okay upscaling, s > 2, log(s) > 1
|
|
|
|
{ 0.8f, 0.1f, 0.f, 1.f } }; // Severe upscaling, s > 8, log(s) > 3
|
|
|
|
static const SkScalar kLogScaleFactors[] = { -3.f, -1.f, 0.f, 1.f, 3.f };
|
|
|
|
static const SkScalar kGradientStops[] = { 0.f, 0.33333f, 0.5f, 0.66667f, 1.f };
|
|
|
|
static const int kStopCount = (int) SK_ARRAY_COUNT(kScaleGradientColors);
|
|
|
|
|
|
|
|
static void draw_scale_key(SkCanvas* canvas, float y) {
|
|
|
|
SkRect key = SkRect::MakeXYWH(15.f, y + 30.f, 15.f, 100.f);
|
|
|
|
SkPoint pts[] = {{key.centerX(), key.fTop}, {key.centerX(), key.fBottom}};
|
|
|
|
sk_sp<SkShader> gradient = SkGradientShader::MakeLinear(
|
|
|
|
pts, kScaleGradientColors, nullptr, kGradientStops, kStopCount, SkTileMode::kClamp,
|
|
|
|
SkGradientShader::kInterpolateColorsInPremul_Flag, nullptr);
|
|
|
|
SkPaint keyPaint;
|
|
|
|
keyPaint.setShader(gradient);
|
|
|
|
canvas->drawRect(key, keyPaint);
|
|
|
|
for (int i = 0; i < kStopCount; ++i) {
|
|
|
|
print_label(canvas, key.fRight + 5.f, key.fTop + kGradientStops[i] * key.height(),
|
|
|
|
SkScalarPow(2.f, kLogScaleFactors[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void draw_scale_factors(SkCanvas* canvas, const skif::Mapping& mapping, const SkRect& rect) {
|
|
|
|
SkPoint testPoints[5];
|
|
|
|
testPoints[0] = {rect.centerX(), rect.centerY()};
|
|
|
|
rect.toQuad(testPoints + 1);
|
|
|
|
for (int i = 0; i < 5; ++i) {
|
2020-10-16 16:30:07 +00:00
|
|
|
float scale = SkMatrixPriv::DifferentialAreaScale(
|
2020-10-16 14:38:31 +00:00
|
|
|
mapping.deviceMatrix(),
|
|
|
|
SkPoint(mapping.paramToLayer(skif::ParameterSpace<SkPoint>(testPoints[i]))));
|
|
|
|
SkColor4f color = {0.f, 0.f, 0.f, 1.f};
|
|
|
|
|
|
|
|
if (SkScalarIsFinite(scale)) {
|
|
|
|
float logScale = SkScalarLog2(scale);
|
|
|
|
for (int j = 0; j <= kStopCount; ++j) {
|
|
|
|
if (j == kStopCount) {
|
|
|
|
color = kScaleGradientColors[j - 1];
|
|
|
|
break;
|
|
|
|
} else if (kLogScaleFactors[j] >= logScale) {
|
|
|
|
if (j == 0) {
|
|
|
|
color = kScaleGradientColors[0];
|
|
|
|
} else {
|
|
|
|
SkScalar t = (logScale - kLogScaleFactors[j - 1]) /
|
|
|
|
(kLogScaleFactors[j] - kLogScaleFactors[j - 1]);
|
|
|
|
|
|
|
|
SkColor4f a = kScaleGradientColors[j - 1] * (1.f - t);
|
|
|
|
SkColor4f b = kScaleGradientColors[j] * t;
|
|
|
|
color = {a.fR + b.fR, a.fG + b.fG, a.fB + b.fB, a.fA + b.fA};
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPaint p;
|
|
|
|
p.setAntiAlias(true);
|
|
|
|
p.setColor4f(color, nullptr);
|
|
|
|
canvas->drawRect(SkRect::MakeLTRB(testPoints[i].fX - 4.f, testPoints[i].fY - 4.f,
|
|
|
|
testPoints[i].fX + 4.f, testPoints[i].fY + 4.f), p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-14 19:59:02 +00:00
|
|
|
class FilterBoundsSample : public Sample {
|
|
|
|
public:
|
|
|
|
FilterBoundsSample() {}
|
|
|
|
|
|
|
|
void onOnceBeforeDraw() override {
|
|
|
|
fBlur = SkImageFilters::Blur(8.f, 8.f, nullptr);
|
2020-12-23 16:50:36 +00:00
|
|
|
fImage = ToolUtils::create_checkerboard_image(
|
|
|
|
300, 300, SK_ColorMAGENTA, SK_ColorLTGRAY, 50);
|
2020-10-14 19:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void onDrawContent(SkCanvas* canvas) override {
|
|
|
|
// The local content, e.g. what would be submitted to drawRect or the bounds to saveLayer
|
|
|
|
const SkRect localContentRect = SkRect::MakeLTRB(100.f, 20.f, 180.f, 140.f);
|
2020-12-03 16:21:31 +00:00
|
|
|
SkMatrix ctm = canvas->getLocalToDeviceAs3x3();
|
2020-10-14 19:59:02 +00:00
|
|
|
|
|
|
|
// Base rendering of a filter
|
|
|
|
SkPaint blurPaint;
|
|
|
|
blurPaint.setImageFilter(fBlur);
|
|
|
|
canvas->saveLayer(&localContentRect, &blurPaint);
|
2020-12-28 19:45:28 +00:00
|
|
|
canvas->drawImageRect(fImage.get(), localContentRect, localContentRect,
|
2021-01-15 02:59:01 +00:00
|
|
|
SkSamplingOptions(SkFilterMode::kLinear),
|
Reland "Add new virts, hide old ones"
This reverts commit 8f924ac0ce63806886b7297e8be554984a6e7ce5.
Reason for revert: suppressions landed for fuchsia images to rebaseline
Original change's description:
> Revert "Add new virts, hide old ones"
>
> This reverts commit c56e2e5aa65dd129e5927224d2f6c1f82edff74e.
>
> Reason for revert: suspected of breaking chrome roll
>
> Original change's description:
> > Add new virts, hide old ones
> >
> > Add virtuals for the draw methods that now take sampling/filtermode.
> >
> > drawImage
> > drawImageRect
> > drawImageLattice
> > drawAtlas
> >
> > Add a flag that can remove the older virtuals, once each client has
> > stopped overriding them. In that situation, the older public methods
> > will simplify extract the sampling from the paint, and call the new
> > public methods.
> >
> > Bug: skia:11105, skia:7650
> > Change-Id: I8b0029727295caa983e8148fc743a55cfbecd043
> > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/347022
> > Commit-Queue: Mike Reed <reed@google.com>
> > Reviewed-by: Florin Malita <fmalita@chromium.org>
> > Reviewed-by: Brian Salomon <bsalomon@google.com>
>
> TBR=bsalomon@google.com,fmalita@chromium.org,reed@google.com
>
> Change-Id: I0a90952c11a180d918126ea06a630f4a0bf9b49b
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Bug: skia:11105
> Bug: skia:7650
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/348194
> Reviewed-by: Derek Sollenberger <djsollen@google.com>
> Commit-Queue: Derek Sollenberger <djsollen@google.com>
TBR=djsollen@google.com,bsalomon@google.com,fmalita@chromium.org,reed@google.com
# Not skipping CQ checks because this is a reland.
Bug: skia:11105
Bug: skia:7650
Change-Id: Ia2b4537a2d330460b7554278d2c05075cf27162a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/348876
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Mike Reed <reed@google.com>
2020-12-30 14:22:42 +00:00
|
|
|
nullptr, SkCanvas::kFast_SrcRectConstraint);
|
2020-10-14 19:59:02 +00:00
|
|
|
canvas->restore();
|
|
|
|
|
|
|
|
// Now visualize the underlying bounds calculations used to determine the layer for the blur
|
2020-10-16 16:30:07 +00:00
|
|
|
SkIRect target = ctm.mapRect(localContentRect).roundOut();
|
|
|
|
if (!target.intersect(SkIRect::MakeWH(canvas->imageInfo().width(),
|
|
|
|
canvas->imageInfo().height()))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
skif::DeviceSpace<SkIRect> targetOutput(target);
|
|
|
|
skif::ParameterSpace<SkRect> contentBounds(localContentRect);
|
Don't assume DecomposeCTM and setDeviceCoordSystem always succeed
Very originally, skif::Mapping::DecomposeCTM() and
SkBaseDevice::setDeviceCoordinateSystem assumed that if the canvas
matrix was invertible, then any scale decomposition would produce a
valid device coordinate system. This proved not to be true and fuzzers
quickly caught it, but I had attempted to address it by forcing
SkCanvas to do extra work so that the above two functions remained
unchanged.
However, it's become apparent that even making the assumption that the
product of two invertible matrices remains invertible does not always
hold true in the wonderful world of floating point math.
Instead, this rewrites DecomposeCTM and setDeviceCoordinateSystem to
return bools, allowing them to fail. This cleans up some of the earlier
checks that SkCanvas makes while computing the skif::Mapping, and it
also ensures that once we fold in the prior device's transform, the
net layer->global transform remains valid. If any of this fails, it
just gets rid of the new device and sets the clip to empty, basically
preventing drawing until the invalid layer has been restored.
Bug: chromium:1239968, chromium:1240685
Change-Id: Ib9ce8f95859e726a9eacf1154f6eef8dd3995500
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/442017
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2021-08-25 19:04:10 +00:00
|
|
|
skif::ParameterSpace<SkPoint> contentCenter({localContentRect.centerX(),
|
|
|
|
localContentRect.centerY()});
|
|
|
|
skif::Mapping mapping;
|
|
|
|
SkAssertResult(mapping.decomposeCTM(ctm, fBlur.get(), contentCenter));
|
2020-10-14 19:59:02 +00:00
|
|
|
|
|
|
|
// Add axis lines, to show perspective distortion
|
2020-10-16 16:30:07 +00:00
|
|
|
canvas->save();
|
|
|
|
canvas->setMatrix(mapping.deviceMatrix());
|
|
|
|
canvas->drawPath(create_axis_path(SkRect(mapping.paramToLayer(contentBounds)), 20.f),
|
|
|
|
line_paint(SK_ColorGRAY));
|
|
|
|
canvas->restore();
|
2020-10-14 19:59:02 +00:00
|
|
|
|
2020-10-16 14:38:31 +00:00
|
|
|
// Visualize scale factors at the four corners and center of the local rect
|
|
|
|
draw_scale_factors(canvas, mapping, localContentRect);
|
|
|
|
|
2020-10-14 19:59:02 +00:00
|
|
|
// The device content rect, e.g. the clip bounds if 'localContentRect' were used as a clip
|
|
|
|
// before the draw or saveLayer, representing what the filter must cover if it affects
|
|
|
|
// transparent black or doesn't have a local content hint.
|
|
|
|
canvas->setMatrix(SkMatrix::I());
|
2020-10-16 16:30:07 +00:00
|
|
|
canvas->drawRect(ctm.mapRect(localContentRect), line_paint(SK_ColorDKGRAY));
|
2020-10-14 19:59:02 +00:00
|
|
|
|
|
|
|
// Layer bounds for the filter, in the layer space compatible with the filter's matrix
|
|
|
|
// type requirements.
|
|
|
|
skif::LayerSpace<SkIRect> targetOutputInLayer = mapping.deviceToLayer(targetOutput);
|
|
|
|
skif::LayerSpace<SkIRect> hintedLayerBounds = as_IFB(fBlur)->getInputBounds(
|
|
|
|
mapping, targetOutput, &contentBounds);
|
|
|
|
skif::LayerSpace<SkIRect> unhintedLayerBounds = as_IFB(fBlur)->getInputBounds(
|
|
|
|
mapping, targetOutput, nullptr);
|
|
|
|
|
|
|
|
canvas->setMatrix(mapping.deviceMatrix());
|
|
|
|
canvas->drawRect(SkRect::Make(SkIRect(targetOutputInLayer)),
|
|
|
|
line_paint(SK_ColorDKGRAY, true));
|
|
|
|
canvas->drawRect(SkRect::Make(SkIRect(hintedLayerBounds)), line_paint(SK_ColorRED));
|
|
|
|
canvas->drawRect(SkRect::Make(SkIRect(unhintedLayerBounds)), line_paint(SK_ColorGREEN));
|
|
|
|
|
|
|
|
// For visualization purposes, we want to show the layer-space output, this is what we get
|
|
|
|
// when contentBounds is provided as a hint in local/parameter space.
|
|
|
|
skif::Mapping layerOnly(SkMatrix::I(), mapping.layerMatrix());
|
|
|
|
skif::DeviceSpace<SkIRect> hintedOutputBounds = as_IFB(fBlur)->getOutputBounds(
|
|
|
|
layerOnly, contentBounds);
|
|
|
|
canvas->drawRect(SkRect::Make(SkIRect(hintedOutputBounds)), line_paint(SK_ColorBLUE));
|
|
|
|
|
|
|
|
canvas->resetMatrix();
|
2020-10-16 14:38:31 +00:00
|
|
|
float y = print_info(canvas, SkIRect(mapping.paramToLayer(contentBounds).roundOut()),
|
2020-10-16 16:30:07 +00:00
|
|
|
SkIRect(targetOutput),
|
|
|
|
SkIRect(hintedOutputBounds),
|
|
|
|
SkIRect(unhintedLayerBounds));
|
2020-10-16 14:38:31 +00:00
|
|
|
|
|
|
|
// Draw color key for layer visualization
|
|
|
|
draw_scale_key(canvas, y);
|
2020-10-14 19:59:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SkString name() override { return SkString("FilterBounds"); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
sk_sp<SkImageFilter> fBlur;
|
|
|
|
sk_sp<SkImage> fImage;
|
|
|
|
|
|
|
|
using INHERITED = Sample;
|
|
|
|
};
|
|
|
|
|
|
|
|
DEF_SAMPLE(return new FilterBoundsSample();)
|