Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ScaleImage to create its own texture cache and remove unnecessary sampling parameters. #349

Merged
merged 3 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 20 additions & 24 deletions include/tgfx/core/Image.h
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,7 @@ class Image {
/**
* Returns true if the Image is guaranteed to produce a single-plane texture without extra
* transforms and can be cached for repeat drawing. Extra transforms may include orientation,
* scale, subsets, filters, or RGBAAA layouts. A YUV Image is also not flat since it has multiple
* planes.
* subsets, filters, or RGBAAA layouts. A YUV Image is also not flat since it has multiple planes.
*/
virtual bool isFlat() const = 0;

Expand All @@ -225,25 +224,19 @@ class Image {
* its own GPU cache but uses the full resolution cache created by the original Image. If you want
* the subset Image to create its own GPU cache, you should call makeFlattened() on the subset
* Image.
* @param mipmapped Specifies whether the flattened Image should have mipmaps. Ignored if the
* Image is already flat.
* @param sampling The sampling options may be applied if the image has scaling transforms.
* Ignored if the Image is already flat.
* @return If the Image is already flat, the original Image is returned.
*/
std::shared_ptr<Image> makeFlattened(bool mipmapped = false,
const SamplingOptions& sampling = {}) const;
std::shared_ptr<Image> makeFlattened() const;

/**
* Returns an Image backed by a GPU texture associated with the given context. If a corresponding
* texture cache exists in the context, it returns an Image that wraps that texture. Otherwise, it
* creates one immediately, applying the sampling options if the image is scaled. If the Image is
* already texture-backed and the context is compatible with the GPU texture, it returns the
* original Image. Otherwise, it returns nullptr. It's safe to release the original Image to
* reduce CPU memory usage, as the returned Image holds a strong reference to the texture cache.
* creates one immediately. If the Image is already texture-backed and the context is compatible
* with the GPU texture, it returns the original Image. Otherwise, it returns nullptr. It's safe
* to release the original Image to reduce CPU memory usage, as the returned Image holds a strong
* reference to the texture cache.
*/
virtual std::shared_ptr<Image> makeTextureImage(Context* context,
const SamplingOptions& sampling = {}) const;
virtual std::shared_ptr<Image> makeTextureImage(Context* context) const;

/**
* Retrieves the backend texture of the Image. Returns an invalid BackendTexture if the Image is
Expand Down Expand Up @@ -281,11 +274,18 @@ class Image {
std::shared_ptr<Image> makeOriented(Orientation orientation) const;

/**
* Returns an Image with the specified scale. The returned Image always shares pixels and caches
* with the original Image. If both scaleX and scaleY are 1.0, the original Image is returned.
* If scaleX or scaleY is less than zero, nullptr is returned.
* Returns an Image scaled by the given factor. The scaled Image will have its own GPU cache at
* the new resolution and will not have mipmaps, even if the original Image does. You can call
* makeMipmapped() on the scaled Image to enable mipmaps. If the scale is greater than 1.0, it
* may result in blurring. Note that calling makeScaled() on an already scaled Image may return
* its internal non-scaled Image if the multiplied scale is 1.0.
* @param scale The factor to scale the Image by.
* @param sampling The sampling options to use when scaling the Image.
* @return The scaled Image. If the scale is 1.0, the original Image is returned. If the scale is
* less than 0, nullptr is returned.
*/
std::shared_ptr<Image> makeScaled(float scaleX, float scaleY) const;
virtual std::shared_ptr<Image> makeScaled(float scale,
const SamplingOptions& sampling = {}) const;

/**
* Returns a filtered Image with the specified filter. The filter has the potential to alter the
Expand Down Expand Up @@ -321,19 +321,14 @@ class Image {

virtual std::shared_ptr<Image> onMakeOriented(Orientation orientation) const;

virtual std::shared_ptr<Image> onMakeScaled(float scaleX, float scaleY) const;

virtual std::shared_ptr<Image> onMakeWithFilter(std::shared_ptr<ImageFilter> filter,
Point* offset, const Rect* clipRect) const;

/**
* Returns a texture proxy for the entire Image.
* @param args The TPArgs used to create the texture proxy.
* @param sampling The sampling options applied when rasterizing the Image. This option
* may be ignored if the Image has no scaling transforms.
*/
virtual std::shared_ptr<TextureProxy> lockTextureProxy(const TPArgs& args,
const SamplingOptions& sampling) const;
virtual std::shared_ptr<TextureProxy> lockTextureProxy(const TPArgs& args) const;

/**
* Returns a fragment processor for the entire Image.
Expand All @@ -354,6 +349,7 @@ class Image {
friend class TransformImage;
friend class RGBAAAImage;
friend class FlattenImage;
friend class ScaleImage;
friend class ImageShader;
};
} // namespace tgfx
5 changes: 2 additions & 3 deletions include/tgfx/core/ImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,10 @@ class ImageFilter {
* @param source The source image.
* @param clipBounds The clip bounds of the filtered image, relative to the source image.
* @param args The arguments for creating the texture proxy.
* @param sampling The sampling options used for sampling the source image.
*/
virtual std::shared_ptr<TextureProxy> lockTextureProxy(std::shared_ptr<Image> source,
const Rect& clipBounds, const TPArgs& args,
const SamplingOptions& sampling) const;
const Rect& clipBounds,
const TPArgs& args) const;

/**
* Returns a FragmentProcessor that applies this filter to the source image. The returned
Expand Down
8 changes: 4 additions & 4 deletions src/core/ImageFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ Rect ImageFilter::onFilterBounds(const Rect& srcRect) const {

std::shared_ptr<TextureProxy> ImageFilter::lockTextureProxy(std::shared_ptr<Image> source,
const Rect& clipBounds,
const TPArgs& args,
const SamplingOptions& sampling) const {
const TPArgs& args) const {
auto renderTarget = RenderTargetProxy::MakeFallback(
args.context, static_cast<int>(clipBounds.width()), static_cast<int>(clipBounds.height()),
source->isAlphaOnly(), 1, args.mipmapped);
Expand All @@ -49,7 +48,8 @@ std::shared_ptr<TextureProxy> ImageFilter::lockTextureProxy(std::shared_ptr<Imag
auto drawRect = Rect::MakeWH(renderTarget->width(), renderTarget->height());
FPArgs fpArgs(args.context, args.renderFlags, drawRect, Matrix::I());
auto offsetMatrix = Matrix::MakeTrans(clipBounds.x(), clipBounds.y());
auto processor = asFragmentProcessor(std::move(source), fpArgs, sampling, &offsetMatrix);
// There is no scaling for the source image, so we can use the default sampling options.
auto processor = asFragmentProcessor(std::move(source), fpArgs, {}, &offsetMatrix);
if (!processor) {
return nullptr;
}
Expand Down Expand Up @@ -84,7 +84,7 @@ std::unique_ptr<FragmentProcessor> ImageFilter::makeFPFromTextureProxy(
auto isAlphaOnly = source->isAlphaOnly();
auto mipmapped = source->hasMipmaps() && NeedMipmaps(sampling, args.viewMatrix, uvMatrix);
TPArgs tpArgs(args.context, args.renderFlags, mipmapped);
auto textureProxy = lockTextureProxy(std::move(source), dstBounds, tpArgs, sampling);
auto textureProxy = lockTextureProxy(std::move(source), dstBounds, tpArgs);
if (textureProxy == nullptr) {
return nullptr;
}
Expand Down
8 changes: 4 additions & 4 deletions src/core/filters/BlurImageFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,9 @@ Rect BlurImageFilter::onFilterBounds(const Rect& srcRect) const {
return srcRect.makeOutset(blurOffset.x * mul, blurOffset.y * mul);
}

std::shared_ptr<TextureProxy> BlurImageFilter::lockTextureProxy(
std::shared_ptr<Image> source, const Rect& clipBounds, const TPArgs& args,
const SamplingOptions& sampling) const {
std::shared_ptr<TextureProxy> BlurImageFilter::lockTextureProxy(std::shared_ptr<Image> source,
const Rect& clipBounds,
const TPArgs& args) const {
auto isAlphaOnly = source->isAlphaOnly();
auto lastRenderTarget = RenderTargetProxy::MakeFallback(
args.context, static_cast<int>(clipBounds.width()), static_cast<int>(clipBounds.height()),
Expand All @@ -117,7 +117,7 @@ std::shared_ptr<TextureProxy> BlurImageFilter::lockTextureProxy(
}
auto drawRect = Rect::MakeWH(lastRenderTarget->width(), lastRenderTarget->height());
FPArgs fpArgs(args.context, args.renderFlags, drawRect, Matrix::I());
auto processor = FragmentProcessor::Make(source, fpArgs, tileMode, tileMode, sampling);
auto processor = FragmentProcessor::Make(source, fpArgs, tileMode, tileMode, {});
auto imageBounds = clipBounds;
std::vector<std::shared_ptr<RenderTargetProxy>> renderTargets = {};
for (int i = 0; i < iteration; ++i) {
Expand Down
4 changes: 2 additions & 2 deletions src/core/filters/BlurImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class BlurImageFilter : public ImageFilter {
Rect onFilterBounds(const Rect& srcRect) const override;

std::shared_ptr<TextureProxy> lockTextureProxy(std::shared_ptr<Image> source,
const Rect& clipBounds, const TPArgs& args,
const SamplingOptions& sampling) const override;
const Rect& clipBounds,
const TPArgs& args) const override;

std::unique_ptr<FragmentProcessor> asFragmentProcessor(std::shared_ptr<Image> source,
const FPArgs& args,
Expand Down
5 changes: 1 addition & 4 deletions src/core/filters/DropShadowImageFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,7 @@ Rect DropShadowImageFilter::onFilterBounds(const Rect& srcRect) const {
std::unique_ptr<FragmentProcessor> DropShadowImageFilter::asFragmentProcessor(
std::shared_ptr<Image> source, const FPArgs& args, const SamplingOptions& sampling,
const Matrix* uvMatrix) const {
if (!source->isFlat()) {
auto needMipmaps = NeedMipmaps(sampling, args.viewMatrix, uvMatrix);
source = source->makeFlattened(needMipmaps, sampling);
}
source = source->makeFlattened();
std::unique_ptr<FragmentProcessor> shadowProcessor;
auto shadowMatrix = Matrix::MakeTrans(-dx, -dy);
if (uvMatrix != nullptr) {
Expand Down
6 changes: 1 addition & 5 deletions src/core/filters/InnerShadowImageFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,7 @@ InnerShadowImageFilter::InnerShadowImageFilter(float dx, float dy, float blurrin
std::unique_ptr<FragmentProcessor> InnerShadowImageFilter::asFragmentProcessor(
std::shared_ptr<Image> source, const FPArgs& args, const SamplingOptions& sampling,
const Matrix* uvMatrix) const {
if (!source->isFlat()) {
auto needMipmaps = NeedMipmaps(sampling, args.viewMatrix, uvMatrix);
source = source->makeFlattened(needMipmaps, sampling);
}

source = source->makeFlattened();
// get inverted shadow mask
auto shadowMatrix = Matrix::MakeTrans(-dx, -dy);
if (uvMatrix != nullptr) {
Expand Down
10 changes: 5 additions & 5 deletions src/core/filters/RuntimeImageFilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ Rect RuntimeImageFilter::onFilterBounds(const Rect& srcRect) const {
return effect->filterBounds(srcRect);
}

std::shared_ptr<TextureProxy> RuntimeImageFilter::lockTextureProxy(
std::shared_ptr<Image> source, const Rect& clipBounds, const TPArgs& args,
const SamplingOptions& sampling) const {
std::shared_ptr<TextureProxy> RuntimeImageFilter::lockTextureProxy(std::shared_ptr<Image> source,
const Rect& clipBounds,
const TPArgs& args) const {
auto renderTarget = RenderTargetProxy::MakeFallback(
args.context, static_cast<int>(clipBounds.width()), static_cast<int>(clipBounds.height()),
source->isAlphaOnly(), effect->sampleCount(), args.mipmapped);
Expand All @@ -49,7 +49,7 @@ std::shared_ptr<TextureProxy> RuntimeImageFilter::lockTextureProxy(
// Request a texture proxy from the source image without mipmaps to save memory.
// It may be ignored if the source image has preset mipmaps.
TPArgs tpArgs(args.context, args.renderFlags, false);
auto textureProxy = source->lockTextureProxy(tpArgs, sampling);
auto textureProxy = source->lockTextureProxy(tpArgs);
if (textureProxy == nullptr) {
return nullptr;
}
Expand All @@ -60,7 +60,7 @@ std::shared_ptr<TextureProxy> RuntimeImageFilter::lockTextureProxy(
LOGE("RuntimeImageFilter::lockTextureProxy() extraInput %d is nullptr", i);
return nullptr;
}
textureProxy = input->lockTextureProxy(tpArgs, sampling);
textureProxy = input->lockTextureProxy(tpArgs);
if (textureProxy == nullptr) {
return nullptr;
}
Expand Down
4 changes: 2 additions & 2 deletions src/core/filters/RuntimeImageFilter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class RuntimeImageFilter : public ImageFilter {
Rect onFilterBounds(const Rect& srcRect) const override;

std::shared_ptr<TextureProxy> lockTextureProxy(std::shared_ptr<Image> source,
const Rect& clipBounds, const TPArgs& args,
const SamplingOptions& sampling) const override;
const Rect& clipBounds,
const TPArgs& args) const override;

std::unique_ptr<FragmentProcessor> asFragmentProcessor(std::shared_ptr<Image> source,
const FPArgs& args,
Expand Down
2 changes: 2 additions & 0 deletions src/core/images/BufferImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

namespace tgfx {
std::shared_ptr<Image> Image::MakeFrom(std::shared_ptr<ImageBuffer> buffer) {
TRACE_EVENT;
if (buffer == nullptr) {
return nullptr;
}
Expand All @@ -34,6 +35,7 @@ BufferImage::BufferImage(UniqueKey uniqueKey, std::shared_ptr<ImageBuffer> buffe
}

std::shared_ptr<TextureProxy> BufferImage::onLockTextureProxy(const TPArgs& args) const {
TRACE_EVENT;
return args.context->proxyProvider()->createTextureProxy(args.uniqueKey, imageBuffer,
args.mipmapped, args.renderFlags);
}
Expand Down
2 changes: 2 additions & 0 deletions src/core/images/DecoderImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
namespace tgfx {
std::shared_ptr<Image> DecoderImage::MakeFrom(UniqueKey uniqueKey,
std::shared_ptr<ImageDecoder> decoder) {
TRACE_EVENT;
if (decoder == nullptr) {
return nullptr;
}
Expand All @@ -37,6 +38,7 @@ DecoderImage::DecoderImage(UniqueKey uniqueKey, std::shared_ptr<ImageDecoder> de
}

std::shared_ptr<TextureProxy> DecoderImage::onLockTextureProxy(const TPArgs& args) const {
TRACE_EVENT;
return args.context->proxyProvider()->createTextureProxy(args.uniqueKey, decoder, args.mipmapped,
args.renderFlags);
}
Expand Down
12 changes: 8 additions & 4 deletions src/core/images/FilterImage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ namespace tgfx {
std::shared_ptr<Image> FilterImage::MakeFrom(std::shared_ptr<Image> source,
std::shared_ptr<ImageFilter> filter, Point* offset,
const Rect* clipRect) {
TRACE_EVENT;
if (source == nullptr) {
return nullptr;
}
Expand Down Expand Up @@ -72,13 +73,15 @@ std::shared_ptr<Image> FilterImage::onCloneWith(std::shared_ptr<Image> newSource
}

std::shared_ptr<Image> FilterImage::onMakeSubset(const Rect& subset) const {
TRACE_EVENT;
auto newBounds = subset;
newBounds.offset(bounds.x(), bounds.y());
return FilterImage::Wrap(source, newBounds, filter);
}

std::shared_ptr<Image> FilterImage::onMakeWithFilter(std::shared_ptr<ImageFilter> imageFilter,
Point* offset, const Rect* clipRect) const {
TRACE_EVENT;
if (imageFilter == nullptr) {
return nullptr;
}
Expand Down Expand Up @@ -111,18 +114,19 @@ std::shared_ptr<Image> FilterImage::onMakeWithFilter(std::shared_ptr<ImageFilter
return FilterImage::Wrap(source, filterBounds, std::move(composeFilter));
}

std::shared_ptr<TextureProxy> FilterImage::lockTextureProxy(const TPArgs& args,
const SamplingOptions& sampling) const {
std::shared_ptr<TextureProxy> FilterImage::lockTextureProxy(const TPArgs& args) const {
TRACE_EVENT;
auto inputBounds = Rect::MakeWH(source->width(), source->height());
auto filterBounds = filter->filterBounds(inputBounds);
return filter->lockTextureProxy(source, filterBounds, args, sampling);
return filter->lockTextureProxy(source, filterBounds, args);
}

std::unique_ptr<FragmentProcessor> FilterImage::asFragmentProcessor(const FPArgs& args,
TileMode tileModeX,
TileMode tileModeY,
const SamplingOptions& sampling,
const Matrix* uvMatrix) const {
TRACE_EVENT;
auto fpMatrix = concatUVMatrix(uvMatrix);
auto inputBounds = Rect::MakeWH(source->width(), source->height());
auto drawBounds = args.drawRect;
Expand All @@ -143,7 +147,7 @@ std::unique_ptr<FragmentProcessor> FilterImage::asFragmentProcessor(const FPArgs
auto mipmapped =
source->hasMipmaps() && NeedMipmaps(sampling, args.viewMatrix, AddressOf(fpMatrix));
TPArgs tpArgs(args.context, args.renderFlags, mipmapped);
auto textureProxy = filter->lockTextureProxy(source, dstBounds, tpArgs, sampling);
auto textureProxy = filter->lockTextureProxy(source, dstBounds, tpArgs);
if (textureProxy == nullptr) {
return nullptr;
}
Expand Down
7 changes: 1 addition & 6 deletions src/core/images/FilterImage.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ class FilterImage : public SubsetImage {
return false;
}

bool isFlat() const override {
return false;
}

protected:
FilterImage(std::shared_ptr<Image> source, const Rect& bounds,
std::shared_ptr<ImageFilter> filter);
Expand All @@ -62,8 +58,7 @@ class FilterImage : public SubsetImage {
std::shared_ptr<Image> onMakeWithFilter(std::shared_ptr<ImageFilter> filter, Point* offset,
const Rect* clipRect) const override;

std::shared_ptr<TextureProxy> lockTextureProxy(const TPArgs& args,
const SamplingOptions& sampling) const override;
std::shared_ptr<TextureProxy> lockTextureProxy(const TPArgs& args) const override;

std::unique_ptr<FragmentProcessor> asFragmentProcessor(const FPArgs& args, TileMode tileModeX,
TileMode tileModeY,
Expand Down
Loading