From 2411ae616720e8d26286c2bb53abb38dff672d3f Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Wed, 18 Dec 2024 14:51:26 +0800 Subject: [PATCH 1/8] Modify the behavior of LayerFilter to allow LayerFilter to customize rendering logic. --- include/tgfx/layers/Layer.h | 2 +- include/tgfx/layers/filters/BlendFilter.h | 6 +- include/tgfx/layers/filters/BlurFilter.h | 6 +- .../tgfx/layers/filters/ColorMatrixFilter.h | 6 +- .../tgfx/layers/filters/DropShadowFilter.h | 6 +- .../tgfx/layers/filters/InnerShadowFilter.h | 6 +- include/tgfx/layers/filters/LayerFilter.h | 33 ++----- .../tgfx/layers/filters/LayerImageFilter.h | 63 ++++++++++++ src/layers/Layer.cpp | 35 +++---- src/layers/filters/BlendFilter.cpp | 2 +- src/layers/filters/BlurFilter.cpp | 2 +- src/layers/filters/ColorMatrixFilter.cpp | 2 +- src/layers/filters/DropShadowFilter.cpp | 4 +- src/layers/filters/InnerShadowFilter.cpp | 4 +- .../{LayerFilter.cpp => LayerImageFilter.cpp} | 36 +++++-- test/src/LayerShadowFilter.cpp | 98 +++++++++++++++++++ test/src/LayerShadowFilter.h | 59 +++++++++++ test/src/LayerTest.cpp | 33 +++++++ 18 files changed, 327 insertions(+), 76 deletions(-) create mode 100644 include/tgfx/layers/filters/LayerImageFilter.h rename src/layers/filters/{LayerFilter.cpp => LayerImageFilter.cpp} (57%) create mode 100644 test/src/LayerShadowFilter.cpp create mode 100644 test/src/LayerShadowFilter.h diff --git a/include/tgfx/layers/Layer.h b/include/tgfx/layers/Layer.h index 04937905..5b11dcbb 100644 --- a/include/tgfx/layers/Layer.h +++ b/include/tgfx/layers/Layer.h @@ -502,7 +502,7 @@ class Layer { Paint getLayerPaint(float alpha, BlendMode blendMode); - std::shared_ptr getLayerFilter(float contentScale); + std::shared_ptr applyFilter(std::shared_ptr source, float contentScale); LayerContent* getRasterizedCache(const DrawArgs& args); diff --git a/include/tgfx/layers/filters/BlendFilter.h b/include/tgfx/layers/filters/BlendFilter.h index 77e72505..f5303222 100644 --- a/include/tgfx/layers/filters/BlendFilter.h +++ b/include/tgfx/layers/filters/BlendFilter.h @@ -18,13 +18,13 @@ #pragma once -#include "tgfx/layers/filters/LayerFilter.h" +#include "tgfx/layers/filters/LayerImageFilter.h" namespace tgfx { -class BlendFilter : public LayerFilter { +class BlendFilter : public LayerImageFilter { public: - virtual ~BlendFilter() = default; + ~BlendFilter() override = default; /** * Creates a new ColorFilter that applies blends between the constant color (src) and input color diff --git a/include/tgfx/layers/filters/BlurFilter.h b/include/tgfx/layers/filters/BlurFilter.h index 49e7a7a2..725124f2 100644 --- a/include/tgfx/layers/filters/BlurFilter.h +++ b/include/tgfx/layers/filters/BlurFilter.h @@ -18,13 +18,13 @@ #pragma once -#include "tgfx/layers/filters/LayerFilter.h" +#include "tgfx/layers/filters/LayerImageFilter.h" namespace tgfx { -class BlurFilter : public LayerFilter { +class BlurFilter : public LayerImageFilter { public: - virtual ~BlurFilter() = default; + ~BlurFilter() override = default; /** * Create a filter that blurs its input by the separate X and Y blurriness. The provided tile mode diff --git a/include/tgfx/layers/filters/ColorMatrixFilter.h b/include/tgfx/layers/filters/ColorMatrixFilter.h index 26063e15..38839ed6 100644 --- a/include/tgfx/layers/filters/ColorMatrixFilter.h +++ b/include/tgfx/layers/filters/ColorMatrixFilter.h @@ -18,13 +18,13 @@ #pragma once -#include "tgfx/layers/filters/LayerFilter.h" +#include "tgfx/layers/filters/LayerImageFilter.h" namespace tgfx { -class ColorMatrixFilter : public LayerFilter { +class ColorMatrixFilter : public LayerImageFilter { public: - virtual ~ColorMatrixFilter() = default; + ~ColorMatrixFilter() override = default; /** * Creates a new ColorMatrixFilter that transforms the color using the given 4x5 matrix. The matrix can diff --git a/include/tgfx/layers/filters/DropShadowFilter.h b/include/tgfx/layers/filters/DropShadowFilter.h index d9070585..6b414110 100644 --- a/include/tgfx/layers/filters/DropShadowFilter.h +++ b/include/tgfx/layers/filters/DropShadowFilter.h @@ -18,12 +18,12 @@ #pragma once -#include "tgfx/layers/filters/LayerFilter.h" +#include "tgfx/layers/filters/LayerImageFilter.h" namespace tgfx { -class DropShadowFilter : public LayerFilter { +class DropShadowFilter : public LayerImageFilter { public: - virtual ~DropShadowFilter() = default; + ~DropShadowFilter() override = default; /** * Create a filter that draws a drop shadow under the input content. diff --git a/include/tgfx/layers/filters/InnerShadowFilter.h b/include/tgfx/layers/filters/InnerShadowFilter.h index e54893ba..0d412cce 100644 --- a/include/tgfx/layers/filters/InnerShadowFilter.h +++ b/include/tgfx/layers/filters/InnerShadowFilter.h @@ -18,12 +18,12 @@ #pragma once -#include "tgfx/layers/filters/LayerFilter.h" +#include "tgfx/layers/filters/LayerImageFilter.h" namespace tgfx { -class InnerShadowFilter : public LayerFilter { +class InnerShadowFilter : public LayerImageFilter { public: - virtual ~InnerShadowFilter() = default; + ~InnerShadowFilter() override = default; /** * Create a filter that draws an inner shadow over the input content. diff --git a/include/tgfx/layers/filters/LayerFilter.h b/include/tgfx/layers/filters/LayerFilter.h index d78d6c7e..f58d474c 100644 --- a/include/tgfx/layers/filters/LayerFilter.h +++ b/include/tgfx/layers/filters/LayerFilter.h @@ -28,35 +28,18 @@ namespace tgfx { */ class LayerFilter : public LayerProperty { public: - /** - * Returns the current image filter for the given scale factor. If the filter has not been - * created yet, it will be created and cached. + /* + * Applies the filter to the given picture and draws it to the canvas. * @param scale The scale factor to apply to the filter. - * @return The current image filter. + * @return True if the filter was applied and drawn, false otherwise. */ - std::shared_ptr getImageFilter(float scale); + virtual bool drawWithFilter(Canvas* canvas, std::shared_ptr picture, float scale) = 0; - protected: - /** - * Creates a new image filter for the given scale factor. When it is necessary to recreate the - * ImageFilter, the onCreateImageFilter method will be called. + /* + * Return the bounds of after applying the filter to the given bounds. * @param scale The scale factor to apply to the filter. - * @return A new image filter. + * @return The bounds of the filtered image. */ - virtual std::shared_ptr onCreateImageFilter(float scale) = 0; - - /** - * Marks the filter as dirty and invalidates the cached filter. - */ - void invalidateFilter(); - - private: - bool dirty = true; - - float lastScale = 1.0f; - - std::unique_ptr _clipBounds = nullptr; - - std::shared_ptr lastFilter; + virtual Rect filterBounds(const Rect& srcRect, float scale) = 0; }; } // namespace tgfx diff --git a/include/tgfx/layers/filters/LayerImageFilter.h b/include/tgfx/layers/filters/LayerImageFilter.h new file mode 100644 index 00000000..6a800cc6 --- /dev/null +++ b/include/tgfx/layers/filters/LayerImageFilter.h @@ -0,0 +1,63 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/layers/filters/LayerFilter.h" + +namespace tgfx { + +class LayerImageFilter : public LayerFilter { + public: + /** + * Applies the filter to the given picture. + * @param scale The scale factor to apply to the filter. + */ + bool drawWithFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; + + /** + * Return the bounds of after applying the filter to the given bounds. + * @param scale The scale factor to apply to the filter. + * @return The bounds of the filtered image. + */ + Rect filterBounds(const Rect& srcRect, float scale) override; + + protected: + /** + * Creates a new image filter for the given scale factor. When it is necessary to recreate the + * ImageFilter, the onCreateImageFilter method will be called. + * @param scale The scale factor to apply to the filter. + * @return A new image filter. + */ + virtual std::shared_ptr onCreateImageFilter(float scale) = 0; + + /** + * Marks the filter as dirty and invalidates the cached filter. + */ + void invalidateFilter(); + + private: + std::shared_ptr getImageFilter(float scale); + + bool dirty = true; + + float lastScale = 1.0f; + + std::shared_ptr lastFilter; +}; +} // namespace tgfx diff --git a/src/layers/Layer.cpp b/src/layers/Layer.cpp index 525ce14f..6a43bb89 100644 --- a/src/layers/Layer.cpp +++ b/src/layers/Layer.cpp @@ -346,9 +346,8 @@ Rect Layer::getBounds(const Layer* targetCoordinateSpace) { bounds.join(childBounds); } - auto imageFilter = getLayerFilter(1.0f); - if (imageFilter) { - bounds = imageFilter->filterBounds(bounds); + for (auto filter : _filters) { + bounds = filter->filterBounds(bounds, 1.0f); } if (targetCoordinateSpace && targetCoordinateSpace != this) { @@ -535,19 +534,23 @@ Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) { return paint; } -std::shared_ptr Layer::getLayerFilter(float contentScale) { - if (_filters.empty() || FloatNearlyZero(contentScale)) { +std::shared_ptr Layer::applyFilter(std::shared_ptr source, float contentScale) { + if (source == nullptr) { return nullptr; } - std::vector> imageFilters; + if (_filters.empty() || FloatNearlyZero(contentScale)) { + return source; + } + auto picture = source; for (const auto& filter : _filters) { - auto imageFilter = filter->getImageFilter(contentScale); - if (!imageFilter) { + Recorder recorder; + auto canvas = recorder.beginRecording(); + if (!filter->drawWithFilter(canvas, picture, contentScale)) { continue; } - imageFilters.push_back(imageFilter); + picture = recorder.finishRecordingAsPicture(); } - return ImageFilter::Compose(imageFilters); + return picture; } LayerContent* Layer::getRasterizedCache(const DrawArgs& args) { @@ -580,6 +583,7 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con Matrix* drawingMatrix) { DEBUG_ASSERT(drawingMatrix != nullptr); auto picture = getLayerContents(args, contentScale); + picture = applyFilter(std::move(picture), contentScale); if (!picture) { return nullptr; } @@ -593,14 +597,6 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con } drawingMatrix->setScale(1.0f / contentScale, 1.0f / contentScale); drawingMatrix->preTranslate(bounds.left, bounds.top); - if (auto filter = getLayerFilter(contentScale)) { - auto offset = Point::Zero(); - image = image->makeWithFilter(std::move(filter), &offset); - if (image == nullptr) { - return nullptr; - } - drawingMatrix->preTranslate(offset.x, offset.y); - } return image; } @@ -674,6 +670,7 @@ std::shared_ptr Layer::getMaskFilter(const DrawArgs& args, float sca void Layer::drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, BlendMode blendMode) { auto contentScale = canvas->getMatrix().getMaxScale(); auto picture = getLayerContents(args, contentScale); + picture = applyFilter(std::move(picture), contentScale); if (picture == nullptr) { return; } @@ -681,8 +678,6 @@ void Layer::drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, Ble paint.setAlpha(alpha); paint.setBlendMode(blendMode); paint.setMaskFilter(getMaskFilter(args, contentScale)); - auto filter = getLayerFilter(contentScale); - paint.setImageFilter(filter); auto matrix = Matrix::MakeScale(1.0f / contentScale); canvas->drawPicture(std::move(picture), &matrix, &paint); } diff --git a/src/layers/filters/BlendFilter.cpp b/src/layers/filters/BlendFilter.cpp index e3bc2f98..e20ca273 100644 --- a/src/layers/filters/BlendFilter.cpp +++ b/src/layers/filters/BlendFilter.cpp @@ -45,7 +45,7 @@ std::shared_ptr BlendFilter::onCreateImageFilter(float) { } BlendFilter::BlendFilter(const Color& color, BlendMode blendMode) - : LayerFilter(), _color(std::move(color)), _blendMode(blendMode) { + : _color(std::move(color)), _blendMode(blendMode) { } } // namespace tgfx diff --git a/src/layers/filters/BlurFilter.cpp b/src/layers/filters/BlurFilter.cpp index ee4faebd..a20c25b1 100644 --- a/src/layers/filters/BlurFilter.cpp +++ b/src/layers/filters/BlurFilter.cpp @@ -50,7 +50,7 @@ void BlurFilter::setTileMode(TileMode tileMode) { } BlurFilter::BlurFilter(float blurrinessX, float blurrinessY, TileMode tileMode) - : LayerFilter(), _blurrinessX(blurrinessX), _blurrinessY(blurrinessY), _tileMode(tileMode) { + : _blurrinessX(blurrinessX), _blurrinessY(blurrinessY), _tileMode(tileMode) { } std::shared_ptr BlurFilter::onCreateImageFilter(float scale) { diff --git a/src/layers/filters/ColorMatrixFilter.cpp b/src/layers/filters/ColorMatrixFilter.cpp index 68fab02b..9ff763da 100644 --- a/src/layers/filters/ColorMatrixFilter.cpp +++ b/src/layers/filters/ColorMatrixFilter.cpp @@ -37,7 +37,7 @@ std::shared_ptr ColorMatrixFilter::onCreateImageFilter(float) { } ColorMatrixFilter::ColorMatrixFilter(const std::array& matrix) - : LayerFilter(), _matrix(std::move(matrix)) { + : _matrix(std::move(matrix)) { } } // namespace tgfx \ No newline at end of file diff --git a/src/layers/filters/DropShadowFilter.cpp b/src/layers/filters/DropShadowFilter.cpp index b625e2dc..74a13eee 100644 --- a/src/layers/filters/DropShadowFilter.cpp +++ b/src/layers/filters/DropShadowFilter.cpp @@ -86,8 +86,8 @@ std::shared_ptr DropShadowFilter::onCreateImageFilter(float scale) DropShadowFilter::DropShadowFilter(float offsetX, float offsetY, float blurrinessX, float blurrinessY, const Color& color, bool dropsShadowOnly) - : LayerFilter(), _offsetX(offsetX), _offsetY(offsetY), _blurrinessX(blurrinessX), - _blurrinessY(blurrinessY), _color(std::move(color)), _dropsShadowOnly(dropsShadowOnly) { + : _offsetX(offsetX), _offsetY(offsetY), _blurrinessX(blurrinessX), _blurrinessY(blurrinessY), + _color(std::move(color)), _dropsShadowOnly(dropsShadowOnly) { } } // namespace tgfx diff --git a/src/layers/filters/InnerShadowFilter.cpp b/src/layers/filters/InnerShadowFilter.cpp index 3277b892..7d1bdda9 100644 --- a/src/layers/filters/InnerShadowFilter.cpp +++ b/src/layers/filters/InnerShadowFilter.cpp @@ -86,8 +86,8 @@ std::shared_ptr InnerShadowFilter::onCreateImageFilter(float scale) InnerShadowFilter::InnerShadowFilter(float offsetX, float offsetY, float blurrinessX, float blurrinessY, const Color& color, bool innerShadowOnly) - : LayerFilter(), _offsetX(offsetX), _offsetY(offsetY), _blurrinessX(blurrinessX), - _blurrinessY(blurrinessY), _color(std::move(color)), _innerShadowOnly(innerShadowOnly) { + : _offsetX(offsetX), _offsetY(offsetY), _blurrinessX(blurrinessX), _blurrinessY(blurrinessY), + _color(std::move(color)), _innerShadowOnly(innerShadowOnly) { } } // namespace tgfx diff --git a/src/layers/filters/LayerFilter.cpp b/src/layers/filters/LayerImageFilter.cpp similarity index 57% rename from src/layers/filters/LayerFilter.cpp rename to src/layers/filters/LayerImageFilter.cpp index 4c09002f..9890c861 100644 --- a/src/layers/filters/LayerFilter.cpp +++ b/src/layers/filters/LayerImageFilter.cpp @@ -16,22 +16,42 @@ // ///////////////////////////////////////////////////////////////////////////////////////////////// -#include "tgfx/layers/filters/LayerFilter.h" +#include "tgfx/layers/filters/LayerImageFilter.h" #include "tgfx/layers/Layer.h" namespace tgfx { -std::shared_ptr LayerFilter::getImageFilter(float filterScale) { - if (dirty || lastScale != filterScale) { - lastFilter = onCreateImageFilter(filterScale); - lastScale = filterScale; - dirty = false; +bool LayerImageFilter::drawWithFilter(Canvas* canvas, std::shared_ptr picture, + float scale) { + auto filter = getImageFilter(scale); + if (!filter) { + return false; } - return lastFilter; + Paint paint; + paint.setImageFilter(filter); + canvas->drawPicture(picture, nullptr, &paint); + return true; +} + +Rect LayerImageFilter::filterBounds(const Rect& srcRect, float scale) { + auto filter = getImageFilter(scale); + if (!filter) { + return srcRect; + } + return filter->filterBounds(srcRect); } -void LayerFilter::invalidateFilter() { +void LayerImageFilter::invalidateFilter() { dirty = true; invalidate(); } +std::shared_ptr LayerImageFilter::getImageFilter(float scale) { + if (dirty || lastScale != scale) { + lastFilter = onCreateImageFilter(scale); + lastScale = scale; + dirty = false; + } + return lastFilter; +} + } // namespace tgfx \ No newline at end of file diff --git a/test/src/LayerShadowFilter.cpp b/test/src/LayerShadowFilter.cpp new file mode 100644 index 00000000..57aec41c --- /dev/null +++ b/test/src/LayerShadowFilter.cpp @@ -0,0 +1,98 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#include "LayerShadowFilter.h" +#include +#include "tgfx/core/Canvas.h" +#include "tgfx/core/ImageFilter.h" +#include "tgfx/core/Paint.h" + +namespace tgfx { + +std::shared_ptr LayerShadowFilter::Make( + const std::vector& params) { + return std::shared_ptr(new LayerShadowFilter(params)); +} + +LayerShadowFilter::LayerShadowFilter(const std::vector& params) : params(params) { +} + +void LayerShadowFilter::setShadowParams(const std::vector& shadowParams) { + params = shadowParams; + invalidate(); +} + +bool LayerShadowFilter::drawWithFilter(Canvas* canvas, std::shared_ptr picture, + float scale) { + auto bounds = picture->getBounds(); + bounds.roundOut(); + Matrix matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); + auto image = Image::MakeFrom(picture, static_cast(bounds.width()), + static_cast(bounds.height()), &matrix); + if (!image) { + return false; + } + + // record shadow picture + Recorder shadowRecorder; + auto shadowCanvas = shadowRecorder.beginRecording(); + for (const auto& param : params) { + auto filter = createShadowFilter(param, scale); + if (!filter) { + continue; + } + Paint paint; + paint.setImageFilter(filter); + shadowCanvas->drawImage(image, bounds.x(), bounds.y(), &paint); + } + auto shadowPicture = shadowRecorder.finishRecordingAsPicture(); + + // Draw shadow beside the original picture area + auto boundsAfterFilter = filterBounds(bounds, scale); + auto shader = Shader::MakeImageShader(image, TileMode::Decal, TileMode::Decal); + shader = shader->makeWithMatrix(Matrix::MakeTrans(-boundsAfterFilter.left, -boundsAfterFilter.top)); + auto maskFilter = MaskFilter::MakeShader(shader, true); + Paint shadowPaint; + shadowPaint.setMaskFilter(maskFilter); + canvas->drawPicture(shadowPicture, nullptr, &shadowPaint); + + // draw original image + canvas->drawImage(image, bounds.x(), bounds.y()); + return true; +} + +Rect LayerShadowFilter::filterBounds(const Rect& srcRect, float scale) { + auto maxRect = srcRect; + for (const auto& param : params) { + auto filter = createShadowFilter(param, scale); + if (!filter) { + continue; + } + maxRect.join(filter->filterBounds(srcRect)); + } + return maxRect; +} + +std::shared_ptr LayerShadowFilter::createShadowFilter(const LayerShadowParam& param, + float scale) { + return ImageFilter::DropShadowOnly(param.offsetX * scale, param.offsetY * scale, + param.blurrinessX * scale, param.blurrinessY * scale, + param.color); +} + +} // namespace tgfx \ No newline at end of file diff --git a/test/src/LayerShadowFilter.h b/test/src/LayerShadowFilter.h new file mode 100644 index 00000000..c0ca3a33 --- /dev/null +++ b/test/src/LayerShadowFilter.h @@ -0,0 +1,59 @@ +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// Tencent is pleased to support the open source community by making tgfx available. +// +// Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved. +// +// Licensed under the BSD 3-Clause License (the "License"); you may not use this file except +// in compliance with the License. You may obtain a copy of the License at +// +// https://opensource.org/licenses/BSD-3-Clause +// +// unless required by applicable law or agreed to in writing, software distributed under the +// license is distributed on an "as is" basis, without warranties or conditions of any kind, +// either express or implied. see the license for the specific language governing permissions +// and limitations under the license. +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "tgfx/layers/filters/LayerFilter.h" + +namespace tgfx { + +struct LayerShadowParam { + float offsetX = 0.0f; + float offsetY = 0.0f; + float blurrinessX = 0.0f; + float blurrinessY = 0.0f; + Color color = Color::Black(); +}; + +class LayerShadowFilter : public LayerFilter { + public: + /** + * Create a filter that draws drop shadows under the input content. + */ + static std::shared_ptr Make(const std::vector& params); + + std::vector shadowParams() const { + return params; + } + + void setShadowParams(const std::vector& params); + + bool drawWithFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; + + Rect filterBounds(const Rect& srcRect, float scale) override; + + private: + static std::shared_ptr createShadowFilter(const LayerShadowParam& param, + float scale); + + explicit LayerShadowFilter(const std::vector& params); + + std::vector params = {}; +}; + +} // namespace tgfx diff --git a/test/src/LayerTest.cpp b/test/src/LayerTest.cpp index d65326c6..dd7f40d5 100644 --- a/test/src/LayerTest.cpp +++ b/test/src/LayerTest.cpp @@ -18,6 +18,7 @@ #include #include +#include "LayerShadowFilter.h" #include "core/filters/BlurImageFilter.h" #include "core/utils/Profiling.h" #include "tgfx/core/PathEffect.h" @@ -1741,4 +1742,36 @@ TGFX_TEST(LayerTest, DirtyFlag) { EXPECT_TRUE(!child->bitFields.childrenDirty && !child->bitFields.contentDirty); EXPECT_TRUE(root->bitFields.childrenDirty && !root->bitFields.contentDirty); } + +TGFX_TEST(LayerTest, LayerShadowFilter) { + ContextScope scope; + auto context = scope.getContext(); + EXPECT_TRUE(context != nullptr); + auto surface = Surface::Make(context, 150, 150); + auto displayList = std::make_unique(); + auto layer = ImageLayer::Make(); + layer->setMatrix(Matrix::MakeTrans(30, 30)); + auto image = MakeImage("resources/apitest/imageReplacement.png"); + EXPECT_TRUE(image != nullptr); + layer->setImage(image); + auto filter = LayerShadowFilter::Make({}); + layer->setFilters({filter}); + LayerShadowParam param0 = {}; + param0.offsetX = 10; + param0.offsetY = 10; + LayerShadowParam param1 = {}; + param1.offsetX = -10; + param1.offsetY = -10; + param1.color = Color::White(); + filter->setShadowParams({param0, param1}); + displayList->root()->addChild(layer); + displayList->render(surface.get()); + EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/layerShadowFilter")); + + param1.offsetX = 0; + param1.offsetY = -20; + filter->setShadowParams({param0, param1}); + displayList->render(surface.get()); + EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/layerShadowFilter1")); +} } // namespace tgfx From 21bb34f83980eb3f8327fcfc3b72c994d3001efb Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Thu, 19 Dec 2024 16:25:09 +0800 Subject: [PATCH 2/8] Modify the behavior of LayerFilter to allow LayerFilter to customize rendering logic. --- include/tgfx/layers/filters/LayerFilter.h | 22 +++++----- .../tgfx/layers/filters/LayerImageFilter.h | 5 ++- src/layers/Layer.cpp | 2 +- src/layers/filters/LayerImageFilter.cpp | 3 +- test/src/LayerShadowFilter.cpp | 42 ++++++++++++++----- test/src/LayerShadowFilter.h | 15 ++++++- test/src/LayerTest.cpp | 11 +++-- 7 files changed, 69 insertions(+), 31 deletions(-) diff --git a/include/tgfx/layers/filters/LayerFilter.h b/include/tgfx/layers/filters/LayerFilter.h index f58d474c..2648d314 100644 --- a/include/tgfx/layers/filters/LayerFilter.h +++ b/include/tgfx/layers/filters/LayerFilter.h @@ -28,18 +28,18 @@ namespace tgfx { */ class LayerFilter : public LayerProperty { public: - /* - * Applies the filter to the given picture and draws it to the canvas. - * @param scale The scale factor to apply to the filter. - * @return True if the filter was applied and drawn, false otherwise. - */ - virtual bool drawWithFilter(Canvas* canvas, std::shared_ptr picture, float scale) = 0; + /** + * Applies the filter to the given picture and draws it to the canvas. + * @param scale The scale factor to apply to the filter. + * @return True if the filter was applied and drawn, false otherwise. + */ + virtual bool applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) = 0; - /* - * Return the bounds of after applying the filter to the given bounds. - * @param scale The scale factor to apply to the filter. - * @return The bounds of the filtered image. - */ + /** + * Return the bounds of after applying the filter to the given bounds. + * @param scale The scale factor to apply to the filter. + * @return The bounds of the filtered image. + */ virtual Rect filterBounds(const Rect& srcRect, float scale) = 0; }; } // namespace tgfx diff --git a/include/tgfx/layers/filters/LayerImageFilter.h b/include/tgfx/layers/filters/LayerImageFilter.h index 6a800cc6..c517a32a 100644 --- a/include/tgfx/layers/filters/LayerImageFilter.h +++ b/include/tgfx/layers/filters/LayerImageFilter.h @@ -25,10 +25,11 @@ namespace tgfx { class LayerImageFilter : public LayerFilter { public: /** - * Applies the filter to the given picture. + * Applies the filter to the given picture and draws it to the canvas. * @param scale The scale factor to apply to the filter. + * @return True if the filter was applied and drawn, false otherwise. */ - bool drawWithFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; + bool applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; /** * Return the bounds of after applying the filter to the given bounds. diff --git a/src/layers/Layer.cpp b/src/layers/Layer.cpp index 6a43bb89..a8d88356 100644 --- a/src/layers/Layer.cpp +++ b/src/layers/Layer.cpp @@ -545,7 +545,7 @@ std::shared_ptr Layer::applyFilter(std::shared_ptr source, flo for (const auto& filter : _filters) { Recorder recorder; auto canvas = recorder.beginRecording(); - if (!filter->drawWithFilter(canvas, picture, contentScale)) { + if (!filter->applyFilter(canvas, picture, contentScale)) { continue; } picture = recorder.finishRecordingAsPicture(); diff --git a/src/layers/filters/LayerImageFilter.cpp b/src/layers/filters/LayerImageFilter.cpp index 9890c861..790d8321 100644 --- a/src/layers/filters/LayerImageFilter.cpp +++ b/src/layers/filters/LayerImageFilter.cpp @@ -20,8 +20,7 @@ #include "tgfx/layers/Layer.h" namespace tgfx { -bool LayerImageFilter::drawWithFilter(Canvas* canvas, std::shared_ptr picture, - float scale) { +bool LayerImageFilter::applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) { auto filter = getImageFilter(scale); if (!filter) { return false; diff --git a/test/src/LayerShadowFilter.cpp b/test/src/LayerShadowFilter.cpp index 57aec41c..01b67ca3 100644 --- a/test/src/LayerShadowFilter.cpp +++ b/test/src/LayerShadowFilter.cpp @@ -33,12 +33,22 @@ LayerShadowFilter::LayerShadowFilter(const std::vector& params } void LayerShadowFilter::setShadowParams(const std::vector& shadowParams) { + if (shadowParams == params) { + return; + } params = shadowParams; invalidate(); } -bool LayerShadowFilter::drawWithFilter(Canvas* canvas, std::shared_ptr picture, - float scale) { +void LayerShadowFilter::setShowBehindTransparent(bool showBehindTransparent) { + if (showBehindTransparent == _showBehindTransparent) { + return; + } + _showBehindTransparent = showBehindTransparent; + invalidate(); +} + +bool LayerShadowFilter::applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) { auto bounds = picture->getBounds(); bounds.roundOut(); Matrix matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); @@ -48,7 +58,16 @@ bool LayerShadowFilter::drawWithFilter(Canvas* canvas, std::shared_ptr return false; } - // record shadow picture + // create opaque image + Recorder opaqueRecorder; + auto opaqueCanvas = opaqueRecorder.beginRecording(); + auto opaquePaint = Paint(); + opaquePaint.setColorFilter(ColorFilter::AlphaThreshold(0)); + opaqueCanvas->drawImage(image, &opaquePaint); + auto opaquePicture = opaqueRecorder.finishRecordingAsPicture(); + auto opaqueImage = Image::MakeFrom(opaquePicture, image->width(), image->height()); + + // create shadow picture Recorder shadowRecorder; auto shadowCanvas = shadowRecorder.beginRecording(); for (const auto& param : params) { @@ -58,17 +77,20 @@ bool LayerShadowFilter::drawWithFilter(Canvas* canvas, std::shared_ptr } Paint paint; paint.setImageFilter(filter); - shadowCanvas->drawImage(image, bounds.x(), bounds.y(), &paint); + shadowCanvas->drawImage(opaqueImage, bounds.x(), bounds.y(), &paint); } auto shadowPicture = shadowRecorder.finishRecordingAsPicture(); - // Draw shadow beside the original picture area - auto boundsAfterFilter = filterBounds(bounds, scale); - auto shader = Shader::MakeImageShader(image, TileMode::Decal, TileMode::Decal); - shader = shader->makeWithMatrix(Matrix::MakeTrans(-boundsAfterFilter.left, -boundsAfterFilter.top)); - auto maskFilter = MaskFilter::MakeShader(shader, true); Paint shadowPaint; - shadowPaint.setMaskFilter(maskFilter); + // Draw shadow beside the original picture area + if (!_showBehindTransparent) { + auto boundsAfterFilter = filterBounds(bounds, scale); + auto shader = Shader::MakeImageShader(opaqueImage, TileMode::Decal, TileMode::Decal); + shader = + shader->makeWithMatrix(Matrix::MakeTrans(-boundsAfterFilter.left, -boundsAfterFilter.top)); + auto maskFilter = MaskFilter::MakeShader(shader, true); + shadowPaint.setMaskFilter(maskFilter); + } canvas->drawPicture(shadowPicture, nullptr, &shadowPaint); // draw original image diff --git a/test/src/LayerShadowFilter.h b/test/src/LayerShadowFilter.h index c0ca3a33..b0ffca61 100644 --- a/test/src/LayerShadowFilter.h +++ b/test/src/LayerShadowFilter.h @@ -28,6 +28,11 @@ struct LayerShadowParam { float blurrinessX = 0.0f; float blurrinessY = 0.0f; Color color = Color::Black(); + bool operator==(const LayerShadowParam& value) const { + return offsetX == value.offsetX && offsetY == value.offsetY && + blurrinessX == value.blurrinessX && blurrinessY == value.blurrinessY && + color == value.color; + } }; class LayerShadowFilter : public LayerFilter { @@ -43,7 +48,13 @@ class LayerShadowFilter : public LayerFilter { void setShadowParams(const std::vector& params); - bool drawWithFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; + bool showBehindTransparent() const { + return _showBehindTransparent; + } + + void setShowBehindTransparent(bool showBehindTransparent); + + bool applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; Rect filterBounds(const Rect& srcRect, float scale) override; @@ -54,6 +65,8 @@ class LayerShadowFilter : public LayerFilter { explicit LayerShadowFilter(const std::vector& params); std::vector params = {}; + + bool _showBehindTransparent = false; }; } // namespace tgfx diff --git a/test/src/LayerTest.cpp b/test/src/LayerTest.cpp index dd7f40d5..f90f7869 100644 --- a/test/src/LayerTest.cpp +++ b/test/src/LayerTest.cpp @@ -1749,11 +1749,13 @@ TGFX_TEST(LayerTest, LayerShadowFilter) { EXPECT_TRUE(context != nullptr); auto surface = Surface::Make(context, 150, 150); auto displayList = std::make_unique(); - auto layer = ImageLayer::Make(); + auto layer = ShapeLayer::Make(); layer->setMatrix(Matrix::MakeTrans(30, 30)); - auto image = MakeImage("resources/apitest/imageReplacement.png"); - EXPECT_TRUE(image != nullptr); - layer->setImage(image); + Path path; + path.addRect(Rect::MakeWH(100, 100)); + layer->setPath(path); + auto fillStyle = SolidColor::Make(Color::FromRGBA(100, 0, 0, 128)); + layer->setFillStyle(fillStyle); auto filter = LayerShadowFilter::Make({}); layer->setFilters({filter}); LayerShadowParam param0 = {}; @@ -1771,6 +1773,7 @@ TGFX_TEST(LayerTest, LayerShadowFilter) { param1.offsetX = 0; param1.offsetY = -20; filter->setShadowParams({param0, param1}); + filter->setShowBehindTransparent(true); displayList->render(surface.get()); EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/layerShadowFilter1")); } From 92bba09ddeec4872cbe8c52f1aa3913014ec1b26 Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Thu, 19 Dec 2024 16:30:33 +0800 Subject: [PATCH 3/8] update version.json --- test/baseline/version.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/baseline/version.json b/test/baseline/version.json index 08add24d..f137c66a 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -96,16 +96,18 @@ "draw_text": "b062b9a", "dropShadow": "43cd416", "filterClip": "43cd416", - "filterTest": "43cd416", + "filterTest": "21bb34f", "getBounds": "3888c19", "getLayersUnderPoint": "b062b9a", "greyColorMatrix": "a1605b2", "identityMatrix": "a1605b2", "imageLayer": "99a5cd9", - "imageMask": "af2e3ff", + "imageMask": "21bb34f", "innerShadow": "01cce38", + "layerShadowFilter": "21bb34f", + "layerShadowFilter1": "21bb34f", "shapeMask": "612c09e", - "textMask": "b062b9a" + "textMask": "21bb34f" }, "MaskTest": { "rasterize_emoji": "c0f39f8", From d2e698a9ecf5b194c85dca71eca8df34ad9c705a Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Thu, 19 Dec 2024 16:38:21 +0800 Subject: [PATCH 4/8] fix code-format issues --- test/src/LayerShadowFilter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/LayerShadowFilter.cpp b/test/src/LayerShadowFilter.cpp index 01b67ca3..efcf3a49 100644 --- a/test/src/LayerShadowFilter.cpp +++ b/test/src/LayerShadowFilter.cpp @@ -17,10 +17,10 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "LayerShadowFilter.h" -#include #include "tgfx/core/Canvas.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Paint.h" +#include "tgfx/core/Recorder.h" namespace tgfx { From e346162d8935e5572442c2003f565b11582ee235 Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Thu, 19 Dec 2024 20:55:51 +0800 Subject: [PATCH 5/8] resolve comment's issues --- include/tgfx/layers/Layer.h | 2 +- include/tgfx/layers/filters/BlendFilter.h | 6 +- include/tgfx/layers/filters/BlurFilter.h | 5 +- .../tgfx/layers/filters/ColorMatrixFilter.h | 5 +- .../tgfx/layers/filters/DropShadowFilter.h | 5 +- .../tgfx/layers/filters/InnerShadowFilter.h | 6 +- include/tgfx/layers/filters/LayerFilter.h | 22 +++-- .../tgfx/layers/filters/LayerImageFilter.h | 42 +++++---- src/layers/Layer.cpp | 34 +++++-- src/layers/filters/LayerImageFilter.cpp | 11 ++- test/baseline/version.json | 1 + test/src/LayerShadowFilter.cpp | 93 ++++++++++--------- test/src/LayerShadowFilter.h | 25 ++++- test/src/LayerTest.cpp | 22 +++++ 14 files changed, 178 insertions(+), 101 deletions(-) diff --git a/include/tgfx/layers/Layer.h b/include/tgfx/layers/Layer.h index 5b11dcbb..9b1c713f 100644 --- a/include/tgfx/layers/Layer.h +++ b/include/tgfx/layers/Layer.h @@ -502,7 +502,7 @@ class Layer { Paint getLayerPaint(float alpha, BlendMode blendMode); - std::shared_ptr applyFilter(std::shared_ptr source, float contentScale); + std::shared_ptr applyFilters(std::shared_ptr source, float contentScale); LayerContent* getRasterizedCache(const DrawArgs& args); diff --git a/include/tgfx/layers/filters/BlendFilter.h b/include/tgfx/layers/filters/BlendFilter.h index f5303222..523e4c6b 100644 --- a/include/tgfx/layers/filters/BlendFilter.h +++ b/include/tgfx/layers/filters/BlendFilter.h @@ -22,10 +22,12 @@ namespace tgfx { +/** + * A filter that applies blends between the constant color (src) and input color (dst) based on the + * BlendMode. + */ class BlendFilter : public LayerImageFilter { public: - ~BlendFilter() override = default; - /** * Creates a new ColorFilter that applies blends between the constant color (src) and input color * (dst) based on the BlendMode. diff --git a/include/tgfx/layers/filters/BlurFilter.h b/include/tgfx/layers/filters/BlurFilter.h index 725124f2..86ea8c31 100644 --- a/include/tgfx/layers/filters/BlurFilter.h +++ b/include/tgfx/layers/filters/BlurFilter.h @@ -22,10 +22,11 @@ namespace tgfx { +/** + * A filter that blurs its input by the separate X and Y blurriness. + */ class BlurFilter : public LayerImageFilter { public: - ~BlurFilter() override = default; - /** * Create a filter that blurs its input by the separate X and Y blurriness. The provided tile mode * is used when the blur kernel goes outside the input image. diff --git a/include/tgfx/layers/filters/ColorMatrixFilter.h b/include/tgfx/layers/filters/ColorMatrixFilter.h index 38839ed6..72651127 100644 --- a/include/tgfx/layers/filters/ColorMatrixFilter.h +++ b/include/tgfx/layers/filters/ColorMatrixFilter.h @@ -22,10 +22,11 @@ namespace tgfx { +/** + * A filter that transforms the color using the given 4x5 matrix. + */ class ColorMatrixFilter : public LayerImageFilter { public: - ~ColorMatrixFilter() override = default; - /** * Creates a new ColorMatrixFilter that transforms the color using the given 4x5 matrix. The matrix can * be passed as a single array, and is treated as follows: diff --git a/include/tgfx/layers/filters/DropShadowFilter.h b/include/tgfx/layers/filters/DropShadowFilter.h index 6b414110..62b93ba7 100644 --- a/include/tgfx/layers/filters/DropShadowFilter.h +++ b/include/tgfx/layers/filters/DropShadowFilter.h @@ -21,10 +21,11 @@ #include "tgfx/layers/filters/LayerImageFilter.h" namespace tgfx { +/** + * A filter draws a drop shadow under the input content. + */ class DropShadowFilter : public LayerImageFilter { public: - ~DropShadowFilter() override = default; - /** * Create a filter that draws a drop shadow under the input content. */ diff --git a/include/tgfx/layers/filters/InnerShadowFilter.h b/include/tgfx/layers/filters/InnerShadowFilter.h index 0d412cce..e5b49132 100644 --- a/include/tgfx/layers/filters/InnerShadowFilter.h +++ b/include/tgfx/layers/filters/InnerShadowFilter.h @@ -21,10 +21,12 @@ #include "tgfx/layers/filters/LayerImageFilter.h" namespace tgfx { + +/** + * A filter draws an inner shadow over the input content. + */ class InnerShadowFilter : public LayerImageFilter { public: - ~InnerShadowFilter() override = default; - /** * Create a filter that draws an inner shadow over the input content. */ diff --git a/include/tgfx/layers/filters/LayerFilter.h b/include/tgfx/layers/filters/LayerFilter.h index 2648d314..d9667374 100644 --- a/include/tgfx/layers/filters/LayerFilter.h +++ b/include/tgfx/layers/filters/LayerFilter.h @@ -29,17 +29,19 @@ namespace tgfx { class LayerFilter : public LayerProperty { public: /** - * Applies the filter to the given picture and draws it to the canvas. - * @param scale The scale factor to apply to the filter. - * @return True if the filter was applied and drawn, false otherwise. - */ - virtual bool applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) = 0; + * Applies the filter to the given picture and draws it to the canvas. + * @param contentScale The scale factor of the source Image relative to its original size. + * Some filters have size-related parameters that must be adjusted with this scale factor. + * @return True if the filter was applied and drawn, false otherwise. + */ + virtual bool applyFilter(Canvas* canvas, std::shared_ptr image, float contentScale) = 0; /** - * Return the bounds of after applying the filter to the given bounds. - * @param scale The scale factor to apply to the filter. - * @return The bounds of the filtered image. - */ - virtual Rect filterBounds(const Rect& srcRect, float scale) = 0; + * Return the bounds of after applying the filter to the given bounds. + * @param contentScale The scale factor of the source bounds relative to its original size. + * Some filters have size-related parameters that must be adjusted with this scale factor + * @return The bounds of the filtered image. + */ + virtual Rect filterBounds(const Rect& srcRect, float contentScale) = 0; }; } // namespace tgfx diff --git a/include/tgfx/layers/filters/LayerImageFilter.h b/include/tgfx/layers/filters/LayerImageFilter.h index c517a32a..49abedba 100644 --- a/include/tgfx/layers/filters/LayerImageFilter.h +++ b/include/tgfx/layers/filters/LayerImageFilter.h @@ -22,34 +22,40 @@ namespace tgfx { +/** + * LayerImageFilter is a filter that applies an image filter to a layer. + */ class LayerImageFilter : public LayerFilter { public: /** - * Applies the filter to the given picture and draws it to the canvas. - * @param scale The scale factor to apply to the filter. - * @return True if the filter was applied and drawn, false otherwise. - */ - bool applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; + * Applies the filter to the given picture and draws it to the canvas. + * @param contentScale The scale factor of the source Image relative to its original size. + * Some filters have size-related parameters that must be adjusted with this scale factor. + * @return True if the filter was applied and drawn, false otherwise. + */ + bool applyFilter(Canvas* canvas, std::shared_ptr image, float contentScale) override; /** - * Return the bounds of after applying the filter to the given bounds. - * @param scale The scale factor to apply to the filter. - * @return The bounds of the filtered image. - */ - Rect filterBounds(const Rect& srcRect, float scale) override; + * Return the bounds of after applying the filter to the given bounds. + * @param contentScale The scale factor of the source Image relative to its original size. + * Some filters have size-related parameters that must be adjusted with this scale factor. + * @return The bounds of the filtered image. + */ + Rect filterBounds(const Rect& srcRect, float contentScale) override; protected: /** - * Creates a new image filter for the given scale factor. When it is necessary to recreate the - * ImageFilter, the onCreateImageFilter method will be called. - * @param scale The scale factor to apply to the filter. - * @return A new image filter. - */ - virtual std::shared_ptr onCreateImageFilter(float scale) = 0; + * Creates a new image filter for the given scale factor. When it is necessary to recreate the + * ImageFilter, the onCreateImageFilter method will be called. + * @param contentScale The scale factor of the source Image relative to its original size. + * Some filters have size-related parameters that must be adjusted with this scale factor. + * @return A new image filter. + */ + virtual std::shared_ptr onCreateImageFilter(float contentScale) = 0; /** - * Marks the filter as dirty and invalidates the cached filter. - */ + * Marks the filter as dirty and invalidates the cached filter. + */ void invalidateFilter(); private: diff --git a/src/layers/Layer.cpp b/src/layers/Layer.cpp index a8d88356..7ee2b0a9 100644 --- a/src/layers/Layer.cpp +++ b/src/layers/Layer.cpp @@ -534,23 +534,39 @@ Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) { return paint; } -std::shared_ptr Layer::applyFilter(std::shared_ptr source, float contentScale) { +std::shared_ptr Layer::applyFilters(std::shared_ptr source, float contentScale) { if (source == nullptr) { return nullptr; } if (_filters.empty() || FloatNearlyZero(contentScale)) { return source; } - auto picture = source; + auto sourceBounds = source->getBounds(); + sourceBounds.roundOut(); + auto pictureMatrix = Matrix::MakeTrans(-sourceBounds.x(), -sourceBounds.y()); + auto image = Image::MakeFrom(source, static_cast(sourceBounds.width()), + static_cast(sourceBounds.height()), &pictureMatrix); + Recorder recorder; + Point offset = Point::Make(sourceBounds.x(), sourceBounds.y()); for (const auto& filter : _filters) { - Recorder recorder; auto canvas = recorder.beginRecording(); - if (!filter->applyFilter(canvas, picture, contentScale)) { + if (!filter->applyFilter(canvas, image, contentScale)) { continue; } - picture = recorder.finishRecordingAsPicture(); - } - return picture; + auto picture = recorder.finishRecordingAsPicture(); + if (picture == nullptr) { + return nullptr; + } + auto bounds = picture->getBounds(); + bounds.roundOut(); + auto matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); + image = Image::MakeFrom(std::move(picture), static_cast(bounds.width()), + static_cast(bounds.height()), &matrix); + offset.offset(bounds.x(), bounds.y()); + } + auto canvas = recorder.beginRecording(); + canvas->drawImage(image, offset.x, offset.y); + return recorder.finishRecordingAsPicture(); } LayerContent* Layer::getRasterizedCache(const DrawArgs& args) { @@ -583,7 +599,7 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con Matrix* drawingMatrix) { DEBUG_ASSERT(drawingMatrix != nullptr); auto picture = getLayerContents(args, contentScale); - picture = applyFilter(std::move(picture), contentScale); + picture = applyFilters(std::move(picture), contentScale); if (!picture) { return nullptr; } @@ -670,7 +686,7 @@ std::shared_ptr Layer::getMaskFilter(const DrawArgs& args, float sca void Layer::drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, BlendMode blendMode) { auto contentScale = canvas->getMatrix().getMaxScale(); auto picture = getLayerContents(args, contentScale); - picture = applyFilter(std::move(picture), contentScale); + picture = applyFilters(std::move(picture), contentScale); if (picture == nullptr) { return; } diff --git a/src/layers/filters/LayerImageFilter.cpp b/src/layers/filters/LayerImageFilter.cpp index 790d8321..f8b5aa5f 100644 --- a/src/layers/filters/LayerImageFilter.cpp +++ b/src/layers/filters/LayerImageFilter.cpp @@ -20,19 +20,20 @@ #include "tgfx/layers/Layer.h" namespace tgfx { -bool LayerImageFilter::applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) { - auto filter = getImageFilter(scale); +bool LayerImageFilter::applyFilter(Canvas* canvas, std::shared_ptr image, + float contentScale) { + auto filter = getImageFilter(contentScale); if (!filter) { return false; } Paint paint; paint.setImageFilter(filter); - canvas->drawPicture(picture, nullptr, &paint); + canvas->drawImage(image, &paint); return true; } -Rect LayerImageFilter::filterBounds(const Rect& srcRect, float scale) { - auto filter = getImageFilter(scale); +Rect LayerImageFilter::filterBounds(const Rect& srcRect, float contentScale) { + auto filter = getImageFilter(contentScale); if (!filter) { return srcRect; } diff --git a/test/baseline/version.json b/test/baseline/version.json index f137c66a..7bdb6fd6 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -97,6 +97,7 @@ "dropShadow": "43cd416", "filterClip": "43cd416", "filterTest": "21bb34f", + "filters": "d2e698a", "getBounds": "3888c19", "getLayersUnderPoint": "b062b9a", "greyColorMatrix": "a1605b2", diff --git a/test/src/LayerShadowFilter.cpp b/test/src/LayerShadowFilter.cpp index efcf3a49..5314fd52 100644 --- a/test/src/LayerShadowFilter.cpp +++ b/test/src/LayerShadowFilter.cpp @@ -17,6 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "LayerShadowFilter.h" +#include #include "tgfx/core/Canvas.h" #include "tgfx/core/ImageFilter.h" #include "tgfx/core/Paint.h" @@ -48,60 +49,22 @@ void LayerShadowFilter::setShowBehindTransparent(bool showBehindTransparent) { invalidate(); } -bool LayerShadowFilter::applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) { - auto bounds = picture->getBounds(); - bounds.roundOut(); - Matrix matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); - auto image = Image::MakeFrom(picture, static_cast(bounds.width()), - static_cast(bounds.height()), &matrix); +bool LayerShadowFilter::applyFilter(Canvas* canvas, std::shared_ptr image, + float contentScale) { if (!image) { return false; } - // create opaque image - Recorder opaqueRecorder; - auto opaqueCanvas = opaqueRecorder.beginRecording(); - auto opaquePaint = Paint(); - opaquePaint.setColorFilter(ColorFilter::AlphaThreshold(0)); - opaqueCanvas->drawImage(image, &opaquePaint); - auto opaquePicture = opaqueRecorder.finishRecordingAsPicture(); - auto opaqueImage = Image::MakeFrom(opaquePicture, image->width(), image->height()); - - // create shadow picture - Recorder shadowRecorder; - auto shadowCanvas = shadowRecorder.beginRecording(); - for (const auto& param : params) { - auto filter = createShadowFilter(param, scale); - if (!filter) { - continue; - } - Paint paint; - paint.setImageFilter(filter); - shadowCanvas->drawImage(opaqueImage, bounds.x(), bounds.y(), &paint); - } - auto shadowPicture = shadowRecorder.finishRecordingAsPicture(); + drawShadows(canvas, image, contentScale); - Paint shadowPaint; - // Draw shadow beside the original picture area - if (!_showBehindTransparent) { - auto boundsAfterFilter = filterBounds(bounds, scale); - auto shader = Shader::MakeImageShader(opaqueImage, TileMode::Decal, TileMode::Decal); - shader = - shader->makeWithMatrix(Matrix::MakeTrans(-boundsAfterFilter.left, -boundsAfterFilter.top)); - auto maskFilter = MaskFilter::MakeShader(shader, true); - shadowPaint.setMaskFilter(maskFilter); - } - canvas->drawPicture(shadowPicture, nullptr, &shadowPaint); - - // draw original image - canvas->drawImage(image, bounds.x(), bounds.y()); + canvas->drawImage(image); return true; } Rect LayerShadowFilter::filterBounds(const Rect& srcRect, float scale) { auto maxRect = srcRect; for (const auto& param : params) { - auto filter = createShadowFilter(param, scale); + auto filter = CreateShadowFilter(param, scale); if (!filter) { continue; } @@ -110,11 +73,53 @@ Rect LayerShadowFilter::filterBounds(const Rect& srcRect, float scale) { return maxRect; } -std::shared_ptr LayerShadowFilter::createShadowFilter(const LayerShadowParam& param, +std::shared_ptr LayerShadowFilter::CreateShadowFilter(const LayerShadowParam& param, float scale) { return ImageFilter::DropShadowOnly(param.offsetX * scale, param.offsetY * scale, param.blurrinessX * scale, param.blurrinessY * scale, param.color); } +void LayerShadowFilter::drawShadows(Canvas* canvas, std::shared_ptr image, + float contentScale) { + // create opaque image + auto opaqueFilter = ImageFilter::ColorFilter(ColorFilter::AlphaThreshold(0)); + auto opaqueImage = image->makeWithFilter(opaqueFilter); + + // collect drop shadows into a picture, and then we can apply a mask filter to the picture + Recorder recorder; + auto shadowCanvas = recorder.beginRecording(); + collectDropShadows(shadowCanvas, opaqueImage, contentScale); + auto shadowsPicture = recorder.finishRecordingAsPicture(); + + Paint shadowPaint; + if (!_showBehindTransparent) { + shadowPaint.setMaskFilter(createMask(opaqueImage, contentScale)); + } + canvas->drawPicture(shadowsPicture, nullptr, &shadowPaint); +} + +void LayerShadowFilter::collectDropShadows(Canvas* canvas, std::shared_ptr opaqueSource, + float contentScale) { + for (const auto& param : params) { + if (auto filter = CreateShadowFilter(param, contentScale)) { + Paint paint; + paint.setImageFilter(filter); + canvas->drawImage(opaqueSource, &paint); + } + } +} + +std::shared_ptr LayerShadowFilter::createMask(std::shared_ptr opaqueImage, + float contentScale) { + auto shader = Shader::MakeImageShader(opaqueImage, TileMode::Decal, TileMode::Decal); + // get bounds after applying the filter, so that the mask filter can be applied to the + // correct area + auto boundsAfterFilter = + filterBounds(Rect::MakeWH(opaqueImage->width(), opaqueImage->height()), contentScale); + shader = + shader->makeWithMatrix(Matrix::MakeTrans(-boundsAfterFilter.left, -boundsAfterFilter.top)); + return MaskFilter::MakeShader(shader, true); +} + } // namespace tgfx \ No newline at end of file diff --git a/test/src/LayerShadowFilter.h b/test/src/LayerShadowFilter.h index b0ffca61..fda9055f 100644 --- a/test/src/LayerShadowFilter.h +++ b/test/src/LayerShadowFilter.h @@ -18,16 +18,21 @@ #pragma once +#include #include "tgfx/layers/filters/LayerFilter.h" namespace tgfx { +/** + * Parameters for the drop shadow. + */ struct LayerShadowParam { float offsetX = 0.0f; float offsetY = 0.0f; float blurrinessX = 0.0f; float blurrinessY = 0.0f; Color color = Color::Black(); + bool operator==(const LayerShadowParam& value) const { return offsetX == value.offsetX && offsetY == value.offsetY && blurrinessX == value.blurrinessX && blurrinessY == value.blurrinessY && @@ -38,32 +43,44 @@ struct LayerShadowParam { class LayerShadowFilter : public LayerFilter { public: /** - * Create a filter that draws drop shadows under the input content. - */ + * Create a filter that draws drop shadows under the input content. + */ static std::shared_ptr Make(const std::vector& params); + /** + * The parameters for the drop shadows. + */ std::vector shadowParams() const { return params; } void setShadowParams(const std::vector& params); + /** + * Whether to show shadows behind transparent regions of the input content. + */ bool showBehindTransparent() const { return _showBehindTransparent; } void setShowBehindTransparent(bool showBehindTransparent); - bool applyFilter(Canvas* canvas, std::shared_ptr picture, float scale) override; + bool applyFilter(Canvas* canvas, std::shared_ptr image, float scale) override; Rect filterBounds(const Rect& srcRect, float scale) override; private: - static std::shared_ptr createShadowFilter(const LayerShadowParam& param, + static std::shared_ptr CreateShadowFilter(const LayerShadowParam& param, float scale); explicit LayerShadowFilter(const std::vector& params); + void drawShadows(Canvas* canvas, std::shared_ptr image, float contentScale); + + void collectDropShadows(Canvas* canvas, std::shared_ptr opaqueSource, float contentScale); + + std::shared_ptr createMask(std::shared_ptr opaqueImage, float contentScale); + std::vector params = {}; bool _showBehindTransparent = false; diff --git a/test/src/LayerTest.cpp b/test/src/LayerTest.cpp index f90f7869..0186f8f8 100644 --- a/test/src/LayerTest.cpp +++ b/test/src/LayerTest.cpp @@ -1777,4 +1777,26 @@ TGFX_TEST(LayerTest, LayerShadowFilter) { displayList->render(surface.get()); EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/layerShadowFilter1")); } + +TGFX_TEST(LayerTest, Filters) { + ContextScope scope; + auto context = scope.getContext(); + EXPECT_TRUE(context != nullptr); + auto surface = Surface::Make(context, 150, 150); + auto displayList = std::make_unique(); + auto layer = ShapeLayer::Make(); + layer->setMatrix(Matrix::MakeTrans(30, 30)); + Path path; + path.addRect(Rect::MakeWH(100, 100)); + layer->setPath(path); + auto fillStyle = SolidColor::Make(Color::FromRGBA(100, 0, 0, 128)); + layer->setFillStyle(fillStyle); + auto filter = BlurFilter::Make(10, 10); + auto filter2 = DropShadowFilter::Make(10, 10, 0, 0, Color::Black()); + auto filter3 = InnerShadowFilter::Make(10, 10, 0, 0, Color::White()); + layer->setFilters({filter, filter2, filter3}); + displayList->root()->addChild(layer); + displayList->render(surface.get()); + EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/filters")); +} } // namespace tgfx From 1d2b29d4b76d03e742ba6a23668f6efc9d06324b Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Fri, 20 Dec 2024 12:09:37 +0800 Subject: [PATCH 6/8] Fix comment issues --- include/tgfx/layers/Layer.h | 2 + include/tgfx/layers/filters/LayerFilter.h | 4 +- .../tgfx/layers/filters/LayerImageFilter.h | 12 ----- src/layers/Layer.cpp | 47 ++++++++++--------- test/src/LayerShadowFilter.cpp | 38 ++++----------- test/src/LayerShadowFilter.h | 4 -- 6 files changed, 38 insertions(+), 69 deletions(-) diff --git a/include/tgfx/layers/Layer.h b/include/tgfx/layers/Layer.h index 9b1c713f..37fab073 100644 --- a/include/tgfx/layers/Layer.h +++ b/include/tgfx/layers/Layer.h @@ -502,6 +502,8 @@ class Layer { Paint getLayerPaint(float alpha, BlendMode blendMode); + std::shared_ptr createPictureImage(std::shared_ptr picture, Point* offset); + std::shared_ptr applyFilters(std::shared_ptr source, float contentScale); LayerContent* getRasterizedCache(const DrawArgs& args); diff --git a/include/tgfx/layers/filters/LayerFilter.h b/include/tgfx/layers/filters/LayerFilter.h index d9667374..f78170d5 100644 --- a/include/tgfx/layers/filters/LayerFilter.h +++ b/include/tgfx/layers/filters/LayerFilter.h @@ -29,7 +29,7 @@ namespace tgfx { class LayerFilter : public LayerProperty { public: /** - * Applies the filter to the given picture and draws it to the canvas. + * Applies the filter to the scaled Image of the layer content and draws it on the canvas. * @param contentScale The scale factor of the source Image relative to its original size. * Some filters have size-related parameters that must be adjusted with this scale factor. * @return True if the filter was applied and drawn, false otherwise. @@ -37,7 +37,7 @@ class LayerFilter : public LayerProperty { virtual bool applyFilter(Canvas* canvas, std::shared_ptr image, float contentScale) = 0; /** - * Return the bounds of after applying the filter to the given bounds. + * Returns the bounds after applying the filter to the scaled layer bounds * @param contentScale The scale factor of the source bounds relative to its original size. * Some filters have size-related parameters that must be adjusted with this scale factor * @return The bounds of the filtered image. diff --git a/include/tgfx/layers/filters/LayerImageFilter.h b/include/tgfx/layers/filters/LayerImageFilter.h index 49abedba..2384fdf0 100644 --- a/include/tgfx/layers/filters/LayerImageFilter.h +++ b/include/tgfx/layers/filters/LayerImageFilter.h @@ -27,20 +27,8 @@ namespace tgfx { */ class LayerImageFilter : public LayerFilter { public: - /** - * Applies the filter to the given picture and draws it to the canvas. - * @param contentScale The scale factor of the source Image relative to its original size. - * Some filters have size-related parameters that must be adjusted with this scale factor. - * @return True if the filter was applied and drawn, false otherwise. - */ bool applyFilter(Canvas* canvas, std::shared_ptr image, float contentScale) override; - /** - * Return the bounds of after applying the filter to the given bounds. - * @param contentScale The scale factor of the source Image relative to its original size. - * Some filters have size-related parameters that must be adjusted with this scale factor. - * @return The bounds of the filtered image. - */ Rect filterBounds(const Rect& srcRect, float contentScale) override; protected: diff --git a/src/layers/Layer.cpp b/src/layers/Layer.cpp index 7ee2b0a9..9c71eb45 100644 --- a/src/layers/Layer.cpp +++ b/src/layers/Layer.cpp @@ -534,6 +534,20 @@ Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) { return paint; } +std::shared_ptr Layer::createPictureImage(std::shared_ptr picture, Point* offset) { + if (picture == nullptr) { + return nullptr; + } + auto bounds = picture->getBounds(); + bounds.roundOut(); + auto matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); + auto image = Image::MakeFrom(std::move(picture), static_cast(bounds.width()), + static_cast(bounds.height()), &matrix); + if (offset) { + *offset = Point::Make(bounds.x(), bounds.y()); + } + return image; +} std::shared_ptr Layer::applyFilters(std::shared_ptr source, float contentScale) { if (source == nullptr) { return nullptr; @@ -541,28 +555,20 @@ std::shared_ptr Layer::applyFilters(std::shared_ptr source, fl if (_filters.empty() || FloatNearlyZero(contentScale)) { return source; } - auto sourceBounds = source->getBounds(); - sourceBounds.roundOut(); - auto pictureMatrix = Matrix::MakeTrans(-sourceBounds.x(), -sourceBounds.y()); - auto image = Image::MakeFrom(source, static_cast(sourceBounds.width()), - static_cast(sourceBounds.height()), &pictureMatrix); - Recorder recorder; - Point offset = Point::Make(sourceBounds.x(), sourceBounds.y()); + Point offset = Point::Zero(); + auto image = createPictureImage(std::move(source), &offset); + Recorder recorder{}; for (const auto& filter : _filters) { auto canvas = recorder.beginRecording(); if (!filter->applyFilter(canvas, image, contentScale)) { continue; } - auto picture = recorder.finishRecordingAsPicture(); - if (picture == nullptr) { + Point filterOffset = Point::Zero(); + image = createPictureImage(recorder.finishRecordingAsPicture(), &filterOffset); + if (image == nullptr) { return nullptr; } - auto bounds = picture->getBounds(); - bounds.roundOut(); - auto matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); - image = Image::MakeFrom(std::move(picture), static_cast(bounds.width()), - static_cast(bounds.height()), &matrix); - offset.offset(bounds.x(), bounds.y()); + offset += filterOffset; } auto canvas = recorder.beginRecording(); canvas->drawImage(image, offset.x, offset.y); @@ -603,16 +609,13 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con if (!picture) { return nullptr; } - auto bounds = picture->getBounds(); - auto width = static_cast(ceilf(bounds.width())); - auto height = static_cast(ceilf(bounds.height())); - auto matrix = Matrix::MakeTrans(-bounds.left, -bounds.top); - auto image = Image::MakeFrom(std::move(picture), width, height, &matrix); + Point offset = Point::Zero(); + auto image = createPictureImage(std::move(picture), &offset); if (image == nullptr) { return nullptr; } drawingMatrix->setScale(1.0f / contentScale, 1.0f / contentScale); - drawingMatrix->preTranslate(bounds.left, bounds.top); + drawingMatrix->preTranslate(offset.x, offset.y); return image; } @@ -620,7 +623,7 @@ std::shared_ptr Layer::getLayerContents(const DrawArgs& args, float con if (FloatNearlyZero(contentScale)) { return nullptr; } - Recorder recorder; + Recorder recorder{}; auto contentCanvas = recorder.beginRecording(); contentCanvas->scale(contentScale, contentScale); drawContents(args, contentCanvas, 1.0f); diff --git a/test/src/LayerShadowFilter.cpp b/test/src/LayerShadowFilter.cpp index 5314fd52..b2eb521d 100644 --- a/test/src/LayerShadowFilter.cpp +++ b/test/src/LayerShadowFilter.cpp @@ -86,40 +86,20 @@ void LayerShadowFilter::drawShadows(Canvas* canvas, std::shared_ptr image auto opaqueFilter = ImageFilter::ColorFilter(ColorFilter::AlphaThreshold(0)); auto opaqueImage = image->makeWithFilter(opaqueFilter); - // collect drop shadows into a picture, and then we can apply a mask filter to the picture - Recorder recorder; - auto shadowCanvas = recorder.beginRecording(); - collectDropShadows(shadowCanvas, opaqueImage, contentScale); - auto shadowsPicture = recorder.finishRecordingAsPicture(); - - Paint shadowPaint; - if (!_showBehindTransparent) { - shadowPaint.setMaskFilter(createMask(opaqueImage, contentScale)); - } - canvas->drawPicture(shadowsPicture, nullptr, &shadowPaint); -} + auto shader = Shader::MakeImageShader(opaqueImage, TileMode::Decal, TileMode::Decal); + Point offset = Point::Zero(); -void LayerShadowFilter::collectDropShadows(Canvas* canvas, std::shared_ptr opaqueSource, - float contentScale) { for (const auto& param : params) { if (auto filter = CreateShadowFilter(param, contentScale)) { - Paint paint; - paint.setImageFilter(filter); - canvas->drawImage(opaqueSource, &paint); + auto shadowImage = opaqueImage->makeWithFilter(filter, &offset); + Paint paint{}; + if (!_showBehindTransparent) { + auto matrixShader = shader->makeWithMatrix(Matrix::MakeTrans(-offset.x, -offset.y)); + paint.setMaskFilter(MaskFilter::MakeShader(matrixShader, true)); + } + canvas->drawImage(shadowImage, offset.x, offset.y, &paint); } } } -std::shared_ptr LayerShadowFilter::createMask(std::shared_ptr opaqueImage, - float contentScale) { - auto shader = Shader::MakeImageShader(opaqueImage, TileMode::Decal, TileMode::Decal); - // get bounds after applying the filter, so that the mask filter can be applied to the - // correct area - auto boundsAfterFilter = - filterBounds(Rect::MakeWH(opaqueImage->width(), opaqueImage->height()), contentScale); - shader = - shader->makeWithMatrix(Matrix::MakeTrans(-boundsAfterFilter.left, -boundsAfterFilter.top)); - return MaskFilter::MakeShader(shader, true); -} - } // namespace tgfx \ No newline at end of file diff --git a/test/src/LayerShadowFilter.h b/test/src/LayerShadowFilter.h index fda9055f..c94206c9 100644 --- a/test/src/LayerShadowFilter.h +++ b/test/src/LayerShadowFilter.h @@ -77,10 +77,6 @@ class LayerShadowFilter : public LayerFilter { void drawShadows(Canvas* canvas, std::shared_ptr image, float contentScale); - void collectDropShadows(Canvas* canvas, std::shared_ptr opaqueSource, float contentScale); - - std::shared_ptr createMask(std::shared_ptr opaqueImage, float contentScale); - std::vector params = {}; bool _showBehindTransparent = false; From d9d200f5620f5d55541a0158841f7afa8ce794dc Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Fri, 20 Dec 2024 12:14:54 +0800 Subject: [PATCH 7/8] update version.json --- test/baseline/version.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/baseline/version.json b/test/baseline/version.json index 02426910..5db9e8e8 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -97,6 +97,7 @@ "dropShadow": "43cd416", "filterClip": "43cd416", "filterTest": "44166b7", + "filters": "9000907", "getBounds": "3888c19", "getLayersUnderPoint": "b062b9a", "greyColorMatrix": "a1605b2", @@ -104,8 +105,10 @@ "imageLayer": "99a5cd9", "imageMask": "af2e3ff", "innerShadow": "01cce38", + "layerShadowFilter": "9000907", + "layerShadowFilter1": "9000907", "shapeMask": "612c09e", - "textMask": "b062b9a" + "textMask": "9000907" }, "MaskTest": { "rasterize_emoji": "c0f39f8", From 444ba890f3c774156017e0fe75a622c8564604e6 Mon Sep 17 00:00:00 2001 From: Hparty <420024556@qq.com> Date: Fri, 20 Dec 2024 13:01:45 +0800 Subject: [PATCH 8/8] Fix comment issues --- include/tgfx/layers/Layer.h | 2 - src/layers/Layer.cpp | 50 +++++++++++++------------ src/layers/filters/LayerImageFilter.cpp | 2 +- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/include/tgfx/layers/Layer.h b/include/tgfx/layers/Layer.h index 37fab073..9b1c713f 100644 --- a/include/tgfx/layers/Layer.h +++ b/include/tgfx/layers/Layer.h @@ -502,8 +502,6 @@ class Layer { Paint getLayerPaint(float alpha, BlendMode blendMode); - std::shared_ptr createPictureImage(std::shared_ptr picture, Point* offset); - std::shared_ptr applyFilters(std::shared_ptr source, float contentScale); LayerContent* getRasterizedCache(const DrawArgs& args); diff --git a/src/layers/Layer.cpp b/src/layers/Layer.cpp index 9c71eb45..3b5b1d60 100644 --- a/src/layers/Layer.cpp +++ b/src/layers/Layer.cpp @@ -31,6 +31,21 @@ namespace tgfx { static std::atomic_bool AllowsEdgeAntialiasing = true; static std::atomic_bool AllowsGroupOpacity = false; +static std::shared_ptr CreatePictureImage(std::shared_ptr picture, Point* offset) { + if (picture == nullptr) { + return nullptr; + } + auto bounds = picture->getBounds(); + bounds.roundOut(); + auto matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); + auto image = Image::MakeFrom(std::move(picture), static_cast(bounds.width()), + static_cast(bounds.height()), &matrix); + if (offset) { + *offset = Point::Make(bounds.x(), bounds.y()); + } + return image; +} + bool Layer::DefaultAllowsEdgeAntialiasing() { return AllowsEdgeAntialiasing; } @@ -534,37 +549,23 @@ Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) { return paint; } -std::shared_ptr Layer::createPictureImage(std::shared_ptr picture, Point* offset) { - if (picture == nullptr) { - return nullptr; - } - auto bounds = picture->getBounds(); - bounds.roundOut(); - auto matrix = Matrix::MakeTrans(-bounds.x(), -bounds.y()); - auto image = Image::MakeFrom(std::move(picture), static_cast(bounds.width()), - static_cast(bounds.height()), &matrix); - if (offset) { - *offset = Point::Make(bounds.x(), bounds.y()); - } - return image; -} std::shared_ptr Layer::applyFilters(std::shared_ptr source, float contentScale) { if (source == nullptr) { return nullptr; } - if (_filters.empty() || FloatNearlyZero(contentScale)) { + if (_filters.empty()) { return source; } Point offset = Point::Zero(); - auto image = createPictureImage(std::move(source), &offset); - Recorder recorder{}; + auto image = CreatePictureImage(std::move(source), &offset); + Recorder recorder = {}; for (const auto& filter : _filters) { auto canvas = recorder.beginRecording(); if (!filter->applyFilter(canvas, image, contentScale)) { continue; } Point filterOffset = Point::Zero(); - image = createPictureImage(recorder.finishRecordingAsPicture(), &filterOffset); + image = CreatePictureImage(recorder.finishRecordingAsPicture(), &filterOffset); if (image == nullptr) { return nullptr; } @@ -604,13 +605,16 @@ LayerContent* Layer::getRasterizedCache(const DrawArgs& args) { std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float contentScale, Matrix* drawingMatrix) { DEBUG_ASSERT(drawingMatrix != nullptr); + if (FloatNearlyZero(contentScale)) { + return nullptr; + } auto picture = getLayerContents(args, contentScale); picture = applyFilters(std::move(picture), contentScale); if (!picture) { return nullptr; } Point offset = Point::Zero(); - auto image = createPictureImage(std::move(picture), &offset); + auto image = CreatePictureImage(std::move(picture), &offset); if (image == nullptr) { return nullptr; } @@ -620,10 +624,7 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con } std::shared_ptr Layer::getLayerContents(const DrawArgs& args, float contentScale) { - if (FloatNearlyZero(contentScale)) { - return nullptr; - } - Recorder recorder{}; + Recorder recorder = {}; auto contentCanvas = recorder.beginRecording(); contentCanvas->scale(contentScale, contentScale); drawContents(args, contentCanvas, 1.0f); @@ -688,6 +689,9 @@ std::shared_ptr Layer::getMaskFilter(const DrawArgs& args, float sca void Layer::drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, BlendMode blendMode) { auto contentScale = canvas->getMatrix().getMaxScale(); + if (FloatNearlyZero(contentScale)) { + return; + } auto picture = getLayerContents(args, contentScale); picture = applyFilters(std::move(picture), contentScale); if (picture == nullptr) { diff --git a/src/layers/filters/LayerImageFilter.cpp b/src/layers/filters/LayerImageFilter.cpp index f8b5aa5f..88e430f7 100644 --- a/src/layers/filters/LayerImageFilter.cpp +++ b/src/layers/filters/LayerImageFilter.cpp @@ -26,7 +26,7 @@ bool LayerImageFilter::applyFilter(Canvas* canvas, std::shared_ptr image, if (!filter) { return false; } - Paint paint; + Paint paint = {}; paint.setImageFilter(filter); canvas->drawImage(image, &paint); return true;