diff --git a/drawers/src/base/Drawer.cpp b/drawers/src/base/Drawer.cpp index 0fa177d4..24842cca 100644 --- a/drawers/src/base/Drawer.cpp +++ b/drawers/src/base/Drawer.cpp @@ -81,8 +81,7 @@ void Drawer::draw(tgfx::Canvas* canvas, const AppHost* host) { tgfx::PrintError("Drawer::draw() appHost is nullptr!"); return; } - canvas->save(); + tgfx::AutoCanvasRestore autoRestore(canvas); onDraw(canvas, host); - canvas->restore(); } } // namespace drawers \ No newline at end of file diff --git a/drawers/src/layertree/SimpleLayerTree.cpp b/drawers/src/layertree/SimpleLayerTree.cpp index 8e672a7c..ecd6aab0 100644 --- a/drawers/src/layertree/SimpleLayerTree.cpp +++ b/drawers/src/layertree/SimpleLayerTree.cpp @@ -19,8 +19,8 @@ #include "SimpleLayerTree.h" #include "tgfx/layers/Gradient.h" #include "tgfx/layers/ImageLayer.h" -#include "tgfx/layers/ImagePattern.h" #include "tgfx/layers/ShapeLayer.h" +#include "tgfx/layers/SolidColor.h" #include "tgfx/layers/TextLayer.h" #include "tgfx/layers/filters/DropShadowFilter.h" diff --git a/include/tgfx/core/Canvas.h b/include/tgfx/core/Canvas.h index dd8af027..86586512 100644 --- a/include/tgfx/core/Canvas.h +++ b/include/tgfx/core/Canvas.h @@ -441,4 +441,36 @@ class Canvas { friend class Recorder; friend class SVGExporter; }; + +/** + * AutoCanvasRestore is a helper class that automatically saves the current state of a Canvas when + * created and restores it when destroyed. This is useful for ensuring that the Canvas state is + * restored to its previous state when exiting a scope. + */ +class AutoCanvasRestore { + public: + /** + * Creates an AutoSaveRestore object for the specified Canvas. The current state of the Canvas is + * saved when created and restored when destroyed. + * @param canvas the Canvas to save and restore. + */ + explicit AutoCanvasRestore(Canvas* canvas) : canvas(canvas) { + if (canvas) { + saveCount = canvas->save(); + } + } + + /** + * Restores the Canvas state to the saved state. + */ + ~AutoCanvasRestore() { + if (canvas) { + canvas->restoreToCount(saveCount); + } + } + + private: + Canvas* canvas = nullptr; + int saveCount = 0; +}; } // namespace tgfx diff --git a/include/tgfx/core/Shader.h b/include/tgfx/core/Shader.h index fecc4c40..58e38394 100644 --- a/include/tgfx/core/Shader.h +++ b/include/tgfx/core/Shader.h @@ -116,6 +116,13 @@ class Shader { return false; } + /** + * Returns true if the shader is backed by a single image. + */ + virtual bool isAImage() const { + return false; + } + /** * If the shader has a constant color, this method returns true and updates the color parameter. * Otherwise, it returns false and leaves the color parameter unchanged. diff --git a/include/tgfx/layers/ImagePattern.h b/include/tgfx/layers/ImagePattern.h index 8c81bd13..9b2e0e8b 100644 --- a/include/tgfx/layers/ImagePattern.h +++ b/include/tgfx/layers/ImagePattern.h @@ -53,10 +53,6 @@ class ImagePattern : public ShapeStyle { */ void setMatrix(const Matrix& value); - bool isImage() const override { - return true; - } - protected: std::shared_ptr getShader() const override; diff --git a/include/tgfx/layers/Layer.h b/include/tgfx/layers/Layer.h index d7f7ba78..1f26ba09 100644 --- a/include/tgfx/layers/Layer.h +++ b/include/tgfx/layers/Layer.h @@ -505,10 +505,14 @@ class Layer { virtual std::unique_ptr onUpdateContent(); /** - * Returns the layer contour used for layer styles that require it. - * The default implementation returns the layer content. + * 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. + * @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. */ - virtual LayerContent* getContour(); + virtual void drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const; /** * Attachs a property to this layer. @@ -520,18 +524,6 @@ class Layer { */ void detachProperty(LayerProperty* property) const; - /** - * Draws the layer style onto the given canvas. - */ - void drawLayerStyles(Canvas* canvas, std::shared_ptr content, float contentScale, - std::shared_ptr contour, const Point& contourOffset, float alpha, - LayerStylePosition position); - - /** - * Draws the layer children onto the given canvas. - */ - void drawChildren(const DrawArgs& args, Canvas* canvas, float alpha); - private: /** * Marks the layer's children as changed and needing to be redrawn. @@ -554,7 +546,7 @@ class Layer { Paint getLayerPaint(float alpha, BlendMode blendMode); - std::shared_ptr getCurrentFilter(float contentScale); + std::shared_ptr getImageFilter(float contentScale); LayerContent* getRasterizedCache(const DrawArgs& args); @@ -567,6 +559,12 @@ class Layer { void drawContents(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, + LayerStylePosition position); + bool getLayersUnderPointInternal(float x, float y, std::vector>* results); std::shared_ptr getMaskFilter(const DrawArgs& args, float scale); diff --git a/include/tgfx/layers/ShapeLayer.h b/include/tgfx/layers/ShapeLayer.h index 764b785b..38892a8c 100644 --- a/include/tgfx/layers/ShapeLayer.h +++ b/include/tgfx/layers/ShapeLayer.h @@ -18,7 +18,6 @@ #pragma once -#include "SolidColor.h" #include "tgfx/core/Shape.h" #include "tgfx/layers/Layer.h" #include "tgfx/layers/ShapeStyle.h" @@ -55,6 +54,8 @@ class ShapeLayer : public Layer { */ static std::shared_ptr Make(); + ~ShapeLayer() override; + LayerType type() const override { return LayerType::Shape; } @@ -261,34 +262,25 @@ class ShapeLayer : public Layer { void setStrokeEnd(float end); /** - * Returns the stroke alignment applied to the shape’s path when stroked. The default stroke alignment is Center. - */ + * Returns the stroke alignment applied to the shape’s path when stroked. The default stroke alignment is Center. + */ StrokeAlign strokeAlign() const { return _strokeAlign; } /** - * Sets the stroke alignment applied to the shape’s path when stroked. - */ + * Sets the stroke alignment applied to the shape’s path when stroked. + */ void setStrokeAlign(StrokeAlign align); - ~ShapeLayer() override; - protected: ShapeLayer() = default; - std::shared_ptr createStrokeShape() const; - std::unique_ptr onUpdateContent() override; - LayerContent* getContour() override; + void drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const override; private: - static std::unique_ptr CreateContourWithStyles( - std::shared_ptr shape, const std::vector>& styles); - - void invalidateContentAndContour(); - std::shared_ptr _shape = nullptr; std::vector> _fillStyles = {}; std::vector> _strokeStyles = {}; @@ -299,6 +291,6 @@ class ShapeLayer : public Layer { float _strokeEnd = 1.0f; StrokeAlign _strokeAlign = StrokeAlign::Center; - std::unique_ptr contourContent = nullptr; + std::shared_ptr createStrokeShape() const; }; } // namespace tgfx diff --git a/include/tgfx/layers/ShapeStyle.h b/include/tgfx/layers/ShapeStyle.h index 89f4a988..8a510b42 100644 --- a/include/tgfx/layers/ShapeStyle.h +++ b/include/tgfx/layers/ShapeStyle.h @@ -61,13 +61,6 @@ class ShapeStyle : public LayerProperty { */ virtual std::shared_ptr getShader() const = 0; - /** - * Returns whether the shape style is an image pattern. - */ - virtual bool isImage() const { - return false; - } - private: float _alpha = 1.0f; BlendMode _blendMode = BlendMode::SrcOver; diff --git a/src/core/shaders/ColorFilterShader.h b/src/core/shaders/ColorFilterShader.h index 5256aafd..ff4bb232 100644 --- a/src/core/shaders/ColorFilterShader.h +++ b/src/core/shaders/ColorFilterShader.h @@ -27,6 +27,14 @@ class ColorFilterShader : public Shader { : shader(std::move(shader)), colorFilter(std::move(colorFilter)) { } + bool isOpaque() const override { + return shader->isOpaque() && colorFilter->isAlphaUnchanged(); + } + + bool isAImage() const override { + return shader->isAImage(); + } + protected: Type type() const override { return Type::ColorFilter; diff --git a/src/core/shaders/ImageShader.h b/src/core/shaders/ImageShader.h index 2096626a..3e662f7d 100644 --- a/src/core/shaders/ImageShader.h +++ b/src/core/shaders/ImageShader.h @@ -30,6 +30,10 @@ class ImageShader : public Shader { TileMode tileModeY = TileMode::Clamp; SamplingOptions sampling = {}; + bool isAImage() const override { + return true; + } + protected: Type type() const override { return Type::Image; diff --git a/src/core/shaders/MatrixShader.h b/src/core/shaders/MatrixShader.h index 163b03a8..394cb003 100644 --- a/src/core/shaders/MatrixShader.h +++ b/src/core/shaders/MatrixShader.h @@ -30,6 +30,10 @@ class MatrixShader final : public Shader { return source->isOpaque(); } + bool isAImage() const override { + return source->isAImage(); + } + bool asColor(Color* color) const override { return source->asColor(color); } diff --git a/src/layers/Layer.cpp b/src/layers/Layer.cpp index 620df820..1b0b6095 100644 --- a/src/layers/Layer.cpp +++ b/src/layers/Layer.cpp @@ -424,7 +424,7 @@ Rect Layer::getBounds(const Layer* targetCoordinateSpace) { } } - auto filter = getCurrentFilter(1.0f); + auto filter = getImageFilter(1.0f); if (filter) { bounds = filter->filterBounds(bounds); } @@ -527,10 +527,6 @@ std::unique_ptr Layer::onUpdateContent() { return nullptr; } -LayerContent* Layer::getContour() { - return getContent(); -} - void Layer::attachProperty(LayerProperty* property) const { if (property) { property->attachToLayer(this); @@ -616,7 +612,10 @@ Paint Layer::getLayerPaint(float alpha, BlendMode blendMode) { return paint; } -std::shared_ptr Layer::getCurrentFilter(float contentScale) { +std::shared_ptr Layer::getImageFilter(float contentScale) { + if (_filters.empty()) { + return nullptr; + } std::vector> filters; for (const auto& layerFilter : _filters) { if (auto filter = layerFilter->getImageFilter(contentScale)) { @@ -669,7 +668,7 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con if (image == nullptr) { return nullptr; } - auto filter = getCurrentFilter(contentScale); + auto filter = getImageFilter(contentScale); if (filter) { auto filterOffset = Point::Zero(); image = image->makeWithFilter(std::move(filter), &filterOffset); @@ -680,23 +679,6 @@ std::shared_ptr Layer::getRasterizedImage(const DrawArgs& args, float con return image; } -void Layer::drawLayerStyles(Canvas* canvas, std::shared_ptr content, float contentScale, - std::shared_ptr contour, const Point& contourOffset, float alpha, - LayerStylePosition position) { - for (const auto& layerStyle : _layerStyles) { - if (layerStyle->position() != position) { - continue; - } - canvas->save(); - if (layerStyle->requireLayerContour()) { - layerStyle->drawWithContour(canvas, content, contentScale, contour, contourOffset, alpha); - } else { - layerStyle->draw(canvas, content, contentScale, alpha); - } - canvas->restore(); - } -} - void Layer::drawLayer(const DrawArgs& args, Canvas* canvas, float alpha, BlendMode blendMode) { DEBUG_ASSERT(canvas != nullptr); if (auto rasterizedCache = getRasterizedCache(args)) { @@ -728,7 +710,6 @@ std::shared_ptr Layer::getMaskFilter(const DrawArgs& args, float sca if (!hasValidMask()) { return nullptr; } - auto rasterizedCache = static_cast(_mask->getRasterizedCache(args)); std::shared_ptr maskContentImage = nullptr; auto drawingMatrix = Matrix::I(); @@ -764,41 +745,24 @@ void Layer::drawOffscreen(const DrawArgs& args, Canvas* canvas, float alpha, Ble if (picture == nullptr) { return; } - Paint paint; + Paint paint = {}; + paint.setAntiAlias(bitFields.allowsEdgeAntialiasing); paint.setAlpha(alpha); paint.setBlendMode(blendMode); paint.setMaskFilter(getMaskFilter(args, contentScale)); if (!args.excludeEffects) { - paint.setImageFilter(getCurrentFilter(contentScale)); + paint.setImageFilter(getImageFilter(contentScale)); } auto matrix = Matrix::MakeScale(1.0f / contentScale); canvas->drawPicture(std::move(picture), &matrix, &paint); } -void Layer::drawChildren(const DrawArgs& args, Canvas* canvas, float alpha) { - for (const auto& child : _children) { - if (!child->visible() || child->_alpha <= 0 || child->maskOwner) { - continue; - } - canvas->save(); - canvas->concat(child->getMatrixWithScrollRect()); - if (child->_scrollRect) { - canvas->clipRect(*child->_scrollRect); - } - child->drawLayer(args, canvas, child->_alpha * alpha, child->_blendMode); - canvas->restore(); - } -} - void Layer::drawContents(const DrawArgs& args, Canvas* canvas, float alpha) { auto drawLayerContents = [this, alpha](const DrawArgs& args, Canvas* canvas) { - LayerContent* content = nullptr; + auto content = getContent(); if (args.drawContour) { - content = getContour(); - } else { - content = getContent(); - } - if (content) { + drawContour(content, canvas, getLayerPaint(alpha, BlendMode::SrcOver)); + } else if (content != nullptr) { content->draw(canvas, getLayerPaint(alpha, BlendMode::SrcOver)); } drawChildren(args, canvas, alpha); @@ -817,11 +781,14 @@ void Layer::drawContents(const DrawArgs& args, Canvas* canvas, float alpha) { return; } - canvas->save(); + 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 { @@ -830,16 +797,18 @@ void Layer::drawContents(const DrawArgs& args, Canvas* canvas, float alpha) { auto contentsWithoutEffects = CreatePicture(newArgs, contentScale, drawLayerContents); source = CreatePictureImage(contentsWithoutEffects, &offset); } - - // get contour image + 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) { - DrawArgs newArgs = DrawArgs(args.context, args.renderFlags | RenderFlags::DisableCache, false, - bitFields.excludeChildEffectsInLayerStyle, true); + // 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; @@ -852,7 +821,42 @@ void Layer::drawContents(const DrawArgs& args, Canvas* canvas, float alpha) { canvas->drawPicture(std::move(picture), &matrix, nullptr); drawLayerStyles(canvas, source, contentScale, contour, contourOffset, alpha, LayerStylePosition::Above); - canvas->restore(); +} + +void Layer::drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const { + if (content != nullptr) { + content->draw(canvas, paint); + } +} + +void Layer::drawChildren(const DrawArgs& args, Canvas* canvas, float alpha) { + for (const auto& child : _children) { + if (!child->visible() || child->_alpha <= 0 || child->maskOwner) { + continue; + } + AutoCanvasRestore autoRestore(canvas); + canvas->concat(child->getMatrixWithScrollRect()); + if (child->_scrollRect) { + canvas->clipRect(*child->_scrollRect); + } + child->drawLayer(args, canvas, child->_alpha * alpha, child->_blendMode); + } +} + +void Layer::drawLayerStyles(Canvas* canvas, std::shared_ptr content, float contentScale, + std::shared_ptr contour, const Point& contourOffset, float alpha, + LayerStylePosition position) { + for (const auto& layerStyle : _layerStyles) { + if (layerStyle->position() != position) { + continue; + } + AutoCanvasRestore autoRestore(canvas); + if (layerStyle->requireLayerContour() && contour != nullptr) { + layerStyle->drawWithContour(canvas, content, contentScale, contour, contourOffset, alpha); + } else { + layerStyle->draw(canvas, content, contentScale, alpha); + } + } } bool Layer::getLayersUnderPointInternal(float x, float y, diff --git a/src/layers/ShapeLayer.cpp b/src/layers/ShapeLayer.cpp index 700c03ce..1e41bb21 100644 --- a/src/layers/ShapeLayer.cpp +++ b/src/layers/ShapeLayer.cpp @@ -44,7 +44,7 @@ void ShapeLayer::setPath(Path path) { return; } _shape = Shape::MakeFrom(std::move(path)); - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setShape(std::shared_ptr value) { @@ -52,7 +52,7 @@ void ShapeLayer::setShape(std::shared_ptr value) { return; } _shape = std::move(value); - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setFillStyles(std::vector> fills) { @@ -67,7 +67,7 @@ void ShapeLayer::setFillStyles(std::vector> fills) { for (const auto& style : _fillStyles) { attachProperty(style.get()); } - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::removeFillStyles() { @@ -78,7 +78,7 @@ void ShapeLayer::removeFillStyles() { detachProperty(style.get()); } _fillStyles = {}; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setFillStyle(std::shared_ptr fill) { @@ -95,7 +95,7 @@ void ShapeLayer::addFillStyle(std::shared_ptr fillStyle) { } attachProperty(fillStyle.get()); _fillStyles.push_back(std::move(fillStyle)); - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setStrokeStyles(std::vector> strokes) { @@ -110,7 +110,7 @@ void ShapeLayer::setStrokeStyles(std::vector> stroke for (const auto& style : _strokeStyles) { attachProperty(style.get()); } - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::removeStrokeStyles() { @@ -121,7 +121,7 @@ void ShapeLayer::removeStrokeStyles() { detachProperty(style.get()); } _strokeStyles = {}; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setStrokeStyle(std::shared_ptr stroke) { @@ -138,7 +138,7 @@ void ShapeLayer::addStrokeStyle(std::shared_ptr strokeStyle) { } attachProperty(strokeStyle.get()); _strokeStyles.push_back(std::move(strokeStyle)); - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setLineCap(LineCap cap) { @@ -146,7 +146,7 @@ void ShapeLayer::setLineCap(LineCap cap) { return; } stroke.cap = cap; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setLineJoin(LineJoin join) { @@ -154,7 +154,7 @@ void ShapeLayer::setLineJoin(LineJoin join) { return; } stroke.join = join; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setMiterLimit(float limit) { @@ -162,7 +162,7 @@ void ShapeLayer::setMiterLimit(float limit) { return; } stroke.miterLimit = limit; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setLineWidth(float width) { @@ -170,7 +170,7 @@ void ShapeLayer::setLineWidth(float width) { return; } stroke.width = width; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setLineDashPattern(const std::vector& pattern) { @@ -179,7 +179,7 @@ void ShapeLayer::setLineDashPattern(const std::vector& pattern) { return; } _lineDashPattern = pattern; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setLineDashPhase(float phase) { @@ -187,7 +187,7 @@ void ShapeLayer::setLineDashPhase(float phase) { return; } _lineDashPhase = phase; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setStrokeStart(float start) { @@ -201,7 +201,7 @@ void ShapeLayer::setStrokeStart(float start) { return; } _strokeStart = start; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setStrokeEnd(float end) { @@ -215,7 +215,7 @@ void ShapeLayer::setStrokeEnd(float end) { return; } _strokeEnd = end; - invalidateContentAndContour(); + invalidateContent(); } void ShapeLayer::setStrokeAlign(StrokeAlign align) { @@ -223,7 +223,7 @@ void ShapeLayer::setStrokeAlign(StrokeAlign align) { return; } _strokeAlign = align; - invalidateContentAndContour(); + invalidateContent(); } ShapeLayer::~ShapeLayer() { @@ -235,10 +235,49 @@ ShapeLayer::~ShapeLayer() { } } -std::shared_ptr ShapeLayer::createStrokeShape() const { - if (stroke.width <= 0 || _shape == nullptr) { +static bool NothingToDraw(ShapeStyle* style) { + Paint paint = {}; + paint.setAlpha(style->alpha()); + paint.setBlendMode(style->blendMode()); + return paint.nothingToDraw(); +} + +std::unique_ptr ShapeLayer::onUpdateContent() { + if (_shape == nullptr) { return nullptr; } + std::vector paintList = {}; + paintList.reserve(_fillStyles.size() + _strokeStyles.size()); + for (auto& style : _fillStyles) { + if (!NothingToDraw(style.get())) { + paintList.emplace_back(style->getShader(), style->alpha(), style->blendMode()); + } + } + auto fillPaintCount = paintList.size(); + if (stroke.width > 0) { + for (auto& style : _strokeStyles) { + if (!NothingToDraw(style.get())) { + paintList.emplace_back(style->getShader(), style->alpha(), style->blendMode()); + } + } + } + auto fillShape = fillPaintCount > 0 ? _shape : nullptr; + auto strokeShape = fillPaintCount < paintList.size() ? createStrokeShape() : nullptr; + return std::make_unique(fillShape, strokeShape, std::move(paintList), + fillPaintCount); +} + +void ShapeLayer::drawContour(LayerContent* content, Canvas* canvas, const Paint& paint) const { + auto shapeContent = static_cast(content); + if (shapeContent == nullptr || !shapeContent->hasFills()) { + canvas->drawShape(_shape, paint); + } + if (shapeContent != nullptr) { + shapeContent->drawContour(canvas, paint); + } +} + +std::shared_ptr ShapeLayer::createStrokeShape() const { auto strokeShape = _shape; if ((_strokeStart != 0 || _strokeEnd != 1)) { auto pathEffect = PathEffect::MakeTrim(_strokeStart, _strokeEnd); @@ -267,86 +306,4 @@ std::shared_ptr ShapeLayer::createStrokeShape() const { } return strokeShape; } - -std::unique_ptr ShapeLayer::onUpdateContent() { - if (_shape == nullptr) { - return nullptr; - } - std::vector> contents = {}; - contents.reserve(_fillStyles.size() + _strokeStyles.size()); - for (auto& style : _fillStyles) { - if (style->alpha() <= 0) { - continue; - } - contents.push_back(std::make_unique(_shape, style->getShader(), style->alpha(), - style->blendMode())); - } - if (!_strokeStyles.empty()) { - if (auto strokeShape = createStrokeShape()) { - for (auto& style : _strokeStyles) { - if (style->alpha() <= 0) { - continue; - } - auto content = std::make_unique(strokeShape, style->getShader(), - style->alpha(), style->blendMode()); - contents.push_back(std::move(content)); - } - } - } - return LayerContent::Compose(std::move(contents)); -} - -LayerContent* ShapeLayer::getContour() { - if (contourContent) { - return contourContent.get(); - } - if (_shape == nullptr) { - return nullptr; - } - - contourContent = CreateContourWithStyles(_shape, _fillStyles); - if (contourContent == nullptr) { - contourContent = - std::make_unique(_shape, Shader::MakeColorShader(Color::White())); - } - - if (!_strokeStyles.empty()) { - auto strokeShape = createStrokeShape(); - if (auto strokeContour = CreateContourWithStyles(strokeShape, _strokeStyles)) { - std::vector> contours(2); - contours[0] = std::move(contourContent); - contours[1] = std::move(strokeContour); - contourContent = LayerContent::Compose(std::move(contours)); - } - } - return contourContent.get(); -} - -std::unique_ptr ShapeLayer::CreateContourWithStyles( - std::shared_ptr shape, const std::vector>& styles) { - if (shape == nullptr || styles.empty()) { - return nullptr; - } - std::vector> contours = {}; - auto isAllImageStyle = std::none_of(styles.begin(), styles.end(), [](const auto& style) { - return style->alpha() > 0 && !style->isImage(); - }); - if (!isAllImageStyle) { - contours.push_back( - std::make_unique(shape, Shader::MakeColorShader(Color::White()))); - } else { - for (const auto& style : styles) { - if (style->alpha() > 0) { - contours.push_back(std::make_unique(shape, style->getShader())); - } - } - } - return LayerContent::Compose(std::move(contours)); -} - -void ShapeLayer::invalidateContentAndContour() { - invalidateContent(); - contourContent = nullptr; -} - } // namespace tgfx diff --git a/src/layers/contents/ShapeContent.cpp b/src/layers/contents/ShapeContent.cpp index db10255a..473a8c5b 100644 --- a/src/layers/contents/ShapeContent.cpp +++ b/src/layers/contents/ShapeContent.cpp @@ -19,26 +19,78 @@ #include "ShapeContent.h" namespace tgfx { -ShapeContent::ShapeContent(std::shared_ptr shape, std::shared_ptr shader, - float alpha, BlendMode blendMode) - : bounds(shape->getBounds()), shape(std::move(shape)), shader(std::move(shader)), alpha(alpha), - blendMode(blendMode) { +ShapeContent::ShapeContent(std::shared_ptr fill, std::shared_ptr stroke, + std::vector paintList, size_t fillPaintCount) + : fillShape(std::move(fill)), strokeShape(std::move(stroke)), paintList(std::move(paintList)), + fillPaintCount(fillPaintCount) { + if (fillShape) { + bounds = fillShape->getBounds(); + } + if (strokeShape) { + bounds.join(strokeShape->getBounds()); + } } - void ShapeContent::draw(Canvas* canvas, const Paint& paint) const { - auto shapePaint = paint; - shapePaint.setAlpha(paint.getAlpha() * alpha); + size_t index = 0; + for (auto& shapePaint : paintList) { + auto shape = index++ < fillPaintCount ? fillShape : strokeShape; + drawShape(canvas, paint, std::move(shape), shapePaint); + } +} + +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. - shapePaint.setBlendMode(blendMode); - shapePaint.setShader(shader); - canvas->drawShape(shape, shapePaint); + drawPaint.setBlendMode(shapePaint.blendMode); + drawPaint.setShader(shapePaint.shader); + canvas->drawShape(std::move(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()); + } +} + +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::hitTestPoint(float localX, float localY, bool pixelHitTest) { - if (pixelHitTest) { - auto path = shape->getPath(); - return path.contains(localX, localY); + if (!pixelHitTest) { + return bounds.contains(localX, localY); + } + if (fillShape != nullptr) { + auto path = fillShape->getPath(); + if (path.contains(localX, localY)) { + return true; + } + } + if (strokeShape != nullptr) { + auto path = strokeShape->getPath(); + if (path.contains(localX, localY)) { + return true; + } } - return bounds.contains(localX, localY); + return false; } } // namespace tgfx diff --git a/src/layers/contents/ShapeContent.h b/src/layers/contents/ShapeContent.h index c744569e..7b0cc765 100644 --- a/src/layers/contents/ShapeContent.h +++ b/src/layers/contents/ShapeContent.h @@ -18,28 +18,51 @@ #pragma once -#include "tgfx/core/Path.h" +#include "tgfx/core/Shape.h" #include "tgfx/layers/LayerContent.h" namespace tgfx { +struct ShapePaint { + ShapePaint(std::shared_ptr shader, float alpha, BlendMode blendMode) + : shader(std::move(shader)), alpha(alpha), blendMode(blendMode) { + } + + std::shared_ptr shader = nullptr; + float alpha = 1.0f; + BlendMode blendMode = BlendMode::SrcOver; +}; + class ShapeContent : public LayerContent { public: - ShapeContent(std::shared_ptr shape, std::shared_ptr shader, float alpha = 1.0f, - BlendMode blendMode = BlendMode::SrcOver); + ShapeContent(std::shared_ptr fill, std::shared_ptr stroke, + std::vector paintList, size_t fillPaintCount); Rect getBounds() const override { 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 hitTestPoint(float localX, float localY, bool pixelHitTest) override; private: Rect bounds = Rect::MakeEmpty(); - std::shared_ptr shape = nullptr; - std::shared_ptr shader = nullptr; - float alpha = 1.0f; - BlendMode blendMode = BlendMode::SrcOver; + std::shared_ptr fillShape = nullptr; + 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