From c36ea7b49ed6ff5e37bf0219ea8c35daf7760ab0 Mon Sep 17 00:00:00 2001 From: domrjchen Date: Wed, 22 Jan 2025 13:58:28 +0800 Subject: [PATCH] Remove the initial clip for Surface and fix rendering issues with inverse fill paths. --- include/tgfx/core/Canvas.h | 1 - src/core/Canvas.cpp | 6 ---- src/core/vectors/freetype/FTMask.cpp | 8 ++++- src/core/vectors/freetype/FTPath.cpp | 15 +-------- src/core/vectors/freetype/FTPath.h | 10 +++--- src/gpu/RenderContext.cpp | 49 ++++++++++++++++------------ src/gpu/Surface.cpp | 8 +---- test/baseline/version.json | 4 +-- test/src/CanvasTest.cpp | 1 + 9 files changed, 45 insertions(+), 57 deletions(-) diff --git a/include/tgfx/core/Canvas.h b/include/tgfx/core/Canvas.h index 86586512..7a236f6d 100644 --- a/include/tgfx/core/Canvas.h +++ b/include/tgfx/core/Canvas.h @@ -427,7 +427,6 @@ class Canvas { std::stack> stateStack; explicit Canvas(DrawContext* drawContext); - Canvas(DrawContext* drawContext, const Path& initClip); void drawClip(const FillStyle& style); void drawShape(std::shared_ptr shape, const MCState& state, const FillStyle& style); void drawImage(std::shared_ptr image, const SamplingOptions& sampling, const Paint* paint, diff --git a/src/core/Canvas.cpp b/src/core/Canvas.cpp index 37082ad1..ea6f2ff6 100644 --- a/src/core/Canvas.cpp +++ b/src/core/Canvas.cpp @@ -76,18 +76,12 @@ Canvas::Canvas(DrawContext* drawContext) mcState = std::make_unique(); } -Canvas::Canvas(DrawContext* drawContext, const Path& initClip) - : surface(drawContext->getSurface()), drawContext(drawContext) { - mcState = std::make_unique(initClip); -} - int Canvas::save() { stateStack.push(std::make_unique(*mcState)); return static_cast(stateStack.size()) - 1; } int Canvas::saveLayer(const Paint* paint) { - auto layer = std::make_unique(drawContext, paint); drawContext = layer->layerContext.get(); stateStack.push(std::make_unique(*mcState, std::move(layer))); diff --git a/src/core/vectors/freetype/FTMask.cpp b/src/core/vectors/freetype/FTMask.cpp index 6062a6aa..cb5eb482 100644 --- a/src/core/vectors/freetype/FTMask.cpp +++ b/src/core/vectors/freetype/FTMask.cpp @@ -106,12 +106,18 @@ void FTMask::onFillPath(const Path& path, const Matrix& matrix, bool antiAlias, totalMatrix.postScale(1, -1); totalMatrix.postTranslate(0, static_cast(pixelRef->height())); finalPath.transform(totalMatrix); + if (finalPath.isInverseFillType()) { + Path maskPath = {}; + maskPath.addRect(Rect::MakeWH(info.width(), info.height())); + finalPath.addPath(maskPath, PathOp::Intersect); + } auto bounds = finalPath.getBounds(); bounds.roundOut(); markContentDirty(bounds, true); FTPath ftPath = {}; finalPath.decompose(Iterator, &ftPath); - ftPath.setFillType(path.getFillType()); + auto fillType = finalPath.getFillType(); + ftPath.setEvenOdd(fillType == PathFillType::EvenOdd || fillType == PathFillType::InverseEvenOdd); auto outlines = ftPath.getOutlines(); auto ftLibrary = FTLibrary::Get(); if (!needsGammaCorrection) { diff --git a/src/core/vectors/freetype/FTPath.cpp b/src/core/vectors/freetype/FTPath.cpp index 6ccc56aa..e5926058 100644 --- a/src/core/vectors/freetype/FTPath.cpp +++ b/src/core/vectors/freetype/FTPath.cpp @@ -111,20 +111,7 @@ bool FTPath::finalizeOutline(FreetypeOutline* outline, size_t startPointIndex) c outline->outline.contours = reinterpret_cast(outline->contours.data()); outline->outline.n_points = static_cast(endPointIndex + 1); outline->outline.n_contours = static_cast(outline->contours.size()); - switch (getFillType()) { - case PathFillType::EvenOdd: - outline->outline.flags = FT_OUTLINE_EVEN_ODD_FILL; - break; - case PathFillType::InverseEvenOdd: - outline->outline.flags = FT_OUTLINE_EVEN_ODD_FILL | FT_OUTLINE_REVERSE_FILL; - break; - case PathFillType::Winding: - outline->outline.flags = FT_OUTLINE_NONE; - break; - case PathFillType::InverseWinding: - outline->outline.flags = FT_OUTLINE_REVERSE_FILL; - break; - } + outline->outline.flags = evenOdd ? FT_OUTLINE_EVEN_ODD_FILL : FT_OUTLINE_NONE; return true; } diff --git a/src/core/vectors/freetype/FTPath.h b/src/core/vectors/freetype/FTPath.h index f43531c7..1f7b8255 100644 --- a/src/core/vectors/freetype/FTPath.h +++ b/src/core/vectors/freetype/FTPath.h @@ -30,12 +30,12 @@ struct FreetypeOutline { class FTPath { public: - PathFillType getFillType() const { - return fillType; + bool isEvenOdd() const { + return evenOdd; } - void setFillType(PathFillType type) { - fillType = type; + void setEvenOdd(bool value) { + evenOdd = value; } bool isEmpty() const; @@ -53,6 +53,6 @@ class FTPath { std::vector verbs = {}; std::vector tags = {}; std::vector contours = {}; - PathFillType fillType = PathFillType::Winding; + bool evenOdd = false; }; } // namespace tgfx diff --git a/src/gpu/RenderContext.cpp b/src/gpu/RenderContext.cpp index 29131806..badf36c2 100644 --- a/src/gpu/RenderContext.cpp +++ b/src/gpu/RenderContext.cpp @@ -121,6 +121,16 @@ void RenderContext::drawRRect(const RRect& rRect, const MCState& state, const Fi addDrawOp(std::move(drawOp), rRect.rect, state, style); } +static Rect ToLocalBounds(const Rect& bounds, const Matrix& viewMatrix) { + Matrix invertMatrix = {}; + if (!viewMatrix.invert(&invertMatrix)) { + return Rect::MakeEmpty(); + } + auto localBounds = bounds; + invertMatrix.mapRect(&localBounds); + return localBounds; +} + void RenderContext::drawShape(std::shared_ptr shape, const MCState& state, const FillStyle& style) { DEBUG_ASSERT(shape != nullptr); @@ -128,8 +138,9 @@ void RenderContext::drawShape(std::shared_ptr shape, const MCState& state if (maxScale <= 0.0f) { return; } - auto bounds = shape->getBounds(maxScale); auto clipBounds = getClipBounds(state.clip); + auto bounds = shape->isInverseFillType() ? ToLocalBounds(clipBounds, state.matrix) + : shape->getBounds(maxScale); auto drawOp = ShapeDrawOp::Make(style.color.premultiply(), std::move(shape), state.matrix, clipBounds); addDrawOp(std::move(drawOp), bounds, state, style); @@ -208,14 +219,10 @@ void RenderContext::drawLayer(std::shared_ptr picture, std::shared_ptr< const MCState& state, const FillStyle& style) { DEBUG_ASSERT(style.shader == nullptr); Matrix viewMatrix = {}; - auto bounds = Rect::MakeEmpty(); + Rect bounds = {}; if (filter || style.maskFilter) { if (picture->hasUnboundedFill()) { - Matrix invertMatrix = {}; - if (state.matrix.invert(&invertMatrix)) { - bounds = getClipBounds(state.clip); - invertMatrix.mapRect(&bounds); - } + bounds = ToLocalBounds(getClipBounds(state.clip), state.matrix); } else { bounds = picture->getBounds(); } @@ -305,22 +312,22 @@ static void FlipYIfNeeded(Rect* rect, const RenderTargetProxy* renderTarget) { std::pair, bool> RenderContext::getClipRect(const Path& clip, const Rect* deviceBounds) { auto rect = Rect::MakeEmpty(); - if (clip.isRect(&rect)) { - if (deviceBounds != nullptr && !rect.intersect(*deviceBounds)) { - return {{}, false}; - } - auto renderTarget = opContext->renderTarget(); - FlipYIfNeeded(&rect, renderTarget); - if (IsPixelAligned(rect)) { - rect.round(); - if (rect != Rect::MakeWH(renderTarget->width(), renderTarget->height())) { - return {rect, true}; - } - return {Rect::MakeEmpty(), false}; + if (clip.isInverseFillType() || !clip.isRect(&rect)) { + return {{}, false}; + } + if (deviceBounds != nullptr && !rect.intersect(*deviceBounds)) { + return {{}, false}; + } + auto renderTarget = opContext->renderTarget(); + FlipYIfNeeded(&rect, renderTarget); + if (IsPixelAligned(rect)) { + rect.round(); + if (rect != Rect::MakeWH(renderTarget->width(), renderTarget->height())) { + return {rect, true}; } - return {rect, false}; + return {Rect::MakeEmpty(), false}; } - return {{}, false}; + return {rect, false}; } std::shared_ptr RenderContext::getClipTexture(const Path& clip, AAType aaType) { diff --git a/src/gpu/Surface.cpp b/src/gpu/Surface.cpp index 90d78519..20bdf822 100644 --- a/src/gpu/Surface.cpp +++ b/src/gpu/Surface.cpp @@ -131,16 +131,10 @@ HardwareBufferRef Surface::getHardwareBuffer() { return texture->getHardwareBuffer(); } -static Path GetInitClip(int width, int height) { - Path path = {}; - path.addRect(Rect::MakeWH(width, height)); - return path; -} - Canvas* Surface::getCanvas() { if (canvas == nullptr) { renderContext = new RenderContext(this); - canvas = new Canvas(renderContext, GetInitClip(width(), height())); + canvas = new Canvas(renderContext); } return canvas; } diff --git a/test/baseline/version.json b/test/baseline/version.json index 461fa831..209132b9 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -20,7 +20,7 @@ "Path_addArc_reversed7": "4802e56", "Path_addArc_reversed8": "4802e56", "Path_complex": "e031f25", - "Picture": "002c391", + "Picture": "59ca683", "PictureImage": "3b0ec3b", "PictureImage_Path": "2212c4e", "PictureImage_Text": "b062b9a", @@ -39,7 +39,7 @@ "filter_mode_nearest": "d010fb8", "hardware_render_target_blend": "d010fb8", "inversePath_rect": "6fd4617", - "inversePath_text": "3b0ec3b", + "inversePath_text": "59ca683", "merge_draw_call_rect": "d010fb8", "merge_draw_call_rrect": "d010fb8", "merge_draw_clear_op": "d010fb8", diff --git a/test/src/CanvasTest.cpp b/test/src/CanvasTest.cpp index 4a3a4c52..3137571b 100644 --- a/test/src/CanvasTest.cpp +++ b/test/src/CanvasTest.cpp @@ -192,6 +192,7 @@ TGFX_TEST(CanvasTest, merge_draw_clear_op) { auto surface = Surface::Make(context, width, height); auto canvas = surface->getCanvas(); canvas->clear(Color::White()); + canvas->clipRect(Rect::MakeWH(width, height)); canvas->save(); Path path; path.addRect(Rect::MakeXYWH(0.f, 0.f, 10.f, 10.f));