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       dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
113     }
114   } else {
115     const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
116     const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
117     for (x_out = 0; x_out < x_out_max; ++x_out) {
118       const uint64_t I = (uint64_t)A * frow[x_out]
119                        + (uint64_t)B * irow[x_out];
120       const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
121       const int v = (int)MULT_FIX(J, wrk->fy_scale);
122       dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
123     }
124   }
125 }
126 
WebPRescalerExportRowShrink_C(WebPRescaler * const wrk)127 void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) {
128   int x_out;
129   uint8_t* const dst = wrk->dst;
130   rescaler_t* const irow = wrk->irow;
131   const int x_out_max = wrk->dst_width * wrk->num_channels;
132   const rescaler_t* const frow = wrk->frow;
133   const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
134   assert(!WebPRescalerOutputDone(wrk));
135   assert(wrk->y_accum <= 0);
136   assert(!wrk->y_expand);
137   if (yscale) {
138     for (x_out = 0; x_out < x_out_max; ++x_out) {
139       const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(frow[x_out], yscale);
140       const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
141       dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
142       irow[x_out] = frac;   // new fractional start
143     }
144   } else {
145     for (x_out = 0; x_out < x_out_max; ++x_out) {
146       const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
147       dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
148       irow[x_out] = 0;
149     }
150   }
151 }
152 
153 #undef MULT_FIX_FLOOR
154 #undef MULT_FIX
155 #undef ROUNDER
156 
157 //------------------------------------------------------------------------------
158 // Main entry calls
159 
WebPRescalerImportRow(WebPRescaler * const wrk,const uint8_t * src)160 void WebPRescalerImportRow(WebPRescaler* const wrk, const uint8_t* src) {
161   assert(!WebPRescalerInputDone(wrk));
162   if (!wrk->x_expand) {
163     WebPRescalerImportRowShrink(wrk, src);
164   } else {
165     WebPRescalerImportRowExpand(wrk, src);
166   }
167 }
168 
WebPRescalerExportRow(WebPRescaler * const wrk)169 void WebPRescalerExportRow(WebPRescaler* const wrk) {
170   if (wrk->y_accum <= 0) {
171     assert(!WebPRescalerOutputDone(wrk));
172     if (wrk->y_expand) {
173       WebPRescalerExportRowExpand(wrk);
174     } else if (wrk->fxy_scale) {
175       WebPRescalerExportRowShrink(wrk);
176     } else {  // special case
177       int i;
178       assert(wrk->src_height == wrk->dst_height && wrk->x_add == 1);
179       assert(wrk->src_width == 1 && wrk->dst_width <= 2);
180       for (i = 0; i < wrk->num_channels * wrk->dst_width; ++i) {
181         wrk->dst[i] = wrk->irow[i];
182         wrk->irow[i] = 0;
183       }
184     }
185     wrk->y_accum += wrk->y_add;
186     wrk->dst += wrk->dst_stride;
187     ++wrk->dst_y;
188   }
189 }
190 
191 //------------------------------------------------------------------------------
192 
193 WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
194 WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
195 
196 WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
197 WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
198 
199 extern void WebPRescalerDspInitSSE2(void);
200 extern void WebPRescalerDspInitMIPS32(void);
201 extern void WebPRescalerDspInitMIPSdspR2(void);
202 extern void WebPRescalerDspInitMSA(void);
203 extern void WebPRescalerDspInitNEON(void);
204 
WEBP_DSP_INIT_FUNC(WebPRescalerDspInit)205 WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
206 #if !defined(WEBP_REDUCE_SIZE)
207 #if !WEBP_NEON_OMIT_C_CODE
208   WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C;
209   WebPRescalerExportRowShrink = WebPRescalerExportRowShrink_C;
210 #endif
211 
212   WebPRescalerImportRowExpand = WebPRescalerImportRowExpand_C;
213   WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C;
214 
215   if (VP8GetCPUInfo != NULL) {
216 #if defined(WEBP_USE_SSE2)
217     if (VP8GetCPUInfo(kSSE2)) {
218       WebPRescalerDspInitSSE2();
219     }
220 #endif
221 #if defined(WEBP_USE_MIPS32)
222     if (VP8GetCPUInfo(kMIPS32)) {
223       WebPRescalerDspInitMIPS32();
224     }
225 #endif
226 #if defined(WEBP_USE_MIPS_DSP_R2)
227     if (VP8GetCPUInfo(kMIPSdspR2)) {
228       WebPRescalerDspInitMIPSdspR2();
229     }
230 #endif
231 #if defined(WEBP_USE_MSA)
232     if (VP8GetCPUInfo(kMSA)) {
233       WebPRescalerDspInitMSA();
234     }
235 #endif
236   }
237 
238 #if defined(WEBP_USE_NEON)
239   if (WEBP_NEON_OMIT_C_CODE ||
240       (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
241     WebPRescalerDspInitNEON();
242   }
243 #endif
244 
245   assert(WebPRescalerExportRowExpand != NULL);
246   assert(WebPRescalerExportRowShrink != NULL);
247   assert(WebPRescalerImportRowExpand != NULL);
248   assert(WebPRescalerImportRowShrink != NULL);
249 #endif   // WEBP_REDUCE_SIZE
250 }
251