Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/Data/Format/PNGLoad.cpp
5670 views
1
#include <cstdio>
2
#include <cstdlib>
3
#include <cstring>
4
#include <png.h>
5
6
#include "Common/Data/Format/PNGLoad.h"
7
#include "Common/Log.h"
8
#include "Common/File/FileUtil.h"
9
10
// *image_data_ptr should be deleted with free()
11
// return value of 1 == success.
12
int pngLoad(const char *file, int *pwidth, int *pheight, unsigned char **image_data_ptr) {
13
png_image png;
14
memset(&png, 0, sizeof(png));
15
png.version = PNG_IMAGE_VERSION;
16
17
png_image_begin_read_from_file(&png, file);
18
19
if (PNG_IMAGE_FAILED(png))
20
{
21
WARN_LOG(Log::IO, "pngLoad: %s (%s)", png.message, file);
22
*image_data_ptr = nullptr;
23
return 0;
24
}
25
*pwidth = png.width;
26
*pheight = png.height;
27
png.format = PNG_FORMAT_RGBA;
28
29
int stride = PNG_IMAGE_ROW_STRIDE(png);
30
*image_data_ptr = (unsigned char *)malloc(PNG_IMAGE_SIZE(png));
31
png_image_finish_read(&png, NULL, *image_data_ptr, stride, NULL);
32
return 1;
33
}
34
35
// Custom error handler
36
void pngErrorHandler(png_structp png_ptr, png_const_charp error_msg) {
37
ERROR_LOG(Log::System, "libpng error: %s\n", error_msg);
38
longjmp(png_jmpbuf(png_ptr), 1);
39
}
40
41
void pngWarningHandler(png_structp png_ptr, png_const_charp warning_msg) {
42
DEBUG_LOG(Log::System, "libpng warning: %s\n", warning_msg);
43
}
44
45
int pngLoadPtr(const unsigned char *input_ptr, size_t input_len, int *pwidth, int *pheight, unsigned char **image_data_ptr) {
46
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, pngErrorHandler, pngWarningHandler);
47
if (!png) {
48
return 0;
49
}
50
if (input_len == 0) {
51
return 0;
52
}
53
54
// Ignore incorrect sRGB profiles
55
png_set_option(png, PNG_SKIP_sRGB_CHECK_PROFILE, PNG_OPTION_ON);
56
png_set_benign_errors(png, PNG_OPTION_ON);
57
58
png_infop info = png_create_info_struct(png);
59
if (!info) {
60
png_destroy_read_struct(&png, NULL, NULL);
61
return 0;
62
}
63
64
if (setjmp(png_jmpbuf(png))) {
65
png_destroy_read_struct(&png, &info, NULL);
66
if (*image_data_ptr) {
67
free(*image_data_ptr);
68
*image_data_ptr = NULL;
69
}
70
return 0;
71
}
72
73
png_set_read_fn(png, (png_voidp)&input_ptr, [](png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead) {
74
const unsigned char **input = (const unsigned char **)png_get_io_ptr(png_ptr);
75
memcpy(outBytes, *input, byteCountToRead);
76
*input += byteCountToRead;
77
});
78
79
png_read_info(png, info);
80
81
*pwidth = png_get_image_width(png, info);
82
*pheight = png_get_image_height(png, info);
83
84
const int color_type = png_get_color_type(png, info);
85
png_set_strip_16(png);
86
png_set_packing(png);
87
if (color_type == PNG_COLOR_TYPE_GRAY)
88
png_set_expand_gray_1_2_4_to_8(png);
89
if (color_type == PNG_COLOR_TYPE_PALETTE) {
90
png_set_palette_to_rgb(png);
91
} else if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
92
png_set_gray_to_rgb(png);
93
}
94
95
if (png_get_valid(png, info, PNG_INFO_tRNS))
96
png_set_tRNS_to_alpha(png);
97
// Force 8-bit RGBA format
98
png_set_filler(png, 0xFF, PNG_FILLER_AFTER);
99
png_set_interlace_handling(png);
100
// Ignore the file's gamma correction
101
png_set_gamma(png, 1.0, 1.0);
102
103
png_read_update_info(png, info);
104
105
size_t row_bytes = png_get_rowbytes(png, info);
106
size_t size = row_bytes * (*pheight);
107
108
*image_data_ptr = (unsigned char *)malloc(size);
109
if (!*image_data_ptr) {
110
png_destroy_read_struct(&png, &info, NULL);
111
return 0;
112
}
113
114
png_bytep *row_pointers = (png_bytep *)malloc(sizeof(png_bytep) * (*pheight));
115
for (int y = 0; y < *pheight; y++) {
116
row_pointers[y] = *image_data_ptr + y * row_bytes;
117
}
118
119
png_read_image(png, row_pointers);
120
free(row_pointers);
121
png_destroy_read_struct(&png, &info, NULL);
122
return 1;
123
}
124
125
bool PNGHeaderPeek::IsValidPNGHeader() const {
126
if (magic != 0x474e5089 || ihdrTag != 0x52444849) {
127
return false;
128
}
129
// Reject crazy sized images, too.
130
if (Width() > 32768 && Height() > 32768) {
131
return false;
132
}
133
return true;
134
}
135
136
bool pngSave(const Path &filename, const void *buffer, int w, int h, int bytesPerPixel) {
137
png_bytepp row_ptrs = nullptr;
138
139
FILE *fp = File::OpenCFile(filename, "wb");
140
if (!fp) {
141
ERROR_LOG(Log::IO, "Unable to open png file for writing: %s", filename.c_str());
142
return false;
143
}
144
145
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, pngErrorHandler, pngWarningHandler);
146
if (png_ptr == nullptr) {
147
fclose(fp);
148
ERROR_LOG(Log::IO, "PNG encode failed.");
149
return false;
150
}
151
152
png_infop info_ptr = png_create_info_struct(png_ptr);
153
if (info_ptr == nullptr) {
154
png_destroy_write_struct(&png_ptr, nullptr);
155
fclose(fp);
156
ERROR_LOG(Log::IO, "PNG encode failed.");
157
return false;
158
}
159
160
if (setjmp(png_jmpbuf(png_ptr))) {
161
if (row_ptrs != nullptr) {
162
png_free(png_ptr, row_ptrs);
163
}
164
png_destroy_write_struct(&png_ptr, &info_ptr);
165
fclose(fp);
166
167
// Should we even do this?
168
File::Delete(filename);
169
170
ERROR_LOG(Log::IO, "PNG encode failed.");
171
return false;
172
}
173
174
png_set_write_fn(png_ptr, fp, [](png_structp png_ptr, png_bytep data, png_size_t size) {
175
if (fwrite(data, 1, size, (FILE *)png_get_io_ptr(png_ptr)) < size) {
176
png_error(png_ptr, "Failed to write to file.");
177
}
178
}, [](png_structp png_ptr) {
179
fflush((FILE *)png_get_io_ptr(png_ptr));
180
});
181
182
png_set_IHDR(png_ptr, info_ptr, w, h, 8, bytesPerPixel == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
183
184
row_ptrs = (png_bytepp)png_malloc(png_ptr, (png_alloc_size_t)h * (png_alloc_size_t)sizeof(png_bytep));
185
for (png_alloc_size_t i = 0; i < h; ++i) {
186
row_ptrs[i] = (png_bytep)buffer + (png_alloc_size_t)w * (png_alloc_size_t)bytesPerPixel * i;
187
}
188
png_set_rows(png_ptr, info_ptr, row_ptrs);
189
190
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, nullptr);
191
192
png_free(png_ptr, row_ptrs);
193
png_destroy_write_struct(&png_ptr, &info_ptr);
194
fclose(fp);
195
return true;
196
}
197
198