Skip to content

Commit

Permalink
Add an ImageFilter to draw inner shadow. (#323)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hparty authored Nov 8, 2024
1 parent 348b9a7 commit d5f31fe
Show file tree
Hide file tree
Showing 8 changed files with 458 additions and 0 deletions.
25 changes: 25 additions & 0 deletions include/tgfx/core/ImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ class ImageFilter {
static std::shared_ptr<ImageFilter> DropShadowOnly(float dx, float dy, float blurrinessX,
float blurrinessY, const Color& color);

/**
* Create a filter that draws an inner shadow over the input content. This filter produces an image
* that includes the inputs' content.
* @param dx The X offset of the shadow.
* @param dy The Y offset of the shadow.
* @param blurrinessX The blur radius for the shadow, along the X axis.
* @param blurrinessY The blur radius for the shadow, along the Y axis.
* @param color The color of the inner shadow.
*/
static std::shared_ptr<ImageFilter> InnerShadow(float dx, float dy, float blurrinessX,
float blurrinessY, const Color& color);

/**
* Create a filter that renders an inner shadow, in exactly the same manner as the InnerShadow(),
* except that the resulting image does not include the input content.
* @param dx The X offset of the shadow.
* @param dy The Y offset of the shadow.
* @param blurrinessX The blur radius for the shadow, along the X axis.
* @param blurrinessY The blur radius for the shadow, along the Y axis.
* @param color The color of the inner shadow.
*/
static std::shared_ptr<ImageFilter> InnerShadowOnly(float dx, float dy, float blurrinessX,
float blurrinessY, const Color& color);

/**
* Create a filter that applies the given color filter to the input image.
*/
Expand Down Expand Up @@ -143,6 +167,7 @@ class ImageFilter {
const Matrix* uvMatrix) const;

friend class DropShadowImageFilter;
friend class InnerShadowImageFilter;
friend class ComposeImageFilter;
friend class FilterImage;
};
Expand Down
126 changes: 126 additions & 0 deletions include/tgfx/layers/filters/InnerShadowFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 InnerShadowFilter : public LayerFilter {
public:
virtual ~InnerShadowFilter() = default;

/**
* Create a filter that draws an inner shadow over the input content.
*/
static std::shared_ptr<InnerShadowFilter> Make(float offsetX, float offsetY, float blurrinessX,
float blurrinessY, const Color& color,
bool innerShadowOnly = false);

/**
* The x offset of the shadow.
*/
float offsetX() const {
return _offsetX;
}

/**
* Set x offset of the shadow.
* @param offsetX
*/
void setOffsetX(float offsetX);

/**
* The y offset of the shadow.
*/
float offsetY() const {
return _offsetY;
}

/**
* Set y offset of the shadow.
* @param offsetY
*/
void setOffsetY(float offsetY);

/**
* The blur radius for the shadow, along the X axis.
*/
float blurrinessX() const {
return _blurrinessX;
}

/**
* Set blur radius for the shadow, along the X axis.
* @param blurrinessX
*/
void setBlurrinessX(float blurrinessX);

/**
* The blur radius for the shadow, along the Y axis.
*/
float blurrinessY() const {
return _blurrinessY;
}

/**
* Set blur radius for the shadow, along the Y axis.
* @param blurrinessY
*/
void setBlurrinessY(float blurrinessY);

/**
* The color of the shadow.
*/
Color color() const {
return _color;
}

/**
* Set the color of the shadow.
* @param color
*/
void setColor(const Color& color);

/**
* Whether the resulting image does not include the input content.
*/
bool innerShadowOnly() const {
return _innerShadowOnly;
}

/**
* Set whether the resulting image does not include the input content.
*/
void setInnerShadowOnly(bool value);

protected:
std::shared_ptr<ImageFilter> onCreateImageFilter(float scale) override;

private:
InnerShadowFilter(float offsetX, float offsetY, float blurrinessX, float blurrinessY,
const Color& color, bool innerShadowOnly);
float _offsetX = 0.0f;
float _offsetY = 0.0f;
float _blurrinessX = 0.0f;
float _blurrinessY = 0.0f;
Color _color = Color::Black();
bool _innerShadowOnly = false;
};

} // namespace tgfx
86 changes: 86 additions & 0 deletions src/core/filters/InnerShadowImageFilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 "InnerShadowImageFilter.h"
#include "core/images/TextureImage.h"
#include "core/utils/NeedMipmaps.h"
#include "gpu/processors/ConstColorProcessor.h"
#include "gpu/processors/FragmentProcessor.h"
#include "gpu/processors/XfermodeFragmentProcessor.h"
namespace tgfx {
std::shared_ptr<ImageFilter> ImageFilter::InnerShadow(float dx, float dy, float blurrinessX,
float blurrinessY, const Color& color) {
return std::make_shared<InnerShadowImageFilter>(dx, dy, blurrinessX, blurrinessY, color, false);
}

std::shared_ptr<ImageFilter> ImageFilter::InnerShadowOnly(float dx, float dy, float blurrinessX,
float blurrinessY, const Color& color) {

return std::make_shared<InnerShadowImageFilter>(dx, dy, blurrinessX, blurrinessY, color, true);
}

InnerShadowImageFilter::InnerShadowImageFilter(float dx, float dy, float blurrinessX,
float blurrinessY, const Color& color,
bool shadowOnly)

: dx(dx), dy(dy), blurFilter(ImageFilter::Blur(blurrinessX, blurrinessY)), color(color),
shadowOnly(shadowOnly) {
}

std::unique_ptr<FragmentProcessor> InnerShadowImageFilter::asFragmentProcessor(
std::shared_ptr<Image> source, const FPArgs& args, const SamplingOptions& sampling,
const Matrix* uvMatrix) const {
if (source->isComplex()) {
auto needMipmaps = NeedMipmaps(sampling, args.viewMatrix, uvMatrix);
source = source->makeRasterized(needMipmaps, sampling);
}

// get inverted shadow mask
auto shadowMatrix = Matrix::MakeTrans(-dx, -dy);
if (uvMatrix != nullptr) {
shadowMatrix.preConcat(*uvMatrix);
}
std::unique_ptr<FragmentProcessor> invertShadowMask;
if (blurFilter != nullptr) {
invertShadowMask = blurFilter->asFragmentProcessor(source, args, sampling, &shadowMatrix);
} else {
invertShadowMask = FragmentProcessor::Make(source, args, TileMode::Decal, TileMode::Decal,
sampling, &shadowMatrix);
}

auto colorProcessor = ConstColorProcessor::Make(color, InputMode::Ignore);

// get shadow mask and fill it with color
auto colorShadowProcessor = XfermodeFragmentProcessor::MakeFromTwoProcessors(
std::move(colorProcessor), std::move(invertShadowMask), BlendMode::SrcOut);

auto imageProcessor = FragmentProcessor::Make(std::move(source), args, TileMode::Decal,
TileMode::Decal, sampling, uvMatrix);

if (shadowOnly) {
// mask the image with origin image
return XfermodeFragmentProcessor::MakeFromTwoProcessors(
std::move(colorShadowProcessor), std::move(imageProcessor), BlendMode::SrcIn);
} else {
// mask the image with origin image and draw the inner shadow mask on top
return XfermodeFragmentProcessor::MakeFromTwoProcessors(
std::move(colorShadowProcessor), std::move(imageProcessor), BlendMode::SrcATop);
}
}

} // namespace tgfx
42 changes: 42 additions & 0 deletions src/core/filters/InnerShadowImageFilter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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/core/ImageFilter.h"

namespace tgfx {
class InnerShadowImageFilter : public ImageFilter {
public:
InnerShadowImageFilter(float dx, float dy, float blurrinessX, float blurrinessY,
const Color& color, bool shadowOnly);

protected:
std::unique_ptr<FragmentProcessor> asFragmentProcessor(std::shared_ptr<Image> source,
const FPArgs& args,
const SamplingOptions& sampling,
const Matrix* uvMatrix) const override;

private:
float dx = 0.0f;
float dy = 0.0f;
std::shared_ptr<ImageFilter> blurFilter = nullptr;
Color color = Color::Black();
bool shadowOnly = false;
};
} // namespace tgfx
93 changes: 93 additions & 0 deletions src/layers/filters/InnerShadowFilter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/////////////////////////////////////////////////////////////////////////////////////////////////
//
// 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 "tgfx/layers/filters/InnerShadowFilter.h"

namespace tgfx {
std::shared_ptr<InnerShadowFilter> InnerShadowFilter::Make(float offsetX, float offsetY,
float blurrinessX, float blurrinessY,
const Color& color,
bool innerShadowOnly) {
return std::shared_ptr<InnerShadowFilter>(
new InnerShadowFilter(offsetX, offsetY, blurrinessX, blurrinessY, color, innerShadowOnly));
}

void InnerShadowFilter::setOffsetX(float offsetX) {
if (_offsetX == offsetX) {
return;
}
_offsetX = offsetX;
invalidateFilter();
}

void InnerShadowFilter::setOffsetY(float offsetY) {
if (_offsetY == offsetY) {
return;
}
_offsetY = offsetY;
invalidateFilter();
}

void InnerShadowFilter::setBlurrinessX(float blurrinessX) {
if (_blurrinessX == blurrinessX) {
return;
}
_blurrinessX = blurrinessX;
invalidateFilter();
}

void InnerShadowFilter::setBlurrinessY(float blurrinessY) {
if (_blurrinessY == blurrinessY) {
return;
}
_blurrinessY = blurrinessY;
invalidateFilter();
}

void InnerShadowFilter::setColor(const Color& color) {
if (_color == color) {
return;
}
_color = color;
invalidateFilter();
}

void InnerShadowFilter::setInnerShadowOnly(bool value) {
if (_innerShadowOnly == value) {
return;
}
_innerShadowOnly = value;
invalidateFilter();
}

std::shared_ptr<ImageFilter> InnerShadowFilter::onCreateImageFilter(float scale) {
if (_innerShadowOnly) {
return ImageFilter::InnerShadowOnly(_offsetX * scale, _offsetY * scale, _blurrinessX * scale,
_blurrinessY * scale, _color);
}
return ImageFilter::InnerShadow(_offsetX * scale, _offsetY * scale, _blurrinessX * scale,
_blurrinessY * scale, _color);
}

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) {
}

} // namespace tgfx
Loading

0 comments on commit d5f31fe

Please sign in to comment.