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