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 "./dsp.h"
17#include "../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
25//------------------------------------------------------------------------------
26// Row import
27
28void WebPRescalerImportRowExpandC(WebPRescaler* const wrk, const uint8_t* src) {
29 const int x_stride = wrk->num_channels;
30 const int x_out_max = wrk->dst_width * wrk->num_channels;
31 int channel;
32 assert(!WebPRescalerInputDone(wrk));
33 assert(wrk->x_expand);
34 for (channel = 0; channel < x_stride; ++channel) {
35 int x_in = channel;
36 int x_out = channel;
37 // simple bilinear interpolation
38 int accum = wrk->x_add;
39 int left = src[x_in];
40 int right = (wrk->src_width > 1) ? src[x_in + x_stride] : left;
41 x_in += x_stride;
42 while (1) {
43 wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
44 x_out += x_stride;
45 if (x_out >= x_out_max) break;
46 accum -= wrk->x_sub;
47 if (accum < 0) {
48 left = right;
49 x_in += x_stride;
50 assert(x_in < wrk->src_width * x_stride);
51 right = src[x_in];
52 accum += wrk->x_add;
53 }
54 }
55 assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
56 }
57}
58
59void WebPRescalerImportRowShrinkC(WebPRescaler* const wrk, const uint8_t* src) {
60 const int x_stride = wrk->num_channels;
61 const int x_out_max = wrk->dst_width * wrk->num_channels;
62 int channel;
63 assert(!WebPRescalerInputDone(wrk));
64 assert(!wrk->x_expand);
65 for (channel = 0; channel < x_stride; ++channel) {
66 int x_in = channel;
67 int x_out = channel;
68 uint32_t sum = 0;
69 int accum = 0;
70 while (x_out < x_out_max) {
71 uint32_t base = 0;
72 accum += wrk->x_add;
73 while (accum > 0) {
74 accum -= wrk->x_sub;
75 assert(x_in < wrk->src_width * x_stride);
76 base = src[x_in];
77 sum += base;
78 x_in += x_stride;
79 }
80 { // Emit next horizontal pixel.
81 const rescaler_t frac = base * (-accum);
82 wrk->frow[x_out] = sum * wrk->x_sub - frac;
83 // fresh fractional start for next pixel
84 sum = (int)MULT_FIX(frac, wrk->fx_scale);
85 }
86 x_out += x_stride;
87 }
88 assert(accum == 0);
89 }
90}
91
92//------------------------------------------------------------------------------
93// Row export
94
95void WebPRescalerExportRowExpandC(WebPRescaler* const wrk) {
96 int x_out;
97 uint8_t* const dst = wrk->dst;
98 rescaler_t* const irow = wrk->irow;
99 const int x_out_max = wrk->dst_width * wrk->num_channels;
100 const rescaler_t* const frow = wrk->frow;
101 assert(!WebPRescalerOutputDone(wrk));
102 assert(wrk->y_accum <= 0);
103 assert(wrk->y_expand);
104 assert(wrk->y_sub != 0);
105 if (wrk->y_accum == 0) {
106 for (x_out = 0; x_out < x_out_max; ++x_out) {
107 const uint32_t J = frow[x_out];
108 const int v = (int)MULT_FIX(J, wrk->fy_scale);
109 assert(v >= 0 && v <= 255);
110 dst[x_out] = v;
111 }
112 } else {
113 const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
114 const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
115 for (x_out = 0; x_out < x_out_max; ++x_out) {
116 const uint64_t I = (uint64_t)A * frow[x_out]
117 + (uint64_t)B * irow[x_out];
118 const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
119 const int v = (int)MULT_FIX(J, wrk->fy_scale);
120 assert(v >= 0 && v <= 255);
121 dst[x_out] = v;
122 }
123 }
124}
125
126void WebPRescalerExportRowShrinkC(WebPRescaler* const wrk) {
127 int x_out;
128 uint8_t* const dst = wrk->dst;
129 rescaler_t* const irow = wrk->irow;
130 const int x_out_max = wrk->dst_width * wrk->num_channels;
131 const rescaler_t* const frow = wrk->frow;
132 const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
133 assert(!WebPRescalerOutputDone(wrk));
134 assert(wrk->y_accum <= 0);
135 assert(!wrk->y_expand);
136 if (yscale) {
137 for (x_out = 0; x_out < x_out_max; ++x_out) {
138 const uint32_t frac = (uint32_t)MULT_FIX(frow[x_out], yscale);
139 const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
140 assert(v >= 0 && v <= 255);
141 dst[x_out] = 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 assert(v >= 0 && v <= 255);
148 dst[x_out] = v;
149 irow[x_out] = 0;
150 }
151 }
152}
153
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
205static volatile VP8CPUInfo rescaler_last_cpuinfo_used =
206 (VP8CPUInfo)&rescaler_last_cpuinfo_used;
207
208WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) {
209 if (rescaler_last_cpuinfo_used == VP8GetCPUInfo) return;
210
211 WebPRescalerImportRowExpand = WebPRescalerImportRowExpandC;
212 WebPRescalerImportRowShrink = WebPRescalerImportRowShrinkC;
213 WebPRescalerExportRowExpand = WebPRescalerExportRowExpandC;
214 WebPRescalerExportRowShrink = WebPRescalerExportRowShrinkC;
215
216 if (VP8GetCPUInfo != NULL) {
217#if defined(WEBP_USE_SSE2)
218 if (VP8GetCPUInfo(kSSE2)) {
219 WebPRescalerDspInitSSE2();
220 }
221#endif
222#if defined(WEBP_USE_NEON)
223 if (VP8GetCPUInfo(kNEON)) {
224 WebPRescalerDspInitNEON();
225 }
226#endif
227#if defined(WEBP_USE_MIPS32)
228 if (VP8GetCPUInfo(kMIPS32)) {
229 WebPRescalerDspInitMIPS32();
230 }
231#endif
232#if defined(WEBP_USE_MIPS_DSP_R2)
233 if (VP8GetCPUInfo(kMIPSdspR2)) {
234 WebPRescalerDspInitMIPSdspR2();
235 }
236#endif
237#if defined(WEBP_USE_MSA)
238 if (VP8GetCPUInfo(kMSA)) {
239 WebPRescalerDspInitMSA();
240 }
241#endif
242 }
243 rescaler_last_cpuinfo_used = VP8GetCPUInfo;
244}
245