Coverage Report

Created: 2026-10-17 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebp/src/enc/picture_enc.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
// WebPPicture class basis
11
//
12
// Author: Skal (pascal.massimino@gmail.com)
13
14
#include <assert.h>
15
#include <limits.h>
16
#include <stdlib.h>
17
#include <string.h>
18
19
#include "src/enc/vp8i_enc.h"
20
#include "src/utils/utils.h"
21
#include "src/webp/encode.h"
22
#include "src/webp/types.h"
23
24
//------------------------------------------------------------------------------
25
// WebPPicture
26
//------------------------------------------------------------------------------
27
28
static int DummyWriter(const uint8_t* data, size_t data_size,
29
0
                       const WebPPicture* const picture) {
30
  // The following are to prevent 'unused variable' error message.
31
0
  (void)data;
32
0
  (void)data_size;
33
0
  (void)picture;
34
0
  return 1;
35
0
}
36
37
498k
int WebPPictureInitInternal(WebPPicture* picture, int version) {
38
498k
  if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
39
0
    return 0;  // caller/system version mismatch!
40
0
  }
41
498k
  if (picture != NULL) {
42
498k
    memset(picture, 0, sizeof(*picture));
43
498k
    picture->writer = DummyWriter;
44
498k
    WebPEncodingSetError(picture, VP8_ENC_OK);
45
498k
  }
46
498k
  return 1;
47
498k
}
48
49
//------------------------------------------------------------------------------
50
51
#ifndef PTRDIFF_MAX
52
#define PTRDIFF_MAX ((ptrdiff_t)(~(size_t)0 >> 1))
53
#endif
54
#ifndef UINTPTR_MAX
55
#define UINTPTR_MAX ((uintptr_t)-1)
56
#endif
57
58
// Returns 1 if 'ptr' is NULL, or if its stride and memory bounds are valid for
59
// a plane of size width x height with 'step' bytes per pixel, 0 otherwise.
60
static int CheckMemoryBounds(const void* ptr, int stride, int step, int width,
61
1.71M
                             int height) {
62
1.71M
  uint64_t abs_stride, row_span, width_bytes, total_bytes;
63
1.71M
  uintptr_t addr;
64
65
1.71M
  if (ptr == NULL) return 1;
66
866k
  if (width <= 0 || height <= 0 || step <= 0) return 0;
67
866k
  if (stride > INT_MAX / step || stride < -INT_MAX / step) return 0;
68
866k
  stride *= step;
69
866k
  if (!CheckStride(stride, step, width)) return 0;
70
71
866k
  abs_stride = (stride < 0) ? (uint64_t)-(int64_t)stride : (uint64_t)stride;
72
866k
  row_span = (uint64_t)(height - 1) * abs_stride;
73
866k
  width_bytes = (uint64_t)width * (uint64_t)step;
74
75
  // Check that the total plane size fits in addressable limits (PTRDIFF_MAX).
76
866k
  if (row_span > (uint64_t)PTRDIFF_MAX - width_bytes) return 0;
77
78
866k
  total_bytes = row_span + width_bytes;
79
866k
  addr = (uintptr_t)ptr;
80
81
866k
  if (stride >= 0) {
82
866k
    if (addr > (uintptr_t)UINTPTR_MAX - (uintptr_t)total_bytes) return 0;
83
866k
  } else {
84
0
    if (addr < (uintptr_t)row_span) return 0;
85
0
    if (addr > (uintptr_t)UINTPTR_MAX - (uintptr_t)width_bytes) return 0;
86
0
  }
87
866k
  return 1;
88
866k
}
89
90
766k
int WebPValidatePicture(const WebPPicture* const picture) {
91
766k
  if (picture == NULL) return 0;
92
766k
  if (picture->width <= 0 || picture->width > INT_MAX / 4 ||
93
766k
      picture->height <= 0 || picture->height > INT_MAX / 4) {
94
0
    return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
95
0
  }
96
766k
  if (picture->colorspace != WEBP_YUV420 &&
97
112k
      picture->colorspace != WEBP_YUV420A) {
98
0
    return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION);
99
0
  }
100
  // If buffers are provided, validate their strides and memory bounds.
101
766k
  if (picture->use_argb) {
102
451k
    if (!CheckMemoryBounds(picture->argb, picture->argb_stride,
103
451k
                           (int)sizeof(*picture->argb), picture->width,
104
451k
                           picture->height)) {
105
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
106
0
    }
107
451k
  } else {
108
    // If YUV planes are provided, validate Y, U/V (subsampled by 2 in each
109
    // dimension for YUV420), and alpha strides and check that each plane fits
110
    // in memory.
111
315k
    const int uv_width = (picture->width + 1) >> 1;
112
315k
    const int uv_height = (picture->height + 1) >> 1;
113
315k
    if (!CheckMemoryBounds(picture->y, picture->y_stride, /*step=*/1,
114
315k
                           picture->width, picture->height) ||
115
315k
        !CheckMemoryBounds(picture->u, picture->uv_stride, /*step=*/1, uv_width,
116
315k
                           uv_height) ||
117
315k
        !CheckMemoryBounds(picture->v, picture->uv_stride, /*step=*/1, uv_width,
118
315k
                           uv_height) ||
119
315k
        !CheckMemoryBounds(picture->a, picture->a_stride, /*step=*/1,
120
315k
                           picture->width, picture->height)) {
121
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
122
0
    }
123
315k
  }
124
766k
  return 1;
125
766k
}
126
127
316k
int WebPValidateAllocatedPicture(const WebPPicture* const picture) {
128
316k
  if (!WebPValidatePicture(picture)) return 0;
129
316k
  if (picture->use_argb) {
130
223k
    if (picture->argb == NULL) {
131
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
132
0
    }
133
223k
  } else {
134
93.3k
    if (picture->y == NULL || picture->u == NULL || picture->v == NULL) {
135
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
136
0
    }
137
93.3k
    if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
138
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
139
0
    }
140
93.3k
  }
141
316k
  return 1;
142
316k
}
143
144
986k
static void WebPPictureResetBufferARGB(WebPPicture* const picture) {
145
986k
  picture->memory_argb_ = NULL;
146
986k
  picture->argb = NULL;
147
986k
  picture->argb_stride = 0;
148
986k
}
149
150
938k
static void WebPPictureResetBufferYUVA(WebPPicture* const picture) {
151
938k
  picture->memory_ = NULL;
152
938k
  picture->y = picture->u = picture->v = picture->a = NULL;
153
938k
  picture->y_stride = picture->uv_stride = 0;
154
938k
  picture->a_stride = 0;
155
938k
}
156
157
809k
void WebPPictureResetBuffers(WebPPicture* const picture) {
158
809k
  WebPPictureResetBufferARGB(picture);
159
809k
  WebPPictureResetBufferYUVA(picture);
160
809k
}
161
162
177k
int WebPPictureAllocARGB(WebPPicture* const picture) {
163
177k
  void* memory;
164
177k
  int width, height;
165
177k
  uint64_t argb_size;
166
167
177k
  assert(picture != NULL);
168
169
177k
  WebPSafeFree(picture->memory_argb_);
170
177k
  WebPPictureResetBufferARGB(picture);
171
172
177k
  if (!WebPValidatePicture(picture)) return 0;
173
174
177k
  width = picture->width;
175
177k
  height = picture->height;
176
177k
  argb_size = (uint64_t)width * height;
177
178
  // allocate a new buffer.
179
177k
  memory = WebPSafeMalloc(argb_size + WEBP_ALIGN_CST, sizeof(*picture->argb));
180
177k
  if (memory == NULL) {
181
207
    return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
182
207
  }
183
177k
  picture->memory_argb_ = memory;
184
177k
  picture->argb = (uint32_t*)WEBP_ALIGN(memory);
185
177k
  picture->argb_stride = width;
186
177k
  return 1;
187
177k
}
188
189
128k
int WebPPictureAllocYUVA(WebPPicture* const picture) {
190
128k
  int has_alpha, width, height, y_stride, uv_width, uv_height, uv_stride;
191
128k
  int a_width, a_stride;
192
128k
  uint64_t y_size, uv_size, a_size, total_size;
193
128k
  uint8_t* mem;
194
195
128k
  assert(picture != NULL);
196
197
128k
  WebPSafeFree(picture->memory_);
198
128k
  WebPPictureResetBufferYUVA(picture);
199
200
128k
  if (!WebPValidatePicture(picture)) return 0;
201
202
128k
  has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT;
203
128k
  width = picture->width;
204
128k
  height = picture->height;
205
128k
  y_stride = width;
206
128k
  uv_width = (width + 1) >> 1;
207
128k
  uv_height = (height + 1) >> 1;
208
128k
  uv_stride = uv_width;
209
  // alpha
210
128k
  a_width = has_alpha ? width : 0;
211
128k
  a_stride = a_width;
212
128k
  y_size = (uint64_t)y_stride * height;
213
128k
  uv_size = (uint64_t)uv_stride * uv_height;
214
128k
  a_size = (uint64_t)a_stride * height;
215
216
128k
  total_size = y_size + a_size + 2 * uv_size;
217
218
  // allocate a new buffer.
219
128k
  mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem));
220
128k
  if (mem == NULL) {
221
301
    return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
222
301
  }
223
224
  // From now on, we're in the clear, we can no longer fail...
225
128k
  picture->memory_ = (void*)mem;
226
128k
  picture->y_stride = y_stride;
227
128k
  picture->uv_stride = uv_stride;
228
128k
  picture->a_stride = a_stride;
229
230
  // TODO(skal): we could align the y/u/v planes and adjust stride.
231
128k
  picture->y = mem;
232
128k
  mem += y_size;
233
234
128k
  picture->u = mem;
235
128k
  mem += uv_size;
236
128k
  picture->v = mem;
237
128k
  mem += uv_size;
238
239
128k
  if (a_size > 0) {
240
50.6k
    picture->a = mem;
241
50.6k
    mem += a_size;
242
50.6k
  }
243
128k
  (void)mem;  // makes the static analyzer happy
244
128k
  return 1;
245
128k
}
246
247
224k
int WebPPictureAlloc(WebPPicture* picture) {
248
224k
  if (picture != NULL) {
249
224k
    WebPPictureFree(picture);  // erase previous buffer
250
251
224k
    if (!picture->use_argb) {
252
78.2k
      return WebPPictureAllocYUVA(picture);
253
146k
    } else {
254
146k
      return WebPPictureAllocARGB(picture);
255
146k
    }
256
224k
  }
257
0
  return 1;
258
224k
}
259
260
621k
void WebPPictureFree(WebPPicture* picture) {
261
621k
  if (picture != NULL) {
262
621k
    WebPSafeFree(picture->memory_);
263
621k
    WebPSafeFree(picture->memory_argb_);
264
621k
    WebPPictureResetBuffers(picture);
265
621k
  }
266
621k
}
267
268
//------------------------------------------------------------------------------
269
// WebPMemoryWriter: Write-to-memory
270
271
208k
void WebPMemoryWriterInit(WebPMemoryWriter* writer) {
272
208k
  writer->mem = NULL;
273
208k
  writer->size = 0;
274
208k
  writer->max_size = 0;
275
208k
}
276
277
int WebPMemoryWrite(const uint8_t* data, size_t data_size,
278
773k
                    const WebPPicture* picture) {
279
773k
  WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
280
773k
  uint64_t next_size;
281
773k
  if (w == NULL) {
282
0
    return 1;
283
0
  }
284
773k
  next_size = (uint64_t)w->size + data_size;
285
773k
  if (next_size > w->max_size) {
286
134k
    uint8_t* new_mem;
287
134k
    uint64_t next_max_size = 2ULL * w->max_size;
288
134k
    if (next_max_size < next_size) next_max_size = next_size;
289
134k
    if (next_max_size < 8192ULL) next_max_size = 8192ULL;
290
134k
    new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1);
291
134k
    if (new_mem == NULL) {
292
0
      return 0;
293
0
    }
294
134k
    if (w->size > 0) {
295
18.5k
      memcpy(new_mem, w->mem, w->size);
296
18.5k
    }
297
134k
    WebPSafeFree(w->mem);
298
134k
    w->mem = new_mem;
299
    // down-cast is ok, thanks to WebPSafeMalloc
300
134k
    w->max_size = (size_t)next_max_size;
301
134k
  }
302
773k
  if (data_size > 0) {
303
773k
    memcpy(w->mem + w->size, data, data_size);
304
773k
    w->size += data_size;
305
773k
  }
306
773k
  return 1;
307
773k
}
308
309
92.1k
void WebPMemoryWriterClear(WebPMemoryWriter* writer) {
310
92.1k
  if (writer != NULL) {
311
92.1k
    WebPSafeFree(writer->mem);
312
92.1k
    WebPMemoryWriterInit(writer);
313
92.1k
  }
314
92.1k
}
315
316
//------------------------------------------------------------------------------
317
// Simplest high-level calls:
318
319
typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int);
320
321
WEBP_NODISCARD static size_t Encode(const uint8_t* rgba, int width, int height,
322
                                    int stride, Importer import,
323
                                    float quality_factor, int lossless,
324
0
                                    uint8_t** output) {
325
0
  WebPPicture pic;
326
0
  WebPConfig config;
327
0
  WebPMemoryWriter wrt;
328
0
  int ok;
329
330
0
  if (output == NULL) return 0;
331
332
0
  if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) ||
333
0
      !WebPPictureInit(&pic)) {
334
0
    return 0;  // shouldn't happen, except if system installation is broken
335
0
  }
336
337
0
  config.lossless = !!lossless;
338
0
  pic.use_argb = !!lossless;
339
0
  pic.width = width;
340
0
  pic.height = height;
341
0
  pic.writer = WebPMemoryWrite;
342
0
  pic.custom_ptr = &wrt;
343
0
  WebPMemoryWriterInit(&wrt);
344
345
0
  ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic);
346
0
  WebPPictureFree(&pic);
347
0
  if (!ok) {
348
0
    WebPMemoryWriterClear(&wrt);
349
0
    *output = NULL;
350
0
    return 0;
351
0
  }
352
0
  *output = wrt.mem;
353
0
  return wrt.size;
354
0
}
355
356
#define ENCODE_FUNC(NAME, IMPORTER)                              \
357
  size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \
358
0
              uint8_t** out) {                                   \
359
0
    return Encode(in, w, h, bps, IMPORTER, q, 0, out);           \
360
0
  }
Unexecuted instantiation: WebPEncodeRGB
Unexecuted instantiation: WebPEncodeRGBA
Unexecuted instantiation: WebPEncodeBGR
Unexecuted instantiation: WebPEncodeBGRA
361
362
ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB)
363
ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA)
364
#if !defined(WEBP_REDUCE_CSP)
365
ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR)
366
ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA)
367
#endif  // WEBP_REDUCE_CSP
368
369
#undef ENCODE_FUNC
370
371
0
#define LOSSLESS_DEFAULT_QUALITY 70.
372
#define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER)                                  \
373
0
  size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) {      \
374
0
    return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \
375
0
  }
Unexecuted instantiation: WebPEncodeLosslessRGB
Unexecuted instantiation: WebPEncodeLosslessRGBA
Unexecuted instantiation: WebPEncodeLosslessBGR
Unexecuted instantiation: WebPEncodeLosslessBGRA
376
377
LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB)
378
LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA)
379
#if !defined(WEBP_REDUCE_CSP)
380
LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR)
381
LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA)
382
#endif  // WEBP_REDUCE_CSP
383
384
#undef LOSSLESS_ENCODE_FUNC
385
386
//------------------------------------------------------------------------------