summaryrefslogtreecommitdiff
path: root/thirdparty/thorvg/src/loaders
diff options
context:
space:
mode:
Diffstat (limited to 'thirdparty/thorvg/src/loaders')
-rw-r--r--thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp164
-rw-r--r--thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h52
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp125
-rw-r--r--thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h46
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp137
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h51
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp3029
-rw-r--r--thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h35
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp2647
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgLodePng.h174
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp194
-rw-r--r--thirdparty/thorvg/src/loaders/png/tvgPngLoader.h54
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp87
-rw-r--r--thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h42
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp188
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h34
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp3275
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h59
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h449
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp564
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h30
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp735
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h30
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp273
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h33
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp583
-rw-r--r--thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h58
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp450
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h54
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp232
-rw-r--r--thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h61
31 files changed, 13945 insertions, 0 deletions
diff --git a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp
new file mode 100644
index 0000000000..522c3c71a6
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2021 - 2022 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 <memory.h>
+#include <turbojpeg.h>
+#include "tvgLoader.h"
+#include "tvgJpgLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+void JpgLoader::clear()
+{
+ if (freeData) free(data);
+ data = nullptr;
+ size = 0;
+ freeData = false;
+}
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+JpgLoader::JpgLoader()
+{
+ jpegDecompressor = tjInitDecompress();
+}
+
+
+JpgLoader::~JpgLoader()
+{
+ if (freeData) free(data);
+ tjDestroy(jpegDecompressor);
+
+ //This image is shared with raster engine.
+ tjFree(image);
+}
+
+
+bool JpgLoader::open(const string& path)
+{
+ clear();
+
+ auto jpegFile = fopen(path.c_str(), "rb");
+ if (!jpegFile) return false;
+
+ auto ret = false;
+
+ //determine size
+ if (fseek(jpegFile, 0, SEEK_END) < 0) goto finalize;
+ if (((size = ftell(jpegFile)) < 1)) goto finalize;
+ if (fseek(jpegFile, 0, SEEK_SET)) goto finalize;
+
+ data = (unsigned char *) malloc(size);
+ if (!data) goto finalize;
+
+ freeData = true;
+
+ if (fread(data, size, 1, jpegFile) < 1) goto failure;
+
+ int width, height, subSample, colorSpace;
+ if (tjDecompressHeader3(jpegDecompressor, data, size, &width, &height, &subSample, &colorSpace) < 0) {
+ TVGERR("JPG LOADER", "%s", tjGetErrorStr());
+ goto failure;
+ }
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ ret = true;
+
+ goto finalize;
+
+failure:
+ clear();
+
+finalize:
+ fclose(jpegFile);
+ return ret;
+}
+
+
+bool JpgLoader::open(const char* data, uint32_t size, bool copy)
+{
+ clear();
+
+ int width, height, subSample, colorSpace;
+ if (tjDecompressHeader3(jpegDecompressor, (unsigned char *) data, size, &width, &height, &subSample, &colorSpace) < 0) return false;
+
+ if (copy) {
+ this->data = (unsigned char *) malloc(size);
+ if (!this->data) return false;
+ memcpy((unsigned char *)this->data, data, size);
+ freeData = true;
+ } else {
+ this->data = (unsigned char *) data;
+ freeData = false;
+ }
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ this->size = size;
+
+ return true;
+}
+
+
+bool JpgLoader::read()
+{
+ if (image) tjFree(image);
+ image = (unsigned char *)tjAlloc(static_cast<int>(w) * static_cast<int>(h) * tjPixelSize[TJPF_BGRX]);
+ if (!image) return false;
+
+ //decompress jpg image
+ if (tjDecompress2(jpegDecompressor, data, size, image, static_cast<int>(w), 0, static_cast<int>(h), TJPF_BGRX, 0) < 0) {
+ TVGERR("JPG LOADER", "%s", tjGetErrorStr());
+ tjFree(image);
+ image = nullptr;
+ return false;
+ }
+
+ return true;
+}
+
+
+bool JpgLoader::close()
+{
+ clear();
+ return true;
+}
+
+
+unique_ptr<Surface> JpgLoader::bitmap()
+{
+ if (!image) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(image);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
diff --git a/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h
new file mode 100644
index 0000000000..3f82af8003
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_jpg/tvgJpgLoader.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2021 - 2022 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.
+ */
+#ifndef _TVG_JPG_LOADER_H_
+#define _TVG_JPG_LOADER_H_
+
+using tjhandle = void*;
+
+//TODO: Use Task?
+class JpgLoader : public LoadModule
+{
+public:
+ JpgLoader();
+ ~JpgLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+
+private:
+ void clear();
+
+ tjhandle jpegDecompressor;
+ unsigned char* data = nullptr;
+ unsigned char *image = nullptr;
+ unsigned long size = 0;
+ bool freeData = false;
+};
+
+#endif //_TVG_JPG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
new file mode 100644
index 0000000000..05db65cb23
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020 - 2022 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 "tvgLoader.h"
+#include "tvgPngLoader.h"
+
+static inline uint32_t PREMULTIPLY(uint32_t c)
+{
+ auto a = (c >> 24);
+ return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
+}
+
+
+static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
+{
+ auto buffer = data;
+ for (uint32_t y = 0; y < h; ++y, buffer += w) {
+ auto src = buffer;
+ for (uint32_t x = 0; x < w; ++x, ++src) {
+ *src = PREMULTIPLY(*src);
+ }
+ }
+}
+
+
+PngLoader::PngLoader()
+{
+ image = static_cast<png_imagep>(calloc(1, sizeof(png_image)));
+ image->version = PNG_IMAGE_VERSION;
+ image->opaque = NULL;
+}
+
+PngLoader::~PngLoader()
+{
+ if (content) {
+ free((void*)content);
+ content = nullptr;
+ }
+ free(image);
+}
+
+bool PngLoader::open(const string& path)
+{
+ image->opaque = NULL;
+
+ if (!png_image_begin_read_from_file(image, path.c_str())) return false;
+
+ w = (float)image->width;
+ h = (float)image->height;
+
+ return true;
+}
+
+bool PngLoader::open(const char* data, uint32_t size, bool copy)
+{
+ image->opaque = NULL;
+
+ if (!png_image_begin_read_from_memory(image, data, size)) return false;
+
+ w = (float)image->width;
+ h = (float)image->height;
+
+ return true;
+}
+
+
+bool PngLoader::read()
+{
+ png_bytep buffer;
+ image->format = PNG_FORMAT_BGRA;
+ buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));
+ if (!buffer) {
+ //out of memory, only time when libpng doesnt free its data
+ png_image_free(image);
+ return false;
+ }
+ if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) {
+ free(buffer);
+ return false;
+ }
+ content = reinterpret_cast<uint32_t*>(buffer);
+
+ _premultiply(reinterpret_cast<uint32_t*>(buffer), image->width, image->height);
+
+ return true;
+}
+
+bool PngLoader::close()
+{
+ png_image_free(image);
+ return true;
+}
+
+unique_ptr<Surface> PngLoader::bitmap()
+{
+ if (!content) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(content);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
new file mode 100644
index 0000000000..8beff0a3b3
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+#ifndef _TVG_PNG_LOADER_H_
+#define _TVG_PNG_LOADER_H_
+
+#include <png.h>
+
+class PngLoader : public LoadModule
+{
+public:
+ PngLoader();
+ ~PngLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+
+private:
+ png_imagep image = nullptr;
+ const uint32_t* content = nullptr;
+};
+
+#endif //_TVG_PNG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
new file mode 100644
index 0000000000..7b90b4aef9
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2021 - 2022 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 <memory.h>
+#include "tvgLoader.h"
+#include "tvgJpgLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+void JpgLoader::clear()
+{
+ jpgdDelete(decoder);
+ if (freeData) free(data);
+ decoder = nullptr;
+ data = nullptr;
+ freeData = false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+JpgLoader::~JpgLoader()
+{
+ jpgdDelete(decoder);
+ if (freeData) free(data);
+ free(image);
+}
+
+
+bool JpgLoader::open(const string& path)
+{
+ clear();
+
+ 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)
+{
+ clear();
+
+ 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()
+{
+ if (!decoder || w <= 0 || h <= 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool JpgLoader::close()
+{
+ this->done();
+ clear();
+ return true;
+}
+
+
+unique_ptr<Surface> JpgLoader::bitmap()
+{
+ this->done();
+
+ if (!image) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(image);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
+
+
+void JpgLoader::run(unsigned tid)
+{
+ if (image) {
+ free(image);
+ image = nullptr;
+ }
+ image = jpgdDecompress(decoder);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
new file mode 100644
index 0000000000..d6b886cb29
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2021 - 2022 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.
+ */
+#ifndef _TVG_JPG_LOADER_H_
+#define _TVG_JPG_LOADER_H_
+
+#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();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+ void run(unsigned tid) override;
+};
+
+#endif //_TVG_JPG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
new file mode 100644
index 0000000000..dacd45ed03
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
@@ -0,0 +1,3029 @@
+/*
+ * Copyright (c) 2021 - 2022 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.cpp - C++ class for JPEG decompression.
+// Public domain, Rich Geldreich <richgel99@gmail.com>
+// Alex Evans: Linear memory allocator (taken from jpge.h).
+// v1.04, May. 19, 2012: Code tweaks to fix VS2008 static code analysis warnings (all looked harmless)
+//
+// Supports progressive and baseline sequential JPEG image files, and the most common chroma subsampling factors: Y, H1V1, H2V1, H1V2, and H2V2.
+//
+// Chroma upsampling quality: H2V2 is upsampled in the frequency domain, H2V1 and H1V2 are upsampled using point sampling.
+// Chroma upsampling reference: "Fast Scheme for Image Size Change in the Compressed Domain"
+// http://vision.ai.uiuc.edu/~dugad/research/dct/index.html
+
+#include <memory.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <setjmp.h>
+#include <stdint.h>
+#include "tvgJpgd.h"
+
+#ifdef _MSC_VER
+ #pragma warning (disable : 4611) // warning C4611: interaction between '_setjmp' and C++ object destruction is non-portable
+ #define JPGD_NORETURN __declspec(noreturn)
+#elif defined(__GNUC__)
+ #define JPGD_NORETURN __attribute__ ((noreturn))
+#else
+ #define JPGD_NORETURN
+#endif
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+// Set to 1 to enable freq. domain chroma upsampling on images using H2V2 subsampling (0=faster nearest neighbor sampling).
+// This is slower, but results in higher quality on images with highly saturated colors.
+#define JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING 1
+
+#define JPGD_ASSERT(x)
+#define JPGD_MAX(a,b) (((a)>(b)) ? (a) : (b))
+#define JPGD_MIN(a,b) (((a)<(b)) ? (a) : (b))
+
+typedef int16_t jpgd_quant_t;
+typedef int16_t jpgd_block_t;
+
+// Success/failure error codes.
+enum jpgd_status
+{
+ JPGD_SUCCESS = 0, JPGD_FAILED = -1, JPGD_DONE = 1,
+ JPGD_BAD_DHT_COUNTS = -256, JPGD_BAD_DHT_INDEX, JPGD_BAD_DHT_MARKER, JPGD_BAD_DQT_MARKER, JPGD_BAD_DQT_TABLE,
+ JPGD_BAD_PRECISION, JPGD_BAD_HEIGHT, JPGD_BAD_WIDTH, JPGD_TOO_MANY_COMPONENTS,
+ JPGD_BAD_SOF_LENGTH, JPGD_BAD_VARIABLE_MARKER, JPGD_BAD_DRI_LENGTH, JPGD_BAD_SOS_LENGTH,
+ JPGD_BAD_SOS_COMP_ID, JPGD_W_EXTRA_BYTES_BEFORE_MARKER, JPGD_NO_ARITHMITIC_SUPPORT, JPGD_UNEXPECTED_MARKER,
+ JPGD_NOT_JPEG, JPGD_UNSUPPORTED_MARKER, JPGD_BAD_DQT_LENGTH, JPGD_TOO_MANY_BLOCKS,
+ JPGD_UNDEFINED_QUANT_TABLE, JPGD_UNDEFINED_HUFF_TABLE, JPGD_NOT_SINGLE_SCAN, JPGD_UNSUPPORTED_COLORSPACE,
+ JPGD_UNSUPPORTED_SAMP_FACTORS, JPGD_DECODE_ERROR, JPGD_BAD_RESTART_MARKER, JPGD_ASSERTION_ERROR,
+ JPGD_BAD_SOS_SPECTRAL, JPGD_BAD_SOS_SUCCESSIVE, JPGD_STREAM_READ, JPGD_NOTENOUGHMEM
+};
+
+enum
+{
+ JPGD_IN_BUF_SIZE = 8192, JPGD_MAX_BLOCKS_PER_MCU = 10, JPGD_MAX_HUFF_TABLES = 8, JPGD_MAX_QUANT_TABLES = 4,
+ JPGD_MAX_COMPONENTS = 4, JPGD_MAX_COMPS_IN_SCAN = 4, JPGD_MAX_BLOCKS_PER_ROW = 8192, JPGD_MAX_HEIGHT = 16384, JPGD_MAX_WIDTH = 16384
+};
+
+// Input stream interface.
+// Derive from this class to read input data from sources other than files or memory. Set m_eof_flag to true when no more data is available.
+// The decoder is rather greedy: it will keep on calling this method until its internal input buffer is full, or until the EOF flag is set.
+// It the input stream contains data after the JPEG stream's EOI (end of image) marker it will probably be pulled into the internal buffer.
+// Call the get_total_bytes_read() method to determine the actual size of the JPEG stream after successful decoding.
+struct jpeg_decoder_stream
+{
+ jpeg_decoder_stream() { }
+ virtual ~jpeg_decoder_stream() { }
+
+ // The read() method is called when the internal input buffer is empty.
+ // Parameters:
+ // pBuf - input buffer
+ // max_bytes_to_read - maximum bytes that can be written to pBuf
+ // pEOF_flag - set this to true if at end of stream (no more bytes remaining)
+ // Returns -1 on error, otherwise return the number of bytes actually written to the buffer (which may be 0).
+ // Notes: This method will be called in a loop until you set *pEOF_flag to true or the internal buffer is full.
+ virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag) = 0;
+};
+
+
+// stdio FILE stream class.
+class jpeg_decoder_file_stream : public jpeg_decoder_stream
+{
+ jpeg_decoder_file_stream(const jpeg_decoder_file_stream &);
+ jpeg_decoder_file_stream &operator =(const jpeg_decoder_file_stream &);
+
+ FILE *m_pFile = nullptr;
+ bool m_eof_flag = false;
+ bool m_error_flag = false;
+
+public:
+ jpeg_decoder_file_stream() {}
+ virtual ~jpeg_decoder_file_stream();
+ bool open(const char *Pfilename);
+ void close();
+ virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag);
+ };
+
+
+// Memory stream class.
+class jpeg_decoder_mem_stream : public jpeg_decoder_stream
+{
+ const uint8_t *m_pSrc_data;
+ uint32_t m_ofs, m_size;
+
+public:
+ jpeg_decoder_mem_stream() : m_pSrc_data(nullptr), m_ofs(0), m_size(0) {}
+ jpeg_decoder_mem_stream(const uint8_t *pSrc_data, uint32_t size) : m_pSrc_data(pSrc_data), m_ofs(0), m_size(size) {}
+ virtual ~jpeg_decoder_mem_stream() {}
+ bool open(const uint8_t *pSrc_data, uint32_t size);
+ void close() { m_pSrc_data = nullptr; m_ofs = 0; m_size = 0; }
+ virtual int read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag);
+};
+
+
+class jpeg_decoder
+{
+public:
+ // Call get_error_code() after constructing to determine if the stream is valid or not. You may call the get_width(), get_height(), etc.
+ // methods after the constructor is called. You may then either destruct the object, or begin decoding the image by calling begin_decoding(), then decode() on each scanline.
+ jpeg_decoder(jpeg_decoder_stream *pStream);
+ ~jpeg_decoder();
+
+ // Call this method after constructing the object to begin decompression.
+ // If JPGD_SUCCESS is returned you may then call decode() on each scanline.
+ int begin_decoding();
+ // Returns the next scan line.
+ // For grayscale images, pScan_line will point to a buffer containing 8-bit pixels (get_bytes_per_pixel() will return 1).
+ // Otherwise, it will always point to a buffer containing 32-bit RGBA pixels (A will always be 255, and get_bytes_per_pixel() will return 4).
+ // Returns JPGD_SUCCESS if a scan line has been returned.
+ // Returns JPGD_DONE if all scan lines have been returned.
+ // Returns JPGD_FAILED if an error occurred. Call get_error_code() for a more info.
+ int decode(const void** pScan_line, uint32_t* pScan_line_len);
+ inline jpgd_status get_error_code() const { return m_error_code; }
+ inline int get_width() const { return m_image_x_size; }
+ inline int get_height() const { return m_image_y_size; }
+ inline int get_num_components() const { return m_comps_in_frame; }
+ inline int get_bytes_per_pixel() const { return m_dest_bytes_per_pixel; }
+ inline int get_bytes_per_scan_line() const { return m_image_x_size * get_bytes_per_pixel(); }
+ // Returns the total number of bytes actually consumed by the decoder (which should equal the actual size of the JPEG file).
+ inline int get_total_bytes_read() const { return m_total_bytes_read; }
+
+private:
+ jpeg_decoder(const jpeg_decoder &);
+ jpeg_decoder &operator =(const jpeg_decoder &);
+
+ typedef void (*pDecode_block_func)(jpeg_decoder *, int, int, int);
+
+ struct huff_tables
+ {
+ bool ac_table;
+ uint32_t look_up[256];
+ uint32_t look_up2[256];
+ uint8_t code_size[256];
+ uint32_t tree[512];
+ };
+
+ struct coeff_buf
+ {
+ uint8_t *pData;
+ int block_num_x, block_num_y;
+ int block_len_x, block_len_y;
+ int block_size;
+ };
+
+ struct mem_block
+ {
+ mem_block *m_pNext;
+ size_t m_used_count;
+ size_t m_size;
+ char m_data[1];
+ };
+
+ jmp_buf m_jmp_state;
+ mem_block *m_pMem_blocks;
+ int m_image_x_size;
+ int m_image_y_size;
+ jpeg_decoder_stream *m_pStream;
+ int m_progressive_flag;
+ uint8_t m_huff_ac[JPGD_MAX_HUFF_TABLES];
+ uint8_t* m_huff_num[JPGD_MAX_HUFF_TABLES]; // pointer to number of Huffman codes per bit size
+ uint8_t* m_huff_val[JPGD_MAX_HUFF_TABLES]; // pointer to Huffman codes per bit size
+ jpgd_quant_t* m_quant[JPGD_MAX_QUANT_TABLES]; // pointer to quantization tables
+ int m_scan_type; // Gray, Yh1v1, Yh1v2, Yh2v1, Yh2v2 (CMYK111, CMYK4114 no longer supported)
+ int m_comps_in_frame; // # of components in frame
+ int m_comp_h_samp[JPGD_MAX_COMPONENTS]; // component's horizontal sampling factor
+ int m_comp_v_samp[JPGD_MAX_COMPONENTS]; // component's vertical sampling factor
+ int m_comp_quant[JPGD_MAX_COMPONENTS]; // component's quantization table selector
+ int m_comp_ident[JPGD_MAX_COMPONENTS]; // component's ID
+ int m_comp_h_blocks[JPGD_MAX_COMPONENTS];
+ int m_comp_v_blocks[JPGD_MAX_COMPONENTS];
+ int m_comps_in_scan; // # of components in scan
+ int m_comp_list[JPGD_MAX_COMPS_IN_SCAN]; // components in this scan
+ int m_comp_dc_tab[JPGD_MAX_COMPONENTS]; // component's DC Huffman coding table selector
+ int m_comp_ac_tab[JPGD_MAX_COMPONENTS]; // component's AC Huffman coding table selector
+ int m_spectral_start; // spectral selection start
+ int m_spectral_end; // spectral selection end
+ int m_successive_low; // successive approximation low
+ int m_successive_high; // successive approximation high
+ int m_max_mcu_x_size; // MCU's max. X size in pixels
+ int m_max_mcu_y_size; // MCU's max. Y size in pixels
+ int m_blocks_per_mcu;
+ int m_max_blocks_per_row;
+ int m_mcus_per_row, m_mcus_per_col;
+ int m_mcu_org[JPGD_MAX_BLOCKS_PER_MCU];
+ int m_total_lines_left; // total # lines left in image
+ int m_mcu_lines_left; // total # lines left in this MCU
+ int m_real_dest_bytes_per_scan_line;
+ int m_dest_bytes_per_scan_line; // rounded up
+ int m_dest_bytes_per_pixel; // 4 (RGB) or 1 (Y)
+ huff_tables* m_pHuff_tabs[JPGD_MAX_HUFF_TABLES];
+ coeff_buf* m_dc_coeffs[JPGD_MAX_COMPONENTS];
+ coeff_buf* m_ac_coeffs[JPGD_MAX_COMPONENTS];
+ int m_eob_run;
+ int m_block_y_mcu[JPGD_MAX_COMPONENTS];
+ uint8_t* m_pIn_buf_ofs;
+ int m_in_buf_left;
+ int m_tem_flag;
+ bool m_eof_flag;
+ uint8_t m_in_buf_pad_start[128];
+ uint8_t m_in_buf[JPGD_IN_BUF_SIZE + 128];
+ uint8_t m_in_buf_pad_end[128];
+ int m_bits_left;
+ uint32_t m_bit_buf;
+ int m_restart_interval;
+ int m_restarts_left;
+ int m_next_restart_num;
+ int m_max_mcus_per_row;
+ int m_max_blocks_per_mcu;
+ int m_expanded_blocks_per_mcu;
+ int m_expanded_blocks_per_row;
+ int m_expanded_blocks_per_component;
+ bool m_freq_domain_chroma_upsample;
+ int m_max_mcus_per_col;
+ uint32_t m_last_dc_val[JPGD_MAX_COMPONENTS];
+ jpgd_block_t* m_pMCU_coefficients;
+ int m_mcu_block_max_zag[JPGD_MAX_BLOCKS_PER_MCU];
+ uint8_t* m_pSample_buf;
+ int m_crr[256];
+ int m_cbb[256];
+ int m_crg[256];
+ int m_cbg[256];
+ uint8_t* m_pScan_line_0;
+ uint8_t* m_pScan_line_1;
+ jpgd_status m_error_code;
+ bool m_ready_flag;
+ int m_total_bytes_read;
+
+ void free_all_blocks();
+ JPGD_NORETURN void stop_decoding(jpgd_status status);
+ void *alloc(size_t n, bool zero = false);
+ void word_clear(void *p, uint16_t c, uint32_t n);
+ void prep_in_buffer();
+ void read_dht_marker();
+ void read_dqt_marker();
+ void read_sof_marker();
+ void skip_variable_marker();
+ void read_dri_marker();
+ void read_sos_marker();
+ int next_marker();
+ int process_markers();
+ void locate_soi_marker();
+ void locate_sof_marker();
+ int locate_sos_marker();
+ void init(jpeg_decoder_stream * pStream);
+ void create_look_ups();
+ void fix_in_buffer();
+ void transform_mcu(int mcu_row);
+ void transform_mcu_expand(int mcu_row);
+ coeff_buf* coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y);
+ inline jpgd_block_t *coeff_buf_getp(coeff_buf *cb, int block_x, int block_y);
+ void load_next_row();
+ void decode_next_row();
+ void make_huff_table(int index, huff_tables *pH);
+ void check_quant_tables();
+ void check_huff_tables();
+ void calc_mcu_block_order();
+ int init_scan();
+ void init_frame();
+ void process_restart();
+ void decode_scan(pDecode_block_func decode_block_func);
+ void init_progressive();
+ void init_sequential();
+ void decode_start();
+ void decode_init(jpeg_decoder_stream * pStream);
+ void H2V2Convert();
+ void H2V1Convert();
+ void H1V2Convert();
+ void H1V1Convert();
+ void gray_convert();
+ void expanded_convert();
+ void find_eoi();
+ inline uint32_t get_char();
+ inline uint32_t get_char(bool *pPadding_flag);
+ inline void stuff_char(uint8_t q);
+ inline uint8_t get_octet();
+ inline uint32_t get_bits(int num_bits);
+ inline uint32_t get_bits_no_markers(int numbits);
+ inline int huff_decode(huff_tables *pH);
+ inline int huff_decode(huff_tables *pH, int& extrabits);
+ static inline uint8_t clamp(int i);
+ static void decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+ static void decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+ static void decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+ static void decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y);
+};
+
+
+// DCT coefficients are stored in this sequence.
+static int g_ZAG[64] = { 0,1,8,16,9,2,3,10,17,24,32,25,18,11,4,5,12,19,26,33,40,48,41,34,27,20,13,6,7,14,21,28,35,42,49,56,57,50,43,36,29,22,15,23,30,37,44,51,58,59,52,45,38,31,39,46,53,60,61,54,47,55,62,63 };
+
+enum JPEG_MARKER
+{
+ M_SOF0 = 0xC0, M_SOF1 = 0xC1, M_SOF2 = 0xC2, M_SOF3 = 0xC3, M_SOF5 = 0xC5, M_SOF6 = 0xC6, M_SOF7 = 0xC7, M_JPG = 0xC8,
+ M_SOF9 = 0xC9, M_SOF10 = 0xCA, M_SOF11 = 0xCB, M_SOF13 = 0xCD, M_SOF14 = 0xCE, M_SOF15 = 0xCF, M_DHT = 0xC4, M_DAC = 0xCC,
+ M_RST0 = 0xD0, M_RST1 = 0xD1, M_RST2 = 0xD2, M_RST3 = 0xD3, M_RST4 = 0xD4, M_RST5 = 0xD5, M_RST6 = 0xD6, M_RST7 = 0xD7,
+ M_SOI = 0xD8, M_EOI = 0xD9, M_SOS = 0xDA, M_DQT = 0xDB, M_DNL = 0xDC, M_DRI = 0xDD, M_DHP = 0xDE, M_EXP = 0xDF,
+ M_APP0 = 0xE0, M_APP15 = 0xEF, M_JPG0 = 0xF0, M_JPG13 = 0xFD, M_COM = 0xFE, M_TEM = 0x01, M_ERROR = 0x100, RST0 = 0xD0
+};
+
+enum JPEG_SUBSAMPLING { JPGD_GRAYSCALE = 0, JPGD_YH1V1, JPGD_YH2V1, JPGD_YH1V2, JPGD_YH2V2 };
+
+#define CONST_BITS 13
+#define PASS1_BITS 2
+#define SCALEDONE ((int32_t)1)
+#define DESCALE(x,n) (((x) + (SCALEDONE << ((n)-1))) >> (n))
+#define DESCALE_ZEROSHIFT(x,n) (((x) + (128 << (n)) + (SCALEDONE << ((n)-1))) >> (n))
+#define MULTIPLY(var, cnst) ((var) * (cnst))
+#define CLAMP(i) ((static_cast<uint32_t>(i) > 255) ? (((~i) >> 31) & 0xFF) : (i))
+
+#define FIX_0_298631336 ((int32_t)2446) /* FIX(0.298631336) */
+#define FIX_0_390180644 ((int32_t)3196) /* FIX(0.390180644) */
+#define FIX_0_541196100 ((int32_t)4433) /* FIX(0.541196100) */
+#define FIX_0_765366865 ((int32_t)6270) /* FIX(0.765366865) */
+#define FIX_0_899976223 ((int32_t)7373) /* FIX(0.899976223) */
+#define FIX_1_175875602 ((int32_t)9633) /* FIX(1.175875602) */
+#define FIX_1_501321110 ((int32_t)12299) /* FIX(1.501321110) */
+#define FIX_1_847759065 ((int32_t)15137) /* FIX(1.847759065) */
+#define FIX_1_961570560 ((int32_t)16069) /* FIX(1.961570560) */
+#define FIX_2_053119869 ((int32_t)16819) /* FIX(2.053119869) */
+#define FIX_2_562915447 ((int32_t)20995) /* FIX(2.562915447) */
+#define FIX_3_072711026 ((int32_t)25172) /* FIX(3.072711026) */
+
+
+// Compiler creates a fast path 1D IDCT for X non-zero columns
+template <int NONZERO_COLS>
+struct Row
+{
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
+ {
+ // ACCESS_COL() will be optimized at compile time to either an array access, or 0.
+ #define ACCESS_COL(x) (((x) < NONZERO_COLS) ? (int)pSrc[x] : 0)
+
+ const int z2 = ACCESS_COL(2), z3 = ACCESS_COL(6);
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
+
+ const int tmp0 = static_cast<unsigned int>(ACCESS_COL(0) + ACCESS_COL(4)) << CONST_BITS;
+ const int tmp1 = static_cast<unsigned int>(ACCESS_COL(0) - ACCESS_COL(4)) << CONST_BITS;
+
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
+
+ const int atmp0 = ACCESS_COL(7), atmp1 = ACCESS_COL(5), atmp2 = ACCESS_COL(3), atmp3 = ACCESS_COL(1);
+
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
+
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
+
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
+
+ pTemp[0] = DESCALE(tmp10 + btmp3, CONST_BITS-PASS1_BITS);
+ pTemp[7] = DESCALE(tmp10 - btmp3, CONST_BITS-PASS1_BITS);
+ pTemp[1] = DESCALE(tmp11 + btmp2, CONST_BITS-PASS1_BITS);
+ pTemp[6] = DESCALE(tmp11 - btmp2, CONST_BITS-PASS1_BITS);
+ pTemp[2] = DESCALE(tmp12 + btmp1, CONST_BITS-PASS1_BITS);
+ pTemp[5] = DESCALE(tmp12 - btmp1, CONST_BITS-PASS1_BITS);
+ pTemp[3] = DESCALE(tmp13 + btmp0, CONST_BITS-PASS1_BITS);
+ pTemp[4] = DESCALE(tmp13 - btmp0, CONST_BITS-PASS1_BITS);
+ }
+};
+
+
+template <>
+struct Row<0>
+{
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
+ {
+#ifdef _MSC_VER
+ pTemp; pSrc;
+#endif
+ }
+};
+
+
+template <>
+struct Row<1>
+{
+ static void idct(int* pTemp, const jpgd_block_t* pSrc)
+ {
+ const int dcval = (pSrc[0] << PASS1_BITS);
+
+ pTemp[0] = dcval;
+ pTemp[1] = dcval;
+ pTemp[2] = dcval;
+ pTemp[3] = dcval;
+ pTemp[4] = dcval;
+ pTemp[5] = dcval;
+ pTemp[6] = dcval;
+ pTemp[7] = dcval;
+ }
+};
+
+
+// Compiler creates a fast path 1D IDCT for X non-zero rows
+template <int NONZERO_ROWS>
+struct Col
+{
+ static void idct(uint8_t* pDst_ptr, const int* pTemp)
+ {
+ // ACCESS_ROW() will be optimized at compile time to either an array access, or 0.
+ #define ACCESS_ROW(x) (((x) < NONZERO_ROWS) ? pTemp[x * 8] : 0)
+
+ const int z2 = ACCESS_ROW(2);
+ const int z3 = ACCESS_ROW(6);
+
+ const int z1 = MULTIPLY(z2 + z3, FIX_0_541196100);
+ const int tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065);
+ const int tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865);
+
+ const int tmp0 = static_cast<unsigned int>(ACCESS_ROW(0) + ACCESS_ROW(4)) << CONST_BITS;
+ const int tmp1 = static_cast<unsigned int>(ACCESS_ROW(0) - ACCESS_ROW(4)) << CONST_BITS;
+
+ const int tmp10 = tmp0 + tmp3, tmp13 = tmp0 - tmp3, tmp11 = tmp1 + tmp2, tmp12 = tmp1 - tmp2;
+
+ const int atmp0 = ACCESS_ROW(7), atmp1 = ACCESS_ROW(5), atmp2 = ACCESS_ROW(3), atmp3 = ACCESS_ROW(1);
+
+ const int bz1 = atmp0 + atmp3, bz2 = atmp1 + atmp2, bz3 = atmp0 + atmp2, bz4 = atmp1 + atmp3;
+ const int bz5 = MULTIPLY(bz3 + bz4, FIX_1_175875602);
+
+ const int az1 = MULTIPLY(bz1, - FIX_0_899976223);
+ const int az2 = MULTIPLY(bz2, - FIX_2_562915447);
+ const int az3 = MULTIPLY(bz3, - FIX_1_961570560) + bz5;
+ const int az4 = MULTIPLY(bz4, - FIX_0_390180644) + bz5;
+
+ const int btmp0 = MULTIPLY(atmp0, FIX_0_298631336) + az1 + az3;
+ const int btmp1 = MULTIPLY(atmp1, FIX_2_053119869) + az2 + az4;
+ const int btmp2 = MULTIPLY(atmp2, FIX_3_072711026) + az2 + az3;
+ const int btmp3 = MULTIPLY(atmp3, FIX_1_501321110) + az1 + az4;
+
+ int i = DESCALE_ZEROSHIFT(tmp10 + btmp3, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*0] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp10 - btmp3, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*7] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp11 + btmp2, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*1] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp11 - btmp2, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*6] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp12 + btmp1, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*2] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp12 - btmp1, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*5] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp13 + btmp0, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*3] = (uint8_t)CLAMP(i);
+
+ i = DESCALE_ZEROSHIFT(tmp13 - btmp0, CONST_BITS+PASS1_BITS+3);
+ pDst_ptr[8*4] = (uint8_t)CLAMP(i);
+ }
+};
+
+
+template <>
+struct Col<1>
+{
+ static void idct(uint8_t* pDst_ptr, const int* pTemp)
+ {
+ int dcval = DESCALE_ZEROSHIFT(pTemp[0], PASS1_BITS+3);
+ const uint8_t dcval_clamped = (uint8_t)CLAMP(dcval);
+ pDst_ptr[0*8] = dcval_clamped;
+ pDst_ptr[1*8] = dcval_clamped;
+ pDst_ptr[2*8] = dcval_clamped;
+ pDst_ptr[3*8] = dcval_clamped;
+ pDst_ptr[4*8] = dcval_clamped;
+ pDst_ptr[5*8] = dcval_clamped;
+ pDst_ptr[6*8] = dcval_clamped;
+ pDst_ptr[7*8] = dcval_clamped;
+ }
+};
+
+
+static const uint8_t s_idct_row_table[] = {
+ 1,0,0,0,0,0,0,0, 2,0,0,0,0,0,0,0, 2,1,0,0,0,0,0,0, 2,1,1,0,0,0,0,0, 2,2,1,0,0,0,0,0, 3,2,1,0,0,0,0,0, 4,2,1,0,0,0,0,0, 4,3,1,0,0,0,0,0,
+ 4,3,2,0,0,0,0,0, 4,3,2,1,0,0,0,0, 4,3,2,1,1,0,0,0, 4,3,2,2,1,0,0,0, 4,3,3,2,1,0,0,0, 4,4,3,2,1,0,0,0, 5,4,3,2,1,0,0,0, 6,4,3,2,1,0,0,0,
+ 6,5,3,2,1,0,0,0, 6,5,4,2,1,0,0,0, 6,5,4,3,1,0,0,0, 6,5,4,3,2,0,0,0, 6,5,4,3,2,1,0,0, 6,5,4,3,2,1,1,0, 6,5,4,3,2,2,1,0, 6,5,4,3,3,2,1,0,
+ 6,5,4,4,3,2,1,0, 6,5,5,4,3,2,1,0, 6,6,5,4,3,2,1,0, 7,6,5,4,3,2,1,0, 8,6,5,4,3,2,1,0, 8,7,5,4,3,2,1,0, 8,7,6,4,3,2,1,0, 8,7,6,5,3,2,1,0,
+ 8,7,6,5,4,2,1,0, 8,7,6,5,4,3,1,0, 8,7,6,5,4,3,2,0, 8,7,6,5,4,3,2,1, 8,7,6,5,4,3,2,2, 8,7,6,5,4,3,3,2, 8,7,6,5,4,4,3,2, 8,7,6,5,5,4,3,2,
+ 8,7,6,6,5,4,3,2, 8,7,7,6,5,4,3,2, 8,8,7,6,5,4,3,2, 8,8,8,6,5,4,3,2, 8,8,8,7,5,4,3,2, 8,8,8,7,6,4,3,2, 8,8,8,7,6,5,3,2, 8,8,8,7,6,5,4,2,
+ 8,8,8,7,6,5,4,3, 8,8,8,7,6,5,4,4, 8,8,8,7,6,5,5,4, 8,8,8,7,6,6,5,4, 8,8,8,7,7,6,5,4, 8,8,8,8,7,6,5,4, 8,8,8,8,8,6,5,4, 8,8,8,8,8,7,5,4,
+ 8,8,8,8,8,7,6,4, 8,8,8,8,8,7,6,5, 8,8,8,8,8,7,6,6, 8,8,8,8,8,7,7,6, 8,8,8,8,8,8,7,6, 8,8,8,8,8,8,8,6, 8,8,8,8,8,8,8,7, 8,8,8,8,8,8,8,8,
+};
+
+
+static const uint8_t s_idct_col_table[] = { 1, 1, 2, 3, 3, 3, 3, 3, 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8 };
+
+
+void idct(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr, int block_max_zag)
+{
+ JPGD_ASSERT(block_max_zag >= 1);
+ JPGD_ASSERT(block_max_zag <= 64);
+
+ if (block_max_zag <= 1) {
+ int k = ((pSrc_ptr[0] + 4) >> 3) + 128;
+ k = CLAMP(k);
+ k = k | (k<<8);
+ k = k | (k<<16);
+ for (int i = 8; i > 0; i--) {
+ *(int*)&pDst_ptr[0] = k;
+ *(int*)&pDst_ptr[4] = k;
+ pDst_ptr += 8;
+ }
+ return;
+ }
+
+ int temp[64];
+ const jpgd_block_t* pSrc = pSrc_ptr;
+ int* pTemp = temp;
+ const uint8_t* pRow_tab = &s_idct_row_table[(block_max_zag - 1) * 8];
+ int i;
+ for (i = 8; i > 0; i--, pRow_tab++) {
+ switch (*pRow_tab) {
+ case 0: Row<0>::idct(pTemp, pSrc); break;
+ case 1: Row<1>::idct(pTemp, pSrc); break;
+ case 2: Row<2>::idct(pTemp, pSrc); break;
+ case 3: Row<3>::idct(pTemp, pSrc); break;
+ case 4: Row<4>::idct(pTemp, pSrc); break;
+ case 5: Row<5>::idct(pTemp, pSrc); break;
+ case 6: Row<6>::idct(pTemp, pSrc); break;
+ case 7: Row<7>::idct(pTemp, pSrc); break;
+ case 8: Row<8>::idct(pTemp, pSrc); break;
+ }
+ pSrc += 8;
+ pTemp += 8;
+ }
+
+ pTemp = temp;
+
+ const int nonzero_rows = s_idct_col_table[block_max_zag - 1];
+ for (i = 8; i > 0; i--) {
+ switch (nonzero_rows) {
+ case 1: Col<1>::idct(pDst_ptr, pTemp); break;
+ case 2: Col<2>::idct(pDst_ptr, pTemp); break;
+ case 3: Col<3>::idct(pDst_ptr, pTemp); break;
+ case 4: Col<4>::idct(pDst_ptr, pTemp); break;
+ case 5: Col<5>::idct(pDst_ptr, pTemp); break;
+ case 6: Col<6>::idct(pDst_ptr, pTemp); break;
+ case 7: Col<7>::idct(pDst_ptr, pTemp); break;
+ case 8: Col<8>::idct(pDst_ptr, pTemp); break;
+ }
+ pTemp++;
+ pDst_ptr++;
+ }
+}
+
+
+void idct_4x4(const jpgd_block_t* pSrc_ptr, uint8_t* pDst_ptr)
+{
+ int temp[64];
+ int* pTemp = temp;
+ const jpgd_block_t* pSrc = pSrc_ptr;
+
+ for (int i = 4; i > 0; i--) {
+ Row<4>::idct(pTemp, pSrc);
+ pSrc += 8;
+ pTemp += 8;
+ }
+
+ pTemp = temp;
+
+ for (int i = 8; i > 0; i--) {
+ Col<4>::idct(pDst_ptr, pTemp);
+ pTemp++;
+ pDst_ptr++;
+ }
+}
+
+
+// Retrieve one character from the input stream.
+inline uint32_t jpeg_decoder::get_char()
+{
+ // Any bytes remaining in buffer?
+ if (!m_in_buf_left) {
+ // Try to get more bytes.
+ prep_in_buffer();
+ // Still nothing to get?
+ if (!m_in_buf_left) {
+ // Pad the end of the stream with 0xFF 0xD9 (EOI marker)
+ int t = m_tem_flag;
+ m_tem_flag ^= 1;
+ if (t) return 0xD9;
+ else return 0xFF;
+ }
+ }
+ uint32_t c = *m_pIn_buf_ofs++;
+ m_in_buf_left--;
+ return c;
+}
+
+
+// Same as previous method, except can indicate if the character is a pad character or not.
+inline uint32_t jpeg_decoder::get_char(bool *pPadding_flag)
+{
+ if (!m_in_buf_left) {
+ prep_in_buffer();
+ if (!m_in_buf_left) {
+ *pPadding_flag = true;
+ int t = m_tem_flag;
+ m_tem_flag ^= 1;
+ if (t) return 0xD9;
+ else return 0xFF;
+ }
+ }
+ *pPadding_flag = false;
+ uint32_t c = *m_pIn_buf_ofs++;
+ m_in_buf_left--;
+
+ return c;
+}
+
+
+// Inserts a previously retrieved character back into the input buffer.
+inline void jpeg_decoder::stuff_char(uint8_t q)
+{
+ *(--m_pIn_buf_ofs) = q;
+ m_in_buf_left++;
+}
+
+
+// Retrieves one character from the input stream, but does not read past markers. Will continue to return 0xFF when a marker is encountered.
+inline uint8_t jpeg_decoder::get_octet()
+{
+ bool padding_flag;
+ int c = get_char(&padding_flag);
+
+ if (c == 0xFF) {
+ if (padding_flag) return 0xFF;
+
+ c = get_char(&padding_flag);
+ if (padding_flag) {
+ stuff_char(0xFF);
+ return 0xFF;
+ }
+ if (c == 0x00) return 0xFF;
+ else {
+ stuff_char(static_cast<uint8_t>(c));
+ stuff_char(0xFF);
+ return 0xFF;
+ }
+ }
+ return static_cast<uint8_t>(c);
+}
+
+
+// Retrieves a variable number of bits from the input stream. Does not recognize markers.
+inline uint32_t jpeg_decoder::get_bits(int num_bits)
+{
+ if (!num_bits) return 0;
+
+ uint32_t i = m_bit_buf >> (32 - num_bits);
+
+ if ((m_bits_left -= num_bits) <= 0) {
+ m_bit_buf <<= (num_bits += m_bits_left);
+ uint32_t c1 = get_char();
+ uint32_t c2 = get_char();
+ m_bit_buf = (m_bit_buf & 0xFFFF0000) | (c1 << 8) | c2;
+ m_bit_buf <<= -m_bits_left;
+ m_bits_left += 16;
+ JPGD_ASSERT(m_bits_left >= 0);
+ }
+ else m_bit_buf <<= num_bits;
+
+ return i;
+}
+
+
+// Retrieves a variable number of bits from the input stream. Markers will not be read into the input bit buffer. Instead, an infinite number of all 1's will be returned when a marker is encountered.
+inline uint32_t jpeg_decoder::get_bits_no_markers(int num_bits)
+{
+ if (!num_bits)return 0;
+
+ uint32_t i = m_bit_buf >> (32 - num_bits);
+
+ if ((m_bits_left -= num_bits) <= 0) {
+ m_bit_buf <<= (num_bits += m_bits_left);
+ if ((m_in_buf_left < 2) || (m_pIn_buf_ofs[0] == 0xFF) || (m_pIn_buf_ofs[1] == 0xFF)) {
+ uint32_t c1 = get_octet();
+ uint32_t c2 = get_octet();
+ m_bit_buf |= (c1 << 8) | c2;
+ } else {
+ m_bit_buf |= ((uint32_t)m_pIn_buf_ofs[0] << 8) | m_pIn_buf_ofs[1];
+ m_in_buf_left -= 2;
+ m_pIn_buf_ofs += 2;
+ }
+ m_bit_buf <<= -m_bits_left;
+ m_bits_left += 16;
+ JPGD_ASSERT(m_bits_left >= 0);
+ } else m_bit_buf <<= num_bits;
+
+ return i;
+}
+
+
+// Decodes a Huffman encoded symbol.
+inline int jpeg_decoder::huff_decode(huff_tables *pH)
+{
+ int symbol;
+
+ // Check first 8-bits: do we have a complete symbol?
+ if ((symbol = pH->look_up[m_bit_buf >> 24]) < 0) {
+ // Decode more bits, use a tree traversal to find symbol.
+ int ofs = 23;
+ do {
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
+ ofs--;
+ } while (symbol < 0);
+ get_bits_no_markers(8 + (23 - ofs));
+ } else get_bits_no_markers(pH->code_size[symbol]);
+
+ return symbol;
+}
+
+
+// Decodes a Huffman encoded symbol.
+inline int jpeg_decoder::huff_decode(huff_tables *pH, int& extra_bits)
+{
+ int symbol;
+
+ // Check first 8-bits: do we have a complete symbol?
+ if ((symbol = pH->look_up2[m_bit_buf >> 24]) < 0) {
+ // Use a tree traversal to find symbol.
+ int ofs = 23;
+ do {
+ symbol = pH->tree[-(int)(symbol + ((m_bit_buf >> ofs) & 1))];
+ ofs--;
+ } while (symbol < 0);
+
+ get_bits_no_markers(8 + (23 - ofs));
+ extra_bits = get_bits_no_markers(symbol & 0xF);
+ } else {
+ JPGD_ASSERT(((symbol >> 8) & 31) == pH->code_size[symbol & 255] + ((symbol & 0x8000) ? (symbol & 15) : 0));
+
+ if (symbol & 0x8000) {
+ get_bits_no_markers((symbol >> 8) & 31);
+ extra_bits = symbol >> 16;
+ } else {
+ int code_size = (symbol >> 8) & 31;
+ int num_extra_bits = symbol & 0xF;
+ int bits = code_size + num_extra_bits;
+ if (bits <= (m_bits_left + 16)) extra_bits = get_bits_no_markers(bits) & ((1 << num_extra_bits) - 1);
+ else {
+ get_bits_no_markers(code_size);
+ extra_bits = get_bits_no_markers(num_extra_bits);
+ }
+ }
+ symbol &= 0xFF;
+ }
+ return symbol;
+}
+
+
+// Tables and macro used to fully decode the DPCM differences.
+static const int s_extend_test[16] = { 0, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000 };
+static const unsigned int s_extend_offset[16] = { 0, ((-1u)<<1) + 1, ((-1u)<<2) + 1, ((-1u)<<3) + 1, ((-1u)<<4) + 1, ((-1u)<<5) + 1, ((-1u)<<6) + 1, ((-1u)<<7) + 1, ((-1u)<<8) + 1, ((-1u)<<9) + 1, ((-1u)<<10) + 1, ((-1u)<<11) + 1, ((-1u)<<12) + 1, ((-1u)<<13) + 1, ((-1u)<<14) + 1, ((-1u)<<15) + 1 };
+
+// The logical AND's in this macro are to shut up static code analysis (aren't really necessary - couldn't find another way to do this)
+#define JPGD_HUFF_EXTEND(x, s) (((x) < s_extend_test[s & 15]) ? ((x) + s_extend_offset[s & 15]) : (x))
+
+
+// Clamps a value between 0-255.
+inline uint8_t jpeg_decoder::clamp(int i)
+{
+ if (static_cast<uint32_t>(i) > 255) i = (((~i) >> 31) & 0xFF);
+ return static_cast<uint8_t>(i);
+}
+
+
+namespace DCT_Upsample
+{
+ struct Matrix44
+ {
+ typedef int Element_Type;
+ enum { NUM_ROWS = 4, NUM_COLS = 4 };
+
+ Element_Type v[NUM_ROWS][NUM_COLS];
+
+ inline int rows() const { return NUM_ROWS; }
+ inline int cols() const { return NUM_COLS; }
+ inline const Element_Type & at(int r, int c) const { return v[r][c]; }
+ inline Element_Type & at(int r, int c) { return v[r][c]; }
+
+ inline Matrix44() {}
+
+ inline Matrix44& operator += (const Matrix44& a)
+ {
+ for (int r = 0; r < NUM_ROWS; r++) {
+ at(r, 0) += a.at(r, 0);
+ at(r, 1) += a.at(r, 1);
+ at(r, 2) += a.at(r, 2);
+ at(r, 3) += a.at(r, 3);
+ }
+ return *this;
+ }
+
+ inline Matrix44& operator -= (const Matrix44& a)
+ {
+ for (int r = 0; r < NUM_ROWS; r++) {
+ at(r, 0) -= a.at(r, 0);
+ at(r, 1) -= a.at(r, 1);
+ at(r, 2) -= a.at(r, 2);
+ at(r, 3) -= a.at(r, 3);
+ }
+ return *this;
+ }
+
+ friend inline Matrix44 operator + (const Matrix44& a, const Matrix44& b)
+ {
+ Matrix44 ret;
+ for (int r = 0; r < NUM_ROWS; r++) {
+ ret.at(r, 0) = a.at(r, 0) + b.at(r, 0);
+ ret.at(r, 1) = a.at(r, 1) + b.at(r, 1);
+ ret.at(r, 2) = a.at(r, 2) + b.at(r, 2);
+ ret.at(r, 3) = a.at(r, 3) + b.at(r, 3);
+ }
+ return ret;
+ }
+
+ friend inline Matrix44 operator - (const Matrix44& a, const Matrix44& b)
+ {
+ Matrix44 ret;
+ for (int r = 0; r < NUM_ROWS; r++) {
+ ret.at(r, 0) = a.at(r, 0) - b.at(r, 0);
+ ret.at(r, 1) = a.at(r, 1) - b.at(r, 1);
+ ret.at(r, 2) = a.at(r, 2) - b.at(r, 2);
+ ret.at(r, 3) = a.at(r, 3) - b.at(r, 3);
+ }
+ return ret;
+ }
+
+ static inline void add_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
+ {
+ for (int r = 0; r < 4; r++) {
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) + b.at(r, 0));
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) + b.at(r, 1));
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) + b.at(r, 2));
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) + b.at(r, 3));
+ }
+ }
+
+ static inline void sub_and_store(jpgd_block_t* pDst, const Matrix44& a, const Matrix44& b)
+ {
+ for (int r = 0; r < 4; r++) {
+ pDst[0*8 + r] = static_cast<jpgd_block_t>(a.at(r, 0) - b.at(r, 0));
+ pDst[1*8 + r] = static_cast<jpgd_block_t>(a.at(r, 1) - b.at(r, 1));
+ pDst[2*8 + r] = static_cast<jpgd_block_t>(a.at(r, 2) - b.at(r, 2));
+ pDst[3*8 + r] = static_cast<jpgd_block_t>(a.at(r, 3) - b.at(r, 3));
+ }
+ }
+ };
+
+ const int FRACT_BITS = 10;
+ const int SCALE = 1 << FRACT_BITS;
+
+ typedef int Temp_Type;
+ #define D(i) (((i) + (SCALE >> 1)) >> FRACT_BITS)
+ #define F(i) ((int)((i) * SCALE + .5f))
+
+ // Any decent C++ compiler will optimize this at compile time to a 0, or an array access.
+ #define AT(c, r) ((((c)>=NUM_COLS)||((r)>=NUM_ROWS)) ? 0 : pSrc[(c)+(r)*8])
+
+ // NUM_ROWS/NUM_COLS = # of non-zero rows/cols in input matrix
+ template<int NUM_ROWS, int NUM_COLS>
+ struct P_Q
+ {
+ static void calc(Matrix44& P, Matrix44& Q, const jpgd_block_t* pSrc)
+ {
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
+ const Temp_Type X000 = AT(0, 0);
+ const Temp_Type X001 = AT(0, 1);
+ const Temp_Type X002 = AT(0, 2);
+ const Temp_Type X003 = AT(0, 3);
+ const Temp_Type X004 = AT(0, 4);
+ const Temp_Type X005 = AT(0, 5);
+ const Temp_Type X006 = AT(0, 6);
+ const Temp_Type X007 = AT(0, 7);
+ const Temp_Type X010 = D(F(0.415735f) * AT(1, 0) + F(0.791065f) * AT(3, 0) + F(-0.352443f) * AT(5, 0) + F(0.277785f) * AT(7, 0));
+ const Temp_Type X011 = D(F(0.415735f) * AT(1, 1) + F(0.791065f) * AT(3, 1) + F(-0.352443f) * AT(5, 1) + F(0.277785f) * AT(7, 1));
+ const Temp_Type X012 = D(F(0.415735f) * AT(1, 2) + F(0.791065f) * AT(3, 2) + F(-0.352443f) * AT(5, 2) + F(0.277785f) * AT(7, 2));
+ const Temp_Type X013 = D(F(0.415735f) * AT(1, 3) + F(0.791065f) * AT(3, 3) + F(-0.352443f) * AT(5, 3) + F(0.277785f) * AT(7, 3));
+ const Temp_Type X014 = D(F(0.415735f) * AT(1, 4) + F(0.791065f) * AT(3, 4) + F(-0.352443f) * AT(5, 4) + F(0.277785f) * AT(7, 4));
+ const Temp_Type X015 = D(F(0.415735f) * AT(1, 5) + F(0.791065f) * AT(3, 5) + F(-0.352443f) * AT(5, 5) + F(0.277785f) * AT(7, 5));
+ const Temp_Type X016 = D(F(0.415735f) * AT(1, 6) + F(0.791065f) * AT(3, 6) + F(-0.352443f) * AT(5, 6) + F(0.277785f) * AT(7, 6));
+ const Temp_Type X017 = D(F(0.415735f) * AT(1, 7) + F(0.791065f) * AT(3, 7) + F(-0.352443f) * AT(5, 7) + F(0.277785f) * AT(7, 7));
+ const Temp_Type X020 = AT(4, 0);
+ const Temp_Type X021 = AT(4, 1);
+ const Temp_Type X022 = AT(4, 2);
+ const Temp_Type X023 = AT(4, 3);
+ const Temp_Type X024 = AT(4, 4);
+ const Temp_Type X025 = AT(4, 5);
+ const Temp_Type X026 = AT(4, 6);
+ const Temp_Type X027 = AT(4, 7);
+ const Temp_Type X030 = D(F(0.022887f) * AT(1, 0) + F(-0.097545f) * AT(3, 0) + F(0.490393f) * AT(5, 0) + F(0.865723f) * AT(7, 0));
+ const Temp_Type X031 = D(F(0.022887f) * AT(1, 1) + F(-0.097545f) * AT(3, 1) + F(0.490393f) * AT(5, 1) + F(0.865723f) * AT(7, 1));
+ const Temp_Type X032 = D(F(0.022887f) * AT(1, 2) + F(-0.097545f) * AT(3, 2) + F(0.490393f) * AT(5, 2) + F(0.865723f) * AT(7, 2));
+ const Temp_Type X033 = D(F(0.022887f) * AT(1, 3) + F(-0.097545f) * AT(3, 3) + F(0.490393f) * AT(5, 3) + F(0.865723f) * AT(7, 3));
+ const Temp_Type X034 = D(F(0.022887f) * AT(1, 4) + F(-0.097545f) * AT(3, 4) + F(0.490393f) * AT(5, 4) + F(0.865723f) * AT(7, 4));
+ const Temp_Type X035 = D(F(0.022887f) * AT(1, 5) + F(-0.097545f) * AT(3, 5) + F(0.490393f) * AT(5, 5) + F(0.865723f) * AT(7, 5));
+ const Temp_Type X036 = D(F(0.022887f) * AT(1, 6) + F(-0.097545f) * AT(3, 6) + F(0.490393f) * AT(5, 6) + F(0.865723f) * AT(7, 6));
+ const Temp_Type X037 = D(F(0.022887f) * AT(1, 7) + F(-0.097545f) * AT(3, 7) + F(0.490393f) * AT(5, 7) + F(0.865723f) * AT(7, 7));
+
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ P.at(0, 0) = X000;
+ P.at(0, 1) = D(X001 * F(0.415735f) + X003 * F(0.791065f) + X005 * F(-0.352443f) + X007 * F(0.277785f));
+ P.at(0, 2) = X004;
+ P.at(0, 3) = D(X001 * F(0.022887f) + X003 * F(-0.097545f) + X005 * F(0.490393f) + X007 * F(0.865723f));
+ P.at(1, 0) = X010;
+ P.at(1, 1) = D(X011 * F(0.415735f) + X013 * F(0.791065f) + X015 * F(-0.352443f) + X017 * F(0.277785f));
+ P.at(1, 2) = X014;
+ P.at(1, 3) = D(X011 * F(0.022887f) + X013 * F(-0.097545f) + X015 * F(0.490393f) + X017 * F(0.865723f));
+ P.at(2, 0) = X020;
+ P.at(2, 1) = D(X021 * F(0.415735f) + X023 * F(0.791065f) + X025 * F(-0.352443f) + X027 * F(0.277785f));
+ P.at(2, 2) = X024;
+ P.at(2, 3) = D(X021 * F(0.022887f) + X023 * F(-0.097545f) + X025 * F(0.490393f) + X027 * F(0.865723f));
+ P.at(3, 0) = X030;
+ P.at(3, 1) = D(X031 * F(0.415735f) + X033 * F(0.791065f) + X035 * F(-0.352443f) + X037 * F(0.277785f));
+ P.at(3, 2) = X034;
+ P.at(3, 3) = D(X031 * F(0.022887f) + X033 * F(-0.097545f) + X035 * F(0.490393f) + X037 * F(0.865723f));
+ // 40 muls 24 adds
+
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ Q.at(0, 0) = D(X001 * F(0.906127f) + X003 * F(-0.318190f) + X005 * F(0.212608f) + X007 * F(-0.180240f));
+ Q.at(0, 1) = X002;
+ Q.at(0, 2) = D(X001 * F(-0.074658f) + X003 * F(0.513280f) + X005 * F(0.768178f) + X007 * F(-0.375330f));
+ Q.at(0, 3) = X006;
+ Q.at(1, 0) = D(X011 * F(0.906127f) + X013 * F(-0.318190f) + X015 * F(0.212608f) + X017 * F(-0.180240f));
+ Q.at(1, 1) = X012;
+ Q.at(1, 2) = D(X011 * F(-0.074658f) + X013 * F(0.513280f) + X015 * F(0.768178f) + X017 * F(-0.375330f));
+ Q.at(1, 3) = X016;
+ Q.at(2, 0) = D(X021 * F(0.906127f) + X023 * F(-0.318190f) + X025 * F(0.212608f) + X027 * F(-0.180240f));
+ Q.at(2, 1) = X022;
+ Q.at(2, 2) = D(X021 * F(-0.074658f) + X023 * F(0.513280f) + X025 * F(0.768178f) + X027 * F(-0.375330f));
+ Q.at(2, 3) = X026;
+ Q.at(3, 0) = D(X031 * F(0.906127f) + X033 * F(-0.318190f) + X035 * F(0.212608f) + X037 * F(-0.180240f));
+ Q.at(3, 1) = X032;
+ Q.at(3, 2) = D(X031 * F(-0.074658f) + X033 * F(0.513280f) + X035 * F(0.768178f) + X037 * F(-0.375330f));
+ Q.at(3, 3) = X036;
+ // 40 muls 24 adds
+ }
+ };
+
+
+ template<int NUM_ROWS, int NUM_COLS>
+ struct R_S
+ {
+ static void calc(Matrix44& R, Matrix44& S, const jpgd_block_t* pSrc)
+ {
+ // 4x8 = 4x8 times 8x8, matrix 0 is constant
+ const Temp_Type X100 = D(F(0.906127f) * AT(1, 0) + F(-0.318190f) * AT(3, 0) + F(0.212608f) * AT(5, 0) + F(-0.180240f) * AT(7, 0));
+ const Temp_Type X101 = D(F(0.906127f) * AT(1, 1) + F(-0.318190f) * AT(3, 1) + F(0.212608f) * AT(5, 1) + F(-0.180240f) * AT(7, 1));
+ const Temp_Type X102 = D(F(0.906127f) * AT(1, 2) + F(-0.318190f) * AT(3, 2) + F(0.212608f) * AT(5, 2) + F(-0.180240f) * AT(7, 2));
+ const Temp_Type X103 = D(F(0.906127f) * AT(1, 3) + F(-0.318190f) * AT(3, 3) + F(0.212608f) * AT(5, 3) + F(-0.180240f) * AT(7, 3));
+ const Temp_Type X104 = D(F(0.906127f) * AT(1, 4) + F(-0.318190f) * AT(3, 4) + F(0.212608f) * AT(5, 4) + F(-0.180240f) * AT(7, 4));
+ const Temp_Type X105 = D(F(0.906127f) * AT(1, 5) + F(-0.318190f) * AT(3, 5) + F(0.212608f) * AT(5, 5) + F(-0.180240f) * AT(7, 5));
+ const Temp_Type X106 = D(F(0.906127f) * AT(1, 6) + F(-0.318190f) * AT(3, 6) + F(0.212608f) * AT(5, 6) + F(-0.180240f) * AT(7, 6));
+ const Temp_Type X107 = D(F(0.906127f) * AT(1, 7) + F(-0.318190f) * AT(3, 7) + F(0.212608f) * AT(5, 7) + F(-0.180240f) * AT(7, 7));
+ const Temp_Type X110 = AT(2, 0);
+ const Temp_Type X111 = AT(2, 1);
+ const Temp_Type X112 = AT(2, 2);
+ const Temp_Type X113 = AT(2, 3);
+ const Temp_Type X114 = AT(2, 4);
+ const Temp_Type X115 = AT(2, 5);
+ const Temp_Type X116 = AT(2, 6);
+ const Temp_Type X117 = AT(2, 7);
+ const Temp_Type X120 = D(F(-0.074658f) * AT(1, 0) + F(0.513280f) * AT(3, 0) + F(0.768178f) * AT(5, 0) + F(-0.375330f) * AT(7, 0));
+ const Temp_Type X121 = D(F(-0.074658f) * AT(1, 1) + F(0.513280f) * AT(3, 1) + F(0.768178f) * AT(5, 1) + F(-0.375330f) * AT(7, 1));
+ const Temp_Type X122 = D(F(-0.074658f) * AT(1, 2) + F(0.513280f) * AT(3, 2) + F(0.768178f) * AT(5, 2) + F(-0.375330f) * AT(7, 2));
+ const Temp_Type X123 = D(F(-0.074658f) * AT(1, 3) + F(0.513280f) * AT(3, 3) + F(0.768178f) * AT(5, 3) + F(-0.375330f) * AT(7, 3));
+ const Temp_Type X124 = D(F(-0.074658f) * AT(1, 4) + F(0.513280f) * AT(3, 4) + F(0.768178f) * AT(5, 4) + F(-0.375330f) * AT(7, 4));
+ const Temp_Type X125 = D(F(-0.074658f) * AT(1, 5) + F(0.513280f) * AT(3, 5) + F(0.768178f) * AT(5, 5) + F(-0.375330f) * AT(7, 5));
+ const Temp_Type X126 = D(F(-0.074658f) * AT(1, 6) + F(0.513280f) * AT(3, 6) + F(0.768178f) * AT(5, 6) + F(-0.375330f) * AT(7, 6));
+ const Temp_Type X127 = D(F(-0.074658f) * AT(1, 7) + F(0.513280f) * AT(3, 7) + F(0.768178f) * AT(5, 7) + F(-0.375330f) * AT(7, 7));
+ const Temp_Type X130 = AT(6, 0);
+ const Temp_Type X131 = AT(6, 1);
+ const Temp_Type X132 = AT(6, 2);
+ const Temp_Type X133 = AT(6, 3);
+ const Temp_Type X134 = AT(6, 4);
+ const Temp_Type X135 = AT(6, 5);
+ const Temp_Type X136 = AT(6, 6);
+ const Temp_Type X137 = AT(6, 7);
+ // 80 muls 48 adds
+
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ R.at(0, 0) = X100;
+ R.at(0, 1) = D(X101 * F(0.415735f) + X103 * F(0.791065f) + X105 * F(-0.352443f) + X107 * F(0.277785f));
+ R.at(0, 2) = X104;
+ R.at(0, 3) = D(X101 * F(0.022887f) + X103 * F(-0.097545f) + X105 * F(0.490393f) + X107 * F(0.865723f));
+ R.at(1, 0) = X110;
+ R.at(1, 1) = D(X111 * F(0.415735f) + X113 * F(0.791065f) + X115 * F(-0.352443f) + X117 * F(0.277785f));
+ R.at(1, 2) = X114;
+ R.at(1, 3) = D(X111 * F(0.022887f) + X113 * F(-0.097545f) + X115 * F(0.490393f) + X117 * F(0.865723f));
+ R.at(2, 0) = X120;
+ R.at(2, 1) = D(X121 * F(0.415735f) + X123 * F(0.791065f) + X125 * F(-0.352443f) + X127 * F(0.277785f));
+ R.at(2, 2) = X124;
+ R.at(2, 3) = D(X121 * F(0.022887f) + X123 * F(-0.097545f) + X125 * F(0.490393f) + X127 * F(0.865723f));
+ R.at(3, 0) = X130;
+ R.at(3, 1) = D(X131 * F(0.415735f) + X133 * F(0.791065f) + X135 * F(-0.352443f) + X137 * F(0.277785f));
+ R.at(3, 2) = X134;
+ R.at(3, 3) = D(X131 * F(0.022887f) + X133 * F(-0.097545f) + X135 * F(0.490393f) + X137 * F(0.865723f));
+ // 40 muls 24 adds
+ // 4x4 = 4x8 times 8x4, matrix 1 is constant
+ S.at(0, 0) = D(X101 * F(0.906127f) + X103 * F(-0.318190f) + X105 * F(0.212608f) + X107 * F(-0.180240f));
+ S.at(0, 1) = X102;
+ S.at(0, 2) = D(X101 * F(-0.074658f) + X103 * F(0.513280f) + X105 * F(0.768178f) + X107 * F(-0.375330f));
+ S.at(0, 3) = X106;
+ S.at(1, 0) = D(X111 * F(0.906127f) + X113 * F(-0.318190f) + X115 * F(0.212608f) + X117 * F(-0.180240f));
+ S.at(1, 1) = X112;
+ S.at(1, 2) = D(X111 * F(-0.074658f) + X113 * F(0.513280f) + X115 * F(0.768178f) + X117 * F(-0.375330f));
+ S.at(1, 3) = X116;
+ S.at(2, 0) = D(X121 * F(0.906127f) + X123 * F(-0.318190f) + X125 * F(0.212608f) + X127 * F(-0.180240f));
+ S.at(2, 1) = X122;
+ S.at(2, 2) = D(X121 * F(-0.074658f) + X123 * F(0.513280f) + X125 * F(0.768178f) + X127 * F(-0.375330f));
+ S.at(2, 3) = X126;
+ S.at(3, 0) = D(X131 * F(0.906127f) + X133 * F(-0.318190f) + X135 * F(0.212608f) + X137 * F(-0.180240f));
+ S.at(3, 1) = X132;
+ S.at(3, 2) = D(X131 * F(-0.074658f) + X133 * F(0.513280f) + X135 * F(0.768178f) + X137 * F(-0.375330f));
+ S.at(3, 3) = X136;
+ // 40 muls 24 adds
+ }
+ };
+} // end namespace DCT_Upsample
+
+
+// Unconditionally frees all allocated m_blocks.
+void jpeg_decoder::free_all_blocks()
+{
+ delete(m_pStream);
+ m_pStream = nullptr;
+
+ for (mem_block *b = m_pMem_blocks; b; ) {
+ mem_block *n = b->m_pNext;
+ free(b);
+ b = n;
+ }
+ m_pMem_blocks = nullptr;
+}
+
+
+// This method handles all errors. It will never return.
+// It could easily be changed to use C++ exceptions.
+JPGD_NORETURN void jpeg_decoder::stop_decoding(jpgd_status status)
+{
+ m_error_code = status;
+ free_all_blocks();
+ longjmp(m_jmp_state, status);
+}
+
+
+void *jpeg_decoder::alloc(size_t nSize, bool zero)
+{
+ nSize = (JPGD_MAX(nSize, 1) + 3) & ~3;
+ char *rv = nullptr;
+ for (mem_block *b = m_pMem_blocks; b; b = b->m_pNext) {
+ if ((b->m_used_count + nSize) <= b->m_size) {
+ rv = b->m_data + b->m_used_count;
+ b->m_used_count += nSize;
+ break;
+ }
+ }
+ if (!rv) {
+ int capacity = JPGD_MAX(32768 - 256, (nSize + 2047) & ~2047);
+ mem_block *b = (mem_block*)malloc(sizeof(mem_block) + capacity);
+ if (!b) stop_decoding(JPGD_NOTENOUGHMEM);
+ b->m_pNext = m_pMem_blocks; m_pMem_blocks = b;
+ b->m_used_count = nSize;
+ b->m_size = capacity;
+ rv = b->m_data;
+ }
+ if (zero) memset(rv, 0, nSize);
+ return rv;
+}
+
+
+void jpeg_decoder::word_clear(void *p, uint16_t c, uint32_t n)
+{
+ uint8_t *pD = (uint8_t*)p;
+ const uint8_t l = c & 0xFF, h = (c >> 8) & 0xFF;
+ while (n) {
+ pD[0] = l; pD[1] = h; pD += 2;
+ n--;
+ }
+}
+
+
+// Refill the input buffer.
+// This method will sit in a loop until (A) the buffer is full or (B)
+// the stream's read() method reports and end of file condition.
+void jpeg_decoder::prep_in_buffer()
+{
+ m_in_buf_left = 0;
+ m_pIn_buf_ofs = m_in_buf;
+
+ if (m_eof_flag) return;
+
+ do {
+ int bytes_read = m_pStream->read(m_in_buf + m_in_buf_left, JPGD_IN_BUF_SIZE - m_in_buf_left, &m_eof_flag);
+ if (bytes_read == -1) stop_decoding(JPGD_STREAM_READ);
+ m_in_buf_left += bytes_read;
+ } while ((m_in_buf_left < JPGD_IN_BUF_SIZE) && (!m_eof_flag));
+
+ m_total_bytes_read += m_in_buf_left;
+
+ // Pad the end of the block with M_EOI (prevents the decompressor from going off the rails if the stream is invalid).
+ // (This dates way back to when this decompressor was written in C/asm, and the all-asm Huffman decoder did some fancy things to increase perf.)
+ word_clear(m_pIn_buf_ofs + m_in_buf_left, 0xD9FF, 64);
+}
+
+
+// Read a Huffman code table.
+void jpeg_decoder::read_dht_marker()
+{
+ int i, index, count;
+ uint8_t huff_num[17];
+ uint8_t huff_val[256];
+ uint32_t num_left = get_bits(16);
+
+ if (num_left < 2) stop_decoding(JPGD_BAD_DHT_MARKER);
+ num_left -= 2;
+
+ while (num_left) {
+ index = get_bits(8);
+ huff_num[0] = 0;
+ count = 0;
+
+ for (i = 1; i <= 16; i++) {
+ huff_num[i] = static_cast<uint8_t>(get_bits(8));
+ count += huff_num[i];
+ }
+
+ if (count > 255) stop_decoding(JPGD_BAD_DHT_COUNTS);
+
+ for (i = 0; i < count; i++)
+ huff_val[i] = static_cast<uint8_t>(get_bits(8));
+
+ i = 1 + 16 + count;
+
+ if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DHT_MARKER);
+ num_left -= i;
+
+ if ((index & 0x10) > 0x10) stop_decoding(JPGD_BAD_DHT_INDEX);
+ index = (index & 0x0F) + ((index & 0x10) >> 4) * (JPGD_MAX_HUFF_TABLES >> 1);
+ if (index >= JPGD_MAX_HUFF_TABLES) stop_decoding(JPGD_BAD_DHT_INDEX);
+
+ if (!m_huff_num[index]) m_huff_num[index] = (uint8_t *)alloc(17);
+ if (!m_huff_val[index]) m_huff_val[index] = (uint8_t *)alloc(256);
+
+ m_huff_ac[index] = (index & 0x10) != 0;
+ memcpy(m_huff_num[index], huff_num, 17);
+ memcpy(m_huff_val[index], huff_val, 256);
+ }
+}
+
+
+// Read a quantization table.
+void jpeg_decoder::read_dqt_marker()
+{
+ int n, i, prec;
+ uint32_t temp;
+ uint32_t num_left = get_bits(16);
+ if (num_left < 2) stop_decoding(JPGD_BAD_DQT_MARKER);
+ num_left -= 2;
+
+ while (num_left) {
+ n = get_bits(8);
+ prec = n >> 4;
+ n &= 0x0F;
+
+ if (n >= JPGD_MAX_QUANT_TABLES) stop_decoding(JPGD_BAD_DQT_TABLE);
+
+ if (!m_quant[n]) m_quant[n] = (jpgd_quant_t *)alloc(64 * sizeof(jpgd_quant_t));
+
+ // read quantization entries, in zag order
+ for (i = 0; i < 64; i++) {
+ temp = get_bits(8);
+ if (prec) temp = (temp << 8) + get_bits(8);
+ m_quant[n][i] = static_cast<jpgd_quant_t>(temp);
+ }
+ i = 64 + 1;
+ if (prec) i += 64;
+ if (num_left < (uint32_t)i) stop_decoding(JPGD_BAD_DQT_LENGTH);
+ num_left -= i;
+ }
+}
+
+
+// Read the start of frame (SOF) marker.
+void jpeg_decoder::read_sof_marker()
+{
+ int i;
+ uint32_t num_left = get_bits(16);
+
+ if (get_bits(8) != 8) stop_decoding(JPGD_BAD_PRECISION); /* precision: sorry, only 8-bit precision is supported right now */
+
+ m_image_y_size = get_bits(16);
+ if ((m_image_y_size < 1) || (m_image_y_size > JPGD_MAX_HEIGHT)) stop_decoding(JPGD_BAD_HEIGHT);
+
+ m_image_x_size = get_bits(16);
+ if ((m_image_x_size < 1) || (m_image_x_size > JPGD_MAX_WIDTH)) stop_decoding(JPGD_BAD_WIDTH);
+
+ m_comps_in_frame = get_bits(8);
+ if (m_comps_in_frame > JPGD_MAX_COMPONENTS) stop_decoding(JPGD_TOO_MANY_COMPONENTS);
+
+ if (num_left != (uint32_t)(m_comps_in_frame * 3 + 8)) stop_decoding(JPGD_BAD_SOF_LENGTH);
+
+ for (i = 0; i < m_comps_in_frame; i++) {
+ m_comp_ident[i] = get_bits(8);
+ m_comp_h_samp[i] = get_bits(4);
+ m_comp_v_samp[i] = get_bits(4);
+ m_comp_quant[i] = get_bits(8);
+ }
+}
+
+
+// Used to skip unrecognized markers.
+void jpeg_decoder::skip_variable_marker()
+{
+ uint32_t num_left = get_bits(16);
+ if (num_left < 2) stop_decoding(JPGD_BAD_VARIABLE_MARKER);
+ num_left -= 2;
+
+ while (num_left) {
+ get_bits(8);
+ num_left--;
+ }
+}
+
+
+// Read a define restart interval (DRI) marker.
+void jpeg_decoder::read_dri_marker()
+{
+ if (get_bits(16) != 4) stop_decoding(JPGD_BAD_DRI_LENGTH);
+ m_restart_interval = get_bits(16);
+}
+
+
+// Read a start of scan (SOS) marker.
+void jpeg_decoder::read_sos_marker()
+{
+ int i, ci, c, cc;
+ uint32_t num_left = get_bits(16);
+ int n = get_bits(8);
+
+ m_comps_in_scan = n;
+ num_left -= 3;
+
+ if ( (num_left != (uint32_t)(n * 2 + 3)) || (n < 1) || (n > JPGD_MAX_COMPS_IN_SCAN) ) stop_decoding(JPGD_BAD_SOS_LENGTH);
+
+ for (i = 0; i < n; i++) {
+ cc = get_bits(8);
+ c = get_bits(8);
+ num_left -= 2;
+
+ for (ci = 0; ci < m_comps_in_frame; ci++)
+ if (cc == m_comp_ident[ci]) break;
+
+ if (ci >= m_comps_in_frame) stop_decoding(JPGD_BAD_SOS_COMP_ID);
+
+ m_comp_list[i] = ci;
+ m_comp_dc_tab[ci] = (c >> 4) & 15;
+ m_comp_ac_tab[ci] = (c & 15) + (JPGD_MAX_HUFF_TABLES >> 1);
+ }
+ m_spectral_start = get_bits(8);
+ m_spectral_end = get_bits(8);
+ m_successive_high = get_bits(4);
+ m_successive_low = get_bits(4);
+
+ if (!m_progressive_flag) {
+ m_spectral_start = 0;
+ m_spectral_end = 63;
+ }
+ num_left -= 3;
+
+ while (num_left) { /* read past whatever is num_left */
+ get_bits(8);
+ num_left--;
+ }
+}
+
+
+// Finds the next marker.
+int jpeg_decoder::next_marker()
+{
+ uint32_t c, bytes = 0;
+
+ do {
+ do {
+ bytes++;
+ c = get_bits(8);
+ } while (c != 0xFF);
+
+ do {
+ c = get_bits(8);
+ } while (c == 0xFF);
+ } while (c == 0);
+
+ // If bytes > 0 here, there where extra bytes before the marker (not good).
+ return c;
+}
+
+
+// Process markers. Returns when an SOFx, SOI, EOI, or SOS marker is
+// encountered.
+int jpeg_decoder::process_markers()
+{
+ int c;
+
+ for ( ; ; ) {
+ c = next_marker();
+ switch (c) {
+ case M_SOF0:
+ case M_SOF1:
+ case M_SOF2:
+ case M_SOF3:
+ case M_SOF5:
+ case M_SOF6:
+ case M_SOF7:
+ // case M_JPG:
+ case M_SOF9:
+ case M_SOF10:
+ case M_SOF11:
+ case M_SOF13:
+ case M_SOF14:
+ case M_SOF15:
+ case M_SOI:
+ case M_EOI:
+ case M_SOS: return c;
+ case M_DHT: {
+ read_dht_marker();
+ break;
+ }
+ // No arithmitic support - dumb patents!
+ case M_DAC: {
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+ break;
+ }
+ case M_DQT: {
+ read_dqt_marker();
+ break;
+ }
+ case M_DRI: {
+ read_dri_marker();
+ break;
+ }
+ //case M_APP0: /* no need to read the JFIF marker */
+ case M_JPG:
+ case M_RST0: /* no parameters */
+ case M_RST1:
+ case M_RST2:
+ case M_RST3:
+ case M_RST4:
+ case M_RST5:
+ case M_RST6:
+ case M_RST7:
+ case M_TEM: {
+ stop_decoding(JPGD_UNEXPECTED_MARKER);
+ break;
+ }
+ default: { /* must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn or APP0 */
+ skip_variable_marker();
+ break;
+ }
+ }
+ }
+}
+
+
+// Finds the start of image (SOI) marker.
+// This code is rather defensive: it only checks the first 512 bytes to avoid
+// false positives.
+void jpeg_decoder::locate_soi_marker()
+{
+ uint32_t lastchar = get_bits(8);
+ uint32_t thischar = get_bits(8);
+
+ /* ok if it's a normal JPEG file without a special header */
+ if ((lastchar == 0xFF) && (thischar == M_SOI)) return;
+
+ uint32_t bytesleft = 4096; //512;
+
+ while (true) {
+ if (--bytesleft == 0) stop_decoding(JPGD_NOT_JPEG);
+
+ lastchar = thischar;
+ thischar = get_bits(8);
+
+ if (lastchar == 0xFF) {
+ if (thischar == M_SOI) break;
+ else if (thischar == M_EOI) stop_decoding(JPGD_NOT_JPEG); // get_bits will keep returning M_EOI if we read past the end
+ }
+ }
+
+ // Check the next character after marker: if it's not 0xFF, it can't be the start of the next marker, so the file is bad.
+ thischar = (m_bit_buf >> 24) & 0xFF;
+ if (thischar != 0xFF) stop_decoding(JPGD_NOT_JPEG);
+}
+
+
+// Find a start of frame (SOF) marker.
+void jpeg_decoder::locate_sof_marker()
+{
+ locate_soi_marker();
+ int c = process_markers();
+
+ switch (c) {
+ case M_SOF2: m_progressive_flag = true;
+ case M_SOF0: /* baseline DCT */
+ case M_SOF1: { /* extended sequential DCT */
+ read_sof_marker();
+ break;
+ }
+ case M_SOF9: { /* Arithmitic coding */
+ stop_decoding(JPGD_NO_ARITHMITIC_SUPPORT);
+ break;
+ }
+ default: {
+ stop_decoding(JPGD_UNSUPPORTED_MARKER);
+ break;
+ }
+ }
+}
+
+
+// Find a start of scan (SOS) marker.
+int jpeg_decoder::locate_sos_marker()
+{
+ int c = process_markers();
+ if (c == M_EOI) return false;
+ else if (c != M_SOS) stop_decoding(JPGD_UNEXPECTED_MARKER);
+ read_sos_marker();
+ return true;
+}
+
+
+// Reset everything to default/uninitialized state.
+void jpeg_decoder::init(jpeg_decoder_stream *pStream)
+{
+ m_pMem_blocks = nullptr;
+ m_error_code = JPGD_SUCCESS;
+ m_ready_flag = false;
+ m_image_x_size = m_image_y_size = 0;
+ m_pStream = pStream;
+ m_progressive_flag = false;
+
+ memset(m_huff_ac, 0, sizeof(m_huff_ac));
+ memset(m_huff_num, 0, sizeof(m_huff_num));
+ memset(m_huff_val, 0, sizeof(m_huff_val));
+ memset(m_quant, 0, sizeof(m_quant));
+
+ m_scan_type = 0;
+ m_comps_in_frame = 0;
+
+ memset(m_comp_h_samp, 0, sizeof(m_comp_h_samp));
+ memset(m_comp_v_samp, 0, sizeof(m_comp_v_samp));
+ memset(m_comp_quant, 0, sizeof(m_comp_quant));
+ memset(m_comp_ident, 0, sizeof(m_comp_ident));
+ memset(m_comp_h_blocks, 0, sizeof(m_comp_h_blocks));
+ memset(m_comp_v_blocks, 0, sizeof(m_comp_v_blocks));
+
+ m_comps_in_scan = 0;
+ memset(m_comp_list, 0, sizeof(m_comp_list));
+ memset(m_comp_dc_tab, 0, sizeof(m_comp_dc_tab));
+ memset(m_comp_ac_tab, 0, sizeof(m_comp_ac_tab));
+
+ m_spectral_start = 0;
+ m_spectral_end = 0;
+ m_successive_low = 0;
+ m_successive_high = 0;
+ m_max_mcu_x_size = 0;
+ m_max_mcu_y_size = 0;
+ m_blocks_per_mcu = 0;
+ m_max_blocks_per_row = 0;
+ m_mcus_per_row = 0;
+ m_mcus_per_col = 0;
+ m_expanded_blocks_per_component = 0;
+ m_expanded_blocks_per_mcu = 0;
+ m_expanded_blocks_per_row = 0;
+ m_freq_domain_chroma_upsample = false;
+
+ memset(m_mcu_org, 0, sizeof(m_mcu_org));
+
+ m_total_lines_left = 0;
+ m_mcu_lines_left = 0;
+ m_real_dest_bytes_per_scan_line = 0;
+ m_dest_bytes_per_scan_line = 0;
+ m_dest_bytes_per_pixel = 0;
+
+ memset(m_pHuff_tabs, 0, sizeof(m_pHuff_tabs));
+
+ memset(m_dc_coeffs, 0, sizeof(m_dc_coeffs));
+ memset(m_ac_coeffs, 0, sizeof(m_ac_coeffs));
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
+
+ m_eob_run = 0;
+
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
+
+ m_pIn_buf_ofs = m_in_buf;
+ m_in_buf_left = 0;
+ m_eof_flag = false;
+ m_tem_flag = 0;
+
+ memset(m_in_buf_pad_start, 0, sizeof(m_in_buf_pad_start));
+ memset(m_in_buf, 0, sizeof(m_in_buf));
+ memset(m_in_buf_pad_end, 0, sizeof(m_in_buf_pad_end));
+
+ m_restart_interval = 0;
+ m_restarts_left = 0;
+ m_next_restart_num = 0;
+
+ m_max_mcus_per_row = 0;
+ m_max_blocks_per_mcu = 0;
+ m_max_mcus_per_col = 0;
+
+ memset(m_last_dc_val, 0, sizeof(m_last_dc_val));
+ m_pMCU_coefficients = nullptr;
+ m_pSample_buf = nullptr;
+
+ m_total_bytes_read = 0;
+
+ m_pScan_line_0 = nullptr;
+ m_pScan_line_1 = nullptr;
+
+ // Ready the input buffer.
+ prep_in_buffer();
+
+ // Prime the bit buffer.
+ m_bits_left = 16;
+ m_bit_buf = 0;
+
+ get_bits(16);
+ get_bits(16);
+
+ for (int i = 0; i < JPGD_MAX_BLOCKS_PER_MCU; i++) {
+ m_mcu_block_max_zag[i] = 64;
+ }
+}
+
+#define SCALEBITS 16
+#define ONE_HALF ((int) 1 << (SCALEBITS-1))
+#define FIX(x) ((int) ((x) * (1L<<SCALEBITS) + 0.5f))
+
+
+// Create a few tables that allow us to quickly convert YCbCr to RGB.
+void jpeg_decoder::create_look_ups()
+{
+ for (int i = 0; i <= 255; i++) {
+ int k = i - 128;
+ m_crr[i] = ( FIX(1.40200f) * k + ONE_HALF) >> SCALEBITS;
+ m_cbb[i] = ( FIX(1.77200f) * k + ONE_HALF) >> SCALEBITS;
+ m_crg[i] = (-FIX(0.71414f)) * k;
+ m_cbg[i] = (-FIX(0.34414f)) * k + ONE_HALF;
+ }
+}
+
+
+// This method throws back into the stream any bytes that where read
+// into the bit buffer during initial marker scanning.
+void jpeg_decoder::fix_in_buffer()
+{
+ // In case any 0xFF's where pulled into the buffer during marker scanning.
+ JPGD_ASSERT((m_bits_left & 7) == 0);
+
+ if (m_bits_left == 16) stuff_char( (uint8_t)(m_bit_buf & 0xFF));
+ if (m_bits_left >= 8) stuff_char( (uint8_t)((m_bit_buf >> 8) & 0xFF));
+
+ stuff_char((uint8_t)((m_bit_buf >> 16) & 0xFF));
+ stuff_char((uint8_t)((m_bit_buf >> 24) & 0xFF));
+
+ m_bits_left = 16;
+ get_bits_no_markers(16);
+ get_bits_no_markers(16);
+}
+
+
+void jpeg_decoder::transform_mcu(int mcu_row)
+{
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
+ uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_blocks_per_mcu * 64;
+
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
+ pSrc_ptr += 64;
+ pDst_ptr += 64;
+ }
+}
+
+
+static const uint8_t s_max_rc[64] =
+{
+ 17, 18, 34, 50, 50, 51, 52, 52, 52, 68, 84, 84, 84, 84, 85, 86, 86, 86, 86, 86,
+ 102, 118, 118, 118, 118, 118, 118, 119, 120, 120, 120, 120, 120, 120, 120, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
+ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136
+};
+
+
+void jpeg_decoder::transform_mcu_expand(int mcu_row)
+{
+ jpgd_block_t* pSrc_ptr = m_pMCU_coefficients;
+ uint8_t* pDst_ptr = m_pSample_buf + mcu_row * m_expanded_blocks_per_mcu * 64;
+
+ // Y IDCT
+ int mcu_block;
+ for (mcu_block = 0; mcu_block < m_expanded_blocks_per_component; mcu_block++) {
+ idct(pSrc_ptr, pDst_ptr, m_mcu_block_max_zag[mcu_block]);
+ pSrc_ptr += 64;
+ pDst_ptr += 64;
+ }
+
+ // Chroma IDCT, with upsampling
+ jpgd_block_t temp_block[64];
+
+ for (int i = 0; i < 2; i++) {
+ DCT_Upsample::Matrix44 P, Q, R, S;
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] >= 1);
+ JPGD_ASSERT(m_mcu_block_max_zag[mcu_block] <= 64);
+
+ int max_zag = m_mcu_block_max_zag[mcu_block++] - 1;
+ if (max_zag <= 0) max_zag = 0; // should never happen, only here to shut up static analysis
+
+ switch (s_max_rc[max_zag]) {
+ case 1*16+1:
+ DCT_Upsample::P_Q<1, 1>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<1, 1>::calc(R, S, pSrc_ptr);
+ break;
+ case 1*16+2:
+ DCT_Upsample::P_Q<1, 2>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<1, 2>::calc(R, S, pSrc_ptr);
+ break;
+ case 2*16+2:
+ DCT_Upsample::P_Q<2, 2>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<2, 2>::calc(R, S, pSrc_ptr);
+ break;
+ case 3*16+2:
+ DCT_Upsample::P_Q<3, 2>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<3, 2>::calc(R, S, pSrc_ptr);
+ break;
+ case 3*16+3:
+ DCT_Upsample::P_Q<3, 3>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<3, 3>::calc(R, S, pSrc_ptr);
+ break;
+ case 3*16+4:
+ DCT_Upsample::P_Q<3, 4>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<3, 4>::calc(R, S, pSrc_ptr);
+ break;
+ case 4*16+4:
+ DCT_Upsample::P_Q<4, 4>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<4, 4>::calc(R, S, pSrc_ptr);
+ break;
+ case 5*16+4:
+ DCT_Upsample::P_Q<5, 4>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<5, 4>::calc(R, S, pSrc_ptr);
+ break;
+ case 5*16+5:
+ DCT_Upsample::P_Q<5, 5>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<5, 5>::calc(R, S, pSrc_ptr);
+ break;
+ case 5*16+6:
+ DCT_Upsample::P_Q<5, 6>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<5, 6>::calc(R, S, pSrc_ptr);
+ break;
+ case 6*16+6:
+ DCT_Upsample::P_Q<6, 6>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<6, 6>::calc(R, S, pSrc_ptr);
+ break;
+ case 7*16+6:
+ DCT_Upsample::P_Q<7, 6>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<7, 6>::calc(R, S, pSrc_ptr);
+ break;
+ case 7*16+7:
+ DCT_Upsample::P_Q<7, 7>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<7, 7>::calc(R, S, pSrc_ptr);
+ break;
+ case 7*16+8:
+ DCT_Upsample::P_Q<7, 8>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<7, 8>::calc(R, S, pSrc_ptr);
+ break;
+ case 8*16+8:
+ DCT_Upsample::P_Q<8, 8>::calc(P, Q, pSrc_ptr);
+ DCT_Upsample::R_S<8, 8>::calc(R, S, pSrc_ptr);
+ break;
+ default:
+ JPGD_ASSERT(false);
+ }
+ DCT_Upsample::Matrix44 a(P + Q); P -= Q;
+ DCT_Upsample::Matrix44& b = P;
+ DCT_Upsample::Matrix44 c(R + S); R -= S;
+ DCT_Upsample::Matrix44& d = R;
+
+ DCT_Upsample::Matrix44::add_and_store(temp_block, a, c);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, a, c);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+
+ DCT_Upsample::Matrix44::add_and_store(temp_block, b, d);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+
+ DCT_Upsample::Matrix44::sub_and_store(temp_block, b, d);
+ idct_4x4(temp_block, pDst_ptr);
+ pDst_ptr += 64;
+ pSrc_ptr += 64;
+ }
+}
+
+
+// Loads and dequantizes the next row of (already decoded) coefficients.
+// Progressive images only.
+void jpeg_decoder::load_next_row()
+{
+ int i;
+ jpgd_block_t *p;
+ jpgd_quant_t *q;
+ int mcu_row, mcu_block, row_block = 0;
+ int component_num, component_id;
+ int block_x_mcu[JPGD_MAX_COMPONENTS];
+
+ memset(block_x_mcu, 0, JPGD_MAX_COMPONENTS * sizeof(int));
+
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
+
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
+ component_id = m_mcu_org[mcu_block];
+ q = m_quant[m_comp_quant[component_id]];
+ p = m_pMCU_coefficients + 64 * mcu_block;
+
+ jpgd_block_t* pAC = coeff_buf_getp(m_ac_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
+ jpgd_block_t* pDC = coeff_buf_getp(m_dc_coeffs[component_id], block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
+ p[0] = pDC[0];
+ memcpy(&p[1], &pAC[1], 63 * sizeof(jpgd_block_t));
+
+ for (i = 63; i > 0; i--) {
+ if (p[g_ZAG[i]]) break;
+ }
+
+ m_mcu_block_max_zag[mcu_block] = i + 1;
+
+ for ( ; i >= 0; i--) {
+ if (p[g_ZAG[i]]) {
+ p[g_ZAG[i]] = static_cast<jpgd_block_t>(p[g_ZAG[i]] * q[i]);
+ }
+ }
+
+ row_block++;
+
+ if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
+ else {
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) block_x_mcu_ofs = 0;
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) {
+ block_y_mcu_ofs = 0;
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
+ }
+ }
+ }
+ if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row);
+ else transform_mcu(mcu_row);
+ }
+ if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++;
+ else {
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
+ component_id = m_comp_list[component_num];
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
+ }
+ }
+}
+
+
+// Restart interval processing.
+void jpeg_decoder::process_restart()
+{
+ int i;
+ int c = 0;
+
+ // Align to a byte boundry
+ // FIXME: Is this really necessary? get_bits_no_markers() never reads in markers!
+ //get_bits_no_markers(m_bits_left & 7);
+
+ // Let's scan a little bit to find the marker, but not _too_ far.
+ // 1536 is a "fudge factor" that determines how much to scan.
+ for (i = 1536; i > 0; i--) {
+ if (get_char() == 0xFF) break;
+ }
+ if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
+
+ for ( ; i > 0; i--) {
+ if ((c = get_char()) != 0xFF) break;
+ }
+ if (i == 0) stop_decoding(JPGD_BAD_RESTART_MARKER);
+
+ // Is it the expected marker? If not, something bad happened.
+ if (c != (m_next_restart_num + M_RST0)) stop_decoding(JPGD_BAD_RESTART_MARKER);
+
+ // Reset each component's DC prediction values.
+ memset(&m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
+
+ m_eob_run = 0;
+ m_restarts_left = m_restart_interval;
+ m_next_restart_num = (m_next_restart_num + 1) & 7;
+
+ // Get the bit buffer going again...
+ m_bits_left = 16;
+ get_bits_no_markers(16);
+ get_bits_no_markers(16);
+}
+
+
+static inline int dequantize_ac(int c, int q)
+{
+ c *= q;
+ return c;
+}
+
+// Decodes and dequantizes the next row of coefficients.
+void jpeg_decoder::decode_next_row()
+{
+ int row_block = 0;
+
+ for (int mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
+ if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
+
+ jpgd_block_t* p = m_pMCU_coefficients;
+
+ for (int mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++, p += 64) {
+ int component_id = m_mcu_org[mcu_block];
+ jpgd_quant_t* q = m_quant[m_comp_quant[component_id]];
+
+ int r, s;
+ s = huff_decode(m_pHuff_tabs[m_comp_dc_tab[component_id]], r);
+ s = JPGD_HUFF_EXTEND(r, s);
+
+ m_last_dc_val[component_id] = (s += m_last_dc_val[component_id]);
+
+ p[0] = static_cast<jpgd_block_t>(s * q[0]);
+
+ int prev_num_set = m_mcu_block_max_zag[mcu_block];
+ huff_tables *pH = m_pHuff_tabs[m_comp_ac_tab[component_id]];
+ int k;
+ for (k = 1; k < 64; k++) {
+ int extra_bits;
+ s = huff_decode(pH, extra_bits);
+ r = s >> 4;
+ s &= 15;
+
+ if (s) {
+ if (r) {
+ if ((k + r) > 63) stop_decoding(JPGD_DECODE_ERROR);
+ if (k < prev_num_set) {
+ int n = JPGD_MIN(r, prev_num_set - k);
+ int kt = k;
+ while (n--) p[g_ZAG[kt++]] = 0;
+ }
+ k += r;
+ }
+ s = JPGD_HUFF_EXTEND(extra_bits, s);
+ JPGD_ASSERT(k < 64);
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(dequantize_ac(s, q[k])); //s * q[k];
+ } else {
+ if (r == 15) {
+ if ((k + 16) > 64) stop_decoding(JPGD_DECODE_ERROR);
+ if (k < prev_num_set) {
+ int n = JPGD_MIN(16, prev_num_set - k);
+ int kt = k;
+ while (n--) {
+ JPGD_ASSERT(kt <= 63);
+ p[g_ZAG[kt++]] = 0;
+ }
+ }
+ k += 16 - 1; // - 1 because the loop counter is k
+ JPGD_ASSERT(p[g_ZAG[k]] == 0);
+ } else break;
+ }
+ }
+
+ if (k < prev_num_set) {
+ int kt = k;
+ while (kt < prev_num_set) p[g_ZAG[kt++]] = 0;
+ }
+
+ m_mcu_block_max_zag[mcu_block] = k;
+ row_block++;
+ }
+ if (m_freq_domain_chroma_upsample) transform_mcu_expand(mcu_row);
+ else transform_mcu(mcu_row);
+ m_restarts_left--;
+ }
+}
+
+
+// YCbCr H1V1 (1x1:1:1, 3 m_blocks per MCU) to RGB
+void jpeg_decoder::H1V1Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d = m_pScan_line_0;
+ uint8_t *s = m_pSample_buf + row * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int j = 0; j < 8; j++) {
+ int y = s[j];
+ int cb = s[64+j];
+ int cr = s[128+j];
+
+ d[0] = clamp(y + m_crr[cr]);
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
+ d[2] = clamp(y + m_cbb[cb]);
+ d[3] = 255;
+ d += 4;
+ }
+ s += 64*3;
+ }
+}
+
+
+// YCbCr H2V1 (2x1:1:1, 4 m_blocks per MCU) to RGB
+void jpeg_decoder::H2V1Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d0 = m_pScan_line_0;
+ uint8_t *y = m_pSample_buf + row * 8;
+ uint8_t *c = m_pSample_buf + 2*64 + row * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int l = 0; l < 2; l++) {
+ for (int j = 0; j < 4; j++) {
+ int cb = c[0];
+ int cr = c[64];
+
+ int rc = m_crr[cr];
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
+ int bc = m_cbb[cb];
+
+ int yy = y[j<<1];
+ d0[0] = clamp(yy+rc);
+ d0[1] = clamp(yy+gc);
+ d0[2] = clamp(yy+bc);
+ d0[3] = 255;
+
+ yy = y[(j<<1)+1];
+ d0[4] = clamp(yy+rc);
+ d0[5] = clamp(yy+gc);
+ d0[6] = clamp(yy+bc);
+ d0[7] = 255;
+ d0 += 8;
+ c++;
+ }
+ y += 64;
+ }
+ y += 64*4 - 64*2;
+ c += 64*4 - 8;
+ }
+}
+
+
+// YCbCr H2V1 (1x2:1:1, 4 m_blocks per MCU) to RGB
+void jpeg_decoder::H1V2Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d0 = m_pScan_line_0;
+ uint8_t *d1 = m_pScan_line_1;
+ uint8_t *y;
+ uint8_t *c;
+
+ if (row < 8) y = m_pSample_buf + row * 8;
+ else y = m_pSample_buf + 64*1 + (row & 7) * 8;
+
+ c = m_pSample_buf + 64*2 + (row >> 1) * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int j = 0; j < 8; j++) {
+ int cb = c[0+j];
+ int cr = c[64+j];
+
+ int rc = m_crr[cr];
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
+ int bc = m_cbb[cb];
+
+ int yy = y[j];
+ d0[0] = clamp(yy+rc);
+ d0[1] = clamp(yy+gc);
+ d0[2] = clamp(yy+bc);
+ d0[3] = 255;
+
+ yy = y[8+j];
+ d1[0] = clamp(yy+rc);
+ d1[1] = clamp(yy+gc);
+ d1[2] = clamp(yy+bc);
+ d1[3] = 255;
+
+ d0 += 4;
+ d1 += 4;
+ }
+ y += 64*4;
+ c += 64*4;
+ }
+}
+
+
+// YCbCr H2V2 (2x2:1:1, 6 m_blocks per MCU) to RGB
+void jpeg_decoder::H2V2Convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d0 = m_pScan_line_0;
+ uint8_t *d1 = m_pScan_line_1;
+ uint8_t *y;
+ uint8_t *c;
+
+ if (row < 8) y = m_pSample_buf + row * 8;
+ else y = m_pSample_buf + 64*2 + (row & 7) * 8;
+
+ c = m_pSample_buf + 64*4 + (row >> 1) * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int l = 0; l < 2; l++) {
+ for (int j = 0; j < 8; j += 2) {
+ int cb = c[0];
+ int cr = c[64];
+
+ int rc = m_crr[cr];
+ int gc = ((m_crg[cr] + m_cbg[cb]) >> 16);
+ int bc = m_cbb[cb];
+
+ int yy = y[j];
+ d0[0] = clamp(yy+rc);
+ d0[1] = clamp(yy+gc);
+ d0[2] = clamp(yy+bc);
+ d0[3] = 255;
+
+ yy = y[j+1];
+ d0[4] = clamp(yy+rc);
+ d0[5] = clamp(yy+gc);
+ d0[6] = clamp(yy+bc);
+ d0[7] = 255;
+
+ yy = y[j+8];
+ d1[0] = clamp(yy+rc);
+ d1[1] = clamp(yy+gc);
+ d1[2] = clamp(yy+bc);
+ d1[3] = 255;
+
+ yy = y[j+8+1];
+ d1[4] = clamp(yy+rc);
+ d1[5] = clamp(yy+gc);
+ d1[6] = clamp(yy+bc);
+ d1[7] = 255;
+
+ d0 += 8;
+ d1 += 8;
+
+ c++;
+ }
+ y += 64;
+ }
+ y += 64*6 - 64*2;
+ c += 64*6 - 8;
+ }
+}
+
+
+// Y (1 block per MCU) to 8-bit grayscale
+void jpeg_decoder::gray_convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t *d = m_pScan_line_0;
+ uint8_t *s = m_pSample_buf + row * 8;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ *(uint32_t *)d = *(uint32_t *)s;
+ *(uint32_t *)(&d[4]) = *(uint32_t *)(&s[4]);
+ s += 64;
+ d += 8;
+ }
+}
+
+
+void jpeg_decoder::expanded_convert()
+{
+ int row = m_max_mcu_y_size - m_mcu_lines_left;
+ uint8_t* Py = m_pSample_buf + (row / 8) * 64 * m_comp_h_samp[0] + (row & 7) * 8;
+ uint8_t* d = m_pScan_line_0;
+
+ for (int i = m_max_mcus_per_row; i > 0; i--) {
+ for (int k = 0; k < m_max_mcu_x_size; k += 8) {
+ const int Y_ofs = k * 8;
+ const int Cb_ofs = Y_ofs + 64 * m_expanded_blocks_per_component;
+ const int Cr_ofs = Y_ofs + 64 * m_expanded_blocks_per_component * 2;
+ for (int j = 0; j < 8; j++) {
+ int y = Py[Y_ofs + j];
+ int cb = Py[Cb_ofs + j];
+ int cr = Py[Cr_ofs + j];
+
+ d[0] = clamp(y + m_crr[cr]);
+ d[1] = clamp(y + ((m_crg[cr] + m_cbg[cb]) >> 16));
+ d[2] = clamp(y + m_cbb[cb]);
+ d[3] = 255;
+
+ d += 4;
+ }
+ }
+ Py += 64 * m_expanded_blocks_per_mcu;
+ }
+}
+
+
+// Find end of image (EOI) marker, so we can return to the user the exact size of the input stream.
+void jpeg_decoder::find_eoi()
+{
+ if (!m_progressive_flag) {
+ // Attempt to read the EOI marker.
+ //get_bits_no_markers(m_bits_left & 7);
+
+ // Prime the bit buffer
+ m_bits_left = 16;
+ get_bits(16);
+ get_bits(16);
+
+ // The next marker _should_ be EOI
+ process_markers();
+ }
+ m_total_bytes_read -= m_in_buf_left;
+}
+
+
+int jpeg_decoder::decode(const void** pScan_line, uint32_t* pScan_line_len)
+{
+ if ((m_error_code) || (!m_ready_flag)) return JPGD_FAILED;
+ if (m_total_lines_left == 0) return JPGD_DONE;
+ if (m_mcu_lines_left == 0) {
+ if (setjmp(m_jmp_state)) return JPGD_FAILED;
+ if (m_progressive_flag) load_next_row();
+ else decode_next_row();
+ // Find the EOI marker if that was the last row.
+ if (m_total_lines_left <= m_max_mcu_y_size) find_eoi();
+ m_mcu_lines_left = m_max_mcu_y_size;
+ }
+
+ if (m_freq_domain_chroma_upsample) {
+ expanded_convert();
+ *pScan_line = m_pScan_line_0;
+ } else {
+ switch (m_scan_type) {
+ case JPGD_YH2V2: {
+ if ((m_mcu_lines_left & 1) == 0) {
+ H2V2Convert();
+ *pScan_line = m_pScan_line_0;
+ }
+ else *pScan_line = m_pScan_line_1;
+ break;
+ }
+ case JPGD_YH2V1: {
+ H2V1Convert();
+ *pScan_line = m_pScan_line_0;
+ break;
+ }
+ case JPGD_YH1V2: {
+ if ((m_mcu_lines_left & 1) == 0) {
+ H1V2Convert();
+ *pScan_line = m_pScan_line_0;
+ } else *pScan_line = m_pScan_line_1;
+ break;
+ }
+ case JPGD_YH1V1: {
+ H1V1Convert();
+ *pScan_line = m_pScan_line_0;
+ break;
+ }
+ case JPGD_GRAYSCALE: {
+ gray_convert();
+ *pScan_line = m_pScan_line_0;
+ break;
+ }
+ }
+ }
+
+ *pScan_line_len = m_real_dest_bytes_per_scan_line;
+ m_mcu_lines_left--;
+ m_total_lines_left--;
+
+ return JPGD_SUCCESS;
+}
+
+
+// Creates the tables needed for efficient Huffman decoding.
+void jpeg_decoder::make_huff_table(int index, huff_tables *pH)
+{
+ int p, i, l, si;
+ uint8_t huffsize[257];
+ uint32_t huffcode[257];
+ uint32_t code;
+ uint32_t subtree;
+ int code_size;
+ int lastp;
+ int nextfreeentry;
+ int currententry;
+
+ pH->ac_table = m_huff_ac[index] != 0;
+ p = 0;
+
+ for (l = 1; l <= 16; l++) {
+ for (i = 1; i <= m_huff_num[index][l]; i++) {
+ huffsize[p++] = static_cast<uint8_t>(l);
+ }
+ }
+
+ huffsize[p] = 0;
+ lastp = p;
+ code = 0;
+ si = huffsize[0];
+ p = 0;
+
+ while (huffsize[p]) {
+ while (huffsize[p] == si) {
+ huffcode[p++] = code;
+ code++;
+ }
+ code <<= 1;
+ si++;
+ }
+
+ memset(pH->look_up, 0, sizeof(pH->look_up));
+ memset(pH->look_up2, 0, sizeof(pH->look_up2));
+ memset(pH->tree, 0, sizeof(pH->tree));
+ memset(pH->code_size, 0, sizeof(pH->code_size));
+
+ nextfreeentry = -1;
+ p = 0;
+
+ while (p < lastp) {
+ i = m_huff_val[index][p];
+ code = huffcode[p];
+ code_size = huffsize[p];
+ pH->code_size[i] = static_cast<uint8_t>(code_size);
+
+ if (code_size <= 8) {
+ code <<= (8 - code_size);
+ for (l = 1 << (8 - code_size); l > 0; l--) {
+ JPGD_ASSERT(i < 256);
+ pH->look_up[code] = i;
+ bool has_extrabits = false;
+ int extra_bits = 0;
+ int num_extra_bits = i & 15;
+ int bits_to_fetch = code_size;
+
+ if (num_extra_bits) {
+ int total_codesize = code_size + num_extra_bits;
+ if (total_codesize <= 8) {
+ has_extrabits = true;
+ extra_bits = ((1 << num_extra_bits) - 1) & (code >> (8 - total_codesize));
+ JPGD_ASSERT(extra_bits <= 0x7FFF);
+ bits_to_fetch += num_extra_bits;
+ }
+ }
+ if (!has_extrabits) pH->look_up2[code] = i | (bits_to_fetch << 8);
+ else pH->look_up2[code] = i | 0x8000 | (extra_bits << 16) | (bits_to_fetch << 8);
+ code++;
+ }
+ } else {
+ subtree = (code >> (code_size - 8)) & 0xFF;
+ currententry = pH->look_up[subtree];
+
+ if (currententry == 0) {
+ pH->look_up[subtree] = currententry = nextfreeentry;
+ pH->look_up2[subtree] = currententry = nextfreeentry;
+ nextfreeentry -= 2;
+ }
+
+ code <<= (16 - (code_size - 8));
+
+ for (l = code_size; l > 9; l--) {
+ if ((code & 0x8000) == 0) currententry--;
+ if (pH->tree[-currententry - 1] == 0) {
+ pH->tree[-currententry - 1] = nextfreeentry;
+ currententry = nextfreeentry;
+ nextfreeentry -= 2;
+ } else currententry = pH->tree[-currententry - 1];
+ code <<= 1;
+ }
+ if ((code & 0x8000) == 0) currententry--;
+ pH->tree[-currententry - 1] = i;
+ }
+ p++;
+ }
+}
+
+
+// Verifies the quantization tables needed for this scan are available.
+void jpeg_decoder::check_quant_tables()
+{
+ for (int i = 0; i < m_comps_in_scan; i++) {
+ if (m_quant[m_comp_quant[m_comp_list[i]]] == nullptr) stop_decoding(JPGD_UNDEFINED_QUANT_TABLE);
+ }
+}
+
+
+// Verifies that all the Huffman tables needed for this scan are available.
+void jpeg_decoder::check_huff_tables()
+{
+ for (int i = 0; i < m_comps_in_scan; i++) {
+ if ((m_spectral_start == 0) && (m_huff_num[m_comp_dc_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
+ if ((m_spectral_end > 0) && (m_huff_num[m_comp_ac_tab[m_comp_list[i]]] == nullptr)) stop_decoding(JPGD_UNDEFINED_HUFF_TABLE);
+ }
+
+ for (int i = 0; i < JPGD_MAX_HUFF_TABLES; i++) {
+ if (m_huff_num[i]) {
+ if (!m_pHuff_tabs[i]) m_pHuff_tabs[i] = (huff_tables *)alloc(sizeof(huff_tables));
+ make_huff_table(i, m_pHuff_tabs[i]);
+ }
+ }
+}
+
+
+// Determines the component order inside each MCU.
+// Also calcs how many MCU's are on each row, etc.
+void jpeg_decoder::calc_mcu_block_order()
+{
+ int component_num, component_id;
+ int max_h_samp = 0, max_v_samp = 0;
+
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++) {
+ if (m_comp_h_samp[component_id] > max_h_samp) {
+ max_h_samp = m_comp_h_samp[component_id];
+ }
+ if (m_comp_v_samp[component_id] > max_v_samp) {
+ max_v_samp = m_comp_v_samp[component_id];
+ }
+ }
+
+ for (component_id = 0; component_id < m_comps_in_frame; component_id++) {
+ m_comp_h_blocks[component_id] = ((((m_image_x_size * m_comp_h_samp[component_id]) + (max_h_samp - 1)) / max_h_samp) + 7) / 8;
+ m_comp_v_blocks[component_id] = ((((m_image_y_size * m_comp_v_samp[component_id]) + (max_v_samp - 1)) / max_v_samp) + 7) / 8;
+ }
+
+ if (m_comps_in_scan == 1) {
+ m_mcus_per_row = m_comp_h_blocks[m_comp_list[0]];
+ m_mcus_per_col = m_comp_v_blocks[m_comp_list[0]];
+ } else {
+ m_mcus_per_row = (((m_image_x_size + 7) / 8) + (max_h_samp - 1)) / max_h_samp;
+ m_mcus_per_col = (((m_image_y_size + 7) / 8) + (max_v_samp - 1)) / max_v_samp;
+ }
+
+ if (m_comps_in_scan == 1) {
+ m_mcu_org[0] = m_comp_list[0];
+ m_blocks_per_mcu = 1;
+ } else {
+ m_blocks_per_mcu = 0;
+
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
+ int num_blocks;
+ component_id = m_comp_list[component_num];
+ num_blocks = m_comp_h_samp[component_id] * m_comp_v_samp[component_id];
+ while (num_blocks--) m_mcu_org[m_blocks_per_mcu++] = component_id;
+ }
+ }
+}
+
+
+// Starts a new scan.
+int jpeg_decoder::init_scan()
+{
+ if (!locate_sos_marker()) return false;
+
+ calc_mcu_block_order();
+ check_huff_tables();
+ check_quant_tables();
+
+ memset(m_last_dc_val, 0, m_comps_in_frame * sizeof(uint32_t));
+
+ m_eob_run = 0;
+
+ if (m_restart_interval) {
+ m_restarts_left = m_restart_interval;
+ m_next_restart_num = 0;
+ }
+ fix_in_buffer();
+ return true;
+}
+
+
+// Starts a frame. Determines if the number of components or sampling factors
+// are supported.
+void jpeg_decoder::init_frame()
+{
+ int i;
+
+ if (m_comps_in_frame == 1) {
+ if ((m_comp_h_samp[0] != 1) || (m_comp_v_samp[0] != 1)) stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
+ m_scan_type = JPGD_GRAYSCALE;
+ m_max_blocks_per_mcu = 1;
+ m_max_mcu_x_size = 8;
+ m_max_mcu_y_size = 8;
+ } else if (m_comps_in_frame == 3) {
+ if (((m_comp_h_samp[1] != 1) || (m_comp_v_samp[1] != 1)) || ((m_comp_h_samp[2] != 1) || (m_comp_v_samp[2] != 1)))
+ stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
+
+ if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 1)) {
+ m_scan_type = JPGD_YH1V1;
+ m_max_blocks_per_mcu = 3;
+ m_max_mcu_x_size = 8;
+ m_max_mcu_y_size = 8;
+ } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 1)) {
+ m_scan_type = JPGD_YH2V1;
+ m_max_blocks_per_mcu = 4;
+ m_max_mcu_x_size = 16;
+ m_max_mcu_y_size = 8;
+ } else if ((m_comp_h_samp[0] == 1) && (m_comp_v_samp[0] == 2)) {
+ m_scan_type = JPGD_YH1V2;
+ m_max_blocks_per_mcu = 4;
+ m_max_mcu_x_size = 8;
+ m_max_mcu_y_size = 16;
+ } else if ((m_comp_h_samp[0] == 2) && (m_comp_v_samp[0] == 2)) {
+ m_scan_type = JPGD_YH2V2;
+ m_max_blocks_per_mcu = 6;
+ m_max_mcu_x_size = 16;
+ m_max_mcu_y_size = 16;
+ } else stop_decoding(JPGD_UNSUPPORTED_SAMP_FACTORS);
+ } else stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
+
+ m_max_mcus_per_row = (m_image_x_size + (m_max_mcu_x_size - 1)) / m_max_mcu_x_size;
+ m_max_mcus_per_col = (m_image_y_size + (m_max_mcu_y_size - 1)) / m_max_mcu_y_size;
+
+ // These values are for the *destination* pixels: after conversion.
+ if (m_scan_type == JPGD_GRAYSCALE) m_dest_bytes_per_pixel = 1;
+ else m_dest_bytes_per_pixel = 4;
+
+ m_dest_bytes_per_scan_line = ((m_image_x_size + 15) & 0xFFF0) * m_dest_bytes_per_pixel;
+ m_real_dest_bytes_per_scan_line = (m_image_x_size * m_dest_bytes_per_pixel);
+
+ // Initialize two scan line buffers.
+ m_pScan_line_0 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true);
+ if ((m_scan_type == JPGD_YH1V2) || (m_scan_type == JPGD_YH2V2)) {
+ m_pScan_line_1 = (uint8_t *)alloc(m_dest_bytes_per_scan_line, true);
+ }
+
+ m_max_blocks_per_row = m_max_mcus_per_row * m_max_blocks_per_mcu;
+
+ // Should never happen
+ if (m_max_blocks_per_row > JPGD_MAX_BLOCKS_PER_ROW) stop_decoding(JPGD_ASSERTION_ERROR);
+
+ // Allocate the coefficient buffer, enough for one MCU
+ m_pMCU_coefficients = (jpgd_block_t*)alloc(m_max_blocks_per_mcu * 64 * sizeof(jpgd_block_t));
+
+ for (i = 0; i < m_max_blocks_per_mcu; i++) {
+ m_mcu_block_max_zag[i] = 64;
+ }
+
+ m_expanded_blocks_per_component = m_comp_h_samp[0] * m_comp_v_samp[0];
+ m_expanded_blocks_per_mcu = m_expanded_blocks_per_component * m_comps_in_frame;
+ m_expanded_blocks_per_row = m_max_mcus_per_row * m_expanded_blocks_per_mcu;
+ // Freq. domain chroma upsampling is only supported for H2V2 subsampling factor (the most common one I've seen).
+ m_freq_domain_chroma_upsample = false;
+#if JPGD_SUPPORT_FREQ_DOMAIN_UPSAMPLING
+ m_freq_domain_chroma_upsample = (m_expanded_blocks_per_mcu == 4*3);
+#endif
+
+ if (m_freq_domain_chroma_upsample)
+ m_pSample_buf = (uint8_t *)alloc(m_expanded_blocks_per_row * 64);
+ else
+ m_pSample_buf = (uint8_t *)alloc(m_max_blocks_per_row * 64);
+
+ m_total_lines_left = m_image_y_size;
+ m_mcu_lines_left = 0;
+ create_look_ups();
+}
+
+
+// The coeff_buf series of methods originally stored the coefficients
+// into a "virtual" file which was located in EMS, XMS, or a disk file. A cache
+// was used to make this process more efficient. Now, we can store the entire
+// thing in RAM.
+jpeg_decoder::coeff_buf* jpeg_decoder::coeff_buf_open(int block_num_x, int block_num_y, int block_len_x, int block_len_y)
+{
+ coeff_buf* cb = (coeff_buf*)alloc(sizeof(coeff_buf));
+ cb->block_num_x = block_num_x;
+ cb->block_num_y = block_num_y;
+ cb->block_len_x = block_len_x;
+ cb->block_len_y = block_len_y;
+ cb->block_size = (block_len_x * block_len_y) * sizeof(jpgd_block_t);
+ cb->pData = (uint8_t *)alloc(cb->block_size * block_num_x * block_num_y, true);
+ return cb;
+}
+
+
+inline jpgd_block_t *jpeg_decoder::coeff_buf_getp(coeff_buf *cb, int block_x, int block_y)
+{
+ JPGD_ASSERT((block_x < cb->block_num_x) && (block_y < cb->block_num_y));
+ return (jpgd_block_t *)(cb->pData + block_x * cb->block_size + block_y * (cb->block_size * cb->block_num_x));
+}
+
+
+// The following methods decode the various types of m_blocks encountered
+// in progressively encoded images.
+void jpeg_decoder::decode_block_dc_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ int s, r;
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
+
+ if ((s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_dc_tab[component_id]])) != 0) {
+ r = pD->get_bits_no_markers(s);
+ s = JPGD_HUFF_EXTEND(r, s);
+ }
+ pD->m_last_dc_val[component_id] = (s += pD->m_last_dc_val[component_id]);
+ p[0] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
+}
+
+
+void jpeg_decoder::decode_block_dc_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ if (pD->get_bits_no_markers(1)) {
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_dc_coeffs[component_id], block_x, block_y);
+ p[0] |= (1 << pD->m_successive_low);
+ }
+}
+
+
+void jpeg_decoder::decode_block_ac_first(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ int k, s, r;
+
+ if (pD->m_eob_run) {
+ pD->m_eob_run--;
+ return;
+ }
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
+
+ for (k = pD->m_spectral_start; k <= pD->m_spectral_end; k++) {
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
+ r = s >> 4;
+ s &= 15;
+ if (s) {
+ if ((k += r) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
+ r = pD->get_bits_no_markers(s);
+ s = JPGD_HUFF_EXTEND(r, s);
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(static_cast<unsigned int>(s) << pD->m_successive_low);
+ } else {
+ if (r == 15) {
+ if ((k += 15) > 63) pD->stop_decoding(JPGD_DECODE_ERROR);
+ } else {
+ pD->m_eob_run = 1 << r;
+ if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
+ pD->m_eob_run--;
+ break;
+ }
+ }
+ }
+}
+
+
+void jpeg_decoder::decode_block_ac_refine(jpeg_decoder *pD, int component_id, int block_x, int block_y)
+{
+ int s, k, r;
+ int p1 = 1 << pD->m_successive_low;
+ int m1 = static_cast<unsigned int>(-1) << pD->m_successive_low;
+ jpgd_block_t *p = pD->coeff_buf_getp(pD->m_ac_coeffs[component_id], block_x, block_y);
+
+ JPGD_ASSERT(pD->m_spectral_end <= 63);
+
+ k = pD->m_spectral_start;
+
+ if (pD->m_eob_run == 0) {
+ for ( ; k <= pD->m_spectral_end; k++) {
+ s = pD->huff_decode(pD->m_pHuff_tabs[pD->m_comp_ac_tab[component_id]]);
+ r = s >> 4;
+ s &= 15;
+ if (s) {
+ if (s != 1) pD->stop_decoding(JPGD_DECODE_ERROR);
+ if (pD->get_bits_no_markers(1)) s = p1;
+ else s = m1;
+ } else {
+ if (r != 15) {
+ pD->m_eob_run = 1 << r;
+ if (r) pD->m_eob_run += pD->get_bits_no_markers(r);
+ break;
+ }
+ }
+
+ do {
+ jpgd_block_t *this_coef = p + g_ZAG[k & 63];
+
+ if (*this_coef != 0) {
+ if (pD->get_bits_no_markers(1)) {
+ if ((*this_coef & p1) == 0) {
+ if (*this_coef >= 0) *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
+ else *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
+ }
+ }
+ } else {
+ if (--r < 0) break;
+ }
+ k++;
+ } while (k <= pD->m_spectral_end);
+
+ if ((s) && (k < 64)) {
+ p[g_ZAG[k]] = static_cast<jpgd_block_t>(s);
+ }
+ }
+ }
+
+ if (pD->m_eob_run > 0) {
+ for ( ; k <= pD->m_spectral_end; k++) {
+ jpgd_block_t *this_coef = p + g_ZAG[k & 63]; // logical AND to shut up static code analysis
+
+ if (*this_coef != 0) {
+ if (pD->get_bits_no_markers(1)) {
+ if ((*this_coef & p1) == 0) {
+ if (*this_coef >= 0) *this_coef = static_cast<jpgd_block_t>(*this_coef + p1);
+ else *this_coef = static_cast<jpgd_block_t>(*this_coef + m1);
+ }
+ }
+ }
+ }
+ pD->m_eob_run--;
+ }
+}
+
+
+// Decode a scan in a progressively encoded image.
+void jpeg_decoder::decode_scan(pDecode_block_func decode_block_func)
+{
+ int mcu_row, mcu_col, mcu_block;
+ int block_x_mcu[JPGD_MAX_COMPONENTS], m_block_y_mcu[JPGD_MAX_COMPONENTS];
+
+ memset(m_block_y_mcu, 0, sizeof(m_block_y_mcu));
+
+ for (mcu_col = 0; mcu_col < m_mcus_per_col; mcu_col++) {
+ int component_num, component_id;
+ memset(block_x_mcu, 0, sizeof(block_x_mcu));
+
+ for (mcu_row = 0; mcu_row < m_mcus_per_row; mcu_row++) {
+ int block_x_mcu_ofs = 0, block_y_mcu_ofs = 0;
+
+ if ((m_restart_interval) && (m_restarts_left == 0)) process_restart();
+
+ for (mcu_block = 0; mcu_block < m_blocks_per_mcu; mcu_block++) {
+ component_id = m_mcu_org[mcu_block];
+ decode_block_func(this, component_id, block_x_mcu[component_id] + block_x_mcu_ofs, m_block_y_mcu[component_id] + block_y_mcu_ofs);
+
+ if (m_comps_in_scan == 1) block_x_mcu[component_id]++;
+ else {
+ if (++block_x_mcu_ofs == m_comp_h_samp[component_id]) {
+ block_x_mcu_ofs = 0;
+
+ if (++block_y_mcu_ofs == m_comp_v_samp[component_id]) {
+ block_y_mcu_ofs = 0;
+ block_x_mcu[component_id] += m_comp_h_samp[component_id];
+ }
+ }
+ }
+ }
+ m_restarts_left--;
+ }
+
+ if (m_comps_in_scan == 1) m_block_y_mcu[m_comp_list[0]]++;
+ else {
+ for (component_num = 0; component_num < m_comps_in_scan; component_num++) {
+ component_id = m_comp_list[component_num];
+ m_block_y_mcu[component_id] += m_comp_v_samp[component_id];
+ }
+ }
+ }
+}
+
+
+// Decode a progressively encoded image.
+void jpeg_decoder::init_progressive()
+{
+ int i;
+
+ if (m_comps_in_frame == 4) stop_decoding(JPGD_UNSUPPORTED_COLORSPACE);
+
+ // Allocate the coefficient buffers.
+ for (i = 0; i < m_comps_in_frame; i++) {
+ m_dc_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 1, 1);
+ m_ac_coeffs[i] = coeff_buf_open(m_max_mcus_per_row * m_comp_h_samp[i], m_max_mcus_per_col * m_comp_v_samp[i], 8, 8);
+ }
+
+ while (true) {
+ int dc_only_scan, refinement_scan;
+ pDecode_block_func decode_block_func;
+
+ if (!init_scan()) break;
+
+ dc_only_scan = (m_spectral_start == 0);
+ refinement_scan = (m_successive_high != 0);
+
+ if ((m_spectral_start > m_spectral_end) || (m_spectral_end > 63)) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
+
+ if (dc_only_scan) {
+ if (m_spectral_end) stop_decoding(JPGD_BAD_SOS_SPECTRAL);
+ } else if (m_comps_in_scan != 1) { /* AC scans can only contain one component */
+ stop_decoding(JPGD_BAD_SOS_SPECTRAL);
+ }
+
+ if ((refinement_scan) && (m_successive_low != m_successive_high - 1)) stop_decoding(JPGD_BAD_SOS_SUCCESSIVE);
+
+ if (dc_only_scan) {
+ if (refinement_scan) decode_block_func = decode_block_dc_refine;
+ else decode_block_func = decode_block_dc_first;
+ } else {
+ if (refinement_scan) decode_block_func = decode_block_ac_refine;
+ else decode_block_func = decode_block_ac_first;
+ }
+ decode_scan(decode_block_func);
+ m_bits_left = 16;
+ get_bits(16);
+ get_bits(16);
+ }
+
+ m_comps_in_scan = m_comps_in_frame;
+
+ for (i = 0; i < m_comps_in_frame; i++) {
+ m_comp_list[i] = i;
+ }
+
+ calc_mcu_block_order();
+}
+
+
+void jpeg_decoder::init_sequential()
+{
+ if (!init_scan()) stop_decoding(JPGD_UNEXPECTED_MARKER);
+}
+
+
+void jpeg_decoder::decode_start()
+{
+ init_frame();
+ if (m_progressive_flag) init_progressive();
+ else init_sequential();
+}
+
+
+void jpeg_decoder::decode_init(jpeg_decoder_stream *pStream)
+{
+ init(pStream);
+ locate_sof_marker();
+}
+
+
+jpeg_decoder::jpeg_decoder(jpeg_decoder_stream *pStream)
+{
+ if (setjmp(m_jmp_state)) return;
+ decode_init(pStream);
+}
+
+
+int jpeg_decoder::begin_decoding()
+{
+ if (m_ready_flag) return JPGD_SUCCESS;
+ if (m_error_code) return JPGD_FAILED;
+ if (setjmp(m_jmp_state)) return JPGD_FAILED;
+
+ decode_start();
+ m_ready_flag = true;
+
+ return JPGD_SUCCESS;
+}
+
+
+jpeg_decoder::~jpeg_decoder()
+{
+ free_all_blocks();
+}
+
+
+void jpeg_decoder_file_stream::close()
+{
+ if (m_pFile) {
+ fclose(m_pFile);
+ m_pFile = nullptr;
+ }
+ m_eof_flag = false;
+ m_error_flag = false;
+}
+
+
+jpeg_decoder_file_stream::~jpeg_decoder_file_stream()
+{
+ close();
+}
+
+
+bool jpeg_decoder_file_stream::open(const char *Pfilename)
+{
+ close();
+
+ m_eof_flag = false;
+ m_error_flag = false;
+
+#if defined(_MSC_VER)
+ m_pFile = nullptr;
+ fopen_s(&m_pFile, Pfilename, "rb");
+#else
+ m_pFile = fopen(Pfilename, "rb");
+#endif
+ return m_pFile != nullptr;
+}
+
+
+int jpeg_decoder_file_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag)
+{
+ if (!m_pFile) return -1;
+
+ if (m_eof_flag) {
+ *pEOF_flag = true;
+ return 0;
+ }
+
+ if (m_error_flag) return -1;
+
+ int bytes_read = static_cast<int>(fread(pBuf, 1, max_bytes_to_read, m_pFile));
+ if (bytes_read < max_bytes_to_read) {
+ if (ferror(m_pFile)) {
+ m_error_flag = true;
+ return -1;
+ }
+ m_eof_flag = true;
+ *pEOF_flag = true;
+ }
+ return bytes_read;
+}
+
+
+bool jpeg_decoder_mem_stream::open(const uint8_t *pSrc_data, uint32_t size)
+{
+ close();
+ m_pSrc_data = pSrc_data;
+ m_ofs = 0;
+ m_size = size;
+ return true;
+}
+
+
+int jpeg_decoder_mem_stream::read(uint8_t *pBuf, int max_bytes_to_read, bool *pEOF_flag)
+{
+ *pEOF_flag = false;
+ if (!m_pSrc_data) return -1;
+
+ uint32_t bytes_remaining = m_size - m_ofs;
+ if ((uint32_t)max_bytes_to_read > bytes_remaining) {
+ max_bytes_to_read = bytes_remaining;
+ *pEOF_flag = true;
+ }
+ memcpy(pBuf, m_pSrc_data + m_ofs, max_bytes_to_read);
+ m_ofs += max_bytes_to_read;
+
+ return max_bytes_to_read;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+jpeg_decoder* jpgdHeader(const char* data, int size, int* width, int* height)
+{
+ auto decoder = new jpeg_decoder(new jpeg_decoder_mem_stream((const uint8_t*)data, size));
+ if (decoder->get_error_code() != JPGD_SUCCESS) {
+ delete(decoder);
+ return nullptr;
+ }
+
+ if (width) *width = decoder->get_width();
+ if (height) *height = decoder->get_height();
+
+ return decoder;
+}
+
+
+jpeg_decoder* jpgdHeader(const char* filename, int* width, int* height)
+{
+ auto fileStream = new jpeg_decoder_file_stream();
+ if (!fileStream->open(filename)) return nullptr;
+
+ auto decoder = new jpeg_decoder(fileStream);
+ if (decoder->get_error_code() != JPGD_SUCCESS) {
+ delete(decoder);
+ return nullptr;
+ }
+
+ if (width) *width = decoder->get_width();
+ if (height) *height = decoder->get_height();
+
+ return decoder;
+}
+
+
+void jpgdDelete(jpeg_decoder* decoder)
+{
+ delete(decoder);
+}
+
+
+unsigned char* jpgdDecompress(jpeg_decoder* decoder)
+{
+ if (!decoder) return nullptr;
+
+ int req_comps = 4; //TODO: fixed 4 channel components now?
+ if ((req_comps != 1) && (req_comps != 3) && (req_comps != 4)) return nullptr;
+
+ auto image_width = decoder->get_width();
+ auto image_height = decoder->get_height();
+ //auto actual_comps = decoder->get_num_components();
+
+ if (decoder->begin_decoding() != JPGD_SUCCESS) return nullptr;
+
+ const int dst_bpl = image_width * req_comps;
+ uint8_t *pImage_data = (uint8_t*)malloc(dst_bpl * image_height);
+ if (!pImage_data) return nullptr;
+
+ for (int y = 0; y < image_height; y++) {
+ const uint8_t* pScan_line;
+ uint32_t scan_line_len;
+ if (decoder->decode((const void**)&pScan_line, &scan_line_len) != JPGD_SUCCESS) {
+ free(pImage_data);
+ return nullptr;
+ }
+
+ uint8_t *pDst = pImage_data + y * dst_bpl;
+
+ //Return as BGRA
+ if ((req_comps == 4) && (decoder->get_num_components() == 3)) {
+ for (int x = 0; x < image_width; x++) {
+ pDst[0] = pScan_line[x*4+2];
+ pDst[1] = pScan_line[x*4+1];
+ pDst[2] = pScan_line[x*4+0];
+ pDst[3] = 255;
+ pDst += 4;
+ }
+ } else if (((req_comps == 1) && (decoder->get_num_components() == 1)) || ((req_comps == 4) && (decoder->get_num_components() == 3))) {
+ memcpy(pDst, pScan_line, dst_bpl);
+ } else if (decoder->get_num_components() == 1) {
+ if (req_comps == 3) {
+ for (int x = 0; x < image_width; x++) {
+ uint8_t luma = pScan_line[x];
+ pDst[0] = luma;
+ pDst[1] = luma;
+ pDst[2] = luma;
+ pDst += 3;
+ }
+ } else {
+ for (int x = 0; x < image_width; x++) {
+ uint8_t luma = pScan_line[x];
+ pDst[0] = luma;
+ pDst[1] = luma;
+ pDst[2] = luma;
+ pDst[3] = 255;
+ pDst += 4;
+ }
+ }
+ } else if (decoder->get_num_components() == 3) {
+ if (req_comps == 1) {
+ const int YR = 19595, YG = 38470, YB = 7471;
+ for (int x = 0; x < image_width; x++) {
+ int r = pScan_line[x*4+0];
+ int g = pScan_line[x*4+1];
+ int b = pScan_line[x*4+2];
+ *pDst++ = static_cast<uint8_t>((r * YR + g * YG + b * YB + 32768) >> 16);
+ }
+ } else {
+ for (int x = 0; x < image_width; x++) {
+ pDst[0] = pScan_line[x*4+0];
+ pDst[1] = pScan_line[x*4+1];
+ pDst[2] = pScan_line[x*4+2];
+ pDst += 3;
+ }
+ }
+ }
+ }
+ return pImage_data;
+}
diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
new file mode 100644
index 0000000000..ca9cb35c32
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2021 - 2022 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_
diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp b/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp
new file mode 100644
index 0000000000..5c01c5a1e2
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp
@@ -0,0 +1,2647 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+/*
+ LodePNG version 20200306
+
+ Copyright (c) 2005-2020 Lode Vandevenne
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any sourcedistribution.
+*/
+
+#include <cstdlib>
+#include "tvgLodePng.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/
+ #pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/
+ #pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/
+#endif /*_MSC_VER */
+
+
+/* convince the compiler to inline a function, for use when this measurably improves performance */
+/* inline is not available in C90, but use it when supported by the compiler */
+#if (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || (defined(__cplusplus) && (__cplusplus >= 199711L))
+ #define LODEPNG_INLINE inline
+#else
+ #define LODEPNG_INLINE /* not available */
+#endif
+
+/* restrict is not available in C90, but use it when supported by the compiler */
+#if (defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) ||\
+ (defined(_MSC_VER) && (_MSC_VER >= 1400)) || \
+ (defined(__WATCOMC__) && (__WATCOMC__ >= 1250) && !defined(__cplusplus))
+ #define LODEPNG_RESTRICT __restrict
+#else
+ #define LODEPNG_RESTRICT /* not available */
+#endif
+
+#define LODEPNG_MAX(a, b) (((a) > (b)) ? (a) : (b))
+#define LODEPNG_MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define LODEPNG_ABS(x) ((x) < 0 ? -(x) : (x))
+
+
+/* Replacements for C library functions such as memcpy and strlen, to support platforms
+where a full C library is not available. The compiler can recognize them and compile
+to something as fast. */
+
+static void lodepng_memcpy(void* LODEPNG_RESTRICT dst, const void* LODEPNG_RESTRICT src, size_t size)
+{
+ size_t i;
+ for (i = 0; i < size; i++) ((char*)dst)[i] = ((const char*)src)[i];
+}
+
+
+static void lodepng_memset(void* LODEPNG_RESTRICT dst, int value, size_t num)
+{
+ size_t i;
+ for (i = 0; i < num; i++) ((char*)dst)[i] = (char)value;
+}
+
+
+/* does not check memory out of bounds, do not use on untrusted data */
+static size_t lodepng_strlen(const char* a)
+{
+ const char* orig = a;
+ /* avoid warning about unused function in case of disabled COMPILE... macros */
+ (void)(&lodepng_strlen);
+ while (*a) a++;
+ return (size_t)(a - orig);
+}
+
+
+/* Safely check if adding two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_addofl(size_t a, size_t b, size_t* result)
+{
+ *result = a + b; /* Unsigned addition is well defined and safe in C90 */
+ return *result < a;
+}
+
+
+/* Safely check if multiplying two integers will overflow (no undefined
+behavior, compiler removing the code, etc...) and output result. */
+static int lodepng_mulofl(size_t a, size_t b, size_t* result)
+{
+ *result = a * b; /* Unsigned multiplication is well defined and safe in C90 */
+ return (a != 0 && *result / a != b);
+}
+
+
+/* Safely check if a + b > c, even if overflow could happen. */
+static int lodepng_gtofl(size_t a, size_t b, size_t c)
+{
+ size_t d;
+ if (lodepng_addofl(a, b, &d)) return 1;
+ return d > c;
+}
+
+
+/*
+ Often in case of an error a value is assigned to a variable and then it breaks
+ out of a loop (to go to the cleanup phase of a function). This macro does that.
+ It makes the error handling code shorter and more readable.
+
+ Example: if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83);
+*/
+#define CERROR_BREAK(errorvar, code){\
+ errorvar = code;\
+ break;\
+}
+
+/* version of CERROR_BREAK that assumes the common case where the error variable is named "error" */
+#define ERROR_BREAK(code) CERROR_BREAK(error, code)
+
+/* Set error var to the error code, and return it.*/
+#define CERROR_RETURN_ERROR(errorvar, code){\
+ errorvar = code;\
+ return code;\
+}
+
+/* Try the code, if it returns error, also return the error. */
+#define CERROR_TRY_RETURN(call){\
+ unsigned error = call;\
+ if(error) return error;\
+}
+
+/* Set error var to the error code, and return from the void function. */
+#define CERROR_RETURN(errorvar, code){\
+ errorvar = code;\
+ return;\
+}
+
+
+/* dynamic vector of unsigned chars */
+struct ucvector
+{
+ unsigned char* data;
+ size_t size; /*used size*/
+ size_t allocsize; /*allocated size*/
+};
+
+
+/* returns 1 if success, 0 if failure ==> nothing done */
+static unsigned ucvector_resize(ucvector* p, size_t size)
+{
+ if (size > p->allocsize) {
+ size_t newsize = size + (p->allocsize >> 1u);
+ void* data = realloc(p->data, newsize);
+ if(data) {
+ p->allocsize = newsize;
+ p->data = (unsigned char*)data;
+ }
+ else return 0; /*error: not enough memory*/
+ }
+ p->size = size;
+ return 1; /*success*/
+}
+
+
+static ucvector ucvector_init(unsigned char* buffer, size_t size)
+{
+ ucvector v;
+ v.data = buffer;
+ v.allocsize = v.size = size;
+ return v;
+}
+
+
+static unsigned lodepng_read32bitInt(const unsigned char* buffer)
+{
+ return (((unsigned)buffer[0] << 24u) | ((unsigned)buffer[1] << 16u) | ((unsigned)buffer[2] << 8u) | (unsigned)buffer[3]);
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // End of common code and tools. Begin of Zlib related code. // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+struct LodePNGBitReader
+{
+ const unsigned char* data;
+ size_t size; /*size of data in bytes*/
+ size_t bitsize; /*size of data in bits, end of valid bp values, should be 8*size*/
+ size_t bp;
+ unsigned buffer; /*buffer for reading bits. NOTE: 'unsigned' must support at least 32 bits*/
+};
+
+
+/* data size argument is in bytes. Returns error if size too large causing overflow */
+static unsigned LodePNGBitReader_init(LodePNGBitReader* reader, const unsigned char* data, size_t size)
+{
+ size_t temp;
+ reader->data = data;
+ reader->size = size;
+ /* size in bits, return error if overflow (if size_t is 32 bit this supports up to 500MB) */
+ if (lodepng_mulofl(size, 8u, &reader->bitsize)) return 105;
+ /*ensure incremented bp can be compared to bitsize without overflow even when it would be incremented 32 too much and
+ trying to ensure 32 more bits*/
+ if (lodepng_addofl(reader->bitsize, 64u, &temp)) return 105;
+ reader->bp = 0;
+ reader->buffer = 0;
+ return 0; /*ok*/
+ }
+
+/*
+ ensureBits functions:
+ Ensures the reader can at least read nbits bits in one or more readBits calls,
+ safely even if not enough bits are available.
+ Returns 1 if there are enough bits available, 0 if not.
+*/
+
+/*See ensureBits documentation above. This one ensures exactly 1 bit */
+/*static unsigned ensureBits1(LodePNGBitReader* reader) {
+ if(reader->bp >= reader->bitsize) return 0;
+ reader->buffer = (unsigned)reader->data[reader->bp >> 3u] >> (reader->bp & 7u);
+ return 1;
+}*/
+
+/*See ensureBits documentation above. This one ensures up to 9 bits */
+static unsigned ensureBits9(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if (start + 1u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/*See ensureBits documentation above. This one ensures up to 17 bits */
+static unsigned ensureBits17(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if (start + 2u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/*See ensureBits documentation above. This one ensures up to 25 bits */
+static LODEPNG_INLINE unsigned ensureBits25(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if (start + 3u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/*See ensureBits documentation above. This one ensures up to 32 bits */
+static LODEPNG_INLINE unsigned ensureBits32(LodePNGBitReader* reader, size_t nbits)
+{
+ size_t start = reader->bp >> 3u;
+ size_t size = reader->size;
+ if(start + 4u < size) {
+ reader->buffer = (unsigned)reader->data[start + 0] | ((unsigned)reader->data[start + 1] << 8u) | ((unsigned)reader->data[start + 2] << 16u) | ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ reader->buffer |= (((unsigned)reader->data[start + 4] << 24u) << (8u - (reader->bp & 7u)));
+ return 1;
+ } else {
+ reader->buffer = 0;
+ if (start + 0u < size) reader->buffer |= reader->data[start + 0];
+ if (start + 1u < size) reader->buffer |= ((unsigned)reader->data[start + 1] << 8u);
+ if (start + 2u < size) reader->buffer |= ((unsigned)reader->data[start + 2] << 16u);
+ if (start + 3u < size) reader->buffer |= ((unsigned)reader->data[start + 3] << 24u);
+ reader->buffer >>= (reader->bp & 7u);
+ return reader->bp + nbits <= reader->bitsize;
+ }
+}
+
+
+/* Get bits without advancing the bit pointer. Must have enough bits available with ensureBits. Max nbits is 31. */
+static unsigned peekBits(LodePNGBitReader* reader, size_t nbits)
+{
+ /* The shift allows nbits to be only up to 31. */
+ return reader->buffer & ((1u << nbits) - 1u);
+}
+
+
+/* Must have enough bits available with ensureBits */
+static void advanceBits(LodePNGBitReader* reader, size_t nbits)
+{
+ reader->buffer >>= nbits;
+ reader->bp += nbits;
+}
+
+
+/* Must have enough bits available with ensureBits */
+static unsigned readBits(LodePNGBitReader* reader, size_t nbits)
+{
+ unsigned result = peekBits(reader, nbits);
+ advanceBits(reader, nbits);
+ return result;
+}
+
+
+/* Public for testing only. steps and result must have numsteps values. */
+unsigned lode_png_test_bitreader(const unsigned char* data, size_t size, size_t numsteps, const size_t* steps, unsigned* result)
+{
+ size_t i;
+ LodePNGBitReader reader;
+ unsigned error = LodePNGBitReader_init(&reader, data, size);
+ if (error) return 0;
+ for (i = 0; i < numsteps; i++) {
+ size_t step = steps[i];
+ unsigned ok;
+ if (step > 25) ok = ensureBits32(&reader, step);
+ else if (step > 17) ok = ensureBits25(&reader, step);
+ else if (step > 9) ok = ensureBits17(&reader, step);
+ else ok = ensureBits9(&reader, step);
+ if (!ok) return 0;
+ result[i] = readBits(&reader, step);
+ }
+ return 1;
+}
+
+
+static unsigned reverseBits(unsigned bits, unsigned num)
+{
+ /*TODO: implement faster lookup table based version when needed*/
+ unsigned i, result = 0;
+ for (i = 0; i < num; i++) result |= ((bits >> (num - i - 1u)) & 1u) << i;
+ return result;
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Deflate - Huffman / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+#define FIRST_LENGTH_CODE_INDEX 257
+#define LAST_LENGTH_CODE_INDEX 285
+/*256 literals, the end code, some length codes, and 2 unused codes*/
+#define NUM_DEFLATE_CODE_SYMBOLS 288
+/*the distance codes have their own symbols, 30 used, 2 unused*/
+#define NUM_DISTANCE_SYMBOLS 32
+/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/
+#define NUM_CODE_LENGTH_CODES 19
+
+/*the base lengths represented by codes 257-285*/
+static const unsigned LENGTHBASE[29]
+ = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59,
+ 67, 83, 99, 115, 131, 163, 195, 227, 258};
+
+/*the extra bits used by codes 257-285 (added to base length)*/
+static const unsigned LENGTHEXTRA[29]
+ = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
+ 4, 4, 4, 4, 5, 5, 5, 5, 0};
+
+/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/
+static const unsigned DISTANCEBASE[30]
+ = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513,
+ 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
+
+/*the extra bits of backwards distances (added to base)*/
+static const unsigned DISTANCEEXTRA[30]
+ = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8,
+ 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
+
+/*the order in which "code length alphabet code lengths" are stored as specified by deflate, out of this the huffman
+tree of the dynamic huffman tree lengths is generated*/
+static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES]
+ = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*
+Huffman tree struct, containing multiple representations of the tree
+*/
+struct HuffmanTree
+{
+ unsigned* codes; /*the huffman codes (bit patterns representing the symbols)*/
+ unsigned* lengths; /*the lengths of the huffman codes*/
+ unsigned maxbitlen; /*maximum number of bits a single code can get*/
+ unsigned numcodes; /*number of symbols in the alphabet = number of codes*/
+ /* for reading only */
+ unsigned char* table_len; /*length of symbol from lookup table, or max length if secondary lookup needed*/
+ unsigned short* table_value; /*value of symbol from lookup table, or pointer to secondary table if needed*/
+};
+
+
+static void HuffmanTree_init(HuffmanTree* tree)
+{
+ tree->codes = 0;
+ tree->lengths = 0;
+ tree->table_len = 0;
+ tree->table_value = 0;
+}
+
+
+static void HuffmanTree_cleanup(HuffmanTree* tree)
+{
+ free(tree->codes);
+ free(tree->lengths);
+ free(tree->table_len);
+ free(tree->table_value);
+}
+
+
+/* amount of bits for first huffman table lookup (aka root bits), see HuffmanTree_makeTable and huffmanDecodeSymbol.*/
+/* values 8u and 9u work the fastest */
+#define FIRSTBITS 9u
+
+/* a symbol value too big to represent any valid symbol, to indicate reading disallowed huffman bits combination,
+which is possible in case of only 0 or 1 present symbols. */
+#define INVALIDSYMBOL 65535u
+
+/* make table for huffman decoding */
+static unsigned HuffmanTree_makeTable(HuffmanTree* tree)
+{
+ static const unsigned headsize = 1u << FIRSTBITS; /*size of the first table*/
+ static const unsigned mask = (1u << FIRSTBITS) /*headsize*/ - 1u;
+ size_t i, numpresent, pointer, size; /*total table size*/
+ unsigned* maxlens = (unsigned*)malloc(headsize * sizeof(unsigned));
+ if (!maxlens) return 83; /*alloc fail*/
+
+ /* compute maxlens: max total bit length of symbols sharing prefix in the first table*/
+ lodepng_memset(maxlens, 0, headsize * sizeof(*maxlens));
+ for (i = 0; i < tree->numcodes; i++) {
+ unsigned symbol = tree->codes[i];
+ unsigned l = tree->lengths[i];
+ unsigned index;
+ if(l <= FIRSTBITS) continue; /*symbols that fit in first table don't increase secondary table size*/
+ /*get the FIRSTBITS MSBs, the MSBs of the symbol are encoded first. See later comment about the reversing*/
+ index = reverseBits(symbol >> (l - FIRSTBITS), FIRSTBITS);
+ maxlens[index] = LODEPNG_MAX(maxlens[index], l);
+ }
+ /* compute total table size: size of first table plus all secondary tables for symbols longer than FIRSTBITS */
+ size = headsize;
+ for (i = 0; i < headsize; ++i) {
+ unsigned l = maxlens[i];
+ if (l > FIRSTBITS) size += (1u << (l - FIRSTBITS));
+ }
+ tree->table_len = (unsigned char*)malloc(size * sizeof(*tree->table_len));
+ tree->table_value = (unsigned short*)malloc(size * sizeof(*tree->table_value));
+ if (!tree->table_len || !tree->table_value) {
+ free(maxlens);
+ /* freeing tree->table values is done at a higher scope */
+ return 83; /*alloc fail*/
+ }
+ /*initialize with an invalid length to indicate unused entries*/
+ for (i = 0; i < size; ++i) tree->table_len[i] = 16;
+
+ /*fill in the first table for long symbols: max prefix size and pointer to secondary tables*/
+ pointer = headsize;
+ for (i = 0; i < headsize; ++i) {
+ unsigned l = maxlens[i];
+ if(l <= FIRSTBITS) continue;
+ tree->table_len[i] = l;
+ tree->table_value[i] = pointer;
+ pointer += (1u << (l - FIRSTBITS));
+ }
+ free(maxlens);
+
+ /*fill in the first table for short symbols, or secondary table for long symbols*/
+ numpresent = 0;
+ for (i = 0; i < tree->numcodes; ++i) {
+ unsigned l = tree->lengths[i];
+ unsigned symbol = tree->codes[i]; /*the huffman bit pattern. i itself is the value.*/
+ /*reverse bits, because the huffman bits are given in MSB first order but the bit reader reads LSB first*/
+ unsigned reverse = reverseBits(symbol, l);
+ if (l == 0) continue;
+ numpresent++;
+
+ if (l <= FIRSTBITS) {
+ /*short symbol, fully in first table, replicated num times if l < FIRSTBITS*/
+ unsigned num = 1u << (FIRSTBITS - l);
+ unsigned j;
+ for (j = 0; j < num; ++j) {
+ /*bit reader will read the l bits of symbol first, the remaining FIRSTBITS - l bits go to the MSB's*/
+ unsigned index = reverse | (j << l);
+ if(tree->table_len[index] != 16) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
+ tree->table_len[index] = l;
+ tree->table_value[index] = i;
+ }
+ } else {
+ /*long symbol, shares prefix with other long symbols in first lookup table, needs second lookup*/
+ /*the FIRSTBITS MSBs of the symbol are the first table index*/
+ unsigned index = reverse & mask;
+ unsigned maxlen = tree->table_len[index];
+ /*log2 of secondary table length, should be >= l - FIRSTBITS*/
+ unsigned tablelen = maxlen - FIRSTBITS;
+ unsigned start = tree->table_value[index]; /*starting index in secondary table*/
+ unsigned num = 1u << (tablelen - (l - FIRSTBITS)); /*amount of entries of this symbol in secondary table*/
+ unsigned j;
+ if (maxlen < l) return 55; /*invalid tree: long symbol shares prefix with short symbol*/
+ for (j = 0; j < num; ++j) {
+ unsigned reverse2 = reverse >> FIRSTBITS; /* l - FIRSTBITS bits */
+ unsigned index2 = start + (reverse2 | (j << (l - FIRSTBITS)));
+ tree->table_len[index2] = l;
+ tree->table_value[index2] = i;
+ }
+ }
+ }
+
+ if (numpresent < 2) {
+ /* In case of exactly 1 symbol, in theory the huffman symbol needs 0 bits,
+ but deflate uses 1 bit instead. In case of 0 symbols, no symbols can
+ appear at all, but such huffman tree could still exist (e.g. if distance
+ codes are never used). In both cases, not all symbols of the table will be
+ filled in. Fill them in with an invalid symbol value so returning them from
+ huffmanDecodeSymbol will cause error. */
+ for (i = 0; i < size; ++i) {
+ if (tree->table_len[i] == 16) {
+ /* As length, use a value smaller than FIRSTBITS for the head table,
+ and a value larger than FIRSTBITS for the secondary table, to ensure
+ valid behavior for advanceBits when reading this symbol. */
+ tree->table_len[i] = (i < headsize) ? 1 : (FIRSTBITS + 1);
+ tree->table_value[i] = INVALIDSYMBOL;
+ }
+ }
+ } else {
+ /* A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes.
+ If that is not the case (due to too long length codes), the table will not
+ have been fully used, and this is an error (not all bit combinations can be
+ decoded): an oversubscribed huffman tree, indicated by error 55. */
+ for (i = 0; i < size; ++i) {
+ if (tree->table_len[i] == 16) return 55;
+ }
+ }
+ return 0;
+}
+
+
+/*
+ Second step for the ...makeFromLengths and ...makeFromFrequencies functions.
+ numcodes, lengths and maxbitlen must already be filled in correctly. return
+ value is error.
+*/
+static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree)
+{
+ unsigned* blcount;
+ unsigned* nextcode;
+ unsigned error = 0;
+ unsigned bits, n;
+
+ tree->codes = (unsigned*)malloc(tree->numcodes * sizeof(unsigned));
+ blcount = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned));
+ nextcode = (unsigned*)malloc((tree->maxbitlen + 1) * sizeof(unsigned));
+ if (!tree->codes || !blcount || !nextcode) error = 83; /*alloc fail*/
+
+ if (!error) {
+ for (n = 0; n != tree->maxbitlen + 1; n++) blcount[n] = nextcode[n] = 0;
+ /*step 1: count number of instances of each code length*/
+ for (bits = 0; bits != tree->numcodes; ++bits) ++blcount[tree->lengths[bits]];
+ /*step 2: generate the nextcode values*/
+ for(bits = 1; bits <= tree->maxbitlen; ++bits) {
+ nextcode[bits] = (nextcode[bits - 1] + blcount[bits - 1]) << 1u;
+ }
+ /*step 3: generate all the codes*/
+ for (n = 0; n != tree->numcodes; ++n) {
+ if (tree->lengths[n] != 0) {
+ tree->codes[n] = nextcode[tree->lengths[n]]++;
+ /*remove superfluous bits from the code*/
+ tree->codes[n] &= ((1u << tree->lengths[n]) - 1u);
+ }
+ }
+ }
+
+ free(blcount);
+ free(nextcode);
+
+ if (!error) error = HuffmanTree_makeTable(tree);
+ return error;
+}
+
+
+/*
+ given the code lengths (as stored in the PNG file), generate the tree as defined
+ by Deflate. maxbitlen is the maximum bits that a code in the tree can have.
+ return value is error.
+*/
+static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, size_t numcodes, unsigned maxbitlen)
+{
+ unsigned i;
+ tree->lengths = (unsigned*)malloc(numcodes * sizeof(unsigned));
+ if (!tree->lengths) return 83; /*alloc fail*/
+ for (i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i];
+ tree->numcodes = (unsigned)numcodes; /*number of symbols*/
+ tree->maxbitlen = maxbitlen;
+ return HuffmanTree_makeFromLengths2(tree);
+}
+
+
+/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/
+static unsigned generateFixedLitLenTree(HuffmanTree* tree)
+{
+ unsigned i, error = 0;
+ unsigned* bitlen = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
+ if (!bitlen) return 83; /*alloc fail*/
+
+ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/
+ for (i = 0; i <= 143; ++i) bitlen[i] = 8;
+ for (i = 144; i <= 255; ++i) bitlen[i] = 9;
+ for (i = 256; i <= 279; ++i) bitlen[i] = 7;
+ for (i = 280; i <= 287; ++i) bitlen[i] = 8;
+
+ error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15);
+
+ free(bitlen);
+ return error;
+}
+
+
+/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/
+static unsigned generateFixedDistanceTree(HuffmanTree* tree)
+{
+ unsigned i, error = 0;
+ unsigned* bitlen = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
+ if (!bitlen) return 83; /*alloc fail*/
+
+ /*there are 32 distance codes, but 30-31 are unused*/
+ for (i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5;
+ error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15);
+
+ free(bitlen);
+ return error;
+}
+
+
+/*
+ returns the code. The bit reader must already have been ensured at least 15 bits
+*/
+static unsigned huffmanDecodeSymbol(LodePNGBitReader* reader, const HuffmanTree* codetree)
+{
+ unsigned short code = peekBits(reader, FIRSTBITS);
+ unsigned short l = codetree->table_len[code];
+ unsigned short value = codetree->table_value[code];
+ if (l <= FIRSTBITS) {
+ advanceBits(reader, l);
+ return value;
+ } else {
+ unsigned index2;
+ advanceBits(reader, FIRSTBITS);
+ index2 = value + peekBits(reader, l - FIRSTBITS);
+ advanceBits(reader, codetree->table_len[index2] - FIRSTBITS);
+ return codetree->table_value[index2];
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Inflator (Decompressor) / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*get the tree of a deflated block with fixed tree, as specified in the deflate specification
+Returns error code.*/
+static unsigned getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d)
+{
+ unsigned error = generateFixedLitLenTree(tree_ll);
+ if (error) return error;
+ return generateFixedDistanceTree(tree_d);
+}
+
+
+/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/
+static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, LodePNGBitReader* reader)
+{
+ /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/
+ unsigned error = 0;
+ unsigned n, HLIT, HDIST, HCLEN, i;
+
+ /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/
+ unsigned* bitlen_ll = 0; /*lit,len code lengths*/
+ unsigned* bitlen_d = 0; /*dist code lengths*/
+ /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/
+ unsigned* bitlen_cl = 0;
+ HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/
+
+ if (!ensureBits17(reader, 14)) return 49; /*error: the bit pointer is or will go past the memory*/
+
+ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/
+ HLIT = readBits(reader, 5) + 257;
+ /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/
+ HDIST = readBits(reader, 5) + 1;
+ /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/
+ HCLEN = readBits(reader, 4) + 4;
+
+ bitlen_cl = (unsigned*)malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned));
+ if(!bitlen_cl) return 83 /*alloc fail*/;
+
+ HuffmanTree_init(&tree_cl);
+
+ while (!error) {
+ /*read the code length codes out of 3 * (amount of code length codes) bits*/
+ if (lodepng_gtofl(reader->bp, HCLEN * 3, reader->bitsize)) {
+ ERROR_BREAK(50); /*error: the bit pointer is or will go past the memory*/
+ }
+ for (i = 0; i != HCLEN; ++i) {
+ ensureBits9(reader, 3); /*out of bounds already checked above */
+ bitlen_cl[CLCL_ORDER[i]] = readBits(reader, 3);
+ }
+ for (i = HCLEN; i != NUM_CODE_LENGTH_CODES; ++i) {
+ bitlen_cl[CLCL_ORDER[i]] = 0;
+ }
+
+ error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7);
+ if(error) break;
+
+ /*now we can use this tree to read the lengths for the tree that this function will return*/
+ bitlen_ll = (unsigned*)malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned));
+ bitlen_d = (unsigned*)malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned));
+ if (!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/);
+ lodepng_memset(bitlen_ll, 0, NUM_DEFLATE_CODE_SYMBOLS * sizeof(*bitlen_ll));
+ lodepng_memset(bitlen_d, 0, NUM_DISTANCE_SYMBOLS * sizeof(*bitlen_d));
+
+ /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/
+ i = 0;
+ while (i < HLIT + HDIST) {
+ unsigned code;
+ ensureBits25(reader, 22); /* up to 15 bits for huffman code, up to 7 extra bits below*/
+ code = huffmanDecodeSymbol(reader, &tree_cl);
+ if (code <= 15) /*a length code*/ {
+ if (i < HLIT) bitlen_ll[i] = code;
+ else bitlen_d[i - HLIT] = code;
+ ++i;
+ } else if (code == 16) /*repeat previous*/ {
+ unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/
+ unsigned value; /*set value to the previous code*/
+
+ if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/
+
+ replength += readBits(reader, 2);
+
+ if (i < HLIT + 1) value = bitlen_ll[i - 1];
+ else value = bitlen_d[i - HLIT - 1];
+ /*repeat this value in the next lengths*/
+ for (n = 0; n < replength; ++n) {
+ if (i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/
+ if (i < HLIT) bitlen_ll[i] = value;
+ else bitlen_d[i - HLIT] = value;
+ ++i;
+ }
+ } else if(code == 17) /*repeat "0" 3-10 times*/ {
+ unsigned replength = 3; /*read in the bits that indicate repeat length*/
+ replength += readBits(reader, 3);
+
+ /*repeat this value in the next lengths*/
+ for (n = 0; n < replength; ++n) {
+ if (i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/
+
+ if (i < HLIT) bitlen_ll[i] = 0;
+ else bitlen_d[i - HLIT] = 0;
+ ++i;
+ }
+ } else if(code == 18) /*repeat "0" 11-138 times*/ {
+ unsigned replength = 11; /*read in the bits that indicate repeat length*/
+ replength += readBits(reader, 7);
+
+ /*repeat this value in the next lengths*/
+ for (n = 0; n < replength; ++n) {
+ if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/
+
+ if(i < HLIT) bitlen_ll[i] = 0;
+ else bitlen_d[i - HLIT] = 0;
+ ++i;
+ }
+ } else /*if(code == INVALIDSYMBOL)*/ {
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ /*check if any of the ensureBits above went out of bounds*/
+ if (reader->bp > reader->bitsize) {
+ /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
+ (10=no endcode, 11=wrong jump outside of tree)*/
+ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
+ ERROR_BREAK(50); /*error, bit pointer jumps past memory*/
+ }
+ }
+ if (error) break;
+
+ if (bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/
+
+ /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/
+ error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15);
+ if (error) break;
+ error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15);
+
+ break; /*end of error-while*/
+ }
+
+ free(bitlen_cl);
+ free(bitlen_ll);
+ free(bitlen_d);
+ HuffmanTree_cleanup(&tree_cl);
+
+ return error;
+}
+
+
+/*inflate a block with dynamic of fixed Huffman tree. btype must be 1 or 2.*/
+static unsigned inflateHuffmanBlock(ucvector* out, LodePNGBitReader* reader, unsigned btype)
+{
+ unsigned error = 0;
+ HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/
+ HuffmanTree tree_d; /*the huffman tree for distance codes*/
+
+ HuffmanTree_init(&tree_ll);
+ HuffmanTree_init(&tree_d);
+
+ if (btype == 1) error = getTreeInflateFixed(&tree_ll, &tree_d);
+ else /*if(btype == 2)*/ error = getTreeInflateDynamic(&tree_ll, &tree_d, reader);
+
+ while (!error) /*decode all symbols until end reached, breaks at end code*/ {
+ /*code_ll is literal, length or end code*/
+ unsigned code_ll;
+ ensureBits25(reader, 20); /* up to 15 for the huffman symbol, up to 5 for the length extra bits */
+ code_ll = huffmanDecodeSymbol(reader, &tree_ll);
+ if (code_ll <= 255) /*literal symbol*/ {
+ if (!ucvector_resize(out, out->size + 1)) ERROR_BREAK(83 /*alloc fail*/);
+ out->data[out->size - 1] = (unsigned char)code_ll;
+ } else if (code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ {
+ unsigned code_d, distance;
+ unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/
+ size_t start, backward, length;
+
+ /*part 1: get length base*/
+ length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX];
+
+ /*part 2: get extra bits and add the value of that to length*/
+ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX];
+ if (numextrabits_l != 0) {
+ /* bits already ensured above */
+ length += readBits(reader, numextrabits_l);
+ }
+
+ /*part 3: get distance code*/
+ ensureBits32(reader, 28); /* up to 15 for the huffman symbol, up to 13 for the extra bits */
+ code_d = huffmanDecodeSymbol(reader, &tree_d);
+ if (code_d > 29) {
+ if (code_d <= 31) {
+ ERROR_BREAK(18); /*error: invalid distance code (30-31 are never used)*/
+ } else /* if(code_d == INVALIDSYMBOL) */{
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ }
+ distance = DISTANCEBASE[code_d];
+
+ /*part 4: get extra bits from distance*/
+ numextrabits_d = DISTANCEEXTRA[code_d];
+ if (numextrabits_d != 0) {
+ /* bits already ensured above */
+ distance += readBits(reader, numextrabits_d);
+ }
+
+ /*part 5: fill in all the out[n] values based on the length and dist*/
+ start = out->size;
+ if (distance > start) ERROR_BREAK(52); /*too long backward distance*/
+ backward = start - distance;
+
+ if (!ucvector_resize(out, out->size + length)) ERROR_BREAK(83 /*alloc fail*/);
+ if (distance < length) {
+ size_t forward;
+ lodepng_memcpy(out->data + start, out->data + backward, distance);
+ start += distance;
+ for (forward = distance; forward < length; ++forward) {
+ out->data[start++] = out->data[backward++];
+ }
+ } else {
+ lodepng_memcpy(out->data + start, out->data + backward, length);
+ }
+ } else if (code_ll == 256) {
+ break; /*end code, break the loop*/
+ } else /*if(code_ll == INVALIDSYMBOL)*/ {
+ ERROR_BREAK(16); /*error: tried to read disallowed huffman symbol*/
+ }
+ /*check if any of the ensureBits above went out of bounds*/
+ if (reader->bp > reader->bitsize) {
+ /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol
+ (10=no endcode, 11=wrong jump outside of tree)*/
+ /* TODO: revise error codes 10,11,50: the above comment is no longer valid */
+ ERROR_BREAK(51); /*error, bit pointer jumps past memory*/
+ }
+ }
+
+ HuffmanTree_cleanup(&tree_ll);
+ HuffmanTree_cleanup(&tree_d);
+
+ return error;
+}
+
+
+static unsigned inflateNoCompression(ucvector* out, LodePNGBitReader* reader, const LodePNGDecompressSettings* settings)
+{
+ size_t bytepos;
+ size_t size = reader->size;
+ unsigned LEN, NLEN, error = 0;
+
+ /*go to first boundary of byte*/
+ bytepos = (reader->bp + 7u) >> 3u;
+
+ /*read LEN (2 bytes) and NLEN (2 bytes)*/
+ if (bytepos + 4 >= size) return 52; /*error, bit pointer will jump past memory*/
+ LEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
+ NLEN = (unsigned)reader->data[bytepos] + ((unsigned)reader->data[bytepos + 1] << 8u); bytepos += 2;
+
+ /*check if 16-bit NLEN is really the one's complement of LEN*/
+ if (!settings->ignore_nlen && LEN + NLEN != 65535) {
+ return 21; /*error: NLEN is not one's complement of LEN*/
+ }
+
+ if (!ucvector_resize(out, out->size + LEN)) return 83; /*alloc fail*/
+
+ /*read the literal data: LEN bytes are now stored in the out buffer*/
+ if (bytepos + LEN > size) return 23; /*error: reading outside of in buffer*/
+
+ lodepng_memcpy(out->data + out->size - LEN, reader->data + bytepos, LEN);
+ bytepos += LEN;
+
+ reader->bp = bytepos << 3u;
+
+ return error;
+}
+
+
+static unsigned lodepng_inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ unsigned BFINAL = 0;
+ LodePNGBitReader reader;
+ unsigned error = LodePNGBitReader_init(&reader, in, insize);
+
+ if (error) return error;
+
+ while (!BFINAL) {
+ unsigned BTYPE;
+ if (!ensureBits9(&reader, 3)) return 52; /*error, bit pointer will jump past memory*/
+ BFINAL = readBits(&reader, 1);
+ BTYPE = readBits(&reader, 2);
+
+ if (BTYPE == 3) return 20; /*error: invalid BTYPE*/
+ else if (BTYPE == 0) error = inflateNoCompression(out, &reader, settings); /*no compression*/
+ else error = inflateHuffmanBlock(out, &reader, BTYPE); /*compression, BTYPE 01 or 10*/
+
+ if (error) return error;
+ }
+
+ return error;
+}
+
+
+static unsigned inflatev(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ if (settings->custom_inflate) {
+ unsigned error = settings->custom_inflate(&out->data, &out->size, in, insize, settings);
+ out->allocsize = out->size;
+ return error;
+ } else {
+ return lodepng_inflatev(out, in, insize, settings);
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Adler32 / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len)
+{
+ unsigned s1 = adler & 0xffffu;
+ unsigned s2 = (adler >> 16u) & 0xffffu;
+
+ while (len != 0u) {
+ unsigned i;
+ /*at least 5552 sums can be done before the sums overflow, saving a lot of module divisions*/
+ unsigned amount = len > 5552u ? 5552u : len;
+ len -= amount;
+ for (i = 0; i != amount; ++i) {
+ s1 += (*data++);
+ s2 += s1;
+ }
+ s1 %= 65521u;
+ s2 %= 65521u;
+ }
+
+ return (s2 << 16u) | s1;
+}
+
+/*Return the adler32 of the bytes data[0..len-1]*/
+static unsigned adler32(const unsigned char* data, unsigned len)
+{
+ return update_adler32(1u, data, len);
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Zlib / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static unsigned lodepng_zlib_decompressv(ucvector* out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ unsigned error = 0;
+ unsigned CM, CINFO, FDICT;
+
+ if (insize < 2) return 53; /*error, size of zlib data too small*/
+ /*read information from zlib header*/
+ if ((in[0] * 256 + in[1]) % 31 != 0) {
+ /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/
+ return 24;
+ }
+
+ CM = in[0] & 15;
+ CINFO = (in[0] >> 4) & 15;
+ /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/
+ FDICT = (in[1] >> 5) & 1;
+ /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/
+
+ if (CM != 8 || CINFO > 7) {
+ /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/
+ return 25;
+ }
+ if (FDICT != 0) {
+ /*error: the specification of PNG says about the zlib stream:
+ "The additional flags shall not specify a preset dictionary."*/
+ return 26;
+ }
+
+ error = inflatev(out, in + 2, insize - 2, settings);
+ if (error) return error;
+
+ if (!settings->ignore_adler32) {
+ unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]);
+ unsigned checksum = adler32(out->data, (unsigned)(out->size));
+ if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/
+ }
+
+ return 0; /*no error*/
+}
+
+
+/*expected_size is expected output size, to avoid intermediate allocations. Set to 0 if not known. */
+static unsigned zlib_decompress(unsigned char** out, size_t* outsize, size_t expected_size, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings)
+{
+ if(settings->custom_zlib) {
+ return settings->custom_zlib(out, outsize, in, insize, settings);
+ } else {
+ unsigned error;
+ ucvector v = ucvector_init(*out, *outsize);
+ if (expected_size) {
+ /*reserve the memory to avoid intermediate reallocations*/
+ ucvector_resize(&v, *outsize + expected_size);
+ v.size = *outsize;
+ }
+ error = lodepng_zlib_decompressv(&v, in, insize, settings);
+ *out = v.data;
+ *outsize = v.size;
+ return error;
+ }
+}
+
+
+static void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings)
+{
+ settings->ignore_adler32 = 0;
+ settings->ignore_nlen = 0;
+ settings->custom_zlib = 0;
+ settings->custom_inflate = 0;
+ settings->custom_context = 0;
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* // End of Zlib related code. Begin of PNG related code. // */
+/* ////////////////////////////////////////////////////////////////////////// */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+
+#if 0 //thorvg don't use crc
+/* CRC polynomial: 0xedb88320 */
+static unsigned lodepng_crc32_table[256] = {
+ 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u,
+ 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u,
+ 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u,
+ 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u,
+ 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u,
+ 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u,
+ 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u,
+ 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u,
+ 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u,
+ 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u,
+ 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u,
+ 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u,
+ 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u,
+ 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u,
+ 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u,
+ 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u,
+ 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u,
+ 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u,
+ 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u,
+ 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u,
+ 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u,
+ 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u,
+ 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u,
+ 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u,
+ 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u,
+ 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u,
+ 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u,
+ 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u,
+ 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u,
+ 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u,
+ 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u,
+ 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u
+};
+
+
+/* Calculate CRC32 of buffer
+ Return the CRC of the bytes buf[0..len-1]. */
+static unsigned lodepng_crc32(const unsigned char* data, size_t length)
+{
+ unsigned r = 0xffffffffu;
+ size_t i;
+ for (i = 0; i < length; ++i) {
+ r = lodepng_crc32_table[(r ^ data[i]) & 0xffu] ^ (r >> 8u);
+ }
+ return r ^ 0xffffffffu;
+}
+#endif
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Reading and writing PNG color channel bits / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/* The color channel bits of less-than-8-bit pixels are read with the MSB of bytes first,
+so LodePNGBitWriter and LodePNGBitReader can't be used for those. */
+
+static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream)
+{
+ unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1);
+ ++(*bitpointer);
+ return result;
+}
+
+
+/* TODO: make this faster */
+static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits)
+{
+ unsigned result = 0;
+ size_t i;
+ for (i = 0 ; i < nbits; ++i) {
+ result <<= 1u;
+ result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream);
+ }
+ return result;
+}
+
+
+static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit)
+{
+ /*the current bit in bitstream may be 0 or 1 for this to work*/
+ if (bit == 0) bitstream[(*bitpointer) >> 3u] &= (unsigned char)(~(1u << (7u - ((*bitpointer) & 7u))));
+ else bitstream[(*bitpointer) >> 3u] |= (1u << (7u - ((*bitpointer) & 7u)));
+ ++(*bitpointer);
+}
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / PNG chunks / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*
+ The lodepng_chunk functions are normally not needed, except to traverse the
+ unknown chunks stored in the LodePNGInfo struct, or add new ones to it.
+ It also allows traversing the chunks of an encoded PNG file yourself.
+
+ The chunk pointer always points to the beginning of the chunk itself, that is
+ the first byte of the 4 length bytes.
+
+ In the PNG file format, chunks have the following format:
+ -4 bytes length: length of the data of the chunk in bytes (chunk itself is 12 bytes longer)
+ -4 bytes chunk type (ASCII a-z,A-Z only, see below)
+ -length bytes of data (may be 0 bytes if length was 0)
+ -4 bytes of CRC, computed on chunk name + data
+
+ The first chunk starts at the 8th byte of the PNG file, the entire rest of the file
+ exists out of concatenated chunks with the above format.
+
+ PNG standard chunk ASCII naming conventions:
+ -First byte: uppercase = critical, lowercase = ancillary
+ -Second byte: uppercase = public, lowercase = private
+ -Third byte: must be uppercase
+ -Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy
+*/
+
+
+/*
+ Gets the length of the data of the chunk. Total chunk length has 12 bytes more.
+ There must be at least 4 bytes to read from. If the result value is too large,
+ it may be corrupt data.
+*/
+static unsigned lodepng_chunk_length(const unsigned char* chunk)
+{
+ return lodepng_read32bitInt(&chunk[0]);
+}
+
+
+/* check if the type is the given type */
+static unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type)
+{
+ if (lodepng_strlen(type) != 4) return 0;
+ return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]);
+}
+
+
+/* 0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard) */
+static unsigned char lodepng_chunk_ancillary(const unsigned char* chunk)
+{
+ return ((chunk[4] & 32) != 0);
+}
+
+
+static const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk)
+{
+ return &chunk[8];
+}
+
+#if 0 //thorvg don't use crc
+/* returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!) */
+static unsigned lodepng_chunk_check_crc(const unsigned char* chunk)
+{
+ unsigned length = lodepng_chunk_length(chunk);
+ unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]);
+ /*the CRC is taken of the data and the 4 chunk type letters, not the length*/
+ unsigned checksum = lodepng_crc32(&chunk[4], length + 4);
+ if (CRC != checksum) return 1;
+ else return 0;
+}
+#endif
+
+static const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk, const unsigned char* end)
+{
+ if (chunk >= end || end - chunk < 12) return end; /*too small to contain a chunk*/
+ if (chunk[0] == 0x89 && chunk[1] == 0x50 && chunk[2] == 0x4e && chunk[3] == 0x47
+ && chunk[4] == 0x0d && chunk[5] == 0x0a && chunk[6] == 0x1a && chunk[7] == 0x0a) {
+ /* Is PNG magic header at start of PNG file. Jump to first actual chunk. */
+ return chunk + 8;
+ } else {
+ size_t total_chunk_length;
+ const unsigned char* result;
+ if (lodepng_addofl(lodepng_chunk_length(chunk), 12, &total_chunk_length)) return end;
+ result = chunk + total_chunk_length;
+ if (result < chunk) return end; /*pointer overflow*/
+ return result;
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / Color types, channels, bits / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+/*checks if the colortype is valid and the bitdepth bd is allowed for this colortype.
+Return value is a LodePNG error code.*/
+static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd)
+{
+ switch(colortype) {
+ case LCT_GREY: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break;
+ case LCT_RGB: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_PALETTE: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break;
+ case LCT_GREY_ALPHA: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_RGBA: if(!( bd == 8 || bd == 16)) return 37; break;
+ case LCT_MAX_OCTET_VALUE: return 31; /* invalid color type */
+ default: return 31; /* invalid color type */
+ }
+ return 0; /*allowed color type / bits combination*/
+}
+
+
+static unsigned getNumColorChannels(LodePNGColorType colortype)
+{
+ switch(colortype) {
+ case LCT_GREY: return 1;
+ case LCT_RGB: return 3;
+ case LCT_PALETTE: return 1;
+ case LCT_GREY_ALPHA: return 2;
+ case LCT_RGBA: return 4;
+ case LCT_MAX_OCTET_VALUE: return 0; /* invalid color type */
+ default: return 0; /*invalid color type*/
+ }
+}
+
+
+static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth)
+{
+ /*bits per pixel is amount of channels * bits per channel*/
+ return getNumColorChannels(colortype) * bitdepth;
+}
+
+
+static void lodepng_color_mode_init(LodePNGColorMode* info)
+{
+ info->key_defined = 0;
+ info->key_r = info->key_g = info->key_b = 0;
+ info->colortype = LCT_RGBA;
+ info->bitdepth = 8;
+ info->palette = 0;
+ info->palettesize = 0;
+}
+
+
+/*allocates palette memory if needed, and initializes all colors to black*/
+static void lodepng_color_mode_alloc_palette(LodePNGColorMode* info)
+{
+ size_t i;
+ /*if the palette is already allocated, it will have size 1024 so no reallocation needed in that case*/
+ /*the palette must have room for up to 256 colors with 4 bytes each.*/
+ if (!info->palette) info->palette = (unsigned char*)malloc(1024);
+ if (!info->palette) return; /*alloc fail*/
+ for (i = 0; i != 256; ++i) {
+ /*Initialize all unused colors with black, the value used for invalid palette indices.
+ This is an error according to the PNG spec, but common PNG decoders make it black instead.
+ That makes color conversion slightly faster due to no error handling needed.*/
+ info->palette[i * 4 + 0] = 0;
+ info->palette[i * 4 + 1] = 0;
+ info->palette[i * 4 + 2] = 0;
+ info->palette[i * 4 + 3] = 255;
+ }
+}
+
+static void lodepng_palette_clear(LodePNGColorMode* info)
+{
+ if (info->palette) free(info->palette);
+ info->palette = 0;
+ info->palettesize = 0;
+}
+
+
+static void lodepng_color_mode_cleanup(LodePNGColorMode* info)
+{
+ lodepng_palette_clear(info);
+}
+
+
+/*return value is error code (0 means no error)*/
+static unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source)
+{
+ lodepng_color_mode_cleanup(dest);
+ lodepng_memcpy(dest, source, sizeof(LodePNGColorMode));
+ if (source->palette) {
+ dest->palette = (unsigned char*)malloc(1024);
+ if (!dest->palette && source->palettesize) return 83; /*alloc fail*/
+ lodepng_memcpy(dest->palette, source->palette, source->palettesize * 4);
+ }
+ return 0;
+}
+
+
+static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b)
+{
+ size_t i;
+ if (a->colortype != b->colortype) return 0;
+ if (a->bitdepth != b->bitdepth) return 0;
+ if (a->key_defined != b->key_defined) return 0;
+ if (a->key_defined) {
+ if(a->key_r != b->key_r) return 0;
+ if(a->key_g != b->key_g) return 0;
+ if(a->key_b != b->key_b) return 0;
+ }
+ if (a->palettesize != b->palettesize) return 0;
+ for (i = 0; i != a->palettesize * 4; ++i) {
+ if (a->palette[i] != b->palette[i]) return 0;
+ }
+ return 1;
+}
+
+
+static size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth)
+{
+ size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth);
+ size_t n = (size_t)w * (size_t)h;
+ return ((n / 8u) * bpp) + ((n & 7u) * bpp + 7u) / 8u;
+}
+
+
+/* Returns the byte size of a raw image buffer with given width, height and color mode */
+static size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color)
+{
+ return lodepng_get_raw_size_lct(w, h, color->colortype, color->bitdepth);
+}
+
+
+/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer,
+and in addition has one extra byte per line: the filter byte. So this gives a larger
+result than lodepng_get_raw_size. Set h to 1 to get the size of 1 row including filter byte. */
+static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, unsigned bpp)
+{
+ /* + 1 for the filter byte, and possibly plus padding bits per line. */
+ /* Ignoring casts, the expression is equal to (w * bpp + 7) / 8 + 1, but avoids overflow of w * bpp */
+ size_t line = ((size_t)(w / 8u) * bpp) + 1u + ((w & 7u) * bpp + 7u) / 8u;
+ return (size_t)h * line;
+}
+
+
+/* Safely checks whether size_t overflow can be caused due to amount of pixels.
+ This check is overcautious rather than precise. If this check indicates no overflow,
+ you can safely compute in a size_t (but not an unsigned):
+ -(size_t)w * (size_t)h * 8
+ -amount of bytes in IDAT (including filter, padding and Adam7 bytes)
+ -amount of bytes in raw color model
+ Returns 1 if overflow possible, 0 if not. */
+static int lodepng_pixel_overflow(unsigned w, unsigned h, const LodePNGColorMode* pngcolor, const LodePNGColorMode* rawcolor)
+{
+ size_t bpp = LODEPNG_MAX(lodepng_get_bpp_lct(pngcolor->colortype, pngcolor->bitdepth), lodepng_get_bpp_lct(rawcolor->colortype, rawcolor->bitdepth));
+ size_t numpixels, total;
+ size_t line; /* bytes per line in worst case */
+
+ if (lodepng_mulofl((size_t)w, (size_t)h, &numpixels)) return 1;
+ if (lodepng_mulofl(numpixels, 8, &total)) return 1; /* bit pointer with 8-bit color, or 8 bytes per channel color */
+
+ /* Bytes per scanline with the expression "(w / 8u) * bpp) + ((w & 7u) * bpp + 7u) / 8u" */
+ if (lodepng_mulofl((size_t)(w / 8u), bpp, &line)) return 1;
+ if (lodepng_addofl(line, ((w & 7u) * bpp + 7u) / 8u, &line)) return 1;
+
+ if (lodepng_addofl(line, 5, &line)) return 1; /* 5 bytes overhead per line: 1 filterbyte, 4 for Adam7 worst case */
+ if (lodepng_mulofl(line, h, &total)) return 1; /* Total bytes in worst case */
+
+ return 0; /* no overflow */
+}
+
+
+static void lodepng_info_init(LodePNGInfo* info)
+{
+ lodepng_color_mode_init(&info->color);
+ info->interlace_method = 0;
+ info->compression_method = 0;
+ info->filter_method = 0;
+}
+
+
+static void lodepng_info_cleanup(LodePNGInfo* info)
+{
+ lodepng_color_mode_cleanup(&info->color);
+}
+
+
+/* index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to */
+static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in)
+{
+ unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/
+ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/
+ unsigned p = index & m;
+ in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/
+ in = in << (bits * (m - p));
+ if(p == 0) out[index * bits / 8u] = in;
+ else out[index * bits / 8u] |= in;
+}
+
+/*
+ One node of a color tree
+ This is the data structure used to count the number of unique colors and to get a palette
+ index for a color. It's like an octree, but because the alpha channel is used too, each
+ node has 16 instead of 8 children.
+*/
+struct ColorTree
+{
+ ColorTree* children[16]; /* up to 16 pointers to ColorTree of next level */
+ int index; /* the payload. Only has a meaningful value if this is in the last level */
+};
+
+static void color_tree_init(ColorTree* tree)
+{
+ lodepng_memset(tree->children, 0, 16 * sizeof(*tree->children));
+ tree->index = -1;
+}
+
+static void color_tree_cleanup(ColorTree* tree)
+{
+ int i;
+ for (i = 0; i != 16; ++i) {
+ if(tree->children[i]) {
+ color_tree_cleanup(tree->children[i]);
+ free(tree->children[i]);
+ }
+ }
+}
+
+
+/* returns -1 if color not present, its index otherwise */
+static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+ int bit = 0;
+ for (bit = 0; bit < 8; ++bit) {
+ int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
+ if (!tree->children[i]) return -1;
+ else tree = tree->children[i];
+ }
+ return tree ? tree->index : -1;
+}
+
+
+/* color is not allowed to already exist.
+ Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")
+ Returns error code, or 0 if ok */
+static unsigned color_tree_add(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index)
+{
+ int bit;
+ for (bit = 0; bit < 8; ++bit) {
+ int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1);
+ if (!tree->children[i]) {
+ tree->children[i] = (ColorTree*)malloc(sizeof(ColorTree));
+ if (!tree->children[i]) return 83; /*alloc fail*/
+ color_tree_init(tree->children[i]);
+ }
+ tree = tree->children[i];
+ }
+ tree->index = (int)index;
+ return 0;
+}
+
+/* put a pixel, given its RGBA color, into image of any color type */
+static unsigned rgba8ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
+{
+ if (mode->colortype == LCT_GREY) {
+ unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
+ if (mode->bitdepth == 8) out[i] = gray;
+ else if (mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = gray;
+ else {
+ /*take the most significant bits of gray*/
+ gray = ((unsigned)gray >> (8u - mode->bitdepth)) & ((1u << mode->bitdepth) - 1u);
+ addColorBits(out, i, mode->bitdepth, gray);
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ out[i * 3 + 0] = r;
+ out[i * 3 + 1] = g;
+ out[i * 3 + 2] = b;
+ } else {
+ out[i * 6 + 0] = out[i * 6 + 1] = r;
+ out[i * 6 + 2] = out[i * 6 + 3] = g;
+ out[i * 6 + 4] = out[i * 6 + 5] = b;
+ }
+ } else if(mode->colortype == LCT_PALETTE) {
+ int index = color_tree_get(tree, r, g, b, a);
+ if (index < 0) return 82; /*color not in palette*/
+ if (mode->bitdepth == 8) out[i] = index;
+ else addColorBits(out, i, mode->bitdepth, (unsigned)index);
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ unsigned char gray = r; /*((unsigned short)r + g + b) / 3u;*/
+ if (mode->bitdepth == 8) {
+ out[i * 2 + 0] = gray;
+ out[i * 2 + 1] = a;
+ } else if (mode->bitdepth == 16) {
+ out[i * 4 + 0] = out[i * 4 + 1] = gray;
+ out[i * 4 + 2] = out[i * 4 + 3] = a;
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ out[i * 4 + 0] = r;
+ out[i * 4 + 1] = g;
+ out[i * 4 + 2] = b;
+ out[i * 4 + 3] = a;
+ } else {
+ out[i * 8 + 0] = out[i * 8 + 1] = r;
+ out[i * 8 + 2] = out[i * 8 + 3] = g;
+ out[i * 8 + 4] = out[i * 8 + 5] = b;
+ out[i * 8 + 6] = out[i * 8 + 7] = a;
+ }
+ }
+ return 0; /*no error*/
+}
+
+
+/* put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type */
+static void rgba16ToPixel(unsigned char* out, size_t i, const LodePNGColorMode* mode, unsigned short r, unsigned short g, unsigned short b, unsigned short a)
+{
+ if (mode->colortype == LCT_GREY) {
+ unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
+ out[i * 2 + 0] = (gray >> 8) & 255;
+ out[i * 2 + 1] = gray & 255;
+ } else if (mode->colortype == LCT_RGB) {
+ out[i * 6 + 0] = (r >> 8) & 255;
+ out[i * 6 + 1] = r & 255;
+ out[i * 6 + 2] = (g >> 8) & 255;
+ out[i * 6 + 3] = g & 255;
+ out[i * 6 + 4] = (b >> 8) & 255;
+ out[i * 6 + 5] = b & 255;
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ unsigned short gray = r; /*((unsigned)r + g + b) / 3u;*/
+ out[i * 4 + 0] = (gray >> 8) & 255;
+ out[i * 4 + 1] = gray & 255;
+ out[i * 4 + 2] = (a >> 8) & 255;
+ out[i * 4 + 3] = a & 255;
+ } else if (mode->colortype == LCT_RGBA) {
+ out[i * 8 + 0] = (r >> 8) & 255;
+ out[i * 8 + 1] = r & 255;
+ out[i * 8 + 2] = (g >> 8) & 255;
+ out[i * 8 + 3] = g & 255;
+ out[i * 8 + 4] = (b >> 8) & 255;
+ out[i * 8 + 5] = b & 255;
+ out[i * 8 + 6] = (a >> 8) & 255;
+ out[i * 8 + 7] = a & 255;
+ }
+}
+
+
+/* Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type. */
+static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, unsigned char* b, unsigned char* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode)
+{
+ if (mode->colortype == LCT_GREY) {
+ if (mode->bitdepth == 8) {
+ *r = *g = *b = in[i];
+ if (mode->key_defined && *r == mode->key_r) *a = 0;
+ else *a = 255;
+ } else if (mode->bitdepth == 16) {
+ *r = *g = *b = in[i * 2 + 0];
+ if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
+ else *a = 255;
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
+ size_t j = i * mode->bitdepth;
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ *r = *g = *b = (value * 255) / highest;
+ if (mode->key_defined && value == mode->key_r) *a = 0;
+ else *a = 255;
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2];
+ if (mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0;
+ else *a = 255;
+ } else {
+ *r = in[i * 6 + 0];
+ *g = in[i * 6 + 2];
+ *b = in[i * 6 + 4];
+ if (mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
+ else *a = 255;
+ }
+ } else if (mode->colortype == LCT_PALETTE) {
+ unsigned index;
+ if (mode->bitdepth == 8) index = in[i];
+ else {
+ size_t j = i * mode->bitdepth;
+ index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ }
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ *r = mode->palette[index * 4 + 0];
+ *g = mode->palette[index * 4 + 1];
+ *b = mode->palette[index * 4 + 2];
+ *a = mode->palette[index * 4 + 3];
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ if (mode->bitdepth == 8) {
+ *r = *g = *b = in[i * 2 + 0];
+ *a = in[i * 2 + 1];
+ } else {
+ *r = *g = *b = in[i * 4 + 0];
+ *a = in[i * 4 + 2];
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ *r = in[i * 4 + 0];
+ *g = in[i * 4 + 1];
+ *b = in[i * 4 + 2];
+ *a = in[i * 4 + 3];
+ } else {
+ *r = in[i * 8 + 0];
+ *g = in[i * 8 + 2];
+ *b = in[i * 8 + 4];
+ *a = in[i * 8 + 6];
+ }
+ }
+}
+
+
+/* Similar to getPixelColorRGBA8, but with all the for loops inside of the color
+ mode test cases, optimized to convert the colors much faster, when converting
+ to the common case of RGBA with 8 bit per channel. buffer must be RGBA with
+ enough memory.*/
+static void getPixelColorsRGBA8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode)
+{
+ unsigned num_channels = 4;
+ size_t i;
+ if (mode->colortype == LCT_GREY) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i];
+ buffer[3] = 255;
+ }
+ if (mode->key_defined) {
+ buffer -= numpixels * num_channels;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ if(buffer[0] == mode->key_r) buffer[3] = 0;
+ }
+ }
+ } else if (mode->bitdepth == 16) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2];
+ buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255;
+ }
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
+ buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255;
+ }
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ //lodepng_memcpy(buffer, &in[i * 3], 3);
+ //Convert colortype to LCT_BGR?
+ buffer[0] = in[i * 3 + 2];
+ buffer[1] = in[i * 3 + 1];
+ buffer[2] = in[i * 3 + 0];
+ buffer[3] = 255;
+ }
+ if (mode->key_defined) {
+ buffer -= numpixels * num_channels;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ if (buffer[0] == mode->key_r && buffer[1]== mode->key_g && buffer[2] == mode->key_b) buffer[3] = 0;
+ }
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 6 + 0];
+ buffer[1] = in[i * 6 + 2];
+ buffer[2] = in[i * 6 + 4];
+ buffer[3] = mode->key_defined
+ && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255;
+ }
+ }
+ } else if (mode->colortype == LCT_PALETTE) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = in[i];
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
+ }
+ } else {
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 4);
+ }
+ }
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
+ buffer[3] = in[i * 2 + 1];
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
+ buffer[3] = in[i * 4 + 2];
+ }
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ lodepng_memcpy(buffer, in, numpixels * 4);
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 8 + 0];
+ buffer[1] = in[i * 8 + 2];
+ buffer[2] = in[i * 8 + 4];
+ buffer[3] = in[i * 8 + 6];
+ }
+ }
+ }
+}
+
+
+/* Similar to getPixelColorsRGBA8, but with 3-channel RGB output. */
+static void getPixelColorsRGB8(unsigned char* LODEPNG_RESTRICT buffer, size_t numpixels, const unsigned char* LODEPNG_RESTRICT in, const LodePNGColorMode* mode)
+{
+ const unsigned num_channels = 3;
+ size_t i;
+ if (mode->colortype == LCT_GREY) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i];
+ }
+ } else if (mode->bitdepth == 16) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2];
+ }
+ } else {
+ unsigned highest = ((1U << mode->bitdepth) - 1U); /* highest possible value for this bit depth */
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest;
+ }
+ }
+ } else if (mode->colortype == LCT_RGB) {
+ if (mode->bitdepth == 8) {
+ lodepng_memcpy(buffer, in, numpixels * 3);
+ } else {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 6 + 0];
+ buffer[1] = in[i * 6 + 2];
+ buffer[2] = in[i * 6 + 4];
+ }
+ }
+ } else if (mode->colortype == LCT_PALETTE) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = in[i];
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
+ }
+ } else {
+ size_t j = 0;
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ unsigned index = readBitsFromReversedStream(&j, in, mode->bitdepth);
+ /* out of bounds of palette not checked: see lodepng_color_mode_alloc_palette. */
+ lodepng_memcpy(buffer, &mode->palette[index * 4], 3);
+ }
+ }
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ if (mode->bitdepth == 8) {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0];
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0];
+ }
+ }
+ } else if (mode->colortype == LCT_RGBA) {
+ if (mode->bitdepth == 8) {
+ for(i = 0; i != numpixels; ++i, buffer += num_channels) {
+ lodepng_memcpy(buffer, &in[i * 4], 3);
+ }
+ } else {
+ for (i = 0; i != numpixels; ++i, buffer += num_channels) {
+ buffer[0] = in[i * 8 + 0];
+ buffer[1] = in[i * 8 + 2];
+ buffer[2] = in[i * 8 + 4];
+ }
+ }
+ }
+}
+
+
+/* Get RGBA16 color of pixel with index i (y * width + x) from the raw image with
+ given color type, but the given color type must be 16-bit itself. */
+static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, const unsigned char* in, size_t i, const LodePNGColorMode* mode)
+{
+ if (mode->colortype == LCT_GREY) {
+ *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1];
+ if (mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0;
+ else *a = 65535;
+ } else if (mode->colortype == LCT_RGB) {
+ *r = 256u * in[i * 6 + 0] + in[i * 6 + 1];
+ *g = 256u * in[i * 6 + 2] + in[i * 6 + 3];
+ *b = 256u * in[i * 6 + 4] + in[i * 6 + 5];
+ if (mode->key_defined
+ && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r
+ && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g
+ && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0;
+ else *a = 65535;
+ } else if (mode->colortype == LCT_GREY_ALPHA) {
+ *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1];
+ *a = 256u * in[i * 4 + 2] + in[i * 4 + 3];
+ } else if (mode->colortype == LCT_RGBA) {
+ *r = 256u * in[i * 8 + 0] + in[i * 8 + 1];
+ *g = 256u * in[i * 8 + 2] + in[i * 8 + 3];
+ *b = 256u * in[i * 8 + 4] + in[i * 8 + 5];
+ *a = 256u * in[i * 8 + 6] + in[i * 8 + 7];
+ }
+}
+
+/*
+ Converts raw buffer from one color type to another color type, based on
+ LodePNGColorMode structs to describe the input and output color type.
+ See the reference manual at the end of this header file to see which color conversions are supported.
+ return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported)
+ The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel
+ of the output color type (lodepng_get_bpp).
+ For < 8 bpp images, there should not be padding bits at the end of scanlines.
+ For 16-bit per channel colors, uses big endian format like PNG does.
+ Return value is LodePNG error code
+*/
+static unsigned lodepng_convert(unsigned char* out, const unsigned char* in, const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, unsigned w, unsigned h)
+{
+ size_t i;
+ ColorTree tree;
+ size_t numpixels = (size_t)w * (size_t)h;
+ unsigned error = 0;
+
+ if (mode_in->colortype == LCT_PALETTE && !mode_in->palette) {
+ return 107; /* error: must provide palette if input mode is palette */
+ }
+
+ if (lodepng_color_mode_equal(mode_out, mode_in)) {
+ size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
+ lodepng_memcpy(out, in, numbytes);
+ return 0;
+ }
+
+ if (mode_out->colortype == LCT_PALETTE) {
+ size_t palettesize = mode_out->palettesize;
+ const unsigned char* palette = mode_out->palette;
+ size_t palsize = (size_t)1u << mode_out->bitdepth;
+ /* if the user specified output palette but did not give the values, assume
+ they want the values of the input color type (assuming that one is palette).
+ Note that we never create a new palette ourselves.*/
+ if (palettesize == 0) {
+ palettesize = mode_in->palettesize;
+ palette = mode_in->palette;
+ /* if the input was also palette with same bitdepth, then the color types are also
+ equal, so copy literally. This to preserve the exact indices that were in the PNG
+ even in case there are duplicate colors in the palette.*/
+ if (mode_in->colortype == LCT_PALETTE && mode_in->bitdepth == mode_out->bitdepth) {
+ size_t numbytes = lodepng_get_raw_size(w, h, mode_in);
+ lodepng_memcpy(out, in, numbytes);
+ return 0;
+ }
+ }
+ if (palettesize < palsize) palsize = palettesize;
+ color_tree_init(&tree);
+ for (i = 0; i != palsize; ++i) {
+ const unsigned char* p = &palette[i * 4];
+ error = color_tree_add(&tree, p[0], p[1], p[2], p[3], (unsigned)i);
+ if (error) break;
+ }
+ }
+
+ if (!error) {
+ if (mode_in->bitdepth == 16 && mode_out->bitdepth == 16) {
+ for (i = 0; i != numpixels; ++i) {
+ unsigned short r = 0, g = 0, b = 0, a = 0;
+ getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in);
+ rgba16ToPixel(out, i, mode_out, r, g, b, a);
+ }
+ } else if (mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) {
+ getPixelColorsRGBA8(out, numpixels, in, mode_in);
+ } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) {
+ getPixelColorsRGB8(out, numpixels, in, mode_in);
+ } else {
+ unsigned char r = 0, g = 0, b = 0, a = 0;
+ for (i = 0; i != numpixels; ++i) {
+ getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in);
+ error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a);
+ if (error) break;
+ }
+ }
+ }
+
+ if (mode_out->colortype == LCT_PALETTE) {
+ color_tree_cleanup(&tree);
+ }
+
+ return error;
+}
+
+
+/* Paeth predictor, used by PNG filter type 4
+ The parameters are of type short, but should come from unsigned chars, the shorts
+ are only needed to make the paeth calculation correct.
+*/
+static unsigned char paethPredictor(short a, short b, short c)
+{
+ short pa = LODEPNG_ABS(b - c);
+ short pb = LODEPNG_ABS(a - c);
+ short pc = LODEPNG_ABS(a + b - c - c);
+ /* return input value associated with smallest of pa, pb, pc (with certain priority if equal) */
+ if (pb < pa) { a = b; pa = pb; }
+ return (pc < pa) ? c : a;
+}
+
+
+/*shared values used by multiple Adam7 related functions*/
+static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/
+static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/
+static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/
+static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/
+
+/* Outputs various dimensions and positions in the image related to the Adam7 reduced images.
+ passw: output containing the width of the 7 passes
+ passh: output containing the height of the 7 passes
+ filter_passstart: output containing the index of the start and end of each
+ reduced image with filter bytes
+ padded_passstart output containing the index of the start and end of each
+ reduced image when without filter bytes but with padded scanlines
+ passstart: output containing the index of the start and end of each reduced
+ image without padding between scanlines, but still padding between the images
+ w, h: width and height of non-interlaced image
+ bpp: bits per pixel
+ "padded" is only relevant if bpp is less than 8 and a scanline or image does not
+ end at a full byte */
+static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp)
+{
+ /* the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass */
+ unsigned i;
+
+ /* calculate width and height in pixels of each pass */
+ for (i = 0; i != 7; ++i) {
+ passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i];
+ passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i];
+ if(passw[i] == 0) passh[i] = 0;
+ if(passh[i] == 0) passw[i] = 0;
+ }
+
+ filter_passstart[0] = padded_passstart[0] = passstart[0] = 0;
+ for (i = 0; i != 7; ++i) {
+ /* if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte) */
+ filter_passstart[i + 1] = filter_passstart[i]
+ + ((passw[i] && passh[i]) ? passh[i] * (1u + (passw[i] * bpp + 7u) / 8u) : 0);
+ /* bits padded if needed to fill full byte at end of each scanline */
+ padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7u) / 8u);
+ /* only padded at end of reduced image */
+ passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7u) / 8u;
+ }
+}
+
+
+/* ////////////////////////////////////////////////////////////////////////// */
+/* / PNG Decoder / */
+/* ////////////////////////////////////////////////////////////////////////// */
+
+static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, size_t bytewidth, unsigned char filterType, size_t length)
+{
+ /* For PNG filter method 0
+ unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte,
+ the filter works byte per byte (bytewidth = 1)
+ precon is the previous unfiltered scanline, recon the result, scanline the current one
+ the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead
+ recon and scanline MAY be the same memory address! precon must be disjoint. */
+
+ size_t i;
+ switch (filterType) {
+ case 0: {
+ if (bytewidth == 4) {
+ for (i = 0; i < length; i += 4) {
+ //RGBA -> BGRA
+ recon[i + 0] = scanline[i + 2];
+ recon[i + 1] = scanline[i + 1];
+ recon[i + 2] = scanline[i + 0];
+ recon[i + 3] = scanline[i + 3];
+ }
+ } else {
+ for (i = 0; i != length; ++i) recon[i] = scanline[i];
+ }
+ break;
+ }
+ case 1: {
+ for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
+ for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth];
+ break;
+ }
+ case 2: {
+ if (precon) {
+ for (i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i];
+ } else {
+ for (i = 0; i != length; ++i) recon[i] = scanline[i];
+ }
+ break;
+ }
+ case 3: {
+ if (precon) {
+ for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1u);
+ for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1u);
+ } else {
+ for (i = 0; i != bytewidth; ++i) recon[i] = scanline[i];
+ for (i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1u);
+ }
+ break;
+ }
+ case 4: {
+ if (precon) {
+ for (i = 0; i != bytewidth; ++i) {
+ recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/
+ }
+
+ /* Unroll independent paths of the paeth predictor. A 6x and 8x version would also be possible but that
+ adds too much code. Whether this actually speeds anything up at all depends on compiler and settings. */
+ if (bytewidth >= 4) {
+ for (; i + 3 < length; i += 4) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2], s3 = scanline[i + 3];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2], r3 = recon[j + 3];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2], p3 = precon[i + 3];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2], q3 = precon[j + 3];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
+ recon[i + 3] = s3 + paethPredictor(r3, p3, q3);
+ }
+ } else if (bytewidth >= 3) {
+ for (; i + 2 < length; i += 3) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1], s2 = scanline[i + 2];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1], r2 = recon[j + 2];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1], p2 = precon[i + 2];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1], q2 = precon[j + 2];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ recon[i + 2] = s2 + paethPredictor(r2, p2, q2);
+ }
+ } else if (bytewidth >= 2) {
+ for (; i + 1 < length; i += 2) {
+ size_t j = i - bytewidth;
+ unsigned char s0 = scanline[i + 0], s1 = scanline[i + 1];
+ unsigned char r0 = recon[j + 0], r1 = recon[j + 1];
+ unsigned char p0 = precon[i + 0], p1 = precon[i + 1];
+ unsigned char q0 = precon[j + 0], q1 = precon[j + 1];
+ recon[i + 0] = s0 + paethPredictor(r0, p0, q0);
+ recon[i + 1] = s1 + paethPredictor(r1, p1, q1);
+ }
+ }
+
+ for (; i != length; ++i) {
+ recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth]));
+ }
+ } else {
+ for (i = 0; i != bytewidth; ++i) {
+ recon[i] = scanline[i];
+ }
+ for (i = bytewidth; i < length; ++i) {
+ /* paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth] */
+ recon[i] = (scanline[i] + recon[i - bytewidth]);
+ }
+ }
+ break;
+ }
+ default: return 36; /* error: invalid filter type given */
+ }
+ return 0;
+}
+
+
+static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
+{
+ /* For PNG filter method 0
+ this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times)
+ out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline
+ w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel
+ in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) */
+
+ unsigned y;
+ unsigned char* prevline = 0;
+
+ /* bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise */
+ size_t bytewidth = (bpp + 7u) / 8u;
+ /* the width of a scanline in bytes, not including the filter type */
+ size_t linebytes = lodepng_get_raw_size_idat(w, 1, bpp) - 1u;
+
+ for (y = 0; y < h; ++y) {
+ size_t outindex = linebytes * y;
+ size_t inindex = (1 + linebytes) * y; /* the extra filterbyte added to each row */
+ unsigned char filterType = in[inindex];
+ CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes));
+ prevline = &out[outindex];
+ }
+
+ return 0;
+}
+
+/* in: Adam7 interlaced image, with no padding bits between scanlines, but between
+ reduced images so that each reduced image starts at a byte.
+ out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h
+ bpp: bits per pixel
+ out has the following size in bits: w * h * bpp.
+ in is possibly bigger due to padding bits between reduced images.
+ out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation
+ (because that's likely a little bit faster)
+ NOTE: comments about padding bits are only relevant if bpp < 8 */
+static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp)
+{
+ unsigned passw[7], passh[7];
+ size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned i;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ if (bpp >= 8) {
+ for(i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ size_t bytewidth = bpp / 8u;
+ for (y = 0; y < passh[i]; ++y)
+ for (x = 0; x < passw[i]; ++x) {
+ size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth;
+ size_t pixeloutstart = ((ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * (size_t)w + ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bytewidth;
+ for (b = 0; b < bytewidth; ++b) {
+ out[pixeloutstart + b] = in[pixelinstart + b];
+ }
+ }
+ }
+ } else /* bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers */ {
+ for (i = 0; i != 7; ++i) {
+ unsigned x, y, b;
+ unsigned ilinebits = bpp * passw[i];
+ unsigned olinebits = bpp * w;
+ size_t obp, ibp; /* bit pointers (for out and in buffer) */
+ for (y = 0; y < passh[i]; ++y)
+ for (x = 0; x < passw[i]; ++x) {
+ ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp);
+ obp = (ADAM7_IY[i] + (size_t)y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + (size_t)x * ADAM7_DX[i]) * bpp;
+ for (b = 0; b < bpp; ++b) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ }
+ }
+ }
+}
+
+
+static void removePaddingBits(unsigned char* out, const unsigned char* in, size_t olinebits, size_t ilinebits, unsigned h)
+{
+ /* After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need
+ to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers
+ for the Adam7 code, the color convert code and the output to the user.
+ in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must
+ have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits
+ also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7
+ only useful if (ilinebits - olinebits) is a value in the range 1..7 */
+ unsigned y;
+ size_t diff = ilinebits - olinebits;
+ size_t ibp = 0, obp = 0; /*input and output bit pointers*/
+ for (y = 0; y < h; ++y) {
+ size_t x;
+ for (x = 0; x < olinebits; ++x) {
+ unsigned char bit = readBitFromReversedStream(&ibp, in);
+ setBitOfReversedStream(&obp, out, bit);
+ }
+ ibp += diff;
+ }
+}
+
+
+/* out must be buffer big enough to contain full image, and in must contain the full decompressed data from
+ the IDAT chunks (with filter index bytes and possible padding bits)
+ return value is error */
+static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, unsigned w, unsigned h, const LodePNGInfo* info_png)
+{
+ /* This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype.
+ Steps:
+ *) if no Adam7: 1) unfilter 2) remove padding bits (= possible extra bits per scanline if bpp < 8)
+ *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace
+ NOTE: the in buffer will be overwritten with intermediate data! */
+ unsigned bpp = lodepng_get_bpp_lct(info_png->color.colortype, info_png->color.bitdepth);
+ if (bpp == 0) return 31; /* error: invalid colortype */
+
+ if (info_png->interlace_method == 0) {
+ if (bpp < 8 && w * bpp != ((w * bpp + 7u) / 8u) * 8u) {
+ CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp));
+ removePaddingBits(out, in, w * bpp, ((w * bpp + 7u) / 8u) * 8u, h);
+ }
+ /* we can immediately filter into the out buffer, no other steps needed */
+ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp));
+ } else /* interlace_method is 1 (Adam7) */ {
+ unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8];
+ unsigned i;
+
+ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp);
+
+ for (i = 0; i != 7; ++i) {
+ CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp));
+ /* TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline,
+ move bytes instead of bits or move not at all */
+ if (bpp < 8) {
+ /* remove padding bits in scanlines; after this there still may be padding
+ bits between the different reduced images: each reduced image still starts nicely at a byte */
+ removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, ((passw[i] * bpp + 7u) / 8u) * 8u, passh[i]);
+ }
+ }
+ Adam7_deinterlace(out, in, w, h, bpp);
+ }
+ return 0;
+}
+
+
+static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength)
+{
+ unsigned pos = 0, i;
+ color->palettesize = chunkLength / 3u;
+ if (color->palettesize == 0 || color->palettesize > 256) return 38; /* error: palette too small or big */
+ lodepng_color_mode_alloc_palette(color);
+ if (!color->palette && color->palettesize) {
+ color->palettesize = 0;
+ return 83; /* alloc fail */
+ }
+
+ for (i = 0; i != color->palettesize; ++i) {
+ color->palette[4 * i + 0] = data[pos++]; /*R*/
+ color->palette[4 * i + 1] = data[pos++]; /*G*/
+ color->palette[4 * i + 2] = data[pos++]; /*B*/
+ color->palette[4 * i + 3] = 255; /*alpha*/
+ }
+
+ return 0; /* OK */
+}
+
+
+static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength)
+{
+ unsigned i;
+ if (color->colortype == LCT_PALETTE) {
+ /* error: more alpha values given than there are palette entries */
+ if (chunkLength > color->palettesize) return 39;
+
+ for (i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i];
+ } else if (color->colortype == LCT_GREY) {
+ /* error: this chunk must be 2 bytes for grayscale image */
+ if (chunkLength != 2) return 30;
+
+ color->key_defined = 1;
+ color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1];
+ } else if (color->colortype == LCT_RGB) {
+ /* error: this chunk must be 6 bytes for RGB image */
+ if (chunkLength != 6) return 41;
+
+ color->key_defined = 1;
+ color->key_r = 256u * data[0] + data[1];
+ color->key_g = 256u * data[2] + data[3];
+ color->key_b = 256u * data[4] + data[5];
+ }
+ else return 42; /* error: tRNS chunk not allowed for other color models */
+
+ return 0; /* OK */
+}
+
+
+/* read a PNG, the result will be in the same color type as the PNG (hence "generic") */
+static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
+{
+ unsigned char IEND = 0;
+ const unsigned char* chunk;
+ unsigned char* idat; /*the data from idat chunks, zlib compressed*/
+ size_t idatsize = 0;
+ unsigned char* scanlines = 0;
+ size_t scanlines_size = 0, expected_size = 0;
+ size_t outsize = 0;
+
+ /* safe output values in case error happens */
+ *out = 0;
+ *w = *h = 0;
+
+ state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/
+ if (state->error) return;
+
+ if (lodepng_pixel_overflow(*w, *h, &state->info_png.color, &state->info_raw)) {
+ CERROR_RETURN(state->error, 92); /*overflow possible due to amount of pixels*/
+ }
+
+ /*the input filesize is a safe upper bound for the sum of idat chunks size*/
+ idat = (unsigned char*)malloc(insize);
+ if (!idat) CERROR_RETURN(state->error, 83); /*alloc fail*/
+
+ chunk = &in[33]; /*first byte of the first chunk after the header*/
+
+ /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk.
+ IDAT data is put at the start of the in buffer*/
+ while (!IEND && !state->error) {
+ unsigned chunkLength;
+ const unsigned char* data; /*the data in the chunk*/
+
+ /*error: size of the in buffer too small to contain next chunk*/
+ if ((size_t)((chunk - in) + 12) > insize || chunk < in) {
+ if (state->decoder.ignore_end) break; /*other errors may still happen though*/
+ CERROR_BREAK(state->error, 30);
+ }
+
+ /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/
+ chunkLength = lodepng_chunk_length(chunk);
+ /*error: chunk length larger than the max PNG chunk size*/
+ if (chunkLength > 2147483647) {
+ if (state->decoder.ignore_end) break; /*other errors may still happen though*/
+ CERROR_BREAK(state->error, 63);
+ }
+
+ if ((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) {
+ CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/
+ }
+
+ data = lodepng_chunk_data_const(chunk);
+
+ /*for unknown chunk order*/
+ //unsigned unknown = 0;
+
+ /*IDAT chunk, containing compressed image data*/
+ if (lodepng_chunk_type_equals(chunk, "IDAT")) {
+ size_t newsize;
+ if (lodepng_addofl(idatsize, chunkLength, &newsize)) CERROR_BREAK(state->error, 95);
+ if (newsize > insize) CERROR_BREAK(state->error, 95);
+ lodepng_memcpy(idat + idatsize, data, chunkLength);
+ idatsize += chunkLength;
+ } else if (lodepng_chunk_type_equals(chunk, "IEND")) {
+ /*IEND chunk*/
+ IEND = 1;
+ } else if (lodepng_chunk_type_equals(chunk, "PLTE")) {
+ /*palette chunk (PLTE)*/
+ state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength);
+ if (state->error) break;
+ } else if (lodepng_chunk_type_equals(chunk, "tRNS")) {
+ /*palette transparency chunk (tRNS). Even though this one is an ancillary chunk , it is still compiled
+ in without 'LODEPNG_COMPILE_ANCILLARY_CHUNKS' because it contains essential color information that
+ affects the alpha channel of pixels. */
+ state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength);
+ if (state->error) break;
+ } else /*it's not an implemented chunk type, so ignore it: skip over the data*/ {
+ /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/
+ if (!state->decoder.ignore_critical && !lodepng_chunk_ancillary(chunk)) {
+ CERROR_BREAK(state->error, 69);
+ }
+ //unknown = 1;
+ }
+
+#if 0 //We don't use CRC
+ if (!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ {
+ if (lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/
+ }
+#endif
+ if (!IEND) chunk = lodepng_chunk_next_const(chunk, in + insize);
+ }
+
+ if (state->info_png.color.colortype == LCT_PALETTE && !state->info_png.color.palette) {
+ state->error = 106; /* error: PNG file must have PLTE chunk if color type is palette */
+ }
+
+ if (!state->error) {
+ /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation.
+ If the decompressed size does not match the prediction, the image must be corrupt.*/
+ if (state->info_png.interlace_method == 0) {
+ size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth);
+ expected_size = lodepng_get_raw_size_idat(*w, *h, bpp);
+ } else {
+ size_t bpp = lodepng_get_bpp_lct(state->info_png.color.colortype, state->info_png.color.bitdepth);
+ /*Adam-7 interlaced: expected size is the sum of the 7 sub-images sizes*/
+ expected_size = 0;
+ expected_size += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, bpp);
+ if (*w > 4) expected_size += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, bpp);
+ if (*w > 2) expected_size += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, bpp);
+ if (*w > 1) expected_size += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, bpp);
+ expected_size += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, bpp);
+ }
+ state->error = zlib_decompress(&scanlines, &scanlines_size, expected_size, idat, idatsize, &state->decoder.zlibsettings);
+ }
+
+ if (!state->error && scanlines_size != expected_size) state->error = 91; /*decompressed size doesn't match prediction*/
+ free(idat);
+
+ if (!state->error) {
+ outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color);
+ *out = (unsigned char*)malloc(outsize);
+ if (!*out) state->error = 83; /*alloc fail*/
+ }
+ if (!state->error) {
+ lodepng_memset(*out, 0, outsize);
+ state->error = postProcessScanlines(*out, scanlines, *w, *h, &state->info_png);
+ }
+ free(scanlines);
+}
+
+
+static void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings)
+{
+ settings->color_convert = 1;
+ settings->ignore_crc = 0;
+ settings->ignore_critical = 0;
+ settings->ignore_end = 0;
+ lodepng_decompress_settings_init(&settings->zlibsettings);
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+/*read the information from the header and store it in the LodePNGInfo. return value is error*/
+unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
+{
+ unsigned width, height;
+ LodePNGInfo* info = &state->info_png;
+ if (insize == 0 || in == 0) {
+ CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/
+ }
+ if (insize < 33) {
+ CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/
+ }
+
+ /* when decoding a new PNG image, make sure all parameters created after previous decoding are reset */
+ /* TODO: remove this. One should use a new LodePNGState for new sessions */
+ lodepng_info_cleanup(info);
+ lodepng_info_init(info);
+
+ if (in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) {
+ CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/
+ }
+ if (lodepng_chunk_length(in + 8) != 13) {
+ CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/
+ }
+ if (!lodepng_chunk_type_equals(in + 8, "IHDR")) {
+ CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/
+ }
+
+ /*read the values given in the header*/
+ width = lodepng_read32bitInt(&in[16]);
+ height = lodepng_read32bitInt(&in[20]);
+ /*TODO: remove the undocumented feature that allows to give null pointers to width or height*/
+ if (w) *w = width;
+ if (h) *h = height;
+ info->color.bitdepth = in[24];
+ info->color.colortype = (LodePNGColorType)in[25];
+ info->compression_method = in[26];
+ info->filter_method = in[27];
+ info->interlace_method = in[28];
+
+ /*errors returned only after the parsing so other values are still output*/
+
+ /*error: invalid image size*/
+ if (width == 0 || height == 0) CERROR_RETURN_ERROR(state->error, 93);
+ /*error: invalid colortype or bitdepth combination*/
+ state->error = checkColorValidity(info->color.colortype, info->color.bitdepth);
+ if (state->error) return state->error;
+ /*error: only compression method 0 is allowed in the specification*/
+ if (info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32);
+ /*error: only filter method 0 is allowed in the specification*/
+ if (info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33);
+ /*error: only interlace methods 0 and 1 exist in the specification*/
+ if (info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34);
+
+#if 0 //thorvg don't use crc
+ if (!state->decoder.ignore_crc) {
+ unsigned CRC = lodepng_read32bitInt(&in[29]);
+ unsigned checksum = lodepng_crc32(&in[12], 17);
+ if (CRC != checksum) {
+ CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/
+ }
+ }
+#endif
+ return state->error;
+}
+
+
+unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize)
+{
+ *out = 0;
+ decodeGeneric(out, w, h, state, in, insize);
+ if (state->error) return state->error;
+ if (!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) {
+ /*same color type, no copying or converting of data needed*/
+ /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype
+ the raw image has to the end user*/
+ if (!state->decoder.color_convert) {
+ state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color);
+ if (state->error) return state->error;
+ }
+ } else { /*color conversion needed*/
+ unsigned char* data = *out;
+ size_t outsize;
+
+ /*TODO: check if this works according to the statement in the documentation: "The converter can convert
+ from grayscale input color type, to 8-bit grayscale or grayscale with alpha"*/
+ if (!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) && !(state->info_raw.bitdepth == 8)) {
+ return 56; /*unsupported color mode conversion*/
+ }
+
+ outsize = lodepng_get_raw_size(*w, *h, &state->info_raw);
+ *out = (unsigned char*)malloc(outsize);
+ if (!(*out)) {
+ state->error = 83; /*alloc fail*/
+ }
+ else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h);
+ free(data);
+ }
+ return state->error;
+}
+
+
+void lodepng_state_init(LodePNGState* state)
+{
+ lodepng_decoder_settings_init(&state->decoder);
+ lodepng_color_mode_init(&state->info_raw);
+ lodepng_info_init(&state->info_png);
+ state->error = 1;
+}
+
+
+void lodepng_state_cleanup(LodePNGState* state)
+{
+ lodepng_color_mode_cleanup(&state->info_raw);
+ lodepng_info_cleanup(&state->info_png);
+} \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.h b/thirdparty/thorvg/src/loaders/png/tvgLodePng.h
new file mode 100644
index 0000000000..c9f0e56ab6
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgLodePng.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+/*
+ LodePNG version 20200306
+
+ Copyright (c) 2005-2020 Lode Vandevenne
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+*/
+
+#ifndef _TVG_LODEPNG_H_
+#define _TVG_LODEPNG_H_
+
+#include <stddef.h>
+
+/*The PNG color types (also used for raw image).*/
+enum LodePNGColorType
+{
+ LCT_GREY = 0, /*grayscale: 1,2,4,8,16 bit*/
+ LCT_RGB = 2, /*RGB: 8,16 bit*/
+ LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/
+ LCT_GREY_ALPHA = 4, /*grayscale with alpha: 8,16 bit*/
+ LCT_RGBA = 6, /*RGB with alpha: 8,16 bit*/
+ /*LCT_MAX_OCTET_VALUE lets the compiler allow this enum to represent any invalid
+ byte value from 0 to 255 that could be present in an invalid PNG file header. Do
+ not use, compare with or set the name LCT_MAX_OCTET_VALUE, instead either use
+ the valid color type names above, or numeric values like 1 or 7 when checking for
+ particular disallowed color type byte values, or cast to integer to print it.*/
+ LCT_MAX_OCTET_VALUE = 255
+};
+
+/*Settings for zlib decompression*/
+struct LodePNGDecompressSettings
+{
+ /* Check LodePNGDecoderSettings for more ignorable errors such as ignore_crc */
+ unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/
+ unsigned ignore_nlen; /*ignore complement of len checksum in uncompressed blocks*/
+
+ /*use custom zlib decoder instead of built in one (default: null)*/
+ unsigned (*custom_zlib)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
+ /*use custom deflate decoder instead of built in one (default: null) if custom_zlib is not null, custom_inflate is ignored (the zlib format uses deflate)*/
+ unsigned (*custom_inflate)(unsigned char**, size_t*, const unsigned char*, size_t, const LodePNGDecompressSettings*);
+
+ const void* custom_context; /*optional custom settings for custom functions*/
+};
+
+/*
+ Color mode of an image. Contains all information required to decode the pixel
+ bits to RGBA colors. This information is the same as used in the PNG file
+ format, and is used both for PNG and raw image data in LodePNG.
+*/
+struct LodePNGColorMode
+{
+ /*header (IHDR)*/
+ LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/
+ unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/
+
+ /*
+ palette (PLTE and tRNS)
+
+ Dynamically allocated with the colors of the palette, including alpha.
+ This field may not be allocated directly, use lodepng_color_mode_init first,
+ then lodepng_palette_add per color to correctly initialize it (to ensure size
+ of exactly 1024 bytes).
+
+ The alpha channels must be set as well, set them to 255 for opaque images.
+
+ When decoding, by default you can ignore this palette, since LodePNG already
+ fills the palette colors in the pixels of the raw RGBA output.
+
+ The palette is only supported for color type 3.
+ */
+ unsigned char* palette; /*palette in RGBARGBA... order. Must be either 0, or when allocated must have 1024 bytes*/
+ size_t palettesize; /*palette size in number of colors (amount of used bytes is 4 * palettesize)*/
+
+ /*
+ transparent color key (tRNS)
+
+ This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit.
+ For grayscale PNGs, r, g and b will all 3 be set to the same.
+
+ When decoding, by default you can ignore this information, since LodePNG sets
+ pixels with this key to transparent already in the raw RGBA output.
+
+ The color key is only supported for color types 0 and 2.
+ */
+ unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/
+ unsigned key_r; /*red/grayscale component of color key*/
+ unsigned key_g; /*green component of color key*/
+ unsigned key_b; /*blue component of color key*/
+};
+
+/*Information about the PNG image, except pixels, width and height.*/
+struct LodePNGInfo
+{
+ /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/
+ unsigned compression_method;/*compression method of the original file. Always 0.*/
+ unsigned filter_method; /*filter method of the original file*/
+ unsigned interlace_method; /*interlace method of the original file: 0=none, 1=Adam7*/
+ LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/
+};
+
+/*
+ Settings for the decoder. This contains settings for the PNG and the Zlib
+ decoder, but not the Info settings from the Info structs.
+*/
+struct LodePNGDecoderSettings
+{
+ LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/
+
+ /* Check LodePNGDecompressSettings for more ignorable errors such as ignore_adler32 */
+ unsigned ignore_crc; /*ignore CRC checksums*/
+ unsigned ignore_critical; /*ignore unknown critical chunks*/
+ unsigned ignore_end; /*ignore issues at end of file if possible (missing IEND chunk, too large chunk, ...)*/
+ /* TODO: make a system involving warnings with levels and a strict mode instead. Other potentially recoverable
+ errors: srgb rendering intent value, size of content of ancillary chunks, more than 79 characters for some
+ strings, placement/combination rules for ancillary chunks, crc of unknown chunks, allowed characters
+ in string keys, etc... */
+
+ unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/
+};
+
+/*The settings, state and information for extended encoding and decoding.*/
+struct LodePNGState
+{
+ LodePNGDecoderSettings decoder; /*the decoding settings*/
+ LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/
+ LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/
+ unsigned error;
+};
+
+void lodepng_state_init(LodePNGState* state);
+void lodepng_state_cleanup(LodePNGState* state);
+unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
+unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, const unsigned char* in, size_t insize);
+
+#endif //_TVG_LODEPNG_H_ \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp
new file mode 100644
index 0000000000..3e293176b7
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021 - 2022 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 <memory.h>
+#include "tvgLoader.h"
+#include "tvgPngLoader.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+static inline uint32_t PREMULTIPLY(uint32_t c)
+{
+ auto a = (c >> 24);
+ return (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff);
+}
+
+
+static void _premultiply(uint32_t* data, uint32_t w, uint32_t h)
+{
+ auto buffer = data;
+ for (uint32_t y = 0; y < h; ++y, buffer += w) {
+ auto src = buffer;
+ for (uint32_t x = 0; x < w; ++x, ++src) {
+ *src = PREMULTIPLY(*src);
+ }
+ }
+}
+
+
+void PngLoader::clear()
+{
+ lodepng_state_cleanup(&state);
+
+ if (freeData) free(data);
+ data = nullptr;
+ size = 0;
+ freeData = false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+PngLoader::PngLoader()
+{
+ lodepng_state_init(&state);
+}
+
+
+PngLoader::~PngLoader()
+{
+ if (freeData) free(data);
+ free(image);
+}
+
+
+bool PngLoader::open(const string& path)
+{
+ clear();
+
+ auto pngFile = fopen(path.c_str(), "rb");
+ if (!pngFile) return false;
+
+ auto ret = false;
+
+ //determine size
+ if (fseek(pngFile, 0, SEEK_END) < 0) goto finalize;
+ if (((size = ftell(pngFile)) < 1)) goto finalize;
+ if (fseek(pngFile, 0, SEEK_SET)) goto finalize;
+
+ data = (unsigned char *) malloc(size);
+ if (!data) goto finalize;
+
+ freeData = true;
+
+ if (fread(data, size, 1, pngFile) < 1) goto failure;
+
+ lodepng_state_init(&state);
+
+ unsigned int width, height;
+ if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto failure;
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ ret = true;
+
+ goto finalize;
+
+failure:
+ clear();
+
+finalize:
+ fclose(pngFile);
+ return ret;
+}
+
+
+bool PngLoader::open(const char* data, uint32_t size, bool copy)
+{
+ clear();
+
+ lodepng_state_init(&state);
+
+ unsigned int width, height;
+ if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false;
+
+ if (copy) {
+ this->data = (unsigned char *) malloc(size);
+ if (!this->data) return false;
+ memcpy((unsigned char *)this->data, data, size);
+ freeData = true;
+ } else {
+ this->data = (unsigned char *) data;
+ freeData = false;
+ }
+
+ w = static_cast<float>(width);
+ h = static_cast<float>(height);
+ this->size = size;
+
+ return true;
+}
+
+
+bool PngLoader::read()
+{
+ if (!data || w <= 0 || h <= 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool PngLoader::close()
+{
+ this->done();
+ clear();
+ return true;
+}
+
+
+unique_ptr<Surface> PngLoader::bitmap()
+{
+ this->done();
+
+ if (!image) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(image);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
+
+
+void PngLoader::run(unsigned tid)
+{
+ if (image) {
+ free(image);
+ image = nullptr;
+ }
+ auto width = static_cast<unsigned>(w);
+ auto height = static_cast<unsigned>(h);
+
+ lodepng_decode(&image, &width, &height, &state, data, size);
+
+ _premultiply((uint32_t*)(image), width, height);
+}
diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h
new file mode 100644
index 0000000000..8f07f6418f
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 - 2022 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.
+ */
+#ifndef _TVG_PNG_LOADER_H_
+#define _TVG_PNG_LOADER_H_
+
+#include "tvgLodePng.h"
+#include "tvgTaskScheduler.h"
+
+
+class PngLoader : public LoadModule, public Task
+{
+private:
+ LodePNGState state;
+ unsigned char* data = nullptr;
+ unsigned char *image = nullptr;
+ unsigned long size = 0;
+ bool freeData = false;
+
+ void clear();
+
+public:
+ PngLoader();
+ ~PngLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+ void run(unsigned tid) override;
+};
+
+#endif //_TVG_PNG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
new file mode 100644
index 0000000000..2da399d8c3
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 - 2022 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 <fstream>
+#include <string.h>
+#include "tvgLoader.h"
+#include "tvgRawLoader.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+RawLoader::~RawLoader()
+{
+ if (copy && content) {
+ free((void*)content);
+ content = nullptr;
+ }
+}
+
+
+bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy)
+{
+ if (!data || w == 0 || h == 0) return false;
+
+ this->w = (float)w;
+ this->h = (float)h;
+ this->copy = copy;
+
+ if (copy) {
+ content = (uint32_t*)malloc(sizeof(uint32_t) * w * h);
+ if (!content) return false;
+ memcpy((void*)content, data, sizeof(uint32_t) * w * h);
+ }
+ else content = data;
+
+ return true;
+}
+
+
+bool RawLoader::read()
+{
+ return true;
+}
+
+
+bool RawLoader::close()
+{
+ return true;
+}
+
+
+unique_ptr<Surface> RawLoader::bitmap()
+{
+ if (!content) return nullptr;
+
+ auto surface = static_cast<Surface*>(malloc(sizeof(Surface)));
+ surface->buffer = (uint32_t*)(content);
+ surface->stride = w;
+ surface->w = w;
+ surface->h = h;
+ surface->cs = SwCanvas::ARGB8888;
+
+ return unique_ptr<Surface>(surface);
+}
diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
new file mode 100644
index 0000000000..e4f3423b41
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+#ifndef _TVG_RAW_LOADER_H_
+#define _TVG_RAW_LOADER_H_
+
+class RawLoader : public LoadModule
+{
+public:
+ const uint32_t* content = nullptr;
+ bool copy = false;
+
+ ~RawLoader();
+
+ using LoadModule::open;
+ bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) override;
+ bool read() override;
+ bool close() override;
+
+ unique_ptr<Surface> bitmap() override;
+};
+
+
+#endif //_TVG_RAW_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
new file mode 100644
index 0000000000..8f46b62ce9
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2022 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 "tvgSvgCssStyle.h"
+
+#include <cstring>
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static void _copyStyle(SvgStyleProperty* to, const SvgStyleProperty* from)
+{
+ if (from == nullptr) return;
+ //Copy the properties of 'from' only if they were explicitly set (not the default ones).
+ if (from->curColorSet && !((int)to->flags & (int)SvgStyleFlags::Color)) {
+ to->color = from->color;
+ to->curColorSet = true;
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Color);
+ }
+ //Fill
+ if (((int)from->fill.flags & (int)SvgFillFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Fill)) {
+ to->fill.paint.color = from->fill.paint.color;
+ to->fill.paint.none = from->fill.paint.none;
+ to->fill.paint.curColor = from->fill.paint.curColor;
+ if (from->fill.paint.url) to->fill.paint.url = strdup(from->fill.paint.url);
+ to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Paint);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Fill);
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::FillOpacity)) {
+ to->fill.opacity = from->fill.opacity;
+ to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::Opacity);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillOpacity);
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::FillRule) && !((int)to->flags & (int)SvgStyleFlags::FillRule)) {
+ to->fill.fillRule = from->fill.fillRule;
+ to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)SvgFillFlags::FillRule);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::FillRule);
+ }
+ //Stroke
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint) && !((int)to->flags & (int)SvgStyleFlags::Stroke)) {
+ to->stroke.paint.color = from->stroke.paint.color;
+ to->stroke.paint.none = from->stroke.paint.none;
+ to->stroke.paint.curColor = from->stroke.paint.curColor;
+ if (from->stroke.paint.url) to->stroke.paint.url = strdup(from->stroke.paint.url);
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Paint);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Stroke);
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity) && !((int)to->flags & (int)SvgStyleFlags::StrokeOpacity)) {
+ to->stroke.opacity = from->stroke.opacity;
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Opacity);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeOpacity);
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width) && !((int)to->flags & (int)SvgStyleFlags::StrokeWidth)) {
+ to->stroke.width = from->stroke.width;
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Width);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeWidth);
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash) && !((int)to->flags & (int)SvgStyleFlags::StrokeDashArray)) {
+ if (from->stroke.dash.array.count > 0) {
+ to->stroke.dash.array.clear();
+ to->stroke.dash.array.reserve(from->stroke.dash.array.count);
+ for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
+ to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ }
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Dash);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeDashArray);
+ }
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineCap)) {
+ to->stroke.cap = from->stroke.cap;
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Cap);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineCap);
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join) && !((int)to->flags & (int)SvgStyleFlags::StrokeLineJoin)) {
+ to->stroke.join = from->stroke.join;
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)SvgStrokeFlags::Join);
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::StrokeLineJoin);
+ }
+ //Opacity
+ //TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
+ if (from->opacity < 255 && !((int)to->flags & (int)SvgStyleFlags::Opacity)) {
+ to->opacity = from->opacity;
+ to->flags = (SvgStyleFlags)((int)to->flags | (int)SvgStyleFlags::Opacity);
+ }
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+void cssCopyStyleAttr(SvgNode* to, const SvgNode* from)
+{
+ //Copy matrix attribute
+ if (from->transform && !((int)to->style->flags & (int)SvgStyleFlags::Transform)) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) {
+ *to->transform = *from->transform;
+ to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)SvgStyleFlags::Transform);
+ }
+ }
+ //Copy style attribute
+ _copyStyle(to->style, from->style);
+
+ if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
+ if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
+}
+
+
+SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type)
+{
+ if (!style) return nullptr;
+
+ auto child = style->child.data;
+ for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
+ if ((*child)->type == type) {
+ if ((!title && !(*child)->id) || (title && (*child)->id && !strcmp((*child)->id, title))) return (*child);
+ }
+ }
+ return nullptr;
+}
+
+
+SvgNode* cssFindStyleNode(const SvgNode* style, const char* title)
+{
+ if (!style) return nullptr;
+
+ auto child = style->child.data;
+ for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
+ if ((*child)->type == SvgNodeType::CssStyle) {
+ if ((title && (*child)->id && !strcmp((*child)->id, title))) return (*child);
+ }
+ }
+ return nullptr;
+}
+
+
+void cssUpdateStyle(SvgNode* doc, SvgNode* style)
+{
+ if (doc->child.count > 0) {
+ auto child = doc->child.data;
+ for (uint32_t i = 0; i < doc->child.count; ++i, ++child) {
+ if (auto cssNode = cssFindStyleNode(style, nullptr, (*child)->type)) {
+ cssCopyStyleAttr(*child, cssNode);
+ }
+ if (auto cssNode = cssFindStyleNode(style, nullptr)) {
+ cssCopyStyleAttr(*child, cssNode);
+ }
+ cssUpdateStyle(*child, style);
+ }
+ }
+}
+
+
+void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
+{
+ for (uint32_t i = 0; i < postponeds.count; ++i) {
+ auto nodeIdPair = postponeds.data[i];
+
+ //css styling: tag.name has higher priority than .name
+ if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id, nodeIdPair.node->type)) {
+ cssCopyStyleAttr(nodeIdPair.node, cssNode);
+ }
+ if (auto cssNode = cssFindStyleNode(style, nodeIdPair.id)) {
+ cssCopyStyleAttr(nodeIdPair.node, cssNode);
+ }
+ }
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
new file mode 100644
index 0000000000..66477c1a32
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022 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.
+ */
+
+#ifndef _TVG_SVG_CSS_STYLE_H_
+#define _TVG_SVG_CSS_STYLE_H_
+
+#include "tvgSvgLoaderCommon.h"
+
+void cssCopyStyleAttr(SvgNode* to, const SvgNode* from);
+SvgNode* cssFindStyleNode(const SvgNode* style, const char* title, SvgNodeType type);
+SvgNode* cssFindStyleNode(const SvgNode* style, const char* title);
+void cssUpdateStyle(SvgNode* doc, SvgNode* style);
+void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style);
+
+#endif //_TVG_SVG_CSS_STYLE_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
new file mode 100644
index 0000000000..a842b7fa8b
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
@@ -0,0 +1,3275 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+/*
+ * Copyright notice for the EFL:
+
+ * Copyright (C) EFL developers (see AUTHORS)
+
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
+
+#include <cstring>
+#include <fstream>
+#include <float.h>
+#include <math.h>
+#include "tvgLoader.h"
+#include "tvgXmlParser.h"
+#include "tvgSvgLoader.h"
+#include "tvgSvgSceneBuilder.h"
+#include "tvgSvgUtil.h"
+#include "tvgSvgCssStyle.h"
+#include "tvgMath.h"
+
+/************************************************************************/
+/* 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 bool (*parseAttributes)(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
+typedef SvgNode* (*FactoryMethod)(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func);
+typedef SvgStyleGradient* (*GradientFactoryMethod)(SvgLoaderData* loader, const char* buf, unsigned bufLength);
+
+static char* _skipSpace(const char* str, const char* end)
+{
+ while (((end && str < end) || (!end && *str != '\0')) && isspace(*str)) {
+ ++str;
+ }
+ return (char*) str;
+}
+
+
+static char* _copyId(const char* str)
+{
+ if (!str) return nullptr;
+
+ return strdup(str);
+}
+
+
+static const char* _skipComma(const char* content)
+{
+ content = _skipSpace(content, nullptr);
+ if (*content == ',') return content + 1;
+ return content;
+}
+
+
+static bool _parseNumber(const char** content, float* number)
+{
+ char* end = nullptr;
+
+ *number = svgUtilStrtof(*content, &end);
+ //If the start of string is not number
+ if ((*content) == end) return false;
+ //Skip comma if any
+ *content = _skipComma(end);
+ return true;
+}
+
+/**
+ * According to https://www.w3.org/TR/SVG/coords.html#Units
+ */
+static float _toFloat(const SvgParser* svgParse, const char* str, SvgParserLengthType type)
+{
+ float parsedValue = svgUtilStrtof(str, nullptr);
+
+ 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;
+ else //if other then it's radius
+ {
+ float max = (float)svgParse->global.w;
+ if (max < svgParse->global.h)
+ max = (float)svgParse->global.h;
+ parsedValue = (parsedValue / 100.0) * max;
+ }
+ }
+ //TODO: Implement 'em', 'ex' attributes
+
+ return parsedValue;
+}
+
+
+static float _gradientToFloat(const SvgParser* svgParse, const char* str, bool& isPercentage)
+{
+ char* end = nullptr;
+
+ float parsedValue = svgUtilStrtof(str, &end);
+ isPercentage = false;
+
+ 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
+
+ return parsedValue;
+}
+
+
+static float _toOffset(const char* str)
+{
+ char* end = nullptr;
+ auto strEnd = str + strlen(str);
+
+ float parsedValue = svgUtilStrtof(str, &end);
+
+ end = _skipSpace(end, nullptr);
+ auto ptr = strstr(str, "%");
+
+ if (ptr) {
+ parsedValue = parsedValue / 100.0;
+ if (end != ptr || (end + 1) != strEnd) return 0;
+ } else if (end != strEnd) return 0;
+
+ return parsedValue;
+}
+
+
+static int _toOpacity(const char* str)
+{
+ char* end = nullptr;
+ float opacity = svgUtilStrtof(str, &end);
+
+ if (end) {
+ if (end[0] == '%' && end[1] == '\0') return lrint(opacity * 2.55f);
+ else if (*end == '\0') return lrint(opacity * 255);
+ }
+ return 255;
+}
+
+
+static SvgMaskType _toMaskType(const char* str)
+{
+ if (!strcmp(str, "Alpha")) return SvgMaskType::Alpha;
+
+ return SvgMaskType::Luminance;
+}
+
+
+#define _PARSE_TAG(Type, Name, Name1, Tags_Array, Default) \
+ static Type _to##Name1(const char* str) \
+ { \
+ unsigned int i; \
+ \
+ for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
+ if (!strcmp(str, Tags_Array[i].tag)) return Tags_Array[i].Name; \
+ } \
+ return Default; \
+ }
+
+
+/* parse the line cap used during stroking a path.
+ * Value: butt | round | square | inherit
+ * Initial: butt
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static constexpr struct
+{
+ StrokeCap lineCap;
+ const char* tag;
+} lineCapTags[] = {
+ { StrokeCap::Butt, "butt" },
+ { StrokeCap::Round, "round" },
+ { StrokeCap::Square, "square" }
+};
+
+
+_PARSE_TAG(StrokeCap, lineCap, LineCap, lineCapTags, StrokeCap::Butt)
+
+
+/* parse the line join used during stroking a path.
+ * Value: miter | round | bevel | inherit
+ * Initial: miter
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static constexpr struct
+{
+ StrokeJoin lineJoin;
+ const char* tag;
+} lineJoinTags[] = {
+ { StrokeJoin::Miter, "miter" },
+ { StrokeJoin::Round, "round" },
+ { StrokeJoin::Bevel, "bevel" }
+};
+
+
+_PARSE_TAG(StrokeJoin, lineJoin, LineJoin, lineJoinTags, StrokeJoin::Miter)
+
+
+/* parse the fill rule used during filling a path.
+ * Value: nonzero | evenodd | inherit
+ * Initial: nonzero
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static constexpr struct
+{
+ FillRule fillRule;
+ const char* tag;
+} fillRuleTags[] = {
+ { FillRule::EvenOdd, "evenodd" }
+};
+
+
+_PARSE_TAG(FillRule, fillRule, FillRule, fillRuleTags, FillRule::Winding)
+
+
+/* parse the dash pattern used during stroking a path.
+ * Value: none | <dasharray> | inherit
+ * Initial: none
+ * https://www.w3.org/TR/SVG/painting.html
+ */
+static void _parseDashArray(SvgLoaderData* loader, const char *str, SvgDash* dash)
+{
+ if (!strncmp(str, "none", 4)) return;
+
+ char *end = nullptr;
+
+ while (*str) {
+ str = _skipComma(str);
+ float parsedValue = svgUtilStrtof(str, &end);
+ if (str == end) break;
+ if (parsedValue <= 0.0f) break;
+ if (*end == '%') {
+ ++end;
+ //Refers to the diagonal length of the viewport.
+ //https://www.w3.org/TR/SVG2/coords.html#Units
+ parsedValue = (sqrtf(pow(loader->svgParse->global.w, 2) + pow(loader->svgParse->global.h, 2)) / sqrtf(2.0f)) * (parsedValue / 100.0f);
+ }
+ (*dash).array.push(parsedValue);
+ str = end;
+ }
+ //If dash array size is 1, it means that dash and gap size are the same.
+ if ((*dash).array.count == 1) (*dash).array.push((*dash).array.data[0]);
+}
+
+
+static char* _idFromUrl(const char* url)
+{
+ url = _skipSpace(url, nullptr);
+ if ((*url) == '(') {
+ ++url;
+ url = _skipSpace(url, nullptr);
+ }
+
+ if ((*url) == '\'') ++url;
+ if ((*url) == '#') ++url;
+
+ int i = 0;
+ while (url[i] > ' ' && url[i] != ')' && url[i] != '\'') ++i;
+
+ //custom strndup() for portability
+ int len = strlen(url);
+ if (i < len) len = i;
+
+ auto ret = (char*) malloc(len + 1);
+ if (!ret) return 0;
+ ret[len] = '\0';
+ return (char*) memcpy(ret, url, len);
+}
+
+
+static unsigned char _parserColor(const char* value, char** end)
+{
+ float r;
+
+ r = svgUtilStrtof(value, end);
+ *end = _skipSpace(*end, nullptr);
+ if (**end == '%') r = 255 * r / 100;
+ *end = _skipSpace(*end, nullptr);
+
+ if (r < 0 || r > 255) {
+ *end = nullptr;
+ return 0;
+ }
+
+ return lrint(r);
+}
+
+
+static constexpr struct
+{
+ const char* name;
+ unsigned int value;
+} colors[] = {
+ { "aliceblue", 0xfff0f8ff },
+ { "antiquewhite", 0xfffaebd7 },
+ { "aqua", 0xff00ffff },
+ { "aquamarine", 0xff7fffd4 },
+ { "azure", 0xfff0ffff },
+ { "beige", 0xfff5f5dc },
+ { "bisque", 0xffffe4c4 },
+ { "black", 0xff000000 },
+ { "blanchedalmond", 0xffffebcd },
+ { "blue", 0xff0000ff },
+ { "blueviolet", 0xff8a2be2 },
+ { "brown", 0xffa52a2a },
+ { "burlywood", 0xffdeb887 },
+ { "cadetblue", 0xff5f9ea0 },
+ { "chartreuse", 0xff7fff00 },
+ { "chocolate", 0xffd2691e },
+ { "coral", 0xffff7f50 },
+ { "cornflowerblue", 0xff6495ed },
+ { "cornsilk", 0xfffff8dc },
+ { "crimson", 0xffdc143c },
+ { "cyan", 0xff00ffff },
+ { "darkblue", 0xff00008b },
+ { "darkcyan", 0xff008b8b },
+ { "darkgoldenrod", 0xffb8860b },
+ { "darkgray", 0xffa9a9a9 },
+ { "darkgrey", 0xffa9a9a9 },
+ { "darkgreen", 0xff006400 },
+ { "darkkhaki", 0xffbdb76b },
+ { "darkmagenta", 0xff8b008b },
+ { "darkolivegreen", 0xff556b2f },
+ { "darkorange", 0xffff8c00 },
+ { "darkorchid", 0xff9932cc },
+ { "darkred", 0xff8b0000 },
+ { "darksalmon", 0xffe9967a },
+ { "darkseagreen", 0xff8fbc8f },
+ { "darkslateblue", 0xff483d8b },
+ { "darkslategray", 0xff2f4f4f },
+ { "darkslategrey", 0xff2f4f4f },
+ { "darkturquoise", 0xff00ced1 },
+ { "darkviolet", 0xff9400d3 },
+ { "deeppink", 0xffff1493 },
+ { "deepskyblue", 0xff00bfff },
+ { "dimgray", 0xff696969 },
+ { "dimgrey", 0xff696969 },
+ { "dodgerblue", 0xff1e90ff },
+ { "firebrick", 0xffb22222 },
+ { "floralwhite", 0xfffffaf0 },
+ { "forestgreen", 0xff228b22 },
+ { "fuchsia", 0xffff00ff },
+ { "gainsboro", 0xffdcdcdc },
+ { "ghostwhite", 0xfff8f8ff },
+ { "gold", 0xffffd700 },
+ { "goldenrod", 0xffdaa520 },
+ { "gray", 0xff808080 },
+ { "grey", 0xff808080 },
+ { "green", 0xff008000 },
+ { "greenyellow", 0xffadff2f },
+ { "honeydew", 0xfff0fff0 },
+ { "hotpink", 0xffff69b4 },
+ { "indianred", 0xffcd5c5c },
+ { "indigo", 0xff4b0082 },
+ { "ivory", 0xfffffff0 },
+ { "khaki", 0xfff0e68c },
+ { "lavender", 0xffe6e6fa },
+ { "lavenderblush", 0xfffff0f5 },
+ { "lawngreen", 0xff7cfc00 },
+ { "lemonchiffon", 0xfffffacd },
+ { "lightblue", 0xffadd8e6 },
+ { "lightcoral", 0xfff08080 },
+ { "lightcyan", 0xffe0ffff },
+ { "lightgoldenrodyellow", 0xfffafad2 },
+ { "lightgray", 0xffd3d3d3 },
+ { "lightgrey", 0xffd3d3d3 },
+ { "lightgreen", 0xff90ee90 },
+ { "lightpink", 0xffffb6c1 },
+ { "lightsalmon", 0xffffa07a },
+ { "lightseagreen", 0xff20b2aa },
+ { "lightskyblue", 0xff87cefa },
+ { "lightslategray", 0xff778899 },
+ { "lightslategrey", 0xff778899 },
+ { "lightsteelblue", 0xffb0c4de },
+ { "lightyellow", 0xffffffe0 },
+ { "lime", 0xff00ff00 },
+ { "limegreen", 0xff32cd32 },
+ { "linen", 0xfffaf0e6 },
+ { "magenta", 0xffff00ff },
+ { "maroon", 0xff800000 },
+ { "mediumaquamarine", 0xff66cdaa },
+ { "mediumblue", 0xff0000cd },
+ { "mediumorchid", 0xffba55d3 },
+ { "mediumpurple", 0xff9370d8 },
+ { "mediumseagreen", 0xff3cb371 },
+ { "mediumslateblue", 0xff7b68ee },
+ { "mediumspringgreen", 0xff00fa9a },
+ { "mediumturquoise", 0xff48d1cc },
+ { "mediumvioletred", 0xffc71585 },
+ { "midnightblue", 0xff191970 },
+ { "mintcream", 0xfff5fffa },
+ { "mistyrose", 0xffffe4e1 },
+ { "moccasin", 0xffffe4b5 },
+ { "navajowhite", 0xffffdead },
+ { "navy", 0xff000080 },
+ { "oldlace", 0xfffdf5e6 },
+ { "olive", 0xff808000 },
+ { "olivedrab", 0xff6b8e23 },
+ { "orange", 0xffffa500 },
+ { "orangered", 0xffff4500 },
+ { "orchid", 0xffda70d6 },
+ { "palegoldenrod", 0xffeee8aa },
+ { "palegreen", 0xff98fb98 },
+ { "paleturquoise", 0xffafeeee },
+ { "palevioletred", 0xffd87093 },
+ { "papayawhip", 0xffffefd5 },
+ { "peachpuff", 0xffffdab9 },
+ { "peru", 0xffcd853f },
+ { "pink", 0xffffc0cb },
+ { "plum", 0xffdda0dd },
+ { "powderblue", 0xffb0e0e6 },
+ { "purple", 0xff800080 },
+ { "red", 0xffff0000 },
+ { "rosybrown", 0xffbc8f8f },
+ { "royalblue", 0xff4169e1 },
+ { "saddlebrown", 0xff8b4513 },
+ { "salmon", 0xfffa8072 },
+ { "sandybrown", 0xfff4a460 },
+ { "seagreen", 0xff2e8b57 },
+ { "seashell", 0xfffff5ee },
+ { "sienna", 0xffa0522d },
+ { "silver", 0xffc0c0c0 },
+ { "skyblue", 0xff87ceeb },
+ { "slateblue", 0xff6a5acd },
+ { "slategray", 0xff708090 },
+ { "slategrey", 0xff708090 },
+ { "snow", 0xfffffafa },
+ { "springgreen", 0xff00ff7f },
+ { "steelblue", 0xff4682b4 },
+ { "tan", 0xffd2b48c },
+ { "teal", 0xff008080 },
+ { "thistle", 0xffd8bfd8 },
+ { "tomato", 0xffff6347 },
+ { "turquoise", 0xff40e0d0 },
+ { "violet", 0xffee82ee },
+ { "wheat", 0xfff5deb3 },
+ { "white", 0xffffffff },
+ { "whitesmoke", 0xfff5f5f5 },
+ { "yellow", 0xffffff00 },
+ { "yellowgreen", 0xff9acd32 }
+};
+
+
+static void _toColor(const char* str, uint8_t* r, uint8_t* g, uint8_t* b, char** ref)
+{
+ unsigned int len = strlen(str);
+ char *red, *green, *blue;
+ unsigned char tr, tg, tb;
+
+ if (len == 4 && str[0] == '#') {
+ //Case for "#456" should be interprete as "#445566"
+ if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3])) {
+ char tmp[3] = { '\0', '\0', '\0' };
+ tmp[0] = str[1];
+ tmp[1] = str[1];
+ *r = strtol(tmp, nullptr, 16);
+ tmp[0] = str[2];
+ tmp[1] = str[2];
+ *g = strtol(tmp, nullptr, 16);
+ tmp[0] = str[3];
+ tmp[1] = str[3];
+ *b = strtol(tmp, nullptr, 16);
+ }
+ } else if (len == 7 && str[0] == '#') {
+ if (isxdigit(str[1]) && isxdigit(str[2]) && isxdigit(str[3]) && isxdigit(str[4]) && isxdigit(str[5]) && isxdigit(str[6])) {
+ char tmp[3] = { '\0', '\0', '\0' };
+ tmp[0] = str[1];
+ tmp[1] = str[2];
+ *r = strtol(tmp, nullptr, 16);
+ tmp[0] = str[3];
+ tmp[1] = str[4];
+ *g = strtol(tmp, nullptr, 16);
+ tmp[0] = str[5];
+ tmp[1] = str[6];
+ *b = strtol(tmp, nullptr, 16);
+ }
+ } else if (len >= 10 && (str[0] == 'r' || str[0] == 'R') && (str[1] == 'g' || str[1] == 'G') && (str[2] == 'b' || str[2] == 'B') && str[3] == '(' && str[len - 1] == ')') {
+ tr = _parserColor(str + 4, &red);
+ if (red && *red == ',') {
+ tg = _parserColor(red + 1, &green);
+ if (green && *green == ',') {
+ tb = _parserColor(green + 1, &blue);
+ if (blue && blue[0] == ')' && blue[1] == '\0') {
+ *r = tr;
+ *g = tg;
+ *b = tb;
+ }
+ }
+ }
+ } else if (ref && len >= 3 && !strncmp(str, "url", 3)) {
+ *ref = _idFromUrl((const char*)(str + 3));
+ } else {
+ //Handle named color
+ for (unsigned int i = 0; i < (sizeof(colors) / sizeof(colors[0])); i++) {
+ if (!strcasecmp(colors[i].name, str)) {
+ *r = (((uint8_t*)(&(colors[i].value)))[2]);
+ *g = (((uint8_t*)(&(colors[i].value)))[1]);
+ *b = (((uint8_t*)(&(colors[i].value)))[0]);
+ return;
+ }
+ }
+ }
+}
+
+
+static char* _parseNumbersArray(char* str, float* points, int* ptCount, int len)
+{
+ int count = 0;
+ char* end = nullptr;
+
+ str = _skipSpace(str, nullptr);
+ while ((count < len) && (isdigit(*str) || *str == '-' || *str == '+' || *str == '.')) {
+ points[count++] = svgUtilStrtof(str, &end);
+ str = end;
+ str = _skipSpace(str, nullptr);
+ if (*str == ',') ++str;
+ //Eat the rest of space
+ str = _skipSpace(str, nullptr);
+ }
+ *ptCount = count;
+ return str;
+}
+
+
+enum class MatrixState {
+ Unknown,
+ Matrix,
+ Translate,
+ Rotate,
+ Scale,
+ SkewX,
+ SkewY
+};
+
+
+#define MATRIX_DEF(Name, Value) \
+ { \
+#Name, sizeof(#Name), Value \
+ }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ MatrixState state;
+} matrixTags[] = {
+ MATRIX_DEF(matrix, MatrixState::Matrix),
+ MATRIX_DEF(translate, MatrixState::Translate),
+ MATRIX_DEF(rotate, MatrixState::Rotate),
+ MATRIX_DEF(scale, MatrixState::Scale),
+ MATRIX_DEF(skewX, MatrixState::SkewX),
+ MATRIX_DEF(skewY, MatrixState::SkewY)
+};
+
+
+static void _matrixCompose(const Matrix* m1, const Matrix* m2, Matrix* dst)
+{
+ auto a11 = (m1->e11 * m2->e11) + (m1->e12 * m2->e21) + (m1->e13 * m2->e31);
+ auto a12 = (m1->e11 * m2->e12) + (m1->e12 * m2->e22) + (m1->e13 * m2->e32);
+ auto a13 = (m1->e11 * m2->e13) + (m1->e12 * m2->e23) + (m1->e13 * m2->e33);
+
+ auto a21 = (m1->e21 * m2->e11) + (m1->e22 * m2->e21) + (m1->e23 * m2->e31);
+ auto a22 = (m1->e21 * m2->e12) + (m1->e22 * m2->e22) + (m1->e23 * m2->e32);
+ auto a23 = (m1->e21 * m2->e13) + (m1->e22 * m2->e23) + (m1->e23 * m2->e33);
+
+ auto a31 = (m1->e31 * m2->e11) + (m1->e32 * m2->e21) + (m1->e33 * m2->e31);
+ auto a32 = (m1->e31 * m2->e12) + (m1->e32 * m2->e22) + (m1->e33 * m2->e32);
+ auto a33 = (m1->e31 * m2->e13) + (m1->e32 * m2->e23) + (m1->e33 * m2->e33);
+
+ dst->e11 = a11;
+ dst->e12 = a12;
+ dst->e13 = a13;
+ dst->e21 = a21;
+ dst->e22 = a22;
+ dst->e23 = a23;
+ dst->e31 = a31;
+ dst->e32 = a32;
+ dst->e33 = a33;
+}
+
+
+/* parse transform attribute
+ * https://www.w3.org/TR/SVG/coords.html#TransformAttribute
+ */
+static Matrix* _parseTransformationMatrix(const char* value)
+{
+ const int POINT_CNT = 8;
+
+ auto matrix = (Matrix*)malloc(sizeof(Matrix));
+ if (!matrix) return nullptr;
+ *matrix = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+
+ float points[POINT_CNT];
+ int ptCount = 0;
+ char* str = (char*)value;
+ char* end = str + strlen(str);
+
+ while (str < end) {
+ auto state = MatrixState::Unknown;
+
+ if (isspace(*str) || (*str == ',')) {
+ ++str;
+ continue;
+ }
+ for (unsigned int i = 0; i < sizeof(matrixTags) / sizeof(matrixTags[0]); i++) {
+ if (!strncmp(matrixTags[i].tag, str, matrixTags[i].sz - 1)) {
+ state = matrixTags[i].state;
+ str += (matrixTags[i].sz - 1);
+ break;
+ }
+ }
+ if (state == MatrixState::Unknown) goto error;
+
+ str = _skipSpace(str, end);
+ if (*str != '(') goto error;
+ ++str;
+ str = _parseNumbersArray(str, points, &ptCount, POINT_CNT);
+ if (*str != ')') goto error;
+ ++str;
+
+ if (state == MatrixState::Matrix) {
+ if (ptCount != 6) goto error;
+ Matrix tmp = {points[0], points[2], points[4], points[1], points[3], points[5], 0, 0, 1};
+ _matrixCompose(matrix, &tmp, matrix);
+ } else if (state == MatrixState::Translate) {
+ if (ptCount == 1) {
+ Matrix tmp = {1, 0, points[0], 0, 1, 0, 0, 0, 1};
+ _matrixCompose(matrix, &tmp, matrix);
+ } else if (ptCount == 2) {
+ Matrix tmp = {1, 0, points[0], 0, 1, points[1], 0, 0, 1};
+ _matrixCompose(matrix, &tmp, matrix);
+ } else goto error;
+ } else if (state == MatrixState::Rotate) {
+ //Transform to signed.
+ points[0] = fmod(points[0], 360);
+ if (points[0] < 0) points[0] += 360;
+ auto c = cosf(points[0] * (M_PI / 180.0));
+ auto s = sinf(points[0] * (M_PI / 180.0));
+ if (ptCount == 1) {
+ Matrix tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ } else if (ptCount == 3) {
+ Matrix tmp = { 1, 0, points[1], 0, 1, points[2], 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ tmp = { c, -s, 0, s, c, 0, 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ tmp = { 1, 0, -points[1], 0, 1, -points[2], 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ } else {
+ goto error;
+ }
+ } else if (state == MatrixState::Scale) {
+ if (ptCount < 1 || ptCount > 2) goto error;
+ auto sx = points[0];
+ auto sy = sx;
+ if (ptCount == 2) sy = points[1];
+ Matrix tmp = { sx, 0, 0, 0, sy, 0, 0, 0, 1 };
+ _matrixCompose(matrix, &tmp, matrix);
+ }
+ }
+ return matrix;
+error:
+ if (matrix) free(matrix);
+ return nullptr;
+}
+
+
+#define LENGTH_DEF(Name, Value) \
+ { \
+#Name, sizeof(#Name), Value \
+ }
+
+
+static void _postpone(Array<SvgNodeIdPair>& nodes, SvgNode *node, char* id)
+{
+ nodes.push({node, id});
+}
+
+
+/*
+// TODO - remove?
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ SvgLengthType type;
+} lengthTags[] = {
+ LENGTH_DEF(%, SvgLengthType::Percent),
+ LENGTH_DEF(px, SvgLengthType::Px),
+ LENGTH_DEF(pc, SvgLengthType::Pc),
+ LENGTH_DEF(pt, SvgLengthType::Pt),
+ LENGTH_DEF(mm, SvgLengthType::Mm),
+ LENGTH_DEF(cm, SvgLengthType::Cm),
+ LENGTH_DEF(in, SvgLengthType::In)
+};
+
+static float _parseLength(const char* str, SvgLengthType* type)
+{
+ float value;
+ int sz = strlen(str);
+
+ *type = SvgLengthType::Px;
+ for (unsigned int i = 0; i < sizeof(lengthTags) / sizeof(lengthTags[0]); i++) {
+ if (lengthTags[i].sz - 1 == sz && !strncmp(lengthTags[i].tag, str, sz)) *type = lengthTags[i].type;
+ }
+ value = svgUtilStrtof(str, nullptr);
+ return value;
+}
+*/
+
+static bool _parseStyleAttr(void* data, const char* key, const char* value);
+static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style);
+
+
+static bool _attrParseSvgNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgDocNode* doc = &(node->node.doc);
+
+ if (!strcmp(key, "width")) {
+ doc->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+ } else if (!strcmp(key, "height")) {
+ doc->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+ } else if (!strcmp(key, "viewBox")) {
+ if (_parseNumber(&value, &doc->vx)) {
+ if (_parseNumber(&value, &doc->vy)) {
+ if (_parseNumber(&value, &doc->vw)) {
+ _parseNumber(&value, &doc->vh);
+ loader->svgParse->global.h = (uint32_t)doc->vh;
+ }
+ loader->svgParse->global.w = (uint32_t)doc->vw;
+ }
+ loader->svgParse->global.y = (int)doc->vy;
+ }
+ loader->svgParse->global.x = (int)doc->vx;
+ } else if (!strcmp(key, "preserveAspectRatio")) {
+ if (!strcmp(value, "none")) doc->preserveAspect = false;
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+#ifdef THORVG_LOG_ENABLED
+ } else if ((!strcmp(key, "x") || !strcmp(key, "y")) && fabsf(svgUtilStrtof(value, nullptr)) > FLT_EPSILON) {
+ TVGLOG("SVG", "Unsupported attributes used [Elements type: Svg][Attribute: %s][Value: %s]", key, value);
+#endif
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+//https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
+static void _handlePaintAttr(SvgPaint* paint, const char* value)
+{
+ if (!strcmp(value, "none")) {
+ //No paint property
+ paint->none = true;
+ return;
+ }
+ paint->none = false;
+ if (!strcmp(value, "currentColor")) {
+ paint->curColor = true;
+ return;
+ }
+ _toColor(value, &paint->color.r, &paint->color.g, &paint->color.b, &paint->url);
+}
+
+
+static void _handleColorAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ style->curColorSet = true;
+ _toColor(value, &style->color.r, &style->color.g, &style->color.b, nullptr);
+}
+
+
+static void _handleFillAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ style->fill.flags = (SvgFillFlags)((int)style->fill.flags | (int)SvgFillFlags::Paint);
+ _handlePaintAttr(&style->fill.paint, value);
+}
+
+
+static void _handleStrokeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ style->stroke.flags = (SvgStrokeFlags)((int)style->stroke.flags | (int)SvgStrokeFlags::Paint);
+ _handlePaintAttr(&style->stroke.paint, value);
+}
+
+
+static void _handleStrokeOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Opacity);
+ node->style->stroke.opacity = _toOpacity(value);
+}
+
+static void _handleStrokeDashArrayAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Dash);
+ _parseDashArray(loader, value, &node->style->stroke.dash);
+}
+
+static void _handleStrokeWidthAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Width);
+ node->style->stroke.width = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+}
+
+
+static void _handleStrokeLineCapAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Cap);
+ node->style->stroke.cap = _toLineCap(value);
+}
+
+
+static void _handleStrokeLineJoinAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->stroke.flags = (SvgStrokeFlags)((int)node->style->stroke.flags | (int)SvgStrokeFlags::Join);
+ node->style->stroke.join = _toLineJoin(value);
+}
+
+
+static void _handleFillRuleAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::FillRule);
+ node->style->fill.fillRule = _toFillRule(value);
+}
+
+
+static void _handleOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->opacity = _toOpacity(value);
+}
+
+
+static void _handleFillOpacityAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->style->fill.flags = (SvgFillFlags)((int)node->style->fill.flags | (int)SvgFillFlags::Opacity);
+ node->style->fill.opacity = _toOpacity(value);
+}
+
+
+static void _handleTransformAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->transform = _parseTransformationMatrix(value);
+}
+
+
+static void _handleClipPathAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ int len = strlen(value);
+ if (len >= 3 && !strncmp(value, "url", 3)) {
+ if (style->clipPath.url) free(style->clipPath.url);
+ style->clipPath.url = _idFromUrl((const char*)(value + 3));
+ }
+}
+
+
+static void _handleMaskAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ SvgStyleProperty* style = node->style;
+ int len = strlen(value);
+ if (len >= 3 && !strncmp(value, "url", 3)) {
+ if (style->mask.url) free(style->mask.url);
+ style->mask.url = _idFromUrl((const char*)(value + 3));
+ }
+}
+
+
+static void _handleMaskTypeAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ node->node.mask.type = _toMaskType(value);
+}
+
+
+static void _handleDisplayAttr(TVG_UNUSED SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ //TODO : The display attribute can have various values as well as "none".
+ // The default is "inline" which means visible and "none" means invisible.
+ // Depending on the type of node, additional functionality may be required.
+ // refer to https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/display
+ if (!strcmp(value, "none")) node->display = false;
+ else node->display = true;
+}
+
+
+static void _handleCssClassAttr(SvgLoaderData* loader, SvgNode* node, const char* value)
+{
+ auto cssClass = &node->style->cssClass;
+
+ if (*cssClass && value) free(*cssClass);
+ *cssClass = _copyId(value);
+
+ bool cssClassFound = false;
+
+ //css styling: tag.name has higher priority than .name
+ if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass, node->type)) {
+ cssClassFound = true;
+ cssCopyStyleAttr(node, cssNode);
+ }
+ if (auto cssNode = cssFindStyleNode(loader->cssStyle, *cssClass)) {
+ cssClassFound = true;
+ cssCopyStyleAttr(node, cssNode);
+ }
+
+ if (!cssClassFound) _postpone(loader->nodesToStyle, node, *cssClass);
+}
+
+
+typedef void (*styleMethod)(SvgLoaderData* loader, SvgNode* node, const char* value);
+
+#define STYLE_DEF(Name, Name1, Flag) { #Name, sizeof(#Name), _handle##Name1##Attr, Flag }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ styleMethod tagHandler;
+ SvgStyleFlags flag;
+} styleTags[] = {
+ STYLE_DEF(color, Color, SvgStyleFlags::Color),
+ STYLE_DEF(fill, Fill, SvgStyleFlags::Fill),
+ STYLE_DEF(fill-rule, FillRule, SvgStyleFlags::FillRule),
+ STYLE_DEF(fill-opacity, FillOpacity, SvgStyleFlags::FillOpacity),
+ STYLE_DEF(opacity, Opacity, SvgStyleFlags::Opacity),
+ STYLE_DEF(stroke, Stroke, SvgStyleFlags::Stroke),
+ STYLE_DEF(stroke-width, StrokeWidth, SvgStyleFlags::StrokeWidth),
+ STYLE_DEF(stroke-linejoin, StrokeLineJoin, SvgStyleFlags::StrokeLineJoin),
+ STYLE_DEF(stroke-linecap, StrokeLineCap, SvgStyleFlags::StrokeLineCap),
+ STYLE_DEF(stroke-opacity, StrokeOpacity, SvgStyleFlags::StrokeOpacity),
+ STYLE_DEF(stroke-dasharray, StrokeDashArray, SvgStyleFlags::StrokeDashArray),
+ STYLE_DEF(transform, Transform, SvgStyleFlags::Transform),
+ STYLE_DEF(clip-path, ClipPath, SvgStyleFlags::ClipPath),
+ STYLE_DEF(mask, Mask, SvgStyleFlags::Mask),
+ STYLE_DEF(mask-type, MaskType, SvgStyleFlags::MaskType),
+ STYLE_DEF(display, Display, SvgStyleFlags::Display)
+};
+
+
+static bool _parseStyleAttr(void* data, const char* key, const char* value, bool style)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ int sz;
+ if (!key || !value) return false;
+
+ //Trim the white space
+ key = _skipSpace(key, nullptr);
+ value = _skipSpace(value, nullptr);
+
+ sz = strlen(key);
+ for (unsigned int i = 0; i < sizeof(styleTags) / sizeof(styleTags[0]); i++) {
+ if (styleTags[i].sz - 1 == sz && !strncmp(styleTags[i].tag, key, sz)) {
+ if (style) {
+ styleTags[i].tagHandler(loader, node, value);
+ node->style->flags = (SvgStyleFlags)((int)node->style->flags | (int)styleTags[i].flag);
+ } else if (!((int)node->style->flags & (int)styleTags[i].flag)) {
+ styleTags[i].tagHandler(loader, node, value);
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+static bool _parseStyleAttr(void* data, const char* key, const char* value)
+{
+ return _parseStyleAttr(data, key, value, true);
+}
+
+
+/* parse g node
+ * https://www.w3.org/TR/SVG/struct.html#Groups
+ */
+static bool _attrParseGNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "transform")) {
+ node->transform = _parseTransformationMatrix(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+/* parse clipPath node
+ * https://www.w3.org/TR/SVG/struct.html#Groups
+ */
+static bool _attrParseClipPathNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgClipNode* clip = &(node->node.clip);
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "transform")) {
+ node->transform = _parseTransformationMatrix(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else if (!strcmp(key, "clipPathUnits")) {
+ if (!strcmp(value, "objectBoundingBox")) clip->userSpace = false;
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static bool _attrParseMaskNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgMaskNode* mask = &(node->node.mask);
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "transform")) {
+ node->transform = _parseTransformationMatrix(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else if (!strcmp(key, "maskContentUnits")) {
+ if (!strcmp(value, "objectBoundingBox")) mask->userSpace = false;
+ } else if (!strcmp(key, "mask-type")) {
+ mask->type = _toMaskType(value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static bool _attrParseCssStyleNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static bool _attrParseSymbolNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgSymbolNode* symbol = &(node->node.symbol);
+
+ if (!strcmp(key, "viewBox")) {
+ if (!_parseNumber(&value, &symbol->vx) || !_parseNumber(&value, &symbol->vy)) return false;
+ if (!_parseNumber(&value, &symbol->vw) || !_parseNumber(&value, &symbol->vh)) return false;
+ } else if (!strcmp(key, "width")) {
+ symbol->w = _toFloat(loader->svgParse, value, SvgParserLengthType::Horizontal);
+ } else if (!strcmp(key, "height")) {
+ symbol->h = _toFloat(loader->svgParse, value, SvgParserLengthType::Vertical);
+ } else if (!strcmp(key, "preserveAspectRatio")) {
+ if (!strcmp(value, "none")) symbol->preserveAspect = false;
+ } else if (!strcmp(key, "overflow")) {
+ if (!strcmp(value, "visible")) symbol->overflowVisible = true;
+ } else {
+ return _attrParseGNode(data, key, value);
+ }
+
+ return true;
+}
+
+
+static SvgNode* _createNode(SvgNode* parent, SvgNodeType type)
+{
+ SvgNode* node = (SvgNode*)calloc(1, sizeof(SvgNode));
+
+ if (!node) return nullptr;
+
+ //Default fill property
+ node->style = (SvgStyleProperty*)calloc(1, sizeof(SvgStyleProperty));
+
+ if (!node->style) {
+ free(node);
+ return nullptr;
+ }
+
+ //Update the default value of stroke and fill
+ //https://www.w3.org/TR/SVGTiny12/painting.html#SpecifyingPaint
+ node->style->fill.paint.none = false;
+ //Default fill opacity is 1
+ node->style->fill.opacity = 255;
+ node->style->opacity = 255;
+ //Default current color is not set
+ node->style->fill.paint.curColor = false;
+ node->style->curColorSet = false;
+ //Default fill rule is nonzero
+ node->style->fill.fillRule = FillRule::Winding;
+
+ //Default stroke is none
+ node->style->stroke.paint.none = true;
+ //Default stroke opacity is 1
+ node->style->stroke.opacity = 255;
+ //Default stroke current color is not set
+ node->style->stroke.paint.curColor = false;
+ //Default stroke width is 1
+ node->style->stroke.width = 1;
+ //Default line cap is butt
+ node->style->stroke.cap = StrokeCap::Butt;
+ //Default line join is miter
+ node->style->stroke.join = StrokeJoin::Miter;
+ node->style->stroke.scale = 1.0;
+
+ //Default display is true("inline").
+ node->display = true;
+
+ node->parent = parent;
+ node->type = type;
+
+ if (parent) parent->child.push(node);
+ return node;
+}
+
+
+static SvgNode* _createDefsNode(TVG_UNUSED SvgLoaderData* loader, TVG_UNUSED SvgNode* parent, const char* buf, unsigned bufLength, TVG_UNUSED parseAttributes func)
+{
+ if (loader->def && loader->doc->node.doc.defs) return loader->def;
+ SvgNode* node = _createNode(nullptr, SvgNodeType::Defs);
+
+ loader->def = node;
+ loader->doc->node.doc.defs = node;
+ return node;
+}
+
+
+static SvgNode* _createGNode(TVG_UNUSED SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::G);
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParseGNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createSvgNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Doc);
+ if (!loader->svgParse->node) return nullptr;
+ SvgDocNode* doc = &(loader->svgParse->node->node.doc);
+
+ loader->svgParse->global.w = 0;
+ loader->svgParse->global.h = 0;
+
+ doc->preserveAspect = true;
+ func(buf, bufLength, _attrParseSvgNode, loader);
+
+ if (loader->svgParse->global.w == 0) {
+ if (doc->w < FLT_EPSILON) loader->svgParse->global.w = 1;
+ else loader->svgParse->global.w = (uint32_t)doc->w;
+ }
+ if (loader->svgParse->global.h == 0) {
+ if (doc->h < FLT_EPSILON) loader->svgParse->global.h = 1;
+ else loader->svgParse->global.h = (uint32_t)doc->h;
+ }
+
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createMaskNode(SvgLoaderData* loader, SvgNode* parent, TVG_UNUSED const char* buf, TVG_UNUSED unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Mask);
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->node.mask.userSpace = true;
+ loader->svgParse->node->node.mask.type = SvgMaskType::Luminance;
+
+ func(buf, bufLength, _attrParseMaskNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createClipPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::ClipPath);
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->display = false;
+ loader->svgParse->node->node.clip.userSpace = true;
+
+ func(buf, bufLength, _attrParseClipPathNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createCssStyleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::CssStyle);
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParseCssStyleNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createSymbolNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Symbol);
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->display = false;
+ loader->svgParse->node->node.symbol.preserveAspect = true;
+ loader->svgParse->node->node.symbol.overflowVisible = false;
+
+ func(buf, bufLength, _attrParseSymbolNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static bool _attrParsePathNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgPathNode* path = &(node->node.path);
+
+ if (!strcmp(key, "d")) {
+ //Temporary: need to copy
+ path->path = _copyId(value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createPathNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Path);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParsePathNode, loader);
+
+ return loader->svgParse->node;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} circleTags[] = {
+ {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgCircleNode, cx)},
+ {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgCircleNode, cy)},
+ {"r", SvgParserLengthType::Other, sizeof("r"), offsetof(SvgCircleNode, r)}
+};
+
+
+/* parse the attributes for a circle element.
+ * https://www.w3.org/TR/SVG/shapes.html#CircleElement
+ */
+static bool _attrParseCircleNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgCircleNode* circle = &(node->node.circle);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)circle;
+ for (unsigned int i = 0; i < sizeof(circleTags) / sizeof(circleTags[0]); i++) {
+ if (circleTags[i].sz - 1 == sz && !strncmp(circleTags[i].tag, key, sz)) {
+ *((float*)(array + circleTags[i].offset)) = _toFloat(loader->svgParse, value, circleTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createCircleNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Circle);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParseCircleNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} ellipseTags[] = {
+ {"cx", SvgParserLengthType::Horizontal, sizeof("cx"), offsetof(SvgEllipseNode, cx)},
+ {"cy", SvgParserLengthType::Vertical, sizeof("cy"), offsetof(SvgEllipseNode, cy)},
+ {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgEllipseNode, rx)},
+ {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgEllipseNode, ry)}
+};
+
+
+/* parse the attributes for an ellipse element.
+ * https://www.w3.org/TR/SVG/shapes.html#EllipseElement
+ */
+static bool _attrParseEllipseNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgEllipseNode* ellipse = &(node->node.ellipse);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)ellipse;
+ for (unsigned int i = 0; i < sizeof(ellipseTags) / sizeof(ellipseTags[0]); i++) {
+ if (ellipseTags[i].sz - 1 == sz && !strncmp(ellipseTags[i].tag, key, sz)) {
+ *((float*)(array + ellipseTags[i].offset)) = _toFloat(loader->svgParse, value, ellipseTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createEllipseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Ellipse);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParseEllipseNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static bool _attrParsePolygonPoints(const char* str, float** points, int* ptCount)
+{
+ float tmp[50];
+ int tmpCount = 0;
+ int count = 0;
+ float num;
+ float *pointArray = nullptr, *tmpArray;
+
+ while (_parseNumber(&str, &num)) {
+ tmp[tmpCount++] = num;
+ if (tmpCount == 50) {
+ tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
+ if (!tmpArray) goto error_alloc;
+ pointArray = tmpArray;
+ memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
+ count += tmpCount;
+ tmpCount = 0;
+ }
+ }
+
+ if (tmpCount > 0) {
+ tmpArray = (float*)realloc(pointArray, (count + tmpCount) * sizeof(float));
+ if (!tmpArray) goto error_alloc;
+ pointArray = tmpArray;
+ memcpy(&pointArray[count], tmp, tmpCount * sizeof(float));
+ count += tmpCount;
+ }
+ *ptCount = count;
+ *points = pointArray;
+ return true;
+
+error_alloc:
+ return false;
+}
+
+
+/* parse the attributes for a polygon element.
+ * https://www.w3.org/TR/SVG/shapes.html#PolylineElement
+ */
+static bool _attrParsePolygonNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgPolygonNode* polygon = nullptr;
+
+ if (node->type == SvgNodeType::Polygon) polygon = &(node->node.polygon);
+ else polygon = &(node->node.polyline);
+
+ if (!strcmp(key, "points")) {
+ return _attrParsePolygonPoints(value, &polygon->points, &polygon->pointsCount);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createPolygonNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Polygon);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParsePolygonNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _createPolylineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Polyline);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParsePolygonNode, loader);
+ return loader->svgParse->node;
+}
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} rectTags[] = {
+ {"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)},
+ {"rx", SvgParserLengthType::Horizontal, sizeof("rx"), offsetof(SvgRectNode, rx)},
+ {"ry", SvgParserLengthType::Vertical, sizeof("ry"), offsetof(SvgRectNode, ry)}
+};
+
+
+/* parse the attributes for a rect element.
+ * https://www.w3.org/TR/SVG/shapes.html#RectElement
+ */
+static bool _attrParseRectNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgRectNode* rect = &(node->node.rect);
+ unsigned char* array;
+ bool ret = true;
+ int sz = strlen(key);
+
+ array = (unsigned char*)rect;
+ for (unsigned int i = 0; i < sizeof(rectTags) / sizeof(rectTags[0]); i++) {
+ if (rectTags[i].sz - 1 == sz && !strncmp(rectTags[i].tag, key, sz)) {
+ *((float*)(array + rectTags[i].offset)) = _toFloat(loader->svgParse, value, rectTags[i].type);
+
+ //Case if only rx or ry is declared
+ if (!strncmp(rectTags[i].tag, "rx", sz)) rect->hasRx = true;
+ if (!strncmp(rectTags[i].tag, "ry", sz)) rect->hasRy = true;
+
+ if ((rect->rx >= FLT_EPSILON) && (rect->ry < FLT_EPSILON) && rect->hasRx && !rect->hasRy) rect->ry = rect->rx;
+ if ((rect->ry >= FLT_EPSILON) && (rect->rx < FLT_EPSILON) && !rect->hasRx && rect->hasRy) rect->rx = rect->ry;
+ return ret;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else if (!strcmp(key, "style")) {
+ ret = simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ ret = _parseStyleAttr(loader, key, value, false);
+ }
+
+ return ret;
+}
+
+
+static SvgNode* _createRectNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Rect);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->node.rect.hasRx = loader->svgParse->node->node.rect.hasRy = false;
+
+ func(buf, bufLength, _attrParseRectNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} lineTags[] = {
+ {"x1", SvgParserLengthType::Horizontal, sizeof("x1"), offsetof(SvgLineNode, x1)},
+ {"y1", SvgParserLengthType::Vertical, sizeof("y1"), offsetof(SvgLineNode, y1)},
+ {"x2", SvgParserLengthType::Horizontal, sizeof("x2"), offsetof(SvgLineNode, x2)},
+ {"y2", SvgParserLengthType::Vertical, sizeof("y2"), offsetof(SvgLineNode, y2)}
+};
+
+
+/* parse the attributes for a line element.
+ * https://www.w3.org/TR/SVG/shapes.html#LineElement
+ */
+static bool _attrParseLineNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgLineNode* line = &(node->node.line);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)line;
+ for (unsigned int i = 0; i < sizeof(lineTags) / sizeof(lineTags[0]); i++) {
+ if (lineTags[i].sz - 1 == sz && !strncmp(lineTags[i].tag, key, sz)) {
+ *((float*)(array + lineTags[i].offset)) = _toFloat(loader->svgParse, value, lineTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else {
+ return _parseStyleAttr(loader, key, value, false);
+ }
+ return true;
+}
+
+
+static SvgNode* _createLineNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Line);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParseLineNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static char* _idFromHref(const char* href)
+{
+ href = _skipSpace(href, nullptr);
+ if ((*href) == '#') href++;
+ return strdup(href);
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} imageTags[] = {
+ {"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)},
+};
+
+
+/* parse the attributes for a image element.
+ * https://www.w3.org/TR/SVG/embedded.html#ImageElement
+ */
+static bool _attrParseImageNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode* node = loader->svgParse->node;
+ SvgImageNode* image = &(node->node.image);
+ unsigned char* array;
+ int sz = strlen(key);
+
+ array = (unsigned char*)image;
+ for (unsigned int i = 0; i < sizeof(imageTags) / sizeof(imageTags[0]); i++) {
+ if (imageTags[i].sz - 1 == sz && !strncmp(imageTags[i].tag, key, sz)) {
+ *((float*)(array + imageTags[i].offset)) = _toFloat(loader->svgParse, value, imageTags[i].type);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (image->href && value) free(image->href);
+ image->href = _idFromHref(value);
+ } else if (!strcmp(key, "id")) {
+ if (node->id && value) free(node->id);
+ node->id = _copyId(value);
+ } else if (!strcmp(key, "class")) {
+ _handleCssClassAttr(loader, node, value);
+ } else if (!strcmp(key, "style")) {
+ return simpleXmlParseW3CAttribute(value, strlen(value), _parseStyleAttr, loader);
+ } else if (!strcmp(key, "clip-path")) {
+ _handleClipPathAttr(loader, node, value);
+ } else if (!strcmp(key, "mask")) {
+ _handleMaskAttr(loader, node, value);
+ } else if (!strcmp(key, "transform")) {
+ node->transform = _parseTransformationMatrix(value);
+ } else {
+ return _parseStyleAttr(loader, key, value);
+ }
+ return true;
+}
+
+
+static SvgNode* _createImageNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Image);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ func(buf, bufLength, _attrParseImageNode, loader);
+ return loader->svgParse->node;
+}
+
+
+static SvgNode* _getDefsNode(SvgNode* node)
+{
+ if (!node) return nullptr;
+
+ while (node->parent != nullptr) {
+ node = node->parent;
+ }
+
+ if (node->type == SvgNodeType::Doc) return node->node.doc.defs;
+
+ return nullptr;
+}
+
+
+static SvgNode* _findChildById(const SvgNode* node, const char* id)
+{
+ if (!node) return nullptr;
+
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ if (((*child)->id) && !strcmp((*child)->id, id)) return (*child);
+ }
+ return nullptr;
+}
+
+static SvgNode* _findNodeById(SvgNode *node, const char* id)
+{
+ SvgNode* result = nullptr;
+ if (node->id && !strcmp(node->id, id)) return node;
+
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ result = _findNodeById(*child, id);
+ if (result) break;
+ }
+ }
+ return result;
+}
+
+static void _cloneGradStops(Array<Fill::ColorStop>& dst, const Array<Fill::ColorStop>& src)
+{
+ for (uint32_t i = 0; i < src.count; ++i) {
+ dst.push(src.data[i]);
+ }
+}
+
+
+static SvgStyleGradient* _cloneGradient(SvgStyleGradient* from)
+{
+ if (!from) return nullptr;
+
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ if (!grad) return nullptr;
+
+ grad->type = from->type;
+ grad->id = from->id ? _copyId(from->id) : nullptr;
+ grad->ref = from->ref ? _copyId(from->ref) : nullptr;
+ grad->spread = from->spread;
+ grad->userSpace = from->userSpace;
+
+ if (from->transform) {
+ grad->transform = (Matrix*)calloc(1, sizeof(Matrix));
+ if (grad->transform) memcpy(grad->transform, from->transform, sizeof(Matrix));
+ }
+
+ if (grad->type == SvgGradientType::Linear) {
+ grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
+ if (!grad->linear) goto error_grad_alloc;
+ memcpy(grad->linear, from->linear, sizeof(SvgLinearGradient));
+ } else if (grad->type == SvgGradientType::Radial) {
+ grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
+ if (!grad->radial) goto error_grad_alloc;
+ memcpy(grad->radial, from->radial, sizeof(SvgRadialGradient));
+ }
+
+ _cloneGradStops(grad->stops, from->stops);
+
+ return grad;
+
+error_grad_alloc:
+ if (grad) {
+ grad->clear();
+ free(grad);
+ }
+ return nullptr;
+}
+
+
+static void _styleInherit(SvgStyleProperty* child, const SvgStyleProperty* parent)
+{
+ if (parent == nullptr) return;
+ //Inherit the property of parent if not present in child.
+ if (!child->curColorSet) {
+ child->color = parent->color;
+ child->curColorSet = parent->curColorSet;
+ }
+ //Fill
+ if (!((int)child->fill.flags & (int)SvgFillFlags::Paint)) {
+ child->fill.paint.color = parent->fill.paint.color;
+ child->fill.paint.none = parent->fill.paint.none;
+ child->fill.paint.curColor = parent->fill.paint.curColor;
+ if (parent->fill.paint.url) child->fill.paint.url = _copyId(parent->fill.paint.url);
+ }
+ if (!((int)child->fill.flags & (int)SvgFillFlags::Opacity)) {
+ child->fill.opacity = parent->fill.opacity;
+ }
+ if (!((int)child->fill.flags & (int)SvgFillFlags::FillRule)) {
+ child->fill.fillRule = parent->fill.fillRule;
+ }
+ //Stroke
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Paint)) {
+ child->stroke.paint.color = parent->stroke.paint.color;
+ child->stroke.paint.none = parent->stroke.paint.none;
+ child->stroke.paint.curColor = parent->stroke.paint.curColor;
+ child->stroke.paint.url = parent->stroke.paint.url ? _copyId(parent->stroke.paint.url) : nullptr;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
+ child->stroke.opacity = parent->stroke.opacity;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Width)) {
+ child->stroke.width = parent->stroke.width;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Dash)) {
+ if (parent->stroke.dash.array.count > 0) {
+ child->stroke.dash.array.clear();
+ child->stroke.dash.array.reserve(parent->stroke.dash.array.count);
+ for (uint32_t i = 0; i < parent->stroke.dash.array.count; ++i) {
+ child->stroke.dash.array.push(parent->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Cap)) {
+ child->stroke.cap = parent->stroke.cap;
+ }
+ if (!((int)child->stroke.flags & (int)SvgStrokeFlags::Join)) {
+ child->stroke.join = parent->stroke.join;
+ }
+}
+
+
+static void _styleCopy(SvgStyleProperty* to, const SvgStyleProperty* from)
+{
+ if (from == nullptr) return;
+ //Copy the properties of 'from' only if they were explicitly set (not the default ones).
+ if (from->curColorSet) {
+ to->color = from->color;
+ to->curColorSet = true;
+ }
+ //Fill
+ to->fill.flags = (SvgFillFlags)((int)to->fill.flags | (int)from->fill.flags);
+ if (((int)from->fill.flags & (int)SvgFillFlags::Paint)) {
+ to->fill.paint.color = from->fill.paint.color;
+ to->fill.paint.none = from->fill.paint.none;
+ to->fill.paint.curColor = from->fill.paint.curColor;
+ if (from->fill.paint.url) to->fill.paint.url = _copyId(from->fill.paint.url);
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::Opacity)) {
+ to->fill.opacity = from->fill.opacity;
+ }
+ if (((int)from->fill.flags & (int)SvgFillFlags::FillRule)) {
+ to->fill.fillRule = from->fill.fillRule;
+ }
+ //Stroke
+ to->stroke.flags = (SvgStrokeFlags)((int)to->stroke.flags | (int)from->stroke.flags);
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Paint)) {
+ to->stroke.paint.color = from->stroke.paint.color;
+ to->stroke.paint.none = from->stroke.paint.none;
+ to->stroke.paint.curColor = from->stroke.paint.curColor;
+ to->stroke.paint.url = from->stroke.paint.url ? _copyId(from->stroke.paint.url) : nullptr;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Opacity)) {
+ to->stroke.opacity = from->stroke.opacity;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Width)) {
+ to->stroke.width = from->stroke.width;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Dash)) {
+ if (from->stroke.dash.array.count > 0) {
+ to->stroke.dash.array.clear();
+ to->stroke.dash.array.reserve(from->stroke.dash.array.count);
+ for (uint32_t i = 0; i < from->stroke.dash.array.count; ++i) {
+ to->stroke.dash.array.push(from->stroke.dash.array.data[i]);
+ }
+ }
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Cap)) {
+ to->stroke.cap = from->stroke.cap;
+ }
+ if (((int)from->stroke.flags & (int)SvgStrokeFlags::Join)) {
+ to->stroke.join = from->stroke.join;
+ }
+}
+
+
+static void _copyAttr(SvgNode* to, const SvgNode* from)
+{
+ //Copy matrix attribute
+ if (from->transform) {
+ to->transform = (Matrix*)malloc(sizeof(Matrix));
+ if (to->transform) *to->transform = *from->transform;
+ }
+ //Copy style attribute
+ _styleCopy(to->style, from->style);
+ to->style->flags = (SvgStyleFlags)((int)to->style->flags | (int)from->style->flags);
+ if (from->style->fill.paint.url) to->style->fill.paint.url = strdup(from->style->fill.paint.url);
+ if (from->style->stroke.paint.url) to->style->stroke.paint.url = strdup(from->style->stroke.paint.url);
+ if (from->style->clipPath.url) to->style->clipPath.url = strdup(from->style->clipPath.url);
+ if (from->style->mask.url) to->style->mask.url = strdup(from->style->mask.url);
+
+ //Copy node attribute
+ switch (from->type) {
+ case SvgNodeType::Circle: {
+ to->node.circle.cx = from->node.circle.cx;
+ to->node.circle.cy = from->node.circle.cy;
+ to->node.circle.r = from->node.circle.r;
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ to->node.ellipse.cx = from->node.ellipse.cx;
+ to->node.ellipse.cy = from->node.ellipse.cy;
+ to->node.ellipse.rx = from->node.ellipse.rx;
+ to->node.ellipse.ry = from->node.ellipse.ry;
+ break;
+ }
+ case SvgNodeType::Rect: {
+ to->node.rect.x = from->node.rect.x;
+ to->node.rect.y = from->node.rect.y;
+ to->node.rect.w = from->node.rect.w;
+ to->node.rect.h = from->node.rect.h;
+ to->node.rect.rx = from->node.rect.rx;
+ to->node.rect.ry = from->node.rect.ry;
+ to->node.rect.hasRx = from->node.rect.hasRx;
+ to->node.rect.hasRy = from->node.rect.hasRy;
+ break;
+ }
+ case SvgNodeType::Line: {
+ to->node.line.x1 = from->node.line.x1;
+ to->node.line.y1 = from->node.line.y1;
+ to->node.line.x2 = from->node.line.x2;
+ to->node.line.y2 = from->node.line.y2;
+ break;
+ }
+ case SvgNodeType::Path: {
+ if (from->node.path.path) to->node.path.path = strdup(from->node.path.path);
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ if ((to->node.polygon.pointsCount = from->node.polygon.pointsCount)) {
+ to->node.polygon.points = (float*)malloc(to->node.polygon.pointsCount * sizeof(float));
+ memcpy(to->node.polygon.points, from->node.polygon.points, to->node.polygon.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ if ((to->node.polyline.pointsCount = from->node.polyline.pointsCount)) {
+ to->node.polyline.points = (float*)malloc(to->node.polyline.pointsCount * sizeof(float));
+ memcpy(to->node.polyline.points, from->node.polyline.points, to->node.polyline.pointsCount * sizeof(float));
+ }
+ break;
+ }
+ case SvgNodeType::Image: {
+ to->node.image.x = from->node.image.x;
+ to->node.image.y = from->node.image.y;
+ to->node.image.w = from->node.image.w;
+ to->node.image.h = from->node.image.h;
+ if (from->node.image.href) to->node.image.href = strdup(from->node.image.href);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+
+static void _cloneNode(SvgNode* from, SvgNode* parent, int depth)
+{
+ /* Exception handling: Prevent invalid SVG data input.
+ The size is the arbitrary value, we need an experimental size. */
+ if (depth == 8192) {
+ TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
+ return;
+ }
+
+ SvgNode* newNode;
+ if (!from || !parent || from == parent) return;
+
+ newNode = _createNode(parent, from->type);
+ if (!newNode) return;
+
+ _styleInherit(newNode->style, parent->style);
+ _copyAttr(newNode, from);
+
+ auto child = from->child.data;
+ for (uint32_t i = 0; i < from->child.count; ++i, ++child) {
+ _cloneNode(*child, newNode, depth + 1);
+ }
+}
+
+
+static void _clonePostponedNodes(Array<SvgNodeIdPair>* cloneNodes, SvgNode* doc)
+{
+ for (uint32_t i = 0; i < cloneNodes->count; ++i) {
+ auto nodeIdPair = cloneNodes->data[i];
+ auto defs = _getDefsNode(nodeIdPair.node);
+ auto nodeFrom = _findChildById(defs, nodeIdPair.id);
+ if (!nodeFrom) nodeFrom = _findChildById(doc, nodeIdPair.id);
+ _cloneNode(nodeFrom, nodeIdPair.node, 0);
+ if (nodeFrom && nodeFrom->type == SvgNodeType::Symbol && nodeIdPair.node->type == SvgNodeType::Use) {
+ nodeIdPair.node->node.use.symbol = nodeFrom;
+ }
+ free(nodeIdPair.id);
+ }
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ SvgParserLengthType type;
+ int sz;
+ size_t offset;
+} useTags[] = {
+ {"x", SvgParserLengthType::Horizontal, sizeof("x"), offsetof(SvgUseNode, x)},
+ {"y", SvgParserLengthType::Vertical, sizeof("y"), offsetof(SvgUseNode, y)},
+ {"width", SvgParserLengthType::Horizontal, sizeof("width"), offsetof(SvgUseNode, w)},
+ {"height", SvgParserLengthType::Vertical, sizeof("height"), offsetof(SvgUseNode, h)}
+};
+
+
+static bool _attrParseUseNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgNode *defs, *nodeFrom, *node = loader->svgParse->node;
+ char* 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);
+
+ if (useTags[i].offset == offsetof(SvgUseNode, w)) use->isWidthSet = true;
+ else if (useTags[i].offset == offsetof(SvgUseNode, h)) use->isHeightSet = true;
+
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ id = _idFromHref(value);
+ defs = _getDefsNode(node);
+ nodeFrom = _findChildById(defs, id);
+ if (nodeFrom) {
+ _cloneNode(nodeFrom, node, 0);
+ if (nodeFrom->type == SvgNodeType::Symbol) use->symbol = nodeFrom;
+ free(id);
+ } else {
+ //some svg export software include <defs> element at the end of the file
+ //if so the 'from' element won't be found now and we have to repeat finding
+ //after the whole file is parsed
+ _postpone(loader->cloneNodes, node, id);
+ }
+ } else {
+ return _attrParseGNode(data, key, value);
+ }
+ return true;
+}
+
+
+static SvgNode* _createUseNode(SvgLoaderData* loader, SvgNode* parent, const char* buf, unsigned bufLength, parseAttributes func)
+{
+ loader->svgParse->node = _createNode(parent, SvgNodeType::Use);
+
+ if (!loader->svgParse->node) return nullptr;
+
+ loader->svgParse->node->node.use.isWidthSet = false;
+ loader->svgParse->node->node.use.isHeightSet = false;
+
+ func(buf, bufLength, _attrParseUseNode, loader);
+ return loader->svgParse->node;
+}
+
+
+//TODO: Implement 'text' primitive
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ FactoryMethod tagHandler;
+} graphicsTags[] = {
+ {"use", sizeof("use"), _createUseNode},
+ {"circle", sizeof("circle"), _createCircleNode},
+ {"ellipse", sizeof("ellipse"), _createEllipseNode},
+ {"path", sizeof("path"), _createPathNode},
+ {"polygon", sizeof("polygon"), _createPolygonNode},
+ {"rect", sizeof("rect"), _createRectNode},
+ {"polyline", sizeof("polyline"), _createPolylineNode},
+ {"line", sizeof("line"), _createLineNode},
+ {"image", sizeof("image"), _createImageNode}
+};
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ FactoryMethod tagHandler;
+} groupTags[] = {
+ {"defs", sizeof("defs"), _createDefsNode},
+ {"g", sizeof("g"), _createGNode},
+ {"svg", sizeof("svg"), _createSvgNode},
+ {"mask", sizeof("mask"), _createMaskNode},
+ {"clipPath", sizeof("clipPath"), _createClipPathNode},
+ {"style", sizeof("style"), _createCssStyleNode},
+ {"symbol", sizeof("symbol"), _createSymbolNode}
+};
+
+
+#define FIND_FACTORY(Short_Name, Tags_Array) \
+ static FactoryMethod \
+ _find##Short_Name##Factory(const char* name) \
+ { \
+ unsigned int i; \
+ int sz = strlen(name); \
+ \
+ for (i = 0; i < sizeof(Tags_Array) / sizeof(Tags_Array[0]); i++) { \
+ if (Tags_Array[i].sz - 1 == sz && !strncmp(Tags_Array[i].tag, name, sz)) { \
+ return Tags_Array[i].tagHandler; \
+ } \
+ } \
+ return nullptr; \
+ }
+
+FIND_FACTORY(Group, groupTags)
+FIND_FACTORY(Graphics, graphicsTags)
+
+
+FillSpread _parseSpreadValue(const char* value)
+{
+ auto spread = FillSpread::Pad;
+
+ if (!strcmp(value, "reflect")) {
+ spread = FillSpread::Reflect;
+ } else if (!strcmp(value, "repeat")) {
+ spread = FillSpread::Repeat;
+ }
+
+ return spread;
+}
+
+
+static void _handleRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ 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, 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, radial->isFxPercentage);
+ loader->svgParse->gradient.parsedFx = true;
+}
+
+
+static void _handleRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value)
+{
+ 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, radial->isRPercentage);
+}
+
+
+static void _recalcRadialCxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isCxPercentage) radial->cx = radial->cx / loader->svgParse->global.w;
+}
+
+
+static void _recalcRadialCyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isCyPercentage) radial->cy = radial->cy / loader->svgParse->global.h;
+}
+
+
+static void _recalcRadialFxAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isFxPercentage) radial->fx = radial->fx / loader->svgParse->global.w;
+}
+
+
+static void _recalcRadialFyAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ if (userSpace && !radial->isFyPercentage) radial->fy = radial->fy / loader->svgParse->global.h;
+}
+
+
+static void _recalcRadialRAttr(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace)
+{
+ // 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));
+}
+
+
+typedef void (*radialMethod)(SvgLoaderData* loader, SvgRadialGradient* radial, const char* value);
+typedef void (*radialMethodRecalc)(SvgLoaderData* loader, SvgRadialGradient* radial, bool userSpace);
+
+
+#define RADIAL_DEF(Name, Name1) \
+ { \
+#Name, sizeof(#Name), _handleRadial##Name1##Attr, _recalcRadial##Name1##Attr \
+ }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ radialMethod tagHandler;
+ radialMethodRecalc tagRecalc;
+} radialTags[] = {
+ RADIAL_DEF(cx, Cx),
+ RADIAL_DEF(cy, Cy),
+ RADIAL_DEF(fx, Fx),
+ RADIAL_DEF(fy, Fy),
+ RADIAL_DEF(r, R)
+};
+
+
+static bool _attrParseRadialGradientNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgStyleGradient* grad = loader->svgParse->styleGrad;
+ SvgRadialGradient* radial = grad->radial;
+ int sz = strlen(key);
+
+ for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
+ if (radialTags[i].sz - 1 == sz && !strncmp(radialTags[i].tag, key, sz)) {
+ radialTags[i].tagHandler(loader, radial, value);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (grad->id && value) free(grad->id);
+ grad->id = _copyId(value);
+ } else if (!strcmp(key, "spreadMethod")) {
+ grad->spread = _parseSpreadValue(value);
+ } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (grad->ref && value) free(grad->ref);
+ grad->ref = _idFromHref(value);
+ } else if (!strcmp(key, "gradientUnits")) {
+ if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ } else if (!strcmp(key, "gradientTransform")) {
+ grad->transform = _parseTransformationMatrix(value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static SvgStyleGradient* _createRadialGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
+{
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ loader->svgParse->styleGrad = grad;
+
+ grad->type = SvgGradientType::Radial;
+ grad->userSpace = false;
+ grad->radial = (SvgRadialGradient*)calloc(1, sizeof(SvgRadialGradient));
+ if (!grad->radial) {
+ grad->clear();
+ free(grad);
+ return nullptr;
+ }
+ /**
+ * Default values of gradient transformed into global percentage
+ */
+ 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;
+ simpleXmlParseAttributes(buf, bufLength,
+ _attrParseRadialGradientNode, loader);
+
+ for (unsigned int i = 0; i < sizeof(radialTags) / sizeof(radialTags[0]); i++) {
+ radialTags[i].tagRecalc(loader, grad->radial, grad->userSpace);
+ }
+
+ return loader->svgParse->styleGrad;
+}
+
+
+static bool _attrParseStopsStyle(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ auto stop = &loader->svgParse->gradStop;
+
+ if (!strcmp(key, "stop-opacity")) {
+ stop->a = _toOpacity(value);
+ loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopOpacity);
+ } else if (!strcmp(key, "stop-color")) {
+ _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
+ loader->svgParse->flags = (SvgStopStyleFlags)((int)loader->svgParse->flags | (int)SvgStopStyleFlags::StopColor);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static bool _attrParseStops(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ auto stop = &loader->svgParse->gradStop;
+
+ if (!strcmp(key, "offset")) {
+ stop->offset = _toOffset(value);
+ } else if (!strcmp(key, "stop-opacity")) {
+ if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopOpacity)) {
+ stop->a = _toOpacity(value);
+ }
+ } else if (!strcmp(key, "stop-color")) {
+ if (!((int)loader->svgParse->flags & (int)SvgStopStyleFlags::StopColor)) {
+ _toColor(value, &stop->r, &stop->g, &stop->b, nullptr);
+ }
+ } else if (!strcmp(key, "style")) {
+ simpleXmlParseW3CAttribute(value, strlen(value), _attrParseStopsStyle, data);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static void _handleLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->x1 = _gradientToFloat(loader->svgParse, value, linear->isX1Percentage);
+}
+
+
+static void _handleLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->y1 = _gradientToFloat(loader->svgParse, value, linear->isY1Percentage);
+}
+
+
+static void _handleLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->x2 = _gradientToFloat(loader->svgParse, value, linear->isX2Percentage);
+}
+
+
+static void _handleLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value)
+{
+ linear->y2 = _gradientToFloat(loader->svgParse, value, linear->isY2Percentage);
+}
+
+
+static void _recalcLinearX1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isX1Percentage) linear->x1 = linear->x1 / loader->svgParse->global.w;
+}
+
+
+static void _recalcLinearY1Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isY1Percentage) linear->y1 = linear->y1 / loader->svgParse->global.h;
+}
+
+
+static void _recalcLinearX2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isX2Percentage) linear->x2 = linear->x2 / loader->svgParse->global.w;
+}
+
+
+static void _recalcLinearY2Attr(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace)
+{
+ if (userSpace && !linear->isY2Percentage) linear->y2 = linear->y2 / loader->svgParse->global.h;
+}
+
+
+typedef void (*Linear_Method)(SvgLoaderData* loader, SvgLinearGradient* linear, const char* value);
+typedef void (*Linear_Method_Recalc)(SvgLoaderData* loader, SvgLinearGradient* linear, bool userSpace);
+
+
+#define LINEAR_DEF(Name, Name1) \
+ { \
+#Name, sizeof(#Name), _handleLinear##Name1##Attr, _recalcLinear##Name1##Attr \
+ }
+
+
+static constexpr struct
+{
+ const char* tag;
+ int sz;
+ Linear_Method tagHandler;
+ Linear_Method_Recalc tagRecalc;
+} linear_tags[] = {
+ LINEAR_DEF(x1, X1),
+ LINEAR_DEF(y1, Y1),
+ LINEAR_DEF(x2, X2),
+ LINEAR_DEF(y2, Y2)
+};
+
+
+static bool _attrParseLinearGradientNode(void* data, const char* key, const char* value)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ SvgStyleGradient* grad = loader->svgParse->styleGrad;
+ SvgLinearGradient* linear = grad->linear;
+ int sz = strlen(key);
+
+ for (unsigned int i = 0; i < sizeof(linear_tags) / sizeof(linear_tags[0]); i++) {
+ if (linear_tags[i].sz - 1 == sz && !strncmp(linear_tags[i].tag, key, sz)) {
+ linear_tags[i].tagHandler(loader, linear, value);
+ return true;
+ }
+ }
+
+ if (!strcmp(key, "id")) {
+ if (grad->id && value) free(grad->id);
+ grad->id = _copyId(value);
+ } else if (!strcmp(key, "spreadMethod")) {
+ grad->spread = _parseSpreadValue(value);
+ } else if (!strcmp(key, "href") || !strcmp(key, "xlink:href")) {
+ if (grad->ref && value) free(grad->ref);
+ grad->ref = _idFromHref(value);
+ } else if (!strcmp(key, "gradientUnits")) {
+ if (!strcmp(value, "userSpaceOnUse")) grad->userSpace = true;
+ } else if (!strcmp(key, "gradientTransform")) {
+ grad->transform = _parseTransformationMatrix(value);
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+
+static SvgStyleGradient* _createLinearGradient(SvgLoaderData* loader, const char* buf, unsigned bufLength)
+{
+ auto grad = (SvgStyleGradient*)(calloc(1, sizeof(SvgStyleGradient)));
+ loader->svgParse->styleGrad = grad;
+
+ grad->type = SvgGradientType::Linear;
+ grad->userSpace = false;
+ grad->linear = (SvgLinearGradient*)calloc(1, sizeof(SvgLinearGradient));
+ if (!grad->linear) {
+ grad->clear();
+ free(grad);
+ return nullptr;
+ }
+ /**
+ * Default value of x2 is 100% - transformed to the global percentage
+ */
+ 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++) {
+ linear_tags[i].tagRecalc(loader, grad->linear, grad->userSpace);
+ }
+
+ return loader->svgParse->styleGrad;
+}
+
+
+#define GRADIENT_DEF(Name, Name1) \
+ { \
+#Name, sizeof(#Name), _create##Name1 \
+ }
+
+
+/**
+ * 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
+{
+ const char* tag;
+ int sz;
+ GradientFactoryMethod tagHandler;
+} gradientTags[] = {
+ GRADIENT_DEF(linearGradient, LinearGradient),
+ GRADIENT_DEF(radialGradient, RadialGradient)
+};
+
+
+static GradientFactoryMethod _findGradientFactory(const char* name)
+{
+ int sz = strlen(name);
+
+ for (unsigned int i = 0; i < sizeof(gradientTags) / sizeof(gradientTags[0]); i++) {
+ if (gradientTags[i].sz - 1 == sz && !strncmp(gradientTags[i].tag, name, sz)) {
+ return gradientTags[i].tagHandler;
+ }
+ }
+ return nullptr;
+}
+
+
+static constexpr struct
+{
+ const char* tag;
+ size_t sz;
+} popArray[] = {
+ {"g", sizeof("g")},
+ {"svg", sizeof("svg")},
+ {"defs", sizeof("defs")},
+ {"mask", sizeof("mask")},
+ {"clipPath", sizeof("clipPath")},
+ {"style", sizeof("style")},
+ {"symbol", sizeof("symbol")}
+};
+
+
+static void _svgLoaderParserXmlClose(SvgLoaderData* loader, const char* content)
+{
+ content = _skipSpace(content, nullptr);
+
+ for (unsigned int i = 0; i < sizeof(popArray) / sizeof(popArray[0]); i++) {
+ if (!strncmp(content, popArray[i].tag, popArray[i].sz - 1)) {
+ loader->stack.pop();
+ break;
+ }
+ }
+
+ loader->level--;
+}
+
+
+static void _svgLoaderParserXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length, bool empty)
+{
+ const char* attrs = nullptr;
+ int attrsLength = 0;
+ int sz = length;
+ char tagName[20] = "";
+ FactoryMethod method;
+ GradientFactoryMethod gradientMethod;
+ SvgNode *node = nullptr, *parent = nullptr;
+ loader->level++;
+ attrs = simpleXmlFindAttributesTag(content, length);
+
+ if (!attrs) {
+ //Parse the empty tag
+ attrs = content;
+ while ((attrs != nullptr) && *attrs != '>') attrs++;
+ }
+
+ if (attrs) {
+ //Find out the tag name starting from content till sz length
+ sz = attrs - content;
+ while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
+ if ((unsigned)sz >= sizeof(tagName)) return;
+ strncpy(tagName, content, sz);
+ tagName[sz] = '\0';
+ attrsLength = length - sz;
+ }
+
+ if ((method = _findGroupFactory(tagName))) {
+ //Group
+ if (empty) return;
+ if (!loader->doc) {
+ if (strcmp(tagName, "svg")) return; //Not a valid svg document
+ node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
+ loader->doc = node;
+ } else {
+ if (!strcmp(tagName, "svg")) return; //Already loaded <svg>(SvgNodeType::Doc) tag
+ if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ else parent = loader->doc;
+ if (!strcmp(tagName, "style")) {
+ node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
+ loader->cssStyle = node;
+ loader->doc->node.doc.style = node;
+ loader->style = true;
+ } else {
+ node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
+ }
+ }
+
+ if (!node) return;
+ if (node->type != SvgNodeType::Defs || !empty) {
+ loader->stack.push(node);
+ }
+ } else if ((method = _findGraphicsFactory(tagName))) {
+ if (loader->stack.count > 0) parent = loader->stack.data[loader->stack.count - 1];
+ else parent = loader->doc;
+ node = method(loader, parent, attrs, attrsLength, simpleXmlParseAttributes);
+ } else if ((gradientMethod = _findGradientFactory(tagName))) {
+ SvgStyleGradient* gradient;
+ gradient = gradientMethod(loader, attrs, attrsLength);
+ //FIXME: The current parsing structure does not distinguish end tags.
+ // There is no way to know if the currently parsed gradient is in defs.
+ // If a gradient is declared outside of defs after defs is set, it is included in the gradients of defs.
+ // But finally, the loader has a gradient style list regardless of defs.
+ // This is only to support this when multiple gradients are declared, even if no defs are declared.
+ // refer to: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs
+ if (loader->def && loader->doc->node.doc.defs) {
+ loader->def->node.defs.gradients.push(gradient);
+ } else {
+ loader->gradients.push(gradient);
+ }
+ loader->latestGradient = gradient;
+ } else if (!strcmp(tagName, "stop")) {
+ if (!loader->latestGradient) {
+ TVGLOG("SVG", "Stop element is used outside of the Gradient element");
+ return;
+ }
+ /* default value for opacity */
+ loader->svgParse->gradStop = {0.0f, 0, 0, 0, 255};
+ simpleXmlParseAttributes(attrs, attrsLength, _attrParseStops, loader);
+ loader->latestGradient->stops.push(loader->svgParse->gradStop);
+ } else if (!isIgnoreUnsupportedLogElements(tagName)) {
+ TVGLOG("SVG", "Unsupported elements used [Elements: %s]", tagName);
+ }
+}
+
+
+static void _svgLoaderParserXmlCssStyle(SvgLoaderData* loader, const char* content, unsigned int length)
+{
+ char* tag;
+ char* name;
+ const char* attrs = nullptr;
+ unsigned int attrsLength = 0;
+
+ FactoryMethod method;
+ GradientFactoryMethod gradientMethod;
+ SvgNode *node = nullptr;
+
+ while (auto next = simpleXmlParseCSSAttribute(content, length, &tag, &name, &attrs, &attrsLength)) {
+ if ((method = _findGroupFactory(tag))) {
+ if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
+ } else if ((method = _findGraphicsFactory(tag))) {
+ if ((node = method(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
+ } else if ((gradientMethod = _findGradientFactory(tag))) {
+ TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
+ } else if (!strcmp(tag, "stop")) {
+ TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
+ } else if (!strcmp(tag, "all")) {
+ if ((node = _createCssStyleNode(loader, loader->cssStyle, attrs, attrsLength, simpleXmlParseW3CAttribute))) node->id = _copyId(name);
+ } else if (!isIgnoreUnsupportedLogElements(tag)) {
+ TVGLOG("SVG", "Unsupported elements used in the internal CSS style sheets [Elements: %s]", tag);
+ }
+
+ length -= next - content;
+ content = next;
+
+ free(tag);
+ free(name);
+ }
+ loader->style = false;
+}
+
+
+static bool _svgLoaderParser(void* data, SimpleXMLType type, const char* content, unsigned int length)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+
+ switch (type) {
+ case SimpleXMLType::Open: {
+ _svgLoaderParserXmlOpen(loader, content, length, false);
+ break;
+ }
+ case SimpleXMLType::OpenEmpty: {
+ _svgLoaderParserXmlOpen(loader, content, length, true);
+ break;
+ }
+ case SimpleXMLType::Close: {
+ _svgLoaderParserXmlClose(loader, content);
+ break;
+ }
+ case SimpleXMLType::Data:
+ case SimpleXMLType::CData: {
+ if (loader->style) _svgLoaderParserXmlCssStyle(loader, content, length);
+ break;
+ }
+ case SimpleXMLType::DoctypeChild: {
+ break;
+ }
+ case SimpleXMLType::Ignored:
+ case SimpleXMLType::Comment:
+ case SimpleXMLType::Doctype: {
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ return true;
+}
+
+
+static void _inefficientNodeCheck(TVG_UNUSED SvgNode* node)
+{
+#ifdef THORVG_LOG_ENABLED
+ auto type = simpleXmlNodeTypeToString(node->type);
+
+ if (!node->display && node->type != SvgNodeType::ClipPath && node->type != SvgNodeType::Symbol) TVGLOG("SVG", "Inefficient elements used [Display is none][Node Type : %s]", type);
+ if (node->style->opacity == 0) TVGLOG("SVG", "Inefficient elements used [Opacity is zero][Node Type : %s]", type);
+ if (node->style->fill.opacity == 0 && node->style->stroke.opacity == 0) TVGLOG("SVG", "Inefficient elements used [Fill opacity and stroke opacity are zero][Node Type : %s]", type);
+
+ switch (node->type) {
+ case SvgNodeType::Path: {
+ if (!node->node.path.path) TVGLOG("SVG", "Inefficient elements used [Empty path][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ if (node->node.ellipse.rx == 0 && node->node.ellipse.ry == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Polygon:
+ case SvgNodeType::Polyline: {
+ if (node->node.polygon.pointsCount < 2) TVGLOG("SVG", "Inefficient elements used [Invalid Polygon][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Circle: {
+ if (node->node.circle.r == 0) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Rect: {
+ if (node->node.rect.w == 0 && node->node.rect.h) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ case SvgNodeType::Line: {
+ if (node->node.line.x1 == node->node.line.x2 && node->node.line.y1 == node->node.line.y2) TVGLOG("SVG", "Inefficient elements used [Size is zero][Node Type : %s]", type);
+ break;
+ }
+ default: break;
+ }
+#endif
+}
+
+
+static void _updateStyle(SvgNode* node, SvgStyleProperty* parentStyle)
+{
+ _styleInherit(node->style, parentStyle);
+ _inefficientNodeCheck(node);
+
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _updateStyle(*child, node->style);
+ }
+}
+
+
+static SvgStyleGradient* _gradientDup(Array<SvgStyleGradient*>* gradients, const char* id)
+{
+ SvgStyleGradient* result = nullptr;
+
+ auto gradList = gradients->data;
+
+ for (uint32_t i = 0; i < gradients->count; ++i) {
+ if (!strcmp((*gradList)->id, id)) {
+ result = _cloneGradient(*gradList);
+ break;
+ }
+ ++gradList;
+ }
+
+ if (result && result->ref) {
+ gradList = gradients->data;
+ for (uint32_t i = 0; i < gradients->count; ++i) {
+ if (!strcmp((*gradList)->id, result->ref)) {
+ if (result->stops.count == 0) _cloneGradStops(result->stops, (*gradList)->stops);
+ //TODO: Properly inherit other property
+ break;
+ }
+ ++gradList;
+ }
+ }
+
+ return result;
+}
+
+
+static void _updateGradient(SvgNode* node, Array<SvgStyleGradient*>* gradients)
+{
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _updateGradient(*child, gradients);
+ }
+ } else {
+ if (node->style->fill.paint.url) {
+ if (node->style->fill.paint.gradient) {
+ node->style->fill.paint.gradient->clear();
+ free(node->style->fill.paint.gradient);
+ }
+ node->style->fill.paint.gradient = _gradientDup(gradients, node->style->fill.paint.url);
+ }
+ if (node->style->stroke.paint.url) {
+ if (node->style->stroke.paint.gradient) {
+ node->style->stroke.paint.gradient->clear();
+ free(node->style->stroke.paint.gradient);
+ }
+ node->style->stroke.paint.gradient = _gradientDup(gradients, node->style->stroke.paint.url);
+ }
+ }
+}
+
+
+static void _updateComposite(SvgNode* node, SvgNode* root)
+{
+ if (node->style->clipPath.url && !node->style->clipPath.node) {
+ SvgNode* findResult = _findNodeById(root, node->style->clipPath.url);
+ if (findResult) node->style->clipPath.node = findResult;
+ }
+ if (node->style->mask.url && !node->style->mask.node) {
+ SvgNode* findResult = _findNodeById(root, node->style->mask.url);
+ if (findResult) node->style->mask.node = findResult;
+ }
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _updateComposite(*child, root);
+ }
+ }
+}
+
+
+static void _freeNodeStyle(SvgStyleProperty* style)
+{
+ if (!style) return;
+
+ //style->clipPath.node and style->mask.node has only the addresses of node. Therefore, node is released from _freeNode.
+ free(style->clipPath.url);
+ free(style->mask.url);
+ free(style->cssClass);
+
+ if (style->fill.paint.gradient) {
+ style->fill.paint.gradient->clear();
+ free(style->fill.paint.gradient);
+ }
+ if (style->stroke.paint.gradient) {
+ style->stroke.paint.gradient->clear();
+ free(style->stroke.paint.gradient);
+ }
+ free(style->fill.paint.url);
+ free(style->stroke.paint.url);
+ style->stroke.dash.array.reset();
+ free(style);
+}
+
+
+static void _freeNode(SvgNode* node)
+{
+ if (!node) return;
+
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ _freeNode(*child);
+ }
+ node->child.reset();
+
+ free(node->id);
+ free(node->transform);
+ _freeNodeStyle(node->style);
+ switch (node->type) {
+ case SvgNodeType::Path: {
+ free(node->node.path.path);
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ free(node->node.polygon.points);
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ free(node->node.polyline.points);
+ break;
+ }
+ case SvgNodeType::Doc: {
+ _freeNode(node->node.doc.defs);
+ _freeNode(node->node.doc.style);
+ break;
+ }
+ case SvgNodeType::Defs: {
+ auto gradients = node->node.defs.gradients.data;
+ for (size_t i = 0; i < node->node.defs.gradients.count; ++i) {
+ (*gradients)->clear();
+ free(*gradients);
+ ++gradients;
+ }
+ node->node.defs.gradients.reset();
+ break;
+ }
+ case SvgNodeType::Image: {
+ free(node->node.image.href);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+ free(node);
+}
+
+
+static bool _svgLoaderParserForValidCheckXmlOpen(SvgLoaderData* loader, const char* content, unsigned int length)
+{
+ const char* attrs = nullptr;
+ int sz = length;
+ char tagName[20] = "";
+ FactoryMethod method;
+ SvgNode *node = nullptr;
+ int attrsLength = 0;
+ loader->level++;
+ attrs = simpleXmlFindAttributesTag(content, length);
+
+ if (!attrs) {
+ //Parse the empty tag
+ attrs = content;
+ while ((attrs != nullptr) && *attrs != '>') attrs++;
+ }
+
+ if (attrs) {
+ sz = attrs - content;
+ while ((sz > 0) && (isspace(content[sz - 1]))) sz--;
+ if ((unsigned)sz >= sizeof(tagName)) return false;
+ strncpy(tagName, content, sz);
+ tagName[sz] = '\0';
+ attrsLength = length - sz;
+ }
+
+ if ((method = _findGroupFactory(tagName))) {
+ if (!loader->doc) {
+ if (strcmp(tagName, "svg")) return true; //Not a valid svg document
+ node = method(loader, nullptr, attrs, attrsLength, simpleXmlParseAttributes);
+ loader->doc = node;
+ loader->stack.push(node);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static bool _svgLoaderParserForValidCheck(void* data, SimpleXMLType type, const char* content, unsigned int length)
+{
+ SvgLoaderData* loader = (SvgLoaderData*)data;
+ bool res = true;;
+
+ switch (type) {
+ case SimpleXMLType::Open:
+ case SimpleXMLType::OpenEmpty: {
+ //If 'res' is false, it means <svg> tag is found.
+ res = _svgLoaderParserForValidCheckXmlOpen(loader, content, length);
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ return res;
+}
+
+
+void SvgLoader::clear()
+{
+ if (copy) free((char*)content);
+ size = 0;
+ content = nullptr;
+ copy = false;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+SvgLoader::SvgLoader()
+{
+}
+
+
+SvgLoader::~SvgLoader()
+{
+ close();
+}
+
+
+void SvgLoader::run(unsigned tid)
+{
+ if (!simpleXmlParse(content, size, true, _svgLoaderParser, &(loaderData))) return;
+
+ if (loaderData.doc) {
+ auto defs = loaderData.doc->node.doc.defs;
+
+ if (loaderData.nodesToStyle.count > 0) cssApplyStyleToPostponeds(loaderData.nodesToStyle, loaderData.cssStyle);
+ if (loaderData.cssStyle) cssUpdateStyle(loaderData.doc, loaderData.cssStyle);
+
+ if (loaderData.cloneNodes.count > 0) _clonePostponedNodes(&loaderData.cloneNodes, loaderData.doc);
+
+ _updateComposite(loaderData.doc, loaderData.doc);
+ if (defs) _updateComposite(loaderData.doc, defs);
+
+ if (loaderData.gradients.count > 0) _updateGradient(loaderData.doc, &loaderData.gradients);
+ if (defs) _updateGradient(loaderData.doc, &defs->node.defs.gradients);
+
+ _updateStyle(loaderData.doc, nullptr);
+ }
+ root = svgSceneBuild(loaderData.doc, vx, vy, vw, vh, w, h, preserveAspect, svgPath);
+}
+
+
+bool SvgLoader::header()
+{
+ //For valid check, only <svg> tag is parsed first.
+ //If the <svg> tag is found, the loaded file is valid and stores viewbox information.
+ //After that, the remaining content data is parsed in order with async.
+ loaderData.svgParse = (SvgParser*)malloc(sizeof(SvgParser));
+ if (!loaderData.svgParse) return false;
+
+ loaderData.svgParse->flags = SvgStopStyleFlags::StopDefault;
+
+ simpleXmlParse(content, size, true, _svgLoaderParserForValidCheck, &(loaderData));
+
+ if (loaderData.doc && loaderData.doc->type == SvgNodeType::Doc) {
+ //Return the brief resource info such as viewbox:
+ vx = loaderData.doc->node.doc.vx;
+ vy = loaderData.doc->node.doc.vy;
+ w = vw = loaderData.doc->node.doc.vw;
+ h = vh = loaderData.doc->node.doc.vh;
+
+ //Override size
+ if (loaderData.doc->node.doc.w > 0) {
+ w = loaderData.doc->node.doc.w;
+ if (vw < FLT_EPSILON) vw = w;
+ }
+ if (loaderData.doc->node.doc.h > 0) {
+ h = loaderData.doc->node.doc.h;
+ if (vh < FLT_EPSILON) vh = h;
+ }
+
+ preserveAspect = loaderData.doc->node.doc.preserveAspect;
+ } else {
+ TVGLOG("SVG", "No SVG File. There is no <svg/>");
+ return false;
+ }
+
+ return true;
+}
+
+
+bool SvgLoader::open(const char* data, uint32_t size, bool copy)
+{
+ clear();
+
+ if (copy) {
+ content = (char*)malloc(size);
+ if (!content) return false;
+ memcpy((char*)content, data, size);
+ } else content = data;
+
+ this->size = size;
+ this->copy = copy;
+
+ return header();
+}
+
+
+bool SvgLoader::open(const string& path)
+{
+ clear();
+
+ ifstream f;
+ f.open(path);
+
+ if (!f.is_open()) return false;
+
+ svgPath = path;
+ getline(f, filePath, '\0');
+ f.close();
+
+ if (filePath.empty()) return false;
+
+ content = filePath.c_str();
+ size = filePath.size();
+
+ return header();
+}
+
+
+bool SvgLoader::resize(Paint* paint, float w, float h)
+{
+ if (!paint) return false;
+
+ auto sx = w / this->w;
+ auto sy = h / this->h;
+
+ if (preserveAspect) {
+ //Scale
+ auto scale = sx < sy ? sx : sy;
+ paint->scale(scale);
+ //Align
+ auto tx = 0.0f;
+ auto ty = 0.0f;
+ auto tw = this->w * scale;
+ auto th = this->h * scale;
+ if (tw > th) ty -= (h - th) * 0.5f;
+ else tx -= (w - tw) * 0.5f;
+ paint->translate(-tx, -ty);
+ } else {
+ //Align
+ auto tx = 0.0f;
+ auto ty = 0.0f;
+ auto tw = this->w * sx;
+ auto th = this->h * sy;
+ if (tw > th) ty -= (h - th) * 0.5f;
+ else tx -= (w - tw) * 0.5f;
+
+ Matrix m = {sx, 0, -tx, 0, sy, -ty, 0, 0, 1};
+ paint->transform(m);
+ }
+ return true;
+}
+
+
+bool SvgLoader::read()
+{
+ if (!content || size == 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool SvgLoader::close()
+{
+ this->done();
+
+ if (loaderData.svgParse) {
+ free(loaderData.svgParse);
+ loaderData.svgParse = nullptr;
+ }
+ auto gradients = loaderData.gradients.data;
+ for (size_t i = 0; i < loaderData.gradients.count; ++i) {
+ (*gradients)->clear();
+ free(*gradients);
+ ++gradients;
+ }
+ loaderData.gradients.reset();
+
+ _freeNode(loaderData.doc);
+ loaderData.doc = nullptr;
+ loaderData.stack.reset();
+
+ clear();
+
+ return true;
+}
+
+
+unique_ptr<Paint> SvgLoader::paint()
+{
+ this->done();
+ if (root) return move(root);
+ else return nullptr;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
new file mode 100644
index 0000000000..093fb671b3
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+#ifndef _TVG_SVG_LOADER_H_
+#define _TVG_SVG_LOADER_H_
+
+#include "tvgTaskScheduler.h"
+#include "tvgSvgLoaderCommon.h"
+
+class SvgLoader : public LoadModule, public Task
+{
+public:
+ string filePath;
+ string svgPath = "";
+ const char* content = nullptr;
+ uint32_t size = 0;
+
+ SvgLoaderData loaderData;
+ unique_ptr<Scene> root;
+
+ bool copy = false;
+
+ SvgLoader();
+ ~SvgLoader();
+
+ using LoadModule::open;
+ bool open(const string& path) override;
+ bool open(const char* data, uint32_t size, bool copy) override;
+ bool resize(Paint* paint, float w, float h) override;
+ bool read() override;
+ bool close() override;
+ unique_ptr<Paint> paint() override;
+
+private:
+ bool header();
+ void clear();
+ void run(unsigned tid) override;
+};
+
+
+#endif //_TVG_SVG_LOADER_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
new file mode 100644
index 0000000000..1f25e82adc
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+#ifndef _TVG_SVG_LOADER_COMMON_H_
+#define _TVG_SVG_LOADER_COMMON_H_
+
+#include "tvgCommon.h"
+#include "tvgArray.h"
+
+struct SvgNode;
+struct SvgStyleGradient;
+
+//NOTE: Please update simpleXmlNodeTypeToString() as well.
+enum class SvgNodeType
+{
+ Doc,
+ G,
+ Defs,
+ Animation,
+ Arc,
+ Circle,
+ Ellipse,
+ Image,
+ Line,
+ Path,
+ Polygon,
+ Polyline,
+ Rect,
+ Text,
+ TextArea,
+ Tspan,
+ Use,
+ Video,
+ ClipPath,
+ Mask,
+ CssStyle,
+ Symbol,
+ Unknown
+};
+
+/*
+// TODO - remove?
+enum class SvgLengthType
+{
+ Percent,
+ Px,
+ Pc,
+ Pt,
+ Mm,
+ Cm,
+ In,
+};
+*/
+
+enum class SvgFillFlags
+{
+ Paint = 0x01,
+ Opacity = 0x02,
+ Gradient = 0x04,
+ FillRule = 0x08,
+ ClipPath = 0x16
+};
+
+enum class SvgStrokeFlags
+{
+ Paint = 0x1,
+ Opacity = 0x2,
+ Gradient = 0x4,
+ Scale = 0x8,
+ Width = 0x10,
+ Cap = 0x20,
+ Join = 0x40,
+ Dash = 0x80,
+};
+
+enum class SvgGradientType
+{
+ Linear,
+ Radial
+};
+
+enum class SvgStyleFlags
+{
+ Color = 0x01,
+ Fill = 0x02,
+ FillRule = 0x04,
+ FillOpacity = 0x08,
+ Opacity = 0x010,
+ Stroke = 0x20,
+ StrokeWidth = 0x40,
+ StrokeLineJoin = 0x80,
+ StrokeLineCap = 0x100,
+ StrokeOpacity = 0x200,
+ StrokeDashArray = 0x400,
+ Transform = 0x800,
+ ClipPath = 0x1000,
+ Mask = 0x2000,
+ MaskType = 0x4000,
+ Display = 0x8000
+};
+
+enum class SvgStopStyleFlags
+{
+ StopDefault = 0x0,
+ StopOpacity = 0x01,
+ StopColor = 0x02
+};
+
+enum class SvgFillRule
+{
+ Winding = 0,
+ OddEven = 1
+};
+
+enum class SvgMaskType
+{
+ Luminance = 0,
+ Alpha
+};
+
+//Length type to recalculate %, pt, pc, mm, cm etc
+enum class SvgParserLengthType
+{
+ Vertical,
+ Horizontal,
+ //In case of, for example, radius of radial gradient
+ Other
+};
+
+struct SvgDocNode
+{
+ float w;
+ float h;
+ float vx;
+ float vy;
+ float vw;
+ float vh;
+ SvgNode* defs;
+ SvgNode* style;
+ bool preserveAspect;
+};
+
+struct SvgGNode
+{
+};
+
+struct SvgDefsNode
+{
+ Array<SvgStyleGradient*> gradients;
+};
+
+struct SvgSymbolNode
+{
+ float w, h;
+ float vx, vy, vw, vh;
+ bool preserveAspect;
+ bool overflowVisible;
+};
+
+struct SvgUseNode
+{
+ float x, y, w, h;
+ bool isWidthSet;
+ bool isHeightSet;
+ SvgNode* symbol;
+};
+
+struct SvgEllipseNode
+{
+ float cx;
+ float cy;
+ float rx;
+ float ry;
+};
+
+struct SvgCircleNode
+{
+ float cx;
+ float cy;
+ float r;
+};
+
+struct SvgRectNode
+{
+ float x;
+ float y;
+ float w;
+ float h;
+ float rx;
+ float ry;
+ bool hasRx;
+ bool hasRy;
+};
+
+struct SvgLineNode
+{
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+};
+
+struct SvgImageNode
+{
+ float x, y, w, h;
+ char* href;
+};
+
+struct SvgPathNode
+{
+ char* path;
+};
+
+struct SvgPolygonNode
+{
+ int pointsCount;
+ float* points;
+};
+
+struct SvgClipNode
+{
+ bool userSpace;
+};
+
+struct SvgMaskNode
+{
+ SvgMaskType type;
+ bool userSpace;
+};
+
+struct SvgCssStyleNode
+{
+};
+
+struct SvgLinearGradient
+{
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+ bool isX1Percentage;
+ bool isY1Percentage;
+ bool isX2Percentage;
+ bool isY2Percentage;
+};
+
+struct SvgRadialGradient
+{
+ float cx;
+ float cy;
+ float fx;
+ float fy;
+ float r;
+ bool isCxPercentage;
+ bool isCyPercentage;
+ bool isFxPercentage;
+ bool isFyPercentage;
+ bool isRPercentage;
+};
+
+struct SvgComposite
+{
+ char *url;
+ SvgNode* node;
+ bool applying; //flag for checking circular dependency.
+};
+
+struct SvgColor
+{
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+};
+
+struct SvgPaint
+{
+ SvgStyleGradient* gradient;
+ char *url;
+ SvgColor color;
+ bool none;
+ bool curColor;
+};
+
+struct SvgDash
+{
+ Array<float> array;
+};
+
+struct SvgStyleGradient
+{
+ SvgGradientType type;
+ char* id;
+ char* ref;
+ FillSpread spread;
+ SvgRadialGradient* radial;
+ SvgLinearGradient* linear;
+ Matrix* transform;
+ Array<Fill::ColorStop> stops;
+ bool userSpace;
+
+ void clear()
+ {
+ stops.reset();
+ free(transform);
+ free(radial);
+ free(linear);
+ free(ref);
+ free(id);
+ }
+};
+
+struct SvgStyleFill
+{
+ SvgFillFlags flags;
+ SvgPaint paint;
+ int opacity;
+ FillRule fillRule;
+};
+
+struct SvgStyleStroke
+{
+ SvgStrokeFlags flags;
+ SvgPaint paint;
+ int opacity;
+ float scale;
+ float width;
+ float centered;
+ StrokeCap cap;
+ StrokeJoin join;
+ SvgDash dash;
+ int dashCount;
+};
+
+struct SvgStyleProperty
+{
+ SvgStyleFill fill;
+ SvgStyleStroke stroke;
+ SvgComposite clipPath;
+ SvgComposite mask;
+ int opacity;
+ SvgColor color;
+ bool curColorSet;
+ char* cssClass;
+ SvgStyleFlags flags;
+};
+
+struct SvgNode
+{
+ SvgNodeType type;
+ SvgNode* parent;
+ Array<SvgNode*> child;
+ char *id;
+ SvgStyleProperty *style;
+ Matrix* transform;
+ union {
+ SvgGNode g;
+ SvgDocNode doc;
+ SvgDefsNode defs;
+ SvgUseNode use;
+ SvgCircleNode circle;
+ SvgEllipseNode ellipse;
+ SvgPolygonNode polygon;
+ SvgPolygonNode polyline;
+ SvgRectNode rect;
+ SvgPathNode path;
+ SvgLineNode line;
+ SvgImageNode image;
+ SvgMaskNode mask;
+ SvgClipNode clip;
+ SvgCssStyleNode cssStyle;
+ SvgSymbolNode symbol;
+ } node;
+ bool display;
+ ~SvgNode();
+};
+
+struct SvgParser
+{
+ SvgNode* node;
+ SvgStyleGradient* styleGrad;
+ Fill::ColorStop gradStop;
+ SvgStopStyleFlags flags;
+ struct
+ {
+ int x, y;
+ uint32_t w, h;
+ } global;
+ struct
+ {
+ bool parsedFx;
+ bool parsedFy;
+ } gradient;
+};
+
+struct SvgNodeIdPair
+{
+ SvgNode* node;
+ char *id;
+};
+
+struct SvgLoaderData
+{
+ Array<SvgNode*> stack = {nullptr, 0, 0};
+ SvgNode* doc = nullptr;
+ SvgNode* def = nullptr;
+ SvgNode* cssStyle = nullptr;
+ Array<SvgStyleGradient*> gradients;
+ SvgStyleGradient* latestGradient = nullptr; //For stops
+ SvgParser* svgParse = nullptr;
+ Array<SvgNodeIdPair> cloneNodes;
+ Array<SvgNodeIdPair> nodesToStyle;
+ int level = 0;
+ bool result = false;
+ bool style = false;
+};
+
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtod-l-wcstod-wcstod-l?view=vs-2017
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
+float customStrtof(const char *nptr, char **endptr);
+
+#endif
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
new file mode 100644
index 0000000000..a09a2797d0
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
@@ -0,0 +1,564 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+/*
+ * Copyright notice for the EFL:
+
+ * Copyright (C) EFL developers (see AUTHORS)
+
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define _USE_MATH_DEFINES //Math Constants are not defined in Standard C/C++.
+
+#include <cstring>
+#include <math.h>
+#include <clocale>
+#include <ctype.h>
+#include "tvgSvgLoaderCommon.h"
+#include "tvgSvgPath.h"
+#include "tvgSvgUtil.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static char* _skipComma(const char* content)
+{
+ while (*content && isspace(*content)) {
+ content++;
+ }
+ if (*content == ',') return (char*)content + 1;
+ return (char*)content;
+}
+
+
+static bool _parseNumber(char** content, float* number)
+{
+ char* end = NULL;
+ *number = svgUtilStrtof(*content, &end);
+ //If the start of string is not number
+ if ((*content) == end) return false;
+ //Skip comma if any
+ *content = _skipComma(end);
+ return true;
+}
+
+
+static bool _parseFlag(char** content, int* number)
+{
+ char* end = NULL;
+ if (*(*content) != '0' && *(*content) != '1') return false;
+ *number = *(*content) - '0';
+ *content += 1;
+ end = *content;
+ *content = _skipComma(end);
+
+ return true;
+}
+
+
+void _pathAppendArcTo(Array<PathCommand>* cmds, Array<Point>* pts, Point* cur, Point* curCtl, float x, float y, float rx, float ry, float angle, bool largeArc, bool sweep)
+{
+ float cxp, cyp, cx, cy;
+ float sx, sy;
+ float cosPhi, sinPhi;
+ float dx2, dy2;
+ float x1p, y1p;
+ float x1p2, y1p2;
+ float rx2, ry2;
+ float lambda;
+ float c;
+ float at;
+ float theta1, deltaTheta;
+ float nat;
+ float delta, bcp;
+ float cosPhiRx, cosPhiRy;
+ float sinPhiRx, sinPhiRy;
+ float cosTheta1, sinTheta1;
+ int segments;
+
+ //Some helpful stuff is available here:
+ //http://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
+ sx = cur->x;
+ sy = cur->y;
+
+ //If start and end points are identical, then no arc is drawn
+ if ((fabsf(x - sx) < (1.0f / 256.0f)) && (fabsf(y - sy) < (1.0f / 256.0f))) return;
+
+ //Correction of out-of-range radii, see F6.6.1 (step 2)
+ rx = fabsf(rx);
+ ry = fabsf(ry);
+
+ angle = angle * M_PI / 180.0f;
+ cosPhi = cosf(angle);
+ sinPhi = sinf(angle);
+ dx2 = (sx - x) / 2.0f;
+ dy2 = (sy - y) / 2.0f;
+ x1p = cosPhi * dx2 + sinPhi * dy2;
+ y1p = cosPhi * dy2 - sinPhi * dx2;
+ x1p2 = x1p * x1p;
+ y1p2 = y1p * y1p;
+ rx2 = rx * rx;
+ ry2 = ry * ry;
+ lambda = (x1p2 / rx2) + (y1p2 / ry2);
+
+ //Correction of out-of-range radii, see F6.6.2 (step 4)
+ if (lambda > 1.0f) {
+ //See F6.6.3
+ float lambdaRoot = sqrtf(lambda);
+
+ rx *= lambdaRoot;
+ ry *= lambdaRoot;
+ //Update rx2 and ry2
+ rx2 = rx * rx;
+ ry2 = ry * ry;
+ }
+
+ c = (rx2 * ry2) - (rx2 * y1p2) - (ry2 * x1p2);
+
+ //Check if there is no possible solution
+ //(i.e. we can't do a square root of a negative value)
+ if (c < 0.0f) {
+ //Scale uniformly until we have a single solution
+ //(see F6.2) i.e. when c == 0.0
+ float scale = sqrtf(1.0f - c / (rx2 * ry2));
+ rx *= scale;
+ ry *= scale;
+ //Update rx2 and ry2
+ rx2 = rx * rx;
+ ry2 = ry * ry;
+
+ //Step 2 (F6.5.2) - simplified since c == 0.0
+ cxp = 0.0f;
+ cyp = 0.0f;
+ //Step 3 (F6.5.3 first part) - simplified since cxp and cyp == 0.0
+ cx = 0.0f;
+ cy = 0.0f;
+ } else {
+ //Complete c calculation
+ c = sqrtf(c / ((rx2 * y1p2) + (ry2 * x1p2)));
+ //Inverse sign if Fa == Fs
+ if (largeArc == sweep) c = -c;
+
+ //Step 2 (F6.5.2)
+ cxp = c * (rx * y1p / ry);
+ cyp = c * (-ry * x1p / rx);
+
+ //Step 3 (F6.5.3 first part)
+ cx = cosPhi * cxp - sinPhi * cyp;
+ cy = sinPhi * cxp + cosPhi * cyp;
+ }
+
+ //Step 3 (F6.5.3 second part) we now have the center point of the ellipse
+ cx += (sx + x) / 2.0f;
+ cy += (sy + y) / 2.0f;
+
+ //Sstep 4 (F6.5.4)
+ //We dont' use arccos (as per w3c doc), see
+ //http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm
+ //Note: atan2 (0.0, 1.0) == 0.0
+ at = atan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
+ theta1 = (at < 0.0f) ? 2.0f * M_PI + at : at;
+
+ nat = atan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
+ deltaTheta = (nat < at) ? 2.0f * M_PI - at + nat : nat - at;
+
+ if (sweep) {
+ //Ensure delta theta < 0 or else add 360 degrees
+ if (deltaTheta < 0.0f) deltaTheta += (float)(2.0f * M_PI);
+ } else {
+ //Ensure delta theta > 0 or else substract 360 degrees
+ if (deltaTheta > 0.0f) deltaTheta -= (float)(2.0f * M_PI);
+ }
+
+ //Add several cubic bezier to approximate the arc
+ //(smaller than 90 degrees)
+ //We add one extra segment because we want something
+ //Smaller than 90deg (i.e. not 90 itself)
+ segments = static_cast<int>(fabsf(deltaTheta / float(M_PI_2)) + 1.0f);
+ delta = deltaTheta / segments;
+
+ //http://www.stillhq.com/ctpfaq/2001/comp.text.pdf-faq-2001-04.txt (section 2.13)
+ bcp = 4.0f / 3.0f * (1.0f - cosf(delta / 2.0f)) / sinf(delta / 2.0f);
+
+ cosPhiRx = cosPhi * rx;
+ cosPhiRy = cosPhi * ry;
+ sinPhiRx = sinPhi * rx;
+ sinPhiRy = sinPhi * ry;
+
+ cosTheta1 = cosf(theta1);
+ sinTheta1 = sinf(theta1);
+
+ for (int i = 0; i < segments; ++i) {
+ //End angle (for this segment) = current + delta
+ float c1x, c1y, ex, ey, c2x, c2y;
+ float theta2 = theta1 + delta;
+ float cosTheta2 = cosf(theta2);
+ float sinTheta2 = sinf(theta2);
+ Point p[3];
+
+ //First control point (based on start point sx,sy)
+ c1x = sx - bcp * (cosPhiRx * sinTheta1 + sinPhiRy * cosTheta1);
+ c1y = sy + bcp * (cosPhiRy * cosTheta1 - sinPhiRx * sinTheta1);
+
+ //End point (for this segment)
+ ex = cx + (cosPhiRx * cosTheta2 - sinPhiRy * sinTheta2);
+ ey = cy + (sinPhiRx * cosTheta2 + cosPhiRy * sinTheta2);
+
+ //Second control point (based on end point ex,ey)
+ c2x = ex + bcp * (cosPhiRx * sinTheta2 + sinPhiRy * cosTheta2);
+ c2y = ey + bcp * (sinPhiRx * sinTheta2 - cosPhiRy * cosTheta2);
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {c1x, c1y};
+ p[1] = {c2x, c2y};
+ p[2] = {ex, ey};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = p[1];
+ *cur = p[2];
+
+ //Next start point is the current end point (same for angle)
+ sx = ex;
+ sy = ey;
+ theta1 = theta2;
+ //Avoid recomputations
+ cosTheta1 = cosTheta2;
+ sinTheta1 = sinTheta2;
+ }
+}
+
+static int _numberCount(char cmd)
+{
+ int count = 0;
+ switch (cmd) {
+ case 'M':
+ case 'm':
+ case 'L':
+ case 'l':
+ case 'T':
+ case 't': {
+ count = 2;
+ break;
+ }
+ case 'C':
+ case 'c':
+ case 'E':
+ case 'e': {
+ count = 6;
+ break;
+ }
+ case 'H':
+ case 'h':
+ case 'V':
+ case 'v': {
+ count = 1;
+ break;
+ }
+ case 'S':
+ case 's':
+ case 'Q':
+ case 'q': {
+ count = 4;
+ break;
+ }
+ case 'A':
+ case 'a': {
+ count = 7;
+ break;
+ }
+ default:
+ break;
+ }
+ return count;
+}
+
+
+static bool _processCommand(Array<PathCommand>* cmds, Array<Point>* pts, char cmd, float* arr, int count, Point* cur, Point* curCtl, Point* startPoint, bool *isQuadratic)
+{
+ switch (cmd) {
+ case 'm':
+ case 'l':
+ case 'c':
+ case 's':
+ case 'q':
+ case 't': {
+ for (int i = 0; i < count - 1; i += 2) {
+ arr[i] = arr[i] + cur->x;
+ arr[i + 1] = arr[i + 1] + cur->y;
+ }
+ break;
+ }
+ case 'h': {
+ arr[0] = arr[0] + cur->x;
+ break;
+ }
+ case 'v': {
+ arr[0] = arr[0] + cur->y;
+ break;
+ }
+ case 'a': {
+ arr[5] = arr[5] + cur->x;
+ arr[6] = arr[6] + cur->y;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ switch (cmd) {
+ case 'm':
+ case 'M': {
+ Point p = {arr[0], arr[1]};
+ cmds->push(PathCommand::MoveTo);
+ pts->push(p);
+ *cur = {arr[0], arr[1]};
+ *startPoint = {arr[0], arr[1]};
+ break;
+ }
+ case 'l':
+ case 'L': {
+ Point p = {arr[0], arr[1]};
+ cmds->push(PathCommand::LineTo);
+ pts->push(p);
+ *cur = {arr[0], arr[1]};
+ break;
+ }
+ case 'c':
+ case 'C': {
+ Point p[3];
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {arr[0], arr[1]};
+ p[1] = {arr[2], arr[3]};
+ p[2] = {arr[4], arr[5]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = p[1];
+ *cur = p[2];
+ *isQuadratic = false;
+ break;
+ }
+ case 's':
+ case 'S': {
+ Point p[3], ctrl;
+ if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ !(*isQuadratic)) {
+ ctrl.x = 2 * cur->x - curCtl->x;
+ ctrl.y = 2 * cur->y - curCtl->y;
+ } else {
+ ctrl = *cur;
+ }
+ cmds->push(PathCommand::CubicTo);
+ p[0] = ctrl;
+ p[1] = {arr[0], arr[1]};
+ p[2] = {arr[2], arr[3]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = p[1];
+ *cur = p[2];
+ *isQuadratic = false;
+ break;
+ }
+ case 'q':
+ case 'Q': {
+ Point p[3];
+ float ctrl_x0 = (cur->x + 2 * arr[0]) * (1.0 / 3.0);
+ float ctrl_y0 = (cur->y + 2 * arr[1]) * (1.0 / 3.0);
+ float ctrl_x1 = (arr[2] + 2 * arr[0]) * (1.0 / 3.0);
+ float ctrl_y1 = (arr[3] + 2 * arr[1]) * (1.0 / 3.0);
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {ctrl_x0, ctrl_y0};
+ p[1] = {ctrl_x1, ctrl_y1};
+ p[2] = {arr[2], arr[3]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = {arr[0], arr[1]};
+ *cur = p[2];
+ *isQuadratic = true;
+ break;
+ }
+ case 't':
+ case 'T': {
+ Point p[3], ctrl;
+ if ((cmds->count > 1) && (cmds->data[cmds->count - 1] == PathCommand::CubicTo) &&
+ *isQuadratic) {
+ ctrl.x = 2 * cur->x - curCtl->x;
+ ctrl.y = 2 * cur->y - curCtl->y;
+ } else {
+ ctrl = *cur;
+ }
+ float ctrl_x0 = (cur->x + 2 * ctrl.x) * (1.0 / 3.0);
+ float ctrl_y0 = (cur->y + 2 * ctrl.y) * (1.0 / 3.0);
+ float ctrl_x1 = (arr[0] + 2 * ctrl.x) * (1.0 / 3.0);
+ float ctrl_y1 = (arr[1] + 2 * ctrl.y) * (1.0 / 3.0);
+ cmds->push(PathCommand::CubicTo);
+ p[0] = {ctrl_x0, ctrl_y0};
+ p[1] = {ctrl_x1, ctrl_y1};
+ p[2] = {arr[0], arr[1]};
+ pts->push(p[0]);
+ pts->push(p[1]);
+ pts->push(p[2]);
+ *curCtl = {ctrl.x, ctrl.y};
+ *cur = p[2];
+ *isQuadratic = true;
+ break;
+ }
+ case 'h':
+ case 'H': {
+ Point p = {arr[0], cur->y};
+ cmds->push(PathCommand::LineTo);
+ pts->push(p);
+ cur->x = arr[0];
+ break;
+ }
+ case 'v':
+ case 'V': {
+ Point p = {cur->x, arr[0]};
+ cmds->push(PathCommand::LineTo);
+ pts->push(p);
+ cur->y = arr[0];
+ break;
+ }
+ case 'z':
+ case 'Z': {
+ cmds->push(PathCommand::Close);
+ *cur = *startPoint;
+ break;
+ }
+ case 'a':
+ case 'A': {
+ _pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], arr[0], arr[1], arr[2], arr[3], arr[4]);
+ *cur = *curCtl = {arr[5], arr[6]};
+ *isQuadratic = false;
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static char* _nextCommand(char* path, char* cmd, float* arr, int* count)
+{
+ int large, sweep;
+
+ path = _skipComma(path);
+ if (isalpha(*path)) {
+ *cmd = *path;
+ path++;
+ *count = _numberCount(*cmd);
+ } else {
+ if (*cmd == 'm') *cmd = 'l';
+ else if (*cmd == 'M') *cmd = 'L';
+ }
+ if (*count == 7) {
+ //Special case for arc command
+ if (_parseNumber(&path, &arr[0])) {
+ if (_parseNumber(&path, &arr[1])) {
+ if (_parseNumber(&path, &arr[2])) {
+ if (_parseFlag(&path, &large)) {
+ if (_parseFlag(&path, &sweep)) {
+ if (_parseNumber(&path, &arr[5])) {
+ if (_parseNumber(&path, &arr[6])) {
+ arr[3] = (float)large;
+ arr[4] = (float)sweep;
+ return path;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ *count = 0;
+ return NULL;
+ }
+ for (int i = 0; i < *count; i++) {
+ if (!_parseNumber(&path, &arr[i])) {
+ *count = 0;
+ return NULL;
+ }
+ path = _skipComma(path);
+ }
+ return path;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts)
+{
+ float numberArray[7];
+ int numberCount = 0;
+ Point cur = { 0, 0 };
+ Point curCtl = { 0, 0 };
+ Point startPoint = { 0, 0 };
+ char cmd = 0;
+ bool isQuadratic = false;
+ char* path = (char*)svgPath;
+ char* curLocale;
+
+ curLocale = setlocale(LC_NUMERIC, NULL);
+ if (curLocale) curLocale = strdup(curLocale);
+ setlocale(LC_NUMERIC, "POSIX");
+
+ while ((path[0] != '\0')) {
+ path = _nextCommand(path, &cmd, numberArray, &numberCount);
+ if (!path) break;
+ if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break;
+ }
+
+ setlocale(LC_NUMERIC, curLocale);
+ if (curLocale) free(curLocale);
+
+ return true;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
new file mode 100644
index 0000000000..8f5f9035dc
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+#ifndef _TVG_SVG_PATH_H_
+#define _TVG_SVG_PATH_H_
+
+#include <tvgCommon.h>
+
+bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts);
+
+#endif //_TVG_SVG_PATH_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
new file mode 100644
index 0000000000..90705f2523
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
@@ -0,0 +1,735 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+/*
+ * Copyright notice for the EFL:
+
+ * Copyright (C) EFL developers (see AUTHORS)
+
+ * All rights reserved.
+
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <cstring>
+#include <string>
+#include "tvgMath.h"
+#include "tvgSvgLoaderCommon.h"
+#include "tvgSvgSceneBuilder.h"
+#include "tvgSvgPath.h"
+#include "tvgSvgUtil.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct Box
+{
+ float x, y, w, h;
+};
+
+
+static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite = nullptr);
+
+
+static inline bool _isGroupType(SvgNodeType type)
+{
+ if (type == SvgNodeType::Doc || type == SvgNodeType::G || type == SvgNodeType::Use || type == SvgNodeType::ClipPath || type == SvgNodeType::Symbol) return true;
+ return 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
+static Box _boundingBox(const Shape* shape)
+{
+ float x, y, w, h;
+ shape->bounds(&x, &y, &w, &h, false);
+
+ if (auto strokeW = shape->strokeWidth()) {
+ x += 0.5f * strokeW;
+ y += 0.5f * strokeW;
+ w -= strokeW;
+ h -= strokeW;
+ }
+
+ return {x, y, w, h};
+}
+
+
+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, const Box& vBox, int opacity)
+{
+ Fill::ColorStop* stops;
+ int stopCount = 0;
+ auto fillGrad = LinearGradient::gen();
+
+ 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) {
+ g->linear->x1 = g->linear->x1 * vBox.w;
+ g->linear->y1 = g->linear->y1 * vBox.h;
+ g->linear->x2 = g->linear->x2 * vBox.w;
+ g->linear->y2 = g->linear->y2 * vBox.h;
+ } else {
+ Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 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);
+
+ //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.data[i];
+ //Use premultiplied color
+ stops[i].r = colorStop->r;
+ stops[i].g = colorStop->g;
+ stops[i].b = colorStop->b;
+ stops[i].a = static_cast<uint8_t>((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);
+ }
+ return fillGrad;
+}
+
+
+static unique_ptr<RadialGradient> _applyRadialGradientProperty(SvgStyleGradient* g, const Shape* vg, const Box& vBox, int opacity)
+{
+ Fill::ColorStop *stops;
+ int stopCount = 0;
+ auto fillGrad = RadialGradient::gen();
+
+ 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 * vBox.w;
+ g->radial->cy = g->radial->cy * vBox.h;
+ g->radial->r = g->radial->r * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
+ g->radial->fx = g->radial->fx * vBox.w;
+ g->radial->fy = g->radial->fy * vBox.h;
+ } else {
+ Matrix m = {vBox.w, 0, vBox.x, 0, vBox.h, vBox.y, 0, 0, 1};
+ if (isTransform) _transformMultiply(&m, &finalTransform);
+ else {
+ finalTransform = m;
+ isTransform = true;
+ }
+ }
+
+ if (isTransform) fillGrad->transform(finalTransform);
+
+ //TODO: Tvg is not support to focal
+ //if (g->radial->fx != 0 && g->radial->fy != 0) {
+ // fillGrad->radial(g->radial->fx, g->radial->fy, g->radial->r);
+ //}
+ fillGrad->radial(g->radial->cx, g->radial->cy, g->radial->r);
+ 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.data[i];
+ //Use premultiplied color
+ stops[i].r = colorStop->r;
+ stops[i].g = colorStop->g;
+ stops[i].b = colorStop->b;
+ stops[i].a = static_cast<uint8_t>((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);
+ }
+ return fillGrad;
+}
+
+
+static bool _appendChildShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+{
+ auto valid = false;
+
+ if (_appendShape(node, shape, vBox, svgPath)) valid = true;
+
+ if (node->child.count > 0) {
+ auto child = node->child.data;
+ for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
+ if (_appendChildShape(*child, shape, vBox, svgPath)) valid = true;
+ }
+ }
+
+ return valid;
+}
+
+
+static void _applyComposition(Paint* paint, const SvgNode* node, const Box& vBox, const string& svgPath)
+{
+ /* ClipPath */
+ /* Do not drop in Circular Dependency for ClipPath.
+ Composition can be applied recursively if its children nodes have composition target to this one. */
+ if (node->style->clipPath.applying) {
+ TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
+ } else {
+ auto compNode = node->style->clipPath.node;
+ if (compNode && compNode->child.count > 0) {
+ node->style->clipPath.applying = true;
+
+ auto comp = Shape::gen();
+ comp->fill(255, 255, 255, 255);
+ if (node->transform) comp->transform(*node->transform);
+
+ auto child = compNode->child.data;
+ auto valid = false; //Composite only when valid shapes are existed
+
+ for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
+ if (_appendChildShape(*child, comp.get(), vBox, svgPath)) valid = true;
+ }
+
+ if (valid) paint->composite(move(comp), CompositeMethod::ClipPath);
+
+ node->style->clipPath.applying = false;
+ }
+ }
+
+ /* Mask */
+ /* Do not drop in Circular Dependency for Mask.
+ Composition can be applied recursively if its children nodes have composition target to this one. */
+ if (node->style->mask.applying) {
+ TVGLOG("SVG", "Multiple Composition Tried! Check out Circular dependency?");
+ } else {
+ auto compNode = node->style->mask.node;
+ if (compNode && compNode->child.count > 0) {
+ node->style->mask.applying = true;
+
+ bool isMaskWhite = true;
+ auto comp = _sceneBuildHelper(compNode, vBox, svgPath, true, &isMaskWhite);
+ if (comp) {
+ if (node->transform) comp->transform(*node->transform);
+
+ if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) {
+ paint->composite(move(comp), CompositeMethod::LumaMask);
+ } else {
+ paint->composite(move(comp), CompositeMethod::AlphaMask);
+ }
+ }
+
+ node->style->mask.applying = false;
+ }
+ }
+}
+
+
+static void _applyProperty(SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath)
+{
+ SvgStyleProperty* style = node->style;
+
+ if (node->transform) vg->transform(*node->transform);
+ if (node->type == SvgNodeType::Doc || !node->display) return;
+
+ //If fill property is nullptr then do nothing
+ if (style->fill.paint.none) {
+ //Do nothing
+ } else if (style->fill.paint.gradient) {
+ Box bBox = vBox;
+ if (!style->fill.paint.gradient->userSpace) bBox = _boundingBox(vg);
+
+ if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
+ auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
+ vg->fill(move(linear));
+ } else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
+ auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, vg, bBox, style->fill.opacity);
+ vg->fill(move(radial));
+ }
+ } else if (style->fill.paint.url) {
+ //TODO: Apply the color pointed by url
+ } else if (style->fill.paint.curColor) {
+ //Apply the current style color
+ vg->fill(style->color.r, style->color.g, style->color.b, style->fill.opacity);
+ } else {
+ //Apply the fill color
+ 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);
+
+ //Apply node opacity
+ if (style->opacity < 255) vg->opacity(style->opacity);
+
+ if (node->type == SvgNodeType::G || node->type == SvgNodeType::Use) return;
+
+ //Apply the stroke style property
+ vg->stroke(style->stroke.width);
+ vg->stroke(style->stroke.cap);
+ vg->stroke(style->stroke.join);
+ if (style->stroke.dash.array.count > 0) {
+ vg->stroke(style->stroke.dash.array.data, style->stroke.dash.array.count);
+ }
+
+ //If stroke property is nullptr then do nothing
+ if (style->stroke.paint.none) {
+ //Do nothing
+ } else if (style->stroke.paint.gradient) {
+ Box bBox = vBox;
+ if (!style->stroke.paint.gradient->userSpace) bBox = _boundingBox(vg);
+
+ if (style->stroke.paint.gradient->type == SvgGradientType::Linear) {
+ auto linear = _applyLinearGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
+ vg->stroke(move(linear));
+ } else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) {
+ auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, vg, bBox, style->stroke.opacity);
+ vg->stroke(move(radial));
+ }
+ } else if (style->stroke.paint.url) {
+ //TODO: Apply the color pointed by url
+ } else if (style->stroke.paint.curColor) {
+ //Apply the current style color
+ vg->stroke(style->color.r, style->color.g, style->color.b, style->stroke.opacity);
+ } else {
+ //Apply the stroke color
+ vg->stroke(style->stroke.paint.color.r, style->stroke.paint.color.g, style->stroke.paint.color.b, style->stroke.opacity);
+ }
+
+ _applyComposition(vg, node, vBox, svgPath);
+}
+
+
+static unique_ptr<Shape> _shapeBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
+{
+ auto shape = Shape::gen();
+ if (_appendShape(node, shape.get(), vBox, svgPath)) return shape;
+ else return nullptr;
+}
+
+
+static bool _appendShape(SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
+{
+ Array<PathCommand> cmds;
+ Array<Point> pts;
+
+ switch (node->type) {
+ case SvgNodeType::Path: {
+ if (node->node.path.path) {
+ if (svgPathToTvgPath(node->node.path.path, cmds, pts)) {
+ shape->appendPath(cmds.data, cmds.count, pts.data, pts.count);
+ }
+ }
+ break;
+ }
+ case SvgNodeType::Ellipse: {
+ shape->appendCircle(node->node.ellipse.cx, node->node.ellipse.cy, node->node.ellipse.rx, node->node.ellipse.ry);
+ break;
+ }
+ case SvgNodeType::Polygon: {
+ if (node->node.polygon.pointsCount < 2) break;
+ shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
+ for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
+ shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
+ }
+ shape->close();
+ break;
+ }
+ case SvgNodeType::Polyline: {
+ if (node->node.polygon.pointsCount < 2) break;
+ shape->moveTo(node->node.polygon.points[0], node->node.polygon.points[1]);
+ for (int i = 2; i < node->node.polygon.pointsCount - 1; i += 2) {
+ shape->lineTo(node->node.polygon.points[i], node->node.polygon.points[i + 1]);
+ }
+ break;
+ }
+ case SvgNodeType::Circle: {
+ shape->appendCircle(node->node.circle.cx, node->node.circle.cy, node->node.circle.r, node->node.circle.r);
+ break;
+ }
+ case SvgNodeType::Rect: {
+ shape->appendRect(node->node.rect.x, node->node.rect.y, node->node.rect.w, node->node.rect.h, node->node.rect.rx, node->node.rect.ry);
+ break;
+ }
+ case SvgNodeType::Line: {
+ shape->moveTo(node->node.line.x1, node->node.line.y1);
+ shape->lineTo(node->node.line.x2, node->node.line.y2);
+ break;
+ }
+ default: {
+ return false;
+ }
+ }
+
+ _applyProperty(node, shape, vBox, svgPath);
+ return true;
+}
+
+
+enum class imageMimeTypeEncoding
+{
+ base64 = 0x1,
+ utf8 = 0x2
+};
+constexpr imageMimeTypeEncoding operator|(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
+ return static_cast<imageMimeTypeEncoding>(static_cast<int>(a) | static_cast<int>(b));
+}
+constexpr bool operator&(imageMimeTypeEncoding a, imageMimeTypeEncoding b) {
+ return (static_cast<int>(a) & static_cast<int>(b));
+}
+
+
+static constexpr struct
+{
+ const char* name;
+ int sz;
+ imageMimeTypeEncoding encoding;
+} imageMimeTypes[] = {
+ {"jpeg", sizeof("jpeg"), imageMimeTypeEncoding::base64},
+ {"png", sizeof("png"), imageMimeTypeEncoding::base64},
+ {"svg+xml", sizeof("svg+xml"), imageMimeTypeEncoding::base64 | imageMimeTypeEncoding::utf8},
+};
+
+
+static bool _isValidImageMimeTypeAndEncoding(const char** href, const char** mimetype, imageMimeTypeEncoding* encoding) {
+ if (strncmp(*href, "image/", sizeof("image/") - 1)) return false; //not allowed mime type
+ *href += sizeof("image/") - 1;
+
+ //RFC2397 data:[<mediatype>][;base64],<data>
+ //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;
+
+ 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
+ }
+ }
+ }
+ //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;
+}
+
+
+static unique_ptr<Picture> _imageBuildHelper(SvgNode* node, const Box& vBox, const string& svgPath)
+{
+ if (!node->node.image.href) return nullptr;
+ auto picture = Picture::gen();
+
+ const char* href = node->node.image.href;
+ if (!strncmp(href, "data:", sizeof("data:") - 1)) {
+ href += sizeof("data:") - 1;
+ const char* mimetype;
+ imageMimeTypeEncoding encoding;
+ if (!_isValidImageMimeTypeAndEncoding(&href, &mimetype, &encoding)) return nullptr; //not allowed mime type or encoding
+ if (encoding == imageMimeTypeEncoding::base64) {
+ string decoded = svgUtilBase64Decode(href);
+ if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ } else {
+ string decoded = svgUtilURLDecode(href);
+ if (picture->load(decoded.c_str(), decoded.size(), mimetype, true) != Result::Success) return nullptr;
+ }
+ } else {
+ if (!strncmp(href, "file://", sizeof("file://") - 1)) href += sizeof("file://") - 1;
+ //TODO: protect against recursive svg image loading
+ //Temporarily disable embedded svg:
+ const char *dot = strrchr(href, '.');
+ if (dot && !strcmp(dot, ".svg")) {
+ TVGLOG("SVG", "Embedded svg file is disabled.");
+ 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;
+ Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ 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};
+ }
+ if (node->transform) m = mathMultiply(node->transform, &m);
+ picture->transform(m);
+
+ _applyComposition(picture.get(), node, vBox, svgPath);
+ return picture;
+}
+
+
+static unique_ptr<Scene> _useBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool* isMaskWhite)
+{
+ unique_ptr<Scene> finalScene;
+ auto scene = _sceneBuildHelper(node, vBox, svgPath, false, isMaskWhite);
+
+ // mUseTransform = mUseTransform * mTranslate
+ Matrix mUseTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ if (node->transform) mUseTransform = *node->transform;
+ if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
+ Matrix mTranslate = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
+ mUseTransform = mathMultiply(&mUseTransform, &mTranslate);
+ }
+
+ if (node->node.use.symbol) {
+ auto symbol = node->node.use.symbol->node.symbol;
+
+ auto width = symbol.w;
+ if (node->node.use.isWidthSet) width = node->node.use.w;
+ auto height = symbol.h;
+ if (node->node.use.isHeightSet) height = node->node.use.h;
+
+ Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
+ if ((!mathEqual(width, symbol.vw) || !mathEqual(height, symbol.vh)) && symbol.vw > 0 && symbol.vh > 0) {
+ auto sx = width / symbol.vw;
+ auto sy = height / symbol.vh;
+ if (symbol.preserveAspect) {
+ if (sx < sy) sy = sx;
+ else sx = sy;
+ }
+
+ auto tvx = symbol.vx * sx;
+ auto tvy = symbol.vy * sy;
+ auto tvw = symbol.vw * sx;
+ auto tvh = symbol.vh * sy;
+ tvy -= (symbol.h - tvh) * 0.5f;
+ tvx -= (symbol.w - tvw) * 0.5f;
+ mViewBox = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
+ } else if (!mathZero(symbol.vx) || !mathZero(symbol.vy)) {
+ mViewBox = {1, 0, -symbol.vx, 0, 1, -symbol.vy, 0, 0, 1};
+ }
+
+ // mSceneTransform = mUseTransform * mSymbolTransform * mViewBox
+ Matrix mSceneTransform = mViewBox;
+ if (node->node.use.symbol->transform) {
+ mSceneTransform = mathMultiply(node->node.use.symbol->transform, &mViewBox);
+ }
+ mSceneTransform = mathMultiply(&mUseTransform, &mSceneTransform);
+ scene->transform(mSceneTransform);
+
+ if (node->node.use.symbol->node.symbol.overflowVisible) {
+ finalScene = move(scene);
+ } else {
+ auto viewBoxClip = Shape::gen();
+ viewBoxClip->appendRect(0, 0, width, height, 0, 0);
+
+ // mClipTransform = mUseTransform * mSymbolTransform
+ Matrix mClipTransform = mUseTransform;
+ if (node->node.use.symbol->transform) {
+ mClipTransform = mathMultiply(&mUseTransform, node->node.use.symbol->transform);
+ }
+ viewBoxClip->transform(mClipTransform);
+
+ auto compositeLayer = Scene::gen();
+ compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath);
+ compositeLayer->push(move(scene));
+
+ auto root = Scene::gen();
+ root->push(move(compositeLayer));
+
+ finalScene = move(root);
+ }
+ } else {
+ if (!mathIdentity((const Matrix*)(&mUseTransform))) scene->transform(mUseTransform);
+ finalScene = move(scene);
+ }
+
+ return finalScene;
+}
+
+
+static unique_ptr<Scene> _sceneBuildHelper(const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, bool* isMaskWhite)
+{
+ 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 (node->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(*child, vBox, svgPath, isMaskWhite));
+ else
+ scene->push(_sceneBuildHelper(*child, vBox, svgPath, false, isMaskWhite));
+ } else if ((*child)->type == SvgNodeType::Image) {
+ auto image = _imageBuildHelper(*child, vBox, svgPath);
+ if (image) scene->push(move(image));
+ } else if ((*child)->type != SvgNodeType::Mask) {
+ auto shape = _shapeBuildHelper(*child, vBox, svgPath);
+ if (shape) {
+ if (isMaskWhite) {
+ uint8_t r, g, b;
+ shape->fillColor(&r, &g, &b, nullptr);
+ if (shape->fill() || r < 255 || g < 255 || b < 255 || shape->strokeFill() ||
+ (shape->strokeColor(&r, &g, &b, nullptr) == Result::Success && (r < 255 || g < 255 || b < 255))) {
+ *isMaskWhite = false;
+ }
+ }
+ scene->push(move(shape));
+ }
+ }
+ }
+ _applyComposition(scene.get(), node, vBox, svgPath);
+ scene->opacity(node->style->opacity);
+ }
+ return scene;
+ }
+ return nullptr;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+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;
+
+ Box vBox = {vx, vy, vw, vh};
+ auto docNode = _sceneBuildHelper(node, vBox, svgPath, false);
+
+ if (!mathEqual(w, vw) || !mathEqual(h, vh)) {
+ auto sx = w / vw;
+ auto sy = h / vh;
+
+ if (preserveAspect) {
+ //Scale
+ auto scale = sx < sy ? sx : sy;
+ docNode->scale(scale);
+ //Align
+ auto tvx = vx * scale;
+ auto tvy = vy * scale;
+ auto tvw = vw * scale;
+ auto tvh = vh * scale;
+ tvx -= (w - tvw) * 0.5f;
+ tvy -= (h - tvh) * 0.5f;
+ docNode->translate(-tvx, -tvy);
+ } else {
+ //Align
+ auto tvx = vx * sx;
+ auto tvy = vy * sy;
+ Matrix m = {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
+ docNode->transform(m);
+ }
+ } else if (!mathZero(vx) || !mathZero(vy)) {
+ docNode->translate(-vx, -vy);
+ }
+
+ auto viewBoxClip = Shape::gen();
+ viewBoxClip->appendRect(0, 0, w, h, 0, 0);
+ viewBoxClip->fill(0, 0, 0, 255);
+
+ auto compositeLayer = Scene::gen();
+ compositeLayer->composite(move(viewBoxClip), CompositeMethod::ClipPath);
+ compositeLayer->push(move(docNode));
+
+ auto root = Scene::gen();
+ root->push(move(compositeLayer));
+
+ return root;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
new file mode 100644
index 0000000000..cecbbf02a8
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+#ifndef _TVG_SVG_SCENE_BUILDER_H_
+#define _TVG_SVG_SCENE_BUILDER_H_
+
+#include "tvgCommon.h"
+
+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_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
new file mode 100644
index 0000000000..1f1fe2a718
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2020 - 2022 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 <cstring>
+#include <math.h>
+#include <memory.h>
+#include "tvgSvgUtil.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+static inline bool _floatExact(float a, float b)
+{
+ return memcmp(&a, &b, sizeof(float)) == 0;
+}
+
+static uint8_t _hexCharToDec(const char c)
+{
+ if (c >= 'a') return c - 'a' + 10;
+ else if (c >= 'A') return c - 'A' + 10;
+ else return c - '0';
+}
+
+static uint8_t _base64Value(const char chr)
+{
+ if (chr >= 'A' && chr <= 'Z') return chr - 'A';
+ else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
+ else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
+ else if (chr == '+' || chr == '-') return 62;
+ else return 63;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+
+/*
+ * https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtof-strtof-l-wcstof-wcstof-l?view=msvc-160
+ *
+ * src should be one of the following form :
+ *
+ * [whitespace] [sign] {digits [radix digits] | radix digits} [{e | E} [sign] digits]
+ * [whitespace] [sign] {INF | INFINITY}
+ * [whitespace] [sign] NAN [sequence]
+ *
+ * No hexadecimal form supported
+ * no sequence supported after NAN
+ */
+float svgUtilStrtof(const char *nPtr, char **endPtr)
+{
+ if (endPtr) *endPtr = (char*)(nPtr);
+ if (!nPtr) return 0.0f;
+
+ auto a = nPtr;
+ auto iter = nPtr;
+ auto val = 0.0f;
+ unsigned long long integerPart = 0;
+ int minus = 1;
+
+ //ignore leading whitespaces
+ while (isspace(*iter)) iter++;
+
+ //signed or not
+ if (*iter == '-') {
+ minus = -1;
+ iter++;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ if (tolower(*iter) == 'i') {
+ if ((tolower(*(iter + 1)) == 'n') && (tolower(*(iter + 2)) == 'f')) iter += 3;
+ else goto error;
+
+ if (tolower(*(iter + 3)) == 'i') {
+ if ((tolower(*(iter + 4)) == 'n') && (tolower(*(iter + 5)) == 'i') && (tolower(*(iter + 6)) == 't') && (tolower(*(iter + 7)) == 'y')) iter += 5;
+ else goto error;
+ }
+ if (endPtr) *endPtr = (char *)(iter);
+ return (minus == -1) ? -INFINITY : INFINITY;
+ }
+
+ if (tolower(*iter) == 'n') {
+ if ((tolower(*(iter + 1)) == 'a') && (tolower(*(iter + 2)) == 'n')) iter += 3;
+ else goto error;
+
+ if (endPtr) *endPtr = (char *)(iter);
+ return (minus == -1) ? -NAN : NAN;
+ }
+
+ //Optional: integer part before dot
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++) {
+ integerPart = integerPart * 10ULL + (unsigned long long)(*iter - '0');
+ }
+ a = iter;
+ } else if (*iter != '.') {
+ goto success;
+ }
+
+ val = static_cast<float>(integerPart);
+
+ //Optional: decimal part after dot
+ if (*iter == '.') {
+ unsigned long long decimalPart = 0;
+ unsigned long long pow10 = 1;
+ int count = 0;
+
+ iter++;
+
+ if (isdigit(*iter)) {
+ for (; isdigit(*iter); iter++, count++) {
+ if (count < 19) {
+ decimalPart = decimalPart * 10ULL + + static_cast<unsigned long long>(*iter - '0');
+ pow10 *= 10ULL;
+ }
+ }
+ }
+ val += static_cast<float>(decimalPart) / static_cast<float>(pow10);
+ a = iter;
+ }
+
+ //Optional: exponent
+ if (*iter == 'e' || *iter == 'E') {
+ ++iter;
+
+ //Exception: svg may have 'em' unit for fonts. ex) 5em, 10.5em
+ if ((*iter == 'm') || (*iter == 'M')) {
+ //TODO: We don't support font em unit now, but has to multiply val * font size later...
+ a = iter + 1;
+ goto success;
+ }
+
+ //signed or not
+ int minus_e = 1;
+
+ if (*iter == '-') {
+ minus_e = -1;
+ ++iter;
+ } else if (*iter == '+') {
+ iter++;
+ }
+
+ unsigned int exponentPart = 0;
+
+ if (isdigit(*iter)) {
+ while (*iter == '0') iter++;
+ for (; isdigit(*iter); iter++) {
+ exponentPart = exponentPart * 10U + static_cast<unsigned int>(*iter - '0');
+ }
+ } else if (!isdigit(*(a - 1))) {
+ a = nPtr;
+ goto success;
+ } else if (*iter == 0) {
+ goto success;
+ }
+
+ //if ((_floatExact(val, 2.2250738585072011f)) && ((minus_e * static_cast<int>(exponentPart)) <= -308)) {
+ if ((_floatExact(val, 1.175494351f)) && ((minus_e * static_cast<int>(exponentPart)) <= -38)) {
+ //val *= 1.0e-308f;
+ val *= 1.0e-38f;
+ a = iter;
+ goto success;
+ }
+
+ a = iter;
+ auto scale = 1.0f;
+
+ while (exponentPart >= 8U) {
+ scale *= 1E8;
+ exponentPart -= 8U;
+ }
+ while (exponentPart > 0U) {
+ scale *= 10.0f;
+ exponentPart--;
+ }
+ val = (minus_e == -1) ? (val / scale) : (val * scale);
+ } else if ((iter > nPtr) && !isdigit(*(iter - 1))) {
+ a = nPtr;
+ goto success;
+ }
+
+success:
+ if (endPtr) *endPtr = (char*)(a);
+ return minus * val;
+
+error:
+ if (endPtr) *endPtr = (char*)(nPtr);
+ return 0.0f;
+}
+
+
+string svgUtilURLDecode(const char *src)
+{
+ if (!src) return nullptr;
+
+ auto length = strlen(src);
+ if (length == 0) return nullptr;
+
+ string decoded;
+ decoded.reserve(length);
+
+ char a, b;
+ while (*src) {
+ if (*src == '%' &&
+ ((a = src[1]) && (b = src[2])) &&
+ (isxdigit(a) && isxdigit(b))) {
+ decoded += (_hexCharToDec(a) << 4) + _hexCharToDec(b);
+ src+=3;
+ } else if (*src == '+') {
+ decoded += ' ';
+ src++;
+ } else {
+ decoded += *src++;
+ }
+ }
+ return decoded;
+}
+
+
+string svgUtilBase64Decode(const char *src)
+{
+ if (!src) return nullptr;
+
+ auto length = strlen(src);
+ if (length == 0) return nullptr;
+
+ string decoded;
+ decoded.reserve(3*(1+(length >> 2)));
+
+ while (*src && *(src+1)) {
+ if (*src <= 0x20) {
+ ++src;
+ continue;
+ }
+
+ auto value1 = _base64Value(src[0]);
+ auto value2 = _base64Value(src[1]);
+ decoded += (value1 << 2) + ((value2 & 0x30) >> 4);
+
+ if (!src[2] || src[2] == '=' || src[2] == '.') break;
+ auto value3 = _base64Value(src[2]);
+ decoded += ((value2 & 0x0f) << 4) + ((value3 & 0x3c) >> 2);
+
+ if (!src[3] || src[3] == '=' || src[3] == '.') break;
+ auto value4 = _base64Value(src[3]);
+ decoded += ((value3 & 0x03) << 6) + value4;
+ src += 4;
+ }
+ return decoded;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
new file mode 100644
index 0000000000..b5e6e1bdb2
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+#ifndef _TVG_SVG_UTIL_H_
+#define _TVG_SVG_UTIL_H_
+
+#include "tvgCommon.h"
+
+float svgUtilStrtof(const char *nPtr, char **endPtr);
+
+string svgUtilURLDecode(const char *src);
+string svgUtilBase64Decode(const char *src);
+
+#endif //_TVG_SVG_UTIL_H_
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
new file mode 100644
index 0000000000..a12689c7dd
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
@@ -0,0 +1,583 @@
+/*
+ * Copyright (c) 2020 - 2022 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 <cstring>
+#include <ctype.h>
+#include <string>
+
+#ifdef _WIN32
+ #include <malloc.h>
+#else
+ #include <alloca.h>
+#endif
+
+#include "tvgXmlParser.h"
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+bool _isIgnoreUnsupportedLogAttributes(TVG_UNUSED const char* tagAttribute, TVG_UNUSED const char* tagValue)
+{
+#ifdef THORVG_LOG_ENABLED
+ const auto attributesNum = 6;
+ const struct
+ {
+ const char* tag;
+ bool tagWildcard; //If true, it is assumed that a wildcard is used after the tag. (ex: tagName*)
+ const char* value;
+ } attributes[] = {
+ {"id", false, nullptr},
+ {"data-name", false, nullptr},
+ {"overflow", false, "visible"},
+ {"version", false, nullptr},
+ {"xmlns", true, nullptr},
+ {"xml:space", false, nullptr},
+ };
+
+ for (unsigned int i = 0; i < attributesNum; ++i) {
+ if (!strncmp(tagAttribute, attributes[i].tag, attributes[i].tagWildcard ? strlen(attributes[i].tag) : strlen(tagAttribute))) {
+ if (attributes[i].value && tagValue) {
+ if (!strncmp(tagValue, attributes[i].value, strlen(tagValue))) {
+ return true;
+ } else continue;
+ }
+ return true;
+ }
+ }
+ return false;
+#endif
+ return true;
+}
+
+
+static const char* _simpleXmlFindWhiteSpace(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if (isspace((unsigned char)*itr)) break;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlSkipWhiteSpace(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if (!isspace((unsigned char)*itr)) break;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlUnskipWhiteSpace(const char* itr, const char* itrStart)
+{
+ for (itr--; itr > itrStart; itr--) {
+ if (!isspace((unsigned char)*itr)) break;
+ }
+ return itr + 1;
+}
+
+
+static const char* _simpleXmlSkipXmlEntities(const char* itr, const char* itrEnd)
+{
+ auto p = itr;
+ while (itr < itrEnd && *itr == '&') {
+ for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
+ if (strncmp(itr, xmlEntity[i], xmlEntityLength[i]) == 0) {
+ itr += xmlEntityLength[i];
+ break;
+ }
+ }
+ if (itr == p) break;
+ p = itr;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlUnskipXmlEntities(const char* itr, const char* itrStart)
+{
+ auto p = itr;
+ while (itr > itrStart && *(itr - 1) == ';') {
+ for (int i = 0; i < NUMBER_OF_XML_ENTITIES; ++i) {
+ if (itr - xmlEntityLength[i] > itrStart &&
+ strncmp(itr - xmlEntityLength[i], xmlEntity[i], xmlEntityLength[i]) == 0) {
+ itr -= xmlEntityLength[i];
+ break;
+ }
+ }
+ if (itr == p) break;
+ p = itr;
+ }
+ return itr;
+}
+
+
+static const char* _skipWhiteSpacesAndXmlEntities(const char* itr, const char* itrEnd)
+{
+ itr = _simpleXmlSkipWhiteSpace(itr, itrEnd);
+ auto p = itr;
+ while (true) {
+ if (p != (itr = _simpleXmlSkipXmlEntities(itr, itrEnd))) p = itr;
+ else break;
+ if (p != (itr = _simpleXmlSkipWhiteSpace(itr, itrEnd))) p = itr;
+ else break;
+ }
+ return itr;
+}
+
+
+static const char* _unskipWhiteSpacesAndXmlEntities(const char* itr, const char* itrStart)
+{
+ itr = _simpleXmlUnskipWhiteSpace(itr, itrStart);
+ auto p = itr;
+ while (true) {
+ if (p != (itr = _simpleXmlUnskipXmlEntities(itr, itrStart))) p = itr;
+ else break;
+ if (p != (itr = _simpleXmlUnskipWhiteSpace(itr, itrStart))) p = itr;
+ else break;
+ }
+ return itr;
+}
+
+
+static const char* _simpleXmlFindStartTag(const char* itr, const char* itrEnd)
+{
+ return (const char*)memchr(itr, '<', itrEnd - itr);
+}
+
+
+static const char* _simpleXmlFindEndTag(const char* itr, const char* itrEnd)
+{
+ bool insideQuote = false;
+ for (; itr < itrEnd; itr++) {
+ if (*itr == '"') insideQuote = !insideQuote;
+ if (!insideQuote) {
+ if ((*itr == '>') || (*itr == '<'))
+ return itr;
+ }
+ }
+ return nullptr;
+}
+
+
+static const char* _simpleXmlFindEndCommentTag(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if ((*itr == '-') && ((itr + 1 < itrEnd) && (*(itr + 1) == '-')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
+ }
+ return nullptr;
+}
+
+
+static const char* _simpleXmlFindEndCdataTag(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if ((*itr == ']') && ((itr + 1 < itrEnd) && (*(itr + 1) == ']')) && ((itr + 2 < itrEnd) && (*(itr + 2) == '>'))) return itr + 2;
+ }
+ return nullptr;
+}
+
+
+static const char* _simpleXmlFindDoctypeChildEndTag(const char* itr, const char* itrEnd)
+{
+ for (; itr < itrEnd; itr++) {
+ if (*itr == '>') return itr;
+ }
+ return nullptr;
+}
+
+
+static SimpleXMLType _getXMLType(const char* itr, const char* itrEnd, size_t &toff)
+{
+ toff = 0;
+ if (itr[1] == '/') {
+ toff = 1;
+ return SimpleXMLType::Close;
+ } else if (itr[1] == '?') {
+ toff = 1;
+ return SimpleXMLType::Processing;
+ } else if (itr[1] == '!') {
+ if ((itr + sizeof("<!DOCTYPE>") - 1 < itrEnd) && (!memcmp(itr + 2, "DOCTYPE", sizeof("DOCTYPE") - 1)) && ((itr[2 + sizeof("DOCTYPE") - 1] == '>') || (isspace((unsigned char)itr[2 + sizeof("DOCTYPE") - 1])))) {
+ toff = sizeof("!DOCTYPE") - 1;
+ return SimpleXMLType::Doctype;
+ } else if ((itr + sizeof("<![CDATA[]]>") - 1 < itrEnd) && (!memcmp(itr + 2, "[CDATA[", sizeof("[CDATA[") - 1))) {
+ toff = sizeof("![CDATA[") - 1;
+ return SimpleXMLType::CData;
+ } else if ((itr + sizeof("<!---->") - 1 < itrEnd) && (!memcmp(itr + 2, "--", sizeof("--") - 1))) {
+ toff = sizeof("!--") - 1;
+ return SimpleXMLType::Comment;
+ } else if (itr + sizeof("<!>") - 1 < itrEnd) {
+ toff = sizeof("!") - 1;
+ return SimpleXMLType::DoctypeChild;
+ }
+ return SimpleXMLType::Open;
+ }
+ return SimpleXMLType::Open;
+}
+
+
+static char* _strndup(const char* src, unsigned len)
+{
+ auto ret = (char*)malloc(len + 1);
+ if (!ret) return nullptr;
+ ret[len] = '\0';
+ return (char*)memcpy(ret, src, len);
+}
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+const char* simpleXmlNodeTypeToString(TVG_UNUSED SvgNodeType type)
+{
+#ifdef THORVG_LOG_ENABLED
+ static const char* TYPE_NAMES[] = {
+ "Svg",
+ "G",
+ "Defs",
+ "Animation",
+ "Arc",
+ "Circle",
+ "Ellipse",
+ "Image",
+ "Line",
+ "Path",
+ "Polygon",
+ "Polyline",
+ "Rect",
+ "Text",
+ "TextArea",
+ "Tspan",
+ "Use",
+ "Video",
+ "ClipPath",
+ "Mask",
+ "Symbol",
+ "Unknown",
+ };
+ return TYPE_NAMES[(int) type];
+#endif
+ return nullptr;
+}
+
+
+bool isIgnoreUnsupportedLogElements(TVG_UNUSED const char* tagName)
+{
+#ifdef THORVG_LOG_ENABLED
+ const auto elementsNum = 1;
+ const char* const elements[] = { "title" };
+
+ for (unsigned int i = 0; i < elementsNum; ++i) {
+ if (!strncmp(tagName, elements[i], strlen(tagName))) {
+ return true;
+ }
+ }
+ return false;
+#else
+ return true;
+#endif
+}
+
+
+bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
+{
+ const char *itr = buf, *itrEnd = buf + bufLength;
+ char* tmpBuf = (char*)alloca(bufLength + 1);
+
+ if (!buf || !func) return false;
+
+ while (itr < itrEnd) {
+ const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd);
+ const char *key, *keyEnd, *value, *valueEnd;
+ char* tval;
+
+ if (p == itrEnd) return true;
+
+ key = p;
+ for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
+ if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
+ }
+ if (keyEnd == itrEnd) return false;
+ if (keyEnd == key) continue;
+
+ if (*keyEnd == '=') value = keyEnd + 1;
+ else {
+ value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
+ if (!value) return false;
+ value++;
+ }
+ keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
+
+ value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
+ if (value == itrEnd) return false;
+
+ if ((*value == '"') || (*value == '\'')) {
+ valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
+ if (!valueEnd) return false;
+ value++;
+ } else {
+ valueEnd = _simpleXmlFindWhiteSpace(value, itrEnd);
+ }
+
+ itr = valueEnd + 1;
+
+ value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
+ valueEnd = _unskipWhiteSpacesAndXmlEntities(valueEnd, value);
+
+ memcpy(tmpBuf, key, keyEnd - key);
+ tmpBuf[keyEnd - key] = '\0';
+
+ tval = tmpBuf + (keyEnd - key) + 1;
+ int i = 0;
+ while (value < valueEnd) {
+ value = _simpleXmlSkipXmlEntities(value, valueEnd);
+ tval[i++] = *value;
+ value++;
+ }
+ tval[i] = '\0';
+
+ if (!func((void*)data, tmpBuf, tval)) {
+ if (!_isIgnoreUnsupportedLogAttributes(tmpBuf, tval)) {
+ TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", tmpBuf, tval ? tval : "NONE");
+ }
+ }
+ }
+ return true;
+}
+
+
+bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data)
+{
+ const char *itr = buf, *itrEnd = buf + bufLength;
+
+ if (!buf || !func) return false;
+
+ while (itr < itrEnd) {
+ if (itr[0] == '<') {
+ //Invalid case
+ if (itr + 1 >= itrEnd) return false;
+
+ size_t toff = 0;
+ SimpleXMLType type = _getXMLType(itr, itrEnd, toff);
+
+ const char* p;
+ if (type == SimpleXMLType::CData) p = _simpleXmlFindEndCdataTag(itr + 1 + toff, itrEnd);
+ else if (type == SimpleXMLType::DoctypeChild) p = _simpleXmlFindDoctypeChildEndTag(itr + 1 + toff, itrEnd);
+ else if (type == SimpleXMLType::Comment) p = _simpleXmlFindEndCommentTag(itr + 1 + toff, itrEnd);
+ else p = _simpleXmlFindEndTag(itr + 1 + toff, itrEnd);
+
+ if (p) {
+ //Invalid case: '<' nested
+ if (*p == '<') return false;
+ const char *start, *end;
+
+ start = itr + 1 + toff;
+ end = p;
+
+ switch (type) {
+ case SimpleXMLType::Open: {
+ if (p[-1] == '/') {
+ type = SimpleXMLType::OpenEmpty;
+ end--;
+ }
+ break;
+ }
+ case SimpleXMLType::CData: {
+ if (!memcmp(p - 2, "]]", 2)) end -= 2;
+ break;
+ }
+ case SimpleXMLType::Processing: {
+ if (p[-1] == '?') end--;
+ break;
+ }
+ case SimpleXMLType::Comment: {
+ if (!memcmp(p - 2, "--", 2)) end -= 2;
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+
+ if (strip && (type != SimpleXMLType::CData)) {
+ start = _skipWhiteSpacesAndXmlEntities(start, end);
+ end = _unskipWhiteSpacesAndXmlEntities(end, start);
+ }
+
+ if (!func((void*)data, type, start, (unsigned int)(end - start))) return false;
+
+ itr = p + 1;
+ } else {
+ return false;
+ }
+ } else {
+ const char *p, *end;
+
+ if (strip) {
+ p = itr;
+ p = _skipWhiteSpacesAndXmlEntities(p, itrEnd);
+ if (p) {
+ if (!func((void*)data, SimpleXMLType::Ignored, itr, (unsigned int)(p - itr))) return false;
+ itr = p;
+ }
+ }
+
+ p = _simpleXmlFindStartTag(itr, itrEnd);
+ if (!p) p = itrEnd;
+
+ end = p;
+ if (strip) end = _unskipWhiteSpacesAndXmlEntities(end, itr);
+
+ if (itr != end && !func((void*)data, SimpleXMLType::Data, itr, (unsigned int)(end - itr))) return false;
+
+ if (strip && (end < p) && !func((void*)data, SimpleXMLType::Ignored, end, (unsigned int)(p - end))) return false;
+
+ itr = p;
+ }
+ }
+ return true;
+}
+
+
+bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data)
+{
+ const char* end;
+ char* key;
+ char* val;
+ char* next;
+
+ if (!buf) return false;
+
+ end = buf + bufLength;
+ key = (char*)alloca(end - buf + 1);
+ val = (char*)alloca(end - buf + 1);
+
+ if (buf == end) return true;
+
+ do {
+ char* sep = (char*)strchr(buf, ':');
+ next = (char*)strchr(buf, ';');
+ if (sep >= end) {
+ next = nullptr;
+ sep = nullptr;
+ }
+ if (next >= end) next = nullptr;
+
+ key[0] = '\0';
+ val[0] = '\0';
+
+ if (next == nullptr && sep != nullptr) {
+ memcpy(key, buf, sep - buf);
+ key[sep - buf] = '\0';
+
+ memcpy(val, sep + 1, end - sep - 1);
+ val[end - sep - 1] = '\0';
+ } else if (sep < next && sep != nullptr) {
+ memcpy(key, buf, sep - buf);
+ key[sep - buf] = '\0';
+
+ memcpy(val, sep + 1, next - sep - 1);
+ val[next - sep - 1] = '\0';
+ } else if (next) {
+ memcpy(key, buf, next - buf);
+ key[next - buf] = '\0';
+ }
+
+ if (key[0]) {
+ key = const_cast<char*>(_simpleXmlSkipWhiteSpace(key, key + strlen(key)));
+ key[_simpleXmlUnskipWhiteSpace(key + strlen(key) , key) - key] = '\0';
+ val = const_cast<char*>(_simpleXmlSkipWhiteSpace(val, val + strlen(val)));
+ val[_simpleXmlUnskipWhiteSpace(val + strlen(val) , val) - val] = '\0';
+
+ if (!func((void*)data, key, val)) {
+ if (!_isIgnoreUnsupportedLogAttributes(key, val)) {
+ TVGLOG("SVG", "Unsupported attributes used [Elements type: %s][Id : %s][Attribute: %s][Value: %s]", simpleXmlNodeTypeToString(((SvgLoaderData*)data)->svgParse->node->type), ((SvgLoaderData*)data)->svgParse->node->id ? ((SvgLoaderData*)data)->svgParse->node->id : "NO_ID", key, val ? val : "NONE");
+ }
+ }
+ }
+
+ buf = next + 1;
+ } while (next != nullptr);
+
+ return true;
+}
+
+
+/*
+ * Supported formats:
+ * tag {}, .name {}, tag.name{}
+ */
+const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength)
+{
+ if (!buf) return nullptr;
+
+ *tag = *name = nullptr;
+ *attrsLength = 0;
+
+ auto itr = _simpleXmlSkipWhiteSpace(buf, buf + bufLength);
+ auto itrEnd = (const char*)memchr(buf, '{', bufLength);
+
+ if (!itrEnd || itr == itrEnd) return nullptr;
+
+ auto nextElement = (const char*)memchr(itrEnd, '}', bufLength - (itrEnd - buf));
+ if (!nextElement) return nullptr;
+
+ *attrs = itrEnd + 1;
+ *attrsLength = nextElement - *attrs;
+
+ const char *p;
+
+ itrEnd = _simpleXmlUnskipWhiteSpace(itrEnd, itr);
+ if (*(itrEnd - 1) == '.') return nullptr;
+
+ for (p = itr; p < itrEnd; p++) {
+ if (*p == '.') break;
+ }
+
+ if (p == itr) *tag = strdup("all");
+ else *tag = _strndup(itr, p - itr);
+
+ if (p == itrEnd) *name = nullptr;
+ else *name = _strndup(p + 1, itrEnd - p - 1);
+
+ return (nextElement ? nextElement + 1 : nullptr);
+}
+
+
+const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength)
+{
+ const char *itr = buf, *itrEnd = buf + bufLength;
+
+ for (; itr < itrEnd; itr++) {
+ if (!isspace((unsigned char)*itr)) {
+ //User skip tagname and already gave it the attributes.
+ if (*itr == '=') return buf;
+ } else {
+ itr = _simpleXmlUnskipXmlEntities(itr, buf);
+ if (itr == itrEnd) return nullptr;
+ return itr;
+ }
+ }
+
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
new file mode 100644
index 0000000000..e2761ca8da
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020 - 2022 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.
+ */
+
+#ifndef _TVG_SIMPLE_XML_PARSER_H_
+#define _TVG_SIMPLE_XML_PARSER_H_
+
+#include "tvgSvgLoaderCommon.h"
+
+#define NUMBER_OF_XML_ENTITIES 8
+const char* const xmlEntity[] = {"&quot;", "&nbsp;", "&apos;", "&amp;", "&lt;", "&gt;", "&#035;", "&#039;"};
+const int xmlEntityLength[] = {6, 6, 6, 5, 4, 4, 6, 6};
+
+enum class SimpleXMLType
+{
+ Open = 0, //!< \<tag attribute="value"\>
+ OpenEmpty, //!< \<tag attribute="value" /\>
+ Close, //!< \</tag\>
+ Data, //!< tag text data
+ CData, //!< \<![cdata[something]]\>
+ Error, //!< error contents
+ Processing, //!< \<?xml ... ?\> \<?php .. ?\>
+ Doctype, //!< \<!doctype html
+ Comment, //!< \<!-- something --\>
+ Ignored, //!< whatever is ignored by parser, like whitespace
+ DoctypeChild //!< \<!doctype_child
+};
+
+typedef bool (*simpleXMLCb)(void* data, SimpleXMLType type, const char* content, unsigned int length);
+typedef bool (*simpleXMLAttributeCb)(void* data, const char* key, const char* value);
+
+bool simpleXmlParseAttributes(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
+bool simpleXmlParse(const char* buf, unsigned bufLength, bool strip, simpleXMLCb func, const void* data);
+bool simpleXmlParseW3CAttribute(const char* buf, unsigned bufLength, simpleXMLAttributeCb func, const void* data);
+const char* simpleXmlParseCSSAttribute(const char* buf, unsigned bufLength, char** tag, char** name, const char** attrs, unsigned* attrsLength);
+const char* simpleXmlFindAttributesTag(const char* buf, unsigned bufLength);
+bool isIgnoreUnsupportedLogElements(const char* tagName);
+const char* simpleXmlNodeTypeToString(SvgNodeType type);
+
+#endif //_TVG_SIMPLE_XML_PARSER_H_
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
new file mode 100644
index 0000000000..2b85342b2c
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgBinInterpreter.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (c) 2021 - 2022 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 <memory.h>
+
+#ifdef _WIN32
+ #include <malloc.h>
+#else
+ #include <alloca.h>
+#endif
+
+#include "tvgTvgCommon.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+struct TvgBinBlock
+{
+ TvgBinTag type;
+ TvgBinCounter length;
+ const char* data;
+ const char* end;
+};
+
+static Paint* _parsePaint(TvgBinBlock baseBlock);
+
+
+static TvgBinBlock _readBlock(const char *ptr)
+{
+ TvgBinBlock block;
+ block.type = *ptr;
+ READ_UI32(&block.length, ptr + SIZE(TvgBinTag));
+ block.data = ptr + SIZE(TvgBinTag) + SIZE(TvgBinCounter);
+ block.end = block.data + block.length;
+ return block;
+}
+
+
+static bool _parseCmpTarget(const char *ptr, const char *end, Paint *paint)
+{
+ auto block = _readBlock(ptr);
+ if (block.end > end) return false;
+
+ if (block.type != TVG_TAG_PAINT_CMP_METHOD) return false;
+ if (block.length != SIZE(TvgBinFlag)) return false;
+
+ auto cmpMethod = static_cast<CompositeMethod>(*block.data);
+
+ ptr = block.end;
+
+ auto cmpBlock = _readBlock(ptr);
+ if (cmpBlock.end > end) return false;
+
+ paint->composite(unique_ptr<Paint>(_parsePaint(cmpBlock)), cmpMethod);
+
+ return true;
+}
+
+
+static bool _parsePaintProperty(TvgBinBlock block, Paint *paint)
+{
+ switch (block.type) {
+ case TVG_TAG_PAINT_OPACITY: {
+ if (block.length != SIZE(uint8_t)) return false;
+ paint->opacity(*block.data);
+ return true;
+ }
+ case TVG_TAG_PAINT_TRANSFORM: {
+ if (block.length != SIZE(Matrix)) return false;
+ Matrix matrix;
+ memcpy(&matrix, block.data, SIZE(Matrix));
+ paint->transform(matrix);
+ return true;
+ }
+ case TVG_TAG_PAINT_CMP_TARGET: {
+ if (block.length < SIZE(TvgBinTag) + SIZE(TvgBinCounter)) return false;
+ return _parseCmpTarget(block.data, block.end, paint);
+ }
+ }
+ return false;
+}
+
+
+static bool _parseScene(TvgBinBlock block, Paint *paint)
+{
+ auto scene = static_cast<Scene*>(paint);
+
+ //Case1: scene reserve count
+ if (block.type == TVG_TAG_SCENE_RESERVEDCNT) {
+ if (block.length != SIZE(uint32_t)) return false;
+ uint32_t reservedCnt;
+ READ_UI32(&reservedCnt, block.data);
+ scene->reserve(reservedCnt);
+ return true;
+ }
+
+ //Case2: Base Paint Properties
+ if (_parsePaintProperty(block, scene)) return true;
+
+ //Case3: A Child paint
+ if (auto paint = _parsePaint(block)) {
+ scene->push(unique_ptr<Paint>(paint));
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool _parseShapePath(const char *ptr, const char *end, Shape *shape)
+{
+ uint32_t cmdCnt, ptsCnt;
+
+ READ_UI32(&cmdCnt, ptr);
+ ptr += SIZE(cmdCnt);
+
+ READ_UI32(&ptsCnt, ptr);
+ ptr += SIZE(ptsCnt);
+
+ auto cmds = (TvgBinFlag*) ptr;
+ ptr += SIZE(TvgBinFlag) * cmdCnt;
+
+ auto pts = (Point*) ptr;
+ ptr += SIZE(Point) * ptsCnt;
+
+ if (ptr > end) return false;
+
+ /* Recover to PathCommand(4 bytes) from TvgBinFlag(1 byte) */
+ PathCommand* inCmds = (PathCommand*)alloca(sizeof(PathCommand) * cmdCnt);
+ for (uint32_t i = 0; i < cmdCnt; ++i) {
+ inCmds[i] = static_cast<PathCommand>(cmds[i]);
+ }
+
+ shape->appendPath(inCmds, cmdCnt, pts, ptsCnt);
+
+ return true;
+}
+
+
+static unique_ptr<Fill> _parseShapeFill(const char *ptr, const char *end)
+{
+ unique_ptr<Fill> fillGrad;
+
+ while (ptr < end) {
+ auto block = _readBlock(ptr);
+ if (block.end > end) return nullptr;
+
+ switch (block.type) {
+ case TVG_TAG_FILL_RADIAL_GRADIENT: {
+ if (block.length != 3 * SIZE(float)) return nullptr;
+
+ auto ptr = block.data;
+ float x, y, radius;
+
+ READ_FLOAT(&x, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&y, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&radius, ptr);
+
+ auto fillGradRadial = RadialGradient::gen();
+ fillGradRadial->radial(x, y, radius);
+ fillGrad = move(fillGradRadial);
+ break;
+ }
+ case TVG_TAG_FILL_LINEAR_GRADIENT: {
+ if (block.length != 4 * SIZE(float)) return nullptr;
+
+ auto ptr = block.data;
+ float x1, y1, x2, y2;
+
+ READ_FLOAT(&x1, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&y1, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&x2, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&y2, ptr);
+
+ auto fillGradLinear = LinearGradient::gen();
+ fillGradLinear->linear(x1, y1, x2, y2);
+ fillGrad = move(fillGradLinear);
+ break;
+ }
+ case TVG_TAG_FILL_FILLSPREAD: {
+ if (!fillGrad) return nullptr;
+ if (block.length != SIZE(TvgBinFlag)) return nullptr;
+ fillGrad->spread((FillSpread) *block.data);
+ break;
+ }
+ case TVG_TAG_FILL_COLORSTOPS: {
+ if (!fillGrad) return nullptr;
+ if (block.length == 0 || block.length & 0x07) return nullptr;
+ uint32_t stopsCnt = block.length >> 3; // 8 bytes per ColorStop
+ if (stopsCnt > 1023) return nullptr;
+ Fill::ColorStop* stops = (Fill::ColorStop*)alloca(sizeof(Fill::ColorStop) * stopsCnt);
+ auto p = block.data;
+ for (uint32_t i = 0; i < stopsCnt; i++, p += 8) {
+ READ_FLOAT(&stops[i].offset, p);
+ stops[i].r = p[4];
+ stops[i].g = p[5];
+ stops[i].b = p[6];
+ stops[i].a = p[7];
+ }
+ fillGrad->colorStops(stops, stopsCnt);
+ break;
+ }
+ case TVG_TAG_FILL_TRANSFORM: {
+ if (!fillGrad || block.length != SIZE(Matrix)) return nullptr;
+ Matrix gradTransform;
+ memcpy(&gradTransform, block.data, SIZE(Matrix));
+ fillGrad->transform(gradTransform);
+ 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;
+ }
+ return fillGrad;
+}
+
+
+static bool _parseShapeStrokeDashPattern(const char *ptr, const char *end, Shape *shape)
+{
+ uint32_t dashPatternCnt;
+ READ_UI32(&dashPatternCnt, ptr);
+ ptr += SIZE(uint32_t);
+ if (dashPatternCnt > 0) {
+ float* dashPattern = static_cast<float*>(malloc(sizeof(float) * dashPatternCnt));
+ if (!dashPattern) return false;
+ memcpy(dashPattern, ptr, sizeof(float) * dashPatternCnt);
+ ptr += SIZE(float) * dashPatternCnt;
+
+ if (ptr > end) {
+ free(dashPattern);
+ return false;
+ }
+
+ shape->stroke(dashPattern, dashPatternCnt);
+ free(dashPattern);
+ }
+ return true;
+}
+
+
+static bool _parseShapeStroke(const char *ptr, const char *end, Shape *shape)
+{
+ while (ptr < end) {
+ auto block = _readBlock(ptr);
+ if (block.end > end) return false;
+
+ switch (block.type) {
+ case TVG_TAG_SHAPE_STROKE_CAP: {
+ if (block.length != SIZE(TvgBinFlag)) return false;
+ shape->stroke((StrokeCap) *block.data);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_JOIN: {
+ if (block.length != SIZE(TvgBinFlag)) return false;
+ shape->stroke((StrokeJoin) *block.data);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_WIDTH: {
+ if (block.length != SIZE(float)) return false;
+ float width;
+ READ_FLOAT(&width, block.data);
+ shape->stroke(width);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_COLOR: {
+ if (block.length != 4) return false;
+ shape->stroke(block.data[0], block.data[1], block.data[2], block.data[3]);
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_FILL: {
+ auto fill = _parseShapeFill(block.data, block.end);
+ if (!fill) return false;
+ shape->stroke(move(move(fill)));
+ break;
+ }
+ case TVG_TAG_SHAPE_STROKE_DASHPTRN: {
+ 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;
+ }
+ return true;
+}
+
+
+static bool _parseShape(TvgBinBlock block, Paint* paint)
+{
+ auto shape = static_cast<Shape*>(paint);
+
+ //Case1: Shape specific properties
+ switch (block.type) {
+ case TVG_TAG_SHAPE_PATH: {
+ return _parseShapePath(block.data, block.end, shape);
+ }
+ case TVG_TAG_SHAPE_STROKE: {
+ return _parseShapeStroke(block.data, block.end, shape);
+ }
+ case TVG_TAG_SHAPE_FILL: {
+ auto fill = _parseShapeFill(block.data, block.end);
+ if (!fill) return false;
+ shape->fill(move(fill));
+ return true;
+ }
+ case TVG_TAG_SHAPE_COLOR: {
+ if (block.length != 4) return false;
+ shape->fill(block.data[0], block.data[1], block.data[2], block.data[3]);
+ return true;
+ }
+ case TVG_TAG_SHAPE_FILLRULE: {
+ if (block.length != SIZE(TvgBinFlag)) return false;
+ shape->fill((FillRule)*block.data);
+ return true;
+ }
+ }
+
+ //Case2: Base Paint Properties
+ return _parsePaintProperty(block, shape);
+}
+
+
+static bool _parsePicture(TvgBinBlock block, Paint* paint)
+{
+ auto picture = static_cast<Picture*>(paint);
+
+ //Case1: Image Picture
+ if (block.type == TVG_TAG_PICTURE_RAW_IMAGE) {
+ if (block.length < 2 * SIZE(uint32_t)) return false;
+
+ auto ptr = block.data;
+ uint32_t w, h;
+
+ READ_UI32(&w, ptr);
+ ptr += SIZE(uint32_t);
+ READ_UI32(&h, ptr);
+ ptr += SIZE(uint32_t);
+
+ auto size = w * h * SIZE(uint32_t);
+ if (block.length != 2 * SIZE(uint32_t) + size) return false;
+
+ picture->load((uint32_t*) ptr, w, h, true);
+ return true;
+ }
+
+ //Case2: Base Paint Properties
+ if (_parsePaintProperty(block, picture)) return true;
+
+ //Vector Picture won't be requested since Saver replaces it with the Scene
+ return false;
+}
+
+
+static Paint* _parsePaint(TvgBinBlock baseBlock)
+{
+ bool (*parser)(TvgBinBlock, Paint*);
+ Paint *paint;
+
+ //1. Decide the type of paint.
+ switch (baseBlock.type) {
+ case TVG_TAG_CLASS_SCENE: {
+ paint = Scene::gen().release();
+ parser = _parseScene;
+ break;
+ }
+ case TVG_TAG_CLASS_SHAPE: {
+ paint = Shape::gen().release();
+ parser = _parseShape;
+ break;
+ }
+ case TVG_TAG_CLASS_PICTURE: {
+ paint = Picture::gen().release();
+ parser = _parsePicture;
+ break;
+ }
+ default: {
+ TVGERR("TVG", "Invalid Paint Type %d (0x%x)", baseBlock.type, baseBlock.type);
+ return nullptr;
+ }
+ }
+
+ auto ptr = baseBlock.data;
+
+ //2. Read Subsquent properties of the current paint.
+ while (ptr < baseBlock.end) {
+ 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 (0x%x)", baseBlock.type, baseBlock.type);
+ return paint;
+ }
+ ptr = block.end;
+ }
+ return paint;
+}
+
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+unique_ptr<Scene> TvgBinInterpreter::run(const char *ptr, const char* end)
+{
+ auto scene = Scene::gen();
+ if (!scene) return nullptr;
+
+ while (ptr < end) {
+ auto block = _readBlock(ptr);
+ if (block.end > end) {
+ TVGERR("TVG", "Corrupted tvg file.");
+ return nullptr;
+ }
+ scene->push(unique_ptr<Paint>(_parsePaint(block)));
+ ptr = block.end;
+ }
+
+ return scene;
+}
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
new file mode 100644
index 0000000000..62181605a2
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgCommon.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021 - 2022 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.
+ */
+
+#ifndef _TVG_TVG_COMMON_H_
+#define _TVG_TVG_COMMON_H_
+
+#include "tvgCommon.h"
+#include "tvgBinaryDesc.h"
+
+#define SIZE(A) sizeof(A)
+#define READ_UI32(dst, src) memcpy(dst, (src), sizeof(uint32_t))
+#define READ_FLOAT(dst, src) memcpy(dst, (src), sizeof(float))
+
+
+/* Interface for Tvg Binary Interpreter */
+class TvgBinInterpreterBase
+{
+public:
+ virtual ~TvgBinInterpreterBase() {}
+
+ /* ptr: points the tvg binary body (after header)
+ end: end of the tvg binary data */
+ virtual unique_ptr<Scene> run(const char* ptr, const char* end) = 0;
+};
+
+
+/* Version 0 */
+class TvgBinInterpreter : public TvgBinInterpreterBase
+{
+public:
+ unique_ptr<Scene> run(const char* ptr, const char* end) override;
+};
+
+
+#endif //_TVG_TVG_COMMON_H_ \ No newline at end of file
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
new file mode 100644
index 0000000000..95d629d1f6
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2021 - 2022 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 <memory.h>
+#include <fstream>
+#include "tvgLoader.h"
+#include "tvgTvgLoader.h"
+#include "tvgLzw.h"
+
+
+/************************************************************************/
+/* Internal Class Implementation */
+/************************************************************************/
+
+
+void TvgLoader::clear()
+{
+ if (copy) free((char*)data);
+ ptr = data = nullptr;
+ size = 0;
+ copy = false;
+
+ if (interpreter) {
+ delete(interpreter);
+ interpreter = nullptr;
+ }
+}
+
+
+/* WARNING: Header format shall not change! */
+bool TvgLoader::readHeader()
+{
+ if (!ptr) return false;
+
+ //Make sure the size is large enough to hold the header
+ if (size < TVG_HEADER_SIZE) return false;
+
+ //1. Signature
+ if (memcmp(ptr, TVG_HEADER_SIGNATURE, TVG_HEADER_SIGNATURE_LENGTH)) return false;
+ ptr += TVG_HEADER_SIGNATURE_LENGTH;
+
+ //2. Version
+ char version[TVG_HEADER_VERSION_LENGTH + 1];
+ memcpy(version, ptr, TVG_HEADER_VERSION_LENGTH);
+ version[TVG_HEADER_VERSION_LENGTH - 1] = '\0';
+ ptr += TVG_HEADER_VERSION_LENGTH;
+ this->version = atoi(version);
+ if (this->version > THORVG_VERSION_NUMBER()) {
+ TVGLOG("TVG", "This TVG file expects a higher version(%d) of ThorVG symbol(%d)", this->version, THORVG_VERSION_NUMBER());
+ }
+
+ //3. View Size
+ READ_FLOAT(&w, ptr);
+ ptr += SIZE(float);
+ READ_FLOAT(&h, ptr);
+ ptr += SIZE(float);
+
+ //4. Reserved
+ if (*ptr & TVG_HEAD_FLAG_COMPRESSED) compressed = true;
+ ptr += TVG_HEADER_RESERVED_LENGTH;
+
+ //5. Compressed Size if any
+ if (compressed) {
+ auto p = ptr;
+
+ //TVG_HEADER_UNCOMPRESSED_SIZE
+ memcpy(&uncompressedSize, p, sizeof(uint32_t));
+ p += SIZE(uint32_t);
+
+ //TVG_HEADER_COMPRESSED_SIZE
+ memcpy(&compressedSize, p, sizeof(uint32_t));
+ p += SIZE(uint32_t);
+
+ //TVG_HEADER_COMPRESSED_SIZE_BITS
+ memcpy(&compressedSizeBits, p, sizeof(uint32_t));
+ }
+
+ ptr += TVG_HEADER_COMPRESS_SIZE;
+
+ //Decide the proper Tvg Binary Interpreter based on the current file version
+ if (this->version >= 0) interpreter = new TvgBinInterpreter;
+
+ return true;
+}
+
+
+/************************************************************************/
+/* External Class Implementation */
+/************************************************************************/
+
+TvgLoader::~TvgLoader()
+{
+ close();
+}
+
+
+bool TvgLoader::open(const string &path)
+{
+ clear();
+
+ ifstream f;
+ f.open(path, ifstream::in | ifstream::binary | ifstream::ate);
+
+ if (!f.is_open()) return false;
+
+ size = f.tellg();
+ f.seekg(0, ifstream::beg);
+
+ copy = true;
+ data = (char*)malloc(size);
+ if (!data) {
+ clear();
+ f.close();
+ return false;
+ }
+
+ if (!f.read((char*)data, size))
+ {
+ clear();
+ f.close();
+ return false;
+ }
+
+ f.close();
+
+ ptr = data;
+
+ return readHeader();
+}
+
+
+bool TvgLoader::open(const char *data, uint32_t size, bool copy)
+{
+ clear();
+
+ if (copy) {
+ this->data = (char*)malloc(size);
+ if (!this->data) return false;
+ memcpy((char*)this->data, data, size);
+ } else this->data = data;
+
+ this->ptr = this->data;
+ this->size = size;
+ this->copy = copy;
+
+ return readHeader();
+}
+
+
+bool TvgLoader::resize(Paint* paint, float w, float h)
+{
+ if (!paint) return false;
+
+ auto sx = w / this->w;
+ auto sy = h / this->h;
+
+ //Scale
+ auto scale = sx < sy ? sx : sy;
+ paint->scale(scale);
+
+ //Align
+ float tx = 0, ty = 0;
+ auto sw = this->w * scale;
+ auto sh = this->h * scale;
+ if (sw > sh) ty -= (h - sh) * 0.5f;
+ else tx -= (w - sw) * 0.5f;
+ paint->translate(-tx, -ty);
+
+ return true;
+}
+
+
+bool TvgLoader::read()
+{
+ if (!ptr || size == 0) return false;
+
+ TaskScheduler::request(this);
+
+ return true;
+}
+
+
+bool TvgLoader::close()
+{
+ this->done();
+ clear();
+ return true;
+}
+
+
+void TvgLoader::run(unsigned tid)
+{
+ if (root) root.reset();
+
+ auto data = const_cast<char*>(ptr);
+
+ if (compressed) {
+ data = (char*) lzwDecode((uint8_t*) data, compressedSize, compressedSizeBits, uncompressedSize);
+ root = interpreter->run(data, data + uncompressedSize);
+ free(data);
+ } else {
+ root = interpreter->run(data, this->data + size);
+ }
+
+ if (!root) clear();
+}
+
+
+unique_ptr<Paint> TvgLoader::paint()
+{
+ this->done();
+ if (root) return move(root);
+ return nullptr;
+}
diff --git a/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
new file mode 100644
index 0000000000..3ae841aa85
--- /dev/null
+++ b/thirdparty/thorvg/src/loaders/tvg/tvgTvgLoader.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2021 - 2022 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.
+ */
+
+#ifndef _TVG_TVG_LOADER_H_
+#define _TVG_TVG_LOADER_H_
+
+#include "tvgTaskScheduler.h"
+#include "tvgTvgCommon.h"
+
+
+class TvgLoader : public LoadModule, public Task
+{
+public:
+ const char* data = nullptr;
+ const char* ptr = nullptr;
+ uint32_t size = 0;
+ uint16_t version = 0;
+ unique_ptr<Scene> root = nullptr;
+ TvgBinInterpreterBase* interpreter = nullptr;
+ uint32_t uncompressedSize = 0;
+ uint32_t compressedSize = 0;
+ uint32_t compressedSizeBits = 0;
+ bool copy = false;
+ bool compressed = false;
+
+ ~TvgLoader();
+
+ using LoadModule::open;
+ bool open(const string &path) override;
+ bool open(const char *data, uint32_t size, bool copy) override;
+ bool read() override;
+ bool close() override;
+ bool resize(Paint* paint, float w, float h) override;
+ unique_ptr<Paint> paint() override;
+
+private:
+ bool readHeader();
+ void run(unsigned tid) override;
+ void clear();
+};
+
+#endif //_TVG_TVG_LOADER_H_