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 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
61void 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
98void 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
127void 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
160void 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
169void 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
193WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
194WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
195
196WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
197WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
198
199extern void WebPRescalerDspInitSSE2(void);
200extern void WebPRescalerDspInitMIPS32(void);
201extern void WebPRescalerDspInitMIPSdspR2(void);
202extern void WebPRescalerDspInitMSA(void);
203extern void WebPRescalerDspInitNEON(void);
204
205WEBP_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