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_AOMI420:
67 case AOM_IMG_FMT_AOMYV12: bps = 12; break;
68 case AOM_IMG_FMT_I422: bps = 16; break;
69 case AOM_IMG_FMT_I444: bps = 24; break;
70 case AOM_IMG_FMT_YV1216:
71 case AOM_IMG_FMT_I42016: bps = 24; break;
72 case AOM_IMG_FMT_I42216: bps = 32; break;
73 case AOM_IMG_FMT_I44416: bps = 48; break;
74 default: bps = 16; break;
75 }
76
77 bit_depth = (fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
78
79 /* Get chroma shift values for this format */
80 switch (fmt) {
81 case AOM_IMG_FMT_I420:
82 case AOM_IMG_FMT_YV12:
83 case AOM_IMG_FMT_AOMI420:
84 case AOM_IMG_FMT_AOMYV12:
85 case AOM_IMG_FMT_I422:
86 case AOM_IMG_FMT_I42016:
87 case AOM_IMG_FMT_YV1216:
88 case AOM_IMG_FMT_I42216: xcs = 1; break;
89 default: xcs = 0; break;
90 }
91
92 switch (fmt) {
93 case AOM_IMG_FMT_I420:
94 case AOM_IMG_FMT_YV12:
95 case AOM_IMG_FMT_AOMI420:
96 case AOM_IMG_FMT_AOMYV12:
97 case AOM_IMG_FMT_YV1216:
98 case AOM_IMG_FMT_I42016: ycs = 1; break;
99 default: ycs = 0; break;
100 }
101
102 /* Calculate storage sizes given the chroma subsampling */
103 w = align_image_dimension(d_w, xcs, size_align);
104 h = align_image_dimension(d_h, ycs, size_align);
105
106 s = (fmt & AOM_IMG_FMT_PLANAR) ? w : bps * w / bit_depth;
107 s = (s + 2 * border + stride_align - 1) & ~(stride_align - 1);
108 stride_in_bytes = s * bit_depth / 8;
109
110 /* Allocate the new image */
111 if (!img) {
112 img = (aom_image_t *)calloc(1, sizeof(aom_image_t));
113
114 if (!img) goto fail;
115
116 img->self_allocd = 1;
117 }
118
119 img->img_data = img_data;
120
121 if (!img_data) {
122 const uint64_t alloc_size =
123 (fmt & AOM_IMG_FMT_PLANAR)
124 ? (uint64_t)(h + 2 * border) * stride_in_bytes * bps / bit_depth
125 : (uint64_t)(h + 2 * border) * stride_in_bytes;
126
127 if (alloc_size != (size_t)alloc_size) goto fail;
128
129 if (alloc_cb) {
130 const size_t padded_alloc_size = (size_t)alloc_size + buf_align - 1;
131 img->img_data = (uint8_t *)alloc_cb(cb_priv, padded_alloc_size);
132 if (img->img_data) {
133 img->img_data = (uint8_t *)aom_align_addr(img->img_data, buf_align);
134 }
135 img->img_data_owner = 0;
136 } else {
137 img->img_data = (uint8_t *)aom_memalign(buf_align, (size_t)alloc_size);
138 img->img_data_owner = 1;
139 }
140 img->sz = (size_t)alloc_size;
141 }
142
143 if (!img->img_data) goto fail;
144
145 img->fmt = fmt;
146 img->bit_depth = bit_depth;
147 // aligned width and aligned height
148 img->w = w;
149 img->h = h;
150 img->x_chroma_shift = xcs;
151 img->y_chroma_shift = ycs;
152 img->bps = bps;
153
154 /* Calculate strides */
155 img->stride[AOM_PLANE_Y] = stride_in_bytes;
156 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 aom_img_set_rect(img, 0, 0, d_w, d_h, border);
161 return img;
162
163 fail:
164 aom_img_free(img);
165 return NULL;
166 }
167
aom_img_alloc(aom_image_t * img,aom_img_fmt_t fmt,unsigned int d_w,unsigned int d_h,unsigned int align)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 unsigned int align) {
171 return img_alloc_helper(img, fmt, d_w, d_h, align, align, 1, 0, NULL, NULL,
172 NULL);
173 }
174
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)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 void *cb_priv) {
180 return img_alloc_helper(img, fmt, d_w, d_h, align, align, 1, 0, NULL,
181 alloc_cb, cb_priv);
182 }
183
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)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 unsigned char *img_data) {
187 /* Set buf_align = 1. It is ignored by img_alloc_helper because img_data is
188 * not NULL. */
189 return img_alloc_helper(img, fmt, d_w, d_h, 1, stride_align, 1, 0, img_data,
190 NULL, NULL);
191 }
192
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)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 unsigned int border) {
198 return img_alloc_helper(img, fmt, d_w, d_h, align, align, size_align, border,
199 NULL, NULL, NULL);
200 }
201
aom_img_set_rect(aom_image_t * img,unsigned int x,unsigned int y,unsigned int w,unsigned int h,unsigned int border)202 int aom_img_set_rect(aom_image_t *img, unsigned int x, unsigned int y,
203 unsigned int w, unsigned int h, unsigned int border) {
204 if (x <= UINT_MAX - w && x + w <= img->w && y <= UINT_MAX - h &&
205 y + h <= img->h) {
206 img->d_w = w;
207 img->d_h = h;
208
209 x += border;
210 y += border;
211
212 /* Calculate plane pointers */
213 if (!(img->fmt & AOM_IMG_FMT_PLANAR)) {
214 img->planes[AOM_PLANE_PACKED] =
215 img->img_data + x * img->bps / 8 + y * img->stride[AOM_PLANE_PACKED];
216 } else {
217 const int bytes_per_sample =
218 (img->fmt & AOM_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
219 unsigned char *data = img->img_data;
220
221 img->planes[AOM_PLANE_Y] =
222 data + x * bytes_per_sample + y * img->stride[AOM_PLANE_Y];
223 data += (img->h + 2 * border) * img->stride[AOM_PLANE_Y];
224
225 unsigned int uv_border_h = border >> img->y_chroma_shift;
226 unsigned int uv_x = x >> img->x_chroma_shift;
227 unsigned int uv_y = y >> img->y_chroma_shift;
228 if (!(img->fmt & AOM_IMG_FMT_UV_FLIP)) {
229 img->planes[AOM_PLANE_U] =
230 data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_U];
231 data += ((img->h >> img->y_chroma_shift) + 2 * uv_border_h) *
232 img->stride[AOM_PLANE_U];
233 img->planes[AOM_PLANE_V] =
234 data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_V];
235 } else {
236 img->planes[AOM_PLANE_V] =
237 data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_V];
238 data += ((img->h >> img->y_chroma_shift) + 2 * uv_border_h) *
239 img->stride[AOM_PLANE_V];
240 img->planes[AOM_PLANE_U] =
241 data + uv_x * bytes_per_sample + uv_y * img->stride[AOM_PLANE_U];
242 }
243 }
244 return 0;
245 }
246 return -1;
247 }
248
aom_img_flip(aom_image_t * img)249 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 img->planes[AOM_PLANE_Y] += (signed)(img->d_h - 1) * img->stride[AOM_PLANE_Y];
257 img->stride[AOM_PLANE_Y] = -img->stride[AOM_PLANE_Y];
258
259 img->planes[AOM_PLANE_U] += (signed)((img->d_h >> img->y_chroma_shift) - 1) *
260 img->stride[AOM_PLANE_U];
261 img->stride[AOM_PLANE_U] = -img->stride[AOM_PLANE_U];
262
263 img->planes[AOM_PLANE_V] += (signed)((img->d_h >> img->y_chroma_shift) - 1) *
264 img->stride[AOM_PLANE_V];
265 img->stride[AOM_PLANE_V] = -img->stride[AOM_PLANE_V];
266 }
267
aom_img_free(aom_image_t * img)268 void aom_img_free(aom_image_t *img) {
269 if (img) {
270 aom_img_remove_metadata(img);
271 if (img->img_data && img->img_data_owner) aom_free(img->img_data);
272
273 if (img->self_allocd) free(img);
274 }
275 }
276
aom_img_plane_width(const aom_image_t * img,int plane)277 int aom_img_plane_width(const aom_image_t *img, int plane) {
278 if (plane > 0 && img->x_chroma_shift > 0)
279 return (img->d_w + 1) >> img->x_chroma_shift;
280 else
281 return img->d_w;
282 }
283
aom_img_plane_height(const aom_image_t * img,int plane)284 int aom_img_plane_height(const aom_image_t *img, int plane) {
285 if (plane > 0 && img->y_chroma_shift > 0)
286 return (img->d_h + 1) >> img->y_chroma_shift;
287 else
288 return img->d_h;
289 }
290
aom_img_metadata_alloc(uint32_t type,const uint8_t * data,size_t sz,aom_metadata_insert_flags_t insert_flag)291 aom_metadata_t *aom_img_metadata_alloc(
292 uint32_t type, const uint8_t *data, size_t sz,
293 aom_metadata_insert_flags_t insert_flag) {
294 if (!data || sz == 0) return NULL;
295 aom_metadata_t *metadata = (aom_metadata_t *)malloc(sizeof(aom_metadata_t));
296 if (!metadata) return NULL;
297 metadata->type = type;
298 metadata->payload = (uint8_t *)malloc(sz);
299 if (!metadata->payload) {
300 free(metadata);
301 return NULL;
302 }
303 memcpy(metadata->payload, data, sz);
304 metadata->sz = sz;
305 metadata->insert_flag = insert_flag;
306 return metadata;
307 }
308
aom_img_metadata_free(aom_metadata_t * metadata)309 void aom_img_metadata_free(aom_metadata_t *metadata) {
310 if (metadata) {
311 if (metadata->payload) free(metadata->payload);
312 free(metadata);
313 }
314 }
315
aom_img_metadata_array_alloc(size_t sz)316 aom_metadata_array_t *aom_img_metadata_array_alloc(size_t sz) {
317 aom_metadata_array_t *arr =
318 (aom_metadata_array_t *)calloc(1, sizeof(aom_metadata_array_t));
319 if (!arr) return NULL;
320 if (sz > 0) {
321 arr->metadata_array =
322 (aom_metadata_t **)calloc(sz, sizeof(aom_metadata_t *));
323 if (!arr->metadata_array) {
324 aom_img_metadata_array_free(arr);
325 return NULL;
326 }
327 arr->sz = sz;
328 }
329 return arr;
330 }
331
aom_img_metadata_array_free(aom_metadata_array_t * arr)332 void aom_img_metadata_array_free(aom_metadata_array_t *arr) {
333 if (arr) {
334 if (arr->metadata_array) {
335 for (size_t i = 0; i < arr->sz; i++) {
336 aom_img_metadata_free(arr->metadata_array[i]);
337 }
338 free(arr->metadata_array);
339 }
340 free(arr);
341 }
342 }
343
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)344 int aom_img_add_metadata(aom_image_t *img, uint32_t type, const uint8_t *data,
345 size_t sz, aom_metadata_insert_flags_t insert_flag) {
346 if (!img) return -1;
347 if (!img->metadata) {
348 img->metadata = aom_img_metadata_array_alloc(0);
349 if (!img->metadata) return -1;
350 }
351 aom_metadata_t *metadata =
352 aom_img_metadata_alloc(type, data, sz, insert_flag);
353 if (!metadata) return -1;
354 aom_metadata_t **metadata_array =
355 (aom_metadata_t **)realloc(img->metadata->metadata_array,
356 (img->metadata->sz + 1) * sizeof(metadata));
357 if (!metadata_array) {
358 aom_img_metadata_free(metadata);
359 return -1;
360 }
361 img->metadata->metadata_array = metadata_array;
362 img->metadata->metadata_array[img->metadata->sz] = metadata;
363 img->metadata->sz++;
364 return 0;
365 }
366
aom_img_remove_metadata(aom_image_t * img)367 void aom_img_remove_metadata(aom_image_t *img) {
368 if (img && img->metadata) {
369 aom_img_metadata_array_free(img->metadata);
370 img->metadata = NULL;
371 }
372 }
373
aom_img_get_metadata(const aom_image_t * img,size_t index)374 const aom_metadata_t *aom_img_get_metadata(const aom_image_t *img,
375 size_t index) {
376 if (!img) return NULL;
377 const aom_metadata_array_t *array = img->metadata;
378 if (array && index < array->sz) {
379 return array->metadata_array[index];
380 }
381 return NULL;
382 }
383
aom_img_num_metadata(const aom_image_t * img)384 size_t aom_img_num_metadata(const aom_image_t *img) {
385 if (!img || !img->metadata) return 0;
386 return img->metadata->sz;
387 }
388