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