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