Coverage Report

Created: 2026-09-28 07:02

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
#include <string.h>
17
18
#include "src/dec/alphai_dec.h"
19
#include "src/dec/vp8_dec.h"
20
#include "src/dec/vp8i_dec.h"
21
#include "src/dec/vp8li_dec.h"
22
#include "src/dec/webpi_dec.h"
23
#include "src/dsp/dsp.h"
24
#include "src/utils/quant_levels_dec_utils.h"
25
#include "src/utils/utils.h"
26
#include "src/webp/decode.h"
27
#include "src/webp/format_constants.h"
28
#include "src/webp/types.h"
29
30
WEBP_ASSUME_UNSAFE_INDEXABLE_ABI
31
32
//------------------------------------------------------------------------------
33
// ALPHDecoder object.
34
35
// Allocates a new alpha decoder instance.
36
0
WEBP_NODISCARD static ALPHDecoder* ALPHNew(void) {
37
0
  ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
38
0
  return dec;
39
0
}
40
41
// Clears and deallocates an alpha decoder instance.
42
3.21k
static void ALPHDelete(ALPHDecoder* const dec) {
43
3.21k
  if (dec != NULL) {
44
0
    VP8LDelete(dec->vp8l_dec);
45
0
    dec->vp8l_dec = NULL;
46
0
    WebPSafeFree(dec);
47
0
  }
48
3.21k
}
49
50
//------------------------------------------------------------------------------
51
// Decoding.
52
53
// Initialize alpha decoding by parsing the alpha header and decoding the image
54
// header for alpha data stored using lossless compression.
55
// Returns VP8_STATUS_OK on success, the reason for the failure otherwise
56
// (data too short, invalid compression method or filter, error in the
57
// lossless header data, etc.).
58
WEBP_NODISCARD static VP8StatusCode ALPHInit(ALPHDecoder* const dec,
59
                                             const uint8_t* data,
60
                                             size_t data_size,
61
                                             const VP8Io* const src_io,
62
0
                                             uint8_t* output) {
63
0
  VP8StatusCode status;
64
0
  const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
65
0
  int rsrv;
66
0
  VP8Io* const io = &dec->io;
67
68
0
  assert(data != NULL && output != NULL && src_io != NULL);
69
70
0
  VP8FiltersInit();
71
0
  dec->output = output;
72
0
  dec->width = src_io->width;
73
0
  dec->height = src_io->height;
74
0
  assert(dec->width > 0 && dec->height > 0);
75
76
0
  if (data_size <= ALPHA_HEADER_LEN) {
77
0
    return VP8_STATUS_BITSTREAM_ERROR;
78
0
  }
79
80
0
  dec->method = (data[0] >> 0) & 0x03;
81
0
  dec->filter = (WEBP_FILTER_TYPE)((data[0] >> 2) & 0x03);
82
0
  dec->pre_processing = (data[0] >> 4) & 0x03;
83
0
  rsrv = (data[0] >> 6) & 0x03;
84
0
  if (dec->method < ALPHA_NO_COMPRESSION ||
85
0
      dec->method > ALPHA_LOSSLESS_COMPRESSION ||
86
0
      dec->filter >= WEBP_FILTER_LAST ||
87
0
      dec->pre_processing > ALPHA_PREPROCESSED_LEVELS || rsrv != 0) {
88
0
    return VP8_STATUS_BITSTREAM_ERROR;
89
0
  }
90
91
  // Copy the necessary parameters from src_io to io
92
0
  if (!VP8InitIo(io)) {
93
0
    return VP8_STATUS_INVALID_PARAM;
94
0
  }
95
  // Don't plug the I/O functions: they expect a WebPDecParams* opaque.
96
0
  io->opaque = dec;
97
0
  io->width = src_io->width;
98
0
  io->height = src_io->height;
99
100
0
  io->use_cropping = src_io->use_cropping;
101
0
  io->crop_left = src_io->crop_left;
102
0
  io->crop_right = src_io->crop_right;
103
0
  io->crop_top = src_io->crop_top;
104
0
  io->crop_bottom = src_io->crop_bottom;
105
  // No need to copy the scaling parameters.
106
107
0
  {
108
0
    const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
109
0
    if (dec->method == ALPHA_NO_COMPRESSION) {
110
0
      const size_t alpha_decoded_size = dec->width * dec->height;
111
0
      status = (alpha_data_size >= alpha_decoded_size)
112
0
                   ? VP8_STATUS_OK
113
0
                   : VP8_STATUS_BITSTREAM_ERROR;
114
0
    } else {
115
0
      assert(dec->method == ALPHA_LOSSLESS_COMPRESSION);
116
0
      {
117
0
        const uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha_data =
118
0
            WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(const uint8_t*, alpha_data,
119
0
                                             alpha_data_size);
120
0
        status =
121
0
            VP8LDecodeAlphaHeader(dec, bounded_alpha_data, alpha_data_size);
122
0
      }
123
0
    }
124
0
  }
125
126
0
  return status;
127
0
}
128
129
1.62k
int WebPGetAlphaWindowRows(const VP8Decoder* const dec, const VP8Io* const io) {
130
1.62k
  const int width = io->width;
131
  // Alpha rows are always decoded starting from row 0 (for spatial filtering
132
  // and lossless stream dependencies) and decoding stops at io->crop_bottom
133
  // (which equals io->height when cropping is not used).
134
1.62k
  const int height = io->crop_bottom;
135
1.62k
  if (dec->alpha_data == NULL) return 0;
136
  // For each non-last macroblock row (16 pixel rows), FinishRow() in
137
  // frame_dec.c holds back up to 8 bottom pixel rows (kFilterExtraRows[] for
138
  // complex filtering) until the next macroblock row is decoded. On the last
139
  // macroblock row, no bottom rows are held back, so FinishRow() requests the 8
140
  // pixel rows held back from the previous macroblock row plus all 16 pixel
141
  // rows of the last macroblock row (24 pixel rows total), while retaining 1
142
  // preceding row (row - 1) for spatial unfiltering and fancy upsampling.
143
0
  return (dec->alpha_dithering > 0)
144
0
             ? height
145
0
             : VP8LGetWindowRows(width, height, /*max_history_rows=*/25);
146
1.62k
}
147
148
void WebPShiftAlphaWindow(ALPHDecoder* const alph_dec, int current_end_row,
149
0
                          int last_row) {
150
0
  const int width = alph_dec->width;
151
0
  int min_keep_row = alph_dec->min_needed_row;
152
0
  ptrdiff_t shift_pixels;
153
0
  if (last_row - alph_dec->output_start_row <= alph_dec->num_output_rows) {
154
0
    return;
155
0
  }
156
0
  if (alph_dec->prev_line != NULL) {
157
0
    const int prev_row =
158
0
        alph_dec->output_start_row +
159
0
        (int)((alph_dec->prev_line - alph_dec->output) / width);
160
0
    if (prev_row < min_keep_row) min_keep_row = prev_row;
161
0
  }
162
0
  if (min_keep_row - alph_dec->output_start_row >= alph_dec->num_output_rows) {
163
0
    alph_dec->output_start_row = min_keep_row;
164
0
    return;
165
0
  }
166
0
  shift_pixels = VP8LShiftWindowBuffer(
167
0
      alph_dec->output, width, sizeof(*alph_dec->output), min_keep_row,
168
0
      GetAlphaWindowRowOffset(alph_dec, current_end_row),
169
0
      &alph_dec->output_start_row);
170
0
  if (shift_pixels > 0 && alph_dec->prev_line != NULL) {
171
0
    alph_dec->prev_line -= shift_pixels;
172
0
  }
173
0
}
174
175
// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha
176
// starting from row number 'row'. It assumes that rows up to (row - 1) have
177
// already been decoded.
178
// Returns false in case of bitstream error.
179
WEBP_NODISCARD static int ALPHDecode(VP8Decoder* const dec, int row,
180
0
                                     int num_rows) {
181
0
  ALPHDecoder* const alph_dec = dec->alph_dec;
182
0
  const int width = alph_dec->width;
183
0
  const int height = alph_dec->io.crop_bottom;
184
  // Fancy chroma upsampling looks 1 row back into alpha (GetAlphaSourceRow).
185
0
  alph_dec->min_needed_row = (row > 0) ? (row - 1) : 0;
186
0
  if (alph_dec->method == ALPHA_NO_COMPRESSION) {
187
0
    int y;
188
0
    const uint8_t* prev_line;
189
0
    const uint8_t* deltas = dec->alpha_data + ALPHA_HEADER_LEN + row * width;
190
0
    uint8_t* dst;
191
0
    WebPShiftAlphaWindow(alph_dec, row, row + num_rows);
192
0
    prev_line = alph_dec->prev_line;
193
0
    dst = dec->alpha_plane + GetAlphaWindowRowOffset(alph_dec, row);
194
0
    assert(deltas <= &dec->alpha_data[dec->alpha_data_size]);
195
0
    assert(WebPUnfilters[alph_dec->filter] != NULL);
196
0
    for (y = 0; y < num_rows; ++y) {
197
0
      WebPUnfilters[alph_dec->filter](prev_line, deltas, dst, width);
198
0
      prev_line = dst;
199
0
      dst += width;
200
0
      deltas += width;
201
0
    }
202
0
    alph_dec->prev_line = prev_line;
203
0
  } else {  // alph_dec->method == ALPHA_LOSSLESS_COMPRESSION
204
0
    assert(alph_dec->vp8l_dec != NULL);
205
0
    if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
206
      // SUSPENDED means truncated, but the ALPH chunk is whole by now.
207
0
      const VP8StatusCode status = alph_dec->vp8l_dec->status;
208
0
      return VP8SetError(dec,
209
0
                         (status == VP8_STATUS_SUSPENDED)
210
0
                             ? VP8_STATUS_BITSTREAM_ERROR
211
0
                             : status,
212
0
                         "Could not decode alpha data.");
213
0
    }
214
0
  }
215
216
0
  if (row + num_rows >= height) {
217
0
    dec->is_alpha_decoded = 1;
218
0
  }
219
0
  return 1;
220
0
}
221
222
3.21k
void WebPDeallocateAlphaMemory(VP8Decoder* const dec) {
223
3.21k
  assert(dec != NULL);
224
3.21k
  dec->alpha_plane = NULL;
225
3.21k
  ALPHDelete(dec->alph_dec);
226
3.21k
  dec->alph_dec = NULL;
227
3.21k
}
228
229
//------------------------------------------------------------------------------
230
// Main entry point.
231
232
WEBP_NODISCARD const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
233
                                                     const VP8Io* const io,
234
0
                                                     int row, int num_rows) {
235
0
  const int width = io->width;
236
0
  const int height = io->crop_bottom;
237
0
  int start_row = 0;
238
239
0
  assert(dec != NULL && io != NULL);
240
241
0
  if (row < 0 || num_rows <= 0 || row + num_rows > height) {
242
0
    return NULL;
243
0
  }
244
245
0
  if (!dec->is_alpha_decoded) {
246
0
    if (dec->alph_dec == NULL) {  // Initialize decoder.
247
0
      assert(dec->alpha_plane != NULL);
248
0
      dec->alph_dec = ALPHNew();
249
0
      if (dec->alph_dec == NULL) {
250
0
        VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
251
0
                    "Alpha decoder initialization failed.");
252
0
        return NULL;
253
0
      }
254
0
      dec->alph_dec->num_output_rows = WebPGetAlphaWindowRows(dec, io);
255
0
      {
256
0
        const VP8StatusCode status =
257
0
            ALPHInit(dec->alph_dec, dec->alpha_data, dec->alpha_data_size, io,
258
0
                     dec->alpha_plane);
259
0
        if (status != VP8_STATUS_OK) {
260
0
          VP8SetError(dec, status, "Alpha decoder initialization failed.");
261
0
          goto Error;
262
0
        }
263
0
      }
264
0
      if (dec->alpha_dithering > 0) {
265
0
        num_rows = height - row;  // decode everything in one pass
266
0
      }
267
0
    }
268
269
0
    assert(dec->alph_dec != NULL);
270
0
    assert(row + num_rows <= height);
271
0
    if (!ALPHDecode(dec, row, num_rows)) goto Error;
272
0
    start_row = dec->alph_dec->output_start_row;
273
274
0
    if (dec->is_alpha_decoded) {  // finished?
275
0
      ALPHDelete(dec->alph_dec);
276
0
      dec->alph_dec = NULL;
277
0
      if (dec->alpha_dithering > 0) {
278
0
        uint8_t* const alpha =
279
0
            dec->alpha_plane + io->crop_top * width + io->crop_left;
280
0
        uint8_t* WEBP_BIDI_INDEXABLE const bounded_alpha =
281
0
            WEBP_UNSAFE_FORGE_BIDI_INDEXABLE(
282
0
                uint8_t*, alpha,
283
0
                (size_t)width*(io->crop_bottom - io->crop_top));
284
0
        if (!WebPDequantizeLevels(bounded_alpha, io->crop_right - io->crop_left,
285
0
                                  io->crop_bottom - io->crop_top, width,
286
0
                                  dec->alpha_dithering)) {
287
          // Dimensions and strength are checked upstream: only the scratch
288
          // buffer can fail.
289
0
          VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
290
0
                      "no memory for alpha dithering.");
291
0
          goto Error;
292
0
        }
293
0
      }
294
0
    }
295
0
  }
296
297
  // Return a pointer to the current decoded row.
298
0
  return dec->alpha_plane + (ptrdiff_t)(row - start_row) * width;
299
300
0
Error:
301
0
  WebPDeallocateAlphaMemory(dec);
302
  return NULL;
303
0
}