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