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 <stdlib.h>
12#include <string.h>
13
14#include "vpx/vpx_image.h"
15#include "vpx/vpx_integer.h"
16#include "vpx_mem/vpx_mem.h"
17
18static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt,
19                                     unsigned int d_w, unsigned int d_h,
20                                     unsigned int buf_align,
21                                     unsigned int stride_align,
22                                     unsigned char *img_data) {
23  unsigned int h, w, s, xcs, ycs, bps;
24  unsigned int stride_in_bytes;
25  int align;
26
27  /* Treat align==0 like align==1 */
28  if (!buf_align) buf_align = 1;
29
30  /* Validate alignment (must be power of 2) */
31  if (buf_align & (buf_align - 1)) goto fail;
32
33  /* Treat align==0 like align==1 */
34  if (!stride_align) stride_align = 1;
35
36  /* Validate alignment (must be power of 2) */
37  if (stride_align & (stride_align - 1)) goto fail;
38
39  /* Get sample size for this format */
40  switch (fmt) {
41    case VPX_IMG_FMT_RGB32:
42    case VPX_IMG_FMT_RGB32_LE:
43    case VPX_IMG_FMT_ARGB:
44    case VPX_IMG_FMT_ARGB_LE: bps = 32; break;
45    case VPX_IMG_FMT_RGB24:
46    case VPX_IMG_FMT_BGR24: bps = 24; break;
47    case VPX_IMG_FMT_RGB565:
48    case VPX_IMG_FMT_RGB565_LE:
49    case VPX_IMG_FMT_RGB555:
50    case VPX_IMG_FMT_RGB555_LE:
51    case VPX_IMG_FMT_UYVY:
52    case VPX_IMG_FMT_YUY2:
53    case VPX_IMG_FMT_YVYU: bps = 16; break;
54    case VPX_IMG_FMT_I420:
55    case VPX_IMG_FMT_YV12:
56    case VPX_IMG_FMT_VPXI420:
57    case VPX_IMG_FMT_VPXYV12: bps = 12; break;
58    case VPX_IMG_FMT_I422:
59    case VPX_IMG_FMT_I440: bps = 16; break;
60    case VPX_IMG_FMT_I444: bps = 24; break;
61    case VPX_IMG_FMT_I42016: bps = 24; break;
62    case VPX_IMG_FMT_I42216:
63    case VPX_IMG_FMT_I44016: bps = 32; break;
64    case VPX_IMG_FMT_I44416: bps = 48; break;
65    default: bps = 16; break;
66  }
67
68  /* Get chroma shift values for this format */
69  switch (fmt) {
70    case VPX_IMG_FMT_I420:
71    case VPX_IMG_FMT_YV12:
72    case VPX_IMG_FMT_VPXI420:
73    case VPX_IMG_FMT_VPXYV12:
74    case VPX_IMG_FMT_I422:
75    case VPX_IMG_FMT_I42016:
76    case VPX_IMG_FMT_I42216: xcs = 1; break;
77    default: xcs = 0; break;
78  }
79
80  switch (fmt) {
81    case VPX_IMG_FMT_I420:
82    case VPX_IMG_FMT_I440:
83    case VPX_IMG_FMT_YV12:
84    case VPX_IMG_FMT_VPXI420:
85    case VPX_IMG_FMT_VPXYV12:
86    case VPX_IMG_FMT_I42016:
87    case VPX_IMG_FMT_I44016: ycs = 1; break;
88    default: ycs = 0; break;
89  }
90
91  /* Calculate storage sizes given the chroma subsampling */
92  align = (1 << xcs) - 1;
93  w = (d_w + align) & ~align;
94  align = (1 << ycs) - 1;
95  h = (d_h + align) & ~align;
96  s = (fmt & VPX_IMG_FMT_PLANAR) ? w : bps * w / 8;
97  s = (s + stride_align - 1) & ~(stride_align - 1);
98  stride_in_bytes = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s;
99
100  /* Allocate the new image */
101  if (!img) {
102    img = (vpx_image_t *)calloc(1, sizeof(vpx_image_t));
103
104    if (!img) goto fail;
105
106    img->self_allocd = 1;
107  } else {
108    memset(img, 0, sizeof(vpx_image_t));
109  }
110
111  img->img_data = img_data;
112
113  if (!img_data) {
114    const uint64_t alloc_size = (fmt & VPX_IMG_FMT_PLANAR)
115                                    ? (uint64_t)h * s * bps / 8
116                                    : (uint64_t)h * s;
117
118    if (alloc_size != (size_t)alloc_size) goto fail;
119
120    img->img_data = (uint8_t *)vpx_memalign(buf_align, (size_t)alloc_size);
121    img->img_data_owner = 1;
122  }
123
124  if (!img->img_data) goto fail;
125
126  img->fmt = fmt;
127  img->bit_depth = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 16 : 8;
128  img->w = w;
129  img->h = h;
130  img->x_chroma_shift = xcs;
131  img->y_chroma_shift = ycs;
132  img->bps = bps;
133
134  /* Calculate strides */
135  img->stride[VPX_PLANE_Y] = img->stride[VPX_PLANE_ALPHA] = stride_in_bytes;
136  img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = stride_in_bytes >> xcs;
137
138  /* Default viewport to entire image */
139  if (!vpx_img_set_rect(img, 0, 0, d_w, d_h)) return img;
140
141fail:
142  vpx_img_free(img);
143  return NULL;
144}
145
146vpx_image_t *vpx_img_alloc(vpx_image_t *img, vpx_img_fmt_t fmt,
147                           unsigned int d_w, unsigned int d_h,
148                           unsigned int align) {
149  return img_alloc_helper(img, fmt, d_w, d_h, align, align, NULL);
150}
151
152vpx_image_t *vpx_img_wrap(vpx_image_t *img, vpx_img_fmt_t fmt, unsigned int d_w,
153                          unsigned int d_h, unsigned int stride_align,
154                          unsigned char *img_data) {
155  /* By setting buf_align = 1, we don't change buffer alignment in this
156   * function. */
157  return img_alloc_helper(img, fmt, d_w, d_h, 1, stride_align, img_data);
158}
159
160int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y,
161                     unsigned int w, unsigned int h) {
162  unsigned char *data;
163
164  if (x + w <= img->w && y + h <= img->h) {
165    img->d_w = w;
166    img->d_h = h;
167
168    /* Calculate plane pointers */
169    if (!(img->fmt & VPX_IMG_FMT_PLANAR)) {
170      img->planes[VPX_PLANE_PACKED] =
171          img->img_data + x * img->bps / 8 + y * img->stride[VPX_PLANE_PACKED];
172    } else {
173      const int bytes_per_sample =
174          (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1;
175      data = img->img_data;
176
177      if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) {
178        img->planes[VPX_PLANE_ALPHA] =
179            data + x * bytes_per_sample + y * img->stride[VPX_PLANE_ALPHA];
180        data += img->h * img->stride[VPX_PLANE_ALPHA];
181      }
182
183      img->planes[VPX_PLANE_Y] =
184          data + x * bytes_per_sample + y * img->stride[VPX_PLANE_Y];
185      data += img->h * img->stride[VPX_PLANE_Y];
186
187      if (!(img->fmt & VPX_IMG_FMT_UV_FLIP)) {
188        img->planes[VPX_PLANE_U] =
189            data + (x >> img->x_chroma_shift) * bytes_per_sample +
190            (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U];
191        data += (img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_U];
192        img->planes[VPX_PLANE_V] =
193            data + (x >> img->x_chroma_shift) * bytes_per_sample +
194            (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_V];
195      } else {
196        img->planes[VPX_PLANE_V] =
197            data + (x >> img->x_chroma_shift) * bytes_per_sample +
198            (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_V];
199        data += (img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_V];
200        img->planes[VPX_PLANE_U] =
201            data + (x >> img->x_chroma_shift) * bytes_per_sample +
202            (y >> img->y_chroma_shift) * img->stride[VPX_PLANE_U];
203      }
204    }
205    return 0;
206  }
207  return -1;
208}
209
210void vpx_img_flip(vpx_image_t *img) {
211  /* Note: In the calculation pointer adjustment calculation, we want the
212   * rhs to be promoted to a signed type. Section 6.3.1.8 of the ISO C99
213   * standard indicates that if the adjustment parameter is unsigned, the
214   * stride parameter will be promoted to unsigned, causing errors when
215   * the lhs is a larger type than the rhs.
216   */
217  img->planes[VPX_PLANE_Y] += (signed)(img->d_h - 1) * img->stride[VPX_PLANE_Y];
218  img->stride[VPX_PLANE_Y] = -img->stride[VPX_PLANE_Y];
219
220  img->planes[VPX_PLANE_U] += (signed)((img->d_h >> img->y_chroma_shift) - 1) *
221                              img->stride[VPX_PLANE_U];
222  img->stride[VPX_PLANE_U] = -img->stride[VPX_PLANE_U];
223
224  img->planes[VPX_PLANE_V] += (signed)((img->d_h >> img->y_chroma_shift) - 1) *
225                              img->stride[VPX_PLANE_V];
226  img->stride[VPX_PLANE_V] = -img->stride[VPX_PLANE_V];
227
228  img->planes[VPX_PLANE_ALPHA] +=
229      (signed)(img->d_h - 1) * img->stride[VPX_PLANE_ALPHA];
230  img->stride[VPX_PLANE_ALPHA] = -img->stride[VPX_PLANE_ALPHA];
231}
232
233void vpx_img_free(vpx_image_t *img) {
234  if (img) {
235    if (img->img_data && img->img_data_owner) vpx_free(img->img_data);
236
237    if (img->self_allocd) free(img);
238  }
239}
240