feat: updated engine version to 4.4-rc1
This commit is contained in:
parent
ee00efde1f
commit
21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions
|
|
@ -23,8 +23,8 @@
|
|||
#ifndef _TVG_SW_COMMON_H_
|
||||
#define _TVG_SW_COMMON_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgMath.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
#define SW_CURVE_TYPE_POINT 0
|
||||
|
|
@ -113,7 +113,7 @@ struct SwSpan
|
|||
uint8_t coverage;
|
||||
};
|
||||
|
||||
struct SwRleData
|
||||
struct SwRle
|
||||
{
|
||||
SwSpan *spans;
|
||||
uint32_t alloc;
|
||||
|
|
@ -134,7 +134,6 @@ struct SwFill
|
|||
{
|
||||
struct SwLinear {
|
||||
float dx, dy;
|
||||
float len;
|
||||
float offset;
|
||||
};
|
||||
|
||||
|
|
@ -154,6 +153,7 @@ struct SwFill
|
|||
uint32_t* ctable;
|
||||
FillSpread spread;
|
||||
|
||||
bool solid = false; //solid color fill with the last color from colorStops
|
||||
bool translucent;
|
||||
};
|
||||
|
||||
|
|
@ -211,8 +211,8 @@ struct SwShape
|
|||
SwOutline* outline = nullptr;
|
||||
SwStroke* stroke = nullptr;
|
||||
SwFill* fill = nullptr;
|
||||
SwRleData* rle = nullptr;
|
||||
SwRleData* strokeRle = nullptr;
|
||||
SwRle* rle = nullptr;
|
||||
SwRle* strokeRle = nullptr;
|
||||
SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
|
||||
|
||||
bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
|
||||
|
|
@ -221,7 +221,7 @@ struct SwShape
|
|||
struct SwImage
|
||||
{
|
||||
SwOutline* outline = nullptr;
|
||||
SwRleData* rle = nullptr;
|
||||
SwRle* rle = nullptr;
|
||||
union {
|
||||
pixel_t* data; //system based data pointer
|
||||
uint32_t* buf32; //for explicit 32bits channels
|
||||
|
|
@ -244,13 +244,13 @@ typedef uint8_t(*SwAlpha)(uint8_t*); //bl
|
|||
|
||||
struct SwCompositor;
|
||||
|
||||
struct SwSurface : Surface
|
||||
struct SwSurface : RenderSurface
|
||||
{
|
||||
SwJoin join;
|
||||
SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5
|
||||
SwBlender blender = nullptr; //blender (optional)
|
||||
SwCompositor* compositor = nullptr; //compositor (optional)
|
||||
BlendMethod blendMethod; //blending method (uint8_t)
|
||||
BlendMethod blendMethod = BlendMethod::Normal;
|
||||
|
||||
SwAlpha alpha(CompositeMethod method)
|
||||
{
|
||||
|
|
@ -262,7 +262,7 @@ struct SwSurface : Surface
|
|||
{
|
||||
}
|
||||
|
||||
SwSurface(const SwSurface* rhs) : Surface(rhs)
|
||||
SwSurface(const SwSurface* rhs) : RenderSurface(rhs)
|
||||
{
|
||||
join = rhs->join;
|
||||
memcpy(alphas, rhs->alphas, sizeof(alphas));
|
||||
|
|
@ -272,7 +272,7 @@ struct SwSurface : Surface
|
|||
}
|
||||
};
|
||||
|
||||
struct SwCompositor : Compositor
|
||||
struct SwCompositor : RenderCompositor
|
||||
{
|
||||
SwSurface* recoverSfc; //Recover surface when composition is started
|
||||
SwCompositor* recoverCmp; //Recover compositor when composition is done
|
||||
|
|
@ -301,8 +301,8 @@ static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3)
|
|||
|
||||
static inline uint32_t ALPHA_BLEND(uint32_t c, uint32_t a)
|
||||
{
|
||||
return (((((c >> 8) & 0x00ff00ff) * a + 0x00ff00ff) & 0xff00ff00) +
|
||||
((((c & 0x00ff00ff) * a + 0x00ff00ff) >> 8) & 0x00ff00ff));
|
||||
++a;
|
||||
return (((((c >> 8) & 0x00ff00ff) * a) & 0xff00ff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff));
|
||||
}
|
||||
|
||||
static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
|
||||
|
|
@ -379,10 +379,13 @@ static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint
|
|||
|
||||
static inline uint32_t opBlendExclusion(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
//A + B - 2AB
|
||||
auto c1 = std::min(255, C1(s) + C1(d) - std::min(255, (C1(s) * C1(d)) << 1));
|
||||
auto c2 = std::min(255, C2(s) + C2(d) - std::min(255, (C2(s) * C2(d)) << 1));
|
||||
auto c3 = std::min(255, C3(s) + C3(d) - std::min(255, (C3(s) * C3(d)) << 1));
|
||||
// (s + d) - (2 * s * d)
|
||||
auto c1 = C1(s) + C1(d) - 2 * MULTIPLY(C1(s), C1(d));
|
||||
tvg::clamp(c1, 0, 255);
|
||||
auto c2 = C2(s) + C2(d) - 2 * MULTIPLY(C2(s), C2(d));
|
||||
tvg::clamp(c2, 0, 255);
|
||||
auto c3 = C3(s) + C3(d) - 2 * MULTIPLY(C3(s), C3(d));
|
||||
tvg::clamp(c3, 0, 255);
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
|
@ -404,7 +407,6 @@ static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
|
|||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// s * d
|
||||
|
|
@ -414,7 +416,6 @@ static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_
|
|||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t opBlendOverlay(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// if (2 * d < da) => 2 * s * d,
|
||||
|
|
@ -446,10 +447,10 @@ static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t
|
|||
static inline uint32_t opBlendColorDodge(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// d / (1 - s)
|
||||
auto is = 0xffffffff - s;
|
||||
auto c1 = (C1(is) > 0) ? (C1(d) / C1(is)) : C1(d);
|
||||
auto c2 = (C2(is) > 0) ? (C2(d) / C2(is)) : C2(d);
|
||||
auto c3 = (C3(is) > 0) ? (C3(d) / C3(is)) : C3(d);
|
||||
s = 0xffffffff - s;
|
||||
auto c1 = (C1(s) == 0) ? C1(d) : std::min(C1(d) * 255 / C1(s), 255);
|
||||
auto c2 = (C2(s) == 0) ? C2(d) : std::min(C2(d) * 255 / C2(s), 255);
|
||||
auto c3 = (C3(s) == 0) ? C3(d) : std::min(C3(d) * 255 / C3(s), 255);
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
|
@ -457,14 +458,17 @@ static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8
|
|||
{
|
||||
// 1 - (1 - d) / s
|
||||
auto id = 0xffffffff - d;
|
||||
auto c1 = 255 - ((C1(s) > 0) ? (C1(id) / C1(s)) : C1(id));
|
||||
auto c2 = 255 - ((C2(s) > 0) ? (C2(id) / C2(s)) : C2(id));
|
||||
auto c3 = 255 - ((C3(s) > 0) ? (C3(id) / C3(s)) : C3(id));
|
||||
auto c1 = (C1(s) == 0) ? C1(d) : 255 - std::min(C1(id) * 255 / C1(s), 255);
|
||||
auto c2 = (C2(s) == 0) ? C2(d) : 255 - std::min(C2(id) * 255 / C2(s), 255);
|
||||
auto c3 = (C3(s) == 0) ? C3(d) : 255 - std::min(C3(id) * 255 / C3(s), 255);
|
||||
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// if (s < sa), (2 * s * d)
|
||||
// else (sa * da) - 2 * (da - s) * (sa - d)
|
||||
auto c1 = (C1(s) < 128) ? std::min(255, 2 * MULTIPLY(C1(s), C1(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C1(s), 255 - C1(d))));
|
||||
auto c2 = (C2(s) < 128) ? std::min(255, 2 * MULTIPLY(C2(s), C2(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C2(s), 255 - C2(d))));
|
||||
auto c3 = (C3(s) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
|
||||
|
|
@ -474,9 +478,9 @@ static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8
|
|||
static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
//(255 - 2 * s) * (d * d) + (2 * s * b)
|
||||
auto c1 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + 2 * MULTIPLY(C1(s), C1(d)));
|
||||
auto c2 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + 2 * MULTIPLY(C2(s), C2(d)));
|
||||
auto c3 = std::min(255, MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + 2 * MULTIPLY(C3(s), C3(d)));
|
||||
auto c1 = MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + MULTIPLY(std::min(255, 2 * C1(s)), C1(d));
|
||||
auto c2 = MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + MULTIPLY(std::min(255, 2 * C2(s)), C2(d));
|
||||
auto c3 = MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + MULTIPLY(std::min(255, 2 * C3(s)), C3(d));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
|
@ -490,42 +494,44 @@ SwFixed mathAtan(const SwPoint& pt);
|
|||
SwFixed mathCos(SwFixed angle);
|
||||
SwFixed mathSin(SwFixed angle);
|
||||
void mathSplitCubic(SwPoint* base);
|
||||
void mathSplitLine(SwPoint* base);
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
|
||||
SwFixed mathLength(const SwPoint& pt);
|
||||
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
|
||||
int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
|
||||
SwPoint mathTransform(const Point* to, const Matrix* transform);
|
||||
SwPoint mathTransform(const Point* to, const Matrix& transform);
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee);
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
|
||||
|
||||
void shapeReset(SwShape* shape);
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||
bool shapePrepared(const SwShape* shape);
|
||||
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
|
||||
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform);
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
void shapeFree(SwShape* shape);
|
||||
void shapeDelStroke(SwShape* shape);
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable);
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable);
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_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 RenderShape* shape, const Matrix* transform);
|
||||
void strokeReset(SwStroke* stroke, const RenderShape* shape, const Matrix& transform);
|
||||
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline);
|
||||
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
|
||||
void strokeFree(SwStroke* stroke);
|
||||
|
||||
bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
|
||||
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
|
||||
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);
|
||||
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);
|
||||
|
||||
|
|
@ -542,13 +548,13 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
|
||||
|
||||
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
|
||||
SwRleData* rleRender(const SwBBox* bbox);
|
||||
void rleFree(SwRleData* rle);
|
||||
void rleReset(SwRleData* rle);
|
||||
void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2);
|
||||
void rleClipPath(SwRleData* rle, const SwRleData* clip);
|
||||
void rleClipRect(SwRleData* rle, const SwBBox* clip);
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
|
||||
SwRle* rleRender(const SwBBox* bbox);
|
||||
void rleFree(SwRle* rle);
|
||||
void rleReset(SwRle* rle);
|
||||
void rleMerge(SwRle* rle, SwRle* clip1, SwRle* clip2);
|
||||
bool rleClip(SwRle* rle, const SwRle* clip);
|
||||
bool rleClip(SwRle* rle, const SwBBox* clip);
|
||||
|
||||
SwMpool* mpoolInit(uint32_t threads);
|
||||
bool mpoolTerm(SwMpool* mpool);
|
||||
|
|
@ -561,16 +567,33 @@ 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 rasterImage(SwSurface* surface, SwImage* image, 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 rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
|
||||
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, pixel_t val = 0);
|
||||
void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
|
||||
void rasterTranslucentPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
|
||||
void rasterPixel32(uint32_t* dst, uint32_t* src, uint32_t len, uint8_t opacity);
|
||||
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
|
||||
void rasterUnpremultiply(Surface* surface);
|
||||
void rasterPremultiply(Surface* surface);
|
||||
bool rasterConvertCS(Surface* surface, ColorSpace to);
|
||||
void rasterXYFlip(uint32_t* src, uint32_t* dst, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, bool flipped);
|
||||
void rasterUnpremultiply(RenderSurface* surface);
|
||||
void rasterPremultiply(RenderSurface* surface);
|
||||
bool rasterConvertCS(RenderSurface* surface, ColorSpace to);
|
||||
uint32_t rasterUnpremultiply(uint32_t data);
|
||||
|
||||
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params);
|
||||
bool effectGaussianBlurRegion(RenderEffectGaussianBlur* effect);
|
||||
void effectGaussianBlurUpdate(RenderEffectGaussianBlur* effect, const Matrix& transform);
|
||||
bool effectDropShadow(SwCompositor* cmp, SwSurface* surfaces[2], const RenderEffectDropShadow* params, bool direct);
|
||||
bool effectDropShadowRegion(RenderEffectDropShadow* effect);
|
||||
void effectDropShadowUpdate(RenderEffectDropShadow* effect, const Matrix& transform);
|
||||
void effectFillUpdate(RenderEffectFill* effect);
|
||||
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct);
|
||||
void effectTintUpdate(RenderEffectTint* effect);
|
||||
bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct);
|
||||
void effectTritoneUpdate(RenderEffectTritone* effect);
|
||||
bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct);
|
||||
|
||||
#endif /* _TVG_SW_COMMON_H_ */
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
|
|||
auto deltaDeltaRr = 2.0f * (radial->a11 * radial->a11 + radial->a21 * radial->a21) * radial->invA;
|
||||
|
||||
det = b * b + (rr - radial->fr * radial->fr) * radial->invA;
|
||||
deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr;
|
||||
deltaDet = 2.0f * b * deltaB + deltaB * deltaB + deltaRr + deltaDeltaRr * 0.5f;
|
||||
deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
|
||||
}
|
||||
|
||||
|
|
@ -66,15 +66,15 @@ static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, f
|
|||
static uint32_t _estimateAAMargin(const Fill* fdata)
|
||||
{
|
||||
constexpr float marginScalingFactor = 800.0f;
|
||||
if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
|
||||
if (fdata->type() == Type::RadialGradient) {
|
||||
auto radius = P(static_cast<const RadialGradient*>(fdata))->r;
|
||||
return mathZero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
|
||||
return tvg::zero(radius) ? 0 : static_cast<uint32_t>(marginScalingFactor / radius);
|
||||
}
|
||||
auto grad = P(static_cast<const LinearGradient*>(fdata));
|
||||
Point p1 {grad->x1, grad->y1};
|
||||
Point p2 {grad->x2, grad->y2};
|
||||
auto length = mathLength(&p1, &p2);
|
||||
return mathZero(length) ? 0 : static_cast<uint32_t>(marginScalingFactor / length);
|
||||
auto len = length(&p1, &p2);
|
||||
return tvg::zero(len) ? 0 : static_cast<uint32_t>(marginScalingFactor / 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;
|
||||
|
|
@ -205,49 +207,52 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
|
|||
}
|
||||
|
||||
|
||||
bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* transform)
|
||||
bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix& transform)
|
||||
{
|
||||
float x1, x2, y1, y2;
|
||||
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
|
||||
|
||||
fill->linear.dx = x2 - x1;
|
||||
fill->linear.dy = y2 - y1;
|
||||
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
|
||||
auto len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
|
||||
|
||||
if (fill->linear.len < FLOAT_EPSILON) return true;
|
||||
if (len < FLOAT_EPSILON) {
|
||||
if (tvg::zero(fill->linear.dx) && tvg::zero(fill->linear.dy)) {
|
||||
fill->solid = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fill->linear.dx /= fill->linear.len;
|
||||
fill->linear.dy /= fill->linear.len;
|
||||
fill->linear.dx /= len;
|
||||
fill->linear.dy /= len;
|
||||
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
|
||||
|
||||
auto gradTransform = linear->transform();
|
||||
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
|
||||
bool isTransformation = !identity((const Matrix*)(&gradTransform));
|
||||
|
||||
if (isTransformation) {
|
||||
if (transform) gradTransform = *transform * gradTransform;
|
||||
} else if (transform) {
|
||||
gradTransform = *transform;
|
||||
gradTransform = transform * gradTransform;
|
||||
} else {
|
||||
gradTransform = transform;
|
||||
isTransformation = true;
|
||||
}
|
||||
|
||||
if (isTransformation) {
|
||||
Matrix invTransform;
|
||||
if (!mathInverse(&gradTransform, &invTransform)) return false;
|
||||
if (!inverse(&gradTransform, &invTransform)) return false;
|
||||
|
||||
fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
|
||||
|
||||
auto dx = fill->linear.dx;
|
||||
fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
|
||||
fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
|
||||
|
||||
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
|
||||
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& transform)
|
||||
{
|
||||
auto cx = P(radial)->cx;
|
||||
auto cy = P(radial)->cy;
|
||||
|
|
@ -256,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 (tvg::zero(r)) {
|
||||
fill->solid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
fill->radial.dr = r - fr;
|
||||
fill->radial.dx = cx - fx;
|
||||
|
|
@ -287,19 +295,17 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* tr
|
|||
if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
|
||||
|
||||
auto gradTransform = radial->transform();
|
||||
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
|
||||
bool isTransformation = !identity((const Matrix*)(&gradTransform));
|
||||
|
||||
if (transform) {
|
||||
if (isTransformation) gradTransform = *transform * gradTransform;
|
||||
else {
|
||||
gradTransform = *transform;
|
||||
isTransformation = true;
|
||||
}
|
||||
if (isTransformation) gradTransform = transform * gradTransform;
|
||||
else {
|
||||
gradTransform = transform;
|
||||
isTransformation = true;
|
||||
}
|
||||
|
||||
if (isTransformation) {
|
||||
Matrix invTransform;
|
||||
if (!mathInverse(&gradTransform, &invTransform)) return false;
|
||||
if (!inverse(&gradTransform, &invTransform)) return false;
|
||||
fill->radial.a11 = invTransform.e11;
|
||||
fill->radial.a12 = invTransform.e12;
|
||||
fill->radial.a13 = invTransform.e13;
|
||||
|
|
@ -481,6 +487,7 @@ void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
|
|||
auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
det += deltaDet;
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
|
|
@ -547,7 +554,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (opacity == 255) {
|
||||
if (mathZero(inc)) {
|
||||
if (tvg::zero(inc)) {
|
||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
|
||||
*dst = opBlendNormal(color, *dst, alpha(cmp));
|
||||
|
|
@ -578,7 +585,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (mathZero(inc)) {
|
||||
if (tvg::zero(inc)) {
|
||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst, cmp += csize) {
|
||||
*dst = opBlendNormal(color, *dst, MULTIPLY(alpha(cmp), opacity));
|
||||
|
|
@ -620,7 +627,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
|
|||
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (mathZero(inc)) {
|
||||
if (tvg::zero(inc)) {
|
||||
auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
|
|
@ -637,7 +644,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
|
|||
auto t2 = static_cast<int32_t>(t * FIXPT_SIZE);
|
||||
auto inc2 = static_cast<int32_t>(inc * FIXPT_SIZE);
|
||||
for (uint32_t j = 0; j < len; ++j, ++dst) {
|
||||
auto src = MULTIPLY(_fixedPixel(fill, t2), a);
|
||||
auto src = MULTIPLY(A(_fixedPixel(fill, t2)), a);
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
t2 += inc2;
|
||||
}
|
||||
|
|
@ -645,7 +652,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
|
|||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
|
||||
auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
++dst;
|
||||
t += inc;
|
||||
|
|
@ -662,7 +669,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
|
|||
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (mathZero(inc)) {
|
||||
if (tvg::zero(inc)) {
|
||||
auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
|
||||
src = MULTIPLY(src, a);
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst, ++cmp) {
|
||||
|
|
@ -709,7 +716,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (mathZero(inc)) {
|
||||
if (tvg::zero(inc)) {
|
||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
*dst = op(color, *dst, a);
|
||||
|
|
@ -749,7 +756,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (GRADIENT_STOP_SIZE - 1);
|
||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
||||
|
||||
if (mathZero(inc)) {
|
||||
if (tvg::zero(inc)) {
|
||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
||||
if (a == 255) {
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
|
|
@ -816,25 +823,32 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
|||
}
|
||||
|
||||
|
||||
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
|
||||
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
|
||||
{
|
||||
if (!fill) return false;
|
||||
|
||||
fill->spread = fdata->spread();
|
||||
|
||||
if (ctable) {
|
||||
if (!_updateColorTable(fill, fdata, surface, opacity)) return false;
|
||||
if (fdata->type() == Type::LinearGradient) {
|
||||
if (!_prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform)) return false;
|
||||
} else if (fdata->type() == Type::RadialGradient) {
|
||||
if (!_prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform)) return false;
|
||||
}
|
||||
|
||||
if (fdata->identifier() == TVG_CLASS_ID_LINEAR) {
|
||||
return _prepareLinear(fill, static_cast<const LinearGradient*>(fdata), transform);
|
||||
} else if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
|
||||
return _prepareRadial(fill, static_cast<const RadialGradient*>(fdata), transform);
|
||||
}
|
||||
if (ctable) return _updateColorTable(fill, fdata, surface, opacity);
|
||||
return true;
|
||||
}
|
||||
|
||||
//LOG: What type of gradient?!
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -845,6 +859,7 @@ void fillReset(SwFill* fill)
|
|||
fill->ctable = nullptr;
|
||||
}
|
||||
fill->translucent = false;
|
||||
fill->solid = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -27,14 +27,14 @@
|
|||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static inline bool _onlyShifted(const Matrix* m)
|
||||
static inline bool _onlyShifted(const Matrix& m)
|
||||
{
|
||||
if (mathEqual(m->e11, 1.0f) && mathEqual(m->e22, 1.0f) && mathZero(m->e12) && mathZero(m->e21)) return true;
|
||||
if (tvg::equal(m.e11, 1.0f) && tvg::equal(m.e22, 1.0f) && tvg::zero(m.e12) && tvg::zero(m.e21)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid)
|
||||
static bool _genOutline(SwImage* image, const Matrix& transform, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
image->outline = mpoolReqOutline(mpool, tid);
|
||||
auto outline = image->outline;
|
||||
|
|
@ -45,48 +45,12 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
|
|||
outline->closed.reserve(1);
|
||||
|
||||
Point to[4];
|
||||
if (mesh->triangleCnt > 0) {
|
||||
// TODO: Optimise me. We appear to calculate this exact min/max bounding area in multiple
|
||||
// places. We should be able to re-use one we have already done? Also see:
|
||||
// tvgPicture.h --> bounds
|
||||
// tvgSwRasterTexmap.h --> _rasterTexmapPolygonMesh
|
||||
//
|
||||
// TODO: Should we calculate the exact path(s) of the triangle mesh instead?
|
||||
// i.e. copy tvgSwShape.capp -> _genOutline?
|
||||
//
|
||||
// TODO: Cntrs?
|
||||
auto triangles = mesh->triangles;
|
||||
auto min = triangles[0].vertex[0].pt;
|
||||
auto max = triangles[0].vertex[0].pt;
|
||||
|
||||
for (uint32_t i = 0; i < mesh->triangleCnt; ++i) {
|
||||
if (triangles[i].vertex[0].pt.x < min.x) min.x = triangles[i].vertex[0].pt.x;
|
||||
else if (triangles[i].vertex[0].pt.x > max.x) max.x = triangles[i].vertex[0].pt.x;
|
||||
if (triangles[i].vertex[0].pt.y < min.y) min.y = triangles[i].vertex[0].pt.y;
|
||||
else if (triangles[i].vertex[0].pt.y > max.y) max.y = triangles[i].vertex[0].pt.y;
|
||||
|
||||
if (triangles[i].vertex[1].pt.x < min.x) min.x = triangles[i].vertex[1].pt.x;
|
||||
else if (triangles[i].vertex[1].pt.x > max.x) max.x = triangles[i].vertex[1].pt.x;
|
||||
if (triangles[i].vertex[1].pt.y < min.y) min.y = triangles[i].vertex[1].pt.y;
|
||||
else if (triangles[i].vertex[1].pt.y > max.y) max.y = triangles[i].vertex[1].pt.y;
|
||||
|
||||
if (triangles[i].vertex[2].pt.x < min.x) min.x = triangles[i].vertex[2].pt.x;
|
||||
else if (triangles[i].vertex[2].pt.x > max.x) max.x = triangles[i].vertex[2].pt.x;
|
||||
if (triangles[i].vertex[2].pt.y < min.y) min.y = triangles[i].vertex[2].pt.y;
|
||||
else if (triangles[i].vertex[2].pt.y > max.y) max.y = triangles[i].vertex[2].pt.y;
|
||||
}
|
||||
to[0] = {min.x, min.y};
|
||||
to[1] = {max.x, min.y};
|
||||
to[2] = {max.x, max.y};
|
||||
to[3] = {min.x, max.y};
|
||||
} else {
|
||||
auto w = static_cast<float>(image->w);
|
||||
auto h = static_cast<float>(image->h);
|
||||
to[0] = {0, 0};
|
||||
to[1] = {w, 0};
|
||||
to[2] = {w, h};
|
||||
to[3] = {0, h};
|
||||
}
|
||||
auto w = static_cast<float>(image->w);
|
||||
auto h = static_cast<float>(image->h);
|
||||
to[0] = {0, 0};
|
||||
to[1] = {w, 0};
|
||||
to[2] = {w, h};
|
||||
to[3] = {0, h};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
outline->pts.push(mathTransform(&to[i], transform));
|
||||
|
|
@ -108,25 +72,25 @@ static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* tr
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool imagePrepare(SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
image->direct = _onlyShifted(transform);
|
||||
|
||||
//Fast track: Non-transformed image but just shifted.
|
||||
if (image->direct) {
|
||||
image->ox = -static_cast<int32_t>(nearbyint(transform->e13));
|
||||
image->oy = -static_cast<int32_t>(nearbyint(transform->e23));
|
||||
image->ox = -static_cast<int32_t>(nearbyint(transform.e13));
|
||||
image->oy = -static_cast<int32_t>(nearbyint(transform.e23));
|
||||
//Figure out the scale factor by transform matrix
|
||||
} else {
|
||||
auto scaleX = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
|
||||
auto scaleY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12));
|
||||
auto scaleX = sqrtf((transform.e11 * transform.e11) + (transform.e21 * transform.e21));
|
||||
auto scaleY = sqrtf((transform.e22 * transform.e22) + (transform.e12 * transform.e12));
|
||||
image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
|
||||
|
||||
if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true;
|
||||
if (tvg::zero(transform.e12) && tvg::zero(transform.e21)) image->scaled = true;
|
||||
else image->scaled = false;
|
||||
}
|
||||
|
||||
if (!_genOutline(image, mesh, transform, mpool, tid)) return false;
|
||||
if (!_genOutline(image, transform, mpool, tid)) return false;
|
||||
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ SwFixed mathMean(SwFixed angle1, SwFixed angle2)
|
|||
}
|
||||
|
||||
|
||||
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
|
||||
int mathCubicAngle(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
|
||||
{
|
||||
auto d1 = base[2] - base[3];
|
||||
auto d2 = base[1] - base[2];
|
||||
|
|
@ -54,7 +54,7 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
|
|||
if (d2.small()) {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = 0;
|
||||
return true;
|
||||
return -1; //ignoreable
|
||||
} else {
|
||||
angleIn = angleMid = angleOut = mathAtan(d3);
|
||||
}
|
||||
|
|
@ -90,8 +90,8 @@ bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, Sw
|
|||
auto theta1 = abs(mathDiff(angleIn, angleMid));
|
||||
auto theta2 = abs(mathDiff(angleMid, angleOut));
|
||||
|
||||
if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return true;
|
||||
return false;
|
||||
if ((theta1 < (SW_ANGLE_PI / 8)) && (theta2 < (SW_ANGLE_PI / 8))) return 0; //small size
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -179,7 +179,7 @@ SwFixed mathTan(SwFixed angle)
|
|||
SwFixed mathAtan(const SwPoint& pt)
|
||||
{
|
||||
if (pt.zero()) return 0;
|
||||
return SwFixed(mathAtan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
|
||||
return SwFixed(tvg::atan2(TO_FLOAT(pt.y), TO_FLOAT(pt.x)) * (180.0f / MATH_PI) * 65536.0f);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -242,6 +242,15 @@ void mathSplitCubic(SwPoint* base)
|
|||
}
|
||||
|
||||
|
||||
void mathSplitLine(SwPoint* base)
|
||||
{
|
||||
base[2] = base[1];
|
||||
|
||||
base[1].x = (base[0].x + base[1].x) >> 1;
|
||||
base[1].y = (base[0].y + base[1].y) >> 1;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
auto delta = angle2 - angle1;
|
||||
|
|
@ -254,30 +263,28 @@ SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
|
|||
}
|
||||
|
||||
|
||||
SwPoint mathTransform(const Point* to, const Matrix* transform)
|
||||
SwPoint mathTransform(const Point* to, const Matrix& transform)
|
||||
{
|
||||
if (!transform) return {TO_SWCOORD(to->x), TO_SWCOORD(to->y)};
|
||||
|
||||
auto tx = to->x * transform->e11 + to->y * transform->e12 + transform->e13;
|
||||
auto ty = to->x * transform->e21 + to->y * transform->e22 + transform->e23;
|
||||
auto tx = to->x * transform.e11 + to->y * transform.e12 + transform.e13;
|
||||
auto ty = to->x * transform.e21 + to->y * transform.e22 + transform.e23;
|
||||
|
||||
return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
|
||||
}
|
||||
|
||||
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee)
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee)
|
||||
{
|
||||
clipee.max.x = (clipee.max.x < clipper.max.x) ? clipee.max.x : clipper.max.x;
|
||||
clipee.max.y = (clipee.max.y < clipper.max.y) ? clipee.max.y : clipper.max.y;
|
||||
clipee.min.x = (clipee.min.x > clipper.min.x) ? clipee.min.x : clipper.min.x;
|
||||
clipee.min.y = (clipee.min.y > clipper.min.y) ? clipee.min.y : clipper.min.y;
|
||||
clippee.max.x = (clippee.max.x < clipper.max.x) ? clippee.max.x : clipper.max.x;
|
||||
clippee.max.y = (clippee.max.y < clipper.max.y) ? clippee.max.y : clipper.max.y;
|
||||
clippee.min.x = (clippee.min.x > clipper.min.x) ? clippee.min.x : clipper.min.x;
|
||||
clippee.min.y = (clippee.min.y > clipper.min.y) ? clippee.min.y : clipper.min.y;
|
||||
|
||||
//Check valid region
|
||||
if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.min.y < 1) return false;
|
||||
if (clippee.max.x - clippee.min.x < 1 && clippee.max.y - clippee.min.y < 1) return false;
|
||||
|
||||
//Check boundary
|
||||
if (clipee.min.x >= clipper.max.x || clipee.min.y >= clipper.max.y ||
|
||||
clipee.max.x <= clipper.min.x || clipee.max.y <= clipper.min.y) return false;
|
||||
if (clippee.min.x >= clipper.max.x || clippee.min.y >= clipper.max.y ||
|
||||
clippee.max.x <= clipper.min.x || clippee.max.y <= clipper.min.y) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -305,9 +312,7 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
|
|||
if (yMin > pt->y) yMin = pt->y;
|
||||
if (yMax < pt->y) yMax = pt->y;
|
||||
}
|
||||
//Since no antialiasing is applied in the Fast Track case,
|
||||
//the rasterization region has to be rearranged.
|
||||
//https://github.com/Samsung/thorvg/issues/916
|
||||
|
||||
if (fastTrack) {
|
||||
renderRegion.min.x = static_cast<SwCoord>(nearbyint(xMin / 64.0f));
|
||||
renderRegion.max.x = static_cast<SwCoord>(nearbyint(xMax / 64.0f));
|
||||
|
|
|
|||
589
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp
vendored
Normal file
589
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwPostEffect.cpp
vendored
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
/*
|
||||
* Copyright (c) 2024 the ThorVG project. All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Gaussian Blur Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct SwGaussianBlur
|
||||
{
|
||||
static constexpr int MAX_LEVEL = 3;
|
||||
int level;
|
||||
int kernel[MAX_LEVEL];
|
||||
int extends;
|
||||
};
|
||||
|
||||
|
||||
static inline int _gaussianEdgeWrap(int end, int idx)
|
||||
{
|
||||
auto r = idx % (end + 1);
|
||||
return (r < 0) ? (end + 1) + r : r;
|
||||
}
|
||||
|
||||
|
||||
static inline int _gaussianEdgeExtend(int end, int idx)
|
||||
{
|
||||
if (idx < 0) return 0;
|
||||
else if (idx > end) return end;
|
||||
return idx;
|
||||
}
|
||||
|
||||
|
||||
template<int border>
|
||||
static inline int _gaussianRemap(int end, int idx)
|
||||
{
|
||||
if (border == 1) return _gaussianEdgeWrap(end, idx);
|
||||
return _gaussianEdgeExtend(end, idx);
|
||||
}
|
||||
|
||||
|
||||
//TODO: SIMD OPTIMIZATION?
|
||||
template<int border = 0>
|
||||
static void _gaussianFilter(uint8_t* dst, uint8_t* src, int32_t stride, int32_t w, int32_t h, const SwBBox& bbox, int32_t dimension, bool flipped)
|
||||
{
|
||||
if (flipped) {
|
||||
src += (bbox.min.x * stride + bbox.min.y) << 2;
|
||||
dst += (bbox.min.x * stride + bbox.min.y) << 2;
|
||||
} else {
|
||||
src += (bbox.min.y * stride + bbox.min.x) << 2;
|
||||
dst += (bbox.min.y * stride + bbox.min.x) << 2;
|
||||
}
|
||||
|
||||
auto iarr = 1.0f / (dimension + dimension + 1);
|
||||
auto end = w - 1;
|
||||
|
||||
#pragma omp parallel for
|
||||
for (int y = 0; y < h; ++y) {
|
||||
auto p = y * stride;
|
||||
auto i = p * 4; //current index
|
||||
auto l = -(dimension + 1); //left index
|
||||
auto r = dimension; //right index
|
||||
int acc[4] = {0, 0, 0, 0}; //sliding accumulator
|
||||
|
||||
//initial accumulation
|
||||
for (int x = l; x < r; ++x) {
|
||||
auto id = (_gaussianRemap<border>(end, x) + p) * 4;
|
||||
acc[0] += src[id++];
|
||||
acc[1] += src[id++];
|
||||
acc[2] += src[id++];
|
||||
acc[3] += src[id];
|
||||
}
|
||||
//perform filtering
|
||||
for (int x = 0; x < w; ++x, ++r, ++l) {
|
||||
auto rid = (_gaussianRemap<border>(end, r) + p) * 4;
|
||||
auto lid = (_gaussianRemap<border>(end, l) + p) * 4;
|
||||
acc[0] += src[rid++] - src[lid++];
|
||||
acc[1] += src[rid++] - src[lid++];
|
||||
acc[2] += src[rid++] - src[lid++];
|
||||
acc[3] += src[rid] - src[lid];
|
||||
//ignored rounding for the performance. It should be originally: acc[idx] * iarr + 0.5f
|
||||
dst[i++] = static_cast<uint8_t>(acc[0] * iarr);
|
||||
dst[i++] = static_cast<uint8_t>(acc[1] * iarr);
|
||||
dst[i++] = static_cast<uint8_t>(acc[2] * iarr);
|
||||
dst[i++] = static_cast<uint8_t>(acc[3] * iarr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int _gaussianInit(SwGaussianBlur* data, float sigma, int quality)
|
||||
{
|
||||
const auto MAX_LEVEL = SwGaussianBlur::MAX_LEVEL;
|
||||
|
||||
if (tvg::zero(sigma)) return 0;
|
||||
|
||||
data->level = int(SwGaussianBlur::MAX_LEVEL * ((quality - 1) * 0.01f)) + 1;
|
||||
|
||||
//compute box kernel sizes
|
||||
auto wl = (int) sqrt((12 * sigma / MAX_LEVEL) + 1);
|
||||
if (wl % 2 == 0) --wl;
|
||||
auto wu = wl + 2;
|
||||
auto mi = (12 * sigma - MAX_LEVEL * wl * wl - 4 * MAX_LEVEL * wl - 3 * MAX_LEVEL) / (-4 * wl - 4);
|
||||
auto m = int(mi + 0.5f);
|
||||
auto extends = 0;
|
||||
|
||||
for (int i = 0; i < data->level; i++) {
|
||||
data->kernel[i] = ((i < m ? wl : wu) - 1) / 2;
|
||||
extends += data->kernel[i];
|
||||
}
|
||||
|
||||
return extends;
|
||||
}
|
||||
|
||||
|
||||
bool effectGaussianBlurRegion(RenderEffectGaussianBlur* params)
|
||||
{
|
||||
//bbox region expansion for feathering
|
||||
auto& region = params->extend;
|
||||
auto extra = static_cast<SwGaussianBlur*>(params->rd)->extends;
|
||||
|
||||
if (params->direction != 2) {
|
||||
region.x = -extra;
|
||||
region.w = extra * 2;
|
||||
}
|
||||
if (params->direction != 1) {
|
||||
region.y = -extra;
|
||||
region.h = extra * 2;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void effectGaussianBlurUpdate(RenderEffectGaussianBlur* params, const Matrix& transform)
|
||||
{
|
||||
if (!params->rd) params->rd = (SwGaussianBlur*)malloc(sizeof(SwGaussianBlur));
|
||||
auto rd = static_cast<SwGaussianBlur*>(params->rd);
|
||||
|
||||
//compute box kernel sizes
|
||||
auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12);
|
||||
rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
|
||||
|
||||
//invalid
|
||||
if (rd->extends == 0) {
|
||||
params->valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
params->valid = true;
|
||||
}
|
||||
|
||||
|
||||
bool effectGaussianBlur(SwCompositor* cmp, SwSurface* surface, const RenderEffectGaussianBlur* params)
|
||||
{
|
||||
auto& buffer = surface->compositor->image;
|
||||
auto data = static_cast<SwGaussianBlur*>(params->rd);
|
||||
auto& bbox = cmp->bbox;
|
||||
auto w = (bbox.max.x - bbox.min.x);
|
||||
auto h = (bbox.max.y - bbox.min.y);
|
||||
auto stride = cmp->image.stride;
|
||||
auto front = cmp->image.buf32;
|
||||
auto back = buffer.buf32;
|
||||
auto swapped = false;
|
||||
|
||||
TVGLOG("SW_ENGINE", "GaussianFilter region(%ld, %ld, %ld, %ld) params(%f %d %d), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->sigma, params->direction, params->border, data->level);
|
||||
|
||||
/* It is best to take advantage of the Gaussian blur’s separable property
|
||||
by dividing the process into two passes. horizontal and vertical.
|
||||
We can expect fewer calculations. */
|
||||
|
||||
//horizontal
|
||||
if (params->direction != 2) {
|
||||
for (int i = 0; i < data->level; ++i) {
|
||||
_gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, w, h, bbox, data->kernel[i], false);
|
||||
std::swap(front, back);
|
||||
swapped = !swapped;
|
||||
}
|
||||
}
|
||||
|
||||
//vertical. x/y flipping and horionztal access is pretty compatible with the memory architecture.
|
||||
if (params->direction != 1) {
|
||||
rasterXYFlip(front, back, stride, w, h, bbox, false);
|
||||
std::swap(front, back);
|
||||
|
||||
for (int i = 0; i < data->level; ++i) {
|
||||
_gaussianFilter(reinterpret_cast<uint8_t*>(back), reinterpret_cast<uint8_t*>(front), stride, h, w, bbox, data->kernel[i], true);
|
||||
std::swap(front, back);
|
||||
swapped = !swapped;
|
||||
}
|
||||
|
||||
rasterXYFlip(front, back, stride, h, w, bbox, true);
|
||||
std::swap(front, back);
|
||||
}
|
||||
|
||||
if (swapped) std::swap(cmp->image.buf8, buffer.buf8);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* Drop Shadow Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct SwDropShadow : SwGaussianBlur
|
||||
{
|
||||
SwPoint offset;
|
||||
};
|
||||
|
||||
|
||||
//TODO: SIMD OPTIMIZATION?
|
||||
static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, int h, const SwBBox& bbox, int32_t dimension, uint32_t color, bool flipped)
|
||||
{
|
||||
if (flipped) {
|
||||
src += (bbox.min.x * stride + bbox.min.y);
|
||||
dst += (bbox.min.x * stride + bbox.min.y);
|
||||
} else {
|
||||
src += (bbox.min.y * stride + bbox.min.x);
|
||||
dst += (bbox.min.y * stride + bbox.min.x);
|
||||
}
|
||||
auto iarr = 1.0f / (dimension + dimension + 1);
|
||||
auto end = w - 1;
|
||||
|
||||
#pragma omp parallel for
|
||||
for (int y = 0; y < h; ++y) {
|
||||
auto p = y * stride;
|
||||
auto i = p; //current index
|
||||
auto l = -(dimension + 1); //left index
|
||||
auto r = dimension; //right index
|
||||
int acc = 0; //sliding accumulator
|
||||
|
||||
//initial accumulation
|
||||
for (int x = l; x < r; ++x) {
|
||||
auto id = _gaussianEdgeExtend(end, x) + p;
|
||||
acc += A(src[id]);
|
||||
}
|
||||
//perform filtering
|
||||
for (int x = 0; x < w; ++x, ++r, ++l) {
|
||||
auto rid = _gaussianEdgeExtend(end, r) + p;
|
||||
auto lid = _gaussianEdgeExtend(end, l) + p;
|
||||
acc += A(src[rid]) - A(src[lid]);
|
||||
//ignored rounding for the performance. It should be originally: acc * iarr
|
||||
dst[i++] = ALPHA_BLEND(color, static_cast<uint8_t>(acc * iarr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int stride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct)
|
||||
{
|
||||
src += (region.min.y * stride + region.min.x);
|
||||
dst += (region.min.y * stride + region.min.x);
|
||||
|
||||
auto w = region.max.x - region.min.x;
|
||||
auto h = region.max.y - region.min.y;
|
||||
auto translucent = (direct || opacity < 255);
|
||||
|
||||
//shift offset
|
||||
if (region.min.x + offset.x < 0) src -= offset.x;
|
||||
else dst += offset.x;
|
||||
|
||||
if (region.min.y + offset.y < 0) src -= (offset.y * stride);
|
||||
else dst += (offset.y * stride);
|
||||
|
||||
for (auto y = 0; y < h; ++y) {
|
||||
if (translucent) rasterTranslucentPixel32(dst, src, w, opacity);
|
||||
else rasterPixel32(dst, src, w, opacity);
|
||||
src += stride;
|
||||
dst += stride;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool effectDropShadowRegion(RenderEffectDropShadow* params)
|
||||
{
|
||||
//bbox region expansion for feathering
|
||||
auto& region = params->extend;
|
||||
auto& offset = static_cast<SwDropShadow*>(params->rd)->offset;
|
||||
auto extra = static_cast<SwDropShadow*>(params->rd)->extends;
|
||||
|
||||
region.x = -extra;
|
||||
region.w = extra * 2;
|
||||
region.y = -extra;
|
||||
region.h = extra * 2;
|
||||
|
||||
region.x = std::min(region.x + (int32_t)offset.x, region.x);
|
||||
region.y = std::min(region.y + (int32_t)offset.y, region.y);
|
||||
region.w += abs(offset.x);
|
||||
region.h += abs(offset.y);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transform)
|
||||
{
|
||||
if (!params->rd) params->rd = (SwDropShadow*)malloc(sizeof(SwDropShadow));
|
||||
auto rd = static_cast<SwDropShadow*>(params->rd);
|
||||
|
||||
//compute box kernel sizes
|
||||
auto scale = sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12);
|
||||
rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
|
||||
|
||||
//invalid
|
||||
if (rd->extends == 0 || params->color[3] == 0) {
|
||||
params->valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//offset
|
||||
if (params->distance > 0.0f) {
|
||||
auto radian = tvg::deg2rad(90.0f - params->angle);
|
||||
rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))};
|
||||
} else {
|
||||
rd->offset = {0, 0};
|
||||
}
|
||||
|
||||
params->valid = true;
|
||||
}
|
||||
|
||||
|
||||
//A quite same integration with effectGaussianBlur(). See it for detailed comments.
|
||||
//surface[0]: the original image, to overlay it into the filtered image.
|
||||
//surface[1]: temporary buffer for generating the filtered image.
|
||||
bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffectDropShadow* params, bool direct)
|
||||
{
|
||||
//FIXME: if the body is partially visible due to clipping, the shadow also becomes partially visible.
|
||||
|
||||
auto data = static_cast<SwDropShadow*>(params->rd);
|
||||
auto& bbox = cmp->bbox;
|
||||
auto w = (bbox.max.x - bbox.min.x);
|
||||
auto h = (bbox.max.y - bbox.min.y);
|
||||
|
||||
//outside the screen
|
||||
if (abs(data->offset.x) >= w || abs(data->offset.y) >= h) return true;
|
||||
|
||||
SwImage* buffer[] = {&surface[0]->compositor->image, &surface[1]->compositor->image};
|
||||
auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255);
|
||||
auto stride = cmp->image.stride;
|
||||
auto front = cmp->image.buf32;
|
||||
auto back = buffer[1]->buf32;
|
||||
|
||||
auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3];
|
||||
|
||||
TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level);
|
||||
|
||||
//saving the original image in order to overlay it into the filtered image.
|
||||
_dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false);
|
||||
std::swap(front, buffer[0]->buf32);
|
||||
std::swap(front, back);
|
||||
|
||||
//horizontal
|
||||
for (int i = 1; i < data->level; ++i) {
|
||||
_dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[i], color, false);
|
||||
std::swap(front, back);
|
||||
}
|
||||
|
||||
//vertical
|
||||
rasterXYFlip(front, back, stride, w, h, bbox, false);
|
||||
std::swap(front, back);
|
||||
|
||||
for (int i = 0; i < data->level; ++i) {
|
||||
_dropShadowFilter(back, front, stride, h, w, bbox, data->kernel[i], color, true);
|
||||
std::swap(front, back);
|
||||
}
|
||||
|
||||
rasterXYFlip(front, back, stride, h, w, bbox, true);
|
||||
std::swap(cmp->image.buf32, back);
|
||||
|
||||
//draw to the main surface directly
|
||||
if (direct) {
|
||||
_dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct);
|
||||
std::swap(cmp->image.buf32, buffer[0]->buf32);
|
||||
return true;
|
||||
}
|
||||
|
||||
//draw to the intermediate surface
|
||||
rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h);
|
||||
_dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, bbox, data->offset, opacity, direct);
|
||||
std::swap(cmp->image.buf32, buffer[1]->buf32);
|
||||
|
||||
//compositing shadow and body
|
||||
auto s = buffer[0]->buf32 + (bbox.min.y * buffer[0]->stride + bbox.min.x);
|
||||
auto d = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
|
||||
|
||||
for (auto y = 0; y < h; ++y) {
|
||||
rasterTranslucentPixel32(d, s, w, 255);
|
||||
s += buffer[0]->stride;
|
||||
d += cmp->image.stride;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Fill Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void effectFillUpdate(RenderEffectFill* params)
|
||||
{
|
||||
params->valid = true;
|
||||
}
|
||||
|
||||
|
||||
bool effectFill(SwCompositor* cmp, const RenderEffectFill* params, bool direct)
|
||||
{
|
||||
auto opacity = direct ? MULTIPLY(params->color[3], cmp->opacity) : params->color[3];
|
||||
|
||||
auto& bbox = cmp->bbox;
|
||||
auto w = size_t(bbox.max.x - bbox.min.x);
|
||||
auto h = size_t(bbox.max.y - bbox.min.y);
|
||||
auto color = cmp->recoverSfc->join(params->color[0], params->color[1], params->color[2], 255);
|
||||
|
||||
TVGLOG("SW_ENGINE", "Fill region(%ld, %ld, %ld, %ld), param(%d %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->color[0], params->color[1], params->color[2], params->color[3]);
|
||||
|
||||
if (direct) {
|
||||
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
|
||||
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
auto dst = dbuffer;
|
||||
auto src = sbuffer;
|
||||
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
|
||||
auto a = MULTIPLY(opacity, A(*src));
|
||||
auto tmp = ALPHA_BLEND(color, a);
|
||||
*dst = tmp + ALPHA_BLEND(*dst, 255 - a);
|
||||
}
|
||||
dbuffer += cmp->image.stride;
|
||||
sbuffer += cmp->recoverSfc->stride;
|
||||
}
|
||||
cmp->valid = true; //no need the subsequent composition
|
||||
} else {
|
||||
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
auto dst = dbuffer;
|
||||
for (size_t x = 0; x < w; ++x, ++dst) {
|
||||
*dst = ALPHA_BLEND(color, MULTIPLY(opacity, A(*dst)));
|
||||
}
|
||||
dbuffer += cmp->image.stride;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Tint Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void effectTintUpdate(RenderEffectTint* params)
|
||||
{
|
||||
params->valid = true;
|
||||
}
|
||||
|
||||
|
||||
bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct)
|
||||
{
|
||||
auto& bbox = cmp->bbox;
|
||||
auto w = size_t(bbox.max.x - bbox.min.x);
|
||||
auto h = size_t(bbox.max.y - bbox.min.y);
|
||||
auto black = cmp->recoverSfc->join(params->black[0], params->black[1], params->black[2], 255);
|
||||
auto white = cmp->recoverSfc->join(params->white[0], params->white[1], params->white[2], 255);
|
||||
auto opacity = cmp->opacity;
|
||||
auto luma = cmp->recoverSfc->alphas[2]; //luma function
|
||||
|
||||
TVGLOG("SW_ENGINE", "Tint region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity);
|
||||
|
||||
/* Tint Formula: (1 - L) * Black + L * White, where the L is Luminance. */
|
||||
|
||||
if (direct) {
|
||||
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
|
||||
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
auto dst = dbuffer;
|
||||
auto src = sbuffer;
|
||||
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
|
||||
auto tmp = rasterUnpremultiply(*src);
|
||||
auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity);
|
||||
*dst = INTERPOLATE(val, *dst, MULTIPLY(opacity, A(tmp)));
|
||||
}
|
||||
dbuffer += cmp->image.stride;
|
||||
sbuffer += cmp->recoverSfc->stride;
|
||||
}
|
||||
cmp->valid = true; //no need the subsequent composition
|
||||
} else {
|
||||
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
auto dst = dbuffer;
|
||||
for (size_t x = 0; x < w; ++x, ++dst) {
|
||||
auto tmp = rasterUnpremultiply(*dst);
|
||||
auto val = INTERPOLATE(INTERPOLATE(black, white, luma((uint8_t*)&tmp)), tmp, params->intensity);
|
||||
*dst = ALPHA_BLEND(val, A(tmp));
|
||||
}
|
||||
dbuffer += cmp->image.stride;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Tritone Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l)
|
||||
{
|
||||
/* Tritone Formula:
|
||||
if (L < 0.5) { (1 - 2L) * Shadow + 2L * Midtone }
|
||||
else { (1 - 2(L - 0.5)) * Midtone + (2(L - 0.5)) * Highlight }
|
||||
Where the L is Luminance. */
|
||||
|
||||
if (l < 128) {
|
||||
auto a = std::min(l * 2, 255);
|
||||
return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a);
|
||||
} else {
|
||||
auto a = 2 * std::max(0, l - 128);
|
||||
return ALPHA_BLEND(m, 255 - a) + ALPHA_BLEND(h, a);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void effectTritoneUpdate(RenderEffectTritone* params)
|
||||
{
|
||||
params->valid = true;
|
||||
}
|
||||
|
||||
|
||||
bool effectTritone(SwCompositor* cmp, const RenderEffectTritone* params, bool direct)
|
||||
{
|
||||
auto& bbox = cmp->bbox;
|
||||
auto w = size_t(bbox.max.x - bbox.min.x);
|
||||
auto h = size_t(bbox.max.y - bbox.min.y);
|
||||
auto shadow = cmp->recoverSfc->join(params->shadow[0], params->shadow[1], params->shadow[2], 255);
|
||||
auto midtone = cmp->recoverSfc->join(params->midtone[0], params->midtone[1], params->midtone[2], 255);
|
||||
auto highlight = cmp->recoverSfc->join(params->highlight[0], params->highlight[1], params->highlight[2], 255);
|
||||
auto opacity = cmp->opacity;
|
||||
auto luma = cmp->recoverSfc->alphas[2]; //luma function
|
||||
|
||||
TVGLOG("SW_ENGINE", "Tritone region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d %d %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->shadow[0], params->shadow[1], params->shadow[2], params->midtone[0], params->midtone[1], params->midtone[2], params->highlight[0], params->highlight[1], params->highlight[2]);
|
||||
|
||||
if (direct) {
|
||||
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
|
||||
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
auto dst = dbuffer;
|
||||
auto src = sbuffer;
|
||||
for (size_t x = 0; x < w; ++x, ++dst, ++src) {
|
||||
auto tmp = rasterUnpremultiply(*src);
|
||||
*dst = INTERPOLATE(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), *dst, MULTIPLY(opacity, A(tmp)));
|
||||
}
|
||||
dbuffer += cmp->image.stride;
|
||||
sbuffer += cmp->recoverSfc->stride;
|
||||
}
|
||||
cmp->valid = true; //no need the subsequent composition
|
||||
} else {
|
||||
auto dbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
|
||||
for (size_t y = 0; y < h; ++y) {
|
||||
auto dst = dbuffer;
|
||||
for (size_t x = 0; x < w; ++x, ++dst) {
|
||||
auto tmp = rasterUnpremultiply(*dst);
|
||||
*dst = ALPHA_BLEND(_trintone(shadow, midtone, highlight, luma((uint8_t*)&tmp)), A(tmp));
|
||||
}
|
||||
dbuffer += cmp->image.stride;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -158,7 +158,7 @@ static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, u
|
|||
}
|
||||
|
||||
|
||||
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, ui
|
|||
}
|
||||
|
||||
//2. fill the aligned memory using avx - N_32BITS_IN_128REG pixels processed at once
|
||||
//In order to avoid unneccessary avx variables declarations a check is made whether there are any iterations at all
|
||||
//In order to avoid unnecessary avx variables declarations a check is made whether there are any iterations at all
|
||||
uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
|
||||
uint32_t avxFilled = 0;
|
||||
if (iterations > 0) {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,38 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
template<typename PIXEL_T>
|
||||
static void inline cRasterTranslucentPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity)
|
||||
{
|
||||
//TODO: 64bits faster?
|
||||
if (opacity == 255) {
|
||||
for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
|
||||
*dst = *src + ALPHA_BLEND(*dst, IA(*src));
|
||||
}
|
||||
} else {
|
||||
for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
|
||||
auto tmp = ALPHA_BLEND(*src, opacity);
|
||||
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename PIXEL_T>
|
||||
static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T* src, uint32_t len, uint32_t opacity)
|
||||
{
|
||||
//TODO: 64bits faster?
|
||||
if (opacity == 255) {
|
||||
for (uint32_t x = 0; x < len; ++x, ++dst, ++src) {
|
||||
*dst = *src;
|
||||
}
|
||||
} else {
|
||||
cRasterTranslucentPixels(dst, src, len, opacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename PIXEL_T>
|
||||
static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len)
|
||||
{
|
||||
|
|
@ -60,7 +92,7 @@ static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int
|
|||
}
|
||||
|
||||
|
||||
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
|
|
@ -125,7 +157,7 @@ static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& regi
|
|||
}
|
||||
|
||||
|
||||
static bool inline cRasterABGRtoARGB(Surface* surface)
|
||||
static bool inline cRasterABGRtoARGB(RenderSurface* surface)
|
||||
{
|
||||
TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h);
|
||||
|
||||
|
|
@ -156,7 +188,7 @@ static bool inline cRasterABGRtoARGB(Surface* surface)
|
|||
}
|
||||
|
||||
|
||||
static bool inline cRasterARGBtoABGR(Surface* surface)
|
||||
static bool inline cRasterARGBtoABGR(RenderSurface* surface)
|
||||
{
|
||||
//exactly same with ABGRtoARGB
|
||||
return cRasterABGRtoARGB(surface);
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int3
|
|||
}
|
||||
|
||||
|
||||
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRle* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,17 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
Point pt;
|
||||
Point uv;
|
||||
};
|
||||
|
||||
struct Polygon
|
||||
{
|
||||
Vertex vertex[3];
|
||||
};
|
||||
|
||||
struct AALine
|
||||
{
|
||||
int32_t x[2];
|
||||
|
|
@ -64,197 +75,8 @@ static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, in
|
|||
|
||||
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
|
||||
{
|
||||
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
|
||||
return false;
|
||||
|
||||
#if 0 //Enable it when GRAYSCALE image is supported
|
||||
auto maskOp = _getMaskOp(surface->compositor->method);
|
||||
auto direct = _direct(surface->compositor->method);
|
||||
float _dudx = dudx, _dvdx = dvdx;
|
||||
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
||||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||
auto sbuf = image->buf8;
|
||||
int32_t sw = static_cast<int32_t>(image->stride);
|
||||
int32_t sh = image->h;
|
||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
int32_t vv = 0, uu = 0;
|
||||
int32_t minx = INT32_MAX, maxx = 0;
|
||||
float dx, u, v, iptr;
|
||||
SwSpan* span = nullptr; //used only when rle based.
|
||||
|
||||
if (!_arrange(image, region, yStart, yEnd)) return false;
|
||||
|
||||
//Loop through all lines in the segment
|
||||
uint32_t spanIdx = 0;
|
||||
|
||||
if (region) {
|
||||
minx = region->min.x;
|
||||
maxx = region->max.x;
|
||||
} else {
|
||||
span = image->rle->spans;
|
||||
while (span->y < yStart) {
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
|
||||
y = yStart;
|
||||
|
||||
while (y < yEnd) {
|
||||
x1 = (int32_t)_xa;
|
||||
x2 = (int32_t)_xb;
|
||||
|
||||
if (!region) {
|
||||
minx = INT32_MAX;
|
||||
maxx = 0;
|
||||
//one single row, could be consisted of multiple spans.
|
||||
while (span->y == y && spanIdx < image->rle->size) {
|
||||
if (minx > span->x) minx = span->x;
|
||||
if (maxx < span->x + span->len) maxx = span->x + span->len;
|
||||
++span;
|
||||
++spanIdx;
|
||||
}
|
||||
}
|
||||
if (x1 < minx) x1 = minx;
|
||||
if (x2 > maxx) x2 = maxx;
|
||||
|
||||
//Anti-Aliasing frames
|
||||
ay = y - aaSpans->yStart;
|
||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
||||
|
||||
//Range allowed
|
||||
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
|
||||
|
||||
//Perform subtexel pre-stepping on UV
|
||||
dx = 1 - (_xa - x1);
|
||||
u = _ua + dx * _dudx;
|
||||
v = _va + dx * _dvdx;
|
||||
|
||||
x = x1;
|
||||
|
||||
auto cmp = &surface->compositor->image.buf8[y * surface->compositor->image.stride + x1];
|
||||
auto dst = &surface->buf8[y * surface->stride + x1];
|
||||
|
||||
if (opacity == 255) {
|
||||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
if (uu >= sw) continue;
|
||||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
if (direct) {
|
||||
auto tmp = maskOp(px, *cmp, 0); //not use alpha
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
++dst;
|
||||
} else {
|
||||
*cmp = maskOp(px, *cmp, ~px);
|
||||
}
|
||||
++cmp;
|
||||
|
||||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
//range over?
|
||||
if ((uint32_t)v >= image->h) break;
|
||||
}
|
||||
} else {
|
||||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
if (uu >= sw) continue;
|
||||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
}
|
||||
|
||||
if (direct) {
|
||||
auto tmp = maskOp(MULTIPLY(px, opacity), *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
++dst;
|
||||
} else {
|
||||
auto tmp = MULTIPLY(px, opacity);
|
||||
*cmp = maskOp(tmp, *cmp, ~px);
|
||||
}
|
||||
++cmp;
|
||||
|
||||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
//range over?
|
||||
if ((uint32_t)v >= image->h) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Step along both edges
|
||||
_xa += _dxdya;
|
||||
_xb += _dxdyb;
|
||||
_ua += _dudya;
|
||||
_va += _dvdya;
|
||||
|
||||
if (!region && spanIdx >= image->rle->size) break;
|
||||
|
||||
++y;
|
||||
}
|
||||
xa = _xa;
|
||||
xb = _xb;
|
||||
ua = _ua;
|
||||
va = _va;
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -265,9 +87,8 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||
auto sbuf = image->buf32;
|
||||
auto dbuf = surface->buf32;
|
||||
int32_t sw = static_cast<int32_t>(image->stride);
|
||||
int32_t sh = image->h;
|
||||
int32_t dw = surface->stride;
|
||||
int32_t sw = static_cast<int32_t>(image->w);
|
||||
int32_t sh = static_cast<int32_t>(image->h);
|
||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
int32_t vv = 0, uu = 0;
|
||||
int32_t minx = INT32_MAX, maxx = 0;
|
||||
|
|
@ -324,7 +145,7 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
u = _ua + dx * _dudx;
|
||||
v = _va + dx * _dvdx;
|
||||
|
||||
buf = dbuf + ((y * dw) + x1);
|
||||
buf = dbuf + ((y * surface->stride) + x1);
|
||||
|
||||
x = x1;
|
||||
|
||||
|
|
@ -332,32 +153,32 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
if (uu >= sw) continue;
|
||||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
px = *(sbuf + (vv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
|
|
@ -368,39 +189,37 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
//range over?
|
||||
if ((uint32_t)v >= image->h) break;
|
||||
}
|
||||
} else {
|
||||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
if (uu >= sw) continue;
|
||||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
||||
|
||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
px = *(sbuf + (vv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
|
|
@ -412,8 +231,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
|||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
//range over?
|
||||
if ((uint32_t)v >= image->h) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -442,9 +259,8 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||
auto sbuf = image->buf32;
|
||||
auto dbuf = surface->buf32;
|
||||
int32_t sw = static_cast<int32_t>(image->stride);
|
||||
int32_t sh = image->h;
|
||||
int32_t dw = surface->stride;
|
||||
int32_t sw = static_cast<int32_t>(image->w);
|
||||
int32_t sh = static_cast<int32_t>(image->h);
|
||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||
int32_t vv = 0, uu = 0;
|
||||
int32_t minx = INT32_MAX, maxx = 0;
|
||||
|
|
@ -506,7 +322,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
u = _ua + dx * _dudx;
|
||||
v = _va + dx * _dvdx;
|
||||
|
||||
buf = dbuf + ((y * dw) + x1);
|
||||
buf = dbuf + ((y * surface->stride) + x1);
|
||||
|
||||
x = x1;
|
||||
|
||||
|
|
@ -516,32 +332,32 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
//Draw horizontal line
|
||||
while (x++ < x2) {
|
||||
uu = (int) u;
|
||||
if (uu >= sw) continue;
|
||||
vv = (int) v;
|
||||
if (vv >= sh) continue;
|
||||
|
||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
||||
|
||||
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
|
||||
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
px = *(sbuf + (vv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
|
|
@ -559,8 +375,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
//range over?
|
||||
if ((uint32_t)v >= image->h) break;
|
||||
}
|
||||
} else {
|
||||
//Draw horizontal line
|
||||
|
|
@ -568,30 +382,30 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
uu = (int) u;
|
||||
vv = (int) v;
|
||||
|
||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
||||
|
||||
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
|
||||
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
|
||||
iru = uu + 1;
|
||||
irv = vv + 1;
|
||||
|
||||
if (vv >= sh) continue;
|
||||
|
||||
px = *(sbuf + (vv * sw) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* right pixel */
|
||||
int px2 = *(sbuf + (vv * sw) + iru);
|
||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
||||
px = INTERPOLATE(px, px2, ar);
|
||||
}
|
||||
/* vertical interpolate */
|
||||
if (irv < sh) {
|
||||
/* bottom pixel */
|
||||
int px2 = *(sbuf + (irv * sw) + uu);
|
||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
||||
|
||||
/* horizontal interpolate */
|
||||
if (iru < sw) {
|
||||
/* bottom right pixel */
|
||||
int px3 = *(sbuf + (irv * sw) + iru);
|
||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
||||
px2 = INTERPOLATE(px2, px3, ar);
|
||||
}
|
||||
px = INTERPOLATE(px, px2, ab);
|
||||
|
|
@ -609,8 +423,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
|||
//Step UV horizontally
|
||||
u += _dudx;
|
||||
v += _dvdx;
|
||||
//range over?
|
||||
if ((uint32_t)v >= image->h) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -675,7 +487,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
auto denom = ((x[2] - x[0]) * (y[1] - y[0]) - (x[1] - x[0]) * (y[2] - y[0]));
|
||||
|
||||
//Skip poly if it's an infinitely thin line
|
||||
if (mathZero(denom)) return;
|
||||
if (tvg::zero(denom)) return;
|
||||
|
||||
denom = 1 / denom; //Reciprocal for speeding up
|
||||
dudx = ((u[2] - u[0]) * (y[1] - y[0]) - (u[1] - u[0]) * (y[2] - y[0])) * denom;
|
||||
|
|
@ -691,8 +503,8 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
|||
//Determine which side of the polygon the longer edge is on
|
||||
auto side = (dxdy[1] > dxdy[0]) ? true : false;
|
||||
|
||||
if (mathEqual(y[0], y[1])) side = x[0] > x[1];
|
||||
if (mathEqual(y[1], y[2])) side = x[2] > x[1];
|
||||
if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
|
||||
if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
|
||||
|
||||
auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
|
||||
auto compositing = _compositing(surface); //Composition required
|
||||
|
|
@ -868,10 +680,8 @@ static void _calcVertCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t re
|
|||
|
||||
static void _calcHorizCoverage(AALine *lines, int32_t eidx, int32_t y, int32_t x, int32_t x2)
|
||||
{
|
||||
if (lines[y].length[eidx] < abs(x - x2)) {
|
||||
lines[y].length[eidx] = abs(x - x2);
|
||||
lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
|
||||
}
|
||||
lines[y].length[eidx] = abs(x - x2);
|
||||
lines[y].coverage[eidx] = (255 / (lines[y].length[eidx] + 1));
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -897,9 +707,14 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
|
|||
ptx[1] = tx[1]; \
|
||||
} while (0)
|
||||
|
||||
struct Point
|
||||
{
|
||||
int32_t x, y;
|
||||
};
|
||||
|
||||
int32_t y = 0;
|
||||
SwPoint pEdge = {-1, -1}; //previous edge point
|
||||
SwPoint edgeDiff = {0, 0}; //temporary used for point distance
|
||||
Point pEdge = {-1, -1}; //previous edge point
|
||||
Point edgeDiff = {0, 0}; //temporary used for point distance
|
||||
|
||||
/* store bigger to tx[0] between prev and current edge's x positions. */
|
||||
int32_t tx[2] = {0, 0};
|
||||
|
|
@ -1024,6 +839,7 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
|
|||
|
||||
static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
||||
{
|
||||
auto end = surface->buf32 + surface->h * surface->stride;
|
||||
auto y = aaSpans->yStart;
|
||||
uint32_t pixel;
|
||||
uint32_t* dst;
|
||||
|
|
@ -1044,8 +860,13 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
|||
dst = surface->buf32 + (offset + line->x[0]);
|
||||
if (line->x[0] > 1) pixel = *(dst - 1);
|
||||
else pixel = *dst;
|
||||
|
||||
pos = 1;
|
||||
|
||||
//exceptional handling. out of memory bound.
|
||||
if (dst + line->length[0] >= end) {
|
||||
pos += (dst + line->length[0] - end);
|
||||
}
|
||||
|
||||
while (pos <= line->length[0]) {
|
||||
*dst = INTERPOLATE(*dst, pixel, line->coverage[0] * pos);
|
||||
++dst;
|
||||
|
|
@ -1053,17 +874,21 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
|||
}
|
||||
|
||||
//Right edge
|
||||
dst = surface->buf32 + (offset + line->x[1] - 1);
|
||||
dst = surface->buf32 + offset + line->x[1] - 1;
|
||||
|
||||
if (line->x[1] < (int32_t)(surface->w - 1)) pixel = *(dst + 1);
|
||||
else pixel = *dst;
|
||||
pos = line->length[1];
|
||||
|
||||
pos = width;
|
||||
while ((int32_t)(width - line->length[1]) < pos) {
|
||||
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * (line->length[1] - (width - pos))));
|
||||
//exceptional handling. out of memory bound.
|
||||
if (dst - pos < surface->buf32) --pos;
|
||||
|
||||
while (pos > 0) {
|
||||
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
|
||||
--dst;
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
y++;
|
||||
}
|
||||
|
||||
|
|
@ -1084,7 +909,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
|||
| / |
|
||||
3 -- 2
|
||||
*/
|
||||
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix* transform, const SwBBox* region, uint8_t opacity)
|
||||
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity)
|
||||
{
|
||||
if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
|
||||
|
|
@ -1092,7 +917,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
}
|
||||
|
||||
//Exceptions: No dedicated drawing area?
|
||||
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
|
||||
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
|
||||
|
||||
/* Prepare vertices.
|
||||
shift XY coordinates to match the sub-pixeling technique. */
|
||||
|
|
@ -1104,7 +929,7 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
|
||||
float ys = FLT_MAX, ye = -1.0f;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (transform) vertices[i].pt *= *transform;
|
||||
vertices[i].pt *= transform;
|
||||
if (vertices[i].pt.y < ys) ys = vertices[i].pt.y;
|
||||
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
|
||||
}
|
||||
|
|
@ -1135,68 +960,3 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
|||
#endif
|
||||
return _apply(surface, aaSpans);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Provide any number of triangles to draw a mesh using the supplied image.
|
||||
Indexes are not used, so each triangle (Polygon) vertex has to be defined, even if they copy the previous one.
|
||||
Example:
|
||||
|
||||
0 -- 1 0 -- 1 0
|
||||
| / | --> | / / |
|
||||
| / | | / / |
|
||||
2 -- 3 2 1 -- 2
|
||||
|
||||
Should provide two Polygons, one for each triangle.
|
||||
// TODO: region?
|
||||
*/
|
||||
static bool _rasterTexmapPolygonMesh(SwSurface* surface, const SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox* region, uint8_t opacity)
|
||||
{
|
||||
if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon mesh!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//Exceptions: No dedicated drawing area?
|
||||
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return false;
|
||||
|
||||
// Step polygons once to transform
|
||||
auto transformedTris = (Polygon*)malloc(sizeof(Polygon) * mesh->triangleCnt);
|
||||
float ys = FLT_MAX, ye = -1.0f;
|
||||
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
|
||||
transformedTris[i] = mesh->triangles[i];
|
||||
transformedTris[i].vertex[0].pt *= *transform;
|
||||
transformedTris[i].vertex[1].pt *= *transform;
|
||||
transformedTris[i].vertex[2].pt *= *transform;
|
||||
|
||||
if (transformedTris[i].vertex[0].pt.y < ys) ys = transformedTris[i].vertex[0].pt.y;
|
||||
else if (transformedTris[i].vertex[0].pt.y > ye) ye = transformedTris[i].vertex[0].pt.y;
|
||||
if (transformedTris[i].vertex[1].pt.y < ys) ys = transformedTris[i].vertex[1].pt.y;
|
||||
else if (transformedTris[i].vertex[1].pt.y > ye) ye = transformedTris[i].vertex[1].pt.y;
|
||||
if (transformedTris[i].vertex[2].pt.y < ys) ys = transformedTris[i].vertex[2].pt.y;
|
||||
else if (transformedTris[i].vertex[2].pt.y > ye) ye = transformedTris[i].vertex[2].pt.y;
|
||||
|
||||
// Convert normalized UV coordinates to image coordinates
|
||||
transformedTris[i].vertex[0].uv.x *= (float)image->w;
|
||||
transformedTris[i].vertex[0].uv.y *= (float)image->h;
|
||||
transformedTris[i].vertex[1].uv.x *= (float)image->w;
|
||||
transformedTris[i].vertex[1].uv.y *= (float)image->h;
|
||||
transformedTris[i].vertex[2].uv.x *= (float)image->w;
|
||||
transformedTris[i].vertex[2].uv.y *= (float)image->h;
|
||||
}
|
||||
|
||||
// Get AA spans and step polygons again to draw
|
||||
if (auto aaSpans = _AASpans(ys, ye, image, region)) {
|
||||
for (uint32_t i = 0; i < mesh->triangleCnt; i++) {
|
||||
_rasterPolygonImage(surface, image, region, transformedTris[i], aaSpans, opacity);
|
||||
}
|
||||
#if 0
|
||||
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
|
||||
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
|
||||
}
|
||||
#endif
|
||||
_apply(surface, aaSpans);
|
||||
}
|
||||
free(transformedTris);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef THORVG_SW_OPENMP_SUPPORT
|
||||
#include <omp.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSwCommon.h"
|
||||
|
|
@ -38,8 +41,8 @@ struct SwTask : Task
|
|||
{
|
||||
SwSurface* surface = nullptr;
|
||||
SwMpool* mpool = nullptr;
|
||||
SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
|
||||
Matrix* transform = nullptr;
|
||||
SwBBox bbox; //Rendering Region
|
||||
Matrix transform;
|
||||
Array<RenderData> clips;
|
||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||
uint8_t opacity;
|
||||
|
|
@ -65,13 +68,8 @@ struct SwTask : Task
|
|||
}
|
||||
|
||||
virtual void dispose() = 0;
|
||||
virtual bool clip(SwRleData* target) = 0;
|
||||
virtual SwRleData* rle() = 0;
|
||||
|
||||
virtual ~SwTask()
|
||||
{
|
||||
free(transform);
|
||||
}
|
||||
virtual bool clip(SwRle* target) = 0;
|
||||
virtual ~SwTask() {}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -95,40 +93,32 @@ struct SwShapeTask : SwTask
|
|||
if (!rshape->stroke) return 0.0f;
|
||||
|
||||
auto width = rshape->stroke->width;
|
||||
if (mathZero(width)) return 0.0f;
|
||||
if (tvg::zero(width)) return 0.0f;
|
||||
|
||||
if (!rshape->stroke->fill && (MULTIPLY(rshape->stroke->color[3], opacity) == 0)) return 0.0f;
|
||||
if (mathZero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
|
||||
if (tvg::zero(rshape->stroke->trim.begin - rshape->stroke->trim.end)) return 0.0f;
|
||||
|
||||
if (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12));
|
||||
else return width;
|
||||
return (width * sqrt(transform.e11 * transform.e11 + transform.e12 * transform.e12));
|
||||
}
|
||||
|
||||
|
||||
bool clip(SwRleData* target) override
|
||||
bool clip(SwRle* target) override
|
||||
{
|
||||
if (shape.fastTrack) rleClipRect(target, &bbox);
|
||||
else if (shape.rle) rleClipPath(target, shape.rle);
|
||||
else return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SwRleData* rle() override
|
||||
{
|
||||
if (!shape.rle && shape.fastTrack) {
|
||||
shape.rle = rleRender(&shape.bbox);
|
||||
}
|
||||
return shape.rle;
|
||||
if (shape.fastTrack) return rleClip(target, &bbox);
|
||||
else if (shape.rle) return rleClip(target, shape.rle);
|
||||
return false;
|
||||
}
|
||||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
if (opacity == 0 && !clipper) return; //Invisible
|
||||
//Invisible
|
||||
if (opacity == 0 && !clipper) {
|
||||
bbox.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
auto strokeWidth = validStrokeWidth();
|
||||
bool visibleFill = false;
|
||||
auto clipRegion = bbox;
|
||||
SwBBox renderRegion{};
|
||||
auto visibleFill = false;
|
||||
|
||||
//This checks also for the case, if the invisible shape turned to visible by alpha.
|
||||
auto prepareShape = false;
|
||||
|
|
@ -140,10 +130,11 @@ struct SwShapeTask : SwTask
|
|||
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||
alpha = MULTIPLY(alpha, opacity);
|
||||
visibleFill = (alpha > 0 || rshape->fill);
|
||||
shapeReset(&shape);
|
||||
if (visibleFill || clipper) {
|
||||
shapeReset(&shape);
|
||||
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) {
|
||||
if (!shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
|
||||
visibleFill = false;
|
||||
renderRegion.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -164,8 +155,8 @@ struct SwShapeTask : SwTask
|
|||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) {
|
||||
if (strokeWidth > 0.0f) {
|
||||
shapeResetStroke(&shape, rshape, transform);
|
||||
if (!shapeGenStrokeRle(&shape, rshape, transform, clipRegion, bbox, mpool, tid)) goto err;
|
||||
|
||||
if (!shapeGenStrokeRle(&shape, rshape, transform, bbox, renderRegion, mpool, tid)) goto err;
|
||||
if (auto fill = rshape->strokeFill()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||
if (ctable) shapeResetStrokeFill(&shape);
|
||||
|
|
@ -184,14 +175,16 @@ struct SwShapeTask : SwTask
|
|||
//Clip Path
|
||||
for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
|
||||
auto clipper = static_cast<SwTask*>(*clip);
|
||||
//Clip shape rle
|
||||
if (shape.rle && !clipper->clip(shape.rle)) goto err;
|
||||
//Clip stroke rle
|
||||
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err;
|
||||
if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip shape rle
|
||||
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; //Clip stroke rle
|
||||
}
|
||||
|
||||
bbox = renderRegion; //sync
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
bbox.reset();
|
||||
shapeReset(&shape);
|
||||
shapeDelOutline(&shape, mpool, tid);
|
||||
}
|
||||
|
|
@ -203,77 +196,17 @@ struct SwShapeTask : SwTask
|
|||
};
|
||||
|
||||
|
||||
struct SwSceneTask : SwTask
|
||||
{
|
||||
Array<RenderData> scene; //list of paints render data (SwTask)
|
||||
SwRleData* sceneRle = nullptr;
|
||||
|
||||
bool clip(SwRleData* target) override
|
||||
{
|
||||
//Only one shape
|
||||
if (scene.count == 1) {
|
||||
return static_cast<SwTask*>(*scene.data)->clip(target);
|
||||
}
|
||||
|
||||
//More than one shapes
|
||||
if (sceneRle) rleClipPath(target, sceneRle);
|
||||
else TVGLOG("SW_ENGINE", "No clippers in a scene?");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SwRleData* rle() override
|
||||
{
|
||||
return sceneRle;
|
||||
}
|
||||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
//TODO: Skip the run if the scene hans't changed.
|
||||
if (!sceneRle) sceneRle = static_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
|
||||
else rleReset(sceneRle);
|
||||
|
||||
//Merge shapes if it has more than one shapes
|
||||
if (scene.count > 1) {
|
||||
//Merge first two clippers
|
||||
auto clipper1 = static_cast<SwTask*>(*scene.data);
|
||||
auto clipper2 = static_cast<SwTask*>(*(scene.data + 1));
|
||||
|
||||
rleMerge(sceneRle, clipper1->rle(), clipper2->rle());
|
||||
|
||||
//Unify the remained clippers
|
||||
for (auto rd = scene.begin() + 2; rd < scene.end(); ++rd) {
|
||||
auto clipper = static_cast<SwTask*>(*rd);
|
||||
rleMerge(sceneRle, sceneRle, clipper->rle());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() override
|
||||
{
|
||||
rleFree(sceneRle);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SwImageTask : SwTask
|
||||
{
|
||||
SwImage image;
|
||||
Surface* source; //Image source
|
||||
const RenderMesh* mesh = nullptr; //Should be valid ptr in action
|
||||
RenderSurface* source; //Image source
|
||||
|
||||
bool clip(SwRleData* target) override
|
||||
bool clip(SwRle* target) override
|
||||
{
|
||||
TVGERR("SW_ENGINE", "Image is used as ClipPath?");
|
||||
return true;
|
||||
}
|
||||
|
||||
SwRleData* rle() override
|
||||
{
|
||||
TVGERR("SW_ENGINE", "Image is used as Scene ClipPath?");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
auto clipRegion = bbox;
|
||||
|
|
@ -293,10 +226,9 @@ struct SwImageTask : SwTask
|
|||
imageReset(&image);
|
||||
if (!image.data || image.w == 0 || image.h == 0) goto end;
|
||||
|
||||
if (!imagePrepare(&image, mesh, transform, clipRegion, bbox, mpool, tid)) goto end;
|
||||
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
|
||||
|
||||
// TODO: How do we clip the triangle mesh? Only clip non-meshed images for now
|
||||
if (mesh->triangleCnt == 0 && clips.count > 0) {
|
||||
if (clips.count > 0) {
|
||||
if (!imageGenRle(&image, bbox, false)) goto end;
|
||||
if (image.rle) {
|
||||
//Clear current task memorypool here if the clippers would use the same memory pool
|
||||
|
|
@ -336,7 +268,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);
|
||||
|
|
@ -348,7 +280,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);
|
||||
|
|
@ -480,7 +412,7 @@ bool SwRenderer::renderImage(RenderData data)
|
|||
|
||||
if (task->opacity == 0) return true;
|
||||
|
||||
return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity);
|
||||
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -512,27 +444,18 @@ bool SwRenderer::blend(BlendMethod method)
|
|||
surface->blendMethod = method;
|
||||
|
||||
switch (method) {
|
||||
case BlendMethod::Add:
|
||||
surface->blender = opBlendAdd;
|
||||
break;
|
||||
case BlendMethod::Screen:
|
||||
surface->blender = opBlendScreen;
|
||||
case BlendMethod::Normal:
|
||||
surface->blender = nullptr;
|
||||
break;
|
||||
case BlendMethod::Multiply:
|
||||
surface->blender = opBlendMultiply;
|
||||
break;
|
||||
case BlendMethod::Screen:
|
||||
surface->blender = opBlendScreen;
|
||||
break;
|
||||
case BlendMethod::Overlay:
|
||||
surface->blender = opBlendOverlay;
|
||||
break;
|
||||
case BlendMethod::Difference:
|
||||
surface->blender = opBlendDifference;
|
||||
break;
|
||||
case BlendMethod::Exclusion:
|
||||
surface->blender = opBlendExclusion;
|
||||
break;
|
||||
case BlendMethod::SrcOver:
|
||||
surface->blender = opBlendSrcOver;
|
||||
break;
|
||||
case BlendMethod::Darken:
|
||||
surface->blender = opBlendDarken;
|
||||
break;
|
||||
|
|
@ -551,7 +474,17 @@ bool SwRenderer::blend(BlendMethod method)
|
|||
case BlendMethod::SoftLight:
|
||||
surface->blender = opBlendSoftLight;
|
||||
break;
|
||||
case BlendMethod::Difference:
|
||||
surface->blender = opBlendDifference;
|
||||
break;
|
||||
case BlendMethod::Exclusion:
|
||||
surface->blender = opBlendExclusion;
|
||||
break;
|
||||
case BlendMethod::Add:
|
||||
surface->blender = opBlendAdd;
|
||||
break;
|
||||
default:
|
||||
TVGLOG("SW_ENGINE", "Non supported blending option = %d", (int) method);
|
||||
surface->blender = nullptr;
|
||||
break;
|
||||
}
|
||||
|
|
@ -565,7 +498,7 @@ RenderRegion SwRenderer::region(RenderData data)
|
|||
}
|
||||
|
||||
|
||||
bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity)
|
||||
bool SwRenderer::beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity)
|
||||
{
|
||||
if (!cmp) return false;
|
||||
auto p = static_cast<SwCompositor*>(cmp);
|
||||
|
|
@ -603,13 +536,60 @@ bool SwRenderer::mempool(bool shared)
|
|||
}
|
||||
|
||||
|
||||
const Surface* SwRenderer::mainSurface()
|
||||
const RenderSurface* SwRenderer::mainSurface()
|
||||
{
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
||||
Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
|
||||
SwSurface* SwRenderer::request(int channelSize, bool square)
|
||||
{
|
||||
SwSurface* cmp = nullptr;
|
||||
uint32_t w, h;
|
||||
|
||||
if (square) {
|
||||
//Same Dimensional Size is demanded for the Post Processing Fast Flipping
|
||||
w = h = std::max(surface->w, surface->h);
|
||||
} else {
|
||||
w = surface->w;
|
||||
h = surface->h;
|
||||
}
|
||||
|
||||
//Use cached data
|
||||
for (auto p = compositors.begin(); p < compositors.end(); ++p) {
|
||||
auto cur = *p;
|
||||
if (cur->compositor->valid && cur->compositor->image.channelSize == channelSize) {
|
||||
if (w == cur->w && h == cur->h) {
|
||||
cmp = *p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//New Composition
|
||||
if (!cmp) {
|
||||
//Inherits attributes from main surface
|
||||
cmp = new SwSurface(surface);
|
||||
cmp->compositor = new SwCompositor;
|
||||
cmp->compositor->image.data = (pixel_t*)malloc(channelSize * w * h);
|
||||
cmp->w = cmp->compositor->image.w = w;
|
||||
cmp->h = cmp->compositor->image.h = h;
|
||||
cmp->stride = cmp->compositor->image.stride = w;
|
||||
cmp->compositor->image.direct = true;
|
||||
cmp->compositor->valid = true;
|
||||
cmp->channelSize = cmp->compositor->image.channelSize = channelSize;
|
||||
|
||||
compositors.push(cmp);
|
||||
}
|
||||
|
||||
//Sync. This may have been modified by post-processing.
|
||||
cmp->data = cmp->compositor->image.data;
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
|
||||
RenderCompositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags)
|
||||
{
|
||||
auto x = region.x;
|
||||
auto y = region.y;
|
||||
|
|
@ -621,35 +601,16 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
|
|||
//Out of boundary
|
||||
if (x >= sw || y >= sh || x + w < 0 || y + h < 0) return nullptr;
|
||||
|
||||
SwSurface* cmp = nullptr;
|
||||
|
||||
auto reqChannelSize = CHANNEL_SIZE(cs);
|
||||
|
||||
//Use cached data
|
||||
for (auto p = compositors.begin(); p < compositors.end(); ++p) {
|
||||
if ((*p)->compositor->valid && (*p)->compositor->image.channelSize == reqChannelSize) {
|
||||
cmp = *p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//New Composition
|
||||
if (!cmp) {
|
||||
//Inherits attributes from main surface
|
||||
cmp = new SwSurface(surface);
|
||||
cmp->compositor = new SwCompositor;
|
||||
|
||||
//TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h)
|
||||
cmp->compositor->image.data = (pixel_t*)malloc(reqChannelSize * surface->stride * surface->h);
|
||||
cmp->channelSize = cmp->compositor->image.channelSize = reqChannelSize;
|
||||
|
||||
compositors.push(cmp);
|
||||
}
|
||||
auto cmp = request(CHANNEL_SIZE(cs), (flags & CompositionFlag::PostProcessing));
|
||||
|
||||
//Boundary Check
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (x + w > sw) w = (sw - x);
|
||||
if (y + h > sh) h = (sh - y);
|
||||
|
||||
if (w == 0 || h == 0) return nullptr;
|
||||
|
||||
cmp->compositor->recoverSfc = surface;
|
||||
cmp->compositor->recoverCmp = surface->compositor;
|
||||
cmp->compositor->valid = false;
|
||||
|
|
@ -657,16 +618,11 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
|
|||
cmp->compositor->bbox.min.y = y;
|
||||
cmp->compositor->bbox.max.x = x + w;
|
||||
cmp->compositor->bbox.max.y = y + h;
|
||||
cmp->compositor->image.stride = surface->stride;
|
||||
cmp->compositor->image.w = surface->w;
|
||||
cmp->compositor->image.h = surface->h;
|
||||
cmp->compositor->image.direct = true;
|
||||
|
||||
cmp->data = cmp->compositor->image.data;
|
||||
cmp->w = cmp->compositor->image.w;
|
||||
cmp->h = cmp->compositor->image.h;
|
||||
|
||||
rasterClear(cmp, x, y, w, h);
|
||||
/* TODO: Currently, only blending might work.
|
||||
Blending and composition must be handled together. */
|
||||
auto color = (surface->blender && !surface->compositor) ? 0x00ffffff : 0x00000000;
|
||||
rasterClear(cmp, x, y, w, h, color);
|
||||
|
||||
//Switch render target
|
||||
surface = cmp;
|
||||
|
|
@ -675,26 +631,89 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
|
|||
}
|
||||
|
||||
|
||||
bool SwRenderer::endComposite(Compositor* cmp)
|
||||
bool SwRenderer::endComposite(RenderCompositor* cmp)
|
||||
{
|
||||
if (!cmp) return false;
|
||||
|
||||
auto p = static_cast<SwCompositor*>(cmp);
|
||||
p->valid = true;
|
||||
|
||||
//Recover Context
|
||||
surface = p->recoverSfc;
|
||||
surface->compositor = p->recoverCmp;
|
||||
|
||||
//only invalid (currently used) surface can be composited
|
||||
if (p->valid) return true;
|
||||
p->valid = true;
|
||||
|
||||
//Default is alpha blending
|
||||
if (p->method == CompositeMethod::None) {
|
||||
return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity);
|
||||
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void SwRenderer::prepare(RenderEffect* effect, const Matrix& transform)
|
||||
{
|
||||
switch (effect->type) {
|
||||
case SceneEffect::GaussianBlur: effectGaussianBlurUpdate(static_cast<RenderEffectGaussianBlur*>(effect), transform); break;
|
||||
case SceneEffect::DropShadow: effectDropShadowUpdate(static_cast<RenderEffectDropShadow*>(effect), transform); break;
|
||||
case SceneEffect::Fill: effectFillUpdate(static_cast<RenderEffectFill*>(effect)); break;
|
||||
case SceneEffect::Tint: effectTintUpdate(static_cast<RenderEffectTint*>(effect)); break;
|
||||
case SceneEffect::Tritone: effectTritoneUpdate(static_cast<RenderEffectTritone*>(effect)); break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::region(RenderEffect* effect)
|
||||
{
|
||||
switch (effect->type) {
|
||||
case SceneEffect::GaussianBlur: return effectGaussianBlurRegion(static_cast<RenderEffectGaussianBlur*>(effect));
|
||||
case SceneEffect::DropShadow: return effectDropShadowRegion(static_cast<RenderEffectDropShadow*>(effect));
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, bool direct)
|
||||
{
|
||||
auto p = static_cast<SwCompositor*>(cmp);
|
||||
|
||||
if (p->image.channelSize != sizeof(uint32_t)) {
|
||||
TVGERR("SW_ENGINE", "Not supported grayscale Gaussian Blur!");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (effect->type) {
|
||||
case SceneEffect::GaussianBlur: {
|
||||
return effectGaussianBlur(p, request(surface->channelSize, true), static_cast<const RenderEffectGaussianBlur*>(effect));
|
||||
}
|
||||
case SceneEffect::DropShadow: {
|
||||
auto cmp1 = request(surface->channelSize, true);
|
||||
cmp1->compositor->valid = false;
|
||||
auto cmp2 = request(surface->channelSize, true);
|
||||
SwSurface* surfaces[] = {cmp1, cmp2};
|
||||
auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), direct);
|
||||
cmp1->compositor->valid = true;
|
||||
return ret;
|
||||
}
|
||||
case SceneEffect::Fill: {
|
||||
return effectFill(p, static_cast<const RenderEffectFill*>(effect), direct);
|
||||
}
|
||||
case SceneEffect::Tint: {
|
||||
return effectTint(p, static_cast<const RenderEffectTint*>(effect), direct);
|
||||
}
|
||||
case SceneEffect::Tritone: {
|
||||
return effectTritone(p, static_cast<const RenderEffectTritone*>(effect), direct);
|
||||
}
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ColorSpace SwRenderer::colorSpace()
|
||||
{
|
||||
if (surface) return surface->cs;
|
||||
|
|
@ -714,7 +733,7 @@ void SwRenderer::dispose(RenderData data)
|
|||
}
|
||||
|
||||
|
||||
void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
{
|
||||
if (!surface) return task;
|
||||
if (flags == RenderUpdateFlag::None) return task;
|
||||
|
|
@ -727,29 +746,20 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
|
|||
}
|
||||
|
||||
task->clips = clips;
|
||||
|
||||
if (transform) {
|
||||
if (!task->transform) task->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
|
||||
*task->transform = transform->m;
|
||||
} else {
|
||||
if (task->transform) free(task->transform);
|
||||
task->transform = nullptr;
|
||||
}
|
||||
|
||||
task->transform = transform;
|
||||
|
||||
//zero size?
|
||||
if (task->transform) {
|
||||
if (task->transform->e11 == 0.0f && task->transform->e12 == 0.0f) return task; //zero width
|
||||
if (task->transform->e21 == 0.0f && task->transform->e22 == 0.0f) return task; //zero height
|
||||
}
|
||||
if (task->transform.e11 == 0.0f && task->transform.e12 == 0.0f) return task; //zero width
|
||||
if (task->transform.e21 == 0.0f && task->transform.e22 == 0.0f) return task; //zero height
|
||||
|
||||
task->opacity = opacity;
|
||||
task->surface = surface;
|
||||
task->mpool = mpool;
|
||||
task->flags = flags;
|
||||
task->bbox.min.x = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
|
||||
task->bbox.min.y = mathMax(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
|
||||
task->bbox.max.x = mathMin(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
|
||||
task->bbox.max.y = mathMin(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
|
||||
task->bbox.min.x = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.x));
|
||||
task->bbox.min.y = std::max(static_cast<SwCoord>(0), static_cast<SwCoord>(vport.y));
|
||||
task->bbox.max.x = std::min(static_cast<SwCoord>(surface->w), static_cast<SwCoord>(vport.x + vport.w));
|
||||
task->bbox.max.y = std::min(static_cast<SwCoord>(surface->h), static_cast<SwCoord>(vport.y + vport.h));
|
||||
|
||||
if (!task->pushed) {
|
||||
task->pushed = true;
|
||||
|
|
@ -762,7 +772,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform,
|
|||
}
|
||||
|
||||
|
||||
RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
RenderData SwRenderer::prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwImageTask*>(data);
|
||||
|
|
@ -770,33 +780,12 @@ RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderD
|
|||
else task->done();
|
||||
|
||||
task->source = surface;
|
||||
task->mesh = mesh;
|
||||
|
||||
return prepareCommon(task, transform, clips, opacity, flags);
|
||||
}
|
||||
|
||||
|
||||
RenderData SwRenderer::prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwSceneTask*>(data);
|
||||
if (!task) task = new SwSceneTask;
|
||||
else task->done();
|
||||
|
||||
task->scene = scene;
|
||||
|
||||
//TODO: Failed threading them. It would be better if it's possible.
|
||||
//See: https://github.com/thorvg/thorvg/issues/1409
|
||||
//Guarantee composition targets get ready.
|
||||
for (auto task = scene.begin(); task < scene.end(); ++task) {
|
||||
static_cast<SwTask*>(*task)->done();
|
||||
}
|
||||
|
||||
return prepareCommon(task, transform, clips, opacity, flags);
|
||||
}
|
||||
|
||||
|
||||
RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
|
||||
RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwShapeTask*>(data);
|
||||
|
|
@ -834,6 +823,10 @@ bool SwRenderer::init(uint32_t threads)
|
|||
|
||||
int32_t SwRenderer::init()
|
||||
{
|
||||
#ifdef THORVG_SW_OPENMP_SUPPORT
|
||||
omp_set_num_threads(TaskScheduler::threads());
|
||||
#endif
|
||||
|
||||
return initEngineCnt;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,9 +36,8 @@ namespace tvg
|
|||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
public:
|
||||
RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
|
||||
RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
|
||||
RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
|
||||
RenderData prepare(const RenderShape& rshape, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) override;
|
||||
RenderData prepare(RenderSurface* surface, RenderData data, const Matrix& transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) override;
|
||||
bool preRender() override;
|
||||
bool renderShape(RenderData data) override;
|
||||
bool renderImage(RenderData data) override;
|
||||
|
|
@ -49,18 +48,22 @@ public:
|
|||
bool viewport(const RenderRegion& vp) override;
|
||||
bool blend(BlendMethod method) override;
|
||||
ColorSpace colorSpace() override;
|
||||
const Surface* mainSurface() override;
|
||||
const RenderSurface* mainSurface() override;
|
||||
|
||||
bool clear() override;
|
||||
bool sync() override;
|
||||
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
|
||||
bool mempool(bool shared);
|
||||
|
||||
Compositor* target(const RenderRegion& region, ColorSpace cs) override;
|
||||
bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) override;
|
||||
bool endComposite(Compositor* cmp) override;
|
||||
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
|
||||
bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
|
||||
bool endComposite(RenderCompositor* cmp) override;
|
||||
void clearCompositors();
|
||||
|
||||
void prepare(RenderEffect* effect, const Matrix& transform) override;
|
||||
bool region(RenderEffect* effect) override;
|
||||
bool render(RenderCompositor* cmp, const RenderEffect* effect, bool direct) override;
|
||||
|
||||
static SwRenderer* gen();
|
||||
static bool init(uint32_t threads);
|
||||
static int32_t init();
|
||||
|
|
@ -77,7 +80,8 @@ private:
|
|||
SwRenderer();
|
||||
~SwRenderer();
|
||||
|
||||
RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
|
||||
SwSurface* request(int channelSize, bool square);
|
||||
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,7 +188,6 @@
|
|||
* http://www.freetype.org
|
||||
*/
|
||||
|
||||
#include <setjmp.h>
|
||||
#include <limits.h>
|
||||
#include <memory.h>
|
||||
#include "tvgSwCommon.h"
|
||||
|
|
@ -217,7 +216,7 @@ struct Cell
|
|||
|
||||
struct RleWorker
|
||||
{
|
||||
SwRleData* rle;
|
||||
SwRle* rle;
|
||||
|
||||
SwPoint cellPos;
|
||||
SwPoint cellMin;
|
||||
|
|
@ -235,6 +234,7 @@ struct RleWorker
|
|||
SwPoint pos;
|
||||
|
||||
SwPoint bezStack[32 * 3 + 1];
|
||||
SwPoint lineStack[32 + 1];
|
||||
int levStack[32];
|
||||
|
||||
SwOutline* outline;
|
||||
|
|
@ -242,8 +242,6 @@ struct RleWorker
|
|||
int bandSize;
|
||||
int bandShoot;
|
||||
|
||||
jmp_buf jmpBuf;
|
||||
|
||||
void* buffer;
|
||||
long bufferSize;
|
||||
|
||||
|
|
@ -297,7 +295,7 @@ static inline SwCoord HYPOT(SwPoint pt)
|
|||
}
|
||||
|
||||
|
||||
static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord acount)
|
||||
static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord aCount)
|
||||
{
|
||||
x += rw.cellMin.x;
|
||||
y += rw.cellMin.y;
|
||||
|
|
@ -341,11 +339,11 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
|
|||
if ((span->coverage == coverage) && (span->y == y) && (span->x + span->len == x)) {
|
||||
//Clip x range
|
||||
SwCoord xOver = 0;
|
||||
if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
|
||||
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
|
||||
if (x < rw.cellMin.x) xOver -= (rw.cellMin.x - x);
|
||||
|
||||
//span->len += (acount + xOver) - 1;
|
||||
span->len += (acount + xOver);
|
||||
//span->len += (aCount + xOver) - 1;
|
||||
span->len += (aCount + xOver);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -361,20 +359,20 @@ static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoor
|
|||
|
||||
//Clip x range
|
||||
SwCoord xOver = 0;
|
||||
if (x + acount >= rw.cellMax.x) xOver -= (x + acount - rw.cellMax.x);
|
||||
if (x + aCount >= rw.cellMax.x) xOver -= (x + aCount - rw.cellMax.x);
|
||||
if (x < rw.cellMin.x) {
|
||||
xOver -= (rw.cellMin.x - x);
|
||||
x = rw.cellMin.x;
|
||||
}
|
||||
|
||||
//Nothing to draw
|
||||
if (acount + xOver <= 0) return;
|
||||
if (aCount + xOver <= 0) return;
|
||||
|
||||
//add a span to the current list
|
||||
auto span = rle->spans + rle->size;
|
||||
span->x = x;
|
||||
span->y = y;
|
||||
span->len = (acount + xOver);
|
||||
span->len = (aCount + xOver);
|
||||
span->coverage = coverage;
|
||||
rle->size++;
|
||||
}
|
||||
|
|
@ -417,7 +415,7 @@ static Cell* _findCell(RleWorker& rw)
|
|||
pcell = &cell->next;
|
||||
}
|
||||
|
||||
if (rw.cellsCnt >= rw.maxCells) longjmp(rw.jmpBuf, 1);
|
||||
if (rw.cellsCnt >= rw.maxCells) return nullptr;
|
||||
|
||||
auto cell = rw.cells + rw.cellsCnt++;
|
||||
cell->x = x;
|
||||
|
|
@ -430,17 +428,22 @@ static Cell* _findCell(RleWorker& rw)
|
|||
}
|
||||
|
||||
|
||||
static void _recordCell(RleWorker& rw)
|
||||
static bool _recordCell(RleWorker& rw)
|
||||
{
|
||||
if (rw.area | rw.cover) {
|
||||
auto cell = _findCell(rw);
|
||||
|
||||
if (cell == nullptr) return false;
|
||||
|
||||
cell->area += rw.area;
|
||||
cell->cover += rw.cover;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _setCell(RleWorker& rw, SwPoint pos)
|
||||
static bool _setCell(RleWorker& rw, SwPoint pos)
|
||||
{
|
||||
/* Move the cell pointer to a new position. We set the `invalid' */
|
||||
/* flag to indicate that the cell isn't part of those we're interested */
|
||||
|
|
@ -457,22 +460,26 @@ static void _setCell(RleWorker& rw, SwPoint pos)
|
|||
pos.x -= rw.cellMin.x;
|
||||
pos.y -= rw.cellMin.y;
|
||||
|
||||
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
|
||||
//exceptions
|
||||
if (pos.x < 0) pos.x = -1;
|
||||
else if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
|
||||
|
||||
//Are we moving to a different cell?
|
||||
if (pos != rw.cellPos) {
|
||||
//Record the current one if it is valid
|
||||
if (!rw.invalid) _recordCell(rw);
|
||||
if (!rw.invalid) {
|
||||
if (!_recordCell(rw)) return false;
|
||||
}
|
||||
rw.area = rw.cover = 0;
|
||||
rw.cellPos = pos;
|
||||
}
|
||||
|
||||
rw.area = 0;
|
||||
rw.cover = 0;
|
||||
rw.cellPos = pos;
|
||||
rw.invalid = ((unsigned)pos.y >= (unsigned)rw.cellYCnt || pos.x >= rw.cellXCnt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _startCell(RleWorker& rw, SwPoint pos)
|
||||
static bool _startCell(RleWorker& rw, SwPoint pos)
|
||||
{
|
||||
if (pos.x > rw.cellMax.x) pos.x = rw.cellMax.x;
|
||||
if (pos.x < rw.cellMin.x) pos.x = rw.cellMin.x;
|
||||
|
|
@ -482,23 +489,27 @@ static void _startCell(RleWorker& rw, SwPoint pos)
|
|||
rw.cellPos = pos - rw.cellMin;
|
||||
rw.invalid = false;
|
||||
|
||||
_setCell(rw, pos);
|
||||
return _setCell(rw, pos);
|
||||
}
|
||||
|
||||
|
||||
static void _moveTo(RleWorker& rw, const SwPoint& to)
|
||||
static bool _moveTo(RleWorker& rw, const SwPoint& to)
|
||||
{
|
||||
//record current cell, if any */
|
||||
if (!rw.invalid) _recordCell(rw);
|
||||
if (!rw.invalid) {
|
||||
if (!_recordCell(rw)) return false;
|
||||
}
|
||||
|
||||
//start to a new position
|
||||
_startCell(rw, TRUNC(to));
|
||||
if (!_startCell(rw, TRUNC(to))) return false;
|
||||
|
||||
rw.pos = to;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _lineTo(RleWorker& rw, const SwPoint& to)
|
||||
static bool _lineTo(RleWorker& rw, const SwPoint& to)
|
||||
{
|
||||
#define SW_UDIV(a, b) \
|
||||
static_cast<SwCoord>(((unsigned long)(a) * (unsigned long)(b)) >> \
|
||||
|
|
@ -510,105 +521,123 @@ static void _lineTo(RleWorker& rw, const SwPoint& to)
|
|||
//vertical clipping
|
||||
if ((e1.y >= rw.cellMax.y && e2.y >= rw.cellMax.y) || (e1.y < rw.cellMin.y && e2.y < rw.cellMin.y)) {
|
||||
rw.pos = to;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto diff = to - rw.pos;
|
||||
auto f1 = rw.pos - SUBPIXELS(e1);
|
||||
SwPoint f2;
|
||||
auto line = rw.lineStack;
|
||||
line[0] = to;
|
||||
line[1] = rw.pos;
|
||||
|
||||
//inside one cell
|
||||
if (e1 == e2) {
|
||||
;
|
||||
//any horizontal line
|
||||
} else if (diff.y == 0) {
|
||||
e1.x = e2.x;
|
||||
_setCell(rw, e1);
|
||||
} else if (diff.x == 0) {
|
||||
//vertical line up
|
||||
if (diff.y > 0) {
|
||||
do {
|
||||
f2.y = ONE_PIXEL;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * f1.x * 2;
|
||||
f1.y = 0;
|
||||
++e1.y;
|
||||
_setCell(rw, e1);
|
||||
} while(e1.y != e2.y);
|
||||
//vertical line down
|
||||
} else {
|
||||
do {
|
||||
f2.y = 0;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * f1.x * 2;
|
||||
f1.y = ONE_PIXEL;
|
||||
--e1.y;
|
||||
_setCell(rw, e1);
|
||||
} while(e1.y != e2.y);
|
||||
while (true) {
|
||||
auto diff = line[0] - line[1];
|
||||
auto L = HYPOT(diff);
|
||||
|
||||
if (L > SHRT_MAX) {
|
||||
mathSplitLine(line);
|
||||
++line;
|
||||
continue;
|
||||
}
|
||||
//any other line
|
||||
} else {
|
||||
Area prod = diff.x * f1.y - diff.y * f1.x;
|
||||
e1 = TRUNC(line[1]);
|
||||
e2 = TRUNC(line[0]);
|
||||
|
||||
/* These macros speed up repetitive divisions by replacing them
|
||||
with multiplications and right shifts. */
|
||||
auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
|
||||
auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
|
||||
auto f1 = line[1] - SUBPIXELS(e1);
|
||||
SwPoint f2;
|
||||
|
||||
/* The fundamental value `prod' determines which side and the */
|
||||
/* exact coordinate where the line exits current cell. It is */
|
||||
/* also easily updated when moving from one cell to the next. */
|
||||
do {
|
||||
auto px = diff.x * ONE_PIXEL;
|
||||
auto py = diff.y * ONE_PIXEL;
|
||||
|
||||
//left
|
||||
if (prod <= 0 && prod - px > 0) {
|
||||
f2 = {0, SW_UDIV(-prod, -dx_r)};
|
||||
prod -= py;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {ONE_PIXEL, f2.y};
|
||||
--e1.x;
|
||||
//up
|
||||
} else if (prod - px <= 0 && prod - px + py > 0) {
|
||||
prod -= px;
|
||||
f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {f2.x, 0};
|
||||
++e1.y;
|
||||
//right
|
||||
} else if (prod - px + py <= 0 && prod + py >= 0) {
|
||||
prod += py;
|
||||
f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {0, f2.y};
|
||||
++e1.x;
|
||||
//down
|
||||
//inside one cell
|
||||
if (e1 == e2) {
|
||||
;
|
||||
//any horizontal line
|
||||
} else if (diff.y == 0) {
|
||||
e1.x = e2.x;
|
||||
if (!_setCell(rw, e1)) return false;
|
||||
} else if (diff.x == 0) {
|
||||
//vertical line up
|
||||
if (diff.y > 0) {
|
||||
do {
|
||||
f2.y = ONE_PIXEL;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * f1.x * 2;
|
||||
f1.y = 0;
|
||||
++e1.y;
|
||||
if (!_setCell(rw, e1)) return false;
|
||||
} while(e1.y != e2.y);
|
||||
//vertical line down
|
||||
} else {
|
||||
f2 = {SW_UDIV(prod, -dy_r), 0};
|
||||
prod += px;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {f2.x, ONE_PIXEL};
|
||||
--e1.y;
|
||||
do {
|
||||
f2.y = 0;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * f1.x * 2;
|
||||
f1.y = ONE_PIXEL;
|
||||
--e1.y;
|
||||
if (!_setCell(rw, e1)) return false;
|
||||
} while(e1.y != e2.y);
|
||||
}
|
||||
//any other line
|
||||
} else {
|
||||
Area prod = diff.x * f1.y - diff.y * f1.x;
|
||||
|
||||
_setCell(rw, e1);
|
||||
/* These macros speed up repetitive divisions by replacing them
|
||||
with multiplications and right shifts. */
|
||||
auto dx_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.x);
|
||||
auto dy_r = static_cast<long>(ULONG_MAX >> PIXEL_BITS) / (diff.y);
|
||||
|
||||
} while(e1 != e2);
|
||||
/* The fundamental value `prod' determines which side and the */
|
||||
/* exact coordinate where the line exits current cell. It is */
|
||||
/* also easily updated when moving from one cell to the next. */
|
||||
do {
|
||||
auto px = diff.x * ONE_PIXEL;
|
||||
auto py = diff.y * ONE_PIXEL;
|
||||
|
||||
//left
|
||||
if (prod <= 0 && prod - px > 0) {
|
||||
f2 = {0, SW_UDIV(-prod, -dx_r)};
|
||||
prod -= py;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {ONE_PIXEL, f2.y};
|
||||
--e1.x;
|
||||
//up
|
||||
} else if (prod - px <= 0 && prod - px + py > 0) {
|
||||
prod -= px;
|
||||
f2 = {SW_UDIV(-prod, dy_r), ONE_PIXEL};
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {f2.x, 0};
|
||||
++e1.y;
|
||||
//right
|
||||
} else if (prod - px + py <= 0 && prod + py >= 0) {
|
||||
prod += py;
|
||||
f2 = {ONE_PIXEL, SW_UDIV(prod, dx_r)};
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {0, f2.y};
|
||||
++e1.x;
|
||||
//down
|
||||
} else {
|
||||
f2 = {SW_UDIV(prod, -dy_r), 0};
|
||||
prod += px;
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
f1 = {f2.x, ONE_PIXEL};
|
||||
--e1.y;
|
||||
}
|
||||
|
||||
if (!_setCell(rw, e1)) return false;
|
||||
|
||||
} while(e1 != e2);
|
||||
}
|
||||
|
||||
f2 = {line[0].x - SUBPIXELS(e2.x), line[0].y - SUBPIXELS(e2.y)};
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
rw.pos = line[0];
|
||||
|
||||
if (line-- == rw.lineStack) return true;
|
||||
}
|
||||
|
||||
f2 = {to.x - SUBPIXELS(e2.x), to.y - SUBPIXELS(e2.y)};
|
||||
rw.cover += (f2.y - f1.y);
|
||||
rw.area += (f2.y - f1.y) * (f1.x + f2.x);
|
||||
rw.pos = to;
|
||||
}
|
||||
|
||||
|
||||
static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
static bool _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
auto arc = rw.bezStack;
|
||||
arc[0] = to;
|
||||
|
|
@ -672,14 +701,14 @@ static void _cubicTo(RleWorker& rw, const SwPoint& ctrl1, const SwPoint& ctrl2,
|
|||
continue;
|
||||
|
||||
draw:
|
||||
_lineTo(rw, arc[0]);
|
||||
if (arc == rw.bezStack) return;
|
||||
if (!_lineTo(rw, arc[0])) return false;
|
||||
if (arc == rw.bezStack) return true;
|
||||
arc -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _decomposeOutline(RleWorker& rw)
|
||||
static bool _decomposeOutline(RleWorker& rw)
|
||||
{
|
||||
auto outline = rw.outline;
|
||||
auto first = 0; //index of first point in contour
|
||||
|
|
@ -690,48 +719,49 @@ static void _decomposeOutline(RleWorker& rw)
|
|||
auto start = UPSCALE(outline->pts[first]);
|
||||
auto pt = outline->pts.data + first;
|
||||
auto types = outline->types.data + first;
|
||||
++types;
|
||||
|
||||
_moveTo(rw, UPSCALE(outline->pts[first]));
|
||||
if (!_moveTo(rw, UPSCALE(outline->pts[first]))) return false;
|
||||
|
||||
while (pt < limit) {
|
||||
++pt;
|
||||
++types;
|
||||
|
||||
//emit a single line_to
|
||||
if (types[0] == SW_CURVE_TYPE_POINT) {
|
||||
_lineTo(rw, UPSCALE(*pt));
|
||||
++pt;
|
||||
++types;
|
||||
if (!_lineTo(rw, UPSCALE(*pt))) return false;
|
||||
//types cubic
|
||||
} else {
|
||||
pt += 2;
|
||||
types += 2;
|
||||
|
||||
pt += 3;
|
||||
types += 3;
|
||||
if (pt <= limit) {
|
||||
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]));
|
||||
continue;
|
||||
if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), UPSCALE(pt[0]))) return false;
|
||||
}
|
||||
_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start);
|
||||
goto close;
|
||||
else if (pt - 1 == limit) {
|
||||
if (!_cubicTo(rw, UPSCALE(pt[-2]), UPSCALE(pt[-1]), start)) return false;
|
||||
}
|
||||
else goto close;
|
||||
}
|
||||
}
|
||||
_lineTo(rw, start);
|
||||
close:
|
||||
if (!_lineTo(rw, start)) return false;
|
||||
first = last + 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static int _genRle(RleWorker& rw)
|
||||
{
|
||||
if (setjmp(rw.jmpBuf) == 0) {
|
||||
_decomposeOutline(rw);
|
||||
if (!rw.invalid) _recordCell(rw);
|
||||
return 0;
|
||||
if (!_decomposeOutline(rw)) return -1;
|
||||
if (!rw.invalid) {
|
||||
if (!_recordCell(rw)) return -1;
|
||||
}
|
||||
return -1; //lack of cell memory
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *target, SwSpan *outSpans, uint32_t outSpansCnt)
|
||||
static SwSpan* _intersectSpansRegion(const SwRle *clip, const SwRle *target, SwSpan *outSpans, uint32_t outSpansCnt)
|
||||
{
|
||||
auto out = outSpans;
|
||||
auto spans = target->spans;
|
||||
|
|
@ -740,7 +770,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
|
|||
auto clipEnd = clip->spans + clip->size;
|
||||
|
||||
while (spans < end && clipSpans < clipEnd) {
|
||||
//align y cooridnates.
|
||||
//align y-coordinates.
|
||||
if (clipSpans->y > spans->y) {
|
||||
++spans;
|
||||
continue;
|
||||
|
|
@ -750,7 +780,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
|
|||
continue;
|
||||
}
|
||||
|
||||
//Try clipping with all clip spans which have a same y coordinate.
|
||||
//Try clipping with all clip spans which have a same y-coordinate.
|
||||
auto temp = clipSpans;
|
||||
while(temp < clipEnd && outSpansCnt > 0 && temp->y == clipSpans->y) {
|
||||
auto sx1 = spans->x;
|
||||
|
|
@ -783,7 +813,7 @@ static SwSpan* _intersectSpansRegion(const SwRleData *clip, const SwRleData *tar
|
|||
}
|
||||
|
||||
|
||||
static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
|
||||
static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRle *targetRle, SwSpan *outSpans, uint32_t outSpansCnt)
|
||||
{
|
||||
auto out = outSpans;
|
||||
auto spans = targetRle->spans;
|
||||
|
|
@ -822,47 +852,7 @@ static SwSpan* _intersectSpansRect(const SwBBox *bbox, const SwRleData *targetRl
|
|||
}
|
||||
|
||||
|
||||
static SwSpan* _mergeSpansRegion(const SwRleData *clip1, const SwRleData *clip2, SwSpan *outSpans)
|
||||
{
|
||||
auto out = outSpans;
|
||||
auto spans1 = clip1->spans;
|
||||
auto end1 = clip1->spans + clip1->size;
|
||||
auto spans2 = clip2->spans;
|
||||
auto end2 = clip2->spans + clip2->size;
|
||||
|
||||
//list two spans up in y order
|
||||
//TODO: Remove duplicated regions?
|
||||
while (spans1 < end1 && spans2 < end2) {
|
||||
while (spans1 < end1 && spans1->y <= spans2->y) {
|
||||
*out = *spans1;
|
||||
++spans1;
|
||||
++out;
|
||||
}
|
||||
if (spans1 >= end1) break;
|
||||
while (spans2 < end2 && spans2->y <= spans1->y) {
|
||||
*out = *spans2;
|
||||
++spans2;
|
||||
++out;
|
||||
}
|
||||
}
|
||||
|
||||
//Leftovers
|
||||
while (spans1 < end1) {
|
||||
*out = *spans1;
|
||||
++spans1;
|
||||
++out;
|
||||
}
|
||||
while (spans2 < end2) {
|
||||
*out = *spans2;
|
||||
++spans2;
|
||||
++out;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
|
||||
void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
|
||||
{
|
||||
free(rle->spans);
|
||||
rle->spans = clippedSpans;
|
||||
|
|
@ -874,7 +864,7 @@ void _replaceClipSpan(SwRleData *rle, SwSpan* clippedSpans, uint32_t size)
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
|
||||
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
|
||||
{
|
||||
constexpr auto RENDER_POOL_SIZE = 16384L;
|
||||
constexpr auto BAND_SIZE = 40;
|
||||
|
|
@ -902,7 +892,7 @@ SwRleData* rleRender(SwRleData* rle, const SwOutline* outline, const SwBBox& ren
|
|||
rw.bandShoot = 0;
|
||||
rw.antiAlias = antiAlias;
|
||||
|
||||
if (!rle) rw.rle = reinterpret_cast<SwRleData*>(calloc(1, sizeof(SwRleData)));
|
||||
if (!rle) rw.rle = reinterpret_cast<SwRle*>(calloc(1, sizeof(SwRle)));
|
||||
else rw.rle = rle;
|
||||
|
||||
//Generate RLE
|
||||
|
|
@ -993,12 +983,12 @@ error:
|
|||
}
|
||||
|
||||
|
||||
SwRleData* rleRender(const SwBBox* bbox)
|
||||
SwRle* rleRender(const SwBBox* bbox)
|
||||
{
|
||||
auto width = static_cast<uint16_t>(bbox->max.x - bbox->min.x);
|
||||
auto height = static_cast<uint16_t>(bbox->max.y - bbox->min.y);
|
||||
|
||||
auto rle = static_cast<SwRleData*>(malloc(sizeof(SwRleData)));
|
||||
auto rle = static_cast<SwRle*>(malloc(sizeof(SwRle)));
|
||||
rle->spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * height));
|
||||
rle->size = height;
|
||||
rle->alloc = height;
|
||||
|
|
@ -1015,14 +1005,14 @@ SwRleData* rleRender(const SwBBox* bbox)
|
|||
}
|
||||
|
||||
|
||||
void rleReset(SwRleData* rle)
|
||||
void rleReset(SwRle* rle)
|
||||
{
|
||||
if (!rle) return;
|
||||
rle->size = 0;
|
||||
}
|
||||
|
||||
|
||||
void rleFree(SwRleData* rle)
|
||||
void rleFree(SwRle* rle)
|
||||
{
|
||||
if (!rle) return;
|
||||
if (rle->spans) free(rle->spans);
|
||||
|
|
@ -1030,65 +1020,31 @@ void rleFree(SwRleData* rle)
|
|||
}
|
||||
|
||||
|
||||
void rleMerge(SwRleData* rle, SwRleData* clip1, SwRleData* clip2)
|
||||
bool rleClip(SwRle *rle, const SwRle *clip)
|
||||
{
|
||||
if (!rle || (!clip1 && !clip2)) return;
|
||||
if (clip1 && clip1->size == 0 && clip2 && clip2->size == 0) return;
|
||||
|
||||
TVGLOG("SW_ENGINE", "Unifying Rle!");
|
||||
|
||||
//clip1 is empty, just copy clip2
|
||||
if (!clip1 || clip1->size == 0) {
|
||||
if (clip2) {
|
||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip2->size)));
|
||||
memcpy(spans, clip2->spans, clip2->size);
|
||||
_replaceClipSpan(rle, spans, clip2->size);
|
||||
} else {
|
||||
_replaceClipSpan(rle, nullptr, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//clip2 is empty, just copy clip1
|
||||
if (!clip2 || clip2->size == 0) {
|
||||
if (clip1) {
|
||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (clip1->size)));
|
||||
memcpy(spans, clip1->spans, clip1->size);
|
||||
_replaceClipSpan(rle, spans, clip1->size);
|
||||
} else {
|
||||
_replaceClipSpan(rle, nullptr, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto spanCnt = clip1->size + clip2->size;
|
||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * spanCnt));
|
||||
auto spansEnd = _mergeSpansRegion(clip1, clip2, spans);
|
||||
|
||||
_replaceClipSpan(rle, spans, spansEnd - spans);
|
||||
}
|
||||
|
||||
|
||||
void rleClipPath(SwRleData *rle, const SwRleData *clip)
|
||||
{
|
||||
if (rle->size == 0 || clip->size == 0) return;
|
||||
if (rle->size == 0 || clip->size == 0) return false;
|
||||
auto spanCnt = rle->size > clip->size ? rle->size : clip->size;
|
||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (spanCnt)));
|
||||
auto spansEnd = _intersectSpansRegion(clip, rle, spans, spanCnt);
|
||||
|
||||
_replaceClipSpan(rle, spans, spansEnd - spans);
|
||||
|
||||
TVGLOG("SW_ENGINE", "Using ClipPath!");
|
||||
TVGLOG("SW_ENGINE", "Using Path Clipping!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void rleClipRect(SwRleData *rle, const SwBBox* clip)
|
||||
bool rleClip(SwRle *rle, const SwBBox* clip)
|
||||
{
|
||||
if (rle->size == 0) return;
|
||||
if (rle->size == 0) return false;
|
||||
auto spans = static_cast<SwSpan*>(malloc(sizeof(SwSpan) * (rle->size)));
|
||||
auto spansEnd = _intersectSpansRect(clip, rle, spans, rle->size);
|
||||
|
||||
_replaceClipSpan(rle, spans, spansEnd - spans);
|
||||
|
||||
TVGLOG("SW_ENGINE", "Using ClipRect!");
|
||||
TVGLOG("SW_ENGINE", "Using Box Clipping!");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
#include "tvgSwCommon.h"
|
||||
#include "tvgMath.h"
|
||||
#include "tvgLines.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
|
|
@ -49,7 +48,7 @@ static bool _outlineEnd(SwOutline& outline)
|
|||
}
|
||||
|
||||
|
||||
static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* transform, bool closed = false)
|
||||
static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix& transform, bool closed = false)
|
||||
{
|
||||
//make it a contour, if the last contour is not closed yet.
|
||||
if (!closed) _outlineEnd(outline);
|
||||
|
|
@ -60,14 +59,14 @@ static bool _outlineMoveTo(SwOutline& outline, const Point* to, const Matrix* tr
|
|||
}
|
||||
|
||||
|
||||
static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix* transform)
|
||||
static void _outlineLineTo(SwOutline& outline, const Point* to, const Matrix& transform)
|
||||
{
|
||||
outline.pts.push(mathTransform(to, transform));
|
||||
outline.types.push(SW_CURVE_TYPE_POINT);
|
||||
}
|
||||
|
||||
|
||||
static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
|
||||
static void _outlineCubicTo(SwOutline& outline, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
|
||||
{
|
||||
outline.pts.push(mathTransform(ctrl1, transform));
|
||||
outline.types.push(SW_CURVE_TYPE_CUBIC);
|
||||
|
|
@ -99,12 +98,12 @@ static bool _outlineClose(SwOutline& outline)
|
|||
}
|
||||
|
||||
|
||||
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
|
||||
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix& transform)
|
||||
{
|
||||
Line cur = {dash.ptCur, *to};
|
||||
auto len = lineLength(cur.pt1, cur.pt2);
|
||||
auto len = cur.length();
|
||||
|
||||
if (mathZero(len)) {
|
||||
if (tvg::zero(len)) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
//draw the current line fully
|
||||
} else if (len <= dash.curLen) {
|
||||
|
|
@ -122,7 +121,7 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
|
|||
Line left, right;
|
||||
if (dash.curLen > 0) {
|
||||
len -= dash.curLen;
|
||||
lineSplitAt(cur, dash.curLen, left, right);
|
||||
cur.split(dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
|
||||
_outlineMoveTo(*dash.outline, &left.pt1, transform);
|
||||
|
|
@ -160,13 +159,13 @@ static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* trans
|
|||
}
|
||||
|
||||
|
||||
static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix* transform)
|
||||
static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ctrl2, const Point* to, const Matrix& transform)
|
||||
{
|
||||
Bezier cur = {dash.ptCur, *ctrl1, *ctrl2, *to};
|
||||
auto len = bezLength(cur);
|
||||
auto len = cur.length();
|
||||
|
||||
//draw the current line fully
|
||||
if (mathZero(len)) {
|
||||
if (tvg::zero(len)) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
} else if (len <= dash.curLen) {
|
||||
dash.curLen -= len;
|
||||
|
|
@ -183,7 +182,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
|||
Bezier left, right;
|
||||
if (dash.curLen > 0) {
|
||||
len -= dash.curLen;
|
||||
bezSplitAt(cur, dash.curLen, left, right);
|
||||
cur.split(dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
|
||||
_outlineMoveTo(*dash.outline, &left.start, transform);
|
||||
|
|
@ -210,7 +209,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
|||
}
|
||||
_outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
|
||||
}
|
||||
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
|
||||
if (dash.curLen < 0.1f && TO_SWCOORD(len) > 1) {
|
||||
//move to next dash
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
|
|
@ -221,7 +220,7 @@ static void _dashCubicTo(SwDashStroke& dash, const Point* ctrl1, const Point* ct
|
|||
}
|
||||
|
||||
|
||||
static void _dashClose(SwDashStroke& dash, const Matrix* transform)
|
||||
static void _dashClose(SwDashStroke& dash, const Matrix& transform)
|
||||
{
|
||||
_dashLineTo(dash, &dash.ptStart, transform);
|
||||
}
|
||||
|
|
@ -245,10 +244,10 @@ static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const
|
|||
}
|
||||
|
||||
|
||||
static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length)
|
||||
static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length, float trimBegin, float trimEnd)
|
||||
{
|
||||
auto begin = length * rshape->stroke->trim.begin;
|
||||
auto end = length * rshape->stroke->trim.end;
|
||||
auto begin = length * trimBegin;
|
||||
auto end = length * trimEnd;
|
||||
|
||||
//default
|
||||
if (end > begin) {
|
||||
|
|
@ -284,7 +283,7 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
|
|||
if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
|
||||
|
||||
const Point* close = nullptr;
|
||||
auto length = 0.0f;
|
||||
auto len = 0.0f;
|
||||
|
||||
//must begin with moveTo
|
||||
if (cmds[0] == PathCommand::MoveTo) {
|
||||
|
|
@ -297,34 +296,34 @@ static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32
|
|||
while (cmdCnt-- > 0) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
length += mathLength(pts - 1, close);
|
||||
if (subpath) return length;
|
||||
len += length(pts - 1, close);
|
||||
if (subpath) return len;
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
if (subpath) return length;
|
||||
if (subpath) return len;
|
||||
close = pts;
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
length += mathLength(pts - 1, pts);
|
||||
len += length(pts - 1, pts);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
|
||||
len += Bezier{*(pts - 1), *pts, *(pts + 1), *(pts + 2)}.length();
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++cmds;
|
||||
}
|
||||
return length;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* transform, bool trimmed, SwMpool* mpool, unsigned tid)
|
||||
static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& transform, bool trimmed, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
const PathCommand* cmds = rshape->path.cmds.data;
|
||||
auto cmdCnt = rshape->path.cmds.count;
|
||||
|
|
@ -341,6 +340,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
|||
auto offset = 0.0f;
|
||||
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
|
||||
auto simultaneous = rshape->stroke->trim.simultaneous;
|
||||
float trimBegin = 0.0f, trimEnd = 1.0f;
|
||||
if (trimmed) rshape->stroke->strokeTrim(trimBegin, trimEnd);
|
||||
|
||||
if (dash.cnt == 0) {
|
||||
if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
|
||||
|
|
@ -353,7 +354,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
|||
//offset
|
||||
auto patternLength = 0.0f;
|
||||
uint32_t offIdx = 0;
|
||||
if (!mathZero(offset)) {
|
||||
if (!tvg::zero(offset)) {
|
||||
for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
|
||||
bool isOdd = dash.cnt % 2;
|
||||
if (isOdd) patternLength *= 2;
|
||||
|
|
@ -372,7 +373,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
|||
|
||||
//must begin with moveTo
|
||||
if (cmds[0] == PathCommand::MoveTo) {
|
||||
if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous));
|
||||
if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous), trimBegin, trimEnd);
|
||||
_dashMoveTo(dash, offIdx, offset, pts);
|
||||
cmds++;
|
||||
pts++;
|
||||
|
|
@ -387,7 +388,7 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix* trans
|
|||
case PathCommand::MoveTo: {
|
||||
if (trimmed) {
|
||||
if (simultaneous) {
|
||||
_trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true));
|
||||
_trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true), trimBegin, trimEnd);
|
||||
_dashMoveTo(dash, offIdx, offset, pts);
|
||||
} else _dashMoveTo(dash, pts);
|
||||
} else _dashMoveTo(dash, offIdx, offset, pts);
|
||||
|
|
@ -436,7 +437,7 @@ static bool _axisAlignedRect(const SwOutline* outline)
|
|||
}
|
||||
|
||||
|
||||
static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix& transform, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
const PathCommand* cmds = rshape->path.cmds.data;
|
||||
auto cmdCnt = rshape->path.cmds.count;
|
||||
|
|
@ -492,12 +493,11 @@ static bool _genOutline(SwShape* shape, const RenderShape* rshape, const Matrix*
|
|||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
|
||||
{
|
||||
if (!_genOutline(shape, rshape, transform, mpool, tid, hasComposite)) return false;
|
||||
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
|
||||
|
||||
//Keep it for Rasterization Region
|
||||
shape->bbox = renderRegion;
|
||||
|
||||
//Check valid region
|
||||
|
|
@ -575,7 +575,7 @@ void shapeDelStroke(SwShape* shape)
|
|||
}
|
||||
|
||||
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* transform)
|
||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform)
|
||||
{
|
||||
if (!shape->stroke) shape->stroke = static_cast<SwStroke*>(calloc(1, sizeof(SwStroke)));
|
||||
auto stroke = shape->stroke;
|
||||
|
|
@ -586,7 +586,7 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix* t
|
|||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
SwOutline* shapeOutline = nullptr;
|
||||
SwOutline* strokeOutline = nullptr;
|
||||
|
|
@ -629,13 +629,13 @@ clear:
|
|||
}
|
||||
|
||||
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
|
||||
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
|
||||
{
|
||||
return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
|
||||
}
|
||||
|
||||
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint8_t opacity, bool ctable)
|
||||
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix& transform, SwSurface* surface, uint8_t opacity, bool ctable)
|
||||
{
|
||||
return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -374,8 +374,12 @@ static void _lineTo(SwStroke& stroke, const SwPoint& to)
|
|||
{
|
||||
auto delta = to - stroke.center;
|
||||
|
||||
//a zero-length lineto is a no-op; avoid creating a spurious corner
|
||||
if (delta.zero()) return;
|
||||
//a zero-length lineto is a no-op
|
||||
if (delta.zero()) {
|
||||
//round and square caps are expected to be drawn as a dot even for zero-length lines
|
||||
if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* The lineLength is used to determine the intersection of strokes outlines.
|
||||
The scale needs to be reverted since the stroke width has not been scaled.
|
||||
|
|
@ -441,13 +445,26 @@ static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl
|
|||
//initialize with current direction
|
||||
angleIn = angleOut = angleMid = stroke.angleIn;
|
||||
|
||||
if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
|
||||
auto valid = mathCubicAngle(arc, angleIn, angleMid, angleOut);
|
||||
|
||||
//valid size
|
||||
if (valid > 0 && arc < limit) {
|
||||
if (stroke.firstPt) stroke.angleIn = angleIn;
|
||||
mathSplitCubic(arc);
|
||||
arc += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
//ignoreable size
|
||||
if (valid < 0 && arc == bezStack) {
|
||||
stroke.center = to;
|
||||
|
||||
//round and square caps are expected to be drawn as a dot even for zero-length lines
|
||||
if (stroke.firstPt && stroke.cap != StrokeCap::Butt) _firstSubPath(stroke, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
//small size
|
||||
if (firstArc) {
|
||||
firstArc = false;
|
||||
//process corner if necessary
|
||||
|
|
@ -662,7 +679,7 @@ static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed)
|
|||
/* Determine if we need to check whether the border radius is greater
|
||||
than the radius of curvature of a curve, to handle this case specially.
|
||||
This is only required if bevel joins or butt caps may be created because
|
||||
round & miter joins and round & square caps cover the nagative sector
|
||||
round & miter joins and round & square caps cover the negative sector
|
||||
created with wide strokes. */
|
||||
if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
|
||||
stroke.handleWideStrokes = true;
|
||||
|
|
@ -715,7 +732,7 @@ static void _endSubPath(SwStroke& stroke)
|
|||
_addCap(stroke, stroke.subPathAngle + SW_ANGLE_PI, 0);
|
||||
|
||||
/* now end the right subpath accordingly. The left one is rewind
|
||||
and deosn't need further processing */
|
||||
and doesn't need further processing */
|
||||
_borderClose(right, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -805,15 +822,10 @@ void strokeFree(SwStroke* stroke)
|
|||
}
|
||||
|
||||
|
||||
void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix* transform)
|
||||
void strokeReset(SwStroke* stroke, const RenderShape* rshape, const Matrix& transform)
|
||||
{
|
||||
if (transform) {
|
||||
stroke->sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
|
||||
stroke->sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
|
||||
} else {
|
||||
stroke->sx = stroke->sy = 1.0f;
|
||||
}
|
||||
|
||||
stroke->sx = sqrtf(powf(transform.e11, 2.0f) + powf(transform.e21, 2.0f));
|
||||
stroke->sy = sqrtf(powf(transform.e12, 2.0f) + powf(transform.e22, 2.0f));
|
||||
stroke->width = HALF_STROKE(rshape->strokeWidth());
|
||||
stroke->cap = rshape->strokeCap();
|
||||
stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit() * 65536.0f);
|
||||
|
|
@ -850,31 +862,25 @@ bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
|
|||
|
||||
//A contour cannot start with a cubic control point
|
||||
if (type == SW_CURVE_TYPE_CUBIC) return false;
|
||||
++types;
|
||||
|
||||
auto closed = outline.closed.data ? outline.closed.data[i]: false;
|
||||
|
||||
_beginSubPath(*stroke, start, closed);
|
||||
|
||||
while (pt < limit) {
|
||||
++pt;
|
||||
++types;
|
||||
|
||||
//emit a signel line_to
|
||||
//emit a single line_to
|
||||
if (types[0] == SW_CURVE_TYPE_POINT) {
|
||||
++pt;
|
||||
++types;
|
||||
_lineTo(*stroke, *pt);
|
||||
//types cubic
|
||||
} else {
|
||||
if (pt + 1 > limit || types[1] != SW_CURVE_TYPE_CUBIC) return false;
|
||||
|
||||
pt += 2;
|
||||
types += 2;
|
||||
|
||||
if (pt <= limit) {
|
||||
_cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
|
||||
continue;
|
||||
}
|
||||
_cubicTo(*stroke, pt[-2], pt[-1], start);
|
||||
goto close;
|
||||
pt += 3;
|
||||
types += 3;
|
||||
if (pt <= limit) _cubicTo(*stroke, pt[-2], pt[-1], pt[0]);
|
||||
else if (pt - 1 == limit) _cubicTo(*stroke, pt[-2], pt[-1], start);
|
||||
else goto close;
|
||||
}
|
||||
}
|
||||
close:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue