Coverage Report

Created: 2025-11-14 07:32

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
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
}