mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 05:33:36 +00:00
sw_engine: adding a gradient as a stroke feature
* sw_engine: adding a gradient as a stroke feature Similarly as a shape may have a gradient fill so can the stroke. * Capi: adding APIs for a gradient stroke Co-authored-by: Hermet Park <hermetpark@gmail.com>
This commit is contained in:
parent
20324dc48e
commit
d86032df36
11 changed files with 155 additions and 30 deletions
|
@ -230,6 +230,7 @@ public:
|
|||
//Stroke
|
||||
Result stroke(float width) noexcept;
|
||||
Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept;
|
||||
Result stroke(std::unique_ptr<Fill> f) noexcept;
|
||||
Result stroke(const float* dashPattern, uint32_t cnt) noexcept;
|
||||
Result stroke(StrokeCap cap) noexcept;
|
||||
Result stroke(StrokeJoin join) noexcept;
|
||||
|
@ -248,6 +249,7 @@ public:
|
|||
|
||||
float strokeWidth() const noexcept;
|
||||
Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept;
|
||||
const Fill* strokeFill() const noexcept;
|
||||
uint32_t strokeDash(const float** dashPattern) const noexcept;
|
||||
StrokeCap strokeCap() const noexcept;
|
||||
StrokeJoin strokeJoin() const noexcept;
|
||||
|
|
|
@ -829,6 +829,10 @@ TVG_EXPORT Tvg_Result tvg_shape_set_stroke_color(Tvg_Paint* paint, uint8_t r, ui
|
|||
*/
|
||||
TVG_EXPORT Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint* paint, uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a);
|
||||
|
||||
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* grad);
|
||||
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* grad);
|
||||
TVG_EXPORT Tvg_Result tvg_shape_get_stroke_gradient(const Tvg_Paint* paint, Tvg_Gradient** grad);
|
||||
|
||||
|
||||
/*!
|
||||
* \fn TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt)
|
||||
|
|
|
@ -313,6 +313,28 @@ TVG_EXPORT Tvg_Result tvg_shape_get_stroke_color(const Tvg_Paint* paint, uint8_t
|
|||
}
|
||||
|
||||
|
||||
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_linear_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient)
|
||||
{
|
||||
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke(unique_ptr<LinearGradient>((LinearGradient*)(gradient)));
|
||||
}
|
||||
|
||||
|
||||
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_radial_gradient(Tvg_Paint* paint, Tvg_Gradient* gradient)
|
||||
{
|
||||
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
return (Tvg_Result) reinterpret_cast<Shape*>(paint)->stroke(unique_ptr<RadialGradient>((RadialGradient*)(gradient)));
|
||||
}
|
||||
|
||||
|
||||
TVG_EXPORT Tvg_Result tvg_shape_stroke_get_gradient(const Tvg_Paint* paint, Tvg_Gradient** gradient)
|
||||
{
|
||||
if (!paint || !gradient) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
*gradient = (Tvg_Gradient*)(reinterpret_cast<Shape*>(CCP(paint))->strokeFill());
|
||||
return TVG_RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt)
|
||||
{
|
||||
if (!paint) return TVG_RESULT_INVALID_ARGUMENT;
|
||||
|
|
|
@ -127,6 +127,32 @@ struct SwBBox
|
|||
SwPoint min, max;
|
||||
};
|
||||
|
||||
struct SwFill
|
||||
{
|
||||
struct SwLinear {
|
||||
float dx, dy;
|
||||
float len;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SwRadial {
|
||||
float cx, cy;
|
||||
float a;
|
||||
float inv2a;
|
||||
};
|
||||
|
||||
union {
|
||||
SwLinear linear;
|
||||
SwRadial radial;
|
||||
};
|
||||
|
||||
uint32_t* ctable;
|
||||
FillSpread spread;
|
||||
float sx, sy;
|
||||
|
||||
bool translucent;
|
||||
};
|
||||
|
||||
struct SwStrokeBorder
|
||||
{
|
||||
uint32_t ptsCnt;
|
||||
|
@ -152,6 +178,7 @@ struct SwStroke
|
|||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
StrokeJoin joinSaved;
|
||||
SwFill* fill = nullptr;
|
||||
|
||||
SwStrokeBorder borders[2];
|
||||
|
||||
|
@ -174,32 +201,6 @@ struct SwDashStroke
|
|||
bool curOpGap;
|
||||
};
|
||||
|
||||
struct SwFill
|
||||
{
|
||||
struct SwLinear {
|
||||
float dx, dy;
|
||||
float len;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SwRadial {
|
||||
float cx, cy;
|
||||
float a;
|
||||
float inv2a;
|
||||
};
|
||||
|
||||
union {
|
||||
SwLinear linear;
|
||||
SwRadial radial;
|
||||
};
|
||||
|
||||
uint32_t* ctable;
|
||||
FillSpread spread;
|
||||
float sx, sy;
|
||||
|
||||
bool translucent;
|
||||
};
|
||||
|
||||
struct SwShape
|
||||
{
|
||||
SwOutline* outline = nullptr;
|
||||
|
@ -297,8 +298,11 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const M
|
|||
void shapeFree(SwShape* shape);
|
||||
void shapeDelStroke(SwShape* shape);
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
|
||||
void shapeResetFill(SwShape* shape);
|
||||
void shapeResetStrokeFill(SwShape* shape);
|
||||
void shapeDelFill(SwShape* shape);
|
||||
void shapeDelStrokeFill(SwShape* shape);
|
||||
|
||||
void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
|
||||
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
||||
|
@ -339,6 +343,7 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
|
|||
bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity);
|
||||
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
|
||||
bool rasterClear(SwSurface* surface);
|
||||
|
||||
static inline void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||
|
|
|
@ -817,6 +817,15 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
|
|||
}
|
||||
|
||||
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
|
||||
{
|
||||
if (id == FILL_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
|
||||
return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool rasterClear(SwSurface* surface)
|
||||
{
|
||||
if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false;
|
||||
|
|
|
@ -76,6 +76,7 @@ struct SwShapeTask : SwTask
|
|||
if (HALF_STROKE(strokeWidth) > 0) {
|
||||
sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
|
||||
}
|
||||
bool validStroke = (strokeAlpha > 0) || sdata->strokeFill();
|
||||
|
||||
SwSize clip = {static_cast<SwCoord>(surface->w), static_cast<SwCoord>(surface->h)};
|
||||
|
||||
|
@ -89,7 +90,7 @@ struct SwShapeTask : SwTask
|
|||
sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||
alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
|
||||
bool renderShape = (alpha > 0 || sdata->fill());
|
||||
if (renderShape || strokeAlpha) {
|
||||
if (renderShape || validStroke) {
|
||||
shapeReset(&shape);
|
||||
if (!shapePrepare(&shape, sdata, tid, clip, transform, bbox)) goto err;
|
||||
if (renderShape) {
|
||||
|
@ -118,10 +119,18 @@ struct SwShapeTask : SwTask
|
|||
|
||||
//Stroke
|
||||
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
||||
if (strokeAlpha > 0) {
|
||||
if (validStroke) {
|
||||
shapeResetStroke(&shape, sdata, transform);
|
||||
if (!shapeGenStrokeRle(&shape, sdata, tid, transform, clip, bbox)) goto err;
|
||||
++addStroking;
|
||||
|
||||
if (auto fill = sdata->strokeFill()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||
if (ctable) shapeResetStrokeFill(&shape);
|
||||
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
|
||||
} else {
|
||||
shapeDelStrokeFill(&shape);
|
||||
}
|
||||
} else {
|
||||
shapeDelStroke(&shape);
|
||||
}
|
||||
|
@ -324,6 +333,9 @@ bool SwRenderer::renderShape(RenderData data)
|
|||
if (a > 0) rasterSolidShape(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
|
||||
if (auto strokeFill = task->sdata->strokeFill()) {
|
||||
rasterGradientStroke(surface, &task->shape, strokeFill->id());
|
||||
} else {
|
||||
if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
|
||||
a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
|
||||
if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
|
||||
|
|
|
@ -650,6 +650,12 @@ bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transfor
|
|||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
|
||||
{
|
||||
return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
|
||||
}
|
||||
|
||||
|
||||
void shapeResetFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->fill) {
|
||||
|
@ -660,9 +666,28 @@ void shapeResetFill(SwShape* shape)
|
|||
}
|
||||
|
||||
|
||||
void shapeResetStrokeFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke->fill) {
|
||||
shape->stroke->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
|
||||
if (!shape->stroke->fill) return;
|
||||
}
|
||||
fillReset(shape->stroke->fill);
|
||||
}
|
||||
|
||||
|
||||
void shapeDelFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->fill) return;
|
||||
fillFree(shape->fill);
|
||||
shape->fill = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void shapeDelStrokeFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke->fill) return;
|
||||
fillFree(shape->stroke->fill);
|
||||
shape->stroke->fill = nullptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
#include <math.h>
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
@ -821,6 +820,9 @@ void strokeFree(SwStroke* stroke)
|
|||
if (stroke->borders[1].pts) free(stroke->borders[1].pts);
|
||||
if (stroke->borders[1].tags) free(stroke->borders[1].tags);
|
||||
|
||||
fillFree(stroke->fill);
|
||||
stroke->fill = nullptr;
|
||||
|
||||
free(stroke);
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ struct Compositor {
|
|||
uint32_t opacity;
|
||||
};
|
||||
|
||||
enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, All = 63};
|
||||
enum RenderUpdateFlag {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, All = 127};
|
||||
|
||||
struct RenderTransform
|
||||
{
|
||||
|
|
|
@ -335,6 +335,20 @@ Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
|
|||
}
|
||||
|
||||
|
||||
Result Shape::stroke(unique_ptr<Fill> f) noexcept
|
||||
{
|
||||
return pImpl->strokeFill(move(f));
|
||||
}
|
||||
|
||||
|
||||
const Fill* Shape::strokeFill() const noexcept
|
||||
{
|
||||
if (!pImpl->stroke) return nullptr;
|
||||
|
||||
return pImpl->stroke->fill;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
|
||||
{
|
||||
if (cnt < 2 || !dashPattern) return Result::InvalidArguments;
|
||||
|
|
|
@ -33,6 +33,7 @@ struct ShapeStroke
|
|||
{
|
||||
float width = 0;
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
Fill *fill = nullptr;
|
||||
float* dashPattern = nullptr;
|
||||
uint32_t dashCnt = 0;
|
||||
StrokeCap cap = StrokeCap::Square;
|
||||
|
@ -49,11 +50,14 @@ struct ShapeStroke
|
|||
memcpy(color, src->color, sizeof(color));
|
||||
dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
|
||||
memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
|
||||
if (src->fill)
|
||||
fill = src->fill->duplicate();
|
||||
}
|
||||
|
||||
~ShapeStroke()
|
||||
{
|
||||
if (dashPattern) free(dashPattern);
|
||||
if (fill) delete(fill);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -291,6 +295,12 @@ struct Shape::Impl
|
|||
if (!stroke) stroke = new ShapeStroke();
|
||||
if (!stroke) return false;
|
||||
|
||||
if (stroke->fill) {
|
||||
delete(stroke->fill);
|
||||
stroke->fill = nullptr;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
|
||||
stroke->color[0] = r;
|
||||
stroke->color[1] = g;
|
||||
stroke->color[2] = b;
|
||||
|
@ -301,6 +311,23 @@ struct Shape::Impl
|
|||
return true;
|
||||
}
|
||||
|
||||
Result strokeFill(unique_ptr<Fill> f)
|
||||
{
|
||||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
if (!stroke) stroke = new ShapeStroke();
|
||||
if (!stroke) return Result::FailedAllocation;
|
||||
|
||||
if (stroke->fill && stroke->fill != p) delete(stroke->fill);
|
||||
stroke->fill = p;
|
||||
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
bool strokeDash(const float* pattern, uint32_t cnt)
|
||||
{
|
||||
if (!stroke) stroke = new ShapeStroke();
|
||||
|
@ -363,6 +390,9 @@ struct Shape::Impl
|
|||
if (stroke) {
|
||||
dup->stroke = new ShapeStroke(stroke);
|
||||
dup->flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
if (stroke->fill)
|
||||
dup->flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
|
||||
//Fill
|
||||
|
|
Loading…
Add table
Reference in a new issue