Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/drivers/png/png_driver_common.cpp
9982 views
1
/**************************************************************************/
2
/* png_driver_common.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10
/* */
11
/* Permission is hereby granted, free of charge, to any person obtaining */
12
/* a copy of this software and associated documentation files (the */
13
/* "Software"), to deal in the Software without restriction, including */
14
/* without limitation the rights to use, copy, modify, merge, publish, */
15
/* distribute, sublicense, and/or sell copies of the Software, and to */
16
/* permit persons to whom the Software is furnished to do so, subject to */
17
/* the following conditions: */
18
/* */
19
/* The above copyright notice and this permission notice shall be */
20
/* included in all copies or substantial portions of the Software. */
21
/* */
22
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29
/**************************************************************************/
30
31
#include "png_driver_common.h"
32
33
#include "core/config/engine.h"
34
35
#include <png.h>
36
37
namespace PNGDriverCommon {
38
39
// Print any warnings.
40
// On error, set explain and return true.
41
// Call should be wrapped in ERR_FAIL_COND
42
static bool check_error(const png_image &image) {
43
const png_uint_32 failed = PNG_IMAGE_FAILED(image);
44
if (failed & PNG_IMAGE_ERROR) {
45
return true;
46
} else if (failed) {
47
#ifdef TOOLS_ENABLED
48
// suppress this warning, to avoid log spam when opening assetlib
49
const static char *const noisy = "iCCP: known incorrect sRGB profile";
50
const Engine *const eng = Engine::get_singleton();
51
if (eng && eng->is_editor_hint() && !strcmp(image.message, noisy)) {
52
return false;
53
}
54
#endif
55
WARN_PRINT(image.message);
56
}
57
return false;
58
}
59
60
Error png_to_image(const uint8_t *p_source, size_t p_size, bool p_force_linear, Ref<Image> p_image) {
61
png_image png_img;
62
memset(&png_img, 0, sizeof(png_img));
63
png_img.version = PNG_IMAGE_VERSION;
64
65
// fetch image properties
66
int success = png_image_begin_read_from_memory(&png_img, p_source, p_size);
67
ERR_FAIL_COND_V_MSG(check_error(png_img), ERR_FILE_CORRUPT, png_img.message);
68
ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT);
69
70
// flags to be masked out of input format to give target format
71
const png_uint_32 format_mask = ~(
72
// convert component order to RGBA
73
PNG_FORMAT_FLAG_BGR | PNG_FORMAT_FLAG_AFIRST
74
// convert 16 bit components to 8 bit
75
| PNG_FORMAT_FLAG_LINEAR
76
// convert indexed image to direct color
77
| PNG_FORMAT_FLAG_COLORMAP);
78
79
png_img.format &= format_mask;
80
81
Image::Format dest_format;
82
switch (png_img.format) {
83
case PNG_FORMAT_GRAY:
84
dest_format = Image::FORMAT_L8;
85
break;
86
case PNG_FORMAT_GA:
87
dest_format = Image::FORMAT_LA8;
88
break;
89
case PNG_FORMAT_RGB:
90
dest_format = Image::FORMAT_RGB8;
91
break;
92
case PNG_FORMAT_RGBA:
93
dest_format = Image::FORMAT_RGBA8;
94
break;
95
default:
96
png_image_free(&png_img); // only required when we return before finish_read
97
ERR_PRINT("Unsupported png format.");
98
return ERR_UNAVAILABLE;
99
}
100
101
if (!p_force_linear) {
102
// assume 16 bit pngs without sRGB or gAMA chunks are in sRGB format
103
png_img.flags |= PNG_IMAGE_FLAG_16BIT_sRGB;
104
}
105
106
const png_uint_32 stride = PNG_IMAGE_ROW_STRIDE(png_img);
107
Vector<uint8_t> buffer;
108
Error err = buffer.resize(PNG_IMAGE_BUFFER_SIZE(png_img, stride));
109
if (err) {
110
png_image_free(&png_img); // only required when we return before finish_read
111
return err;
112
}
113
uint8_t *writer = buffer.ptrw();
114
115
// read image data to buffer and release libpng resources
116
success = png_image_finish_read(&png_img, nullptr, writer, stride, nullptr);
117
ERR_FAIL_COND_V_MSG(check_error(png_img), ERR_FILE_CORRUPT, png_img.message);
118
ERR_FAIL_COND_V(!success, ERR_FILE_CORRUPT);
119
120
//print_line("png width: "+itos(png_img.width)+" height: "+itos(png_img.height));
121
p_image->set_data(png_img.width, png_img.height, false, dest_format, buffer);
122
123
return OK;
124
}
125
126
Error image_to_png(const Ref<Image> &p_image, Vector<uint8_t> &p_buffer) {
127
Ref<Image> source_image = p_image->duplicate();
128
129
if (source_image->is_compressed()) {
130
source_image->decompress();
131
}
132
133
ERR_FAIL_COND_V(source_image->is_compressed(), FAILED);
134
135
png_image png_img;
136
memset(&png_img, 0, sizeof(png_img));
137
png_img.version = PNG_IMAGE_VERSION;
138
png_img.width = source_image->get_width();
139
png_img.height = source_image->get_height();
140
141
switch (source_image->get_format()) {
142
case Image::FORMAT_L8:
143
png_img.format = PNG_FORMAT_GRAY;
144
break;
145
case Image::FORMAT_LA8:
146
png_img.format = PNG_FORMAT_GA;
147
break;
148
case Image::FORMAT_RGB8:
149
png_img.format = PNG_FORMAT_RGB;
150
break;
151
case Image::FORMAT_RGBA8:
152
png_img.format = PNG_FORMAT_RGBA;
153
break;
154
default:
155
if (source_image->detect_alpha() != Image::ALPHA_NONE) {
156
source_image->convert(Image::FORMAT_RGBA8);
157
png_img.format = PNG_FORMAT_RGBA;
158
} else {
159
source_image->convert(Image::FORMAT_RGB8);
160
png_img.format = PNG_FORMAT_RGB;
161
}
162
}
163
164
const Vector<uint8_t> image_data = source_image->get_data();
165
const uint8_t *reader = image_data.ptr();
166
167
// we may be passed a buffer with existing content we're expected to append to
168
const int buffer_offset = p_buffer.size();
169
170
const size_t png_size_estimate = PNG_IMAGE_PNG_SIZE_MAX(png_img);
171
172
// try with estimated size
173
size_t compressed_size = png_size_estimate;
174
int success = 0;
175
{ // scope writer lifetime
176
Error err = p_buffer.resize(buffer_offset + png_size_estimate);
177
ERR_FAIL_COND_V(err, err);
178
179
uint8_t *writer = p_buffer.ptrw();
180
success = png_image_write_to_memory(&png_img, &writer[buffer_offset],
181
&compressed_size, 0, reader, 0, nullptr);
182
ERR_FAIL_COND_V_MSG(check_error(png_img), FAILED, png_img.message);
183
}
184
if (!success) {
185
// buffer was big enough, must be some other error
186
ERR_FAIL_COND_V(compressed_size <= png_size_estimate, FAILED);
187
188
// write failed due to buffer size, resize and retry
189
Error err = p_buffer.resize(buffer_offset + compressed_size);
190
ERR_FAIL_COND_V(err, err);
191
192
uint8_t *writer = p_buffer.ptrw();
193
success = png_image_write_to_memory(&png_img, &writer[buffer_offset],
194
&compressed_size, 0, reader, 0, nullptr);
195
ERR_FAIL_COND_V_MSG(check_error(png_img), FAILED, png_img.message);
196
ERR_FAIL_COND_V(!success, FAILED);
197
}
198
199
// trim buffer size to content
200
Error err = p_buffer.resize(buffer_offset + compressed_size);
201
ERR_FAIL_COND_V(err, err);
202
203
return OK;
204
}
205
} // namespace PNGDriverCommon
206
207