Not drawing.

This commit is contained in:
K. S. Ernest (iFire) Lee 2021-11-10 08:04:15 -08:00
parent 609389af46
commit bccac9ef00
45 changed files with 3737 additions and 594 deletions

View file

@ -96,6 +96,7 @@ static Ref<Texture2D> flip_icon(Ref<Texture2D> p_texture, bool p_flip_y = false,
Ref<ImageTexture> texture(memnew(ImageTexture));
Ref<Image> img = p_texture->get_image();
ERR_FAIL_NULL_V(img, Ref<Texture2D>());
img = img->duplicate();
if (p_flip_y) {

View file

@ -11,7 +11,6 @@ thirdparty_obj = []
thirdparty_dir = "#thirdparty/thorvg/"
thirdparty_sources = [
"src/lib/tvgAccessor.cpp",
"src/lib/tvgBezier.cpp",
"src/lib/tvgCanvas.cpp",
"src/lib/tvgGlCanvas.cpp",
@ -38,8 +37,8 @@ thirdparty_sources = [
"src/loaders/tvg/tvgTvgBinInterpreter.cpp",
"src/loaders/tvg/tvgTvgLoader.cpp",
"src/loaders/jpg/tvgJpgLoader.cpp",
"src/loaders/jpg/tvgJpgd.cpp",
"src/loaders/png/tvgPngLoader.cpp",
"src/loaders/png/tvgLodePng.cpp",
"src/lib/sw_engine/tvgSwFill.cpp",
"src/lib/sw_engine/tvgSwImage.cpp",
"src/lib/sw_engine/tvgSwMath.cpp",

View file

@ -43,35 +43,35 @@
ImageLoaderSVG::ConvertTVGColorsFunc ImageLoaderSVG::convert_tvg_color_func = ImageLoaderSVG::_convert_tvg_paints;
void ImageLoaderSVG::set_convert_colors(Dictionary *p_replace_color) {
if (p_replace_color) {
Dictionary replace_color = *p_replace_color;
for (int i = 0; i < replace_color.keys().size(); i++) {
Variant o_c = replace_color.keys()[i];
Variant n_c = replace_color[replace_color.keys()[i]];
if (o_c.get_type() == Variant::COLOR && n_c.get_type() == Variant::COLOR) {
Color old_color = o_c;
Color new_color = n_c;
replace_colors.old_colors.push_back(old_color.to_abgr32());
replace_colors.new_colors.push_back(new_color.to_abgr32());
}
}
} else {
replace_colors.old_colors.clear();
replace_colors.new_colors.clear();
}
// if (p_replace_color) {
// Dictionary replace_color = *p_replace_color;
// for (int i = 0; i < replace_color.keys().size(); i++) {
// Variant o_c = replace_color.keys()[i];
// Variant n_c = replace_color[replace_color.keys()[i]];
// if (o_c.get_type() == Variant::COLOR && n_c.get_type() == Variant::COLOR) {
// Color old_color = o_c;
// Color new_color = n_c;
// replace_colors.old_colors.push_back(old_color.to_abgr32());
// replace_colors.new_colors.push_back(new_color.to_abgr32());
// }
// }
// } else {
// replace_colors.old_colors.clear();
// replace_colors.new_colors.clear();
// }
}
void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_string, float p_scale, bool p_upsample, bool p_convert_color) {
ERR_FAIL_COND(Math::is_zero_approx(p_scale));
Vector<uint8_t> data = p_string.to_utf8_buffer();
if (tvg::Initializer::init(tvg::CanvasEngine::Sw, 1) != tvg::Result::Success) {
return;
}
uint32_t bgColor = 0xffffffff;
std::unique_ptr<tvg::Picture> picture = tvg::Picture::gen();
float fw, fh;
if (picture->load((const char *)data.ptr(), data.size()) != tvg::Result::Success) {
CharString char_string = p_string.utf8();
tvg::Result result = picture->load(char_string.get_data(), char_string.size(), "svg") ;
if (result != tvg::Result::Success) {
return;
}
picture->viewbox(nullptr, nullptr, &fw, &fh);
@ -83,7 +83,7 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
std::unique_ptr<tvg::SwCanvas> sw_canvas = tvg::SwCanvas::gen();
uint32_t *buffer = (uint32_t *)malloc(sizeof(uint32_t) * width * height);
tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888);
tvg::Result res = sw_canvas->target(buffer, width, width, height, tvg::SwCanvas::ARGB8888_STRAIGHT);
if (res != tvg::Result::Success) {
return;
}
@ -102,20 +102,17 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
}
}
if (p_convert_color) {
std::unique_ptr<tvg::Accessor>
accessor = tvg::Accessor::gen();
picture = accessor->access(move(picture), convert_tvg_color_func);
}
// if (p_convert_color) {
// std::unique_ptr<tvg::Accessor>
// accessor = tvg::Accessor::gen();
// picture = accessor->access(move(picture), convert_tvg_color_func);
// }
sw_canvas->push(move(picture));
if (sw_canvas->draw() == tvg::Result::Success) {
sw_canvas->sync();
} else {
return;
}
res = sw_canvas->draw();
Vector<uint8_t> image;
image.resize(width * height * sizeof(uint32_t));
sw_canvas->sync();
for (uint32_t y = 0; y < height; y++) {
for (uint32_t x = 0; x < width; x++) {
uint32_t n = buffer[y * width + x];
@ -128,6 +125,7 @@ void ImageLoaderSVG::create_image_from_string(Ref<Image> p_image, String p_strin
free(buffer);
p_image->create(width, height, false, Image::FORMAT_RGBA8, image);
tvg::Initializer::term(tvg::CanvasEngine::Sw);
}
void ImageLoaderSVG::get_recognized_extensions(List<String> *p_extensions) const {
@ -144,73 +142,62 @@ Error ImageLoaderSVG::load_image(Ref<Image> p_image, FileAccess *p_fileaccess, b
return OK;
}
ImageLoaderSVG::ReplaceColors ImageLoaderSVG::replace_colors;
bool ImageLoaderSVG::_convert_tvg_paints(const tvg::Paint *p_paint) {
union ByteColor {
uint32_t i = 0;
uint8_t b[sizeof(float)];
};
if (p_paint->identifier() == tvg::Shape::identifier()) {
tvg::Shape *shape = (tvg::Shape *)p_paint;
uint8_t r = 0;
uint8_t g = 0;
uint8_t b = 0;
uint8_t a = 0;
shape->fillColor(&r, &g, &b, &a);
for (int i = 0; i < replace_colors.old_colors.size(); i++) {
ByteColor old_color = {};
old_color.b[0] = r;
old_color.b[1] = g;
old_color.b[2] = b;
old_color.b[3] = a;
// union ByteColor {
// uint32_t i = 0;
// uint8_t b[sizeof(float)];
// };
// if (p_paint->identifier() == tvg::Shape::identifier()) {
// tvg::Shape *shape = (tvg::Shape *)p_paint;
// uint8_t r = 0;
// uint8_t g = 0;
// uint8_t b = 0;
// uint8_t a = 0;
// shape->fillColor(&r, &g, &b, &a);
// for (int i = 0; i < replace_colors.old_colors.size(); i++) {
// ByteColor old_color = {};
// old_color.b[0] = r;
// old_color.b[1] = g;
// old_color.b[2] = b;
// old_color.b[3] = a;
ByteColor new_color = {};
new_color.i = replace_colors.new_colors[i];
ByteColor replace_color = {};
replace_color.i = replace_colors.new_colors[i];
if (new_color.i == old_color.i) {
shape->fill(replace_color.b[0], replace_color.b[1], replace_color.b[2],
replace_color.b[3]);
return true;
}
}
} else if (p_paint->identifier() == tvg::LinearGradient::identifier() || p_paint->identifier() == tvg::RadialGradient::identifier()) {
tvg::Shape *shape = (tvg::Shape *)p_paint;
if (tvg::Fill *fill = shape->fill()) {
tvg::Fill::ColorStop *colorStop;
uint32_t count = fill->colorStops(&colorStop);
for (int count_i = 0; count_i < count; ++count_i) {
tvg::Fill::ColorStop &p = colorStop[count_i];
for (int i = 0; i < replace_colors.old_colors.size(); i++) {
ByteColor old_color = {};
old_color.b[0] = p.r;
old_color.b[1] = p.g;
old_color.b[2] = p.b;
old_color.b[3] = p.a;
// ByteColor new_color = {};
// new_color.i = replace_colors.new_colors[i];
// ByteColor replace_color = {};
// replace_color.i = replace_colors.new_colors[i];
// if (new_color.i == old_color.i) {
// shape->fill(replace_color.b[0], replace_color.b[1], replace_color.b[2],
// replace_color.b[3]);
// return true;
// }
// }
// } else if (p_paint->identifier() == tvg::LinearGradient::identifier() || p_paint->identifier() == tvg::RadialGradient::identifier()) {
// tvg::Shape *shape = (tvg::Shape *)p_paint;
// if (tvg::Fill *fill = shape->fill()) {
// tvg::Fill::ColorStop *colorStop;
// uint32_t count = fill->colorStops(&colorStop);
// for (int count_i = 0; count_i < count; ++count_i) {
// tvg::Fill::ColorStop &p = colorStop[count_i];
// for (int i = 0; i < replace_colors.old_colors.size(); i++) {
// ByteColor old_color = {};
// old_color.b[0] = p.r;
// old_color.b[1] = p.g;
// old_color.b[2] = p.b;
// old_color.b[3] = p.a;
ByteColor new_color = {};
new_color.i = replace_colors.new_colors[i];
ByteColor replace_color = {};
replace_color.i = replace_colors.new_colors[i];
if (new_color.i == old_color.i) {
p.r = replace_color.b[0];
p.g = replace_color.b[1];
p.b = replace_color.b[2];
p.a = replace_color.b[3];
}
}
}
}
}
// ByteColor new_color = {};
// new_color.i = replace_colors.new_colors[i];
// ByteColor replace_color = {};
// replace_color.i = replace_colors.new_colors[i];
// if (new_color.i == old_color.i) {
// p.r = replace_color.b[0];
// p.g = replace_color.b[1];
// p.b = replace_color.b[2];
// p.a = replace_color.b[3];
// }
// }
// }
// }
// }
return true;
};
ImageLoaderSVG::ImageLoaderSVG() {
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
uint32_t threads = std::thread::hardware_concurrency();
if (threads > 0) {
--threads;
}
if (tvg::Initializer::init(tvgEngine, threads) != tvg::Result::Success) {
return;
}
}

View file

@ -36,10 +36,10 @@
#include "thirdparty/thorvg/inc/thorvg.h"
class ImageLoaderSVG : public ImageFormatLoader {
static struct ReplaceColors {
List<uint32_t> old_colors;
List<uint32_t> new_colors;
} replace_colors;
// static struct ReplaceColors {
// List<uint32_t> old_colors;
// List<uint32_t> new_colors;
// } replace_colors;
public:
typedef bool (*ConvertTVGColorsFunc)(const tvg::Paint *);
@ -51,10 +51,6 @@ public:
virtual Error load_image(Ref<Image> p_image, FileAccess *p_fileaccess, bool p_force_linear, float p_scale) override;
virtual void get_recognized_extensions(List<String> *p_extensions) const override;
ImageLoaderSVG();
virtual ~ImageLoaderSVG() {
tvg::Initializer::term(tvg::CanvasEngine::Sw);
}
};
#endif // IMAGE_LOADER_SVG_H

View file

@ -37,16 +37,10 @@
static ImageLoaderSVG *image_loader_svg = nullptr;
void register_svg_types() {
tvg::CanvasEngine tvgEngine = tvg::CanvasEngine::Sw;
uint32_t threads = std::thread::hardware_concurrency();
if (tvg::Initializer::init(tvgEngine, threads) != tvg::Result::Success) {
return;
}
image_loader_svg = memnew(ImageLoaderSVG);
ImageLoader::add_image_format_loader(image_loader_svg);
}
void unregister_svg_types() {
memdelete(image_loader_svg);
tvg::Initializer::term(tvg::CanvasEngine::Sw);
}

View file

@ -2,10 +2,10 @@
#pragma once
#define THORVG_SVG_LOADER_SUPPORT 1
#define THORVG_SW_RASTER_SUPPORT 1
#define THORVG_SVG_LOADER_SUPPORT 1
#define THORVG_PNG_LOADER_SUPPORT 1
#define THORVG_TVG_LOADER_SUPPORT 1

View file

@ -52,6 +52,7 @@ protected: \
friend Picture; \
friend IteratorAccessor
#define _TVG_DECALRE_IDENTIFIER() \
protected: \
unsigned _id
@ -390,7 +391,7 @@ public:
*
* @return Result::Success when succeed.
*/
Result colorStops(ColorStop* colorStops, uint32_t cnt) noexcept;
Result colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept;
/**
* @brief Sets the FillSpread value, which specifies how to fill the area outside the gradient bounds.
@ -421,7 +422,7 @@ public:
*
* @return The number of colors used in the gradient. This value corresponds to the length of the @p colorStops array.
*/
uint32_t colorStops(ColorStop** colorStops) noexcept;
uint32_t colorStops(const ColorStop** colorStops) const noexcept;
/**
* @brief Gets the FillSpread value of the fill.
@ -985,7 +986,7 @@ public:
*
* @return The pointer to the gradient fill of the stroke when succeed, @c nullptr in case no fill was set.
*/
Fill* fill() noexcept;
const Fill* fill() const noexcept;
/**
* @brief Gets the solid color of the shape.
@ -1030,7 +1031,7 @@ public:
*
* @return The pointer to the gradient fill of the stroke when succeed, @c nullptr otherwise.
*/
Fill* strokeFill() noexcept;
const Fill* strokeFill() const noexcept;
/**
* @brief Gets the dash pattern of the stroke.
@ -1309,8 +1310,10 @@ public:
*/
enum Colorspace
{
ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red.
ARGB8888 ///< The channels are joined in the order: alpha, red, green, blue.
ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied.
ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied.
ABGR8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied.
ARGB8888_STRAIGHT, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied.
};
/**
@ -1518,8 +1521,8 @@ public:
* Thus, if you wish to have a benefit of it, you must call sync() after the save() in the proper delayed time.
* Otherwise, you can call sync() immediately.
*
* @return Result::Success when succeed.
* @return Result::InsufficientCondition otherwise.
* @retval Result::Success when succeed.
* @retval Result::InsufficientCondition otherwise.
*
* @note The asynchronous tasking is dependent on the Saver module implementation.
* @see Saver::save()
@ -1540,49 +1543,6 @@ public:
_TVG_DECLARE_PRIVATE(Saver);
};
/**
* @class Accessor
*
* @brief The Accessor is a utility class to debug the Scene structure by traversing the scene-tree.
*
* The Accessor helps you search specific nodes to read the property information, figure out the structure of the scene tree and its size.
*
* @warning We strongly warn you not to change the paints of a scene unless you really know the design-structure.
*
* @BETA_API
*/
class TVG_EXPORT Accessor final
{
public:
~Accessor();
/**
* @brief Access the Picture scene stree nodes.
*
* @param[in] picture The picture node to traverse the internal scene-tree.
* @param[in] func The callback function calling for every paint nodes of the Picture.
*
* @return Return the given @p picture instance.
*
* @note The bitmap based picture might not have the scene-tree.
*
* @BETA_API
*/
std::unique_ptr<Picture> access(std::unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept;
/**
* @brief Creates a new Accessor object.
*
* @return A new Accessor object.
*
* @BETA_API
*/
static std::unique_ptr<Accessor> gen() noexcept;
_TVG_DECLARE_PRIVATE(Accessor);
};
/** @}*/
} //namespace
@ -1591,4 +1551,4 @@ public:
}
#endif
#endif //_THORVG_H_
#endif //_THORVG_H_

View file

@ -142,9 +142,10 @@ struct SwFill
};
struct SwRadial {
float cx, cy;
float a11, a12, shiftX;
float a21, a22, shiftY;
float detSecDeriv;
float a;
float inva;
};
union {
@ -154,7 +155,6 @@ struct SwFill
uint32_t* ctable;
FillSpread spread;
float sx, sy;
bool translucent;
};
@ -215,7 +215,7 @@ struct SwShape
SwRleData* strokeRle = nullptr;
SwBBox bbox; //Keep it boundary without stroke region. Using for optimal filling.
bool rect = false; //Fast Track: Othogonal rectangle?
bool fastTrack = false; //Fast Track: axis-aligned rectangle without any clips?
};
struct SwImage
@ -298,19 +298,22 @@ SwFixed mathLength(const SwPoint& pt);
bool mathSmallCubic(const SwPoint* base, SwFixed& angleIn, SwFixed& angleMid, SwFixed& angleOut);
SwFixed mathMean(SwFixed angle1, SwFixed angle2);
SwPoint mathTransform(const Point* to, const Matrix* transform);
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion);
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack);
bool mathInverse(const Matrix* m, Matrix* invM);
bool mathMultiply(const Matrix* lhs, Matrix* rhs);
bool mathIdentity(const Matrix* m);
void shapeReset(SwShape* shape);
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite);
bool shapePrepared(const SwShape* shape);
bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias, bool hasComposite);
bool shapeGenRle(SwShape* shape, const Shape* sdata, bool antiAlias);
void shapeDelOutline(SwShape* shape, SwMpool* mpool, uint32_t tid);
void shapeResetStroke(SwShape* shape, const Shape* sdata, const Matrix* transform);
bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid);
void shapeFree(SwShape* shape);
void shapeDelStroke(SwShape* shape);
bool shapeGenFillColors(SwShape* shape, Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
bool shapeGenStrokeFillColors(SwShape* shape, Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
void shapeResetFill(SwShape* shape);
void shapeResetStrokeFill(SwShape* shape);
void shapeDelFill(SwShape* shape);
@ -327,7 +330,7 @@ void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid);
void imageReset(SwImage* image);
void imageFree(SwImage* image);
bool fillGenColorTable(SwFill* fill, Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable);
void fillReset(SwFill* fill);
void fillFree(SwFill* fill);
void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len);
@ -355,5 +358,6 @@ bool rasterStroke(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g, uint
bool rasterGradientStroke(SwSurface* surface, SwShape* shape, unsigned id);
bool rasterClear(SwSurface* surface);
void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len);
void rasterUnpremultiply(SwSurface* surface);
#endif /* _TVG_SW_COMMON_H_ */

View file

@ -33,14 +33,14 @@
#define FIXPT_SIZE (1<<FIXPT_BITS)
static bool _updateColorTable(SwFill* fill, Fill* fdata, const SwSurface* surface, uint32_t opacity)
static bool _updateColorTable(SwFill* fill, const Fill* fdata, const SwSurface* surface, uint32_t opacity)
{
if (!fill->ctable) {
fill->ctable = static_cast<uint32_t*>(malloc(GRADIENT_STOP_SIZE * sizeof(uint32_t)));
if (!fill->ctable) return false;
}
Fill::ColorStop* colors;
const Fill::ColorStop* colors;
auto cnt = fdata->colorStops(&colors);
if (cnt == 0 || !colors) return false;
@ -106,15 +106,6 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
float x1, x2, y1, y2;
if (linear->linear(&x1, &y1, &x2, &y2) != Result::Success) return false;
if (transform) {
auto t1 = x1;
x1 = t1 * transform->e11 + y1 * transform->e12 + transform->e13;
y1 = t1 * transform->e21 + y1 * transform->e22 + transform->e23;
auto t2 = x2;
x2 = t2 * transform->e11 + y2 * transform->e12 + transform->e13;
y2 = t2 * transform->e21 + y2 * transform->e22 + transform->e23;
}
fill->linear.dx = x2 - x1;
fill->linear.dy = y2 - y1;
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
@ -123,7 +114,31 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
fill->linear.dx /= fill->linear.len;
fill->linear.dy /= fill->linear.len;
fill->linear.offset = -fill->linear.dx * x1 -fill->linear.dy * y1;
fill->linear.offset = -fill->linear.dx * x1 - fill->linear.dy * y1;
auto gradTransform = linear->transform();
bool isTransformation = !mathIdentity(&gradTransform);
if (isTransformation) {
if (transform) mathMultiply(transform, &gradTransform);
} else if (transform) {
gradTransform = *transform;
isTransformation = true;
}
if (isTransformation) {
Matrix invTransform;
if (!mathInverse(&gradTransform, &invTransform)) return false;
fill->linear.offset += fill->linear.dx * invTransform.e13 + fill->linear.dy * invTransform.e23;
auto dx = fill->linear.dx;
fill->linear.dx = dx * invTransform.e11 + fill->linear.dy * invTransform.e21;
fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22;
fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy;
if (fill->linear.len < FLT_EPSILON) return true;
}
return true;
}
@ -131,33 +146,45 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr
bool _prepareRadial(SwFill* fill, const RadialGradient* radial, const Matrix* transform)
{
float radius;
if (radial->radial(&fill->radial.cx, &fill->radial.cy, &radius) != Result::Success) return false;
float radius, cx, cy;
if (radial->radial(&cx, &cy, &radius) != Result::Success) return false;
if (radius < FLT_EPSILON) return true;
fill->sx = 1.0f;
fill->sy = 1.0f;
float invR = 1.0f / radius;
fill->radial.shiftX = -cx;
fill->radial.shiftY = -cy;
fill->radial.a = radius;
if (transform) {
auto tx = fill->radial.cx * transform->e11 + fill->radial.cy * transform->e12 + transform->e13;
auto ty = fill->radial.cx * transform->e21 + fill->radial.cy * transform->e22 + transform->e23;
fill->radial.cx = tx;
fill->radial.cy = ty;
auto gradTransform = radial->transform();
bool isTransformation = !mathIdentity(&gradTransform);
auto sx = sqrtf(powf(transform->e11, 2.0f) + powf(transform->e21, 2.0f));
auto sy = sqrtf(powf(transform->e12, 2.0f) + powf(transform->e22, 2.0f));
//FIXME; Scale + Rotation is not working properly
radius *= sx;
if (fabsf(sx - sy) > FLT_EPSILON) {
fill->sx = sx;
fill->sy = sy;
}
if (isTransformation) {
if (transform) mathMultiply(transform, &gradTransform);
} else if (transform) {
gradTransform = *transform;
isTransformation = true;
}
fill->radial.a = radius * radius;
fill->radial.inva = 1.0 / fill->radial.a;
if (isTransformation) {
Matrix invTransform;
if (!mathInverse(&gradTransform, &invTransform)) return false;
fill->radial.a11 = invTransform.e11 * invR;
fill->radial.a12 = invTransform.e12 * invR;
fill->radial.shiftX += invTransform.e13;
fill->radial.a21 = invTransform.e21 * invR;
fill->radial.a22 = invTransform.e22 * invR;
fill->radial.shiftY += invTransform.e23;
fill->radial.detSecDeriv = 2.0f * fill->radial.a11 * fill->radial.a11 + 2 * fill->radial.a21 * fill->radial.a21;
fill->radial.a *= sqrt(pow(invTransform.e11, 2) + pow(invTransform.e21, 2));
} else {
fill->radial.a11 = fill->radial.a22 = invR;
fill->radial.a12 = fill->radial.a21 = 0.0f;
fill->radial.detSecDeriv = 2.0f * invR * invR;
}
fill->radial.shiftX *= invR;
fill->radial.shiftY *= invR;
return true;
}
@ -208,21 +235,20 @@ static inline uint32_t _pixel(const SwFill* fill, float pos)
void fillFetchRadial(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x, uint32_t len)
{
//Rotation
auto rx = (x + 0.5f - fill->radial.cx) * fill->sy;
auto ry = (y + 0.5f - fill->radial.cy) * fill->sx;
auto rxy = rx * rx + ry * ry;
auto rxryPlus = 2 * rx;
auto inva = fill->radial.inva;
auto det = rxy * inva;
auto detDelta = (rxryPlus + 1.0f) * inva;
auto detDelta2 = 2.0f * inva;
auto rx = (x + 0.5f) * fill->radial.a11 + (y + 0.5f) * fill->radial.a12 + fill->radial.shiftX;
auto ry = (x + 0.5f) * fill->radial.a21 + (y + 0.5f) * fill->radial.a22 + fill->radial.shiftY;
// detSecondDerivative = d(detFirstDerivative)/dx = d( d(det)/dx )/dx
auto detSecondDerivative = fill->radial.detSecDeriv;
// detFirstDerivative = d(det)/dx
auto detFirstDerivative = 2.0f * (fill->radial.a11 * rx + fill->radial.a21 * ry) + 0.5f * detSecondDerivative;
auto det = rx * rx + ry * ry;
for (uint32_t i = 0 ; i < len ; ++i) {
*dst = _pixel(fill, sqrtf(det));
++dst;
det += detDelta;
detDelta += detDelta2;
det += detFirstDerivative;
detFirstDerivative += detSecondDerivative;
}
}
@ -266,7 +292,7 @@ void fillFetchLinear(const SwFill* fill, uint32_t* dst, uint32_t y, uint32_t x,
}
bool fillGenColorTable(SwFill* fill, Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
bool fillGenColorTable(SwFill* fill, const Fill* fdata, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
{
if (!fill) return false;

View file

@ -26,7 +26,7 @@
/* Internal Class Implementation */
/************************************************************************/
static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid)
static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool, unsigned tid)
{
image->outline = mpoolReqOutline(mpool, tid);
auto outline = image->outline;
@ -75,7 +75,7 @@ static bool _genOutline(SwImage* image, const Matrix* transform, SwMpool* mpool,
bool imagePrepare(SwImage* image, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
{
if (!_genOutline(image, transform, mpool, tid)) return false;
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion);
return mathUpdateOutlineBBox(image->outline, clipRegion, renderRegion, false);
}
@ -97,7 +97,6 @@ void imageDelOutline(SwImage* image, SwMpool* mpool, uint32_t tid)
void imageReset(SwImage* image)
{
rleReset(image->rle);
image->rle = nullptr;
}

View file

@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include <math.h>
#include <float.h>
#include "tvgSwCommon.h"
@ -442,7 +443,7 @@ SwPoint mathTransform(const Point* to, const Matrix* transform)
}
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion)
bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, SwBBox& renderRegion, bool fastTrack)
{
if (!outline) return false;
@ -466,10 +467,21 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
if (yMin > pt->y) yMin = pt->y;
if (yMax < pt->y) yMax = pt->y;
}
renderRegion.min.x = xMin >> 6;
renderRegion.max.x = (xMax + 63) >> 6;
renderRegion.min.y = yMin >> 6;
renderRegion.max.y = (yMax + 63) >> 6;
//Since no antialiasing is applied in the Fast Track case,
//the rasterization region has to be rearranged.
//https://github.com/Samsung/thorvg/issues/916
if (fastTrack) {
renderRegion.min.x = static_cast<SwCoord>(round(xMin / 64.0f));
renderRegion.max.x = static_cast<SwCoord>(round(xMax / 64.0f));
renderRegion.min.y = static_cast<SwCoord>(round(yMin / 64.0f));
renderRegion.max.y = static_cast<SwCoord>(round(yMax / 64.0f));
} else {
renderRegion.min.x = xMin >> 6;
renderRegion.max.x = (xMax + 63) >> 6;
renderRegion.min.y = yMin >> 6;
renderRegion.max.y = (yMax + 63) >> 6;
}
renderRegion.max.x = (renderRegion.max.x < clipRegion.max.x) ? renderRegion.max.x : clipRegion.max.x;
renderRegion.max.y = (renderRegion.max.y < clipRegion.max.y) ? renderRegion.max.y : clipRegion.max.y;
@ -485,3 +497,63 @@ bool mathUpdateOutlineBBox(const SwOutline* outline, const SwBBox& clipRegion, S
return true;
}
bool mathInverse(const Matrix* m, Matrix* invM)
{
auto det = m->e11 * (m->e22 * m->e33 - m->e32 * m->e23) -
m->e12 * (m->e21 * m->e33 - m->e23 * m->e31) +
m->e13 * (m->e21 * m->e32 - m->e22 * m->e31);
if (fabsf(det) < FLT_EPSILON) return false;
auto invDet = 1 / det;
invM->e11 = (m->e22 * m->e33 - m->e32 * m->e23) * invDet;
invM->e12 = (m->e13 * m->e32 - m->e12 * m->e33) * invDet;
invM->e13 = (m->e12 * m->e23 - m->e13 * m->e22) * invDet;
invM->e21 = (m->e23 * m->e31 - m->e21 * m->e33) * invDet;
invM->e22 = (m->e11 * m->e33 - m->e13 * m->e31) * invDet;
invM->e23 = (m->e21 * m->e13 - m->e11 * m->e23) * invDet;
invM->e31 = (m->e21 * m->e32 - m->e31 * m->e22) * invDet;
invM->e32 = (m->e31 * m->e12 - m->e11 * m->e32) * invDet;
invM->e33 = (m->e11 * m->e22 - m->e21 * m->e12) * invDet;
return true;
}
bool mathMultiply(const Matrix* lhs, Matrix* rhs)
{
Matrix m;
m.e11 = lhs->e11 * rhs->e11 + lhs->e12 * rhs->e21 + lhs->e13 * rhs->e31;
m.e12 = lhs->e11 * rhs->e12 + lhs->e12 * rhs->e22 + lhs->e13 * rhs->e32;
m.e13 = lhs->e11 * rhs->e13 + lhs->e12 * rhs->e23 + lhs->e13 * rhs->e33;
m.e21 = lhs->e21 * rhs->e11 + lhs->e22 * rhs->e21 + lhs->e23 * rhs->e31;
m.e22 = lhs->e21 * rhs->e12 + lhs->e22 * rhs->e22 + lhs->e23 * rhs->e32;
m.e23 = lhs->e21 * rhs->e13 + lhs->e22 * rhs->e23 + lhs->e23 * rhs->e33;
m.e31 = lhs->e31 * rhs->e11 + lhs->e32 * rhs->e21 + lhs->e33 * rhs->e31;
m.e32 = lhs->e31 * rhs->e12 + lhs->e32 * rhs->e22 + lhs->e33 * rhs->e32;
m.e33 = lhs->e31 * rhs->e13 + lhs->e32 * rhs->e23 + lhs->e33 * rhs->e33;
*rhs = m;
return true;
}
bool mathIdentity(const Matrix* m)
{
if (m) {
if (m->e11 != 1.0f || m->e12 != 0.0f || m->e13 != 0.0f ||
m->e21 != 0.0f || m->e22 != 1.0f || m->e23 != 0.0f ||
m->e31 != 0.0f || m->e32 != 0.0f || m->e33 != 1.0f) {
return false;
}
}
return true;
}

View file

@ -59,11 +59,9 @@ void mpoolRetStrokeOutline(SwMpool* mpool, unsigned idx)
SwMpool* mpoolInit(unsigned threads)
{
auto mpool = new SwMpool;
if (!mpool) return nullptr;
if (threads == 0) threads = 1;
auto mpool = new SwMpool;
mpool->outline = static_cast<SwOutline*>(calloc(1, sizeof(SwOutline) * threads));
if (!mpool->outline) goto err;

View file

@ -49,45 +49,6 @@ static uint32_t _argbJoin(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
}
static bool _inverse(const Matrix* transform, Matrix* invM)
{
//computes the inverse of a matrix m
auto det = transform->e11 * (transform->e22 * transform->e33 - transform->e32 * transform->e23) -
transform->e12 * (transform->e21 * transform->e33 - transform->e23 * transform->e31) +
transform->e13 * (transform->e21 * transform->e32 - transform->e22 * transform->e31);
if (fabsf(det) < FLT_EPSILON) return false;
auto invDet = 1 / det;
invM->e11 = (transform->e22 * transform->e33 - transform->e32 * transform->e23) * invDet;
invM->e12 = (transform->e13 * transform->e32 - transform->e12 * transform->e33) * invDet;
invM->e13 = (transform->e12 * transform->e23 - transform->e13 * transform->e22) * invDet;
invM->e21 = (transform->e23 * transform->e31 - transform->e21 * transform->e33) * invDet;
invM->e22 = (transform->e11 * transform->e33 - transform->e13 * transform->e31) * invDet;
invM->e23 = (transform->e21 * transform->e13 - transform->e11 * transform->e23) * invDet;
invM->e31 = (transform->e21 * transform->e32 - transform->e31 * transform->e22) * invDet;
invM->e32 = (transform->e31 * transform->e12 - transform->e11 * transform->e32) * invDet;
invM->e33 = (transform->e11 * transform->e22 - transform->e21 * transform->e12) * invDet;
return true;
}
static bool _identify(const Matrix* transform)
{
if (transform) {
if (transform->e11 != 1.0f || transform->e12 != 0.0f || transform->e13 != 0.0f ||
transform->e21 != 0.0f || transform->e22 != 1.0f || transform->e23 != 0.0f ||
transform->e31 != 0.0f || transform->e32 != 0.0f || transform->e33 != 1.0f) {
return false;
}
}
return true;
}
static bool _translucent(const SwSurface* surface, uint8_t a)
{
if (a < 255) return true;
@ -1438,10 +1399,10 @@ void rasterRGBA32(uint32_t *dst, uint32_t val, uint32_t offset, int32_t len)
bool rasterCompositor(SwSurface* surface)
{
if (surface->cs == SwCanvas::ABGR8888) {
if (surface->cs == SwCanvas::ABGR8888 || surface->cs == SwCanvas::ABGR8888_STRAIGHT) {
surface->blender.alpha = _colorAlpha;
surface->blender.join = _abgrJoin;
} else if (surface->cs == SwCanvas::ARGB8888) {
} else if (surface->cs == SwCanvas::ARGB8888 || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
surface->blender.alpha = _colorAlpha;
surface->blender.join = _argbJoin;
} else {
@ -1460,7 +1421,7 @@ bool rasterGradientShape(SwSurface* surface, SwShape* shape, unsigned id)
auto translucent = shape->fill->translucent || (surface->compositor && surface->compositor->method != CompositeMethod::None);
//Fast Track
if (shape->rect) {
if (shape->fastTrack) {
if (id == TVG_CLASS_ID_LINEAR) {
if (translucent) return _rasterTranslucentLinearGradientRect(surface, shape->bbox, shape->fill);
return _rasterOpaqueLinearGradientRect(surface, shape->bbox, shape->fill);
@ -1494,7 +1455,7 @@ bool rasterSolidShape(SwSurface* surface, SwShape* shape, uint8_t r, uint8_t g,
auto translucent = _translucent(surface, a);
//Fast Track
if (shape->rect) {
if (shape->fastTrack) {
if (translucent) return _rasterTranslucentRect(surface, shape->bbox, color);
return _rasterSolidRect(surface, shape->bbox, color);
}
@ -1554,13 +1515,38 @@ bool rasterClear(SwSurface* surface)
}
void rasterUnpremultiply(SwSurface* surface)
{
//TODO: Create simd avx and neon version
for (uint32_t y = 0; y < surface->h; y++) {
auto buffer = surface->buffer + surface->stride * y;
for (uint32_t x = 0; x < surface->w; ++x) {
uint8_t a = buffer[x] >> 24;
if (a == 255) {
continue;
} else if (a == 0) {
buffer[x] = 0x00ffffff;
} else {
uint16_t r = ((buffer[x] >> 8) & 0xff00) / a;
uint16_t g = ((buffer[x]) & 0xff00) / a;
uint16_t b = ((buffer[x] << 8) & 0xff00) / a;
if (r > 0xff) r = 0xff;
if (g > 0xff) g = 0xff;
if (b > 0xff) b = 0xff;
buffer[x] = (a << 24) | (r << 16) | (g << 8) | (b);
}
}
}
}
bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, const SwBBox& bbox, uint32_t opacity)
{
Matrix invTransform;
float scaling = 1.0f;
if (transform) {
if (!_inverse(transform, &invTransform)) return false;
if (!mathInverse(transform, &invTransform)) return false;
scaling = sqrtf((transform->e11 * transform->e11) + (transform->e21 * transform->e21));
auto scalingY = sqrtf((transform->e22 * transform->e22) + (transform->e12 * transform->e12));
//TODO:If the x and y axis scaling is different, a separate algorithm for each axis should be applied.
@ -1573,7 +1559,7 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co
if (image->rle) {
//Fast track
if (_identify(transform)) {
if (mathIdentity(transform)) {
//OPTIMIZE ME: Support non transformed image. Only shifted image can use these routines.
if (translucent) return _rasterTranslucentImageRle(surface, image->rle, image->data, image->w, image->h, opacity);
return _rasterImageRle(surface, image->rle, image->data, image->w, image->h);
@ -1590,7 +1576,7 @@ bool rasterImage(SwSurface* surface, SwImage* image, const Matrix* transform, co
}
else {
//Fast track
if (_identify(transform)) {
if (mathIdentity(transform)) {
//OPTIMIZE ME: Support non transformed image. Only shifted image can use these routines.
if (translucent) return _rasterTranslucentImage(surface, image->data, image->w, image->h, opacity, bbox);
return _rasterImage(surface, image->data, image->w, image->h, bbox);

View file

@ -62,7 +62,7 @@ struct SwTask : Task
struct SwShapeTask : SwTask
{
SwShape shape;
Shape* sdata = nullptr;
const Shape* sdata = nullptr;
bool cmpStroking = false;
void run(unsigned tid) override
@ -91,7 +91,7 @@ struct SwShapeTask : SwTask
visibleFill = (alpha > 0 || sdata->fill());
if (visibleFill || visibleStroke) {
shapeReset(&shape);
if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid)) goto err;
if (!shapePrepare(&shape, sdata, transform, clipRegion, bbox, mpool, tid, clips.count > 0 ? true : false)) goto err;
}
}
@ -107,7 +107,7 @@ struct SwShapeTask : SwTask
Thus it turns off antialising in that condition.
Also, it shouldn't be dash style. */
auto antiAlias = (strokeAlpha == 255 && sdata->strokeWidth() > 2 && sdata->strokeDash(nullptr) == 0) ? false : true;
if (!shapeGenRle(&shape, sdata, antiAlias, clips.count > 0 ? true : false)) goto err;
if (!shapeGenRle(&shape, sdata, antiAlias)) goto err;
}
if (auto fill = sdata->fill()) {
auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false;
@ -141,13 +141,13 @@ struct SwShapeTask : SwTask
auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
//Clip shape rle
if (shape.rle) {
if (clipper->rect) rleClipRect(shape.rle, &clipper->bbox);
if (clipper->fastTrack) rleClipRect(shape.rle, &clipper->bbox);
else if (clipper->rle) rleClipPath(shape.rle, clipper->rle);
else goto err;
}
//Clip stroke rle
if (shape.strokeRle) {
if (clipper->rect) rleClipRect(shape.strokeRle, &clipper->bbox);
if (clipper->fastTrack) rleClipRect(shape.strokeRle, &clipper->bbox);
else if (clipper->rle) rleClipPath(shape.strokeRle, clipper->rle);
else goto err;
}
@ -192,7 +192,7 @@ struct SwImageTask : SwTask
if (image.rle) {
for (auto clip = clips.data; clip < (clips.data + clips.count); ++clip) {
auto clipper = &static_cast<SwShapeTask*>(*clip)->shape;
if (clipper->rect) rleClipRect(image.rle, &clipper->bbox);
if (clipper->fastTrack) rleClipRect(image.rle, &clipper->bbox);
else if (clipper->rle) rleClipPath(image.rle, clipper->rle);
else goto err;
}
@ -283,10 +283,7 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
{
if (!buffer || stride == 0 || w == 0 || h == 0 || w > stride) return false;
if (!surface) {
surface = new SwSurface;
if (!surface) return false;
}
if (!surface) surface = new SwSurface;
surface->buffer = buffer;
surface->stride = stride;
@ -321,6 +318,11 @@ void SwRenderer::clearCompositors()
bool SwRenderer::postRender()
{
//Unmultiply alpha if needed
if (surface->cs == SwCanvas::ABGR8888_STRAIGHT || surface->cs == SwCanvas::ARGB8888_STRAIGHT) {
rasterUnpremultiply(surface);
}
tasks.clear();
clearCompositors();
return true;
@ -597,7 +599,7 @@ RenderData SwRenderer::prepare(const Picture& pdata, RenderData data, const Rend
}
RenderData SwRenderer::prepare(Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
RenderData SwRenderer::prepare(const Shape& sdata, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags)
{
//prepare task
auto task = static_cast<SwShapeTask*>(data);

View file

@ -35,7 +35,7 @@ namespace tvg
class SwRenderer : public RenderMethod
{
public:
RenderData prepare(Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) override;
bool preRender() override;
bool renderShape(RenderData data) override;

View file

@ -22,6 +22,7 @@
#include "tvgSwCommon.h"
#include "tvgBezier.h"
#include <float.h>
#include <math.h>
/************************************************************************/
/* Internal Class Implementation */
@ -359,9 +360,9 @@ static SwOutline* _genDashOutline(const Shape* sdata, const Matrix* transform)
}
static bool _fastTrack(const SwOutline* outline)
static bool _axisAlignedRect(const SwOutline* outline)
{
//Fast Track: Othogonal rectangle?
//Fast Track: axis-aligned rectangle?
if (outline->ptsCnt != 5) return false;
auto pt1 = outline->pts + 0;
@ -379,7 +380,7 @@ static bool _fastTrack(const SwOutline* outline)
static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid)
static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transform, SwMpool* mpool, unsigned tid, bool hasComposite)
{
const PathCommand* cmds = nullptr;
auto cmdCnt = sdata->pathCommands(&cmds);
@ -468,6 +469,7 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf
outline->fillRule = sdata->fillRule();
shape->outline = outline;
shape->fastTrack = (!hasComposite && _axisAlignedRect(shape->outline));
return true;
}
@ -476,10 +478,10 @@ static bool _genOutline(SwShape* shape, const Shape* sdata, const Matrix* transf
/* External Class Implementation */
/************************************************************************/
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid)
bool shapePrepare(SwShape* shape, const Shape* sdata, const Matrix* transform, const SwBBox& clipRegion, SwBBox& renderRegion, SwMpool* mpool, unsigned tid, bool hasComposite)
{
if (!_genOutline(shape, sdata, transform, mpool, tid)) return false;
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion)) return false;
if (!_genOutline(shape, sdata, transform, mpool, tid, hasComposite)) return false;
if (!mathUpdateOutlineBBox(shape->outline, clipRegion, renderRegion, shape->fastTrack)) return false;
//Keep it for Rasterization Region
shape->bbox = renderRegion;
@ -501,15 +503,16 @@ bool shapePrepared(const SwShape* shape)
}
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias, bool hasComposite)
bool shapeGenRle(SwShape* shape, TVG_UNUSED const Shape* sdata, bool antiAlias)
{
//FIXME: Should we draw it?
//Case: Stroke Line
//if (shape.outline->opened) return true;
//Case A: Fast Track Rectangle Drawing
if (!hasComposite && (shape->rect = _fastTrack(shape->outline))) return true;
//Case B: Normale Shape RLE Drawing
if (shape->fastTrack) return true;
//Case B: Normal Shape RLE Drawing
if ((shape->rle = rleRender(shape->rle, shape->outline, shape->bbox, antiAlias))) return true;
return false;
@ -527,7 +530,7 @@ void shapeReset(SwShape* shape)
{
rleReset(shape->rle);
rleReset(shape->strokeRle);
shape->rect = false;
shape->fastTrack = false;
shape->bbox.reset();
}
@ -580,7 +583,7 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transfo
//Normal Style stroke
} else {
if (!shape->outline) {
if (!_genOutline(shape, sdata, transform, mpool, tid)) return false;
if (!_genOutline(shape, sdata, transform, mpool, tid, false)) return false;
}
shapeOutline = shape->outline;
}
@ -596,7 +599,7 @@ bool shapeGenStrokeRle(SwShape* shape, const Shape* sdata, const Matrix* transfo
goto fail;
}
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion)) {
if (!mathUpdateOutlineBBox(strokeOutline, clipRegion, renderRegion, false)) {
ret = false;
goto fail;
}
@ -617,13 +620,13 @@ fail:
}
bool shapeGenFillColors(SwShape* shape, Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
bool shapeGenFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
{
return fillGenColorTable(shape->fill, fill, transform, surface, opacity, ctable);
}
bool shapeGenStrokeFillColors(SwShape* shape, Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
bool shapeGenStrokeFillColors(SwShape* shape, const Fill* fill, const Matrix* transform, SwSurface* surface, uint32_t opacity, bool ctable)
{
return fillGenColorTable(shape->stroke->fill, fill, transform, surface, opacity, ctable);
}

View file

@ -1,87 +0,0 @@
/*
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "tvgIteratorAccessor.h"
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
static bool accessChildren(Iterator* it, bool(*func)(const Paint* paint), IteratorAccessor& itrAccessor)
{
while (auto child = it->next()) {
//Access the child
if (!func(child)) return false;
//Access the children of the child
if (auto it2 = itrAccessor.iterator(child)) {
if (!accessChildren(it2, func, itrAccessor)) {
delete(it2);
return false;
}
delete(it2);
}
}
return true;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
unique_ptr<Picture> Accessor::access(unique_ptr<Picture> picture, bool(*func)(const Paint* paint)) noexcept
{
auto p = picture.get();
if (!p || !func) return picture;
//Use the Preorder Tree-Search
//Root
if (!func(p)) return picture;
//Children
IteratorAccessor itrAccessor;
if (auto it = itrAccessor.iterator(p)) {
accessChildren(it, func, itrAccessor);
delete(it);
}
return picture;
}
Accessor::~Accessor()
{
}
Accessor::Accessor()
{
}
unique_ptr<Accessor> Accessor::gen() noexcept
{
return unique_ptr<Accessor>(new Accessor);
}

View file

@ -87,6 +87,7 @@ using TvgBinFlag = TvgBinByte;
#define TVG_TAG_FILL_RADIAL_GRADIENT (TvgBinTag)0x61
#define TVG_TAG_FILL_COLORSTOPS (TvgBinTag)0x62
#define TVG_TAG_FILL_FILLSPREAD (TvgBinTag)0x63
#define TVG_TAG_FILL_TRANSFORM (TvgBinTag)0x64
//Picture

View file

@ -48,7 +48,7 @@ using namespace tvg;
#define TVG_FALLTHROUGH
#endif
#if defined(__clang__) && !defined(__EMSCRIPTEN__)
#if defined(_MSC_VER)
#define strncpy strncpy_s
#define strdup _strdup
#endif

View file

@ -41,7 +41,7 @@ Fill::~Fill()
}
Result Fill::colorStops(ColorStop* colorStops, uint32_t cnt) noexcept
Result Fill::colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept
{
if ((!colorStops && cnt > 0) || (colorStops && cnt == 0)) return Result::InvalidArguments;
@ -65,7 +65,7 @@ Result Fill::colorStops(ColorStop* colorStops, uint32_t cnt) noexcept
}
uint32_t Fill::colorStops(ColorStop** colorStops) noexcept
uint32_t Fill::colorStops(const ColorStop** colorStops) const noexcept
{
if (colorStops) *colorStops = pImpl->colorStops;
@ -90,7 +90,6 @@ FillSpread Fill::spread() const noexcept
Result Fill::transform(const Matrix& m) noexcept
{
if (!pImpl->transform) pImpl->transform = new Matrix();
if (!pImpl->transform) return Result::FailedAllocation;
*pImpl->transform = m;
return Result::Success;
}

View file

@ -78,7 +78,7 @@ struct Fill::Impl
memcpy(ret->pImpl->colorStops, colorStops, sizeof(ColorStop) * cnt);
if (transform) {
ret->pImpl->transform = new Matrix;
if (ret->pImpl->transform) *ret->pImpl->transform = *transform;
*ret->pImpl->transform = *transform;
}
return ret;
}

View file

@ -149,4 +149,4 @@ Result Initializer::term(CanvasEngine engine) noexcept
uint16_t THORVG_VERSION_NUMBER()
{
return _version;
}
}

View file

@ -206,9 +206,8 @@ shared_ptr<LoadModule> LoaderMgr::loader(const uint32_t *data, uint32_t w, uint3
{
//function is dedicated for raw images only
auto loader = new RawLoader;
if (loader) {
if (loader->open(data, w, h, copy)) return shared_ptr<LoadModule>(loader);
else delete(loader);
}
if (loader->open(data, w, h, copy)) return shared_ptr<LoadModule>(loader);
else delete(loader);
return nullptr;
}

View file

@ -242,7 +242,6 @@ struct BitStreamReader
}
};
namespace {
struct Dictionary
{
@ -305,8 +304,6 @@ struct Dictionary
}
};
}
static bool outputByte(int code, uint8_t*& output, int outputSizeBytes, int& bytesDecodedSoFar)
{

View file

@ -99,10 +99,8 @@ Paint* Paint::Impl::duplicate()
//duplicate Transform
if (rTransform) {
ret->pImpl->rTransform = new RenderTransform();
if (ret->pImpl->rTransform) {
*ret->pImpl->rTransform = *rTransform;
ret->pImpl->flag |= RenderUpdateFlag::Transform;
}
*ret->pImpl->rTransform = *rTransform;
ret->pImpl->flag |= RenderUpdateFlag::Transform;
}
ret->pImpl->opacity = opacity;
@ -122,7 +120,6 @@ bool Paint::Impl::rotate(float degree)
} else {
if (fabsf(degree) <= FLT_EPSILON) return true;
rTransform = new RenderTransform();
if (!rTransform) return false;
}
rTransform->degree = degree;
if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
@ -138,7 +135,6 @@ bool Paint::Impl::scale(float factor)
} else {
if (fabsf(factor) <= FLT_EPSILON) return true;
rTransform = new RenderTransform();
if (!rTransform) return false;
}
rTransform->scale = factor;
if (!rTransform->overriding) flag |= RenderUpdateFlag::Transform;
@ -154,7 +150,6 @@ bool Paint::Impl::translate(float x, float y)
} else {
if (fabsf(x) <= FLT_EPSILON && fabsf(y) <= FLT_EPSILON) return true;
rTransform = new RenderTransform();
if (!rTransform) return false;
}
rTransform->x = x;
rTransform->y = y;

View file

@ -83,7 +83,7 @@ class RenderMethod
{
public:
virtual ~RenderMethod() {}
virtual RenderData prepare(Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual RenderData prepare(const Shape& shape, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual RenderData prepare(const Picture& picture, RenderData data, const RenderTransform* transform, uint32_t opacity, Array<RenderData>& clips, RenderUpdateFlag flags) = 0;
virtual bool preRender() = 0;
virtual bool renderShape(RenderData data) = 0;

View file

@ -306,7 +306,7 @@ Result Shape::fillColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a) const no
return Result::Success;
}
Fill* Shape::fill() noexcept
const Fill* Shape::fill() const noexcept
{
return pImpl->fill;
}
@ -354,7 +354,7 @@ Result Shape::stroke(unique_ptr<Fill> f) noexcept
}
Fill* Shape::strokeFill() noexcept
const Fill* Shape::strokeFill() const noexcept
{
if (!pImpl->stroke) return nullptr;

View file

@ -261,8 +261,6 @@ struct Shape::Impl
//TODO: Size Exception?
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
stroke->width = width;
flag |= RenderUpdateFlag::Stroke;
@ -272,8 +270,6 @@ struct Shape::Impl
bool strokeCap(StrokeCap cap)
{
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
stroke->cap = cap;
flag |= RenderUpdateFlag::Stroke;
@ -283,8 +279,6 @@ struct Shape::Impl
bool strokeJoin(StrokeJoin join)
{
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
stroke->join = join;
flag |= RenderUpdateFlag::Stroke;
@ -294,8 +288,6 @@ struct Shape::Impl
bool strokeColor(uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
if (stroke->fill) {
delete(stroke->fill);
stroke->fill = nullptr;
@ -318,8 +310,6 @@ struct Shape::Impl
if (!p) return Result::MemoryCorruption;
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return Result::FailedAllocation;
if (stroke->fill && stroke->fill != p) delete(stroke->fill);
stroke->fill = p;
@ -337,8 +327,6 @@ struct Shape::Impl
stroke->dashPattern = nullptr;
} else {
if (!stroke) stroke = new ShapeStroke();
if (!stroke) return false;
if (stroke->dashCnt != cnt) {
free(stroke->dashPattern);
stroke->dashPattern = nullptr;

View file

@ -20,6 +20,7 @@
* SOFTWARE.
*/
#include <memory.h>
#include "tvgLoader.h"
#include "tvgJpgLoader.h"
@ -27,58 +28,96 @@
/* Internal Class Implementation */
/************************************************************************/
void JpgLoader::clear()
{
jpgdDelete(decoder);
if (freeData) free(data);
decoder = nullptr;
data = nullptr;
freeData = false;
}
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
JpgLoader::JpgLoader()
{
//TODO:
}
JpgLoader::~JpgLoader()
{
//TODO:
jpgdDelete(decoder);
if (freeData) free(data);
}
bool JpgLoader::open(const string& path)
{
//TODO:
clear();
return false;
int width, height;
decoder = jpgdHeader(path.c_str(), &width, &height);
if (!decoder) return false;
w = static_cast<float>(width);
h = static_cast<float>(height);
return true;
}
bool JpgLoader::open(const char* data, uint32_t size, bool copy)
{
//TODO:
clear();
return false;
if (copy) {
this->data = (char *) malloc(size);
if (!this->data) return false;
memcpy((char *)this->data, data, size);
freeData = true;
} else {
this->data = (char *) data;
freeData = false;
}
int width, height;
decoder = jpgdHeader(this->data, size, &width, &height);
if (!decoder) return false;
w = static_cast<float>(width);
h = static_cast<float>(height);
return true;
}
bool JpgLoader::read()
{
//TODO:
if (!decoder || w <= 0 || h <= 0) return false;
return false;
TaskScheduler::request(this);
return true;
}
bool JpgLoader::close()
{
//TODO:
return false;
this->done();
clear();
return true;
}
const uint32_t* JpgLoader::pixels()
{
//TODO:
this->done();
return nullptr;
return (const uint32_t*)image;
}
void JpgLoader::run(unsigned tid)
{
image = jpgdDecompress(decoder);
}

View file

@ -22,11 +22,20 @@
#ifndef _TVG_JPG_LOADER_H_
#define _TVG_JPG_LOADER_H_
//TODO: Use Task?
class JpgLoader : public LoadModule
#include "tvgTaskScheduler.h"
#include "tvgJpgd.h"
class JpgLoader : public LoadModule, public Task
{
private:
jpeg_decoder* decoder = nullptr;
char* data = nullptr;
unsigned char *image = nullptr;
bool freeData = false;
void clear();
public:
JpgLoader();
~JpgLoader();
using LoadModule::open;
@ -36,6 +45,7 @@ public:
bool close() override;
const uint32_t* pixels() override;
void run(unsigned tid) override;
};
#endif //_TVG_JPG_LOADER_H_

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021 Samsung Electronics Co., Ltd. All rights reserved.
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// jpgd.h - C++ class for JPEG decompression.
// Public domain, Rich Geldreich <richgel99@gmail.com>
#ifndef _TVG_JPGD_H_
#define _TVG_JPGD_H_
class jpeg_decoder;
jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height);
jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height);
unsigned char* jpgdDecompress(jpeg_decoder* decoder);
void jpgdDelete(jpeg_decoder* decoder);
#endif //_TVG_JPGD_H_

View file

@ -1122,9 +1122,6 @@ static void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings
}
const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0, 0};
/* ////////////////////////////////////////////////////////////////////////// */
/* ////////////////////////////////////////////////////////////////////////// */
/* // End of Zlib related code. Begin of PNG related code. // */

1
thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp vendored Executable file → Normal file
View file

@ -136,7 +136,6 @@ bool PngLoader::read()
bool PngLoader::close()
{
this->done();
clear();
return true;
}

1
thirdparty/thorvg/src/loaders/png/tvgPngLoader.h vendored Executable file → Normal file
View file

@ -48,7 +48,6 @@ public:
bool close() override;
const uint32_t* pixels() override;
void run(unsigned tid) override;
};

View file

@ -64,6 +64,17 @@
/* Internal Class Implementation */
/************************************************************************/
/*
* According to: https://www.w3.org/TR/SVG2/coords.html#Units
* and: https://www.w3.org/TR/css-values-4/#absolute-lengths
*/
#define PX_PER_IN 96 //1 in = 96 px
#define PX_PER_PC 16 //1 pc = 1/6 in -> PX_PER_IN/6
#define PX_PER_PT 1.333333f //1 pt = 1/72 in -> PX_PER_IN/72
#define PX_PER_MM 3.779528f //1 in = 25.4 mm -> PX_PER_IN/25.4
#define PX_PER_CM 37.79528f //1 in = 2.54 cm -> PX_PER_IN/2.54
typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength);
typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
@ -107,20 +118,16 @@ static bool _parseNumber(const char** content, float* number)
/**
* According to https://www.w3.org/TR/SVG/coords.html#Units
*
* TODO
* Since this documentation is not obvious, more clean recalculation with dpi
* is required, but for now default w3 constants would be used
*/
static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
{
float parsedValue = svgUtilStrtof(str, nullptr);
if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
else if (strstr(str, "in")) parsedValue = parsedValue * 90;
if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
else if (strstr(str, "%")) {
if (type == SvgParserLengthType::Vertical) parsedValue = (parsedValue / 100.0) * svgParse->global.h;
else if (type == SvgParserLengthType::Horizontal) parsedValue = (parsedValue / 100.0) * svgParse->global.w;
@ -132,40 +139,30 @@ static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengt
parsedValue = (parsedValue / 100.0) * max;
}
}
//TODO: Implement 'em', 'ex' attributes
return parsedValue;
}
static float _gradientToFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool& isPercentage)
{
char* end = nullptr;
float parsedValue = svgUtilStrtof(str, &end);
float max = 1;
isPercentage = false;
/**
* That is according to Units in here
*
* https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
*/
if (type == SvgParserLengthType::Vertical) max = (float)svgParse->global.h;
else if (type == SvgParserLengthType::Horizontal) max = (float)svgParse->global.w;
else if (type == SvgParserLengthType::Other) max = sqrtf(pow(svgParse->global.h, 2) + pow(svgParse->global.w, 2)) / sqrtf(2.0);
if (strstr(str, "%")) parsedValue = parsedValue / 100.0;
else if (strstr(str, "cm")) parsedValue = parsedValue * 35.43307;
else if (strstr(str, "mm")) parsedValue = parsedValue * 3.543307;
else if (strstr(str, "pt")) parsedValue = parsedValue * 1.25;
else if (strstr(str, "pc")) parsedValue = parsedValue * 15;
else if (strstr(str, "in")) parsedValue = parsedValue * 90;
if (strstr(str, "%")) {
parsedValue = parsedValue / 100.0;
isPercentage = true;
}
else if (strstr(str, "cm")) parsedValue *= PX_PER_CM;
else if (strstr(str, "mm")) parsedValue *= PX_PER_MM;
else if (strstr(str, "pt")) parsedValue *= PX_PER_PT;
else if (strstr(str, "pc")) parsedValue *= PX_PER_PC;
else if (strstr(str, "in")) parsedValue *= PX_PER_IN;
//TODO: Implement 'em', 'ex' attributes
//Transform into global percentage
parsedValue = parsedValue / max;
return parsedValue;
}
@ -1679,8 +1676,6 @@ static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
if (!from) return nullptr;
auto grad = new SvgStyleGradient;
if (!grad) return nullptr;
grad->type = from->type;
grad->id = from->id ? _copyId(from->id->c_str()) : nullptr;
grad->ref = from->ref ? _copyId(from->ref->c_str()) : nullptr;
@ -1827,12 +1822,36 @@ static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes) {
}
static constexpr struct
{
const char* tag;
SvgParserLengthType type;
int sz;
size_t offset;
} useTags[] = {
{"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgRectNode, x)},
{"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgRectNode, y)},
{"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgRectNode, w)},
{"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgRectNode, h)}
};
static bool _attrParseUseNode(void* data, const char* key, const char* value)
{
SvgLoaderData* loader = (SvgLoaderData*)data;
SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
string* id;
SvgUseNode* use = &(node->node.use);
int sz = strlen(key);
unsigned char* array = (unsigned char*)use;
for (unsigned int i = 0; i < sizeof(useTags) / sizeof(useTags[0]); i++) {
if (useTags[i].sz - 1 == sz && !strncmp(useTags[i].tag, key, sz)) {
*((float*)(array + useTags[i].offset)) = _toFloat(loader->svgParse, value, useTags[i].type);
return true;
}
}
if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
id = _idFromHref(value);
defs = _getDefsNode(node);
@ -1935,65 +1954,72 @@ FillSpread _parseSpreadValue(const char* value)
static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
{
radial->cx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
if (!loader->svgParse->gradient.parsedFx) radial->fx = radial->cx;
radial->cx = _gradientToFloat(loader->svgParse, value, radial->isCxPercentage);
if (!loader->svgParse->gradient.parsedFx) {
radial->fx = radial->cx;
radial->isFxPercentage = radial->isCxPercentage;
}
}
static void _handleRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
{
radial->cy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
if (!loader->svgParse->gradient.parsedFy) radial->fy = radial->cy;
radial->cy = _gradientToFloat(loader->svgParse, value, radial->isCyPercentage);
if (!loader->svgParse->gradient.parsedFy) {
radial->fy = radial->cy;
radial->isFyPercentage = radial->isCyPercentage;
}
}
static void _handleRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
{
radial->fx = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
radial->fx = _gradientToFloat(loader->svgParse, value, radial->isFxPercentage);
loader->svgParse->gradient.parsedFx = true;
}
static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
{
radial->fy = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
radial->fy = _gradientToFloat(loader->svgParse, value, radial->isFyPercentage);
loader->svgParse->gradient.parsedFy = true;
}
static void _handleRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
{
radial->r = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Other);
radial->r = _gradientToFloat(loader->svgParse, value, radial->isRPercentage);
}
static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
if (!userSpace) radial->cx = radial->cx * loader->svgParse->global.w;
if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w;
}
static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
if (!userSpace) radial->cy = radial->cy * loader->svgParse->global.h;
if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h;
}
static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
if (!userSpace) radial->fx = radial->fx * loader->svgParse->global.w;
if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w;
}
static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
if (!userSpace) radial->fy = radial->fy * loader->svgParse->global.h;
if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h;
}
static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
{
if (!userSpace) radial->r = radial->r * (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0));
// scaling factor based on the Units paragraph from : https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
if (userSpace && !radial->isRPercentage) radial->r = radial->r / (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0));
}
@ -2057,8 +2083,6 @@ static bool _attrParseRadialGradientNode(void* data, const char* key, const char
static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
{
auto grad = new SvgStyleGradient;
if (!grad) return nullptr;
loader->svgParse->styleGrad = grad;
grad->type = SvgGradientType::Radial;
@ -2071,11 +2095,16 @@ static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char
/**
* Default values of gradient transformed into global percentage
*/
grad->radial->cx = 0.5f / loader->svgParse->global.w;
grad->radial->cy = 0.5f / loader->svgParse->global.h;
grad->radial->fx = 0.5f / loader->svgParse->global.w;
grad->radial->fy = 0.5f / loader->svgParse->global.h;
grad->radial->r = 0.5f / (sqrtf(pow(loader->svgParse->global.h, 2) + pow(loader->svgParse->global.w, 2)) / sqrtf(2.0f));
grad->radial->cx = 0.5f;
grad->radial->cy = 0.5f;
grad->radial->fx = 0.5f;
grad->radial->fy = 0.5f;
grad->radial->r = 0.5f;
grad->radial->isCxPercentage = true;
grad->radial->isCyPercentage = true;
grad->radial->isFxPercentage = true;
grad->radial->isFyPercentage = true;
grad->radial->isRPercentage = true;
loader->svgParse->gradient.parsedFx = false;
loader->svgParse->gradient.parsedFy = false;
@ -2136,49 +2165,49 @@ static bool _attrParseStops(void* data, const char* key, const char* value)
static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
{
linear->x1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
linear->x1 = _gradientToFloat(loader->svgParse, value, linear->isX1Percentage);
}
static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
{
linear->y1 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
linear->y1 = _gradientToFloat(loader->svgParse, value, linear->isY1Percentage);
}
static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
{
linear->x2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
linear->x2 = _gradientToFloat(loader->svgParse, value, linear->isX2Percentage);
}
static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
{
linear->y2 = _gradientToFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
linear->y2 = _gradientToFloat(loader->svgParse, value, linear->isY2Percentage);
}
static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
{
if (!userSpace) linear->x1 = linear->x1 * loader->svgParse->global.w;
if (userSpace && !linear->isX1Percentage) linear->x1 = linear->x1 / loader->svgParse->global.w;
}
static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
{
if (!userSpace) linear->y1 = linear->y1 * loader->svgParse->global.h;
if (userSpace && !linear->isY1Percentage) linear->y1 = linear->y1 / loader->svgParse->global.h;
}
static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
{
if (!userSpace) linear->x2 = linear->x2 * loader->svgParse->global.w;
if (userSpace && !linear->isX2Percentage) linear->x2 = linear->x2 / loader->svgParse->global.w;
}
static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
{
if (!userSpace) linear->y2 = linear->y2 * loader->svgParse->global.h;
if (userSpace && !linear->isY2Percentage) linear->y2 = linear->y2 / loader->svgParse->global.h;
}
@ -2241,8 +2270,6 @@ static bool _attrParseLinearGradientNode(void* data, const char* key, const char
static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
{
auto grad = new SvgStyleGradient;
if (!grad) return nullptr;
loader->svgParse->styleGrad = grad;
grad->type = SvgGradientType::Linear;
@ -2255,7 +2282,9 @@ static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char
/**
* Default value of x2 is 100% - transformed to the global percentage
*/
grad->linear->x2 = 1.0f / loader->svgParse->global.w;
grad->linear->x2 = 1.0f;
grad->linear->isX2Percentage = true;
simpleXmlParseAttributes(buf, bufLength, _attrParseLinearGradientNode, loader);
for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
@ -2273,10 +2302,9 @@ static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char
/**
* For all Gradients lengths would be calculated into percentages related to
* canvas width and height.
*
* if user then recalculate actual pixels into percentages
* In the case when the gradients lengths are given as numbers (not percentages)
* in the current user coordinate system, they are recalculated into percentages
* related to the canvas width and height.
*/
static constexpr struct
{
@ -2788,7 +2816,7 @@ void SvgLoader::run(unsigned tid)
if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes);
}
root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect);
root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath);
}
@ -2857,6 +2885,7 @@ bool SvgLoader::open(const string& path)
if (!f.is_open()) return false;
svgPath = path;
getline(f, filePath, '\0');
f.close();

View file

@ -29,6 +29,7 @@ class SvgLoader : public LoadModule, public Task
{
public:
string filePath;
string svgPath = "";
const char* content = nullptr;
uint32_t size = 0;

View file

@ -157,6 +157,11 @@ struct SvgDefsNode
Array<SvgStyleGradient*> gradients;
};
struct SvgUseNode
{
float x, y, w, h;
};
struct SvgEllipseNode
{
@ -216,6 +221,11 @@ struct SvgLinearGradient
float y1;
float x2;
float y2;
bool isX1Percentage;
bool isY1Percentage;
bool isX2Percentage;
bool isY2Percentage;
};
struct SvgRadialGradient
@ -225,6 +235,11 @@ struct SvgRadialGradient
float fx;
float fy;
float r;
bool isCxPercentage;
bool isCyPercentage;
bool isFxPercentage;
bool isFyPercentage;
bool isRPercentage;
};
struct SvgComposite
@ -324,6 +339,7 @@ struct SvgNode
SvgGNode g;
SvgDocNode doc;
SvgDefsNode defs;
SvgUseNode use;
SvgCircleNode circle;
SvgEllipseNode ellipse;
SvgPolygonNode polygon;

View file

@ -57,6 +57,7 @@
#include <float.h>
static bool _appendShape(SvgNode* node, Shape* shape, float vx, float vy, float vw, float vh);
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, float vx, float vy, float vw, float vh, const string& svgPath);
/************************************************************************/
/* Internal Class Implementation */
@ -69,29 +70,44 @@ static inline bool _isGroupType(SvgNodeType type)
}
static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf)
{
gradTransf->e13 = gradTransf->e13 * mBBox->e11 + mBBox->e13;
gradTransf->e12 *= mBBox->e11;
gradTransf->e11 *= mBBox->e11;
gradTransf->e23 = gradTransf->e23 * mBBox->e22 + mBBox->e23;
gradTransf->e22 *= mBBox->e22;
gradTransf->e21 *= mBBox->e22;
}
static unique_ptr<LinearGradient> _applyLinearGradientProperty(SvgStyleGradient* g, const Shape* vg, float rx, float ry, float rw, float rh, int opacity)
{
Fill::ColorStop* stops;
int stopCount = 0;
auto fillGrad = LinearGradient::gen();
g->linear->x1 = g->linear->x1 * rw + rx;
g->linear->y1 = g->linear->y1 * rh + ry;
g->linear->x2 = g->linear->x2 * rw + rx;
g->linear->y2 = g->linear->y2 * rh + ry;
bool isTransform = (g->transform ? true : false);
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (isTransform) finalTransform = *g->transform;
if (g->transform) {
//Calc start point
auto x = g->linear->x1;
g->linear->x1 = x * g->transform->e11 + g->linear->y1 * g->transform->e12 + g->transform->e13;
g->linear->y1 = x * g->transform->e21 + g->linear->y1 * g->transform->e22 + g->transform->e23;
//Calc end point
x = g->linear->x2;
g->linear->x2 = x * g->transform->e11 + g->linear->y2 * g->transform->e12 + g->transform->e13;
g->linear->y2 = x * g->transform->e21 + g->linear->y2 * g->transform->e22 + g->transform->e23;
if (g->userSpace) {
g->linear->x1 = g->linear->x1 * rw;
g->linear->y1 = g->linear->y1 * rh;
g->linear->x2 = g->linear->x2 * rw;
g->linear->y2 = g->linear->y2 * rh;
} else {
Matrix m = {rw, 0, rx, 0, rh, ry, 0, 0, 1};
if (isTransform) _transformMultiply(&m, &finalTransform);
else {
finalTransform = m;
isTransform = true;
}
}
if (isTransform) fillGrad->transform(finalTransform);
fillGrad->linear(g->linear->x1, g->linear->y1, g->linear->x2, g->linear->y2);
fillGrad->spread(g->spread);
@ -125,32 +141,30 @@ static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient*
{
Fill::ColorStop *stops;
int stopCount = 0;
float radius;
auto fillGrad = RadialGradient::gen();
radius = sqrtf(powf(rw, 2.0f) + powf(rh, 2.0f)) / sqrtf(2.0f);
if (!g->userSpace) {
//That is according to Units in here
//https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
int min = static_cast<int>((rh > rw) ? rw : rh);
radius = sqrtf(pow(min, 2) + pow(min, 2)) / sqrtf(2.0f);
bool isTransform = (g->transform ? true : false);
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
if (isTransform) finalTransform = *g->transform;
if (g->userSpace) {
//The radius scalling is done according to the Units section:
//https://www.w3.org/TR/2015/WD-SVG2-20150915/coords.html
g->radial->cx = g->radial->cx * rw;
g->radial->cy = g->radial->cy * rh;
g->radial->r = g->radial->r * sqrtf(powf(rw, 2.0f) + powf(rh, 2.0f)) / sqrtf(2.0f);
g->radial->fx = g->radial->fx * rw;
g->radial->fy = g->radial->fy * rh;
} else {
Matrix m = {rw, 0, rx, 0, rh, ry, 0, 0, 1};
if (isTransform) _transformMultiply(&m, &finalTransform);
else {
finalTransform = m;
isTransform = true;
}
}
g->radial->cx = g->radial->cx * rw + rx;
g->radial->cy = g->radial->cy * rh + ry;
g->radial->r = g->radial->r * radius;
g->radial->fx = g->radial->fx * rw + rx;
g->radial->fy = g->radial->fy * rh + ry;
//TODO: Radial gradient transformation - all tests possible after rx/ry implementation
if (g->transform) {
auto cx = g->radial->cx * g->transform->e11 + g->radial->cy * g->transform->e12 + g->transform->e13;
g->radial->cy = g->radial->cx * g->transform->e21 + g->radial->cy * g->transform->e22 + g->transform->e23;
g->radial->cx = cx;
auto sx = sqrtf(powf(g->transform->e11, 2.0f) + powf(g->transform->e21, 2.0f));
g->radial->r *= sx;
}
if (isTransform) fillGrad->transform(finalTransform);
//TODO: Tvg is not support to focal
//if (g->radial->fx != 0 && g->radial->fy != 0) {
@ -271,7 +285,17 @@ static void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float v
if (style->fill.paint.none) {
//Do nothing
} else if (style->fill.paint.gradient) {
if (!style->fill.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh, false);
if (!style->fill.paint.gradient->userSpace) {
vg->bounds(&vx, &vy, &vw, &vh, false);
//According to: https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits (the last paragraph)
//a stroke width should be ignored for bounding box calculations
if (auto strokeW = vg->strokeWidth()) {
vx += 0.5f * strokeW;
vy += 0.5f * strokeW;
vw -= strokeW;
vh -= strokeW;
}
}
if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, vx, vy, vw, vh, style->fill.opacity);
@ -310,7 +334,17 @@ static void _applyProperty(SvgNode* node, Shape* vg, float vx, float vy, float v
if (style->stroke.paint.none) {
//Do nothing
} else if (style->stroke.paint.gradient) {
if (!style->stroke.paint.gradient->userSpace) vg->bounds(&vx, &vy, &vw, &vh, false);
if (!style->stroke.paint.gradient->userSpace) {
//According to: https://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBoxUnits (the last paragraph)
//a stroke width should be ignored for bounding box calculations
vg->bounds(&vx, &vy, &vw, &vh, false);
if (auto strokeW = vg->strokeWidth()) {
vx += 0.5f * strokeW;
vy += 0.5f * strokeW;
vw -= strokeW;
vh -= strokeW;
}
}
if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, vx, vy, vw, vh, style->stroke.opacity);
@ -469,7 +503,7 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim
}
static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh)
static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, float vx, float vy, float vw, float vh, const string& svgPath)
{
if (!node->node.image.href) return nullptr;
auto picture = Picture::gen();
@ -496,7 +530,12 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, float vx, float vy,
TVGLOG("SVG", "Embedded svg file is disabled.");
return nullptr;
}
if (picture->load(href) != Result::Success) return nullptr;
string imagePath = href;
if (strncmp(href, "/", 1)) {
auto last = svgPath.find_last_of("/");
imagePath = svgPath.substr(0, (last == string::npos ? 0 : last + 1 )) + imagePath;
}
if (picture->load(imagePath) != Result::Success) return nullptr;
}
float w, h;
@ -512,7 +551,20 @@ static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, float vx, float vy,
}
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, float vx, float vy, float vw, float vh)
static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, float vx, float vy, float vw, float vh, const string& svgPath)
{
auto scene = _sceneBuildHelper(node, vx, vy, vw, vh, svgPath);
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
scene->translate(node->node.use.x, node->node.use.y);
}
if (node->node.use.w > 0.0f && node->node.use.h > 0.0f) {
//TODO: handle width/height properties
}
return scene;
}
static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, float vx, float vy, float vw, float vh, const string& svgPath)
{
if (_isGroupType(node->type)) {
auto scene = Scene::gen();
@ -522,9 +574,12 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, float vx, float
auto child = node->child.data;
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
if (_isGroupType((*child)->type)) {
scene->push(_sceneBuildHelper(*child, vx, vy, vw, vh));
if ((*child)->type == SvgNodeType::Use)
scene->push(_useBuildHelper(*child, vx, vy, vw, vh, svgPath));
else
scene->push(_sceneBuildHelper(*child, vx, vy, vw, vh, svgPath));
} else if ((*child)->type == SvgNodeType::Image) {
auto image = _imageBuildHelper(*child, vx, vy, vw, vh);
auto image = _imageBuildHelper(*child, vx, vy, vw, vh, svgPath);
if (image) scene->push(move(image));
} else {
auto shape = _shapeBuildHelper(*child, vx, vy, vw, vh);
@ -544,11 +599,11 @@ static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, float vx, float
/* External Class Implementation */
/************************************************************************/
unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect)
unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath)
{
if (!node || (node->type != SvgNodeType::Doc)) return nullptr;
auto docNode = _sceneBuildHelper(node, vx, vy, vw, vh);
auto docNode = _sceneBuildHelper(node, vx, vy, vw, vh, svgPath);
if (fabsf(w - vw) > FLT_EPSILON || fabsf(h - vh) > FLT_EPSILON) {
auto sx = w / vw;

View file

@ -25,6 +25,6 @@
#include "tvgCommon.h"
unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect);
unique_ptr<Scene> svgSceneBuild(SvgNode* node, float vx, float vy, float vw, float vh, float w, float h, bool preserveAspect, const string& svgPath);
#endif //_TVG_SVG_SCENE_BUILDER_H_

View file

@ -87,9 +87,8 @@ static bool _parsePaintProperty(TvgBinBlock block, Paint *paint)
}
case TVG_TAG_PAINT_TRANSFORM: {
if (block.length != SIZE(Matrix)) return false;
Matrix matrix;
memcpy(&matrix, block.data, SIZE(Matrix));
paint->transform(matrix);
auto transform = (Matrix*)(block.data);
paint->transform(*transform);
return true;
}
case TVG_TAG_PAINT_CMP_TARGET: {
@ -225,6 +224,16 @@ static unique_ptr<Fill> _parseShapeFill(const char *ptr, const char *end)
fillGrad->colorStops(stops, stopsCnt);
break;
}
case TVG_TAG_FILL_TRANSFORM: {
if (!fillGrad || block.length != SIZE(Matrix)) return nullptr;
auto transform = (Matrix*)(block.data);
fillGrad->transform(*transform);
break;
}
default: {
TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of the fill properties, %d bytes skipped", block.type, block.type, block.length);
break;
}
}
ptr = block.end;
}
@ -286,6 +295,10 @@ static bool _parseShapeStroke(const char *ptr, const char *end, Shape *shape)
if (!_parseShapeStrokeDashPattern(block.data, block.end, shape)) return false;
break;
}
default: {
TVGLOG("TVG", "Unsupported tag %d (0x%x) used as one of stroke properties, %d bytes skipped", block.type, block.type, block.length);
break;
}
}
ptr = block.end;
}
@ -382,7 +395,7 @@ static Paint* _parsePaint(TvgBinBlock baseBlock)
break;
}
default: {
TVGERR("TVG", "Invalid Paint Type (%d)", baseBlock.type);
TVGERR("TVG", "Invalid Paint Type %d (0x%x)", baseBlock.type, baseBlock.type);
return nullptr;
}
}
@ -394,7 +407,7 @@ static Paint* _parsePaint(TvgBinBlock baseBlock)
auto block = _readBlock(ptr);
if (block.end > baseBlock.end) return paint;
if (!parser(block, paint)) {
TVGERR("TVG", "Encountered the wrong paint properties... Paint Class (%d)", baseBlock.type);
TVGERR("TVG", "Encountered the wrong paint properties... Paint Class %d (0x%x)", baseBlock.type, baseBlock.type);
return paint;
}
ptr = block.end;
@ -424,4 +437,4 @@ unique_ptr<Scene> TvgBinInterpreter::run(const char *ptr, const char* end)
}
return scene;
}
}

View file

@ -323,12 +323,12 @@ TvgBinCounter TvgSaver::writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const
}
TvgBinCounter TvgSaver::writeTransform(const Matrix* transform)
TvgBinCounter TvgSaver::writeTransform(const Matrix* transform, TvgBinTag tag)
{
if (fabs(transform->e11 - 1) > FLT_EPSILON || fabs(transform->e12) > FLT_EPSILON || fabs(transform->e13) > FLT_EPSILON ||
fabs(transform->e21) > FLT_EPSILON || fabs(transform->e22 - 1) > FLT_EPSILON || fabs(transform->e23) > FLT_EPSILON ||
fabs(transform->e31) > FLT_EPSILON || fabs(transform->e32) > FLT_EPSILON || fabs(transform->e33 - 1) > FLT_EPSILON) {
return writeTagProperty(TVG_TAG_PAINT_TRANSFORM, SIZE(Matrix), transform);
return writeTagProperty(tag, SIZE(Matrix), transform);
}
return 0;
}
@ -423,7 +423,7 @@ TvgBinCounter TvgSaver::serializeScene(const Scene* scene, const Matrix* pTransf
}
TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag)
TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform)
{
const Fill::ColorStop* stops = nullptr;
auto stopsCnt = fill->colorStops(&stops);
@ -450,6 +450,10 @@ TvgBinCounter TvgSaver::serializeFill(const Fill* fill, TvgBinTag tag)
cnt += writeTagProperty(TVG_TAG_FILL_FILLSPREAD, SIZE(TvgBinFlag), &flag);
cnt += writeTagProperty(TVG_TAG_FILL_COLORSTOPS, stopsCnt * SIZE(Fill::ColorStop), stops);
auto gTransform = fill->transform();
if (pTransform) gTransform = _multiply(pTransform, &gTransform);
cnt += writeTransform(&gTransform, TVG_TAG_FILL_TRANSFORM);
writeReservedCount(cnt);
return SERIAL_DONE(cnt);
@ -476,7 +480,7 @@ TvgBinCounter TvgSaver::serializeStroke(const Shape* shape, const Matrix* pTrans
//fill
if (auto fill = shape->strokeFill()) {
cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL);
cnt += serializeFill(fill, TVG_TAG_SHAPE_STROKE_FILL, (preTransform ? pTransform : nullptr));
} else {
uint8_t color[4] = {0, 0, 0, 0};
shape->strokeColor(color, color + 1, color + 2, color + 3);
@ -555,33 +559,32 @@ TvgBinCounter TvgSaver::serializeShape(const Shape* shape, const Matrix* pTransf
cnt = writeTagProperty(TVG_TAG_SHAPE_FILLRULE, SIZE(TvgBinFlag), &flag);
}
//the pre-transformation can't be applied in the case where any fill is present or when the stroke is dashed or irregulary scaled
//the pre-transformation can't be applied in the case when the stroke is dashed or irregulary scaled
bool preTransform = true;
//fill
if (auto fill = shape->fill()) {
preTransform = false;
cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL);
} else {
uint8_t color[4] = {0, 0, 0, 0};
shape->fillColor(color, color + 1, color + 2, color + 3);
if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color);
}
//stroke
if (shape->strokeWidth() > 0) {
uint8_t color[4] = {0, 0, 0, 0};
shape->strokeColor(color, color + 1, color + 2, color + 3);
auto fill = shape->strokeFill();
if (fill || color[3] > 0) {
if (fill || abs(cTransform->e11 - cTransform->e22) > FLT_EPSILON || shape->strokeDash(nullptr) > 0) preTransform = false;
if (fabsf(cTransform->e11 - cTransform->e22) > FLT_EPSILON || (fabsf(cTransform->e11) < FLT_EPSILON && fabsf(cTransform->e12 - cTransform->e21) > FLT_EPSILON) || shape->strokeDash(nullptr) > 0) preTransform = false;
cnt += serializeStroke(shape, cTransform, preTransform);
}
}
//fill
if (auto fill = shape->fill()) {
cnt += serializeFill(fill, TVG_TAG_SHAPE_FILL, (preTransform ? cTransform : nullptr));
} else {
uint8_t color[4] = {0, 0, 0, 0};
shape->fillColor(color, color + 1, color + 2, color + 3);
if (color[3] > 0) cnt += writeTagProperty(TVG_TAG_SHAPE_COLOR, SIZE(color), color);
}
cnt += serializePath(shape, cTransform, preTransform);
if (!preTransform) cnt += writeTransform(cTransform);
if (!preTransform) cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
cnt += serializePaint(shape, pTransform);
writeReservedCount(cnt);
@ -636,7 +639,7 @@ TvgBinCounter TvgSaver::serializePicture(const Picture* picture, const Matrix* p
cnt += SIZE(TvgBinTag) + SIZE(TvgBinCounter);
//Bitmap picture needs the transform info.
cnt += writeTransform(cTransform);
cnt += writeTransform(cTransform, TVG_TAG_PAINT_TRANSFORM);
cnt += serializePaint(picture, pTransform);

View file

@ -50,14 +50,14 @@ private:
void writeReservedCount(TvgBinCounter cnt);
TvgBinCounter writeData(const void* data, TvgBinCounter cnt);
TvgBinCounter writeTagProperty(TvgBinTag tag, TvgBinCounter cnt, const void* data);
TvgBinCounter writeTransform(const Matrix* transform);
TvgBinCounter writeTransform(const Matrix* transform, TvgBinTag tag);
TvgBinCounter serialize(const Paint* paint, const Matrix* pTransform, bool compTarget = false);
TvgBinCounter serializeScene(const Scene* scene, const Matrix* pTransform, const Matrix* cTransform);
TvgBinCounter serializeShape(const Shape* shape, const Matrix* pTransform, const Matrix* cTransform);
TvgBinCounter serializePicture(const Picture* picture, const Matrix* pTransform, const Matrix* cTransform);
TvgBinCounter serializePaint(const Paint* paint, const Matrix* pTransform);
TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag);
TvgBinCounter serializeFill(const Fill* fill, TvgBinTag tag, const Matrix* pTransform);
TvgBinCounter serializeStroke(const Shape* shape, const Matrix* pTransform, bool preTransform);
TvgBinCounter serializePath(const Shape* shape, const Matrix* transform, bool preTransform);
TvgBinCounter serializeComposite(const Paint* cmpTarget, CompositeMethod cmpMethod, const Matrix* pTransform);

Binary file not shown.

View file

@ -1,5 +1,5 @@
rm -rf AUTHORS inc LICENSE src *.zip
curl -L -O https://github.com/Samsung/thorvg/archive/041b946175392390c70de185b0a0aace811a7592.zip
curl -L -O https://github.com/Samsung/thorvg/archive/refs/tags/v0.6.0.zip
bsdtar --strip-components=1 -xvf *.zip
rm *.zip
rm -rf .github docs pc res test tools .git* *.md *.txt wasm_build.sh