| /* |
| * Copyright 2022 Google LLC |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef skgpu_graphite_DrawParams_DEFINED |
| #define skgpu_graphite_DrawParams_DEFINED |
| |
| |
| #include "include/core/SkPaint.h" |
| #include "include/core/SkRect.h" |
| #include "src/gpu/graphite/DrawOrder.h" |
| #include "src/gpu/graphite/geom/Geometry.h" |
| #include "src/gpu/graphite/geom/Rect.h" |
| #include "src/gpu/graphite/geom/Transform_graphite.h" |
| |
| #include <optional> |
| |
| namespace skgpu::graphite { |
| |
| // NOTE: Only represents the stroke or hairline styles; stroke-and-fill must be handled higher up. |
| class StrokeStyle { |
| public: |
| StrokeStyle() : fHalfWidth(0.f), fJoinLimit(0.f), fCap(SkPaint::kButt_Cap) {} |
| StrokeStyle(float width, |
| float miterLimit, |
| SkPaint::Join join, |
| SkPaint::Cap cap) |
| : fHalfWidth(std::max(0.f, 0.5f * width)) |
| , fJoinLimit(join == SkPaint::kMiter_Join ? std::max(0.f, miterLimit) : |
| (join == SkPaint::kBevel_Join ? 0.f : -1.f)) |
| , fCap(cap) {} |
| |
| StrokeStyle(const StrokeStyle&) = default; |
| |
| StrokeStyle& operator=(const StrokeStyle&) = default; |
| |
| bool isMiterJoin() const { return fJoinLimit > 0.f; } |
| bool isBevelJoin() const { return fJoinLimit == 0.f; } |
| bool isRoundJoin() const { return fJoinLimit < 0.f; } |
| |
| float halfWidth() const { return fHalfWidth; } |
| float width() const { return 2.f * fHalfWidth; } |
| float miterLimit() const { return std::max(0.f, fJoinLimit); } |
| SkPaint::Cap cap() const { return fCap; } |
| SkPaint::Join join() const { |
| return fJoinLimit > 0.f ? SkPaint::kMiter_Join : |
| (fJoinLimit == 0.f ? SkPaint::kBevel_Join : SkPaint::kRound_Join); |
| } |
| |
| // Raw join limit, compatible with tess::StrokeParams |
| float joinLimit() const { return fJoinLimit; } |
| |
| private: |
| float fHalfWidth; // >0: relative to transform; ==0: hairline, 1px in device space |
| float fJoinLimit; // >0: miter join; ==0: bevel join; <0: round join |
| SkPaint::Cap fCap; |
| }; |
| |
| // TBD: Separate DashParams extracted from an SkDashPathEffect? Or folded into StrokeStyle? |
| |
| class Clip { |
| public: |
| Clip() = default; |
| Clip(const Rect& drawBounds, |
| const Rect& shapeBounds, |
| const SkIRect& scissor, |
| const SkShader* shader) |
| : fDrawBounds(drawBounds) |
| , fTransformedShapeBounds(shapeBounds) |
| , fScissor(scissor) |
| , fShader(shader) {} |
| |
| // Tight bounds of the draw, including any padding/outset for stroking and expansion due to |
| // inverse fill and intersected with the scissor. |
| const Rect& drawBounds() const { return fDrawBounds; } |
| |
| // The scissor rectangle obtained by restricting the bounds of the clip stack that affects the |
| // draw to the device bounds. The scissor must contain drawBounds() and must already be |
| // intersected with the device bounds. |
| const SkIRect& scissor() const { return fScissor; } |
| |
| // Clipped bounds of the shape in device space, including any padding/outset for stroking, |
| // intersected with the scissor and ignoring the fill rule. For a regular fill this is identical |
| // to drawBounds(). For an inverse fill, this is a subset of drawBounds(). |
| const Rect& transformedShapeBounds() const { return fTransformedShapeBounds; } |
| |
| // If set, the clip shader's output alpha is further used to clip the draw. |
| const SkShader* shader() const { return fShader; } |
| |
| bool isClippedOut() const { return fDrawBounds.isEmptyNegativeOrNaN(); } |
| |
| private: |
| // DrawList assumes the DrawBounds are correct for a given shape, transform, and style. They |
| // are provided to the DrawList to avoid re-calculating the same bounds. |
| Rect fDrawBounds; |
| Rect fTransformedShapeBounds; |
| SkIRect fScissor; |
| const SkShader* fShader; |
| |
| // TODO: If we add more complex analytic shapes for clipping, e.g. coverage rrect, it should |
| // go here. |
| }; |
| |
| // Encapsulates all geometric state for a single high-level draw call. RenderSteps are responsible |
| // for transforming this state into actual rendering; shading from PaintParams is handled separately |
| class DrawParams { |
| public: |
| DrawParams(const Transform& transform, |
| const Geometry& geometry, |
| const Clip& clip, |
| DrawOrder drawOrder, |
| const StrokeStyle* stroke) |
| : fTransform(transform) |
| , fGeometry(geometry) |
| , fClip(clip) |
| , fOrder(drawOrder) |
| , fStroke(stroke ? std::optional<StrokeStyle>(*stroke) : std::nullopt) {} |
| |
| const Transform& transform() const { return fTransform; } |
| const Geometry& geometry() const { return fGeometry; } |
| const Clip& clip() const { return fClip; } |
| DrawOrder order() const { return fOrder; } |
| |
| // Optional stroke parameters if the geometry is stroked instead of filled |
| bool isStroke() const { return fStroke.has_value(); } |
| const StrokeStyle& strokeStyle() const { |
| SkASSERT(this->isStroke()); |
| return *fStroke; |
| } |
| |
| private: |
| const Transform& fTransform; // Lifetime of the transform must be held longer than the geometry |
| |
| Geometry fGeometry; |
| Clip fClip; |
| DrawOrder fOrder; |
| |
| std::optional<StrokeStyle> fStroke; // Not present implies fill |
| }; |
| |
| } // namespace skgpu::graphite |
| |
| #endif // skgpu_graphite_DrawParams_DEFINED |