/src/libraw/src/utils/thumb_utils.cpp
Line | Count | Source |
1 | | /* -*- C++ -*- |
2 | | * Copyright 2019-2025 LibRaw LLC (info@libraw.org) |
3 | | * |
4 | | |
5 | | LibRaw is free software; you can redistribute it and/or modify |
6 | | it under the terms of the one of two licenses as you choose: |
7 | | |
8 | | 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1 |
9 | | (See file LICENSE.LGPL provided in LibRaw distribution archive for details). |
10 | | |
11 | | 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 |
12 | | (See file LICENSE.CDDL provided in LibRaw distribution archive for details). |
13 | | |
14 | | */ |
15 | | |
16 | | #include "../../internal/libraw_cxx_defs.h" |
17 | | #include <vector> |
18 | | |
19 | | void LibRaw::dng_ycbcr_thumb_loader() |
20 | 0 | { |
21 | | // Placeholder: the only known camera w/ YCC previews in DNG provides broken files |
22 | 0 | throw LIBRAW_EXCEPTION_UNSUPPORTED_FORMAT; |
23 | 0 | } |
24 | | |
25 | | void LibRaw::kodak_thumb_loader() |
26 | 175 | { |
27 | 175 | INT64 est_datasize = |
28 | 175 | T.theight * T.twidth / 3; // is 0.3 bytes per pixel good estimate? |
29 | 175 | if (ID.toffset < 0) |
30 | 0 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
31 | | |
32 | 175 | if (ID.toffset + est_datasize > ID.input->size() + THUMB_READ_BEYOND) |
33 | 9 | throw LIBRAW_EXCEPTION_IO_EOF; |
34 | | |
35 | 166 | if(INT64(T.theight) * INT64(T.twidth) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB) |
36 | 0 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
37 | | |
38 | 166 | if (INT64(T.theight) * INT64(T.twidth) < 64ULL) |
39 | 1 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
40 | | |
41 | 165 | if(T.twidth < 16 || T.twidth > 8192 || T.theight < 16 || T.theight > 8192) |
42 | 2 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
43 | | |
44 | | // some kodak cameras |
45 | 163 | ushort s_height = S.height, s_width = S.width, s_iwidth = S.iwidth, |
46 | 163 | s_iheight = S.iheight; |
47 | 163 | ushort s_flags = libraw_internal_data.unpacker_data.load_flags; |
48 | 163 | libraw_internal_data.unpacker_data.load_flags = 12; |
49 | 163 | int s_colors = P1.colors; |
50 | 163 | unsigned s_filters = P1.filters; |
51 | 163 | ushort(*s_image)[4] = imgdata.image; |
52 | | |
53 | 163 | S.height = T.theight; |
54 | 163 | S.width = T.twidth; |
55 | 163 | P1.filters = 0; |
56 | | |
57 | 507 | #define Tformat libraw_internal_data.unpacker_data.thumb_format |
58 | | |
59 | | |
60 | 163 | if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_YCBCR) |
61 | 55 | { |
62 | 55 | S.height += S.height & 1; |
63 | 55 | S.width += S.width & 1; |
64 | 55 | } |
65 | | |
66 | 163 | S.iheight = S.height; |
67 | 163 | S.iwidth = S.width; |
68 | | |
69 | 163 | imgdata.image = |
70 | 163 | (ushort(*)[4])calloc(S.iheight * S.iwidth, sizeof(*imgdata.image)); |
71 | | |
72 | 163 | ID.input->seek(ID.toffset, SEEK_SET); |
73 | | // read kodak thumbnail into T.image[] |
74 | 163 | try |
75 | 163 | { |
76 | 163 | if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_YCBCR) |
77 | 55 | kodak_ycbcr_load_raw(); |
78 | 108 | else if(Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_RGB) |
79 | 35 | kodak_rgb_load_raw(); |
80 | 73 | else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_THUMB) |
81 | 73 | kodak_thumb_load_raw(); |
82 | 163 | } |
83 | 163 | catch (...) |
84 | 163 | { |
85 | 20 | free(imgdata.image); |
86 | 20 | imgdata.image = s_image; |
87 | | |
88 | 20 | T.twidth = 0; |
89 | 20 | S.width = s_width; |
90 | | |
91 | 20 | S.iwidth = s_iwidth; |
92 | 20 | S.iheight = s_iheight; |
93 | | |
94 | 20 | T.theight = 0; |
95 | 20 | S.height = s_height; |
96 | | |
97 | 20 | T.tcolors = 0; |
98 | 20 | P1.colors = s_colors; |
99 | | |
100 | 20 | P1.filters = s_filters; |
101 | 20 | T.tlength = 0; |
102 | 20 | libraw_internal_data.unpacker_data.load_flags = s_flags; |
103 | 20 | return; |
104 | 20 | } |
105 | | |
106 | | // from scale_colors |
107 | 143 | { |
108 | 143 | double dmax; |
109 | 143 | float scale_mul[4]; |
110 | 143 | int c, val; |
111 | 572 | for (dmax = DBL_MAX, c = 0; c < 3; c++) |
112 | 429 | if (dmax > C.pre_mul[c]) |
113 | 150 | dmax = C.pre_mul[c]; |
114 | | |
115 | 572 | for (c = 0; c < 3; c++) |
116 | 429 | scale_mul[c] = float((C.pre_mul[c] / dmax) * 65535. / C.maximum); |
117 | 143 | scale_mul[3] = scale_mul[1]; |
118 | | |
119 | 143 | size_t size = S.height * S.width; |
120 | 7.66M | for (unsigned i = 0; i < size * 4; i++) |
121 | 7.66M | { |
122 | 7.66M | val = imgdata.image[0][i]; |
123 | 7.66M | if (!val) |
124 | 4.41M | continue; |
125 | 3.24M | val = int(val * scale_mul[i & 3]); |
126 | 3.24M | imgdata.image[0][i] = CLIP(val); |
127 | 3.24M | } |
128 | 143 | } |
129 | | |
130 | | // from convert_to_rgb |
131 | 143 | ushort *img; |
132 | 143 | int row, col; |
133 | | |
134 | 143 | int(*t_hist)[LIBRAW_HISTOGRAM_SIZE] = |
135 | 143 | (int(*)[LIBRAW_HISTOGRAM_SIZE])calloc(sizeof(*t_hist), 4); |
136 | | |
137 | 143 | if (imgdata.idata.maker_index == LIBRAW_CAMERAMAKER_Canon) // Skip color conversion for canon PPM tiffs |
138 | 36 | { |
139 | 10.6k | for (img = imgdata.image[0], row = 0; row < S.height; row++) |
140 | 725k | for (col = 0; col < S.width; col++, img += 4) |
141 | 1.95M | for (int c = 0; c < P1.colors; c++) |
142 | 1.24M | t_hist[c][img[c] >> 3]++; |
143 | 36 | } |
144 | 107 | else |
145 | 107 | { |
146 | 107 | float out[3], out_cam[3][4] = {{2.81761312f, -1.98369181f, 0.166078627f, 0}, |
147 | 107 | {-0.111855984f, 1.73688626f, -0.625030339f, 0}, |
148 | 107 | {-0.0379119813f, -0.891268849f, 1.92918086f, 0}}; |
149 | | |
150 | 40.1k | for (img = imgdata.image[0], row = 0; row < S.height; row++) |
151 | 1.24M | for (col = 0; col < S.width; col++, img += 4) |
152 | 1.20M | { |
153 | 1.20M | out[0] = out[1] = out[2] = 0; |
154 | 1.20M | int c; |
155 | 4.80M | for (c = 0; c < 3; c++) |
156 | 3.60M | { |
157 | 3.60M | out[0] += out_cam[0][c] * img[c]; |
158 | 3.60M | out[1] += out_cam[1][c] * img[c]; |
159 | 3.60M | out[2] += out_cam[2][c] * img[c]; |
160 | 3.60M | } |
161 | 4.80M | for (c = 0; c < 3; c++) |
162 | 3.60M | img[c] = CLIP((int)out[c]); |
163 | 4.34M | for (c = 0; c < P1.colors; c++) |
164 | 3.14M | t_hist[c][img[c] >> 3]++; |
165 | 1.20M | } |
166 | 107 | } |
167 | | |
168 | | // from gamma_lut |
169 | 143 | int(*save_hist)[LIBRAW_HISTOGRAM_SIZE] = |
170 | 143 | libraw_internal_data.output_data.histogram; |
171 | 143 | libraw_internal_data.output_data.histogram = t_hist; |
172 | | |
173 | | // make curve output curve! |
174 | 143 | ushort *t_curve = (ushort *)calloc(sizeof(C.curve), 1); |
175 | 143 | memmove(t_curve, C.curve, sizeof(C.curve)); |
176 | 143 | memset(C.curve, 0, sizeof(C.curve)); |
177 | 143 | { |
178 | 143 | int perc, val, total, t_white = 0x2000, c; |
179 | | |
180 | 143 | perc = int(S.width * S.height * 0.01f); /* 99th percentile white level */ |
181 | 143 | if (IO.fuji_width) |
182 | 8 | perc /= 2; |
183 | 143 | if (!((O.highlight & ~2) || O.no_auto_bright)) |
184 | 452 | for (t_white = c = 0; c < P1.colors; c++) |
185 | 309 | { |
186 | 667k | for (val = 0x2000, total = 0; --val > 32;) |
187 | 667k | if ((total += libraw_internal_data.output_data.histogram[c][val]) > |
188 | 667k | perc) |
189 | 272 | break; |
190 | 309 | if (t_white < val) |
191 | 120 | t_white = val; |
192 | 309 | } |
193 | 143 | gamma_curve(O.gamm[0], O.gamm[1], 2, int((t_white << 3) / O.bright)); |
194 | 143 | } |
195 | | |
196 | 143 | libraw_internal_data.output_data.histogram = save_hist; |
197 | 143 | free(t_hist); |
198 | | |
199 | | // from write_ppm_tiff - copy pixels into bitmap |
200 | | |
201 | 143 | int s_flip = imgdata.sizes.flip; |
202 | 143 | if (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_NO_ROTATE_FOR_KODAK_THUMBNAILS) |
203 | 0 | imgdata.sizes.flip = 0; |
204 | | |
205 | 143 | S.iheight = S.height; |
206 | 143 | S.iwidth = S.width; |
207 | 143 | if (S.flip & 4) |
208 | 7 | SWAP(S.height, S.width); |
209 | | |
210 | 143 | if (T.thumb) |
211 | 0 | free(T.thumb); |
212 | 143 | T.thumb = (char *)calloc(S.width * S.height, P1.colors); |
213 | 143 | T.tlength = S.width * S.height * P1.colors; |
214 | | |
215 | | // from write_tiff_ppm |
216 | 143 | { |
217 | 143 | int soff = flip_index(0, 0); |
218 | 143 | int cstep = flip_index(0, 1) - soff; |
219 | 143 | int rstep = flip_index(1, 0) - flip_index(0, S.width); |
220 | | |
221 | 49.9k | for (int rr = 0; rr < S.height; rr++, soff += rstep) |
222 | 49.7k | { |
223 | 49.7k | char *ppm = T.thumb + rr * S.width * P1.colors; |
224 | 1.96M | for (int cc = 0; cc < S.width; cc++, soff += cstep) |
225 | 6.30M | for (int c = 0; c < P1.colors; c++) |
226 | 4.38M | ppm[cc * P1.colors + c] = |
227 | 4.38M | imgdata.color.curve[imgdata.image[soff][c]] >> 8; |
228 | 49.7k | } |
229 | 143 | } |
230 | | |
231 | 143 | memmove(C.curve, t_curve, sizeof(C.curve)); |
232 | 143 | free(t_curve); |
233 | | |
234 | | // restore variables |
235 | 143 | free(imgdata.image); |
236 | 143 | imgdata.image = s_image; |
237 | | |
238 | 143 | if (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_NO_ROTATE_FOR_KODAK_THUMBNAILS) |
239 | 0 | imgdata.sizes.flip = s_flip; |
240 | | |
241 | 143 | T.twidth = S.width; |
242 | 143 | S.width = s_width; |
243 | | |
244 | 143 | S.iwidth = s_iwidth; |
245 | 143 | S.iheight = s_iheight; |
246 | | |
247 | 143 | T.theight = S.height; |
248 | 143 | S.height = s_height; |
249 | | |
250 | 143 | T.tcolors = P1.colors; |
251 | 143 | P1.colors = s_colors; |
252 | | |
253 | 143 | P1.filters = s_filters; |
254 | 143 | libraw_internal_data.unpacker_data.load_flags = s_flags; |
255 | 143 | } |
256 | | |
257 | | // ������� thumbnail �� �����, ������ thumb_format � ������������ � �������� |
258 | | |
259 | | int LibRaw::thumbOK(INT64 maxsz) |
260 | 0 | { |
261 | 0 | if (!ID.input) |
262 | 0 | return 0; |
263 | 0 | if (!ID.toffset && !(imgdata.thumbnail.tlength > 0 && |
264 | 0 | load_raw == &LibRaw::broadcom_load_raw) // RPi |
265 | | #ifdef USE_6BY9RPI |
266 | | && !(imgdata.thumbnail.tlength > 0 && libraw_internal_data.unpacker_data.load_flags & 0x4000 && |
267 | | (load_raw == &LibRaw::rpi_load_raw8 || load_raw == &LibRaw::nokia_load_raw || |
268 | | load_raw == &LibRaw::rpi_load_raw12 || load_raw == &LibRaw::rpi_load_raw14)) |
269 | | #endif |
270 | 0 | ) |
271 | 0 | return 0; |
272 | 0 | INT64 fsize = ID.input->size(); |
273 | 0 | if (fsize > 0xffffffffU) |
274 | 0 | return 0; // No thumb for raw > 4Gb-1 |
275 | 0 | int tsize = 0; |
276 | 0 | int tcol = (T.tcolors > 0 && T.tcolors < 4) ? T.tcolors : 3; |
277 | 0 | if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_DNG_YCBCR) |
278 | 0 | return 0; // Temp: unless we get correct DNG w/ YCBCR preview |
279 | 0 | else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_JPEG) |
280 | 0 | tsize = T.tlength; |
281 | 0 | else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_PPM) |
282 | 0 | tsize = tcol * T.twidth * T.theight; |
283 | 0 | else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_PPM16) |
284 | 0 | tsize = tcol * T.twidth * T.theight * |
285 | 0 | ((imgdata.rawparams.options & LIBRAW_RAWOPTIONS_USE_PPM16_THUMBS) ? 2 : 1); |
286 | | #ifdef USE_X3FTOOLS |
287 | | else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_X3F) |
288 | | { |
289 | | tsize = x3f_thumb_size(); |
290 | | } |
291 | | #endif |
292 | 0 | else // Kodak => no check |
293 | 0 | tsize = 1; |
294 | 0 | if (tsize < 0) |
295 | 0 | return 0; |
296 | 0 | if (maxsz > 0 && tsize > maxsz) |
297 | 0 | return 0; |
298 | 0 | return (tsize + ID.toffset <= fsize) ? 1 : 0; |
299 | 0 | } |
300 | | |
301 | | int LibRaw::dcraw_thumb_writer(const char *fname) |
302 | 0 | { |
303 | | // CHECK_ORDER_LOW(LIBRAW_PROGRESS_THUMB_LOAD); |
304 | |
|
305 | 0 | if (!fname) |
306 | 0 | return ENOENT; |
307 | | |
308 | 0 | FILE *tfp = fopen(fname, "wb"); |
309 | |
|
310 | 0 | if (!tfp) |
311 | 0 | return errno; |
312 | | |
313 | 0 | if (!T.thumb) |
314 | 0 | { |
315 | 0 | fclose(tfp); |
316 | 0 | return LIBRAW_OUT_OF_ORDER_CALL; |
317 | 0 | } |
318 | | |
319 | 0 | try |
320 | 0 | { |
321 | 0 | switch (T.tformat) |
322 | 0 | { |
323 | 0 | case LIBRAW_THUMBNAIL_JPEGXL: |
324 | 0 | fwrite(T.thumb, 1, T.tlength, tfp); |
325 | 0 | break; |
326 | 0 | case LIBRAW_THUMBNAIL_JPEG: |
327 | 0 | jpeg_thumb_writer(tfp, T.thumb, T.tlength); |
328 | 0 | break; |
329 | 0 | case LIBRAW_THUMBNAIL_BITMAP: |
330 | 0 | fprintf(tfp, "P%d\n%d %d\n255\n", T.tcolors == 1 ? 5 : 6, T.twidth, T.theight); |
331 | 0 | fwrite(T.thumb, 1, T.tlength, tfp); |
332 | 0 | break; |
333 | 0 | default: |
334 | 0 | fclose(tfp); |
335 | 0 | return LIBRAW_UNSUPPORTED_THUMBNAIL; |
336 | 0 | } |
337 | 0 | fclose(tfp); |
338 | 0 | return 0; |
339 | 0 | } |
340 | 0 | catch (const std::bad_alloc&) |
341 | 0 | { |
342 | 0 | fclose(tfp); |
343 | 0 | EXCEPTION_HANDLER(LIBRAW_EXCEPTION_ALLOC); |
344 | 0 | } |
345 | 0 | catch (const LibRaw_exceptions& err) |
346 | 0 | { |
347 | 0 | fclose(tfp); |
348 | 0 | EXCEPTION_HANDLER(err); |
349 | 0 | } |
350 | 0 | } |