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 // Rescaling functions
11 //
12 // Author: Skal (pascal.massimino@gmail.com)
13 
14 #include <assert.h>
15 
16 #include "src/dsp/dsp.h"
17 #include "src/utils/rescaler_utils.h"
18 
19 //------------------------------------------------------------------------------
20 // Implementations of critical functions ImportRow / ExportRow
21 
22 #define ROUNDER (WEBP_RESCALER_ONE >> 1)
23 #define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
24 #define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX)
25 
26 //------------------------------------------------------------------------------
27 // Row import
28 
WebPRescalerImportRowExpand_C(WebPRescaler * const wrk,const uint8_t * src)29 void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk,
30                                    const uint8_t* src) {
31   const int x_stride = wrk->num_channels;
32   const int x_out_max = wrk->dst_width * wrk->num_channels;
33   int channel;
34   assert(!WebPRescalerInputDone(wrk));
35   assert(wrk->x_expand);
36   for (channel = 0; channel < x_stride; ++channel) {
37     int x_in = channel;
38     int x_out = channel;
39     // simple bilinear interpolation
40     int accum = wrk->x_add;
41     int left = src[x_in];
42     int right = (wrk->src_width > 1) ? src[x_in + x_stride] : left;
43     x_in += x_stride;
44     while (1) {
45       wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
46       x_out += x_stride;
47       if (x_out >= x_out_max) break;
48       accum -= wrk->x_sub;
49       if (accum < 0) {
50         left = right;
51         x_in += x_stride;
52         assert(x_in < wrk->src_width * x_stride);
53         right = src[x_in];
54         accum += wrk->x_add;
55       }
56     }
57     assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
58   }
59 }
60 
WebPRescalerImportRowShrink_C(WebPRescaler * const wrk,const uint8_t * src)61 void WebPRescalerImportRowShrink_C(WebPRescaler* const wrk,
62                                    const uint8_t* src) {
63   const int x_stride = wrk->num_channels;
64   const int x_out_max = wrk->dst_width * wrk->num_channels;
65   int channel;
66   assert(!WebPRescalerInputDone(wrk));
67   assert(!wrk->x_expand);
68   for (channel = 0; channel < x_stride; ++channel) {
69     int x_in = channel;
70     int x_out = channel;
71     uint32_t sum = 0;
72     int accum = 0;
73     while (x_out < x_out_max) {
74       uint32_t base = 0;
75       accum += wrk->x_add;
76       while (accum > 0) {
77         accum -= wrk->x_sub;
78         assert(x_in < wrk->src_width * x_stride);
79         base = src[x_in];
80         sum += base;
81         x_in += x_stride;
82       }
83       {        // Emit next horizontal pixel.
84         const rescaler_t frac = base * (-accum);
85         wrk->frow[x_out] = sum * wrk->x_sub - frac;
86         // fresh fractional start for next pixel
87         sum = (int)MULT_FIX(frac, wrk->fx_scale);
88       }
89       x_out += x_stride;
90     }
91     assert(accum == 0);
92   }
93 }
94 
95 //------------------------------------------------------------------------------
96 // Row export
97 
WebPRescalerExportRowExpand_C(WebPRescaler * const wrk)98 void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
99   int x_out;
100   uint8_t* const dst = wrk->dst;
101   rescaler_t* const irow = wrk->irow;
102   const int x_out_max = wrk->dst_width * wrk->num_channels;
103   const rescaler_t* const frow = wrk->frow;
104   assert(!WebPRescalerOutputDone(wrk));
105   assert(wrk->y_accum <= 0);
106   assert(wrk->y_expand);
107   assert(wrk->y_sub != 0);
108   if (wrk->y_accum == 0) {
109     for (x_out = 0; x_out < x_out_max; ++x_out) {
110       const uint32_t J = frow[x_out];
111       const int v = (int)MULT_FIX(J, wrk->fy_scale);
112       assert(v >= 0 && v <= 255);
113       dst[x_out] = v;
114     }
115   } else {
116     const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
117     const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
118     for (x_out = 0; x_out < x_out_max; ++x_out) {
119       const uint64_t I = (uint64_t)A * frow[x_out]
120                        + (uint64_t)B * irow[x_out];
121       const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
122       const int v = (int)MULT_FIX(J, wrk->fy_scale);
123       assert(v >= 0 && v <= 255);
124       dst[x_out] = v;
125     }
126   }
127 }
128 
WebPRescalerExportRowShrink_C(WebPRescaler * const wrk)129 void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) {
130   int x_out;
131   uint8_t* const dst = wrk->dst;
132   rescaler_t* const irow = wrk->irow;
133   const int x_out_max = wrk->dst_width * wrk->num_channels;
134   const rescaler_t* const frow = wrk->frow;
135   const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
136   assert(!WebPRescalerOutputDone(wrk));
137   assert(wrk->y_accum <= 0);
138   assert(!wrk->y_expand);
139   if (yscale) {
140     for (x_out = 0; x_out < x_out_max; ++x_out) {
141       const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale);
142       const int v = (int)MULT_FIX_FLOOR(irow[x_out] - frac, wrk->fxy_scale);
143       assert(v >= 0 && v <= 255);
144       dst[x_out] = v;
145       irow[x_out] = frac;   // new fractional start
146     }
147   } else {
148     for (x_out = 0; x_out < x_out_max; ++x_out) {
149       const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
150       assert(v >= 0 && v <= 255);
151       dst[x_out] = v;
152       irow[x_out] = 0;
153     }
154   }
155 }
156 
157 #undef MULT_FIX_FLOOR
158 #undef MULT_FIX
159 #undef ROUNDER
160 
161 //------------------------------------------------------------------------------
162 // Main entry calls
163 
WebPRescalerImportRow(WebPRescaler * const wrk,const uint8_t * src)164 void WebPRescalerImportRow(WebPRescaler* const wrk, const uint8_t* src) {
165   assert(!WebPRescalerInputDone(wrk));
166   if (!wrk->x_expand) {
167     WebPRescalerImportRowShrink(wrk, src);
168   } else {
169     WebPRescalerImportRowExpand(wrk, src);
170   }
171 }
172 
WebPRescalerExportRow(WebPRescaler * const wrk)173 void WebPRescalerExportRow(WebPRescaler* const wrk) {
174   if (wrk->y_accum <= 0) {
175     assert(!WebPRescalerOutputDone(wrk));
176     if (wrk->y_expand) {
177       WebPRescalerExportRowExpand(wrk);
178     } else if (wrk->fxy_scale) {
179       WebPRescalerExportRowShrink(wrk);
180     } else {  // special case
181       int i;
182       assert(wrk->src_height == wrk->dst_height && wrk->x_add == 1);
183       assert(wrk->src_width == 1 && wrk->dst_width <= 2);
184       for (i = 0; i < wrk->num_channels * wrk->dst_width; ++i) {
185         wrk->dst[i] = wrk->irow[i];
186         wrk->irow[i] = 0;
187       }
188     }
189     wrk->y_accum += wrk->y_add;
190     wrk->dst += wrk->dst_stride;
191     ++wrk->dst_y;
192   }
193 }
194 
195 //------------------------------------------------------------------------------
196 
197 WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
198 WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
199 
200 WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
201 WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
202 
203 extern void WebPRescalerDspInitSSE2(void);
204 extern void WebPRescalerDspInitMIPS32(void);
205 extern void WebPRescalerDspInitMIPSdspR2(void);
206 extern void WebPRescalerDspInitMSA(void);
207 extern void WebPRescalerDspInitNEON(void);
208 
WEBP_DSP_INIT_FUNC(WebPRescalerDspInit)209 WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
210 #if !defined(WEBP_REDUCE_SIZE)
211 #if !WEBP_NEON_OMIT_C_CODE
212   WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C;
213   WebPRescalerExportRowShrink = WebPRescalerExportRowShrink_C;
214 #endif
215 
216   WebPRescalerImportRowExpand = WebPRescalerImportRowExpand_C;
217   WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C;
218 
219   if (VP8GetCPUInfo != NULL) {
220 #if defined(WEBP_USE_SSE2)
221     if (VP8GetCPUInfo(kSSE2)) {
222       WebPRescalerDspInitSSE2();
223     }
224 #endif
225 #if defined(WEBP_USE_MIPS32)
226     if (VP8GetCPUInfo(kMIPS32)) {
227       WebPRescalerDspInitMIPS32();
228     }
229 #endif
230 #if defined(WEBP_USE_MIPS_DSP_R2)
231     if (VP8GetCPUInfo(kMIPSdspR2)) {
232       WebPRescalerDspInitMIPSdspR2();
233     }
234 #endif
235 #if defined(WEBP_USE_MSA)
236     if (VP8GetCPUInfo(kMSA)) {
237       WebPRescalerDspInitMSA();
238     }
239 #endif
240   }
241 
242 #if defined(WEBP_USE_NEON)
243   if (WEBP_NEON_OMIT_C_CODE ||
244       (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
245     WebPRescalerDspInitNEON();
246   }
247 #endif
248 
249   assert(WebPRescalerExportRowExpand != NULL);
250   assert(WebPRescalerExportRowShrink != NULL);
251   assert(WebPRescalerImportRowExpand != NULL);
252   assert(WebPRescalerImportRowShrink != NULL);
253 #endif   // WEBP_REDUCE_SIZE
254 }
255