| /* |
| * 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" |
| |
| #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" |
| #include "include/effects/SkImageFilters.h" |
| |
| #include "src/core/SkImageFilterTypes.h" |
| #include "src/core/SkImageFilter_Base.h" |
| |
| #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; |
| } |
| |
| 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; |
| } |
| |
| class FilterBoundsSample : public Sample { |
| public: |
| FilterBoundsSample() {} |
| |
| void onOnceBeforeDraw() override { |
| fBlur = SkImageFilters::Blur(8.f, 8.f, nullptr); |
| fImage = SkImage::MakeFromBitmap(ToolUtils::create_checkerboard_bitmap( |
| 300, 300, SK_ColorMAGENTA, SK_ColorLTGRAY, 50)); |
| } |
| |
| 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); |
| SkMatrix ctm = canvas->getTotalMatrix(); |
| |
| // Base rendering of a filter |
| SkPaint blurPaint; |
| blurPaint.setImageFilter(fBlur); |
| canvas->saveLayer(&localContentRect, &blurPaint); |
| SkPaint imagePaint; |
| imagePaint.setFilterQuality(kLow_SkFilterQuality); |
| canvas->drawImageRect(fImage, localContentRect, &imagePaint); |
| canvas->restore(); |
| |
| // Now visualize the underlying bounds calculations used to determine the layer for the blur |
| skif::Mapping mapping = skif::Mapping::DecomposeCTM(ctm, fBlur.get()); |
| |
| // Add axis lines, to show perspective distortion |
| canvas->drawPath(create_axis_path(localContentRect, 10.f), line_paint(SK_ColorGRAY)); |
| |
| // 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. |
| const SkRect devContentRect = ctm.mapRect(localContentRect); |
| canvas->setMatrix(SkMatrix::I()); |
| canvas->drawRect(devContentRect, line_paint(SK_ColorDKGRAY)); |
| |
| // Layer bounds for the filter, in the layer space compatible with the filter's matrix |
| // type requirements. |
| skif::ParameterSpace<SkRect> contentBounds(localContentRect); |
| skif::DeviceSpace<SkIRect> targetOutput(devContentRect.roundOut()); |
| 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(); |
| print_info(canvas, SkIRect(mapping.paramToLayer(contentBounds).roundOut()), |
| devContentRect.roundOut(), |
| SkIRect(hintedOutputBounds), |
| SkIRect(unhintedLayerBounds)); |
| } |
| |
| SkString name() override { return SkString("FilterBounds"); } |
| |
| private: |
| sk_sp<SkImageFilter> fBlur; |
| sk_sp<SkImage> fImage; |
| |
| using INHERITED = Sample; |
| }; |
| |
| DEF_SAMPLE(return new FilterBoundsSample();) |