Coverage Report

Created: 2026-09-02 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/aom/aom/src/aom_image.c
Line
Count
Source
1
/*
2
 * Copyright (c) 2016, Alliance for Open Media. All rights reserved
3
 *
4
 * This source code is subject to the terms of the BSD 2 Clause License and
5
 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
6
 * was not distributed with this source code in the LICENSE file, you can
7
 * obtain it at www.aomedia.org/license/software. If the Alliance for Open
8
 * Media Patent License 1.0 was not distributed with this source code in the
9
 * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
10
 */
11
12
#include <limits.h>
13
#include <stdlib.h>
14
#include <string.h>
15
16
#include "aom/aom_image.h"
17
#include "aom/aom_integer.h"
18
#include "aom/internal/aom_image_internal.h"
19
#include "aom_mem/aom_mem.h"
20
21
static INLINE unsigned int align_image_dimension(unsigned int d,
22
                                                 unsigned int subsampling,
23
1.64k
                                                 unsigned int size_align) {
24
1.64k
  unsigned int align;
25
26
1.64k
  align = (1 << subsampling) - 1;
27
1.64k
  align = (size_align - 1 > align) ? (size_align - 1) : align;
28
1.64k
  return ((d + align) & ~align);
29
1.64k
}
30
31
static aom_image_t *img_alloc_helper(
32
    aom_image_t *img, aom_img_fmt_t fmt, unsigned int d_w, unsigned int d_h,
33
    unsigned int buf_align, unsigned int stride_align, unsigned int size_align,
34
    unsigned int border, unsigned char *img_data,
35
823
    aom_alloc_img_data_cb_fn_t alloc_cb, void *cb_priv) {
36
  /* NOTE: In this function, bit_depth is either 8 or 16 (if
37
   * AOM_IMG_FMT_HIGHBITDEPTH is set), never 10 or 12.
38
   */
39
823
  unsigned int h, w, s, xcs, ycs, bps, bit_depth;
40
823
  unsigned int stride_in_bytes;
41
42
823
  if (img != NULL) memset(img, 0, sizeof(aom_image_t));
43
44
  /* Treat align==0 like align==1 */
45
823
  if (!buf_align) buf_align = 1;
46
47
  /* Validate alignment (must be power of 2) */
48
823
  if (buf_align & (buf_align - 1)) goto fail;
49
50
  /* Treat align==0 like align==1 */
51
823
  if (!stride_align) stride_align = 1;
52
53
  /* Validate alignment (must be power of 2) */
54
823
  if (stride_align & (stride_align - 1)) goto fail;
55
56
  /* Treat align==0 like align==1 */
57
823
  if (!size_align) size_align = 1;
58
59
  /* Validate alignment (must be power of 2) */
60
823
  if (size_align & (size_align - 1)) goto fail;
61
62
  /* Get sample size for this format */
63
823
  switch (fmt) {
64
823
    case AOM_IMG_FMT_I420:
65
823
    case AOM_IMG_FMT_YV12:
66
823
    case AOM_IMG_FMT_AOMI420:
67
823
    case AOM_IMG_FMT_AOMYV12: bps = 12; break;
68
0
    case AOM_IMG_FMT_I422: bps = 16; break;
69
0
    case AOM_IMG_FMT_I444: bps = 24; break;
70
0
    case AOM_IMG_FMT_YV1216:
71
0
    case AOM_IMG_FMT_I42016: bps = 24; break;
72
0
    case AOM_IMG_FMT_I42216: bps = 32; break;
73
0
    case AOM_IMG_FMT_I44416: bps = 48; break;
74
0
    default: bps = 16; break;
75
823
  }
76
77
823
  bit_depth = (fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
78
79
  /* Get chroma shift values for this format */
80
823
  switch (fmt) {
81
823
    case AOM_IMG_FMT_I420:
82
823
    case AOM_IMG_FMT_YV12:
83
823
    case AOM_IMG_FMT_AOMI420:
84
823
    case AOM_IMG_FMT_AOMYV12:
85
823
    case AOM_IMG_FMT_I422:
86
823
    case AOM_IMG_FMT_I42016:
87
823
    case AOM_IMG_FMT_YV1216:
88
823
    case AOM_IMG_FMT_I42216: xcs = 1; break;
89
0
    default: xcs = 0; break;
90
823
  }
91
92
823
  switch (fmt) {
93
823
    case AOM_IMG_FMT_I420:
94
823
    case AOM_IMG_FMT_YV12:
95
823
    case AOM_IMG_FMT_AOMI420:
96
823
    case AOM_IMG_FMT_AOMYV12:
97
823
    case AOM_IMG_FMT_YV1216:
98
823
    case AOM_IMG_FMT_I42016: ycs = 1; break;
99
0
    default: ycs = 0; break;
100
823
  }
101
102
  /* Calculate storage sizes given the chroma subsampling */
103
823
  w = align_image_dimension(d_w, xcs, size_align);
104
823
  h = align_image_dimension(d_h, ycs, size_align);
105
106
823
  s = (fmt & AOM_IMG_FMT_PLANAR) ? w : bps * w / bit_depth;
107
823
  s = (s + 2 * border + stride_align - 1) & ~(stride_align - 1);
108
823
  stride_in_bytes = s * bit_depth / 8;
109
110
  /* Allocate the new image */
111
823
  if (!img) {
112
823
    img = (aom_image_t *)calloc(1, sizeof(aom_image_t));
113
114
823
    if (!img) goto fail;
115
116
823
    img->self_allocd = 1;
117
823
  }
118
119
823
  img->img_data = img_data;
120
121
823
  if (!img_data) {
122
823
    const uint64_t alloc_size =
123
823
        (fmt & AOM_IMG_FMT_PLANAR)
124
823
            ? (uint64_t)(h + 2 * border) * stride_in_bytes * bps / bit_depth
125
823
            : (uint64_t)(h + 2 * border) * stride_in_bytes;
126
127
823
    if (alloc_size != (size_t)alloc_size) goto fail;
128
129
823
    if (alloc_cb) {
130
0
      const size_t padded_alloc_size = (size_t)alloc_size + buf_align - 1;
131
0
      img->img_data = (uint8_t *)alloc_cb(cb_priv, padded_alloc_size);
132
0
      if (img->img_data) {
133
0
        img->img_data = (uint8_t *)aom_align_addr(img->img_data, buf_align);
134
0
      }
135
0
      img->img_data_owner = 0;
136
823
    } else {
137
823
      img->img_data = (uint8_t *)aom_memalign(buf_align, (size_t)alloc_size);
138
823
      img->img_data_owner = 1;
139
823
    }
140
823
    img->sz = (size_t)alloc_size;
141
823
  }
142
143
823
  if (!img->img_data) goto fail;
144
145
823
  img->fmt = fmt;
146
823
  img->bit_depth = bit_depth;
147
  // aligned width and aligned height
148
823
  img->w = w;
149
823
  img->h = h;
150
823
  img->x_chroma_shift = xcs;
151
823
  img->y_chroma_shift = ycs;
152
823
  img->bps = bps;
153
154
  /* Calculate strides */
155
823
  img->stride[AOM_PLANE_Y] = stride_in_bytes;
156
823
  img->stride[AOM_PLANE_U] = img->stride[AOM_PLANE_V] = stride_in_bytes >> xcs;
157
158
  /* Default viewport to entire image. (This aom_img_set_rect call always
159
   * succeeds.) */
160
823
  aom_img_set_rect(img, 0, 0, d_w, d_h, border);
161
823
  return img;
162
163
0
fail:
164
0
  aom_img_free(img);
165
0
  return NULL;
166
823
}
167
168
aom_image_t *aom_img_alloc(aom_image_t *img, aom_img_fmt_t fmt,
169
                           unsigned int d_w, unsigned int d_h,
170
823
                           unsigned int align) {
171
823
  return img_alloc_helper(img, fmt, d_w, d_h, align, align, 1, 0, NULL, NULL,
172
823
                          NULL);
173
823
}
174
175
aom_image_t *aom_img_alloc_with_cb(aom_image_t *img, aom_img_fmt_t fmt,
176
                                   unsigned int d_w, unsigned int d_h,
177
                                   unsigned int align,
178
                                   aom_alloc_img_data_cb_fn_t alloc_cb,
179
0
                                   void *cb_priv) {
180
0
  return img_alloc_helper(img, fmt, d_w, d_h, align, align, 1, 0, NULL,
181
0
                          alloc_cb, cb_priv);
182
0
}
183
184
aom_image_t *aom_img_wrap(aom_image_t *img, aom_img_fmt_t fmt, unsigned int d_w,
185
                          unsigned int d_h, unsigned int stride_align,
186
0
                          unsigned char *img_data) {
187
  /* Set buf_align = 1. It is ignored by img_alloc_helper because img_data is
188
   * not NULL. */
189
0
  return img_alloc_helper(img, fmt, d_w, d_h, 1, stride_align, 1, 0, img_data,
190
0
                          NULL, NULL);
191
0
}
192
193
aom_image_t *aom_img_alloc_with_border(aom_image_t *img, aom_img_fmt_t fmt,
194
                                       unsigned int d_w, unsigned int d_h,
195
                                       unsigned int align,
196
                                       unsigned int size_align,
197
0
                                       unsigned int border) {
198
0
  return img_alloc_helper(img, fmt, d_w, d_h, align, align, size_align, border,
199
0
                          NULL, NULL, NULL);
200
0
}
201
202
int aom_img_set_rect(aom_image_t *img, unsigned int x, unsigned int y,
203
823
                     unsigned int w, unsigned int h, unsigned int border) {
204
823
  if (x <= UINT_MAX - w && x + w <= img->w && y <= UINT_MAX - h &&
205
823
      y + h <= img->h) {
206
823
    img->d_w = w;
207
823
    img->d_h = h;
208
209
823
    x += border;
210
823
    y += border;
211
212
    /* Calculate plane pointers */
213
823
    if (!(img->fmt & AOM_IMG_FMT_PLANAR)) {
214
0
      img->planes[AOM_PLANE_PACKED] =
215
0
          img->img_data + x * img->bps / 8 + y * img->stride[AOM_PLANE_PACKED];
216
823
    } else {
217
823
      const int bytes_per_sample =
218
823
          (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
219
823
      unsigned char *data = img->img_data;
220
221
823
      img->planes[AOM_PLANE_Y] =
222
823
          data + x * bytes_per_sample + y * img->stride[AOM_PLANE_Y];
223
823
      data += (img->h + 2 * border) * img->stride[AOM_PLANE_Y];
224
225
823
      unsigned int uv_border_h = border >> img->y_chroma_shift;
226
823
      unsigned int uv_x = x >> img->x_chroma_shift;
227
823
      unsigned int uv_y = y >> img->y_chroma_shift;
228
823
      if (!(img->fmt & AOM_IMG_FMT_UV_FLIP)) {
229
823
        img->planes[AOM_PLANE_U] =
230
823
            data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_U];
231
823
        data += ((img->h >> img->y_chroma_shift) + 2 * uv_border_h) *
232
823
                img->stride[AOM_PLANE_U];
233
823
        img->planes[AOM_PLANE_V] =
234
823
            data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_V];
235
823
      } else {
236
0
        img->planes[AOM_PLANE_V] =
237
0
            data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_V];
238
0
        data += ((img->h >> img->y_chroma_shift) + 2 * uv_border_h) *
239
0
                img->stride[AOM_PLANE_V];
240
0
        img->planes[AOM_PLANE_U] =
241
0
            data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_U];
242
0
      }
243
823
    }
244
823
    return 0;
245
823
  }
246
0
  return -1;
247
823
}
248
249
0
void aom_img_flip(aom_image_t *img) {
250
  /* Note: In the calculation pointer adjustment calculation, we want the
251
   * rhs to be promoted to a signed type. Section 6.3.1.8 of the ISO C99
252
   * standard indicates that if the adjustment parameter is unsigned, the
253
   * stride parameter will be promoted to unsigned, causing errors when
254
   * the lhs is a larger type than the rhs.
255
   */
256
0
  img->planes[AOM_PLANE_Y] += (signed)(img->d_h - 1) * img->stride[AOM_PLANE_Y];
257
0
  img->stride[AOM_PLANE_Y] = -img->stride[AOM_PLANE_Y];
258
259
0
  img->planes[AOM_PLANE_U] += (signed)((img->d_h >> img->y_chroma_shift) - 1) *
260
0
                              img->stride[AOM_PLANE_U];
261
0
  img->stride[AOM_PLANE_U] = -img->stride[AOM_PLANE_U];
262
263
0
  img->planes[AOM_PLANE_V] += (signed)((img->d_h >> img->y_chroma_shift) - 1) *
264
0
                              img->stride[AOM_PLANE_V];
265
0
  img->stride[AOM_PLANE_V] = -img->stride[AOM_PLANE_V];
266
0
}
267
268
823
void aom_img_free(aom_image_t *img) {
269
823
  if (img) {
270
823
    aom_img_remove_metadata(img);
271
823
    if (img->img_data && img->img_data_owner) aom_free(img->img_data);
272
273
823
    if (img->self_allocd) free(img);
274
823
  }
275
823
}
276
277
0
int aom_img_plane_width(const aom_image_t *img, int plane) {
278
0
  if (plane > 0 && img->x_chroma_shift > 0)
279
0
    return (img->d_w + 1) >> img->x_chroma_shift;
280
0
  else
281
0
    return img->d_w;
282
0
}
283
284
0
int aom_img_plane_height(const aom_image_t *img, int plane) {
285
0
  if (plane > 0 && img->y_chroma_shift > 0)
286
0
    return (img->d_h + 1) >> img->y_chroma_shift;
287
0
  else
288
0
    return img->d_h;
289
0
}
290
291
aom_metadata_t *aom_img_metadata_alloc(
292
    uint32_t type, const uint8_t *data, size_t sz,
293
0
    aom_metadata_insert_flags_t insert_flag) {
294
0
  if (!data || sz == 0) return NULL;
295
0
  aom_metadata_t *metadata = (aom_metadata_t *)malloc(sizeof(aom_metadata_t));
296
0
  if (!metadata) return NULL;
297
0
  metadata->type = type;
298
0
  metadata->payload = (uint8_t *)malloc(sz);
299
0
  if (!metadata->payload) {
300
0
    free(metadata);
301
0
    return NULL;
302
0
  }
303
0
  memcpy(metadata->payload, data, sz);
304
0
  metadata->sz = sz;
305
0
  metadata->insert_flag = insert_flag;
306
0
  return metadata;
307
0
}
308
309
0
void aom_img_metadata_free(aom_metadata_t *metadata) {
310
0
  if (metadata) {
311
0
    if (metadata->payload) free(metadata->payload);
312
0
    free(metadata);
313
0
  }
314
0
}
315
316
0
aom_metadata_array_t *aom_img_metadata_array_alloc(size_t sz) {
317
0
  aom_metadata_array_t *arr =
318
0
      (aom_metadata_array_t *)calloc(1, sizeof(aom_metadata_array_t));
319
0
  if (!arr) return NULL;
320
0
  if (sz > 0) {
321
0
    arr->metadata_array =
322
0
        (aom_metadata_t **)calloc(sz, sizeof(aom_metadata_t *));
323
0
    if (!arr->metadata_array) {
324
0
      aom_img_metadata_array_free(arr);
325
0
      return NULL;
326
0
    }
327
0
    arr->sz = sz;
328
0
  }
329
0
  return arr;
330
0
}
331
332
0
void aom_img_metadata_array_free(aom_metadata_array_t *arr) {
333
0
  if (arr) {
334
0
    if (arr->metadata_array) {
335
0
      for (size_t i = 0; i < arr->sz; i++) {
336
0
        aom_img_metadata_free(arr->metadata_array[i]);
337
0
      }
338
0
      free(arr->metadata_array);
339
0
    }
340
0
    free(arr);
341
0
  }
342
0
}
343
344
int aom_img_add_metadata(aom_image_t *img, uint32_t type, const uint8_t *data,
345
0
                         size_t sz, aom_metadata_insert_flags_t insert_flag) {
346
0
  if (!img) return -1;
347
0
  if (!img->metadata) {
348
0
    img->metadata = aom_img_metadata_array_alloc(0);
349
0
    if (!img->metadata) return -1;
350
0
  }
351
0
  aom_metadata_t *metadata =
352
0
      aom_img_metadata_alloc(type, data, sz, insert_flag);
353
0
  if (!metadata) return -1;
354
0
  aom_metadata_t **metadata_array =
355
0
      (aom_metadata_t **)realloc(img->metadata->metadata_array,
356
0
                                 (img->metadata->sz + 1) * sizeof(metadata));
357
0
  if (!metadata_array) {
358
0
    aom_img_metadata_free(metadata);
359
0
    return -1;
360
0
  }
361
0
  img->metadata->metadata_array = metadata_array;
362
0
  img->metadata->metadata_array[img->metadata->sz] = metadata;
363
0
  img->metadata->sz++;
364
0
  return 0;
365
0
}
366
367
823
void aom_img_remove_metadata(aom_image_t *img) {
368
823
  if (img && img->metadata) {
369
0
    aom_img_metadata_array_free(img->metadata);
370
0
    img->metadata = NULL;
371
0
  }
372
823
}
373
374
const aom_metadata_t *aom_img_get_metadata(const aom_image_t *img,
375
0
                                           size_t index) {
376
0
  if (!img) return NULL;
377
0
  const aom_metadata_array_t *array = img->metadata;
378
0
  if (array && index < array->sz) {
379
0
    return array->metadata_array[index];
380
0
  }
381
0
  return NULL;
382
0
}
383
384
0
size_t aom_img_num_metadata(const aom_image_t *img) {
385
0
  if (!img || !img->metadata) return 0;
386
0
  return img->metadata->sz;
387
0
}