2017-08-19 04:21:24 +02:00
/*************************************************************************/
/* image_loader_svg.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2017-08-19 04:21:24 +02:00
/*************************************************************************/
2020-01-01 11:16:22 +01:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2017-08-19 04:21:24 +02:00
/* */
/* 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. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2017-08-19 04:21:24 +02:00
# include "image_loader_svg.h"
2020-02-06 21:51:36 +01:00
# include <nanosvg.h>
# include <nanosvgrast.h>
2017-08-19 04:21:24 +02:00
void SVGRasterizer : : rasterize ( NSVGimage * p_image , float p_tx , float p_ty , float p_scale , unsigned char * p_dst , int p_w , int p_h , int p_stride ) {
nsvgRasterize ( rasterizer , p_image , p_tx , p_ty , p_scale , p_dst , p_w , p_h , p_stride ) ;
}
SVGRasterizer : : SVGRasterizer ( ) {
rasterizer = nsvgCreateRasterizer ( ) ;
}
SVGRasterizer : : ~ SVGRasterizer ( ) {
nsvgDeleteRasterizer ( rasterizer ) ;
}
SVGRasterizer ImageLoaderSVG : : rasterizer ;
2017-08-30 20:29:35 +02:00
inline void change_nsvg_paint_color ( NSVGpaint * p_paint , const uint32_t p_old , const uint32_t p_new ) {
if ( p_paint - > type = = NSVG_PAINT_COLOR ) {
if ( p_paint - > color < < 8 = = p_old < < 8 ) {
2017-09-30 14:48:41 +02:00
p_paint - > color = ( p_paint - > color & 0xFF000000 ) | ( p_new & 0x00FFFFFF ) ;
2017-08-30 20:29:35 +02:00
}
}
if ( p_paint - > type = = NSVG_PAINT_LINEAR_GRADIENT | | p_paint - > type = = NSVG_PAINT_RADIAL_GRADIENT ) {
for ( int stop_index = 0 ; stop_index < p_paint - > gradient - > nstops ; stop_index + + ) {
if ( p_paint - > gradient - > stops [ stop_index ] . color < < 8 = = p_old < < 8 ) {
p_paint - > gradient - > stops [ stop_index ] . color = p_new ;
}
}
}
}
2017-09-08 04:31:12 +02:00
void ImageLoaderSVG : : _convert_colors ( NSVGimage * p_svg_image ) {
2017-08-30 20:29:35 +02:00
for ( NSVGshape * shape = p_svg_image - > shapes ; shape ! = NULL ; shape = shape - > next ) {
2017-09-08 04:31:12 +02:00
for ( int i = 0 ; i < replace_colors . old_colors . size ( ) ; i + + ) {
change_nsvg_paint_color ( & ( shape - > stroke ) , replace_colors . old_colors [ i ] , replace_colors . new_colors [ i ] ) ;
change_nsvg_paint_color ( & ( shape - > fill ) , replace_colors . old_colors [ i ] , replace_colors . new_colors [ i ] ) ;
}
}
}
void ImageLoaderSVG : : set_convert_colors ( Dictionary * p_replace_color ) {
if ( p_replace_color ) {
Dictionary replace_color = * p_replace_color ;
for ( int i = 0 ; i < replace_color . keys ( ) . size ( ) ; i + + ) {
Variant o_c = replace_color . keys ( ) [ i ] ;
Variant n_c = replace_color [ replace_color . keys ( ) [ i ] ] ;
if ( o_c . get_type ( ) = = Variant : : COLOR & & n_c . get_type ( ) = = Variant : : COLOR ) {
Color old_color = o_c ;
Color new_color = n_c ;
replace_colors . old_colors . push_back ( old_color . to_abgr32 ( ) ) ;
replace_colors . new_colors . push_back ( new_color . to_abgr32 ( ) ) ;
}
2017-08-30 20:29:35 +02:00
}
2017-09-08 04:31:12 +02:00
} else {
replace_colors . old_colors . clear ( ) ;
replace_colors . new_colors . clear ( ) ;
2017-08-30 20:29:35 +02:00
}
}
2020-02-17 22:06:54 +01:00
Error ImageLoaderSVG : : _create_image ( Ref < Image > p_image , const Vector < uint8_t > * p_data , float p_scale , bool upsample , bool convert_colors ) {
2017-08-19 04:21:24 +02:00
NSVGimage * svg_image ;
2020-02-17 22:06:54 +01:00
const uint8_t * src_r = p_data - > ptr ( ) ;
svg_image = nsvgParse ( ( char * ) src_r , " px " , 96 ) ;
2017-08-19 04:21:24 +02:00
if ( svg_image = = NULL ) {
ERR_PRINT ( " SVG Corrupted " ) ;
return ERR_FILE_CORRUPT ;
}
2020-01-19 23:21:49 +01:00
if ( convert_colors ) {
2017-09-08 04:31:12 +02:00
_convert_colors ( svg_image ) ;
2020-01-19 23:21:49 +01:00
}
2017-08-19 09:09:58 +02:00
2020-01-19 23:21:49 +01:00
const float upscale = upsample ? 2.0 : 1.0 ;
2017-08-19 09:09:58 +02:00
2020-01-19 23:21:49 +01:00
const int w = ( int ) ( svg_image - > width * p_scale * upscale ) ;
2019-08-11 10:49:53 +02:00
ERR_FAIL_COND_V_MSG ( w > Image : : MAX_WIDTH , ERR_PARAMETER_RANGE_ERROR , vformat ( " Can't create image from SVG with scale %s, the resulting image size exceeds max width. " , rtos ( p_scale ) ) ) ;
2019-01-28 15:33:56 +01:00
2020-01-19 23:21:49 +01:00
const int h = ( int ) ( svg_image - > height * p_scale * upscale ) ;
2019-08-11 10:49:53 +02:00
ERR_FAIL_COND_V_MSG ( h > Image : : MAX_HEIGHT , ERR_PARAMETER_RANGE_ERROR , vformat ( " Can't create image from SVG with scale %s, the resulting image size exceeds max height. " , rtos ( p_scale ) ) ) ;
2017-08-19 04:21:24 +02:00
2020-02-17 22:06:54 +01:00
Vector < uint8_t > dst_image ;
2017-08-19 04:21:24 +02:00
dst_image . resize ( w * h * 4 ) ;
2020-02-17 22:06:54 +01:00
uint8_t * dw = dst_image . ptrw ( ) ;
2017-08-19 04:21:24 +02:00
2020-02-17 22:06:54 +01:00
rasterizer . rasterize ( svg_image , 0 , 0 , p_scale * upscale , ( unsigned char * ) dw , w , h , w * 4 ) ;
2017-08-19 04:21:24 +02:00
p_image - > create ( w , h , false , Image : : FORMAT_RGBA8 , dst_image ) ;
2020-01-19 23:21:49 +01:00
if ( upsample ) {
2017-08-19 04:21:24 +02:00
p_image - > shrink_x2 ( ) ;
2020-01-19 23:21:49 +01:00
}
2017-08-19 04:21:24 +02:00
nsvgDelete ( svg_image ) ;
return OK ;
}
2017-09-14 19:49:11 +02:00
Error ImageLoaderSVG : : create_image_from_string ( Ref < Image > p_image , const char * p_svg_str , float p_scale , bool upsample , bool convert_colors ) {
2017-08-19 04:21:24 +02:00
2017-09-14 19:49:11 +02:00
size_t str_len = strlen ( p_svg_str ) ;
2020-02-17 22:06:54 +01:00
Vector < uint8_t > src_data ;
2017-08-22 19:39:10 +02:00
src_data . resize ( str_len + 1 ) ;
2020-02-17 22:06:54 +01:00
uint8_t * src_w = src_data . ptrw ( ) ;
memcpy ( src_w , p_svg_str , str_len + 1 ) ;
2017-08-19 04:21:24 +02:00
2017-09-08 04:31:12 +02:00
return _create_image ( p_image , & src_data , p_scale , upsample , convert_colors ) ;
2017-08-19 04:21:24 +02:00
}
Error ImageLoaderSVG : : load_image ( Ref < Image > p_image , FileAccess * f , bool p_force_linear , float p_scale ) {
uint32_t size = f - > get_len ( ) ;
2020-02-17 22:06:54 +01:00
Vector < uint8_t > src_image ;
2017-08-22 19:39:10 +02:00
src_image . resize ( size + 1 ) ;
2020-02-17 22:06:54 +01:00
uint8_t * src_w = src_image . ptrw ( ) ;
f - > get_buffer ( src_w , size ) ;
src_w [ size ] = ' \0 ' ;
2017-08-19 04:21:24 +02:00
2017-08-19 09:09:58 +02:00
return _create_image ( p_image , & src_image , p_scale , 1.0 ) ;
2017-08-19 04:21:24 +02:00
}
void ImageLoaderSVG : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " svg " ) ;
p_extensions - > push_back ( " svgz " ) ;
}
ImageLoaderSVG : : ImageLoaderSVG ( ) {
}
2017-09-08 04:31:12 +02:00
2017-09-14 19:49:11 +02:00
ImageLoaderSVG : : ReplaceColors ImageLoaderSVG : : replace_colors ;