blob: 6cef1ee4279d27ddd95a7c8eccb54499bdff8e08 [file] [log] [blame]
/*
* 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