Coverage Report

Created: 2026-11-12 07:35

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
7.72k
int WebPPictureInitInternal(WebPPicture* picture, int version) {
38
7.72k
  if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
39
0
    return 0;  // caller/system version mismatch!
40
0
  }
41
7.72k
  if (picture != NULL) {
42
7.72k
    memset(picture, 0, sizeof(*picture));
43
7.72k
    picture->writer = DummyWriter;
44
7.72k
    WebPEncodingSetError(picture, VP8_ENC_OK);
45
7.72k
  }
46
7.72k
  return 1;
47
7.72k
}
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
23.2k
                             int height) {
62
23.2k
  uint64_t abs_stride, row_span, width_bytes, total_bytes;
63
23.2k
  uintptr_t addr;
64
65
23.2k
  if (ptr == NULL) return 1;
66
12.7k
  if (width <= 0 || height <= 0 || step <= 0) return 0;
67
12.7k
  if (stride > INT_MAX / step || stride < -INT_MAX / step) return 0;
68
12.7k
  stride *= step;
69
12.7k
  if (!CheckStride(stride, step, width)) return 0;
70
71
12.7k
  abs_stride = (stride < 0) ? (uint64_t)-(int64_t)stride : (uint64_t)stride;
72
12.7k
  row_span = (uint64_t)(height - 1) * abs_stride;
73
12.7k
  width_bytes = (uint64_t)width * (uint64_t)step;
74
75
  // Check that the total plane size fits in addressable limits (PTRDIFF_MAX).
76
12.7k
  if (row_span > (uint64_t)PTRDIFF_MAX - width_bytes) return 0;
77
78
12.7k
  total_bytes = row_span + width_bytes;
79
12.7k
  addr = (uintptr_t)ptr;
80
81
12.7k
  if (stride >= 0) {
82
12.7k
    if (addr > (uintptr_t)UINTPTR_MAX - (uintptr_t)total_bytes) return 0;
83
12.7k
  } 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
12.7k
  return 1;
88
12.7k
}
89
90
11.5k
int WebPValidatePicture(const WebPPicture* const picture) {
91
11.5k
  if (picture == NULL) return 0;
92
11.5k
  if (picture->width <= 0 || picture->width > INT_MAX / 4 ||
93
11.5k
      picture->height <= 0 || picture->height > INT_MAX / 4) {
94
0
    return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
95
0
  }
96
11.5k
  if (picture->colorspace != WEBP_YUV420 &&
97
2.79k
      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
11.5k
  if (picture->use_argb) {
102
7.59k
    if (!CheckMemoryBounds(picture->argb, picture->argb_stride,
103
7.59k
                           (int)sizeof(*picture->argb), picture->width,
104
7.59k
                           picture->height)) {
105
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
106
0
    }
107
7.59k
  } 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
3.91k
    const int uv_width = (picture->width + 1) >> 1;
112
3.91k
    const int uv_height = (picture->height + 1) >> 1;
113
3.91k
    if (!CheckMemoryBounds(picture->y, picture->y_stride, /*step=*/1,
114
3.91k
                           picture->width, picture->height) ||
115
3.91k
        !CheckMemoryBounds(picture->u, picture->uv_stride, /*step=*/1, uv_width,
116
3.91k
                           uv_height) ||
117
3.91k
        !CheckMemoryBounds(picture->v, picture->uv_stride, /*step=*/1, uv_width,
118
3.91k
                           uv_height) ||
119
3.91k
        !CheckMemoryBounds(picture->a, picture->a_stride, /*step=*/1,
120
3.91k
                           picture->width, picture->height)) {
121
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_BAD_DIMENSION);
122
0
    }
123
3.91k
  }
124
11.5k
  return 1;
125
11.5k
}
126
127
4.71k
int WebPValidateAllocatedPicture(const WebPPicture* const picture) {
128
4.71k
  if (!WebPValidatePicture(picture)) return 0;
129
4.71k
  if (picture->use_argb) {
130
2.75k
    if (picture->argb == NULL) {
131
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
132
0
    }
133
2.75k
  } else {
134
1.95k
    if (picture->y == NULL || picture->u == NULL || picture->v == NULL) {
135
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
136
0
    }
137
1.95k
    if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) {
138
0
      return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER);
139
0
    }
140
1.95k
  }
141
4.71k
  return 1;
142
4.71k
}
143
144
9.00k
static void WebPPictureResetBufferARGB(WebPPicture* const picture) {
145
9.00k
  picture->memory_argb_ = NULL;
146
9.00k
  picture->argb = NULL;
147
9.00k
  picture->argb_stride = 0;
148
9.00k
}
149
150
8.88k
static void WebPPictureResetBufferYUVA(WebPPicture* const picture) {
151
8.88k
  picture->memory_ = NULL;
152
8.88k
  picture->y = picture->u = picture->v = picture->a = NULL;
153
8.88k
  picture->y_stride = picture->uv_stride = 0;
154
8.88k
  picture->a_stride = 0;
155
8.88k
}
156
157
6.92k
void WebPPictureResetBuffers(WebPPicture* const picture) {
158
6.92k
  WebPPictureResetBufferARGB(picture);
159
6.92k
  WebPPictureResetBufferYUVA(picture);
160
6.92k
}
161
162
2.08k
int WebPPictureAllocARGB(WebPPicture* const picture) {
163
2.08k
  void* memory;
164
2.08k
  int width, height;
165
2.08k
  uint64_t argb_size;
166
167
2.08k
  assert(picture != NULL);
168
169
2.08k
  WebPSafeFree(picture->memory_argb_);
170
2.08k
  WebPPictureResetBufferARGB(picture);
171
172
2.08k
  if (!WebPValidatePicture(picture)) return 0;
173
174
2.08k
  width = picture->width;
175
2.08k
  height = picture->height;
176
2.08k
  argb_size = (uint64_t)width * height;
177
178
  // allocate a new buffer.
179
2.08k
  memory = WebPSafeMalloc(argb_size + WEBP_ALIGN_CST, sizeof(*picture->argb));
180
2.08k
  if (memory == NULL) {
181
0
    return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
182
0
  }
183
2.08k
  picture->memory_argb_ = memory;
184
2.08k
  picture->argb = (uint32_t*)WEBP_ALIGN(memory);
185
2.08k
  picture->argb_stride = width;
186
2.08k
  return 1;
187
2.08k
}
188
189
1.95k
int WebPPictureAllocYUVA(WebPPicture* const picture) {
190
1.95k
  int has_alpha, width, height, y_stride, uv_width, uv_height, uv_stride;
191
1.95k
  int a_width, a_stride;
192
1.95k
  uint64_t y_size, uv_size, a_size, total_size;
193
1.95k
  uint8_t* mem;
194
195
1.95k
  assert(picture != NULL);
196
197
1.95k
  WebPSafeFree(picture->memory_);
198
1.95k
  WebPPictureResetBufferYUVA(picture);
199
200
1.95k
  if (!WebPValidatePicture(picture)) return 0;
201
202
1.95k
  has_alpha = (int)picture->colorspace & WEBP_CSP_ALPHA_BIT;
203
1.95k
  width = picture->width;
204
1.95k
  height = picture->height;
205
1.95k
  y_stride = width;
206
1.95k
  uv_width = (width + 1) >> 1;
207
1.95k
  uv_height = (height + 1) >> 1;
208
1.95k
  uv_stride = uv_width;
209
  // alpha
210
1.95k
  a_width = has_alpha ? width : 0;
211
1.95k
  a_stride = a_width;
212
1.95k
  y_size = (uint64_t)y_stride * height;
213
1.95k
  uv_size = (uint64_t)uv_stride * uv_height;
214
1.95k
  a_size = (uint64_t)a_stride * height;
215
216
1.95k
  total_size = y_size + a_size + 2 * uv_size;
217
218
  // allocate a new buffer.
219
1.95k
  mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem));
220
1.95k
  if (mem == NULL) {
221
0
    return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY);
222
0
  }
223
224
  // From now on, we're in the clear, we can no longer fail...
225
1.95k
  picture->memory_ = (void*)mem;
226
1.95k
  picture->y_stride = y_stride;
227
1.95k
  picture->uv_stride = uv_stride;
228
1.95k
  picture->a_stride = a_stride;
229
230
  // TODO(skal): we could align the y/u/v planes and adjust stride.
231
1.95k
  picture->y = mem;
232
1.95k
  mem += y_size;
233
234
1.95k
  picture->u = mem;
235
1.95k
  mem += uv_size;
236
1.95k
  picture->v = mem;
237
1.95k
  mem += uv_size;
238
239
1.95k
  if (a_size > 0) {
240
1.39k
    picture->a = mem;
241
1.39k
    mem += a_size;
242
1.39k
  }
243
1.95k
  (void)mem;  // makes the static analyzer happy
244
1.95k
  return 1;
245
1.95k
}
246
247
2.08k
int WebPPictureAlloc(WebPPicture* picture) {
248
2.08k
  if (picture != NULL) {
249
2.08k
    WebPPictureFree(picture);  // erase previous buffer
250
251
2.08k
    if (!picture->use_argb) {
252
0
      return WebPPictureAllocYUVA(picture);
253
2.08k
    } else {
254
2.08k
      return WebPPictureAllocARGB(picture);
255
2.08k
    }
256
2.08k
  }
257
0
  return 1;
258
2.08k
}
259
260
6.92k
void WebPPictureFree(WebPPicture* picture) {
261
6.92k
  if (picture != NULL) {
262
6.92k
    WebPSafeFree(picture->memory_);
263
6.92k
    WebPSafeFree(picture->memory_argb_);
264
6.92k
    WebPPictureResetBuffers(picture);
265
6.92k
  }
266
6.92k
}
267
268
//------------------------------------------------------------------------------
269
// WebPMemoryWriter: Write-to-memory
270
271
2.75k
void WebPMemoryWriterInit(WebPMemoryWriter* writer) {
272
2.75k
  writer->mem = NULL;
273
2.75k
  writer->size = 0;
274
2.75k
  writer->max_size = 0;
275
2.75k
}
276
277
int WebPMemoryWrite(const uint8_t* data, size_t data_size,
278
17.7k
                    const WebPPicture* picture) {
279
17.7k
  WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr;
280
17.7k
  uint64_t next_size;
281
17.7k
  if (w == NULL) {
282
0
    return 1;
283
0
  }
284
17.7k
  next_size = (uint64_t)w->size + data_size;
285
17.7k
  if (next_size > w->max_size) {
286
3.79k
    uint8_t* new_mem;
287
3.79k
    uint64_t next_max_size = 2ULL * w->max_size;
288
3.79k
    if (next_max_size < next_size) next_max_size = next_size;
289
3.79k
    if (next_max_size < 8192ULL) next_max_size = 8192ULL;
290
3.79k
    new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1);
291
3.79k
    if (new_mem == NULL) {
292
0
      return 0;
293
0
    }
294
3.79k
    if (w->size > 0) {
295
1.03k
      memcpy(new_mem, w->mem, w->size);
296
1.03k
    }
297
3.79k
    WebPSafeFree(w->mem);
298
3.79k
    w->mem = new_mem;
299
    // down-cast is ok, thanks to WebPSafeMalloc
300
3.79k
    w->max_size = (size_t)next_max_size;
301
3.79k
  }
302
17.7k
  if (data_size > 0) {
303
17.7k
    memcpy(w->mem + w->size, data, data_size);
304
17.7k
    w->size += data_size;
305
17.7k
  }
306
17.7k
  return 1;
307
17.7k
}
308
309
0
void WebPMemoryWriterClear(WebPMemoryWriter* writer) {
310
0
  if (writer != NULL) {
311
0
    WebPSafeFree(writer->mem);
312
0
    WebPMemoryWriterInit(writer);
313
0
  }
314
0
}
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
//------------------------------------------------------------------------------