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
29void 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
62void 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
99void 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
128void 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
161void 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
170void 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
194WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
195WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
196
197WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
198WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
199
200extern VP8CPUInfo VP8GetCPUInfo;
201extern void WebPRescalerDspInitSSE2(void);
202extern void WebPRescalerDspInitMIPS32(void);
203extern void WebPRescalerDspInitMIPSdspR2(void);
204extern void WebPRescalerDspInitMSA(void);
205extern void WebPRescalerDspInitNEON(void);
206
207WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
208#if !defined(WEBP_REDUCE_SIZE)
209#if !WEBP_NEON_OMIT_C_CODE
210 WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C;
211 WebPRescalerExportRowShrink = WebPRescalerExportRowShrink_C;
212#endif
213
214 WebPRescalerImportRowExpand = WebPRescalerImportRowExpand_C;
215 WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C;
216
217 if (VP8GetCPUInfo != NULL) {
218#if defined(WEBP_HAVE_SSE2)
219 if (VP8GetCPUInfo(kSSE2)) {
220 WebPRescalerDspInitSSE2();
221 }
222#endif
223#if defined(WEBP_USE_MIPS32)
224 if (VP8GetCPUInfo(kMIPS32)) {
225 WebPRescalerDspInitMIPS32();
226 }
227#endif
228#if defined(WEBP_USE_MIPS_DSP_R2)
229 if (VP8GetCPUInfo(kMIPSdspR2)) {
230 WebPRescalerDspInitMIPSdspR2();
231 }
232#endif
233#if defined(WEBP_USE_MSA)
234 if (VP8GetCPUInfo(kMSA)) {
235 WebPRescalerDspInitMSA();
236 }
237#endif
238 }
239
240#if defined(WEBP_HAVE_NEON)
241 if (WEBP_NEON_OMIT_C_CODE ||
242 (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
243 WebPRescalerDspInitNEON();
244 }
245#endif
246
247 assert(WebPRescalerExportRowExpand != NULL);
248 assert(WebPRescalerExportRowShrink != NULL);
249 assert(WebPRescalerImportRowExpand != NULL);
250 assert(WebPRescalerImportRowShrink != NULL);
251#endif // WEBP_REDUCE_SIZE
252}
253