Coverage Report

Created: 2025-12-20 06:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}