Coverage Report

Created: 2026-05-16 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwebp/src/dsp/rescaler.c
Line
Count
Source
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
#include <stddef.h>
16
17
#include "src/dsp/cpu.h"
18
#include "src/dsp/dsp.h"
19
#include "src/utils/rescaler_utils.h"
20
#include "src/webp/types.h"
21
22
//------------------------------------------------------------------------------
23
// Implementations of critical functions ImportRow / ExportRow
24
25
0
#define ROUNDER (WEBP_RESCALER_ONE >> 1)
26
0
#define MULT_FIX(x, y) (((uint64_t)(x) * (y) + ROUNDER) >> WEBP_RESCALER_RFIX)
27
0
#define MULT_FIX_FLOOR(x, y) (((uint64_t)(x) * (y)) >> WEBP_RESCALER_RFIX)
28
29
//------------------------------------------------------------------------------
30
// Row import
31
WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW void WebPRescalerImportRowExpand_C(
32
0
    WebPRescaler* WEBP_RESTRICT const wrk, const uint8_t* WEBP_RESTRICT src) {
33
0
  const int x_stride = wrk->num_channels;
34
0
  const int x_out_max = wrk->dst_width * wrk->num_channels;
35
0
  int channel;
36
0
  assert(!WebPRescalerInputDone(wrk));
37
0
  assert(wrk->x_expand);
38
0
  for (channel = 0; channel < x_stride; ++channel) {
39
0
    int x_in = channel;
40
0
    int x_out = channel;
41
    // simple bilinear interpolation
42
0
    int accum = wrk->x_add;
43
0
    rescaler_t left = (rescaler_t)src[x_in];
44
0
    rescaler_t right =
45
0
        (wrk->src_width > 1) ? (rescaler_t)src[x_in + x_stride] : left;
46
0
    x_in += x_stride;
47
0
    while (1) {
48
0
      wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
49
0
      x_out += x_stride;
50
0
      if (x_out >= x_out_max) break;
51
0
      accum -= wrk->x_sub;
52
0
      if (accum < 0) {
53
0
        left = right;
54
0
        x_in += x_stride;
55
0
        assert(x_in < wrk->src_width * x_stride);
56
0
        right = (rescaler_t)src[x_in];
57
0
        accum += wrk->x_add;
58
0
      }
59
0
    }
60
0
    assert(wrk->x_sub == 0 /* <- special case for src_width=1 */ || accum == 0);
61
0
  }
62
0
}
63
64
void WebPRescalerImportRowShrink_C(WebPRescaler* WEBP_RESTRICT const wrk,
65
0
                                   const uint8_t* WEBP_RESTRICT src) {
66
0
  const int x_stride = wrk->num_channels;
67
0
  const int x_out_max = wrk->dst_width * wrk->num_channels;
68
0
  int channel;
69
0
  assert(!WebPRescalerInputDone(wrk));
70
0
  assert(!wrk->x_expand);
71
0
  for (channel = 0; channel < x_stride; ++channel) {
72
0
    int x_in = channel;
73
0
    int x_out = channel;
74
0
    uint32_t sum = 0;
75
0
    int accum = 0;
76
0
    while (x_out < x_out_max) {
77
0
      uint32_t base = 0;
78
0
      accum += wrk->x_add;
79
0
      while (accum > 0) {
80
0
        accum -= wrk->x_sub;
81
0
        assert(x_in < wrk->src_width * x_stride);
82
0
        base = src[x_in];
83
0
        sum += base;
84
0
        x_in += x_stride;
85
0
      }
86
0
      {  // Emit next horizontal pixel.
87
0
        const rescaler_t frac = base * (-accum);
88
0
        wrk->frow[x_out] = sum * wrk->x_sub - frac;
89
        // fresh fractional start for next pixel
90
0
        sum = (int)MULT_FIX(frac, wrk->fx_scale);
91
0
      }
92
0
      x_out += x_stride;
93
0
    }
94
0
    assert(accum == 0);
95
0
  }
96
0
}
97
98
//------------------------------------------------------------------------------
99
// Row export
100
101
0
void WebPRescalerExportRowExpand_C(WebPRescaler* const wrk) {
102
0
  int x_out;
103
0
  uint8_t* const dst = wrk->dst;
104
0
  rescaler_t* const irow = wrk->irow;
105
0
  const int x_out_max = wrk->dst_width * wrk->num_channels;
106
0
  const rescaler_t* const frow = wrk->frow;
107
0
  assert(!WebPRescalerOutputDone(wrk));
108
0
  assert(wrk->y_accum <= 0);
109
0
  assert(wrk->y_expand);
110
0
  assert(wrk->y_sub != 0);
111
0
  if (wrk->y_accum == 0) {
112
0
    for (x_out = 0; x_out < x_out_max; ++x_out) {
113
0
      const uint32_t J = frow[x_out];
114
0
      const int v = (int)MULT_FIX(J, wrk->fy_scale);
115
0
      dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
116
0
    }
117
0
  } else {
118
0
    const uint32_t B = WEBP_RESCALER_FRAC(-wrk->y_accum, wrk->y_sub);
119
0
    const uint32_t A = (uint32_t)(WEBP_RESCALER_ONE - B);
120
0
    for (x_out = 0; x_out < x_out_max; ++x_out) {
121
0
      const uint64_t I = (uint64_t)A * frow[x_out] + (uint64_t)B * irow[x_out];
122
0
      const uint32_t J = (uint32_t)((I + ROUNDER) >> WEBP_RESCALER_RFIX);
123
0
      const int v = (int)MULT_FIX(J, wrk->fy_scale);
124
0
      dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
125
0
    }
126
0
  }
127
0
}
128
129
0
void WebPRescalerExportRowShrink_C(WebPRescaler* const wrk) {
130
0
  int x_out;
131
0
  uint8_t* const dst = wrk->dst;
132
0
  rescaler_t* const irow = wrk->irow;
133
0
  const int x_out_max = wrk->dst_width * wrk->num_channels;
134
0
  const rescaler_t* const frow = wrk->frow;
135
0
  const uint32_t yscale = wrk->fy_scale * (-wrk->y_accum);
136
0
  assert(!WebPRescalerOutputDone(wrk));
137
0
  assert(wrk->y_accum <= 0);
138
0
  assert(!wrk->y_expand);
139
0
  if (yscale) {
140
0
    for (x_out = 0; x_out < x_out_max; ++x_out) {
141
0
      const uint32_t frac = (uint32_t)MULT_FIX_FLOOR(frow[x_out], yscale);
142
0
      const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
143
0
      dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
144
0
      irow[x_out] = frac;  // new fractional start
145
0
    }
146
0
  } else {
147
0
    for (x_out = 0; x_out < x_out_max; ++x_out) {
148
0
      const int v = (int)MULT_FIX(irow[x_out], wrk->fxy_scale);
149
0
      dst[x_out] = (v > 255) ? 255u : (uint8_t)v;
150
0
      irow[x_out] = 0;
151
0
    }
152
0
  }
153
0
}
154
155
#undef MULT_FIX_FLOOR
156
#undef MULT_FIX
157
#undef ROUNDER
158
159
//------------------------------------------------------------------------------
160
// Main entry calls
161
162
void WebPRescalerImportRow(WebPRescaler* WEBP_RESTRICT const wrk,
163
0
                           const uint8_t* WEBP_RESTRICT src) {
164
0
  assert(!WebPRescalerInputDone(wrk));
165
0
  if (!wrk->x_expand) {
166
0
    WebPRescalerImportRowShrink(wrk, src);
167
0
  } else {
168
0
    WebPRescalerImportRowExpand(wrk, src);
169
0
  }
170
0
}
171
172
0
void WebPRescalerExportRow(WebPRescaler* const wrk) {
173
0
  if (wrk->y_accum <= 0) {
174
0
    assert(!WebPRescalerOutputDone(wrk));
175
0
    if (wrk->y_expand) {
176
0
      WebPRescalerExportRowExpand(wrk);
177
0
    } else if (wrk->fxy_scale) {
178
0
      WebPRescalerExportRowShrink(wrk);
179
0
    } else {  // special case
180
0
      int i;
181
0
      assert(wrk->src_height == wrk->dst_height && wrk->x_add == 1);
182
0
      assert(wrk->src_width == 1 && wrk->dst_width <= 2);
183
0
      for (i = 0; i < wrk->num_channels * wrk->dst_width; ++i) {
184
0
        wrk->dst[i] = wrk->irow[i];
185
0
        wrk->irow[i] = 0;
186
0
      }
187
0
    }
188
0
    wrk->y_accum += wrk->y_add;
189
0
    wrk->dst += wrk->dst_stride;
190
0
    ++wrk->dst_y;
191
0
  }
192
0
}
193
194
//------------------------------------------------------------------------------
195
196
WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
197
WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
198
199
WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
200
WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
201
202
extern VP8CPUInfo VP8GetCPUInfo;
203
extern void WebPRescalerDspInitSSE2(void);
204
extern void WebPRescalerDspInitMIPS32(void);
205
extern void WebPRescalerDspInitMIPSdspR2(void);
206
extern void WebPRescalerDspInitMSA(void);
207
extern void WebPRescalerDspInitNEON(void);
208
209
0
WEBP_DSP_INIT_FUNC(WebPRescalerDspInit) {
210
0
#if !defined(WEBP_REDUCE_SIZE)
211
0
#if !WEBP_NEON_OMIT_C_CODE
212
0
  WebPRescalerExportRowExpand = WebPRescalerExportRowExpand_C;
213
0
  WebPRescalerExportRowShrink = WebPRescalerExportRowShrink_C;
214
0
#endif
215
216
0
  WebPRescalerImportRowExpand = WebPRescalerImportRowExpand_C;
217
0
  WebPRescalerImportRowShrink = WebPRescalerImportRowShrink_C;
218
219
0
  if (VP8GetCPUInfo != NULL) {
220
0
#if defined(WEBP_HAVE_SSE2)
221
0
    if (VP8GetCPUInfo(kSSE2)) {
222
0
      WebPRescalerDspInitSSE2();
223
0
    }
224
0
#endif
225
#if defined(WEBP_USE_MIPS32)
226
    if (VP8GetCPUInfo(kMIPS32)) {
227
      WebPRescalerDspInitMIPS32();
228
    }
229
#endif
230
#if defined(WEBP_USE_MIPS_DSP_R2)
231
    if (VP8GetCPUInfo(kMIPSdspR2)) {
232
      WebPRescalerDspInitMIPSdspR2();
233
    }
234
#endif
235
#if defined(WEBP_USE_MSA)
236
    if (VP8GetCPUInfo(kMSA)) {
237
      WebPRescalerDspInitMSA();
238
    }
239
#endif
240
0
  }
241
242
#if defined(WEBP_HAVE_NEON)
243
  if (WEBP_NEON_OMIT_C_CODE ||
244
      (VP8GetCPUInfo != NULL && VP8GetCPUInfo(kNEON))) {
245
    WebPRescalerDspInitNEON();
246
  }
247
#endif
248
249
0
  assert(WebPRescalerExportRowExpand != NULL);
250
0
  assert(WebPRescalerExportRowShrink != NULL);
251
0
  assert(WebPRescalerImportRowExpand != NULL);
252
0
  assert(WebPRescalerImportRowShrink != NULL);
253
0
#endif  // WEBP_REDUCE_SIZE
254
0
}