diff --git a/include/tgfx/core/Image.h b/include/tgfx/core/Image.h index f2f707c9..b1f69809 100644 --- a/include/tgfx/core/Image.h +++ b/include/tgfx/core/Image.h @@ -35,8 +35,7 @@ class FPArgs; class Context; class ImageFilter; class FragmentProcessor; -class ImageCodec; -class DrawOp; +class TextureProxy; /** * The Image class represents a two-dimensional array of pixels for drawing. These pixels can be @@ -285,12 +284,16 @@ class Image { virtual std::shared_ptr onMakeWithFilter(std::shared_ptr filter, Point* offset, const Rect* clipRect) const; + virtual std::shared_ptr lockTextureProxy(Context* context, + uint32_t renderFlags = 0) const; + virtual std::unique_ptr asFragmentProcessor( const FPArgs& args, TileMode tileModeX, TileMode tileModeY, const SamplingOptions& sampling, const Matrix* localMatrix) const = 0; friend class FragmentProcessor; friend class TransformImage; + friend class RGBAAAImage; friend class RasterImage; friend class ImageShader; }; diff --git a/include/tgfx/core/ImageFilter.h b/include/tgfx/core/ImageFilter.h index d2de748d..95794452 100644 --- a/include/tgfx/core/ImageFilter.h +++ b/include/tgfx/core/ImageFilter.h @@ -120,6 +120,11 @@ class ImageFilter { bool applyCropRect(const Rect& srcRect, Rect* dstRect, const Rect* clipBounds = nullptr) const; + std::unique_ptr makeFPFromFilteredImage(std::shared_ptr source, + const FPArgs& args, + const SamplingOptions& sampling, + const Matrix* localMatrix) const; + friend class DropShadowImageFilter; friend class ComposeImageFilter; friend class FilterImage; diff --git a/src/core/ImageFilter.cpp b/src/core/ImageFilter.cpp index 820b09a4..b8006641 100644 --- a/src/core/ImageFilter.cpp +++ b/src/core/ImageFilter.cpp @@ -20,6 +20,7 @@ #include "gpu/DrawingManager.h" #include "gpu/OpContext.h" #include "gpu/processors/FragmentProcessor.h" +#include "gpu/processors/TextureEffect.h" #include "gpu/proxies/RenderTargetProxy.h" namespace tgfx { @@ -37,9 +38,9 @@ std::shared_ptr ImageFilter::onFilterImage(Context* context, std::shared_ptr source, const Rect& filterBounds, bool mipmapped, uint32_t renderFlags) const { - auto renderTarget = RenderTargetProxy::Make(context, static_cast(filterBounds.width()), - static_cast(filterBounds.height()), - PixelFormat::RGBA_8888, 1, mipmapped); + auto renderTarget = RenderTargetProxy::MakeFallback( + context, static_cast(filterBounds.width()), static_cast(filterBounds.height()), + source->isAlphaOnly(), 1, mipmapped); if (renderTarget == nullptr) { return nullptr; } @@ -51,8 +52,8 @@ std::shared_ptr ImageFilter::onFilterImage(Context* context, if (!processor) { return nullptr; } - OpContext opContext(renderTarget); - opContext.fillWithFP(std::move(processor), Matrix::I(), true); + OpContext opContext(renderTarget, true); + opContext.fillWithFP(std::move(processor), Matrix::I()); return renderTarget->getTextureProxy(); } @@ -66,4 +67,28 @@ bool ImageFilter::applyCropRect(const Rect& srcRect, Rect* dstRect, const Rect* dstRect->roundOut(); return true; } + +std::unique_ptr ImageFilter::makeFPFromFilteredImage( + std::shared_ptr source, const FPArgs& args, const SamplingOptions& sampling, + const Matrix* localMatrix) const { + auto inputBounds = Rect::MakeWH(source->width(), source->height()); + auto clipBounds = args.drawRect; + if (localMatrix) { + clipBounds = localMatrix->mapRect(clipBounds); + } + Rect dstBounds = Rect::MakeEmpty(); + if (!applyCropRect(inputBounds, &dstBounds, &clipBounds)) { + return nullptr; + } + auto mipmapped = source->hasMipmaps() && sampling.mipmapMode != MipmapMode::None; + auto textureProxy = onFilterImage(args.context, source, dstBounds, mipmapped, args.renderFlags); + if (textureProxy == nullptr) { + return nullptr; + } + auto fpMatrix = Matrix::MakeTrans(-dstBounds.x(), -dstBounds.y()); + if (localMatrix != nullptr) { + fpMatrix.preConcat(*localMatrix); + } + return TextureEffect::Make(std::move(textureProxy), sampling, &fpMatrix); +} } // namespace tgfx diff --git a/src/filters/BlurImageFilter.cpp b/src/filters/BlurImageFilter.cpp index d22efad6..d947c7ae 100644 --- a/src/filters/BlurImageFilter.cpp +++ b/src/filters/BlurImageFilter.cpp @@ -95,8 +95,8 @@ void BlurImageFilter::draw(std::shared_ptr renderTarget, auto blurProcessor = DualBlurFragmentProcessor::Make(isDown ? DualBlurPassMode::Down : DualBlurPassMode::Up, std::move(imageProcessor), blurOffset, texelSize); - OpContext opContext(std::move(renderTarget)); - opContext.fillWithFP(std::move(blurProcessor), localMatrix, true); + OpContext opContext(std::move(renderTarget), true); + opContext.fillWithFP(std::move(blurProcessor), localMatrix); } Rect BlurImageFilter::onFilterBounds(const Rect& srcRect) const { @@ -109,9 +109,10 @@ std::shared_ptr BlurImageFilter::onFilterImage(Context* context, const Rect& filterBounds, bool mipmapped, uint32_t renderFlags) const { - auto lastRenderTarget = RenderTargetProxy::Make(context, static_cast(filterBounds.width()), - static_cast(filterBounds.height()), - PixelFormat::RGBA_8888, 1, mipmapped); + auto isAlphaOnly = source->isAlphaOnly(); + auto lastRenderTarget = RenderTargetProxy::MakeFallback( + context, static_cast(filterBounds.width()), static_cast(filterBounds.height()), + isAlphaOnly, 1, mipmapped); if (lastRenderTarget == nullptr) { return nullptr; } @@ -127,7 +128,8 @@ std::shared_ptr BlurImageFilter::onFilterImage(Context* context, } auto downWidth = std::max(static_cast(roundf(imageBounds.width() * downScaling)), 1); auto downHeight = std::max(static_cast(roundf(imageBounds.height() * downScaling)), 1); - auto renderTarget = RenderTargetProxy::Make(args.context, downWidth, downHeight); + auto renderTarget = + RenderTargetProxy::MakeFallback(args.context, downWidth, downHeight, isAlphaOnly); if (renderTarget == nullptr) { return nullptr; } @@ -148,24 +150,6 @@ std::shared_ptr BlurImageFilter::onFilterImage(Context* context, std::unique_ptr BlurImageFilter::asFragmentProcessor( std::shared_ptr source, const FPArgs& args, const SamplingOptions& sampling, const Matrix* localMatrix) const { - auto inputBounds = Rect::MakeWH(source->width(), source->height()); - auto clipBounds = args.drawRect; - if (localMatrix) { - clipBounds = localMatrix->mapRect(clipBounds); - } - Rect dstBounds = Rect::MakeEmpty(); - if (!applyCropRect(inputBounds, &dstBounds, &clipBounds)) { - return nullptr; - } - auto mipmapped = source->hasMipmaps() && sampling.mipmapMode != MipmapMode::None; - auto textureProxy = onFilterImage(args.context, source, dstBounds, mipmapped, args.renderFlags); - if (textureProxy == nullptr) { - return nullptr; - } - auto fpMatrix = Matrix::MakeTrans(-dstBounds.x(), -dstBounds.y()); - if (localMatrix != nullptr) { - fpMatrix.preConcat(*localMatrix); - } - return TextureEffect::Make(std::move(textureProxy), sampling, &fpMatrix); + return makeFPFromFilteredImage(source, args, sampling, localMatrix); } } // namespace tgfx diff --git a/src/filters/BlurImageFilter.h b/src/filters/BlurImageFilter.h index 4734ae34..e45a13b6 100644 --- a/src/filters/BlurImageFilter.h +++ b/src/filters/BlurImageFilter.h @@ -18,20 +18,15 @@ #pragma once +#include "gpu/proxies/RenderTargetProxy.h" #include "tgfx/core/ImageFilter.h" -#include "tgfx/gpu/Surface.h" namespace tgfx { class BlurImageFilter : public ImageFilter { public: BlurImageFilter(Point blurOffset, float downScaling, int iteration, TileMode tileMode); - private: - Point blurOffset; - float downScaling; - int iteration; - TileMode tileMode; - + protected: Rect onFilterBounds(const Rect& srcRect) const override; std::shared_ptr onFilterImage(Context* context, std::shared_ptr source, @@ -46,5 +41,11 @@ class BlurImageFilter : public ImageFilter { void draw(std::shared_ptr renderTarget, std::unique_ptr imageProcessor, const Rect& imageBounds, bool isDown) const; + + private: + Point blurOffset; + float downScaling; + int iteration; + TileMode tileMode; }; } // namespace tgfx diff --git a/src/gpu/OpContext.cpp b/src/gpu/OpContext.cpp index d077a33e..9b4cd067 100644 --- a/src/gpu/OpContext.cpp +++ b/src/gpu/OpContext.cpp @@ -21,8 +21,14 @@ #include "gpu/ops/FillRectOp.h" namespace tgfx { -void OpContext::fillWithFP(std::unique_ptr fp, const Matrix& localMatrix, - bool autoResolve) { +OpContext::~OpContext() { + if (autoResolve) { + auto drawingManager = renderTargetProxy->getContext()->drawingManager(); + drawingManager->addTextureResolveTask(renderTargetProxy); + } +} + +void OpContext::fillWithFP(std::unique_ptr fp, const Matrix& localMatrix) { fillRectWithFP(Rect::MakeWH(renderTargetProxy->width(), renderTargetProxy->height()), std::move(fp), localMatrix); if (autoResolve) { diff --git a/src/gpu/OpContext.h b/src/gpu/OpContext.h index 85747a96..6d27b782 100644 --- a/src/gpu/OpContext.h +++ b/src/gpu/OpContext.h @@ -28,16 +28,20 @@ namespace tgfx { */ class OpContext { public: - explicit OpContext(std::shared_ptr renderTargetProxy) - : renderTargetProxy(std::move(renderTargetProxy)) { + /** + * If autoResolve is true, the RenderTarget will be resolved after OpContext is destroyed. + */ + explicit OpContext(std::shared_ptr renderTargetProxy, bool autoResolve = false) + : renderTargetProxy(std::move(renderTargetProxy)), autoResolve(autoResolve) { } + ~OpContext(); + RenderTargetProxy* renderTarget() const { return renderTargetProxy.get(); } - void fillWithFP(std::unique_ptr fp, const Matrix& localMatrix, - bool autoResolve = false); + void fillWithFP(std::unique_ptr fp, const Matrix& localMatrix); void fillRectWithFP(const Rect& dstRect, std::unique_ptr fp, const Matrix& localMatrix); @@ -47,5 +51,6 @@ class OpContext { private: std::shared_ptr renderTargetProxy = nullptr; std::shared_ptr opsTask = nullptr; + bool autoResolve = false; }; } // namespace tgfx diff --git a/src/gpu/RenderContext.cpp b/src/gpu/RenderContext.cpp index 3472fdeb..c106ee06 100644 --- a/src/gpu/RenderContext.cpp +++ b/src/gpu/RenderContext.cpp @@ -404,12 +404,9 @@ std::shared_ptr RenderContext::getClipTexture(const Path& clip) { auto drawOp = TriangulatingPathOp::Make(Color::White(), clip, rasterizeMatrix, nullptr, renderFlags); drawOp->setAA(AAType::Coverage); - auto renderTarget = RenderTargetProxy::Make(getContext(), width, height, PixelFormat::ALPHA_8); + auto renderTarget = RenderTargetProxy::MakeFallback(getContext(), width, height, true); if (renderTarget == nullptr) { - renderTarget = RenderTargetProxy::Make(getContext(), width, height, PixelFormat::RGBA_8888); - if (renderTarget == nullptr) { - return nullptr; - } + return nullptr; } OpContext context(renderTarget); // Since the clip may not coverage the entire render target, we need to clear the render target diff --git a/src/gpu/RenderContext.h b/src/gpu/RenderContext.h index 9b592338..47f59dab 100644 --- a/src/gpu/RenderContext.h +++ b/src/gpu/RenderContext.h @@ -21,6 +21,7 @@ #include #include "core/DrawContext.h" #include "gpu/OpContext.h" +#include "gpu/ops/DrawOp.h" namespace tgfx { class RenderContext : public DrawContext { diff --git a/src/gpu/ops/ClearOp.h b/src/gpu/ops/ClearOp.h index 3de49c3a..e894b550 100644 --- a/src/gpu/ops/ClearOp.h +++ b/src/gpu/ops/ClearOp.h @@ -30,8 +30,7 @@ class ClearOp : public Op { void execute(RenderPass* renderPass) override; private: - explicit ClearOp(Color color, const Rect& scissor) - : Op(ClassID()), color(color), scissor(scissor) { + ClearOp(Color color, const Rect& scissor) : Op(ClassID()), color(color), scissor(scissor) { } bool onCombineIfPossible(Op* op) override; diff --git a/src/gpu/proxies/RenderTargetProxy.cpp b/src/gpu/proxies/RenderTargetProxy.cpp index f52c187b..1ef1232a 100644 --- a/src/gpu/proxies/RenderTargetProxy.cpp +++ b/src/gpu/proxies/RenderTargetProxy.cpp @@ -99,6 +99,16 @@ std::shared_ptr RenderTargetProxy::MakeFallback(Context* cont return nullptr; } +std::shared_ptr RenderTargetProxy::MakeFallback(Context* context, int width, + int height, bool isAlphaOnly, + int sampleCount, bool mipmapped, + ImageOrigin origin) { + auto formats = isAlphaOnly + ? std::vector{PixelFormat::ALPHA_8, PixelFormat::RGBA_8888} + : std::vector{PixelFormat::RGBA_8888}; + return MakeFallback(context, width, height, std::move(formats), sampleCount, mipmapped, origin); +} + std::shared_ptr RenderTargetProxy::Create(Context* context, int width, int height, PixelFormat format, int sampleCount, bool mipmapped, diff --git a/src/gpu/proxies/RenderTargetProxy.h b/src/gpu/proxies/RenderTargetProxy.h index ecae2382..1707d48b 100644 --- a/src/gpu/proxies/RenderTargetProxy.h +++ b/src/gpu/proxies/RenderTargetProxy.h @@ -72,6 +72,17 @@ class RenderTargetProxy : public ResourceProxy { bool mipmapped = false, ImageOrigin origin = ImageOrigin::TopLeft); + /** + * Creates a new RenderTargetProxy instance with the specified context, width, height, sample + * count, mipmap state, and origin. If `isAlphaOnly` is true, it will try to use the ALPHA_8 + * format and fall back to RGBA_8888 if not supported. Otherwise, it will use the RGBA_8888 + * format. + */ + static std::shared_ptr MakeFallback(Context* context, int width, int height, + bool isAlphaOnly, int sampleCount = 1, + bool mipmapped = false, + ImageOrigin origin = ImageOrigin::TopLeft); + /** * Returns the width of the render target. */ diff --git a/src/images/Image.cpp b/src/images/Image.cpp index e84f7953..887b71ce 100644 --- a/src/images/Image.cpp +++ b/src/images/Image.cpp @@ -17,8 +17,8 @@ ///////////////////////////////////////////////////////////////////////////////////////////////// #include "tgfx/core/Image.h" +#include "gpu/OpContext.h" #include "gpu/ProxyProvider.h" -#include "gpu/ops/FillRectOp.h" #include "images/BufferImage.h" #include "images/FilterImage.h" #include "images/GeneratorImage.h" @@ -27,7 +27,6 @@ #include "images/TextureImage.h" #include "tgfx/core/ImageCodec.h" #include "tgfx/core/Pixmap.h" -#include "tgfx/gpu/Surface.h" namespace tgfx { class PixelDataConverter : public ImageGenerator { @@ -156,13 +155,7 @@ std::shared_ptr Image::makeRasterized(float rasterizationScale, } std::shared_ptr Image::makeTextureImage(Context* context) const { - auto surface = Surface::Make(context, width(), height(), isAlphaOnly(), 1, hasMipmaps()); - if (surface == nullptr) { - return nullptr; - } - auto canvas = surface->getCanvas(); - canvas->drawImage(weakThis.lock(), 0, 0); - return surface->makeImageSnapshot(); + return TextureImage::Wrap(lockTextureProxy(context)); } std::shared_ptr Image::makeDecoded(Context* context) const { @@ -236,4 +229,22 @@ std::shared_ptr Image::makeRGBAAA(int displayWidth, int displayHeight, in std::shared_ptr Image::onMakeRGBAAA(int, int, int, int) const { return nullptr; } + +std::shared_ptr Image::lockTextureProxy(Context* context, + uint32_t renderFlags) const { + auto renderTarget = + RenderTargetProxy::MakeFallback(context, width(), height(), isAlphaOnly(), 1, hasMipmaps()); + if (renderTarget == nullptr) { + return nullptr; + } + auto drawRect = Rect::MakeWH(width(), height()); + FPArgs args(context, renderFlags, drawRect, Matrix::I()); + auto processor = FragmentProcessor::Make(weakThis.lock(), args, {}); + if (processor == nullptr) { + return nullptr; + } + OpContext opContext(renderTarget, true); + opContext.fillWithFP(std::move(processor), Matrix::I()); + return renderTarget->getTextureProxy(); +} } // namespace tgfx diff --git a/src/images/RGBAAAImage.cpp b/src/images/RGBAAAImage.cpp index 5c609e7f..ce218342 100644 --- a/src/images/RGBAAAImage.cpp +++ b/src/images/RGBAAAImage.cpp @@ -51,8 +51,7 @@ std::shared_ptr RGBAAAImage::onCloneWith(std::shared_ptr newSource std::unique_ptr RGBAAAImage::asFragmentProcessor( const FPArgs& args, TileMode, TileMode, const SamplingOptions& sampling, const Matrix* localMatrix) const { - auto proxy = std::static_pointer_cast(source)->lockTextureProxy(args.context, - args.renderFlags); + auto proxy = source->lockTextureProxy(args.context, args.renderFlags); auto matrix = concatLocalMatrix(localMatrix); return TextureEffect::MakeRGBAAA(std::move(proxy), alphaStart, sampling, AddressOf(matrix)); } diff --git a/src/images/RasterImage.cpp b/src/images/RasterImage.cpp index 08295f77..69f95185 100644 --- a/src/images/RasterImage.cpp +++ b/src/images/RasterImage.cpp @@ -113,8 +113,8 @@ std::shared_ptr RasterImage::onLockTextureProxy(Context* context, if (processor == nullptr) { return nullptr; } - OpContext opContext(renderTarget); - opContext.fillWithFP(std::move(processor), Matrix::I(), true); + OpContext opContext(renderTarget, true); + opContext.fillWithFP(std::move(processor), Matrix::I()); return textureProxy; } diff --git a/src/images/ResourceImage.cpp b/src/images/ResourceImage.cpp index bebac299..8efe3aac 100644 --- a/src/images/ResourceImage.cpp +++ b/src/images/ResourceImage.cpp @@ -35,11 +35,7 @@ std::shared_ptr ResourceImage::makeRasterized(float rasterizationScale, return Image::makeRasterized(rasterizationScale, sampling); } -std::shared_ptr ResourceImage::makeTextureImage(Context* context) const { - return TextureImage::Wrap(lockTextureProxy(context)); -} - -std::shared_ptr ResourceImage::lockTextureProxy(tgfx::Context* context, +std::shared_ptr ResourceImage::lockTextureProxy(Context* context, uint32_t renderFlags) const { if (context == nullptr) { return nullptr; diff --git a/src/images/ResourceImage.h b/src/images/ResourceImage.h index 417f5f6e..93f412f5 100644 --- a/src/images/ResourceImage.h +++ b/src/images/ResourceImage.h @@ -33,10 +33,6 @@ class ResourceImage : public Image { std::shared_ptr makeRasterized(float rasterizationScale = 1.0f, const SamplingOptions& sampling = {}) const override; - std::shared_ptr makeTextureImage(Context* context) const override; - - std::shared_ptr lockTextureProxy(Context* context, uint32_t renderFlags = 0) const; - protected: UniqueKey uniqueKey = {}; @@ -50,6 +46,9 @@ class ResourceImage : public Image { const SamplingOptions& sampling, const Matrix* localMatrix) const override; + std::shared_ptr lockTextureProxy(Context* context, + uint32_t renderFlags) const override final; + virtual std::shared_ptr onLockTextureProxy(Context* context, const UniqueKey& key, bool mipmapped, uint32_t renderFlags) const = 0; diff --git a/test/baseline/version.json b/test/baseline/version.json index 20c3bc90..d0055b65 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -3,7 +3,7 @@ "Clip": "d010fb8", "NothingToDraw": "d010fb8", "Picture": "72edd24", - "drawImage": "9208ab7", + "drawImage": "aa303d4", "filter_mode_linear": "d010fb8", "filter_mode_nearest": "d010fb8", "hardware_render_target_blend": "d010fb8",