Coverage Report

Created: 2026-09-14 08:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libvpx/vpx/src/vpx_image.c
Line
Count
Source
1
/*
2
 *  Copyright (c) 2010 The WebM project authors. All Rights Reserved.
3
 *
4
 *  Use of this source code is governed by a BSD-style license
5
 *  that can be found in the LICENSE file in the root of the source
6
 *  tree. An additional intellectual property rights grant can be found
7
 *  in the file PATENTS.  All contributing project authors may
8
 *  be found in the AUTHORS file in the root of the source tree.
9
 */
10
11
#include <assert.h>
12
#include <limits.h>
13
#include <stddef.h>
14
#include <stdlib.h>
15
#include <string.h>
16
17
#include "vpx/vpx_image.h"
18
#include "vpx/vpx_integer.h"
19
#include "vpx_mem/vpx_mem.h"
20
21
10.0k
static int is_valid_img_fmt(vpx_img_fmt_t fmt) {
22
10.0k
  switch (fmt) {
23
0
    case VPX_IMG_FMT_YV12:
24
8.98k
    case VPX_IMG_FMT_I420:
25
9.11k
    case VPX_IMG_FMT_I422:
26
9.34k
    case VPX_IMG_FMT_I444:
27
9.46k
    case VPX_IMG_FMT_I440:
28
9.46k
    case VPX_IMG_FMT_NV12:
29
9.68k
    case VPX_IMG_FMT_I42016:
30
9.97k
    case VPX_IMG_FMT_I42216:
31
10.0k
    case VPX_IMG_FMT_I44416:
32
10.0k
    case VPX_IMG_FMT_I44016: return 1;
33
0
    default: return 0;
34
10.0k
  }
35
10.0k
}
36
37
static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt,
38
                                     unsigned int d_w, unsigned int d_h,
39
                                     unsigned int buf_align,
40
                                     unsigned int stride_align,
41
10.0k
                                     unsigned char *img_data) {
42
10.0k
  unsigned int h, w, xcs, ycs, bps;
43
10.0k
  uint64_t s;
44
10.0k
  int stride_in_bytes;
45
10.0k
  unsigned int align;
46
47
10.0k
  if (img != NULL) memset(img, 0, sizeof(vpx_image_t));
48
49
10.0k
  if (!is_valid_img_fmt(fmt)) goto fail;
50
51
  /* Impose maximum values on input parameters so that this function can
52
   * perform arithmetic operations without worrying about overflows.
53
   */
54
10.0k
  if (d_w > 0x08000000 || d_h > 0x08000000 || buf_align > 65536 ||
55
10.0k
      stride_align > 65536) {
56
0
    goto fail;
57
0
  }
58
59
  /* Treat align==0 like align==1 */
60
10.0k
  if (!buf_align) buf_align = 1;
61
62
  /* Validate alignment (must be power of 2) */
63
10.0k
  if (buf_align & (buf_align - 1)) goto fail;
64
65
  /* Treat align==0 like align==1 */
66
10.0k
  if (!stride_align) stride_align = 1;
67
68
  /* Validate alignment (must be power of 2) */
69
10.0k
  if (stride_align & (stride_align - 1)) goto fail;
70
71
  /* Get sample size for this format */
72
10.0k
  switch (fmt) {
73
8.98k
    case VPX_IMG_FMT_I420:
74
8.98k
    case VPX_IMG_FMT_YV12:
75
8.98k
    case VPX_IMG_FMT_NV12: bps = 12; break;
76
137
    case VPX_IMG_FMT_I422:
77
261
    case VPX_IMG_FMT_I440: bps = 16; break;
78
224
    case VPX_IMG_FMT_I444: bps = 24; break;
79
215
    case VPX_IMG_FMT_I42016: bps = 24; break;
80
296
    case VPX_IMG_FMT_I42216:
81
313
    case VPX_IMG_FMT_I44016: bps = 32; break;
82
62
    case VPX_IMG_FMT_I44416: bps = 48; break;
83
0
    default: bps = 16; break;
84
10.0k
  }
85
86
  /* Get chroma shift values for this format */
87
10.0k
  switch (fmt) {
88
8.98k
    case VPX_IMG_FMT_I420:
89
8.98k
    case VPX_IMG_FMT_YV12:
90
8.98k
    case VPX_IMG_FMT_NV12:
91
9.11k
    case VPX_IMG_FMT_I422:
92
9.33k
    case VPX_IMG_FMT_I42016:
93
9.63k
    case VPX_IMG_FMT_I42216: xcs = 1; break;
94
427
    default: xcs = 0; break;
95
10.0k
  }
96
97
10.0k
  switch (fmt) {
98
8.98k
    case VPX_IMG_FMT_I420:
99
8.98k
    case VPX_IMG_FMT_NV12:
100
9.10k
    case VPX_IMG_FMT_I440:
101
9.10k
    case VPX_IMG_FMT_YV12:
102
9.32k
    case VPX_IMG_FMT_I42016:
103
9.33k
    case VPX_IMG_FMT_I44016: ycs = 1; break;
104
719
    default: ycs = 0; break;
105
10.0k
  }
106
107
  /* Calculate storage sizes. */
108
10.0k
  if (img_data) {
109
    /* If the buffer was allocated externally, the width and height shouldn't
110
     * be adjusted. */
111
10.0k
    w = d_w;
112
10.0k
    h = d_h;
113
10.0k
  } else {
114
    /* Calculate storage sizes given the chroma subsampling */
115
0
    align = (1 << xcs) - 1;
116
0
    w = (d_w + align) & ~align;
117
0
    assert(d_w <= w);
118
0
    align = (1 << ycs) - 1;
119
0
    h = (d_h + align) & ~align;
120
0
    assert(d_h <= h);
121
0
  }
122
123
10.0k
  s = (fmt & VPX_IMG_FMT_PLANAR) ? w : (uint64_t)bps * w / 8;
124
10.0k
  s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s;
125
10.0k
  s = (s + stride_align - 1) & ~((uint64_t)stride_align - 1);
126
10.0k
  if (s > INT_MAX) goto fail;
127
10.0k
  stride_in_bytes = (int)s;
128
10.0k
  uint64_t uv_s = s;
129
  // Convert to number of samples.
130
10.0k
  uv_s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? uv_s / 2 : uv_s;
131
  // Apply chroma subsampling.
132
10.0k
  uv_s = (uv_s + xcs) >> xcs;
133
  // Convert back to number of bytes.
134
10.0k
  uv_s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? uv_s * 2 : uv_s;
135
10.0k
  const int uv_stride_in_bytes = (int)uv_s;
136
137
  /* Allocate the new image */
138
10.0k
  if (!img) {
139
0
    img = (vpx_image_t *)calloc(1, sizeof(vpx_image_t));
140
141
0
    if (!img) goto fail;
142
143
0
    img->self_allocd = 1;
144
0
  }
145
146
10.0k
  img->img_data = img_data;
147
148
10.0k
  if (!img_data) {
149
0
    uint64_t alloc_size;
150
0
    s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s / 2 : s;
151
0
    alloc_size = (fmt & VPX_IMG_FMT_PLANAR) ? (uint64_t)h * s * bps / 8
152
0
                                            : (uint64_t)h * s;
153
154
0
    if (alloc_size != (size_t)alloc_size) goto fail;
155
156
0
    img->img_data = (uint8_t *)vpx_memalign(buf_align, (size_t)alloc_size);
157
0
    img->img_data_owner = 1;
158
0
  }
159
160
10.0k
  if (!img->img_data) goto fail;
161
162
10.0k
  img->fmt = fmt;
163
10.0k
  img->bit_depth = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
164
10.0k
  img->w = w;
165
10.0k
  img->h = h;
166
10.0k
  img->x_chroma_shift = xcs;
167
10.0k
  img->y_chroma_shift = ycs;
168
10.0k
  img->bps = bps;
169
170
  /* Calculate strides */
171
10.0k
  img->stride[VPX_PLANE_Y] = img->stride[VPX_PLANE_ALPHA] = stride_in_bytes;
172
10.0k
  img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = uv_stride_in_bytes;
173
174
10.0k
  if (fmt == VPX_IMG_FMT_NV12) {
175
0
    img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = stride_in_bytes;
176
0
  }
177
178
10.0k
  if (fmt == VPX_IMG_FMT_NV12) {
179
0
    img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = stride_in_bytes;
180
0
  }
181
182
  /* Default viewport to entire image. (This vpx_img_set_rect call always
183
   * succeeds.) */
184
10.0k
  int ret = vpx_img_set_rect(img, 0, 0, d_w, d_h);
185
10.0k
  assert(ret == 0);
186
10.0k
  (void)ret;
187
10.0k
  return img;
188
189
0
fail:
190
0
  vpx_img_free(img);
191
0
  return NULL;
192
10.0k
}
193
194
vpx_image_t *vpx_img_alloc(vpx_image_t *img, vpx_img_fmt_t fmt,
195
                           unsigned int d_w, unsigned int d_h,
196
0
                           unsigned int align) {
197
0
  return img_alloc_helper(img, fmt, d_w, d_h, align, align, NULL);
198
0
}
199
200
vpx_image_t *vpx_img_wrap(vpx_image_t *img, vpx_img_fmt_t fmt, unsigned int d_w,
201
                          unsigned int d_h, unsigned int stride_align,
202
10.0k
                          unsigned char *img_data) {
203
  /* Set buf_align = 1. It is ignored by img_alloc_helper because img_data is
204
   * not NULL. */
205
10.0k
  return img_alloc_helper(img, fmt, d_w, d_h, 1, stride_align, img_data);
206
10.0k
}
207
208
0
static void negate_strides(vpx_image_t *img) {
209
0
  img->stride[VPX_PLANE_Y] = -img->stride[VPX_PLANE_Y];
210
0
  if (img->fmt & VPX_IMG_FMT_PLANAR) {
211
0
    img->stride[VPX_PLANE_U] = -img->stride[VPX_PLANE_U];
212
0
    img->stride[VPX_PLANE_V] = -img->stride[VPX_PLANE_V];
213
0
    if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) {
214
0
      img->stride[VPX_PLANE_ALPHA] = -img->stride[VPX_PLANE_ALPHA];
215
0
    }
216
0
  }
217
0
}
218
219
int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y,
220
10.0k
                     unsigned int w, unsigned int h) {
221
10.0k
  if (x <= UINT_MAX - w && x + w <= img->w && y <= UINT_MAX - h &&
222
10.0k
      y + h <= img->h) {
223
10.0k
    const int flipped = img->stride[VPX_PLANE_Y] < 0;
224
    /* Plane offsets are calculated from the unflipped allocation base. */
225
10.0k
    if (flipped) negate_strides(img);
226
227
10.0k
    img->d_w = w;
228
10.0k
    img->d_h = h;
229
230
    /* Calculate plane pointers */
231
10.0k
    if (!(img->fmt & VPX_IMG_FMT_PLANAR)) {
232
0
      img->planes[VPX_PLANE_PACKED] =
233
0
          img->img_data + x * img->bps / 8 + y * img->stride[VPX_PLANE_PACKED];
234
10.0k
    } else {
235
10.0k
      const int bytes_per_sample =
236
10.0k
          (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
237
10.0k
      unsigned char *data = img->img_data;
238
239
10.0k
      if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) {
240
0
        img->planes[VPX_PLANE_ALPHA] =
241
0
            data + x * bytes_per_sample + y * img->stride[VPX_PLANE_ALPHA];
242
0
        data += (size_t)img->h * img->stride[VPX_PLANE_ALPHA];
243
0
      }
244
245
10.0k
      img->planes[VPX_PLANE_Y] =
246
10.0k
          data + x * bytes_per_sample + y * img->stride[VPX_PLANE_Y];
247
10.0k
      data += (size_t)img->h * img->stride[VPX_PLANE_Y];
248
249
10.0k
      unsigned int uv_x = x >> img->x_chroma_shift;
250
10.0k
      unsigned int uv_y = y >> img->y_chroma_shift;
251
10.0k
      if (img->fmt == VPX_IMG_FMT_NV12) {
252
0
        img->planes[VPX_PLANE_U] = data + uv_x * bytes_per_sample * 2 +
253
0
                                   uv_y * img->stride[VPX_PLANE_U];
254
0
        img->planes[VPX_PLANE_V] = img->planes[VPX_PLANE_U] + bytes_per_sample;
255
10.0k
      } else if (!(img->fmt & VPX_IMG_FMT_UV_FLIP)) {
256
10.0k
        img->planes[VPX_PLANE_U] =
257
10.0k
            data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_U];
258
10.0k
        data +=
259
10.0k
            (size_t)(img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_U];
260
10.0k
        img->planes[VPX_PLANE_V] =
261
10.0k
            data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_V];
262
10.0k
      } else {
263
0
        img->planes[VPX_PLANE_V] =
264
0
            data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_V];
265
0
        data +=
266
0
            (size_t)(img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_V];
267
0
        img->planes[VPX_PLANE_U] =
268
0
            data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_U];
269
0
      }
270
10.0k
    }
271
10.0k
    if (flipped) {
272
0
      if (h == 0) {
273
        /* An empty image has no last row for vpx_img_flip() to select. */
274
0
        negate_strides(img);
275
0
      } else {
276
0
        vpx_img_flip(img);
277
0
      }
278
0
    }
279
10.0k
    return 0;
280
10.0k
  }
281
0
  return -1;
282
10.0k
}
283
284
0
void vpx_img_flip(vpx_image_t *img) {
285
0
  const unsigned int chroma_height =
286
0
      (img->d_h + img->y_chroma_shift) >> img->y_chroma_shift;
287
288
0
  img->planes[VPX_PLANE_Y] +=
289
0
      (ptrdiff_t)(img->d_h - 1) * img->stride[VPX_PLANE_Y];
290
0
  img->stride[VPX_PLANE_Y] = -img->stride[VPX_PLANE_Y];
291
292
0
  img->planes[VPX_PLANE_U] +=
293
0
      (ptrdiff_t)(chroma_height - 1) * img->stride[VPX_PLANE_U];
294
0
  img->stride[VPX_PLANE_U] = -img->stride[VPX_PLANE_U];
295
296
0
  img->planes[VPX_PLANE_V] +=
297
0
      (ptrdiff_t)(chroma_height - 1) * img->stride[VPX_PLANE_V];
298
0
  img->stride[VPX_PLANE_V] = -img->stride[VPX_PLANE_V];
299
300
0
  if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) {
301
0
    img->planes[VPX_PLANE_ALPHA] +=
302
0
        (ptrdiff_t)(img->d_h - 1) * img->stride[VPX_PLANE_ALPHA];
303
0
    img->stride[VPX_PLANE_ALPHA] = -img->stride[VPX_PLANE_ALPHA];
304
0
  }
305
0
}
306
307
0
void vpx_img_free(vpx_image_t *img) {
308
0
  if (img) {
309
0
    if (img->img_data && img->img_data_owner) vpx_free(img->img_data);
310
311
0
    if (img->self_allocd) free(img);
312
0
  }
313
0
}