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
13#include "./vpx_config.h"
14#include "vpx_scale/yv12config.h"
15#include "vpx_mem/vpx_mem.h"
16#if CONFIG_VP9 && CONFIG_VP9_HIGHBITDEPTH
17#include "vp9/common/vp9_common.h"
18#endif
19
20/****************************************************************************
21*  Exports
22****************************************************************************/
23
24/****************************************************************************
25 *
26 ****************************************************************************/
27#define yv12_align_addr(addr, align) \
28    (void*)(((size_t)(addr) + ((align) - 1)) & (size_t)-(align))
29
30int
31vp8_yv12_de_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf) {
32  if (ybf) {
33    // If libvpx is using frame buffer callbacks then buffer_alloc_sz must
34    // not be set.
35    if (ybf->buffer_alloc_sz > 0) {
36      vpx_free(ybf->buffer_alloc);
37    }
38
39    /* buffer_alloc isn't accessed by most functions.  Rather y_buffer,
40      u_buffer and v_buffer point to buffer_alloc and are used.  Clear out
41      all of this so that a freed pointer isn't inadvertently used */
42    vpx_memset(ybf, 0, sizeof(YV12_BUFFER_CONFIG));
43  } else {
44    return -1;
45  }
46
47  return 0;
48}
49
50int vp8_yv12_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
51                                  int width, int height, int border) {
52  if (ybf) {
53    int aligned_width = (width + 15) & ~15;
54    int aligned_height = (height + 15) & ~15;
55    int y_stride = ((aligned_width + 2 * border) + 31) & ~31;
56    int yplane_size = (aligned_height + 2 * border) * y_stride;
57    int uv_width = aligned_width >> 1;
58    int uv_height = aligned_height >> 1;
59    /** There is currently a bunch of code which assumes
60      *  uv_stride == y_stride/2, so enforce this here. */
61    int uv_stride = y_stride >> 1;
62    int uvplane_size = (uv_height + border) * uv_stride;
63    const int frame_size = yplane_size + 2 * uvplane_size;
64
65    if (!ybf->buffer_alloc) {
66      ybf->buffer_alloc = (uint8_t *)vpx_memalign(32, frame_size);
67      ybf->buffer_alloc_sz = frame_size;
68    }
69
70    if (!ybf->buffer_alloc || ybf->buffer_alloc_sz < frame_size)
71      return -1;
72
73    /* Only support allocating buffers that have a border that's a multiple
74     * of 32. The border restriction is required to get 16-byte alignment of
75     * the start of the chroma rows without introducing an arbitrary gap
76     * between planes, which would break the semantics of things like
77     * vpx_img_set_rect(). */
78    if (border & 0x1f)
79      return -3;
80
81    ybf->y_crop_width = width;
82    ybf->y_crop_height = height;
83    ybf->y_width  = aligned_width;
84    ybf->y_height = aligned_height;
85    ybf->y_stride = y_stride;
86
87    ybf->uv_crop_width = (width + 1) / 2;
88    ybf->uv_crop_height = (height + 1) / 2;
89    ybf->uv_width = uv_width;
90    ybf->uv_height = uv_height;
91    ybf->uv_stride = uv_stride;
92
93    ybf->alpha_width = 0;
94    ybf->alpha_height = 0;
95    ybf->alpha_stride = 0;
96
97    ybf->border = border;
98    ybf->frame_size = frame_size;
99
100    ybf->y_buffer = ybf->buffer_alloc + (border * y_stride) + border;
101    ybf->u_buffer = ybf->buffer_alloc + yplane_size + (border / 2  * uv_stride) + border / 2;
102    ybf->v_buffer = ybf->buffer_alloc + yplane_size + uvplane_size + (border / 2  * uv_stride) + border / 2;
103    ybf->alpha_buffer = NULL;
104
105    ybf->corrupted = 0; /* assume not currupted by errors */
106    return 0;
107  }
108  return -2;
109}
110
111int vp8_yv12_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
112                                int width, int height, int border) {
113  if (ybf) {
114    vp8_yv12_de_alloc_frame_buffer(ybf);
115    return vp8_yv12_realloc_frame_buffer(ybf, width, height, border);
116  }
117  return -2;
118}
119
120#if CONFIG_VP9
121// TODO(jkoleszar): Maybe replace this with struct vpx_image
122
123int vp9_free_frame_buffer(YV12_BUFFER_CONFIG *ybf) {
124  if (ybf) {
125    if (ybf->buffer_alloc_sz > 0) {
126      vpx_free(ybf->buffer_alloc);
127    }
128
129    /* buffer_alloc isn't accessed by most functions.  Rather y_buffer,
130      u_buffer and v_buffer point to buffer_alloc and are used.  Clear out
131      all of this so that a freed pointer isn't inadvertently used */
132    vpx_memset(ybf, 0, sizeof(YV12_BUFFER_CONFIG));
133  } else {
134    return -1;
135  }
136
137  return 0;
138}
139
140int vp9_realloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
141                             int width, int height,
142                             int ss_x, int ss_y,
143#if CONFIG_VP9_HIGHBITDEPTH
144                             int use_highbitdepth,
145#endif
146                             int border,
147                             vpx_codec_frame_buffer_t *fb,
148                             vpx_get_frame_buffer_cb_fn_t cb,
149                             void *cb_priv) {
150  if (ybf) {
151    const int aligned_width = (width + 7) & ~7;
152    const int aligned_height = (height + 7) & ~7;
153    const int y_stride = ((aligned_width + 2 * border) + 31) & ~31;
154    const uint64_t yplane_size = (aligned_height + 2 * border) *
155                                 (uint64_t)y_stride;
156    const int uv_width = aligned_width >> ss_x;
157    const int uv_height = aligned_height >> ss_y;
158    const int uv_stride = y_stride >> ss_x;
159    const int uv_border_w = border >> ss_x;
160    const int uv_border_h = border >> ss_y;
161    const uint64_t uvplane_size = (uv_height + 2 * uv_border_h) *
162                                  (uint64_t)uv_stride;
163#if CONFIG_ALPHA
164    const int alpha_width = aligned_width;
165    const int alpha_height = aligned_height;
166    const int alpha_stride = y_stride;
167    const int alpha_border_w = border;
168    const int alpha_border_h = border;
169    const uint64_t alpha_plane_size = (alpha_height + 2 * alpha_border_h) *
170                                      (uint64_t)alpha_stride;
171#if CONFIG_VP9_HIGHBITDEPTH
172    const uint64_t frame_size = (1 + use_highbitdepth) *
173        (yplane_size + 2 * uvplane_size + alpha_plane_size);
174#else
175    const uint64_t frame_size = yplane_size + 2 * uvplane_size +
176                                alpha_plane_size;
177#endif  // CONFIG_VP9_HIGHBITDEPTH
178#else
179#if CONFIG_VP9_HIGHBITDEPTH
180    const uint64_t frame_size =
181        (1 + use_highbitdepth) * (yplane_size + 2 * uvplane_size);
182#else
183    const uint64_t frame_size = yplane_size + 2 * uvplane_size;
184#endif  // CONFIG_VP9_HIGHBITDEPTH
185#endif  // CONFIG_ALPHA
186    if (cb != NULL) {
187      const int align_addr_extra_size = 31;
188      const uint64_t external_frame_size = frame_size + align_addr_extra_size;
189
190      assert(fb != NULL);
191
192      if (external_frame_size != (size_t)external_frame_size)
193        return -1;
194
195      // Allocation to hold larger frame, or first allocation.
196      if (cb(cb_priv, (size_t)external_frame_size, fb) < 0)
197        return -1;
198
199      if (fb->data == NULL || fb->size < external_frame_size)
200        return -1;
201
202      ybf->buffer_alloc = (uint8_t *)yv12_align_addr(fb->data, 32);
203    } else if (frame_size > (size_t)ybf->buffer_alloc_sz) {
204      // Allocation to hold larger frame, or first allocation.
205      vpx_free(ybf->buffer_alloc);
206      ybf->buffer_alloc = NULL;
207
208      if (frame_size != (size_t)frame_size)
209        return -1;
210
211      ybf->buffer_alloc = (uint8_t *)vpx_memalign(32, (size_t)frame_size);
212      if (!ybf->buffer_alloc)
213        return -1;
214
215      ybf->buffer_alloc_sz = (int)frame_size;
216
217      // This memset is needed for fixing valgrind error from C loop filter
218      // due to access uninitialized memory in frame border. It could be
219      // removed if border is totally removed.
220      vpx_memset(ybf->buffer_alloc, 0, ybf->buffer_alloc_sz);
221    }
222
223    /* Only support allocating buffers that have a border that's a multiple
224     * of 32. The border restriction is required to get 16-byte alignment of
225     * the start of the chroma rows without introducing an arbitrary gap
226     * between planes, which would break the semantics of things like
227     * vpx_img_set_rect(). */
228    if (border & 0x1f)
229      return -3;
230
231    ybf->y_crop_width = width;
232    ybf->y_crop_height = height;
233    ybf->y_width  = aligned_width;
234    ybf->y_height = aligned_height;
235    ybf->y_stride = y_stride;
236
237    ybf->uv_crop_width = (width + ss_x) >> ss_x;
238    ybf->uv_crop_height = (height + ss_y) >> ss_y;
239    ybf->uv_width = uv_width;
240    ybf->uv_height = uv_height;
241    ybf->uv_stride = uv_stride;
242
243    ybf->border = border;
244    ybf->frame_size = (int)frame_size;
245
246#if CONFIG_VP9_HIGHBITDEPTH
247    if (use_highbitdepth) {
248      // Store uint16 addresses when using 16bit framebuffers
249      uint8_t *p = CONVERT_TO_BYTEPTR(ybf->buffer_alloc);
250      ybf->y_buffer = p + (border * y_stride) + border;
251      ybf->u_buffer = p + yplane_size +
252          (uv_border_h * uv_stride) + uv_border_w;
253      ybf->v_buffer = p + yplane_size + uvplane_size +
254          (uv_border_h * uv_stride) + uv_border_w;
255      ybf->flags = YV12_FLAG_HIGHBITDEPTH;
256    } else {
257      ybf->y_buffer = ybf->buffer_alloc + (border * y_stride) + border;
258      ybf->u_buffer = ybf->buffer_alloc + yplane_size +
259          (uv_border_h * uv_stride) + uv_border_w;
260      ybf->v_buffer = ybf->buffer_alloc + yplane_size + uvplane_size +
261          (uv_border_h * uv_stride) + uv_border_w;
262      ybf->flags = 0;
263    }
264#else
265    ybf->y_buffer = ybf->buffer_alloc + (border * y_stride) + border;
266    ybf->u_buffer = ybf->buffer_alloc + yplane_size +
267                    (uv_border_h * uv_stride) + uv_border_w;
268    ybf->v_buffer = ybf->buffer_alloc + yplane_size + uvplane_size +
269                    (uv_border_h * uv_stride) + uv_border_w;
270#endif  // CONFIG_VP9_HIGHBITDEPTH
271
272#if CONFIG_ALPHA
273    ybf->alpha_width = alpha_width;
274    ybf->alpha_height = alpha_height;
275    ybf->alpha_stride = alpha_stride;
276    ybf->alpha_buffer = ybf->buffer_alloc + yplane_size + 2 * uvplane_size +
277                        (alpha_border_h * alpha_stride) + alpha_border_w;
278#endif
279    ybf->corrupted = 0; /* assume not corrupted by errors */
280    return 0;
281  }
282  return -2;
283}
284
285int vp9_alloc_frame_buffer(YV12_BUFFER_CONFIG *ybf,
286                           int width, int height,
287                           int ss_x, int ss_y,
288#if CONFIG_VP9_HIGHBITDEPTH
289                           int use_highbitdepth,
290#endif
291                           int border) {
292  if (ybf) {
293    vp9_free_frame_buffer(ybf);
294    return vp9_realloc_frame_buffer(ybf, width, height, ss_x, ss_y,
295#if CONFIG_VP9_HIGHBITDEPTH
296                                    use_highbitdepth,
297#endif
298                                    border, NULL, NULL, NULL);
299  }
300  return -2;
301}
302#endif
303