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:
Mira Grudzinska 2021-02-19 09:16:10 +01:00 committed by GitHub
parent 20324dc48e
commit d86032df36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 155 additions and 30 deletions

View file

@ -230,6 +230,7 @@ public:
//Stroke //Stroke
Result stroke(float width) noexcept; Result stroke(float width) noexcept;
Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) 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(const float* dashPattern, uint32_t cnt) noexcept;
Result stroke(StrokeCap cap) noexcept; Result stroke(StrokeCap cap) noexcept;
Result stroke(StrokeJoin join) noexcept; Result stroke(StrokeJoin join) noexcept;
@ -248,6 +249,7 @@ public:
float strokeWidth() const noexcept; float strokeWidth() const noexcept;
Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) 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; uint32_t strokeDash(const float** dashPattern) const noexcept;
StrokeCap strokeCap() const noexcept; StrokeCap strokeCap() const noexcept;
StrokeJoin strokeJoin() const noexcept; StrokeJoin strokeJoin() const noexcept;

View file

@ -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_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) * \fn TVG_EXPORT Tvg_Result tvg_shape_set_stroke_dash(Tvg_Paint* paint, const float* dashPattern, uint32_t cnt)

View file

@ -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) 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; if (!paint) return TVG_RESULT_INVALID_ARGUMENT;

View file

@ -127,6 +127,32 @@ struct SwBBox
SwPoint min, max; 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 struct SwStrokeBorder
{ {
uint32_t ptsCnt; uint32_t ptsCnt;
@ -152,6 +178,7 @@ struct SwStroke
StrokeCap cap; StrokeCap cap;
StrokeJoin join; StrokeJoin join;
StrokeJoin joinSaved; StrokeJoin joinSaved;
SwFill* fill = nullptr;
SwStrokeBorder borders[2]; SwStrokeBorder borders[2];
@ -174,32 +201,6 @@ struct SwDashStroke
bool curOpGap; 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 struct SwShape
{ {
SwOutline* outline = nullptr; SwOutline* outline = nullptr;
@ -297,8 +298,11 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, unsigned tid, const M
void shapeFree(SwShape* shape); void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape); void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable); 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 shapeResetFill(SwShape* shape);
void shapeResetStrokeFill(SwShape* shape);
void shapeDelFill(SwShape* shape); void shapeDelFill(SwShape* shape);
void shapeDelStrokeFill(SwShape* shape);
void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform); void strokeReset(SwStroke* stroke, const Shape* shape, const Matrix* transform);
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline); 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 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 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 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); bool rasterClear(SwSurface* surface);
static inline void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len) static inline void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)

View file

@ -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) bool rasterClear(SwSurface* surface)
{ {
if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false; if (!surface || !surface->buffer || surface->stride <= 0 || surface->w <= 0 || surface->h <= 0) return false;

View file

@ -76,6 +76,7 @@ struct SwShapeTask : SwTask
if (HALF_STROKE(strokeWidth) > 0) { if (HALF_STROKE(strokeWidth) > 0) {
sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha);
} }
bool validStroke = (strokeAlpha > 0) || sdata->strokeFill();
SwSize clip = {static_cast<SwCoord>(surface->w), static_cast<SwCoord>(surface->h)}; 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); sdata->fillColor(nullptr, nullptr, nullptr, &alpha);
alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255); alpha = static_cast<uint8_t>(static_cast<uint32_t>(alpha) * opacity / 255);
bool renderShape = (alpha > 0 || sdata->fill()); bool renderShape = (alpha > 0 || sdata->fill());
if (renderShape || strokeAlpha) { if (renderShape || validStroke) {
shapeReset(&shape); shapeReset(&shape);
if (!shapePrepare(&shape, sdata, tid, clip, transform, bbox)) goto err; if (!shapePrepare(&shape, sdata, tid, clip, transform, bbox)) goto err;
if (renderShape) { if (renderShape) {
@ -118,10 +119,18 @@ struct SwShapeTask : SwTask
//Stroke //Stroke
if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
if (strokeAlpha > 0) { if (validStroke) {
shapeResetStroke(&shape, sdata, transform); shapeResetStroke(&shape, sdata, transform);
if (!shapeGenStrokeRle(&shape, sdata, tid, transform, clip, bbox)) goto err; if (!shapeGenStrokeRle(&shape, sdata, tid, transform, clip, bbox)) goto err;
++addStroking; ++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 { } else {
shapeDelStroke(&shape); shapeDelStroke(&shape);
} }
@ -324,6 +333,9 @@ bool SwRenderer::renderShape(RenderData data)
if (a > 0) rasterSolidShape(surface, &task->shape, r, g, b, a); 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) { if (task->sdata->strokeColor(&r, &g, &b, &a) == Result::Success) {
a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255); a = static_cast<uint8_t>((opacity * (uint32_t) a) / 255);
if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);

View file

@ -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) void shapeResetFill(SwShape* shape)
{ {
if (!shape->fill) { 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) void shapeDelFill(SwShape* shape)
{ {
if (!shape->fill) return; if (!shape->fill) return;
fillFree(shape->fill); fillFree(shape->fill);
shape->fill = nullptr; shape->fill = nullptr;
} }
void shapeDelStrokeFill(SwShape* shape)
{
if (!shape->stroke->fill) return;
fillFree(shape->stroke->fill);
shape->stroke->fill = nullptr;
}

View file

@ -23,7 +23,6 @@
#include <math.h> #include <math.h>
#include "tvgSwCommon.h" #include "tvgSwCommon.h"
/************************************************************************/ /************************************************************************/
/* Internal Class Implementation */ /* 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].pts) free(stroke->borders[1].pts);
if (stroke->borders[1].tags) free(stroke->borders[1].tags); if (stroke->borders[1].tags) free(stroke->borders[1].tags);
fillFree(stroke->fill);
stroke->fill = nullptr;
free(stroke); free(stroke);
} }

View file

@ -44,7 +44,7 @@ struct Compositor {
uint32_t opacity; 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 struct RenderTransform
{ {

View file

@ -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 Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
{ {
if (cnt < 2 || !dashPattern) return Result::InvalidArguments; if (cnt < 2 || !dashPattern) return Result::InvalidArguments;

View file

@ -33,6 +33,7 @@ struct ShapeStroke
{ {
float width = 0; float width = 0;
uint8_t color[4] = {0, 0, 0, 0}; uint8_t color[4] = {0, 0, 0, 0};
Fill *fill = nullptr;
float* dashPattern = nullptr; float* dashPattern = nullptr;
uint32_t dashCnt = 0; uint32_t dashCnt = 0;
StrokeCap cap = StrokeCap::Square; StrokeCap cap = StrokeCap::Square;
@ -49,11 +50,14 @@ struct ShapeStroke
memcpy(color, src->color, sizeof(color)); memcpy(color, src->color, sizeof(color));
dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt)); dashPattern = static_cast<float*>(malloc(sizeof(float) * dashCnt));
memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt); memcpy(dashPattern, src->dashPattern, sizeof(float) * dashCnt);
if (src->fill)
fill = src->fill->duplicate();
} }
~ShapeStroke() ~ShapeStroke()
{ {
if (dashPattern) free(dashPattern); if (dashPattern) free(dashPattern);
if (fill) delete(fill);
} }
}; };
@ -291,6 +295,12 @@ struct Shape::Impl
if (!stroke) stroke = new ShapeStroke(); if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false; if (!stroke) return false;
if (stroke->fill) {
delete(stroke->fill);
stroke->fill = nullptr;
flag |= RenderUpdateFlag::GradientStroke;
}
stroke->color[0] = r; stroke->color[0] = r;
stroke->color[1] = g; stroke->color[1] = g;
stroke->color[2] = b; stroke->color[2] = b;
@ -301,6 +311,23 @@ struct Shape::Impl
return true; 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) bool strokeDash(const float* pattern, uint32_t cnt)
{ {
if (!stroke) stroke = new ShapeStroke(); if (!stroke) stroke = new ShapeStroke();
@ -363,6 +390,9 @@ struct Shape::Impl
if (stroke) { if (stroke) {
dup->stroke = new ShapeStroke(stroke); dup->stroke = new ShapeStroke(stroke);
dup->flag |= RenderUpdateFlag::Stroke; dup->flag |= RenderUpdateFlag::Stroke;
if (stroke->fill)
dup->flag |= RenderUpdateFlag::GradientStroke;
} }
//Fill //Fill