fast_resize 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/bindings/ruby/ext/fastresize/extconf.rb +79 -105
- data/bindings/ruby/lib/fastresize/platform.rb +102 -56
- data/bindings/ruby/lib/fastresize/version.rb +1 -1
- data/bindings/ruby/lib/fastresize.rb +321 -6
- data/bindings/ruby/prebuilt/linux-aarch64/bin/fast_resize +0 -0
- data/bindings/ruby/prebuilt/linux-aarch64.tar.gz +0 -0
- data/bindings/ruby/prebuilt/linux-x86_64/bin/fast_resize +0 -0
- data/bindings/ruby/prebuilt/linux-x86_64/lib/libfastresize.a +0 -0
- data/bindings/ruby/prebuilt/linux-x86_64.tar.gz +0 -0
- data/bindings/ruby/prebuilt/macos-arm64/bin/fast_resize +0 -0
- data/bindings/ruby/prebuilt/macos-arm64/lib/libfastresize.a +0 -0
- data/bindings/ruby/prebuilt/macos-arm64.tar.gz +0 -0
- data/bindings/ruby/prebuilt/macos-x86_64/bin/fast_resize +0 -0
- data/bindings/ruby/prebuilt/macos-x86_64/lib/libfastresize.a +0 -0
- data/bindings/ruby/prebuilt/macos-x86_64.tar.gz +0 -0
- metadata +4 -22
- data/CMakeLists.txt +0 -311
- data/bindings/ruby/ext/fastresize/fastresize_ext.cpp +0 -377
- data/include/fastresize.h +0 -189
- data/include/stb_image.h +0 -7988
- data/include/stb_image_resize2.h +0 -10651
- data/include/stb_image_write.h +0 -1724
- data/src/cli.cpp +0 -540
- data/src/decoder.cpp +0 -647
- data/src/encoder.cpp +0 -376
- data/src/fastresize.cpp +0 -445
- data/src/internal.h +0 -108
- data/src/pipeline.cpp +0 -284
- data/src/pipeline.h +0 -175
- data/src/resizer.cpp +0 -199
- data/src/simd_resize.cpp +0 -384
- data/src/simd_resize.h +0 -72
- data/src/simd_utils.h +0 -127
- data/src/thread_pool.cpp +0 -232
data/src/decoder.cpp
DELETED
|
@@ -1,647 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* FastResize - The Fastest Image Resizing Library On The Planet
|
|
3
|
-
* Copyright (C) 2025 Tran Huu Canh (0xTh3OKrypt) and FastResize Contributors
|
|
4
|
-
*
|
|
5
|
-
* Resize 1,000 images in 2 seconds. Up to 2.9x faster than libvips,
|
|
6
|
-
* 3.1x faster than imageflow. Uses 3-4x less RAM than alternatives.
|
|
7
|
-
*
|
|
8
|
-
* Author: Tran Huu Canh (0xTh3OKrypt)
|
|
9
|
-
* Email: tranhuucanh39@gmail.com
|
|
10
|
-
* Homepage: https://github.com/tranhuucanh/fast_resize
|
|
11
|
-
*
|
|
12
|
-
* BSD 3-Clause License
|
|
13
|
-
*
|
|
14
|
-
* Redistribution and use in source and binary forms, with or without
|
|
15
|
-
* modification, are permitted provided that the following conditions are met:
|
|
16
|
-
*
|
|
17
|
-
* 1. Redistributions of source code must retain the above copyright notice,
|
|
18
|
-
* this list of conditions and the following disclaimer.
|
|
19
|
-
*
|
|
20
|
-
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
21
|
-
* this list of conditions and the following disclaimer in the documentation
|
|
22
|
-
* and/or other materials provided with the distribution.
|
|
23
|
-
*
|
|
24
|
-
* 3. Neither the name of the copyright holder nor the names of its
|
|
25
|
-
* contributors may be used to endorse or promote products derived from
|
|
26
|
-
* this software without specific prior written permission.
|
|
27
|
-
*
|
|
28
|
-
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
29
|
-
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
30
|
-
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
31
|
-
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
32
|
-
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
33
|
-
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
34
|
-
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
35
|
-
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
36
|
-
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
37
|
-
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
38
|
-
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
39
|
-
*/
|
|
40
|
-
|
|
41
|
-
#include "internal.h"
|
|
42
|
-
#include <cstdio>
|
|
43
|
-
#include <cstring>
|
|
44
|
-
#include <csetjmp>
|
|
45
|
-
|
|
46
|
-
#include <jpeglib.h>
|
|
47
|
-
#include <png.h>
|
|
48
|
-
#include <webp/decode.h>
|
|
49
|
-
|
|
50
|
-
#define STB_IMAGE_IMPLEMENTATION
|
|
51
|
-
#include "stb_image.h"
|
|
52
|
-
|
|
53
|
-
#ifndef _WIN32
|
|
54
|
-
#include <sys/mman.h>
|
|
55
|
-
#include <sys/stat.h>
|
|
56
|
-
#include <fcntl.h>
|
|
57
|
-
#include <unistd.h>
|
|
58
|
-
#else
|
|
59
|
-
#include <windows.h>
|
|
60
|
-
#endif
|
|
61
|
-
|
|
62
|
-
#include "simd_utils.h"
|
|
63
|
-
|
|
64
|
-
namespace fastresize {
|
|
65
|
-
namespace internal {
|
|
66
|
-
|
|
67
|
-
// ============================================
|
|
68
|
-
// Memory-Mapped File
|
|
69
|
-
// ============================================
|
|
70
|
-
|
|
71
|
-
struct MappedFile {
|
|
72
|
-
void* data;
|
|
73
|
-
size_t size;
|
|
74
|
-
int fd;
|
|
75
|
-
|
|
76
|
-
#ifdef _WIN32
|
|
77
|
-
HANDLE hFile;
|
|
78
|
-
HANDLE hMapping;
|
|
79
|
-
#endif
|
|
80
|
-
|
|
81
|
-
MappedFile() : data(nullptr), size(0), fd(-1) {
|
|
82
|
-
#ifdef _WIN32
|
|
83
|
-
hFile = INVALID_HANDLE_VALUE;
|
|
84
|
-
hMapping = nullptr;
|
|
85
|
-
#endif
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
~MappedFile() {
|
|
89
|
-
unmap();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
bool map(const std::string& path) {
|
|
93
|
-
#ifdef _WIN32
|
|
94
|
-
// Windows implementation
|
|
95
|
-
hFile = CreateFileA(path.c_str(), GENERIC_READ,
|
|
96
|
-
FILE_SHARE_READ, NULL,
|
|
97
|
-
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
98
|
-
if (hFile == INVALID_HANDLE_VALUE) return false;
|
|
99
|
-
|
|
100
|
-
hMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
101
|
-
if (!hMapping) {
|
|
102
|
-
CloseHandle(hFile);
|
|
103
|
-
hFile = INVALID_HANDLE_VALUE;
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
data = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0);
|
|
108
|
-
LARGE_INTEGER fileSize;
|
|
109
|
-
GetFileSizeEx(hFile, &fileSize);
|
|
110
|
-
size = fileSize.QuadPart;
|
|
111
|
-
|
|
112
|
-
if (!data) {
|
|
113
|
-
CloseHandle(hMapping);
|
|
114
|
-
CloseHandle(hFile);
|
|
115
|
-
hMapping = nullptr;
|
|
116
|
-
hFile = INVALID_HANDLE_VALUE;
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
#else
|
|
120
|
-
// Unix/macOS implementation
|
|
121
|
-
fd = open(path.c_str(), O_RDONLY);
|
|
122
|
-
if (fd < 0) return false;
|
|
123
|
-
|
|
124
|
-
struct stat sb;
|
|
125
|
-
if (fstat(fd, &sb) < 0) {
|
|
126
|
-
close(fd);
|
|
127
|
-
fd = -1;
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
size = sb.st_size;
|
|
132
|
-
if (size == 0) {
|
|
133
|
-
close(fd);
|
|
134
|
-
fd = -1;
|
|
135
|
-
return false;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
|
|
139
|
-
if (data == MAP_FAILED) {
|
|
140
|
-
close(fd);
|
|
141
|
-
fd = -1;
|
|
142
|
-
data = nullptr;
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
#endif
|
|
146
|
-
return data != nullptr;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
void unmap() {
|
|
150
|
-
if (data) {
|
|
151
|
-
#ifdef _WIN32
|
|
152
|
-
UnmapViewOfFile(data);
|
|
153
|
-
if (hMapping) CloseHandle(hMapping);
|
|
154
|
-
if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile);
|
|
155
|
-
hMapping = nullptr;
|
|
156
|
-
hFile = INVALID_HANDLE_VALUE;
|
|
157
|
-
#else
|
|
158
|
-
munmap(data, size);
|
|
159
|
-
if (fd >= 0) close(fd);
|
|
160
|
-
fd = -1;
|
|
161
|
-
#endif
|
|
162
|
-
data = nullptr;
|
|
163
|
-
size = 0;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
// ============================================
|
|
169
|
-
// Image Format Detection
|
|
170
|
-
// ============================================
|
|
171
|
-
|
|
172
|
-
ImageFormat detect_format(const std::string& path) {
|
|
173
|
-
FILE* f = fopen(path.c_str(), "rb");
|
|
174
|
-
if (!f) return FORMAT_UNKNOWN;
|
|
175
|
-
|
|
176
|
-
unsigned char header[12];
|
|
177
|
-
size_t n = fread(header, 1, 12, f);
|
|
178
|
-
fclose(f);
|
|
179
|
-
|
|
180
|
-
if (n < 4) return FORMAT_UNKNOWN;
|
|
181
|
-
|
|
182
|
-
if (header[0] == 0xFF && header[1] == 0xD8 && header[2] == 0xFF)
|
|
183
|
-
return FORMAT_JPEG;
|
|
184
|
-
|
|
185
|
-
if (header[0] == 0x89 && header[1] == 0x50 &&
|
|
186
|
-
header[2] == 0x4E && header[3] == 0x47)
|
|
187
|
-
return FORMAT_PNG;
|
|
188
|
-
|
|
189
|
-
if (n >= 12 &&
|
|
190
|
-
header[0] == 'R' && header[1] == 'I' &&
|
|
191
|
-
header[2] == 'F' && header[3] == 'F' &&
|
|
192
|
-
header[8] == 'W' && header[9] == 'E' &&
|
|
193
|
-
header[10] == 'B' && header[11] == 'P')
|
|
194
|
-
return FORMAT_WEBP;
|
|
195
|
-
|
|
196
|
-
if (header[0] == 'B' && header[1] == 'M')
|
|
197
|
-
return FORMAT_BMP;
|
|
198
|
-
|
|
199
|
-
return FORMAT_UNKNOWN;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
std::string format_to_string(ImageFormat format) {
|
|
203
|
-
switch (format) {
|
|
204
|
-
case FORMAT_JPEG: return "jpg";
|
|
205
|
-
case FORMAT_PNG: return "png";
|
|
206
|
-
case FORMAT_WEBP: return "webp";
|
|
207
|
-
case FORMAT_BMP: return "bmp";
|
|
208
|
-
default: return "unknown";
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
ImageFormat string_to_format(const std::string& str) {
|
|
213
|
-
if (str == "jpg" || str == "jpeg") return FORMAT_JPEG;
|
|
214
|
-
if (str == "png") return FORMAT_PNG;
|
|
215
|
-
if (str == "webp") return FORMAT_WEBP;
|
|
216
|
-
if (str == "bmp") return FORMAT_BMP;
|
|
217
|
-
return FORMAT_UNKNOWN;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// ============================================
|
|
221
|
-
// JPEG Decoding
|
|
222
|
-
// ============================================
|
|
223
|
-
|
|
224
|
-
struct jpeg_error_mgr_ext {
|
|
225
|
-
struct jpeg_error_mgr pub;
|
|
226
|
-
jmp_buf setjmp_buffer;
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
static void jpeg_error_exit(j_common_ptr cinfo) {
|
|
230
|
-
jpeg_error_mgr_ext* myerr = (jpeg_error_mgr_ext*)cinfo->err;
|
|
231
|
-
longjmp(myerr->setjmp_buffer, 1);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
ImageData decode_jpeg(const std::string& path) {
|
|
235
|
-
ImageData data;
|
|
236
|
-
data.pixels = nullptr;
|
|
237
|
-
data.width = 0;
|
|
238
|
-
data.height = 0;
|
|
239
|
-
data.channels = 0;
|
|
240
|
-
|
|
241
|
-
MappedFile mapped;
|
|
242
|
-
if (!mapped.map(path)) {
|
|
243
|
-
FILE* infile = fopen(path.c_str(), "rb");
|
|
244
|
-
if (!infile) return data;
|
|
245
|
-
|
|
246
|
-
struct jpeg_decompress_struct cinfo;
|
|
247
|
-
struct jpeg_error_mgr_ext jerr;
|
|
248
|
-
|
|
249
|
-
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
250
|
-
jerr.pub.error_exit = jpeg_error_exit;
|
|
251
|
-
|
|
252
|
-
if (setjmp(jerr.setjmp_buffer)) {
|
|
253
|
-
jpeg_destroy_decompress(&cinfo);
|
|
254
|
-
fclose(infile);
|
|
255
|
-
return data;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
jpeg_create_decompress(&cinfo);
|
|
259
|
-
jpeg_stdio_src(&cinfo, infile);
|
|
260
|
-
jpeg_read_header(&cinfo, TRUE);
|
|
261
|
-
|
|
262
|
-
cinfo.dct_method = JDCT_IFAST;
|
|
263
|
-
cinfo.do_fancy_upsampling = FALSE;
|
|
264
|
-
|
|
265
|
-
jpeg_start_decompress(&cinfo);
|
|
266
|
-
|
|
267
|
-
data.width = cinfo.output_width;
|
|
268
|
-
data.height = cinfo.output_height;
|
|
269
|
-
data.channels = cinfo.output_components;
|
|
270
|
-
|
|
271
|
-
size_t row_stride = data.width * data.channels;
|
|
272
|
-
data.pixels = new unsigned char[data.height * row_stride];
|
|
273
|
-
|
|
274
|
-
JSAMPROW row_pointer[1];
|
|
275
|
-
while (cinfo.output_scanline < cinfo.output_height) {
|
|
276
|
-
row_pointer[0] = &data.pixels[cinfo.output_scanline * row_stride];
|
|
277
|
-
jpeg_read_scanlines(&cinfo, row_pointer, 1);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
jpeg_finish_decompress(&cinfo);
|
|
281
|
-
jpeg_destroy_decompress(&cinfo);
|
|
282
|
-
fclose(infile);
|
|
283
|
-
|
|
284
|
-
return data;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
struct jpeg_decompress_struct cinfo;
|
|
288
|
-
struct jpeg_error_mgr_ext jerr;
|
|
289
|
-
|
|
290
|
-
cinfo.err = jpeg_std_error(&jerr.pub);
|
|
291
|
-
jerr.pub.error_exit = jpeg_error_exit;
|
|
292
|
-
|
|
293
|
-
if (setjmp(jerr.setjmp_buffer)) {
|
|
294
|
-
jpeg_destroy_decompress(&cinfo);
|
|
295
|
-
return data;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
jpeg_create_decompress(&cinfo);
|
|
299
|
-
jpeg_mem_src(&cinfo, (unsigned char*)mapped.data, mapped.size);
|
|
300
|
-
jpeg_read_header(&cinfo, TRUE);
|
|
301
|
-
|
|
302
|
-
cinfo.dct_method = JDCT_IFAST;
|
|
303
|
-
cinfo.do_fancy_upsampling = FALSE;
|
|
304
|
-
|
|
305
|
-
jpeg_start_decompress(&cinfo);
|
|
306
|
-
|
|
307
|
-
data.width = cinfo.output_width;
|
|
308
|
-
data.height = cinfo.output_height;
|
|
309
|
-
data.channels = cinfo.output_components;
|
|
310
|
-
|
|
311
|
-
size_t row_stride = data.width * data.channels;
|
|
312
|
-
data.pixels = new unsigned char[data.height * row_stride];
|
|
313
|
-
|
|
314
|
-
JSAMPROW row_pointer[1];
|
|
315
|
-
while (cinfo.output_scanline < cinfo.output_height) {
|
|
316
|
-
row_pointer[0] = &data.pixels[cinfo.output_scanline * row_stride];
|
|
317
|
-
jpeg_read_scanlines(&cinfo, row_pointer, 1);
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
jpeg_finish_decompress(&cinfo);
|
|
321
|
-
jpeg_destroy_decompress(&cinfo);
|
|
322
|
-
|
|
323
|
-
return data;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
// ============================================
|
|
327
|
-
// PNG Decoding
|
|
328
|
-
// ============================================
|
|
329
|
-
|
|
330
|
-
struct PngMemoryReader {
|
|
331
|
-
const unsigned char* data;
|
|
332
|
-
size_t size;
|
|
333
|
-
size_t offset;
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
static void png_read_from_memory(png_structp png, png_bytep out, png_size_t count) {
|
|
337
|
-
PngMemoryReader* reader = (PngMemoryReader*)png_get_io_ptr(png);
|
|
338
|
-
if (reader->offset + count > reader->size) {
|
|
339
|
-
png_error(png, "Read past end of data");
|
|
340
|
-
return;
|
|
341
|
-
}
|
|
342
|
-
memcpy(out, reader->data + reader->offset, count);
|
|
343
|
-
reader->offset += count;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
ImageData decode_png(const std::string& path) {
|
|
347
|
-
ImageData data;
|
|
348
|
-
data.pixels = nullptr;
|
|
349
|
-
data.width = 0;
|
|
350
|
-
data.height = 0;
|
|
351
|
-
data.channels = 0;
|
|
352
|
-
|
|
353
|
-
MappedFile mapped;
|
|
354
|
-
PngMemoryReader mem_reader = {nullptr, 0, 0};
|
|
355
|
-
FILE* fp = nullptr;
|
|
356
|
-
|
|
357
|
-
bool use_mmap = mapped.map(path);
|
|
358
|
-
if (use_mmap) {
|
|
359
|
-
mem_reader.data = (const unsigned char*)mapped.data;
|
|
360
|
-
mem_reader.size = mapped.size;
|
|
361
|
-
mem_reader.offset = 0;
|
|
362
|
-
} else {
|
|
363
|
-
fp = fopen(path.c_str(), "rb");
|
|
364
|
-
if (!fp) return data;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
|
|
368
|
-
if (!png) {
|
|
369
|
-
if (fp) fclose(fp);
|
|
370
|
-
return data;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
png_infop info = png_create_info_struct(png);
|
|
374
|
-
if (!info) {
|
|
375
|
-
png_destroy_read_struct(&png, nullptr, nullptr);
|
|
376
|
-
if (fp) fclose(fp);
|
|
377
|
-
return data;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
if (setjmp(png_jmpbuf(png))) {
|
|
381
|
-
png_destroy_read_struct(&png, &info, nullptr);
|
|
382
|
-
if (fp) fclose(fp);
|
|
383
|
-
if (data.pixels) delete[] data.pixels;
|
|
384
|
-
data.pixels = nullptr;
|
|
385
|
-
return data;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (use_mmap) {
|
|
389
|
-
png_set_read_fn(png, &mem_reader, png_read_from_memory);
|
|
390
|
-
} else {
|
|
391
|
-
png_init_io(png, fp);
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
png_read_info(png, info);
|
|
395
|
-
|
|
396
|
-
data.width = png_get_image_width(png, info);
|
|
397
|
-
data.height = png_get_image_height(png, info);
|
|
398
|
-
png_byte color_type = png_get_color_type(png, info);
|
|
399
|
-
png_byte bit_depth = png_get_bit_depth(png, info);
|
|
400
|
-
|
|
401
|
-
if (bit_depth == 16)
|
|
402
|
-
png_set_strip_16(png);
|
|
403
|
-
|
|
404
|
-
if (color_type == PNG_COLOR_TYPE_PALETTE)
|
|
405
|
-
png_set_palette_to_rgb(png);
|
|
406
|
-
|
|
407
|
-
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
|
408
|
-
png_set_expand_gray_1_2_4_to_8(png);
|
|
409
|
-
|
|
410
|
-
if (png_get_valid(png, info, PNG_INFO_tRNS))
|
|
411
|
-
png_set_tRNS_to_alpha(png);
|
|
412
|
-
|
|
413
|
-
png_read_update_info(png, info);
|
|
414
|
-
|
|
415
|
-
switch (png_get_color_type(png, info)) {
|
|
416
|
-
case PNG_COLOR_TYPE_GRAY:
|
|
417
|
-
data.channels = 1;
|
|
418
|
-
break;
|
|
419
|
-
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
|
420
|
-
data.channels = 2;
|
|
421
|
-
break;
|
|
422
|
-
case PNG_COLOR_TYPE_RGB:
|
|
423
|
-
data.channels = 3;
|
|
424
|
-
break;
|
|
425
|
-
case PNG_COLOR_TYPE_RGB_ALPHA:
|
|
426
|
-
data.channels = 4;
|
|
427
|
-
break;
|
|
428
|
-
default:
|
|
429
|
-
data.channels = 3;
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
size_t row_bytes = png_get_rowbytes(png, info);
|
|
433
|
-
data.pixels = new unsigned char[data.height * row_bytes];
|
|
434
|
-
|
|
435
|
-
png_bytep* row_pointers = new png_bytep[data.height];
|
|
436
|
-
for (int y = 0; y < data.height; y++) {
|
|
437
|
-
row_pointers[y] = data.pixels + y * row_bytes;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
png_read_image(png, row_pointers);
|
|
441
|
-
png_read_end(png, nullptr);
|
|
442
|
-
|
|
443
|
-
delete[] row_pointers;
|
|
444
|
-
png_destroy_read_struct(&png, &info, nullptr);
|
|
445
|
-
if (fp) fclose(fp);
|
|
446
|
-
|
|
447
|
-
return data;
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// ============================================
|
|
451
|
-
// WEBP Decoding
|
|
452
|
-
// ============================================
|
|
453
|
-
|
|
454
|
-
ImageData decode_webp(const std::string& path) {
|
|
455
|
-
ImageData data;
|
|
456
|
-
data.pixels = nullptr;
|
|
457
|
-
data.width = 0;
|
|
458
|
-
data.height = 0;
|
|
459
|
-
data.channels = 0;
|
|
460
|
-
|
|
461
|
-
MappedFile mapped;
|
|
462
|
-
if (!mapped.map(path)) {
|
|
463
|
-
FILE* fp = fopen(path.c_str(), "rb");
|
|
464
|
-
if (!fp) return data;
|
|
465
|
-
|
|
466
|
-
fseek(fp, 0, SEEK_END);
|
|
467
|
-
long file_size = ftell(fp);
|
|
468
|
-
fseek(fp, 0, SEEK_SET);
|
|
469
|
-
|
|
470
|
-
if (file_size <= 0) {
|
|
471
|
-
fclose(fp);
|
|
472
|
-
return data;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
unsigned char* file_data = new unsigned char[file_size];
|
|
476
|
-
size_t read_size = fread(file_data, 1, file_size, fp);
|
|
477
|
-
fclose(fp);
|
|
478
|
-
|
|
479
|
-
if (read_size != (size_t)file_size) {
|
|
480
|
-
delete[] file_data;
|
|
481
|
-
return data;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
WebPBitstreamFeatures features;
|
|
485
|
-
if (WebPGetFeatures(file_data, file_size, &features) != VP8_STATUS_OK) {
|
|
486
|
-
delete[] file_data;
|
|
487
|
-
return data;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
data.width = features.width;
|
|
491
|
-
data.height = features.height;
|
|
492
|
-
data.channels = features.has_alpha ? 4 : 3;
|
|
493
|
-
|
|
494
|
-
unsigned char* webp_pixels = nullptr;
|
|
495
|
-
if (data.channels == 4) {
|
|
496
|
-
webp_pixels = WebPDecodeRGBA(file_data, file_size, &data.width, &data.height);
|
|
497
|
-
} else {
|
|
498
|
-
webp_pixels = WebPDecodeRGB(file_data, file_size, &data.width, &data.height);
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
delete[] file_data;
|
|
502
|
-
|
|
503
|
-
if (!webp_pixels) {
|
|
504
|
-
data.width = 0;
|
|
505
|
-
data.height = 0;
|
|
506
|
-
data.channels = 0;
|
|
507
|
-
return data;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
size_t pixel_count = data.width * data.height * data.channels;
|
|
511
|
-
data.pixels = new unsigned char[pixel_count];
|
|
512
|
-
fast_copy_aligned(data.pixels, webp_pixels, pixel_count);
|
|
513
|
-
WebPFree(webp_pixels);
|
|
514
|
-
|
|
515
|
-
return data;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
WebPBitstreamFeatures features;
|
|
519
|
-
if (WebPGetFeatures((unsigned char*)mapped.data, mapped.size, &features) != VP8_STATUS_OK) {
|
|
520
|
-
return data;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
data.width = features.width;
|
|
524
|
-
data.height = features.height;
|
|
525
|
-
data.channels = features.has_alpha ? 4 : 3;
|
|
526
|
-
|
|
527
|
-
unsigned char* webp_pixels = nullptr;
|
|
528
|
-
if (data.channels == 4) {
|
|
529
|
-
webp_pixels = WebPDecodeRGBA((unsigned char*)mapped.data, mapped.size,
|
|
530
|
-
&data.width, &data.height);
|
|
531
|
-
} else {
|
|
532
|
-
webp_pixels = WebPDecodeRGB((unsigned char*)mapped.data, mapped.size,
|
|
533
|
-
&data.width, &data.height);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if (!webp_pixels) {
|
|
537
|
-
data.width = 0;
|
|
538
|
-
data.height = 0;
|
|
539
|
-
data.channels = 0;
|
|
540
|
-
return data;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
size_t pixel_count = data.width * data.height * data.channels;
|
|
544
|
-
data.pixels = new unsigned char[pixel_count];
|
|
545
|
-
fast_copy_aligned(data.pixels, webp_pixels, pixel_count);
|
|
546
|
-
WebPFree(webp_pixels);
|
|
547
|
-
|
|
548
|
-
return data;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
// ============================================
|
|
552
|
-
// Image Decoding
|
|
553
|
-
// ============================================
|
|
554
|
-
|
|
555
|
-
ImageData decode_image(const std::string& path, ImageFormat format) {
|
|
556
|
-
ImageData data;
|
|
557
|
-
data.pixels = nullptr;
|
|
558
|
-
data.width = 0;
|
|
559
|
-
data.height = 0;
|
|
560
|
-
data.channels = 0;
|
|
561
|
-
|
|
562
|
-
switch (format) {
|
|
563
|
-
case FORMAT_JPEG:
|
|
564
|
-
return decode_jpeg(path);
|
|
565
|
-
case FORMAT_PNG:
|
|
566
|
-
return decode_png(path);
|
|
567
|
-
case FORMAT_WEBP:
|
|
568
|
-
return decode_webp(path);
|
|
569
|
-
case FORMAT_BMP:
|
|
570
|
-
data.pixels = stbi_load(path.c_str(),
|
|
571
|
-
&data.width,
|
|
572
|
-
&data.height,
|
|
573
|
-
&data.channels,
|
|
574
|
-
0);
|
|
575
|
-
return data;
|
|
576
|
-
default:
|
|
577
|
-
data.pixels = stbi_load(path.c_str(),
|
|
578
|
-
&data.width,
|
|
579
|
-
&data.height,
|
|
580
|
-
&data.channels,
|
|
581
|
-
0);
|
|
582
|
-
return data;
|
|
583
|
-
}
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
void free_image_data(ImageData& data) {
|
|
587
|
-
if (data.pixels) {
|
|
588
|
-
delete[] data.pixels;
|
|
589
|
-
data.pixels = nullptr;
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
// ============================================
|
|
594
|
-
// Image Info
|
|
595
|
-
// ============================================
|
|
596
|
-
|
|
597
|
-
bool get_image_dimensions(const std::string& path, int& width, int& height, int& channels) {
|
|
598
|
-
ImageFormat format = detect_format(path);
|
|
599
|
-
|
|
600
|
-
if (format == FORMAT_WEBP) {
|
|
601
|
-
FILE* fp = fopen(path.c_str(), "rb");
|
|
602
|
-
if (!fp) return false;
|
|
603
|
-
|
|
604
|
-
fseek(fp, 0, SEEK_END);
|
|
605
|
-
long file_size = ftell(fp);
|
|
606
|
-
fseek(fp, 0, SEEK_SET);
|
|
607
|
-
|
|
608
|
-
if (file_size <= 0) {
|
|
609
|
-
fclose(fp);
|
|
610
|
-
return false;
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
unsigned char* file_data = new unsigned char[file_size];
|
|
614
|
-
size_t read_size = fread(file_data, 1, file_size, fp);
|
|
615
|
-
fclose(fp);
|
|
616
|
-
|
|
617
|
-
if (read_size != (size_t)file_size) {
|
|
618
|
-
delete[] file_data;
|
|
619
|
-
return false;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
WebPBitstreamFeatures features;
|
|
623
|
-
if (WebPGetFeatures(file_data, file_size, &features) == VP8_STATUS_OK) {
|
|
624
|
-
width = features.width;
|
|
625
|
-
height = features.height;
|
|
626
|
-
channels = features.has_alpha ? 4 : 3;
|
|
627
|
-
delete[] file_data;
|
|
628
|
-
return true;
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
delete[] file_data;
|
|
632
|
-
return false;
|
|
633
|
-
} else {
|
|
634
|
-
int w, h, c;
|
|
635
|
-
int result = stbi_info(path.c_str(), &w, &h, &c);
|
|
636
|
-
if (result) {
|
|
637
|
-
width = w;
|
|
638
|
-
height = h;
|
|
639
|
-
channels = c;
|
|
640
|
-
return true;
|
|
641
|
-
}
|
|
642
|
-
return false;
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
}
|
|
647
|
-
}
|