Coverage Report

Created: 2025-10-13 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebp/src/dec/alpha_dec.c
Line
Count
Source
1
// Copyright 2011 Google Inc. All Rights Reserved.
2
//
3
// Use of this source code is governed by a BSD-style license
4
// that can be found in the COPYING file in the root of the source
5
// tree. An additional intellectual property rights grant can be found
6
// in the file PATENTS. All contributing project authors may
7
// be found in the AUTHORS file in the root of the source tree.
8
// -----------------------------------------------------------------------------
9
//
10
// Alpha-plane decompression.
11
//
12
// Author: Skal (pascal.massimino@gmail.com)
13
14
#include <assert.h>
15
#include <stdlib.h>
16
17
#include "src/dec/alphai_dec.h"
18
#include "src/dec/vp8_dec.h"
19
#include "src/dec/vp8i_dec.h"
20
#include "src/dec/vp8li_dec.h"
21
#include "src/dec/webpi_dec.h"
22
#include "src/dsp/dsp.h"
23
#include "src/utils/quant_levels_dec_utils.h"
24
#include "src/utils/utils.h"
25
#include "src/webp/decode.h"
26
#include "src/webp/format_constants.h"
27
#include "src/webp/types.h"
28
29
//------------------------------------------------------------------------------
30
// ALPHDecoder object.
31
32
// Allocates a new alpha decoder instance.
33
2.82k
WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) {
34
2.82k
  ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
35
2.82k
  return dec;
36
2.82k
}
37
38
// Clears and deallocates an alpha decoder instance.
39
41.7k
static void ALPHDelete(ALPHDecoder* const dec) {
40
41.7k
  if (dec != NULL) {
41
2.82k
    VP8LDelete(dec->vp8l_dec);
42
2.82k
    dec->vp8l_dec = NULL;
43
2.82k
    WebPSafeFree(dec);
44
2.82k
  }
45
41.7k
}
46
47
//------------------------------------------------------------------------------
48
// Decoding.
49
50
// Initialize alpha decoding by parsing the alpha header and decoding the image
51
// header for alpha data stored using lossless compression.
52
// Returns false in case of error in alpha header (data too short, invalid
53
// compression method or filter, error in lossless header data etc).
54
WEBP_NODISCARD static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
55
                                   size_t data_size, const VP8Io* const src_io,
56
2.82k
                                   uint8_t* output) {
57
2.82k
  int ok = 0;
58
2.82k
  const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
59
2.82k
  const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
60
2.82k
  int rsrv;
61
2.82k
  VP8Io* const io = &dec->io;
62
63
2.82k
  assert(data != NULL && output != NULL && src_io != NULL);
64
65
2.82k
  VP8FiltersInit();
66
2.82k
  dec->output = output;
67
2.82k
  dec->width = src_io->width;
68
2.82k
  dec->height = src_io->height;
69
2.82k
  assert(dec->width > 0 && dec->height > 0);
70
71
2.82k
  if (data_size <= ALPHA_HEADER_LEN) {
72
0
    return 0;
73
0
  }
74
75
2.82k
  dec->method = (data[0] >> 0) & 0x03;
76
2.82k
  dec->filter = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03);
77
2.82k
  dec->pre_processing = (data[0] >> 4) & 0x03;
78
2.82k
  rsrv = (data[0] >> 6) & 0x03;
79
2.82k
  if (dec->method < ALPHA_NO_COMPRESSION ||
80
2.82k
      dec->method > ALPHA_LOSSLESS_COMPRESSION ||
81
2.82k
      dec->filter >= WEBP_FILTER_LAST ||
82
2.82k
      dec->pre_processing > ALPHA_PREPROCESSED_LEVELS || rsrv != 0) {
83
0
    return 0;
84
0
  }
85
86
  // Copy the necessary parameters from src_io to io
87
2.82k
  if (!VP8InitIo(io)) {
88
0
    return 0;
89
0
  }
90
2.82k
  WebPInitCustomIo(NULL, io);
91
2.82k
  io->opaque = dec;
92
2.82k
  io->width = src_io->width;
93
2.82k
  io->height = src_io->height;
94
95
2.82k
  io->use_cropping = src_io->use_cropping;
96
2.82k
  io->crop_left = src_io->crop_left;
97
2.82k
  io->crop_right = src_io->crop_right;
98
2.82k
  io->crop_top = src_io->crop_top;
99
2.82k
  io->crop_bottom = src_io->crop_bottom;
100
  // No need to copy the scaling parameters.
101
102
2.82k
  if (dec->method == ALPHA_NO_COMPRESSION) {
103
979
    const size_t alpha_decoded_size = dec->width * dec->height;
104
979
    ok = (alpha_data_size >= alpha_decoded_size);
105
1.84k
  } else {
106
1.84k
    assert(dec->method == ALPHA_LOSSLESS_COMPRESSION);
107
1.84k
    ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size);
108
1.84k
  }
109
110
2.82k
  return ok;
111
2.82k
}
112
113
// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha
114
// starting from row number 'row'. It assumes that rows up to (row - 1) have
115
// already been decoded.
116
// Returns false in case of bitstream error.
117
WEBP_NODISCARD static int ALPHDecode(VP8Decoder* const dec, int row,
118
7.43k
                                     int num_rows) {
119
7.43k
  ALPHDecoder* const alph_dec = dec->alph_dec;
120
7.43k
  const int width = alph_dec->width;
121
7.43k
  const int height = alph_dec->io.crop_bottom;
122
7.43k
  if (alph_dec->method == ALPHA_NO_COMPRESSION) {
123
1.32k
    int y;
124
1.32k
    const uint8_t* prev_line = dec->alpha_prev_line;
125
1.32k
    const uint8_t* deltas = dec->alpha_data + ALPHA_HEADER_LEN + row * width;
126
1.32k
    uint8_t* dst = dec->alpha_plane + row * width;
127
1.32k
    assert(deltas <= &dec->alpha_data[dec->alpha_data_size]);
128
1.32k
    assert(WebPUnfilters[alph_dec->filter] != NULL);
129
311k
    for (y = 0; y < num_rows; ++y) {
130
309k
      WebPUnfilters[alph_dec->filter](prev_line, deltas, dst, width);
131
309k
      prev_line = dst;
132
309k
      dst += width;
133
309k
      deltas += width;
134
309k
    }
135
1.32k
    dec->alpha_prev_line = prev_line;
136
6.10k
  } else {  // alph_dec->method == ALPHA_LOSSLESS_COMPRESSION
137
6.10k
    assert(alph_dec->vp8l_dec != NULL);
138
6.10k
    if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
139
0
      return 0;
140
0
    }
141
6.10k
  }
142
143
7.43k
  if (row + num_rows >= height) {
144
2.82k
    dec->is_alpha_decoded = 1;
145
2.82k
  }
146
7.43k
  return 1;
147
7.43k
}
148
149
WEBP_NODISCARD static int AllocateAlphaPlane(VP8Decoder* const dec,
150
2.82k
                                             const VP8Io* const io) {
151
2.82k
  const int stride = io->width;
152
2.82k
  const int height = io->crop_bottom;
153
2.82k
  const uint64_t alpha_size = (uint64_t)stride * height;
154
2.82k
  assert(dec->alpha_plane_mem == NULL);
155
2.82k
  dec->alpha_plane_mem =
156
2.82k
      (uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane));
157
2.82k
  if (dec->alpha_plane_mem == NULL) {
158
0
    return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
159
0
                       "Alpha decoder initialization failed.");
160
0
  }
161
2.82k
  dec->alpha_plane = dec->alpha_plane_mem;
162
2.82k
  dec->alpha_prev_line = NULL;
163
2.82k
  return 1;
164
2.82k
}
165
166
38.9k
void WebPDeallocateAlphaMemory(VP8Decoder* const dec) {
167
38.9k
  assert(dec != NULL);
168
38.9k
  WebPSafeFree(dec->alpha_plane_mem);
169
38.9k
  dec->alpha_plane_mem = NULL;
170
38.9k
  dec->alpha_plane = NULL;
171
38.9k
  ALPHDelete(dec->alph_dec);
172
38.9k
  dec->alph_dec = NULL;
173
38.9k
}
174
175
//------------------------------------------------------------------------------
176
// Main entry point.
177
178
WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
179
                                                     const VP8Io* const io,
180
57.1k
                                                     int row, int num_rows) {
181
57.1k
  const int width = io->width;
182
57.1k
  const int height = io->crop_bottom;
183
184
57.1k
  assert(dec != NULL && io != NULL);
185
186
57.1k
  if (row < 0 || num_rows <= 0 || row + num_rows > height) {
187
0
    return NULL;
188
0
  }
189
190
57.1k
  if (!dec->is_alpha_decoded) {
191
7.43k
    if (dec->alph_dec == NULL) {  // Initialize decoder.
192
2.82k
      dec->alph_dec = ALPHNew();
193
2.82k
      if (dec->alph_dec == NULL) {
194
0
        VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
195
0
                    "Alpha decoder initialization failed.");
196
0
        return NULL;
197
0
      }
198
2.82k
      if (!AllocateAlphaPlane(dec, io)) goto Error;
199
2.82k
      if (!ALPHInit(dec->alph_dec, dec->alpha_data, dec->alpha_data_size, io,
200
2.82k
                    dec->alpha_plane)) {
201
0
        VP8LDecoder* const vp8l_dec = dec->alph_dec->vp8l_dec;
202
0
        VP8SetError(
203
0
            dec,
204
0
            (vp8l_dec == NULL) ? VP8_STATUS_OUT_OF_MEMORY : vp8l_dec->status,
205
0
            "Alpha decoder initialization failed.");
206
0
        goto Error;
207
0
      }
208
      // if we allowed use of alpha dithering, check whether it's needed at all
209
2.82k
      if (dec->alph_dec->pre_processing != ALPHA_PREPROCESSED_LEVELS) {
210
505
        dec->alpha_dithering = 0;  // disable dithering
211
2.31k
      } else {
212
2.31k
        num_rows = height - row;  // decode everything in one pass
213
2.31k
      }
214
2.82k
    }
215
216
7.43k
    assert(dec->alph_dec != NULL);
217
7.43k
    assert(row + num_rows <= height);
218
7.43k
    if (!ALPHDecode(dec, row, num_rows)) goto Error;
219
220
7.43k
    if (dec->is_alpha_decoded) {  // finished?
221
2.82k
      ALPHDelete(dec->alph_dec);
222
2.82k
      dec->alph_dec = NULL;
223
2.82k
      if (dec->alpha_dithering > 0) {
224
0
        uint8_t* const alpha =
225
0
            dec->alpha_plane + io->crop_top * width + io->crop_left;
226
0
        if (!WebPDequantizeLevels(alpha, io->crop_right - io->crop_left,
227
0
                                  io->crop_bottom - io->crop_top, width,
228
0
                                  dec->alpha_dithering)) {
229
0
          goto Error;
230
0
        }
231
0
      }
232
2.82k
    }
233
7.43k
  }
234
235
  // Return a pointer to the current decoded row.
236
57.1k
  return dec->alpha_plane + row * width;
237
238
0
Error:
239
0
  WebPDeallocateAlphaMemory(dec);
240
  return NULL;
241
57.1k
}