mirror of
https://github.com/thorvg/thorvg.git
synced 2025-06-08 13:43:43 +00:00
sw_engine: handle grad edge cases
For a linear gradient defined by identical start and end points, and for a radial gradient with a radius of 0, the rendered shape should have the color of the last specified color stop. The documentation has been updated accordingly. @Issue: https://github.com/thorvg/thorvg/issues/2582
This commit is contained in:
parent
a5f69a9bbc
commit
5ff9c08ba9
6 changed files with 57 additions and 18 deletions
|
@ -675,7 +675,8 @@ public:
|
|||
* @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds.
|
||||
* @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds.
|
||||
*
|
||||
* @note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered.
|
||||
* @note In case the first and the second points are equal, an object is filled with a single color using the last color specified in the colorStops().
|
||||
* @see Fill::colorStops()
|
||||
*/
|
||||
Result linear(float x1, float y1, float x2, float y2) noexcept;
|
||||
|
||||
|
@ -734,6 +735,8 @@ public:
|
|||
* @param[in] radius The radius of the bounding circle.
|
||||
*
|
||||
* @retval Result::InvalidArguments in case the @p radius value is zero or less.
|
||||
*
|
||||
* @note In case the @p radius is zero, an object is filled with a single color using the last color specified in the colorStops().
|
||||
*/
|
||||
Result radial(float cx, float cy, float radius) noexcept;
|
||||
|
||||
|
|
|
@ -1745,7 +1745,8 @@ TVG_API Tvg_Gradient* tvg_radial_gradient_new(void);
|
|||
* \return Tvg_Result enumeration.
|
||||
* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer.
|
||||
*
|
||||
* \note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered.
|
||||
* \note In case the first and the second points are equal, an object is filled with a single color using the last color specified in the tvg_gradient_set_color_stops().
|
||||
* \see tvg_gradient_set_color_stops()
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_linear_gradient_set(Tvg_Gradient* grad, float x1, float y1, float x2, float y2);
|
||||
|
||||
|
@ -1781,6 +1782,9 @@ TVG_API Tvg_Result tvg_linear_gradient_get(Tvg_Gradient* grad, float* x1, float*
|
|||
*
|
||||
* \return Tvg_Result enumeration.
|
||||
* \retval TVG_RESULT_INVALID_ARGUMENT An invalid Tvg_Gradient pointer or the @p radius value less than zero.
|
||||
*
|
||||
* \note In case the @p radius is zero, an object is filled with a single color using the last color specified in the specified in the tvg_gradient_set_color_stops().
|
||||
* \see tvg_gradient_set_color_stops()
|
||||
*/
|
||||
TVG_API Tvg_Result tvg_radial_gradient_set(Tvg_Gradient* grad, float cx, float cy, float radius);
|
||||
|
||||
|
|
|
@ -153,6 +153,7 @@ struct SwFill
|
|||
uint32_t* ctable;
|
||||
FillSpread spread;
|
||||
|
||||
bool solid = false; //solid color fill with the last color from colorStops
|
||||
bool translucent;
|
||||
};
|
||||
|
||||
|
@ -525,6 +526,7 @@ void imageReset(SwImage* image);
|
|||
void imageFree(SwImage* image);
|
||||
|
||||
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
|
||||
const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata);
|
||||
void fillReset(SwFill* fill);
|
||||
void fillFree(SwFill* fill);
|
||||
|
||||
|
@ -560,11 +562,11 @@ SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
|
|||
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
|
||||
|
||||
bool rasterCompositor(SwSurface* surface);
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id);
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
|
||||
bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix& transform, const SwBBox& bbox, uint8_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 rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
|
||||
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
|
||||
void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
|
||||
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
|
||||
|
|
|
@ -125,6 +125,8 @@ static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end)
|
|||
|
||||
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
|
||||
{
|
||||
if (fill->solid) return true;
|
||||
|
||||
if (!fill->ctable) {
|
||||
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
|
||||
if (!fill->ctable) return false;
|
||||
|
@ -214,7 +216,12 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& tr
|
|||
fill->linear.dy = y2 - y1;
|
||||
auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
|
||||
|
||||
if (len < FLOAT_EPSILON) return true;
|
||||
if (len < FLOAT_EPSILON) {
|
||||
if (mathZero(fill->linear.dx) && mathZero(fill->linear.dy)) {
|
||||
fill->solid = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fill->linear.dx /= len;
|
||||
fill->linear.dy /= len;
|
||||
|
@ -254,7 +261,10 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
|
|||
auto fy = P(radial)->fy;
|
||||
auto fr = P(radial)->fr;
|
||||
|
||||
if (r < FLOAT_EPSILON) return true;
|
||||
if (mathZero(r)) {
|
||||
fill->solid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
fill->radial.dr = r - fr;
|
||||
fill->radial.dx = cx - fx;
|
||||
|
@ -818,19 +828,26 @@ bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform,
|
|||
|
||||
fill->spread = fdata->spread();
|
||||
|
||||
if (ctable) {
|
||||
if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
|
||||
}
|
||||
|
||||
if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
|
||||
return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
|
||||
if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
|
||||
} else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
|
||||
return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
|
||||
if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
|
||||
}
|
||||
|
||||
//LOG: What type of gradient?!
|
||||
if (ctable) return _updateColorTable(fill, fdata, surface, opacity);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata)
|
||||
{
|
||||
if (!fill->solid) return nullptr;
|
||||
|
||||
const Fill::ColorStop* colors;
|
||||
auto cnt = fdata->colorStops(&colors);
|
||||
if (cnt == 0 || !colors) return nullptr;
|
||||
|
||||
return colors + cnt - 1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -841,6 +858,7 @@ void fillReset(SwFill* fill)
|
|||
fill->ctable = nullptr;
|
||||
}
|
||||
fill->translucent = false;
|
||||
fill->solid = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1912,10 +1912,16 @@ void rasterPremultiply(Surface* surface)
|
|||
}
|
||||
|
||||
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
|
||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
|
||||
{
|
||||
if (!shape->fill) return false;
|
||||
|
||||
if (auto color = fillFetchSolid(shape->fill, fdata)) {
|
||||
auto a = MULTIPLY(color->a, opacity);
|
||||
return a > 0 ? rasterShape(surface, shape, color->r, color->g, color->b, a) : true;
|
||||
}
|
||||
|
||||
auto id = fdata->identifier();
|
||||
if (shape->fastTrack) {
|
||||
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRect(surface, shape->bbox, shape->fill);
|
||||
else if (id == TVG_CLASS_ID_RADIAL)return _rasterRadialGradientRect(surface, shape->bbox, shape->fill);
|
||||
|
@ -1927,10 +1933,16 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
|
|||
}
|
||||
|
||||
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id)
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
|
||||
{
|
||||
if (!shape->stroke || !shape->stroke->fill || !shape->strokeRle) return false;
|
||||
|
||||
if (auto color = fillFetchSolid(shape->stroke->fill, fdata)) {
|
||||
auto a = MULTIPLY(color->a, opacity);
|
||||
return a > 0 ? rasterStroke(surface, shape, color->r, color->g, color->b, a) : true;
|
||||
}
|
||||
|
||||
auto id = fdata->identifier();
|
||||
if (id == TVG_CLASS_ID_LINEAR) return _rasterLinearGradientRle(surface, shape->strokeRle, shape->stroke->fill);
|
||||
else if (id == TVG_CLASS_ID_RADIAL) return _rasterRadialGradientRle(surface, shape->strokeRle, shape->stroke->fill);
|
||||
|
||||
|
|
|
@ -332,7 +332,7 @@ static void _renderFill(SwShapeTask* task, SwSurface* surface, uint8_t opacity)
|
|||
{
|
||||
uint8_t r, g, b, a;
|
||||
if (auto fill = task->rshape->fill) {
|
||||
rasterGradientShape(surface, &task->shape, fill->identifier());
|
||||
rasterGradientShape(surface, &task->shape, fill, opacity);
|
||||
} else {
|
||||
task->rshape->fillColor(&r, &g, &b, &a);
|
||||
a = MULTIPLY(opacity, a);
|
||||
|
@ -344,7 +344,7 @@ static void _renderStroke(SwShapeTask* task, SwSurface* surface, uint8_t opacity
|
|||
{
|
||||
uint8_t r, g, b, a;
|
||||
if (auto strokeFill = task->rshape->strokeFill()) {
|
||||
rasterGradientStroke(surface, &task->shape, strokeFill->identifier());
|
||||
rasterGradientStroke(surface, &task->shape, strokeFill, opacity);
|
||||
} else {
|
||||
if (task->rshape->strokeColor(&r, &g, &b, &a)) {
|
||||
a = MULTIPLY(opacity, a);
|
||||
|
|
Loading…
Add table
Reference in a new issue