oil 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }