feat: godot-engine-source-4.3-stable
This commit is contained in:
parent
c59a7dcade
commit
7125d019b5
11149 changed files with 5070401 additions and 0 deletions
113
engine/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
vendored
Normal file
113
engine/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp
vendored
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 "tvgPngLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void PngLoader::clear()
|
||||
{
|
||||
png_image_free(image);
|
||||
free(image);
|
||||
image = nullptr;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
PngLoader::PngLoader() : ImageLoader(FileType::Png)
|
||||
{
|
||||
image = static_cast<png_imagep>(calloc(1, sizeof(png_image)));
|
||||
image->version = PNG_IMAGE_VERSION;
|
||||
image->opaque = NULL;
|
||||
}
|
||||
|
||||
PngLoader::~PngLoader()
|
||||
{
|
||||
clear();
|
||||
free((void*)surface.buf32);
|
||||
}
|
||||
|
||||
|
||||
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()
|
||||
{
|
||||
if (!LoadModule::read()) return true;
|
||||
|
||||
if (w == 0 || h == 0) return false;
|
||||
|
||||
if (cs == ColorSpace::ARGB8888 || cs == ColorSpace::ARGB8888S) {
|
||||
image->format = PNG_FORMAT_BGRA;
|
||||
surface.cs = ColorSpace::ARGB8888;
|
||||
} else {
|
||||
image->format = PNG_FORMAT_RGBA;
|
||||
surface.cs = ColorSpace::ABGR8888;
|
||||
}
|
||||
|
||||
auto buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image))));
|
||||
if (!png_image_finish_read(image, NULL, buffer, 0, NULL)) {
|
||||
free(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
//setup the surface
|
||||
surface.buf32 = reinterpret_cast<uint32_t*>(buffer);
|
||||
surface.stride = (uint32_t)w;
|
||||
surface.w = (uint32_t)w;
|
||||
surface.h = (uint32_t)h;
|
||||
surface.channelSize = sizeof(uint32_t);
|
||||
//TODO: we can acquire a pre-multiplied image. See "png_structrp"
|
||||
surface.premultiplied = false;
|
||||
|
||||
clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
45
engine/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
vendored
Normal file
45
engine/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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>
|
||||
#include "tvgLoader.h"
|
||||
|
||||
class PngLoader : public ImageLoader
|
||||
{
|
||||
public:
|
||||
PngLoader();
|
||||
~PngLoader();
|
||||
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
|
||||
private:
|
||||
void clear();
|
||||
|
||||
png_imagep image = nullptr;
|
||||
};
|
||||
|
||||
#endif //_TVG_PNG_LOADER_H_
|
||||
142
engine/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp
vendored
Normal file
142
engine/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.cpp
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2023 - 2024 the ThorVG project. 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 <webp/decode.h>
|
||||
|
||||
#include "tvgWebpLoader.h"
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
|
||||
void WebpLoader::run(unsigned tid)
|
||||
{
|
||||
//TODO: acquire the current colorspace format & pre-multiplied alpha image.
|
||||
surface.buf8 = WebPDecodeBGRA(data, size, nullptr, nullptr);
|
||||
surface.stride = (uint32_t)w;
|
||||
surface.w = (uint32_t)w;
|
||||
surface.h = (uint32_t)h;
|
||||
surface.channelSize = sizeof(uint32_t);
|
||||
surface.cs = ColorSpace::ARGB8888;
|
||||
surface.premultiplied = false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
WebpLoader::WebpLoader() : ImageLoader(FileType::Webp)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
WebpLoader::~WebpLoader()
|
||||
{
|
||||
this->done();
|
||||
|
||||
if (freeData) free(data);
|
||||
data = nullptr;
|
||||
size = 0;
|
||||
freeData = false;
|
||||
WebPFree(surface.buf8);
|
||||
}
|
||||
|
||||
|
||||
bool WebpLoader::open(const string& path)
|
||||
{
|
||||
auto webpFile = fopen(path.c_str(), "rb");
|
||||
if (!webpFile) return false;
|
||||
|
||||
auto ret = false;
|
||||
|
||||
//determine size
|
||||
if (fseek(webpFile, 0, SEEK_END) < 0) goto finalize;
|
||||
if (((size = ftell(webpFile)) < 1)) goto finalize;
|
||||
if (fseek(webpFile, 0, SEEK_SET)) goto finalize;
|
||||
|
||||
data = (unsigned char *) malloc(size);
|
||||
if (!data) goto finalize;
|
||||
|
||||
freeData = true;
|
||||
|
||||
if (fread(data, size, 1, webpFile) < 1) goto finalize;
|
||||
|
||||
int width, height;
|
||||
if (!WebPGetInfo(data, size, &width, &height)) goto finalize;
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
|
||||
ret = true;
|
||||
|
||||
finalize:
|
||||
fclose(webpFile);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool WebpLoader::open(const char* data, uint32_t size, bool copy)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
int width, height;
|
||||
if (!WebPGetInfo(this->data, size, &width, &height)) return false;
|
||||
|
||||
w = static_cast<float>(width);
|
||||
h = static_cast<float>(height);
|
||||
surface.cs = ColorSpace::ARGB8888;
|
||||
this->size = size;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool WebpLoader::read()
|
||||
{
|
||||
if (!LoadModule::read()) return true;
|
||||
|
||||
if (!data || w == 0 || h == 0) return false;
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Surface* WebpLoader::bitmap()
|
||||
{
|
||||
this->done();
|
||||
|
||||
return ImageLoader::bitmap();
|
||||
}
|
||||
49
engine/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h
vendored
Normal file
49
engine/thirdparty/thorvg/src/loaders/external_webp/tvgWebpLoader.h
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright (c) 2023 - 2024 the ThorVG project. 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_WEBP_LOADER_H_
|
||||
#define _TVG_WEBP_LOADER_H_
|
||||
|
||||
#include "tvgLoader.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
class WebpLoader : public ImageLoader, public Task
|
||||
{
|
||||
public:
|
||||
WebpLoader();
|
||||
~WebpLoader();
|
||||
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
|
||||
Surface* bitmap() override;
|
||||
|
||||
private:
|
||||
void run(unsigned tid) override;
|
||||
|
||||
unsigned char* data = nullptr;
|
||||
unsigned long size = 0;
|
||||
bool freeData = false;
|
||||
};
|
||||
|
||||
#endif //_TVG_WEBP_LOADER_H_
|
||||
132
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
vendored
Normal file
132
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp
vendored
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 "tvgJpgLoader.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void JpgLoader::clear()
|
||||
{
|
||||
jpgdDelete(decoder);
|
||||
if (freeData) free(data);
|
||||
decoder = nullptr;
|
||||
data = nullptr;
|
||||
freeData = false;
|
||||
}
|
||||
|
||||
|
||||
void JpgLoader::run(unsigned tid)
|
||||
{
|
||||
surface.buf8 = jpgdDecompress(decoder);
|
||||
surface.stride = static_cast<uint32_t>(w);
|
||||
surface.w = static_cast<uint32_t>(w);
|
||||
surface.h = static_cast<uint32_t>(h);
|
||||
surface.cs = ColorSpace::ARGB8888;
|
||||
surface.channelSize = sizeof(uint32_t);
|
||||
surface.premultiplied = true;
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
JpgLoader::JpgLoader() : ImageLoader(FileType::Jpg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
JpgLoader::~JpgLoader()
|
||||
{
|
||||
clear();
|
||||
free(surface.buf8);
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::open(const string& path)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 (!LoadModule::read()) return true;
|
||||
|
||||
if (!decoder || w == 0 || h == 0) return false;
|
||||
|
||||
TaskScheduler::request(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool JpgLoader::close()
|
||||
{
|
||||
if (!LoadModule::close()) return false;
|
||||
this->done();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Surface* JpgLoader::bitmap()
|
||||
{
|
||||
this->done();
|
||||
return ImageLoader::bitmap();
|
||||
}
|
||||
52
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
vendored
Normal file
52
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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 "tvgLoader.h"
|
||||
#include "tvgTaskScheduler.h"
|
||||
#include "tvgJpgd.h"
|
||||
|
||||
class JpgLoader : public ImageLoader, public Task
|
||||
{
|
||||
private:
|
||||
jpeg_decoder* decoder = nullptr;
|
||||
char* data = nullptr;
|
||||
bool freeData = false;
|
||||
|
||||
void clear();
|
||||
void run(unsigned tid) override;
|
||||
|
||||
public:
|
||||
JpgLoader();
|
||||
~JpgLoader();
|
||||
|
||||
bool open(const string& path) override;
|
||||
bool open(const char* data, uint32_t size, bool copy) override;
|
||||
bool read() override;
|
||||
bool close() override;
|
||||
|
||||
Surface* bitmap() override;
|
||||
};
|
||||
|
||||
#endif //_TVG_JPG_LOADER_H_
|
||||
3030
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
vendored
Normal file
3030
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
35
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
vendored
Normal file
35
engine/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (c) 2021 - 2024 the ThorVG project. 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_
|
||||
82
engine/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
vendored
Normal file
82
engine/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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() : ImageLoader(FileType::Raw)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
RawLoader::~RawLoader()
|
||||
{
|
||||
if (copy) free(surface.buf32);
|
||||
}
|
||||
|
||||
|
||||
bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy)
|
||||
{
|
||||
if (!LoadModule::read()) return true;
|
||||
|
||||
if (!data || w == 0 || h == 0) return false;
|
||||
|
||||
this->w = (float)w;
|
||||
this->h = (float)h;
|
||||
this->copy = copy;
|
||||
|
||||
if (copy) {
|
||||
surface.buf32 = (uint32_t*)malloc(sizeof(uint32_t) * w * h);
|
||||
if (!surface.buf32) return false;
|
||||
memcpy((void*)surface.buf32, data, sizeof(uint32_t) * w * h);
|
||||
}
|
||||
else surface.buf32 = const_cast<uint32_t*>(data);
|
||||
|
||||
//setup the surface
|
||||
surface.stride = w;
|
||||
surface.w = w;
|
||||
surface.h = h;
|
||||
surface.cs = ColorSpace::ARGB8888;
|
||||
surface.channelSize = sizeof(uint32_t);
|
||||
surface.premultiplied = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool RawLoader::read()
|
||||
{
|
||||
LoadModule::read();
|
||||
|
||||
return true;
|
||||
}
|
||||
40
engine/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
vendored
Normal file
40
engine/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 ImageLoader
|
||||
{
|
||||
public:
|
||||
bool copy = false;
|
||||
|
||||
RawLoader();
|
||||
~RawLoader();
|
||||
|
||||
using LoadModule::open;
|
||||
bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy);
|
||||
bool read() override;
|
||||
};
|
||||
|
||||
|
||||
#endif //_TVG_RAW_LOADER_H_
|
||||
265
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
vendored
Normal file
265
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp
vendored
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
* Copyright (c) 2022 - 2024 the ThorVG project. 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 bool _isImportanceApplicable(SvgStyleFlags &toFlagsImportance, SvgStyleFlags fromFlagsImportance, SvgStyleFlags flag)
|
||||
{
|
||||
if (!(toFlagsImportance & flag) && (fromFlagsImportance & flag)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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 && !(to->flags & SvgStyleFlags::Color)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Color)) {
|
||||
to->color = from->color;
|
||||
to->curColorSet = true;
|
||||
to->flags = (to->flags | SvgStyleFlags::Color);
|
||||
if (from->flagsImportance & SvgStyleFlags::Color) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Color);
|
||||
}
|
||||
}
|
||||
if (((from->flags & SvgStyleFlags::PaintOrder) && !(to->flags & SvgStyleFlags::PaintOrder)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::PaintOrder)) {
|
||||
to->paintOrder = from->paintOrder;
|
||||
to->flags = (to->flags | SvgStyleFlags::PaintOrder);
|
||||
if (from->flagsImportance & SvgStyleFlags::PaintOrder) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::PaintOrder);
|
||||
}
|
||||
}
|
||||
if (((from->flags & SvgStyleFlags::Display) && !(to->flags & SvgStyleFlags::Display)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Display)) {
|
||||
to->display = from->display;
|
||||
to->flags = (to->flags | SvgStyleFlags::Display);
|
||||
if (from->flagsImportance & SvgStyleFlags::Display) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Display);
|
||||
}
|
||||
}
|
||||
//Fill
|
||||
if (((from->fill.flags & SvgFillFlags::Paint) && !(to->flags & SvgStyleFlags::Fill)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, 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) {
|
||||
if (to->fill.paint.url) free(to->fill.paint.url);
|
||||
to->fill.paint.url = strdup(from->fill.paint.url);
|
||||
}
|
||||
to->fill.flags = (to->fill.flags | SvgFillFlags::Paint);
|
||||
to->flags = (to->flags | SvgStyleFlags::Fill);
|
||||
if (from->flagsImportance & SvgStyleFlags::Fill) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Fill);
|
||||
}
|
||||
}
|
||||
if (((from->fill.flags & SvgFillFlags::Opacity) && !(to->flags & SvgStyleFlags::FillOpacity)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillOpacity)) {
|
||||
to->fill.opacity = from->fill.opacity;
|
||||
to->fill.flags = (to->fill.flags | SvgFillFlags::Opacity);
|
||||
to->flags = (to->flags | SvgStyleFlags::FillOpacity);
|
||||
if (from->flagsImportance & SvgStyleFlags::FillOpacity) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillOpacity);
|
||||
}
|
||||
}
|
||||
if (((from->fill.flags & SvgFillFlags::FillRule) && !(to->flags & SvgStyleFlags::FillRule)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::FillRule)) {
|
||||
to->fill.fillRule = from->fill.fillRule;
|
||||
to->fill.flags = (to->fill.flags | SvgFillFlags::FillRule);
|
||||
to->flags = (to->flags | SvgStyleFlags::FillRule);
|
||||
if (from->flagsImportance & SvgStyleFlags::FillRule) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::FillRule);
|
||||
}
|
||||
}
|
||||
//Stroke
|
||||
if (((from->stroke.flags & SvgStrokeFlags::Paint) && !(to->flags & SvgStyleFlags::Stroke)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, 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) {
|
||||
if (to->stroke.paint.url) free(to->stroke.paint.url);
|
||||
to->stroke.paint.url = strdup(from->stroke.paint.url);
|
||||
}
|
||||
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Paint);
|
||||
to->flags = (to->flags | SvgStyleFlags::Stroke);
|
||||
if (from->flagsImportance & SvgStyleFlags::Stroke) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Stroke);
|
||||
}
|
||||
}
|
||||
if (((from->stroke.flags & SvgStrokeFlags::Opacity) && !(to->flags & SvgStyleFlags::StrokeOpacity)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeOpacity)) {
|
||||
to->stroke.opacity = from->stroke.opacity;
|
||||
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Opacity);
|
||||
to->flags = (to->flags | SvgStyleFlags::StrokeOpacity);
|
||||
if (from->flagsImportance & SvgStyleFlags::StrokeOpacity) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeOpacity);
|
||||
}
|
||||
}
|
||||
if (((from->stroke.flags & SvgStrokeFlags::Width) && !(to->flags & SvgStyleFlags::StrokeWidth)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeWidth)) {
|
||||
to->stroke.width = from->stroke.width;
|
||||
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Width);
|
||||
to->flags = (to->flags | SvgStyleFlags::StrokeWidth);
|
||||
if (from->flagsImportance & SvgStyleFlags::StrokeWidth) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeWidth);
|
||||
}
|
||||
}
|
||||
if (((from->stroke.flags & SvgStrokeFlags::Dash) && !(to->flags & SvgStyleFlags::StrokeDashArray)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, 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[i]);
|
||||
}
|
||||
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Dash);
|
||||
to->flags = (to->flags | SvgStyleFlags::StrokeDashArray);
|
||||
if (from->flagsImportance & SvgStyleFlags::StrokeDashArray) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeDashArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (((from->stroke.flags & SvgStrokeFlags::Cap) && !(to->flags & SvgStyleFlags::StrokeLineCap)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineCap)) {
|
||||
to->stroke.cap = from->stroke.cap;
|
||||
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Cap);
|
||||
to->flags = (to->flags | SvgStyleFlags::StrokeLineCap);
|
||||
if (from->flagsImportance & SvgStyleFlags::StrokeLineCap) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineCap);
|
||||
}
|
||||
}
|
||||
if (((from->stroke.flags & SvgStrokeFlags::Join) && !(to->flags & SvgStyleFlags::StrokeLineJoin)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::StrokeLineJoin)) {
|
||||
to->stroke.join = from->stroke.join;
|
||||
to->stroke.flags = (to->stroke.flags | SvgStrokeFlags::Join);
|
||||
to->flags = (to->flags | SvgStyleFlags::StrokeLineJoin);
|
||||
if (from->flagsImportance & SvgStyleFlags::StrokeLineJoin) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::StrokeLineJoin);
|
||||
}
|
||||
}
|
||||
//Opacity
|
||||
//TODO: it can be set to be 255 and shouldn't be changed by attribute 'opacity'
|
||||
if ((from->opacity < 255 && !(to->flags & SvgStyleFlags::Opacity)) ||
|
||||
_isImportanceApplicable(to->flagsImportance, from->flagsImportance, SvgStyleFlags::Opacity)) {
|
||||
to->opacity = from->opacity;
|
||||
to->flags = (to->flags | SvgStyleFlags::Opacity);
|
||||
if (from->flagsImportance & SvgStyleFlags::Opacity) {
|
||||
to->flagsImportance = (to->flagsImportance | SvgStyleFlags::Opacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
void cssCopyStyleAttr(SvgNode* to, const SvgNode* from)
|
||||
{
|
||||
//Copy matrix attribute
|
||||
if (from->transform && !(to->style->flags & SvgStyleFlags::Transform)) {
|
||||
to->transform = (Matrix*)malloc(sizeof(Matrix));
|
||||
if (to->transform) {
|
||||
*to->transform = *from->transform;
|
||||
to->style->flags = (to->style->flags | SvgStyleFlags::Transform);
|
||||
}
|
||||
}
|
||||
//Copy style attribute
|
||||
_copyStyle(to->style, from->style);
|
||||
|
||||
if (from->style->clipPath.url) {
|
||||
if (to->style->clipPath.url) free(to->style->clipPath.url);
|
||||
to->style->clipPath.url = strdup(from->style->clipPath.url);
|
||||
}
|
||||
if (from->style->mask.url) {
|
||||
if (to->style->mask.url) free(to->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 || !title) return nullptr;
|
||||
|
||||
auto child = style->child.data;
|
||||
for (uint32_t i = 0; i < style->child.count; ++i, ++child) {
|
||||
if ((*child)->type == SvgNodeType::CssStyle) {
|
||||
if ((*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);
|
||||
}
|
||||
cssUpdateStyle(*child, style);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void cssApplyStyleToPostponeds(Array<SvgNodeIdPair>& postponeds, SvgNode* style)
|
||||
{
|
||||
for (uint32_t i = 0; i < postponeds.count; ++i) {
|
||||
auto nodeIdPair = postponeds[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);
|
||||
}
|
||||
}
|
||||
}
|
||||
34
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
vendored
Normal file
34
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2022 - 2024 the ThorVG project. 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_
|
||||
4009
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
vendored
Normal file
4009
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp
vendored
Normal file
File diff suppressed because it is too large
Load diff
68
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
vendored
Normal file
68
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 ImageLoader, public Task
|
||||
{
|
||||
public:
|
||||
string filePath;
|
||||
string svgPath = "";
|
||||
char* content = nullptr;
|
||||
uint32_t size = 0;
|
||||
|
||||
SvgLoaderData loaderData;
|
||||
Scene* root = nullptr;
|
||||
|
||||
bool copy = false;
|
||||
|
||||
SvgLoader();
|
||||
~SvgLoader();
|
||||
|
||||
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;
|
||||
|
||||
Paint* paint() override;
|
||||
|
||||
private:
|
||||
SvgViewFlag viewFlag = SvgViewFlag::None;
|
||||
AspectRatioAlign align = AspectRatioAlign::XMidYMid;
|
||||
AspectRatioMeetOrSlice meetOrSlice = AspectRatioMeetOrSlice::Meet;
|
||||
float vx = 0;
|
||||
float vy = 0;
|
||||
float vw = 0;
|
||||
float vh = 0;
|
||||
|
||||
bool header();
|
||||
void clear(bool all = true);
|
||||
void run(unsigned tid) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //_TVG_SVG_LOADER_H_
|
||||
587
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
vendored
Normal file
587
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h
vendored
Normal file
|
|
@ -0,0 +1,587 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgFillFlags a, SvgFillFlags b)
|
||||
{
|
||||
return int(a) & int(b);
|
||||
}
|
||||
|
||||
constexpr SvgFillFlags operator |(SvgFillFlags a, SvgFillFlags b)
|
||||
{
|
||||
return SvgFillFlags(int(a) | int(b));
|
||||
}
|
||||
|
||||
enum class SvgStrokeFlags
|
||||
{
|
||||
Paint = 0x1,
|
||||
Opacity = 0x2,
|
||||
Gradient = 0x4,
|
||||
Scale = 0x8,
|
||||
Width = 0x10,
|
||||
Cap = 0x20,
|
||||
Join = 0x40,
|
||||
Dash = 0x80,
|
||||
Miterlimit = 0x100,
|
||||
DashOffset = 0x200
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStrokeFlags a, SvgStrokeFlags b)
|
||||
{
|
||||
return int(a) & int(b);
|
||||
}
|
||||
|
||||
constexpr SvgStrokeFlags operator |(SvgStrokeFlags a, SvgStrokeFlags b)
|
||||
{
|
||||
return SvgStrokeFlags(int(a) | int(b));
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
PaintOrder = 0x10000,
|
||||
StrokeMiterlimit = 0x20000,
|
||||
StrokeDashOffset = 0x40000,
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStyleFlags a, SvgStyleFlags b)
|
||||
{
|
||||
return int(a) & int(b);
|
||||
}
|
||||
|
||||
constexpr SvgStyleFlags operator |(SvgStyleFlags a, SvgStyleFlags b)
|
||||
{
|
||||
return SvgStyleFlags(int(a) | int(b));
|
||||
}
|
||||
|
||||
enum class SvgStopStyleFlags
|
||||
{
|
||||
StopDefault = 0x0,
|
||||
StopOpacity = 0x01,
|
||||
StopColor = 0x02
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgStopStyleFlags a, SvgStopStyleFlags b)
|
||||
{
|
||||
return int(a) & int(b);
|
||||
}
|
||||
|
||||
constexpr SvgStopStyleFlags operator |(SvgStopStyleFlags a, SvgStopStyleFlags b)
|
||||
{
|
||||
return SvgStopStyleFlags(int(a) | int(b));
|
||||
}
|
||||
|
||||
enum class SvgGradientFlags
|
||||
{
|
||||
None = 0x0,
|
||||
GradientUnits = 0x1,
|
||||
SpreadMethod = 0x2,
|
||||
X1 = 0x4,
|
||||
X2 = 0x8,
|
||||
Y1 = 0x10,
|
||||
Y2 = 0x20,
|
||||
Cx = 0x40,
|
||||
Cy = 0x80,
|
||||
R = 0x100,
|
||||
Fx = 0x200,
|
||||
Fy = 0x400,
|
||||
Fr = 0x800
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgGradientFlags a, SvgGradientFlags b)
|
||||
{
|
||||
return int(a) & int(b);
|
||||
}
|
||||
|
||||
constexpr SvgGradientFlags operator |(SvgGradientFlags a, SvgGradientFlags b)
|
||||
{
|
||||
return SvgGradientFlags(int(a) | int(b));
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
enum class SvgViewFlag
|
||||
{
|
||||
None = 0x0,
|
||||
Width = 0x01, //viewPort width
|
||||
Height = 0x02, //viewPort height
|
||||
Viewbox = 0x04, //viewBox x,y,w,h - used only if all 4 are correctly set
|
||||
WidthInPercent = 0x08,
|
||||
HeightInPercent = 0x10
|
||||
};
|
||||
|
||||
constexpr bool operator &(SvgViewFlag a, SvgViewFlag b)
|
||||
{
|
||||
return static_cast<int>(a) & static_cast<int>(b);
|
||||
}
|
||||
|
||||
constexpr SvgViewFlag operator |(SvgViewFlag a, SvgViewFlag b)
|
||||
{
|
||||
return SvgViewFlag(int(a) | int(b));
|
||||
}
|
||||
|
||||
constexpr SvgViewFlag operator ^(SvgViewFlag a, SvgViewFlag b)
|
||||
{
|
||||
return SvgViewFlag(int(a) ^ int(b));
|
||||
}
|
||||
|
||||
enum class AspectRatioAlign
|
||||
{
|
||||
None,
|
||||
XMinYMin,
|
||||
XMidYMin,
|
||||
XMaxYMin,
|
||||
XMinYMid,
|
||||
XMidYMid,
|
||||
XMaxYMid,
|
||||
XMinYMax,
|
||||
XMidYMax,
|
||||
XMaxYMax
|
||||
};
|
||||
|
||||
enum class AspectRatioMeetOrSlice
|
||||
{
|
||||
Meet,
|
||||
Slice
|
||||
};
|
||||
|
||||
struct SvgDocNode
|
||||
{
|
||||
float w; //unit: point or in percentage see: SvgViewFlag
|
||||
float h; //unit: point or in percentage see: SvgViewFlag
|
||||
float vx;
|
||||
float vy;
|
||||
float vw;
|
||||
float vh;
|
||||
SvgViewFlag viewFlag;
|
||||
SvgNode* defs;
|
||||
SvgNode* style;
|
||||
AspectRatioAlign align;
|
||||
AspectRatioMeetOrSlice meetOrSlice;
|
||||
};
|
||||
|
||||
struct SvgGNode
|
||||
{
|
||||
};
|
||||
|
||||
struct SvgDefsNode
|
||||
{
|
||||
Array<SvgStyleGradient*> gradients;
|
||||
};
|
||||
|
||||
struct SvgSymbolNode
|
||||
{
|
||||
float w, h;
|
||||
float vx, vy, vw, vh;
|
||||
AspectRatioAlign align;
|
||||
AspectRatioMeetOrSlice meetOrSlice;
|
||||
bool overflowVisible;
|
||||
bool hasViewBox;
|
||||
bool hasWidth;
|
||||
bool hasHeight;
|
||||
};
|
||||
|
||||
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
|
||||
{
|
||||
Array<float> pts;
|
||||
};
|
||||
|
||||
struct SvgClipNode
|
||||
{
|
||||
bool userSpace;
|
||||
};
|
||||
|
||||
struct SvgMaskNode
|
||||
{
|
||||
SvgMaskType type;
|
||||
bool userSpace;
|
||||
};
|
||||
|
||||
struct SvgCssStyleNode
|
||||
{
|
||||
};
|
||||
|
||||
struct SvgTextNode
|
||||
{
|
||||
char* text;
|
||||
char* fontFamily;
|
||||
float x, y;
|
||||
float fontSize;
|
||||
};
|
||||
|
||||
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;
|
||||
float fr;
|
||||
bool isCxPercentage;
|
||||
bool isCyPercentage;
|
||||
bool isFxPercentage;
|
||||
bool isFyPercentage;
|
||||
bool isRPercentage;
|
||||
bool isFrPercentage;
|
||||
};
|
||||
|
||||
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;
|
||||
float offset;
|
||||
};
|
||||
|
||||
struct SvgStyleGradient
|
||||
{
|
||||
SvgGradientType type;
|
||||
char* id;
|
||||
char* ref;
|
||||
FillSpread spread;
|
||||
SvgRadialGradient* radial;
|
||||
SvgLinearGradient* linear;
|
||||
Matrix* transform;
|
||||
Array<Fill::ColorStop> stops;
|
||||
SvgGradientFlags flags;
|
||||
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;
|
||||
float miterlimit;
|
||||
SvgDash dash;
|
||||
};
|
||||
|
||||
struct SvgStyleProperty
|
||||
{
|
||||
SvgStyleFill fill;
|
||||
SvgStyleStroke stroke;
|
||||
SvgComposite clipPath;
|
||||
SvgComposite mask;
|
||||
int opacity;
|
||||
SvgColor color;
|
||||
char* cssClass;
|
||||
SvgStyleFlags flags;
|
||||
SvgStyleFlags flagsImportance; //indicates the importance of the flag - if set, higher priority is applied (https://drafts.csswg.org/css-cascade-4/#importance)
|
||||
bool curColorSet;
|
||||
bool paintOrder; //true if default (fill, stroke), false otherwise
|
||||
bool display;
|
||||
};
|
||||
|
||||
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;
|
||||
SvgTextNode text;
|
||||
} node;
|
||||
~SvgNode();
|
||||
};
|
||||
|
||||
struct SvgParser
|
||||
{
|
||||
SvgNode* node;
|
||||
SvgStyleGradient* styleGrad;
|
||||
Fill::ColorStop gradStop;
|
||||
SvgStopStyleFlags flags;
|
||||
struct
|
||||
{
|
||||
float x, y, w, h;
|
||||
} global;
|
||||
struct
|
||||
{
|
||||
bool parsedFx;
|
||||
bool parsedFy;
|
||||
} gradient;
|
||||
};
|
||||
|
||||
struct SvgNodeIdPair
|
||||
{
|
||||
SvgNode* node;
|
||||
char *id;
|
||||
};
|
||||
|
||||
enum class OpenedTagType : uint8_t
|
||||
{
|
||||
Other = 0,
|
||||
Style,
|
||||
Text
|
||||
};
|
||||
|
||||
struct SvgLoaderData
|
||||
{
|
||||
Array<SvgNode*> stack;
|
||||
SvgNode* doc = nullptr;
|
||||
SvgNode* def = nullptr; //also used to store nested graphic nodes
|
||||
SvgNode* cssStyle = nullptr;
|
||||
Array<SvgStyleGradient*> gradients;
|
||||
SvgStyleGradient* latestGradient = nullptr; //For stops
|
||||
SvgParser* svgParse = nullptr;
|
||||
Array<SvgNodeIdPair> cloneNodes;
|
||||
Array<SvgNodeIdPair> nodesToStyle;
|
||||
Array<char*> images; //embedded images
|
||||
int level = 0;
|
||||
bool result = false;
|
||||
OpenedTagType openedTag = OpenedTagType::Other;
|
||||
SvgNode* currentGraphicsNode = nullptr;
|
||||
};
|
||||
|
||||
struct Box
|
||||
{
|
||||
float x, y, w, h;
|
||||
};
|
||||
|
||||
#endif
|
||||
571
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
vendored
Normal file
571
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp
vendored
Normal file
|
|
@ -0,0 +1,571 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 <ctype.h>
|
||||
#include "tvgMath.h"
|
||||
#include "tvgShape.h"
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
#include "tvgSvgPath.h"
|
||||
#include "tvgStr.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 = strToFloat(*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;
|
||||
|
||||
//Correction of out-of-range radii, see F6.6.1 (step 2)
|
||||
rx = fabsf(rx);
|
||||
ry = fabsf(ry);
|
||||
|
||||
angle = mathDeg2Rad(angle);
|
||||
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 = mathAtan2(((y1p - cyp) / ry), ((x1p - cxp) / rx));
|
||||
theta1 = (at < 0.0f) ? 2.0f * MATH_PI + at : at;
|
||||
|
||||
nat = mathAtan2(((-y1p - cyp) / ry), ((-x1p - cxp) / rx));
|
||||
deltaTheta = (nat < at) ? 2.0f * MATH_PI - at + nat : nat - at;
|
||||
|
||||
if (sweep) {
|
||||
//Ensure delta theta < 0 or else add 360 degrees
|
||||
if (deltaTheta < 0.0f) deltaTheta += 2.0f * MATH_PI;
|
||||
} else {
|
||||
//Ensure delta theta > 0 or else substract 360 degrees
|
||||
if (deltaTheta > 0.0f) deltaTheta -= 2.0f * MATH_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 / MATH_PI2) + 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, bool* closed)
|
||||
{
|
||||
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->last() == 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->last() == 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;
|
||||
*closed = true;
|
||||
break;
|
||||
}
|
||||
case 'a':
|
||||
case 'A': {
|
||||
if (mathZero(arr[0]) || mathZero(arr[1])) {
|
||||
Point p = {arr[5], arr[6]};
|
||||
cmds->push(PathCommand::LineTo);
|
||||
pts->push(p);
|
||||
*cur = {arr[5], arr[6]};
|
||||
} else if (!mathEqual(cur->x, arr[5]) || !mathEqual(cur->y, arr[6])) {
|
||||
_pathAppendArcTo(cmds, pts, cur, curCtl, arr[5], arr[6], fabsf(arr[0]), fabsf(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, bool* closed)
|
||||
{
|
||||
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';
|
||||
else {
|
||||
if (*closed) return nullptr;
|
||||
}
|
||||
}
|
||||
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 svgPathToShape(const char* svgPath, Shape* shape)
|
||||
{
|
||||
float numberArray[7];
|
||||
int numberCount = 0;
|
||||
Point cur = { 0, 0 };
|
||||
Point curCtl = { 0, 0 };
|
||||
Point startPoint = { 0, 0 };
|
||||
char cmd = 0;
|
||||
bool isQuadratic = false;
|
||||
bool closed = false;
|
||||
char* path = (char*)svgPath;
|
||||
|
||||
auto& pts = P(shape)->rs.path.pts;
|
||||
auto& cmds = P(shape)->rs.path.cmds;
|
||||
auto lastCmds = cmds.count;
|
||||
|
||||
while ((path[0] != '\0')) {
|
||||
path = _nextCommand(path, &cmd, numberArray, &numberCount, &closed);
|
||||
if (!path) break;
|
||||
closed = false;
|
||||
if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic, &closed)) break;
|
||||
}
|
||||
|
||||
if (cmds.count > lastCmds && cmds[lastCmds] != PathCommand::MoveTo) return false;
|
||||
return true;
|
||||
}
|
||||
30
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
vendored
Normal file
30
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 svgPathToShape(const char* svgPath, Shape* shape);
|
||||
|
||||
#endif //_TVG_SVG_PATH_H_
|
||||
954
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
vendored
Normal file
954
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp
vendored
Normal file
|
|
@ -0,0 +1,954 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 "tvgMath.h" /* to include math.h before cstring */
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include "tvgShape.h"
|
||||
#include "tvgCompressor.h"
|
||||
#include "tvgPaint.h"
|
||||
#include "tvgFill.h"
|
||||
#include "tvgStr.h"
|
||||
#include "tvgSvgLoaderCommon.h"
|
||||
#include "tvgSvgSceneBuilder.h"
|
||||
#include "tvgSvgPath.h"
|
||||
#include "tvgSvgUtil.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
static bool _appendShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath);
|
||||
static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform);
|
||||
static unique_ptr<Scene> _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, 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 Box _boundingBox(const Text* text)
|
||||
{
|
||||
float x, y, w, h;
|
||||
text->bounds(&x, &y, &w, &h, false);
|
||||
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 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[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 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;
|
||||
g->radial->fr = g->radial->fr * sqrtf(powf(vBox.w, 2.0f) + powf(vBox.h, 2.0f)) / sqrtf(2.0f);
|
||||
} 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);
|
||||
|
||||
P(fillGrad)->radial(g->radial->cx, g->radial->cy, g->radial->r, g->radial->fx, g->radial->fy, g->radial->fr);
|
||||
fillGrad->spread(g->spread);
|
||||
|
||||
//Update the stops
|
||||
stopCount = g->stops.count;
|
||||
if (stopCount > 0) {
|
||||
stops = (Fill::ColorStop*)calloc(stopCount, sizeof(Fill::ColorStop));
|
||||
if (!stops) return fillGrad;
|
||||
auto prevOffset = 0.0f;
|
||||
for (uint32_t i = 0; i < g->stops.count; ++i) {
|
||||
auto colorStop = &g->stops[i];
|
||||
//Use premultiplied color
|
||||
stops[i].r = colorStop->r;
|
||||
stops[i].g = colorStop->g;
|
||||
stops[i].b = colorStop->b;
|
||||
stops[i].a = static_cast<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;
|
||||
}
|
||||
|
||||
|
||||
//The SVG standard allows only for 'use' nodes that point directly to a basic shape.
|
||||
static bool _appendClipUseNode(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
if (node->child.count != 1) return false;
|
||||
auto child = *(node->child.data);
|
||||
|
||||
Matrix finalTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
if (node->transform) finalTransform = *node->transform;
|
||||
if (node->node.use.x != 0.0f || node->node.use.y != 0.0f) {
|
||||
Matrix m = {1, 0, node->node.use.x, 0, 1, node->node.use.y, 0, 0, 1};
|
||||
finalTransform *= m;
|
||||
}
|
||||
if (child->transform) finalTransform = *child->transform * finalTransform;
|
||||
|
||||
return _appendClipShape(loaderData, child, shape, vBox, svgPath, mathIdentity((const Matrix*)(&finalTransform)) ? nullptr : &finalTransform);
|
||||
}
|
||||
|
||||
|
||||
static bool _appendClipChild(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, bool clip)
|
||||
{
|
||||
if (node->type == SvgNodeType::Use) {
|
||||
return _appendClipUseNode(loaderData, node, shape, vBox, svgPath);
|
||||
}
|
||||
return _appendClipShape(loaderData, node, shape, vBox, svgPath, nullptr);
|
||||
}
|
||||
|
||||
|
||||
static Matrix _compositionTransform(Paint* paint, const SvgNode* node, const SvgNode* compNode, SvgNodeType type)
|
||||
{
|
||||
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
//The initial mask transformation ignored according to the SVG standard.
|
||||
if (node->transform && type != SvgNodeType::Mask) {
|
||||
m = *node->transform;
|
||||
}
|
||||
if (compNode->transform) {
|
||||
m *= *compNode->transform;
|
||||
}
|
||||
if (!compNode->node.clip.userSpace) {
|
||||
float x, y, w, h;
|
||||
P(paint)->bounds(&x, &y, &w, &h, false, false);
|
||||
Matrix mBBox = {w, 0, x, 0, h, y, 0, 0, 1};
|
||||
m *= mBBox;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
static void _applyComposition(SvgLoaderData& loaderData, 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();
|
||||
|
||||
auto child = compNode->child.data;
|
||||
auto valid = false; //Composite only when valid shapes exist
|
||||
|
||||
for (uint32_t i = 0; i < compNode->child.count; ++i, ++child) {
|
||||
if (_appendClipChild(loaderData, *child, comp.get(), vBox, svgPath, compNode->child.count > 1)) valid = true;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::ClipPath);
|
||||
comp->transform(finalTransform);
|
||||
|
||||
paint->composite(std::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;
|
||||
if (auto comp = _sceneBuildHelper(loaderData, compNode, vBox, svgPath, true, 0, &isMaskWhite)) {
|
||||
if (!compNode->node.mask.userSpace) {
|
||||
Matrix finalTransform = _compositionTransform(paint, node, compNode, SvgNodeType::Mask);
|
||||
comp->transform(finalTransform);
|
||||
} else {
|
||||
if (node->transform) comp->transform(*node->transform);
|
||||
}
|
||||
|
||||
if (compNode->node.mask.type == SvgMaskType::Luminance && !isMaskWhite) {
|
||||
paint->composite(std::move(comp), CompositeMethod::LumaMask);
|
||||
} else {
|
||||
paint->composite(std::move(comp), CompositeMethod::AlphaMask);
|
||||
}
|
||||
}
|
||||
|
||||
node->style->mask.applying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void _applyProperty(SvgLoaderData& loaderData, SvgNode* node, Shape* vg, const Box& vBox, const string& svgPath, bool clip)
|
||||
{
|
||||
SvgStyleProperty* style = node->style;
|
||||
|
||||
//Clip transformation is applied directly to the path in the _appendClipShape function
|
||||
if (node->transform && !clip) vg->transform(*node->transform);
|
||||
if (node->type == SvgNodeType::Doc || !node->style->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, bBox, style->fill.opacity);
|
||||
vg->fill(std::move(linear));
|
||||
} else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
|
||||
auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity);
|
||||
vg->fill(std::move(radial));
|
||||
}
|
||||
} else if (style->fill.paint.url) {
|
||||
//TODO: Apply the color pointed by url
|
||||
TVGLOG("SVG", "The fill's url not supported.");
|
||||
} 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);
|
||||
//Rendering order
|
||||
vg->order(!style->paintOrder);
|
||||
|
||||
//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);
|
||||
vg->strokeMiterlimit(style->stroke.miterlimit);
|
||||
if (style->stroke.dash.array.count > 0) {
|
||||
P(vg)->strokeDash(style->stroke.dash.array.data, style->stroke.dash.array.count, style->stroke.dash.offset);
|
||||
}
|
||||
|
||||
//If stroke property is nullptr then do nothing
|
||||
if (style->stroke.paint.none) {
|
||||
vg->stroke(0.0f);
|
||||
} 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, bBox, style->stroke.opacity);
|
||||
vg->stroke(std::move(linear));
|
||||
} else if (style->stroke.paint.gradient->type == SvgGradientType::Radial) {
|
||||
auto radial = _applyRadialGradientProperty(style->stroke.paint.gradient, bBox, style->stroke.opacity);
|
||||
vg->stroke(std::move(radial));
|
||||
}
|
||||
} else if (style->stroke.paint.url) {
|
||||
//TODO: Apply the color pointed by url
|
||||
TVGLOG("SVG", "The stroke's url not supported.");
|
||||
} 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(loaderData, vg, node, vBox, svgPath);
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Shape> _shapeBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
auto shape = Shape::gen();
|
||||
if (_appendShape(loaderData, node, shape.get(), vBox, svgPath)) return shape;
|
||||
else return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static bool _recognizeShape(SvgNode* node, Shape* shape)
|
||||
{
|
||||
switch (node->type) {
|
||||
case SvgNodeType::Path: {
|
||||
if (node->node.path.path) {
|
||||
if (!svgPathToShape(node->node.path.path, shape)) {
|
||||
TVGERR("SVG", "Invalid path information.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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.pts.count < 2) break;
|
||||
auto pts = node->node.polygon.pts.begin();
|
||||
shape->moveTo(pts[0], pts[1]);
|
||||
for (pts += 2; pts < node->node.polygon.pts.end(); pts += 2) {
|
||||
shape->lineTo(pts[0], pts[1]);
|
||||
}
|
||||
shape->close();
|
||||
break;
|
||||
}
|
||||
case SvgNodeType::Polyline: {
|
||||
if (node->node.polyline.pts.count < 2) break;
|
||||
auto pts = node->node.polyline.pts.begin();
|
||||
shape->moveTo(pts[0], pts[1]);
|
||||
for (pts += 2; pts < node->node.polyline.pts.end(); pts += 2) {
|
||||
shape->lineTo(pts[0], pts[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;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _appendShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
if (!_recognizeShape(node, shape)) return false;
|
||||
|
||||
_applyProperty(loaderData, node, shape, vBox, svgPath, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool _appendClipShape(SvgLoaderData& loaderData, SvgNode* node, Shape* shape, const Box& vBox, const string& svgPath, const Matrix* transform)
|
||||
{
|
||||
//The 'transform' matrix has higher priority than the node->transform, since it already contains it
|
||||
auto m = transform ? transform : (node->transform ? node->transform : nullptr);
|
||||
|
||||
uint32_t currentPtsCnt = 0;
|
||||
if (m) {
|
||||
const Point *tmp = nullptr;
|
||||
currentPtsCnt = shape->pathCoords(&tmp);
|
||||
}
|
||||
|
||||
if (!_recognizeShape(node, shape)) return false;
|
||||
|
||||
if (m) {
|
||||
const Point *pts = nullptr;
|
||||
auto ptsCnt = shape->pathCoords(&pts);
|
||||
|
||||
auto p = const_cast<Point*>(pts) + currentPtsCnt;
|
||||
while (currentPtsCnt++ < ptsCnt) {
|
||||
*p *= *m;
|
||||
++p;
|
||||
}
|
||||
}
|
||||
|
||||
_applyProperty(loaderData, node, shape, vBox, svgPath, true);
|
||||
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},
|
||||
{"webp", sizeof("webp"), 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;
|
||||
}
|
||||
|
||||
#include "tvgTaskScheduler.h"
|
||||
|
||||
static unique_ptr<Picture> _imageBuildHelper(SvgLoaderData& loaderData, SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
if (!node->node.image.href || !strlen(node->node.image.href)) return nullptr;
|
||||
auto picture = Picture::gen();
|
||||
|
||||
TaskScheduler::async(false); //force to load a picture on the same thread
|
||||
|
||||
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
|
||||
char *decoded = nullptr;
|
||||
if (encoding == imageMimeTypeEncoding::base64) {
|
||||
auto size = b64Decode(href, strlen(href), &decoded);
|
||||
if (picture->load(decoded, size, mimetype, false) != Result::Success) {
|
||||
free(decoded);
|
||||
TaskScheduler::async(true);
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
auto size = svgUtilURLDecode(href, &decoded);
|
||||
if (picture->load(decoded, size, mimetype, false) != Result::Success) {
|
||||
free(decoded);
|
||||
TaskScheduler::async(true);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
loaderData.images.push(decoded);
|
||||
} 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.");
|
||||
TaskScheduler::async(true);
|
||||
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) {
|
||||
TaskScheduler::async(true);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
TaskScheduler::async(true);
|
||||
|
||||
float w, h;
|
||||
Matrix m = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
if (picture->size(&w, &h) == Result::Success && w > 0 && h > 0) {
|
||||
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 = *node->transform * m;
|
||||
picture->transform(m);
|
||||
|
||||
_applyComposition(loaderData, picture.get(), node, vBox, svgPath);
|
||||
|
||||
return picture;
|
||||
}
|
||||
|
||||
|
||||
static Matrix _calculateAspectRatioMatrix(AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, float width, float height, const Box& box)
|
||||
{
|
||||
auto sx = width / box.w;
|
||||
auto sy = height / box.h;
|
||||
auto tvx = box.x * sx;
|
||||
auto tvy = box.y * sy;
|
||||
|
||||
if (align == AspectRatioAlign::None)
|
||||
return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
|
||||
|
||||
//Scale
|
||||
if (meetOrSlice == AspectRatioMeetOrSlice::Meet) {
|
||||
if (sx < sy) sy = sx;
|
||||
else sx = sy;
|
||||
} else {
|
||||
if (sx < sy) sx = sy;
|
||||
else sy = sx;
|
||||
}
|
||||
|
||||
//Align
|
||||
tvx = box.x * sx;
|
||||
tvy = box.y * sy;
|
||||
auto tvw = box.w * sx;
|
||||
auto tvh = box.h * sy;
|
||||
|
||||
switch (align) {
|
||||
case AspectRatioAlign::XMinYMin: {
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMidYMin: {
|
||||
tvx -= (width - tvw) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMaxYMin: {
|
||||
tvx -= width - tvw;
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMinYMid: {
|
||||
tvy -= (height - tvh) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMidYMid: {
|
||||
tvx -= (width - tvw) * 0.5f;
|
||||
tvy -= (height - tvh) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMaxYMid: {
|
||||
tvx -= width - tvw;
|
||||
tvy -= (height - tvh) * 0.5f;
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMinYMax: {
|
||||
tvy -= height - tvh;
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMidYMax: {
|
||||
tvx -= (width - tvw) * 0.5f;
|
||||
tvy -= height - tvh;
|
||||
break;
|
||||
}
|
||||
case AspectRatioAlign::XMaxYMax: {
|
||||
tvx -= width - tvw;
|
||||
tvy -= height - tvh;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return {sx, 0, -tvx, 0, sy, -tvy, 0, 0, 1};
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Scene> _useBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, int depth, bool* isMaskWhite)
|
||||
{
|
||||
unique_ptr<Scene> finalScene;
|
||||
auto scene = _sceneBuildHelper(loaderData, node, vBox, svgPath, false, depth + 1, 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 *= mTranslate;
|
||||
}
|
||||
|
||||
if (node->node.use.symbol) {
|
||||
auto symbol = node->node.use.symbol->node.symbol;
|
||||
|
||||
auto width = (symbol.hasWidth ? symbol.w : vBox.w);
|
||||
if (node->node.use.isWidthSet) width = node->node.use.w;
|
||||
auto height = (symbol.hasHeight ? symbol.h : vBox.h);;
|
||||
if (node->node.use.isHeightSet) height = node->node.use.h;
|
||||
auto vw = (symbol.hasViewBox ? symbol.vw : width);
|
||||
auto vh = (symbol.hasViewBox ? symbol.vh : height);
|
||||
|
||||
Matrix mViewBox = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
if ((!mathEqual(width, vw) || !mathEqual(height, vh)) && vw > 0 && vh > 0) {
|
||||
Box box = {symbol.vx, symbol.vy, vw, vh};
|
||||
mViewBox = _calculateAspectRatioMatrix(symbol.align, symbol.meetOrSlice, width, height, box);
|
||||
} 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 = *node->node.use.symbol->transform * mViewBox;
|
||||
}
|
||||
mSceneTransform = mUseTransform * mSceneTransform;
|
||||
scene->transform(mSceneTransform);
|
||||
|
||||
if (node->node.use.symbol->node.symbol.overflowVisible) {
|
||||
finalScene = std::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 = mUseTransform * *node->node.use.symbol->transform;
|
||||
}
|
||||
viewBoxClip->transform(mClipTransform);
|
||||
|
||||
auto compositeLayer = Scene::gen();
|
||||
compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
|
||||
compositeLayer->push(std::move(scene));
|
||||
|
||||
auto root = Scene::gen();
|
||||
root->push(std::move(compositeLayer));
|
||||
|
||||
finalScene = std::move(root);
|
||||
}
|
||||
} else {
|
||||
scene->transform(mUseTransform);
|
||||
finalScene = std::move(scene);
|
||||
}
|
||||
|
||||
return finalScene;
|
||||
}
|
||||
|
||||
|
||||
static void _applyTextFill(SvgStyleProperty* style, Text* text, const Box& vBox)
|
||||
{
|
||||
//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(text);
|
||||
|
||||
if (style->fill.paint.gradient->type == SvgGradientType::Linear) {
|
||||
auto linear = _applyLinearGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity);
|
||||
text->fill(std::move(linear));
|
||||
} else if (style->fill.paint.gradient->type == SvgGradientType::Radial) {
|
||||
auto radial = _applyRadialGradientProperty(style->fill.paint.gradient, bBox, style->fill.opacity);
|
||||
text->fill(std::move(radial));
|
||||
}
|
||||
} else if (style->fill.paint.url) {
|
||||
//TODO: Apply the color pointed by url
|
||||
TVGLOG("SVG", "The fill's url not supported.");
|
||||
} else if (style->fill.paint.curColor) {
|
||||
//Apply the current style color
|
||||
text->fill(style->color.r, style->color.g, style->color.b);
|
||||
text->opacity(style->fill.opacity);
|
||||
} else {
|
||||
//Apply the fill color
|
||||
text->fill(style->fill.paint.color.r, style->fill.paint.color.g, style->fill.paint.color.b);
|
||||
text->opacity(style->fill.opacity);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Text> _textBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath)
|
||||
{
|
||||
auto textNode = &node->node.text;
|
||||
if (!textNode->text) return nullptr;
|
||||
auto text = Text::gen();
|
||||
|
||||
Matrix textTransform = {1, 0, 0, 0, 1, 0, 0, 0, 1};
|
||||
if (node->transform) textTransform = *node->transform;
|
||||
mathTranslateR(&textTransform, node->node.text.x, node->node.text.y - textNode->fontSize);
|
||||
text->transform(textTransform);
|
||||
|
||||
//TODO: handle def values of font and size as used in a system?
|
||||
const float ptPerPx = 0.75f; //1 pt = 1/72; 1 in = 96 px; -> 72/96 = 0.75
|
||||
auto fontSizePt = textNode->fontSize * ptPerPx;
|
||||
if (textNode->fontFamily) text->font(textNode->fontFamily, fontSizePt);
|
||||
text->text(textNode->text);
|
||||
|
||||
_applyTextFill(node->style, text.get(), vBox);
|
||||
_applyComposition(loaderData, text.get(), node, vBox, svgPath);
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
static unique_ptr<Scene> _sceneBuildHelper(SvgLoaderData& loaderData, const SvgNode* node, const Box& vBox, const string& svgPath, bool mask, int depth, bool* isMaskWhite)
|
||||
{
|
||||
/* Exception handling: Prevent invalid SVG data input.
|
||||
The size is the arbitrary value, we need an experimental size. */
|
||||
if (depth > 2192) {
|
||||
TVGERR("SVG", "Infinite recursive call - stopped after %d calls! Svg file may be incorrectly formatted.", depth);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (_isGroupType(node->type) || mask) {
|
||||
auto scene = Scene::gen();
|
||||
// For a Symbol node, the viewBox transformation has to be applied first - see _useBuildHelper()
|
||||
if (!mask && node->transform && node->type != SvgNodeType::Symbol) scene->transform(*node->transform);
|
||||
|
||||
if (node->style->display && node->style->opacity != 0) {
|
||||
auto child = node->child.data;
|
||||
for (uint32_t i = 0; i < node->child.count; ++i, ++child) {
|
||||
if (_isGroupType((*child)->type)) {
|
||||
if ((*child)->type == SvgNodeType::Use)
|
||||
scene->push(_useBuildHelper(loaderData, *child, vBox, svgPath, depth + 1, isMaskWhite));
|
||||
else if (!((*child)->type == SvgNodeType::Symbol && node->type != SvgNodeType::Use))
|
||||
scene->push(_sceneBuildHelper(loaderData, *child, vBox, svgPath, false, depth + 1, isMaskWhite));
|
||||
} else if ((*child)->type == SvgNodeType::Image) {
|
||||
auto image = _imageBuildHelper(loaderData, *child, vBox, svgPath);
|
||||
if (image) {
|
||||
scene->push(std::move(image));
|
||||
if (isMaskWhite) *isMaskWhite = false;
|
||||
}
|
||||
} else if ((*child)->type == SvgNodeType::Text) {
|
||||
auto text = _textBuildHelper(loaderData, *child, vBox, svgPath);
|
||||
if (text) scene->push(std::move(text));
|
||||
} else if ((*child)->type != SvgNodeType::Mask) {
|
||||
auto shape = _shapeBuildHelper(loaderData, *child, vBox, svgPath);
|
||||
if (shape) {
|
||||
if (isMaskWhite) {
|
||||
uint8_t r, g, b;
|
||||
shape->fillColor(&r, &g, &b);
|
||||
if (shape->fill() || r < 255 || g < 255 || b < 255 || shape->strokeFill() ||
|
||||
(shape->strokeColor(&r, &g, &b) == Result::Success && (r < 255 || g < 255 || b < 255))) {
|
||||
*isMaskWhite = false;
|
||||
}
|
||||
}
|
||||
scene->push(std::move(shape));
|
||||
}
|
||||
}
|
||||
}
|
||||
_applyComposition(loaderData, scene.get(), node, vBox, svgPath);
|
||||
scene->opacity(node->style->opacity);
|
||||
}
|
||||
return scene;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, float& h, SvgViewFlag viewFlag)
|
||||
{
|
||||
bool validWidth = (viewFlag & SvgViewFlag::Width);
|
||||
bool validHeight = (viewFlag & SvgViewFlag::Height);
|
||||
|
||||
float x, y;
|
||||
scene->bounds(&x, &y, &vBox.w, &vBox.h, false);
|
||||
if (!validWidth && !validHeight) {
|
||||
vBox.x = x;
|
||||
vBox.y = y;
|
||||
} else {
|
||||
if (validWidth) vBox.w = w;
|
||||
if (validHeight) vBox.h = h;
|
||||
}
|
||||
|
||||
//the size would have 1x1 or percentage values.
|
||||
if (!validWidth) w *= vBox.w;
|
||||
if (!validHeight) h *= vBox.h;
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag)
|
||||
{
|
||||
//TODO: aspect ratio is valid only if viewBox was set
|
||||
|
||||
if (!loaderData.doc || (loaderData.doc->type != SvgNodeType::Doc)) return nullptr;
|
||||
|
||||
auto docNode = _sceneBuildHelper(loaderData, loaderData.doc, vBox, svgPath, false, 0);
|
||||
|
||||
if (!(viewFlag & SvgViewFlag::Viewbox)) _updateInvalidViewSize(docNode.get(), vBox, w, h, viewFlag);
|
||||
|
||||
if (!mathEqual(w, vBox.w) || !mathEqual(h, vBox.h)) {
|
||||
Matrix m = _calculateAspectRatioMatrix(align, meetOrSlice, w, h, vBox);
|
||||
docNode->transform(m);
|
||||
} else if (!mathZero(vBox.x) || !mathZero(vBox.y)) {
|
||||
docNode->translate(-vBox.x, -vBox.y);
|
||||
}
|
||||
|
||||
auto viewBoxClip = Shape::gen();
|
||||
viewBoxClip->appendRect(0, 0, w, h);
|
||||
|
||||
auto compositeLayer = Scene::gen();
|
||||
compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath);
|
||||
compositeLayer->push(std::move(docNode));
|
||||
|
||||
auto root = Scene::gen();
|
||||
root->push(std::move(compositeLayer));
|
||||
|
||||
loaderData.doc->node.doc.vx = vBox.x;
|
||||
loaderData.doc->node.doc.vy = vBox.y;
|
||||
loaderData.doc->node.doc.vw = vBox.w;
|
||||
loaderData.doc->node.doc.vh = vBox.h;
|
||||
loaderData.doc->node.doc.w = w;
|
||||
loaderData.doc->node.doc.h = h;
|
||||
|
||||
return root.release();
|
||||
}
|
||||
30
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
vendored
Normal file
30
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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"
|
||||
|
||||
Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag);
|
||||
|
||||
#endif //_TVG_SVG_SCENE_BUILDER_H_
|
||||
71
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
vendored
Normal file
71
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 "tvgSvgUtil.h"
|
||||
|
||||
/************************************************************************/
|
||||
/* Internal Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* External Class Implementation */
|
||||
/************************************************************************/
|
||||
|
||||
size_t svgUtilURLDecode(const char *src, char** dst)
|
||||
{
|
||||
if (!src) return 0;
|
||||
|
||||
auto length = strlen(src);
|
||||
if (length == 0) return 0;
|
||||
|
||||
char* decoded = (char*)malloc(sizeof(char) * length + 1);
|
||||
|
||||
char a, b;
|
||||
int idx =0;
|
||||
while (*src) {
|
||||
if (*src == '%' &&
|
||||
((a = src[1]) && (b = src[2])) &&
|
||||
(isxdigit(a) && isxdigit(b))) {
|
||||
decoded[idx++] = (_hexCharToDec(a) << 4) + _hexCharToDec(b);
|
||||
src+=3;
|
||||
} else if (*src == '+') {
|
||||
decoded[idx++] = ' ';
|
||||
src++;
|
||||
} else {
|
||||
decoded[idx++] = *src++;
|
||||
}
|
||||
}
|
||||
decoded[idx] = '\0';
|
||||
|
||||
*dst = decoded;
|
||||
return idx + 1;
|
||||
}
|
||||
|
||||
30
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
vendored
Normal file
30
engine/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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"
|
||||
|
||||
size_t svgUtilURLDecode(const char *src, char** dst);
|
||||
|
||||
#endif //_TVG_SVG_UTIL_H_
|
||||
589
engine/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
vendored
Normal file
589
engine/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp
vendored
Normal file
|
|
@ -0,0 +1,589 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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>
|
||||
#elif defined(__linux__)
|
||||
#include <alloca.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#endif
|
||||
|
||||
#include "tvgXmlParser.h"
|
||||
#include "tvgStr.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[2] = {false, false}; // 0: ", 1: '
|
||||
for (; itr < itrEnd; itr++) {
|
||||
if (*itr == '"' && !insideQuote[1]) insideQuote[0] = !insideQuote[0];
|
||||
if (*itr == '\'' && !insideQuote[0]) insideQuote[1] = !insideQuote[1];
|
||||
if (!insideQuote[0] && !insideQuote[1]) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
/* 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*)malloc(bufLength + 1);
|
||||
|
||||
if (!buf || !func || !tmpBuf) goto error;
|
||||
|
||||
while (itr < itrEnd) {
|
||||
const char* p = _skipWhiteSpacesAndXmlEntities(itr, itrEnd);
|
||||
const char *key, *keyEnd, *value, *valueEnd;
|
||||
char* tval;
|
||||
|
||||
if (p == itrEnd) goto success;
|
||||
|
||||
key = p;
|
||||
for (keyEnd = key; keyEnd < itrEnd; keyEnd++) {
|
||||
if ((*keyEnd == '=') || (isspace((unsigned char)*keyEnd))) break;
|
||||
}
|
||||
if (keyEnd == itrEnd) goto error;
|
||||
if (keyEnd == key) { // There is no key. This case is invalid, but explores the following syntax.
|
||||
itr = keyEnd + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (*keyEnd == '=') value = keyEnd + 1;
|
||||
else {
|
||||
value = (const char*)memchr(keyEnd, '=', itrEnd - keyEnd);
|
||||
if (!value) goto error;
|
||||
value++;
|
||||
}
|
||||
keyEnd = _simpleXmlUnskipXmlEntities(keyEnd, key);
|
||||
|
||||
value = _skipWhiteSpacesAndXmlEntities(value, itrEnd);
|
||||
if (value == itrEnd) goto error;
|
||||
|
||||
if ((*value == '"') || (*value == '\'')) {
|
||||
valueEnd = (const char*)memchr(value + 1, *value, itrEnd - value);
|
||||
if (!valueEnd) goto error;
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
success:
|
||||
free(tmpBuf);
|
||||
return true;
|
||||
|
||||
error:
|
||||
free(tmpBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
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 == '<' && type != SimpleXMLType::Doctype) 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 = strDuplicate(itr, p - itr);
|
||||
|
||||
if (p == itrEnd) *name = nullptr;
|
||||
else *name = strDuplicate(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;
|
||||
}
|
||||
58
engine/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
vendored
Normal file
58
engine/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2020 - 2024 the ThorVG project. 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 9
|
||||
const char* const xmlEntity[] = {" ", """, " ", "'", "&", "<", ">", "#", "'"};
|
||||
const int xmlEntityLength[] = {5, 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_
|
||||
Loading…
Add table
Add a link
Reference in a new issue