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