mirror of https://github.com/godotengine/godot
Merge pull request #109651 from Chubercik/thorvg-0.15.14
thorvg: Update to 0.15.16
This commit is contained in:
commit
36f07362d4
|
|
@ -1062,7 +1062,7 @@ Files extracted from upstream source:
|
||||||
## thorvg
|
## thorvg
|
||||||
|
|
||||||
- Upstream: https://github.com/thorvg/thorvg
|
- Upstream: https://github.com/thorvg/thorvg
|
||||||
- Version: 0.15.13 (c597365b99f27cb46e2a5ac2942da45bb73d5a55, 2025)
|
- Version: 0.15.16 (e15069de7afcc5e853edf1561e69d9b8383e2c6c, 2025)
|
||||||
- License: MIT
|
- License: MIT
|
||||||
|
|
||||||
Files extracted from upstream source:
|
Files extracted from upstream source:
|
||||||
|
|
|
||||||
|
|
@ -15,5 +15,5 @@
|
||||||
// For internal debugging:
|
// For internal debugging:
|
||||||
//#define THORVG_LOG_ENABLED
|
//#define THORVG_LOG_ENABLED
|
||||||
|
|
||||||
#define THORVG_VERSION_STRING "0.15.13"
|
#define THORVG_VERSION_STRING "0.15.16"
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -260,10 +260,14 @@ enum class Type : uint8_t
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A data structure representing a point in two-dimensional space.
|
* @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
|
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.
|
* @return The class type ID of the Paint instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
virtual Type type() const noexcept = 0;
|
virtual Type type() const noexcept = 0;
|
||||||
|
|
||||||
|
|
@ -462,7 +466,7 @@ public:
|
||||||
*
|
*
|
||||||
* This is reserved to specify an paint instance in a scene.
|
* This is reserved to specify an paint instance in a scene.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
uint32_t id = 0;
|
uint32_t id = 0;
|
||||||
|
|
||||||
|
|
@ -568,7 +572,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return The class type ID of the Fill instance.
|
* @return The class type ID of the Fill instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
virtual Type type() const noexcept = 0;
|
virtual Type type() const noexcept = 0;
|
||||||
|
|
||||||
|
|
@ -758,7 +762,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return The class type ID of the LinearGradient instance.
|
* @return The class type ID of the LinearGradient instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Type type() const noexcept override;
|
Type type() const noexcept override;
|
||||||
|
|
||||||
|
|
@ -823,7 +827,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return The class type ID of the LinearGradient instance.
|
* @return The class type ID of the LinearGradient instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Type type() const noexcept override;
|
Type type() const noexcept override;
|
||||||
|
|
||||||
|
|
@ -1223,7 +1227,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return The class type ID of the Shape instance.
|
* @return The class type ID of the Shape instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Type type() const noexcept override;
|
Type type() const noexcept override;
|
||||||
|
|
||||||
|
|
@ -1361,7 +1365,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return The class type ID of the Picture instance.
|
* @return The class type ID of the Picture instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Type type() const noexcept override;
|
Type type() const noexcept override;
|
||||||
|
|
||||||
|
|
@ -1461,7 +1465,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return The class type ID of the Scene instance.
|
* @return The class type ID of the Scene instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Type type() const noexcept override;
|
Type type() const noexcept override;
|
||||||
|
|
||||||
|
|
@ -1493,13 +1497,22 @@ public:
|
||||||
* It sets the font name, size and optionally the style.
|
* 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.
|
* @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] 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'.
|
* @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.
|
* 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.
|
* @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;
|
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.
|
* @return The class type ID of the Text instance.
|
||||||
*
|
*
|
||||||
* @since Experimental API
|
* @note Experimental API
|
||||||
*/
|
*/
|
||||||
Type type() const noexcept override;
|
Type type() const noexcept override;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,10 +85,17 @@ bool identity(const Matrix* m);
|
||||||
Matrix operator*(const Matrix& lhs, const Matrix& rhs);
|
Matrix operator*(const Matrix& lhs, const Matrix& rhs);
|
||||||
bool 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)
|
static inline bool rightAngle(const Matrix& m)
|
||||||
{
|
{
|
||||||
auto radian = fabsf(tvg::atan2(m.e21, m.e11));
|
auto radian = tvg::radian(m);
|
||||||
if (radian < FLOAT_EPSILON || tvg::equal(radian, MATH_PI2) || tvg::equal(radian, MATH_PI)) return true;
|
if (tvg::zero(radian) || tvg::zero(radian - MATH_PI2) || tvg::zero(radian - MATH_PI)) return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -230,11 +230,12 @@ char* strAppend(char* lhs, const char* rhs, size_t n)
|
||||||
|
|
||||||
char* strDirname(const char* path)
|
char* strDirname(const char* path)
|
||||||
{
|
{
|
||||||
const char *ptr = strrchr(path, '/');
|
auto ptr = strrchr(path, '/');
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (ptr) ptr = strrchr(ptr + 1, '\\');
|
auto ptr2 = strrchr(ptr ? ptr : path, '\\');
|
||||||
|
if (ptr2) ptr = ptr2;
|
||||||
#endif
|
#endif
|
||||||
int len = int(ptr + 1 - path); // +1 to include '/'
|
auto len = ptr ? size_t(ptr - path + 1) : SIZE_MAX;
|
||||||
return strDuplicate(path, len);
|
return strDuplicate(path, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -393,8 +393,8 @@ static char* _idFromUrl(const char* url)
|
||||||
++open;
|
++open;
|
||||||
--close;
|
--close;
|
||||||
|
|
||||||
//trim the rest of the spaces if any
|
//trim the rest of the spaces and the quote marks if any
|
||||||
while (open < close && *close == ' ') --close;
|
while (open < close && (*close == ' ' || *close == '\'' || *close == '\"')) --close;
|
||||||
|
|
||||||
//quick verification
|
//quick verification
|
||||||
for (auto id = open; id < close; id++) {
|
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);
|
||||||
static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
|
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);
|
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
|
static constexpr struct
|
||||||
{
|
{
|
||||||
const char* tag;
|
const char* tag;
|
||||||
|
|
@ -2149,17 +2129,30 @@ static bool _attrParseUseNode(void* data, const char* key, const char* value)
|
||||||
nodeFrom = _findNodeById(defs, id);
|
nodeFrom = _findNodeById(defs, id);
|
||||||
if (nodeFrom) {
|
if (nodeFrom) {
|
||||||
if (!_findParentById(node, id, loader->doc)) {
|
if (!_findParentById(node, id, loader->doc)) {
|
||||||
_cloneNode(nodeFrom, node, 0);
|
//Check if none of nodeFrom's children are in the cloneNodes list
|
||||||
if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
|
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 {
|
} else {
|
||||||
TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id);
|
TVGLOG("SVG", "%s is ancestor element. This reference is invalid.", id);
|
||||||
|
free(id);
|
||||||
}
|
}
|
||||||
free(id);
|
|
||||||
} else {
|
} else {
|
||||||
//some svg export software include <defs> element at the end of the file
|
//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
|
//if so the 'from' element won't be found now and we have to repeat finding
|
||||||
//after the whole file is parsed
|
//after the whole file is parsed
|
||||||
_postpone(loader->cloneNodes, node, id);
|
loader->cloneNodes.back(new((SvgNodeIdPair*)malloc(sizeof(SvgNodeIdPair))) SvgNodeIdPair(node, id));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return _attrParseGNode(data, key, value);
|
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->front();
|
||||||
auto nodeIdPair = (*cloneNodes)[i];
|
while (nodeIdPair) {
|
||||||
auto defs = _getDefsNode(nodeIdPair.node);
|
if (!_findParentById(nodeIdPair->node, nodeIdPair->id, doc)) {
|
||||||
auto nodeFrom = _findNodeById(defs, nodeIdPair.id);
|
//Check if none of nodeFrom's children are in the cloneNodes list
|
||||||
if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair.id);
|
auto postpone = false;
|
||||||
if (!_findParentById(nodeIdPair.node, nodeIdPair.id, doc)) {
|
auto nodeFrom = _findNodeById(_getDefsNode(nodeIdPair->node), nodeIdPair->id);
|
||||||
_cloneNode(nodeFrom, nodeIdPair.node, 0);
|
if (!nodeFrom) nodeFrom = _findNodeById(doc, nodeIdPair->id);
|
||||||
if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
|
if (nodeFrom) {
|
||||||
nodeIdPair.node->node.use.symbol = 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 {
|
} 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.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle);
|
||||||
if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, 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);
|
_updateComposite(loaderData.doc, loaderData.doc);
|
||||||
if (defs) _updateComposite(loaderData.doc, defs);
|
if (defs) _updateComposite(loaderData.doc, defs);
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "tvgCommon.h"
|
#include "tvgCommon.h"
|
||||||
#include "tvgArray.h"
|
#include "tvgArray.h"
|
||||||
|
#include "tvgInlist.h"
|
||||||
|
|
||||||
struct SvgNode;
|
struct SvgNode;
|
||||||
struct SvgStyleGradient;
|
struct SvgStyleGradient;
|
||||||
|
|
@ -551,6 +552,8 @@ struct SvgParser
|
||||||
|
|
||||||
struct SvgNodeIdPair
|
struct SvgNodeIdPair
|
||||||
{
|
{
|
||||||
|
INLIST_ITEM(SvgNodeIdPair);
|
||||||
|
SvgNodeIdPair(SvgNode* n, char* i) : node{n}, id{i} {}
|
||||||
SvgNode* node;
|
SvgNode* node;
|
||||||
char *id;
|
char *id;
|
||||||
};
|
};
|
||||||
|
|
@ -579,7 +582,7 @@ struct SvgLoaderData
|
||||||
Array<SvgStyleGradient*> gradients;
|
Array<SvgStyleGradient*> gradients;
|
||||||
Array<SvgStyleGradient*> gradientStack; //For stops
|
Array<SvgStyleGradient*> gradientStack; //For stops
|
||||||
SvgParser* svgParse = nullptr;
|
SvgParser* svgParse = nullptr;
|
||||||
Array<SvgNodeIdPair> cloneNodes;
|
Inlist<SvgNodeIdPair> cloneNodes;
|
||||||
Array<SvgNodeIdPair> nodesToStyle;
|
Array<SvgNodeIdPair> nodesToStyle;
|
||||||
Array<char*> images; //embedded images
|
Array<char*> images; //embedded images
|
||||||
Array<FontFace> fonts;
|
Array<FontFace> fonts;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@
|
||||||
|
|
||||||
using SwCoord = signed long;
|
using SwCoord = signed long;
|
||||||
using SwFixed = signed long long;
|
using SwFixed = signed long long;
|
||||||
|
#define SW_COLOR_TABLE 1024
|
||||||
|
|
||||||
|
|
||||||
static inline float TO_FLOAT(SwCoord val)
|
static inline float TO_FLOAT(SwCoord val)
|
||||||
|
|
@ -128,6 +129,19 @@ struct SwBBox
|
||||||
{
|
{
|
||||||
min.x = min.y = max.x = max.y = 0;
|
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
|
struct SwFill
|
||||||
|
|
@ -150,7 +164,7 @@ struct SwFill
|
||||||
SwRadial radial;
|
SwRadial radial;
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t* ctable;
|
uint32_t ctable[SW_COLOR_TABLE];
|
||||||
FillSpread spread;
|
FillSpread spread;
|
||||||
|
|
||||||
bool solid = false; //solid color fill with the last color from colorStops
|
bool solid = false; //solid color fill with the last color from colorStops
|
||||||
|
|
@ -269,7 +283,7 @@ struct SwSurface : RenderSurface
|
||||||
blender = rhs->blender;
|
blender = rhs->blender;
|
||||||
compositor = rhs->compositor;
|
compositor = rhs->compositor;
|
||||||
blendMethod = rhs->blendMethod;
|
blendMethod = rhs->blendMethod;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SwCompositor : RenderCompositor
|
struct SwCompositor : RenderCompositor
|
||||||
|
|
@ -281,11 +295,22 @@ struct SwCompositor : RenderCompositor
|
||||||
bool valid;
|
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
|
struct SwMpool
|
||||||
{
|
{
|
||||||
SwOutline* outline;
|
SwOutline* outline;
|
||||||
SwOutline* strokeOutline;
|
SwOutline* strokeOutline;
|
||||||
SwOutline* dashOutline;
|
SwCellPool* cellPool;
|
||||||
unsigned allocSize;
|
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)
|
static inline uint32_t opBlendSoftLight(uint32_t s, uint32_t d, TVG_UNUSED uint8_t a)
|
||||||
{
|
{
|
||||||
//(255 - 2 * s) * (d * d) + (2 * s * b)
|
//(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 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))) + MULTIPLY(std::min(255, 2 * C2(s)), C2(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))) + MULTIPLY(std::min(255, 2 * C3(s)), C3(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);
|
return JOIN(255, c1, c2, c3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -506,7 +531,7 @@ bool mathClipBBox(const SwBBox& clipper, SwBBox& clippee);
|
||||||
void shapeReset(SwShape* shape);
|
void shapeReset(SwShape* shape);
|
||||||
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
bool shapePrepare(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
|
||||||
bool shapePrepared(const SwShape* shape);
|
bool 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 shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
|
||||||
void shapeResetStroke(SwShape* shape, const RenderShape* rshape, const Matrix& transform);
|
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);
|
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);
|
void strokeFree(SwStroke* stroke);
|
||||||
|
|
||||||
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
bool imagePrepare(SwImage* image, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
|
||||||
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, bool antiAlias);
|
bool imageGenRle(SwImage* image, const SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool antiAlias);
|
||||||
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
|
void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
|
||||||
void imageReset(SwImage* image);
|
void imageReset(SwImage* image);
|
||||||
void imageFree(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, 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.
|
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);
|
SwRle* rleRender(const SwBBox* bbox);
|
||||||
void rleFree(SwRle* rle);
|
void rleFree(SwRle* rle);
|
||||||
void rleReset(SwRle* rle);
|
void rleReset(SwRle* rle);
|
||||||
|
|
@ -565,11 +590,16 @@ SwOutline* mpoolReqStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||||
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
|
void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx);
|
||||||
SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
|
SwOutline* mpoolReqDashOutline(SwMpool* mpool, unsigned idx);
|
||||||
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
|
void mpoolRetDashOutline(SwMpool* mpool, unsigned idx);
|
||||||
|
SwCellPool* mpoolReqCellPool(SwMpool* mpool, unsigned idx);
|
||||||
|
|
||||||
bool rasterCompositor(SwSurface* surface);
|
bool rasterCompositor(SwSurface* surface);
|
||||||
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity);
|
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 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 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 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);
|
bool rasterClear(SwSurface* surface, uint32_t x, uint32_t y, uint32_t w, uint32_t h, pixel_t val = 0);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
|
|
||||||
#define RADIAL_A_THRESHOLD 0.0005f
|
#define RADIAL_A_THRESHOLD 0.0005f
|
||||||
#define GRADIENT_STOP_SIZE 1024
|
|
||||||
#define FIXPT_BITS 8
|
#define FIXPT_BITS 8
|
||||||
#define FIXPT_SIZE (1<<FIXPT_BITS)
|
#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;
|
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 rgbaEnd = _alphaUnblend(fill->ctable[i]);
|
||||||
auto rgbaBegin = _alphaUnblend(fill->ctable[begin]);
|
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);
|
auto color = INTERPOLATE(rgbaEnd, rgbaBegin, dist);
|
||||||
fill->ctable[i++] = ALPHA_BLEND((color | 0xff000000), (color >> 24));
|
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;
|
t += dt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,11 +126,6 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
|
||||||
{
|
{
|
||||||
if (fill->solid) return true;
|
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;
|
const Fill::ColorStop* colors;
|
||||||
auto cnt = fdata->colorStops(&colors);
|
auto cnt = fdata->colorStops(&colors);
|
||||||
if (cnt == 0 || !colors) return false;
|
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 g = pColors->g;
|
||||||
auto b = pColors->b;
|
auto b = pColors->b;
|
||||||
auto rgba = surface->join(r, g, b, a);
|
auto rgba = surface->join(r, g, b, a);
|
||||||
|
auto inc = 1.0f / static_cast<float>(SW_COLOR_TABLE);
|
||||||
auto inc = 1.0f / static_cast<float>(GRADIENT_STOP_SIZE);
|
|
||||||
auto pos = 1.5f * inc;
|
auto pos = 1.5f * inc;
|
||||||
uint32_t i = 0;
|
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) {
|
for (uint32_t j = 0; j < cnt - 1; ++j) {
|
||||||
if (repeat && j == cnt - 2 && iAAEnd == 0) {
|
if (repeat && j == cnt - 2 && iAAEnd == 0) {
|
||||||
iAAEnd = iAABegin;
|
iAAEnd = iAABegin;
|
||||||
_adjustAAMargin(iAAEnd, GRADIENT_STOP_SIZE - i);
|
_adjustAAMargin(iAAEnd, SW_COLOR_TABLE - i);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto curr = colors + j;
|
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);
|
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 t = (pos - curr->offset) * delta;
|
||||||
auto dist = static_cast<int32_t>(255 * t);
|
auto dist = static_cast<int32_t>(255 * t);
|
||||||
auto dist2 = 255 - dist;
|
auto dist2 = 255 - dist;
|
||||||
|
|
@ -195,13 +188,13 @@ static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface*
|
||||||
}
|
}
|
||||||
rgba = ALPHA_BLEND((rgba | 0xff000000), a);
|
rgba = ALPHA_BLEND((rgba | 0xff000000), a);
|
||||||
|
|
||||||
for (; i < GRADIENT_STOP_SIZE; ++i)
|
for (; i < SW_COLOR_TABLE; ++i)
|
||||||
fill->ctable[i] = rgba;
|
fill->ctable[i] = rgba;
|
||||||
|
|
||||||
//For repeat fill spread apply anti-aliasing between the last and first colors,
|
//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.
|
//othewise make sure the last color stop is represented at the end of the table.
|
||||||
if (repeat) _applyAA(fill, iAABegin, iAAEnd);
|
if (repeat) _applyAA(fill, iAABegin, iAAEnd);
|
||||||
else fill->ctable[GRADIENT_STOP_SIZE - 1] = rgba;
|
else fill->ctable[SW_COLOR_TABLE - 1] = rgba;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -277,10 +270,12 @@ bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix& tr
|
||||||
//This condition fulfills the SVG 1.1 std:
|
//This condition fulfills the SVG 1.1 std:
|
||||||
//the focal point, if outside the end circle, is moved to be on the end circle
|
//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
|
//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);
|
auto dist = sqrtf(fill->radial.dx * fill->radial.dx + fill->radial.dy * fill->radial.dy);
|
||||||
fill->radial.fx = cx + r * (fx - cx) / dist;
|
//retract focal point slightly from edge to avoid numerical errors:
|
||||||
fill->radial.fy = cy + r * (fy - cy) / dist;
|
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.dx = cx - fill->radial.fx;
|
||||||
fill->radial.dy = cy - fill->radial.fy;
|
fill->radial.dy = cy - fill->radial.fy;
|
||||||
// Prevent loss of precision on Apple Silicon when dr=dy and dx=0 due to FMA
|
// 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) {
|
switch (fill->spread) {
|
||||||
case FillSpread::Pad: {
|
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;
|
else if (pos < 0) pos = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FillSpread::Repeat: {
|
case FillSpread::Repeat: {
|
||||||
pos = pos % GRADIENT_STOP_SIZE;
|
pos = pos % SW_COLOR_TABLE;
|
||||||
if (pos < 0) pos = GRADIENT_STOP_SIZE + pos;
|
if (pos < 0) pos = SW_COLOR_TABLE + pos;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case FillSpread::Reflect: {
|
case FillSpread::Reflect: {
|
||||||
auto limit = GRADIENT_STOP_SIZE * 2;
|
auto limit = SW_COLOR_TABLE * 2;
|
||||||
pos = pos % limit;
|
pos = pos % limit;
|
||||||
if (pos < 0) pos = limit + pos;
|
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;
|
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)
|
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)];
|
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
|
//Rotation
|
||||||
float rx = x + 0.5f;
|
float rx = x + 0.5f;
|
||||||
float ry = y + 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 t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
|
||||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
|
||||||
|
|
||||||
if (opacity == 255) {
|
if (opacity == 255) {
|
||||||
if (tvg::zero(inc)) {
|
if (tvg::zero(inc)) {
|
||||||
|
|
@ -578,7 +573,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
||||||
} else {
|
} else {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
while (counter++ < len) {
|
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;
|
++dst;
|
||||||
t += inc;
|
t += inc;
|
||||||
cmp += csize;
|
cmp += csize;
|
||||||
|
|
@ -609,7 +604,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
||||||
} else {
|
} else {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
while (counter++ < len) {
|
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;
|
++dst;
|
||||||
t += inc;
|
t += inc;
|
||||||
cmp += csize;
|
cmp += csize;
|
||||||
|
|
@ -624,8 +619,8 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
|
||||||
//Rotation
|
//Rotation
|
||||||
float rx = x + 0.5f;
|
float rx = x + 0.5f;
|
||||||
float ry = y + 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 t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
|
||||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
|
||||||
|
|
||||||
if (tvg::zero(inc)) {
|
if (tvg::zero(inc)) {
|
||||||
auto src = MULTIPLY(a, A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE))));
|
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 {
|
} else {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
while (counter++ < len) {
|
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 = maskOp(src, *dst, ~src);
|
||||||
++dst;
|
++dst;
|
||||||
t += inc;
|
t += inc;
|
||||||
|
|
@ -666,8 +661,8 @@ void fillLinear(const SwFill* fill, uint8_t* dst, uint32_t y, uint32_t x, uint32
|
||||||
//Rotation
|
//Rotation
|
||||||
float rx = x + 0.5f;
|
float rx = x + 0.5f;
|
||||||
float ry = y + 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 t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
|
||||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
|
||||||
|
|
||||||
if (tvg::zero(inc)) {
|
if (tvg::zero(inc)) {
|
||||||
auto src = A(_fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE)));
|
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 {
|
} else {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
while (counter++ < len) {
|
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);
|
auto tmp = maskOp(src, *cmp, 0);
|
||||||
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
*dst = tmp + MULTIPLY(*dst, ~tmp);
|
||||||
++dst;
|
++dst;
|
||||||
|
|
@ -713,8 +708,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
||||||
//Rotation
|
//Rotation
|
||||||
float rx = x + 0.5f;
|
float rx = x + 0.5f;
|
||||||
float ry = y + 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 t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
|
||||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
|
||||||
|
|
||||||
if (tvg::zero(inc)) {
|
if (tvg::zero(inc)) {
|
||||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
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 {
|
} else {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
while (counter++ < len) {
|
while (counter++ < len) {
|
||||||
*dst = op(_pixel(fill, t / GRADIENT_STOP_SIZE), *dst, a);
|
*dst = op(_pixel(fill, t / SW_COLOR_TABLE), *dst, a);
|
||||||
++dst;
|
++dst;
|
||||||
t += inc;
|
t += inc;
|
||||||
}
|
}
|
||||||
|
|
@ -753,8 +748,8 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
||||||
//Rotation
|
//Rotation
|
||||||
float rx = x + 0.5f;
|
float rx = x + 0.5f;
|
||||||
float ry = y + 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 t = (fill->linear.dx * rx + fill->linear.dy * ry + fill->linear.offset) * (SW_COLOR_TABLE - 1);
|
||||||
float inc = (fill->linear.dx) * (GRADIENT_STOP_SIZE - 1);
|
float inc = (fill->linear.dx) * (SW_COLOR_TABLE - 1);
|
||||||
|
|
||||||
if (tvg::zero(inc)) {
|
if (tvg::zero(inc)) {
|
||||||
auto color = _fixedPixel(fill, static_cast<int32_t>(t * FIXPT_SIZE));
|
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 {
|
} else {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
while (counter++ < len) {
|
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 = op2(tmp, *dst, 255);
|
||||||
++dst;
|
++dst;
|
||||||
t += inc;
|
t += inc;
|
||||||
|
|
@ -812,7 +807,7 @@ void fillLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint3
|
||||||
} else {
|
} else {
|
||||||
uint32_t counter = 0;
|
uint32_t counter = 0;
|
||||||
while (counter++ < len) {
|
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);
|
auto tmp2 = op2(tmp, *dst, 255);
|
||||||
*dst = INTERPOLATE(tmp2, *dst, a);
|
*dst = INTERPOLATE(tmp2, *dst, a);
|
||||||
++dst;
|
++dst;
|
||||||
|
|
@ -854,10 +849,6 @@ const Fill::ColorStop* fillFetchSolid(const SwFill* fill, const Fill* fdata)
|
||||||
|
|
||||||
void fillReset(SwFill* fill)
|
void fillReset(SwFill* fill)
|
||||||
{
|
{
|
||||||
if (fill->ctable) {
|
|
||||||
free(fill->ctable);
|
|
||||||
fill->ctable = nullptr;
|
|
||||||
}
|
|
||||||
fill->translucent = false;
|
fill->translucent = false;
|
||||||
fill->solid = false;
|
fill->solid = false;
|
||||||
}
|
}
|
||||||
|
|
@ -865,9 +856,5 @@ void fillReset(SwFill* fill)
|
||||||
|
|
||||||
void fillFree(SwFill* fill)
|
void fillFree(SwFill* fill)
|
||||||
{
|
{
|
||||||
if (!fill) return;
|
if (fill) free(fill);
|
||||||
|
|
||||||
if (fill->ctable) free(fill->ctable);
|
|
||||||
|
|
||||||
free(fill);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -245,9 +245,7 @@ void mathSplitCubic(SwPoint* base)
|
||||||
void mathSplitLine(SwPoint* base)
|
void mathSplitLine(SwPoint* base)
|
||||||
{
|
{
|
||||||
base[2] = base[1];
|
base[2] = base[1];
|
||||||
|
base[1] = {(base[0].x >> 1) + (base[1].x >> 1), (base[0].y >> 1) + (base[1].y >> 1)};
|
||||||
base[1].x = (base[0].x + base[1].x) >> 1;
|
|
||||||
base[1].y = (base[0].y + base[1].y) >> 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,6 @@
|
||||||
#include "tvgSwCommon.h"
|
#include "tvgSwCommon.h"
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
|
||||||
/* Internal Class Implementation */
|
|
||||||
/************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* External 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];
|
return &mpool->cellPool[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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -84,7 +70,8 @@ SwMpool* mpoolInit(uint32_t threads)
|
||||||
auto mpool = static_cast<SwMpool*>(calloc(1, sizeof(SwMpool)));
|
auto mpool = static_cast<SwMpool*>(calloc(1, sizeof(SwMpool)));
|
||||||
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
|
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * allocSize));
|
||||||
mpool->strokeOutline = 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;
|
mpool->allocSize = allocSize;
|
||||||
|
|
||||||
return mpool;
|
return mpool;
|
||||||
|
|
@ -103,11 +90,6 @@ bool mpoolClear(SwMpool* mpool)
|
||||||
mpool->strokeOutline[i].cntrs.reset();
|
mpool->strokeOutline[i].cntrs.reset();
|
||||||
mpool->strokeOutline[i].types.reset();
|
mpool->strokeOutline[i].types.reset();
|
||||||
mpool->strokeOutline[i].closed.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;
|
return true;
|
||||||
|
|
@ -122,7 +104,7 @@ bool mpoolTerm(SwMpool* mpool)
|
||||||
|
|
||||||
free(mpool->outline);
|
free(mpool->outline);
|
||||||
free(mpool->strokeOutline);
|
free(mpool->strokeOutline);
|
||||||
free(mpool->dashOutline);
|
delete[](mpool->cellPool);
|
||||||
free(mpool);
|
free(mpool);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -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);
|
size.w = bbox.max.x - bbox.min.x;
|
||||||
dst += (region.min.y * dstride + region.min.x);
|
size.h = bbox.max.y - bbox.min.y;
|
||||||
|
|
||||||
auto w = region.max.x - region.min.x;
|
//shift
|
||||||
auto h = region.max.y - region.min.y;
|
if (offset.x < 0) {
|
||||||
auto translucent = (direct || opacity < 255);
|
*src -= offset.x;
|
||||||
|
size.w += offset.x;
|
||||||
|
} else {
|
||||||
|
*dst += offset.x;
|
||||||
|
size.w -= offset.x;
|
||||||
|
}
|
||||||
|
|
||||||
//shift offset
|
if (offset.y < 0) {
|
||||||
if (region.min.x + offset.x < 0) src -= offset.x;
|
*src -= (offset.y * sstride);
|
||||||
else dst += offset.x;
|
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) {
|
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)
|
||||||
if (translucent) rasterTranslucentPixel32(dst, src, w, opacity);
|
{
|
||||||
else rasterPixel32(dst, src, w, opacity);
|
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;
|
src += sstride;
|
||||||
dst += dstride;
|
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);
|
rd->extends = _gaussianInit(rd, std::pow(params->sigma * scale, 2), params->quality);
|
||||||
|
|
||||||
//invalid
|
//invalid
|
||||||
if (rd->extends == 0 || params->color[3] == 0) {
|
if (params->color[3] == 0) {
|
||||||
params->valid = false;
|
params->valid = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//offset
|
//offset
|
||||||
if (params->distance > 0.0f) {
|
auto radian = tvg::deg2rad(90.0f - params->angle) - tvg::radian(transform);
|
||||||
auto radian = tvg::deg2rad(90.0f - params->angle);
|
rd->offset = {(int32_t)((params->distance * scale) * cosf(radian)), (int32_t)(-1.0f * (params->distance * scale) * sinf(radian))};
|
||||||
rd->offset = {(SwCoord)(params->distance * cosf(radian)), (SwCoord)(-1.0f * params->distance * sinf(radian))};
|
|
||||||
} else {
|
|
||||||
rd->offset = {0, 0};
|
|
||||||
}
|
|
||||||
|
|
||||||
params->valid = true;
|
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);
|
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.
|
//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);
|
_dropShadowFilter(back, front, stride, w, h, bbox, data->kernel[0], color, false);
|
||||||
std::swap(front, buffer[0]->buf32);
|
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
|
//draw to the main surface directly
|
||||||
if (direct) {
|
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);
|
std::swap(cmp->image.buf32, buffer[0]->buf32);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//draw to the intermediate surface
|
//draw to the intermediate surface
|
||||||
rasterClear(surface[1], bbox.min.x, bbox.min.y, w, h);
|
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);
|
std::swap(cmp->image.buf32, buffer[1]->buf32);
|
||||||
|
|
||||||
//compositing shadow and body
|
//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);
|
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) {
|
if (direct) {
|
||||||
auto dbuffer = cmp->recoverSfc->buf32 + (bbox.min.y * cmp->recoverSfc->stride + bbox.min.x);
|
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);
|
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)
|
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) {
|
if (l < 128) {
|
||||||
auto a = std::min(l * 2, 255);
|
auto a = std::min(l * 2, 255);
|
||||||
return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a);
|
return ALPHA_BLEND(s, 255 - a) + ALPHA_BLEND(m, a);
|
||||||
|
|
|
||||||
|
|
@ -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 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) {
|
for (auto y = region.min.y; y < region.max.y; ++y) {
|
||||||
auto dst = dbuffer;
|
auto dst = dbuffer;
|
||||||
|
|
@ -239,7 +239,7 @@ static bool _compositeMaskImage(SwSurface* surface, const SwImage* image, const
|
||||||
*dst = *src + MULTIPLY(*dst, ~*src);
|
*dst = *src + MULTIPLY(*dst, ~*src);
|
||||||
}
|
}
|
||||||
dbuffer += surface->stride;
|
dbuffer += surface->stride;
|
||||||
sbuffer += image->stride;
|
sbuffer += image.stride;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +339,7 @@ static bool _rasterCompositeMaskedRect(SwSurface* surface, const SwBBox& region,
|
||||||
}
|
}
|
||||||
cbuffer += cstride;
|
cbuffer += cstride;
|
||||||
}
|
}
|
||||||
return _compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
|
return _compositeMaskImage(surface, surface->compositor->image, surface->compositor->bbox);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -387,7 +387,7 @@ static bool _rasterMattedRect(SwSurface* surface, const SwBBox& region, uint8_t
|
||||||
auto alpha = surface->alpha(surface->compositor->method);
|
auto alpha = surface->alpha(surface->compositor->method);
|
||||||
|
|
||||||
TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
|
TVGLOG("SW_ENGINE", "Matted(%d) Rect [Region: %lu %lu %u %u]", (int)surface->compositor->method, region.min.x, region.min.y, w, h);
|
||||||
|
|
||||||
//32bits channels
|
//32bits channels
|
||||||
if (surface->channelSize == sizeof(uint32_t)) {
|
if (surface->channelSize == sizeof(uint32_t)) {
|
||||||
auto color = surface->join(r, g, b, a);
|
auto color = surface->join(r, g, b, a);
|
||||||
|
|
@ -506,7 +506,7 @@ static bool _rasterCompositeMaskedRle(SwSurface* surface, SwRle* rle, SwMask mas
|
||||||
*cmp = maskOp(src, *cmp, ialpha);
|
*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) \
|
#define SCALED_IMAGE_RANGE_Y(y) \
|
||||||
auto sy = (y) * itransform->e22 + itransform->e23 - 0.49f; \
|
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) { \
|
if (scaleMethod == _interpDownScaler) { \
|
||||||
auto my = (int32_t)nearbyint(sy); \
|
auto my = (int32_t)nearbyint(sy); \
|
||||||
miny = my - (int32_t)sampleSize; \
|
miny = my - (int32_t)sampleSize; \
|
||||||
if (miny < 0) miny = 0; \
|
if (miny < 0) miny = 0; \
|
||||||
maxy = my + (int32_t)sampleSize; \
|
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 \
|
#define SCALED_IMAGE_RANGE_X \
|
||||||
auto sx = (x) * itransform->e11 + itransform->e13 - 0.49f; \
|
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);
|
TVGERR("SW_ENGINE", "Not Supported Scaled Masked(%d) Rle Image", (int)surface->compositor->method);
|
||||||
return false;
|
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);
|
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 csize = surface->compositor->image.channelSize;
|
||||||
auto alpha = surface->alpha(surface->compositor->method);
|
auto alpha = surface->alpha(surface->compositor->method);
|
||||||
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
||||||
auto sampleSize = _sampleSize(image->scale);
|
auto sampleSize = _sampleSize(image.scale);
|
||||||
int32_t miny = 0, maxy = 0;
|
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)
|
SCALED_IMAGE_RANGE_Y(span->y)
|
||||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
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 cmp = &surface->compositor->image.buf8[(span->y * surface->compositor->image.stride + span->x) * csize];
|
||||||
auto a = MULTIPLY(span->coverage, opacity);
|
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) {
|
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
|
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));
|
src = ALPHA_BLEND(src, (a == 255) ? alpha(cmp) : MULTIPLY(alpha(cmp), a));
|
||||||
*dst = src + ALPHA_BLEND(*dst, IA(src));
|
*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 span = image.rle->spans;
|
||||||
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
||||||
auto sampleSize = _sampleSize(image->scale);
|
auto sampleSize = _sampleSize(image.scale);
|
||||||
int32_t miny = 0, maxy = 0;
|
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)
|
SCALED_IMAGE_RANGE_Y(span->y)
|
||||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||||
auto alpha = MULTIPLY(span->coverage, opacity);
|
auto alpha = MULTIPLY(span->coverage, opacity);
|
||||||
if (alpha == 255) {
|
if (alpha == 255) {
|
||||||
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
|
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
|
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);
|
auto tmp = surface->blender(src, *dst, 255);
|
||||||
*dst = INTERPOLATE(tmp, *dst, A(src));
|
*dst = INTERPOLATE(tmp, *dst, A(src));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (uint32_t x = static_cast<uint32_t>(span->x); x < static_cast<uint32_t>(span->x) + span->len; ++x, ++dst) {
|
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
|
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);
|
auto tmp = surface->blender(src, *dst, 255);
|
||||||
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(alpha, A(src)));
|
*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 span = image.rle->spans;
|
||||||
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
||||||
auto sampleSize = _sampleSize(image->scale);
|
auto sampleSize = _sampleSize(image.scale);
|
||||||
int32_t miny = 0, maxy = 0;
|
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)
|
SCALED_IMAGE_RANGE_Y(span->y)
|
||||||
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
auto dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||||
auto alpha = MULTIPLY(span->coverage, opacity);
|
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) {
|
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
|
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);
|
if (alpha < 255) src = ALPHA_BLEND(src, alpha);
|
||||||
*dst = src + ALPHA_BLEND(*dst, IA(src));
|
*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 */
|
/* 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);
|
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 csize = surface->compositor->image.channelSize;
|
||||||
auto cbuffer = surface->compositor->image.buf8;
|
auto cbuffer = surface->compositor->image.buf8;
|
||||||
auto alpha = surface->alpha(surface->compositor->method);
|
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 dst = &surface->buf32[span->y * surface->stride + span->x];
|
||||||
auto cmp = &cbuffer[(span->y * surface->compositor->image.stride + span->x) * csize];
|
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);
|
auto a = MULTIPLY(span->coverage, opacity);
|
||||||
if (a == 255) {
|
if (a == 255) {
|
||||||
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img, cmp += csize) {
|
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 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);
|
auto alpha = MULTIPLY(span->coverage, opacity);
|
||||||
if (alpha == 255) {
|
if (alpha == 255) {
|
||||||
for (uint32_t x = 0; x < span->len; ++x, ++dst, ++img) {
|
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 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);
|
auto alpha = MULTIPLY(span->coverage, opacity);
|
||||||
rasterTranslucentPixel32(dst, img, span->len, alpha);
|
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);
|
TVGERR("SW_ENGINE", "Not Supported Direct Masked(%d) Rle Image", (int)surface->compositor->method);
|
||||||
return false;
|
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 */
|
/*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!");
|
TVGERR("SW_ENGINE", "Not Supported Scaled Masked Image!");
|
||||||
return false;
|
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)) {
|
if (surface->channelSize == sizeof(uint8_t)) {
|
||||||
TVGERR("SW_ENGINE", "Not supported grayscale scaled matted image!");
|
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);
|
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 scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
||||||
auto sampleSize = _sampleSize(image->scale);
|
auto sampleSize = _sampleSize(image.scale);
|
||||||
int32_t miny = 0, maxy = 0;
|
int32_t miny = 0, maxy = 0;
|
||||||
|
|
||||||
for (auto y = region.min.y; y < region.max.y; ++y) {
|
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;
|
auto cmp = cbuffer;
|
||||||
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
|
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, cmp += csize) {
|
||||||
SCALED_IMAGE_RANGE_X
|
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)));
|
auto tmp = ALPHA_BLEND(src, opacity == 255 ? alpha(cmp) : MULTIPLY(opacity, alpha(cmp)));
|
||||||
*dst = tmp + ALPHA_BLEND(*dst, IA(tmp));
|
*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)) {
|
if (surface->channelSize == sizeof(uint8_t)) {
|
||||||
TVGERR("SW_ENGINE", "Not supported grayscale scaled blending image!");
|
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 dbuffer = surface->buf32 + (region.min.y * surface->stride + region.min.x);
|
||||||
auto scaleMethod = image->scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
auto scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
||||||
auto sampleSize = _sampleSize(image->scale);
|
auto sampleSize = _sampleSize(image.scale);
|
||||||
int32_t miny = 0, maxy = 0;
|
int32_t miny = 0, maxy = 0;
|
||||||
|
|
||||||
for (auto y = region.min.y; y < region.max.y; ++y, dbuffer += surface->stride) {
|
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;
|
auto dst = dbuffer;
|
||||||
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
|
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
|
||||||
SCALED_IMAGE_RANGE_X
|
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);
|
auto tmp = surface->blender(src, *dst, 255);
|
||||||
*dst = INTERPOLATE(tmp, *dst, MULTIPLY(opacity, A(src)));
|
*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 scaleMethod = image.scale < DOWN_SCALE_TOLERANCE ? _interpDownScaler : _interpUpScaler;
|
||||||
auto sampleSize = _sampleSize(image->scale);
|
auto sampleSize = _sampleSize(image.scale);
|
||||||
int32_t miny = 0, maxy = 0;
|
int32_t miny = 0, maxy = 0;
|
||||||
|
|
||||||
//32bits channels
|
//32bits channels
|
||||||
|
|
@ -987,7 +945,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
|
||||||
auto dst = buffer;
|
auto dst = buffer;
|
||||||
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
|
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
|
||||||
SCALED_IMAGE_RANGE_X
|
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);
|
if (opacity < 255) src = ALPHA_BLEND(src, opacity);
|
||||||
*dst = src + ALPHA_BLEND(*dst, IA(src));
|
*dst = src + ALPHA_BLEND(*dst, IA(src));
|
||||||
}
|
}
|
||||||
|
|
@ -999,7 +957,7 @@ static bool _rasterScaledImage(SwSurface* surface, const SwImage* image, const M
|
||||||
auto dst = buffer;
|
auto dst = buffer;
|
||||||
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
|
for (auto x = region.min.x; x < region.max.x; ++x, ++dst) {
|
||||||
SCALED_IMAGE_RANGE_X
|
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);
|
*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 */
|
/* 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");
|
TVGERR("SW_ENGINE", "Not Supported: Direct Masked Image");
|
||||||
return false;
|
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 h = static_cast<uint32_t>(region.max.y - region.min.y);
|
||||||
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
auto w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||||
auto csize = surface->compositor->image.channelSize;
|
auto csize = surface->compositor->image.channelSize;
|
||||||
auto alpha = surface->alpha(surface->compositor->method);
|
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 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);
|
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;
|
buffer += surface->stride;
|
||||||
cbuffer += surface->compositor->image.stride * csize;
|
cbuffer += surface->compositor->image.stride * csize;
|
||||||
sbuffer += image->stride;
|
sbuffer += image.stride;
|
||||||
}
|
}
|
||||||
//8 bits
|
//8 bits
|
||||||
} else if (surface->channelSize == sizeof(uint8_t)) {
|
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||||
|
|
@ -1090,14 +1030,14 @@ static bool _rasterDirectMattedImage(SwSurface* surface, const SwImage* image, c
|
||||||
}
|
}
|
||||||
buffer += surface->stride;
|
buffer += surface->stride;
|
||||||
cbuffer += surface->compositor->image.stride * csize;
|
cbuffer += surface->compositor->image.stride * csize;
|
||||||
sbuffer += image->stride;
|
sbuffer += image.stride;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
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)) {
|
if (surface->channelSize == sizeof(uint8_t)) {
|
||||||
TVGERR("SW_ENGINE", "Not supported grayscale image!");
|
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 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) {
|
for (auto y = region.min.y; y < region.max.y; ++y) {
|
||||||
auto dst = dbuffer;
|
auto dst = dbuffer;
|
||||||
|
|
@ -1122,15 +1062,15 @@ static bool _rasterDirectBlendingImage(SwSurface* surface, const SwImage* image,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dbuffer += surface->stride;
|
dbuffer += surface->stride;
|
||||||
sbuffer += image->stride;
|
sbuffer += image.stride;
|
||||||
}
|
}
|
||||||
return true;
|
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
|
//32bits channels
|
||||||
if (surface->channelSize == sizeof(uint32_t)) {
|
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) {
|
for (auto y = region.min.y; y < region.max.y; ++y) {
|
||||||
rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity);
|
rasterTranslucentPixel32(dbuffer, sbuffer, region.max.x - region.min.x, opacity);
|
||||||
dbuffer += surface->stride;
|
dbuffer += surface->stride;
|
||||||
sbuffer += image->stride;
|
sbuffer += image.stride;
|
||||||
}
|
}
|
||||||
//8bits grayscale
|
//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)) {
|
} else if (surface->channelSize == sizeof(uint8_t)) {
|
||||||
auto dbuffer = &surface->buf8[region.min.y * surface->stride + region.min.x];
|
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 dst = dbuffer;
|
||||||
auto src = sbuffer;
|
auto src = sbuffer;
|
||||||
if (opacity == 255) {
|
if (opacity == 255) {
|
||||||
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
|
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 {
|
} else {
|
||||||
for (auto x = region.min.x; x < region.max.x; ++x, ++dst, ++src) {
|
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)) {
|
if (surface->channelSize == sizeof(uint8_t)) {
|
||||||
TVGERR("SW_ENGINE", "Not supported grayscale image!");
|
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 w = static_cast<uint32_t>(region.max.x - region.min.x);
|
||||||
auto csize = surface->compositor->image.channelSize;
|
auto csize = surface->compositor->image.channelSize;
|
||||||
auto alpha = surface->alpha(surface->compositor->method);
|
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 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;
|
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;
|
buffer += surface->stride;
|
||||||
cbuffer += surface->compositor->image.stride * csize;
|
cbuffer += surface->compositor->image.stride * csize;
|
||||||
sbuffer += image->stride;
|
sbuffer += image.stride;
|
||||||
}
|
}
|
||||||
return true;
|
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, ®ion, opacity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/************************************************************************/
|
/************************************************************************/
|
||||||
/* Rect Gradient */
|
/* 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);
|
fillMethod()(fill, cbuffer, region.min.y + y, region.min.x, w, maskOp, 255);
|
||||||
cbuffer += surface->stride;
|
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];
|
auto cmp = &cbuffer[span->y * cstride + span->x];
|
||||||
fillMethod()(fill, cmp, span->y, span->x, span->len, maskOp, span->coverage);
|
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)
|
bool rasterGradientShape(SwSurface* surface, SwShape* shape, const Fill* fdata, uint8_t opacity)
|
||||||
{
|
{
|
||||||
if (!shape->fill) return false;
|
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)
|
bool rasterConvertCS(RenderSurface* surface, ColorSpace to)
|
||||||
{
|
{
|
||||||
ScopedLock lock(surface->key);
|
ScopedLock lock(surface->key);
|
||||||
|
|
|
||||||
|
|
@ -51,94 +51,51 @@ static float dxdya, dxdyb, dudya, dvdya;
|
||||||
static float xa, xb, ua, va;
|
static float xa, xb, ua, va;
|
||||||
|
|
||||||
|
|
||||||
//Y Range exception handling
|
static inline int32_t _modf(float v)
|
||||||
static bool _arrange(const SwImage* image, const SwBBox* region, int& yStart, int& yEnd)
|
|
||||||
{
|
{
|
||||||
int32_t regionTop, regionBottom;
|
return 255 - ((int(v * 256.0f)) & 255);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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()");
|
TVGERR("SW_ENGINE", "TODO: _rasterMaskedPolygonImageSegment()");
|
||||||
return false;
|
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 _dudx = dudx, _dvdx = dvdx;
|
||||||
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
||||||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||||
auto sbuf = image->buf32;
|
auto sbuf = image.buf32;
|
||||||
auto dbuf = surface->buf32;
|
auto dbuf = surface->buf32;
|
||||||
int32_t sw = static_cast<int32_t>(image->w);
|
int32_t sw = static_cast<int32_t>(image.w);
|
||||||
int32_t sh = static_cast<int32_t>(image->h);
|
int32_t sh = static_cast<int32_t>(image.h);
|
||||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||||
int32_t vv = 0, uu = 0;
|
int32_t vv = 0, uu = 0;
|
||||||
int32_t minx = INT32_MAX, maxx = 0;
|
float dx, u, v;
|
||||||
float dx, u, v, iptr;
|
|
||||||
uint32_t* buf;
|
uint32_t* buf;
|
||||||
SwSpan* span = nullptr; //used only when rle based.
|
|
||||||
|
|
||||||
if (!_arrange(image, region, yStart, yEnd)) return;
|
if (yStart < bbox.min.y) yStart = bbox.min.y;
|
||||||
|
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
|
||||||
//Loop through all lines in the segment
|
|
||||||
uint32_t spanIdx = 0;
|
|
||||||
|
|
||||||
if (region) {
|
|
||||||
minx = region->min.x;
|
|
||||||
maxx = region->max.x;
|
|
||||||
} else {
|
|
||||||
span = image->rle->spans;
|
|
||||||
while (span->y < yStart) {
|
|
||||||
++span;
|
|
||||||
++spanIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
y = yStart;
|
y = yStart;
|
||||||
|
|
||||||
while (y < yEnd) {
|
while (y < yEnd) {
|
||||||
x1 = (int32_t)_xa;
|
x1 = std::max((SwCoord)_xa, bbox.min.x);
|
||||||
x2 = (int32_t)_xb;
|
x2 = std::min((SwCoord)_xb, bbox.max.x);
|
||||||
|
|
||||||
if (!region) {
|
|
||||||
minx = INT32_MAX;
|
|
||||||
maxx = 0;
|
|
||||||
//one single row, could be consisted of multiple spans.
|
|
||||||
while (span->y == y && spanIdx < image->rle->size) {
|
|
||||||
if (minx > span->x) minx = span->x;
|
|
||||||
if (maxx < span->x + span->len) maxx = span->x + span->len;
|
|
||||||
++span;
|
|
||||||
++spanIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (x1 < minx) x1 = minx;
|
|
||||||
if (x2 > maxx) x2 = maxx;
|
|
||||||
|
|
||||||
//Anti-Aliasing frames
|
//Anti-Aliasing frames
|
||||||
ay = y - aaSpans->yStart;
|
if (aaSpans) {
|
||||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
ay = y - aaSpans->yStart;
|
||||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
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
|
//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
|
//Perform subtexel pre-stepping on UV
|
||||||
dx = 1 - (_xa - x1);
|
dx = 1 - (_xa - x1);
|
||||||
|
|
@ -149,89 +106,45 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
||||||
|
|
||||||
x = x1;
|
x = x1;
|
||||||
|
|
||||||
if (opacity == 255) {
|
//Draw horizontal line
|
||||||
//Draw horizontal line
|
while (x++ < x2) {
|
||||||
while (x++ < x2) {
|
uu = (int) u;
|
||||||
uu = (int) u;
|
vv = (int) v;
|
||||||
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)));
|
ar = _modf(u);
|
||||||
ab = (int)(255 * (1 - modff(v, &iptr)));
|
ab = _modf(v);
|
||||||
iru = uu + 1;
|
iru = uu + 1;
|
||||||
irv = vv + 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 */
|
/* horizontal interpolate */
|
||||||
if (iru < sw) {
|
if (iru < sw) {
|
||||||
/* right pixel */
|
/* bottom right pixel */
|
||||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
int px3 = *(sbuf + (irv * image.stride) + iru);
|
||||||
px = INTERPOLATE(px, px2, ar);
|
px2 = INTERPOLATE(px2, px3, ar);
|
||||||
}
|
}
|
||||||
/* vertical interpolate */
|
px = INTERPOLATE(px, px2, ab);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
} else {
|
auto tmp = surface->blender(px, *buf, 255);
|
||||||
//Draw horizontal line
|
*buf = INTERPOLATE(tmp, *buf, MULTIPLY(opacity, A(px)));
|
||||||
while (x++ < x2) {
|
++buf;
|
||||||
uu = (int) u;
|
|
||||||
vv = (int) v;
|
|
||||||
|
|
||||||
if ((uint32_t) uu >= image->w || (uint32_t) vv >= image->h) continue;
|
//Step UV horizontally
|
||||||
|
u += _dudx;
|
||||||
ar = (int)(255 * (1 - modff(u, &iptr)));
|
v += _dvdx;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -241,8 +154,6 @@ static void _rasterBlendingPolygonImageSegment(SwSurface* surface, const SwImage
|
||||||
_ua += _dudya;
|
_ua += _dudya;
|
||||||
_va += _dvdya;
|
_va += _dvdya;
|
||||||
|
|
||||||
if (!region && spanIdx >= image->rle->size) break;
|
|
||||||
|
|
||||||
++y;
|
++y;
|
||||||
}
|
}
|
||||||
xa = _xa;
|
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 _dudx = dudx, _dvdx = dvdx;
|
||||||
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
float _dxdya = dxdya, _dxdyb = dxdyb, _dudya = dudya, _dvdya = dvdya;
|
||||||
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
float _xa = xa, _xb = xb, _ua = ua, _va = va;
|
||||||
auto sbuf = image->buf32;
|
auto sbuf = image.buf32;
|
||||||
auto dbuf = surface->buf32;
|
auto dbuf = surface->buf32;
|
||||||
int32_t sw = static_cast<int32_t>(image->w);
|
int32_t sw = static_cast<int32_t>(image.w);
|
||||||
int32_t sh = static_cast<int32_t>(image->h);
|
int32_t sh = static_cast<int32_t>(image.h);
|
||||||
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
int32_t x1, x2, x, y, ar, ab, iru, irv, px, ay;
|
||||||
int32_t vv = 0, uu = 0;
|
int32_t vv = 0, uu = 0;
|
||||||
int32_t minx = INT32_MAX, maxx = 0;
|
float dx, u, v;
|
||||||
float dx, u, v, iptr;
|
|
||||||
uint32_t* buf;
|
uint32_t* buf;
|
||||||
SwSpan* span = nullptr; //used only when rle based.
|
|
||||||
|
|
||||||
//for matting(composition)
|
//for matting(composition)
|
||||||
auto csize = matting ? surface->compositor->image.channelSize: 0;
|
auto csize = matting ? surface->compositor->image.channelSize: 0;
|
||||||
auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
|
auto alpha = matting ? surface->alpha(surface->compositor->method) : nullptr;
|
||||||
uint8_t* cmp = nullptr;
|
uint8_t* cmp = nullptr;
|
||||||
|
|
||||||
if (!_arrange(image, region, yStart, yEnd)) return;
|
if (yStart < bbox.min.y) yStart = bbox.min.y;
|
||||||
|
if (yEnd > bbox.max.y) yEnd = bbox.max.y;
|
||||||
//Loop through all lines in the segment
|
|
||||||
uint32_t spanIdx = 0;
|
|
||||||
|
|
||||||
if (region) {
|
|
||||||
minx = region->min.x;
|
|
||||||
maxx = region->max.x;
|
|
||||||
} else {
|
|
||||||
span = image->rle->spans;
|
|
||||||
while (span->y < yStart) {
|
|
||||||
++span;
|
|
||||||
++spanIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
y = yStart;
|
y = yStart;
|
||||||
|
|
||||||
while (y < yEnd) {
|
while (y < yEnd) {
|
||||||
x1 = (int32_t)_xa;
|
x1 = std::max((SwCoord)_xa, bbox.min.x);
|
||||||
x2 = (int32_t)_xb;
|
x2 = std::min((SwCoord)_xb, bbox.max.x);
|
||||||
|
|
||||||
if (!region) {
|
|
||||||
minx = INT32_MAX;
|
|
||||||
maxx = 0;
|
|
||||||
//one single row, could be consisted of multiple spans.
|
|
||||||
while (span->y == y && spanIdx < image->rle->size) {
|
|
||||||
if (minx > span->x) minx = span->x;
|
|
||||||
if (maxx < span->x + span->len) maxx = span->x + span->len;
|
|
||||||
++span;
|
|
||||||
++spanIdx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (x1 < minx) x1 = minx;
|
|
||||||
if (x2 > maxx) x2 = maxx;
|
|
||||||
|
|
||||||
//Anti-Aliasing frames
|
//Anti-Aliasing frames
|
||||||
ay = y - aaSpans->yStart;
|
if (aaSpans) {
|
||||||
if (aaSpans->lines[ay].x[0] > x1) aaSpans->lines[ay].x[0] = x1;
|
ay = y - aaSpans->yStart;
|
||||||
if (aaSpans->lines[ay].x[1] < x2) aaSpans->lines[ay].x[1] = x2;
|
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
|
//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
|
//Perform subtexel pre-stepping on UV
|
||||||
dx = 1 - (_xa - x1);
|
dx = 1 - (_xa - x1);
|
||||||
|
|
@ -334,30 +218,31 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
||||||
uu = (int) u;
|
uu = (int) u;
|
||||||
vv = (int) v;
|
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;
|
iru = uu + 1;
|
||||||
irv = vv + 1;
|
irv = vv + 1;
|
||||||
|
|
||||||
px = *(sbuf + (vv * image->stride) + uu);
|
px = *(sbuf + (vv * image.stride) + uu);
|
||||||
|
|
||||||
/* horizontal interpolate */
|
/* horizontal interpolate */
|
||||||
if (iru < sw) {
|
if (iru < sw) {
|
||||||
/* right pixel */
|
/* right pixel */
|
||||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
int px2 = *(sbuf + (vv * image.stride) + iru);
|
||||||
px = INTERPOLATE(px, px2, ar);
|
px = INTERPOLATE(px, px2, ar);
|
||||||
}
|
}
|
||||||
/* vertical interpolate */
|
/* vertical interpolate */
|
||||||
if (irv < sh) {
|
if (irv < sh) {
|
||||||
/* bottom pixel */
|
/* bottom pixel */
|
||||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
int px2 = *(sbuf + (irv * image.stride) + uu);
|
||||||
|
|
||||||
/* horizontal interpolate */
|
/* horizontal interpolate */
|
||||||
if (iru < sw) {
|
if (iru < sw) {
|
||||||
/* bottom right pixel */
|
/* bottom right pixel */
|
||||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
int px3 = *(sbuf + (irv * image.stride) + iru);
|
||||||
px2 = INTERPOLATE(px2, px3, ar);
|
px2 = INTERPOLATE(px2, px3, ar);
|
||||||
}
|
}
|
||||||
px = INTERPOLATE(px, px2, ab);
|
px = INTERPOLATE(px, px2, ab);
|
||||||
|
|
@ -382,30 +267,31 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
||||||
uu = (int) u;
|
uu = (int) u;
|
||||||
vv = (int) v;
|
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;
|
iru = uu + 1;
|
||||||
irv = vv + 1;
|
irv = vv + 1;
|
||||||
|
|
||||||
px = *(sbuf + (vv * sw) + uu);
|
px = *(sbuf + (vv * image.stride) + uu);
|
||||||
|
|
||||||
/* horizontal interpolate */
|
/* horizontal interpolate */
|
||||||
if (iru < sw) {
|
if (iru < sw) {
|
||||||
/* right pixel */
|
/* right pixel */
|
||||||
int px2 = *(sbuf + (vv * image->stride) + iru);
|
int px2 = *(sbuf + (vv * image.stride) + iru);
|
||||||
px = INTERPOLATE(px, px2, ar);
|
px = INTERPOLATE(px, px2, ar);
|
||||||
}
|
}
|
||||||
/* vertical interpolate */
|
/* vertical interpolate */
|
||||||
if (irv < sh) {
|
if (irv < sh) {
|
||||||
/* bottom pixel */
|
/* bottom pixel */
|
||||||
int px2 = *(sbuf + (irv * image->stride) + uu);
|
int px2 = *(sbuf + (irv * image.stride) + uu);
|
||||||
|
|
||||||
/* horizontal interpolate */
|
/* horizontal interpolate */
|
||||||
if (iru < sw) {
|
if (iru < sw) {
|
||||||
/* bottom right pixel */
|
/* bottom right pixel */
|
||||||
int px3 = *(sbuf + (irv * image->stride) + iru);
|
int px3 = *(sbuf + (irv * image.stride) + iru);
|
||||||
px2 = INTERPOLATE(px2, px3, ar);
|
px2 = INTERPOLATE(px2, px3, ar);
|
||||||
}
|
}
|
||||||
px = INTERPOLATE(px, px2, ab);
|
px = INTERPOLATE(px, px2, ab);
|
||||||
|
|
@ -433,8 +319,6 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
||||||
_ua += _dudya;
|
_ua += _dudya;
|
||||||
_va += _dvdya;
|
_va += _dvdya;
|
||||||
|
|
||||||
if (!region && spanIdx >= image->rle->size) break;
|
|
||||||
|
|
||||||
++y;
|
++y;
|
||||||
}
|
}
|
||||||
xa = _xa;
|
xa = _xa;
|
||||||
|
|
@ -445,7 +329,7 @@ static void _rasterPolygonImageSegment(SwSurface* surface, const SwImage* image,
|
||||||
|
|
||||||
|
|
||||||
/* This mapping algorithm is based on Mikael Kalms's. */
|
/* 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 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};
|
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[0], y[1])) side = x[0] > x[1];
|
||||||
if (tvg::equal(y[1], y[2])) side = x[2] > 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 compositing = _compositing(surface); //Composition required
|
||||||
auto blending = _blending(surface); //Blending 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
|
//Draw upper segment if possibly visible
|
||||||
if (yi[0] < yi[1]) {
|
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);
|
xa += (off_y * dxdya);
|
||||||
ua += (off_y * dudya);
|
ua += (off_y * dudya);
|
||||||
va += (off_y * dvdya);
|
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);
|
xb = x[0] + dy * dxdyb + (off_y * dxdyb);
|
||||||
|
|
||||||
if (compositing) {
|
if (compositing) {
|
||||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
|
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
|
||||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 1);
|
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 1);
|
||||||
} else if (blending) {
|
} else if (blending) {
|
||||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
|
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
|
||||||
} else {
|
} 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;
|
upper = true;
|
||||||
}
|
}
|
||||||
//Draw lower segment if possibly visible
|
//Draw lower segment if possibly visible
|
||||||
if (yi[1] < yi[2]) {
|
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) {
|
if (!upper) {
|
||||||
xa += (off_y * dxdya);
|
xa += (off_y * dxdya);
|
||||||
ua += (off_y * dudya);
|
ua += (off_y * dudya);
|
||||||
|
|
@ -556,12 +439,12 @@ static void _rasterPolygonImage(SwSurface* surface, const SwImage* image, const
|
||||||
dxdyb = dxdy[2];
|
dxdyb = dxdy[2];
|
||||||
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
|
xb = x[1] + (1 - (y[1] - yi[1])) * dxdyb + (off_y * dxdyb);
|
||||||
if (compositing) {
|
if (compositing) {
|
||||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
|
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
|
||||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 2);
|
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 2);
|
||||||
} else if (blending) {
|
} else if (blending) {
|
||||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
|
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
|
||||||
} else {
|
} 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
|
//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
|
//Draw upper segment if possibly visible
|
||||||
if (yi[0] < yi[1]) {
|
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);
|
xb += (off_y *dxdyb);
|
||||||
|
|
||||||
// Set slopes along left edge and perform subpixel pre-stepping
|
// 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);
|
va = v[0] + dy * dvdya + (off_y * dvdya);
|
||||||
|
|
||||||
if (compositing) {
|
if (compositing) {
|
||||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, true);
|
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, true);
|
||||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity, 3);
|
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity, 3);
|
||||||
} else if (blending) {
|
} else if (blending) {
|
||||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[0], yi[1], aaSpans, opacity);
|
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[0], yi[1], aaSpans, opacity);
|
||||||
} else {
|
} 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;
|
upper = true;
|
||||||
}
|
}
|
||||||
//Draw lower segment if possibly visible
|
//Draw lower segment if possibly visible
|
||||||
if (yi[1] < yi[2]) {
|
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);
|
if (!upper) xb += (off_y *dxdyb);
|
||||||
|
|
||||||
// Set slopes along left edge and perform subpixel pre-stepping
|
// 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);
|
va = v[1] + dy * dvdya + (off_y * dvdya);
|
||||||
|
|
||||||
if (compositing) {
|
if (compositing) {
|
||||||
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, true);
|
if (_matting(surface)) _rasterPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, true);
|
||||||
else _rasterMaskedPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity, 4);
|
else _rasterMaskedPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity, 4);
|
||||||
} else if (blending) {
|
} else if (blending) {
|
||||||
_rasterBlendingPolygonImageSegment(surface, image, region, yi[1], yi[2], aaSpans, opacity);
|
_rasterBlendingPolygonImageSegment(surface, image, bbox, yi[1], yi[2], aaSpans, opacity);
|
||||||
} else {
|
} 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)));
|
auto aaSpans = static_cast<AASpans*>(malloc(sizeof(AASpans)));
|
||||||
aaSpans->yStart = yStart;
|
aaSpans->yStart = yStart;
|
||||||
aaSpans->yEnd = yEnd;
|
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 end = surface->buf32 + surface->h * surface->stride;
|
||||||
auto y = aaSpans->yStart;
|
auto y = aaSpans->yStart;
|
||||||
|
|
@ -864,7 +742,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
||||||
|
|
||||||
//exceptional handling. out of memory bound.
|
//exceptional handling. out of memory bound.
|
||||||
if (dst + line->length[0] >= end) {
|
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]) {
|
while (pos <= line->length[0]) {
|
||||||
|
|
@ -881,7 +759,7 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
||||||
pos = line->length[1];
|
pos = line->length[1];
|
||||||
|
|
||||||
//exceptional handling. out of memory bound.
|
//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) {
|
while (pos > 0) {
|
||||||
*dst = INTERPOLATE(*dst, pixel, 255 - (line->coverage[1] * pos));
|
*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->lines);
|
||||||
free(aaSpans);
|
free(aaSpans);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -909,23 +785,19 @@ static bool _apply(SwSurface* surface, AASpans* aaSpans)
|
||||||
| / |
|
| / |
|
||||||
3 -- 2
|
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)) {
|
if (surface->channelSize == sizeof(uint8_t)) {
|
||||||
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
|
TVGERR("SW_ENGINE", "Not supported grayscale Textmap polygon!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Exceptions: No dedicated drawing area?
|
//Prepare vertices. Shift XY coordinates to match the sub-pixeling technique.
|
||||||
if ((!image->rle && !region) || (image->rle && image->rle->size == 0)) return true;
|
|
||||||
|
|
||||||
/* Prepare vertices.
|
|
||||||
shift XY coordinates to match the sub-pixeling technique. */
|
|
||||||
Vertex vertices[4];
|
Vertex vertices[4];
|
||||||
vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
|
vertices[0] = {{0.0f, 0.0f}, {0.0f, 0.0f}};
|
||||||
vertices[1] = {{float(image->w), 0.0f}, {float(image->w), 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[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[3] = {{0.0f, float(image.h)}, {0.0f, float(image.h)}};
|
||||||
|
|
||||||
float ys = FLT_MAX, ye = -1.0f;
|
float ys = FLT_MAX, ye = -1.0f;
|
||||||
for (int i = 0; i < 4; i++) {
|
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;
|
if (vertices[i].pt.y > ye) ye = vertices[i].pt.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto aaSpans = _AASpans(ys, ye, image, region);
|
auto yStart = std::max(static_cast<SwCoord>(ys), bbox.min.y);
|
||||||
if (!aaSpans) return true;
|
auto yEnd = std::min(static_cast<SwCoord>(ye), bbox.max.y);
|
||||||
|
auto aaSpans = rightAngle(transform) ? nullptr : _AASpans(yStart, yEnd);
|
||||||
|
|
||||||
Polygon polygon;
|
Polygon polygon;
|
||||||
|
|
||||||
|
|
@ -944,19 +817,20 @@ static bool _rasterTexmapPolygon(SwSurface* surface, const SwImage* image, const
|
||||||
polygon.vertex[1] = vertices[1];
|
polygon.vertex[1] = vertices[1];
|
||||||
polygon.vertex[2] = vertices[3];
|
polygon.vertex[2] = vertices[3];
|
||||||
|
|
||||||
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
|
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
|
||||||
|
|
||||||
//Draw the second polygon
|
//Draw the second polygon
|
||||||
polygon.vertex[0] = vertices[1];
|
polygon.vertex[0] = vertices[1];
|
||||||
polygon.vertex[1] = vertices[2];
|
polygon.vertex[1] = vertices[2];
|
||||||
polygon.vertex[2] = vertices[3];
|
polygon.vertex[2] = vertices[3];
|
||||||
|
|
||||||
_rasterPolygonImage(surface, image, region, polygon, aaSpans, opacity);
|
_rasterPolygonImage(surface, image, bbox, polygon, aaSpans, opacity);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
|
if (_compositing(surface) && _masking(surface) && !_direct(surface->compositor->method)) {
|
||||||
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
|
_compositeMaskImage(surface, &surface->compositor->image, surface->compositor->bbox);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return _apply(surface, aaSpans);
|
if (aaSpans) _apply(surface, aaSpans);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ struct SwShapeTask : SwTask
|
||||||
if (updateShape) shapeReset(&shape);
|
if (updateShape) shapeReset(&shape);
|
||||||
if (updateFill || clipper) {
|
if (updateFill || clipper) {
|
||||||
if (shapePrepare(&shape, rshape, transform, bbox, renderRegion, mpool, tid, clips.count > 0 ? true : false)) {
|
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 {
|
} else {
|
||||||
updateFill = false;
|
updateFill = false;
|
||||||
renderRegion.reset();
|
renderRegion.reset();
|
||||||
|
|
@ -165,8 +165,9 @@ struct SwShapeTask : SwTask
|
||||||
//Clip Path
|
//Clip Path
|
||||||
for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
|
for (auto clip = clips.begin(); clip < clips.end(); ++clip) {
|
||||||
auto clipper = static_cast<SwTask*>(*clip);
|
auto clipper = static_cast<SwTask*>(*clip);
|
||||||
if (shape.rle && !clipper->clip(shape.rle)) goto err; //Clip shape rle
|
auto clipShapeRle = shape.rle ? clipper->clip(shape.rle) : true;
|
||||||
if (shape.strokeRle && !clipper->clip(shape.strokeRle)) goto err; //Clip stroke rle
|
auto clipStrokeRle = shape.strokeRle ? clipper->clip(shape.strokeRle) : true;
|
||||||
|
if (!clipShapeRle && !clipStrokeRle) goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
bbox = renderRegion; //sync
|
bbox = renderRegion; //sync
|
||||||
|
|
@ -220,7 +221,7 @@ struct SwImageTask : SwTask
|
||||||
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
|
if (!imagePrepare(&image, transform, clipRegion, bbox, mpool, tid)) goto end;
|
||||||
|
|
||||||
if (clips.count > 0) {
|
if (clips.count > 0) {
|
||||||
if (!imageGenRle(&image, bbox, false)) goto end;
|
if (!imageGenRle(&image, bbox, mpool, tid, false)) goto end;
|
||||||
if (image.rle) {
|
if (image.rle) {
|
||||||
//Clear current task memorypool here if the clippers would use the same memory pool
|
//Clear current task memorypool here if the clippers would use the same memory pool
|
||||||
imageDelOutline(&image, mpool, tid);
|
imageDelOutline(&image, mpool, tid);
|
||||||
|
|
@ -403,7 +404,32 @@ bool SwRenderer::renderImage(RenderData data)
|
||||||
|
|
||||||
if (task->opacity == 0) return true;
|
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
|
//Default is alpha blending
|
||||||
if (p->method == CompositeMethod::None) {
|
if (p->method == CompositeMethod::None) {
|
||||||
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
return rasterDirectImage(surface, p->image, p->bbox, p->opacity);
|
||||||
return rasterImage(surface, &p->image, m, p->bbox, p->opacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -678,13 +703,16 @@ bool SwRenderer::render(RenderCompositor* cmp, const RenderEffect* effect, bool
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: Support grayscale effects.
|
||||||
|
if (p->recoverSfc->channelSize != sizeof(uint32_t)) direct = false;
|
||||||
|
|
||||||
switch (effect->type) {
|
switch (effect->type) {
|
||||||
case SceneEffect::GaussianBlur: {
|
case SceneEffect::GaussianBlur: {
|
||||||
return effectGaussianBlur(p, request(surface->channelSize, true), static_cast<const RenderEffectGaussianBlur*>(effect));
|
return effectGaussianBlur(p, request(surface->channelSize, true), static_cast<const RenderEffectGaussianBlur*>(effect));
|
||||||
}
|
}
|
||||||
case SceneEffect::DropShadow: {
|
case SceneEffect::DropShadow: {
|
||||||
auto cmp1 = request(surface->channelSize, true);
|
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);
|
auto cmp2 = request(surface->channelSize, true);
|
||||||
SwSurface* surfaces[] = {cmp1, cmp2};
|
SwSurface* surfaces[] = {cmp1, cmp2};
|
||||||
auto ret = effectDropShadow(p, surfaces, static_cast<const RenderEffectDropShadow*>(effect), direct);
|
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)
|
void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags)
|
||||||
{
|
{
|
||||||
if (!surface) return task;
|
if (!surface || (transform.e11 == 0.0f && transform.e12 == 0.0f) || (transform.e21 == 0.0f && transform.e22 == 0.0f)) return task; //invalid
|
||||||
if (flags == RenderUpdateFlag::None) return task;
|
|
||||||
|
|
||||||
//TODO: Failed threading them. It would be better if it's possible.
|
//TODO: Failed threading them. It would be better if it's possible.
|
||||||
//See: https://github.com/thorvg/thorvg/issues/1409
|
//See: https://github.com/thorvg/thorvg/issues/1409
|
||||||
|
|
@ -738,7 +765,7 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
|
||||||
|
|
||||||
task->clips = clips;
|
task->clips = clips;
|
||||||
task->transform = transform;
|
task->transform = transform;
|
||||||
|
|
||||||
//zero size?
|
//zero size?
|
||||||
if (task->transform.e11 == 0.0f && task->transform.e12 == 0.0f) return task; //zero width
|
if (task->transform.e11 == 0.0f && task->transform.e12 == 0.0f) return task; //zero width
|
||||||
if (task->transform.e21 == 0.0f && task->transform.e22 == 0.0f) return task; //zero height
|
if (task->transform.e21 == 0.0f && task->transform.e22 == 0.0f) return task; //zero height
|
||||||
|
|
@ -757,7 +784,16 @@ void* SwRenderer::prepareCommon(SwTask* task, const Matrix& transform, const Arr
|
||||||
tasks.push(task);
|
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;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
@ -767,10 +803,11 @@ RenderData SwRenderer::prepare(RenderSurface* surface, RenderData data, const Ma
|
||||||
{
|
{
|
||||||
//prepare task
|
//prepare task
|
||||||
auto task = static_cast<SwImageTask*>(data);
|
auto task = static_cast<SwImageTask*>(data);
|
||||||
if (!task) task = new SwImageTask;
|
if (task) task->done();
|
||||||
else task->done();
|
else {
|
||||||
|
task = new SwImageTask;
|
||||||
task->source = surface;
|
task->source = surface;
|
||||||
|
}
|
||||||
|
|
||||||
return prepareCommon(task, transform, clips, opacity, flags);
|
return prepareCommon(task, transform, clips, opacity, flags);
|
||||||
}
|
}
|
||||||
|
|
@ -780,10 +817,12 @@ RenderData SwRenderer::prepare(const RenderShape& rshape, RenderData data, const
|
||||||
{
|
{
|
||||||
//prepare task
|
//prepare task
|
||||||
auto task = static_cast<SwShapeTask*>(data);
|
auto task = static_cast<SwShapeTask*>(data);
|
||||||
if (!task) task = new SwShapeTask;
|
if (task) task->done();
|
||||||
else task->done();
|
else {
|
||||||
|
task = new SwShapeTask;
|
||||||
|
task->rshape = &rshape;
|
||||||
|
}
|
||||||
|
|
||||||
task->rshape = &rshape;
|
|
||||||
task->clipper = clipper;
|
task->clipper = clipper;
|
||||||
|
|
||||||
return prepareCommon(task, transform, clips, opacity, flags);
|
return prepareCommon(task, transform, clips, opacity, flags);
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ public:
|
||||||
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
|
bool target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, ColorSpace cs);
|
||||||
bool mempool(bool shared);
|
bool mempool(bool shared);
|
||||||
|
|
||||||
|
SwSurface* request(int channelSize, bool square);
|
||||||
|
|
||||||
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
|
RenderCompositor* target(const RenderRegion& region, ColorSpace cs, CompositionFlag flags) override;
|
||||||
bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
|
bool beginComposite(RenderCompositor* cmp, CompositeMethod method, uint8_t opacity) override;
|
||||||
bool endComposite(RenderCompositor* cmp) override;
|
bool endComposite(RenderCompositor* cmp) override;
|
||||||
|
|
@ -80,7 +82,6 @@ private:
|
||||||
SwRenderer();
|
SwRenderer();
|
||||||
~SwRenderer();
|
~SwRenderer();
|
||||||
|
|
||||||
SwSurface* request(int channelSize, bool square);
|
|
||||||
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
|
RenderData prepareCommon(SwTask* task, const Matrix& transform, const Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -199,21 +199,11 @@
|
||||||
constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
|
constexpr auto PIXEL_BITS = 8; //must be at least 6 bits!
|
||||||
constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
|
constexpr auto ONE_PIXEL = (1L << PIXEL_BITS);
|
||||||
|
|
||||||
using Area = long;
|
|
||||||
|
|
||||||
struct Band
|
struct Band
|
||||||
{
|
{
|
||||||
SwCoord min, max;
|
SwCoord min, max;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Cell
|
|
||||||
{
|
|
||||||
SwCoord x;
|
|
||||||
SwCoord cover;
|
|
||||||
Area area;
|
|
||||||
Cell *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RleWorker
|
struct RleWorker
|
||||||
{
|
{
|
||||||
SwRle* rle;
|
SwRle* rle;
|
||||||
|
|
@ -227,7 +217,7 @@ struct RleWorker
|
||||||
Area area;
|
Area area;
|
||||||
SwCoord cover;
|
SwCoord cover;
|
||||||
|
|
||||||
Cell* cells;
|
SwCell* cells;
|
||||||
ptrdiff_t maxCells;
|
ptrdiff_t maxCells;
|
||||||
ptrdiff_t cellsCnt;
|
ptrdiff_t cellsCnt;
|
||||||
|
|
||||||
|
|
@ -242,11 +232,11 @@ struct RleWorker
|
||||||
int bandSize;
|
int bandSize;
|
||||||
int bandShoot;
|
int bandShoot;
|
||||||
|
|
||||||
void* buffer;
|
SwCell* buffer;
|
||||||
long bufferSize;
|
uint32_t bufferSize;
|
||||||
|
|
||||||
Cell** yCells;
|
SwCell** yCells;
|
||||||
SwCoord yCnt;
|
int32_t yCnt;
|
||||||
|
|
||||||
bool invalid;
|
bool invalid;
|
||||||
bool antiAlias;
|
bool antiAlias;
|
||||||
|
|
@ -282,12 +272,11 @@ static inline SwCoord SUBPIXELS(const SwCoord x)
|
||||||
return SwCoord(((unsigned long) x) << PIXEL_BITS);
|
return SwCoord(((unsigned long) x) << PIXEL_BITS);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
|
// Approximate sqrt(x*x+y*y) using the `alpha max plus beta min' algorithm.
|
||||||
* algorithm. We use alpha = 1, beta = 3/8, giving us results with a
|
// We use alpha = 1, beta = 3/8, giving us results with a largest error
|
||||||
* largest error less than 7% compared to the exact value.
|
// less than 7% compared to the exact value.
|
||||||
*/
|
static inline int32_t HYPOT(SwPoint pt)
|
||||||
static inline SwCoord HYPOT(SwPoint pt)
|
|
||||||
{
|
{
|
||||||
if (pt.x < 0) pt.x = -pt.x;
|
if (pt.x < 0) pt.x = -pt.x;
|
||||||
if (pt.y < 0) pt.y = -pt.y;
|
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;
|
x += rw.cellMin.x;
|
||||||
y += rw.cellMin.y;
|
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;
|
auto x = rw.cellPos.x;
|
||||||
if (x > rw.cellXCnt) x = rw.cellXCnt;
|
if (x > rw.cellXCnt) x = rw.cellXCnt;
|
||||||
|
|
@ -409,7 +408,7 @@ static Cell* _findCell(RleWorker& rw)
|
||||||
auto pcell = &rw.yCells[rw.cellPos.y];
|
auto pcell = &rw.yCells[rw.cellPos.y];
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
Cell* cell = *pcell;
|
auto cell = *pcell;
|
||||||
if (!cell || cell->x > x) break;
|
if (!cell || cell->x > x) break;
|
||||||
if (cell->x == x) return cell;
|
if (cell->x == x) return cell;
|
||||||
pcell = &cell->next;
|
pcell = &cell->next;
|
||||||
|
|
@ -529,14 +528,12 @@ static bool _lineTo(RleWorker& rw, const SwPoint& to)
|
||||||
line[1] = rw.pos;
|
line[1] = rw.pos;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
auto diff = line[0] - line[1];
|
if (SAFE_HYPOT(line[0], line[1]) > SHRT_MAX) {
|
||||||
auto L = HYPOT(diff);
|
|
||||||
|
|
||||||
if (L > SHRT_MAX) {
|
|
||||||
mathSplitLine(line);
|
mathSplitLine(line);
|
||||||
++line;
|
++line;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
auto diff = line[0] - line[1];
|
||||||
e1 = TRUNC(line[1]);
|
e1 = TRUNC(line[1]);
|
||||||
e2 = TRUNC(line[0]);
|
e2 = TRUNC(line[0]);
|
||||||
|
|
||||||
|
|
@ -864,21 +861,25 @@ void _replaceClipSpan(SwRle *rle, SwSpan* clippedSpans, uint32_t size)
|
||||||
/* External Class Implementation */
|
/* 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;
|
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;
|
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
|
//Init Cells
|
||||||
rw.buffer = buffer;
|
rw.buffer = cellPool->buffer;
|
||||||
rw.bufferSize = sizeof(buffer);
|
rw.bufferSize = cellPool->size;
|
||||||
rw.yCells = reinterpret_cast<Cell**>(buffer);
|
rw.yCells = reinterpret_cast<SwCell**>(cellPool->buffer);
|
||||||
rw.cells = nullptr;
|
rw.cells = nullptr;
|
||||||
rw.maxCells = 0;
|
rw.maxCells = 0;
|
||||||
rw.cellsCnt = 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.cellXCnt = rw.cellMax.x - rw.cellMin.x;
|
||||||
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
|
rw.cellYCnt = rw.cellMax.y - rw.cellMin.y;
|
||||||
rw.outline = const_cast<SwOutline*>(outline);
|
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.bandShoot = 0;
|
||||||
rw.antiAlias = antiAlias;
|
rw.antiAlias = antiAlias;
|
||||||
|
|
||||||
|
|
@ -898,6 +899,8 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
|
||||||
else rw.rle = rle;
|
else rw.rle = rle;
|
||||||
|
|
||||||
//Generate RLE
|
//Generate RLE
|
||||||
|
constexpr auto BAND_SIZE = 40;
|
||||||
|
|
||||||
Band bands[BAND_SIZE];
|
Band bands[BAND_SIZE];
|
||||||
Band* band;
|
Band* band;
|
||||||
|
|
||||||
|
|
@ -920,16 +923,16 @@ SwRle* rleRender(SwRle* rle, const SwOutline* outline, const SwBBox& renderRegio
|
||||||
band = bands;
|
band = bands;
|
||||||
|
|
||||||
while (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;
|
rw.yCnt = band->max - band->min;
|
||||||
|
|
||||||
int cellStart = sizeof(Cell*) * (int)rw.yCnt;
|
int cellStart = sizeof(SwCell*) * (int)rw.yCnt;
|
||||||
int cellMod = cellStart % sizeof(Cell);
|
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);
|
auto cellsMax = reinterpret_cast<SwCell*>((char*)rw.buffer + rw.bufferSize);
|
||||||
rw.cells = reinterpret_cast<Cell*>((char*)rw.buffer + cellStart);
|
rw.cells = reinterpret_cast<SwCell*>((char*)rw.buffer + cellStart);
|
||||||
|
|
||||||
if (rw.cells >= cellsMax) goto reduce_bands;
|
if (rw.cells >= cellsMax) goto reduce_bands;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
//must begin with moveTo
|
||||||
if (cmds[0] == PathCommand::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?
|
//FIXME: Should we draw it?
|
||||||
//Case: Stroke Line
|
//Case: Stroke Line
|
||||||
|
|
@ -526,7 +527,7 @@ bool shapeGenRle(SwShape* shape, TVG_UNUSED const RenderShape* rshape, bool anti
|
||||||
if (shape->fastTrack) return true;
|
if (shape->fastTrack) return true;
|
||||||
|
|
||||||
//Case B: Normal Shape RLE Drawing
|
//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;
|
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)
|
bool shapeGenStrokeRle(SwShape* shape, const RenderShape* rshape, const Matrix& transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
|
||||||
{
|
{
|
||||||
SwOutline* shapeOutline = nullptr;
|
SwOutline* shapeOutline = nullptr;
|
||||||
SwOutline* strokeOutline = nullptr;
|
|
||||||
auto dashStroking = false;
|
|
||||||
auto ret = true;
|
|
||||||
|
|
||||||
//Dash style (+trimming)
|
//Dash style (+trimming)
|
||||||
auto trimmed = rshape->strokeTrim();
|
auto trimmed = rshape->strokeTrim();
|
||||||
if (rshape->stroke->dashCnt > 0 || trimmed) {
|
if (rshape->stroke->dashCnt > 0 || trimmed) {
|
||||||
shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
|
shapeOutline = _genDashOutline(rshape, transform, trimmed, mpool, tid);
|
||||||
if (!shapeOutline) return false;
|
if (!shapeOutline) return false;
|
||||||
dashStroking = true;
|
//Trimming & Normal style
|
||||||
//Normal style
|
|
||||||
} else {
|
} else {
|
||||||
if (!shape->outline) {
|
if (!shape->outline) {
|
||||||
if (!_genOutline(shape, rshape, transform, mpool, tid, false)) return false;
|
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;
|
shapeOutline = shape->outline;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strokeParseOutline(shape->stroke, *shapeOutline)) {
|
if (!strokeParseOutline(shape->stroke, *shapeOutline)) return false;
|
||||||
ret = false;
|
auto strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
|
||||||
goto clear;
|
auto ret = mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false);
|
||||||
}
|
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, mpool, tid, true);
|
||||||
|
|
||||||
strokeOutline = strokeExportOutline(shape->stroke, mpool, tid);
|
|
||||||
|
|
||||||
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
|
|
||||||
ret = false;
|
|
||||||
goto clear;
|
|
||||||
}
|
|
||||||
|
|
||||||
shape->strokeRle = rleRender(shape->strokeRle, strokeOutline, renderRegion, true);
|
|
||||||
|
|
||||||
clear:
|
|
||||||
if (dashStroking) mpoolRetDashOutline(mpool, tid);
|
|
||||||
mpoolRetStrokeOutline(mpool, tid);
|
mpoolRetStrokeOutline(mpool, tid);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,6 @@ using namespace tvg;
|
||||||
|
|
||||||
enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown };
|
enum class FileType { Png = 0, Jpg, Webp, Tvg, Svg, Lottie, Ttf, Raw, Gif, Unknown };
|
||||||
|
|
||||||
using Size = Point;
|
|
||||||
|
|
||||||
#ifdef THORVG_LOG_ENABLED
|
#ifdef THORVG_LOG_ENABLED
|
||||||
constexpr auto ErrorColor = "\033[31m"; //red
|
constexpr auto ErrorColor = "\033[31m"; //red
|
||||||
constexpr auto ErrorBgColor = "\033[41m";//bg red
|
constexpr auto ErrorBgColor = "\033[41m";//bg red
|
||||||
|
|
|
||||||
|
|
@ -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 c[4]; //corners
|
||||||
Point tmp[4];
|
|
||||||
Point min = {FLT_MAX, FLT_MAX};
|
|
||||||
Point max = {0.0f, 0.0f};
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
tmp[i] = pts[i];
|
c[i] = pts[i] * m;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
//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]) {
|
auto pointInConvexQuad = [](const Point& p, const Point* quad) {
|
||||||
//viewport region is same, nothing to do.
|
auto sign = [](const Point& p1, const Point& p2, const Point& p3) {
|
||||||
return Result::Success;
|
return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y);
|
||||||
//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]) {
|
auto b1 = sign(p, quad[0], quad[1]) < 0.0f;
|
||||||
renderer->viewport({0, 0, 0, 0});
|
auto b2 = sign(p, quad[1], quad[2]) < 0.0f;
|
||||||
return Result::Success;
|
auto b3 = sign(p, quad[2], quad[3]) < 0.0f;
|
||||||
}
|
auto b4 = sign(p, quad[3], quad[0]) < 0.0f;
|
||||||
return Result::InsufficientCondition;
|
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. */
|
/* Access Shape class by Paint is bad... but it's ok still it's an internal usage. */
|
||||||
auto shape = static_cast<Shape*>(cmpTarget);
|
auto shape = static_cast<Shape*>(cmpTarget);
|
||||||
|
|
||||||
|
//Trimming likely makes the shape non-rectangular
|
||||||
|
if (P(shape)->rs.strokeTrim()) return false;
|
||||||
|
|
||||||
//Rectangle Candidates?
|
//Rectangle Candidates?
|
||||||
const Point* pts;
|
const Point* pts;
|
||||||
auto ptsCnt = shape->pathCoords(&pts);
|
auto ptsCnt = shape->pathCoords(&pts);
|
||||||
|
|
||||||
//nothing to clip
|
//No rectangle format
|
||||||
if (ptsCnt == 0) return Result::InvalidArguments;
|
if (ptsCnt != 4) return false;
|
||||||
if (ptsCnt != 4) return Result::InsufficientCondition;
|
|
||||||
|
|
||||||
auto& rm = P(cmpTarget)->transform();
|
|
||||||
|
|
||||||
//No rotation and no skewing, still can try out clipping the rect region.
|
//No rotation and no skewing, still can try out clipping the rect region.
|
||||||
auto tryClip = false;
|
auto tm = pm * cmpTarget->transform();
|
||||||
|
|
||||||
if ((!rightAngle(pm) || skewed(pm))) tryClip = true;
|
|
||||||
if ((!rightAngle(rm) || skewed(rm))) tryClip = true;
|
|
||||||
|
|
||||||
if (tryClip) return _clipRect(renderer, pts, pm, rm, before);
|
|
||||||
|
|
||||||
//Perpendicular Rectangle?
|
//Perpendicular Rectangle?
|
||||||
auto pt1 = pts + 0;
|
if (rightAngle(tm) && !skewed(tm)) {
|
||||||
auto pt2 = pts + 1;
|
auto pt1 = pts + 0;
|
||||||
auto pt3 = pts + 2;
|
auto pt2 = pts + 1;
|
||||||
auto pt4 = pts + 3;
|
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)) ||
|
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))) {
|
(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 v1 = *pt1;
|
||||||
auto v2 = *pt3;
|
auto v2 = *pt3;
|
||||||
v1 *= rm;
|
v1 *= tm;
|
||||||
v2 *= rm;
|
v2 *= tm;
|
||||||
v1 *= pm;
|
|
||||||
v2 *= pm;
|
|
||||||
|
|
||||||
//sorting
|
//sorting
|
||||||
if (v1.x > v2.x) std::swap(v1.x, v2.x);
|
if (v1.x > v2.x) std::swap(v1.x, v2.x);
|
||||||
if (v1.y > v2.y) std::swap(v1.y, v2.y);
|
if (v1.y > v2.y) std::swap(v1.y, v2.y);
|
||||||
|
|
||||||
after.x = static_cast<int32_t>(v1.x);
|
after.x = static_cast<int32_t>(nearbyint(v1.x));
|
||||||
after.y = static_cast<int32_t>(v1.y);
|
after.y = static_cast<int32_t>(nearbyint(v1.y));
|
||||||
after.w = static_cast<int32_t>(ceil(v2.x - after.x));
|
after.w = static_cast<int32_t>(nearbyint(v2.x)) - after.x;
|
||||||
after.h = static_cast<int32_t>(ceil(v2.y - after.y));
|
after.h = static_cast<int32_t>(nearbyint(v2.y)) - after.y;
|
||||||
|
|
||||||
if (after.w < 0) after.w = 0;
|
if (after.w < 0) after.w = 0;
|
||||||
if (after.h < 0) after.h = 0;
|
if (after.h < 0) after.h = 0;
|
||||||
|
|
||||||
after.intersect(before);
|
after.intersect(before);
|
||||||
renderer->viewport(after);
|
renderer->viewport(after);
|
||||||
|
return true;
|
||||||
return Result::Success;
|
}
|
||||||
}
|
}
|
||||||
return Result::InsufficientCondition;
|
return _clipRect(renderer, pts, tm, before);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -214,8 +205,12 @@ bool Paint::Impl::render(RenderMethod* renderer)
|
||||||
RenderRegion region;
|
RenderRegion region;
|
||||||
PAINT_METHOD(region, bounds(renderer));
|
PAINT_METHOD(region, bounds(renderer));
|
||||||
|
|
||||||
if (MASK_REGION_MERGING(compData->method)) region.add(P(compData->target)->bounds(renderer));
|
auto cData = compData;
|
||||||
if (region.w == 0 || region.h == 0) return true;
|
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);
|
cmp = renderer->target(region, COMPOSITE_TO_COLORSPACE(renderer, compData->method), CompositionFlag::Masking);
|
||||||
if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) {
|
if (renderer->beginComposite(cmp, CompositeMethod::None, 255)) {
|
||||||
compData->target->pImpl->render(renderer);
|
compData->target->pImpl->render(renderer);
|
||||||
|
|
@ -246,7 +241,7 @@ RenderData Paint::Impl::update(RenderMethod* renderer, const Matrix& pm, Array<R
|
||||||
/* 1. Composition Pre Processing */
|
/* 1. Composition Pre Processing */
|
||||||
RenderData trd = nullptr; //composite target render data
|
RenderData trd = nullptr; //composite target render data
|
||||||
RenderRegion viewport;
|
RenderRegion viewport;
|
||||||
Result compFastTrack = Result::InsufficientCondition;
|
auto compFastTrack = false;
|
||||||
|
|
||||||
if (compData) {
|
if (compData) {
|
||||||
auto target = compData->target;
|
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 (!shape->fill() && !(PP(shape)->compData)) {
|
||||||
if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) {
|
if ((method == CompositeMethod::AlphaMask && a == 255 && PP(shape)->opacity == 255) || (method == CompositeMethod::InvAlphaMask && (a == 0 || PP(shape)->opacity == 0))) {
|
||||||
viewport = renderer->viewport();
|
viewport = renderer->viewport();
|
||||||
if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport)) == Result::Success) {
|
if ((compFastTrack = _compFastTrack(renderer, target, pm, viewport))) {
|
||||||
P(target)->ctxFlag |= ContextFlag::FastTrack;
|
P(target)->ctxFlag |= ContextFlag::FastTrack;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (compFastTrack == Result::InsufficientCondition) {
|
if (!compFastTrack) {
|
||||||
trd = P(target)->update(renderer, pm, clips, 255, pFlag, false);
|
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();
|
viewport = renderer->viewport();
|
||||||
/* TODO: Intersect the clipper's clipper, if both are FastTrack.
|
/* TODO: Intersect the clipper's clipper, if both are FastTrack.
|
||||||
Update the subsequent clipper first and check its ctxFlag. */
|
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;
|
pclip->ctxFlag |= ContextFlag::FastTrack;
|
||||||
compFastTrack = Result::Success;
|
compFastTrack = true;
|
||||||
} else {
|
} else {
|
||||||
trd = pclip->update(renderer, pm, clips, 255, pFlag, true);
|
trd = pclip->update(renderer, pm, clips, 255, pFlag, true);
|
||||||
clips.push(trd);
|
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));
|
PAINT_METHOD(rd, update(renderer, tr.cm, clips, opacity, newFlag, clipper));
|
||||||
|
|
||||||
/* 4. Composition Post Processing */
|
/* 4. Composition Post Processing */
|
||||||
if (compFastTrack == Result::Success) renderer->viewport(viewport);
|
if (compFastTrack) renderer->viewport(viewport);
|
||||||
else if (this->clipper) clips.pop();
|
else if (this->clipper) clips.pop();
|
||||||
|
|
||||||
return rd;
|
return rd;
|
||||||
|
|
|
||||||
|
|
@ -74,9 +74,9 @@ namespace tvg
|
||||||
} tr;
|
} tr;
|
||||||
RenderUpdateFlag renderFlag = RenderUpdateFlag::None;
|
RenderUpdateFlag renderFlag = RenderUpdateFlag::None;
|
||||||
BlendMethod blendMethod;
|
BlendMethod blendMethod;
|
||||||
|
uint16_t refCnt = 0; //reference count
|
||||||
uint8_t ctxFlag;
|
uint8_t ctxFlag;
|
||||||
uint8_t opacity;
|
uint8_t opacity;
|
||||||
uint8_t refCnt = 0; //reference count
|
|
||||||
|
|
||||||
Impl(Paint* pnt) : paint(pnt)
|
Impl(Paint* pnt) : paint(pnt)
|
||||||
{
|
{
|
||||||
|
|
@ -95,7 +95,6 @@ namespace tvg
|
||||||
|
|
||||||
uint8_t ref()
|
uint8_t ref()
|
||||||
{
|
{
|
||||||
if (refCnt == 255) TVGERR("RENDERER", "Corrupted reference count!");
|
|
||||||
return ++refCnt;
|
return ++refCnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,10 +97,11 @@ struct Scene::Impl
|
||||||
//Half translucent requires intermediate composition.
|
//Half translucent requires intermediate composition.
|
||||||
if (opacity == 255) return compFlag;
|
if (opacity == 255) return compFlag;
|
||||||
|
|
||||||
//If scene has several children or only scene, it may require composition.
|
//Only shape or picture may not require composition.
|
||||||
//OPTIMIZE: the bitmap type of the picture would not need the composition.
|
if (paints.size() == 1) {
|
||||||
//OPTIMIZE: a single paint of a scene would not need the composition.
|
auto type = paints.front()->type();
|
||||||
if (paints.size() == 1 && paints.front()->type() == Type::Shape) return compFlag;
|
if (type == Type::Shape || type == Type::Picture) return compFlag;
|
||||||
|
}
|
||||||
|
|
||||||
compFlag |= CompositionFlag::Opacity;
|
compFlag |= CompositionFlag::Opacity;
|
||||||
|
|
||||||
|
|
@ -244,7 +245,40 @@ struct Scene::Impl
|
||||||
dup->paints.push_back(cdup);
|
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;
|
return scene;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,7 @@ struct Text::Impl
|
||||||
bool italic = false;
|
bool italic = false;
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
Impl(Text* p) : paint(p), shape(Shape::gen().release())
|
Impl(Text* p) : paint(p), shape(Shape::gen().release()) {}
|
||||||
{
|
|
||||||
shape->fill(FillRule::EvenOdd);
|
|
||||||
}
|
|
||||||
|
|
||||||
~Impl()
|
~Impl()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/bash -e
|
#!/bin/bash -e
|
||||||
|
|
||||||
VERSION=0.15.13
|
VERSION=0.15.16
|
||||||
# Uncomment and set a git hash to use specific commit instead of tag.
|
# Uncomment and set a git hash to use specific commit instead of tag.
|
||||||
#GIT_COMMIT=
|
#GIT_COMMIT=
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue