/src/libvpx/vpx/src/vpx_image.c
Line | Count | Source |
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 <assert.h> |
12 | | #include <limits.h> |
13 | | #include <stddef.h> |
14 | | #include <stdlib.h> |
15 | | #include <string.h> |
16 | | |
17 | | #include "vpx/vpx_image.h" |
18 | | #include "vpx/vpx_integer.h" |
19 | | #include "vpx_mem/vpx_mem.h" |
20 | | |
21 | 10.0k | static int is_valid_img_fmt(vpx_img_fmt_t fmt) { |
22 | 10.0k | switch (fmt) { |
23 | 0 | case VPX_IMG_FMT_YV12: |
24 | 8.98k | case VPX_IMG_FMT_I420: |
25 | 9.11k | case VPX_IMG_FMT_I422: |
26 | 9.34k | case VPX_IMG_FMT_I444: |
27 | 9.46k | case VPX_IMG_FMT_I440: |
28 | 9.46k | case VPX_IMG_FMT_NV12: |
29 | 9.68k | case VPX_IMG_FMT_I42016: |
30 | 9.97k | case VPX_IMG_FMT_I42216: |
31 | 10.0k | case VPX_IMG_FMT_I44416: |
32 | 10.0k | case VPX_IMG_FMT_I44016: return 1; |
33 | 0 | default: return 0; |
34 | 10.0k | } |
35 | 10.0k | } |
36 | | |
37 | | static vpx_image_t *img_alloc_helper(vpx_image_t *img, vpx_img_fmt_t fmt, |
38 | | unsigned int d_w, unsigned int d_h, |
39 | | unsigned int buf_align, |
40 | | unsigned int stride_align, |
41 | 10.0k | unsigned char *img_data) { |
42 | 10.0k | unsigned int h, w, xcs, ycs, bps; |
43 | 10.0k | uint64_t s; |
44 | 10.0k | int stride_in_bytes; |
45 | 10.0k | unsigned int align; |
46 | | |
47 | 10.0k | if (img != NULL) memset(img, 0, sizeof(vpx_image_t)); |
48 | | |
49 | 10.0k | if (!is_valid_img_fmt(fmt)) goto fail; |
50 | | |
51 | | /* Impose maximum values on input parameters so that this function can |
52 | | * perform arithmetic operations without worrying about overflows. |
53 | | */ |
54 | 10.0k | if (d_w > 0x08000000 || d_h > 0x08000000 || buf_align > 65536 || |
55 | 10.0k | stride_align > 65536) { |
56 | 0 | goto fail; |
57 | 0 | } |
58 | | |
59 | | /* Treat align==0 like align==1 */ |
60 | 10.0k | if (!buf_align) buf_align = 1; |
61 | | |
62 | | /* Validate alignment (must be power of 2) */ |
63 | 10.0k | if (buf_align & (buf_align - 1)) goto fail; |
64 | | |
65 | | /* Treat align==0 like align==1 */ |
66 | 10.0k | if (!stride_align) stride_align = 1; |
67 | | |
68 | | /* Validate alignment (must be power of 2) */ |
69 | 10.0k | if (stride_align & (stride_align - 1)) goto fail; |
70 | | |
71 | | /* Get sample size for this format */ |
72 | 10.0k | switch (fmt) { |
73 | 8.98k | case VPX_IMG_FMT_I420: |
74 | 8.98k | case VPX_IMG_FMT_YV12: |
75 | 8.98k | case VPX_IMG_FMT_NV12: bps = 12; break; |
76 | 137 | case VPX_IMG_FMT_I422: |
77 | 261 | case VPX_IMG_FMT_I440: bps = 16; break; |
78 | 224 | case VPX_IMG_FMT_I444: bps = 24; break; |
79 | 215 | case VPX_IMG_FMT_I42016: bps = 24; break; |
80 | 296 | case VPX_IMG_FMT_I42216: |
81 | 313 | case VPX_IMG_FMT_I44016: bps = 32; break; |
82 | 62 | case VPX_IMG_FMT_I44416: bps = 48; break; |
83 | 0 | default: bps = 16; break; |
84 | 10.0k | } |
85 | | |
86 | | /* Get chroma shift values for this format */ |
87 | 10.0k | switch (fmt) { |
88 | 8.98k | case VPX_IMG_FMT_I420: |
89 | 8.98k | case VPX_IMG_FMT_YV12: |
90 | 8.98k | case VPX_IMG_FMT_NV12: |
91 | 9.11k | case VPX_IMG_FMT_I422: |
92 | 9.33k | case VPX_IMG_FMT_I42016: |
93 | 9.63k | case VPX_IMG_FMT_I42216: xcs = 1; break; |
94 | 427 | default: xcs = 0; break; |
95 | 10.0k | } |
96 | | |
97 | 10.0k | switch (fmt) { |
98 | 8.98k | case VPX_IMG_FMT_I420: |
99 | 8.98k | case VPX_IMG_FMT_NV12: |
100 | 9.10k | case VPX_IMG_FMT_I440: |
101 | 9.10k | case VPX_IMG_FMT_YV12: |
102 | 9.32k | case VPX_IMG_FMT_I42016: |
103 | 9.33k | case VPX_IMG_FMT_I44016: ycs = 1; break; |
104 | 719 | default: ycs = 0; break; |
105 | 10.0k | } |
106 | | |
107 | | /* Calculate storage sizes. */ |
108 | 10.0k | if (img_data) { |
109 | | /* If the buffer was allocated externally, the width and height shouldn't |
110 | | * be adjusted. */ |
111 | 10.0k | w = d_w; |
112 | 10.0k | h = d_h; |
113 | 10.0k | } else { |
114 | | /* Calculate storage sizes given the chroma subsampling */ |
115 | 0 | align = (1 << xcs) - 1; |
116 | 0 | w = (d_w + align) & ~align; |
117 | 0 | assert(d_w <= w); |
118 | 0 | align = (1 << ycs) - 1; |
119 | 0 | h = (d_h + align) & ~align; |
120 | 0 | assert(d_h <= h); |
121 | 0 | } |
122 | | |
123 | 10.0k | s = (fmt & VPX_IMG_FMT_PLANAR) ? w : (uint64_t)bps * w / 8; |
124 | 10.0k | s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s * 2 : s; |
125 | 10.0k | s = (s + stride_align - 1) & ~((uint64_t)stride_align - 1); |
126 | 10.0k | if (s > INT_MAX) goto fail; |
127 | 10.0k | stride_in_bytes = (int)s; |
128 | 10.0k | uint64_t uv_s = s; |
129 | | // Convert to number of samples. |
130 | 10.0k | uv_s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? uv_s / 2 : uv_s; |
131 | | // Apply chroma subsampling. |
132 | 10.0k | uv_s = (uv_s + xcs) >> xcs; |
133 | | // Convert back to number of bytes. |
134 | 10.0k | uv_s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? uv_s * 2 : uv_s; |
135 | 10.0k | const int uv_stride_in_bytes = (int)uv_s; |
136 | | |
137 | | /* Allocate the new image */ |
138 | 10.0k | if (!img) { |
139 | 0 | img = (vpx_image_t *)calloc(1, sizeof(vpx_image_t)); |
140 | |
|
141 | 0 | if (!img) goto fail; |
142 | | |
143 | 0 | img->self_allocd = 1; |
144 | 0 | } |
145 | | |
146 | 10.0k | img->img_data = img_data; |
147 | | |
148 | 10.0k | if (!img_data) { |
149 | 0 | uint64_t alloc_size; |
150 | 0 | s = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? s / 2 : s; |
151 | 0 | alloc_size = (fmt & VPX_IMG_FMT_PLANAR) ? (uint64_t)h * s * bps / 8 |
152 | 0 | : (uint64_t)h * s; |
153 | |
|
154 | 0 | if (alloc_size != (size_t)alloc_size) goto fail; |
155 | | |
156 | 0 | img->img_data = (uint8_t *)vpx_memalign(buf_align, (size_t)alloc_size); |
157 | 0 | img->img_data_owner = 1; |
158 | 0 | } |
159 | | |
160 | 10.0k | if (!img->img_data) goto fail; |
161 | | |
162 | 10.0k | img->fmt = fmt; |
163 | 10.0k | img->bit_depth = (fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 16 : 8; |
164 | 10.0k | img->w = w; |
165 | 10.0k | img->h = h; |
166 | 10.0k | img->x_chroma_shift = xcs; |
167 | 10.0k | img->y_chroma_shift = ycs; |
168 | 10.0k | img->bps = bps; |
169 | | |
170 | | /* Calculate strides */ |
171 | 10.0k | img->stride[VPX_PLANE_Y] = img->stride[VPX_PLANE_ALPHA] = stride_in_bytes; |
172 | 10.0k | img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = uv_stride_in_bytes; |
173 | | |
174 | 10.0k | if (fmt == VPX_IMG_FMT_NV12) { |
175 | 0 | img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = stride_in_bytes; |
176 | 0 | } |
177 | | |
178 | 10.0k | if (fmt == VPX_IMG_FMT_NV12) { |
179 | 0 | img->stride[VPX_PLANE_U] = img->stride[VPX_PLANE_V] = stride_in_bytes; |
180 | 0 | } |
181 | | |
182 | | /* Default viewport to entire image. (This vpx_img_set_rect call always |
183 | | * succeeds.) */ |
184 | 10.0k | int ret = vpx_img_set_rect(img, 0, 0, d_w, d_h); |
185 | 10.0k | assert(ret == 0); |
186 | 10.0k | (void)ret; |
187 | 10.0k | return img; |
188 | | |
189 | 0 | fail: |
190 | 0 | vpx_img_free(img); |
191 | 0 | return NULL; |
192 | 10.0k | } |
193 | | |
194 | | vpx_image_t *vpx_img_alloc(vpx_image_t *img, vpx_img_fmt_t fmt, |
195 | | unsigned int d_w, unsigned int d_h, |
196 | 0 | unsigned int align) { |
197 | 0 | return img_alloc_helper(img, fmt, d_w, d_h, align, align, NULL); |
198 | 0 | } |
199 | | |
200 | | vpx_image_t *vpx_img_wrap(vpx_image_t *img, vpx_img_fmt_t fmt, unsigned int d_w, |
201 | | unsigned int d_h, unsigned int stride_align, |
202 | 10.0k | unsigned char *img_data) { |
203 | | /* Set buf_align = 1. It is ignored by img_alloc_helper because img_data is |
204 | | * not NULL. */ |
205 | 10.0k | return img_alloc_helper(img, fmt, d_w, d_h, 1, stride_align, img_data); |
206 | 10.0k | } |
207 | | |
208 | 0 | static void negate_strides(vpx_image_t *img) { |
209 | 0 | img->stride[VPX_PLANE_Y] = -img->stride[VPX_PLANE_Y]; |
210 | 0 | if (img->fmt & VPX_IMG_FMT_PLANAR) { |
211 | 0 | img->stride[VPX_PLANE_U] = -img->stride[VPX_PLANE_U]; |
212 | 0 | img->stride[VPX_PLANE_V] = -img->stride[VPX_PLANE_V]; |
213 | 0 | if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) { |
214 | 0 | img->stride[VPX_PLANE_ALPHA] = -img->stride[VPX_PLANE_ALPHA]; |
215 | 0 | } |
216 | 0 | } |
217 | 0 | } |
218 | | |
219 | | int vpx_img_set_rect(vpx_image_t *img, unsigned int x, unsigned int y, |
220 | 10.0k | unsigned int w, unsigned int h) { |
221 | 10.0k | if (x <= UINT_MAX - w && x + w <= img->w && y <= UINT_MAX - h && |
222 | 10.0k | y + h <= img->h) { |
223 | 10.0k | const int flipped = img->stride[VPX_PLANE_Y] < 0; |
224 | | /* Plane offsets are calculated from the unflipped allocation base. */ |
225 | 10.0k | if (flipped) negate_strides(img); |
226 | | |
227 | 10.0k | img->d_w = w; |
228 | 10.0k | img->d_h = h; |
229 | | |
230 | | /* Calculate plane pointers */ |
231 | 10.0k | if (!(img->fmt & VPX_IMG_FMT_PLANAR)) { |
232 | 0 | img->planes[VPX_PLANE_PACKED] = |
233 | 0 | img->img_data + x * img->bps / 8 + y * img->stride[VPX_PLANE_PACKED]; |
234 | 10.0k | } else { |
235 | 10.0k | const int bytes_per_sample = |
236 | 10.0k | (img->fmt & VPX_IMG_FMT_HIGHBITDEPTH) ? 2 : 1; |
237 | 10.0k | unsigned char *data = img->img_data; |
238 | | |
239 | 10.0k | if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) { |
240 | 0 | img->planes[VPX_PLANE_ALPHA] = |
241 | 0 | data + x * bytes_per_sample + y * img->stride[VPX_PLANE_ALPHA]; |
242 | 0 | data += (size_t)img->h * img->stride[VPX_PLANE_ALPHA]; |
243 | 0 | } |
244 | | |
245 | 10.0k | img->planes[VPX_PLANE_Y] = |
246 | 10.0k | data + x * bytes_per_sample + y * img->stride[VPX_PLANE_Y]; |
247 | 10.0k | data += (size_t)img->h * img->stride[VPX_PLANE_Y]; |
248 | | |
249 | 10.0k | unsigned int uv_x = x >> img->x_chroma_shift; |
250 | 10.0k | unsigned int uv_y = y >> img->y_chroma_shift; |
251 | 10.0k | if (img->fmt == VPX_IMG_FMT_NV12) { |
252 | 0 | img->planes[VPX_PLANE_U] = data + uv_x * bytes_per_sample * 2 + |
253 | 0 | uv_y * img->stride[VPX_PLANE_U]; |
254 | 0 | img->planes[VPX_PLANE_V] = img->planes[VPX_PLANE_U] + bytes_per_sample; |
255 | 10.0k | } else if (!(img->fmt & VPX_IMG_FMT_UV_FLIP)) { |
256 | 10.0k | img->planes[VPX_PLANE_U] = |
257 | 10.0k | data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_U]; |
258 | 10.0k | data += |
259 | 10.0k | (size_t)(img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_U]; |
260 | 10.0k | img->planes[VPX_PLANE_V] = |
261 | 10.0k | data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_V]; |
262 | 10.0k | } else { |
263 | 0 | img->planes[VPX_PLANE_V] = |
264 | 0 | data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_V]; |
265 | 0 | data += |
266 | 0 | (size_t)(img->h >> img->y_chroma_shift) * img->stride[VPX_PLANE_V]; |
267 | 0 | img->planes[VPX_PLANE_U] = |
268 | 0 | data + uv_x * bytes_per_sample + uv_y * img->stride[VPX_PLANE_U]; |
269 | 0 | } |
270 | 10.0k | } |
271 | 10.0k | if (flipped) { |
272 | 0 | if (h == 0) { |
273 | | /* An empty image has no last row for vpx_img_flip() to select. */ |
274 | 0 | negate_strides(img); |
275 | 0 | } else { |
276 | 0 | vpx_img_flip(img); |
277 | 0 | } |
278 | 0 | } |
279 | 10.0k | return 0; |
280 | 10.0k | } |
281 | 0 | return -1; |
282 | 10.0k | } |
283 | | |
284 | 0 | void vpx_img_flip(vpx_image_t *img) { |
285 | 0 | const unsigned int chroma_height = |
286 | 0 | (img->d_h + img->y_chroma_shift) >> img->y_chroma_shift; |
287 | |
|
288 | 0 | img->planes[VPX_PLANE_Y] += |
289 | 0 | (ptrdiff_t)(img->d_h - 1) * img->stride[VPX_PLANE_Y]; |
290 | 0 | img->stride[VPX_PLANE_Y] = -img->stride[VPX_PLANE_Y]; |
291 | |
|
292 | 0 | img->planes[VPX_PLANE_U] += |
293 | 0 | (ptrdiff_t)(chroma_height - 1) * img->stride[VPX_PLANE_U]; |
294 | 0 | img->stride[VPX_PLANE_U] = -img->stride[VPX_PLANE_U]; |
295 | |
|
296 | 0 | img->planes[VPX_PLANE_V] += |
297 | 0 | (ptrdiff_t)(chroma_height - 1) * img->stride[VPX_PLANE_V]; |
298 | 0 | img->stride[VPX_PLANE_V] = -img->stride[VPX_PLANE_V]; |
299 | |
|
300 | 0 | if (img->fmt & VPX_IMG_FMT_HAS_ALPHA) { |
301 | 0 | img->planes[VPX_PLANE_ALPHA] += |
302 | 0 | (ptrdiff_t)(img->d_h - 1) * img->stride[VPX_PLANE_ALPHA]; |
303 | 0 | img->stride[VPX_PLANE_ALPHA] = -img->stride[VPX_PLANE_ALPHA]; |
304 | 0 | } |
305 | 0 | } |
306 | | |
307 | 0 | void vpx_img_free(vpx_image_t *img) { |
308 | 0 | if (img) { |
309 | 0 | if (img->img_data && img->img_data_owner) vpx_free(img->img_data); |
310 | |
|
311 | 0 | if (img->self_allocd) free(img); |
312 | 0 | } |
313 | 0 | } |