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.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
WebPRescalerImportRowExpandC(WebPRescaler * const wrk,const uint8_t * src)28 void 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
WebPRescalerImportRowShrinkC(WebPRescaler * const wrk,const uint8_t * src)59 void 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
WebPRescalerExportRowExpandC(WebPRescaler * const wrk)95 void 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
WebPRescalerExportRowShrinkC(WebPRescaler * const wrk)126 void 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
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 { // very special case for src = dst = 1x1
177 int i;
178 assert(wrk->src_width == 1 && wrk->dst_width <= 2);
179 assert(wrk->src_height == 1 && wrk->dst_height == 1);
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 WebPRescalerDspInitNEON(void);
203
204 static volatile VP8CPUInfo rescaler_last_cpuinfo_used =
205 (VP8CPUInfo)&rescaler_last_cpuinfo_used;
206
WebPRescalerDspInit(void)207 WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) {
208 if (rescaler_last_cpuinfo_used == VP8GetCPUInfo) return;
209
210 WebPRescalerImportRowExpand = WebPRescalerImportRowExpandC;
211 WebPRescalerImportRowShrink = WebPRescalerImportRowShrinkC;
212 WebPRescalerExportRowExpand = WebPRescalerExportRowExpandC;
213 WebPRescalerExportRowShrink = WebPRescalerExportRowShrinkC;
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_NEON)
222 if (VP8GetCPUInfo(kNEON)) {
223 WebPRescalerDspInitNEON();
224 }
225 #endif
226 #if defined(WEBP_USE_MIPS32)
227 if (VP8GetCPUInfo(kMIPS32)) {
228 WebPRescalerDspInitMIPS32();
229 }
230 #endif
231 #if defined(WEBP_USE_MIPS_DSP_R2)
232 if (VP8GetCPUInfo(kMIPSdspR2)) {
233 WebPRescalerDspInitMIPSdspR2();
234 }
235 #endif
236 }
237 rescaler_last_cpuinfo_used = VP8GetCPUInfo;
238 }
239