From 3b66bdcc02817512cfcc747e346908a66db8164e Mon Sep 17 00:00:00 2001 From: domrjchen Date: Thu, 9 Jan 2025 11:34:58 +0800 Subject: [PATCH] Add a strokeOnTop property to the ShapeLayer class. --- include/tgfx/layers/Layer.h | 25 ++-- include/tgfx/layers/ShapeLayer.h | 16 ++- src/layers/DrawArgs.h | 7 +- src/layers/Layer.cpp | 167 +++++++++++++-------------- src/layers/ShapeLayer.cpp | 34 +++++- src/layers/contents/ShapeContent.cpp | 67 ++++++----- src/layers/contents/ShapeContent.h | 15 +-- test/baseline/version.json | 2 + test/src/LayerTest.cpp | 66 ++++++++--- 9 files changed, 231 insertions(+), 168 deletions(-) diff --git a/include/tgfx/layers/Layer.h b/include/tgfx/layers/Layer.h index 1f26ba09..0909f4e8 100644 --- a/include/tgfx/layers/Layer.h +++ b/include/tgfx/layers/Layer.h @@ -31,6 +31,7 @@ namespace tgfx { class DisplayList; class DrawArgs; +struct LayerStyleSource; /** * The base class for all layers that can be placed on the display list. The layer class includes @@ -505,14 +506,17 @@ class Layer { virtual std::unique_ptr onUpdateContent(); /** - * Draws the layer contour on the given canvas. The layer contour is the outline of the layer - * content, used for applying layer styles that need the contour. By default, this calls the - * draw() method with the layer content and the given paint object. + * Draws the layer content and its children on the given canvas. By default, this method draws the + * layer content first, followed by the children. Subclasses can override this method to change + * the drawing order or the way the layer content is drawn. * @param content The layer content to draw. This can be nullptr. - * @param canvas The canvas to draw the layer contour on. - * @param paint The paint object used to draw the layer contour. + * @param canvas The canvas to draw the layer content on. + * @param alpha The alpha transparency value used for drawing the layer content. + * @param forContour Whether to draw the layer content for the contour. + * @param drawChildren A callback function that draws the children of the layer. */ - virtual void drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const; + virtual void drawContents(LayerContent* content, Canvas* canvas, float alpha, bool forContour, + const std::function& drawChildren) const; /** * Attachs a property to this layer. @@ -544,7 +548,7 @@ class Layer { LayerContent* getContent(); - Paint getLayerPaint(float alpha, BlendMode blendMode); + Paint getLayerPaint(float alpha, BlendMode blendMode = BlendMode::SrcOver) const; std::shared_ptr getImageFilter(float contentScale); @@ -557,12 +561,13 @@ class Layer { void drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, BlendMode blendMode); - void drawContents(const DrawArgs& args, Canvas* canvas, float alpha); + void drawDirectly(const DrawArgs& args, Canvas* canvas, float alpha); void drawChildren(const DrawArgs& args, Canvas* canvas, float alpha); - void drawLayerStyles(Canvas* canvas, std::shared_ptr content, float contentScale, - std::shared_ptr contour, const Point& contourOffset, float alpha, + std::unique_ptr getLayerStyleSource(const DrawArgs& args, const Matrix& matrix); + + void drawLayerStyles(Canvas* canvas, float alpha, const LayerStyleSource* source, LayerStylePosition position); bool getLayersUnderPointInternal(float x, float y, std::vector>* results); diff --git a/include/tgfx/layers/ShapeLayer.h b/include/tgfx/layers/ShapeLayer.h index 38892a8c..041dccd0 100644 --- a/include/tgfx/layers/ShapeLayer.h +++ b/include/tgfx/layers/ShapeLayer.h @@ -273,12 +273,24 @@ class ShapeLayer : public Layer { */ void setStrokeAlign(StrokeAlign align); + /** + * Indicates whether strokes are drawn on top of child layers and layer styles. Normally, strokes + * are drawn above fills but below child layers. If true, strokes are drawn above all child layers + * and layer styles. The default value is false. + */ + bool strokeOnTop() const { + return _strokeOnTop; + } + + void setStrokeOnTop(bool value); + protected: ShapeLayer() = default; std::unique_ptr onUpdateContent() override; - void drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const override; + void drawContents(LayerContent* content, Canvas* canvas, float alpha, bool forContour, + const std::function& drawChildren) const override; private: std::shared_ptr _shape = nullptr; @@ -290,7 +302,9 @@ class ShapeLayer : public Layer { float _strokeStart = 0.0f; float _strokeEnd = 1.0f; StrokeAlign _strokeAlign = StrokeAlign::Center; + bool _strokeOnTop = false; + Paint getPaint(float alpha) const; std::shared_ptr createStrokeShape() const; }; } // namespace tgfx diff --git a/src/layers/DrawArgs.h b/src/layers/DrawArgs.h index 671ab2d4..f0e52a02 100644 --- a/src/layers/DrawArgs.h +++ b/src/layers/DrawArgs.h @@ -29,22 +29,21 @@ class DrawArgs { DrawArgs() = default; DrawArgs(Context* context, uint32_t renderFlags, bool cleanDirtyFlags = false, - bool excludeEffects = false, bool drawContour = false) + bool excludeEffects = false, bool forContour = false) : context(context), renderFlags(renderFlags), cleanDirtyFlags(cleanDirtyFlags), - excludeEffects(excludeEffects), drawContour(drawContour) { + excludeEffects(excludeEffects), forContour(forContour) { } // The GPU context to be used during the drawing process. Note: this could be nullptr. Context* context = nullptr; // Render flags to be used during the drawing process. uint32_t renderFlags = 0; - // Whether to clean the dirty flags of the associated Layer during the drawing process. bool cleanDirtyFlags = false; // Whether to exclude effects during the drawing process. bool excludeEffects = false; // Whether to draw the contour of the associated Layer during the drawing process. If true, the // contour will be drawn instead of the content. - bool drawContour = false; + bool forContour = false; }; } // namespace tgfx diff --git a/src/layers/Layer.cpp b/src/layers/Layer.cpp index 1b0b6095..eecb9633 100644 --- a/src/layers/Layer.cpp +++ b/src/layers/Layer.cpp @@ -32,17 +32,13 @@ namespace tgfx { static std::atomic_bool AllowsEdgeAntialiasing = true; static std::atomic_bool AllowsGroupOpacity = false; -static bool ChildrenContainEffects(const Layer* layer) { - for (auto child : layer->children()) { - if (!child->layerStyles().empty() || !child->filters().empty()) { - return true; - } - if (ChildrenContainEffects(child.get())) { - return true; - } - } - return false; -} +struct LayerStyleSource { + float contentScale = 1.0f; + std::shared_ptr content = nullptr; + Point contentOffset = Point::Zero(); + std::shared_ptr contour = nullptr; + Point contourOffset = Point::Zero(); +}; static std::shared_ptr CreatePicture( const DrawArgs& args, float contentScale, @@ -604,8 +600,8 @@ LayerContent* Layer::getContent() { return layerContent.get(); } -Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) { - Paint paint; +Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) const { + Paint paint = {}; paint.setAntiAlias(bitFields.allowsEdgeAntialiasing); paint.setAlpha(alpha); paint.setBlendMode(blendMode); @@ -658,7 +654,7 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con return nullptr; } auto picture = CreatePicture(args, contentScale, [this](const DrawArgs& args, Canvas* canvas) { - drawContents(args, canvas, 1.0f); + drawDirectly(args, canvas, 1.0f); }); if (!picture) { return nullptr; @@ -688,7 +684,7 @@ void Layer::drawLayer(const DrawArgs& args, Canvas* canvas, float alpha, BlendMo drawOffscreen(args, canvas, alpha, blendMode); } else { // draw directly - drawContents(args, canvas, alpha); + drawDirectly(args, canvas, alpha); } } @@ -740,15 +736,12 @@ void Layer::drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, Ble } auto picture = CreatePicture(args, contentScale, [this](const DrawArgs& args, Canvas* canvas) { - drawContents(args, canvas, 1.0f); + drawDirectly(args, canvas, 1.0f); }); if (picture == nullptr) { return; } - Paint paint = {}; - paint.setAntiAlias(bitFields.allowsEdgeAntialiasing); - paint.setAlpha(alpha); - paint.setBlendMode(blendMode); + auto paint = getLayerPaint(alpha, blendMode); paint.setMaskFilter(getMaskFilter(args, contentScale)); if (!args.excludeEffects) { paint.setImageFilter(getImageFilter(contentScale)); @@ -757,76 +750,25 @@ void Layer::drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, Ble canvas->drawPicture(std::move(picture), &matrix, &paint); } -void Layer::drawContents(const DrawArgs& args, Canvas* canvas, float alpha) { - auto drawLayerContents = [this, alpha](const DrawArgs& args, Canvas* canvas) { - auto content = getContent(); - if (args.drawContour) { - drawContour(content, canvas, getLayerPaint(alpha, BlendMode::SrcOver)); - } else if (content != nullptr) { - content->draw(canvas, getLayerPaint(alpha, BlendMode::SrcOver)); - } +void Layer::drawDirectly(const DrawArgs& args, Canvas* canvas, float alpha) { + auto layerStyleSource = getLayerStyleSource(args, canvas->getMatrix()); + if (layerStyleSource) { + drawLayerStyles(canvas, alpha, layerStyleSource.get(), LayerStylePosition::Below); + } + drawContents(getContent(), canvas, alpha, args.forContour, [&]() { drawChildren(args, canvas, alpha); - if (args.cleanDirtyFlags) { - bitFields.childrenDirty = false; + if (layerStyleSource) { + drawLayerStyles(canvas, alpha, layerStyleSource.get(), LayerStylePosition::Above); } - }; - - if (_layerStyles.empty() || args.excludeEffects) { - drawLayerContents(args, canvas); - return; - } - - auto contentScale = canvas->getMatrix().getMaxScale(); - if (FloatNearlyZero(contentScale)) { - return; - } - - AutoCanvasRestore autoRestore(canvas); - canvas->scale(1.f / contentScale, 1.f / contentScale); - Point offset = Point::Zero(); - std::shared_ptr source = nullptr; - auto picture = CreatePicture(args, contentScale, drawLayerContents); - if (picture == nullptr) { - return; - } - if (!bitFields.excludeChildEffectsInLayerStyle || !ChildrenContainEffects(this)) { - source = CreatePictureImage(picture, &offset); - } else { - auto newArgs = - DrawArgs(args.context, args.renderFlags | RenderFlags::DisableCache, false, true); - auto contentsWithoutEffects = CreatePicture(newArgs, contentScale, drawLayerContents); - source = CreatePictureImage(contentsWithoutEffects, &offset); - } - if (source == nullptr) { - return; - } - std::shared_ptr contour = nullptr; - Point contourOffset = offset; - auto needContour = - std::any_of(_layerStyles.begin(), _layerStyles.end(), - [](const auto& layerStyle) { return layerStyle->requireLayerContour(); }); - if (needContour) { - // Child effects are always excluded when drawing the layer contour. - DrawArgs newArgs = - DrawArgs(args.context, args.renderFlags | RenderFlags::DisableCache, false, true, true); - auto contourPicture = CreatePicture(newArgs, contentScale, drawLayerContents); - contour = CreatePictureImage(contourPicture, &contourOffset); - contourOffset -= offset; - } - - canvas->translate(offset.x, offset.y); - drawLayerStyles(canvas, source, contentScale, contour, contourOffset, alpha, - LayerStylePosition::Below); - auto matrix = Matrix::MakeTrans(-offset.x, -offset.y); - canvas->drawPicture(std::move(picture), &matrix, nullptr); - drawLayerStyles(canvas, source, contentScale, contour, contourOffset, alpha, - LayerStylePosition::Above); + }); } -void Layer::drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const { +void Layer::drawContents(LayerContent* content, Canvas* canvas, float alpha, bool, + const std::function& drawChildren) const { if (content != nullptr) { - content->draw(canvas, paint); + content->draw(canvas, getLayerPaint(alpha)); } + drawChildren(); } void Layer::drawChildren(const DrawArgs& args, Canvas* canvas, float alpha) { @@ -841,20 +783,69 @@ void Layer::drawChildren(const DrawArgs& args, Canvas* canvas, float alpha) { } child->drawLayer(args, canvas, child->_alpha * alpha, child->_blendMode); } + if (args.cleanDirtyFlags) { + bitFields.childrenDirty = false; + } +} + +std::unique_ptr Layer::getLayerStyleSource(const DrawArgs& args, + const Matrix& matrix) { + if (_layerStyles.empty() || args.excludeEffects) { + return nullptr; + } + auto contentScale = matrix.getMaxScale(); + if (FloatNearlyZero(contentScale)) { + return nullptr; + } + + auto drawLayerContents = [this](const DrawArgs& drawArgs, Canvas* canvas) { + drawContents(getContent(), canvas, 1.0f, drawArgs.forContour, + [&]() { drawChildren(drawArgs, canvas, 1.0f); }); + }; + + DrawArgs drawArgs(args.context, args.renderFlags | RenderFlags::DisableCache, false, + bitFields.excludeChildEffectsInLayerStyle); + auto contentPicture = CreatePicture(drawArgs, contentScale, drawLayerContents); + auto contentOffset = Point::Zero(); + auto content = CreatePictureImage(contentPicture, &contentOffset); + if (content == nullptr) { + return nullptr; + } + auto source = std::make_unique(); + source->contentScale = contentScale; + source->content = std::move(content); + source->contentOffset = contentOffset; + auto needContour = + std::any_of(_layerStyles.begin(), _layerStyles.end(), + [](const auto& layerStyle) { return layerStyle->requireLayerContour(); }); + if (needContour) { + // Child effects are always excluded when drawing the layer contour. + drawArgs.excludeEffects = true; + drawArgs.forContour = true; + auto contourPicture = CreatePicture(drawArgs, contentScale, drawLayerContents); + source->contour = CreatePictureImage(contourPicture, &source->contourOffset); + } + return source; } -void Layer::drawLayerStyles(Canvas* canvas, std::shared_ptr content, float contentScale, - std::shared_ptr contour, const Point& contourOffset, float alpha, +void Layer::drawLayerStyles(Canvas* canvas, float alpha, const LayerStyleSource* source, LayerStylePosition position) { + DEBUG_ASSERT(source != nullptr && !FloatNearlyZero(source->contentScale)); + auto matrix = Matrix::MakeScale(1.f / source->contentScale, 1.f / source->contentScale); + matrix.preTranslate(source->contentOffset.x, source->contentOffset.y); + auto& contour = source->contour; + auto contourOffset = source->contourOffset - source->contentOffset; for (const auto& layerStyle : _layerStyles) { if (layerStyle->position() != position) { continue; } AutoCanvasRestore autoRestore(canvas); + canvas->concat(matrix); if (layerStyle->requireLayerContour() && contour != nullptr) { - layerStyle->drawWithContour(canvas, content, contentScale, contour, contourOffset, alpha); + layerStyle->drawWithContour(canvas, source->content, source->contentScale, contour, + contourOffset, alpha); } else { - layerStyle->draw(canvas, content, contentScale, alpha); + layerStyle->draw(canvas, source->content, source->contentScale, alpha); } } } diff --git a/src/layers/ShapeLayer.cpp b/src/layers/ShapeLayer.cpp index 1e41bb21..0b3a302f 100644 --- a/src/layers/ShapeLayer.cpp +++ b/src/layers/ShapeLayer.cpp @@ -226,6 +226,14 @@ void ShapeLayer::setStrokeAlign(StrokeAlign align) { invalidateContent(); } +void ShapeLayer::setStrokeOnTop(bool value) { + if (_strokeOnTop == value) { + return; + } + _strokeOnTop = value; + invalidateContent(); +} + ShapeLayer::~ShapeLayer() { for (auto& style : _fillStyles) { detachProperty(style.get()); @@ -267,14 +275,30 @@ std::unique_ptr ShapeLayer::onUpdateContent() { fillPaintCount); } -void ShapeLayer::drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const { +void ShapeLayer::drawContents(LayerContent* content, Canvas* canvas, float alpha, bool forContour, + const std::function& drawChildren) const { auto shapeContent = static_cast(content); - if (shapeContent == nullptr || !shapeContent->hasFills()) { - canvas->drawShape(_shape, paint); + if (!shapeContent || !shapeContent->drawFills(canvas, getPaint(alpha), forContour)) { + if (forContour) { + canvas->drawShape(_shape, getPaint(alpha)); + } } - if (shapeContent != nullptr) { - shapeContent->drawContour(canvas, paint); + if (_strokeOnTop) { + drawChildren(); } + if (shapeContent) { + shapeContent->drawStrokes(canvas, getPaint(alpha), forContour); + } + if (!_strokeOnTop) { + drawChildren(); + } +} + +Paint ShapeLayer::getPaint(float alpha) const { + Paint paint = {}; + paint.setAntiAlias(allowsEdgeAntialiasing()); + paint.setAlpha(alpha); + return paint; } std::shared_ptr ShapeLayer::createStrokeShape() const { diff --git a/src/layers/contents/ShapeContent.cpp b/src/layers/contents/ShapeContent.cpp index 473a8c5b..09a56da0 100644 --- a/src/layers/contents/ShapeContent.cpp +++ b/src/layers/contents/ShapeContent.cpp @@ -31,48 +31,47 @@ ShapeContent::ShapeContent(std::shared_ptr fill, std::shared_ptr s } } void ShapeContent::draw(Canvas* canvas, const Paint& paint) const { - size_t index = 0; - for (auto& shapePaint : paintList) { - auto shape = index++ < fillPaintCount ? fillShape : strokeShape; - drawShape(canvas, paint, std::move(shape), shapePaint); - } + drawFills(canvas, paint, false); + drawStrokes(canvas, paint, false); } -void ShapeContent::drawShape(Canvas* canvas, const Paint& paint, std::shared_ptr shape, - const ShapePaint& shapePaint) const { - auto drawPaint = paint; - drawPaint.setAlpha(paint.getAlpha() * shapePaint.alpha); - // The blend mode in the paint is always SrcOver, use our own blend mode instead. - drawPaint.setBlendMode(shapePaint.blendMode); - drawPaint.setShader(shapePaint.shader); - canvas->drawShape(std::move(shape), drawPaint); +static void DrawShape(Canvas* canvas, const Paint& paint, std::shared_ptr shape, + bool forContour, const std::vector::const_iterator& begin, + const std::vector::const_iterator& end) { + if (forContour) { + auto hasNonImageShader = std::any_of( + begin, end, [](const auto& shapePaint) { return !shapePaint.shader->isAImage(); }); + if (hasNonImageShader) { + canvas->drawShape(std::move(shape), paint); + return; + } + } + for (auto iter = begin; iter != end; iter++) { + auto drawPaint = paint; + drawPaint.setAlpha(paint.getAlpha() * iter->alpha); + // The blend mode in the paint is always SrcOver, use our own blend mode instead. + drawPaint.setBlendMode(iter->blendMode); + drawPaint.setShader(iter->shader); + canvas->drawShape(shape, drawPaint); + } } -void ShapeContent::drawContour(Canvas* canvas, const Paint& paint) const { - if (fillShape != nullptr) { - drawShapeContour(canvas, paint, fillShape, paintList.begin(), - paintList.begin() + static_cast(fillPaintCount)); - } - if (strokeShape != nullptr) { - drawShapeContour(canvas, paint, strokeShape, - paintList.begin() + static_cast(fillPaintCount), - paintList.end()); +bool ShapeContent::drawFills(Canvas* canvas, const Paint& paint, bool forContour) const { + if (!fillShape) { + return false; } + DrawShape(canvas, paint, fillShape, forContour, paintList.begin(), + paintList.begin() + static_cast(fillPaintCount)); + return true; } -void ShapeContent::drawShapeContour(Canvas* canvas, const Paint& paint, - std::shared_ptr shape, - std::vector::const_iterator begin, - std::vector::const_iterator end) const { - auto allShadersAreImages = std::none_of( - begin, end, [](const auto& shapePaint) { return !shapePaint.shader->isAImage(); }); - if (allShadersAreImages) { - for (auto it = begin; it != end; it++) { - drawShape(canvas, paint, shape, *it); - } - } else { - canvas->drawShape(shape, paint); +bool ShapeContent::drawStrokes(Canvas* canvas, const Paint& paint, bool forContour) const { + if (!strokeShape) { + return false; } + DrawShape(canvas, paint, strokeShape, forContour, + paintList.begin() + static_cast(fillPaintCount), paintList.end()); + return true; } bool ShapeContent::hitTestPoint(float localX, float localY, bool pixelHitTest) { diff --git a/src/layers/contents/ShapeContent.h b/src/layers/contents/ShapeContent.h index 7b0cc765..5680a16c 100644 --- a/src/layers/contents/ShapeContent.h +++ b/src/layers/contents/ShapeContent.h @@ -41,13 +41,11 @@ class ShapeContent : public LayerContent { return bounds; } - bool hasFills() const { - return fillPaintCount > 0; - } - void draw(Canvas* canvas, const Paint& paint) const override; - void drawContour(Canvas* canvas, const Paint& paint) const; + bool drawFills(Canvas* canvas, const Paint& paint, bool forContour) const; + + bool drawStrokes(Canvas* canvas, const Paint& paint, bool forContour) const; bool hitTestPoint(float localX, float localY, bool pixelHitTest) override; @@ -57,12 +55,5 @@ class ShapeContent : public LayerContent { std::shared_ptr strokeShape = nullptr; std::vector paintList = {}; size_t fillPaintCount = 0; - - void drawShape(Canvas* canvas, const Paint& paint, std::shared_ptr shape, - const ShapePaint& shapePaint) const; - - void drawShapeContour(Canvas* canvas, const Paint& paint, std::shared_ptr shape, - std::vector::const_iterator begin, - std::vector::const_iterator end) const; }; } // namespace tgfx diff --git a/test/baseline/version.json b/test/baseline/version.json index 93e9451d..3293ca28 100644 --- a/test/baseline/version.json +++ b/test/baseline/version.json @@ -106,6 +106,8 @@ "Layer_hitTestPointNested": "b062b9a", "ModeColorFilter": "43cd416", "PassThoughAndNormal": "43cd416", + "StrokeOnTop_Off": "02cad7c", + "StrokeOnTop_On": "02cad7c", "draw_shape": "67dfa72", "draw_solid": "b4a1231", "draw_text": "b062b9a", diff --git a/test/src/LayerTest.cpp b/test/src/LayerTest.cpp index ca81fef5..e998f9fa 100644 --- a/test/src/LayerTest.cpp +++ b/test/src/LayerTest.cpp @@ -444,12 +444,12 @@ TGFX_TEST(LayerTest, shapeLayer) { auto layer = Layer::Make(); displayList->root()->addChild(layer); for (int i = 0; i < 3; i++) { - auto shaperLayer = ShapeLayer::Make(); + auto shapeLayer = ShapeLayer::Make(); auto rect = Rect::MakeXYWH(10, 10 + 100 * i, 140, 80); Path path = {}; path.addRect(rect); - shaperLayer->setPath(path); - shaperLayer->removeFillStyles(); + shapeLayer->setPath(path); + shapeLayer->removeFillStyles(); std::shared_ptr fillStyle = nullptr; switch (i) { case 0: @@ -470,26 +470,26 @@ TGFX_TEST(LayerTest, shapeLayer) { break; } fillStyle->setAlpha(0.8f); - shaperLayer->addFillStyle(fillStyle); + shapeLayer->addFillStyle(fillStyle); // stroke style - shaperLayer->setLineWidth(10.0f); - shaperLayer->setLineCap(LineCap::Butt); - shaperLayer->setLineJoin(LineJoin::Miter); + shapeLayer->setLineWidth(10.0f); + shapeLayer->setLineCap(LineCap::Butt); + shapeLayer->setLineJoin(LineJoin::Miter); auto strokeStyle = SolidColor::Make(Color::Red()); - shaperLayer->setStrokeStyle(strokeStyle); + shapeLayer->setStrokeStyle(strokeStyle); strokeStyle = SolidColor::Make(Color::Green()); strokeStyle->setAlpha(0.5f); strokeStyle->setBlendMode(BlendMode::Lighten); - shaperLayer->addStrokeStyle(strokeStyle); + shapeLayer->addStrokeStyle(strokeStyle); if (i != 2) { std::vector dashPattern = {10.0f, 10.0f}; - shaperLayer->setLineDashPattern(dashPattern); - shaperLayer->setLineDashPhase(5.0f); + shapeLayer->setLineDashPattern(dashPattern); + shapeLayer->setLineDashPhase(5.0f); } - shaperLayer->setStrokeAlign(static_cast(i)); - layer->addChild(shaperLayer); - auto shapeLayerRect = shaperLayer->getBounds(); + shapeLayer->setStrokeAlign(static_cast(i)); + layer->addChild(shapeLayer); + auto shapeLayerRect = shapeLayer->getBounds(); switch (i) { case 0: EXPECT_EQ(shapeLayerRect, Rect::MakeLTRB(5, 5, 155, 95)); @@ -531,6 +531,44 @@ TGFX_TEST(LayerTest, solidLayer) { EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/draw_solid")); } +TGFX_TEST(LayerTest, StrokeOnTop) { + ContextScope scope; + auto context = scope.getContext(); + ASSERT_TRUE(context != nullptr); + auto surface = Surface::Make(context, 200, 200); + auto displayList = std::make_unique(); + auto layer = Layer::Make(); + displayList->root()->addChild(layer); + auto shapeLayer = ShapeLayer::Make(); + Path path = {}; + path.addRect(Rect::MakeXYWH(20, 20, 150, 150)); + shapeLayer->setPath(path); + shapeLayer->setFillStyle(SolidColor::Make(Color::Red())); + auto strokeColor = SolidColor::Make(Color::Green()); + strokeColor->setAlpha(0.5f); + shapeLayer->setStrokeStyle(strokeColor); + shapeLayer->setLineWidth(16); + auto innerShadow = InnerShadowStyle::Make(30, 30, 0, 0, Color::FromRGBA(100, 0, 0, 128)); + auto dropShadow = DropShadowStyle::Make(-20, -20, 0, 0, Color::Black()); + dropShadow->setShowBehindLayer(false); + shapeLayer->setLayerStyles({dropShadow, innerShadow}); + shapeLayer->setExcludeChildEffectsInLayerStyle(true); + layer->addChild(shapeLayer); + auto solidLayer = SolidLayer::Make(); + solidLayer->setWidth(100); + solidLayer->setHeight(100); + solidLayer->setRadiusX(25); + solidLayer->setRadiusY(25); + solidLayer->setMatrix(Matrix::MakeTrans(75, 75)); + solidLayer->setColor(Color::Blue()); + shapeLayer->addChild(solidLayer); + displayList->render(surface.get()); + EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/StrokeOnTop_Off")); + shapeLayer->setStrokeOnTop(true); + displayList->render(surface.get()); + EXPECT_TRUE(Baseline::Compare(surface, "LayerTest/StrokeOnTop_On")); +} + TGFX_TEST(LayerTest, FilterTest) { auto filter = DropShadowFilter::Make(-80, -80, 0, 0, Color::Black()); auto filter2 = DropShadowFilter::Make(-40, -40, 0, 0, Color::Green());