oil 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,24 @@
1
+ /**
2
+ * Copyright (c) 2014-2016 Timothy Elliott
3
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ * of this software and associated documentation files (the "Software"), to deal
5
+ * in the Software without restriction, including without limitation the rights
6
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ * copies of the Software, and to permit persons to whom the Software is
8
+ * furnished to do so, subject to the following conditions:
9
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
13
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ * THE SOFTWARE.
20
+ */
21
+
1
22
  #include "resample.h"
2
23
  #include <stdint.h>
3
24
  #include <math.h>
@@ -26,7 +47,7 @@ typedef int64_t fix33_30;
26
47
  * The best possible value was determined by comparing to a reference
27
48
  * implementation and comparing values for the minimal number of errors.
28
49
  */
29
- #define TOPOFF 4096
50
+ #define TOPOFF 8192
30
51
 
31
52
  /**
32
53
  * Signed type that uses 1 bit for signedness, 1 bit for the integer, and 30
@@ -42,9 +63,9 @@ typedef int32_t fix1_30;
42
63
  /**
43
64
  * Calculate the greatest common denominator between a and b.
44
65
  */
45
- static long gcd (long a, long b)
66
+ static uint32_t gcd(uint32_t a, uint32_t b)
46
67
  {
47
- long c;
68
+ uint32_t c;
48
69
  while (a != 0) {
49
70
  c = a;
50
71
  a = b%a;
@@ -56,46 +77,32 @@ static long gcd (long a, long b)
56
77
  /**
57
78
  * Round and clamp a fix33_30 value between 0 and 255. Returns an unsigned char.
58
79
  */
59
- static unsigned char clamp(fix33_30 x)
80
+ static uint16_t clamp(fix33_30 x)
60
81
  {
61
- if (x < 0) {
62
- return 0;
63
- }
64
-
65
- /* bump up rounding errors before truncating */
66
- x += TOPOFF;
67
-
68
- /* This is safe because we have the < 0 check above and a sample can't
69
- * end up with a value over 512 */
70
- if (x & (1l<<38)) {
71
- return 255;
72
- }
73
-
82
+ x += 1 << 29;
83
+ x = x < 0 ? 0 : (x > (65535L << 30) ? (65535L << 30) : x);
74
84
  return x >> 30;
75
85
  }
76
86
 
77
- /**
78
- * Map from a discreet dest coordinate to a continuous source coordinate.
79
- * The resulting coordinate can range from -0.5 to the maximum of the
80
- * destination image dimension.
81
- */
82
- static double map(long pos, double scale)
87
+ static uint8_t clamp8(fix33_30 x)
83
88
  {
84
- return (pos + 0.5) / scale - 0.5;
89
+ return (clamp(x) + (1 << 7)) / 257;
85
90
  }
86
91
 
87
92
  /**
88
93
  * Given input and output dimensions and an output position, return the
89
94
  * corresponding input position and put the sub-pixel remainder in rest.
95
+ *
96
+ * Map from a discreet dest coordinate to a continuous source coordinate.
97
+ * The resulting coordinate can range from -0.5 to the maximum of the
98
+ * destination image dimension.
90
99
  */
91
- long split_map(unsigned long dim_in, unsigned long dim_out, unsigned long pos,
92
- float *rest)
100
+ int32_t split_map(uint32_t dim_in, uint32_t dim_out, uint32_t pos, float *rest)
93
101
  {
94
- double scale, smp;
95
- long smp_i;
102
+ double smp;
103
+ int32_t smp_i;
96
104
 
97
- scale = dim_out / (double)dim_in;
98
- smp = map(pos, scale);
105
+ smp = (pos + 0.5) * ((double)dim_in / dim_out) - 0.5;
99
106
  smp_i = smp < 0 ? -1 : smp;
100
107
  *rest = smp - smp_i;
101
108
  return smp_i;
@@ -108,42 +115,14 @@ long split_map(unsigned long dim_in, unsigned long dim_out, unsigned long pos,
108
115
  * When we reduce an image by a factor of two, we need to scale our resampling
109
116
  * function by two as well in order to avoid aliasing.
110
117
  */
111
- long calc_taps(long dim_in, long dim_out)
118
+ uint64_t calc_taps(uint32_t dim_in, uint32_t dim_out)
112
119
  {
113
- long tmp;
114
-
120
+ uint64_t tmp;
115
121
  if (dim_out > dim_in) {
116
122
  return TAPS;
117
123
  }
118
-
119
- tmp = dim_in * TAPS / dim_out;
120
- /* Round up to the nearest even integer */
121
- return tmp + (tmp&1);
122
- }
123
-
124
- /**
125
- * Helper macros to extract byte components from an int32_t holding rgba.
126
- */
127
- #define rgba_r(x) ((x) & 0x000000FF)
128
- #define rgba_g(x) (((x) & 0x0000FF00) >> 8)
129
- #define rgba_b(x) (((x) & 0x00FF0000) >> 16)
130
- #define rgba_a(x) (((x) & 0xFF000000) >> 24)
131
-
132
- /**
133
- * Convert rgb values in fix33_30 types to a uint32_t.
134
- */
135
- static uint32_t fix33_30_to_rgbx(fix33_30 r, fix33_30 g, fix33_30 b)
136
- {
137
- return clamp(r) + ((uint32_t)clamp(g) << 8) +
138
- ((uint32_t)clamp(b) << 16);
139
- }
140
-
141
- /**
142
- * Convert rgba values in fix33_30 types to a uint32_t.
143
- */
144
- static uint32_t fix33_30_to_rgba(fix33_30 r, fix33_30 g, fix33_30 b, fix33_30 a)
145
- {
146
- return fix33_30_to_rgbx(r, g, b) + ((uint32_t)clamp(a) << 24);
124
+ tmp = (uint64_t)TAPS * dim_in / dim_out;
125
+ return tmp - (tmp & 1);
147
126
  }
148
127
 
149
128
  /**
@@ -152,9 +131,9 @@ static uint32_t fix33_30_to_rgba(fix33_30 r, fix33_30 g, fix33_30 b, fix33_30 a)
152
131
  static float catrom(float x)
153
132
  {
154
133
  if (x<1) {
155
- return (3*x*x*x - 5*x*x + 2) / 2;
134
+ return ((3*x - 5)*x*x + 2) / 2;
156
135
  }
157
- return (-1*x*x*x + 5*x*x - 8*x + 4) / 2;
136
+ return (((5 - x)*x - 8)*x + 4) / 2;
158
137
  }
159
138
 
160
139
  /**
@@ -171,127 +150,191 @@ static fix1_30 f_to_fix1_30(float x)
171
150
  *
172
151
  * The coefficients are stored as fix1_30 fixed point ints in coeffs.
173
152
  */
174
- static void calc_coeffs(fix1_30 *coeffs, float tx, long taps)
153
+ static void calc_coeffs(fix1_30 *coeffs, float tx, uint32_t taps)
175
154
  {
176
- long i;
177
- float tmp, total, tap_mult;
155
+ uint32_t i;
156
+ float tmp, tap_mult, fudge;
178
157
  fix1_30 tmp_fixed;
179
158
 
180
- total = 0;
181
-
182
- tap_mult = (double)taps / TAPS;
159
+ tap_mult = (float)taps / TAPS;
183
160
  tx = 1 - tx - taps / 2;
161
+ fudge = 1.0;
184
162
 
185
163
  for (i=0; i<taps; i++) {
186
- tmp = catrom(fabsf(tx) / tap_mult);
164
+ tmp = catrom(fabsf(tx) / tap_mult) / tap_mult;
165
+ fudge -= tmp;
187
166
  tmp_fixed = f_to_fix1_30(tmp);
188
167
  coeffs[i] = tmp_fixed;
189
- total += tmp;
190
168
  tx += 1;
191
169
  }
170
+ coeffs[taps / 2] += f_to_fix1_30(fudge);
171
+ }
192
172
 
193
- for (i=0; i<taps; i++) {
194
- coeffs[i] /= total;
173
+ /* bicubic y-scaler */
174
+
175
+ static uint8_t linear_sample_to_srgb(uint16_t in)
176
+ {
177
+ double in_f, s1, s2, s3;
178
+ if (in <= 248) {
179
+ return (in * 3295 + 32768) >> 16;
195
180
  }
181
+ in_f = in / 65535.0;
182
+ s1 = sqrt(in_f);
183
+ s2 = sqrt(s1);
184
+ s3 = sqrt(s2);
185
+ return (0.0427447 + 0.547242 * s1 + 0.928361 * s2 - 0.518123 * s3) * 255 + 0.5;
196
186
  }
197
187
 
198
- /**
199
- * Generic yscaler, operates on arbitrary sized samples.
200
- */
201
- static void yscale_gen(long len, long height, fix1_30 *coeffs,
202
- unsigned char **in, unsigned char *out)
188
+ static void strip_scale_rgbx(uint16_t **in, uint32_t strip_height, size_t len,
189
+ uint8_t *out, fix1_30 *coeffs)
203
190
  {
204
- long i, j;
205
- fix33_30 coeff, total;
191
+ size_t i;
192
+ uint32_t j;
193
+ fix33_30 coeff, sum[3];
206
194
 
207
- for (i=0; i<len; i++) {
208
- total = 0;
209
- for (j=0; j<height; j++) {
195
+ for (i=0; i<len; i+=4) {
196
+ sum[0] = sum[1] = sum[2] = 0;
197
+ for (j=0; j<strip_height; j++) {
210
198
  coeff = coeffs[j];
211
- total += coeff * in[j][i];
199
+ sum[0] += coeff * in[j][i];
200
+ sum[1] += coeff * in[j][i + 1];
201
+ sum[2] += coeff * in[j][i + 2];
212
202
  }
213
- out[i] = clamp(total);
203
+ out[0] = linear_sample_to_srgb(clamp(sum[0]));
204
+ out[1] = linear_sample_to_srgb(clamp(sum[1]));
205
+ out[2] = linear_sample_to_srgb(clamp(sum[2]));
206
+ out[3] = 0;
207
+ out += 4;
214
208
  }
215
209
  }
216
210
 
217
- /**
218
- * RGBA yscaler, fetches 32-bytes at a time from memory to improve mem read
219
- * performance.
220
- */
221
- static void yscale_rgba(long width, long height, fix1_30 *coeffs,
222
- uint32_t **sl_in, uint32_t *sl_out)
211
+ static void strip_scale_rgb(uint16_t **in, uint32_t strip_height, size_t len,
212
+ uint8_t *out, fix1_30 *coeffs)
223
213
  {
224
- long i, j;
225
- fix33_30 r, g, b, a, coeff;
226
- uint32_t sample;
214
+ size_t i;
215
+ uint32_t j;
216
+ fix33_30 coeff, sum[3];
227
217
 
228
- for (i=0; i<width; i++) {
229
- r = g = b = a = 0;
230
- for (j=0; j<height; j++) {
218
+ for (i=0; i<len; i+=4) {
219
+ sum[0] = sum[1] = sum[2] = 0;
220
+ for (j=0; j<strip_height; j++) {
231
221
  coeff = coeffs[j];
232
- sample = sl_in[j][i];
233
- r += coeff * rgba_r(sample);
234
- g += coeff * rgba_g(sample);
235
- b += coeff * rgba_b(sample);
236
- a += coeff * rgba_a(sample);
222
+ sum[0] += coeff * in[j][i];
223
+ sum[1] += coeff * in[j][i + 1];
224
+ sum[2] += coeff * in[j][i + 2];
237
225
  }
238
- sl_out[i] = fix33_30_to_rgba(r, g, b, a);
226
+ out[0] = linear_sample_to_srgb(clamp(sum[0]));
227
+ out[1] = linear_sample_to_srgb(clamp(sum[1]));
228
+ out[2] = linear_sample_to_srgb(clamp(sum[2]));
229
+ out += 3;
239
230
  }
240
231
  }
241
232
 
242
- /**
243
- * RGBX yscaler, fetches 32-bytes at a time from memory to improve mem read
244
- * performance and ignores the last value.
245
- */
246
- static void yscale_rgbx(long width, long height, fix1_30 *coeffs,
247
- uint32_t **sl_in, uint32_t *sl_out)
233
+ static void strip_scale_g(uint16_t **in, uint32_t strip_height, size_t len,
234
+ uint8_t *out, fix1_30 *coeffs)
235
+ {
236
+ size_t i;
237
+ uint32_t j;
238
+ fix33_30 sum;
239
+
240
+ for (i=0; i<len; i++) {
241
+ sum = 0;
242
+ for (j=0; j<strip_height; j++) {
243
+ sum += (fix33_30)coeffs[j] * in[j][i];
244
+ }
245
+ out[i] = clamp8(sum);
246
+ }
247
+ }
248
+
249
+ static void strip_scale_rgba(uint16_t **in, uint32_t strip_height, size_t len,
250
+ uint8_t *out, fix1_30 *coeffs)
248
251
  {
249
- long i, j;
250
- fix33_30 r, g, b, coeff;
251
- uint32_t sample;
252
+ size_t i;
253
+ uint32_t j;
254
+ fix33_30 coeff, sum[4];
252
255
 
253
- for (i=0; i<width; i++) {
254
- r = g = b = 0;
255
- for (j=0; j<height; j++) {
256
+ for (i=0; i<len; i+=4) {
257
+ sum[0] = sum[1] = sum[2] = sum[3] = 0;
258
+ for (j=0; j<strip_height; j++) {
256
259
  coeff = coeffs[j];
257
- sample = sl_in[j][i];
258
- r += coeff * rgba_r(sample);
259
- g += coeff * rgba_g(sample);
260
- b += coeff * rgba_b(sample);
260
+ sum[0] += coeff * in[j][i];
261
+ sum[1] += coeff * in[j][i + 1];
262
+ sum[2] += coeff * in[j][i + 2];
263
+ sum[3] += coeff * in[j][i + 3];
261
264
  }
262
- sl_out[i] = fix33_30_to_rgbx(r, g, b);
265
+ out[0] = linear_sample_to_srgb(clamp(sum[0]));
266
+ out[1] = linear_sample_to_srgb(clamp(sum[1]));
267
+ out[2] = linear_sample_to_srgb(clamp(sum[2]));
268
+ out[3] = clamp8(sum[3]);
269
+ out += 4;
263
270
  }
264
271
  }
265
272
 
266
- void strip_scale(void **in, long strip_height, long width, void *out, float ty,
267
- int cmp, int opts)
273
+ static void strip_scale_cmyk(uint16_t **in, uint32_t strip_height, size_t len,
274
+ uint8_t *out, fix1_30 *coeffs)
275
+ {
276
+ size_t i;
277
+ uint32_t j;
278
+ fix33_30 coeff, sum[4];
279
+
280
+ for (i=0; i<len; i+=4) {
281
+ sum[0] = sum[1] = sum[2] = sum[3] = 0;
282
+ for (j=0; j<strip_height; j++) {
283
+ coeff = coeffs[j];
284
+ sum[0] += coeff * in[j][i];
285
+ sum[1] += coeff * in[j][i + 1];
286
+ sum[2] += coeff * in[j][i + 2];
287
+ sum[3] += coeff * in[j][i + 3];
288
+ }
289
+ out[0] = clamp8(sum[0]);
290
+ out[1] = clamp8(sum[1]);
291
+ out[2] = clamp8(sum[2]);
292
+ out[3] = clamp8(sum[3]);
293
+ out += 4;
294
+ }
295
+ }
296
+
297
+ int strip_scale(uint16_t **in, uint32_t strip_height, size_t len, uint8_t *out,
298
+ float ty, enum oil_colorspace cs)
268
299
  {
269
300
  fix1_30 *coeffs;
270
301
 
271
302
  coeffs = malloc(strip_height * sizeof(fix1_30));
303
+ if (!coeffs) {
304
+ return -2; // unable to allocate
305
+ }
272
306
  calc_coeffs(coeffs, ty, strip_height);
273
307
 
274
- if (cmp == 4 && (opts & OIL_FILLER)) {
275
- yscale_rgbx(width, strip_height, coeffs, (uint32_t **)in,
276
- (uint32_t *)out);
277
- } else if (cmp == 4) {
278
- yscale_rgba(width, strip_height, coeffs, (uint32_t **)in,
279
- (uint32_t *)out);
280
- } else {
281
- yscale_gen(cmp * width, strip_height, coeffs,
282
- (unsigned char **)in, (unsigned char *)out);
308
+ switch(cs) {
309
+ case OIL_CS_G:
310
+ case OIL_CS_GA:
311
+ strip_scale_g(in, strip_height, len, out, coeffs);
312
+ break;
313
+ case OIL_CS_RGB:
314
+ strip_scale_rgb(in, strip_height, len, out, coeffs);
315
+ break;
316
+ case OIL_CS_RGBX:
317
+ strip_scale_rgbx(in, strip_height, len, out, coeffs);
318
+ break;
319
+ case OIL_CS_RGBA:
320
+ strip_scale_rgba(in, strip_height, len, out, coeffs);
321
+ break;
322
+ case OIL_CS_CMYK:
323
+ strip_scale_cmyk(in, strip_height, len, out, coeffs);
324
+ break;
283
325
  }
284
326
 
285
327
  free(coeffs);
328
+ return 0;
286
329
  }
287
330
 
288
331
  /* Bicubic x scaler */
289
332
 
290
- static void sample_generic(int cmp, long taps, fix1_30 *coeffs,
291
- unsigned char *in, unsigned char *out)
333
+ static void sample_generic(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
334
+ uint16_t *out, uint8_t cmp)
292
335
  {
293
- int i;
294
- long j;
336
+ uint8_t i;
337
+ uint32_t j;
295
338
  fix33_30 total, coeff;
296
339
 
297
340
  for (i=0; i<cmp; i++) {
@@ -304,139 +347,113 @@ static void sample_generic(int cmp, long taps, fix1_30 *coeffs,
304
347
  }
305
348
  }
306
349
 
307
- static uint32_t sample_rgba(long taps, fix1_30 *coeffs, uint32_t *in)
350
+ static void sample_rgba(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
351
+ uint16_t *out)
308
352
  {
309
- long i;
310
- fix33_30 r, g, b, a, coeff;
311
- uint32_t sample;
353
+ uint32_t i;
354
+ fix33_30 sum[4], coeff;
312
355
 
313
- r = g = b = a = 0;
356
+ sum[0] = sum[1] = sum[2] = sum[3] = 0;
314
357
  for (i=0; i<taps; i++) {
315
358
  coeff = coeffs[i];
316
- sample = in[i];
317
- r += coeff * rgba_r(sample);
318
- g += coeff * rgba_g(sample);
319
- b += coeff * rgba_b(sample);
320
- a += coeff * rgba_a(sample);
359
+ sum[0] += coeff * in[0];
360
+ sum[1] += coeff * in[1];
361
+ sum[2] += coeff * in[2];
362
+ sum[3] += coeff * in[3];
363
+ in += 4;
321
364
  }
322
- return fix33_30_to_rgba(r, g, b, a);
365
+ out[0] = clamp(sum[0]);
366
+ out[1] = clamp(sum[1]);
367
+ out[2] = clamp(sum[2]);
368
+ out[3] = clamp(sum[3]);
323
369
  }
324
370
 
325
- static uint32_t sample_rgbx(long taps, fix1_30 *coeffs, uint32_t *in)
371
+ static void sample_rgbx(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
372
+ uint16_t *out)
326
373
  {
327
- long i;
328
- fix33_30 r, g, b, coeff;
329
- uint32_t sample;
374
+ uint32_t i;
375
+ fix33_30 sum[3], coeff;
330
376
 
331
- r = g = b = 0;
377
+ sum[0] = sum[1] = sum[2] = 0;
332
378
  for (i=0; i<taps; i++) {
333
379
  coeff = coeffs[i];
334
- sample = in[i];
335
- r += coeff * rgba_r(sample);
336
- g += coeff * rgba_g(sample);
337
- b += coeff * rgba_b(sample);
380
+ sum[0] += coeff * in[0];
381
+ sum[1] += coeff * in[1];
382
+ sum[2] += coeff * in[2];
383
+ in += 4;
338
384
  }
339
- return fix33_30_to_rgbx(r, g, b);
385
+ out[0] = clamp(sum[0]);
386
+ out[1] = clamp(sum[1]);
387
+ out[2] = clamp(sum[2]);
388
+ out[3] = 0;
340
389
  }
341
390
 
342
- static void xscale_set_sample(long taps, fix1_30 *coeffs, void *in, void *out,
343
- int cmp, int opts)
391
+ static void xscale_set_sample(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
392
+ uint16_t *out, enum oil_colorspace cs)
344
393
  {
345
- if (cmp == 4 && (opts & OIL_FILLER)) {
346
- *(uint32_t *)out = sample_rgbx(taps, coeffs, (uint32_t *)in);
347
- } else if (cmp == 4) {
348
- *(uint32_t *)out = sample_rgba(taps, coeffs, (uint32_t *)in);
349
- } else {
350
- sample_generic(cmp, taps, coeffs, in, out);
394
+ switch(cs) {
395
+ case OIL_CS_G:
396
+ case OIL_CS_GA:
397
+ case OIL_CS_RGB:
398
+ sample_generic(taps, coeffs, in, out, CS_TO_CMP(cs));
399
+ break;
400
+ case OIL_CS_RGBX:
401
+ sample_rgbx(taps, coeffs, in, out);
402
+ break;
403
+ case OIL_CS_RGBA:
404
+ case OIL_CS_CMYK:
405
+ sample_rgba(taps, coeffs, in, out);
406
+ break;
351
407
  }
352
408
  }
353
409
 
354
- /* padded scanline */
355
-
356
- /**
357
- * Scanline with extra space at the beginning and end. This allows us to extend
358
- * a scanline to the left and right. This in turn allows resizing functions
359
- * to operate past the edges of the scanline without having to check for
360
- * boundaries.
361
- */
362
- struct padded_sl {
363
- unsigned char *buf;
364
- unsigned char *pad_left;
365
- long inner_width;
366
- long pad_width;
367
- int cmp;
368
- };
369
-
370
- void padded_sl_init(struct padded_sl *psl, long inner_width, long pad_width,
371
- int cmp)
372
- {
373
- psl->inner_width = inner_width;
374
- psl->pad_width = pad_width;
375
- psl->cmp = cmp;
376
- psl->pad_left = malloc((inner_width + 2 * pad_width) * cmp);
377
- psl->buf = psl->pad_left + pad_width * cmp;
378
- }
379
-
380
- void padded_sl_free(struct padded_sl *psl)
381
- {
382
- free(psl->pad_left);
383
- }
384
-
385
- /**
386
- * pad points to the first byte in the pad area.
387
- * src points to the sample that will be replicated in the pad area.
388
- * width is the number of samples in the pad area.
389
- * cmp is the number of components per sample.
390
- */
391
- static void padded_sl_pad(unsigned char *pad, unsigned char *src, int width,
392
- int cmp)
410
+ void padded_sl_extend_edges(uint16_t *buf, uint32_t width, size_t pad_len,
411
+ uint8_t cmp)
393
412
  {
394
- int i, j;
395
-
396
- for (i=0; i<width; i++)
397
- for (j=0; j<cmp; j++)
398
- pad[i * cmp + j] = src[j];
413
+ uint16_t *pad_right;
414
+ size_t i;
415
+ pad_right = buf + pad_len + (size_t)width * cmp;
416
+ for (i=0; i<pad_len; i++) {
417
+ buf[i] = (buf + pad_len)[i % cmp];
418
+ pad_right[i] = (pad_right - cmp)[i % cmp];
419
+ }
399
420
  }
400
421
 
401
- static void padded_sl_extend_edges(struct padded_sl *psl)
422
+ size_t padded_sl_len_offset(uint32_t in_width, uint32_t out_width,
423
+ uint8_t cmp, size_t *offset)
402
424
  {
403
- unsigned char *pad_right;
404
-
405
- padded_sl_pad(psl->pad_left, psl->buf, psl->pad_width, psl->cmp);
406
- pad_right = psl->buf + psl->inner_width * psl->cmp;
407
- padded_sl_pad(pad_right, pad_right - psl->cmp, psl->pad_width, psl->cmp);
425
+ uint64_t taps;
426
+ taps = calc_taps(in_width, out_width);
427
+ *offset = (taps / 2 + 1) * cmp;
428
+ return ((size_t)in_width * cmp + *offset * 2) * sizeof(uint16_t);
408
429
  }
409
430
 
410
- void xscale(unsigned char *in, long in_width, unsigned char *out,
411
- long out_width, int cmp, int opts)
431
+ int xscale_padded(uint16_t *in, uint32_t in_width, uint16_t *out,
432
+ uint32_t out_width, enum oil_colorspace cs)
412
433
  {
413
434
  float tx;
414
435
  fix1_30 *coeffs;
415
- long i, j, xsmp_i, in_chunk, out_chunk, scale_gcd, taps;
416
- unsigned char *out_pos, *rpadv, *tmp;
417
- struct padded_sl psl;
436
+ uint32_t i, j, in_chunk, out_chunk, scale_gcd;
437
+ int32_t xsmp_i;
438
+ uint64_t taps;
439
+ uint16_t *out_pos, *tmp;
440
+ uint8_t cmp;
441
+
442
+ if (!in_width || !out_width) {
443
+ return -1; // bad input parameter
444
+ }
418
445
 
446
+ cmp = CS_TO_CMP(cs);
419
447
  taps = calc_taps(in_width, out_width);
420
448
  coeffs = malloc(taps * sizeof(fix1_30));
449
+ if (!coeffs) {
450
+ return -2; // unable to allocate space for coefficients
451
+ }
421
452
 
422
453
  scale_gcd = gcd(in_width, out_width);
423
454
  in_chunk = in_width / scale_gcd;
424
455
  out_chunk = out_width / scale_gcd;
425
456
 
426
- if (in_width < taps * 2) {
427
- padded_sl_init(&psl, in_width, taps / 2 + 1, cmp);
428
- memcpy(psl.buf, in, in_width * cmp);
429
- rpadv = psl.buf;
430
- } else {
431
- /* just the ends of the scanline with edges extended */
432
- padded_sl_init(&psl, 2 * taps - 2, taps / 2 + 1, cmp);
433
- memcpy(psl.buf, in, (taps - 1) * cmp);
434
- memcpy(psl.buf + (taps - 1) * cmp, in + (in_width - taps + 1) * cmp, (taps - 1) * cmp);
435
- rpadv = psl.buf + (2 * taps - 2 - in_width) * cmp;
436
- }
437
-
438
- padded_sl_extend_edges(&psl);
439
-
440
457
  for (i=0; i<out_chunk; i++) {
441
458
  xsmp_i = split_map(in_width, out_width, i, &tx);
442
459
  calc_coeffs(coeffs, tx, taps);
@@ -444,21 +461,478 @@ void xscale(unsigned char *in, long in_width, unsigned char *out,
444
461
  xsmp_i += 1 - taps / 2;
445
462
  out_pos = out + i * cmp;
446
463
  for (j=0; j<scale_gcd; j++) {
447
- if (xsmp_i < 0) {
448
- tmp = psl.buf;
449
- } else if (xsmp_i > in_width - taps) {
450
- tmp = rpadv;
451
- } else {
452
- tmp = in;
453
- }
454
- tmp += xsmp_i * cmp;
455
- xscale_set_sample(taps, coeffs, tmp, out_pos, cmp, opts);
464
+ tmp = in + xsmp_i * cmp;
465
+ xscale_set_sample(taps, coeffs, tmp, out_pos, cs);
456
466
  out_pos += out_chunk * cmp;
457
-
458
467
  xsmp_i += in_chunk;
459
468
  }
460
469
  }
461
470
 
462
- padded_sl_free(&psl);
463
471
  free(coeffs);
472
+ return 0;
473
+ }
474
+
475
+ /* scanline ring buffer */
476
+
477
+ int sl_rbuf_init(struct sl_rbuf *rb, uint32_t height, size_t sl_len)
478
+ {
479
+ rb->height = height;
480
+ rb->count = 0;
481
+ rb->length = sl_len;
482
+ rb->buf = malloc(sl_len * height * sizeof(uint16_t));
483
+ if (!rb->buf) {
484
+ return -2;
485
+ }
486
+ rb->virt = malloc(sizeof(uint8_t *) * height);
487
+ if (!rb->virt) {
488
+ free(rb->buf);
489
+ return -2;
490
+ }
491
+ return 0;
492
+ }
493
+
494
+ void sl_rbuf_free(struct sl_rbuf *rb)
495
+ {
496
+ free(rb->buf);
497
+ free(rb->virt);
498
+ }
499
+
500
+ uint16_t *sl_rbuf_next(struct sl_rbuf *rb)
501
+ {
502
+ return rb->buf + (rb->count++ % rb->height) * rb->length;
503
+ }
504
+
505
+ uint16_t **sl_rbuf_virt(struct sl_rbuf *rb, uint32_t last_target)
506
+ {
507
+ uint32_t i, safe, height, last_idx;
508
+ height = rb->height;
509
+ last_idx = rb->count - 1;
510
+
511
+ // Make sure we have the 1st scanline if extending upwards
512
+ if (last_target < last_idx && last_idx > height - 1) {
513
+ return 0;
514
+ }
515
+
516
+ for (i=0; i<height; i++) {
517
+ safe = last_target < i ? 0 : last_target - i;
518
+ safe = safe > last_idx ? last_idx : safe;
519
+ rb->virt[height - i - 1] = rb->buf + (safe % height) * rb->length;
520
+ }
521
+ return rb->virt;
522
+ }
523
+
524
+ /* xscaler */
525
+
526
+ int xscaler_init(struct xscaler *xs, uint32_t width_in, uint32_t width_out,
527
+ enum oil_colorspace cs)
528
+ {
529
+ size_t psl_len, psl_offset;
530
+ uint16_t *psl_buf;
531
+
532
+ psl_len = padded_sl_len_offset(width_in, width_out, CS_TO_CMP(cs), &psl_offset);
533
+ psl_buf = malloc(psl_len);
534
+ if (!psl_buf) {
535
+ return -2;
536
+ }
537
+
538
+ xs->psl_buf = psl_buf;
539
+ xs->psl_offset = psl_offset;
540
+ xs->psl_pos0 = psl_buf + psl_offset;
541
+ xs->width_in = width_in;
542
+ xs->width_out = width_out;
543
+ xs->cs = cs;
544
+
545
+ return 0;
546
+ }
547
+
548
+ void xscaler_free(struct xscaler *xs)
549
+ {
550
+ free(xs->psl_buf);
551
+ }
552
+
553
+ void xscaler_scale(struct xscaler *xs, uint16_t *out_buf)
554
+ {
555
+ padded_sl_extend_edges(xs->psl_buf, xs->width_in, xs->psl_offset, CS_TO_CMP(xs->cs));
556
+ xscale_padded(xs->psl_pos0, xs->width_in, out_buf, xs->width_out, xs->cs);
557
+ }
558
+
559
+ /* yscaler */
560
+
561
+ static void yscaler_map_pos(struct yscaler *ys, uint32_t pos)
562
+ {
563
+ long target;
564
+ target = split_map(ys->in_height, ys->out_height, pos, &ys->ty);
565
+ ys->target = target + ys->rb.height / 2;
566
+ }
567
+
568
+ int yscaler_init(struct yscaler *ys, uint32_t in_height, uint32_t out_height,
569
+ uint32_t width, enum oil_colorspace cs)
570
+ {
571
+ uint8_t cmp;
572
+ int ret;
573
+ uint32_t taps;
574
+ taps = calc_taps(in_height, out_height);
575
+ ys->in_height = in_height;
576
+ ys->out_height = out_height;
577
+ ys->width = width;
578
+ ys->cs = cs;
579
+ cmp = CS_TO_CMP(cs);
580
+ if (cs == OIL_CS_RGB) {
581
+ cmp = 4;
582
+ }
583
+ ret = sl_rbuf_init(&ys->rb, taps, width * cmp);
584
+ yscaler_map_pos(ys, 0);
585
+ return ret;
586
+ }
587
+
588
+ void yscaler_free(struct yscaler *ys)
589
+ {
590
+ sl_rbuf_free(&ys->rb);
591
+ }
592
+
593
+ uint16_t *yscaler_next(struct yscaler *ys)
594
+ {
595
+ if (ys->rb.count == ys->in_height || ys->rb.count > ys->target) {
596
+ return 0;
597
+ }
598
+ return sl_rbuf_next(&ys->rb);
599
+ }
600
+
601
+ int yscaler_scale(struct yscaler *ys, uint8_t *out, uint32_t pos)
602
+ {
603
+ int ret;
604
+ uint16_t **virt;
605
+ virt = sl_rbuf_virt(&ys->rb, ys->target);
606
+ ret = strip_scale(virt, ys->rb.height, ys->rb.length, out, ys->ty, ys->cs);
607
+ yscaler_map_pos(ys, pos + 1);
608
+ return ret;
609
+ }
610
+
611
+ /* Color Space Helpers */
612
+
613
+ #define EXPAND8(X) ((X<<8) + X)
614
+
615
+ static uint16_t srgb_sample_to_linear(uint8_t x)
616
+ {
617
+ static const uint16_t s2l_map[256] = {
618
+ 0x0000, 0x0014, 0x0028, 0x003c, 0x0050, 0x0063, 0x0077, 0x008b,
619
+ 0x009f, 0x00b3, 0x00c7, 0x00db, 0x00f1, 0x0108, 0x0120, 0x0139,
620
+ 0x0154, 0x016f, 0x018c, 0x01ab, 0x01ca, 0x01eb, 0x020e, 0x0232,
621
+ 0x0257, 0x027d, 0x02a5, 0x02ce, 0x02f9, 0x0325, 0x0353, 0x0382,
622
+ 0x03b3, 0x03e5, 0x0418, 0x044d, 0x0484, 0x04bc, 0x04f6, 0x0532,
623
+ 0x056f, 0x05ad, 0x05ed, 0x062f, 0x0673, 0x06b8, 0x06fe, 0x0747,
624
+ 0x0791, 0x07dd, 0x082a, 0x087a, 0x08ca, 0x091d, 0x0972, 0x09c8,
625
+ 0x0a20, 0x0a79, 0x0ad5, 0x0b32, 0x0b91, 0x0bf2, 0x0c55, 0x0cba,
626
+ 0x0d20, 0x0d88, 0x0df2, 0x0e5e, 0x0ecc, 0x0f3c, 0x0fae, 0x1021,
627
+ 0x1097, 0x110e, 0x1188, 0x1203, 0x1280, 0x1300, 0x1381, 0x1404,
628
+ 0x1489, 0x1510, 0x159a, 0x1625, 0x16b2, 0x1741, 0x17d3, 0x1866,
629
+ 0x18fb, 0x1993, 0x1a2c, 0x1ac8, 0x1b66, 0x1c06, 0x1ca7, 0x1d4c,
630
+ 0x1df2, 0x1e9a, 0x1f44, 0x1ff1, 0x20a0, 0x2150, 0x2204, 0x22b9,
631
+ 0x2370, 0x242a, 0x24e5, 0x25a3, 0x2664, 0x2726, 0x27eb, 0x28b1,
632
+ 0x297b, 0x2a46, 0x2b14, 0x2be3, 0x2cb6, 0x2d8a, 0x2e61, 0x2f3a,
633
+ 0x3015, 0x30f2, 0x31d2, 0x32b4, 0x3399, 0x3480, 0x3569, 0x3655,
634
+ 0x3742, 0x3833, 0x3925, 0x3a1a, 0x3b12, 0x3c0b, 0x3d07, 0x3e06,
635
+ 0x3f07, 0x400a, 0x4110, 0x4218, 0x4323, 0x4430, 0x453f, 0x4651,
636
+ 0x4765, 0x487c, 0x4995, 0x4ab1, 0x4bcf, 0x4cf0, 0x4e13, 0x4f39,
637
+ 0x5061, 0x518c, 0x52b9, 0x53e9, 0x551b, 0x5650, 0x5787, 0x58c1,
638
+ 0x59fe, 0x5b3d, 0x5c7e, 0x5dc2, 0x5f09, 0x6052, 0x619e, 0x62ed,
639
+ 0x643e, 0x6591, 0x66e8, 0x6840, 0x699c, 0x6afa, 0x6c5b, 0x6dbe,
640
+ 0x6f24, 0x708d, 0x71f8, 0x7366, 0x74d7, 0x764a, 0x77c0, 0x7939,
641
+ 0x7ab4, 0x7c32, 0x7db3, 0x7f37, 0x80bd, 0x8246, 0x83d1, 0x855f,
642
+ 0x86f0, 0x8884, 0x8a1b, 0x8bb4, 0x8d50, 0x8eef, 0x9090, 0x9235,
643
+ 0x93dc, 0x9586, 0x9732, 0x98e2, 0x9a94, 0x9c49, 0x9e01, 0x9fbb,
644
+ 0xa179, 0xa339, 0xa4fc, 0xa6c2, 0xa88b, 0xaa56, 0xac25, 0xadf6,
645
+ 0xafca, 0xb1a1, 0xb37b, 0xb557, 0xb737, 0xb919, 0xbaff, 0xbce7,
646
+ 0xbed2, 0xc0c0, 0xc2b1, 0xc4a5, 0xc69c, 0xc895, 0xca92, 0xcc91,
647
+ 0xce94, 0xd099, 0xd2a1, 0xd4ad, 0xd6bb, 0xd8cc, 0xdae0, 0xdcf7,
648
+ 0xdf11, 0xe12e, 0xe34e, 0xe571, 0xe797, 0xe9c0, 0xebec, 0xee1b,
649
+ 0xf04d, 0xf282, 0xf4ba, 0xf6f5, 0xf933, 0xfb74, 0xfdb8, 0xffff,
650
+ };
651
+ return s2l_map[x];
652
+ }
653
+
654
+ static void srgbx_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
655
+ {
656
+ uint32_t i, j;
657
+ uint32_t sums[3];
658
+ for (i=0; i<in_width/n; i++) {
659
+ sums[0] = sums[1] = sums[2] = 0;
660
+ for (j=0; j<n; j++) {
661
+ sums[0] += srgb_sample_to_linear(in[0]);
662
+ sums[1] += srgb_sample_to_linear(in[1]);
663
+ sums[2] += srgb_sample_to_linear(in[2]);
664
+ in += 4;
665
+ }
666
+ out[0] = sums[0] / n;
667
+ out[1] = sums[1] / n;
668
+ out[2] = sums[2] / n;
669
+ out[3] = 0;
670
+ out += 4;
671
+ }
672
+ }
673
+
674
+ static void srgba_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
675
+ {
676
+ uint32_t i, j;
677
+ uint32_t sums[4];
678
+ for (i=0; i<in_width/n; i++) {
679
+ sums[0] = sums[1] = sums[2] = sums[3] = 0;
680
+ for (j=0; j<n; j++) {
681
+ sums[0] += srgb_sample_to_linear(in[0]);
682
+ sums[1] += srgb_sample_to_linear(in[1]);
683
+ sums[2] += srgb_sample_to_linear(in[2]);
684
+ sums[3] += EXPAND8(in[3]);
685
+ in += 4;
686
+ }
687
+ out[0] = sums[0] / n;
688
+ out[1] = sums[1] / n;
689
+ out[2] = sums[2] / n;
690
+ out[3] = sums[3] / n;
691
+ out += 4;
692
+ }
693
+ }
694
+
695
+ static void srgb_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
696
+ {
697
+ uint32_t i, j;
698
+ uint32_t sums[3];
699
+ for (i=0; i<in_width/n; i++) {
700
+ sums[0] = sums[1] = sums[2] = 0;
701
+ for (j=0; j<n; j++) {
702
+ sums[0] += srgb_sample_to_linear(in[0]);
703
+ sums[1] += srgb_sample_to_linear(in[1]);
704
+ sums[2] += srgb_sample_to_linear(in[2]);
705
+ in += 3;
706
+ }
707
+ out[0] = sums[0] / n;
708
+ out[1] = sums[1] / n;
709
+ out[2] = sums[2] / n;
710
+ out[3] = 0;
711
+ out += 4;
712
+ }
713
+ }
714
+
715
+ static void g_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
716
+ {
717
+ uint32_t i, j;
718
+ uint32_t sum;
719
+ for (i=0; i<in_width/n; i++) {
720
+ sum = 0;
721
+ for (j=0; j<n; j++) {
722
+ sum += EXPAND8(in[0]);
723
+ in++;
724
+ }
725
+ out[0] = sum / n;
726
+ out++;
727
+ }
728
+ }
729
+
730
+ static void ga_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
731
+ {
732
+ uint32_t i, j;
733
+ uint32_t sums[2];
734
+ for (i=0; i<in_width/n; i++) {
735
+ sums[0] = sums[1] = 0;
736
+ for (j=0; j<n; j++) {
737
+ sums[0] += EXPAND8(in[0]);
738
+ sums[1] += EXPAND8(in[1]);
739
+ in += 2;
740
+ }
741
+ out[0] = sums[0] / n;
742
+ out[1] = sums[1] / n;
743
+ out += 2;
744
+ }
745
+ }
746
+
747
+ static void cmyk_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
748
+ {
749
+ uint32_t i, j;
750
+ uint32_t sums[4];
751
+ for (i=0; i<in_width/n; i++) {
752
+ sums[0] = sums[1] = sums[2] = sums[3] = 0;
753
+ for (j=0; j<n; j++) {
754
+ sums[0] += EXPAND8(in[0]);
755
+ sums[1] += EXPAND8(in[1]);
756
+ sums[2] += EXPAND8(in[2]);
757
+ sums[3] += EXPAND8(in[3]);
758
+ in += 4;
759
+ }
760
+ out[0] = sums[0] / n;
761
+ out[1] = sums[1] / n;
762
+ out[2] = sums[2] / n;
763
+ out[3] = sums[3] / n;
764
+ out += 4;
765
+ }
766
+ }
767
+
768
+ static uint32_t calc_pre_shrink(uint32_t dim_in, uint32_t dim_out)
769
+ {
770
+ uint32_t max;
771
+ max = (2 * dim_in) / dim_out / 3;
772
+ if (max >= 4 && (dim_in % 4) == 0) {
773
+ return 4;
774
+ }
775
+ if (max >= 2 && (dim_in % 2) == 0) {
776
+ return 2;
777
+ }
778
+ return 1;
779
+ }
780
+
781
+ int preprocess_xscaler_init(struct preprocess_xscaler *pxs, uint32_t width_in,
782
+ uint32_t width_out, enum oil_colorspace cs_in)
783
+ {
784
+ enum oil_colorspace cs_out;
785
+
786
+ pxs->width_in = width_in;
787
+ pxs->cs_in = cs_in;
788
+ pxs->scale_factor = calc_pre_shrink(width_in, width_out);
789
+
790
+ /* Auto promote rgb components to rgbx for performance */
791
+ cs_out = cs_in == OIL_CS_RGB ? OIL_CS_RGBX : cs_in;
792
+ return xscaler_init(&pxs->xs, width_in / pxs->scale_factor, width_out, cs_out);
793
+ }
794
+
795
+ void preprocess_xscaler_free(struct preprocess_xscaler *pxs)
796
+ {
797
+ xscaler_free(&pxs->xs);
798
+ }
799
+
800
+ static void pre_convert_g(uint8_t *in, uint16_t *out,
801
+ uint32_t width_in, uint32_t scale_factor)
802
+ {
803
+ switch(scale_factor) {
804
+ case 1:
805
+ g_preprocess_nx(in, out, width_in, 1);
806
+ break;
807
+ case 2:
808
+ g_preprocess_nx(in, out, width_in, 2);
809
+ break;
810
+ case 4:
811
+ g_preprocess_nx(in, out, width_in, 4);
812
+ break;
813
+ }
814
+ }
815
+
816
+ static void pre_convert_ga(uint8_t *in, uint16_t *out,
817
+ uint32_t width_in, uint32_t scale_factor)
818
+ {
819
+ switch(scale_factor) {
820
+ case 1:
821
+ ga_preprocess_nx(in, out, width_in, 1);
822
+ break;
823
+ case 2:
824
+ ga_preprocess_nx(in, out, width_in, 2);
825
+ break;
826
+ case 4:
827
+ ga_preprocess_nx(in, out, width_in, 4);
828
+ break;
829
+ }
830
+ }
831
+
832
+ static void pre_convert_cmyk(uint8_t *in, uint16_t *out,
833
+ uint32_t width_in, uint32_t scale_factor)
834
+ {
835
+ switch(scale_factor) {
836
+ case 1:
837
+ cmyk_preprocess_nx(in, out, width_in, 1);
838
+ break;
839
+ case 2:
840
+ cmyk_preprocess_nx(in, out, width_in, 2);
841
+ break;
842
+ case 4:
843
+ cmyk_preprocess_nx(in, out, width_in, 4);
844
+ break;
845
+ }
846
+ }
847
+
848
+ static void pre_convert_rgbx(uint8_t *in, uint16_t *out,
849
+ uint32_t width_in, uint32_t scale_factor)
850
+ {
851
+ switch(scale_factor) {
852
+ case 1:
853
+ srgbx_preprocess_nx(in, out, width_in, 1);
854
+ break;
855
+ case 2:
856
+ srgbx_preprocess_nx(in, out, width_in, 2);
857
+ break;
858
+ case 4:
859
+ srgbx_preprocess_nx(in, out, width_in, 4);
860
+ break;
861
+ }
862
+ }
863
+
864
+ static void pre_convert_rgba(uint8_t *in, uint16_t *out,
865
+ uint32_t width_in, uint32_t scale_factor)
866
+ {
867
+ switch(scale_factor) {
868
+ case 1:
869
+ srgba_preprocess_nx(in, out, width_in, 1);
870
+ break;
871
+ case 2:
872
+ srgba_preprocess_nx(in, out, width_in, 2);
873
+ break;
874
+ case 4:
875
+ srgba_preprocess_nx(in, out, width_in, 4);
876
+ break;
877
+ }
878
+ }
879
+
880
+ static void pre_convert_rgb(uint8_t *in, uint16_t *out,
881
+ uint32_t width_in, uint32_t scale_factor)
882
+ {
883
+ switch(scale_factor) {
884
+ case 1:
885
+ srgb_preprocess_nx(in, out, width_in, 1);
886
+ break;
887
+ case 2:
888
+ srgb_preprocess_nx(in, out, width_in, 2);
889
+ break;
890
+ case 4:
891
+ srgb_preprocess_nx(in, out, width_in, 4);
892
+ break;
893
+ }
894
+ }
895
+
896
+ void preprocess_xscaler_scale(struct preprocess_xscaler *pxs, uint8_t *in,
897
+ uint16_t *out)
898
+ {
899
+ switch(pxs->cs_in) {
900
+ case OIL_CS_G:
901
+ pre_convert_g(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
902
+ break;
903
+ case OIL_CS_GA:
904
+ pre_convert_ga(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
905
+ break;
906
+ case OIL_CS_RGB:
907
+ pre_convert_rgb(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
908
+ break;
909
+ case OIL_CS_RGBX:
910
+ pre_convert_rgbx(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
911
+ break;
912
+ case OIL_CS_RGBA:
913
+ pre_convert_rgba(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
914
+ break;
915
+ case OIL_CS_CMYK:
916
+ pre_convert_cmyk(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
917
+ break;
918
+ }
919
+ xscaler_scale(&pxs->xs, out);
920
+ }
921
+
922
+
923
+ /* Utility helpers */
924
+ void fix_ratio(uint32_t src_width, uint32_t src_height, uint32_t *out_width,
925
+ uint32_t *out_height)
926
+ {
927
+ double width_ratio, height_ratio;
928
+
929
+ width_ratio = *out_width / (double)src_width;
930
+ height_ratio = *out_height / (double)src_height;
931
+ if (width_ratio < height_ratio) {
932
+ *out_height = round(width_ratio * src_height);
933
+ *out_height = *out_height ? *out_height : 1;
934
+ } else {
935
+ *out_width = round(height_ratio * src_width);
936
+ *out_width = *out_width ? *out_width : 1;
937
+ }
464
938
  }