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
25//------------------------------------------------------------------------------
26// Row import
27
28void WebPRescalerImportRowExpand_C(WebPRescaler* const wrk,
29                                   const uint8_t* src) {
30  const int x_stride = wrk->num_channels;
31  const int x_out_max = wrk->dst_width * wrk->num_channels;
32  int channel;
33  assert(!WebPRescalerInputDone(wrk));
34  assert(wrk->x_expand);
35  for (channel = 0; channel < x_stride; ++channel) {
36    int x_in = channel;
37    int x_out = channel;
38    // simple bilinear interpolation
39    int accum = wrk->x_add;
40    int left = src[x_in];
41    int right = (wrk->src_width > 1) ? src[x_in + x_stride] : left;
42    x_in += x_stride;
43    while (1) {
44      wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
45      x_out += x_stride;
46      if (x_out >= x_out_max) break;
47      accum -= wrk->x_sub;
48      if (accum < 0) {
49        left = right;
50        x_in += x_stride;
51        assert(x_in < wrk->src_width * x_stride);
52        right = src[x_in];
53        accum += wrk->x_add;
54      }
55    }
56    assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
57  }
58}
59
60void WebPRescalerImportRowShrink_C(WebPRescaler* const wrk,
61                                   const uint8_t* src) {
62  const int x_stride = wrk->num_channels;
63  const int x_out_max = wrk->dst_width * wrk->num_channels;
64  int channel;
65  assert(!WebPRescalerInputDone(wrk));
66  assert(!wrk->x_expand);
67  for (channel = 0; channel < x_stride; ++channel) {
68    int x_in = channel;
69    int x_out = channel;
70    uint32_t sum = 0;
71    int accum = 0;
72    while (x_out < x_out_max) {
73      uint32_t base = 0;
74      accum += wrk->x_add;
75      while (accum > 0) {
76        accum -= wrk->x_sub;
77        assert(x_in < wrk->src_width * x_stride);
78        base = src[x_in];
79        sum += base;
80        x_in += x_stride;
81      }
82      {        // Emit next horizontal pixel.
83        const rescaler_t frac = base * (-accum);
84        wrk->frow[x_out] = sum * wrk->x_sub - frac;
85        // fresh fractional start for next pixel
86        sum = (int)MULT_FIX(frac, wrk->fx_scale);
87      }
88      x_out += x_stride;
89    }
90    assert(accum == 0);
91  }
92}
93
94//------------------------------------------------------------------------------
95// Row export
96
97void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
98  int x_out;
99  uint8_t* const dst = wrk->dst;
100  rescaler_t* const irow = wrk->irow;
101  const int x_out_max = wrk->dst_width * wrk->num_channels;
102  const rescaler_t* const frow = wrk->frow;
103  assert(!WebPRescalerOutputDone(wrk));
104  assert(wrk->y_accum <= 0);
105  assert(wrk->y_expand);
106  assert(wrk->y_sub != 0);
107  if (wrk->y_accum == 0) {
108    for (x_out = 0; x_out < x_out_max; ++x_out) {
109      const uint32_t J = frow[x_out];
110      const int v = (int)MULT_FIX(J, wrk->fy_scale);
111      assert(v >= 0 && v <= 255);
112      dst[x_out] = 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      assert(v >= 0 && v <= 255);
123      dst[x_out] = 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(frow[x_out], yscale);
141      const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
142      assert(v >= 0 && v <= 255);
143      dst[x_out] = v;
144      irow[x_out] = frac;   // new fractional start
145    }
146  } else {
147    for (x_out = 0; x_out < x_out_max; ++x_out) {
148      const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
149      assert(v >= 0 && v <= 255);
150      dst[x_out] = v;
151      irow[x_out] = 0;
152    }
153  }
154}
155
156#undef MULT_FIX
157#undef ROUNDER
158
159//------------------------------------------------------------------------------
160// Main entry calls
161
162void WebPRescalerImportRow(WebPRescaler* const wrk, const uint8_t* src) {
163  assert(!WebPRescalerInputDone(wrk));
164  if (!wrk->x_expand) {
165    WebPRescalerImportRowShrink(wrk, src);
166  } else {
167    WebPRescalerImportRowExpand(wrk, src);
168  }
169}
170
171void WebPRescalerExportRow(WebPRescaler* const wrk) {
172  if (wrk->y_accum <= 0) {
173    assert(!WebPRescalerOutputDone(wrk));
174    if (wrk->y_expand) {
175      WebPRescalerExportRowExpand(wrk);
176    } else if (wrk->fxy_scale) {
177      WebPRescalerExportRowShrink(wrk);
178    } else {  // special case
179      int i;
180      assert(wrk->src_height == wrk->dst_height && wrk->x_add == 1);
181      assert(wrk->src_width == 1 && wrk->dst_width <= 2);
182      for (i = 0; i < wrk->num_channels * wrk->dst_width; ++i) {
183        wrk->dst[i] = wrk->irow[i];
184        wrk->irow[i] = 0;
185      }
186    }
187    wrk->y_accum += wrk->y_add;
188    wrk->dst += wrk->dst_stride;
189    ++wrk->dst_y;
190  }
191}
192
193//------------------------------------------------------------------------------
194
195WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
196WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
197
198WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
199WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
200
201extern void WebPRescalerDspInitSSE2(void);
202extern void WebPRescalerDspInitMIPS32(void);
203extern void WebPRescalerDspInitMIPSdspR2(void);
204extern void WebPRescalerDspInitMSA(void);
205extern void WebPRescalerDspInitNEON(void);
206
207static volatile VP8CPUInfo rescaler_last_cpuinfo_used =
208    (VP8CPUInfo)&rescaler_last_cpuinfo_used;
209
210WEBP_TSAN_IGNORE_FUNCTION void WebPRescalerDspInit(void) {
211  if (rescaler_last_cpuinfo_used == VP8GetCPUInfo) return;
212#if !defined(WEBP_REDUCE_SIZE)
213#if !WEBP_NEON_OMIT_C_CODE
214  WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C;
215  WebPRescalerExportRowShrink = WebPRescalerExportRowShrink_C;
216#endif
217
218  WebPRescalerImportRowExpand = WebPRescalerImportRowExpand_C;
219  WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C;
220
221  if (VP8GetCPUInfo != NULL) {
222#if defined(WEBP_USE_SSE2)
223    if (VP8GetCPUInfo(kSSE2)) {
224      WebPRescalerDspInitSSE2();
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
244#if defined(WEBP_USE_NEON)
245  if (WEBP_NEON_OMIT_C_CODE ||
246      (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
247    WebPRescalerDspInitNEON();
248  }
249#endif
250
251  assert(WebPRescalerExportRowExpand != NULL);
252  assert(WebPRescalerExportRowShrink != NULL);
253  assert(WebPRescalerImportRowExpand != NULL);
254  assert(WebPRescalerImportRowShrink != NULL);
255#endif   // WEBP_REDUCE_SIZE
256  rescaler_last_cpuinfo_used = VP8GetCPUInfo;
257}
258