Coverage Report

Created: 2026-04-29 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/LibRaw/src/decoders/sonycc.cpp
Line
Count
Source
1
/* -*- C++ -*-
2
 * File: sonycc.cpp
3
 * Copyright (C) 2024-2025 Alex Tutubalin, LibRaw LLC
4
 *
5
   Sony YCC (small/medium lossy compressed) decoder
6
7
   Code partially backported from DNGLab's rust code
8
   DNGLab was originally created in 2021 by Daniel Vogelbacher.
9
10
LibRaw is free software; you can redistribute it and/or modify
11
it under the terms of the one of two licenses as you choose:
12
13
1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1
14
   (See file LICENSE.LGPL provided in LibRaw distribution archive for details).
15
16
2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
17
   (See file LICENSE.CDDL provided in LibRaw distribution archive for details).
18
19
 */
20
21
#include "../../internal/libraw_cxx_defs.h"
22
#include "../../internal/losslessjpeg.h"
23
#include <vector>
24
#include <algorithm>
25
26
0
#define ifp libraw_internal_data.internal_data.input
27
0
#define UD libraw_internal_data.unpacker_data
28
0
#define S imgdata.sizes
29
30
struct LibRaw_SonyYCC_Decompressor : public LibRaw_LjpegDecompressor
31
{
32
0
  LibRaw_SonyYCC_Decompressor(uint8_t *b, unsigned s): LibRaw_LjpegDecompressor(b,s){}
33
  bool decode_sony(std::vector<uint16_t> &dest, int width, int height);
34
  bool decode_sony_ljpeg_420(std::vector<uint16_t> &dest, int width, int height);
35
36
};
37
38
static
39
#ifdef _MSC_VER
40
    __forceinline
41
#else
42
    inline
43
#endif
44
void copy_yuv_420(uint16_t *out, uint32_t row, uint32_t col, uint32_t width,
45
  int32_t y1, int32_t y2, int32_t y3, int32_t y4, int32_t cb, int32_t cr)
46
0
{
47
0
  uint32_t pix1 = row * width + col;
48
0
  uint32_t pix2 = pix1 + 3;
49
0
  uint32_t pix3 = (row + 1) * width + col;
50
0
  uint32_t pix4 = pix3 + 3;
51
52
0
  out[pix1 + 0] = uint16_t(y1);
53
0
  out[pix1 + 1] = uint16_t(cb);
54
0
  out[pix1 + 2] = uint16_t(cr);
55
0
  out[pix2 + 0] = uint16_t(y2);
56
0
  out[pix2 + 1] = uint16_t(cb);
57
0
  out[pix2 + 2] = uint16_t(cr);
58
0
  out[pix3 + 0] = uint16_t(y3);
59
0
  out[pix3 + 1] = uint16_t(cb);
60
0
  out[pix3 + 2] = uint16_t(cr);
61
0
  out[pix4 + 0] = uint16_t(y4);
62
0
  out[pix4 + 1] = uint16_t(cb);
63
0
  out[pix4 + 2] = uint16_t(cr);
64
0
}
65
66
67
bool LibRaw_SonyYCC_Decompressor::decode_sony_ljpeg_420(std::vector<uint16_t> &_dest, int width, int height)
68
0
{
69
0
  if (sof.width * 3 != unsigned(width) || sof.height != unsigned(height))
70
0
    return false;
71
0
  if (width % 2 || width % 6 || height % 2)
72
0
    return false;
73
0
  if (_dest.size() < size_t(width*height))
74
0
    return false;
75
76
0
  HuffTable &huff1 = dhts[sof.components[0].dc_tbl];
77
0
  HuffTable &huff2 = dhts[sof.components[1].dc_tbl];
78
0
  HuffTable &huff3 = dhts[sof.components[2].dc_tbl];
79
80
0
  if (!huff1.initialized || !huff2.initialized || !huff3.initialized)
81
0
    return false;
82
83
0
  BitPumpJpeg pump(buffer);
84
85
0
  int32_t base = 1 << (sof.precision - point_transform - 1);
86
0
  int32_t y1 = base + huff1.decode(pump);
87
0
  int32_t y2 = y1 + huff1.decode(pump);
88
0
  int32_t y3 = y1 + huff1.decode(pump); 
89
0
  int32_t y4 = y3 + huff1.decode(pump);
90
91
0
  int32_t cb = base + huff2.decode(pump);
92
0
  int32_t cr = base + huff3.decode(pump);
93
94
0
  uint16_t *dest = _dest.data();
95
0
  copy_yuv_420(dest, 0, 0, width, y1, y2, y3, y4, cb, cr);
96
97
0
  for (int32_t row = 0; row < height; row += 2)
98
0
  {
99
0
    int32_t startcol = row == 0 ? 6 : 0;
100
0
    for (int32_t col = startcol; col < width; col += 6)
101
0
    {
102
0
      int32_t py1, py3, pcb, pcr;
103
0
      if (col == 0)
104
0
      {
105
0
        uint32_t pos = (row - 2) * width; 
106
0
        py1 = dest[pos];
107
0
        py3 = 0;
108
0
        pcb = dest[pos + 1];
109
0
        pcr = dest[pos + 2];
110
0
      }
111
0
      else
112
0
      {
113
0
        uint32_t pos1 = row * width + col - 3;       
114
0
        uint32_t pos3 = (row + 1) * width + col - 3; 
115
0
        py1 = dest[pos1];
116
0
        py3 = dest[pos3];
117
0
        pcb = dest[pos1 + 1];
118
0
        pcr = dest[pos1 + 2];
119
0
      };
120
0
      y1 = py1 + huff1.decode(pump);
121
0
      y2 = y1 + huff1.decode(pump);
122
0
      y3 = ((col == 0) ? y1 : py3) + huff1.decode(pump);
123
0
      y4 = y3 + huff1.decode(pump);
124
0
      cb = pcb + huff2.decode(pump);
125
0
      cr = pcr + huff3.decode(pump);
126
0
      copy_yuv_420(dest, row, col, width, y1, y2, y3, y4, cb, cr);
127
0
    }
128
0
  }
129
0
  return true;
130
0
}
131
132
bool LibRaw_SonyYCC_Decompressor::decode_sony(std::vector<uint16_t> &dest, int width, int height)
133
0
{
134
0
  if (sof.components[0].subsample_h == 2 && sof.components[0].subsample_v == 2)
135
0
  {
136
0
    return decode_sony_ljpeg_420(dest, width, height);
137
0
  }
138
0
  else if (sof.components[0].subsample_h == 2 && sof.components[0].subsample_v == 1)
139
0
    return decode_ljpeg_422(dest, width, height);
140
0
  else
141
0
    return false;
142
0
}
143
144
static
145
#ifdef _MSC_VER
146
    __forceinline
147
#else
148
    inline
149
#endif
150
void copy_ycc(ushort dst[][4], int rawwidth, int rawheight, int destrow0, int destcol0, ushort *src,
151
  int srcwidth, // full array width is 3x
152
  int srcheight, int steph, int stepv)
153
0
{
154
0
  if (steph < 2 && stepv < 2)
155
0
  {
156
0
    for (int tilerow = 0; tilerow < srcheight && destrow0 + tilerow < rawheight; tilerow++)
157
0
    {
158
0
      ushort(*destrow)[4] = &dst[(destrow0 + tilerow) * rawwidth + destcol0];
159
0
      for (int tilecol = 0; tilecol < srcwidth && tilecol + destcol0 < rawwidth; tilecol++)
160
0
      {
161
0
        int pix = (tilerow * srcwidth + tilecol) * 3;
162
0
        destrow[tilecol][0] = src[pix];
163
0
        destrow[tilecol][1] = src[pix + 1] > 8192 ? src[pix + 1] - 8192 : 0;
164
0
        destrow[tilecol][2] = src[pix + 2] > 8192 ? src[pix + 2] - 8192 : 0;
165
0
      }
166
0
    }
167
0
  }
168
0
  else
169
0
  {
170
0
      for (int tilerow = 0; tilerow < srcheight && destrow0 + tilerow < rawheight; tilerow++)
171
0
      {
172
0
        int destrow = destrow0 + tilerow;
173
0
        ushort(*dest)[4] = &dst[destrow * rawwidth + destcol0];
174
0
        for (int tilecol = 0; tilecol < srcwidth && tilecol + destcol0 < rawwidth; tilecol++)
175
0
        {
176
0
          int pix = (tilerow * srcwidth + tilecol) * 3;
177
0
      int destcol = destcol0 + tilecol;
178
0
          dest[tilecol][0] = src[pix];
179
0
      if((destrow%stepv) == 0 && (destcol%steph)==0)
180
0
      {
181
0
            dest[tilecol][1] = src[pix + 1] > 8192 ? src[pix + 1] - 8192 : 0;
182
0
            dest[tilecol][2] = src[pix + 2] > 8192 ? src[pix + 2] - 8192 : 0;
183
0
      }
184
0
        }
185
0
      }
186
187
0
  }
188
0
}
189
190
static
191
#ifdef _MSC_VER
192
    __forceinline
193
#else
194
    inline
195
#endif
196
uint16_t _lim16bit(float q)
197
0
{
198
0
  if (q < 0.f)
199
0
    q = 0.f;
200
0
  else if (q > 65535.f)
201
0
    q = 65535.f;
202
0
  return uint16_t(uint32_t(q));
203
0
}
204
205
static
206
#ifdef _MSC_VER
207
    __forceinline
208
#else
209
    inline
210
#endif
211
void ycc2rgb(uint16_t dst[][4], int rawwidth, int rawheight, int destrow0, int destcol0, ushort *src,
212
                     int srcwidth, // full array width is 3x
213
                     int srcheight)
214
0
{
215
0
  const ushort cdelta = 16383;
216
0
  for (int tilerow = 0; tilerow < srcheight && destrow0 + tilerow < rawheight; tilerow++)
217
0
  {
218
0
    ushort(*destrow)[4] = &dst[(destrow0 + tilerow) * rawwidth + destcol0];
219
0
    for (int tilecol = 0; tilecol < srcwidth && tilecol + destcol0 < rawwidth; tilecol++)
220
0
    {
221
0
      int pix = (tilerow * srcwidth + tilecol) * 3;
222
0
      float Y = float(src[pix]);
223
0
    float Cb = float(src[pix + 1] - cdelta);
224
0
      float Cr = float(src[pix + 2] - cdelta);
225
0
    float R = Y + 1.40200f * Cr;
226
0
    float G = Y - 0.34414f * Cb - 0.71414f * Cr;
227
0
    float B = Y + 1.77200f * Cb;
228
0
    destrow[tilecol][0] = _lim16bit(R);
229
0
    destrow[tilecol][1] = _lim16bit(G);
230
0
    destrow[tilecol][2] = _lim16bit(B);
231
0
    }
232
0
  }
233
0
}
234
235
void LibRaw::sony_ycbcr_load_raw()
236
0
{
237
0
  if (!imgdata.image)
238
0
    throw LIBRAW_EXCEPTION_IO_CORRUPT;
239
  // Sony YCC RAWs are always tiled
240
0
  if (UD.tile_width < 1 || UD.tile_width > S.raw_width)
241
0
    throw LIBRAW_EXCEPTION_IO_CORRUPT;
242
0
  if (UD.tile_length < 1 || UD.tile_length > S.raw_height)
243
0
    throw LIBRAW_EXCEPTION_IO_CORRUPT;
244
245
  // calculate tile count
246
0
  int tile_w = (S.raw_width + UD.tile_width - 1) / UD.tile_width;
247
0
  int tile_h = (S.raw_height + UD.tile_length - 1) / UD.tile_length;
248
0
  int tiles = tile_w * tile_h;
249
0
  if (tiles < 1 || tiles > 1024)
250
0
    throw LIBRAW_EXCEPTION_IO_CORRUPT;
251
252
0
  INT64 fsize = ifp->size();
253
0
  std::vector<INT64> toffsets(tiles);
254
0
  ifp->seek(UD.data_offset, SEEK_SET); // We're already here, but to make sure
255
0
  for (int i = 0; i < tiles; i++)
256
0
    toffsets[i] = get4();
257
258
0
  std::vector<unsigned> tlengths(tiles);
259
0
  ifp->seek(UD.data_size, SEEK_SET);
260
0
  for (int i = 0; i < tiles; i++)
261
0
  {
262
0
    tlengths[i] = get4();
263
0
    if(toffsets[i]+tlengths[i] > fsize)
264
0
        throw LIBRAW_EXCEPTION_IO_CORRUPT;
265
0
  }
266
267
0
  INT64 tilesize = INT64(UD.tile_width) * INT64(UD.tile_length) * 3LL;
268
269
0
  if (tilesize > 2048LL * 1024LL * 1024LL) // Sony tiles are 512x512, so >2Gb decoded tile is definitely broken file.
270
0
    throw LIBRAW_EXCEPTION_IO_CORRUPT;
271
272
0
  if (tilesize > INT64(imgdata.rawparams.max_raw_memory_mb) * INT64(1024 * 1024)) // check against memory size limit
273
0
    throw LIBRAW_EXCEPTION_ALLOC;
274
275
0
  unsigned maxcomprlen = *std::max_element(tlengths.begin(), tlengths.end());
276
277
0
  if (INT64(maxcomprlen) > 1024LL * 1024LL * 1024LL) // Sony tiles are 512x512, so >1Gb compressed tile is definitely broken file.
278
0
    throw LIBRAW_EXCEPTION_IO_CORRUPT;
279
280
0
  if (INT64(maxcomprlen) > INT64(imgdata.rawparams.max_raw_memory_mb) * INT64(1024 * 1024)) // check against memory size limit
281
0
    throw LIBRAW_EXCEPTION_ALLOC;
282
283
0
  std::vector<uint8_t> iobuffer(maxcomprlen+4u); // Extra four bytes to ensure LJPEG byte stream marker search is ok and bit buffer fast lookups are also ok
284
0
  std::vector<uint16_t> tilebuffer;
285
0
  for (int tile = 0; tile < tiles; tile++)
286
0
  {
287
0
    ifp->seek(toffsets[tile],SEEK_SET);
288
0
    int readed =  ifp->read(iobuffer.data(), 1, tlengths[tile]);
289
0
    if(unsigned(readed) != tlengths[tile])
290
0
        throw LIBRAW_EXCEPTION_IO_EOF;
291
0
    LibRaw_SonyYCC_Decompressor dec(iobuffer.data(), readed);
292
0
    if(dec.sof.cps != 3) // !YUV
293
0
        throw LIBRAW_EXCEPTION_IO_CORRUPT;
294
0
    if(dec.state != LibRaw_LjpegDecompressor::State::OK)
295
0
        throw LIBRAW_EXCEPTION_IO_CORRUPT;
296
297
0
    unsigned tiledatatsize = UD.tile_width * UD.tile_length * 3;
298
0
    if (tilebuffer.size() < tiledatatsize)
299
0
      tilebuffer.resize(tiledatatsize);
300
301
0
    if(!dec.decode_sony(tilebuffer, UD.tile_width * 3, UD.tile_length))
302
0
        throw LIBRAW_EXCEPTION_IO_CORRUPT;
303
304
0
    int tilerow = tile / tile_w;
305
0
    int tilecol = tile % tile_w;
306
0
    if (imgdata.rawparams.specials & LIBRAW_RAWSPECIAL_SRAW_NO_RGB)
307
0
    {
308
0
      if(imgdata.rawparams.specials & LIBRAW_RAWSPECIAL_SRAW_NO_INTERPOLATE)
309
0
            copy_ycc(imgdata.image, S.raw_width, S.raw_height, tilerow * UD.tile_length, tilecol * UD.tile_width,
310
0
                   tilebuffer.data(), UD.tile_width, UD.tile_length, dec.sof.components[0].subsample_h, dec.sof.components[0].subsample_v);
311
0
      else
312
0
            copy_ycc(imgdata.image, S.raw_width, S.raw_height, tilerow * UD.tile_length, tilecol * UD.tile_width,
313
0
                     tilebuffer.data(), UD.tile_width, UD.tile_length, 1, 1);
314
0
    }
315
0
    else
316
0
    ycc2rgb(imgdata.image, S.raw_width, S.raw_height, tilerow * UD.tile_length, tilecol * UD.tile_width,
317
0
        tilebuffer.data(), UD.tile_width, UD.tile_length);
318
0
  }
319
320
0
  for (int i = 0; i < 6; i++)
321
0
    imgdata.color.cblack[i] = 0;
322
323
0
  if (imgdata.rawparams.specials & LIBRAW_RAWSPECIAL_SRAW_NO_RGB)
324
0
  {
325
0
    imgdata.color.maximum = 18091; // estimated on over-exposed ISO100 frame
326
0
    imgdata.color.black = 0;
327
0
  }
328
0
  else
329
0
  {
330
0
    imgdata.color.maximum = 17536; // estimated on over-exposed ISO100 frame
331
0
    imgdata.color.black = 1024;
332
0
  }
333
0
}