1// Copyright 2014 Google Inc. All Rights Reserved.
2//
3// Use of this source code is governed by a BSD-style license
4// that can be found in the COPYING file in the root of the source
5// tree. An additional intellectual property rights grant can be found
6// in the file PATENTS. All contributing project authors may
7// be found in the AUTHORS file in the root of the source tree.
8// -----------------------------------------------------------------------------
9//
10// WebPPicture tools: copy, crop, rescaling and view.
11//
12// Author: Skal (pascal.massimino@gmail.com)
13
14#include <assert.h>
15#include <stdlib.h>
16
17#include "./vp8i_enc.h"
18#include "../utils/rescaler_utils.h"
19#include "../utils/utils.h"
20
21#define HALVE(x) (((x) + 1) >> 1)
22
23// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them
24// into 'dst'. Mark 'dst' as not owning any memory.
25static void PictureGrabSpecs(const WebPPicture* const src,
26                             WebPPicture* const dst) {
27  assert(src != NULL && dst != NULL);
28  *dst = *src;
29  WebPPictureResetBuffers(dst);
30}
31
32//------------------------------------------------------------------------------
33
34// Adjust top-left corner to chroma sample position.
35static void SnapTopLeftPosition(const WebPPicture* const pic,
36                                int* const left, int* const top) {
37  if (!pic->use_argb) {
38    *left &= ~1;
39    *top &= ~1;
40  }
41}
42
43// Adjust top-left corner and verify that the sub-rectangle is valid.
44static int AdjustAndCheckRectangle(const WebPPicture* const pic,
45                                   int* const left, int* const top,
46                                   int width, int height) {
47  SnapTopLeftPosition(pic, left, top);
48  if ((*left) < 0 || (*top) < 0) return 0;
49  if (width <= 0 || height <= 0) return 0;
50  if ((*left) + width > pic->width) return 0;
51  if ((*top) + height > pic->height) return 0;
52  return 1;
53}
54
55int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) {
56  if (src == NULL || dst == NULL) return 0;
57  if (src == dst) return 1;
58
59  PictureGrabSpecs(src, dst);
60  if (!WebPPictureAlloc(dst)) return 0;
61
62  if (!src->use_argb) {
63    WebPCopyPlane(src->y, src->y_stride,
64                  dst->y, dst->y_stride, dst->width, dst->height);
65    WebPCopyPlane(src->u, src->uv_stride, dst->u, dst->uv_stride,
66                  HALVE(dst->width), HALVE(dst->height));
67    WebPCopyPlane(src->v, src->uv_stride, dst->v, dst->uv_stride,
68                  HALVE(dst->width), HALVE(dst->height));
69    if (dst->a != NULL)  {
70      WebPCopyPlane(src->a, src->a_stride,
71                    dst->a, dst->a_stride, dst->width, dst->height);
72    }
73  } else {
74    WebPCopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride,
75                  (uint8_t*)dst->argb, 4 * dst->argb_stride,
76                  4 * dst->width, dst->height);
77  }
78  return 1;
79}
80
81int WebPPictureIsView(const WebPPicture* picture) {
82  if (picture == NULL) return 0;
83  if (picture->use_argb) {
84    return (picture->memory_argb_ == NULL);
85  }
86  return (picture->memory_ == NULL);
87}
88
89int WebPPictureView(const WebPPicture* src,
90                    int left, int top, int width, int height,
91                    WebPPicture* dst) {
92  if (src == NULL || dst == NULL) return 0;
93
94  // verify rectangle position.
95  if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0;
96
97  if (src != dst) {  // beware of aliasing! We don't want to leak 'memory_'.
98    PictureGrabSpecs(src, dst);
99  }
100  dst->width = width;
101  dst->height = height;
102  if (!src->use_argb) {
103    dst->y = src->y + top * src->y_stride + left;
104    dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1);
105    dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1);
106    dst->y_stride = src->y_stride;
107    dst->uv_stride = src->uv_stride;
108    if (src->a != NULL) {
109      dst->a = src->a + top * src->a_stride + left;
110      dst->a_stride = src->a_stride;
111    }
112  } else {
113    dst->argb = src->argb + top * src->argb_stride + left;
114    dst->argb_stride = src->argb_stride;
115  }
116  return 1;
117}
118
119//------------------------------------------------------------------------------
120// Picture cropping
121
122int WebPPictureCrop(WebPPicture* pic,
123                    int left, int top, int width, int height) {
124  WebPPicture tmp;
125
126  if (pic == NULL) return 0;
127  if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0;
128
129  PictureGrabSpecs(pic, &tmp);
130  tmp.width = width;
131  tmp.height = height;
132  if (!WebPPictureAlloc(&tmp)) return 0;
133
134  if (!pic->use_argb) {
135    const int y_offset = top * pic->y_stride + left;
136    const int uv_offset = (top / 2) * pic->uv_stride + left / 2;
137    WebPCopyPlane(pic->y + y_offset, pic->y_stride,
138                  tmp.y, tmp.y_stride, width, height);
139    WebPCopyPlane(pic->u + uv_offset, pic->uv_stride,
140                  tmp.u, tmp.uv_stride, HALVE(width), HALVE(height));
141    WebPCopyPlane(pic->v + uv_offset, pic->uv_stride,
142                  tmp.v, tmp.uv_stride, HALVE(width), HALVE(height));
143
144    if (tmp.a != NULL) {
145      const int a_offset = top * pic->a_stride + left;
146      WebPCopyPlane(pic->a + a_offset, pic->a_stride,
147                    tmp.a, tmp.a_stride, width, height);
148    }
149  } else {
150    const uint8_t* const src =
151        (const uint8_t*)(pic->argb + top * pic->argb_stride + left);
152    WebPCopyPlane(src, pic->argb_stride * 4, (uint8_t*)tmp.argb,
153                  tmp.argb_stride * 4, width * 4, height);
154  }
155  WebPPictureFree(pic);
156  *pic = tmp;
157  return 1;
158}
159
160//------------------------------------------------------------------------------
161// Simple picture rescaler
162
163static void RescalePlane(const uint8_t* src,
164                         int src_width, int src_height, int src_stride,
165                         uint8_t* dst,
166                         int dst_width, int dst_height, int dst_stride,
167                         rescaler_t* const work,
168                         int num_channels) {
169  WebPRescaler rescaler;
170  int y = 0;
171  WebPRescalerInit(&rescaler, src_width, src_height,
172                   dst, dst_width, dst_height, dst_stride,
173                   num_channels, work);
174  while (y < src_height) {
175    y += WebPRescalerImport(&rescaler, src_height - y,
176                            src + y * src_stride, src_stride);
177    WebPRescalerExport(&rescaler);
178  }
179}
180
181static void AlphaMultiplyARGB(WebPPicture* const pic, int inverse) {
182  assert(pic->argb != NULL);
183  WebPMultARGBRows((uint8_t*)pic->argb, pic->argb_stride * sizeof(*pic->argb),
184                   pic->width, pic->height, inverse);
185}
186
187static void AlphaMultiplyY(WebPPicture* const pic, int inverse) {
188  if (pic->a != NULL) {
189    WebPMultRows(pic->y, pic->y_stride, pic->a, pic->a_stride,
190                 pic->width, pic->height, inverse);
191  }
192}
193
194int WebPPictureRescale(WebPPicture* pic, int width, int height) {
195  WebPPicture tmp;
196  int prev_width, prev_height;
197  rescaler_t* work;
198
199  if (pic == NULL) return 0;
200  prev_width = pic->width;
201  prev_height = pic->height;
202  if (!WebPRescalerGetScaledDimensions(
203          prev_width, prev_height, &width, &height)) {
204    return 0;
205  }
206
207  PictureGrabSpecs(pic, &tmp);
208  tmp.width = width;
209  tmp.height = height;
210  if (!WebPPictureAlloc(&tmp)) return 0;
211
212  if (!pic->use_argb) {
213    work = (rescaler_t*)WebPSafeMalloc(2ULL * width, sizeof(*work));
214    if (work == NULL) {
215      WebPPictureFree(&tmp);
216      return 0;
217    }
218    // If present, we need to rescale alpha first (for AlphaMultiplyY).
219    if (pic->a != NULL) {
220      WebPInitAlphaProcessing();
221      RescalePlane(pic->a, prev_width, prev_height, pic->a_stride,
222                   tmp.a, width, height, tmp.a_stride, work, 1);
223    }
224
225    // We take transparency into account on the luma plane only. That's not
226    // totally exact blending, but still is a good approximation.
227    AlphaMultiplyY(pic, 0);
228    RescalePlane(pic->y, prev_width, prev_height, pic->y_stride,
229                 tmp.y, width, height, tmp.y_stride, work, 1);
230    AlphaMultiplyY(&tmp, 1);
231
232    RescalePlane(pic->u,
233                 HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
234                 tmp.u,
235                 HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
236    RescalePlane(pic->v,
237                 HALVE(prev_width), HALVE(prev_height), pic->uv_stride,
238                 tmp.v,
239                 HALVE(width), HALVE(height), tmp.uv_stride, work, 1);
240  } else {
241    work = (rescaler_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work));
242    if (work == NULL) {
243      WebPPictureFree(&tmp);
244      return 0;
245    }
246    // In order to correctly interpolate colors, we need to apply the alpha
247    // weighting first (black-matting), scale the RGB values, and remove
248    // the premultiplication afterward (while preserving the alpha channel).
249    WebPInitAlphaProcessing();
250    AlphaMultiplyARGB(pic, 0);
251    RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height,
252                 pic->argb_stride * 4,
253                 (uint8_t*)tmp.argb, width, height,
254                 tmp.argb_stride * 4,
255                 work, 4);
256    AlphaMultiplyARGB(&tmp, 1);
257  }
258  WebPPictureFree(pic);
259  WebPSafeFree(work);
260  *pic = tmp;
261  return 1;
262}
263
264//------------------------------------------------------------------------------
265