/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 | 0 | { |
27 | 0 | INT64 est_datasize = |
28 | 0 | T.theight * T.twidth / 3; // is 0.3 bytes per pixel good estimate? |
29 | 0 | if (ID.toffset < 0) |
30 | 0 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
31 | | |
32 | 0 | if (ID.toffset + est_datasize > ID.input->size() + THUMB_READ_BEYOND) |
33 | 0 | throw LIBRAW_EXCEPTION_IO_EOF; |
34 | | |
35 | 0 | if(INT64(T.theight) * INT64(T.twidth) > 1024ULL * 1024ULL * LIBRAW_MAX_THUMBNAIL_MB) |
36 | 0 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
37 | | |
38 | 0 | if (INT64(T.theight) * INT64(T.twidth) < 64ULL) |
39 | 0 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
40 | | |
41 | 0 | if(T.twidth < 16 || T.twidth > 8192 || T.theight < 16 || T.theight > 8192) |
42 | 0 | throw LIBRAW_EXCEPTION_IO_CORRUPT; |
43 | | |
44 | | // some kodak cameras |
45 | 0 | ushort s_height = S.height, s_width = S.width, s_iwidth = S.iwidth, |
46 | 0 | s_iheight = S.iheight; |
47 | 0 | ushort s_flags = libraw_internal_data.unpacker_data.load_flags; |
48 | 0 | libraw_internal_data.unpacker_data.load_flags = 12; |
49 | 0 | int s_colors = P1.colors; |
50 | 0 | unsigned s_filters = P1.filters; |
51 | 0 | ushort(*s_image)[4] = imgdata.image; |
52 | |
|
53 | 0 | S.height = T.theight; |
54 | 0 | S.width = T.twidth; |
55 | 0 | P1.filters = 0; |
56 | |
|
57 | 0 | #define Tformat libraw_internal_data.unpacker_data.thumb_format |
58 | | |
59 | |
|
60 | 0 | if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_YCBCR) |
61 | 0 | { |
62 | 0 | S.height += S.height & 1; |
63 | 0 | S.width += S.width & 1; |
64 | 0 | } |
65 | |
|
66 | 0 | S.iheight = S.height; |
67 | 0 | S.iwidth = S.width; |
68 | |
|
69 | 0 | imgdata.image = |
70 | 0 | (ushort(*)[4])calloc(S.iheight * S.iwidth, sizeof(*imgdata.image)); |
71 | |
|
72 | 0 | ID.input->seek(ID.toffset, SEEK_SET); |
73 | | // read kodak thumbnail into T.image[] |
74 | 0 | try |
75 | 0 | { |
76 | 0 | if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_YCBCR) |
77 | 0 | kodak_ycbcr_load_raw(); |
78 | 0 | else if(Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_RGB) |
79 | 0 | kodak_rgb_load_raw(); |
80 | 0 | else if (Tformat == LIBRAW_INTERNAL_THUMBNAIL_KODAK_THUMB) |
81 | 0 | kodak_thumb_load_raw(); |
82 | 0 | } |
83 | 0 | catch (...) |
84 | 0 | { |
85 | 0 | free(imgdata.image); |
86 | 0 | imgdata.image = s_image; |
87 | |
|
88 | 0 | T.twidth = 0; |
89 | 0 | S.width = s_width; |
90 | |
|
91 | 0 | S.iwidth = s_iwidth; |
92 | 0 | S.iheight = s_iheight; |
93 | |
|
94 | 0 | T.theight = 0; |
95 | 0 | S.height = s_height; |
96 | |
|
97 | 0 | T.tcolors = 0; |
98 | 0 | P1.colors = s_colors; |
99 | |
|
100 | 0 | P1.filters = s_filters; |
101 | 0 | T.tlength = 0; |
102 | 0 | libraw_internal_data.unpacker_data.load_flags = s_flags; |
103 | 0 | return; |
104 | 0 | } |
105 | | |
106 | | // from scale_colors |
107 | 0 | { |
108 | 0 | double dmax; |
109 | 0 | float scale_mul[4]; |
110 | 0 | int c, val; |
111 | 0 | for (dmax = DBL_MAX, c = 0; c < 3; c++) |
112 | 0 | if (dmax > C.pre_mul[c]) |
113 | 0 | dmax = C.pre_mul[c]; |
114 | |
|
115 | 0 | for (c = 0; c < 3; c++) |
116 | 0 | scale_mul[c] = float((C.pre_mul[c] / dmax) * 65535. / C.maximum); |
117 | 0 | scale_mul[3] = scale_mul[1]; |
118 | |
|
119 | 0 | size_t size = S.height * S.width; |
120 | 0 | for (unsigned i = 0; i < size * 4; i++) |
121 | 0 | { |
122 | 0 | val = imgdata.image[0][i]; |
123 | 0 | if (!val) |
124 | 0 | continue; |
125 | 0 | val = int(val * scale_mul[i & 3]); |
126 | 0 | imgdata.image[0][i] = CLIP(val); |
127 | 0 | } |
128 | 0 | } |
129 | | |
130 | | // from convert_to_rgb |
131 | 0 | ushort *img; |
132 | 0 | int row, col; |
133 | |
|
134 | 0 | int(*t_hist)[LIBRAW_HISTOGRAM_SIZE] = |
135 | 0 | (int(*)[LIBRAW_HISTOGRAM_SIZE])calloc(sizeof(*t_hist), 4); |
136 | |
|
137 | 0 | if (imgdata.idata.maker_index == LIBRAW_CAMERAMAKER_Canon) // Skip color conversion for canon PPM tiffs |
138 | 0 | { |
139 | 0 | for (img = imgdata.image[0], row = 0; row < S.height; row++) |
140 | 0 | for (col = 0; col < S.width; col++, img += 4) |
141 | 0 | for (int c = 0; c < P1.colors; c++) |
142 | 0 | t_hist[c][img[c] >> 3]++; |
143 | 0 | } |
144 | 0 | else |
145 | 0 | { |
146 | 0 | float out[3], out_cam[3][4] = {{2.81761312f, -1.98369181f, 0.166078627f, 0}, |
147 | 0 | {-0.111855984f, 1.73688626f, -0.625030339f, 0}, |
148 | 0 | {-0.0379119813f, -0.891268849f, 1.92918086f, 0}}; |
149 | | |
150 | 0 | for (img = imgdata.image[0], row = 0; row < S.height; row++) |
151 | 0 | for (col = 0; col < S.width; col++, img += 4) |
152 | 0 | { |
153 | 0 | out[0] = out[1] = out[2] = 0; |
154 | 0 | int c; |
155 | 0 | for (c = 0; c < 3; c++) |
156 | 0 | { |
157 | 0 | out[0] += out_cam[0][c] * img[c]; |
158 | 0 | out[1] += out_cam[1][c] * img[c]; |
159 | 0 | out[2] += out_cam[2][c] * img[c]; |
160 | 0 | } |
161 | 0 | for (c = 0; c < 3; c++) |
162 | 0 | img[c] = CLIP((int)out[c]); |
163 | 0 | for (c = 0; c < P1.colors; c++) |
164 | 0 | t_hist[c][img[c] >> 3]++; |
165 | 0 | } |
166 | 0 | } |
167 | | |
168 | | // from gamma_lut |
169 | 0 | int(*save_hist)[LIBRAW_HISTOGRAM_SIZE] = |
170 | 0 | libraw_internal_data.output_data.histogram; |
171 | 0 | libraw_internal_data.output_data.histogram = t_hist; |
172 | | |
173 | | // make curve output curve! |
174 | 0 | ushort *t_curve = (ushort *)calloc(sizeof(C.curve), 1); |
175 | 0 | memmove(t_curve, C.curve, sizeof(C.curve)); |
176 | 0 | memset(C.curve, 0, sizeof(C.curve)); |
177 | 0 | { |
178 | 0 | int perc, val, total, t_white = 0x2000, c; |
179 | |
|
180 | 0 | perc = int(S.width * S.height * 0.01f); /* 99th percentile white level */ |
181 | 0 | if (IO.fuji_width) |
182 | 0 | perc /= 2; |
183 | 0 | if (!((O.highlight & ~2) || O.no_auto_bright)) |
184 | 0 | for (t_white = c = 0; c < P1.colors; c++) |
185 | 0 | { |
186 | 0 | for (val = 0x2000, total = 0; --val > 32;) |
187 | 0 | if ((total += libraw_internal_data.output_data.histogram[c][val]) > |
188 | 0 | perc) |
189 | 0 | break; |
190 | 0 | if (t_white < val) |
191 | 0 | t_white = val; |
192 | 0 | } |
193 | 0 | gamma_curve(O.gamm[0], O.gamm[1], 2, int((t_white << 3) / O.bright)); |
194 | 0 | } |
195 | |
|
196 | 0 | libraw_internal_data.output_data.histogram = save_hist; |
197 | 0 | free(t_hist); |
198 | | |
199 | | // from write_ppm_tiff - copy pixels into bitmap |
200 | |
|
201 | 0 | int s_flip = imgdata.sizes.flip; |
202 | 0 | if (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_NO_ROTATE_FOR_KODAK_THUMBNAILS) |
203 | 0 | imgdata.sizes.flip = 0; |
204 | |
|
205 | 0 | S.iheight = S.height; |
206 | 0 | S.iwidth = S.width; |
207 | 0 | if (S.flip & 4) |
208 | 0 | SWAP(S.height, S.width); |
209 | |
|
210 | 0 | if (T.thumb) |
211 | 0 | free(T.thumb); |
212 | 0 | T.thumb = (char *)calloc(S.width * S.height, P1.colors); |
213 | 0 | T.tlength = S.width * S.height * P1.colors; |
214 | | |
215 | | // from write_tiff_ppm |
216 | 0 | { |
217 | 0 | int soff = flip_index(0, 0); |
218 | 0 | int cstep = flip_index(0, 1) - soff; |
219 | 0 | int rstep = flip_index(1, 0) - flip_index(0, S.width); |
220 | |
|
221 | 0 | for (int rr = 0; rr < S.height; rr++, soff += rstep) |
222 | 0 | { |
223 | 0 | char *ppm = T.thumb + rr * S.width * P1.colors; |
224 | 0 | for (int cc = 0; cc < S.width; cc++, soff += cstep) |
225 | 0 | for (int c = 0; c < P1.colors; c++) |
226 | 0 | ppm[cc * P1.colors + c] = |
227 | 0 | imgdata.color.curve[imgdata.image[soff][c]] >> 8; |
228 | 0 | } |
229 | 0 | } |
230 | |
|
231 | 0 | memmove(C.curve, t_curve, sizeof(C.curve)); |
232 | 0 | free(t_curve); |
233 | | |
234 | | // restore variables |
235 | 0 | free(imgdata.image); |
236 | 0 | imgdata.image = s_image; |
237 | |
|
238 | 0 | if (imgdata.rawparams.options & LIBRAW_RAWOPTIONS_NO_ROTATE_FOR_KODAK_THUMBNAILS) |
239 | 0 | imgdata.sizes.flip = s_flip; |
240 | |
|
241 | 0 | T.twidth = S.width; |
242 | 0 | S.width = s_width; |
243 | |
|
244 | 0 | S.iwidth = s_iwidth; |
245 | 0 | S.iheight = s_iheight; |
246 | |
|
247 | 0 | T.theight = S.height; |
248 | 0 | S.height = s_height; |
249 | |
|
250 | 0 | T.tcolors = P1.colors; |
251 | 0 | P1.colors = s_colors; |
252 | |
|
253 | 0 | P1.filters = s_filters; |
254 | 0 | libraw_internal_data.unpacker_data.load_flags = s_flags; |
255 | 0 | } |
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 | } |