Merge pull request #109651 from Chubercik/thorvg-0.15.14

thorvg: Update to 0.15.16
This commit is contained in:
Thaddeus Crews 2025-12-15 08:01:11 -06:00
commit 36f07362d4
No known key found for this signature in database
GPG Key ID: 8C6E5FEB5FC03CCC
25 changed files with 780 additions and 790 deletions

View File

@ -1062,7 +1062,7 @@ Files extracted from upstream source:
## thorvg
- Upstream: https://github.com/thorvg/thorvg
- Version: 0.15.13 (c597365b99f27cb46e2a5ac2942da45bb73d5a55, 2025)
- Version: 0.15.16 (e15069de7afcc5e853edf1561e69d9b8383e2c6c, 2025)
- License: MIT
Files extracted from upstream source:

View File

@ -15,5 +15,5 @@
// For internal debugging:
//#define THORVG_LOG_ENABLED
#define THORVG_VERSION_STRING "0.15.13"
#define THORVG_VERSION_STRING "0.15.16"
#endif

View File

@ -260,10 +260,14 @@ enum class Type : uint8_t
/**
* @brief A data structure representing a point in two-dimensional space.
*
* This structure defines a single point using Cartesian coordinates.
* It is typically used for specifying positions or coordinates in 2D graphics.
*/
struct Point
{
float x, y;
float x; ///< The x-coordinate of the point.
float y; ///< The y-coordinate of the point.
};
@ -453,7 +457,7 @@ public:
*
* @return The class type ID of the Paint instance.
*
* @since Experimental API
* @note Experimental API
*/
virtual Type type() const noexcept = 0;
@ -462,7 +466,7 @@ public:
*
* This is reserved to specify an paint instance in a scene.
*
* @since Experimental API
* @note Experimental API
*/
uint32_t id = 0;
@ -568,7 +572,7 @@ public:
*
* @return The class type ID of the Fill instance.
*
* @since Experimental API
* @note Experimental API
*/
virtual Type type() const noexcept = 0;
@ -758,7 +762,7 @@ public:
*
* @return The class type ID of the LinearGradient instance.
*
* @since Experimental API
* @note Experimental API
*/
Type type() const noexcept override;
@ -823,7 +827,7 @@ public:
*
* @return The class type ID of the LinearGradient instance.
*
* @since Experimental API
* @note Experimental API
*/
Type type() const noexcept override;
@ -1223,7 +1227,7 @@ public:
*
* @return The class type ID of the Shape instance.
*
* @since Experimental API
* @note Experimental API
*/
Type type() const noexcept override;
@ -1361,7 +1365,7 @@ public:
*
* @return The class type ID of the Picture instance.
*
* @since Experimental API
* @note Experimental API
*/
Type type() const noexcept override;
@ -1461,7 +1465,7 @@ public:
*
* @return The class type ID of the Scene instance.
*
* @since Experimental API
* @note Experimental API
*/
Type type() const noexcept override;
@ -1493,13 +1497,22 @@ public:
* It sets the font name, size and optionally the style.
*
* @param[in] name The name of the font. This should correspond to a font available in the canvas.
* If set to @c nullptr, ThorVG will attempt to select a fallback font available on the system.
* @param[in] size The size of the font in points. This determines how large the text will appear.
* @param[in] style The style of the font. It can be used to set the font to 'italic'.
* If not specified, the default style is used. Only 'italic' style is supported currently.
*
* @retval Result::InsufficientCondition when the specified @p name cannot be found.
*
* @note Experimental API
* @note If the @p name is not specified, ThorVG will select any available font candidate.
* @since 1.0
*
* @code
* // Tip for fallback support to use any available font.
* if (text->font("Arial", 24) != tvg::Result::Success) {
* text->font(nullptr, 24);
* }
* @endcode
*/
Result font(const char* name, float size, const char* style = nullptr) noexcept;
@ -1619,7 +1632,7 @@ public:
*
* @return The class type ID of the Text instance.
*
* @since Experimental API
* @note Experimental API
*/
Type type() const noexcept override;

View File

@ -85,10 +85,17 @@ bool identity(const Matrix* m);
Matrix operator*(const Matrix& lhs, const Matrix& rhs);
bool operator==(const Matrix& lhs, const Matrix& rhs);
static inline float radian(const Matrix& m)
{
return fabsf(tvg::atan2(m.e21, m.e11));
}
static inline bool rightAngle(const Matrix& m)
{
auto radian = fabsf(tvg::atan2(m.e21, m.e11));
if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true;
auto radian = tvg::radian(m);
if (tvg::zero(radian) || tvg::zero(radian - MATH_PI2) || tvg::zero(radian - MATH_PI)) return true;
return false;
}

View File

@ -230,11 +230,12 @@ char* strAppend(char* lhs, const char* rhs, size_t n)
char* strDirname(const char* path)
{
const char *ptr = strrchr(path, '/');
auto ptr = strrchr(path, '/');
#ifdef _WIN32
if (ptr) ptr = strrchr(ptr + 1, '\\');
auto ptr2 = strrchr(ptr ? ptr : path, '\\');
if (ptr2) ptr = ptr2;
#endif
int len = int(ptr + 1 - path); // +1 to include '/'
auto len = ptr ? size_t(ptr - path + 1) : SIZE_MAX;
return strDuplicate(path, len);
}

View File

@ -393,8 +393,8 @@ static char* _idFromUrl(const char* url)
++open;
--close;
//trim the rest of the spaces if any
while (open < close && *close == ' ') --close;
//trim the rest of the spaces and the quote marks if any
while (open < close && (*close == ' ' || *close == '\'' || *close == '\"')) --close;
//quick verification
for (auto id = open; id < close; id++) {
@ -927,43 +927,6 @@ error:
}
static void _postpone(Array<SvgNodeIdPair>& nodes, SvgNode *node, char* id)
{
nodes.push({node, id});
}
/*
// TODO - remove?
static constexpr struct
{
const char* tag;
int sz;
SvgLengthType type;
} lengthTags[] = {
LENGTH_DEF(%, SvgLengthType::Percent),
LENGTH_DEF(px, SvgLengthType::Px),
LENGTH_DEF(pc, SvgLengthType::Pc),
LENGTH_DEF(pt, SvgLengthType::Pt),
LENGTH_DEF(mm, SvgLengthType::Mm),
LENGTH_DEF(cm, SvgLengthType::Cm),
LENGTH_DEF(in, SvgLengthType::In)
};
static float _parseLength(const char* str, SvgLengthType* type)
{
float value;
int sz = strlen(str);
*type = SvgLengthType::Px;
for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
}
value = svgUtilStrtof(str, nullptr);
return value;
}
*/
static bool _parseStyleAttr(void* data, const char* key, const char* value);
static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
@ -1215,7 +1178,7 @@ static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char
cssCopyStyleAttr(node, cssNode);
}
if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass);
if (!cssClassFound) loader->nodesToStyle.push({node, *cssClass});
}
@ -2108,6 +2071,23 @@ static SvgNode* _findParentById(SvgNode* node, char* id, SvgNode* doc)
}
static bool _checkPostponed(SvgNode* node, SvgNode* cloneNode, int depth)
{
if (node == cloneNode) return true;
if (depth == 512) {
TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
return false;
}
for (uint32_t i = 0; i < node->child.count; ++i) {
if (_checkPostponed(node->child[i], cloneNode, depth + 1)) return true;
}
return false;
}
static constexpr struct
{
const char* tag;
@ -2149,17 +2129,30 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
nodeFrom = _findNodeById(defs, id);
if (nodeFrom) {
if (!_findParentById(node, id, loader->doc)) {
_cloneNode(nodeFrom, node, 0);
if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
//Check if none of nodeFrom's children are in the cloneNodes list
auto postpone = false;
for (auto pair = loader->cloneNodes.head; pair; pair = pair->next) {
if (_checkPostponed(nodeFrom, pair->node, 1)) {
postpone = true;
loader->cloneNodes.back(new((SvgNodeIdPair*)malloc(sizeof(SvgNodeIdPair))) SvgNodeIdPair(node, id));
break;
}
}
//None of the children of nodeFrom are on the cloneNodes list, so it can be cloned immediately
if (!postpone) {
_cloneNode(nodeFrom, node, 0);
if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
free(id);
}
} else {
TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id);
free(id);
}
free(id);
} else {
//some svg export software include <defs> element at the end of the file
//if so the 'from' element won't be found now and we have to repeat finding
//after the whole file is parsed
_postpone(loader->cloneNodes, node, id);
loader->cloneNodes.back(new((SvgNodeIdPair*)malloc(sizeof(SvgNodeIdPair))) SvgNodeIdPair(node, id));
}
} else {
return _attrParseGNode(data, key, value);
@ -3322,22 +3315,39 @@ static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
}
static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
static void _clonePostponedNodes(Inlist<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
{
for (uint32_t i = 0; i < cloneNodes->count; ++i) {
auto nodeIdPair = (*cloneNodes)[i];
auto defs = _getDefsNode(nodeIdPair.node);
auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
if (!_findParentById(nodeIdPair.node, nodeIdPair.id, doc)) {
_cloneNode(nodeFrom, nodeIdPair.node, 0);
if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
nodeIdPair.node->node.use.symbol = nodeFrom;
auto nodeIdPair = cloneNodes->front();
while (nodeIdPair) {
if (!_findParentById(nodeIdPair->node, nodeIdPair->id, doc)) {
//Check if none of nodeFrom's children are in the cloneNodes list
auto postpone = false;
auto nodeFrom = _findNodeById(_getDefsNode(nodeIdPair->node), nodeIdPair->id);
if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair->id);
if (nodeFrom) {
for (auto pair = cloneNodes->head; pair; pair = pair->next) {
if (_checkPostponed(nodeFrom, pair->node, 1)) {
postpone = true;
cloneNodes->back(nodeIdPair);
break;
}
}
}
//Since none of the child nodes of nodeFrom are present in the cloneNodes list, it can be cloned immediately
if (!postpone) {
_cloneNode(nodeFrom, nodeIdPair->node, 0);
if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair->node->type == SvgNodeType::Use) {
nodeIdPair->node->node.use.symbol = nodeFrom;
}
free(nodeIdPair->id);
free(nodeIdPair);
}
} else {
TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair.id);
TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", nodeIdPair->id);
free(nodeIdPair->id);
free(nodeIdPair);
}
free(nodeIdPair.id);
nodeIdPair = cloneNodes->front();
}
}
@ -3917,7 +3927,7 @@ void SvgLoader::run(unsigned tid)
if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle);
if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle);
if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
if (!loaderData.cloneNodes.empty()) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
_updateComposite(loaderData.doc, loaderData.doc);
if (defs) _updateComposite(loaderData.doc, defs);

View File

@ -25,6 +25,7 @@
#include "tvgCommon.h"
#include "tvgArray.h"
#include "tvgInlist.h"
struct SvgNode;
struct SvgStyleGradient;
@ -551,6 +552,8 @@ struct SvgParser
struct SvgNodeIdPair
{
INLIST_ITEM(SvgNodeIdPair);
SvgNodeIdPair(SvgNode* n, char* i) : node{n}, id{i} {}
SvgNode* node;
char *id;
};
@ -579,7 +582,7 @@ struct SvgLoaderData
Array<SvgStyleGradient*> gradients;
Array<SvgStyleGradient*> gradientStack; //For stops
SvgParser* svgParse = nullptr;
Array<SvgNodeIdPair> cloneNodes;
Inlist<SvgNodeIdPair> cloneNodes;
Array<SvgNodeIdPair> nodesToStyle;
Array<char*> images; //embedded images
Array<FontFace> fonts;

View File

@ -35,6 +35,7 @@
using SwCoord = signed long;
using SwFixed = signed long long;
#define SW_COLOR_TABLE 1024
static inline float TO_FLOAT(SwCoord val)
@ -128,6 +129,19 @@ struct SwBBox
{
min.x = min.y = max.x = max.y = 0;
}
int32_t w() const { return max.x - min.x; }
int32_t h() const { return max.y - min.y; }
};
using Area = long;
struct SwCell
{
int32_t x;
int32_t cover;
Area area;
SwCell *next;
};
struct SwFill
@ -150,7 +164,7 @@ struct SwFill
SwRadial radial;
};
uint32_t* ctable;
uint32_t ctable[SW_COLOR_TABLE];
FillSpread spread;
bool solid = false; //solid color fill with the last color from colorStops
@ -269,7 +283,7 @@ struct SwSurface : RenderSurface
blender = rhs->blender;
compositor = rhs->compositor;
blendMethod = rhs->blendMethod;
}
}
};
struct SwCompositor : RenderCompositor
@ -281,11 +295,22 @@ struct SwCompositor : RenderCompositor
bool valid;
};
struct SwCellPool
{
#define DEFAULT_POOL_SIZE 16368
uint32_t size;
SwCell* buffer;
SwCellPool() : size(DEFAULT_POOL_SIZE), buffer((SwCell*)malloc(DEFAULT_POOL_SIZE)) {}
~SwCellPool() { free(buffer); }
};
struct SwMpool
{
SwOutline* outline;
SwOutline* strokeOutline;
SwOutline* dashOutline;
SwCellPool* cellPool;
unsigned allocSize;
};
@ -478,9 +503,9 @@ static inline uint32_t opBlendHardLight(uint32_t s, uint32_t d, TVG_UNUSED uint8
static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
{
//(255 - 2 * s) * (d * d) + (2 * s * b)
auto c1 = MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + MULTIPLY(std::min(255, 2 * C1(s)), C1(d));
auto c2 = MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + MULTIPLY(std::min(255, 2 * C2(s)), C2(d));
auto c3 = MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + MULTIPLY(std::min(255, 2 * C3(s)), C3(d));
auto c1 = MULTIPLY(255 - std::min(255, 2 * C1(s)), MULTIPLY(C1(d), C1(d))) + std::min(255, 2 * MULTIPLY(C1(s), C1(d)));
auto c2 = MULTIPLY(255 - std::min(255, 2 * C2(s)), MULTIPLY(C2(d), C2(d))) + std::min(255, 2 * MULTIPLY(C2(s), C2(d)));
auto c3 = MULTIPLY(255 - std::min(255, 2 * C3(s)), MULTIPLY(C3(d), C3(d))) + std::min(255, 2 * MULTIPLY(C3(s), C3(d)));
return JOIN(255, c1, c2, c3);
}
@ -506,7 +531,7 @@ bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
void shapeReset(SwShape* shape);
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
bool shapePrepared(const SwShape* shape);
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, bool antiAlias);
bool shapeGenRle(SwShape* shape, const RenderShape* rshape, SwMpool* mpool, unsigned tid, 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);
@ -525,7 +550,7 @@ SwOutline* strokeExportOutline(SwStroke* stroke, SwMpool* mpool, unsigned tid);
void strokeFree(SwStroke* stroke);
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias);
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image);
void imageFree(SwImage* image);
@ -548,7 +573,7 @@ void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, SwBlender op, SwBlender op2, uint8_t a); //blending + BlendingMethod(op2) ver.
void fillRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len, uint8_t* cmp, SwAlpha alpha, uint8_t csize, uint8_t opacity); //matting ver.
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias);
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias);
SwRle* rleRender(const SwBBox* bbox);
void rleFree(SwRle* rle);
void rleReset(SwRle* rle);
@ -565,11 +590,16 @@ SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx);
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
SwCellPool* mpoolReqCellPool(SwMpool* mpool, unsigned idx);
bool rasterCompositor(SwSurface* surface);
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
bool rasterShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint8_t b, uint8_t a);
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterDirectImage(SwSurface* surface, const SwImage& image, const SwBBox& bbox, uint8_t opacity);
bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity);
bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, 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, const Fill* fdata, uint8_t opacity);
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);

View File

@ -29,7 +29,6 @@
/************************************************************************/
#define RADIAL_A_THRESHOLD 0.0005f
#define GRADIENT_STOP_SIZE 1024
#define FIXPT_BITS 8
#define FIXPT_SIZE (1<<FIXPT_BITS)
@ -102,11 +101,11 @@ static inline uint32_t _alphaUnblend(uint32_t c)
}
static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end)
static void _applyAA(SwFill* fill, uint32_t begin, uint32_t end)
{
if (begin == 0 || end == 0) return;
auto i = GRADIENT_STOP_SIZE - end;
auto i = SW_COLOR_TABLE - end;
auto rgbaEnd = _alphaUnblend(fill->ctable[i]);
auto rgbaBegin = _alphaUnblend(fill->ctable[begin]);
@ -117,7 +116,7 @@ static void _applyAA(const SwFill* fill, uint32_t begin, uint32_t end)
auto color = INTERPOLATE(rgbaEnd, rgbaBegin, dist);
fill->ctable[i++] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
if (i == GRADIENT_STOP_SIZE) i = 0;
if (i == SW_COLOR_TABLE) i = 0;
t += dt;
}
}
@ -127,11 +126,6 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
{
if (fill->solid) return true;
if (!fill->ctable) {
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
if (!fill->ctable) return false;
}
const Fill::ColorStop* colors;
auto cnt = fdata->colorStops(&colors);
if (cnt == 0 || !colors) return false;
@ -145,8 +139,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
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 inc = 1.0f / static_cast<float>(SW_COLOR_TABLE);
auto pos = 1.5f * inc;
uint32_t i = 0;
@ -166,7 +159,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
for (uint32_t j = 0; j < cnt - 1; ++j) {
if (repeat && j == cnt - 2 && iAAEnd == 0) {
iAAEnd = iAABegin;
_adjustAAMargin(iAAEnd, GRADIENT_STOP_SIZE - i);
_adjustAAMargin(iAAEnd, SW_COLOR_TABLE - i);
}
auto curr = colors + j;
@ -177,7 +170,7 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
auto rgba2 = surface->join(next->r, next->g, next->b, a2);
while (pos < next->offset && i < GRADIENT_STOP_SIZE) {
while (pos < next->offset && i < SW_COLOR_TABLE) {
auto t = (pos - curr->offset) * delta;
auto dist = static_cast<int32_t>(255 * t);
auto dist2 = 255 - dist;
@ -195,13 +188,13 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
}
rgba = ALPHA_BLEND((rgba | 0xff000000), a);
for (; i < GRADIENT_STOP_SIZE; ++i)
for (; i < SW_COLOR_TABLE; ++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;
else fill->ctable[SW_COLOR_TABLE - 1] = rgba;
return true;
}
@ -277,10 +270,12 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
//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) {
constexpr float precision = 0.01f;
if (fill->radial.a <= precision) {
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;
//retract focal point slightly from edge to avoid numerical errors:
fill->radial.fx = cx + r * (1.0f - precision) * (fx - cx) / dist;
fill->radial.fy = cy + r * (1.0f - precision) * (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
@ -325,20 +320,20 @@ 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;
if (pos >= SW_COLOR_TABLE) pos = SW_COLOR_TABLE - 1;
else if (pos < 0) pos = 0;
break;
}
case FillSpread::Repeat: {
pos = pos % GRADIENT_STOP_SIZE;
if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
pos = pos % SW_COLOR_TABLE;
if (pos < 0) pos = SW_COLOR_TABLE + pos;
break;
}
case FillSpread::Reflect: {
auto limit = GRADIENT_STOP_SIZE * 2;
auto limit = SW_COLOR_TABLE * 2;
pos = pos % limit;
if (pos < 0) pos = limit + pos;
if (pos >= GRADIENT_STOP_SIZE) pos = (limit - pos - 1);
if (pos >= SW_COLOR_TABLE) pos = (limit - pos - 1);
break;
}
}
@ -355,7 +350,7 @@ static inline uint32_t _fixedPixel(const SwFill* fill, int32_t pos)
static inline uint32_t _pixel(const SwFill* fill, float pos)
{
auto i = static_cast<int32_t>(pos * (GRADIENT_STOP_SIZE - 1) + 0.5f);
auto i = static_cast<int32_t>(pos * (SW_COLOR_TABLE - 1) + 0.5f);
return fill->ctable[_clamp(fill, i)];
}
@ -550,8 +545,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
//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);
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
if (opacity == 255) {
if (tvg::zero(inc)) {
@ -578,7 +573,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
} else {
uint32_t counter = 0;
while (counter++ < len) {
*dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, alpha(cmp));
*dst = opBlendNormal(_pixel(fill, t / SW_COLOR_TABLE), *dst, alpha(cmp));
++dst;
t += inc;
cmp += csize;
@ -609,7 +604,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
} else {
uint32_t counter = 0;
while (counter++ < len) {
*dst = opBlendNormal(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, MULTIPLY(opacity, alpha(cmp)));
*dst = opBlendNormal(_pixel(fill, t / SW_COLOR_TABLE), *dst, MULTIPLY(opacity, alpha(cmp)));
++dst;
t += inc;
cmp += csize;
@ -624,8 +619,8 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
//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);
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
if (tvg::zero(inc)) {
auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
@ -652,7 +647,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
} else {
uint32_t counter = 0;
while (counter++ < len) {
auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
auto src = MULTIPLY(A(_pixel(fill, t / SW_COLOR_TABLE)), a);
*dst = maskOp(src, *dst, ~src);
++dst;
t += inc;
@ -666,8 +661,8 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
//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);
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
if (tvg::zero(inc)) {
auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
@ -697,7 +692,7 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
} else {
uint32_t counter = 0;
while (counter++ < len) {
auto src = MULTIPLY(A(_pixel(fill, t / GRADIENT_STOP_SIZE)), a);
auto src = MULTIPLY(A(_pixel(fill, t / SW_COLOR_TABLE)), a);
auto tmp = maskOp(src, *cmp, 0);
*dst = tmp + MULTIPLY(*dst, ~tmp);
++dst;
@ -713,8 +708,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
//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);
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
if (tvg::zero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
@ -740,7 +735,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
} else {
uint32_t counter = 0;
while (counter++ < len) {
*dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a);
*dst = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, a);
++dst;
t += inc;
}
@ -753,8 +748,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
//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);
float t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
if (tvg::zero(inc)) {
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
@ -791,7 +786,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
} else {
uint32_t counter = 0;
while (counter++ < len) {
auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
auto tmp = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, 255);
*dst = op2(tmp, *dst, 255);
++dst;
t += inc;
@ -812,7 +807,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
} else {
uint32_t counter = 0;
while (counter++ < len) {
auto tmp = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, 255);
auto tmp = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, 255);
auto tmp2 = op2(tmp, *dst, 255);
*dst = INTERPOLATE(tmp2, *dst, a);
++dst;
@ -854,10 +849,6 @@ const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata)
void fillReset(SwFill* fill)
{
if (fill->ctable) {
free(fill->ctable);
fill->ctable = nullptr;
}
fill->translucent = false;
fill->solid = false;
}
@ -865,9 +856,5 @@ void fillReset(SwFill* fill)
void fillFree(SwFill* fill)
{
if (!fill) return;
if (fill->ctable) free(fill->ctable);
free(fill);
if (fill) free(fill);
}

View File

@ -95,9 +95,9 @@ bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipReg
}
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias)
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias)
{
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, antiAlias))) return true;
if ((image->rle = rleRender(image->rle, image->outline, renderRegion, mpool, tid, antiAlias))) return true;
return false;
}

View File

@ -245,9 +245,7 @@ void mathSplitCubic(SwPoint* base)
void mathSplitLine(SwPoint* base)
{
base[2] = base[1];
base[1].x = (base[0].x + base[1].x) >> 1;
base[1].y = (base[0].y + base[1].y) >> 1;
base[1] = {(base[0].x >> 1) + (base[1].x >> 1), (base[0].y >> 1) + (base[1].y >> 1)};
}

View File

@ -23,11 +23,6 @@
#include "tvgSwCommon.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
@ -62,18 +57,9 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
}
SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx)
SwCellPool* mpoolReqCellPool(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();
return &mpool->cellPool[idx];
}
@ -84,7 +70,8 @@ SwMpool* mpoolInit(uint32_t threads)
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->cellPool = new SwCellPool[allocSize];
mpool->allocSize = allocSize;
return mpool;
@ -103,11 +90,6 @@ bool mpoolClear(SwMpool* mpool)
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;
@ -122,7 +104,7 @@ bool mpoolTerm(SwMpool* mpool)
free(mpool->outline);
free(mpool->strokeOutline);
free(mpool->dashOutline);
delete[](mpool->cellPool);
free(mpool);
return true;

View File

@ -266,25 +266,87 @@ static void _dropShadowFilter(uint32_t* dst, uint32_t* src, int stride, int w, i
}
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, SwBBox& region, SwPoint& offset, uint8_t opacity, bool direct)
static void _shift(uint32_t** dst, uint32_t** src, int dstride, int sstride, int wmax, int hmax, const SwBBox& bbox, SwPoint offset, SwSize& size)
{
src += (region.min.y * sstride + region.min.x);
dst += (region.min.y * dstride + region.min.x);
size.w = bbox.max.x - bbox.min.x;
size.h = bbox.max.y - bbox.min.y;
auto w = region.max.x - region.min.x;
auto h = region.max.y - region.min.y;
auto translucent = (direct || opacity < 255);
//shift
if (offset.x < 0) {
*src -= offset.x;
size.w += offset.x;
} else {
*dst += offset.x;
size.w -= offset.x;
}
//shift offset
if (region.min.x + offset.x < 0) src -= offset.x;
else dst += offset.x;
if (offset.y < 0) {
*src -= (offset.y * sstride);
size.h += offset.y;
} else {
*dst += (offset.y * dstride);
size.h -= offset.y;
}
}
if (region.min.y + offset.y < 0) src -= (offset.y * sstride);
else dst += (offset.y * dstride);
for (auto y = 0; y < h; ++y) {
if (translucent) rasterTranslucentPixel32(dst, src, w, opacity);
else rasterPixel32(dst, src, w, opacity);
static void _dropShadowNoFilter(uint32_t* dst, uint32_t* src, int dstride, int sstride, int dw, int dh, const SwBBox& bbox, const SwPoint& offset, uint32_t color, uint8_t opacity, bool direct)
{
src += (bbox.min.y * sstride + bbox.min.x);
dst += (bbox.min.y * dstride + bbox.min.x);
SwSize size;
_shift(&dst, &src, dstride, sstride, dw, dh, bbox, offset, size);
for (auto y = 0; y < size.h; ++y) {
auto s2 = src;
auto d2 = dst;
for (int x = 0; x < size.w; ++x, ++d2, ++s2) {
auto a = MULTIPLY(opacity, A(*s2));
if (!direct || a == 255) *d2 = ALPHA_BLEND(color, a);
else *d2 = INTERPOLATE(color, *d2, a);
}
src += sstride;
dst += dstride;
}
}
static void _dropShadowNoFilter(SwImage* dimg, SwImage* simg, const SwBBox& bbox, const SwPoint& offset, uint32_t color)
{
int dstride = dimg->stride;
int sstride = simg->stride;
//shadow image
_dropShadowNoFilter(dimg->buf32, simg->buf32, dstride, sstride, dimg->w, dimg->h, bbox, offset, color, 255, false);
//original image
auto src = simg->buf32 + (bbox.min.y * sstride + bbox.min.x);
auto dst = dimg->buf32 + (bbox.min.y * dstride + bbox.min.x);
for (auto y = 0; y < (bbox.max.y - bbox.min.y); ++y) {
auto s = src;
auto d = dst;
for (int x = 0; x < (bbox.max.x - bbox.min.x); ++x, ++d, ++s) {
*d = *s + ALPHA_BLEND(*d, IA(*s));
}
src += sstride;
dst += dstride;
}
}
static void _dropShadowShift(uint32_t* dst, uint32_t* src, int dstride, int sstride, int dw, int dh, const SwBBox& bbox, const SwPoint& offset, uint8_t opacity, bool direct)
{
src += (bbox.min.y * sstride + bbox.min.x);
dst += (bbox.min.y * dstride + bbox.min.x);
SwSize size;
_shift(&dst, &src, dstride, sstride, dw, dh, bbox, offset, size);
for (auto y = 0; y < size.h; ++y) {
if (direct) rasterTranslucentPixel32(dst, src, size.w, opacity);
else rasterPixel32(dst, src, size.w, opacity);
src += sstride;
dst += dstride;
}
@ -322,18 +384,14 @@ void effectDropShadowUpdate(RenderEffectDropShadow* params, const Matrix& transf
rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
//invalid
if (rd->extends == 0 || params->color[3] == 0) {
if (params->color[3] == 0) {
params->valid = false;
return;
}
//offset
if (params->distance > 0.0f) {
auto radian = tvg::deg2rad(90.0f - params->angle);
rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))};
} else {
rd->offset = {0, 0};
}
auto radian = tvg::deg2rad(90.0f - params->angle) - tvg::radian(transform);
rd->offset = {(int32_t)((params->distance * scale) * cosf(radian)), (int32_t)(-1.0f * (params->distance * scale) * sinf(radian))};
params->valid = true;
}
@ -364,6 +422,17 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe
TVGLOG("SW_ENGINE", "DropShadow region(%ld, %ld, %ld, %ld) params(%f %f %f), level(%d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->angle, params->distance, params->sigma, data->level);
//no filter required
if (data->extends == 0) {
if (direct) {
_dropShadowNoFilter(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, cmp->image.stride, cmp->recoverSfc->w, cmp->recoverSfc->h, bbox, data->offset, color, opacity, direct);
} else {
_dropShadowNoFilter(buffer[1], &cmp->image, bbox, data->offset, color);
std::swap(cmp->image.buf32, buffer[1]->buf32);
}
return true;
}
//saving the original image in order to overlay it into the filtered image.
_dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false);
std::swap(front, buffer[0]->buf32);
@ -389,14 +458,14 @@ bool effectDropShadow(SwCompositor* cmp, SwSurface* surface[2], const RenderEffe
//draw to the main surface directly
if (direct) {
_dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, stride, bbox, data->offset, opacity, direct);
_dropShadowShift(cmp->recoverSfc->buf32, cmp->image.buf32, cmp->recoverSfc->stride, cmp->image.stride, cmp->recoverSfc->w, cmp->recoverSfc->h, bbox, data->offset, opacity, direct);
std::swap(cmp->image.buf32, buffer[0]->buf32);
return true;
}
//draw to the intermediate surface
rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h);
_dropShadowShift(buffer[1]->buf32, cmp->image.buf32, stride, stride, bbox, data->offset, opacity, direct);
_dropShadowShift(buffer[1]->buf32, cmp->image.buf32, buffer[1]->stride, cmp->image.stride, buffer[1]->w, buffer[1]->h, bbox, data->offset, opacity, direct);
std::swap(cmp->image.buf32, buffer[1]->buf32);
//compositing shadow and body
@ -485,8 +554,6 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct)
TVGLOG("SW_ENGINE", "Tint region(%ld, %ld, %ld, %ld), param(%d %d %d, %d %d %d, %d)", bbox.min.x, bbox.min.y, bbox.max.x, bbox.max.y, params->black[0], params->black[1], params->black[2], params->white[0], params->white[1], params->white[2], params->intensity);
/* Tint Formula: (1 - L) * Black + L * White, where the L is Luminance. */
if (direct) {
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
auto sbuffer = cmp->image.buf32 + (bbox.min.y * cmp->image.stride + bbox.min.x);
@ -525,11 +592,6 @@ bool effectTint(SwCompositor* cmp, const RenderEffectTint* params, bool direct)
static uint32_t _trintone(uint32_t s, uint32_t m, uint32_t h, int l)
{
/* Tritone Formula:
if (L < 0.5) { (1 - 2L) * Shadow + 2L * Midtone }
else { (1 - 2(L - 0.5)) * Midtone + (2(L - 0.5)) * Highlight }
Where the L is Luminance. */
if (l < 128) {
auto a = std::min(l * 2, 255);
return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a);

View File

@ -227,10 +227,10 @@ static inline SwMask _getMaskOp(CompositeMethod method)
}
static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const SwBBox& region)
static bool _compositeMaskImage(SwSurface* surface, const SwImage& image, const SwBBox& region)
{
auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
auto sbuffer = image->buf8 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto sbuffer = image.buf8 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox);
for (auto y = region.min.y; y < region.max.y; ++y) {
auto dst = dbuffer;
@ -239,7 +239,7 @@ static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const
*dst = *src + MULTIPLY(*dst, ~*src);
}
dbuffer += surface->stride;
sbuffer += image->stride;
sbuffer += image.stride;
}
return true;
}
@ -339,7 +339,7 @@ static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region,
}
cbuffer += cstride;
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
}
@ -506,7 +506,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask mas
*cmp = maskOp(src, *cmp, ialpha);
}
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
}
@ -681,45 +681,45 @@ static bool _rasterRle(SwSurface* surface, SwRle* rle, uint8_t r, uint8_t g, uin
#define SCALED_IMAGE_RANGE_Y(y) \
auto sy = (y) * itransform->e22 + itransform->e23 - 0.49f; \
if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image->h) continue; \
if (sy <= -0.5f || (uint32_t)(sy + 0.5f) >= image.h) continue; \
if (scaleMethod == _interpDownScaler) { \
auto my = (int32_t)nearbyint(sy); \
miny = my - (int32_t)sampleSize; \
if (miny < 0) miny = 0; \
maxy = my + (int32_t)sampleSize; \
if (maxy >= (int32_t)image->h) maxy = (int32_t)image->h; \
if (maxy >= (int32_t)image.h) maxy = (int32_t)image.h; \
}
#define SCALED_IMAGE_RANGE_X \
auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \
if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image->w) continue; \
if (sx <= -0.5f || (uint32_t)(sx + 0.5f) >= image.w) continue; \
static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledMaskedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
return false;
}
static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
TVGLOG("SW_ENGINE", "Scaled Matted(%d) Rle Image", (int)surface->compositor->method);
auto span = image->rle->spans;
auto span = image.rle->spans;
auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
for (uint32_t i = 0; i < image.rle->size; ++i, ++span) {
SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
auto a = MULTIPLY(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst, cmp += csize) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a));
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
@ -728,28 +728,28 @@ static bool _rasterScaledMattedRleImage(SwSurface* surface, const SwImage* image
}
static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
auto span = image->rle->spans;
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto span = image.rle->spans;
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
for (uint32_t i = 0; i < image.rle->size; ++i, ++span) {
SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, A(src));
}
} else {
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src)));
}
@ -759,20 +759,20 @@ static bool _rasterScaledBlendingRleImage(SwSurface* surface, const SwImage* ima
}
static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
auto span = image->rle->spans;
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto span = image.rle->spans;
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
for (uint32_t i = 0; i < image.rle->size; ++i, ++span) {
SCALED_IMAGE_RANGE_Y(span->y)
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto alpha = MULTIPLY(span->coverage, opacity);
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
if (alpha < 255) src = ALPHA_BLEND(src, alpha);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
@ -781,46 +781,23 @@ static bool _rasterScaledRleImage(SwSurface* surface, const SwImage* image, cons
}
static bool _scaledRleImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported scaled rle image!");
return false;
}
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, region, opacity);
else return _rasterScaledMaskedRleImage(surface, image, &itransform, region, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingRleImage(surface, image, &itransform, region, opacity);
} else {
return _rasterScaledRleImage(surface, image, &itransform, region, opacity);
}
return false;
}
/************************************************************************/
/* RLE Direct Image */
/************************************************************************/
static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{
TVGLOG("SW_ENGINE", "Direct Matted(%d) Rle Image", (int)surface->compositor->method);
auto span = image->rle->spans;
auto span = image.rle->spans;
auto csize = surface->compositor->image.channelSize;
auto cbuffer = surface->compositor->image.buf8;
auto alpha = surface->alpha(surface->compositor->method);
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
for (uint32_t i = 0; i < image.rle->size; ++i, ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox);
auto a = MULTIPLY(span->coverage, opacity);
if (a == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
@ -838,13 +815,13 @@ static bool _rasterDirectMattedRleImage(SwSurface* surface, const SwImage* image
}
static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{
auto span = image->rle->spans;
auto span = image.rle->spans;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
for (uint32_t i = 0; i < image.rle->size; ++i, ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox);
auto alpha = MULTIPLY(span->coverage, opacity);
if (alpha == 255) {
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
@ -861,13 +838,13 @@ static bool _rasterDirectBlendingRleImage(SwSurface* surface, const SwImage* ima
}
static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
static bool _rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{
auto span = image->rle->spans;
auto span = image.rle->spans;
for (uint32_t i = 0; i < image->rle->size; ++i, ++span) {
for (uint32_t i = 0; i < image.rle->size; ++i, ++span) {
auto dst = &surface->buf32[span->y * surface->stride + span->x];
auto img = image->buf32 + (span->y + image->oy) * image->stride + (span->x + image->ox);
auto img = image.buf32 + (span->y + image.oy) * image.stride + (span->x + image.ox);
auto alpha = MULTIPLY(span->coverage, opacity);
rasterTranslucentPixel32(dst, img, span->len, alpha);
}
@ -875,44 +852,25 @@ static bool _rasterDirectRleImage(SwSurface* surface, const SwImage* image, uint
}
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
static bool _rasterDirectMaskedRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{
TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method);
return false;
}
static bool _directRleImage(SwSurface* surface, const SwImage* image, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale rle image!");
return false;
}
if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity);
else return _rasterDirectMaskedRleImage(surface, image, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingRleImage(surface, image, opacity);
} else {
return _rasterDirectRleImage(surface, image, opacity);
}
return false;
}
/************************************************************************/
/*Scaled Image */
/************************************************************************/
static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledMaskedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!");
return false;
}
static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale scaled matted image!");
@ -926,8 +884,8 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
TVGLOG("SW_ENGINE", "Scaled Matted(%d) Image [Region: %lu %lu %lu %lu]", (int)surface->compositor->method, region.min.x, region.min.y, region.max.x - region.min.x, region.max.y - region.min.y);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y) {
@ -936,7 +894,7 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
auto cmp = cbuffer;
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp)));
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
}
@ -947,7 +905,7 @@ static bool _rasterScaledMattedImage(SwSurface* surface, const SwImage* image, c
}
static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale scaled blending image!");
@ -955,8 +913,8 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
}
auto dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0;
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
@ -964,7 +922,7 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
auto dst = dbuffer;
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
auto tmp = surface->blender(src, *dst, 255);
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src)));
}
@ -973,10 +931,10 @@ static bool _rasterScaledBlendingImage(SwSurface* surface, const SwImage* image,
}
static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
static bool _rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix* itransform, const SwBBox& region, uint8_t opacity)
{
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image->scale);
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
auto sampleSize = _sampleSize(image.scale);
int32_t miny = 0, maxy = 0;
//32bits channels
@ -987,7 +945,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
auto dst = buffer;
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
if (opacity < 255) src = ALPHA_BLEND(src, opacity);
*dst = src + ALPHA_BLEND(*dst, IA(src));
}
@ -999,7 +957,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
auto dst = buffer;
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
SCALED_IMAGE_RANGE_X
auto src = scaleMethod(image->buf32, image->stride, image->w, image->h, sx, sy, miny, maxy, sampleSize);
auto src = scaleMethod(image.buf32, image.stride, image.w, image.h, sx, sy, miny, maxy, sampleSize);
*dst = MULTIPLY(A(src), opacity);
}
}
@ -1008,42 +966,24 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
}
static bool _scaledImage(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
{
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, region, opacity);
else return _rasterScaledMaskedImage(surface, image, &itransform, region, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingImage(surface, image, &itransform, region, opacity);
} else {
return _rasterScaledImage(surface, image, &itransform, region, opacity);
}
return false;
}
/************************************************************************/
/* Direct Image */
/************************************************************************/
static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectMaskedImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity)
{
TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image");
return false;
}
static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity)
{
auto h = static_cast<uint32_t>(region.max.y - region.min.y);
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method);
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto sbuffer = image.buf32 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox);
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
TVGLOG("SW_ENGINE", "Direct Matted(%d) Image [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
@ -1068,7 +1008,7 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
}
buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride;
sbuffer += image.stride;
}
//8 bits
} else if (surface->channelSize == sizeof(uint8_t)) {
@ -1090,14 +1030,14 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
}
buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride;
sbuffer += image.stride;
}
}
return true;
}
static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale image!");
@ -1105,7 +1045,7 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
}
auto dbuffer = &surface->buf32[region.min.y * surface->stride + region.min.x];
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto sbuffer = image.buf32 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox);
for (auto y = region.min.y; y < region.max.y; ++y) {
auto dst = dbuffer;
@ -1122,15 +1062,15 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
}
}
dbuffer += surface->stride;
sbuffer += image->stride;
sbuffer += image.stride;
}
return true;
}
static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity)
{
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto sbuffer = image.buf32 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox);
//32bits channels
if (surface->channelSize == sizeof(uint32_t)) {
@ -1138,17 +1078,18 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S
for (auto y = region.min.y; y < region.max.y; ++y) {
rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity);
dbuffer += surface->stride;
sbuffer += image->stride;
sbuffer += image.stride;
}
//8bits grayscale
//32 -> 8 direct converting seems an avoidable stage. maybe draw to a masking image after an intermediate scene. Can get rid of this?
} else if (surface->channelSize == sizeof(uint8_t)) {
auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image->stride) {
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride, sbuffer += image.stride) {
auto dst = dbuffer;
auto src = sbuffer;
if (opacity == 255) {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
*dst = *src + MULTIPLY(*dst, IA(*src));
*dst = A(*src) + MULTIPLY(*dst, IA(*src));
}
} else {
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
@ -1161,7 +1102,7 @@ static bool _rasterDirectImage(SwSurface* surface, const SwImage* image, const S
}
static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage& image, const SwBBox& region, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale image!");
@ -1172,7 +1113,7 @@ static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage*
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
auto csize = surface->compositor->image.channelSize;
auto alpha = surface->alpha(surface->compositor->method);
auto sbuffer = image->buf32 + (region.min.y + image->oy) * image->stride + (region.min.x + image->ox);
auto sbuffer = image.buf32 + (region.min.y + image.oy) * image.stride + (region.min.x + image.ox);
auto cbuffer = surface->compositor->image.buf8 + (region.min.y * surface->compositor->image.stride + region.min.x) * csize; //compositor buffer
auto buffer = surface->buf32 + (region.min.y * surface->stride) + region.min.x;
@ -1193,46 +1134,12 @@ static bool _rasterDirectMattedBlendingImage(SwSurface* surface, const SwImage*
}
buffer += surface->stride;
cbuffer += surface->compositor->image.stride * csize;
sbuffer += image->stride;
sbuffer += image.stride;
}
return true;
}
//Blenders for the following scenarios: [Composition / Non-Composition] * [Opaque / Translucent]
static bool _directImage(SwSurface* surface, const SwImage* image, const SwBBox& region, uint8_t opacity)
{
if (_compositing(surface)) {
if (_matting(surface)) {
if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, region, opacity);
else return _rasterDirectMattedImage(surface, image, region, opacity);
} else return _rasterDirectMaskedImage(surface, image, region, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingImage(surface, image, region, opacity);
} else {
return _rasterDirectImage(surface, image, region, opacity);
}
return false;
}
//Blenders for the following scenarios: [RLE / Whole] * [Direct / Scaled / Transformed]
static bool _rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& region, uint8_t opacity)
{
//RLE Image
if (image->rle) {
if (image->direct) return _directRleImage(surface, image, opacity);
else if (image->scaled) return _scaledRleImage(surface, image, transform, region, opacity);
else return _rasterTexmapPolygon(surface, image, transform, nullptr, opacity);
//Whole Image
} else {
if (image->direct) return _directImage(surface, image, region, opacity);
else if (image->scaled) return _scaledImage(surface, image, transform, region, opacity);
else return _rasterTexmapPolygon(surface, image, transform, &region, opacity);
}
}
/************************************************************************/
/* Rect Gradient */
/************************************************************************/
@ -1249,7 +1156,7 @@ static bool _rasterCompositeGradientMaskedRect(SwSurface* surface, const SwBBox&
fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255);
cbuffer += surface->stride;
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
}
@ -1422,7 +1329,7 @@ static bool _rasterCompositeGradientMaskedRle(SwSurface* surface, const SwRle* r
auto cmp = &cbuffer[span->y * cstride + span->x];
fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage);
}
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
}
@ -1719,6 +1626,82 @@ void rasterPremultiply(RenderSurface* surface)
}
bool rasterScaledImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
{
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedImage(surface, image, &itransform, bbox, opacity);
else return _rasterScaledMaskedImage(surface, image, &itransform, bbox, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingImage(surface, image, &itransform, bbox, opacity);
} else {
return _rasterScaledImage(surface, image, &itransform, bbox, opacity);
}
return false;
}
bool rasterDirectImage(SwSurface* surface, const SwImage& image, const SwBBox& bbox, uint8_t opacity)
{
if (_compositing(surface)) {
if (_matting(surface)) {
if (_blending(surface)) return _rasterDirectMattedBlendingImage(surface, image, bbox, opacity);
else return _rasterDirectMattedImage(surface, image, bbox, opacity);
} else return _rasterDirectMaskedImage(surface, image, bbox, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingImage(surface, image, bbox, opacity);
} else {
return _rasterDirectImage(surface, image, bbox, opacity);
}
return false;
}
bool rasterScaledRleImage(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported scaled rle image!");
return false;
}
Matrix itransform;
if (!inverse(&transform, &itransform)) return true;
if (_compositing(surface)) {
if (_matting(surface)) return _rasterScaledMattedRleImage(surface, image, &itransform, bbox, opacity);
else return _rasterScaledMaskedRleImage(surface, image, &itransform, bbox, opacity);
} else if (_blending(surface)) {
return _rasterScaledBlendingRleImage(surface, image, &itransform, bbox, opacity);
} else {
return _rasterScaledRleImage(surface, image, &itransform, bbox, opacity);
}
return false;
}
bool rasterDirectRleImage(SwSurface* surface, const SwImage& image, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale rle image!");
return false;
}
if (_compositing(surface)) {
if (_matting(surface)) return _rasterDirectMattedRleImage(surface, image, opacity);
else return _rasterDirectMaskedRleImage(surface, image, opacity);
} else if (_blending(surface)) {
return _rasterDirectBlendingRleImage(surface, image, opacity);
} else {
return _rasterDirectRleImage(surface, image, opacity);
}
return false;
}
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
{
if (!shape->fill) return false;
@ -1781,15 +1764,6 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
}
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
{
//Outside of the viewport, skip the rendering
if (bbox.max.x < 0 || bbox.max.y < 0 || bbox.min.x >= static_cast<SwCoord>(surface->w) || bbox.min.y >= static_cast<SwCoord>(surface->h)) return true;
return _rasterImage(surface, image, transform, bbox, opacity);
}
bool rasterConvertCS(RenderSurface* surface, ColorSpace to)
{
ScopedLock lock(surface->key);

View File

@ -51,94 +51,51 @@ static float dxdya, dxdyb, dudya, dvdya;
static float xa, xb, ua, va;
//Y Range exception handling
static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
static inline int32_t _modf(float v)
{
int32_t regionTop, regionBottom;
if (region) {
regionTop = region->min.y;
regionBottom = region->max.y;
} else {
regionTop = image->rle->spans->y;
regionBottom = image->rle->spans[image->rle->size - 1].y;
}
if (yStart >= regionBottom) return false;
if (yStart < regionTop) yStart = regionTop;
if (yEnd > regionBottom) yEnd = regionBottom;
return true;
return 255 - ((int(v * 256.0f)) & 255);
}
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
static bool _rasterMaskedPolygonImageSegment(SwSurface* surface, const SwImage& image, const SwBBox& region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, uint8_t dirFlag = 0)
{
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
return false;
}
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage& image, const SwBBox& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity)
{
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32;
auto sbuf = image.buf32;
auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->w);
int32_t sh = static_cast<int32_t>(image->h);
int32_t sw = static_cast<int32_t>(image.w);
int32_t sh = static_cast<int32_t>(image.h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0;
float dx, u, v, iptr;
float dx, u, v;
uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
if (!_arrange(image, region, yStart, yEnd)) return;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
} else {
span = image->rle->spans;
while (span->y < yStart) {
++span;
++spanIdx;
}
}
if (yStart < bbox.min.y) yStart = bbox.min.y;
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
y = yStart;
while (y < yEnd) {
x1 = (int32_t)_xa;
x2 = (int32_t)_xb;
if (!region) {
minx = INT32_MAX;
maxx = 0;
//one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size) {
if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
++spanIdx;
}
}
if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx;
x1 = std::max((SwCoord)_xa, bbox.min.x);
x2 = std::min((SwCoord)_xb, bbox.max.x);
//Anti-Aliasing frames
ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
if (aaSpans) {
ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
}
//Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
//Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1);
@ -149,89 +106,45 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
x = x1;
if (opacity == 255) {
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
vv = (int) v;
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
ar = _modf(u);
ab = _modf(v);
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * image->stride) + uu);
px = *(sbuf + (vv * image.stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * image.stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * image.stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru);
px = INTERPOLATE(px, px2, ar);
/* bottom right pixel */
int px3 = *(sbuf + (irv * image.stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
}
*buf = surface->blender(px, *buf, IA(px));
++buf;
//Step UV horizontally
u += _dudx;
v += _dvdx;
px = INTERPOLATE(px, px2, ab);
}
} else {
//Draw horizontal line
while (x++ < x2) {
uu = (int) u;
vv = (int) v;
auto tmp = surface->blender(px, *buf, 255);
*buf = INTERPOLATE(tmp, *buf, MULTIPLY(opacity, A(px)));
++buf;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
ar = (int)(255 * (1 - modff(u, &iptr)));
ab = (int)(255 * (1 - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
}
auto src = ALPHA_BLEND(px, opacity);
*buf = surface->blender(src, *buf, IA(src));
++buf;
//Step UV horizontally
u += _dudx;
v += _dvdx;
}
//Step UV horizontally
u += _dudx;
v += _dvdx;
}
}
@ -241,8 +154,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
_ua += _dudya;
_va += _dvdya;
if (!region && spanIdx >= image->rle->size) break;
++y;
}
xa = _xa;
@ -252,70 +163,43 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
}
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image, const SwBBox* region, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage& image, const SwBBox& bbox, int yStart, int yEnd, AASpans* aaSpans, uint8_t opacity, bool matting)
{
float _dudx = dudx, _dvdx = dvdx;
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
float _xa = xa, _xb = xb, _ua = ua, _va = va;
auto sbuf = image->buf32;
auto sbuf = image.buf32;
auto dbuf = surface->buf32;
int32_t sw = static_cast<int32_t>(image->w);
int32_t sh = static_cast<int32_t>(image->h);
int32_t sw = static_cast<int32_t>(image.w);
int32_t sh = static_cast<int32_t>(image.h);
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
int32_t vv = 0, uu = 0;
int32_t minx = INT32_MAX, maxx = 0;
float dx, u, v, iptr;
float dx, u, v;
uint32_t* buf;
SwSpan* span = nullptr; //used only when rle based.
//for matting(composition)
auto csize = matting ? surface->compositor->image.channelSize: 0;
auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
uint8_t* cmp = nullptr;
if (!_arrange(image, region, yStart, yEnd)) return;
//Loop through all lines in the segment
uint32_t spanIdx = 0;
if (region) {
minx = region->min.x;
maxx = region->max.x;
} else {
span = image->rle->spans;
while (span->y < yStart) {
++span;
++spanIdx;
}
}
if (yStart < bbox.min.y) yStart = bbox.min.y;
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
y = yStart;
while (y < yEnd) {
x1 = (int32_t)_xa;
x2 = (int32_t)_xb;
if (!region) {
minx = INT32_MAX;
maxx = 0;
//one single row, could be consisted of multiple spans.
while (span->y == y && spanIdx < image->rle->size) {
if (minx > span->x) minx = span->x;
if (maxx < span->x + span->len) maxx = span->x + span->len;
++span;
++spanIdx;
}
}
if (x1 < minx) x1 = minx;
if (x2 > maxx) x2 = maxx;
x1 = std::max((SwCoord)_xa, bbox.min.x);
x2 = std::min((SwCoord)_xb, bbox.max.x);
//Anti-Aliasing frames
ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
if (aaSpans) {
ay = y - aaSpans->yStart;
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
}
//Range allowed
if ((x2 - x1) >= 1 && (x1 < maxx) && (x2 > minx)) {
if ((x2 - x1) >= 1 && (x1 < bbox.max.x) && (x2 > bbox.min.x)) {
//Perform subtexel pre-stepping on UV
dx = 1 - (_xa - x1);
@ -334,30 +218,31 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
uu = (int) u;
vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
ar = _modf(u);
ab = _modf(v);
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * image->stride) + uu);
px = *(sbuf + (vv * image.stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru);
int px2 = *(sbuf + (vv * image.stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu);
int px2 = *(sbuf + (irv * image.stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru);
int px3 = *(sbuf + (irv * image.stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
@ -382,30 +267,31 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
uu = (int) u;
vv = (int) v;
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
if ((uint32_t) uu >= image.w || (uint32_t) vv >= image.h) continue;
ar = _modf(u);
ab = _modf(v);
ar = (int)(255.0f * (1.0f - modff(u, &iptr)));
ab = (int)(255.0f * (1.0f - modff(v, &iptr)));
iru = uu + 1;
irv = vv + 1;
px = *(sbuf + (vv * sw) + uu);
px = *(sbuf + (vv * image.stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* right pixel */
int px2 = *(sbuf + (vv * image->stride) + iru);
int px2 = *(sbuf + (vv * image.stride) + iru);
px = INTERPOLATE(px, px2, ar);
}
/* vertical interpolate */
if (irv < sh) {
/* bottom pixel */
int px2 = *(sbuf + (irv * image->stride) + uu);
int px2 = *(sbuf + (irv * image.stride) + uu);
/* horizontal interpolate */
if (iru < sw) {
/* bottom right pixel */
int px3 = *(sbuf + (irv * image->stride) + iru);
int px3 = *(sbuf + (irv * image.stride) + iru);
px2 = INTERPOLATE(px2, px3, ar);
}
px = INTERPOLATE(px, px2, ab);
@ -433,8 +319,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
_ua += _dudya;
_va += _dvdya;
if (!region && spanIdx >= image->rle->size) break;
++y;
}
xa = _xa;
@ -445,7 +329,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
/* This mapping algorithm is based on Mikael Kalms's. */
static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const SwBBox* region, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
static void _rasterPolygonImage(SwSurface* surface, const SwImage& image, const SwBBox& bbox, Polygon& polygon, AASpans* aaSpans, uint8_t opacity)
{
float x[3] = {polygon.vertex[0].pt.x, polygon.vertex[1].pt.x, polygon.vertex[2].pt.x};
float y[3] = {polygon.vertex[0].pt.y, polygon.vertex[1].pt.y, polygon.vertex[2].pt.y};
@ -506,7 +390,6 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
if (tvg::equal(y[0], y[1])) side = x[0] > x[1];
if (tvg::equal(y[1], y[2])) side = x[2] > x[1];
auto regionTop = region ? region->min.y : image->rle->spans->y; //Normal Image or Rle Image?
auto compositing = _compositing(surface); //Composition required
auto blending = _blending(surface); //Blending required
@ -525,7 +408,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible
if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
xa += (off_y * dxdya);
ua += (off_y * dudya);
va += (off_y * dvdya);
@ -535,18 +418,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
xb = x[0] + dy * dxdyb + (off_y * dxdyb);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 1);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
}
upper = true;
}
//Draw lower segment if possibly visible
if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
if (!upper) {
xa += (off_y * dxdya);
ua += (off_y * dudya);
@ -556,12 +439,12 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
dxdyb = dxdy[2];
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 2);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
}
}
//Longer edge is on the right side
@ -573,7 +456,7 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
//Draw upper segment if possibly visible
if (yi[0] < yi[1]) {
off_y = y[0] < regionTop ? (regionTop - y[0]) : 0;
off_y = y[0] < bbox.min.y ? (bbox.min.y - y[0]) : 0;
xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping
@ -586,18 +469,18 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
va = v[0] + dy * dvdya + (off_y * dvdya);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 3);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, false);
}
upper = true;
}
//Draw lower segment if possibly visible
if (yi[1] < yi[2]) {
off_y = y[1] < regionTop ? (regionTop - y[1]) : 0;
off_y = y[1] < bbox.min.y ? (bbox.min.y - y[1]) : 0;
if (!upper) xb += (off_y *dxdyb);
// Set slopes along left edge and perform subpixel pre-stepping
@ -610,25 +493,20 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
va = v[1] + dy * dvdya + (off_y * dvdya);
if (compositing) {
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4);
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 4);
} else if (blending) {
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
} else {
_rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, false);
_rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, false);
}
}
}
}
static AASpans* _AASpans(float ymin, float ymax, const SwImage* image, const SwBBox* region)
static AASpans* _AASpans(int yStart, int yEnd)
{
auto yStart = static_cast<int>(ymin);
auto yEnd = static_cast<int>(ymax);
if (!_arrange(image, region, yStart, yEnd)) return nullptr;
auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
aaSpans->yStart = yStart;
aaSpans->yEnd = yEnd;
@ -837,7 +715,7 @@ static void _calcAAEdge(AASpans *aaSpans, int32_t eidx)
}
static bool _apply(SwSurface* surface, AASpans* aaSpans)
static void _apply(SwSurface* surface, AASpans* aaSpans)
{
auto end = surface->buf32 + surface->h * surface->stride;
auto y = aaSpans->yStart;
@ -864,7 +742,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
//exceptional handling. out of memory bound.
if (dst + line->length[0] >= end) {
pos += (dst + line->length[0] - end);
pos += static_cast<int32_t>(dst + line->length[0] - end);
}
while (pos <= line->length[0]) {
@ -881,7 +759,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
pos = line->length[1];
//exceptional handling. out of memory bound.
if (dst - pos < surface->buf32) --pos;
if (dst - pos < surface->buf32) pos = static_cast<int32_t>(dst - surface->buf32);
while (pos > 0) {
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
@ -894,8 +772,6 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
free(aaSpans->lines);
free(aaSpans);
return true;
}
@ -909,23 +785,19 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
| / |
3 -- 2
*/
static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const Matrix& transform, const SwBBox* region, uint8_t opacity)
bool rasterTexmapPolygon(SwSurface* surface, const SwImage& image, const Matrix& transform, const SwBBox& bbox, uint8_t opacity)
{
if (surface->channelSize == sizeof(uint8_t)) {
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
return false;
}
//Exceptions: No dedicated drawing area?
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
/* Prepare vertices.
shift XY coordinates to match the sub-pixeling technique. */
//Prepare vertices. Shift XY coordinates to match the sub-pixeling technique.
Vertex vertices[4];
vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 0.0f}};
vertices[2] = {{float(image->w), float(image->h)}, {float(image->w), float(image->h)}};
vertices[3] = {{0.0f, float(image->h)}, {0.0f, float(image->h)}};
vertices[1] = {{float(image.w), 0.0f}, {float(image.w), 0.0f}};
vertices[2] = {{float(image.w), float(image.h)}, {float(image.w), float(image.h)}};
vertices[3] = {{0.0f, float(image.h)}, {0.0f, float(image.h)}};
float ys = FLT_MAX, ye = -1.0f;
for (int i = 0; i < 4; i++) {
@ -934,8 +806,9 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
}
auto aaSpans = _AASpans(ys, ye, image, region);
if (!aaSpans) return true;
auto yStart = std::max(static_cast<SwCoord>(ys), bbox.min.y);
auto yEnd = std::min(static_cast<SwCoord>(ye), bbox.max.y);
auto aaSpans = rightAngle(transform) ? nullptr : _AASpans(yStart, yEnd);
Polygon polygon;
@ -944,19 +817,20 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
polygon.vertex[1] = vertices[1];
polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
//Draw the second polygon
polygon.vertex[0] = vertices[1];
polygon.vertex[1] = vertices[2];
polygon.vertex[2] = vertices[3];
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
#if 0
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
}
#endif
return _apply(surface, aaSpans);
if (aaSpans) _apply(surface, aaSpans);
return true;
}

View File

@ -129,7 +129,7 @@ struct SwShapeTask : SwTask
if (updateShape) shapeReset(&shape);
if (updateFill || clipper) {
if (shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
if (!shapeGenRle(&shape, rshape, antialiasing(strokeWidth))) goto err;
if (!shapeGenRle(&shape, rshape, mpool, tid, antialiasing(strokeWidth))) goto err;
} else {
updateFill = false;
renderRegion.reset();
@ -165,8 +165,9 @@ struct SwShapeTask : SwTask
//Clip Path
for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
auto clipper = static_cast<SwTask*>(*clip);
if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip shape rle
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; //Clip stroke rle
auto clipShapeRle = shape.rle ? clipper->clip(shape.rle) : true;
auto clipStrokeRle = shape.strokeRle ? clipper->clip(shape.strokeRle) : true;
if (!clipShapeRle && !clipStrokeRle) goto err;
}
bbox = renderRegion; //sync
@ -220,7 +221,7 @@ struct SwImageTask : SwTask
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
if (clips.count > 0) {
if (!imageGenRle(&image, bbox, false)) goto end;
if (!imageGenRle(&image, bbox, mpool, tid, false)) goto end;
if (image.rle) {
//Clear current task memorypool here if the clippers would use the same memory pool
imageDelOutline(&image, mpool, tid);
@ -403,7 +404,32 @@ bool SwRenderer::renderImage(RenderData data)
if (task->opacity == 0) return true;
return rasterImage(surface, &task->image, task->transform, task->bbox, task->opacity);
//Outside of the viewport, skip the rendering
auto& bbox = task->bbox;
if (bbox.max.x <= bbox.min.x || bbox.max.y <= bbox.min.y || bbox.min.x >= int32_t(surface->w) || bbox.min.y >= int32_t(surface->h)) return true;
auto& image = task->image;
//RLE Image
if (image.rle) {
if (image.direct) return rasterDirectRleImage(surface, image, task->opacity);
else if (image.scaled) return rasterScaledRleImage(surface, image, task->transform, bbox, task->opacity);
else {
//create a intermediate buffer for rle clipping
auto cmp = request(sizeof(pixel_t), false);
cmp->compositor->method = CompositeMethod::None;
cmp->compositor->valid = true;
cmp->compositor->image.rle = image.rle;
rasterClear(cmp, (uint32_t)bbox.min.x, (uint32_t)bbox.min.y, (uint32_t)(bbox.max.x - bbox.min.x), (uint32_t)(bbox.max.y - bbox.min.y), 0);
rasterTexmapPolygon(cmp, image, task->transform, bbox, 255);
return rasterDirectRleImage(surface, cmp->compositor->image, task->opacity);
}
//Whole Image
} else {
if (image.direct) return rasterDirectImage(surface, image, bbox, task->opacity);
else if (image.scaled) return rasterScaledImage(surface, image, task->transform, bbox, task->opacity);
else return rasterTexmapPolygon(surface, image, task->transform, bbox, task->opacity);
}
}
@ -638,8 +664,7 @@ bool SwRenderer::endComposite(RenderCompositor* cmp)
//Default is alpha blending
if (p->method == CompositeMethod::None) {
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
return rasterDirectImage(surface, p->image, p->bbox, p->opacity);
}
return true;
@ -678,13 +703,16 @@ bool SwRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, bool
return false;
}
//TODO: Support grayscale effects.
if (p->recoverSfc->channelSize != sizeof(uint32_t)) direct = false;
switch (effect->type) {
case SceneEffect::GaussianBlur: {
return effectGaussianBlur(p, request(surface->channelSize, true), static_cast<const RenderEffectGaussianBlur*>(effect));
}
case SceneEffect::DropShadow: {
auto cmp1 = request(surface->channelSize, true);
cmp1->compositor->valid = false;
cmp1->compositor->valid = false; //prevent a conflict with cmp2 request.
auto cmp2 = request(surface->channelSize, true);
SwSurface* surfaces[] = {cmp1, cmp2};
auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), direct);
@ -726,8 +754,7 @@ void SwRenderer::dispose(RenderData data)
void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
{
if (!surface) return task;
if (flags == RenderUpdateFlag::None) return task;
if (!surface || (transform.e11 == 0.0f && transform.e12 == 0.0f) || (transform.e21 == 0.0f && transform.e22 == 0.0f)) return task; //invalid
//TODO: Failed threading them. It would be better if it's possible.
//See: https://github.com/thorvg/thorvg/issues/1409
@ -757,7 +784,16 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
tasks.push(task);
}
TaskScheduler::request(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.
if (flags & RenderUpdateFlag::Clip) {
for (uint32_t i = 0; i < clips.count; ++i) {
static_cast<SwTask*>(clips[i])->done();
}
}
if (flags) TaskScheduler::request(task);
return task;
}
@ -767,10 +803,11 @@ RenderData SwRenderer::prepare(RenderSurface* surface, RenderData data, const Ma
{
//prepare task
auto task = static_cast<SwImageTask*>(data);
if (!task) task = new SwImageTask;
else task->done();
task->source = surface;
if (task) task->done();
else {
task = new SwImageTask;
task->source = surface;
}
return prepareCommon(task, transform, clips, opacity, flags);
}
@ -780,10 +817,12 @@ RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const
{
//prepare task
auto task = static_cast<SwShapeTask*>(data);
if (!task) task = new SwShapeTask;
else task->done();
if (task) task->done();
else {
task = new SwShapeTask;
task->rshape = &rshape;
}
task->rshape = &rshape;
task->clipper = clipper;
return prepareCommon(task, transform, clips, opacity, flags);

View File

@ -55,6 +55,8 @@ public:
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
bool mempool(bool shared);
SwSurface* request(int channelSize, bool square);
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
bool endComposite(RenderCompositor* cmp) override;
@ -80,7 +82,6 @@ private:
SwRenderer();
~SwRenderer();
SwSurface* request(int channelSize, bool square);
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
};

View File

@ -199,21 +199,11 @@
constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
using Area = long;
struct Band
{
SwCoord min, max;
};
struct Cell
{
SwCoord x;
SwCoord cover;
Area area;
Cell *next;
};
struct RleWorker
{
SwRle* rle;
@ -227,7 +217,7 @@ struct RleWorker
Area area;
SwCoord cover;
Cell* cells;
SwCell* cells;
ptrdiff_t maxCells;
ptrdiff_t cellsCnt;
@ -242,11 +232,11 @@ struct RleWorker
int bandSize;
int bandShoot;
void* buffer;
long bufferSize;
SwCell* buffer;
uint32_t bufferSize;
Cell** yCells;
SwCoord yCnt;
SwCell** yCells;
int32_t yCnt;
bool invalid;
bool antiAlias;
@ -282,12 +272,11 @@ static inline SwCoord SUBPIXELS(const SwCoord x)
return SwCoord(((unsigned long) x) << PIXEL_BITS);
}
/*
* Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
* algorithm. We use alpha = 1, beta = 3/8, giving us results with a
* largest error less than 7% compared to the exact value.
*/
static inline SwCoord HYPOT(SwPoint pt)
// Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' algorithm.
// We use alpha = 1, beta = 3/8, giving us results with a largest error
// less than 7% compared to the exact value.
static inline int32_t HYPOT(SwPoint pt)
{
if (pt.x < 0) pt.x = -pt.x;
if (pt.y < 0) pt.y = -pt.y;
@ -295,7 +284,17 @@ static inline SwCoord HYPOT(SwPoint pt)
}
static void _horizLine(RleWorker& rw, SwCoord x, SwCoord y, SwCoord area, SwCoord aCount)
// Used to prevent integer overflow when calculating the distance between points.
// This function uses 64-bit arithmetic to safely compute the difference between coordinates.
static inline uint32_t SAFE_HYPOT(SwPoint& pt1, SwPoint& pt2)
{
auto x = uint32_t(abs(int64_t(pt1.x) - int64_t(pt2.x)));
auto y = uint32_t(abs(int64_t(pt1.y) - int64_t(pt2.y)));
return (x > y) ? (x + (3 * y >> 3)) : (y + (3 * x >> 3));
}
static void _horizLine(RleWorker& rw, int32_t x, int32_t y, int32_t area, int32_t aCount)
{
x += rw.cellMin.x;
y += rw.cellMin.y;
@ -401,7 +400,7 @@ static void _sweep(RleWorker& rw)
}
static Cell* _findCell(RleWorker& rw)
static SwCell* _findCell(RleWorker& rw)
{
auto x = rw.cellPos.x;
if (x > rw.cellXCnt) x = rw.cellXCnt;
@ -409,7 +408,7 @@ static Cell* _findCell(RleWorker& rw)
auto pcell = &rw.yCells[rw.cellPos.y];
while(true) {
Cell* cell = *pcell;
auto cell = *pcell;
if (!cell || cell->x > x) break;
if (cell->x == x) return cell;
pcell = &cell->next;
@ -529,14 +528,12 @@ static bool _lineTo(RleWorker& rw, const SwPoint& to)
line[1] = rw.pos;
while (true) {
auto diff = line[0] - line[1];
auto L = HYPOT(diff);
if (L > SHRT_MAX) {
if (SAFE_HYPOT(line[0], line[1]) > SHRT_MAX) {
mathSplitLine(line);
++line;
continue;
}
auto diff = line[0] - line[1];
e1 = TRUNC(line[1]);
e2 = TRUNC(line[0]);
@ -864,21 +861,25 @@ void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
/* External Class Implementation */
/************************************************************************/
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, bool antiAlias)
SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias)
{
if (!outline) return nullptr;
constexpr auto RENDER_POOL_SIZE = 16384L;
constexpr auto BAND_SIZE = 40;
//TODO: We can preserve several static workers in advance
RleWorker rw;
Cell buffer[RENDER_POOL_SIZE / sizeof(Cell)];
auto cellPool = mpoolReqCellPool(mpool, tid);
auto reqSize = uint32_t(std::max(renderRegion.w(), renderRegion.h()) * 0.75f) * sizeof(SwCell); //experimental decision
// grow by 1.25x and align to multiple of sizeof(SwCell)
if (reqSize > cellPool->size) {
cellPool->size = ((reqSize + (reqSize >> 2)) / sizeof(SwCell)) * sizeof(SwCell);
free(cellPool->buffer);
cellPool->buffer = (SwCell*)malloc(cellPool->size);
}
//Init Cells
rw.buffer = buffer;
rw.bufferSize = sizeof(buffer);
rw.yCells = reinterpret_cast<Cell**>(buffer);
rw.buffer = cellPool->buffer;
rw.bufferSize = cellPool->size;
rw.yCells = reinterpret_cast<SwCell**>(cellPool->buffer);
rw.cells = nullptr;
rw.maxCells = 0;
rw.cellsCnt = 0;
@ -890,7 +891,7 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
rw.cellXCnt = rw.cellMax.x - rw.cellMin.x;
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
rw.outline = const_cast<SwOutline*>(outline);
rw.bandSize = rw.bufferSize / (sizeof(Cell) * 2); //bandSize: 256
rw.bandSize = rw.bufferSize / (sizeof(SwCell) * 2);
rw.bandShoot = 0;
rw.antiAlias = antiAlias;
@ -898,6 +899,8 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
else rw.rle = rle;
//Generate RLE
constexpr auto BAND_SIZE = 40;
Band bands[BAND_SIZE];
Band* band;
@ -920,16 +923,16 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
band = bands;
while (band >= bands) {
rw.yCells = static_cast<Cell**>(rw.buffer);
rw.yCells = reinterpret_cast<SwCell**>(rw.buffer);
rw.yCnt = band->max - band->min;
int cellStart = sizeof(Cell*) * (int)rw.yCnt;
int cellMod = cellStart % sizeof(Cell);
int cellStart = sizeof(SwCell*) * (int)rw.yCnt;
int cellMod = cellStart % sizeof(SwCell);
if (cellMod > 0) cellStart += sizeof(Cell) - cellMod;
if (cellMod > 0) cellStart += sizeof(SwCell) - cellMod;
auto cellsMax = reinterpret_cast<Cell*>((char*)rw.buffer + rw.bufferSize);
rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
auto cellsMax = reinterpret_cast<SwCell*>((char*)rw.buffer + rw.bufferSize);
rw.cells = reinterpret_cast<SwCell*>((char*)rw.buffer + cellStart);
if (rw.cells >= cellsMax) goto reduce_bands;

View File

@ -368,7 +368,8 @@ static SwOutline* _genDashOutline(const RenderShape* rshape, const Matrix& trans
}
}
dash.outline = mpoolReqDashOutline(mpool, tid);
mpoolRetOutline(mpool, tid); //retreive the outline cache and use it for dash outline.
dash.outline = mpoolReqOutline(mpool, tid);
//must begin with moveTo
if (cmds[0] == PathCommand::MoveTo) {
@ -516,7 +517,7 @@ bool shapePrepared(const SwShape* shape)
}
bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool antiAlias)
bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, SwMpool* mpool, unsigned tid, bool antiAlias)
{
//FIXME: Should we draw it?
//Case: Stroke Line
@ -526,7 +527,7 @@ bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool anti
if (shape->fastTrack) return true;
//Case B: Normal Shape RLE Drawing
if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, mpool, tid, antiAlias))) return true;
return false;
}
@ -587,17 +588,13 @@ void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& t
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
{
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
//Trimming & Normal style
} else {
if (!shape->outline) {
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
@ -605,24 +602,11 @@ bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix&
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);
if (!strokeParseOutline(shape->stroke, *shapeOutline)) return false;
auto strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
auto ret = mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false);
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, mpool, tid, true);
mpoolRetStrokeOutline(mpool, tid);
return ret;
}

View File

@ -56,8 +56,6 @@ using namespace tvg;
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

View File

@ -41,97 +41,88 @@
}
static Result _clipRect(RenderMethod* renderer, const Point* pts, const Matrix& pm, const Matrix& rm, RenderRegion& before)
static bool _clipRect(RenderMethod* renderer, const Point* pts, const Matrix& m, RenderRegion& before)
{
//sorting
Point tmp[4];
Point min = {FLT_MAX, FLT_MAX};
Point max = {0.0f, 0.0f};
Point c[4]; //corners
for (int i = 0; i < 4; ++i) {
tmp[i] = pts[i];
tmp[i] *= rm;
tmp[i] *= pm;
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;
c[i] = pts[i] * m;
}
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;
auto pointInConvexQuad = [](const Point& p, const Point* quad) {
auto sign = [](const Point& p1, const Point& p2, const Point& p3) {
return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
};
auto b1 = sign(p, quad[0], quad[1]) < 0.0f;
auto b2 = sign(p, quad[1], quad[2]) < 0.0f;
auto b3 = sign(p, quad[2], quad[3]) < 0.0f;
auto b4 = sign(p, quad[3], quad[0]) < 0.0f;
return ((b1 == b2) && (b2 == b3) && (b3 == b4));
};
if (!pointInConvexQuad({float(before.x), float(before.y)}, c)) return false;
if (!pointInConvexQuad({float(before.x + before.w), float(before.y)}, c)) return false;
if (!pointInConvexQuad({float(before.x + before.w), float(before.y + before.h)}, c)) return false;
if (!pointInConvexQuad({float(before.x), float(before.y + before.h)}, c)) return false;
//same viewport
return true;
}
static Result _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Matrix& pm, RenderRegion& before)
static bool _compFastTrack(RenderMethod* renderer, Paint* cmpTarget, const Matrix& pm, 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);
//Trimming likely makes the shape non-rectangular
if (P(shape)->rs.strokeTrim()) return false;
//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;
auto& rm = P(cmpTarget)->transform();
//No rectangle format
if (ptsCnt != 4) return false;
//No rotation and no skewing, still can try out clipping the rect region.
auto tryClip = false;
if ((!rightAngle(pm) || skewed(pm))) tryClip = true;
if ((!rightAngle(rm) || skewed(rm))) tryClip = true;
if (tryClip) return _clipRect(renderer, pts, pm, rm, before);
auto tm = pm * cmpTarget->transform();
//Perpendicular Rectangle?
auto pt1 = pts + 0;
auto pt2 = pts + 1;
auto pt3 = pts + 2;
auto pt4 = pts + 3;
if (rightAngle(tm) && !skewed(tm)) {
auto pt1 = pts + 0;
auto pt2 = pts + 1;
auto pt3 = pts + 2;
auto pt4 = pts + 3;
if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) ||
(tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) {
if ((tvg::equal(pt1->x, pt2->x) && tvg::equal(pt2->y, pt3->y) && tvg::equal(pt3->x, pt4->x) && tvg::equal(pt1->y, pt4->y)) ||
(tvg::equal(pt2->x, pt3->x) && tvg::equal(pt1->y, pt2->y) && tvg::equal(pt1->x, pt4->x) && tvg::equal(pt3->y, pt4->y))) {
RenderRegion after;
RenderRegion after;
auto v1 = *pt1;
auto v2 = *pt3;
v1 *= rm;
v2 *= rm;
v1 *= pm;
v2 *= pm;
auto v1 = *pt1;
auto v2 = *pt3;
v1 *= tm;
v2 *= tm;
//sorting
if (v1.x > v2.x) std::swap(v1.x, v2.x);
if (v1.y > v2.y) std::swap(v1.y, v2.y);
//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));
after.x = static_cast<int32_t>(nearbyint(v1.x));
after.y = static_cast<int32_t>(nearbyint(v1.y));
after.w = static_cast<int32_t>(nearbyint(v2.x)) - after.x;
after.h = static_cast<int32_t>(nearbyint(v2.y)) - after.y;
if (after.w < 0) after.w = 0;
if (after.h < 0) after.h = 0;
if (after.w < 0) after.w = 0;
if (after.h < 0) after.h = 0;
after.intersect(before);
renderer->viewport(after);
return Result::Success;
after.intersect(before);
renderer->viewport(after);
return true;
}
}
return Result::InsufficientCondition;
return _clipRect(renderer, pts, tm, before);
}
@ -214,8 +205,12 @@ bool Paint::Impl::render(RenderMethod* renderer)
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;
auto cData = compData;
while (cData) {
if (MASK_REGION_MERGING(cData->method)) region.add(P(cData->target)->bounds(renderer));
if (region.w == 0 || region.h == 0) return true;
cData = P(cData->target)->compData;
}
cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method), CompositionFlag::Masking);
if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) {
compData->target->pImpl->render(renderer);
@ -246,7 +241,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
/* 1. Composition Pre Processing */
RenderData trd = nullptr; //composite target render data
RenderRegion viewport;
Result compFastTrack = Result::InsufficientCondition;
auto compFastTrack = false;
if (compData) {
auto target = compData->target;
@ -263,13 +258,13 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
if (!shape->fill() && !(PP(shape)->compData)) {
if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) {
viewport = renderer->viewport();
if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
P(target)->ctxFlag |= ContextFlag::FastTrack;
if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport))) {
P(target)->ctxFlag |= ContextFlag::FastTrack;
}
}
}
}
if (compFastTrack == Result::InsufficientCondition) {
if (!compFastTrack) {
trd = P(target)->update(renderer, pm, clips, 255, pFlag, false);
}
}
@ -282,13 +277,12 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
viewport = renderer->viewport();
/* TODO: Intersect the clipper's clipper, if both are FastTrack.
Update the subsequent clipper first and check its ctxFlag. */
if (!pclip->clipper && _compFastTrack(renderer, this->clipper, pm, viewport) == Result::Success) {
if (!pclip->clipper && static_cast<Shape*>(this->clipper)->strokeWidth() == 0.0f && _compFastTrack(renderer, this->clipper, pm, viewport)) {
pclip->ctxFlag |= ContextFlag::FastTrack;
compFastTrack = Result::Success;
compFastTrack = true;
} else {
trd = pclip->update(renderer, pm, clips, 255, pFlag, true);
clips.push(trd);
compFastTrack = Result::InsufficientCondition;
}
}
@ -303,7 +297,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
/* 4. Composition Post Processing */
if (compFastTrack == Result::Success) renderer->viewport(viewport);
if (compFastTrack) renderer->viewport(viewport);
else if (this->clipper) clips.pop();
return rd;

View File

@ -74,9 +74,9 @@ namespace tvg
} tr;
RenderUpdateFlag renderFlag = RenderUpdateFlag::None;
BlendMethod blendMethod;
uint16_t refCnt = 0; //reference count
uint8_t ctxFlag;
uint8_t opacity;
uint8_t refCnt = 0; //reference count
Impl(Paint* pnt) : paint(pnt)
{
@ -95,7 +95,6 @@ namespace tvg
uint8_t ref()
{
if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
return ++refCnt;
}

View File

@ -97,10 +97,11 @@ struct Scene::Impl
//Half translucent requires intermediate composition.
if (opacity == 255) return compFlag;
//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()->type() == Type::Shape) return compFlag;
//Only shape or picture may not require composition.
if (paints.size() == 1) {
auto type = paints.front()->type();
if (type == Type::Shape || type == Type::Picture) return compFlag;
}
compFlag |= CompositionFlag::Opacity;
@ -244,7 +245,40 @@ struct Scene::Impl
dup->paints.push_back(cdup);
}
if (effects) TVGERR("RENDERER", "TODO: Duplicate Effects?");
if (effects) {
dup->effects = new Array<RenderEffect*>;
for (auto p = effects->begin(); p < effects->end(); ++p) {
RenderEffect* ret = nullptr;
switch ((*p)->type) {
case SceneEffect::GaussianBlur: {
ret = new RenderEffectGaussianBlur(*(RenderEffectGaussianBlur*)(*p));
break;
}
case SceneEffect::DropShadow: {
ret = new RenderEffectDropShadow(*(RenderEffectDropShadow*)(*p));
break;
}
case SceneEffect::Fill: {
ret = new RenderEffectFill(*(RenderEffectFill*)(*p));
break;
}
case SceneEffect::Tint: {
ret = new RenderEffectTint(*(RenderEffectTint*)(*p));
break;
}
case SceneEffect::Tritone: {
ret = new RenderEffectTritone(*(RenderEffectTritone*)(*p));
break;
}
default: break;
}
if (ret) {
ret->rd = nullptr;
ret->valid = false;
dup->effects->push(ret);
}
}
}
return scene;
}

View File

@ -40,10 +40,7 @@ struct Text::Impl
bool italic = false;
bool changed = false;
Impl(Text* p) : paint(p), shape(Shape::gen().release())
{
shape->fill(FillRule::EvenOdd);
}
Impl(Text* p) : paint(p), shape(Shape::gen().release()) {}
~Impl()
{

View File

@ -1,6 +1,6 @@
#!/bin/bash -e
VERSION=0.15.13
VERSION=0.15.16
# Uncomment and set a git hash to use specific commit instead of tag.
#GIT_COMMIT=