Skip to content

Commit

Permalink
Allow inheriting LayerFilter to customize filter behavior. (#390)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hparty authored Dec 20, 2024
1 parent e5ca671 commit 27ac413
Show file tree
Hide file tree
Showing 19 changed files with 431 additions and 92 deletions.
2 changes: 1 addition & 1 deletion include/tgfx/layers/Layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ class Layer {

Paint getLayerPaint(float alpha, BlendMode blendMode);

std::shared_ptr<ImageFilter> getLayerFilter(float contentScale);
std::shared_ptr<Picture> applyFilters(std::shared_ptr<Picture> source, float contentScale);

LayerContent* getRasterizedCache(const DrawArgs& args);

Expand Down
10 changes: 6 additions & 4 deletions include/tgfx/layers/filters/BlendFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@

#pragma once

#include "tgfx/layers/filters/LayerFilter.h"
#include "tgfx/layers/filters/LayerImageFilter.h"

namespace tgfx {

class BlendFilter : public LayerFilter {
/**
* A filter that applies blends between the constant color (src) and input color (dst) based on the
* BlendMode.
*/
class BlendFilter : public LayerImageFilter {
public:
virtual ~BlendFilter() = default;

/**
* Creates a new ColorFilter that applies blends between the constant color (src) and input color
* (dst) based on the BlendMode.
Expand Down
9 changes: 5 additions & 4 deletions include/tgfx/layers/filters/BlurFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@

#pragma once

#include "tgfx/layers/filters/LayerFilter.h"
#include "tgfx/layers/filters/LayerImageFilter.h"

namespace tgfx {

class BlurFilter : public LayerFilter {
/**
* A filter that blurs its input by the separate X and Y blurriness.
*/
class BlurFilter : public LayerImageFilter {
public:
virtual ~BlurFilter() = 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.
Expand Down
9 changes: 5 additions & 4 deletions include/tgfx/layers/filters/ColorMatrixFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,15 @@

#pragma once

#include "tgfx/layers/filters/LayerFilter.h"
#include "tgfx/layers/filters/LayerImageFilter.h"

namespace tgfx {

class ColorMatrixFilter : public LayerFilter {
/**
* A filter that transforms the color using the given 4x5 matrix.
*/
class ColorMatrixFilter : public LayerImageFilter {
public:
virtual ~ColorMatrixFilter() = 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:
Expand Down
9 changes: 5 additions & 4 deletions include/tgfx/layers/filters/DropShadowFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@

#pragma once

#include "tgfx/layers/filters/LayerFilter.h"
#include "tgfx/layers/filters/LayerImageFilter.h"

namespace tgfx {
class DropShadowFilter : public LayerFilter {
/**
* A filter draws a drop shadow under the input content.
*/
class DropShadowFilter : public LayerImageFilter {
public:
virtual ~DropShadowFilter() = default;

/**
* Create a filter that draws a drop shadow under the input content.
*/
Expand Down
10 changes: 6 additions & 4 deletions include/tgfx/layers/filters/InnerShadowFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@

#pragma once

#include "tgfx/layers/filters/LayerFilter.h"
#include "tgfx/layers/filters/LayerImageFilter.h"

namespace tgfx {
class InnerShadowFilter : public LayerFilter {
public:
virtual ~InnerShadowFilter() = default;

/**
* A filter draws an inner shadow over the input content.
*/
class InnerShadowFilter : public LayerImageFilter {
public:
/**
* Create a filter that draws an inner shadow over the input content.
*/
Expand Down
35 changes: 10 additions & 25 deletions include/tgfx/layers/filters/LayerFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,34 +29,19 @@ 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.
* @param scale The scale factor to apply to the filter.
* @return The current image filter.
* 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.
*/
std::shared_ptr<ImageFilter> getImageFilter(float scale);
virtual bool applyFilter(Canvas* canvas, std::shared_ptr<Image> image, float contentScale) = 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.
* @param scale The scale factor to apply to the filter.
* @return A new image filter.
* 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.
*/
virtual std::shared_ptr<ImageFilter> 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<Rect> _clipBounds = nullptr;

std::shared_ptr<ImageFilter> lastFilter;
virtual Rect filterBounds(const Rect& srcRect, float contentScale) = 0;
};
} // namespace tgfx
58 changes: 58 additions & 0 deletions include/tgfx/layers/filters/LayerImageFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 {

/**
* LayerImageFilter is a filter that applies an image filter to a layer.
*/
class LayerImageFilter : public LayerFilter {
public:
bool applyFilter(Canvas* canvas, std::shared_ptr<Image> image, float contentScale) override;

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 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<ImageFilter> onCreateImageFilter(float contentScale) = 0;

/**
* Marks the filter as dirty and invalidates the cached filter.
*/
void invalidateFilter();

private:
std::shared_ptr<ImageFilter> getImageFilter(float scale);

bool dirty = true;

float lastScale = 1.0f;

std::shared_ptr<ImageFilter> lastFilter;
};
} // namespace tgfx
78 changes: 48 additions & 30 deletions src/layers/Layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,21 @@ namespace tgfx {
static std::atomic_bool AllowsEdgeAntialiasing = true;
static std::atomic_bool AllowsGroupOpacity = false;

static std::shared_ptr<Image> CreatePictureImage(std::shared_ptr<Picture> 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<int>(bounds.width()),
static_cast<int>(bounds.height()), &matrix);
if (offset) {
*offset = Point::Make(bounds.x(), bounds.y());
}
return image;
}

bool Layer::DefaultAllowsEdgeAntialiasing() {
return AllowsEdgeAntialiasing;
}
Expand Down Expand Up @@ -346,9 +361,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) {
Expand Down Expand Up @@ -535,19 +549,31 @@ Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) {
return paint;
}

std::shared_ptr<ImageFilter> Layer::getLayerFilter(float contentScale) {
if (_filters.empty() || FloatNearlyZero(contentScale)) {
std::shared_ptr<Picture> Layer::applyFilters(std::shared_ptr<Picture> source, float contentScale) {
if (source == nullptr) {
return nullptr;
}
std::vector<std::shared_ptr<ImageFilter>> imageFilters;
if (_filters.empty()) {
return source;
}
Point offset = Point::Zero();
auto image = CreatePictureImage(std::move(source), &offset);
Recorder recorder = {};
for (const auto& filter : _filters) {
auto imageFilter = filter->getImageFilter(contentScale);
if (!imageFilter) {
auto canvas = recorder.beginRecording();
if (!filter->applyFilter(canvas, image, contentScale)) {
continue;
}
imageFilters.push_back(imageFilter);
Point filterOffset = Point::Zero();
image = CreatePictureImage(recorder.finishRecordingAsPicture(), &filterOffset);
if (image == nullptr) {
return nullptr;
}
offset += filterOffset;
}
return ImageFilter::Compose(imageFilters);
auto canvas = recorder.beginRecording();
canvas->drawImage(image, offset.x, offset.y);
return recorder.finishRecordingAsPicture();
}

LayerContent* Layer::getRasterizedCache(const DrawArgs& args) {
Expand Down Expand Up @@ -579,36 +605,26 @@ LayerContent* Layer::getRasterizedCache(const DrawArgs& args) {
std::shared_ptr<Image> 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;
}
auto bounds = picture->getBounds();
auto width = static_cast<int>(ceilf(bounds.width()));
auto height = static_cast<int>(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);
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);
}
drawingMatrix->preTranslate(offset.x, offset.y);
return image;
}

std::shared_ptr<Picture> 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);
Expand Down Expand Up @@ -673,16 +689,18 @@ std::shared_ptr<MaskFilter> 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) {
return;
}
Paint paint;
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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/layers/filters/BlendFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ std::shared_ptr<ImageFilter> 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
2 changes: 1 addition & 1 deletion src/layers/filters/BlurFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ImageFilter> BlurFilter::onCreateImageFilter(float scale) {
Expand Down
2 changes: 1 addition & 1 deletion src/layers/filters/ColorMatrixFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ std::shared_ptr<ImageFilter> ColorMatrixFilter::onCreateImageFilter(float) {
}

ColorMatrixFilter::ColorMatrixFilter(const std::array<float, 20>& matrix)
: LayerFilter(), _matrix(std::move(matrix)) {
: _matrix(std::move(matrix)) {
}

} // namespace tgfx
4 changes: 2 additions & 2 deletions src/layers/filters/DropShadowFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ std::shared_ptr<ImageFilter> 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
Loading

0 comments on commit 27ac413

Please sign in to comment.