//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;
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;
*/
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)
}
+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;
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;
StrokeCap cap;
StrokeJoin join;
StrokeJoin joinSaved;
+ SwFill* fill = nullptr;
SwStrokeBorder borders[2];
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;
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);
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)
}
+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;
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)};
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) {
//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);
}
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);
}
+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) {
}
+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;
+}
+
#include <math.h>
#include "tvgSwCommon.h"
-
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
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);
}
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
{
}
+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;
{
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;
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);
}
};
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;
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();
if (stroke) {
dup->stroke = new ShapeStroke(stroke);
dup->flag |= RenderUpdateFlag::Stroke;
+
+ if (stroke->fill)
+ dup->flag |= RenderUpdateFlag::GradientStroke;
}
//Fill