From 8492a63052f85dab1dd320d5f6aab754747b9862 Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Tue, 15 Oct 2024 12:49:14 +0900 Subject: [PATCH] svg: code refactoring --cyclomatic complexity, no logical changes. --- src/loaders/svg/tvgSvgSceneBuilder.cpp | 252 +++++++++++-------------- 1 file changed, 114 insertions(+), 138 deletions(-) diff --git a/src/loaders/svg/tvgSvgSceneBuilder.cpp b/src/loaders/svg/tvgSvgSceneBuilder.cpp index fd5d62bf..97c30f1f 100644 --- a/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -22,10 +22,8 @@ #include "tvgMath.h" /* to include math.h before cstring */ #include -#include #include "tvgShape.h" #include "tvgCompressor.h" -#include "tvgPaint.h" #include "tvgFill.h" #include "tvgStr.h" #include "tvgSvgLoaderCommon.h" @@ -89,7 +87,6 @@ static void _transformMultiply(const Matrix* mBBox, Matrix* gradTransf) static unique_ptr _applyLinearGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop* stops; - int stopCount = 0; auto fillGrad = LinearGradient::gen(); auto isTransform = (g->transform ? true : false); auto& finalTransform = fillGrad->transform(); @@ -110,27 +107,25 @@ static unique_ptr _applyLinearGradientProperty(SvgStyleGradient* fillGrad->spread(g->spread); //Update the stops - stopCount = g->stops.count; - if (stopCount > 0) { - stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop)); - if (!stops) return fillGrad; - auto prevOffset = 0.0f; - for (uint32_t i = 0; i < g->stops.count; ++i) { - auto colorStop = &g->stops[i]; - //Use premultiplied color - stops[i].r = colorStop->r; - stops[i].g = colorStop->g; - stops[i].b = colorStop->b; - stops[i].a = static_cast((colorStop->a * opacity) / 255); - stops[i].offset = colorStop->offset; - //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes - if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; - else if (colorStop->offset > 1) stops[i].offset = 1; - prevOffset = stops[i].offset; - } - fillGrad->colorStops(stops, stopCount); - free(stops); + if (g->stops.count == 0) return fillGrad; + + stops = (Fill::ColorStop*)malloc(g->stops.count * sizeof(Fill::ColorStop)); + auto prevOffset = 0.0f; + for (uint32_t i = 0; i < g->stops.count; ++i) { + auto colorStop = &g->stops[i]; + //Use premultiplied color + stops[i].r = colorStop->r; + stops[i].g = colorStop->g; + stops[i].b = colorStop->b; + stops[i].a = static_cast((colorStop->a * opacity) / 255); + stops[i].offset = colorStop->offset; + //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes + if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; + else if (colorStop->offset > 1) stops[i].offset = 1; + prevOffset = stops[i].offset; } + fillGrad->colorStops(stops, g->stops.count); + free(stops); return fillGrad; } @@ -138,7 +133,6 @@ static unique_ptr _applyLinearGradientProperty(SvgStyleGradient* static unique_ptr _applyRadialGradientProperty(SvgStyleGradient* g, const Box& vBox, int opacity) { Fill::ColorStop *stops; - int stopCount = 0; auto fillGrad = RadialGradient::gen(); auto isTransform = (g->transform ? true : false); auto& finalTransform = fillGrad->transform(); @@ -163,27 +157,25 @@ static unique_ptr _applyRadialGradientProperty(SvgStyleGradient* fillGrad->spread(g->spread); //Update the stops - stopCount = g->stops.count; - if (stopCount > 0) { - stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop)); - if (!stops) return fillGrad; - auto prevOffset = 0.0f; - for (uint32_t i = 0; i < g->stops.count; ++i) { - auto colorStop = &g->stops[i]; - //Use premultiplied color - stops[i].r = colorStop->r; - stops[i].g = colorStop->g; - stops[i].b = colorStop->b; - stops[i].a = static_cast((colorStop->a * opacity) / 255); - stops[i].offset = colorStop->offset; - //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes - if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; - else if (colorStop->offset > 1) stops[i].offset = 1; - prevOffset = stops[i].offset; - } - fillGrad->colorStops(stops, stopCount); - free(stops); + if (g->stops.count == 0) return fillGrad; + + stops = (Fill::ColorStop*)malloc(g->stops.count * sizeof(Fill::ColorStop)); + auto prevOffset = 0.0f; + for (uint32_t i = 0; i < g->stops.count; ++i) { + auto colorStop = &g->stops[i]; + //Use premultiplied color + stops[i].r = colorStop->r; + stops[i].g = colorStop->g; + stops[i].b = colorStop->b; + stops[i].a = static_cast((colorStop->a * opacity) / 255); + stops[i].offset = colorStop->offset; + //check the offset corner cases - refer to: https://svgwg.org/svg2-draft/pservers.html#StopNotes + if (colorStop->offset < prevOffset) stops[i].offset = prevOffset; + else if (colorStop->offset > 1) stops[i].offset = 1; + prevOffset = stops[i].offset; } + fillGrad->colorStops(stops, g->stops.count); + free(stops); return fillGrad; } @@ -197,8 +189,7 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape* Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; if (node->transform) finalTransform = *node->transform; if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) { - Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; - finalTransform *= m; + finalTransform *= {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1}; } if (child->transform) finalTransform = *child->transform * finalTransform; @@ -208,9 +199,7 @@ static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape* static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, bool clip) { - if (node->type == SvgNodeType::Use) { - return _appendClipUseNode(loaderData, node, shape, vBox, svgPath); - } + if (node->type == SvgNodeType::Use) return _appendClipUseNode(loaderData, node, shape, vBox, svgPath); return _appendClipShape(loaderData, node, shape, vBox, svgPath, nullptr); } @@ -228,8 +217,7 @@ static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const Svg if (!compNode->node.clip.userSpace) { float x, y, w, h; P(paint)->bounds(&x, &y, &w, &h, false, false); - Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1}; - m *= mBBox; + m *= {w, 0, x, 0, h, y, 0, 0, 1}; } return m; } @@ -248,7 +236,6 @@ static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const Svg node->style->clipPath.applying = true; auto comp = Shape::gen(); - auto child = compNode->child.data; auto valid = false; //Composite only when valid shapes exist @@ -280,15 +267,11 @@ static void _applyComposition(SvgLoaderData& loaderData, Paint* paint, const Svg if (!compNode->node.mask.userSpace) { Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::Mask); comp->transform(finalTransform); - } else { - if (node->transform) comp->transform(*node->transform); - } - - if (compNode->node.mask.type == SvgMaskType::Luminance) { - paint->mask(std::move(comp), MaskMethod::Luma); - } else { - paint->mask(std::move(comp), MaskMethod::Alpha); + } else if (node->transform) { + comp->transform(*node->transform); } + if (compNode->node.mask.type == SvgMaskType::Luminance) paint->mask(std::move(comp), MaskMethod::Luma); + else paint->mask(std::move(comp), MaskMethod::Alpha); } node->style->mask.applying = false; @@ -330,13 +313,9 @@ static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, vg->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b, style->fill.opacity); } - //Apply the fill rule vg->fill((tvg::FillRule)style->fill.fillRule); - //Rendering order vg->order(!style->paintOrder); - - //Apply node opacity - if (style->opacity < 255) vg->opacity(style->opacity); + vg->opacity(style->opacity); if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return; @@ -445,21 +424,16 @@ static unique_ptr _shapeBuildHelper(SvgLoaderData& loaderData, SvgNode* n static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform) { + if (!_recognizeShape(node, shape)) return false; + //The 'transform' matrix has higher priority than the node->transform, since it already contains it auto m = transform ? transform : (node->transform ? node->transform : nullptr); uint32_t currentPtsCnt = 0; if (m) { - const Point *tmp = nullptr; - currentPtsCnt = shape->pathCoords(&tmp); - } - - if (!_recognizeShape(node, shape)) return false; - - if (m) { + currentPtsCnt = shape->pathCoords(nullptr); const Point *pts = nullptr; auto ptsCnt = shape->pathCoords(&pts); - auto p = const_cast(pts) + currentPtsCnt; while (currentPtsCnt++ < ptsCnt) { *p *= *m; @@ -478,10 +452,12 @@ enum class imageMimeTypeEncoding utf8 = 0x2 }; + constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return static_cast(static_cast(a) | static_cast(b)); } + constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) { return (static_cast(a) & static_cast(b)); } @@ -508,38 +484,37 @@ static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mim //mediatype := [ type "/" subtype ] *( ";" parameter ) //parameter := attribute "=" value for (unsigned int i = 0; i < sizeof(imageMimeTypes) / sizeof(imageMimeTypes[0]); i++) { - if (!strncmp(*href, imageMimeTypes[i].name, imageMimeTypes[i].sz - 1)) { - *href += imageMimeTypes[i].sz - 1; - *mimetype = imageMimeTypes[i].name; + if (strncmp(*href, imageMimeTypes[i].name, imageMimeTypes[i].sz - 1)) continue; + *href += imageMimeTypes[i].sz - 1; + *mimetype = imageMimeTypes[i].name; - while (**href && **href != ',') { - while (**href && **href != ';') ++(*href); - if (!**href) return false; - ++(*href); + while (**href && **href != ',') { + while (**href && **href != ';') ++(*href); + if (!**href) return false; + ++(*href); - if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::base64) { - if (!strncmp(*href, "base64,", sizeof("base64,") - 1)) { - *href += sizeof("base64,") - 1; - *encoding = imageMimeTypeEncoding::base64; - return true; //valid base64 - } - } - if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8) { - if (!strncmp(*href, "utf8,", sizeof("utf8,") - 1)) { - *href += sizeof("utf8,") - 1; - *encoding = imageMimeTypeEncoding::utf8; - return true; //valid utf8 - } + if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::base64) { + if (!strncmp(*href, "base64,", sizeof("base64,") - 1)) { + *href += sizeof("base64,") - 1; + *encoding = imageMimeTypeEncoding::base64; + return true; //valid base64 } } - //no encoding defined - if (**href == ',' && (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8)) { - ++(*href); - *encoding = imageMimeTypeEncoding::utf8; - return true; //allow no encoding defined if utf8 expected + if (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8) { + if (!strncmp(*href, "utf8,", sizeof("utf8,") - 1)) { + *href += sizeof("utf8,") - 1; + *encoding = imageMimeTypeEncoding::utf8; + return true; //valid utf8 + } } - return false; } + //no encoding defined + if (**href == ',' && (imageMimeTypes[i].encoding & imageMimeTypeEncoding::utf8)) { + ++(*href); + *encoding = imageMimeTypeEncoding::utf8; + return true; //allow no encoding defined if utf8 expected + } + return false; } return false; } @@ -600,11 +575,13 @@ static unique_ptr _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* TaskScheduler::async(true); float w, h; - Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; - if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) { + Matrix m; + if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) { auto sx = node->node.image.w / w; auto sy = node->node.image.h / h; m = {sx, 0, node->node.image.x, 0, sy, node->node.image.y, 0, 0, 1}; + } else { + m = {1, 0, 0, 0, 1, 0, 0, 0, 1}; } if (node->transform) m = *node->transform * m; picture->transform(m); @@ -622,8 +599,7 @@ static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMee auto tvx = box.x * sx; auto tvy = box.y * sy; - if (align == AspectRatioAlign::None) - return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; + if (align == AspectRatioAlign::None) return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1}; //Scale if (meetOrSlice == AspectRatioMeetOrSlice::Meet) { @@ -703,7 +679,6 @@ static unique_ptr _useBuildHelper(SvgLoaderData& loaderData, const SvgNod if (node->node.use.symbol) { auto symbol = node->node.use.symbol->node.symbol; - auto width = (symbol.hasWidth ? symbol.w : vBox.w); if (node->node.use.isWidthSet) width = node->node.use.w; auto height = (symbol.hasHeight ? symbol.h : vBox.h);; @@ -785,8 +760,10 @@ static unique_ptr _textBuildHelper(SvgLoaderData& loaderData, const SvgNod if (!textNode->text) return nullptr; auto text = Text::gen(); - Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + Matrix textTransform; if (node->transform) textTransform = *node->transform; + else textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1}; + translateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize); text->transform(textTransform); @@ -812,43 +789,42 @@ static unique_ptr _sceneBuildHelper(SvgLoaderData& loaderData, const SvgN return nullptr; } - if (_isGroupType(node->type) || mask) { - auto scene = Scene::gen(); - // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper() - if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform); + if (!_isGroupType(node->type) && !mask) return nullptr; - if (node->style->display && node->style->opacity != 0) { - auto child = node->child.data; - for (uint32_t i = 0; i < node->child.count; ++i, ++child) { - if (_isGroupType((*child)->type)) { - if ((*child)->type == SvgNodeType::Use) - scene->push(_useBuildHelper(loaderData, *child, vBox, svgPath, depth + 1)); - else if (!((*child)->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use)) - scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1)); - if ((*child)->id) scene->id = djb2Encode((*child)->id); - } else if ((*child)->type == SvgNodeType::Image) { - if (auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath)) { - if ((*child)->id) image->id = djb2Encode((*child)->id); - scene->push(std::move(image)); - } - } else if ((*child)->type == SvgNodeType::Text) { - if (auto text = _textBuildHelper(loaderData, *child, vBox, svgPath)) { - if ((*child)->id) text->id = djb2Encode((*child)->id); - scene->push(std::move(text)); - } - } else if ((*child)->type != SvgNodeType::Mask) { - if (auto shape = _shapeBuildHelper(loaderData, *child, vBox, svgPath)) { - if ((*child)->id) shape->id = djb2Encode((*child)->id); - scene->push(std::move(shape)); - } - } + auto scene = Scene::gen(); + // For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper() + if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform); + + if (!node->style->display || node->style->opacity == 0) return scene; + + auto child = node->child.data; + for (uint32_t i = 0; i < node->child.count; ++i, ++child) { + if (_isGroupType((*child)->type)) { + if ((*child)->type == SvgNodeType::Use) + scene->push(_useBuildHelper(loaderData, *child, vBox, svgPath, depth + 1)); + else if (!((*child)->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use)) + scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1)); + if ((*child)->id) scene->id = djb2Encode((*child)->id); + } else if ((*child)->type == SvgNodeType::Image) { + if (auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath)) { + if ((*child)->id) image->id = djb2Encode((*child)->id); + scene->push(std::move(image)); + } + } else if ((*child)->type == SvgNodeType::Text) { + if (auto text = _textBuildHelper(loaderData, *child, vBox, svgPath)) { + if ((*child)->id) text->id = djb2Encode((*child)->id); + scene->push(std::move(text)); + } + } else if ((*child)->type != SvgNodeType::Mask) { + if (auto shape = _shapeBuildHelper(loaderData, *child, vBox, svgPath)) { + if ((*child)->id) shape->id = djb2Encode((*child)->id); + scene->push(std::move(shape)); } - _applyComposition(loaderData, scene.get(), node, vBox, svgPath); - scene->opacity(node->style->opacity); } - return scene; } - return nullptr; + _applyComposition(loaderData, scene.get(), node, vBox, svgPath); + scene->opacity(node->style->opacity); + return scene; }