feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
576
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h
vendored
Normal file
576
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h
vendored
Normal file
|
|
@ -0,0 +1,576 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SW_COMMON_H_
|
||||
#define _TVG_SW_COMMON_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgRender.h"
|
||||
|
||||
#define SW_CURVE_TYPE_POINT 0
|
||||
#define SW_CURVE_TYPE_CUBIC 1
|
||||
#define SW_ANGLE_PI (180L << 16)
|
||||
#define SW_ANGLE_2PI (SW_ANGLE_PI << 1)
|
||||
#define SW_ANGLE_PI2 (SW_ANGLE_PI >> 1)
|
||||
|
||||
using SwCoord = signed long;
|
||||
using SwFixed = signed long long;
|
||||
|
||||
|
||||
static inline float TO_FLOAT(SwCoord val)
|
||||
{
|
||||
return static_cast<float>(val) / 64.0f;
|
||||
}
|
||||
|
||||
struct SwPoint
|
||||
{
|
||||
SwCoord x, y;
|
||||
|
||||
SwPoint& operator+=(const SwPoint& rhs)
|
||||
{
|
||||
x += rhs.x;
|
||||
y += rhs.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SwPoint operator+(const SwPoint& rhs) const
|
||||
{
|
||||
return {x + rhs.x, y + rhs.y};
|
||||
}
|
||||
|
||||
SwPoint operator-(const SwPoint& rhs) const
|
||||
{
|
||||
return {x - rhs.x, y - rhs.y};
|
||||
}
|
||||
|
||||
bool operator==(const SwPoint& rhs) const
|
||||
{
|
||||
return (x == rhs.x && y == rhs.y);
|
||||
}
|
||||
|
||||
bool operator!=(const SwPoint& rhs) const
|
||||
{
|
||||
return (x != rhs.x || y != rhs.y);
|
||||
}
|
||||
|
||||
bool zero() const
|
||||
{
|
||||
if (x == 0 && y == 0) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
bool small() const
|
||||
{
|
||||
//2 is epsilon...
|
||||
if (abs(x) < 2 && abs(y) < 2) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
Point toPoint() const
|
||||
{
|
||||
return {TO_FLOAT(x), TO_FLOAT(y)};
|
||||
}
|
||||
};
|
||||
|
||||
struct SwSize
|
||||
{
|
||||
SwCoord w, h;
|
||||
};
|
||||
|
||||
struct SwOutline
|
||||
{
|
||||
Array<SwPoint> pts; //the outline's points
|
||||
Array<uint32_t> cntrs; //the contour end points
|
||||
Array<uint8_t> types; //curve type
|
||||
Array<bool> closed; //opened or closed path?
|
||||
FillRule fillRule;
|
||||
};
|
||||
|
||||
struct SwSpan
|
||||
{
|
||||
uint16_t x, y;
|
||||
uint16_t len;
|
||||
uint8_t coverage;
|
||||
};
|
||||
|
||||
struct SwRleData
|
||||
{
|
||||
SwSpan *spans;
|
||||
uint32_t alloc;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct SwBBox
|
||||
{
|
||||
SwPoint min, max;
|
||||
|
||||
void reset()
|
||||
{
|
||||
min.x = min.y = max.x = max.y = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct SwFill
|
||||
{
|
||||
struct SwLinear {
|
||||
float dx, dy;
|
||||
float len;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SwRadial {
|
||||
float a11, a12, a13;
|
||||
float a21, a22, a23;
|
||||
float fx, fy, fr;
|
||||
float dx, dy, dr;
|
||||
float invA, a;
|
||||
};
|
||||
|
||||
union {
|
||||
SwLinear linear;
|
||||
SwRadial radial;
|
||||
};
|
||||
|
||||
uint32_t* ctable;
|
||||
FillSpread spread;
|
||||
|
||||
bool translucent;
|
||||
};
|
||||
|
||||
struct SwStrokeBorder
|
||||
{
|
||||
uint32_t ptsCnt;
|
||||
uint32_t maxPts;
|
||||
SwPoint* pts;
|
||||
uint8_t* tags;
|
||||
int32_t start; //index of current sub-path start point
|
||||
bool movable; //true: for ends of lineto borders
|
||||
};
|
||||
|
||||
struct SwStroke
|
||||
{
|
||||
SwFixed angleIn;
|
||||
SwFixed angleOut;
|
||||
SwPoint center;
|
||||
SwFixed lineLength;
|
||||
SwFixed subPathAngle;
|
||||
SwPoint ptStartSubPath;
|
||||
SwFixed subPathLineLength;
|
||||
SwFixed width;
|
||||
SwFixed miterlimit;
|
||||
|
||||
StrokeCap cap;
|
||||
StrokeJoin join;
|
||||
StrokeJoin joinSaved;
|
||||
SwFill* fill = nullptr;
|
||||
|
||||
SwStrokeBorder borders[2];
|
||||
|
||||
float sx, sy;
|
||||
|
||||
bool firstPt;
|
||||
bool closedSubPath;
|
||||
bool handleWideStrokes;
|
||||
};
|
||||
|
||||
struct SwDashStroke
|
||||
{
|
||||
SwOutline* outline = nullptr;
|
||||
float curLen = 0;
|
||||
int32_t curIdx = 0;
|
||||
Point ptStart = {0, 0};
|
||||
Point ptCur = {0, 0};
|
||||
float* pattern = nullptr;
|
||||
uint32_t cnt = 0;
|
||||
bool curOpGap = false;
|
||||
bool move = true;
|
||||
};
|
||||
|
||||
struct SwShape
|
||||
{
|
||||
SwOutline* outline = nullptr;
|
||||
SwStroke* stroke = nullptr;
|
||||
SwFill* fill = nullptr;
|
||||
SwRleData* rle = nullptr;
|
||||
SwRleData* 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?
|
||||
};
|
||||
|
||||
struct SwImage
|
||||
{
|
||||
SwOutline* outline = nullptr;
|
||||
SwRleData* rle = nullptr;
|
||||
union {
|
||||
pixel_t* data; //system based data pointer
|
||||
uint32_t* buf32; //for explicit 32bits channels
|
||||
uint8_t* buf8; //for explicit 8bits grayscale
|
||||
};
|
||||
uint32_t w, h, stride;
|
||||
int32_t ox = 0; //offset x
|
||||
int32_t oy = 0; //offset y
|
||||
float scale;
|
||||
uint8_t channelSize;
|
||||
|
||||
bool direct = false; //draw image directly (with offset)
|
||||
bool scaled = false; //draw scaled image
|
||||
};
|
||||
|
||||
typedef uint8_t(*SwMask)(uint8_t s, uint8_t d, uint8_t a); //src, dst, alpha
|
||||
typedef uint32_t(*SwBlender)(uint32_t s, uint32_t d, uint8_t a); //src, dst, alpha
|
||||
typedef uint32_t(*SwJoin)(uint8_t r, uint8_t g, uint8_t b, uint8_t a); //color channel join
|
||||
typedef uint8_t(*SwAlpha)(uint8_t*); //blending alpha
|
||||
|
||||
struct SwCompositor;
|
||||
|
||||
struct SwSurface : Surface
|
||||
{
|
||||
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)
|
||||
|
||||
SwAlpha alpha(CompositeMethod method)
|
||||
{
|
||||
auto idx = (int)(method) - 2; //0: None, 1: ClipPath
|
||||
return alphas[idx > 3 ? 0 : idx]; //CompositeMethod has only four Matting methods.
|
||||
}
|
||||
|
||||
SwSurface()
|
||||
{
|
||||
}
|
||||
|
||||
SwSurface(const SwSurface* rhs) : Surface(rhs)
|
||||
{
|
||||
join = rhs->join;
|
||||
memcpy(alphas, rhs->alphas, sizeof(alphas));
|
||||
blender = rhs->blender;
|
||||
compositor = rhs->compositor;
|
||||
blendMethod = rhs->blendMethod;
|
||||
}
|
||||
};
|
||||
|
||||
struct SwCompositor : Compositor
|
||||
{
|
||||
SwSurface* recoverSfc; //Recover surface when composition is started
|
||||
SwCompositor* recoverCmp; //Recover compositor when composition is done
|
||||
SwImage image;
|
||||
SwBBox bbox;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct SwMpool
|
||||
{
|
||||
SwOutline* outline;
|
||||
SwOutline* strokeOutline;
|
||||
SwOutline* dashOutline;
|
||||
unsigned allocSize;
|
||||
};
|
||||
|
||||
static inline SwCoord TO_SWCOORD(float val)
|
||||
{
|
||||
return SwCoord(val * 64.0f);
|
||||
}
|
||||
|
||||
static inline uint32_t JOIN(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3)
|
||||
{
|
||||
return (c0 << 24 | c1 << 16 | c2 << 8 | 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));
|
||||
}
|
||||
|
||||
static inline uint32_t INTERPOLATE(uint32_t s, uint32_t d, uint8_t a)
|
||||
{
|
||||
return (((((((s >> 8) & 0xff00ff) - ((d >> 8) & 0xff00ff)) * a) + (d & 0xff00ff00)) & 0xff00ff00) + ((((((s & 0xff00ff) - (d & 0xff00ff)) * a) >> 8) + (d & 0xff00ff)) & 0xff00ff));
|
||||
}
|
||||
|
||||
static inline uint8_t INTERPOLATE8(uint8_t s, uint8_t d, uint8_t a)
|
||||
{
|
||||
return (((s) * (a) + 0xff) >> 8) + (((d) * ~(a) + 0xff) >> 8);
|
||||
}
|
||||
|
||||
static inline SwCoord HALF_STROKE(float width)
|
||||
{
|
||||
return TO_SWCOORD(width * 0.5f);
|
||||
}
|
||||
|
||||
static inline uint8_t A(uint32_t c)
|
||||
{
|
||||
return ((c) >> 24);
|
||||
}
|
||||
|
||||
static inline uint8_t IA(uint32_t c)
|
||||
{
|
||||
return (~(c) >> 24);
|
||||
}
|
||||
|
||||
static inline uint8_t C1(uint32_t c)
|
||||
{
|
||||
return ((c) >> 16);
|
||||
}
|
||||
|
||||
static inline uint8_t C2(uint32_t c)
|
||||
{
|
||||
return ((c) >> 8);
|
||||
}
|
||||
|
||||
static inline uint8_t C3(uint32_t c)
|
||||
{
|
||||
return (c);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendInterp(uint32_t s, uint32_t d, uint8_t a)
|
||||
{
|
||||
return INTERPOLATE(s, d, a);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendNormal(uint32_t s, uint32_t d, uint8_t a)
|
||||
{
|
||||
auto t = ALPHA_BLEND(s, a);
|
||||
return t + ALPHA_BLEND(d, IA(t));
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendPreNormal(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
return s + ALPHA_BLEND(d, IA(s));
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendSrcOver(uint32_t s, TVG_UNUSED uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
//TODO: BlendMethod could remove the alpha parameter.
|
||||
static inline uint32_t opBlendDifference(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
//if (s > d) => s - d
|
||||
//else => d - s
|
||||
auto c1 = (C1(s) > C1(d)) ? (C1(s) - C1(d)) : (C1(d) - C1(s));
|
||||
auto c2 = (C2(s) > C2(d)) ? (C2(s) - C2(d)) : (C2(d) - C2(s));
|
||||
auto c3 = (C3(s) > C3(d)) ? (C3(s) - C3(d)) : (C3(d) - C3(s));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
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));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendAdd(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// s + d
|
||||
auto c1 = std::min(C1(s) + C1(d), 255);
|
||||
auto c2 = std::min(C2(s) + C2(d), 255);
|
||||
auto c3 = std::min(C3(s) + C3(d), 255);
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendScreen(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// s + d - s * d
|
||||
auto c1 = C1(s) + C1(d) - MULTIPLY(C1(s), C1(d));
|
||||
auto c2 = C2(s) + C2(d) - MULTIPLY(C2(s), C2(d));
|
||||
auto c3 = C3(s) + C3(d) - MULTIPLY(C3(s), C3(d));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t opBlendMultiply(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// s * d
|
||||
auto c1 = MULTIPLY(C1(s), C1(d));
|
||||
auto c2 = MULTIPLY(C2(s), C2(d));
|
||||
auto c3 = MULTIPLY(C3(s), C3(d));
|
||||
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,
|
||||
// else => 1 - 2 * (1 - s) * (1 - d)
|
||||
auto c1 = (C1(d) < 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(d) < 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(d) < 128) ? std::min(255, 2 * MULTIPLY(C3(s), C3(d))) : (255 - std::min(255, 2 * MULTIPLY(255 - C3(s), 255 - C3(d))));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendDarken(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// min(s, d)
|
||||
auto c1 = std::min(C1(s), C1(d));
|
||||
auto c2 = std::min(C2(s), C2(d));
|
||||
auto c3 = std::min(C3(s), C3(d));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendLighten(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// max(s, d)
|
||||
auto c1 = std::max(C1(s), C1(d));
|
||||
auto c2 = std::max(C2(s), C2(d));
|
||||
auto c3 = std::max(C3(s), C3(d));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
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);
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendColorBurn(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
// 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));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||
{
|
||||
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))));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
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)));
|
||||
return JOIN(255, c1, c2, c3);
|
||||
}
|
||||
|
||||
|
||||
int64_t mathMultiply(int64_t a, int64_t b);
|
||||
int64_t mathDivide(int64_t a, int64_t b);
|
||||
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c);
|
||||
void mathRotate(SwPoint& pt, SwFixed angle);
|
||||
SwFixed mathTan(SwFixed angle);
|
||||
SwFixed mathAtan(const SwPoint& pt);
|
||||
SwFixed mathCos(SwFixed angle);
|
||||
SwFixed mathSin(SwFixed angle);
|
||||
void mathSplitCubic(SwPoint* base);
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2);
|
||||
SwFixed mathLength(const SwPoint& pt);
|
||||
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
|
||||
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);
|
||||
|
||||
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 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 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);
|
||||
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);
|
||||
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 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);
|
||||
void fillReset(SwFill* fill);
|
||||
void fillFree(SwFill* fill);
|
||||
|
||||
//OPTIMIZE_ME: Skip the function pointer access
|
||||
void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t opacity); //composite masking ver.
|
||||
void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t opacity); //direct masking ver.
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
|
||||
void fillLinear(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 fillLinear(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.
|
||||
|
||||
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask op, uint8_t a); //composite masking ver.
|
||||
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask op, uint8_t a) ; //direct masking ver.
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a); //blending ver.
|
||||
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);
|
||||
|
||||
SwMpool* mpoolInit(uint32_t threads);
|
||||
bool mpoolTerm(SwMpool* mpool);
|
||||
bool mpoolClear(SwMpool* mpool);
|
||||
SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx);
|
||||
void mpoolRetOutline(SwMpool* mpool, unsigned idx);
|
||||
SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||
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 rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, const Matrix* transform, const SwBBox& bbox, uint8_t opacity);
|
||||
bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
|
||||
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
|
||||
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h);
|
||||
void rasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
|
||||
void rasterGrayscale8(uint8_t *dst, uint8_t val, uint32_t offset, int32_t len);
|
||||
void rasterUnpremultiply(Surface* surface);
|
||||
void rasterPremultiply(Surface* surface);
|
||||
bool rasterConvertCS(Surface* surface, ColorSpace to);
|
||||
|
||||
#endif /* _TVG_SW_COMMON_H_ */
|
||||
858
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
vendored
Normal file
858
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp
vendored
Normal file
|
|
@ -0,0 +1,858 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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"
|
||||
#include "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#define RADIAL_A_THRESHOLD 0.0005f
|
||||
#define GRADIENT_STOP_SIZE 1024
|
||||
#define FIXPT_BITS 8
|
||||
#define FIXPT_SIZE (1<<FIXPT_BITS)
|
||||
|
||||
/*
|
||||
* quadratic equation with the following coefficients (rx and ry defined in the _calculateCoefficients()):
|
||||
* A = a // fill->radial.a
|
||||
* B = 2 * (dr * fr + rx * dx + ry * dy)
|
||||
* C = fr^2 - rx^2 - ry^2
|
||||
* Derivatives are computed with respect to dx.
|
||||
* This procedure aims to optimize and eliminate the need to calculate all values from the beginning
|
||||
* for consecutive x values with a constant y. The Taylor series expansions are computed as long as
|
||||
* its terms are non-zero.
|
||||
*/
|
||||
static void _calculateCoefficients(const SwFill* fill, uint32_t x, uint32_t y, float& b, float& deltaB, float& det, float& deltaDet, float& deltaDeltaDet)
|
||||
{
|
||||
auto radial = &fill->radial;
|
||||
|
||||
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
|
||||
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
|
||||
|
||||
b = (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy) * radial->invA;
|
||||
deltaB = (radial->a11 * radial->dx + radial->a21 * radial->dy) * radial->invA;
|
||||
|
||||
auto rr = rx * rx + ry * ry;
|
||||
auto deltaRr = 2.0f * (rx * radial->a11 + ry * radial->a21) * radial->invA;
|
||||
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;
|
||||
deltaDeltaDet = 2.0f * deltaB * deltaB + deltaDeltaRr;
|
||||
}
|
||||
|
||||
|
||||
static uint32_t _estimateAAMargin(const Fill* fdata)
|
||||
{
|
||||
constexpr float marginScalingFactor = 800.0f;
|
||||
if (fdata->identifier() == TVG_CLASS_ID_RADIAL) {
|
||||
auto radius = P(static_cast<const RadialGradient*>(fdata))->r;
|
||||
return mathZero(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);
|
||||
}
|
||||
|
||||
|
||||
static void _adjustAAMargin(uint32_t& iMargin, uint32_t index)
|
||||
{
|
||||
constexpr float threshold = 0.1f;
|
||||
constexpr uint32_t iMarginMax = 40;
|
||||
|
||||
auto iThreshold = static_cast<uint32_t>(index * threshold);
|
||||
if (iMargin > iThreshold) iMargin = iThreshold;
|
||||
if (iMargin > iMarginMax) iMargin = iMarginMax;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t _alphaUnblend(uint32_t c)
|
||||
{
|
||||
auto a = (c >> 24);
|
||||
if (a == 255 || a == 0) return c;
|
||||
auto invA = 255.0f / static_cast<float>(a);
|
||||
auto c0 = static_cast<uint8_t>(static_cast<float>((c >> 16) & 0xFF) * invA);
|
||||
auto c1 = static_cast<uint8_t>(static_cast<float>((c >> 8) & 0xFF) * invA);
|
||||
auto c2 = static_cast<uint8_t>(static_cast<float>(c & 0xFF) * invA);
|
||||
|
||||
return (a << 24) | (c0 << 16) | (c1 << 8) | c2;
|
||||
}
|
||||
|
||||
|
||||
static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end)
|
||||
{
|
||||
if (begin == 0 || end == 0) return;
|
||||
|
||||
auto i = GRADIENT_STOP_SIZE - end;
|
||||
auto rgbaEnd = _alphaUnblend(fill->ctable[i]);
|
||||
auto rgbaBegin = _alphaUnblend(fill->ctable[begin]);
|
||||
|
||||
auto dt = 1.0f / (begin + end + 1.0f);
|
||||
float t = dt;
|
||||
while (i != begin) {
|
||||
auto dist = 255 - static_cast<int32_t>(255 * t);
|
||||
auto color = INTERPOLATE(rgbaEnd, rgbaBegin, dist);
|
||||
fill->ctable[i++] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
|
||||
|
||||
if (i == GRADIENT_STOP_SIZE) i = 0;
|
||||
t += dt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint8_t opacity)
|
||||
{
|
||||
if (!fill->ctable) {
|
||||
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
|
||||
if (!fill->ctable) return false;
|
||||
}
|
||||
|
||||
const Fill::ColorStop* colors;
|
||||
auto cnt = fdata->colorStops(&colors);
|
||||
if (cnt == 0 || !colors) return false;
|
||||
|
||||
auto pColors = colors;
|
||||
|
||||
auto a = MULTIPLY(pColors->a, opacity);
|
||||
if (a < 255) fill->translucent = true;
|
||||
|
||||
auto r = pColors->r;
|
||||
auto g = pColors->g;
|
||||
auto b = pColors->b;
|
||||
auto rgba = surface->join(r, g, b, a);
|
||||
|
||||
auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
|
||||
auto pos = 1.5f * inc;
|
||||
uint32_t i = 0;
|
||||
|
||||
//If repeat is true, anti-aliasing must be applied between the last and the first colors.
|
||||
auto repeat = fill->spread == FillSpread::Repeat;
|
||||
uint32_t iAABegin = repeat ? _estimateAAMargin(fdata) : 0;
|
||||
uint32_t iAAEnd = 0;
|
||||
|
||||
fill->ctable[i++] = ALPHA_BLEND(rgba | 0xff000000, a);
|
||||
|
||||
while (pos <= pColors->offset) {
|
||||
fill->ctable[i] = fill->ctable[i - 1];
|
||||
++i;
|
||||
pos += inc;
|
||||
}
|
||||
|
||||
for (uint32_t j = 0; j < cnt - 1; ++j) {
|
||||
if (repeat && j == cnt - 2 && iAAEnd == 0) {
|
||||
iAAEnd = iAABegin;
|
||||
_adjustAAMargin(iAAEnd, GRADIENT_STOP_SIZE - i);
|
||||
}
|
||||
|
||||
auto curr = colors + j;
|
||||
auto next = curr + 1;
|
||||
auto delta = 1.0f / (next->offset - curr->offset);
|
||||
auto a2 = MULTIPLY(next->a, opacity);
|
||||
if (!fill->translucent && a2 < 255) fill->translucent = true;
|
||||
|
||||
auto rgba2 = surface->join(next->r, next->g, next->b, a2);
|
||||
|
||||
while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
|
||||
auto t = (pos - curr->offset) * delta;
|
||||
auto dist = static_cast<int32_t>(255 * t);
|
||||
auto dist2 = 255 - dist;
|
||||
|
||||
auto color = INTERPOLATE(rgba, rgba2, dist2);
|
||||
fill->ctable[i] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
|
||||
|
||||
++i;
|
||||
pos += inc;
|
||||
}
|
||||
rgba = rgba2;
|
||||
a = a2;
|
||||
|
||||
if (repeat && j == 0) _adjustAAMargin(iAABegin, i - 1);
|
||||
}
|
||||
rgba = ALPHA_BLEND((rgba | 0xff000000), a);
|
||||
|
||||
for (; i < GRADIENT_STOP_SIZE; ++i)
|
||||
fill->ctable[i] = rgba;
|
||||
|
||||
//For repeat fill spread apply anti-aliasing between the last and first colors,
|
||||
//othewise make sure the last color stop is represented at the end of the table.
|
||||
if (repeat) _applyAA(fill, iAABegin, iAAEnd);
|
||||
else fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
if (fill->linear.len < FLOAT_EPSILON) return true;
|
||||
|
||||
fill->linear.dx /= fill->linear.len;
|
||||
fill->linear.dy /= fill->linear.len;
|
||||
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
|
||||
|
||||
auto gradTransform = linear->transform();
|
||||
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
|
||||
|
||||
if (isTransformation) {
|
||||
if (transform) gradTransform = *transform * gradTransform;
|
||||
} else if (transform) {
|
||||
gradTransform = *transform;
|
||||
isTransformation = true;
|
||||
}
|
||||
|
||||
if (isTransformation) {
|
||||
Matrix invTransform;
|
||||
if (!mathInverse(&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)
|
||||
{
|
||||
auto cx = P(radial)->cx;
|
||||
auto cy = P(radial)->cy;
|
||||
auto r = P(radial)->r;
|
||||
auto fx = P(radial)->fx;
|
||||
auto fy = P(radial)->fy;
|
||||
auto fr = P(radial)->fr;
|
||||
|
||||
if (r < FLOAT_EPSILON) return true;
|
||||
|
||||
fill->radial.dr = r - fr;
|
||||
fill->radial.dx = cx - fx;
|
||||
fill->radial.dy = cy - fy;
|
||||
fill->radial.fr = fr;
|
||||
fill->radial.fx = fx;
|
||||
fill->radial.fy = fy;
|
||||
fill->radial.a = fill->radial.dr * fill->radial.dr - fill->radial.dx * fill->radial.dx - fill->radial.dy * fill->radial.dy;
|
||||
|
||||
//This condition fulfills the SVG 1.1 std:
|
||||
//the focal point, if outside the end circle, is moved to be on the end circle
|
||||
//See: the SVG 2 std requirements: https://www.w3.org/TR/SVG2/pservers.html#RadialGradientNotes
|
||||
if (fill->radial.a < 0) {
|
||||
auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy);
|
||||
fill->radial.fx = cx + r * (fx - cx) / dist;
|
||||
fill->radial.fy = cy + r * (fy - cy) / dist;
|
||||
fill->radial.dx = cx - fill->radial.fx;
|
||||
fill->radial.dy = cy - fill->radial.fy;
|
||||
// Prevent loss of precision on Apple Silicon when dr=dy and dx=0 due to FMA
|
||||
// https://github.com/thorvg/thorvg/issues/2014
|
||||
auto dr2 = fill->radial.dr * fill->radial.dr;
|
||||
auto dx2 = fill->radial.dx * fill->radial.dx;
|
||||
auto dy2 = fill->radial.dy * fill->radial.dy;
|
||||
|
||||
fill->radial.a = dr2 - dx2 - dy2;
|
||||
}
|
||||
|
||||
if (fill->radial.a > 0) fill->radial.invA = 1.0f / fill->radial.a;
|
||||
|
||||
auto gradTransform = radial->transform();
|
||||
bool isTransformation = !mathIdentity((const Matrix*)(&gradTransform));
|
||||
|
||||
if (transform) {
|
||||
if (isTransformation) gradTransform = *transform * gradTransform;
|
||||
else {
|
||||
gradTransform = *transform;
|
||||
isTransformation = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isTransformation) {
|
||||
Matrix invTransform;
|
||||
if (!mathInverse(&gradTransform, &invTransform)) return false;
|
||||
fill->radial.a11 = invTransform.e11;
|
||||
fill->radial.a12 = invTransform.e12;
|
||||
fill->radial.a13 = invTransform.e13;
|
||||
fill->radial.a21 = invTransform.e21;
|
||||
fill->radial.a22 = invTransform.e22;
|
||||
fill->radial.a23 = invTransform.e23;
|
||||
} else {
|
||||
fill->radial.a11 = fill->radial.a22 = 1.0f;
|
||||
fill->radial.a12 = fill->radial.a13 = 0.0f;
|
||||
fill->radial.a21 = fill->radial.a23 = 0.0f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t _clamp(const SwFill* fill, int32_t pos)
|
||||
{
|
||||
switch (fill->spread) {
|
||||
case FillSpread::Pad: {
|
||||
if (pos >= GRADIENT_STOP_SIZE) pos = GRADIENT_STOP_SIZE - 1;
|
||||
else if (pos < 0) pos = 0;
|
||||
break;
|
||||
}
|
||||
case FillSpread::Repeat: {
|
||||
pos = pos % GRADIENT_STOP_SIZE;
|
||||
if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
|
||||
break;
|
||||
}
|
||||
case FillSpread::Reflect: {
|
||||
auto limit = GRADIENT_STOP_SIZE * 2;
|
||||
pos = pos % limit;
|
||||
if (pos < 0) pos = limit + pos;
|
||||
if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos)
|
||||
{
|
||||
int32_t i = (pos + (FIXPT_SIZE / 2)) >> FIXPT_BITS;
|
||||
return fill->ctable[_clamp(fill, i)];
|
||||
}
|
||||
|
||||
|
||||
static inline uint32_t _pixel(const SwFill* fill, float pos)
|
||||
{
|
||||
auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
|
||||
return fill->ctable[_clamp(fill, i)];
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
//edge case
|
||||
if (fill->radial.a < RADIAL_A_THRESHOLD) {
|
||||
auto radial = &fill->radial;
|
||||
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
|
||||
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
|
||||
|
||||
if (opacity == 255) {
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
|
||||
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
|
||||
*dst = opBlendNormal(_pixel(fill, x0), *dst, alpha(cmp));
|
||||
rx += radial->a11;
|
||||
ry += radial->a21;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
|
||||
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
|
||||
*dst = opBlendNormal(_pixel(fill, x0), *dst, MULTIPLY(opacity, alpha(cmp)));
|
||||
rx += radial->a11;
|
||||
ry += radial->a21;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float b, deltaB, det, deltaDet, deltaDeltaDet;
|
||||
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
|
||||
|
||||
if (opacity == 255) {
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
|
||||
*dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, alpha(cmp));
|
||||
det += deltaDet;
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst, cmp += csize) {
|
||||
*dst = opBlendNormal(_pixel(fill, sqrtf(det) - b), *dst, MULTIPLY(opacity, alpha(cmp)));
|
||||
det += deltaDet;
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
|
||||
{
|
||||
if (fill->radial.a < RADIAL_A_THRESHOLD) {
|
||||
auto radial = &fill->radial;
|
||||
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
|
||||
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
|
||||
*dst = op(_pixel(fill, x0), *dst, a);
|
||||
rx += radial->a11;
|
||||
ry += radial->a21;
|
||||
}
|
||||
} else {
|
||||
float b, deltaB, det, deltaDet, deltaDeltaDet;
|
||||
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
|
||||
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
*dst = op(_pixel(fill, sqrtf(det) - b), *dst, a);
|
||||
det += deltaDet;
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
|
||||
{
|
||||
if (fill->radial.a < RADIAL_A_THRESHOLD) {
|
||||
auto radial = &fill->radial;
|
||||
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
|
||||
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
|
||||
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
|
||||
auto src = MULTIPLY(a, A(_pixel(fill, x0)));
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
rx += radial->a11;
|
||||
ry += radial->a21;
|
||||
}
|
||||
} else {
|
||||
float b, deltaB, det, deltaDet, deltaDeltaDet;
|
||||
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
|
||||
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
|
||||
auto src = MULTIPLY(a, A(_pixel(fill, sqrtf(det) - b)));
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
det += deltaDet;
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillRadial(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
|
||||
{
|
||||
if (fill->radial.a < RADIAL_A_THRESHOLD) {
|
||||
auto radial = &fill->radial;
|
||||
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
|
||||
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
|
||||
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
|
||||
auto src = MULTIPLY(A(A(_pixel(fill, x0))), a);
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
rx += radial->a11;
|
||||
ry += radial->a21;
|
||||
}
|
||||
} else {
|
||||
float b, deltaB, det, deltaDet, deltaDeltaDet;
|
||||
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
|
||||
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst, ++cmp) {
|
||||
auto src = MULTIPLY(A(_pixel(fill, sqrtf(det))), a);
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
|
||||
{
|
||||
if (fill->radial.a < RADIAL_A_THRESHOLD) {
|
||||
auto radial = &fill->radial;
|
||||
auto rx = (x + 0.5f) * radial->a11 + (y + 0.5f) * radial->a12 + radial->a13 - radial->fx;
|
||||
auto ry = (x + 0.5f) * radial->a21 + (y + 0.5f) * radial->a22 + radial->a23 - radial->fy;
|
||||
|
||||
if (a == 255) {
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
|
||||
auto tmp = op(_pixel(fill, x0), *dst, 255);
|
||||
*dst = op2(tmp, *dst, 255);
|
||||
rx += radial->a11;
|
||||
ry += radial->a21;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
auto x0 = 0.5f * (rx * rx + ry * ry - radial->fr * radial->fr) / (radial->dr * radial->fr + rx * radial->dx + ry * radial->dy);
|
||||
auto tmp = op(_pixel(fill, x0), *dst, 255);
|
||||
auto tmp2 = op2(tmp, *dst, 255);
|
||||
*dst = INTERPOLATE(tmp2, *dst, a);
|
||||
rx += radial->a11;
|
||||
ry += radial->a21;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
float b, deltaB, det, deltaDet, deltaDeltaDet;
|
||||
_calculateCoefficients(fill, x, y, b, deltaB, det, deltaDet, deltaDeltaDet);
|
||||
if (a == 255) {
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
|
||||
auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
|
||||
*dst = op2(tmp, *dst, 255);
|
||||
det += deltaDet;
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0 ; i < len ; ++i, ++dst) {
|
||||
auto tmp = op(_pixel(fill, sqrtf(det) - b), *dst, 255);
|
||||
auto tmp2 = op2(tmp, *dst, 255);
|
||||
*dst = INTERPOLATE(tmp2, *dst, a);
|
||||
det += deltaDet;
|
||||
deltaDet += deltaDeltaDet;
|
||||
b += deltaB;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillLinear(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)
|
||||
{
|
||||
//Rotation
|
||||
float rx = x + 0.5f;
|
||||
float ry = y + 0.5f;
|
||||
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 (opacity == 255) {
|
||||
if (mathZero(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));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
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, cmp += csize) {
|
||||
*dst = opBlendNormal(_fixedPixel(fill, t2), *dst, alpha(cmp));
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
*dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp));
|
||||
++dst;
|
||||
t += inc;
|
||||
cmp += csize;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (mathZero(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));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
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, cmp += csize) {
|
||||
*dst = opBlendNormal(_fixedPixel(fill, t2), *dst, MULTIPLY(alpha(cmp), opacity));
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
*dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp)));
|
||||
++dst;
|
||||
t += inc;
|
||||
cmp += csize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, SwMask maskOp, uint8_t a)
|
||||
{
|
||||
//Rotation
|
||||
float rx = x + 0.5f;
|
||||
float ry = y + 0.5f;
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
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);
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
auto src = MULTIPLY(_pixel(fill, t / GRADIENT_STOP_SIZE), a);
|
||||
*dst = maskOp(src, *dst, ~src);
|
||||
++dst;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwMask maskOp, uint8_t a)
|
||||
{
|
||||
//Rotation
|
||||
float rx = x + 0.5f;
|
||||
float ry = y + 0.5f;
|
||||
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)) {
|
||||
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) {
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
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, ++cmp) {
|
||||
auto src = MULTIPLY(a, A(_fixedPixel(fill, t2)));
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
|
||||
auto tmp = maskOp(src, *cmp, 0);
|
||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||
++dst;
|
||||
++cmp;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, uint8_t a)
|
||||
{
|
||||
//Rotation
|
||||
float rx = x + 0.5f;
|
||||
float ry = y + 0.5f;
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
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) {
|
||||
*dst = op(_fixedPixel(fill, t2), *dst, a);
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
*dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a);
|
||||
++dst;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a)
|
||||
{
|
||||
//Rotation
|
||||
float rx = x + 0.5f;
|
||||
float ry = y + 0.5f;
|
||||
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)) {
|
||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
||||
if (a == 255) {
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
auto tmp = op(color, *dst, a);
|
||||
*dst = op2(tmp, *dst, 255);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i = 0; i < len; ++i, ++dst) {
|
||||
auto tmp = op(color, *dst, a);
|
||||
auto tmp2 = op2(tmp, *dst, 255);
|
||||
*dst = INTERPOLATE(tmp2, *dst, a);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto vMax = static_cast<float>(INT32_MAX >> (FIXPT_BITS + 1));
|
||||
auto vMin = -vMax;
|
||||
auto v = t + (inc * len);
|
||||
|
||||
if (a == 255) {
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
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 tmp = op(_fixedPixel(fill, t2), *dst, 255);
|
||||
*dst = op2(tmp, *dst, 255);
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
|
||||
*dst = op2(tmp, *dst, 255);
|
||||
++dst;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//we can use fixed point math
|
||||
if (v < vMax && v > vMin) {
|
||||
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 tmp = op(_fixedPixel(fill, t2), *dst, 255);
|
||||
auto tmp2 = op2(tmp, *dst, 255);
|
||||
*dst = INTERPOLATE(tmp2, *dst, a);
|
||||
t2 += inc2;
|
||||
}
|
||||
//we have to fallback to float math
|
||||
} else {
|
||||
uint32_t counter = 0;
|
||||
while (counter++ < len) {
|
||||
auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
|
||||
auto tmp2 = op2(tmp, *dst, 255);
|
||||
*dst = INTERPOLATE(tmp2, *dst, a);
|
||||
++dst;
|
||||
t += inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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->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);
|
||||
}
|
||||
|
||||
//LOG: What type of gradient?!
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void fillReset(SwFill* fill)
|
||||
{
|
||||
if (fill->ctable) {
|
||||
free(fill->ctable);
|
||||
fill->ctable = nullptr;
|
||||
}
|
||||
fill->translucent = false;
|
||||
}
|
||||
|
||||
|
||||
void fillFree(SwFill* fill)
|
||||
{
|
||||
if (!fill) return;
|
||||
|
||||
if (fill->ctable) free(fill->ctable);
|
||||
|
||||
free(fill);
|
||||
}
|
||||
158
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp
vendored
Normal file
158
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _genOutline(SwImage* image, const RenderMesh* mesh, const Matrix* transform, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
image->outline = mpoolReqOutline(mpool, tid);
|
||||
auto outline = image->outline;
|
||||
|
||||
outline->pts.reserve(5);
|
||||
outline->types.reserve(5);
|
||||
outline->cntrs.reserve(1);
|
||||
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};
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
outline->pts.push(mathTransform(&to[i], transform));
|
||||
outline->types.push(SW_CURVE_TYPE_POINT);
|
||||
}
|
||||
|
||||
outline->pts.push(outline->pts[0]);
|
||||
outline->types.push(SW_CURVE_TYPE_POINT);
|
||||
outline->cntrs.push(outline->pts.count - 1);
|
||||
outline->closed.push(true);
|
||||
|
||||
image->outline = outline;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
bool imagePrepare(SwImage* image, const RenderMesh* mesh, 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));
|
||||
//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));
|
||||
image->scale = (fabsf(scaleX - scaleY) > 0.01f) ? 1.0f : scaleX;
|
||||
|
||||
if (mathZero(transform->e12) && mathZero(transform->e21)) image->scaled = true;
|
||||
else image->scaled = false;
|
||||
}
|
||||
|
||||
if (!_genOutline(image, mesh, transform, mpool, tid)) return false;
|
||||
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, image->direct);
|
||||
}
|
||||
|
||||
|
||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
|
||||
{
|
||||
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid)
|
||||
{
|
||||
mpoolRetOutline(mpool, tid);
|
||||
image->outline = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void imageReset(SwImage* image)
|
||||
{
|
||||
rleReset(image->rle);
|
||||
}
|
||||
|
||||
|
||||
void imageFree(SwImage* image)
|
||||
{
|
||||
rleFree(image->rle);
|
||||
}
|
||||
323
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
vendored
Normal file
323
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp
vendored
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static float TO_RADIAN(SwFixed angle)
|
||||
{
|
||||
return (float(angle) / 65536.0f) * (MATH_PI / 180.0f);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwFixed mathMean(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
return angle1 + mathDiff(angle1, angle2) / 2;
|
||||
}
|
||||
|
||||
|
||||
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut)
|
||||
{
|
||||
auto d1 = base[2] - base[3];
|
||||
auto d2 = base[1] - base[2];
|
||||
auto d3 = base[0] - base[1];
|
||||
|
||||
if (d1.small()) {
|
||||
if (d2.small()) {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = 0;
|
||||
return true;
|
||||
} else {
|
||||
angleIn = angleMid = angleOut = mathAtan(d3);
|
||||
}
|
||||
} else {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = mathAtan(d2);
|
||||
} else {
|
||||
angleIn = angleMid = mathAtan(d2);
|
||||
angleOut = mathAtan(d3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (d2.small()) {
|
||||
if (d3.small()) {
|
||||
angleIn = angleMid = angleOut = mathAtan(d1);
|
||||
} else {
|
||||
angleIn = mathAtan(d1);
|
||||
angleOut = mathAtan(d3);
|
||||
angleMid = mathMean(angleIn, angleOut);
|
||||
}
|
||||
} else {
|
||||
if (d3.small()) {
|
||||
angleIn = mathAtan(d1);
|
||||
angleMid = angleOut = mathAtan(d2);
|
||||
} else {
|
||||
angleIn = mathAtan(d1);
|
||||
angleMid = mathAtan(d2);
|
||||
angleOut = mathAtan(d3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
int64_t mathMultiply(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t c = (a * b + 0x8000L) >> 16;
|
||||
return (s > 0) ? c : -c;
|
||||
}
|
||||
|
||||
|
||||
int64_t mathDivide(int64_t a, int64_t b)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
int64_t q = b > 0 ? ((a << 16) + (b >> 1)) / b : 0x7FFFFFFFL;
|
||||
return (s < 0 ? -q : q);
|
||||
}
|
||||
|
||||
|
||||
int64_t mathMulDiv(int64_t a, int64_t b, int64_t c)
|
||||
{
|
||||
int32_t s = 1;
|
||||
|
||||
//move sign
|
||||
if (a < 0) {
|
||||
a = -a;
|
||||
s = -s;
|
||||
}
|
||||
if (b < 0) {
|
||||
b = -b;
|
||||
s = -s;
|
||||
}
|
||||
if (c < 0) {
|
||||
c = -c;
|
||||
s = -s;
|
||||
}
|
||||
int64_t d = c > 0 ? (a * b + (c >> 1)) / c : 0x7FFFFFFFL;
|
||||
|
||||
return (s > 0 ? d : -d);
|
||||
}
|
||||
|
||||
|
||||
void mathRotate(SwPoint& pt, SwFixed angle)
|
||||
{
|
||||
if (angle == 0 || pt.zero()) return;
|
||||
|
||||
Point v = pt.toPoint();
|
||||
|
||||
auto radian = TO_RADIAN(angle);
|
||||
auto cosv = cosf(radian);
|
||||
auto sinv = sinf(radian);
|
||||
|
||||
pt.x = SwCoord(nearbyint((v.x * cosv - v.y * sinv) * 64.0f));
|
||||
pt.y = SwCoord(nearbyint((v.x * sinv + v.y * cosv) * 64.0f));
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathTan(SwFixed angle)
|
||||
{
|
||||
if (angle == 0) return 0;
|
||||
return SwFixed(tanf(TO_RADIAN(angle)) * 65536.0f);
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathSin(SwFixed angle)
|
||||
{
|
||||
if (angle == 0) return 0;
|
||||
return mathCos(SW_ANGLE_PI2 - angle);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathCos(SwFixed angle)
|
||||
{
|
||||
return SwFixed(cosf(TO_RADIAN(angle)) * 65536.0f);
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathLength(const SwPoint& pt)
|
||||
{
|
||||
if (pt.zero()) return 0;
|
||||
|
||||
//trivial case
|
||||
if (pt.x == 0) return abs(pt.y);
|
||||
if (pt.y == 0) return abs(pt.x);
|
||||
|
||||
auto v = pt.toPoint();
|
||||
//return static_cast<SwFixed>(sqrtf(v.x * v.x + v.y * v.y) * 65536.0f);
|
||||
|
||||
/* approximate sqrt(x*x + y*y) using alpha max plus beta min algorithm.
|
||||
With alpha = 1, beta = 3/8, giving results with the largest error less
|
||||
than 7% compared to the exact value. */
|
||||
if (v.x < 0) v.x = -v.x;
|
||||
if (v.y < 0) v.y = -v.y;
|
||||
return static_cast<SwFixed>((v.x > v.y) ? (v.x + v.y * 0.375f) : (v.y + v.x * 0.375f));
|
||||
}
|
||||
|
||||
|
||||
void mathSplitCubic(SwPoint* base)
|
||||
{
|
||||
SwCoord a, b, c, d;
|
||||
|
||||
base[6].x = base[3].x;
|
||||
c = base[1].x;
|
||||
d = base[2].x;
|
||||
base[1].x = a = (base[0].x + c) >> 1;
|
||||
base[5].x = b = (base[3].x + d) >> 1;
|
||||
c = (c + d) >> 1;
|
||||
base[2].x = a = (a + c) >> 1;
|
||||
base[4].x = b = (b + c) >> 1;
|
||||
base[3].x = (a + b) >> 1;
|
||||
|
||||
base[6].y = base[3].y;
|
||||
c = base[1].y;
|
||||
d = base[2].y;
|
||||
base[1].y = a = (base[0].y + c) >> 1;
|
||||
base[5].y = b = (base[3].y + d) >> 1;
|
||||
c = (c + d) >> 1;
|
||||
base[2].y = a = (a + c) >> 1;
|
||||
base[4].y = b = (b + c) >> 1;
|
||||
base[3].y = (a + b) >> 1;
|
||||
}
|
||||
|
||||
|
||||
SwFixed mathDiff(SwFixed angle1, SwFixed angle2)
|
||||
{
|
||||
auto delta = angle2 - angle1;
|
||||
|
||||
delta %= SW_ANGLE_2PI;
|
||||
if (delta < 0) delta += SW_ANGLE_2PI;
|
||||
if (delta > SW_ANGLE_PI) delta -= SW_ANGLE_2PI;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
return {TO_SWCOORD(tx), TO_SWCOORD(ty)};
|
||||
}
|
||||
|
||||
|
||||
bool mathClipBBox(const SwBBox& clipper, SwBBox& clipee)
|
||||
{
|
||||
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;
|
||||
|
||||
//Check valid region
|
||||
if (clipee.max.x - clipee.min.x < 1 && clipee.max.y - clipee.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;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
|
||||
{
|
||||
if (!outline) return false;
|
||||
|
||||
if (outline->pts.empty() || outline->cntrs.empty()) {
|
||||
renderRegion.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto pt = outline->pts.begin();
|
||||
|
||||
auto xMin = pt->x;
|
||||
auto xMax = pt->x;
|
||||
auto yMin = pt->y;
|
||||
auto yMax = pt->y;
|
||||
|
||||
for (++pt; pt < outline->pts.end(); ++pt) {
|
||||
if (xMin > pt->x) xMin = pt->x;
|
||||
if (xMax < pt->x) xMax = pt->x;
|
||||
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));
|
||||
renderRegion.min.y = static_cast<SwCoord>(nearbyint(yMin / 64.0f));
|
||||
renderRegion.max.y = static_cast<SwCoord>(nearbyint(yMax / 64.0f));
|
||||
} else {
|
||||
renderRegion.min.x = xMin >> 6;
|
||||
renderRegion.max.x = (xMax + 63) >> 6;
|
||||
renderRegion.min.y = yMin >> 6;
|
||||
renderRegion.max.y = (yMax + 63) >> 6;
|
||||
}
|
||||
return mathClipBBox(clipRegion, renderRegion);
|
||||
}
|
||||
129
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
vendored
Normal file
129
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp
vendored
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgSwCommon.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwOutline* mpoolReqOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
return &mpool->outline[idx];
|
||||
}
|
||||
|
||||
|
||||
void mpoolRetOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
mpool->outline[idx].pts.clear();
|
||||
mpool->outline[idx].cntrs.clear();
|
||||
mpool->outline[idx].types.clear();
|
||||
mpool->outline[idx].closed.clear();
|
||||
}
|
||||
|
||||
|
||||
SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
return &mpool->strokeOutline[idx];
|
||||
}
|
||||
|
||||
|
||||
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
mpool->strokeOutline[idx].pts.clear();
|
||||
mpool->strokeOutline[idx].cntrs.clear();
|
||||
mpool->strokeOutline[idx].types.clear();
|
||||
mpool->strokeOutline[idx].closed.clear();
|
||||
}
|
||||
|
||||
|
||||
SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
return &mpool->dashOutline[idx];
|
||||
}
|
||||
|
||||
|
||||
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx)
|
||||
{
|
||||
mpool->dashOutline[idx].pts.clear();
|
||||
mpool->dashOutline[idx].cntrs.clear();
|
||||
mpool->dashOutline[idx].types.clear();
|
||||
mpool->dashOutline[idx].closed.clear();
|
||||
}
|
||||
|
||||
|
||||
SwMpool* mpoolInit(uint32_t threads)
|
||||
{
|
||||
auto allocSize = threads + 1;
|
||||
|
||||
auto mpool = static_cast<SwMpool*>(calloc(1, sizeof(SwMpool)));
|
||||
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
|
||||
mpool->strokeOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
|
||||
mpool->dashOutline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
|
||||
mpool->allocSize = allocSize;
|
||||
|
||||
return mpool;
|
||||
}
|
||||
|
||||
|
||||
bool mpoolClear(SwMpool* mpool)
|
||||
{
|
||||
for (unsigned i = 0; i < mpool->allocSize; ++i) {
|
||||
mpool->outline[i].pts.reset();
|
||||
mpool->outline[i].cntrs.reset();
|
||||
mpool->outline[i].types.reset();
|
||||
mpool->outline[i].closed.reset();
|
||||
|
||||
mpool->strokeOutline[i].pts.reset();
|
||||
mpool->strokeOutline[i].cntrs.reset();
|
||||
mpool->strokeOutline[i].types.reset();
|
||||
mpool->strokeOutline[i].closed.reset();
|
||||
|
||||
mpool->dashOutline[i].pts.reset();
|
||||
mpool->dashOutline[i].cntrs.reset();
|
||||
mpool->dashOutline[i].types.reset();
|
||||
mpool->dashOutline[i].closed.reset();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool mpoolTerm(SwMpool* mpool)
|
||||
{
|
||||
if (!mpool) return false;
|
||||
|
||||
mpoolClear(mpool);
|
||||
|
||||
free(mpool->outline);
|
||||
free(mpool->strokeOutline);
|
||||
free(mpool->dashOutline);
|
||||
free(mpool);
|
||||
|
||||
return true;
|
||||
}
|
||||
1982
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
vendored
Normal file
1982
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
230
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h
vendored
Normal file
230
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
#ifdef THORVG_AVX_VECTOR_SUPPORT
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
#define N_32BITS_IN_128REG 4
|
||||
#define N_32BITS_IN_256REG 8
|
||||
|
||||
static inline __m128i ALPHA_BLEND(__m128i c, __m128i a)
|
||||
{
|
||||
//1. set the masks for the A/G and R/B channels
|
||||
auto AG = _mm_set1_epi32(0xff00ff00);
|
||||
auto RB = _mm_set1_epi32(0x00ff00ff);
|
||||
|
||||
//2. mask the alpha vector - originally quartet [a, a, a, a]
|
||||
auto aAG = _mm_and_si128(a, AG);
|
||||
auto aRB = _mm_and_si128(a, RB);
|
||||
|
||||
//3. calculate the alpha blending of the 2nd and 4th channel
|
||||
//- mask the color vector
|
||||
//- multiply it by the masked alpha vector
|
||||
//- add the correction to compensate bit shifting used instead of dividing by 255
|
||||
//- shift bits - corresponding to division by 256
|
||||
auto even = _mm_and_si128(c, RB);
|
||||
even = _mm_mullo_epi16(even, aRB);
|
||||
even =_mm_add_epi16(even, RB);
|
||||
even = _mm_srli_epi16(even, 8);
|
||||
|
||||
//4. calculate the alpha blending of the 1st and 3rd channel:
|
||||
//- mask the color vector
|
||||
//- multiply it by the corresponding masked alpha vector and store the high bits of the result
|
||||
//- add the correction to compensate division by 256 instead of by 255 (next step)
|
||||
//- remove the low 8 bits to mimic the division by 256
|
||||
auto odd = _mm_and_si128(c, AG);
|
||||
odd = _mm_mulhi_epu16(odd, aAG);
|
||||
odd = _mm_add_epi16(odd, RB);
|
||||
odd = _mm_and_si128(odd, AG);
|
||||
|
||||
//5. the final result
|
||||
return _mm_or_si128(odd, even);
|
||||
}
|
||||
|
||||
|
||||
static void avxRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len)
|
||||
{
|
||||
dst += offset;
|
||||
|
||||
__m256i vecVal = _mm256_set1_epi8(val);
|
||||
|
||||
int32_t i = 0;
|
||||
for (; i <= len - 32; i += 32) {
|
||||
_mm256_storeu_si256((__m256i*)(dst + i), vecVal);
|
||||
}
|
||||
|
||||
for (; i < len; ++i) {
|
||||
dst[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void avxRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||
{
|
||||
//1. calculate how many iterations we need to cover the length
|
||||
uint32_t iterations = len / N_32BITS_IN_256REG;
|
||||
uint32_t avxFilled = iterations * N_32BITS_IN_256REG;
|
||||
|
||||
//2. set the beginning of the array
|
||||
dst += offset;
|
||||
|
||||
//3. fill the octets
|
||||
for (uint32_t i = 0; i < iterations; ++i, dst += N_32BITS_IN_256REG) {
|
||||
_mm256_storeu_si256((__m256i*)dst, _mm256_set1_epi32(val));
|
||||
}
|
||||
|
||||
//4. fill leftovers (in the first step we have to set the pointer to the place where the avx job is done)
|
||||
int32_t leftovers = len - avxFilled;
|
||||
while (leftovers--) *dst++ = val;
|
||||
}
|
||||
|
||||
|
||||
static bool avxRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(r, g, b, a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
|
||||
uint32_t ialpha = 255 - a;
|
||||
|
||||
auto avxColor = _mm_set1_epi32(color);
|
||||
auto avxIalpha = _mm_set1_epi8(ialpha);
|
||||
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
||||
//1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
|
||||
auto notAligned = ((uintptr_t)dst & 0xf) / 4;
|
||||
if (notAligned) {
|
||||
notAligned = (N_32BITS_IN_128REG - notAligned > w ? w : N_32BITS_IN_128REG - notAligned);
|
||||
for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
|
||||
//2. fill the aligned memory - N_32BITS_IN_128REG pixels processed at once
|
||||
uint32_t iterations = (w - notAligned) / N_32BITS_IN_128REG;
|
||||
uint32_t avxFilled = iterations * N_32BITS_IN_128REG;
|
||||
auto avxDst = (__m128i*)dst;
|
||||
for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
|
||||
*avxDst = _mm_add_epi32(avxColor, ALPHA_BLEND(*avxDst, avxIalpha));
|
||||
}
|
||||
|
||||
//3. fill the remaining pixels
|
||||
int32_t leftovers = w - notAligned - avxFilled;
|
||||
dst += avxFilled;
|
||||
while (leftovers--) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto ialpha = ~a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
*dst = a + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool avxRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(r, g, b, a);
|
||||
uint32_t src;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
||||
auto ialpha = IA(src);
|
||||
|
||||
//1. fill the not aligned memory (for 128-bit registers a 16-bytes alignment is required)
|
||||
auto notAligned = ((uintptr_t)dst & 0xf) / 4;
|
||||
if (notAligned) {
|
||||
notAligned = (N_32BITS_IN_128REG - notAligned > span->len ? span->len : N_32BITS_IN_128REG - notAligned);
|
||||
for (uint32_t x = 0; x < notAligned; ++x, ++dst) {
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
|
||||
//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
|
||||
uint32_t iterations = (span->len - notAligned) / N_32BITS_IN_128REG;
|
||||
uint32_t avxFilled = 0;
|
||||
if (iterations > 0) {
|
||||
auto avxSrc = _mm_set1_epi32(src);
|
||||
auto avxIalpha = _mm_set1_epi8(ialpha);
|
||||
|
||||
avxFilled = iterations * N_32BITS_IN_128REG;
|
||||
auto avxDst = (__m128i*)dst;
|
||||
for (uint32_t x = 0; x < iterations; ++x, ++avxDst) {
|
||||
*avxDst = _mm_add_epi32(avxSrc, ALPHA_BLEND(*avxDst, avxIalpha));
|
||||
}
|
||||
}
|
||||
|
||||
//3. fill the remaining pixels
|
||||
int32_t leftovers = span->len - notAligned - avxFilled;
|
||||
dst += avxFilled;
|
||||
while (leftovers--) {
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
dst++;
|
||||
}
|
||||
|
||||
++span;
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require AVX Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, a);
|
||||
else src = a;
|
||||
auto ialpha = ~a;
|
||||
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
|
||||
*dst = src + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
163
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h
vendored
Normal file
163
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
template<typename PIXEL_T>
|
||||
static void inline cRasterPixels(PIXEL_T* dst, PIXEL_T val, uint32_t offset, int32_t len)
|
||||
{
|
||||
dst += offset;
|
||||
|
||||
//fix the misaligned memory
|
||||
auto alignOffset = (long long) dst % 8;
|
||||
if (alignOffset > 0) {
|
||||
if (sizeof(PIXEL_T) == 4) alignOffset /= 4;
|
||||
else if (sizeof(PIXEL_T) == 1) alignOffset = 8 - alignOffset;
|
||||
while (alignOffset > 0 && len > 0) {
|
||||
*dst++ = val;
|
||||
--len;
|
||||
--alignOffset;
|
||||
}
|
||||
}
|
||||
|
||||
//64bits faster clear
|
||||
if ((sizeof(PIXEL_T) == 4)) {
|
||||
auto val64 = (uint64_t(val) << 32) | uint64_t(val);
|
||||
while (len > 1) {
|
||||
*reinterpret_cast<uint64_t*>(dst) = val64;
|
||||
len -= 2;
|
||||
dst += 2;
|
||||
}
|
||||
} else if (sizeof(PIXEL_T) == 1) {
|
||||
auto val32 = (uint32_t(val) << 24) | (uint32_t(val) << 16) | (uint32_t(val) << 8) | uint32_t(val);
|
||||
auto val64 = (uint64_t(val32) << 32) | val32;
|
||||
while (len > 7) {
|
||||
*reinterpret_cast<uint64_t*>(dst) = val64;
|
||||
len -= 8;
|
||||
dst += 8;
|
||||
}
|
||||
}
|
||||
|
||||
//leftovers
|
||||
while (len--) *dst++ = val;
|
||||
}
|
||||
|
||||
|
||||
static bool inline cRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(r, g, b, a);
|
||||
uint32_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
auto ialpha = IA(src);
|
||||
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, a);
|
||||
else src = a;
|
||||
auto ialpha = ~a;
|
||||
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
|
||||
*dst = src + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool inline cRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(r, g, b, a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto ialpha = 255 - a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto ialpha = ~a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
*dst = a + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool inline cRasterABGRtoARGB(Surface* surface)
|
||||
{
|
||||
TVGLOG("SW_ENGINE", "Convert ColorSpace ABGR - ARGB [Size: %d x %d]", surface->w, surface->h);
|
||||
|
||||
//64bits faster converting
|
||||
if (surface->w % 2 == 0) {
|
||||
auto buffer = reinterpret_cast<uint64_t*>(surface->buf32);
|
||||
for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride / 2) {
|
||||
auto dst = buffer;
|
||||
for (uint32_t x = 0; x < surface->w / 2; ++x, ++dst) {
|
||||
auto c = *dst;
|
||||
//flip Blue, Red channels
|
||||
*dst = (c & 0xff000000ff000000) + ((c & 0x00ff000000ff0000) >> 16) + (c & 0x0000ff000000ff00) + ((c & 0x000000ff000000ff) << 16);
|
||||
}
|
||||
}
|
||||
//default converting
|
||||
} else {
|
||||
auto buffer = surface->buf32;
|
||||
for (uint32_t y = 0; y < surface->h; ++y, buffer += surface->stride) {
|
||||
auto dst = buffer;
|
||||
for (uint32_t x = 0; x < surface->w; ++x, ++dst) {
|
||||
auto c = *dst;
|
||||
//flip Blue, Red channels
|
||||
*dst = (c & 0xff000000) + ((c & 0x00ff0000) >> 16) + (c & 0x0000ff00) + ((c & 0x000000ff) << 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool inline cRasterARGBtoABGR(Surface* surface)
|
||||
{
|
||||
//exactly same with ABGRtoARGB
|
||||
return cRasterABGRtoARGB(surface);
|
||||
}
|
||||
200
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h
vendored
Normal file
200
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h
vendored
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
#ifdef THORVG_NEON_VECTOR_SUPPORT
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
//TODO : need to support windows ARM
|
||||
|
||||
#if defined(__ARM_64BIT_STATE) || defined(_M_ARM64)
|
||||
#define TVG_AARCH64 1
|
||||
#else
|
||||
#define TVG_AARCH64 0
|
||||
#endif
|
||||
|
||||
|
||||
static inline uint8x8_t ALPHA_BLEND(uint8x8_t c, uint8x8_t a)
|
||||
{
|
||||
uint16x8_t t = vmull_u8(c, a);
|
||||
return vshrn_n_u16(t, 8);
|
||||
}
|
||||
|
||||
|
||||
static void neonRasterGrayscale8(uint8_t* dst, uint8_t val, uint32_t offset, int32_t len)
|
||||
{
|
||||
dst += offset;
|
||||
|
||||
int32_t i = 0;
|
||||
const uint8x16_t valVec = vdupq_n_u8(val);
|
||||
#if TVG_AARCH64
|
||||
uint8x16x4_t valQuad = {valVec, valVec, valVec, valVec};
|
||||
for (; i <= len - 16 * 4; i += 16 * 4) {
|
||||
vst1q_u8_x4(dst + i, valQuad);
|
||||
}
|
||||
#else
|
||||
for (; i <= len - 16; i += 16) {
|
||||
vst1q_u8(dst + i, valVec);
|
||||
}
|
||||
#endif
|
||||
for (; i < len; i++) {
|
||||
dst[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void neonRasterPixel32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
|
||||
{
|
||||
dst += offset;
|
||||
|
||||
uint32x4_t vectorVal = vdupq_n_u32(val);
|
||||
|
||||
#if TVG_AARCH64
|
||||
uint32_t iterations = len / 16;
|
||||
uint32_t neonFilled = iterations * 16;
|
||||
uint32x4x4_t valQuad = {vectorVal, vectorVal, vectorVal, vectorVal};
|
||||
for (uint32_t i = 0; i < iterations; ++i) {
|
||||
vst4q_u32(dst, valQuad);
|
||||
dst += 16;
|
||||
}
|
||||
#else
|
||||
uint32_t iterations = len / 4;
|
||||
uint32_t neonFilled = iterations * 4;
|
||||
for (uint32_t i = 0; i < iterations; ++i) {
|
||||
vst1q_u32(dst, vectorVal);
|
||||
dst += 4;
|
||||
}
|
||||
#endif
|
||||
int32_t leftovers = len - neonFilled;
|
||||
while (leftovers--) *dst++ = val;
|
||||
}
|
||||
|
||||
|
||||
static bool neonRasterTranslucentRle(SwSurface* surface, const SwRleData* rle, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto span = rle->spans;
|
||||
|
||||
//32bit channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(r, g, b, a);
|
||||
uint32_t src;
|
||||
uint8x8_t *vDst = nullptr;
|
||||
uint16_t align;
|
||||
|
||||
for (uint32_t i = 0; i < rle->size; ++i) {
|
||||
if (span->coverage < 255) src = ALPHA_BLEND(color, span->coverage);
|
||||
else src = color;
|
||||
|
||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||
auto ialpha = IA(src);
|
||||
|
||||
if ((((uintptr_t) dst) & 0x7) != 0) {
|
||||
//fill not aligned byte
|
||||
*dst = src + ALPHA_BLEND(*dst, ialpha);
|
||||
vDst = (uint8x8_t*)(dst + 1);
|
||||
align = 1;
|
||||
} else {
|
||||
vDst = (uint8x8_t*) dst;
|
||||
align = 0;
|
||||
}
|
||||
|
||||
uint8x8_t vSrc = (uint8x8_t) vdup_n_u32(src);
|
||||
uint8x8_t vIalpha = vdup_n_u8((uint8_t) ialpha);
|
||||
|
||||
for (uint32_t x = 0; x < (span->len - align) / 2; ++x)
|
||||
vDst[x] = vadd_u8(vSrc, ALPHA_BLEND(vDst[x], vIalpha));
|
||||
|
||||
auto leftovers = (span->len - align) % 2;
|
||||
if (leftovers > 0) dst[span->len - 1] = src + ALPHA_BLEND(dst[span->len - 1], ialpha);
|
||||
|
||||
++span;
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
|
||||
uint8_t src;
|
||||
for (uint32_t i = 0; i < rle->size; ++i, ++span) {
|
||||
auto dst = &surface->buf8[span->y * surface->stride + span->x];
|
||||
if (span->coverage < 255) src = MULTIPLY(span->coverage, a);
|
||||
else src = a;
|
||||
auto ialpha = ~a;
|
||||
for (uint32_t x = 0; x < span->len; ++x, ++dst) {
|
||||
*dst = src + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool neonRasterTranslucentRect(SwSurface* surface, const SwBBox& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||
|
||||
//32bits channels
|
||||
if (surface->channelSize == sizeof(uint32_t)) {
|
||||
auto color = surface->join(r, g, b, a);
|
||||
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto ialpha = 255 - a;
|
||||
|
||||
auto vColor = vdup_n_u32(color);
|
||||
auto vIalpha = vdup_n_u8((uint8_t) ialpha);
|
||||
|
||||
uint8x8_t* vDst = nullptr;
|
||||
uint32_t align;
|
||||
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
|
||||
if ((((uintptr_t) dst) & 0x7) != 0) {
|
||||
//fill not aligned byte
|
||||
*dst = color + ALPHA_BLEND(*dst, ialpha);
|
||||
vDst = (uint8x8_t*) (dst + 1);
|
||||
align = 1;
|
||||
} else {
|
||||
vDst = (uint8x8_t*) dst;
|
||||
align = 0;
|
||||
}
|
||||
|
||||
for (uint32_t x = 0; x < (w - align) / 2; ++x)
|
||||
vDst[x] = vadd_u8((uint8x8_t)vColor, ALPHA_BLEND(vDst[x], vIalpha));
|
||||
|
||||
auto leftovers = (w - align) % 2;
|
||||
if (leftovers > 0) dst[w - 1] = color + ALPHA_BLEND(dst[w - 1], ialpha);
|
||||
}
|
||||
//8bit grayscale
|
||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||
TVGLOG("SW_ENGINE", "Require Neon Optimization, Channel Size = %d", surface->channelSize);
|
||||
auto buffer = surface->buf8 + (region.min.y * surface->stride) + region.min.x;
|
||||
auto ialpha = ~a;
|
||||
for (uint32_t y = 0; y < h; ++y) {
|
||||
auto dst = &buffer[y * surface->stride];
|
||||
for (uint32_t x = 0; x < w; ++x, ++dst) {
|
||||
*dst = a + MULTIPLY(*dst, ialpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
1202
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
vendored
Normal file
1202
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h
vendored
Normal file
File diff suppressed because it is too large
Load diff
856
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
vendored
Normal file
856
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp
vendored
Normal file
|
|
@ -0,0 +1,856 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 <algorithm>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgSwCommon.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgSwRenderer.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
static int32_t initEngineCnt = false;
|
||||
static int32_t rendererCnt = 0;
|
||||
static SwMpool* globalMpool = nullptr;
|
||||
static uint32_t threadsCnt = 0;
|
||||
|
||||
struct SwTask : Task
|
||||
{
|
||||
SwSurface* surface = nullptr;
|
||||
SwMpool* mpool = nullptr;
|
||||
SwBBox bbox = {{0, 0}, {0, 0}}; //Whole Rendering Region
|
||||
Matrix* transform = nullptr;
|
||||
Array<RenderData> clips;
|
||||
RenderUpdateFlag flags = RenderUpdateFlag::None;
|
||||
uint8_t opacity;
|
||||
bool pushed = false; //Pushed into task list?
|
||||
bool disposed = false; //Disposed task?
|
||||
|
||||
RenderRegion bounds()
|
||||
{
|
||||
//Can we skip the synchronization?
|
||||
done();
|
||||
|
||||
RenderRegion region;
|
||||
|
||||
//Range over?
|
||||
region.x = bbox.min.x > 0 ? bbox.min.x : 0;
|
||||
region.y = bbox.min.y > 0 ? bbox.min.y : 0;
|
||||
region.w = bbox.max.x - region.x;
|
||||
region.h = bbox.max.y - region.y;
|
||||
if (region.w < 0) region.w = 0;
|
||||
if (region.h < 0) region.h = 0;
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
virtual void dispose() = 0;
|
||||
virtual bool clip(SwRleData* target) = 0;
|
||||
virtual SwRleData* rle() = 0;
|
||||
|
||||
virtual ~SwTask()
|
||||
{
|
||||
free(transform);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct SwShapeTask : SwTask
|
||||
{
|
||||
SwShape shape;
|
||||
const RenderShape* rshape = nullptr;
|
||||
bool clipper = false;
|
||||
|
||||
/* We assume that if the stroke width is greater than 2,
|
||||
the shape's outline beneath the stroke could be adequately covered by the stroke drawing.
|
||||
Therefore, antialiasing is disabled under this condition.
|
||||
Additionally, the stroke style should not be dashed. */
|
||||
bool antialiasing(float strokeWidth)
|
||||
{
|
||||
return strokeWidth < 2.0f || rshape->stroke->dashCnt > 0 || rshape->stroke->strokeFirst || rshape->strokeTrim() || rshape->stroke->color[3] < 255;;
|
||||
}
|
||||
|
||||
float validStrokeWidth()
|
||||
{
|
||||
if (!rshape->stroke) return 0.0f;
|
||||
|
||||
auto width = rshape->stroke->width;
|
||||
if (mathZero(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 (transform) return (width * sqrt(transform->e11 * transform->e11 + transform->e12 * transform->e12));
|
||||
else return width;
|
||||
}
|
||||
|
||||
|
||||
bool clip(SwRleData* 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;
|
||||
}
|
||||
|
||||
void run(unsigned tid) override
|
||||
{
|
||||
if (opacity == 0 && !clipper) return; //Invisible
|
||||
|
||||
auto strokeWidth = validStrokeWidth();
|
||||
bool visibleFill = false;
|
||||
auto clipRegion = bbox;
|
||||
|
||||
//This checks also for the case, if the invisible shape turned to visible by alpha.
|
||||
auto prepareShape = false;
|
||||
if (!shapePrepared(&shape) && (flags & RenderUpdateFlag::Color)) prepareShape = true;
|
||||
|
||||
//Shape
|
||||
if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform) || prepareShape) {
|
||||
uint8_t alpha = 0;
|
||||
rshape->fillColor(nullptr, nullptr, nullptr, &alpha);
|
||||
alpha = MULTIPLY(alpha, opacity);
|
||||
visibleFill = (alpha > 0 || rshape->fill);
|
||||
if (visibleFill || clipper) {
|
||||
shapeReset(&shape);
|
||||
if (!shapePrepare(&shape, rshape, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) {
|
||||
visibleFill = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Fill
|
||||
if (flags & (RenderUpdateFlag::Path |RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) {
|
||||
if (visibleFill || clipper) {
|
||||
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
|
||||
}
|
||||
if (auto fill = rshape->fill) {
|
||||
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
|
||||
if (ctable) shapeResetFill(&shape);
|
||||
if (!shapeGenFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
|
||||
} else {
|
||||
shapeDelFill(&shape);
|
||||
}
|
||||
}
|
||||
//Stroke
|
||||
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 (auto fill = rshape->strokeFill()) {
|
||||
auto ctable = (flags & RenderUpdateFlag::GradientStroke) ? true : false;
|
||||
if (ctable) shapeResetStrokeFill(&shape);
|
||||
if (!shapeGenStrokeFillColors(&shape, fill, transform, surface, opacity, ctable)) goto err;
|
||||
} else {
|
||||
shapeDelStrokeFill(&shape);
|
||||
}
|
||||
} else {
|
||||
shapeDelStroke(&shape);
|
||||
}
|
||||
}
|
||||
|
||||
//Clear current task memorypool here if the clippers would use the same memory pool
|
||||
shapeDelOutline(&shape, mpool, tid);
|
||||
|
||||
//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;
|
||||
}
|
||||
return;
|
||||
|
||||
err:
|
||||
shapeReset(&shape);
|
||||
shapeDelOutline(&shape, mpool, tid);
|
||||
}
|
||||
|
||||
void dispose() override
|
||||
{
|
||||
shapeFree(&shape);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
|
||||
bool clip(SwRleData* 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;
|
||||
|
||||
//Convert colorspace if it's not aligned.
|
||||
rasterConvertCS(source, surface->cs);
|
||||
rasterPremultiply(source);
|
||||
|
||||
image.data = source->data;
|
||||
image.w = source->w;
|
||||
image.h = source->h;
|
||||
image.stride = source->stride;
|
||||
image.channelSize = source->channelSize;
|
||||
|
||||
//Invisible shape turned to visible by alpha.
|
||||
if ((flags & (RenderUpdateFlag::Image | RenderUpdateFlag::Transform | RenderUpdateFlag::Color)) && (opacity > 0)) {
|
||||
imageReset(&image);
|
||||
if (!image.data || image.w == 0 || image.h == 0) goto end;
|
||||
|
||||
if (!imagePrepare(&image, mesh, 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 (!imageGenRle(&image, bbox, false)) goto end;
|
||||
if (image.rle) {
|
||||
//Clear current task memorypool here if the clippers would use the same memory pool
|
||||
imageDelOutline(&image, mpool, tid);
|
||||
for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
|
||||
auto clipper = static_cast<SwTask*>(*clip);
|
||||
if (!clipper->clip(image.rle)) goto err;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
goto end;
|
||||
err:
|
||||
rleReset(image.rle);
|
||||
end:
|
||||
imageDelOutline(&image, mpool, tid);
|
||||
}
|
||||
|
||||
void dispose() override
|
||||
{
|
||||
imageFree(&image);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static void _termEngine()
|
||||
{
|
||||
if (rendererCnt > 0) return;
|
||||
|
||||
mpoolTerm(globalMpool);
|
||||
globalMpool = nullptr;
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
} else {
|
||||
task->rshape->fillColor(&r, &g, &b, &a);
|
||||
a = MULTIPLY(opacity, a);
|
||||
if (a > 0) rasterShape(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
} else {
|
||||
if (task->rshape->strokeColor(&r, &g, &b, &a)) {
|
||||
a = MULTIPLY(opacity, a);
|
||||
if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
SwRenderer::~SwRenderer()
|
||||
{
|
||||
clearCompositors();
|
||||
|
||||
delete(surface);
|
||||
|
||||
if (!sharedMpool) mpoolTerm(mpool);
|
||||
|
||||
--rendererCnt;
|
||||
|
||||
if (rendererCnt == 0 && initEngineCnt == 0) _termEngine();
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::clear()
|
||||
{
|
||||
for (auto task = tasks.begin(); task < tasks.end(); ++task) {
|
||||
if ((*task)->disposed) {
|
||||
delete(*task);
|
||||
} else {
|
||||
(*task)->done();
|
||||
(*task)->pushed = false;
|
||||
}
|
||||
}
|
||||
tasks.clear();
|
||||
|
||||
if (!sharedMpool) mpoolClear(mpool);
|
||||
|
||||
if (surface) {
|
||||
vport.x = vport.y = 0;
|
||||
vport.w = surface->w;
|
||||
vport.h = surface->h;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::sync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RenderRegion SwRenderer::viewport()
|
||||
{
|
||||
return vport;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::viewport(const RenderRegion& vp)
|
||||
{
|
||||
vport = vp;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs)
|
||||
{
|
||||
if (!data || stride == 0 || w == 0 || h == 0 || w > stride) return false;
|
||||
|
||||
clearCompositors();
|
||||
|
||||
if (!surface) surface = new SwSurface;
|
||||
|
||||
surface->data = data;
|
||||
surface->stride = stride;
|
||||
surface->w = w;
|
||||
surface->h = h;
|
||||
surface->cs = cs;
|
||||
surface->channelSize = CHANNEL_SIZE(cs);
|
||||
surface->premultiplied = true;
|
||||
|
||||
return rasterCompositor(surface);
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::preRender()
|
||||
{
|
||||
return rasterClear(surface, 0, 0, surface->w, surface->h);
|
||||
}
|
||||
|
||||
|
||||
void SwRenderer::clearCompositors()
|
||||
{
|
||||
//Free Composite Caches
|
||||
for (auto comp = compositors.begin(); comp < compositors.end(); ++comp) {
|
||||
free((*comp)->compositor->image.data);
|
||||
delete((*comp)->compositor);
|
||||
delete(*comp);
|
||||
}
|
||||
compositors.reset();
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::postRender()
|
||||
{
|
||||
//Unmultiply alpha if needed
|
||||
if (surface->cs == ColorSpace::ABGR8888S || surface->cs == ColorSpace::ARGB8888S) {
|
||||
rasterUnpremultiply(surface);
|
||||
}
|
||||
|
||||
for (auto task = tasks.begin(); task < tasks.end(); ++task) {
|
||||
if ((*task)->disposed) delete(*task);
|
||||
else (*task)->pushed = false;
|
||||
}
|
||||
tasks.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::renderImage(RenderData data)
|
||||
{
|
||||
auto task = static_cast<SwImageTask*>(data);
|
||||
task->done();
|
||||
|
||||
if (task->opacity == 0) return true;
|
||||
|
||||
return rasterImage(surface, &task->image, task->mesh, task->transform, task->bbox, task->opacity);
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::renderShape(RenderData data)
|
||||
{
|
||||
auto task = static_cast<SwShapeTask*>(data);
|
||||
if (!task) return false;
|
||||
|
||||
task->done();
|
||||
|
||||
if (task->opacity == 0) return true;
|
||||
|
||||
//Main raster stage
|
||||
if (task->rshape->stroke && task->rshape->stroke->strokeFirst) {
|
||||
_renderStroke(task, surface, task->opacity);
|
||||
_renderFill(task, surface, task->opacity);
|
||||
} else {
|
||||
_renderFill(task, surface, task->opacity);
|
||||
_renderStroke(task, surface, task->opacity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::blend(BlendMethod method)
|
||||
{
|
||||
if (surface->blendMethod == method) return true;
|
||||
surface->blendMethod = method;
|
||||
|
||||
switch (method) {
|
||||
case BlendMethod::Add:
|
||||
surface->blender = opBlendAdd;
|
||||
break;
|
||||
case BlendMethod::Screen:
|
||||
surface->blender = opBlendScreen;
|
||||
break;
|
||||
case BlendMethod::Multiply:
|
||||
surface->blender = opBlendMultiply;
|
||||
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;
|
||||
case BlendMethod::Lighten:
|
||||
surface->blender = opBlendLighten;
|
||||
break;
|
||||
case BlendMethod::ColorDodge:
|
||||
surface->blender = opBlendColorDodge;
|
||||
break;
|
||||
case BlendMethod::ColorBurn:
|
||||
surface->blender = opBlendColorBurn;
|
||||
break;
|
||||
case BlendMethod::HardLight:
|
||||
surface->blender = opBlendHardLight;
|
||||
break;
|
||||
case BlendMethod::SoftLight:
|
||||
surface->blender = opBlendSoftLight;
|
||||
break;
|
||||
default:
|
||||
surface->blender = nullptr;
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
RenderRegion SwRenderer::region(RenderData data)
|
||||
{
|
||||
return static_cast<SwTask*>(data)->bounds();
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity)
|
||||
{
|
||||
if (!cmp) return false;
|
||||
auto p = static_cast<SwCompositor*>(cmp);
|
||||
|
||||
p->method = method;
|
||||
p->opacity = opacity;
|
||||
|
||||
//Current Context?
|
||||
if (p->method != CompositeMethod::None) {
|
||||
surface = p->recoverSfc;
|
||||
surface->compositor = p;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::mempool(bool shared)
|
||||
{
|
||||
if (shared == sharedMpool) return true;
|
||||
|
||||
if (shared) {
|
||||
if (!sharedMpool) {
|
||||
if (!mpoolTerm(mpool)) return false;
|
||||
mpool = globalMpool;
|
||||
}
|
||||
} else {
|
||||
if (sharedMpool) mpool = mpoolInit(threadsCnt);
|
||||
}
|
||||
|
||||
sharedMpool = shared;
|
||||
|
||||
if (mpool) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const Surface* SwRenderer::mainSurface()
|
||||
{
|
||||
return surface;
|
||||
}
|
||||
|
||||
|
||||
Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs)
|
||||
{
|
||||
auto x = region.x;
|
||||
auto y = region.y;
|
||||
auto w = region.w;
|
||||
auto h = region.h;
|
||||
auto sw = static_cast<int32_t>(surface->w);
|
||||
auto sh = static_cast<int32_t>(surface->h);
|
||||
|
||||
//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);
|
||||
}
|
||||
|
||||
//Boundary Check
|
||||
if (x + w > sw) w = (sw - x);
|
||||
if (y + h > sh) h = (sh - y);
|
||||
|
||||
cmp->compositor->recoverSfc = surface;
|
||||
cmp->compositor->recoverCmp = surface->compositor;
|
||||
cmp->compositor->valid = false;
|
||||
cmp->compositor->bbox.min.x = x;
|
||||
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);
|
||||
|
||||
//Switch render target
|
||||
surface = cmp;
|
||||
|
||||
return cmp->compositor;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::endComposite(Compositor* cmp)
|
||||
{
|
||||
if (!cmp) return false;
|
||||
|
||||
auto p = static_cast<SwCompositor*>(cmp);
|
||||
p->valid = true;
|
||||
|
||||
//Recover Context
|
||||
surface = p->recoverSfc;
|
||||
surface->compositor = p->recoverCmp;
|
||||
|
||||
//Default is alpha blending
|
||||
if (p->method == CompositeMethod::None) {
|
||||
return rasterImage(surface, &p->image, nullptr, nullptr, p->bbox, p->opacity);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
ColorSpace SwRenderer::colorSpace()
|
||||
{
|
||||
if (surface) return surface->cs;
|
||||
else return ColorSpace::Unsupported;
|
||||
}
|
||||
|
||||
|
||||
void SwRenderer::dispose(RenderData data)
|
||||
{
|
||||
auto task = static_cast<SwTask*>(data);
|
||||
if (!task) return;
|
||||
task->done();
|
||||
task->dispose();
|
||||
|
||||
if (task->pushed) task->disposed = true;
|
||||
else delete(task);
|
||||
}
|
||||
|
||||
|
||||
void* SwRenderer::prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
{
|
||||
if (!surface) return task;
|
||||
if (flags == RenderUpdateFlag::None) return task;
|
||||
|
||||
//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 clip = clips.begin(); clip < clips.end(); ++clip) {
|
||||
static_cast<SwTask*>(*clip)->done();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//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
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
if (!task->pushed) {
|
||||
task->pushed = true;
|
||||
tasks.push(task);
|
||||
}
|
||||
|
||||
TaskScheduler::request(task);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
RenderData SwRenderer::prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwImageTask*>(data);
|
||||
if (!task) task = new SwImageTask;
|
||||
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)
|
||||
{
|
||||
//prepare task
|
||||
auto task = static_cast<SwShapeTask*>(data);
|
||||
if (!task) task = new SwShapeTask;
|
||||
else task->done();
|
||||
|
||||
task->rshape = &rshape;
|
||||
task->clipper = clipper;
|
||||
|
||||
return prepareCommon(task, transform, clips, opacity, flags);
|
||||
}
|
||||
|
||||
|
||||
SwRenderer::SwRenderer():mpool(globalMpool)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::init(uint32_t threads)
|
||||
{
|
||||
if ((initEngineCnt++) > 0) return true;
|
||||
|
||||
threadsCnt = threads;
|
||||
|
||||
//Share the memory pool among the renderer
|
||||
globalMpool = mpoolInit(threads);
|
||||
if (!globalMpool) {
|
||||
--initEngineCnt;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
int32_t SwRenderer::init()
|
||||
{
|
||||
return initEngineCnt;
|
||||
}
|
||||
|
||||
|
||||
bool SwRenderer::term()
|
||||
{
|
||||
if ((--initEngineCnt) > 0) return true;
|
||||
|
||||
initEngineCnt = 0;
|
||||
|
||||
_termEngine();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SwRenderer* SwRenderer::gen()
|
||||
{
|
||||
++rendererCnt;
|
||||
return new SwRenderer();
|
||||
}
|
||||
85
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h
vendored
Normal file
85
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SW_RENDERER_H_
|
||||
#define _TVG_SW_RENDERER_H_
|
||||
|
||||
#include "tvgRender.h"
|
||||
|
||||
struct SwSurface;
|
||||
struct SwTask;
|
||||
struct SwCompositor;
|
||||
struct SwMpool;
|
||||
|
||||
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;
|
||||
bool preRender() override;
|
||||
bool renderShape(RenderData data) override;
|
||||
bool renderImage(RenderData data) override;
|
||||
bool postRender() override;
|
||||
void dispose(RenderData data) override;
|
||||
RenderRegion region(RenderData data) override;
|
||||
RenderRegion viewport() override;
|
||||
bool viewport(const RenderRegion& vp) override;
|
||||
bool blend(BlendMethod method) override;
|
||||
ColorSpace colorSpace() override;
|
||||
const Surface* 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;
|
||||
void clearCompositors();
|
||||
|
||||
static SwRenderer* gen();
|
||||
static bool init(uint32_t threads);
|
||||
static int32_t init();
|
||||
static bool term();
|
||||
|
||||
private:
|
||||
SwSurface* surface = nullptr; //active surface
|
||||
Array<SwTask*> tasks; //async task list
|
||||
Array<SwSurface*> compositors; //render targets cache list
|
||||
SwMpool* mpool; //private memory pool
|
||||
RenderRegion vport; //viewport
|
||||
bool sharedMpool = true; //memory-pool behavior policy
|
||||
|
||||
SwRenderer();
|
||||
~SwRenderer();
|
||||
|
||||
RenderData prepareCommon(SwTask* task, const RenderTransform* transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* _TVG_SW_RENDERER_H_ */
|
||||
1094
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
vendored
Normal file
1094
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
677
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
vendored
Normal file
677
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp
vendored
Normal file
|
|
@ -0,0 +1,677 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgSwCommon.h"
|
||||
#include "tvgMath.h"
|
||||
#include "tvgLines.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static bool _outlineBegin(SwOutline& outline)
|
||||
{
|
||||
//Make a contour if lineTo/curveTo without calling close or moveTo beforehand.
|
||||
if (outline.pts.empty()) return false;
|
||||
outline.cntrs.push(outline.pts.count - 1);
|
||||
outline.closed.push(false);
|
||||
outline.pts.push(outline.pts[outline.cntrs.last()]);
|
||||
outline.types.push(SW_CURVE_TYPE_POINT);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static bool _outlineEnd(SwOutline& outline)
|
||||
{
|
||||
if (outline.pts.empty()) return false;
|
||||
outline.cntrs.push(outline.pts.count - 1);
|
||||
outline.closed.push(false);
|
||||
return 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);
|
||||
|
||||
outline.pts.push(mathTransform(to, transform));
|
||||
outline.types.push(SW_CURVE_TYPE_POINT);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
outline.pts.push(mathTransform(ctrl1, transform));
|
||||
outline.types.push(SW_CURVE_TYPE_CUBIC);
|
||||
|
||||
outline.pts.push(mathTransform(ctrl2, transform));
|
||||
outline.types.push(SW_CURVE_TYPE_CUBIC);
|
||||
|
||||
outline.pts.push(mathTransform(to, transform));
|
||||
outline.types.push(SW_CURVE_TYPE_POINT);
|
||||
}
|
||||
|
||||
|
||||
static bool _outlineClose(SwOutline& outline)
|
||||
{
|
||||
uint32_t i;
|
||||
if (outline.cntrs.count > 0) i = outline.cntrs.last() + 1;
|
||||
else i = 0;
|
||||
|
||||
//Make sure there is at least one point in the current path
|
||||
if (outline.pts.count == i) return false;
|
||||
|
||||
//Close the path
|
||||
outline.pts.push(outline.pts[i]);
|
||||
outline.cntrs.push(outline.pts.count - 1);
|
||||
outline.types.push(SW_CURVE_TYPE_POINT);
|
||||
outline.closed.push(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static void _dashLineTo(SwDashStroke& dash, const Point* to, const Matrix* transform)
|
||||
{
|
||||
Line cur = {dash.ptCur, *to};
|
||||
auto len = lineLength(cur.pt1, cur.pt2);
|
||||
|
||||
if (mathZero(len)) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
//draw the current line fully
|
||||
} else if (len <= dash.curLen) {
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
dash.move = false;
|
||||
}
|
||||
_outlineLineTo(*dash.outline, to, transform);
|
||||
}
|
||||
//draw the current line partially
|
||||
} else {
|
||||
while (len - dash.curLen > 0.0001f) {
|
||||
Line left, right;
|
||||
if (dash.curLen > 0) {
|
||||
len -= dash.curLen;
|
||||
lineSplitAt(cur, dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
|
||||
_outlineMoveTo(*dash.outline, &left.pt1, transform);
|
||||
dash.move = false;
|
||||
}
|
||||
_outlineLineTo(*dash.outline, &left.pt2, transform);
|
||||
}
|
||||
} else {
|
||||
right = cur;
|
||||
}
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
cur = right;
|
||||
dash.ptCur = cur.pt1;
|
||||
dash.move = true;
|
||||
}
|
||||
//leftovers
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move) {
|
||||
_outlineMoveTo(*dash.outline, &cur.pt1, transform);
|
||||
dash.move = false;
|
||||
}
|
||||
_outlineLineTo(*dash.outline, &cur.pt2, transform);
|
||||
}
|
||||
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
|
||||
//move to next dash
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
}
|
||||
}
|
||||
dash.ptCur = *to;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
|
||||
//draw the current line fully
|
||||
if (mathZero(len)) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
} else if (len <= dash.curLen) {
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move) {
|
||||
_outlineMoveTo(*dash.outline, &dash.ptCur, transform);
|
||||
dash.move = false;
|
||||
}
|
||||
_outlineCubicTo(*dash.outline, ctrl1, ctrl2, to, transform);
|
||||
}
|
||||
//draw the current line partially
|
||||
} else {
|
||||
while ((len - dash.curLen) > 0.0001f) {
|
||||
Bezier left, right;
|
||||
if (dash.curLen > 0) {
|
||||
len -= dash.curLen;
|
||||
bezSplitAt(cur, dash.curLen, left, right);
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move || dash.pattern[dash.curIdx] - dash.curLen < FLOAT_EPSILON) {
|
||||
_outlineMoveTo(*dash.outline, &left.start, transform);
|
||||
dash.move = false;
|
||||
}
|
||||
_outlineCubicTo(*dash.outline, &left.ctrl1, &left.ctrl2, &left.end, transform);
|
||||
}
|
||||
} else {
|
||||
right = cur;
|
||||
}
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
cur = right;
|
||||
dash.ptCur = right.start;
|
||||
dash.move = true;
|
||||
}
|
||||
//leftovers
|
||||
dash.curLen -= len;
|
||||
if (!dash.curOpGap) {
|
||||
if (dash.move) {
|
||||
_outlineMoveTo(*dash.outline, &cur.start, transform);
|
||||
dash.move = false;
|
||||
}
|
||||
_outlineCubicTo(*dash.outline, &cur.ctrl1, &cur.ctrl2, &cur.end, transform);
|
||||
}
|
||||
if (dash.curLen < 1 && TO_SWCOORD(len) > 1) {
|
||||
//move to next dash
|
||||
dash.curIdx = (dash.curIdx + 1) % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx];
|
||||
dash.curOpGap = !dash.curOpGap;
|
||||
}
|
||||
}
|
||||
dash.ptCur = *to;
|
||||
}
|
||||
|
||||
|
||||
static void _dashClose(SwDashStroke& dash, const Matrix* transform)
|
||||
{
|
||||
_dashLineTo(dash, &dash.ptStart, transform);
|
||||
}
|
||||
|
||||
|
||||
static void _dashMoveTo(SwDashStroke& dash, const Point* pts)
|
||||
{
|
||||
dash.ptCur = *pts;
|
||||
dash.ptStart = *pts;
|
||||
dash.move = true;
|
||||
}
|
||||
|
||||
|
||||
static void _dashMoveTo(SwDashStroke& dash, uint32_t offIdx, float offset, const Point* pts)
|
||||
{
|
||||
dash.curIdx = offIdx % dash.cnt;
|
||||
dash.curLen = dash.pattern[dash.curIdx] - offset;
|
||||
dash.curOpGap = offIdx % 2;
|
||||
dash.ptStart = dash.ptCur = *pts;
|
||||
dash.move = true;
|
||||
}
|
||||
|
||||
|
||||
static void _trimPattern(SwDashStroke* dash, const RenderShape* rshape, float length)
|
||||
{
|
||||
auto begin = length * rshape->stroke->trim.begin;
|
||||
auto end = length * rshape->stroke->trim.end;
|
||||
|
||||
//default
|
||||
if (end > begin) {
|
||||
if (begin > 0.0f) dash->cnt = 4;
|
||||
else dash->cnt = 2;
|
||||
//looping
|
||||
} else dash->cnt = 3;
|
||||
|
||||
if (dash->cnt == 2) {
|
||||
dash->pattern[0] = end - begin;
|
||||
dash->pattern[1] = length - (end - begin);
|
||||
} else if (dash->cnt == 3) {
|
||||
dash->pattern[0] = end;
|
||||
dash->pattern[1] = (begin - end);
|
||||
dash->pattern[2] = length - begin;
|
||||
} else {
|
||||
dash->pattern[0] = 0; //zero dash to start with a space.
|
||||
dash->pattern[1] = begin;
|
||||
dash->pattern[2] = end - begin;
|
||||
dash->pattern[3] = length - end;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static float _outlineLength(const RenderShape* rshape, uint32_t shiftPts, uint32_t shiftCmds, bool subpath)
|
||||
{
|
||||
const PathCommand* cmds = rshape->path.cmds.data + shiftCmds;
|
||||
auto cmdCnt = rshape->path.cmds.count - shiftCmds;
|
||||
const Point* pts = rshape->path.pts.data + shiftPts;
|
||||
auto ptsCnt = rshape->path.pts.count - shiftPts;
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt <= 0 || ptsCnt <= 0) return 0.0f;
|
||||
|
||||
const Point* close = nullptr;
|
||||
auto length = 0.0f;
|
||||
|
||||
//must begin with moveTo
|
||||
if (cmds[0] == PathCommand::MoveTo) {
|
||||
close = pts;
|
||||
cmds++;
|
||||
pts++;
|
||||
cmdCnt--;
|
||||
}
|
||||
|
||||
while (cmdCnt-- > 0) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
length += mathLength(pts - 1, close);
|
||||
if (subpath) return length;
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
if (subpath) return length;
|
||||
close = pts;
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
length += mathLength(pts - 1, pts);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
length += bezLength({*(pts - 1), *pts, *(pts + 1), *(pts + 2)});
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++cmds;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
const Point* pts = rshape->path.pts.data;
|
||||
auto ptsCnt = rshape->path.pts.count;
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt == 0 || ptsCnt == 0) return nullptr;
|
||||
|
||||
auto startPts = pts;
|
||||
auto startCmds = cmds;
|
||||
|
||||
SwDashStroke dash;
|
||||
auto offset = 0.0f;
|
||||
dash.cnt = rshape->strokeDash((const float**)&dash.pattern, &offset);
|
||||
auto simultaneous = rshape->stroke->trim.simultaneous;
|
||||
|
||||
if (dash.cnt == 0) {
|
||||
if (trimmed) dash.pattern = (float*)malloc(sizeof(float) * 4);
|
||||
else return nullptr;
|
||||
} else {
|
||||
//TODO: handle dash + trim - for now trimming ignoring is forced
|
||||
trimmed = false;
|
||||
}
|
||||
|
||||
//offset
|
||||
auto patternLength = 0.0f;
|
||||
uint32_t offIdx = 0;
|
||||
if (!mathZero(offset)) {
|
||||
for (size_t i = 0; i < dash.cnt; ++i) patternLength += dash.pattern[i];
|
||||
bool isOdd = dash.cnt % 2;
|
||||
if (isOdd) patternLength *= 2;
|
||||
|
||||
offset = fmodf(offset, patternLength);
|
||||
if (offset < 0) offset += patternLength;
|
||||
|
||||
for (size_t i = 0; i < dash.cnt * (1 + (size_t)isOdd); ++i, ++offIdx) {
|
||||
auto curPattern = dash.pattern[i % dash.cnt];
|
||||
if (offset < curPattern) break;
|
||||
offset -= curPattern;
|
||||
}
|
||||
}
|
||||
|
||||
dash.outline = mpoolReqDashOutline(mpool, tid);
|
||||
|
||||
//must begin with moveTo
|
||||
if (cmds[0] == PathCommand::MoveTo) {
|
||||
if (trimmed) _trimPattern(&dash, rshape, _outlineLength(rshape, 0, 0, simultaneous));
|
||||
_dashMoveTo(dash, offIdx, offset, pts);
|
||||
cmds++;
|
||||
pts++;
|
||||
}
|
||||
|
||||
while (--cmdCnt > 0) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
_dashClose(dash, transform);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
if (trimmed) {
|
||||
if (simultaneous) {
|
||||
_trimPattern(&dash, rshape, _outlineLength(rshape, pts - startPts, cmds - startCmds, true));
|
||||
_dashMoveTo(dash, offIdx, offset, pts);
|
||||
} else _dashMoveTo(dash, pts);
|
||||
} else _dashMoveTo(dash, offIdx, offset, pts);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
_dashLineTo(dash, pts, transform);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
_dashCubicTo(dash, pts, pts + 1, pts + 2, transform);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++cmds;
|
||||
}
|
||||
|
||||
_outlineEnd(*dash.outline);
|
||||
|
||||
if (trimmed) free(dash.pattern);
|
||||
|
||||
return dash.outline;
|
||||
}
|
||||
|
||||
|
||||
static bool _axisAlignedRect(const SwOutline* outline)
|
||||
{
|
||||
//Fast Track: axis-aligned rectangle?
|
||||
if (outline->pts.count != 5) return false;
|
||||
if (outline->types[2] == SW_CURVE_TYPE_CUBIC) return false;
|
||||
|
||||
auto pt1 = outline->pts.data + 0;
|
||||
auto pt2 = outline->pts.data + 1;
|
||||
auto pt3 = outline->pts.data + 2;
|
||||
auto pt4 = outline->pts.data + 3;
|
||||
|
||||
auto a = SwPoint{pt1->x, pt3->y};
|
||||
auto b = SwPoint{pt3->x, pt1->y};
|
||||
|
||||
if ((*pt2 == a && *pt4 == b) || (*pt2 == b && *pt4 == a)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
const Point* pts = rshape->path.pts.data;
|
||||
auto ptsCnt = rshape->path.pts.count;
|
||||
|
||||
//No actual shape data
|
||||
if (cmdCnt == 0 || ptsCnt == 0) return false;
|
||||
|
||||
shape->outline = mpoolReqOutline(mpool, tid);
|
||||
auto outline = shape->outline;
|
||||
auto closed = false;
|
||||
|
||||
//Generate Outlines
|
||||
while (cmdCnt-- > 0) {
|
||||
switch (*cmds) {
|
||||
case PathCommand::Close: {
|
||||
if (!closed) closed = _outlineClose(*outline);
|
||||
break;
|
||||
}
|
||||
case PathCommand::MoveTo: {
|
||||
closed = _outlineMoveTo(*outline, pts, transform, closed);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::LineTo: {
|
||||
if (closed) closed = _outlineBegin(*outline);
|
||||
_outlineLineTo(*outline, pts, transform);
|
||||
++pts;
|
||||
break;
|
||||
}
|
||||
case PathCommand::CubicTo: {
|
||||
if (closed) closed = _outlineBegin(*outline);
|
||||
_outlineCubicTo(*outline, pts, pts + 1, pts + 2, transform);
|
||||
pts += 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
++cmds;
|
||||
}
|
||||
|
||||
if (!closed) _outlineEnd(*outline);
|
||||
|
||||
outline->fillRule = rshape->rule;
|
||||
shape->outline = outline;
|
||||
|
||||
shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
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
|
||||
if (renderRegion.max.x - renderRegion.min.x < 1 && renderRegion.max.y - renderRegion.min.y < 1) return false;
|
||||
|
||||
//Check boundary
|
||||
if (renderRegion.min.x >= clipRegion.max.x || renderRegion.min.y >= clipRegion.max.y ||
|
||||
renderRegion.max.x <= clipRegion.min.x || renderRegion.max.y <= clipRegion.min.y) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool shapePrepared(const SwShape* shape)
|
||||
{
|
||||
return shape->rle ? true : false;
|
||||
}
|
||||
|
||||
|
||||
bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias)
|
||||
{
|
||||
//FIXME: Should we draw it?
|
||||
//Case: Stroke Line
|
||||
//if (shape.outline->opened) return true;
|
||||
|
||||
//Case A: Fast Track Rectangle Drawing
|
||||
if (shape->fastTrack) return true;
|
||||
|
||||
//Case B: Normal Shape RLE Drawing
|
||||
if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid)
|
||||
{
|
||||
mpoolRetOutline(mpool, tid);
|
||||
shape->outline = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void shapeReset(SwShape* shape)
|
||||
{
|
||||
rleReset(shape->rle);
|
||||
rleReset(shape->strokeRle);
|
||||
shape->fastTrack = false;
|
||||
shape->bbox.reset();
|
||||
}
|
||||
|
||||
|
||||
void shapeFree(SwShape* shape)
|
||||
{
|
||||
rleFree(shape->rle);
|
||||
shape->rle = nullptr;
|
||||
|
||||
shapeDelFill(shape);
|
||||
|
||||
if (shape->stroke) {
|
||||
rleFree(shape->strokeRle);
|
||||
shape->strokeRle = nullptr;
|
||||
strokeFree(shape->stroke);
|
||||
shape->stroke = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void shapeDelStroke(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke) return;
|
||||
rleFree(shape->strokeRle);
|
||||
shape->strokeRle = nullptr;
|
||||
strokeFree(shape->stroke);
|
||||
shape->stroke = nullptr;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
if (!stroke) return;
|
||||
|
||||
strokeReset(stroke, rshape, transform);
|
||||
rleReset(shape->strokeRle);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
auto dashStroking = false;
|
||||
auto ret = true;
|
||||
|
||||
//Dash style (+trimming)
|
||||
auto trimmed = rshape->strokeTrim();
|
||||
if (rshape->stroke->dashCnt > 0 || trimmed) {
|
||||
shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
|
||||
if (!shapeOutline) return false;
|
||||
dashStroking = true;
|
||||
//Normal style
|
||||
} else {
|
||||
if (!shape->outline) {
|
||||
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
|
||||
}
|
||||
shapeOutline = shape->outline;
|
||||
}
|
||||
|
||||
if (!strokeParseOutline(shape->stroke, *shapeOutline)) {
|
||||
ret = false;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
|
||||
|
||||
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
|
||||
ret = false;
|
||||
goto clear;
|
||||
}
|
||||
|
||||
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
|
||||
|
||||
clear:
|
||||
if (dashStroking) mpoolRetDashOutline(mpool, tid);
|
||||
mpoolRetStrokeOutline(mpool, tid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
|
||||
}
|
||||
|
||||
|
||||
void shapeResetFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->fill) {
|
||||
shape->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
|
||||
if (!shape->fill) return;
|
||||
}
|
||||
fillReset(shape->fill);
|
||||
}
|
||||
|
||||
|
||||
void shapeResetStrokeFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke->fill) {
|
||||
shape->stroke->fill = static_cast<SwFill*>(calloc(1, sizeof(SwFill)));
|
||||
if (!shape->stroke->fill) return;
|
||||
}
|
||||
fillReset(shape->stroke->fill);
|
||||
}
|
||||
|
||||
|
||||
void shapeDelFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->fill) return;
|
||||
fillFree(shape->fill);
|
||||
shape->fill = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void shapeDelStrokeFill(SwShape* shape)
|
||||
{
|
||||
if (!shape->stroke->fill) return;
|
||||
fillFree(shape->stroke->fill);
|
||||
shape->stroke->fill = nullptr;
|
||||
}
|
||||
907
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp
vendored
Normal file
907
engine/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp
vendored
Normal file
|
|
@ -0,0 +1,907 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 <string.h>
|
||||
#include <math.h>
|
||||
#include "tvgSwCommon.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static constexpr auto SW_STROKE_TAG_POINT = 1;
|
||||
static constexpr auto SW_STROKE_TAG_CUBIC = 2;
|
||||
static constexpr auto SW_STROKE_TAG_BEGIN = 4;
|
||||
static constexpr auto SW_STROKE_TAG_END = 8;
|
||||
|
||||
static inline SwFixed SIDE_TO_ROTATE(const int32_t s)
|
||||
{
|
||||
return (SW_ANGLE_PI2 - static_cast<SwFixed>(s) * SW_ANGLE_PI);
|
||||
}
|
||||
|
||||
|
||||
static inline void SCALE(const SwStroke& stroke, SwPoint& pt)
|
||||
{
|
||||
pt.x = static_cast<SwCoord>(pt.x * stroke.sx);
|
||||
pt.y = static_cast<SwCoord>(pt.y * stroke.sy);
|
||||
}
|
||||
|
||||
|
||||
static void _growBorder(SwStrokeBorder* border, uint32_t newPts)
|
||||
{
|
||||
auto maxOld = border->maxPts;
|
||||
auto maxNew = border->ptsCnt + newPts;
|
||||
|
||||
if (maxNew <= maxOld) return;
|
||||
|
||||
auto maxCur = maxOld;
|
||||
|
||||
while (maxCur < maxNew)
|
||||
maxCur += (maxCur >> 1) + 16;
|
||||
//OPTIMIZE: use mempool!
|
||||
border->pts = static_cast<SwPoint*>(realloc(border->pts, maxCur * sizeof(SwPoint)));
|
||||
border->tags = static_cast<uint8_t*>(realloc(border->tags, maxCur * sizeof(uint8_t)));
|
||||
border->maxPts = maxCur;
|
||||
}
|
||||
|
||||
|
||||
static void _borderClose(SwStrokeBorder* border, bool reverse)
|
||||
{
|
||||
auto start = border->start;
|
||||
auto count = border->ptsCnt;
|
||||
|
||||
//Don't record empty paths!
|
||||
if (count <= start + 1U) {
|
||||
border->ptsCnt = start;
|
||||
} else {
|
||||
/* Copy the last point to the start of this sub-path,
|
||||
since it contains the adjusted starting coordinates */
|
||||
border->ptsCnt = --count;
|
||||
border->pts[start] = border->pts[count];
|
||||
|
||||
if (reverse) {
|
||||
//reverse the points
|
||||
auto pt1 = border->pts + start + 1;
|
||||
auto pt2 = border->pts + count - 1;
|
||||
|
||||
while (pt1 < pt2) {
|
||||
auto tmp = *pt1;
|
||||
*pt1 = *pt2;
|
||||
*pt2 = tmp;
|
||||
++pt1;
|
||||
--pt2;
|
||||
}
|
||||
|
||||
//reverse the tags
|
||||
auto tag1 = border->tags + start + 1;
|
||||
auto tag2 = border->tags + count - 1;
|
||||
|
||||
while (tag1 < tag2) {
|
||||
auto tmp = *tag1;
|
||||
*tag1 = *tag2;
|
||||
*tag2 = tmp;
|
||||
++tag1;
|
||||
--tag2;
|
||||
}
|
||||
}
|
||||
|
||||
border->tags[start] |= SW_STROKE_TAG_BEGIN;
|
||||
border->tags[count - 1] |= SW_STROKE_TAG_END;
|
||||
}
|
||||
|
||||
border->start = -1;
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _borderCubicTo(SwStrokeBorder* border, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
_growBorder(border, 3);
|
||||
|
||||
auto pt = border->pts + border->ptsCnt;
|
||||
auto tag = border->tags + border->ptsCnt;
|
||||
|
||||
pt[0] = ctrl1;
|
||||
pt[1] = ctrl2;
|
||||
pt[2] = to;
|
||||
|
||||
tag[0] = SW_STROKE_TAG_CUBIC;
|
||||
tag[1] = SW_STROKE_TAG_CUBIC;
|
||||
tag[2] = SW_STROKE_TAG_POINT;
|
||||
|
||||
border->ptsCnt += 3;
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _borderArcTo(SwStrokeBorder* border, const SwPoint& center, SwFixed radius, SwFixed angleStart, SwFixed angleDiff, SwStroke& stroke)
|
||||
{
|
||||
constexpr SwFixed ARC_CUBIC_ANGLE = SW_ANGLE_PI / 2;
|
||||
SwPoint a = {static_cast<SwCoord>(radius), 0};
|
||||
mathRotate(a, angleStart);
|
||||
SCALE(stroke, a);
|
||||
a += center;
|
||||
|
||||
auto total = angleDiff;
|
||||
auto angle = angleStart;
|
||||
auto rotate = (angleDiff >= 0) ? SW_ANGLE_PI2 : -SW_ANGLE_PI2;
|
||||
|
||||
while (total != 0) {
|
||||
auto step = total;
|
||||
if (step > ARC_CUBIC_ANGLE) step = ARC_CUBIC_ANGLE;
|
||||
else if (step < -ARC_CUBIC_ANGLE) step = -ARC_CUBIC_ANGLE;
|
||||
|
||||
auto next = angle + step;
|
||||
auto theta = step;
|
||||
if (theta < 0) theta = -theta;
|
||||
|
||||
theta >>= 1;
|
||||
|
||||
//compute end point
|
||||
SwPoint b = {static_cast<SwCoord>(radius), 0};
|
||||
mathRotate(b, next);
|
||||
SCALE(stroke, b);
|
||||
b += center;
|
||||
|
||||
//compute first and second control points
|
||||
auto length = mathMulDiv(radius, mathSin(theta) * 4, (0x10000L + mathCos(theta)) * 3);
|
||||
|
||||
SwPoint a2 = {static_cast<SwCoord>(length), 0};
|
||||
mathRotate(a2, angle + rotate);
|
||||
SCALE(stroke, a2);
|
||||
a2 += a;
|
||||
|
||||
SwPoint b2 = {static_cast<SwCoord>(length), 0};
|
||||
mathRotate(b2, next - rotate);
|
||||
SCALE(stroke, b2);
|
||||
b2 += b;
|
||||
|
||||
//add cubic arc
|
||||
_borderCubicTo(border, a2, b2, b);
|
||||
|
||||
//process the rest of the arc?
|
||||
a = b;
|
||||
total -= step;
|
||||
angle = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _borderLineTo(SwStrokeBorder* border, const SwPoint& to, bool movable)
|
||||
{
|
||||
if (border->movable) {
|
||||
//move last point
|
||||
border->pts[border->ptsCnt - 1] = to;
|
||||
} else {
|
||||
//don't add zero-length line_to
|
||||
if (border->ptsCnt > 0 && (border->pts[border->ptsCnt - 1] - to).small()) return;
|
||||
|
||||
_growBorder(border, 1);
|
||||
border->pts[border->ptsCnt] = to;
|
||||
border->tags[border->ptsCnt] = SW_STROKE_TAG_POINT;
|
||||
border->ptsCnt += 1;
|
||||
}
|
||||
|
||||
border->movable = movable;
|
||||
}
|
||||
|
||||
|
||||
static void _borderMoveTo(SwStrokeBorder* border, SwPoint& to)
|
||||
{
|
||||
//close current open path if any?
|
||||
if (border->start >= 0) _borderClose(border, false);
|
||||
|
||||
border->start = border->ptsCnt;
|
||||
border->movable = false;
|
||||
|
||||
_borderLineTo(border, to, false);
|
||||
}
|
||||
|
||||
|
||||
static void _arcTo(SwStroke& stroke, int32_t side)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto total = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
if (total == SW_ANGLE_PI) total = -rotate * 2;
|
||||
|
||||
_borderArcTo(border, stroke.center, stroke.width, stroke.angleIn + rotate, total, stroke);
|
||||
border->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _outside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
if (stroke.join == StrokeJoin::Round) {
|
||||
_arcTo(stroke, side);
|
||||
} else {
|
||||
//this is a mitered (pointed) or beveled (truncated) corner
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto bevel = stroke.join == StrokeJoin::Bevel;
|
||||
SwFixed phi = 0;
|
||||
SwFixed thcos = 0;
|
||||
|
||||
if (!bevel) {
|
||||
auto theta = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
if (theta == SW_ANGLE_PI) {
|
||||
theta = rotate;
|
||||
phi = stroke.angleIn;
|
||||
} else {
|
||||
theta /= 2;
|
||||
phi = stroke.angleIn + theta + rotate;
|
||||
}
|
||||
|
||||
thcos = mathCos(theta);
|
||||
auto sigma = mathMultiply(stroke.miterlimit, thcos);
|
||||
|
||||
//is miter limit exceeded?
|
||||
if (sigma < 0x10000L) bevel = true;
|
||||
}
|
||||
|
||||
//this is a bevel (broken angle)
|
||||
if (bevel) {
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
border->movable = false;
|
||||
_borderLineTo(border, delta, false);
|
||||
//this is a miter (intersection)
|
||||
} else {
|
||||
auto length = mathDivide(stroke.width, thcos);
|
||||
SwPoint delta = {static_cast<SwCoord>(length), 0};
|
||||
mathRotate(delta, phi);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
/* Now add and end point
|
||||
Only needed if not lineto (lineLength is zero for curves) */
|
||||
if (lineLength == 0) {
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _inside(SwStroke& stroke, int32_t side, SwFixed lineLength)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
auto theta = mathDiff(stroke.angleIn, stroke.angleOut) / 2;
|
||||
SwPoint delta;
|
||||
bool intersect = false;
|
||||
|
||||
/* Only intersect borders if between two line_to's and both
|
||||
lines are long enough (line length is zero for curves). */
|
||||
if (border->movable && lineLength > 0) {
|
||||
//compute minimum required length of lines
|
||||
SwFixed minLength = abs(mathMultiply(stroke.width, mathTan(theta)));
|
||||
if (stroke.lineLength >= minLength && lineLength >= minLength) intersect = true;
|
||||
}
|
||||
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
|
||||
if (!intersect) {
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, stroke.angleOut + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
border->movable = false;
|
||||
} else {
|
||||
//compute median angle
|
||||
auto phi = stroke.angleIn + theta;
|
||||
auto thcos = mathCos(theta);
|
||||
delta = {static_cast<SwCoord>(mathDivide(stroke.width, thcos)), 0};
|
||||
mathRotate(delta, phi + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
}
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
|
||||
|
||||
void _processCorner(SwStroke& stroke, SwFixed lineLength)
|
||||
{
|
||||
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
|
||||
//no specific corner processing is required if the turn is 0
|
||||
if (turn == 0) return;
|
||||
|
||||
//when we turn to the right, the inside side is 0
|
||||
int32_t inside = 0;
|
||||
|
||||
//otherwise, the inside is 1
|
||||
if (turn < 0) inside = 1;
|
||||
|
||||
//process the inside
|
||||
_inside(stroke, inside, lineLength);
|
||||
|
||||
//process the outside
|
||||
_outside(stroke, 1 - inside, lineLength);
|
||||
}
|
||||
|
||||
|
||||
void _firstSubPath(SwStroke& stroke, SwFixed startAngle, SwFixed lineLength)
|
||||
{
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, startAngle + SW_ANGLE_PI2);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
auto pt = stroke.center + delta;
|
||||
auto border = stroke.borders;
|
||||
_borderMoveTo(border, pt);
|
||||
|
||||
pt = stroke.center - delta;
|
||||
++border;
|
||||
_borderMoveTo(border, pt);
|
||||
|
||||
/* Save angle, position and line length for last join
|
||||
lineLength is zero for curves */
|
||||
stroke.subPathAngle = startAngle;
|
||||
stroke.firstPt = false;
|
||||
stroke.subPathLineLength = lineLength;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
/* 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.
|
||||
An alternative option is to scale the width of the stroke properly by
|
||||
calculating the mixture of the sx/sy rating on the stroke direction. */
|
||||
delta.x = static_cast<SwCoord>(delta.x / stroke.sx);
|
||||
delta.y = static_cast<SwCoord>(delta.y / stroke.sy);
|
||||
auto lineLength = mathLength(delta);
|
||||
auto angle = mathAtan(delta);
|
||||
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle + SW_ANGLE_PI2);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
//process corner if necessary
|
||||
if (stroke.firstPt) {
|
||||
/* This is the first segment of a subpath. We need to add a point to each border
|
||||
at their respective starting point locations. */
|
||||
_firstSubPath(stroke, angle, lineLength);
|
||||
} else {
|
||||
//process the current corner
|
||||
stroke.angleOut = angle;
|
||||
_processCorner(stroke, lineLength);
|
||||
}
|
||||
|
||||
//now add a line segment to both the inside and outside paths
|
||||
auto border = stroke.borders;
|
||||
auto side = 1;
|
||||
|
||||
while (side >= 0) {
|
||||
auto pt = to + delta;
|
||||
|
||||
//the ends of lineto borders are movable
|
||||
_borderLineTo(border, pt, true);
|
||||
|
||||
delta.x = -delta.x;
|
||||
delta.y = -delta.y;
|
||||
|
||||
--side;
|
||||
++border;
|
||||
}
|
||||
|
||||
stroke.angleIn = angle;
|
||||
stroke.center = to;
|
||||
stroke.lineLength = lineLength;
|
||||
}
|
||||
|
||||
|
||||
static void _cubicTo(SwStroke& stroke, const SwPoint& ctrl1, const SwPoint& ctrl2, const SwPoint& to)
|
||||
{
|
||||
SwPoint bezStack[37]; //TODO: static?
|
||||
auto limit = bezStack + 32;
|
||||
auto arc = bezStack;
|
||||
auto firstArc = true;
|
||||
arc[0] = to;
|
||||
arc[1] = ctrl2;
|
||||
arc[2] = ctrl1;
|
||||
arc[3] = stroke.center;
|
||||
|
||||
while (arc >= bezStack) {
|
||||
SwFixed angleIn, angleOut, angleMid;
|
||||
|
||||
//initialize with current direction
|
||||
angleIn = angleOut = angleMid = stroke.angleIn;
|
||||
|
||||
if (arc < limit && !mathSmallCubic(arc, angleIn, angleMid, angleOut)) {
|
||||
if (stroke.firstPt) stroke.angleIn = angleIn;
|
||||
mathSplitCubic(arc);
|
||||
arc += 3;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (firstArc) {
|
||||
firstArc = false;
|
||||
//process corner if necessary
|
||||
if (stroke.firstPt) {
|
||||
_firstSubPath(stroke, angleIn, 0);
|
||||
} else {
|
||||
stroke.angleOut = angleIn;
|
||||
_processCorner(stroke, 0);
|
||||
}
|
||||
} else if (abs(mathDiff(stroke.angleIn, angleIn)) > (SW_ANGLE_PI / 8) / 4) {
|
||||
//if the deviation from one arc to the next is too great add a round corner
|
||||
stroke.center = arc[3];
|
||||
stroke.angleOut = angleIn;
|
||||
stroke.join = StrokeJoin::Round;
|
||||
|
||||
_processCorner(stroke, 0);
|
||||
|
||||
//reinstate line join style
|
||||
stroke.join = stroke.joinSaved;
|
||||
}
|
||||
|
||||
//the arc's angle is small enough; we can add it directly to each border
|
||||
auto theta1 = mathDiff(angleIn, angleMid) / 2;
|
||||
auto theta2 = mathDiff(angleMid, angleOut) / 2;
|
||||
auto phi1 = mathMean(angleIn, angleMid);
|
||||
auto phi2 = mathMean(angleMid, angleOut);
|
||||
auto length1 = mathDivide(stroke.width, mathCos(theta1));
|
||||
auto length2 = mathDivide(stroke.width, mathCos(theta2));
|
||||
SwFixed alpha0 = 0;
|
||||
|
||||
//compute direction of original arc
|
||||
if (stroke.handleWideStrokes) {
|
||||
alpha0 = mathAtan(arc[0] - arc[3]);
|
||||
}
|
||||
|
||||
auto border = stroke.borders;
|
||||
int32_t side = 0;
|
||||
|
||||
while (side < 2) {
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
|
||||
//compute control points
|
||||
SwPoint _ctrl1 = {static_cast<SwCoord>(length1), 0};
|
||||
mathRotate(_ctrl1, phi1 + rotate);
|
||||
SCALE(stroke, _ctrl1);
|
||||
_ctrl1 += arc[2];
|
||||
|
||||
SwPoint _ctrl2 = {static_cast<SwCoord>(length2), 0};
|
||||
mathRotate(_ctrl2, phi2 + rotate);
|
||||
SCALE(stroke, _ctrl2);
|
||||
_ctrl2 += arc[1];
|
||||
|
||||
//compute end point
|
||||
SwPoint _end = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(_end, angleOut + rotate);
|
||||
SCALE(stroke, _end);
|
||||
_end += arc[0];
|
||||
|
||||
if (stroke.handleWideStrokes) {
|
||||
/* determine whether the border radius is greater than the radius of
|
||||
curvature of the original arc */
|
||||
auto _start = border->pts[border->ptsCnt - 1];
|
||||
auto alpha1 = mathAtan(_end - _start);
|
||||
|
||||
//is the direction of the border arc opposite to that of the original arc?
|
||||
if (abs(mathDiff(alpha0, alpha1)) > SW_ANGLE_PI / 2) {
|
||||
|
||||
//use the sine rule to find the intersection point
|
||||
auto beta = mathAtan(arc[3] - _start);
|
||||
auto gamma = mathAtan(arc[0] - _end);
|
||||
auto bvec = _end - _start;
|
||||
auto blen = mathLength(bvec);
|
||||
auto sinA = abs(mathSin(alpha1 - gamma));
|
||||
auto sinB = abs(mathSin(beta - gamma));
|
||||
auto alen = mathMulDiv(blen, sinA, sinB);
|
||||
|
||||
SwPoint delta = {static_cast<SwCoord>(alen), 0};
|
||||
mathRotate(delta, beta);
|
||||
delta += _start;
|
||||
|
||||
//circumnavigate the negative sector backwards
|
||||
border->movable = false;
|
||||
_borderLineTo(border, delta, false);
|
||||
_borderLineTo(border, _end, false);
|
||||
_borderCubicTo(border, _ctrl2, _ctrl1, _start);
|
||||
|
||||
//and then move to the endpoint
|
||||
_borderLineTo(border, _end, false);
|
||||
|
||||
++side;
|
||||
++border;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
_borderCubicTo(border, _ctrl1, _ctrl2, _end);
|
||||
++side;
|
||||
++border;
|
||||
}
|
||||
arc -= 3;
|
||||
stroke.angleIn = angleOut;
|
||||
}
|
||||
stroke.center = to;
|
||||
}
|
||||
|
||||
|
||||
static void _addCap(SwStroke& stroke, SwFixed angle, int32_t side)
|
||||
{
|
||||
if (stroke.cap == StrokeCap::Square) {
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
SwPoint delta2 = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta2, angle + rotate);
|
||||
SCALE(stroke, delta2);
|
||||
delta += stroke.center + delta2;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle);
|
||||
SCALE(stroke, delta);
|
||||
|
||||
delta2 = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta2, angle - rotate);
|
||||
SCALE(stroke, delta2);
|
||||
delta += delta2 + stroke.center;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
} else if (stroke.cap == StrokeCap::Round) {
|
||||
|
||||
stroke.angleIn = angle;
|
||||
stroke.angleOut = angle + SW_ANGLE_PI;
|
||||
_arcTo(stroke, side);
|
||||
return;
|
||||
|
||||
} else { //Butt
|
||||
auto rotate = SIDE_TO_ROTATE(side);
|
||||
auto border = stroke.borders + side;
|
||||
|
||||
SwPoint delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle + rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
|
||||
delta = {static_cast<SwCoord>(stroke.width), 0};
|
||||
mathRotate(delta, angle - rotate);
|
||||
SCALE(stroke, delta);
|
||||
delta += stroke.center;
|
||||
|
||||
_borderLineTo(border, delta, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _addReverseLeft(SwStroke& stroke, bool opened)
|
||||
{
|
||||
auto right = stroke.borders + 0;
|
||||
auto left = stroke.borders + 1;
|
||||
auto newPts = left->ptsCnt - left->start;
|
||||
|
||||
if (newPts <= 0) return;
|
||||
|
||||
_growBorder(right, newPts);
|
||||
|
||||
auto dstPt = right->pts + right->ptsCnt;
|
||||
auto dstTag = right->tags + right->ptsCnt;
|
||||
auto srcPt = left->pts + left->ptsCnt - 1;
|
||||
auto srcTag = left->tags + left->ptsCnt - 1;
|
||||
|
||||
while (srcPt >= left->pts + left->start) {
|
||||
*dstPt = *srcPt;
|
||||
*dstTag = *srcTag;
|
||||
|
||||
if (opened) {
|
||||
dstTag[0] &= ~(SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
|
||||
} else {
|
||||
//switch begin/end tags if necessary
|
||||
auto ttag = dstTag[0] & (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
|
||||
if (ttag == SW_STROKE_TAG_BEGIN || ttag == SW_STROKE_TAG_END)
|
||||
dstTag[0] ^= (SW_STROKE_TAG_BEGIN | SW_STROKE_TAG_END);
|
||||
}
|
||||
--srcPt;
|
||||
--srcTag;
|
||||
++dstPt;
|
||||
++dstTag;
|
||||
}
|
||||
|
||||
left->ptsCnt = left->start;
|
||||
right->ptsCnt += newPts;
|
||||
right->movable = false;
|
||||
left->movable = false;
|
||||
}
|
||||
|
||||
|
||||
static void _beginSubPath(SwStroke& stroke, const SwPoint& to, bool closed)
|
||||
{
|
||||
/* We cannot process the first point because there is not enough
|
||||
information regarding its corner/cap. Later, it will be processed
|
||||
in the _endSubPath() */
|
||||
|
||||
stroke.firstPt = true;
|
||||
stroke.center = to;
|
||||
stroke.closedSubPath = 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
|
||||
created with wide strokes. */
|
||||
if ((stroke.join != StrokeJoin::Round) || (!stroke.closedSubPath && stroke.cap == StrokeCap::Butt))
|
||||
stroke.handleWideStrokes = true;
|
||||
else
|
||||
stroke.handleWideStrokes = false;
|
||||
|
||||
stroke.ptStartSubPath = to;
|
||||
stroke.angleIn = 0;
|
||||
}
|
||||
|
||||
|
||||
static void _endSubPath(SwStroke& stroke)
|
||||
{
|
||||
if (stroke.closedSubPath) {
|
||||
//close the path if needed
|
||||
if (stroke.center != stroke.ptStartSubPath)
|
||||
_lineTo(stroke, stroke.ptStartSubPath);
|
||||
|
||||
//process the corner
|
||||
stroke.angleOut = stroke.subPathAngle;
|
||||
auto turn = mathDiff(stroke.angleIn, stroke.angleOut);
|
||||
|
||||
//No specific corner processing is required if the turn is 0
|
||||
if (turn != 0) {
|
||||
//when we turn to the right, the inside is 0
|
||||
int32_t inside = 0;
|
||||
|
||||
//otherwise, the inside is 1
|
||||
if (turn < 0) inside = 1;
|
||||
|
||||
_inside(stroke, inside, stroke.subPathLineLength); //inside
|
||||
_outside(stroke, 1 - inside, stroke.subPathLineLength); //outside
|
||||
}
|
||||
|
||||
_borderClose(stroke.borders + 0, false);
|
||||
_borderClose(stroke.borders + 1, true);
|
||||
} else {
|
||||
auto right = stroke.borders;
|
||||
|
||||
/* all right, this is an opened path, we need to add a cap between
|
||||
right & left, add the reverse of left, then add a final cap
|
||||
between left & right */
|
||||
_addCap(stroke, stroke.angleIn, 0);
|
||||
|
||||
//add reversed points from 'left' to 'right'
|
||||
_addReverseLeft(stroke, true);
|
||||
|
||||
//now add the final cap
|
||||
stroke.center = stroke.ptStartSubPath;
|
||||
_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 */
|
||||
_borderClose(right, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _getCounts(SwStrokeBorder* border, uint32_t& ptsCnt, uint32_t& cntrsCnt)
|
||||
{
|
||||
auto count = border->ptsCnt;
|
||||
auto tags = border->tags;
|
||||
uint32_t _ptsCnt = 0;
|
||||
uint32_t _cntrsCnt = 0;
|
||||
bool inCntr = false;
|
||||
|
||||
while (count > 0) {
|
||||
if (tags[0] & SW_STROKE_TAG_BEGIN) {
|
||||
if (inCntr) goto fail;
|
||||
inCntr = true;
|
||||
} else if (!inCntr) goto fail;
|
||||
|
||||
if (tags[0] & SW_STROKE_TAG_END) {
|
||||
inCntr = false;
|
||||
++_cntrsCnt;
|
||||
}
|
||||
--count;
|
||||
++_ptsCnt;
|
||||
++tags;
|
||||
}
|
||||
|
||||
if (inCntr) goto fail;
|
||||
|
||||
ptsCnt = _ptsCnt;
|
||||
cntrsCnt = _cntrsCnt;
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
ptsCnt = 0;
|
||||
cntrsCnt = 0;
|
||||
}
|
||||
|
||||
|
||||
static void _exportBorderOutline(const SwStroke& stroke, SwOutline* outline, uint32_t side)
|
||||
{
|
||||
auto border = stroke.borders + side;
|
||||
if (border->ptsCnt == 0) return;
|
||||
|
||||
memcpy(outline->pts.data + outline->pts.count, border->pts, border->ptsCnt * sizeof(SwPoint));
|
||||
|
||||
auto cnt = border->ptsCnt;
|
||||
auto src = border->tags;
|
||||
auto tags = outline->types.data + outline->types.count;
|
||||
auto idx = outline->pts.count;
|
||||
|
||||
while (cnt > 0) {
|
||||
if (*src & SW_STROKE_TAG_POINT) *tags = SW_CURVE_TYPE_POINT;
|
||||
else if (*src & SW_STROKE_TAG_CUBIC) *tags = SW_CURVE_TYPE_CUBIC;
|
||||
else TVGERR("SW_ENGINE", "Invalid stroke tag was given! = %d", *src);
|
||||
if (*src & SW_STROKE_TAG_END) outline->cntrs.push(idx);
|
||||
++src;
|
||||
++tags;
|
||||
++idx;
|
||||
--cnt;
|
||||
}
|
||||
outline->pts.count += border->ptsCnt;
|
||||
outline->types.count += border->ptsCnt;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void strokeFree(SwStroke* stroke)
|
||||
{
|
||||
if (!stroke) return;
|
||||
|
||||
//free borders
|
||||
if (stroke->borders[0].pts) free(stroke->borders[0].pts);
|
||||
if (stroke->borders[0].tags) free(stroke->borders[0].tags);
|
||||
if (stroke->borders[1].pts) free(stroke->borders[1].pts);
|
||||
if (stroke->borders[1].tags) free(stroke->borders[1].tags);
|
||||
|
||||
fillFree(stroke->fill);
|
||||
stroke->fill = nullptr;
|
||||
|
||||
free(stroke);
|
||||
}
|
||||
|
||||
|
||||
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->width = HALF_STROKE(rshape->strokeWidth());
|
||||
stroke->cap = rshape->strokeCap();
|
||||
stroke->miterlimit = static_cast<SwFixed>(rshape->strokeMiterlimit() * 65536.0f);
|
||||
|
||||
//Save line join: it can be temporarily changed when stroking curves...
|
||||
stroke->joinSaved = stroke->join = rshape->strokeJoin();
|
||||
|
||||
stroke->borders[0].ptsCnt = 0;
|
||||
stroke->borders[0].start = -1;
|
||||
stroke->borders[1].ptsCnt = 0;
|
||||
stroke->borders[1].start = -1;
|
||||
}
|
||||
|
||||
|
||||
bool strokeParseOutline(SwStroke* stroke, const SwOutline& outline)
|
||||
{
|
||||
uint32_t first = 0;
|
||||
uint32_t i = 0;
|
||||
|
||||
for (auto cntr = outline.cntrs.begin(); cntr < outline.cntrs.end(); ++cntr, ++i) {
|
||||
auto last = *cntr; //index of last point in contour
|
||||
auto limit = outline.pts.data + last;
|
||||
|
||||
//Skip empty points
|
||||
if (last <= first) {
|
||||
first = last + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto start = outline.pts[first];
|
||||
auto pt = outline.pts.data + first;
|
||||
auto types = outline.types.data + first;
|
||||
auto type = types[0];
|
||||
|
||||
//A contour cannot start with a cubic control point
|
||||
if (type == SW_CURVE_TYPE_CUBIC) return false;
|
||||
|
||||
auto closed = outline.closed.data ? outline.closed.data[i]: false;
|
||||
|
||||
_beginSubPath(*stroke, start, closed);
|
||||
|
||||
while (pt < limit) {
|
||||
++pt;
|
||||
++types;
|
||||
|
||||
//emit a signel line_to
|
||||
if (types[0] == SW_CURVE_TYPE_POINT) {
|
||||
_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;
|
||||
}
|
||||
}
|
||||
close:
|
||||
if (!stroke->firstPt) _endSubPath(*stroke);
|
||||
first = last + 1;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid)
|
||||
{
|
||||
uint32_t count1, count2, count3, count4;
|
||||
|
||||
_getCounts(stroke->borders + 0, count1, count2);
|
||||
_getCounts(stroke->borders + 1, count3, count4);
|
||||
|
||||
auto ptsCnt = count1 + count3;
|
||||
auto cntrsCnt = count2 + count4;
|
||||
|
||||
auto outline = mpoolReqStrokeOutline(mpool, tid);
|
||||
outline->pts.reserve(ptsCnt);
|
||||
outline->types.reserve(ptsCnt);
|
||||
outline->cntrs.reserve(cntrsCnt);
|
||||
|
||||
_exportBorderOutline(*stroke, outline, 0); //left
|
||||
_exportBorderOutline(*stroke, outline, 1); //right
|
||||
|
||||
return outline;
|
||||
}
|
||||
85
engine/thirdparty/thorvg/src/renderer/tvgAccessor.cpp
vendored
Normal file
85
engine/thirdparty/thorvg/src/renderer/tvgAccessor.cpp
vendored
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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 "tvgIteratorAccessor.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static bool accessChildren(Iterator* it, function<bool(const Paint* paint)> func)
|
||||
{
|
||||
while (auto child = it->next()) {
|
||||
//Access the child
|
||||
if (!func(child)) return false;
|
||||
|
||||
//Access the children of the child
|
||||
if (auto it2 = IteratorAccessor::iterator(child)) {
|
||||
if (!accessChildren(it2, func)) {
|
||||
delete(it2);
|
||||
return false;
|
||||
}
|
||||
delete(it2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
unique_ptr<Picture> Accessor::set(unique_ptr<Picture> picture, function<bool(const Paint* paint)> func) noexcept
|
||||
{
|
||||
auto p = picture.get();
|
||||
if (!p || !func) return picture;
|
||||
|
||||
//Use the Preorder Tree-Search
|
||||
|
||||
//Root
|
||||
if (!func(p)) return picture;
|
||||
|
||||
//Children
|
||||
if (auto it = IteratorAccessor::iterator(p)) {
|
||||
accessChildren(it, func);
|
||||
delete(it);
|
||||
}
|
||||
return picture;
|
||||
}
|
||||
|
||||
|
||||
Accessor::~Accessor()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
Accessor::Accessor() : pImpl(nullptr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Accessor> Accessor::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Accessor>(new Accessor);
|
||||
}
|
||||
126
engine/thirdparty/thorvg/src/renderer/tvgAnimation.cpp
vendored
Normal file
126
engine/thirdparty/thorvg/src/renderer/tvgAnimation.cpp
vendored
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (c) 2023 - 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 "tvgFrameModule.h"
|
||||
#include "tvgAnimation.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Animation::~Animation()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Animation::Animation() : pImpl(new Impl)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Result Animation::frame(float no) noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
|
||||
if (static_cast<FrameModule*>(loader)->frame(no)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Picture* Animation::picture() const noexcept
|
||||
{
|
||||
return pImpl->picture;
|
||||
}
|
||||
|
||||
|
||||
float Animation::curFrame() const noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
|
||||
if (!loader) return 0;
|
||||
if (!loader->animatable()) return 0;
|
||||
|
||||
return static_cast<FrameModule*>(loader)->curFrame();
|
||||
}
|
||||
|
||||
|
||||
float Animation::totalFrame() const noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
|
||||
if (!loader) return 0;
|
||||
if (!loader->animatable()) return 0;
|
||||
|
||||
return static_cast<FrameModule*>(loader)->totalFrame();
|
||||
}
|
||||
|
||||
|
||||
float Animation::duration() const noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
|
||||
if (!loader) return 0;
|
||||
if (!loader->animatable()) return 0;
|
||||
|
||||
return static_cast<FrameModule*>(loader)->duration();
|
||||
}
|
||||
|
||||
|
||||
Result Animation::segment(float begin, float end) noexcept
|
||||
{
|
||||
if (begin < 0.0 || end > 1.0 || begin >= end) return Result::InvalidArguments;
|
||||
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
|
||||
static_cast<FrameModule*>(loader)->segment(begin, end);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Animation::segment(float *begin, float *end) noexcept
|
||||
{
|
||||
auto loader = pImpl->picture->pImpl->loader;
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
if (!loader->animatable()) return Result::NonSupport;
|
||||
if (!begin && !end) return Result::InvalidArguments;
|
||||
|
||||
static_cast<FrameModule*>(loader)->segment(begin, end);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Animation> Animation::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Animation>(new Animation);
|
||||
}
|
||||
48
engine/thirdparty/thorvg/src/renderer/tvgAnimation.h
vendored
Normal file
48
engine/thirdparty/thorvg/src/renderer/tvgAnimation.h
vendored
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_ANIMATION_H_
|
||||
#define _TVG_ANIMATION_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgPicture.h"
|
||||
|
||||
struct Animation::Impl
|
||||
{
|
||||
Picture* picture = nullptr;
|
||||
|
||||
Impl()
|
||||
{
|
||||
picture = Picture::gen().release();
|
||||
PP(picture)->ref();
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (PP(picture)->unref() == 0) {
|
||||
delete(picture);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_ANIMATION_H_
|
||||
100
engine/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h
vendored
Normal file
100
engine/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h
vendored
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_BINARY_DESC_H_
|
||||
#define _TVG_BINARY_DESC_H_
|
||||
|
||||
/* TODO: Need to consider whether uin8_t is enough size for extension...
|
||||
Rather than optimal data, we can use enough size and data compress? */
|
||||
|
||||
using TvgBinByte = uint8_t;
|
||||
using TvgBinCounter = uint32_t;
|
||||
using TvgBinTag = TvgBinByte;
|
||||
using TvgBinFlag = TvgBinByte;
|
||||
|
||||
|
||||
//Header
|
||||
#define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE
|
||||
#define TVG_HEADER_SIGNATURE "ThorVG"
|
||||
#define TVG_HEADER_SIGNATURE_LENGTH 6
|
||||
#define TVG_HEADER_VERSION "001200" //Major 00, Minor 12, Micro 00
|
||||
#define TVG_HEADER_VERSION_LENGTH 6
|
||||
#define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions
|
||||
#define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS
|
||||
//Compress Size
|
||||
#define TVG_HEADER_UNCOMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
|
||||
#define TVG_HEADER_COMPRESSED_SIZE 4 //SIZE (TvgBinCounter)
|
||||
#define TVG_HEADER_COMPRESSED_SIZE_BITS 4 //SIZE (TvgBinCounter)
|
||||
//Reserved Flag
|
||||
#define TVG_HEAD_FLAG_COMPRESSED 0x01
|
||||
|
||||
//Paint Type
|
||||
#define TVG_TAG_CLASS_PICTURE (TvgBinTag)0xfc
|
||||
#define TVG_TAG_CLASS_SHAPE (TvgBinTag)0xfd
|
||||
#define TVG_TAG_CLASS_SCENE (TvgBinTag)0xfe
|
||||
|
||||
|
||||
//Paint
|
||||
#define TVG_TAG_PAINT_OPACITY (TvgBinTag)0x10
|
||||
#define TVG_TAG_PAINT_TRANSFORM (TvgBinTag)0x11
|
||||
#define TVG_TAG_PAINT_CMP_TARGET (TvgBinTag)0x01
|
||||
#define TVG_TAG_PAINT_CMP_METHOD (TvgBinTag)0x20
|
||||
|
||||
|
||||
//TODO: Keep this for the compatibility, Remove in TVG 1.0 release
|
||||
//Scene
|
||||
#define TVG_TAG_SCENE_RESERVEDCNT (TvgBinTag)0x30
|
||||
|
||||
|
||||
//Shape
|
||||
#define TVG_TAG_SHAPE_PATH (TvgBinTag)0x40
|
||||
#define TVG_TAG_SHAPE_STROKE (TvgBinTag)0x41
|
||||
#define TVG_TAG_SHAPE_FILL (TvgBinTag)0x42
|
||||
#define TVG_TAG_SHAPE_COLOR (TvgBinTag)0x43
|
||||
#define TVG_TAG_SHAPE_FILLRULE (TvgBinTag)0x44
|
||||
|
||||
|
||||
//Stroke
|
||||
#define TVG_TAG_SHAPE_STROKE_CAP (TvgBinTag)0x50
|
||||
#define TVG_TAG_SHAPE_STROKE_JOIN (TvgBinTag)0x51
|
||||
#define TVG_TAG_SHAPE_STROKE_WIDTH (TvgBinTag)0x52
|
||||
#define TVG_TAG_SHAPE_STROKE_COLOR (TvgBinTag)0x53
|
||||
#define TVG_TAG_SHAPE_STROKE_FILL (TvgBinTag)0x54
|
||||
#define TVG_TAG_SHAPE_STROKE_DASHPTRN (TvgBinTag)0x55
|
||||
#define TVG_TAG_SHAPE_STROKE_MITERLIMIT (TvgBinTag)0x56
|
||||
#define TVG_TAG_SHAPE_STROKE_ORDER (TvgBinTag)0x57
|
||||
#define TVG_TAG_SHAPE_STROKE_DASH_OFFSET (TvgBinTag)0x58
|
||||
|
||||
|
||||
//Fill
|
||||
#define TVG_TAG_FILL_LINEAR_GRADIENT (TvgBinTag)0x60
|
||||
#define TVG_TAG_FILL_RADIAL_GRADIENT (TvgBinTag)0x61
|
||||
#define TVG_TAG_FILL_COLORSTOPS (TvgBinTag)0x62
|
||||
#define TVG_TAG_FILL_FILLSPREAD (TvgBinTag)0x63
|
||||
#define TVG_TAG_FILL_TRANSFORM (TvgBinTag)0x64
|
||||
#define TVG_TAG_FILL_RADIAL_GRADIENT_FOCAL (TvgBinTag)0x65
|
||||
|
||||
//Picture
|
||||
#define TVG_TAG_PICTURE_RAW_IMAGE (TvgBinTag)0x70
|
||||
#define TVG_TAG_PICTURE_MESH (TvgBinTag)0x71
|
||||
|
||||
#endif //_TVG_BINARY_DESC_H_
|
||||
93
engine/thirdparty/thorvg/src/renderer/tvgCanvas.cpp
vendored
Normal file
93
engine/thirdparty/thorvg/src/renderer/tvgCanvas.cpp
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgCanvas.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Canvas::Canvas(RenderMethod *pRenderer):pImpl(new Impl(pRenderer))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::reserve(TVG_UNUSED uint32_t n) noexcept
|
||||
{
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
list<Paint*>& Canvas::paints() noexcept
|
||||
{
|
||||
return pImpl->paints;
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::push(unique_ptr<Paint> paint) noexcept
|
||||
{
|
||||
return pImpl->push(std::move(paint));
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::clear(bool free) noexcept
|
||||
{
|
||||
return pImpl->clear(free);
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::draw() noexcept
|
||||
{
|
||||
TVGLOG("RENDERER", "Draw S. -------------------------------- Canvas(%p)", this);
|
||||
auto ret = pImpl->draw();
|
||||
TVGLOG("RENDERER", "Draw E. -------------------------------- Canvas(%p)", this);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::update(Paint* paint) noexcept
|
||||
{
|
||||
TVGLOG("RENDERER", "Update S. ------------------------------ Canvas(%p)", this);
|
||||
auto ret = pImpl->update(paint, false);
|
||||
TVGLOG("RENDERER", "Update E. ------------------------------ Canvas(%p)", this);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::viewport(int32_t x, int32_t y, int32_t w, int32_t h) noexcept
|
||||
{
|
||||
return pImpl->viewport(x, y, w, h);
|
||||
}
|
||||
|
||||
|
||||
Result Canvas::sync() noexcept
|
||||
{
|
||||
return pImpl->sync();
|
||||
}
|
||||
153
engine/thirdparty/thorvg/src/renderer/tvgCanvas.h
vendored
Normal file
153
engine/thirdparty/thorvg/src/renderer/tvgCanvas.h
vendored
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_CANVAS_H_
|
||||
#define _TVG_CANVAS_H_
|
||||
|
||||
#include "tvgPaint.h"
|
||||
|
||||
|
||||
enum Status : uint8_t {Synced = 0, Updating, Drawing, Damanged};
|
||||
|
||||
struct Canvas::Impl
|
||||
{
|
||||
list<Paint*> paints;
|
||||
RenderMethod* renderer;
|
||||
RenderRegion vport = {0, 0, INT32_MAX, INT32_MAX};
|
||||
Status status = Status::Synced;
|
||||
|
||||
Impl(RenderMethod* pRenderer) : renderer(pRenderer)
|
||||
{
|
||||
renderer->ref();
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
//make it sure any deffered jobs
|
||||
renderer->sync();
|
||||
renderer->clear();
|
||||
|
||||
clearPaints();
|
||||
|
||||
if (renderer->unref() == 0) delete(renderer);
|
||||
}
|
||||
|
||||
void clearPaints()
|
||||
{
|
||||
for (auto paint : paints) {
|
||||
if (P(paint)->unref() == 0) delete(paint);
|
||||
}
|
||||
paints.clear();
|
||||
}
|
||||
|
||||
Result push(unique_ptr<Paint> paint)
|
||||
{
|
||||
//You can not push paints during rendering.
|
||||
if (status == Status::Drawing) return Result::InsufficientCondition;
|
||||
|
||||
auto p = paint.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
PP(p)->ref();
|
||||
paints.push_back(p);
|
||||
|
||||
return update(p, true);
|
||||
}
|
||||
|
||||
Result clear(bool free)
|
||||
{
|
||||
//Clear render target before drawing
|
||||
if (!renderer->clear()) return Result::InsufficientCondition;
|
||||
|
||||
//Free paints
|
||||
if (free) clearPaints();
|
||||
|
||||
status = Status::Synced;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result update(Paint* paint, bool force)
|
||||
{
|
||||
if (paints.empty() || status == Status::Drawing) return Result::InsufficientCondition;
|
||||
|
||||
Array<RenderData> clips;
|
||||
auto flag = RenderUpdateFlag::None;
|
||||
if (status == Status::Damanged || force) flag = RenderUpdateFlag::All;
|
||||
|
||||
if (paint) {
|
||||
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
|
||||
} else {
|
||||
for (auto paint : paints) {
|
||||
paint->pImpl->update(renderer, nullptr, clips, 255, flag);
|
||||
}
|
||||
}
|
||||
status = Status::Updating;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result draw()
|
||||
{
|
||||
if (status == Status::Damanged) update(nullptr, false);
|
||||
if (status == Status::Drawing || paints.empty() || !renderer->preRender()) return Result::InsufficientCondition;
|
||||
|
||||
bool rendered = false;
|
||||
for (auto paint : paints) {
|
||||
if (paint->pImpl->render(renderer)) rendered = true;
|
||||
}
|
||||
|
||||
if (!rendered || !renderer->postRender()) return Result::InsufficientCondition;
|
||||
|
||||
status = Status::Drawing;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result sync()
|
||||
{
|
||||
if (status == Status::Synced || status == Status::Damanged) return Result::InsufficientCondition;
|
||||
|
||||
if (renderer->sync()) {
|
||||
status = Status::Synced;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
return Result::Unknown;
|
||||
}
|
||||
|
||||
Result viewport(int32_t x, int32_t y, int32_t w, int32_t h)
|
||||
{
|
||||
if (status != Status::Damanged && status != Status::Synced) return Result::InsufficientCondition;
|
||||
|
||||
RenderRegion val = {x, y, w, h};
|
||||
//intersect if the target buffer is already set.
|
||||
auto surface = renderer->mainSurface();
|
||||
if (surface && surface->w > 0 && surface->h > 0) {
|
||||
val.intersect({0, 0, (int32_t)surface->w, (int32_t)surface->h});
|
||||
}
|
||||
if (vport == val) return Result::Success;
|
||||
renderer->viewport(val);
|
||||
vport = val;
|
||||
status = Status::Damanged;
|
||||
return Result::Success;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _TVG_CANVAS_H_ */
|
||||
102
engine/thirdparty/thorvg/src/renderer/tvgCommon.h
vendored
Normal file
102
engine/thirdparty/thorvg/src/renderer/tvgCommon.h
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_COMMON_H_
|
||||
#define _TVG_COMMON_H_
|
||||
|
||||
#include "config.h"
|
||||
#include "thorvg.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace tvg;
|
||||
|
||||
//for MSVC Compat
|
||||
#ifdef _MSC_VER
|
||||
#define TVG_UNUSED
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
#else
|
||||
#define TVG_UNUSED __attribute__ ((__unused__))
|
||||
#endif
|
||||
|
||||
// Portable 'fallthrough' attribute
|
||||
#if __has_cpp_attribute(fallthrough)
|
||||
#ifdef _MSC_VER
|
||||
#define TVG_FALLTHROUGH [[fallthrough]];
|
||||
#else
|
||||
#define TVG_FALLTHROUGH __attribute__ ((fallthrough));
|
||||
#endif
|
||||
#else
|
||||
#define TVG_FALLTHROUGH
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && defined(__clang__)
|
||||
#define strncpy strncpy_s
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
|
||||
//TVG class identifier values
|
||||
#define TVG_CLASS_ID_UNDEFINED 0
|
||||
#define TVG_CLASS_ID_SHAPE 1
|
||||
#define TVG_CLASS_ID_SCENE 2
|
||||
#define TVG_CLASS_ID_PICTURE 3
|
||||
#define TVG_CLASS_ID_LINEAR 4
|
||||
#define TVG_CLASS_ID_RADIAL 5
|
||||
#define TVG_CLASS_ID_TEXT 6
|
||||
|
||||
enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown };
|
||||
|
||||
using Size = Point;
|
||||
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
constexpr auto ErrorColor = "\033[31m"; //red
|
||||
constexpr auto ErrorBgColor = "\033[41m";//bg red
|
||||
constexpr auto LogColor = "\033[32m"; //green
|
||||
constexpr auto LogBgColor = "\033[42m"; //bg green
|
||||
constexpr auto GreyColor = "\033[90m"; //grey
|
||||
constexpr auto ResetColors = "\033[0m"; //default
|
||||
#define TVGERR(tag, fmt, ...) fprintf(stderr, "%s[E]%s %s" tag "%s (%s %d): %s" fmt "\n", ErrorBgColor, ResetColors, ErrorColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
|
||||
#define TVGLOG(tag, fmt, ...) fprintf(stdout, "%s[L]%s %s" tag "%s (%s %d): %s" fmt "\n", LogBgColor, ResetColors, LogColor, GreyColor, __FILE__, __LINE__, ResetColors, ##__VA_ARGS__)
|
||||
#else
|
||||
#define TVGERR(...) do {} while(0)
|
||||
#define TVGLOG(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
uint16_t THORVG_VERSION_NUMBER();
|
||||
|
||||
|
||||
#define P(A) ((A)->pImpl) //Access to pimpl.
|
||||
#define PP(A) (((Paint*)(A))->pImpl) //Access to pimpl.
|
||||
|
||||
|
||||
//for debugging
|
||||
#if 0
|
||||
#include <sys/time.h>
|
||||
static inline double THORVG_TIMESTAMP()
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
return (tv.tv_sec + tv.tv_usec / 1000000.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //_TVG_COMMON_H_
|
||||
250
engine/thirdparty/thorvg/src/renderer/tvgFill.cpp
vendored
Normal file
250
engine/thirdparty/thorvg/src/renderer/tvgFill.cpp
vendored
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgFill.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Fill* RadialGradient::Impl::duplicate()
|
||||
{
|
||||
auto ret = RadialGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->cx = cx;
|
||||
ret->pImpl->cy = cy;
|
||||
ret->pImpl->r = r;
|
||||
ret->pImpl->fx = fx;
|
||||
ret->pImpl->fy = fy;
|
||||
ret->pImpl->fr = fr;
|
||||
|
||||
return ret.release();
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::Impl::radial(float cx, float cy, float r, float fx, float fy, float fr)
|
||||
{
|
||||
if (r < 0 || fr < 0) return Result::InvalidArguments;
|
||||
|
||||
this->cx = cx;
|
||||
this->cy = cy;
|
||||
this->r = r;
|
||||
this->fx = fx;
|
||||
this->fy = fy;
|
||||
this->fr = fr;
|
||||
|
||||
return Result::Success;
|
||||
};
|
||||
|
||||
|
||||
Fill* LinearGradient::Impl::duplicate()
|
||||
{
|
||||
auto ret = LinearGradient::gen();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->x1 = x1;
|
||||
ret->pImpl->y1 = y1;
|
||||
ret->pImpl->x2 = x2;
|
||||
ret->pImpl->y2 = y2;
|
||||
|
||||
return ret.release();
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Fill::Fill():pImpl(new Impl())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Fill::~Fill()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
|
||||
{
|
||||
if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments;
|
||||
|
||||
if (cnt == 0) {
|
||||
if (pImpl->colorStops) {
|
||||
free(pImpl->colorStops);
|
||||
pImpl->colorStops = nullptr;
|
||||
pImpl->cnt = 0;
|
||||
}
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
if (pImpl->cnt != cnt) {
|
||||
pImpl->colorStops = static_cast<ColorStop*>(realloc(pImpl->colorStops, cnt * sizeof(ColorStop)));
|
||||
}
|
||||
|
||||
pImpl->cnt = cnt;
|
||||
memcpy(pImpl->colorStops, colorStops, cnt * sizeof(ColorStop));
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
|
||||
{
|
||||
if (colorStops) *colorStops = pImpl->colorStops;
|
||||
|
||||
return pImpl->cnt;
|
||||
}
|
||||
|
||||
|
||||
Result Fill::spread(FillSpread s) noexcept
|
||||
{
|
||||
pImpl->spread = s;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
FillSpread Fill::spread() const noexcept
|
||||
{
|
||||
return pImpl->spread;
|
||||
}
|
||||
|
||||
|
||||
Result Fill::transform(const Matrix& m) noexcept
|
||||
{
|
||||
if (!pImpl->transform) {
|
||||
pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
|
||||
}
|
||||
*pImpl->transform = m;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Matrix Fill::transform() const noexcept
|
||||
{
|
||||
if (pImpl->transform) return *pImpl->transform;
|
||||
return {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
}
|
||||
|
||||
|
||||
Fill* Fill::duplicate() const noexcept
|
||||
{
|
||||
return pImpl->duplicate();
|
||||
}
|
||||
|
||||
|
||||
uint32_t Fill::identifier() const noexcept
|
||||
{
|
||||
return pImpl->id;
|
||||
}
|
||||
|
||||
|
||||
RadialGradient::RadialGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_RADIAL;
|
||||
Fill::pImpl->method(new FillDup<RadialGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
RadialGradient::~RadialGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float cx, float cy, float r) noexcept
|
||||
{
|
||||
return pImpl->radial(cx, cy, r, cx, cy, 0.0f);
|
||||
}
|
||||
|
||||
|
||||
Result RadialGradient::radial(float* cx, float* cy, float* r) const noexcept
|
||||
{
|
||||
if (cx) *cx = pImpl->cx;
|
||||
if (cy) *cy = pImpl->cy;
|
||||
if (r) *r = pImpl->r;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<RadialGradient> RadialGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<RadialGradient>(new RadialGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t RadialGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_RADIAL;
|
||||
}
|
||||
|
||||
|
||||
LinearGradient::LinearGradient():pImpl(new Impl())
|
||||
{
|
||||
Fill::pImpl->id = TVG_CLASS_ID_LINEAR;
|
||||
Fill::pImpl->method(new FillDup<LinearGradient::Impl>(pImpl));
|
||||
}
|
||||
|
||||
|
||||
LinearGradient::~LinearGradient()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float x1, float y1, float x2, float y2) noexcept
|
||||
{
|
||||
pImpl->x1 = x1;
|
||||
pImpl->y1 = y1;
|
||||
pImpl->x2 = x2;
|
||||
pImpl->y2 = y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result LinearGradient::linear(float* x1, float* y1, float* x2, float* y2) const noexcept
|
||||
{
|
||||
if (x1) *x1 = pImpl->x1;
|
||||
if (x2) *x2 = pImpl->x2;
|
||||
if (y1) *y1 = pImpl->y1;
|
||||
if (y2) *y2 = pImpl->y2;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<LinearGradient> LinearGradient::gen() noexcept
|
||||
{
|
||||
return unique_ptr<LinearGradient>(new LinearGradient);
|
||||
}
|
||||
|
||||
|
||||
uint32_t LinearGradient::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_LINEAR;
|
||||
}
|
||||
|
||||
112
engine/thirdparty/thorvg/src/renderer/tvgFill.h
vendored
Normal file
112
engine/thirdparty/thorvg/src/renderer/tvgFill.h
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_FILL_H_
|
||||
#define _TVG_FILL_H_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include "tvgCommon.h"
|
||||
|
||||
template<typename T>
|
||||
struct DuplicateMethod
|
||||
{
|
||||
virtual ~DuplicateMethod() {}
|
||||
virtual T* duplicate() = 0;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct FillDup : DuplicateMethod<Fill>
|
||||
{
|
||||
T* inst = nullptr;
|
||||
|
||||
FillDup(T* _inst) : inst(_inst) {}
|
||||
~FillDup() {}
|
||||
|
||||
Fill* duplicate() override
|
||||
{
|
||||
return inst->duplicate();
|
||||
}
|
||||
};
|
||||
|
||||
struct Fill::Impl
|
||||
{
|
||||
ColorStop* colorStops = nullptr;
|
||||
Matrix* transform = nullptr;
|
||||
uint32_t cnt = 0;
|
||||
FillSpread spread;
|
||||
DuplicateMethod<Fill>* dup = nullptr;
|
||||
uint8_t id;
|
||||
|
||||
~Impl()
|
||||
{
|
||||
delete(dup);
|
||||
free(colorStops);
|
||||
free(transform);
|
||||
}
|
||||
|
||||
void method(DuplicateMethod<Fill>* dup)
|
||||
{
|
||||
this->dup = dup;
|
||||
}
|
||||
|
||||
Fill* duplicate()
|
||||
{
|
||||
auto ret = dup->duplicate();
|
||||
if (!ret) return nullptr;
|
||||
|
||||
ret->pImpl->cnt = cnt;
|
||||
ret->pImpl->spread = spread;
|
||||
ret->pImpl->colorStops = static_cast<ColorStop*>(malloc(sizeof(ColorStop) * cnt));
|
||||
memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
|
||||
if (transform) {
|
||||
ret->pImpl->transform = static_cast<Matrix*>(malloc(sizeof(Matrix)));
|
||||
*ret->pImpl->transform = *transform;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct RadialGradient::Impl
|
||||
{
|
||||
float cx = 0.0f, cy = 0.0f;
|
||||
float fx = 0.0f, fy = 0.0f;
|
||||
float r = 0.0f, fr = 0.0f;
|
||||
|
||||
Fill* duplicate();
|
||||
Result radial(float cx, float cy, float r, float fx, float fy, float fr);
|
||||
};
|
||||
|
||||
|
||||
struct LinearGradient::Impl
|
||||
{
|
||||
float x1 = 0.0f;
|
||||
float y1 = 0.0f;
|
||||
float x2 = 0.0f;
|
||||
float y2 = 0.0f;
|
||||
|
||||
Fill* duplicate();
|
||||
};
|
||||
|
||||
|
||||
#endif //_TVG_FILL_H_
|
||||
62
engine/thirdparty/thorvg/src/renderer/tvgFrameModule.h
vendored
Normal file
62
engine/thirdparty/thorvg/src/renderer/tvgFrameModule.h
vendored
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2023 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_FRAME_MODULE_H_
|
||||
#define _TVG_FRAME_MODULE_H_
|
||||
|
||||
#include "tvgLoadModule.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class FrameModule: public ImageLoader
|
||||
{
|
||||
public:
|
||||
float segmentBegin = 0.0f;
|
||||
float segmentEnd = 1.0f;
|
||||
|
||||
FrameModule(FileType type) : ImageLoader(type) {}
|
||||
virtual ~FrameModule() {}
|
||||
|
||||
virtual bool frame(float no) = 0; //set the current frame number
|
||||
virtual float totalFrame() = 0; //return the total frame count
|
||||
virtual float curFrame() = 0; //return the current frame number
|
||||
virtual float duration() = 0; //return the animation duration in seconds
|
||||
|
||||
void segment(float* begin, float* end)
|
||||
{
|
||||
if (begin) *begin = segmentBegin;
|
||||
if (end) *end = segmentEnd;
|
||||
}
|
||||
|
||||
void segment(float begin, float end)
|
||||
{
|
||||
segmentBegin = begin;
|
||||
segmentEnd = end;
|
||||
}
|
||||
|
||||
virtual bool animatable() override { return true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_FRAME_MODULE_H_
|
||||
93
engine/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp
vendored
Normal file
93
engine/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp
vendored
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgCanvas.h"
|
||||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
#include "tvgGlRenderer.h"
|
||||
#else
|
||||
class GlRenderer : public RenderMethod
|
||||
{
|
||||
//Non Supported. Dummy Class */
|
||||
};
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct GlCanvas::Impl
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
GlCanvas::GlCanvas() : Canvas(GlRenderer::gen()), pImpl(nullptr)
|
||||
#else
|
||||
GlCanvas::GlCanvas() : Canvas(nullptr), pImpl(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
GlCanvas::~GlCanvas()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result GlCanvas::target(int32_t id, uint32_t w, uint32_t h) noexcept
|
||||
{
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
//We know renderer type, avoid dynamic_cast for performance.
|
||||
auto renderer = static_cast<GlRenderer*>(Canvas::pImpl->renderer);
|
||||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(id, w, h)) return Result::Unknown;
|
||||
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(Canvas::pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->status = Status::Damanged;
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<GlCanvas> GlCanvas::gen() noexcept
|
||||
{
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
if (GlRenderer::init() <= 0) return nullptr;
|
||||
return unique_ptr<GlCanvas>(new GlCanvas);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
179
engine/thirdparty/thorvg/src/renderer/tvgInitializer.cpp
vendored
Normal file
179
engine/thirdparty/thorvg/src/renderer/tvgInitializer.cpp
vendored
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgCommon.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgLoader.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <cstring>
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
#include "tvgSwRenderer.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
#include "tvgGlRenderer.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
#include "tvgWgRenderer.h"
|
||||
#endif
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static int _initCnt = 0;
|
||||
static uint16_t _version = 0;
|
||||
|
||||
//enum class operation helper
|
||||
static constexpr bool operator &(CanvasEngine a, CanvasEngine b)
|
||||
{
|
||||
return int(a) & int(b);
|
||||
}
|
||||
|
||||
static bool _buildVersionInfo()
|
||||
{
|
||||
auto SRC = THORVG_VERSION_STRING; //ex) 0.3.99
|
||||
auto p = SRC;
|
||||
const char* x;
|
||||
|
||||
char major[3];
|
||||
x = strchr(p, '.');
|
||||
if (!x) return false;
|
||||
memcpy(major, p, x - p);
|
||||
major[x - p] = '\0';
|
||||
p = x + 1;
|
||||
|
||||
char minor[3];
|
||||
x = strchr(p, '.');
|
||||
if (!x) return false;
|
||||
memcpy(minor, p, x - p);
|
||||
minor[x - p] = '\0';
|
||||
p = x + 1;
|
||||
|
||||
char micro[3];
|
||||
x = SRC + strlen(THORVG_VERSION_STRING);
|
||||
memcpy(micro, p, x - p);
|
||||
micro[x - p] = '\0';
|
||||
|
||||
char sum[7];
|
||||
snprintf(sum, sizeof(sum), "%s%s%s", major, minor, micro);
|
||||
|
||||
_version = atoi(sum);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept
|
||||
{
|
||||
auto nonSupport = true;
|
||||
if (static_cast<int>(engine) == 0) return Result::InvalidArguments;
|
||||
|
||||
if (engine & CanvasEngine::Sw) {
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (!SwRenderer::init(threads)) return Result::FailedAllocation;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (engine & CanvasEngine::Gl) {
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
if (!GlRenderer::init(threads)) return Result::FailedAllocation;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (engine & CanvasEngine::Wg) {
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
if (!WgRenderer::init(threads)) return Result::FailedAllocation;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (nonSupport) return Result::NonSupport;
|
||||
|
||||
if (_initCnt++ > 0) return Result::Success;
|
||||
|
||||
if (!_buildVersionInfo()) return Result::Unknown;
|
||||
|
||||
if (!LoaderMgr::init()) return Result::Unknown;
|
||||
|
||||
TaskScheduler::init(threads);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Initializer::term(CanvasEngine engine) noexcept
|
||||
{
|
||||
if (_initCnt == 0) return Result::InsufficientCondition;
|
||||
|
||||
auto nonSupport = true;
|
||||
if (static_cast<int>(engine) == 0) return Result::InvalidArguments;
|
||||
|
||||
if (engine & CanvasEngine::Sw) {
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (!SwRenderer::term()) return Result::InsufficientCondition;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (engine & CanvasEngine::Gl) {
|
||||
#ifdef THORVG_GL_RASTER_SUPPORT
|
||||
if (!GlRenderer::term()) return Result::InsufficientCondition;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (engine & CanvasEngine::Wg) {
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
if (!WgRenderer::term()) return Result::InsufficientCondition;
|
||||
nonSupport = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (nonSupport) return Result::NonSupport;
|
||||
|
||||
if (--_initCnt > 0) return Result::Success;
|
||||
|
||||
TaskScheduler::term();
|
||||
|
||||
if (!LoaderMgr::term()) return Result::Unknown;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint16_t THORVG_VERSION_NUMBER()
|
||||
{
|
||||
return _version;
|
||||
}
|
||||
|
||||
43
engine/thirdparty/thorvg/src/renderer/tvgIteratorAccessor.h
vendored
Normal file
43
engine/thirdparty/thorvg/src/renderer/tvgIteratorAccessor.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_ITERATOR_ACCESSOR_H_
|
||||
#define _TVG_ITERATOR_ACCESSOR_H_
|
||||
|
||||
#include "tvgPaint.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class IteratorAccessor
|
||||
{
|
||||
public:
|
||||
//Utility Method: Iterator Accessor
|
||||
static Iterator* iterator(const Paint* paint)
|
||||
{
|
||||
return paint->pImpl->iterator();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_ITERATOR_ACCESSOR_H_
|
||||
107
engine/thirdparty/thorvg/src/renderer/tvgLoadModule.h
vendored
Normal file
107
engine/thirdparty/thorvg/src/renderer/tvgLoadModule.h
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_LOAD_MODULE_H_
|
||||
#define _TVG_LOAD_MODULE_H_
|
||||
|
||||
#include "tvgRender.h"
|
||||
#include "tvgInlist.h"
|
||||
|
||||
|
||||
struct LoadModule
|
||||
{
|
||||
INLIST_ITEM(LoadModule);
|
||||
|
||||
//Use either hashkey(data) or hashpath(path)
|
||||
union {
|
||||
uintptr_t hashkey;
|
||||
char* hashpath = nullptr;
|
||||
};
|
||||
|
||||
FileType type; //current loader file type
|
||||
uint16_t sharing = 0; //reference count
|
||||
bool readied = false; //read done already.
|
||||
bool pathcache = false; //cached by path
|
||||
|
||||
LoadModule(FileType type) : type(type) {}
|
||||
virtual ~LoadModule()
|
||||
{
|
||||
if (pathcache) free(hashpath);
|
||||
}
|
||||
|
||||
virtual bool open(const string& path) { return false; }
|
||||
virtual bool open(const char* data, uint32_t size, bool copy) { return false; }
|
||||
virtual bool resize(Paint* paint, float w, float h) { return false; }
|
||||
virtual void sync() {}; //finish immediately if any async update jobs.
|
||||
|
||||
virtual bool read()
|
||||
{
|
||||
if (readied) return false;
|
||||
readied = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cached()
|
||||
{
|
||||
if (hashkey) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool close()
|
||||
{
|
||||
if (sharing == 0) return true;
|
||||
--sharing;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ImageLoader : LoadModule
|
||||
{
|
||||
static ColorSpace cs; //desired value
|
||||
|
||||
float w = 0, h = 0; //default image size
|
||||
Surface surface;
|
||||
|
||||
ImageLoader(FileType type) : LoadModule(type) {}
|
||||
|
||||
virtual bool animatable() { return false; } //true if this loader supports animation.
|
||||
virtual Paint* paint() { return nullptr; }
|
||||
|
||||
virtual Surface* bitmap()
|
||||
{
|
||||
if (surface.data) return &surface;
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct FontLoader : LoadModule
|
||||
{
|
||||
float scale = 1.0f;
|
||||
|
||||
FontLoader(FileType type) : LoadModule(type) {}
|
||||
|
||||
virtual bool request(Shape* shape, char* text, bool italic = false) = 0;
|
||||
};
|
||||
|
||||
#endif //_TVG_LOAD_MODULE_H_
|
||||
459
engine/thirdparty/thorvg/src/renderer/tvgLoader.cpp
vendored
Normal file
459
engine/thirdparty/thorvg/src/renderer/tvgLoader.cpp
vendored
Normal file
|
|
@ -0,0 +1,459 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 <string.h>
|
||||
|
||||
#include "tvgInlist.h"
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgLock.h"
|
||||
|
||||
#ifdef THORVG_SVG_LOADER_SUPPORT
|
||||
#include "tvgSvgLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_PNG_LOADER_SUPPORT
|
||||
#include "tvgPngLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
#include "tvgTvgLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_JPG_LOADER_SUPPORT
|
||||
#include "tvgJpgLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_WEBP_LOADER_SUPPORT
|
||||
#include "tvgWebpLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_TTF_LOADER_SUPPORT
|
||||
#include "tvgTtfLoader.h"
|
||||
#endif
|
||||
|
||||
#ifdef THORVG_LOTTIE_LOADER_SUPPORT
|
||||
#include "tvgLottieLoader.h"
|
||||
#endif
|
||||
|
||||
#include "tvgRawLoader.h"
|
||||
|
||||
|
||||
uintptr_t HASH_KEY(const char* data)
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(data);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
ColorSpace ImageLoader::cs = ColorSpace::ARGB8888;
|
||||
|
||||
static Key key;
|
||||
static Inlist<LoadModule> _activeLoaders;
|
||||
|
||||
|
||||
static LoadModule* _find(FileType type)
|
||||
{
|
||||
switch(type) {
|
||||
case FileType::Png: {
|
||||
#ifdef THORVG_PNG_LOADER_SUPPORT
|
||||
return new PngLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Jpg: {
|
||||
#ifdef THORVG_JPG_LOADER_SUPPORT
|
||||
return new JpgLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Webp: {
|
||||
#ifdef THORVG_WEBP_LOADER_SUPPORT
|
||||
return new WebpLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Tvg: {
|
||||
#ifdef THORVG_TVG_LOADER_SUPPORT
|
||||
return new TvgLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Svg: {
|
||||
#ifdef THORVG_SVG_LOADER_SUPPORT
|
||||
return new SvgLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Ttf: {
|
||||
#ifdef THORVG_TTF_LOADER_SUPPORT
|
||||
return new TtfLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Lottie: {
|
||||
#ifdef THORVG_LOTTIE_LOADER_SUPPORT
|
||||
return new LottieLoader;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Raw: {
|
||||
return new RawLoader;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
const char *format;
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
format = "TVG";
|
||||
break;
|
||||
}
|
||||
case FileType::Svg: {
|
||||
format = "SVG";
|
||||
break;
|
||||
}
|
||||
case FileType::Ttf: {
|
||||
format = "TTF";
|
||||
break;
|
||||
}
|
||||
case FileType::Lottie: {
|
||||
format = "lottie(json)";
|
||||
break;
|
||||
}
|
||||
case FileType::Raw: {
|
||||
format = "RAW";
|
||||
break;
|
||||
}
|
||||
case FileType::Png: {
|
||||
format = "PNG";
|
||||
break;
|
||||
}
|
||||
case FileType::Jpg: {
|
||||
format = "JPG";
|
||||
break;
|
||||
}
|
||||
case FileType::Webp: {
|
||||
format = "WEBP";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
format = "???";
|
||||
break;
|
||||
}
|
||||
}
|
||||
TVGLOG("RENDERER", "%s format is not supported", format);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static LoadModule* _findByPath(const string& path)
|
||||
{
|
||||
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||
if (!ext.compare("tvg")) return _find(FileType::Tvg);
|
||||
if (!ext.compare("svg")) return _find(FileType::Svg);
|
||||
if (!ext.compare("json")) return _find(FileType::Lottie);
|
||||
if (!ext.compare("png")) return _find(FileType::Png);
|
||||
if (!ext.compare("jpg")) return _find(FileType::Jpg);
|
||||
if (!ext.compare("webp")) return _find(FileType::Webp);
|
||||
if (!ext.compare("ttf") || !ext.compare("ttc")) return _find(FileType::Ttf);
|
||||
if (!ext.compare("otf") || !ext.compare("otc")) return _find(FileType::Ttf);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static FileType _convert(const string& mimeType)
|
||||
{
|
||||
auto type = FileType::Unknown;
|
||||
|
||||
if (mimeType == "tvg") type = FileType::Tvg;
|
||||
else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg;
|
||||
else if (mimeType == "ttf" || mimeType == "otf") type = FileType::Ttf;
|
||||
else if (mimeType == "lottie") type = FileType::Lottie;
|
||||
else if (mimeType == "raw") type = FileType::Raw;
|
||||
else if (mimeType == "png") type = FileType::Png;
|
||||
else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg;
|
||||
else if (mimeType == "webp") type = FileType::Webp;
|
||||
else TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str());
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
|
||||
static LoadModule* _findByType(const string& mimeType)
|
||||
{
|
||||
return _find(_convert(mimeType));
|
||||
}
|
||||
|
||||
|
||||
static LoadModule* _findFromCache(const string& path)
|
||||
{
|
||||
ScopedLock lock(key);
|
||||
|
||||
auto loader = _activeLoaders.head;
|
||||
|
||||
while (loader) {
|
||||
if (loader->pathcache && !strcmp(loader->hashpath, path.c_str())) {
|
||||
++loader->sharing;
|
||||
return loader;
|
||||
}
|
||||
loader = loader->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static LoadModule* _findFromCache(const char* data, uint32_t size, const string& mimeType)
|
||||
{
|
||||
auto type = _convert(mimeType);
|
||||
if (type == FileType::Unknown) return nullptr;
|
||||
|
||||
ScopedLock lock(key);
|
||||
auto loader = _activeLoaders.head;
|
||||
|
||||
auto key = HASH_KEY(data);
|
||||
|
||||
while (loader) {
|
||||
if (loader->type == type && loader->hashkey == key) {
|
||||
++loader->sharing;
|
||||
return loader;
|
||||
}
|
||||
loader = loader->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
bool LoaderMgr::init()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LoaderMgr::term()
|
||||
{
|
||||
auto loader = _activeLoaders.head;
|
||||
|
||||
//clean up the remained font loaders which is globally used.
|
||||
while (loader && loader->type == FileType::Ttf) {
|
||||
auto ret = loader->close();
|
||||
auto tmp = loader;
|
||||
loader = loader->next;
|
||||
_activeLoaders.remove(tmp);
|
||||
if (ret) delete(tmp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LoaderMgr::retrieve(LoadModule* loader)
|
||||
{
|
||||
if (!loader) return false;
|
||||
if (loader->close()) {
|
||||
if (loader->cached()) {
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.remove(loader);
|
||||
}
|
||||
delete(loader);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
LoadModule* LoaderMgr::loader(const string& path, bool* invalid)
|
||||
{
|
||||
*invalid = false;
|
||||
|
||||
//TODO: lottie is not sharable.
|
||||
auto allowCache = true;
|
||||
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||
if (!ext.compare("json")) allowCache = false;
|
||||
|
||||
if (allowCache) {
|
||||
if (auto loader = _findFromCache(path)) return loader;
|
||||
}
|
||||
|
||||
if (auto loader = _findByPath(path)) {
|
||||
if (loader->open(path)) {
|
||||
if (allowCache) {
|
||||
loader->hashpath = strdup(path.c_str());
|
||||
loader->pathcache = true;
|
||||
{
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
}
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
delete(loader);
|
||||
}
|
||||
//Unkown MimeType. Try with the candidates in the order
|
||||
for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
|
||||
if (auto loader = _find(static_cast<FileType>(i))) {
|
||||
if (loader->open(path)) {
|
||||
if (allowCache) {
|
||||
loader->hashpath = strdup(path.c_str());
|
||||
loader->pathcache = true;
|
||||
{
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
}
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
delete(loader);
|
||||
}
|
||||
}
|
||||
*invalid = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool LoaderMgr::retrieve(const string& path)
|
||||
{
|
||||
return retrieve(_findFromCache(path));
|
||||
}
|
||||
|
||||
|
||||
LoadModule* LoaderMgr::loader(const char* key)
|
||||
{
|
||||
auto loader = _activeLoaders.head;
|
||||
|
||||
while (loader) {
|
||||
if (loader->pathcache && strstr(loader->hashpath, key)) {
|
||||
++loader->sharing;
|
||||
return loader;
|
||||
}
|
||||
loader = loader->next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy)
|
||||
{
|
||||
//Note that users could use the same data pointer with the different content.
|
||||
//Thus caching is only valid for shareable.
|
||||
auto allowCache = !copy;
|
||||
|
||||
//TODO: lottie is not sharable.
|
||||
if (allowCache) {
|
||||
auto type = _convert(mimeType);
|
||||
if (type == FileType::Lottie) allowCache = false;
|
||||
}
|
||||
|
||||
if (allowCache) {
|
||||
if (auto loader = _findFromCache(data, size, mimeType)) return loader;
|
||||
}
|
||||
|
||||
//Try with the given MimeType
|
||||
if (!mimeType.empty()) {
|
||||
if (auto loader = _findByType(mimeType)) {
|
||||
if (loader->open(data, size, copy)) {
|
||||
if (allowCache) {
|
||||
loader->hashkey = HASH_KEY(data);
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
}
|
||||
return loader;
|
||||
} else {
|
||||
TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported.", mimeType.c_str());
|
||||
delete(loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
//Unkown MimeType. Try with the candidates in the order
|
||||
for (int i = 0; i < static_cast<int>(FileType::Raw); i++) {
|
||||
auto loader = _find(static_cast<FileType>(i));
|
||||
if (loader) {
|
||||
if (loader->open(data, size, copy)) {
|
||||
if (allowCache) {
|
||||
loader->hashkey = HASH_KEY(data);
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
delete(loader);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy)
|
||||
{
|
||||
//Note that users could use the same data pointer with the different content.
|
||||
//Thus caching is only valid for shareable.
|
||||
if (!copy) {
|
||||
//TODO: should we check premultiplied??
|
||||
if (auto loader = _findFromCache((const char*)(data), w * h, "raw")) return loader;
|
||||
}
|
||||
|
||||
//function is dedicated for raw images only
|
||||
auto loader = new RawLoader;
|
||||
if (loader->open(data, w, h, copy)) {
|
||||
if (!copy) {
|
||||
loader->hashkey = HASH_KEY((const char*)data);
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
}
|
||||
return loader;
|
||||
}
|
||||
delete(loader);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
//loads fonts from memory - loader is cached (regardless of copy value) in order to access it while setting font
|
||||
LoadModule* LoaderMgr::loader(const char* name, const char* data, uint32_t size, TVG_UNUSED const string& mimeType, bool copy)
|
||||
{
|
||||
#ifdef THORVG_TTF_LOADER_SUPPORT
|
||||
//TODO: add check for mimetype ?
|
||||
if (auto loader = _findFromCache(name)) return loader;
|
||||
|
||||
//function is dedicated for ttf loader (the only supported font loader)
|
||||
auto loader = new TtfLoader;
|
||||
if (loader->open(data, size, copy)) {
|
||||
loader->hashpath = strdup(name);
|
||||
loader->pathcache = true;
|
||||
ScopedLock lock(key);
|
||||
_activeLoaders.back(loader);
|
||||
return loader;
|
||||
}
|
||||
|
||||
TVGLOG("LOADER", "The font data \"%s\" could not be loaded.", name);
|
||||
delete(loader);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
41
engine/thirdparty/thorvg/src/renderer/tvgLoader.h
vendored
Normal file
41
engine/thirdparty/thorvg/src/renderer/tvgLoader.h
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_LOADER_H_
|
||||
#define _TVG_LOADER_H_
|
||||
|
||||
#include "tvgLoadModule.h"
|
||||
|
||||
struct LoaderMgr
|
||||
{
|
||||
static bool init();
|
||||
static bool term();
|
||||
static LoadModule* loader(const string& path, bool* invalid);
|
||||
static LoadModule* loader(const char* data, uint32_t size, const string& mimeType, bool copy);
|
||||
static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, bool copy);
|
||||
static LoadModule* loader(const char* name, const char* data, uint32_t size, const string& mimeType, bool copy);
|
||||
static LoadModule* loader(const char* key);
|
||||
static bool retrieve(const string& path);
|
||||
static bool retrieve(LoadModule* loader);
|
||||
};
|
||||
|
||||
#endif //_TVG_LOADER_H_
|
||||
503
engine/thirdparty/thorvg/src/renderer/tvgPaint.cpp
vendored
Normal file
503
engine/thirdparty/thorvg/src/renderer/tvgPaint.cpp
vendored
Normal file
|
|
@ -0,0 +1,503 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgPaint.h"
|
||||
#include "tvgShape.h"
|
||||
#include "tvgPicture.h"
|
||||
#include "tvgScene.h"
|
||||
#include "tvgText.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#define PAINT_METHOD(ret, METHOD) \
|
||||
switch (id) { \
|
||||
case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \
|
||||
case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \
|
||||
case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \
|
||||
case TVG_CLASS_ID_TEXT: ret = P((Text*)paint)->METHOD; break; \
|
||||
default: ret = {}; \
|
||||
}
|
||||
|
||||
|
||||
static Result _clipRect(RenderMethod* renderer, const Point* pts, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before)
|
||||
{
|
||||
//sorting
|
||||
Point tmp[4];
|
||||
Point min = {FLT_MAX, FLT_MAX};
|
||||
Point max = {0.0f, 0.0f};
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
tmp[i] = pts[i];
|
||||
if (rTransform) tmp[i] *= rTransform->m;
|
||||
if (pTransform) tmp[i] *= pTransform->m;
|
||||
if (tmp[i].x < min.x) min.x = tmp[i].x;
|
||||
if (tmp[i].x > max.x) max.x = tmp[i].x;
|
||||
if (tmp[i].y < min.y) min.y = tmp[i].y;
|
||||
if (tmp[i].y > max.y) max.y = tmp[i].y;
|
||||
}
|
||||
|
||||
float region[4] = {float(before.x), float(before.x + before.w), float(before.y), float(before.y + before.h)};
|
||||
|
||||
//figure out if the clipper is a superset of the current viewport(before) region
|
||||
if (min.x <= region[0] && max.x >= region[1] && min.y <= region[2] && max.y >= region[3]) {
|
||||
//viewport region is same, nothing to do.
|
||||
return Result::Success;
|
||||
//figure out if the clipper is totally outside of the viewport
|
||||
} else if (max.x <= region[0] || min.x >= region[1] || max.y <= region[2] || min.y >= region[3]) {
|
||||
renderer->viewport({0, 0, 0, 0});
|
||||
return Result::Success;
|
||||
}
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const RenderTransform* pTransform, RenderTransform* rTransform, RenderRegion& before)
|
||||
{
|
||||
/* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
|
||||
auto shape = static_cast<Shape*>(cmpTarget);
|
||||
|
||||
//Rectangle Candidates?
|
||||
const Point* pts;
|
||||
auto ptsCnt = shape->pathCoords(&pts);
|
||||
|
||||
//nothing to clip
|
||||
if (ptsCnt == 0) return Result::InvalidArguments;
|
||||
|
||||
if (ptsCnt != 4) return Result::InsufficientCondition;
|
||||
|
||||
if (rTransform && (cmpTarget->pImpl->renderFlag & RenderUpdateFlag::Transform)) rTransform->update();
|
||||
|
||||
//No rotation and no skewing, still can try out clipping the rect region.
|
||||
auto tryClip = false;
|
||||
|
||||
if (pTransform && (!mathRightAngle(&pTransform->m) || mathSkewed(&pTransform->m))) tryClip = true;
|
||||
if (rTransform && (!mathRightAngle(&rTransform->m) || mathSkewed(&rTransform->m))) tryClip = true;
|
||||
|
||||
if (tryClip) return _clipRect(renderer, pts, pTransform, rTransform, before);
|
||||
|
||||
//Perpendicular Rectangle?
|
||||
auto pt1 = pts + 0;
|
||||
auto pt2 = pts + 1;
|
||||
auto pt3 = pts + 2;
|
||||
auto pt4 = pts + 3;
|
||||
|
||||
if ((mathEqual(pt1->x, pt2->x) && mathEqual(pt2->y, pt3->y) && mathEqual(pt3->x, pt4->x) && mathEqual(pt1->y, pt4->y)) ||
|
||||
(mathEqual(pt2->x, pt3->x) && mathEqual(pt1->y, pt2->y) && mathEqual(pt1->x, pt4->x) && mathEqual(pt3->y, pt4->y))) {
|
||||
|
||||
RenderRegion after;
|
||||
|
||||
auto v1 = *pt1;
|
||||
auto v2 = *pt3;
|
||||
|
||||
if (rTransform) {
|
||||
v1 *= rTransform->m;
|
||||
v2 *= rTransform->m;
|
||||
}
|
||||
|
||||
if (pTransform) {
|
||||
v1 *= pTransform->m;
|
||||
v2 *= pTransform->m;
|
||||
}
|
||||
|
||||
//sorting
|
||||
if (v1.x > v2.x) std::swap(v1.x, v2.x);
|
||||
if (v1.y > v2.y) std::swap(v1.y, v2.y);
|
||||
|
||||
after.x = static_cast<int32_t>(v1.x);
|
||||
after.y = static_cast<int32_t>(v1.y);
|
||||
after.w = static_cast<int32_t>(ceil(v2.x - after.x));
|
||||
after.h = static_cast<int32_t>(ceil(v2.y - after.y));
|
||||
|
||||
if (after.w < 0) after.w = 0;
|
||||
if (after.h < 0) after.h = 0;
|
||||
|
||||
after.intersect(before);
|
||||
renderer->viewport(after);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
RenderRegion Paint::Impl::bounds(RenderMethod* renderer) const
|
||||
{
|
||||
RenderRegion ret;
|
||||
PAINT_METHOD(ret, bounds(renderer));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Iterator* Paint::Impl::iterator()
|
||||
{
|
||||
Iterator* ret;
|
||||
PAINT_METHOD(ret, iterator());
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Paint* Paint::Impl::duplicate()
|
||||
{
|
||||
Paint* ret;
|
||||
PAINT_METHOD(ret, duplicate());
|
||||
|
||||
//duplicate Transform
|
||||
if (rTransform) {
|
||||
ret->pImpl->rTransform = new RenderTransform();
|
||||
*ret->pImpl->rTransform = *rTransform;
|
||||
ret->pImpl->renderFlag |= RenderUpdateFlag::Transform;
|
||||
}
|
||||
|
||||
ret->pImpl->opacity = opacity;
|
||||
|
||||
if (compData) ret->pImpl->composite(ret, compData->target->duplicate(), compData->method);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::rotate(float degree)
|
||||
{
|
||||
if (rTransform) {
|
||||
if (rTransform->overriding) return false;
|
||||
if (mathEqual(degree, rTransform->degree)) return true;
|
||||
} else {
|
||||
if (mathZero(degree)) return true;
|
||||
rTransform = new RenderTransform();
|
||||
}
|
||||
rTransform->degree = degree;
|
||||
renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::scale(float factor)
|
||||
{
|
||||
if (rTransform) {
|
||||
if (rTransform->overriding) return false;
|
||||
if (mathEqual(factor, rTransform->scale)) return true;
|
||||
} else {
|
||||
if (mathEqual(factor, 1.0f)) return true;
|
||||
rTransform = new RenderTransform();
|
||||
}
|
||||
rTransform->scale = factor;
|
||||
renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::translate(float x, float y)
|
||||
{
|
||||
if (rTransform) {
|
||||
if (rTransform->overriding) return false;
|
||||
if (mathEqual(x, rTransform->m.e13) && mathEqual(y, rTransform->m.e23)) return true;
|
||||
} else {
|
||||
if (mathZero(x) && mathZero(y)) return true;
|
||||
rTransform = new RenderTransform();
|
||||
}
|
||||
rTransform->m.e13 = x;
|
||||
rTransform->m.e23 = y;
|
||||
renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::render(RenderMethod* renderer)
|
||||
{
|
||||
Compositor* cmp = nullptr;
|
||||
|
||||
/* Note: only ClipPath is processed in update() step.
|
||||
Create a composition image. */
|
||||
if (compData && compData->method != CompositeMethod::ClipPath && !(compData->target->pImpl->ctxFlag & ContextFlag::FastTrack)) {
|
||||
RenderRegion region;
|
||||
PAINT_METHOD(region, bounds(renderer));
|
||||
|
||||
if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer));
|
||||
if (region.w == 0 || region.h == 0) return true;
|
||||
cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method));
|
||||
if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) {
|
||||
compData->target->pImpl->render(renderer);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmp) renderer->beginComposite(cmp, compData->method, compData->target->pImpl->opacity);
|
||||
|
||||
renderer->blend(blendMethod);
|
||||
|
||||
bool ret;
|
||||
PAINT_METHOD(ret, render(renderer));
|
||||
|
||||
if (cmp) renderer->endComposite(cmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
RenderData Paint::Impl::update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
|
||||
{
|
||||
if (this->renderer != renderer) {
|
||||
if (this->renderer) TVGERR("RENDERER", "paint's renderer has been changed!");
|
||||
renderer->ref();
|
||||
this->renderer = renderer;
|
||||
}
|
||||
|
||||
if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
|
||||
|
||||
/* 1. Composition Pre Processing */
|
||||
RenderData trd = nullptr; //composite target render data
|
||||
RenderRegion viewport;
|
||||
Result compFastTrack = Result::InsufficientCondition;
|
||||
bool childClipper = false;
|
||||
|
||||
if (compData) {
|
||||
auto target = compData->target;
|
||||
auto method = compData->method;
|
||||
target->pImpl->ctxFlag &= ~ContextFlag::FastTrack; //reset
|
||||
|
||||
/* If the transformation has no rotational factors and the ClipPath/Alpha(InvAlpha)Masking involves a simple rectangle,
|
||||
we can optimize by using the viewport instead of the regular ClipPath/AlphaMasking sequence for improved performance. */
|
||||
auto tryFastTrack = false;
|
||||
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
|
||||
if (method == CompositeMethod::ClipPath) tryFastTrack = true;
|
||||
else {
|
||||
auto shape = static_cast<Shape*>(target);
|
||||
uint8_t a;
|
||||
shape->fillColor(nullptr, nullptr, nullptr, &a);
|
||||
//no gradient fill & no compositions of the composition target.
|
||||
if (!shape->fill() && !(PP(shape)->compData)) {
|
||||
if (method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) tryFastTrack = true;
|
||||
else if (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0)) tryFastTrack = true;
|
||||
}
|
||||
}
|
||||
if (tryFastTrack) {
|
||||
viewport = renderer->viewport();
|
||||
if ((compFastTrack = _compFastTrack(renderer, target, pTransform, target->pImpl->rTransform, viewport)) == Result::Success) {
|
||||
target->pImpl->ctxFlag |= ContextFlag::FastTrack;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compFastTrack == Result::InsufficientCondition) {
|
||||
childClipper = compData->method == CompositeMethod::ClipPath ? true : false;
|
||||
trd = target->pImpl->update(renderer, pTransform, clips, 255, pFlag, childClipper);
|
||||
if (childClipper) clips.push(trd);
|
||||
}
|
||||
}
|
||||
|
||||
/* 2. Main Update */
|
||||
auto newFlag = static_cast<RenderUpdateFlag>(pFlag | renderFlag);
|
||||
renderFlag = RenderUpdateFlag::None;
|
||||
opacity = MULTIPLY(opacity, this->opacity);
|
||||
|
||||
RenderData rd = nullptr;
|
||||
RenderTransform outTransform(pTransform, rTransform);
|
||||
PAINT_METHOD(rd, update(renderer, &outTransform, clips, opacity, newFlag, clipper));
|
||||
|
||||
/* 3. Composition Post Processing */
|
||||
if (compFastTrack == Result::Success) renderer->viewport(viewport);
|
||||
else if (childClipper) clips.pop();
|
||||
|
||||
return rd;
|
||||
}
|
||||
|
||||
|
||||
bool Paint::Impl::bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking)
|
||||
{
|
||||
Matrix* m = nullptr;
|
||||
bool ret;
|
||||
|
||||
//Case: No transformed, quick return!
|
||||
if (!transformed || !(m = this->transform())) {
|
||||
PAINT_METHOD(ret, bounds(x, y, w, h, stroking));
|
||||
return ret;
|
||||
}
|
||||
|
||||
//Case: Transformed
|
||||
auto tx = 0.0f;
|
||||
auto ty = 0.0f;
|
||||
auto tw = 0.0f;
|
||||
auto th = 0.0f;
|
||||
|
||||
PAINT_METHOD(ret, bounds(&tx, &ty, &tw, &th, stroking));
|
||||
|
||||
//Get vertices
|
||||
Point pt[4] = {{tx, ty}, {tx + tw, ty}, {tx + tw, ty + th}, {tx, ty + th}};
|
||||
|
||||
//New bounding box
|
||||
auto x1 = FLT_MAX;
|
||||
auto y1 = FLT_MAX;
|
||||
auto x2 = -FLT_MAX;
|
||||
auto y2 = -FLT_MAX;
|
||||
|
||||
//Compute the AABB after transformation
|
||||
for (int i = 0; i < 4; i++) {
|
||||
pt[i] *= *m;
|
||||
|
||||
if (pt[i].x < x1) x1 = pt[i].x;
|
||||
if (pt[i].x > x2) x2 = pt[i].x;
|
||||
if (pt[i].y < y1) y1 = pt[i].y;
|
||||
if (pt[i].y > y2) y2 = pt[i].y;
|
||||
}
|
||||
|
||||
if (x) *x = x1;
|
||||
if (y) *y = y1;
|
||||
if (w) *w = x2 - x1;
|
||||
if (h) *h = y2 - y1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Paint :: Paint() : pImpl(new Impl(this))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Paint :: ~Paint()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Paint::rotate(float degree) noexcept
|
||||
{
|
||||
if (pImpl->rotate(degree)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::scale(float factor) noexcept
|
||||
{
|
||||
if (pImpl->scale(factor)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::translate(float x, float y) noexcept
|
||||
{
|
||||
if (pImpl->translate(x, y)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::transform(const Matrix& m) noexcept
|
||||
{
|
||||
if (pImpl->transform(m)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Matrix Paint::transform() noexcept
|
||||
{
|
||||
auto pTransform = pImpl->transform();
|
||||
if (pTransform) return *pTransform;
|
||||
return {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
}
|
||||
|
||||
|
||||
TVG_DEPRECATED Result Paint::bounds(float* x, float* y, float* w, float* h) const noexcept
|
||||
{
|
||||
return this->bounds(x, y, w, h, false);
|
||||
}
|
||||
|
||||
|
||||
Result Paint::bounds(float* x, float* y, float* w, float* h, bool transform) const noexcept
|
||||
{
|
||||
if (pImpl->bounds(x, y, w, h, transform, true)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Paint* Paint::duplicate() const noexcept
|
||||
{
|
||||
return pImpl->duplicate();
|
||||
}
|
||||
|
||||
|
||||
Result Paint::composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept
|
||||
{
|
||||
auto p = target.release();
|
||||
if (pImpl->composite(this, p, method)) return Result::Success;
|
||||
delete(p);
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
|
||||
CompositeMethod Paint::composite(const Paint** target) const noexcept
|
||||
{
|
||||
if (pImpl->compData) {
|
||||
if (target) *target = pImpl->compData->target;
|
||||
return pImpl->compData->method;
|
||||
} else {
|
||||
if (target) *target = nullptr;
|
||||
return CompositeMethod::None;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Result Paint::opacity(uint8_t o) noexcept
|
||||
{
|
||||
if (pImpl->opacity == o) return Result::Success;
|
||||
|
||||
pImpl->opacity = o;
|
||||
pImpl->renderFlag |= RenderUpdateFlag::Color;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint8_t Paint::opacity() const noexcept
|
||||
{
|
||||
return pImpl->opacity;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Paint::identifier() const noexcept
|
||||
{
|
||||
return pImpl->id;
|
||||
}
|
||||
|
||||
|
||||
Result Paint::blend(BlendMethod method) const noexcept
|
||||
{
|
||||
if (pImpl->blendMethod != method) {
|
||||
pImpl->blendMethod = method;
|
||||
pImpl->renderFlag |= RenderUpdateFlag::Blend;
|
||||
}
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
BlendMethod Paint::blend() const noexcept
|
||||
{
|
||||
return pImpl->blendMethod;
|
||||
}
|
||||
145
engine/thirdparty/thorvg/src/renderer/tvgPaint.h
vendored
Normal file
145
engine/thirdparty/thorvg/src/renderer/tvgPaint.h
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_PAINT_H_
|
||||
#define _TVG_PAINT_H_
|
||||
|
||||
#include "tvgRender.h"
|
||||
#include "tvgMath.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
enum ContextFlag : uint8_t {Invalid = 0, FastTrack = 1};
|
||||
|
||||
struct Iterator
|
||||
{
|
||||
virtual ~Iterator() {}
|
||||
virtual const Paint* next() = 0;
|
||||
virtual uint32_t count() = 0;
|
||||
virtual void begin() = 0;
|
||||
};
|
||||
|
||||
struct Composite
|
||||
{
|
||||
Paint* target;
|
||||
Paint* source;
|
||||
CompositeMethod method;
|
||||
};
|
||||
|
||||
struct Paint::Impl
|
||||
{
|
||||
Paint* paint = nullptr;
|
||||
RenderTransform* rTransform = nullptr;
|
||||
Composite* compData = nullptr;
|
||||
RenderMethod* renderer = nullptr;
|
||||
BlendMethod blendMethod = BlendMethod::Normal; //uint8_t
|
||||
uint8_t renderFlag = RenderUpdateFlag::None;
|
||||
uint8_t ctxFlag = ContextFlag::Invalid;
|
||||
uint8_t id;
|
||||
uint8_t opacity = 255;
|
||||
uint8_t refCnt = 0; //reference count
|
||||
|
||||
Impl(Paint* pnt) : paint(pnt) {}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (compData) {
|
||||
if (P(compData->target)->unref() == 0) delete(compData->target);
|
||||
free(compData);
|
||||
}
|
||||
delete(rTransform);
|
||||
if (renderer && (renderer->unref() == 0)) delete(renderer);
|
||||
}
|
||||
|
||||
uint8_t ref()
|
||||
{
|
||||
if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
|
||||
return ++refCnt;
|
||||
}
|
||||
|
||||
uint8_t unref()
|
||||
{
|
||||
if (refCnt == 0) TVGERR("RENDERER", "Corrupted reference count!");
|
||||
return --refCnt;
|
||||
}
|
||||
|
||||
bool transform(const Matrix& m)
|
||||
{
|
||||
if (!rTransform) {
|
||||
if (mathIdentity(&m)) return true;
|
||||
rTransform = new RenderTransform();
|
||||
}
|
||||
rTransform->override(m);
|
||||
renderFlag |= RenderUpdateFlag::Transform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Matrix* transform()
|
||||
{
|
||||
if (rTransform) {
|
||||
if (renderFlag & RenderUpdateFlag::Transform) rTransform->update();
|
||||
return &rTransform->m;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool composite(Paint* source, Paint* target, CompositeMethod method)
|
||||
{
|
||||
//Invalid case
|
||||
if ((!target && method != CompositeMethod::None) || (target && method == CompositeMethod::None)) return false;
|
||||
|
||||
if (compData) {
|
||||
P(compData->target)->unref();
|
||||
if ((compData->target != target) && P(compData->target)->refCnt == 0) {
|
||||
delete(compData->target);
|
||||
}
|
||||
//Reset scenario
|
||||
if (!target && method == CompositeMethod::None) {
|
||||
free(compData);
|
||||
compData = nullptr;
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!target && method == CompositeMethod::None) return true;
|
||||
compData = static_cast<Composite*>(calloc(1, sizeof(Composite)));
|
||||
}
|
||||
P(target)->ref();
|
||||
compData->target = target;
|
||||
compData->source = source;
|
||||
compData->method = method;
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod* renderer) const;
|
||||
Iterator* iterator();
|
||||
bool rotate(float degree);
|
||||
bool scale(float factor);
|
||||
bool translate(float x, float y);
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool transformed, bool stroking);
|
||||
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper = false);
|
||||
bool render(RenderMethod* renderer);
|
||||
Paint* duplicate();
|
||||
};
|
||||
}
|
||||
|
||||
#endif //_TVG_PAINT_H_
|
||||
232
engine/thirdparty/thorvg/src/renderer/tvgPicture.cpp
vendored
Normal file
232
engine/thirdparty/thorvg/src/renderer/tvgPicture.cpp
vendored
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgPicture.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
RenderUpdateFlag Picture::Impl::load()
|
||||
{
|
||||
if (loader) {
|
||||
if (paint) {
|
||||
loader->sync();
|
||||
} else {
|
||||
paint = loader->paint();
|
||||
if (paint) {
|
||||
if (w != loader->w || h != loader->h) {
|
||||
if (!resizing) {
|
||||
w = loader->w;
|
||||
h = loader->h;
|
||||
}
|
||||
loader->resize(paint, w, h);
|
||||
resizing = false;
|
||||
}
|
||||
return RenderUpdateFlag::None;
|
||||
}
|
||||
}
|
||||
if (!surface) {
|
||||
if ((surface = loader->bitmap())) {
|
||||
return RenderUpdateFlag::Image;
|
||||
}
|
||||
}
|
||||
}
|
||||
return RenderUpdateFlag::None;
|
||||
}
|
||||
|
||||
|
||||
bool Picture::Impl::needComposition(uint8_t opacity)
|
||||
{
|
||||
//In this case, paint(scene) would try composition itself.
|
||||
if (opacity < 255) return false;
|
||||
|
||||
//Composition test
|
||||
const Paint* target;
|
||||
auto method = picture->composite(&target);
|
||||
if (!target || method == tvg::CompositeMethod::ClipPath) return false;
|
||||
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Picture::Impl::render(RenderMethod* renderer)
|
||||
{
|
||||
bool ret = false;
|
||||
if (surface) return renderer->renderImage(rd);
|
||||
else if (paint) {
|
||||
Compositor* cmp = nullptr;
|
||||
if (needComp) {
|
||||
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
|
||||
renderer->beginComposite(cmp, CompositeMethod::None, 255);
|
||||
}
|
||||
ret = paint->pImpl->render(renderer);
|
||||
if (cmp) renderer->endComposite(cmp);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool Picture::Impl::size(float w, float h)
|
||||
{
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
resizing = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
RenderRegion Picture::Impl::bounds(RenderMethod* renderer)
|
||||
{
|
||||
if (rd) return renderer->region(rd);
|
||||
if (paint) return paint->pImpl->bounds(renderer);
|
||||
return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
|
||||
RenderTransform Picture::Impl::resizeTransform(const RenderTransform* pTransform)
|
||||
{
|
||||
//Overriding Transformation by the desired image size
|
||||
auto sx = w / loader->w;
|
||||
auto sy = h / loader->h;
|
||||
auto scale = sx < sy ? sx : sy;
|
||||
|
||||
RenderTransform tmp;
|
||||
tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1};
|
||||
|
||||
if (!pTransform) return tmp;
|
||||
else return RenderTransform(pTransform, &tmp);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::Impl::load(ImageLoader* loader)
|
||||
{
|
||||
//Same resource has been loaded.
|
||||
if (this->loader == loader) {
|
||||
this->loader->sharing--; //make it sure the reference counting.
|
||||
return Result::Success;
|
||||
} else if (this->loader) {
|
||||
LoaderMgr::retrieve(this->loader);
|
||||
}
|
||||
|
||||
this->loader = loader;
|
||||
|
||||
if (!loader->read()) return Result::Unknown;
|
||||
|
||||
this->w = loader->w;
|
||||
this->h = loader->h;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Picture::Picture() : pImpl(new Impl(this))
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_PICTURE;
|
||||
}
|
||||
|
||||
|
||||
Picture::~Picture()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Picture> Picture::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Picture>(new Picture);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Picture::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_PICTURE;
|
||||
}
|
||||
|
||||
|
||||
Result Picture::load(const std::string& path) noexcept
|
||||
{
|
||||
if (path.empty()) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(path);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::load(const char* data, uint32_t size, const string& mimeType, bool copy) noexcept
|
||||
{
|
||||
if (!data || size <= 0) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(data, size, mimeType, copy);
|
||||
}
|
||||
|
||||
|
||||
TVG_DEPRECATED Result Picture::load(const char* data, uint32_t size, bool copy) noexcept
|
||||
{
|
||||
return load(data, size, "", copy);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::load(uint32_t* data, uint32_t w, uint32_t h, bool copy) noexcept
|
||||
{
|
||||
if (!data || w <= 0 || h <= 0) return Result::InvalidArguments;
|
||||
|
||||
return pImpl->load(data, w, h, copy);
|
||||
}
|
||||
|
||||
|
||||
Result Picture::size(float w, float h) noexcept
|
||||
{
|
||||
if (pImpl->size(w, h)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Picture::size(float* w, float* h) const noexcept
|
||||
{
|
||||
if (!pImpl->loader) return Result::InsufficientCondition;
|
||||
if (w) *w = pImpl->w;
|
||||
if (h) *h = pImpl->h;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Picture::mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept
|
||||
{
|
||||
if (!triangles && triangleCnt > 0) return Result::InvalidArguments;
|
||||
if (triangles && triangleCnt == 0) return Result::InvalidArguments;
|
||||
|
||||
pImpl->mesh(triangles, triangleCnt);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Picture::mesh(const Polygon** triangles) const noexcept
|
||||
{
|
||||
if (triangles) *triangles = pImpl->rm.triangles;
|
||||
return pImpl->rm.triangleCnt;
|
||||
}
|
||||
246
engine/thirdparty/thorvg/src/renderer/tvgPicture.h
vendored
Normal file
246
engine/thirdparty/thorvg/src/renderer/tvgPicture.h
vendored
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_PICTURE_H_
|
||||
#define _TVG_PICTURE_H_
|
||||
|
||||
#include <string>
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgLoader.h"
|
||||
|
||||
|
||||
struct PictureIterator : Iterator
|
||||
{
|
||||
Paint* paint = nullptr;
|
||||
Paint* ptr = nullptr;
|
||||
|
||||
PictureIterator(Paint* p) : paint(p) {}
|
||||
|
||||
const Paint* next() override
|
||||
{
|
||||
if (!ptr) ptr = paint;
|
||||
else ptr = nullptr;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
uint32_t count() override
|
||||
{
|
||||
if (paint) return 1;
|
||||
else return 0;
|
||||
}
|
||||
|
||||
void begin() override
|
||||
{
|
||||
ptr = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct Picture::Impl
|
||||
{
|
||||
ImageLoader* loader = nullptr;
|
||||
|
||||
Paint* paint = nullptr; //vector picture uses
|
||||
Surface* surface = nullptr; //bitmap picture uses
|
||||
RenderData rd = nullptr; //engine data
|
||||
float w = 0, h = 0;
|
||||
RenderMesh rm; //mesh data
|
||||
Picture* picture = nullptr;
|
||||
bool resizing = false;
|
||||
bool needComp = false; //need composition
|
||||
|
||||
RenderTransform resizeTransform(const RenderTransform* pTransform);
|
||||
bool needComposition(uint8_t opacity);
|
||||
bool render(RenderMethod* renderer);
|
||||
bool size(float w, float h);
|
||||
RenderRegion bounds(RenderMethod* renderer);
|
||||
Result load(ImageLoader* ploader);
|
||||
|
||||
Impl(Picture* p) : picture(p)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
LoaderMgr::retrieve(loader);
|
||||
if (surface) {
|
||||
if (auto renderer = PP(picture)->renderer) {
|
||||
renderer->dispose(rd);
|
||||
}
|
||||
}
|
||||
delete(paint);
|
||||
}
|
||||
|
||||
RenderData update(RenderMethod* renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
|
||||
{
|
||||
auto flag = static_cast<RenderUpdateFlag>(pFlag | load());
|
||||
|
||||
if (surface) {
|
||||
if (flag == RenderUpdateFlag::None) return rd;
|
||||
auto transform = resizeTransform(pTransform);
|
||||
rd = renderer->prepare(surface, &rm, rd, &transform, clips, opacity, flag);
|
||||
} else if (paint) {
|
||||
if (resizing) {
|
||||
loader->resize(paint, w, h);
|
||||
resizing = false;
|
||||
}
|
||||
needComp = needComposition(opacity) ? true : false;
|
||||
rd = paint->pImpl->update(renderer, pTransform, clips, opacity, flag, clipper);
|
||||
}
|
||||
return rd;
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool stroking)
|
||||
{
|
||||
if (rm.triangleCnt > 0) {
|
||||
auto triangles = rm.triangles;
|
||||
auto min = triangles[0].vertex[0].pt;
|
||||
auto max = triangles[0].vertex[0].pt;
|
||||
|
||||
for (uint32_t i = 0; i < rm.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;
|
||||
}
|
||||
if (x) *x = min.x;
|
||||
if (y) *y = min.y;
|
||||
if (w) *w = max.x - min.x;
|
||||
if (h) *h = max.y - min.y;
|
||||
} else {
|
||||
if (x) *x = 0;
|
||||
if (y) *y = 0;
|
||||
if (w) *w = this->w;
|
||||
if (h) *h = this->h;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Result load(const string& path)
|
||||
{
|
||||
if (paint || surface) return Result::InsufficientCondition;
|
||||
|
||||
bool invalid; //Invalid Path
|
||||
auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(path, &invalid));
|
||||
if (!loader) {
|
||||
if (invalid) return Result::InvalidArguments;
|
||||
return Result::NonSupport;
|
||||
}
|
||||
return load(loader);
|
||||
}
|
||||
|
||||
Result load(const char* data, uint32_t size, const string& mimeType, bool copy)
|
||||
{
|
||||
if (paint || surface) return Result::InsufficientCondition;
|
||||
auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, size, mimeType, copy));
|
||||
if (!loader) return Result::NonSupport;
|
||||
return load(loader);
|
||||
}
|
||||
|
||||
Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy)
|
||||
{
|
||||
if (paint || surface) return Result::InsufficientCondition;
|
||||
|
||||
auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, w, h, copy));
|
||||
if (!loader) return Result::FailedAllocation;
|
||||
|
||||
return load(loader);
|
||||
}
|
||||
|
||||
void mesh(const Polygon* triangles, const uint32_t triangleCnt)
|
||||
{
|
||||
if (triangles && triangleCnt > 0) {
|
||||
this->rm.triangleCnt = triangleCnt;
|
||||
this->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * triangleCnt);
|
||||
memcpy(this->rm.triangles, triangles, sizeof(Polygon) * triangleCnt);
|
||||
} else {
|
||||
free(this->rm.triangles);
|
||||
this->rm.triangles = nullptr;
|
||||
this->rm.triangleCnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
{
|
||||
load();
|
||||
|
||||
auto ret = Picture::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
|
||||
if (paint) dup->paint = paint->duplicate();
|
||||
|
||||
if (loader) {
|
||||
dup->loader = loader;
|
||||
++dup->loader->sharing;
|
||||
PP(ret)->renderFlag |= RenderUpdateFlag::Image;
|
||||
}
|
||||
|
||||
dup->surface = surface;
|
||||
dup->w = w;
|
||||
dup->h = h;
|
||||
dup->resizing = resizing;
|
||||
|
||||
if (rm.triangleCnt > 0) {
|
||||
dup->rm.triangleCnt = rm.triangleCnt;
|
||||
dup->rm.triangles = (Polygon*)malloc(sizeof(Polygon) * rm.triangleCnt);
|
||||
memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
load();
|
||||
return new PictureIterator(paint);
|
||||
}
|
||||
|
||||
uint32_t* data(uint32_t* w, uint32_t* h)
|
||||
{
|
||||
//Try it, If not loaded yet.
|
||||
load();
|
||||
|
||||
if (loader) {
|
||||
if (w) *w = static_cast<uint32_t>(loader->w);
|
||||
if (h) *h = static_cast<uint32_t>(loader->h);
|
||||
} else {
|
||||
if (w) *w = 0;
|
||||
if (h) *h = 0;
|
||||
}
|
||||
if (surface) return surface->buf32;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
RenderUpdateFlag load();
|
||||
};
|
||||
|
||||
#endif //_TVG_PICTURE_H_
|
||||
113
engine/thirdparty/thorvg/src/renderer/tvgRender.cpp
vendored
Normal file
113
engine/thirdparty/thorvg/src/renderer/tvgRender.cpp
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgRender.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
uint32_t RenderMethod::ref()
|
||||
{
|
||||
ScopedLock lock(key);
|
||||
return (++refCnt);
|
||||
}
|
||||
|
||||
|
||||
uint32_t RenderMethod::unref()
|
||||
{
|
||||
ScopedLock lock(key);
|
||||
return (--refCnt);
|
||||
}
|
||||
|
||||
|
||||
void RenderTransform::override(const Matrix& m)
|
||||
{
|
||||
this->m = m;
|
||||
overriding = true;
|
||||
}
|
||||
|
||||
|
||||
void RenderTransform::update()
|
||||
{
|
||||
if (overriding) return;
|
||||
|
||||
m.e11 = 1.0f;
|
||||
m.e12 = 0.0f;
|
||||
|
||||
m.e21 = 0.0f;
|
||||
m.e22 = 1.0f;
|
||||
|
||||
m.e31 = 0.0f;
|
||||
m.e32 = 0.0f;
|
||||
m.e33 = 1.0f;
|
||||
|
||||
mathScale(&m, scale, scale);
|
||||
mathRotate(&m, degree);
|
||||
}
|
||||
|
||||
|
||||
RenderTransform::RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs)
|
||||
{
|
||||
if (lhs && rhs) m = lhs->m * rhs->m;
|
||||
else if (lhs) m = lhs->m;
|
||||
else if (rhs) m = rhs->m;
|
||||
else mathIdentity(&m);
|
||||
}
|
||||
|
||||
|
||||
void RenderRegion::intersect(const RenderRegion& rhs)
|
||||
{
|
||||
auto x1 = x + w;
|
||||
auto y1 = y + h;
|
||||
auto x2 = rhs.x + rhs.w;
|
||||
auto y2 = rhs.y + rhs.h;
|
||||
|
||||
x = (x > rhs.x) ? x : rhs.x;
|
||||
y = (y > rhs.y) ? y : rhs.y;
|
||||
w = ((x1 < x2) ? x1 : x2) - x;
|
||||
h = ((y1 < y2) ? y1 : y2) - y;
|
||||
|
||||
if (w < 0) w = 0;
|
||||
if (h < 0) h = 0;
|
||||
}
|
||||
|
||||
|
||||
void RenderRegion::add(const RenderRegion& rhs)
|
||||
{
|
||||
if (rhs.x < x) {
|
||||
w += (x - rhs.x);
|
||||
x = rhs.x;
|
||||
}
|
||||
if (rhs.y < y) {
|
||||
h += (y - rhs.y);
|
||||
y = rhs.y;
|
||||
}
|
||||
if (rhs.x + rhs.w > x + w) w = (rhs.x + rhs.w) - x;
|
||||
if (rhs.y + rhs.h > y + h) h = (rhs.y + rhs.h) - y;
|
||||
}
|
||||
343
engine/thirdparty/thorvg/src/renderer/tvgRender.h
vendored
Normal file
343
engine/thirdparty/thorvg/src/renderer/tvgRender.h
vendored
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_RENDER_H_
|
||||
#define _TVG_RENDER_H_
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgArray.h"
|
||||
#include "tvgLock.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
using RenderData = void*;
|
||||
using pixel_t = uint32_t;
|
||||
|
||||
enum RenderUpdateFlag : uint8_t {None = 0, Path = 1, Color = 2, Gradient = 4, Stroke = 8, Transform = 16, Image = 32, GradientStroke = 64, Blend = 128, All = 255};
|
||||
|
||||
struct Surface;
|
||||
|
||||
enum ColorSpace
|
||||
{
|
||||
ABGR8888 = 0, //The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
|
||||
ARGB8888, //The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
|
||||
ABGR8888S, //The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
|
||||
ARGB8888S, //The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
|
||||
Grayscale8, //One single channel data.
|
||||
Unsupported //TODO: Change to the default, At the moment, we put it in the last to align with SwCanvas::Colorspace.
|
||||
};
|
||||
|
||||
struct Surface
|
||||
{
|
||||
union {
|
||||
pixel_t* data = nullptr; //system based data pointer
|
||||
uint32_t* buf32; //for explicit 32bits channels
|
||||
uint8_t* buf8; //for explicit 8bits grayscale
|
||||
};
|
||||
Key key; //a reserved lock for the thread safety
|
||||
uint32_t stride = 0;
|
||||
uint32_t w = 0, h = 0;
|
||||
ColorSpace cs = ColorSpace::Unsupported;
|
||||
uint8_t channelSize = 0;
|
||||
bool premultiplied = false; //Alpha-premultiplied
|
||||
|
||||
Surface()
|
||||
{
|
||||
}
|
||||
|
||||
Surface(const Surface* rhs)
|
||||
{
|
||||
data = rhs->data;
|
||||
stride = rhs->stride;
|
||||
w = rhs->w;
|
||||
h = rhs->h;
|
||||
cs = rhs->cs;
|
||||
channelSize = rhs->channelSize;
|
||||
premultiplied = rhs->premultiplied;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
struct Compositor
|
||||
{
|
||||
CompositeMethod method;
|
||||
uint8_t opacity;
|
||||
};
|
||||
|
||||
struct RenderMesh
|
||||
{
|
||||
Polygon* triangles = nullptr;
|
||||
uint32_t triangleCnt = 0;
|
||||
|
||||
~RenderMesh()
|
||||
{
|
||||
free(triangles);
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderRegion
|
||||
{
|
||||
int32_t x, y, w, h;
|
||||
|
||||
void intersect(const RenderRegion& rhs);
|
||||
void add(const RenderRegion& rhs);
|
||||
|
||||
bool operator==(const RenderRegion& rhs) const
|
||||
{
|
||||
if (x == rhs.x && y == rhs.y && w == rhs.w && h == rhs.h) return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderTransform
|
||||
{
|
||||
Matrix m;
|
||||
float degree = 0.0f; //rotation degree
|
||||
float scale = 1.0f; //scale factor
|
||||
bool overriding = false; //user transform?
|
||||
|
||||
void update();
|
||||
void override(const Matrix& m);
|
||||
|
||||
RenderTransform()
|
||||
{
|
||||
m.e13 = m.e23 = 0.0f;
|
||||
}
|
||||
|
||||
RenderTransform(const RenderTransform* lhs, const RenderTransform* rhs);
|
||||
};
|
||||
|
||||
struct RenderStroke
|
||||
{
|
||||
float width = 0.0f;
|
||||
uint8_t color[4] = {0, 0, 0, 0};
|
||||
Fill *fill = nullptr;
|
||||
float* dashPattern = nullptr;
|
||||
uint32_t dashCnt = 0;
|
||||
float dashOffset = 0.0f;
|
||||
StrokeCap cap = StrokeCap::Square;
|
||||
StrokeJoin join = StrokeJoin::Bevel;
|
||||
float miterlimit = 4.0f;
|
||||
bool strokeFirst = false;
|
||||
|
||||
struct {
|
||||
float begin = 0.0f;
|
||||
float end = 1.0f;
|
||||
bool simultaneous = true;
|
||||
} trim;
|
||||
|
||||
~RenderStroke()
|
||||
{
|
||||
free(dashPattern);
|
||||
delete(fill);
|
||||
}
|
||||
};
|
||||
|
||||
struct RenderShape
|
||||
{
|
||||
struct
|
||||
{
|
||||
Array<PathCommand> cmds;
|
||||
Array<Point> pts;
|
||||
} path;
|
||||
|
||||
Fill *fill = nullptr;
|
||||
RenderStroke *stroke = nullptr;
|
||||
uint8_t color[4] = {0, 0, 0, 0}; //r, g, b, a
|
||||
FillRule rule = FillRule::Winding;
|
||||
|
||||
~RenderShape()
|
||||
{
|
||||
delete(fill);
|
||||
delete(stroke);
|
||||
}
|
||||
|
||||
void fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
|
||||
{
|
||||
if (r) *r = color[0];
|
||||
if (g) *g = color[1];
|
||||
if (b) *b = color[2];
|
||||
if (a) *a = color[3];
|
||||
}
|
||||
|
||||
float strokeWidth() const
|
||||
{
|
||||
if (!stroke) return 0;
|
||||
return stroke->width;
|
||||
}
|
||||
|
||||
bool strokeTrim() const
|
||||
{
|
||||
if (!stroke) return false;
|
||||
if (stroke->trim.begin == 0.0f && stroke->trim.end == 1.0f) return false;
|
||||
if (stroke->trim.begin == 1.0f && stroke->trim.end == 0.0f) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const
|
||||
{
|
||||
if (!stroke) return false;
|
||||
|
||||
if (r) *r = stroke->color[0];
|
||||
if (g) *g = stroke->color[1];
|
||||
if (b) *b = stroke->color[2];
|
||||
if (a) *a = stroke->color[3];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Fill* strokeFill() const
|
||||
{
|
||||
if (!stroke) return nullptr;
|
||||
return stroke->fill;
|
||||
}
|
||||
|
||||
uint32_t strokeDash(const float** dashPattern, float* offset) const
|
||||
{
|
||||
if (!stroke) return 0;
|
||||
if (dashPattern) *dashPattern = stroke->dashPattern;
|
||||
if (offset) *offset = stroke->dashOffset;
|
||||
return stroke->dashCnt;
|
||||
}
|
||||
|
||||
StrokeCap strokeCap() const
|
||||
{
|
||||
if (!stroke) return StrokeCap::Square;
|
||||
return stroke->cap;
|
||||
}
|
||||
|
||||
StrokeJoin strokeJoin() const
|
||||
{
|
||||
if (!stroke) return StrokeJoin::Bevel;
|
||||
return stroke->join;
|
||||
}
|
||||
|
||||
float strokeMiterlimit() const
|
||||
{
|
||||
if (!stroke) return 4.0f;
|
||||
|
||||
return stroke->miterlimit;;
|
||||
}
|
||||
};
|
||||
|
||||
class RenderMethod
|
||||
{
|
||||
private:
|
||||
uint32_t refCnt = 0; //reference count
|
||||
Key key;
|
||||
|
||||
public:
|
||||
uint32_t ref();
|
||||
uint32_t unref();
|
||||
|
||||
virtual ~RenderMethod() {}
|
||||
virtual RenderData prepare(const RenderShape& rshape, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags, bool clipper) = 0;
|
||||
virtual RenderData prepare(const Array<RenderData>& scene, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
|
||||
virtual RenderData prepare(Surface* surface, const RenderMesh* mesh, RenderData data, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags) = 0;
|
||||
virtual bool preRender() = 0;
|
||||
virtual bool renderShape(RenderData data) = 0;
|
||||
virtual bool renderImage(RenderData data) = 0;
|
||||
virtual bool postRender() = 0;
|
||||
virtual void dispose(RenderData data) = 0;
|
||||
virtual RenderRegion region(RenderData data) = 0;
|
||||
virtual RenderRegion viewport() = 0;
|
||||
virtual bool viewport(const RenderRegion& vp) = 0;
|
||||
virtual bool blend(BlendMethod method) = 0;
|
||||
virtual ColorSpace colorSpace() = 0;
|
||||
virtual const Surface* mainSurface() = 0;
|
||||
|
||||
virtual bool clear() = 0;
|
||||
virtual bool sync() = 0;
|
||||
|
||||
virtual Compositor* target(const RenderRegion& region, ColorSpace cs) = 0;
|
||||
virtual bool beginComposite(Compositor* cmp, CompositeMethod method, uint8_t opacity) = 0;
|
||||
virtual bool endComposite(Compositor* cmp) = 0;
|
||||
};
|
||||
|
||||
static inline bool MASK_REGION_MERGING(CompositeMethod method)
|
||||
{
|
||||
switch(method) {
|
||||
case CompositeMethod::AlphaMask:
|
||||
case CompositeMethod::InvAlphaMask:
|
||||
case CompositeMethod::LumaMask:
|
||||
case CompositeMethod::InvLumaMask:
|
||||
case CompositeMethod::SubtractMask:
|
||||
case CompositeMethod::IntersectMask:
|
||||
return false;
|
||||
//these might expand the rendering region
|
||||
case CompositeMethod::AddMask:
|
||||
case CompositeMethod::DifferenceMask:
|
||||
return true;
|
||||
default:
|
||||
TVGERR("RENDERER", "Unsupported Composite Method! = %d", (int)method);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t CHANNEL_SIZE(ColorSpace cs)
|
||||
{
|
||||
switch(cs) {
|
||||
case ColorSpace::ABGR8888:
|
||||
case ColorSpace::ABGR8888S:
|
||||
case ColorSpace::ARGB8888:
|
||||
case ColorSpace::ARGB8888S:
|
||||
return sizeof(uint32_t);
|
||||
case ColorSpace::Grayscale8:
|
||||
return sizeof(uint8_t);
|
||||
case ColorSpace::Unsupported:
|
||||
default:
|
||||
TVGERR("RENDERER", "Unsupported Channel Size! = %d", (int)cs);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline ColorSpace COMPOSITE_TO_COLORSPACE(RenderMethod* renderer, CompositeMethod method)
|
||||
{
|
||||
switch(method) {
|
||||
case CompositeMethod::AlphaMask:
|
||||
case CompositeMethod::InvAlphaMask:
|
||||
case CompositeMethod::AddMask:
|
||||
case CompositeMethod::DifferenceMask:
|
||||
case CompositeMethod::SubtractMask:
|
||||
case CompositeMethod::IntersectMask:
|
||||
return ColorSpace::Grayscale8;
|
||||
//TODO: Optimize Luma/InvLuma colorspace to Grayscale8
|
||||
case CompositeMethod::LumaMask:
|
||||
case CompositeMethod::InvLumaMask:
|
||||
return renderer->colorSpace();
|
||||
default:
|
||||
TVGERR("RENDERER", "Unsupported Composite Size! = %d", (int)method);
|
||||
return ColorSpace::Unsupported;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t MULTIPLY(uint8_t c, uint8_t a)
|
||||
{
|
||||
return (((c) * (a) + 0xff) >> 8);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_RENDER_H_
|
||||
43
engine/thirdparty/thorvg/src/renderer/tvgSaveModule.h
vendored
Normal file
43
engine/thirdparty/thorvg/src/renderer/tvgSaveModule.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SAVE_MODULE_H_
|
||||
#define _TVG_SAVE_MODULE_H_
|
||||
|
||||
#include "tvgIteratorAccessor.h"
|
||||
|
||||
namespace tvg
|
||||
{
|
||||
|
||||
class SaveModule
|
||||
{
|
||||
public:
|
||||
virtual ~SaveModule() {}
|
||||
|
||||
virtual bool save(Paint* paint, const string& path, bool compress) = 0;
|
||||
virtual bool save(Animation* animation, Paint* bg, const string& path, uint32_t quality, uint32_t fps) = 0;
|
||||
virtual bool close() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //_TVG_SAVE_MODULE_H_
|
||||
203
engine/thirdparty/thorvg/src/renderer/tvgSaver.cpp
vendored
Normal file
203
engine/thirdparty/thorvg/src/renderer/tvgSaver.cpp
vendored
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 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 "tvgCommon.h"
|
||||
#include "tvgSaveModule.h"
|
||||
#include "tvgPaint.h"
|
||||
|
||||
#ifdef THORVG_TVG_SAVER_SUPPORT
|
||||
#include "tvgTvgSaver.h"
|
||||
#endif
|
||||
#ifdef THORVG_GIF_SAVER_SUPPORT
|
||||
#include "tvgGifSaver.h"
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct Saver::Impl
|
||||
{
|
||||
SaveModule* saveModule = nullptr;
|
||||
Paint* bg = nullptr;
|
||||
|
||||
~Impl()
|
||||
{
|
||||
delete(saveModule);
|
||||
delete(bg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static SaveModule* _find(FileType type)
|
||||
{
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
#ifdef THORVG_TVG_SAVER_SUPPORT
|
||||
return new TvgSaver;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case FileType::Gif: {
|
||||
#ifdef THORVG_GIF_SAVER_SUPPORT
|
||||
return new GifSaver;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef THORVG_LOG_ENABLED
|
||||
const char *format;
|
||||
switch(type) {
|
||||
case FileType::Tvg: {
|
||||
format = "TVG";
|
||||
break;
|
||||
}
|
||||
case FileType::Gif: {
|
||||
format = "GIF";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
format = "???";
|
||||
break;
|
||||
}
|
||||
}
|
||||
TVGLOG("RENDERER", "%s format is not supported", format);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static SaveModule* _find(const string& path)
|
||||
{
|
||||
auto ext = path.substr(path.find_last_of(".") + 1);
|
||||
if (!ext.compare("tvg")) {
|
||||
return _find(FileType::Tvg);
|
||||
} else if (!ext.compare("gif")) {
|
||||
return _find(FileType::Gif);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Saver::Saver() : pImpl(new Impl())
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Saver::~Saver()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compress) noexcept
|
||||
{
|
||||
auto p = paint.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
//Already on saving an other resource.
|
||||
if (pImpl->saveModule) {
|
||||
if (P(p)->refCnt == 0) delete(p);
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
if (auto saveModule = _find(path)) {
|
||||
if (saveModule->save(p, path, compress)) {
|
||||
pImpl->saveModule = saveModule;
|
||||
return Result::Success;
|
||||
} else {
|
||||
if (P(p)->refCnt == 0) delete(p);
|
||||
delete(saveModule);
|
||||
return Result::Unknown;
|
||||
}
|
||||
}
|
||||
if (P(p)->refCnt == 0) delete(p);
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
Result Saver::background(unique_ptr<Paint> paint) noexcept
|
||||
{
|
||||
delete(pImpl->bg);
|
||||
pImpl->bg = paint.release();
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t quality, uint32_t fps) noexcept
|
||||
{
|
||||
auto a = animation.release();
|
||||
if (!a) return Result::MemoryCorruption;
|
||||
|
||||
//animation holds the picture, it must be 1 at the bottom.
|
||||
auto remove = PP(a->picture())->refCnt <= 1 ? true : false;
|
||||
|
||||
if (mathZero(a->totalFrame())) {
|
||||
if (remove) delete(a);
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
//Already on saving an other resource.
|
||||
if (pImpl->saveModule) {
|
||||
if (remove) delete(a);
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
if (auto saveModule = _find(path)) {
|
||||
if (saveModule->save(a, pImpl->bg, path, quality, fps)) {
|
||||
pImpl->saveModule = saveModule;
|
||||
return Result::Success;
|
||||
} else {
|
||||
if (remove) delete(a);
|
||||
delete(saveModule);
|
||||
return Result::Unknown;
|
||||
}
|
||||
}
|
||||
if (remove) delete(a);
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
Result Saver::sync() noexcept
|
||||
{
|
||||
if (!pImpl->saveModule) return Result::InsufficientCondition;
|
||||
pImpl->saveModule->close();
|
||||
delete(pImpl->saveModule);
|
||||
pImpl->saveModule = nullptr;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Saver> Saver::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Saver>(new Saver);
|
||||
}
|
||||
81
engine/thirdparty/thorvg/src/renderer/tvgScene.cpp
vendored
Normal file
81
engine/thirdparty/thorvg/src/renderer/tvgScene.cpp
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgScene.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Scene::Scene() : pImpl(new Impl(this))
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_SCENE;
|
||||
}
|
||||
|
||||
|
||||
Scene::~Scene()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Scene> Scene::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Scene>(new Scene);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Scene::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_SCENE;
|
||||
}
|
||||
|
||||
|
||||
Result Scene::push(unique_ptr<Paint> paint) noexcept
|
||||
{
|
||||
auto p = paint.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
PP(p)->ref();
|
||||
pImpl->paints.push_back(p);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Scene::reserve(TVG_UNUSED uint32_t size) noexcept
|
||||
{
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
Result Scene::clear(bool free) noexcept
|
||||
{
|
||||
pImpl->clear(free);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
list<Paint*>& Scene::paints() noexcept
|
||||
{
|
||||
return pImpl->paints;
|
||||
}
|
||||
229
engine/thirdparty/thorvg/src/renderer/tvgScene.h
vendored
Normal file
229
engine/thirdparty/thorvg/src/renderer/tvgScene.h
vendored
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SCENE_H_
|
||||
#define _TVG_SCENE_H_
|
||||
|
||||
#include <float.h>
|
||||
#include "tvgPaint.h"
|
||||
|
||||
|
||||
struct SceneIterator : Iterator
|
||||
{
|
||||
list<Paint*>* paints;
|
||||
list<Paint*>::iterator itr;
|
||||
|
||||
SceneIterator(list<Paint*>* p) : paints(p)
|
||||
{
|
||||
begin();
|
||||
}
|
||||
|
||||
const Paint* next() override
|
||||
{
|
||||
if (itr == paints->end()) return nullptr;
|
||||
auto paint = *itr;
|
||||
++itr;
|
||||
return paint;
|
||||
}
|
||||
|
||||
uint32_t count() override
|
||||
{
|
||||
return paints->size();
|
||||
}
|
||||
|
||||
void begin() override
|
||||
{
|
||||
itr = paints->begin();
|
||||
}
|
||||
};
|
||||
|
||||
struct Scene::Impl
|
||||
{
|
||||
list<Paint*> paints;
|
||||
RenderData rd = nullptr;
|
||||
Scene* scene = nullptr;
|
||||
uint8_t opacity; //for composition
|
||||
bool needComp = false; //composite or not
|
||||
|
||||
Impl(Scene* s) : scene(s)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
for (auto paint : paints) {
|
||||
if (P(paint)->unref() == 0) delete(paint);
|
||||
}
|
||||
|
||||
if (auto renderer = PP(scene)->renderer) {
|
||||
renderer->dispose(rd);
|
||||
}
|
||||
}
|
||||
|
||||
bool needComposition(uint8_t opacity)
|
||||
{
|
||||
if (opacity == 0 || paints.empty()) return false;
|
||||
|
||||
//Masking may require composition (even if opacity == 255)
|
||||
auto compMethod = scene->composite(nullptr);
|
||||
if (compMethod != CompositeMethod::None && compMethod != CompositeMethod::ClipPath) return true;
|
||||
|
||||
//Blending may require composition (even if opacity == 255)
|
||||
if (scene->blend() != BlendMethod::Normal) return true;
|
||||
|
||||
//Half translucent requires intermediate composition.
|
||||
if (opacity == 255) return false;
|
||||
|
||||
//If scene has several children or only scene, it may require composition.
|
||||
//OPTIMIZE: the bitmap type of the picture would not need the composition.
|
||||
//OPTIMIZE: a single paint of a scene would not need the composition.
|
||||
if (paints.size() == 1 && paints.front()->identifier() == TVG_CLASS_ID_SHAPE) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flag, bool clipper)
|
||||
{
|
||||
if ((needComp = needComposition(opacity))) {
|
||||
/* Overriding opacity value. If this scene is half-translucent,
|
||||
It must do intermeidate composition with that opacity value. */
|
||||
this->opacity = opacity;
|
||||
opacity = 255;
|
||||
}
|
||||
|
||||
if (clipper) {
|
||||
Array<RenderData> rds(paints.size());
|
||||
for (auto paint : paints) {
|
||||
rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true));
|
||||
}
|
||||
rd = renderer->prepare(rds, rd, transform, clips, opacity, flag);
|
||||
return rd;
|
||||
} else {
|
||||
for (auto paint : paints) {
|
||||
paint->pImpl->update(renderer, transform, clips, opacity, flag, false);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool render(RenderMethod* renderer)
|
||||
{
|
||||
Compositor* cmp = nullptr;
|
||||
auto ret = true;
|
||||
|
||||
if (needComp) {
|
||||
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
|
||||
renderer->beginComposite(cmp, CompositeMethod::None, opacity);
|
||||
}
|
||||
|
||||
for (auto paint : paints) {
|
||||
ret &= paint->pImpl->render(renderer);
|
||||
}
|
||||
|
||||
if (cmp) renderer->endComposite(cmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod* renderer) const
|
||||
{
|
||||
if (paints.empty()) return {0, 0, 0, 0};
|
||||
|
||||
int32_t x1 = INT32_MAX;
|
||||
int32_t y1 = INT32_MAX;
|
||||
int32_t x2 = 0;
|
||||
int32_t y2 = 0;
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto region = paint->pImpl->bounds(renderer);
|
||||
|
||||
//Merge regions
|
||||
if (region.x < x1) x1 = region.x;
|
||||
if (x2 < region.x + region.w) x2 = (region.x + region.w);
|
||||
if (region.y < y1) y1 = region.y;
|
||||
if (y2 < region.y + region.h) y2 = (region.y + region.h);
|
||||
}
|
||||
|
||||
return {x1, y1, (x2 - x1), (y2 - y1)};
|
||||
}
|
||||
|
||||
bool bounds(float* px, float* py, float* pw, float* ph, bool stroking)
|
||||
{
|
||||
if (paints.empty()) return false;
|
||||
|
||||
auto x1 = FLT_MAX;
|
||||
auto y1 = FLT_MAX;
|
||||
auto x2 = -FLT_MAX;
|
||||
auto y2 = -FLT_MAX;
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto x = FLT_MAX;
|
||||
auto y = FLT_MAX;
|
||||
auto w = 0.0f;
|
||||
auto h = 0.0f;
|
||||
|
||||
if (!P(paint)->bounds(&x, &y, &w, &h, true, stroking)) continue;
|
||||
|
||||
//Merge regions
|
||||
if (x < x1) x1 = x;
|
||||
if (x2 < x + w) x2 = (x + w);
|
||||
if (y < y1) y1 = y;
|
||||
if (y2 < y + h) y2 = (y + h);
|
||||
}
|
||||
|
||||
if (px) *px = x1;
|
||||
if (py) *py = y1;
|
||||
if (pw) *pw = (x2 - x1);
|
||||
if (ph) *ph = (y2 - y1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
{
|
||||
auto ret = Scene::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
|
||||
for (auto paint : paints) {
|
||||
auto cdup = paint->duplicate();
|
||||
P(cdup)->ref();
|
||||
dup->paints.push_back(cdup);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void clear(bool free)
|
||||
{
|
||||
for (auto paint : paints) {
|
||||
if (P(paint)->unref() == 0 && free) delete(paint);
|
||||
}
|
||||
paints.clear();
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
return new SceneIterator(&paints);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_SCENE_H_
|
||||
429
engine/thirdparty/thorvg/src/renderer/tvgShape.cpp
vendored
Normal file
429
engine/thirdparty/thorvg/src/renderer/tvgShape.cpp
vendored
Normal file
|
|
@ -0,0 +1,429 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgShape.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Shape :: Shape() : pImpl(new Impl(this))
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_SHAPE;
|
||||
}
|
||||
|
||||
|
||||
Shape :: ~Shape()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Shape> Shape::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Shape>(new Shape);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_SHAPE;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::reset() noexcept
|
||||
{
|
||||
pImpl->rs.path.cmds.clear();
|
||||
pImpl->rs.path.pts.clear();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::pathCommands(const PathCommand** cmds) const noexcept
|
||||
{
|
||||
if (cmds) *cmds = pImpl->rs.path.cmds.data;
|
||||
return pImpl->rs.path.cmds.count;
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::pathCoords(const Point** pts) const noexcept
|
||||
{
|
||||
if (pts) *pts = pImpl->rs.path.pts.data;
|
||||
return pImpl->rs.path.pts.count;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendPath(const PathCommand *cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt) noexcept
|
||||
{
|
||||
if (cmdCnt == 0 || ptsCnt == 0 || !cmds || !pts) return Result::InvalidArguments;
|
||||
|
||||
pImpl->grow(cmdCnt, ptsCnt);
|
||||
pImpl->append(cmds, cmdCnt, pts, ptsCnt);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::moveTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->moveTo(x, y);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::lineTo(float x, float y) noexcept
|
||||
{
|
||||
pImpl->lineTo(x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y) noexcept
|
||||
{
|
||||
pImpl->cubicTo(cx1, cy1, cx2, cy2, x, y);
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::close() noexcept
|
||||
{
|
||||
pImpl->close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendCircle(float cx, float cy, float rx, float ry) noexcept
|
||||
{
|
||||
auto rxKappa = rx * PATH_KAPPA;
|
||||
auto ryKappa = ry * PATH_KAPPA;
|
||||
|
||||
pImpl->grow(6, 13);
|
||||
pImpl->moveTo(cx + rx, cy);
|
||||
pImpl->cubicTo(cx + rx, cy + ryKappa, cx + rxKappa, cy + ry, cx, cy + ry);
|
||||
pImpl->cubicTo(cx - rxKappa, cy + ry, cx - rx, cy + ryKappa, cx - rx, cy);
|
||||
pImpl->cubicTo(cx - rx, cy - ryKappa, cx - rxKappa, cy - ry, cx, cy - ry);
|
||||
pImpl->cubicTo(cx + rxKappa, cy - ry, cx + rx, cy - ryKappa, cx + rx, cy);
|
||||
pImpl->close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendArc(float cx, float cy, float radius, float startAngle, float sweep, bool pie) noexcept
|
||||
{
|
||||
//just circle
|
||||
if (sweep >= 360.0f || sweep <= -360.0f) return appendCircle(cx, cy, radius, radius);
|
||||
|
||||
const float arcPrecision = 1e-5f;
|
||||
startAngle = mathDeg2Rad(startAngle);
|
||||
sweep = mathDeg2Rad(sweep);
|
||||
|
||||
auto nCurves = static_cast<int>(fabsf(sweep / MATH_PI2));
|
||||
if (fabsf(sweep / MATH_PI2) - nCurves > arcPrecision) ++nCurves;
|
||||
auto sweepSign = (sweep < 0 ? -1 : 1);
|
||||
auto fract = fmodf(sweep, MATH_PI2);
|
||||
fract = (fabsf(fract) < arcPrecision) ? MATH_PI2 * sweepSign : fract;
|
||||
|
||||
//Start from here
|
||||
Point start = {radius * cosf(startAngle), radius * sinf(startAngle)};
|
||||
|
||||
if (pie) {
|
||||
pImpl->moveTo(cx, cy);
|
||||
pImpl->lineTo(start.x + cx, start.y + cy);
|
||||
} else {
|
||||
pImpl->moveTo(start.x + cx, start.y + cy);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nCurves; ++i) {
|
||||
auto endAngle = startAngle + ((i != nCurves - 1) ? MATH_PI2 * sweepSign : fract);
|
||||
Point end = {radius * cosf(endAngle), radius * sinf(endAngle)};
|
||||
|
||||
//variables needed to calculate bezier control points
|
||||
|
||||
//get bezier control points using article:
|
||||
//(http://itc.ktu.lt/index.php/ITC/article/view/11812/6479)
|
||||
auto ax = start.x;
|
||||
auto ay = start.y;
|
||||
auto bx = end.x;
|
||||
auto by = end.y;
|
||||
auto q1 = ax * ax + ay * ay;
|
||||
auto q2 = ax * bx + ay * by + q1;
|
||||
auto k2 = (4.0f/3.0f) * ((sqrtf(2 * q1 * q2) - q2) / (ax * by - ay * bx));
|
||||
|
||||
start = end; //Next start point is the current end point
|
||||
|
||||
end.x += cx;
|
||||
end.y += cy;
|
||||
|
||||
Point ctrl1 = {ax - k2 * ay + cx, ay + k2 * ax + cy};
|
||||
Point ctrl2 = {bx + k2 * by + cx, by - k2 * bx + cy};
|
||||
|
||||
pImpl->cubicTo(ctrl1.x, ctrl1.y, ctrl2.x, ctrl2.y, end.x, end.y);
|
||||
|
||||
startAngle = endAngle;
|
||||
}
|
||||
|
||||
if (pie) pImpl->close();
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::appendRect(float x, float y, float w, float h, float rx, float ry) noexcept
|
||||
{
|
||||
auto halfW = w * 0.5f;
|
||||
auto halfH = h * 0.5f;
|
||||
|
||||
//clamping cornerRadius by minimum size
|
||||
if (rx > halfW) rx = halfW;
|
||||
if (ry > halfH) ry = halfH;
|
||||
|
||||
//rectangle
|
||||
if (rx == 0 && ry == 0) {
|
||||
pImpl->grow(5, 4);
|
||||
pImpl->moveTo(x, y);
|
||||
pImpl->lineTo(x + w, y);
|
||||
pImpl->lineTo(x + w, y + h);
|
||||
pImpl->lineTo(x, y + h);
|
||||
pImpl->close();
|
||||
//rounded rectangle or circle
|
||||
} else {
|
||||
auto hrx = rx * PATH_KAPPA;
|
||||
auto hry = ry * PATH_KAPPA;
|
||||
pImpl->grow(10, 17);
|
||||
pImpl->moveTo(x + rx, y);
|
||||
pImpl->lineTo(x + w - rx, y);
|
||||
pImpl->cubicTo(x + w - rx + hrx, y, x + w, y + ry - hry, x + w, y + ry);
|
||||
pImpl->lineTo(x + w, y + h - ry);
|
||||
pImpl->cubicTo(x + w, y + h - ry + hry, x + w - rx + hrx, y + h, x + w - rx, y + h);
|
||||
pImpl->lineTo(x + rx, y + h);
|
||||
pImpl->cubicTo(x + rx - hrx, y + h, x, y + h - ry + hry, x, y + h - ry);
|
||||
pImpl->lineTo(x, y + ry);
|
||||
pImpl->cubicTo(x, y + ry - hry, x + rx - hrx, y, x + rx, y);
|
||||
pImpl->close();
|
||||
}
|
||||
|
||||
pImpl->flag |= RenderUpdateFlag::Path;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
if (pImpl->rs.fill) {
|
||||
delete(pImpl->rs.fill);
|
||||
pImpl->rs.fill = nullptr;
|
||||
pImpl->flag |= RenderUpdateFlag::Gradient;
|
||||
}
|
||||
|
||||
if (r == pImpl->rs.color[0] && g == pImpl->rs.color[1] && b == pImpl->rs.color[2] && a == pImpl->rs.color[3]) return Result::Success;
|
||||
|
||||
pImpl->rs.color[0] = r;
|
||||
pImpl->rs.color[1] = g;
|
||||
pImpl->rs.color[2] = b;
|
||||
pImpl->rs.color[3] = a;
|
||||
pImpl->flag |= RenderUpdateFlag::Color;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(unique_ptr<Fill> f) noexcept
|
||||
{
|
||||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
if (pImpl->rs.fill && pImpl->rs.fill != p) delete(pImpl->rs.fill);
|
||||
pImpl->rs.fill = p;
|
||||
pImpl->flag |= RenderUpdateFlag::Gradient;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
|
||||
{
|
||||
pImpl->rs.fillColor(r, g, b, a);
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
const Fill* Shape::fill() const noexcept
|
||||
{
|
||||
return pImpl->rs.fill;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::order(bool strokeFirst) noexcept
|
||||
{
|
||||
pImpl->strokeFirst(strokeFirst);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(float width) noexcept
|
||||
{
|
||||
pImpl->strokeWidth(width);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
float Shape::strokeWidth() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeWidth();
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
|
||||
{
|
||||
pImpl->strokeColor(r, g, b, a);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const noexcept
|
||||
{
|
||||
if (!pImpl->rs.strokeColor(r, g, b, a)) return Result::InsufficientCondition;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(unique_ptr<Fill> f) noexcept
|
||||
{
|
||||
return pImpl->strokeFill(std::move(f));
|
||||
}
|
||||
|
||||
|
||||
const Fill* Shape::strokeFill() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeFill();
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(const float* dashPattern, uint32_t cnt) noexcept
|
||||
{
|
||||
return pImpl->strokeDash(dashPattern, cnt, 0);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Shape::strokeDash(const float** dashPattern) const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeDash(dashPattern, nullptr);
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(StrokeCap cap) noexcept
|
||||
{
|
||||
pImpl->strokeCap(cap);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::stroke(StrokeJoin join) noexcept
|
||||
{
|
||||
pImpl->strokeJoin(join);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeMiterlimit(float miterlimit) noexcept
|
||||
{
|
||||
// https://www.w3.org/TR/SVG2/painting.html#LineJoin
|
||||
// - A negative value for stroke-miterlimit must be treated as an illegal value.
|
||||
if (miterlimit < 0.0f) return Result::InvalidArguments;
|
||||
// TODO Find out a reasonable max value.
|
||||
pImpl->strokeMiterlimit(miterlimit);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
StrokeCap Shape::strokeCap() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeCap();
|
||||
}
|
||||
|
||||
|
||||
StrokeJoin Shape::strokeJoin() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeJoin();
|
||||
}
|
||||
|
||||
|
||||
float Shape::strokeMiterlimit() const noexcept
|
||||
{
|
||||
return pImpl->rs.strokeMiterlimit();
|
||||
}
|
||||
|
||||
|
||||
Result Shape::strokeTrim(float begin, float end, bool simultaneous) noexcept
|
||||
{
|
||||
pImpl->strokeTrim(begin, end, simultaneous);
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
bool Shape::strokeTrim(float* begin, float* end) const noexcept
|
||||
{
|
||||
return pImpl->strokeTrim(begin, end);
|
||||
}
|
||||
|
||||
|
||||
Result Shape::fill(FillRule r) noexcept
|
||||
{
|
||||
pImpl->rs.rule = r;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
FillRule Shape::fillRule() const noexcept
|
||||
{
|
||||
return pImpl->rs.rule;
|
||||
}
|
||||
411
engine/thirdparty/thorvg/src/renderer/tvgShape.h
vendored
Normal file
411
engine/thirdparty/thorvg/src/renderer/tvgShape.h
vendored
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_SHAPE_H_
|
||||
#define _TVG_SHAPE_H_
|
||||
|
||||
#include <memory.h>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgPaint.h"
|
||||
|
||||
|
||||
struct Shape::Impl
|
||||
{
|
||||
RenderShape rs; //shape data
|
||||
RenderData rd = nullptr; //engine data
|
||||
Shape* shape;
|
||||
uint8_t flag = RenderUpdateFlag::None;
|
||||
uint8_t opacity; //for composition
|
||||
bool needComp = false; //composite or not
|
||||
|
||||
Impl(Shape* s) : shape(s)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
if (auto renderer = PP(shape)->renderer) {
|
||||
renderer->dispose(rd);
|
||||
}
|
||||
}
|
||||
|
||||
bool render(RenderMethod* renderer)
|
||||
{
|
||||
Compositor* cmp = nullptr;
|
||||
bool ret;
|
||||
|
||||
if (needComp) {
|
||||
cmp = renderer->target(bounds(renderer), renderer->colorSpace());
|
||||
renderer->beginComposite(cmp, CompositeMethod::None, opacity);
|
||||
}
|
||||
ret = renderer->renderShape(rd);
|
||||
if (cmp) renderer->endComposite(cmp);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool needComposition(uint8_t opacity)
|
||||
{
|
||||
if (opacity == 0) return false;
|
||||
|
||||
//Shape composition is only necessary when stroking & fill are valid.
|
||||
if (!rs.stroke || rs.stroke->width < FLOAT_EPSILON || (!rs.stroke->fill && rs.stroke->color[3] == 0)) return false;
|
||||
if (!rs.fill && rs.color[3] == 0) return false;
|
||||
|
||||
//translucent fill & stroke
|
||||
if (opacity < 255) return true;
|
||||
|
||||
//Composition test
|
||||
const Paint* target;
|
||||
auto method = shape->composite(&target);
|
||||
if (!target || method == CompositeMethod::ClipPath) return false;
|
||||
if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) {
|
||||
if (target->identifier() == TVG_CLASS_ID_SHAPE) {
|
||||
auto shape = static_cast<const Shape*>(target);
|
||||
if (!shape->fill()) {
|
||||
uint8_t r, g, b, a;
|
||||
shape->fillColor(&r, &g, &b, &a);
|
||||
if (a == 0 || a == 255) {
|
||||
if (method == CompositeMethod::LumaMask || method == CompositeMethod::InvLumaMask) {
|
||||
if ((r == 255 && g == 255 && b == 255) || (r == 0 && g == 0 && b == 0)) return false;
|
||||
} else return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
|
||||
{
|
||||
if (static_cast<RenderUpdateFlag>(pFlag | flag) == RenderUpdateFlag::None) return rd;
|
||||
|
||||
if ((needComp = needComposition(opacity))) {
|
||||
/* Overriding opacity value. If this scene is half-translucent,
|
||||
It must do intermeidate composition with that opacity value. */
|
||||
this->opacity = opacity;
|
||||
opacity = 255;
|
||||
}
|
||||
|
||||
rd = renderer->prepare(rs, rd, transform, clips, opacity, static_cast<RenderUpdateFlag>(pFlag | flag), clipper);
|
||||
flag = RenderUpdateFlag::None;
|
||||
return rd;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod* renderer)
|
||||
{
|
||||
return renderer->region(rd);
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h, bool stroking)
|
||||
{
|
||||
//Path bounding size
|
||||
if (rs.path.pts.count > 0 ) {
|
||||
auto pts = rs.path.pts.begin();
|
||||
Point min = { pts->x, pts->y };
|
||||
Point max = { pts->x, pts->y };
|
||||
|
||||
for (auto pts2 = pts + 1; pts2 < rs.path.pts.end(); ++pts2) {
|
||||
if (pts2->x < min.x) min.x = pts2->x;
|
||||
if (pts2->y < min.y) min.y = pts2->y;
|
||||
if (pts2->x > max.x) max.x = pts2->x;
|
||||
if (pts2->y > max.y) max.y = pts2->y;
|
||||
}
|
||||
|
||||
if (x) *x = min.x;
|
||||
if (y) *y = min.y;
|
||||
if (w) *w = max.x - min.x;
|
||||
if (h) *h = max.y - min.y;
|
||||
}
|
||||
|
||||
//Stroke feathering
|
||||
if (stroking && rs.stroke) {
|
||||
if (x) *x -= rs.stroke->width * 0.5f;
|
||||
if (y) *y -= rs.stroke->width * 0.5f;
|
||||
if (w) *w += rs.stroke->width;
|
||||
if (h) *h += rs.stroke->width;
|
||||
}
|
||||
return rs.path.pts.count > 0 ? true : false;
|
||||
}
|
||||
|
||||
void reserveCmd(uint32_t cmdCnt)
|
||||
{
|
||||
rs.path.cmds.reserve(cmdCnt);
|
||||
}
|
||||
|
||||
void reservePts(uint32_t ptsCnt)
|
||||
{
|
||||
rs.path.pts.reserve(ptsCnt);
|
||||
}
|
||||
|
||||
void grow(uint32_t cmdCnt, uint32_t ptsCnt)
|
||||
{
|
||||
rs.path.cmds.grow(cmdCnt);
|
||||
rs.path.pts.grow(ptsCnt);
|
||||
}
|
||||
|
||||
void append(const PathCommand* cmds, uint32_t cmdCnt, const Point* pts, uint32_t ptsCnt)
|
||||
{
|
||||
memcpy(rs.path.cmds.end(), cmds, sizeof(PathCommand) * cmdCnt);
|
||||
memcpy(rs.path.pts.end(), pts, sizeof(Point) * ptsCnt);
|
||||
rs.path.cmds.count += cmdCnt;
|
||||
rs.path.pts.count += ptsCnt;
|
||||
}
|
||||
|
||||
void moveTo(float x, float y)
|
||||
{
|
||||
rs.path.cmds.push(PathCommand::MoveTo);
|
||||
rs.path.pts.push({x, y});
|
||||
}
|
||||
|
||||
void lineTo(float x, float y)
|
||||
{
|
||||
rs.path.cmds.push(PathCommand::LineTo);
|
||||
rs.path.pts.push({x, y});
|
||||
}
|
||||
|
||||
void cubicTo(float cx1, float cy1, float cx2, float cy2, float x, float y)
|
||||
{
|
||||
rs.path.cmds.push(PathCommand::CubicTo);
|
||||
rs.path.pts.push({cx1, cy1});
|
||||
rs.path.pts.push({cx2, cy2});
|
||||
rs.path.pts.push({x, y});
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
//Don't close multiple times.
|
||||
if (rs.path.cmds.count > 0 && rs.path.cmds.last() == PathCommand::Close) return;
|
||||
|
||||
rs.path.cmds.push(PathCommand::Close);
|
||||
}
|
||||
|
||||
void strokeWidth(float width)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->width = width;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
void strokeTrim(float begin, float end, bool simultaneous)
|
||||
{
|
||||
if (!rs.stroke) {
|
||||
if (begin == 0.0f && end == 1.0f) return;
|
||||
rs.stroke = new RenderStroke();
|
||||
}
|
||||
|
||||
if (mathEqual(rs.stroke->trim.begin, begin) && mathEqual(rs.stroke->trim.end, end) &&
|
||||
rs.stroke->trim.simultaneous == simultaneous) return;
|
||||
|
||||
auto loop = true;
|
||||
|
||||
if (begin > 1.0f && end > 1.0f) loop = false;
|
||||
if (begin < 0.0f && end < 0.0f) loop = false;
|
||||
if (begin >= 0.0f && begin <= 1.0f && end >= 0.0f && end <= 1.0f) loop = false;
|
||||
|
||||
if (begin > 1.0f) begin -= 1.0f;
|
||||
if (begin < 0.0f) begin += 1.0f;
|
||||
if (end > 1.0f) end -= 1.0f;
|
||||
if (end < 0.0f) end += 1.0f;
|
||||
|
||||
if ((loop && begin < end) || (!loop && begin > end)) {
|
||||
auto tmp = begin;
|
||||
begin = end;
|
||||
end = tmp;
|
||||
}
|
||||
|
||||
rs.stroke->trim.begin = begin;
|
||||
rs.stroke->trim.end = end;
|
||||
rs.stroke->trim.simultaneous = simultaneous;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
bool strokeTrim(float* begin, float* end)
|
||||
{
|
||||
if (rs.stroke) {
|
||||
if (begin) *begin = rs.stroke->trim.begin;
|
||||
if (end) *end = rs.stroke->trim.end;
|
||||
return rs.stroke->trim.simultaneous;
|
||||
} else {
|
||||
if (begin) *begin = 0.0f;
|
||||
if (end) *end = 1.0f;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void strokeCap(StrokeCap cap)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->cap = cap;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
void strokeJoin(StrokeJoin join)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->join = join;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
void strokeMiterlimit(float miterlimit)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->miterlimit = miterlimit;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
void strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
if (rs.stroke->fill) {
|
||||
delete(rs.stroke->fill);
|
||||
rs.stroke->fill = nullptr;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
|
||||
rs.stroke->color[0] = r;
|
||||
rs.stroke->color[1] = g;
|
||||
rs.stroke->color[2] = b;
|
||||
rs.stroke->color[3] = a;
|
||||
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
Result strokeFill(unique_ptr<Fill> f)
|
||||
{
|
||||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
if (rs.stroke->fill && rs.stroke->fill != p) delete(rs.stroke->fill);
|
||||
rs.stroke->fill = p;
|
||||
rs.stroke->color[3] = 0;
|
||||
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
flag |= RenderUpdateFlag::GradientStroke;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result strokeDash(const float* pattern, uint32_t cnt, float offset)
|
||||
{
|
||||
if ((cnt == 1) || (!pattern && cnt > 0) || (pattern && cnt == 0)) {
|
||||
return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < cnt; i++) {
|
||||
if (pattern[i] < FLOAT_EPSILON) return Result::InvalidArguments;
|
||||
}
|
||||
|
||||
//Reset dash
|
||||
if (!pattern && cnt == 0) {
|
||||
free(rs.stroke->dashPattern);
|
||||
rs.stroke->dashPattern = nullptr;
|
||||
} else {
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
if (rs.stroke->dashCnt != cnt) {
|
||||
free(rs.stroke->dashPattern);
|
||||
rs.stroke->dashPattern = nullptr;
|
||||
}
|
||||
if (!rs.stroke->dashPattern) {
|
||||
rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * cnt));
|
||||
if (!rs.stroke->dashPattern) return Result::FailedAllocation;
|
||||
}
|
||||
for (uint32_t i = 0; i < cnt; ++i) {
|
||||
rs.stroke->dashPattern[i] = pattern[i];
|
||||
}
|
||||
}
|
||||
rs.stroke->dashCnt = cnt;
|
||||
rs.stroke->dashOffset = offset;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
bool strokeFirst()
|
||||
{
|
||||
if (!rs.stroke) return true;
|
||||
return rs.stroke->strokeFirst;
|
||||
}
|
||||
|
||||
void strokeFirst(bool strokeFirst)
|
||||
{
|
||||
if (!rs.stroke) rs.stroke = new RenderStroke();
|
||||
rs.stroke->strokeFirst = strokeFirst;
|
||||
flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
void update(RenderUpdateFlag flag)
|
||||
{
|
||||
this->flag |= flag;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
{
|
||||
auto ret = Shape::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
|
||||
dup->rs.rule = rs.rule;
|
||||
|
||||
//Color
|
||||
memcpy(dup->rs.color, rs.color, sizeof(rs.color));
|
||||
dup->flag = RenderUpdateFlag::Color;
|
||||
|
||||
//Path
|
||||
if (rs.path.cmds.count > 0 && rs.path.pts.count > 0) {
|
||||
dup->rs.path.cmds = rs.path.cmds;
|
||||
dup->rs.path.pts = rs.path.pts;
|
||||
dup->flag |= RenderUpdateFlag::Path;
|
||||
}
|
||||
|
||||
//Stroke
|
||||
if (rs.stroke) {
|
||||
dup->rs.stroke = new RenderStroke();
|
||||
*dup->rs.stroke = *rs.stroke;
|
||||
memcpy(dup->rs.stroke->color, rs.stroke->color, sizeof(rs.stroke->color));
|
||||
if (rs.stroke->dashCnt > 0) {
|
||||
dup->rs.stroke->dashPattern = static_cast<float*>(malloc(sizeof(float) * rs.stroke->dashCnt));
|
||||
memcpy(dup->rs.stroke->dashPattern, rs.stroke->dashPattern, sizeof(float) * rs.stroke->dashCnt);
|
||||
}
|
||||
if (rs.stroke->fill) {
|
||||
dup->rs.stroke->fill = rs.stroke->fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::GradientStroke;
|
||||
}
|
||||
dup->flag |= RenderUpdateFlag::Stroke;
|
||||
}
|
||||
|
||||
//Fill
|
||||
if (rs.fill) {
|
||||
dup->rs.fill = rs.fill->duplicate();
|
||||
dup->flag |= RenderUpdateFlag::Gradient;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_TVG_SHAPE_H_
|
||||
116
engine/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp
vendored
Normal file
116
engine/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgCanvas.h"
|
||||
#include "tvgLoadModule.h"
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
#include "tvgSwRenderer.h"
|
||||
#else
|
||||
class SwRenderer : public RenderMethod
|
||||
{
|
||||
//Non Supported. Dummy Class */
|
||||
};
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct SwCanvas::Impl
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
SwCanvas::SwCanvas() : Canvas(SwRenderer::gen()), pImpl(nullptr)
|
||||
#else
|
||||
SwCanvas::SwCanvas() : Canvas(nullptr), pImpl(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
SwCanvas::~SwCanvas()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result SwCanvas::mempool(MempoolPolicy policy) noexcept
|
||||
{
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
//We know renderer type, avoid dynamic_cast for performance.
|
||||
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
|
||||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
//It can't change the policy during the running.
|
||||
if (!Canvas::pImpl->paints.empty()) return Result::InsufficientCondition;
|
||||
|
||||
if (policy == MempoolPolicy::Individual) renderer->mempool(false);
|
||||
else renderer->mempool(true);
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, Colorspace cs) noexcept
|
||||
{
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
//We know renderer type, avoid dynamic_cast for performance.
|
||||
auto renderer = static_cast<SwRenderer*>(Canvas::pImpl->renderer);
|
||||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target(buffer, stride, w, h, static_cast<ColorSpace>(cs))) return Result::InvalidArguments;
|
||||
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(Canvas::pImpl->vport);
|
||||
|
||||
//FIXME: The value must be associated with an individual canvas instance.
|
||||
ImageLoader::cs = static_cast<ColorSpace>(cs);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->status = Status::Damanged;
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<SwCanvas> SwCanvas::gen() noexcept
|
||||
{
|
||||
#ifdef THORVG_SW_RASTER_SUPPORT
|
||||
if (SwRenderer::init() <= 0) return nullptr;
|
||||
return unique_ptr<SwCanvas>(new SwCanvas);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
229
engine/thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp
vendored
Normal file
229
engine/thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp
vendored
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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 "tvgArray.h"
|
||||
#include "tvgInlist.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
#ifdef THORVG_THREAD_SUPPORT
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
namespace tvg {
|
||||
|
||||
struct TaskSchedulerImpl;
|
||||
static TaskSchedulerImpl* inst = nullptr;
|
||||
|
||||
#ifdef THORVG_THREAD_SUPPORT
|
||||
|
||||
static thread_local bool _async = true;
|
||||
|
||||
struct TaskQueue {
|
||||
Inlist<Task> taskDeque;
|
||||
mutex mtx;
|
||||
condition_variable ready;
|
||||
bool done = false;
|
||||
|
||||
bool tryPop(Task** task)
|
||||
{
|
||||
unique_lock<mutex> lock{mtx, try_to_lock};
|
||||
if (!lock || taskDeque.empty()) return false;
|
||||
*task = taskDeque.front();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tryPush(Task* task)
|
||||
{
|
||||
{
|
||||
unique_lock<mutex> lock{mtx, try_to_lock};
|
||||
if (!lock) return false;
|
||||
taskDeque.back(task);
|
||||
}
|
||||
ready.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
void complete()
|
||||
{
|
||||
{
|
||||
lock_guard<mutex> lock{mtx};
|
||||
done = true;
|
||||
}
|
||||
ready.notify_all();
|
||||
}
|
||||
|
||||
bool pop(Task** task)
|
||||
{
|
||||
unique_lock<mutex> lock{mtx};
|
||||
|
||||
while (taskDeque.empty() && !done) {
|
||||
ready.wait(lock);
|
||||
}
|
||||
|
||||
if (taskDeque.empty()) return false;
|
||||
|
||||
*task = taskDeque.front();
|
||||
return true;
|
||||
}
|
||||
|
||||
void push(Task* task)
|
||||
{
|
||||
{
|
||||
lock_guard<mutex> lock{mtx};
|
||||
taskDeque.back(task);
|
||||
}
|
||||
ready.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct TaskSchedulerImpl
|
||||
{
|
||||
Array<thread*> threads;
|
||||
Array<TaskQueue*> taskQueues;
|
||||
atomic<uint32_t> idx{0};
|
||||
|
||||
TaskSchedulerImpl(uint32_t threadCnt)
|
||||
{
|
||||
threads.reserve(threadCnt);
|
||||
taskQueues.reserve(threadCnt);
|
||||
|
||||
for (uint32_t i = 0; i < threadCnt; ++i) {
|
||||
taskQueues.push(new TaskQueue);
|
||||
threads.push(new thread);
|
||||
}
|
||||
for (uint32_t i = 0; i < threadCnt; ++i) {
|
||||
*threads.data[i] = thread([&, i] { run(i); });
|
||||
}
|
||||
}
|
||||
|
||||
~TaskSchedulerImpl()
|
||||
{
|
||||
for (auto tq = taskQueues.begin(); tq < taskQueues.end(); ++tq) {
|
||||
(*tq)->complete();
|
||||
}
|
||||
for (auto thread = threads.begin(); thread < threads.end(); ++thread) {
|
||||
(*thread)->join();
|
||||
delete(*thread);
|
||||
}
|
||||
for (auto tq = taskQueues.begin(); tq < taskQueues.end(); ++tq) {
|
||||
delete(*tq);
|
||||
}
|
||||
}
|
||||
|
||||
void run(unsigned i)
|
||||
{
|
||||
Task* task;
|
||||
|
||||
//Thread Loop
|
||||
while (true) {
|
||||
auto success = false;
|
||||
for (uint32_t x = 0; x < threads.count * 2; ++x) {
|
||||
if (taskQueues[(i + x) % threads.count]->tryPop(&task)) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success && !taskQueues[i]->pop(&task)) break;
|
||||
(*task)(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void request(Task* task)
|
||||
{
|
||||
//Async
|
||||
if (threads.count > 0 && _async) {
|
||||
task->prepare();
|
||||
auto i = idx++;
|
||||
for (uint32_t n = 0; n < threads.count; ++n) {
|
||||
if (taskQueues[(i + n) % threads.count]->tryPush(task)) return;
|
||||
}
|
||||
taskQueues[i % threads.count]->push(task);
|
||||
//Sync
|
||||
} else {
|
||||
task->run(0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t threadCnt()
|
||||
{
|
||||
return threads.count;
|
||||
}
|
||||
};
|
||||
|
||||
#else //THORVG_THREAD_SUPPORT
|
||||
|
||||
static bool _async = true;
|
||||
|
||||
struct TaskSchedulerImpl
|
||||
{
|
||||
TaskSchedulerImpl(TVG_UNUSED uint32_t threadCnt) {}
|
||||
void request(Task* task) { task->run(0); }
|
||||
uint32_t threadCnt() { return 0; }
|
||||
};
|
||||
|
||||
#endif //THORVG_THREAD_SUPPORT
|
||||
|
||||
} //namespace
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void TaskScheduler::init(uint32_t threads)
|
||||
{
|
||||
if (inst) return;
|
||||
inst = new TaskSchedulerImpl(threads);
|
||||
}
|
||||
|
||||
|
||||
void TaskScheduler::term()
|
||||
{
|
||||
delete(inst);
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void TaskScheduler::request(Task* task)
|
||||
{
|
||||
if (inst) inst->request(task);
|
||||
}
|
||||
|
||||
|
||||
uint32_t TaskScheduler::threads()
|
||||
{
|
||||
if (inst) return inst->threadCnt();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void TaskScheduler::async(bool on)
|
||||
{
|
||||
//toggle async tasking for each thread on/off
|
||||
_async = on;
|
||||
}
|
||||
114
engine/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h
vendored
Normal file
114
engine/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_TASK_SCHEDULER_H_
|
||||
#define _TVG_TASK_SCHEDULER_H_
|
||||
|
||||
#define _DISABLE_CONSTEXPR_MUTEX_CONSTRUCTOR
|
||||
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "tvgCommon.h"
|
||||
#include "tvgInlist.h"
|
||||
|
||||
namespace tvg {
|
||||
|
||||
#ifdef THORVG_THREAD_SUPPORT
|
||||
|
||||
struct Task
|
||||
{
|
||||
private:
|
||||
mutex mtx;
|
||||
condition_variable cv;
|
||||
bool ready = true;
|
||||
bool pending = false;
|
||||
|
||||
public:
|
||||
INLIST_ITEM(Task);
|
||||
|
||||
virtual ~Task() = default;
|
||||
|
||||
void done()
|
||||
{
|
||||
if (!pending) return;
|
||||
|
||||
unique_lock<mutex> lock(mtx);
|
||||
while (!ready) cv.wait(lock);
|
||||
pending = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void run(unsigned tid) = 0;
|
||||
|
||||
private:
|
||||
void operator()(unsigned tid)
|
||||
{
|
||||
run(tid);
|
||||
|
||||
lock_guard<mutex> lock(mtx);
|
||||
ready = true;
|
||||
cv.notify_one();
|
||||
}
|
||||
|
||||
void prepare()
|
||||
{
|
||||
ready = false;
|
||||
pending = true;
|
||||
}
|
||||
|
||||
friend struct TaskSchedulerImpl;
|
||||
};
|
||||
|
||||
#else //THORVG_THREAD_SUPPORT
|
||||
|
||||
struct Task
|
||||
{
|
||||
public:
|
||||
INLIST_ITEM(Task);
|
||||
|
||||
virtual ~Task() = default;
|
||||
void done() {}
|
||||
|
||||
protected:
|
||||
virtual void run(unsigned tid) = 0;
|
||||
|
||||
private:
|
||||
friend struct TaskSchedulerImpl;
|
||||
};
|
||||
|
||||
#endif //THORVG_THREAD_SUPPORT
|
||||
|
||||
|
||||
struct TaskScheduler
|
||||
{
|
||||
static uint32_t threads();
|
||||
static void init(uint32_t threads);
|
||||
static void term();
|
||||
static void request(Task* task);
|
||||
static void async(bool on);
|
||||
};
|
||||
|
||||
} //namespace
|
||||
|
||||
#endif //_TVG_TASK_SCHEDULER_H_
|
||||
|
||||
124
engine/thirdparty/thorvg/src/renderer/tvgText.cpp
vendored
Normal file
124
engine/thirdparty/thorvg/src/renderer/tvgText.cpp
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright (c) 2023 - 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 "tvgText.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
Text::Text() : pImpl(new Impl)
|
||||
{
|
||||
Paint::pImpl->id = TVG_CLASS_ID_TEXT;
|
||||
}
|
||||
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
delete(pImpl);
|
||||
}
|
||||
|
||||
|
||||
Result Text::text(const char* text) noexcept
|
||||
{
|
||||
return pImpl->text(text);
|
||||
}
|
||||
|
||||
|
||||
Result Text::font(const char* name, float size, const char* style) noexcept
|
||||
{
|
||||
return pImpl->font(name, size, style);
|
||||
}
|
||||
|
||||
|
||||
Result Text::load(const std::string& path) noexcept
|
||||
{
|
||||
bool invalid; //invalid path
|
||||
if (!LoaderMgr::loader(path, &invalid)) {
|
||||
if (invalid) return Result::InvalidArguments;
|
||||
else return Result::NonSupport;
|
||||
}
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Text::load(const char* name, const char* data, uint32_t size, const string& mimeType, bool copy) noexcept
|
||||
{
|
||||
if (!name || (size == 0 && data)) return Result::InvalidArguments;
|
||||
|
||||
//unload font
|
||||
if (!data) {
|
||||
if (LoaderMgr::retrieve(name)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
if (!LoaderMgr::loader(name, data, size, mimeType, copy)) return Result::NonSupport;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
|
||||
Result Text::unload(const std::string& path) noexcept
|
||||
{
|
||||
if (LoaderMgr::retrieve(path)) return Result::Success;
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
|
||||
Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept
|
||||
{
|
||||
if (!pImpl->paint) return Result::InsufficientCondition;
|
||||
|
||||
return pImpl->fill(r, g, b);
|
||||
}
|
||||
|
||||
|
||||
Result Text::fill(unique_ptr<Fill> f) noexcept
|
||||
{
|
||||
if (!pImpl->paint) return Result::InsufficientCondition;
|
||||
|
||||
auto p = f.release();
|
||||
if (!p) return Result::MemoryCorruption;
|
||||
|
||||
return pImpl->fill(p);
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<Text> Text::gen() noexcept
|
||||
{
|
||||
return unique_ptr<Text>(new Text);
|
||||
}
|
||||
|
||||
|
||||
uint32_t Text::identifier() noexcept
|
||||
{
|
||||
return TVG_CLASS_ID_TEXT;
|
||||
}
|
||||
183
engine/thirdparty/thorvg/src/renderer/tvgText.h
vendored
Normal file
183
engine/thirdparty/thorvg/src/renderer/tvgText.h
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* Copyright (c) 2023 - 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.
|
||||
*/
|
||||
|
||||
#ifndef _TVG_TEXT_H
|
||||
#define _TVG_TEXT_H
|
||||
|
||||
#include <cstring>
|
||||
#include "tvgShape.h"
|
||||
#include "tvgFill.h"
|
||||
|
||||
#ifdef THORVG_TTF_LOADER_SUPPORT
|
||||
#include "tvgTtfLoader.h"
|
||||
#else
|
||||
#include "tvgLoader.h"
|
||||
#endif
|
||||
|
||||
struct Text::Impl
|
||||
{
|
||||
FontLoader* loader = nullptr;
|
||||
Shape* paint = nullptr;
|
||||
char* utf8 = nullptr;
|
||||
float fontSize;
|
||||
bool italic = false;
|
||||
bool changed = false;
|
||||
|
||||
~Impl()
|
||||
{
|
||||
free(utf8);
|
||||
LoaderMgr::retrieve(loader);
|
||||
delete(paint);
|
||||
}
|
||||
|
||||
Result fill(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
return paint->fill(r, g, b);
|
||||
}
|
||||
|
||||
Result fill(Fill* f)
|
||||
{
|
||||
return paint->fill(cast<Fill>(f));
|
||||
}
|
||||
|
||||
Result text(const char* utf8)
|
||||
{
|
||||
free(this->utf8);
|
||||
if (utf8) this->utf8 = strdup(utf8);
|
||||
else this->utf8 = nullptr;
|
||||
changed = true;
|
||||
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
Result font(const char* name, float size, const char* style)
|
||||
{
|
||||
auto loader = LoaderMgr::loader(name);
|
||||
if (!loader) return Result::InsufficientCondition;
|
||||
|
||||
//Same resource has been loaded.
|
||||
if (this->loader == loader) {
|
||||
this->loader->sharing--; //make it sure the reference counting.
|
||||
return Result::Success;
|
||||
} else if (this->loader) {
|
||||
LoaderMgr::retrieve(this->loader);
|
||||
}
|
||||
this->loader = static_cast<FontLoader*>(loader);
|
||||
|
||||
if (!paint) paint = Shape::gen().release();
|
||||
|
||||
fontSize = size;
|
||||
if (style && strstr(style, "italic")) italic = true;
|
||||
changed = true;
|
||||
return Result::Success;
|
||||
}
|
||||
|
||||
RenderRegion bounds(RenderMethod* renderer)
|
||||
{
|
||||
if (paint) return P(paint)->bounds(renderer);
|
||||
else return {0, 0, 0, 0};
|
||||
}
|
||||
|
||||
bool render(RenderMethod* renderer)
|
||||
{
|
||||
if (paint) return PP(paint)->render(renderer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load()
|
||||
{
|
||||
if (!loader) return false;
|
||||
|
||||
//reload
|
||||
if (changed) {
|
||||
loader->request(paint, utf8, italic);
|
||||
loader->read();
|
||||
changed = false;
|
||||
}
|
||||
if (paint) {
|
||||
loader->resize(paint, fontSize, fontSize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderData update(RenderMethod* renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper)
|
||||
{
|
||||
if (!load()) return nullptr;
|
||||
|
||||
//transform the gradient coordinates based on the final scaled font.
|
||||
if (P(paint)->flag & RenderUpdateFlag::Gradient) {
|
||||
auto fill = P(paint)->rs.fill;
|
||||
auto scale = 1.0f / loader->scale;
|
||||
if (fill->identifier() == TVG_CLASS_ID_LINEAR) {
|
||||
P(static_cast<LinearGradient*>(fill))->x1 *= scale;
|
||||
P(static_cast<LinearGradient*>(fill))->y1 *= scale;
|
||||
P(static_cast<LinearGradient*>(fill))->x2 *= scale;
|
||||
P(static_cast<LinearGradient*>(fill))->y2 *= scale;
|
||||
} else {
|
||||
P(static_cast<RadialGradient*>(fill))->cx *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->cy *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->r *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->fx *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->fy *= scale;
|
||||
P(static_cast<RadialGradient*>(fill))->fr *= scale;
|
||||
}
|
||||
}
|
||||
return PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper);
|
||||
}
|
||||
|
||||
bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking)
|
||||
{
|
||||
if (!load() || !paint) return false;
|
||||
paint->bounds(x, y, w, h, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
Paint* duplicate()
|
||||
{
|
||||
load();
|
||||
|
||||
auto ret = Text::gen().release();
|
||||
auto dup = ret->pImpl;
|
||||
if (paint) dup->paint = static_cast<Shape*>(paint->duplicate());
|
||||
|
||||
if (loader) {
|
||||
dup->loader = loader;
|
||||
++dup->loader->sharing;
|
||||
}
|
||||
|
||||
dup->utf8 = strdup(utf8);
|
||||
dup->italic = italic;
|
||||
dup->fontSize = fontSize;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Iterator* iterator()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //_TVG_TEXT_H
|
||||
89
engine/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp
vendored
Normal file
89
engine/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp
vendored
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2023 - 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 "tvgCanvas.h"
|
||||
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
#include "tvgWgRenderer.h"
|
||||
#endif
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
struct WgCanvas::Impl
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(new Impl)
|
||||
#else
|
||||
WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WgCanvas::~WgCanvas()
|
||||
{
|
||||
delete pImpl;
|
||||
}
|
||||
|
||||
|
||||
Result WgCanvas::target(void* instance, void* surface, uint32_t w, uint32_t h) noexcept
|
||||
{
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
if (Canvas::pImpl->status != Status::Damanged && Canvas::pImpl->status != Status::Synced) {
|
||||
return Result::InsufficientCondition;
|
||||
}
|
||||
|
||||
if (!instance || !surface || (w == 0) || (h == 0)) return Result::InvalidArguments;
|
||||
|
||||
//We know renderer type, avoid dynamic_cast for performance.
|
||||
auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer);
|
||||
if (!renderer) return Result::MemoryCorruption;
|
||||
|
||||
if (!renderer->target((WGPUInstance)instance, (WGPUSurface)surface, w, h)) return Result::Unknown;
|
||||
Canvas::pImpl->vport = {0, 0, (int32_t)w, (int32_t)h};
|
||||
renderer->viewport(Canvas::pImpl->vport);
|
||||
|
||||
//Paints must be updated again with this new target.
|
||||
Canvas::pImpl->status = Status::Damanged;
|
||||
|
||||
return Result::Success;
|
||||
#endif
|
||||
return Result::NonSupport;
|
||||
}
|
||||
|
||||
|
||||
unique_ptr<WgCanvas> WgCanvas::gen() noexcept
|
||||
{
|
||||
#ifdef THORVG_WG_RASTER_SUPPORT
|
||||
return unique_ptr<WgCanvas>(new WgCanvas);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue