oil 0.2.0 → 0.2.1

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.
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Copyright (c) 2014-2019 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
+
22
+ #ifndef OIL_LIBPNG_H
23
+ #define OIL_LIBPNG_H
24
+
25
+ #include <stdio.h>
26
+ #include <png.h>
27
+ #include "oil_resample.h"
28
+
29
+ struct oil_libpng {
30
+ struct oil_scale os;
31
+ png_structp rpng;
32
+ png_infop rinfo;
33
+ int in_vpos;
34
+ unsigned char *inbuf;
35
+ unsigned char **inimage;
36
+ };
37
+
38
+ /**
39
+ * Initialize an oil_libpng struct.
40
+ * @ol: Pointer to the struct to be initialized.
41
+ * @dinfo: Pointer to a libjpeg decompress struct, with header already read.
42
+ * @out_height: Desired height, in pixels, of the output image.
43
+ * @out_width: Desired width, in pixels, of the output image.
44
+ *
45
+ * Returns 0 on success.
46
+ * Returns -1 if an argument is bad.
47
+ * Returns -2 if unable to allocate memory.
48
+ */
49
+ int oil_libpng_init(struct oil_libpng *ol, png_structp rpng, png_infop rinfo,
50
+ int out_width, int out_height);
51
+
52
+ void oil_libpng_free(struct oil_libpng *ol);
53
+
54
+ void oil_libpng_read_scanline(struct oil_libpng *ol, unsigned char *outbuf);
55
+
56
+ enum oil_colorspace png_cs_to_oil(png_byte cs);
57
+
58
+ #endif
@@ -0,0 +1,1012 @@
1
+ /**
2
+ * Copyright (c) 2014-2019 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
+
22
+ #include "oil_resample.h"
23
+ #include <math.h>
24
+ #include <stdlib.h>
25
+ #include <limits.h>
26
+
27
+ /**
28
+ * When shrinking a 10 million pixel wide scanline down to a single pixel, we
29
+ * reach the limits of single-precision floats, and the xscaler will get stuck
30
+ * in an infinite loop. Limit input dimensions to one million by one million
31
+ * pixels to avoid this issue as well as overflow issues with 32-bit ints.
32
+ */
33
+ #define MAX_DIMENSION 1000000
34
+
35
+ /**
36
+ * Bicubic interpolation. 2 base taps on either side.
37
+ */
38
+ #define TAPS 4
39
+
40
+ /**
41
+ * Clamp a float between 0 and 1.
42
+ */
43
+ static float clampf(float x) {
44
+ if (x > 1.0f) {
45
+ return 1.0f;
46
+ } else if (x < 0.0f) {
47
+ return 0.0f;
48
+ }
49
+ return x;
50
+ }
51
+
52
+ /**
53
+ * Convert a float to 8-bit integer.
54
+ */
55
+ static int clamp8(float x)
56
+ {
57
+ return round(clampf(x) * 255.0f);
58
+ }
59
+
60
+ /**
61
+ * Map from the discreet dest coordinate pos to a continuous source coordinate.
62
+ * The resulting coordinate can range from -0.5 to the maximum of the
63
+ * destination image dimension.
64
+ */
65
+ static double map(int dim_in, int dim_out, int pos)
66
+ {
67
+ return (pos + 0.5) * ((double)dim_in / dim_out) - 0.5;
68
+ }
69
+
70
+ /**
71
+ * Returns the mapped input position and put the sub-pixel remainder in rest.
72
+ */
73
+ static int split_map(int dim_in, int dim_out, int pos, float *rest)
74
+ {
75
+ double smp;
76
+ int smp_i;
77
+
78
+ smp = map(dim_in, dim_out, pos);
79
+ smp_i = smp < 0 ? -1 : smp;
80
+ *rest = smp - smp_i;
81
+ return smp_i;
82
+ }
83
+
84
+ /**
85
+ * Given input and output dimension, calculate the total number of taps that
86
+ * will be needed to calculate an output sample.
87
+ *
88
+ * When we reduce an image by a factor of two, we need to scale our resampling
89
+ * function by two as well in order to avoid aliasing.
90
+ */
91
+ static int calc_taps(int dim_in, int dim_out)
92
+ {
93
+ int tmp;
94
+ if (dim_out > dim_in) {
95
+ return TAPS;
96
+ }
97
+ tmp = TAPS * dim_in / dim_out;
98
+ return tmp - (tmp & 1);
99
+ }
100
+
101
+ /**
102
+ * Catmull-Rom interpolator.
103
+ */
104
+ static float catrom(float x)
105
+ {
106
+ if (x>2) {
107
+ return 0;
108
+ }
109
+ if (x<1) {
110
+ return (1.5f*x - 2.5f)*x*x + 1;
111
+ }
112
+ return (((5 - x)*x - 8)*x + 4) / 2;
113
+ }
114
+
115
+ /**
116
+ * Given an offset tx, calculate taps coefficients.
117
+ */
118
+ static void calc_coeffs(float *coeffs, float tx, int taps)
119
+ {
120
+ int i;
121
+ float tmp, tap_mult, fudge;
122
+
123
+ tap_mult = (float)taps / TAPS;
124
+ tx = 1 - tx - taps / 2;
125
+ fudge = 0.0f;
126
+
127
+ for (i=0; i<taps; i++) {
128
+ tmp = catrom(fabsf(tx) / tap_mult) / tap_mult;
129
+ fudge += tmp;
130
+ coeffs[i] = tmp;
131
+ tx += 1;
132
+ }
133
+ fudge = 1 / fudge;
134
+ for (i=0; i<taps; i++) {
135
+ coeffs[i] *= fudge;
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Holds pre-calculated table of linear float to srgb char mappings.
141
+ * Initialized via build_l2s_rights();
142
+ */
143
+ static float l2s_rights[256];
144
+
145
+ /**
146
+ * Populates l2s_rights.
147
+ */
148
+ static void build_l2s_rights()
149
+ {
150
+ int i;
151
+ double srgb_f, tmp, val;
152
+
153
+ for (i=0; i<255; i++) {
154
+ srgb_f = (i + 0.5)/255.0;
155
+ if (srgb_f <= 0.0404482362771082) {
156
+ val = srgb_f / 12.92;
157
+ } else {
158
+ tmp = (srgb_f + 0.055)/1.055;
159
+ val = pow(tmp, 2.4);
160
+ }
161
+ l2s_rights[i] = val;
162
+ }
163
+ l2s_rights[i] = 256.0f;
164
+ }
165
+
166
+ /**
167
+ * Maps the given linear RGB float to sRGB integer.
168
+ *
169
+ * Performs a binary search on l2s_rights.
170
+ */
171
+ static int linear_sample_to_srgb(float in)
172
+ {
173
+ int offs, i;
174
+ offs = 0;
175
+ for (i=128; i>0; i >>= 1) {
176
+ if (in > l2s_rights[offs + i]) {
177
+ offs += i;
178
+ }
179
+ }
180
+ return in > l2s_rights[offs] ? offs + 1 : offs;
181
+ }
182
+
183
+ /**
184
+ * Resizes a strip of RGBX scanlines to a single scanline.
185
+ */
186
+ static void strip_scale_rgbx(float **in, int strip_height, int len,
187
+ unsigned char *out, float *coeffs)
188
+ {
189
+ int i, j;
190
+ double sum[3];
191
+
192
+ for (i=0; i<len; i+=4) {
193
+ sum[0] = sum[1] = sum[2] = 0;
194
+ for (j=0; j<strip_height; j++) {
195
+ sum[0] += coeffs[j] * in[j][i];
196
+ sum[1] += coeffs[j] * in[j][i + 1];
197
+ sum[2] += coeffs[j] * in[j][i + 2];
198
+ }
199
+ out[0] = linear_sample_to_srgb(sum[0]);
200
+ out[1] = linear_sample_to_srgb(sum[1]);
201
+ out[2] = linear_sample_to_srgb(sum[2]);
202
+ out[3] = 0;
203
+ out += 4;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * Resizes a strip of RGB scanlines to a single scanline.
209
+ */
210
+ static void strip_scale_rgb(float **in, int strip_height, int len,
211
+ unsigned char *out, float *coeffs)
212
+ {
213
+ int i, j;
214
+ double sum[3];
215
+
216
+ for (i=0; i<len; i+=3) {
217
+ sum[0] = sum[1] = sum[2] = 0;
218
+ for (j=0; j<strip_height; j++) {
219
+ sum[0] += coeffs[j] * in[j][i];
220
+ sum[1] += coeffs[j] * in[j][i + 1];
221
+ sum[2] += coeffs[j] * in[j][i + 2];
222
+ }
223
+ out[0] = linear_sample_to_srgb(sum[0]);
224
+ out[1] = linear_sample_to_srgb(sum[1]);
225
+ out[2] = linear_sample_to_srgb(sum[2]);
226
+ out += 3;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Resizes a strip of greyscale scanlines to a single scanline.
232
+ */
233
+ static void strip_scale_g(float **in, int strip_height, int len,
234
+ unsigned char *out, float *coeffs)
235
+ {
236
+ int i, j;
237
+ double sum;
238
+
239
+ for (i=0; i<len; i++) {
240
+ sum = 0;
241
+ for (j=0; j<strip_height; j++) {
242
+ sum += coeffs[j] * in[j][i];
243
+ }
244
+ out[i] = clamp8(sum);
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Resizes a strip of greyscale-alpha scanlines to a single scanline.
250
+ */
251
+ static void strip_scale_ga(float **in, int strip_height, int len,
252
+ unsigned char *out, float *coeffs)
253
+ {
254
+ int i, j;
255
+ double sum[2], alpha;
256
+
257
+ for (i=0; i<len; i+=2) {
258
+ sum[0] = sum[1] = 0;
259
+ for (j=0; j<strip_height; j++) {
260
+ sum[0] += coeffs[j] * in[j][i];
261
+ sum[1] += coeffs[j] * in[j][i + 1];
262
+ }
263
+ alpha = clampf(sum[1]);
264
+ if (alpha != 0) {
265
+ sum[0] /= alpha;
266
+ }
267
+ out[0] = clamp8(sum[0]);
268
+ out[1] = round(alpha * 255.0f);
269
+ out += 2;
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Resizes a strip of RGB-alpha scanlines to a single scanline.
275
+ */
276
+ static void strip_scale_rgba(float **in, int strip_height, int len,
277
+ unsigned char *out, float *coeffs)
278
+ {
279
+ int i, j;
280
+ double sum[4], alpha;
281
+
282
+ for (i=0; i<len; i+=4) {
283
+ sum[0] = sum[1] = sum[2] = sum[3] = 0;
284
+ for (j=0; j<strip_height; j++) {
285
+ sum[0] += coeffs[j] * in[j][i];
286
+ sum[1] += coeffs[j] * in[j][i + 1];
287
+ sum[2] += coeffs[j] * in[j][i + 2];
288
+ sum[3] += coeffs[j] * in[j][i + 3];
289
+ }
290
+ alpha = clampf(sum[3]);
291
+ if (alpha != 0) {
292
+ sum[0] /= alpha;
293
+ sum[1] /= alpha;
294
+ sum[2] /= alpha;
295
+ }
296
+ out[0] = linear_sample_to_srgb(sum[0]);
297
+ out[1] = linear_sample_to_srgb(sum[1]);
298
+ out[2] = linear_sample_to_srgb(sum[2]);
299
+ out[3] = round(alpha * 255.0f);
300
+ out += 4;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Resizes a strip of CMYK scanlines to a single scanline.
306
+ */
307
+ static void strip_scale_cmyk(float **in, int strip_height, int len,
308
+ unsigned char *out, float *coeffs)
309
+ {
310
+ int i, j;
311
+ double sum[4];
312
+
313
+ for (i=0; i<len; i+=4) {
314
+ sum[0] = sum[1] = sum[2] = sum[3] = 0;
315
+ for (j=0; j<strip_height; j++) {
316
+ sum[0] += coeffs[j] * in[j][i];
317
+ sum[1] += coeffs[j] * in[j][i + 1];
318
+ sum[2] += coeffs[j] * in[j][i + 2];
319
+ sum[3] += coeffs[j] * in[j][i + 3];
320
+ }
321
+ out[0] = clamp8(sum[0]);
322
+ out[1] = clamp8(sum[1]);
323
+ out[2] = clamp8(sum[2]);
324
+ out[3] = clamp8(sum[3]);
325
+ out += 4;
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Scale a strip of scanlines. Branches to the correct interpolator using the
331
+ * given colorspace.
332
+ */
333
+ static void strip_scale(float **in, int strip_height, int len,
334
+ unsigned char *out, float *coeffs, float ty, enum oil_colorspace cs)
335
+ {
336
+ calc_coeffs(coeffs, ty, strip_height);
337
+
338
+ switch(cs) {
339
+ case OIL_CS_G:
340
+ strip_scale_g(in, strip_height, len, out, coeffs);
341
+ break;
342
+ case OIL_CS_GA:
343
+ strip_scale_ga(in, strip_height, len, out, coeffs);
344
+ break;
345
+ case OIL_CS_RGB:
346
+ strip_scale_rgb(in, strip_height, len, out, coeffs);
347
+ break;
348
+ case OIL_CS_RGBX:
349
+ strip_scale_rgbx(in, strip_height, len, out, coeffs);
350
+ break;
351
+ case OIL_CS_RGBA:
352
+ strip_scale_rgba(in, strip_height, len, out, coeffs);
353
+ break;
354
+ case OIL_CS_CMYK:
355
+ strip_scale_cmyk(in, strip_height, len, out, coeffs);
356
+ break;
357
+ case OIL_CS_UNKNOWN:
358
+ break;
359
+ }
360
+ }
361
+
362
+ /* horizontal scaling */
363
+
364
+ /**
365
+ * Holds pre-calculated mapping of sRGB chars to linear RGB floating point
366
+ * values.
367
+ */
368
+ static float s2l_map_f[256];
369
+
370
+ /**
371
+ * Populates s2l_map_f.
372
+ */
373
+ static void build_s2l()
374
+ {
375
+ int input;
376
+ double in_f, tmp, val;
377
+
378
+ for (input=0; input<=255; input++) {
379
+ in_f = input / 255.0;
380
+ if (in_f <= 0.040448236277) {
381
+ val = in_f / 12.92;
382
+ } else {
383
+ tmp = ((in_f + 0.055)/1.055);
384
+ val = pow(tmp, 2.4);
385
+ }
386
+ s2l_map_f[input] = val;
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Given input & output dimensions, populate a buffer of coefficients and
392
+ * border counters.
393
+ *
394
+ * This method assumes that in_width >= out_width.
395
+ *
396
+ * It generates 4 * in_width coefficients -- 4 for every input sample.
397
+ *
398
+ * It generates out_width border counters, these indicate how many input
399
+ * samples to process before the next output sample is finished.
400
+ */
401
+ static void xscale_calc_coeffs(int in_width, int out_width, float *coeff_buf,
402
+ int *border_buf)
403
+ {
404
+ struct {
405
+ float tx;
406
+ float fudge;
407
+ float *center;
408
+ } out_s[4];
409
+ int i, j, out_pos, border, taps;
410
+ float tap_mult_f, tx;
411
+
412
+ out_pos = 0;
413
+ border = 0;
414
+ taps = calc_taps(in_width, out_width);
415
+ tap_mult_f = (float)TAPS / taps;
416
+
417
+ for (i=0; i<4; i++) {
418
+ out_s[i].tx = -1 * map(in_width, out_width, i) * tap_mult_f;
419
+ out_s[i].fudge = 1.0f;
420
+ }
421
+
422
+ for (i=0; i<in_width; i++) {
423
+ for (j=0; j<4; j++) {
424
+ tx = fabsf(out_s[j].tx);
425
+ coeff_buf[j] = catrom(tx) * tap_mult_f;
426
+ out_s[j].fudge -= coeff_buf[j];
427
+ if (tx < tap_mult_f) {
428
+ out_s[j].center = coeff_buf + j;
429
+ }
430
+ out_s[j].tx += tap_mult_f;
431
+ }
432
+ border++;
433
+ coeff_buf += 4;
434
+ if (out_s[0].tx >= 2.0f) {
435
+ out_s[0].center[0] += out_s[0].fudge;
436
+
437
+ out_s[0] = out_s[1];
438
+ out_s[1] = out_s[2];
439
+ out_s[2] = out_s[3];
440
+
441
+ out_s[3].tx = (i + 1 - map(in_width, out_width, out_pos + 4)) * tap_mult_f;
442
+ out_s[3].fudge = 1.0f;
443
+
444
+ border_buf[out_pos] = border;
445
+ border = 0;
446
+ out_pos++;
447
+ }
448
+ }
449
+
450
+ for (i=0; i + out_pos < out_width; i++) {
451
+ out_s[i].center[0] += out_s[i].fudge;
452
+ border_buf[i + out_pos] = border;
453
+ border = 0;
454
+ }
455
+ }
456
+
457
+ /**
458
+ * Takes an array of 4 floats and shifts them left. The rightmost element is
459
+ * set to 0.0.
460
+ */
461
+ static void shift_left_f(float *f)
462
+ {
463
+ f[0] = f[1];
464
+ f[1] = f[2];
465
+ f[2] = f[3];
466
+ f[3] = 0.0f;
467
+ }
468
+
469
+ /**
470
+ * Takes a sample value, an array of 4 coefficients & 4 accumulators, and
471
+ * adds the product of sample * coeffs[n] to each accumulator.
472
+ */
473
+ static void add_sample_to_sum_f(float sample, float *coeffs, float *sum)
474
+ {
475
+ int i;
476
+ for (i=0; i<4; i++) {
477
+ sum[i] += sample * coeffs[i];
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Takes an array of n 4-element source arrays, writes the first element to the
483
+ * next n positions of the output address, and shifts the source arrays.
484
+ */
485
+ static void dump_out(float *out, float sum[][4], int n)
486
+ {
487
+ int i;
488
+ for (i=0; i<n; i++) {
489
+ out[i] = sum[i][0];
490
+ shift_left_f(sum[i]);
491
+ }
492
+ }
493
+
494
+ static void xscale_down_rgbx(unsigned char *in, int in_width, float *out,
495
+ int out_width, float *coeff_buf, int *border_buf)
496
+ {
497
+ int i, j, k;
498
+ float sum[3][4] = {{ 0.0f }};
499
+
500
+ for (i=0; i<out_width; i++) {
501
+ for (j=border_buf[0]; j>0; j--) {
502
+ for (k=0; k<3; k++) {
503
+ add_sample_to_sum_f(s2l_map_f[in[k]], coeff_buf, sum[k]);
504
+ }
505
+ in += 4;
506
+ coeff_buf += 4;
507
+ }
508
+ dump_out(out, sum, 3);
509
+ out[3] = 0;
510
+ out += 4;
511
+ border_buf++;
512
+ }
513
+ }
514
+
515
+ static void xscale_down_rgb(unsigned char *in, int in_width, float *out,
516
+ int out_width, float *coeff_buf, int *border_buf)
517
+ {
518
+ int i, j, k;
519
+ float sum[3][4] = {{ 0.0f }};
520
+
521
+ for (i=0; i<out_width; i++) {
522
+ for (j=border_buf[0]; j>0; j--) {
523
+ for (k=0; k<3; k++) {
524
+ add_sample_to_sum_f(s2l_map_f[in[k]], coeff_buf, sum[k]);
525
+ }
526
+ in += 3;
527
+ coeff_buf += 4;
528
+ }
529
+ dump_out(out, sum, 3);
530
+ out += 3;
531
+ border_buf++;
532
+ }
533
+ }
534
+
535
+ static void xscale_down_g(unsigned char *in, int in_width, float *out,
536
+ int out_width, float *coeff_buf, int *border_buf)
537
+ {
538
+ int i, j;
539
+ float sum[4] = { 0.0f };
540
+
541
+ for (i=0; i<out_width; i++) {
542
+ for (j=border_buf[0]; j>0; j--) {
543
+ add_sample_to_sum_f(in[0] / 255.0f, coeff_buf, sum);
544
+ in += 1;
545
+ coeff_buf += 4;
546
+ }
547
+ out[0] = sum[0];
548
+ shift_left_f(sum);
549
+ out += 1;
550
+ border_buf++;
551
+ }
552
+ }
553
+
554
+ static void xscale_down_cmyk(unsigned char *in, int in_width, float *out,
555
+ int out_width, float *coeff_buf, int *border_buf)
556
+ {
557
+ int i, j, k;
558
+ float sum[4][4] = {{ 0.0f }};
559
+
560
+ for (i=0; i<out_width; i++) {
561
+ for (j=border_buf[0]; j>0; j--) {
562
+ for (k=0; k<4; k++) {
563
+ add_sample_to_sum_f(in[k] / 255.0f, coeff_buf, sum[k]);
564
+ }
565
+ in += 4;
566
+ coeff_buf += 4;
567
+ }
568
+ dump_out(out, sum, 4);
569
+ out += 4;
570
+ border_buf++;
571
+ }
572
+ }
573
+
574
+ static void xscale_down_rgba(unsigned char *in, int in_width, float *out,
575
+ int out_width, float *coeff_buf, int *border_buf)
576
+ {
577
+ int i, j, k;
578
+ float alpha, sum[4][4] = {{ 0.0f }};
579
+
580
+ for (i=0; i<out_width; i++) {
581
+ for (j=border_buf[0]; j>0; j--) {
582
+ alpha = in[3] / 255.0f;
583
+ for (k=0; k<3; k++) {
584
+ add_sample_to_sum_f(s2l_map_f[in[k]] * alpha, coeff_buf, sum[k]);
585
+ }
586
+ add_sample_to_sum_f(alpha, coeff_buf, sum[3]);
587
+ in += 4;
588
+ coeff_buf += 4;
589
+ }
590
+ dump_out(out, sum, 4);
591
+ out += 4;
592
+ border_buf++;
593
+ }
594
+ }
595
+
596
+ static void xscale_down_ga(unsigned char *in, int in_width, float *out,
597
+ int out_width, float *coeff_buf, int *border_buf)
598
+ {
599
+ int i, j;
600
+ float alpha, sum[2][4] = {{ 0.0f }};
601
+
602
+ for (i=0; i<out_width; i++) {
603
+ for (j=border_buf[0]; j>0; j--) {
604
+ alpha = in[1] / 255.0f;
605
+ add_sample_to_sum_f(in[0] * alpha, coeff_buf, sum[0]);
606
+ add_sample_to_sum_f(alpha, coeff_buf, sum[1]);
607
+ in += 2;
608
+ coeff_buf += 4;
609
+ }
610
+ dump_out(out, sum, 2);
611
+ out += 2;
612
+ border_buf++;
613
+ }
614
+ }
615
+
616
+ static void oil_xscale_down(unsigned char *in, int width_in, float *out,
617
+ int width_out, enum oil_colorspace cs_in, float *coeff_buf,
618
+ int *border_buf)
619
+ {
620
+ switch(cs_in) {
621
+ case OIL_CS_RGBX:
622
+ xscale_down_rgbx(in, width_in, out, width_out, coeff_buf, border_buf);
623
+ break;
624
+ case OIL_CS_RGB:
625
+ xscale_down_rgb(in, width_in, out, width_out, coeff_buf, border_buf);
626
+ break;
627
+ case OIL_CS_G:
628
+ xscale_down_g(in, width_in, out, width_out, coeff_buf, border_buf);
629
+ break;
630
+ case OIL_CS_CMYK:
631
+ xscale_down_cmyk(in, width_in, out, width_out, coeff_buf, border_buf);
632
+ break;
633
+ case OIL_CS_RGBA:
634
+ xscale_down_rgba(in, width_in, out, width_out, coeff_buf, border_buf);
635
+ break;
636
+ case OIL_CS_GA:
637
+ xscale_down_ga(in, width_in, out, width_out, coeff_buf, border_buf);
638
+ break;
639
+ case OIL_CS_UNKNOWN:
640
+ break;
641
+ }
642
+ }
643
+
644
+ static int dim_safe(int i, int max)
645
+ {
646
+ if (i < 0) {
647
+ return 0;
648
+ }
649
+ if (i > max) {
650
+ return max;
651
+ }
652
+ return i;
653
+ }
654
+
655
+ static void xscale_up_rgbx(unsigned char *in, int width_in, float *out,
656
+ int width_out)
657
+ {
658
+ int i, j, k, smp_i;
659
+ float coeffs[4], tx, sum[3];
660
+ unsigned char *in_pos;
661
+
662
+ for (i=0; i<width_out; i++) {
663
+ smp_i = split_map(width_in, width_out, i, &tx) - 1;
664
+ calc_coeffs(coeffs, tx, 4);
665
+ sum[0] = sum[1] = sum[2] = 0.0f;
666
+ for (j=0; j<4; j++) {
667
+ in_pos = in + dim_safe(smp_i + j, width_in - 1) * 4;
668
+ for (k=0; k<3; k++) {
669
+ sum[k] += s2l_map_f[in_pos[k]] * coeffs[j];
670
+ }
671
+ }
672
+ for (k=0; k<3; k++) {
673
+ out[k] = sum[k];
674
+ }
675
+ out[3] = 0.0f;
676
+ out += 4;
677
+ }
678
+ }
679
+
680
+ static void xscale_up_rgb(unsigned char *in, int width_in, float *out,
681
+ int width_out)
682
+ {
683
+ int i, j, k, smp_i;
684
+ float coeffs[4], tx, sum[3];
685
+ unsigned char *in_pos;
686
+
687
+ for (i=0; i<width_out; i++) {
688
+ smp_i = split_map(width_in, width_out, i, &tx) - 1;
689
+ calc_coeffs(coeffs, tx, 4);
690
+ sum[0] = sum[1] = sum[2] = 0.0f;
691
+ for (j=0; j<4; j++) {
692
+ in_pos = in + dim_safe(smp_i + j, width_in - 1) * 3;
693
+ for (k=0; k<3; k++) {
694
+ sum[k] += s2l_map_f[in_pos[k]] * coeffs[j];
695
+ }
696
+ }
697
+ for (k=0; k<3; k++) {
698
+ out[k] = sum[k];
699
+ }
700
+ out += 3;
701
+ }
702
+ }
703
+
704
+ static void xscale_up_cmyk(unsigned char *in, int width_in, float *out,
705
+ int width_out)
706
+ {
707
+ int i, j, k, smp_i;
708
+ float coeffs[4], tx, sum[4];
709
+ unsigned char *in_pos;
710
+
711
+ for (i=0; i<width_out; i++) {
712
+ smp_i = split_map(width_in, width_out, i, &tx) - 1;
713
+ calc_coeffs(coeffs, tx, 4);
714
+ sum[0] = sum[1] = sum[2] = sum[3] = 0.0f;
715
+ for (j=0; j<4; j++) {
716
+ in_pos = in + dim_safe(smp_i + j, width_in - 1) * 4;
717
+ for (k=0; k<4; k++) {
718
+ sum[k] += in_pos[k]/255.0f * coeffs[j];
719
+ }
720
+ }
721
+ for (k=0; k<4; k++) {
722
+ out[k] = sum[k];
723
+ }
724
+ out += 4;
725
+ }
726
+ }
727
+
728
+ static void xscale_up_rgba(unsigned char *in, int width_in, float *out,
729
+ int width_out)
730
+ {
731
+ int i, j, k, smp_i;
732
+ float alpha, coeffs[4], tx, sum[4];
733
+ unsigned char *in_pos;
734
+
735
+ for (i=0; i<width_out; i++) {
736
+ smp_i = split_map(width_in, width_out, i, &tx) - 1;
737
+ calc_coeffs(coeffs, tx, 4);
738
+ sum[0] = sum[1] = sum[2] = sum[3] = 0.0f;
739
+ for (j=0; j<4; j++) {
740
+ in_pos = in + dim_safe(smp_i + j, width_in - 1) * 4;
741
+ alpha = in_pos[3] / 255.0f;
742
+ for (k=0; k<3; k++) {
743
+ sum[k] += alpha * s2l_map_f[in_pos[k]] * coeffs[j];
744
+ }
745
+ sum[3] += alpha * coeffs[j];
746
+ }
747
+ for (k=0; k<4; k++) {
748
+ out[k] = sum[k];
749
+ }
750
+ out += 4;
751
+ }
752
+ }
753
+
754
+ static void xscale_up_ga(unsigned char *in, int width_in, float *out,
755
+ int width_out)
756
+ {
757
+ int i, j, k, smp_i;
758
+ float alpha, coeffs[4], tx, sum[2];
759
+ unsigned char *in_pos;
760
+
761
+ for (i=0; i<width_out; i++) {
762
+ smp_i = split_map(width_in, width_out, i, &tx) - 1;
763
+ calc_coeffs(coeffs, tx, 4);
764
+ sum[0] = sum[1] = 0.0f;
765
+ for (j=0; j<4; j++) {
766
+ in_pos = in + dim_safe(smp_i + j, width_in - 1) * 2;
767
+ alpha = in_pos[1] / 255.0f;
768
+ sum[0] += alpha * in_pos[0]/255.0f * coeffs[j];
769
+ sum[1] += alpha * coeffs[j];
770
+ }
771
+ for (k=0; k<2; k++) {
772
+ out[k] = sum[k];
773
+ }
774
+ out += 2;
775
+ }
776
+ }
777
+
778
+ static void xscale_up_g(unsigned char *in, int width_in, float *out,
779
+ int width_out)
780
+ {
781
+ int i, j, smp_i;
782
+ float coeffs[4], tx, sum;
783
+ unsigned char *in_pos;
784
+
785
+ for (i=0; i<width_out; i++) {
786
+ smp_i = split_map(width_in, width_out, i, &tx) - 1;
787
+ calc_coeffs(coeffs, tx, 4);
788
+ sum = 0.0f;
789
+ for (j=0; j<4; j++) {
790
+ in_pos = in + dim_safe(smp_i + j, width_in - 1);
791
+ sum += in_pos[0]/255.0f * coeffs[j];
792
+ }
793
+ out[0] = sum;
794
+ out += 1;
795
+ }
796
+ }
797
+
798
+ static void oil_xscale_up(unsigned char *in, int width_in, float *out,
799
+ int width_out, enum oil_colorspace cs_in)
800
+ {
801
+ switch(cs_in) {
802
+ case OIL_CS_RGBX:
803
+ xscale_up_rgbx(in, width_in, out, width_out);
804
+ break;
805
+ case OIL_CS_RGB:
806
+ xscale_up_rgb(in, width_in, out, width_out);
807
+ break;
808
+ case OIL_CS_G:
809
+ xscale_up_g(in, width_in, out, width_out);
810
+ break;
811
+ case OIL_CS_CMYK:
812
+ xscale_up_cmyk(in, width_in, out, width_out);
813
+ break;
814
+ case OIL_CS_RGBA:
815
+ xscale_up_rgba(in, width_in, out, width_out);
816
+ break;
817
+ case OIL_CS_GA:
818
+ xscale_up_ga(in, width_in, out, width_out);
819
+ break;
820
+ case OIL_CS_UNKNOWN:
821
+ break;
822
+ }
823
+ }
824
+
825
+ /* Global function helpers */
826
+
827
+ /**
828
+ * Given an oil_scale struct, map the next output scanline to a position &
829
+ * offset in the input image.
830
+ */
831
+ static int yscaler_map_pos(struct oil_scale *ys, float *ty)
832
+ {
833
+ int target;
834
+ target = split_map(ys->in_height, ys->out_height, ys->out_pos, ty);
835
+ return target + ys->taps / 2;
836
+ }
837
+
838
+ /**
839
+ * Return the index of the buffered scanline to use for the tap at position
840
+ * pos.
841
+ */
842
+ static int oil_yscaler_safe_idx(struct oil_scale *ys, int pos)
843
+ {
844
+ int ret, max_height;
845
+
846
+ max_height = ys->in_height - 1;
847
+ ret = ys->target - ys->taps + 1 + pos;
848
+ if (ret < 0) {
849
+ return 0;
850
+ } else if (ret > max_height) {
851
+ return max_height;
852
+ }
853
+ return ret;
854
+ }
855
+
856
+ /* Global functions */
857
+ void oil_global_init()
858
+ {
859
+ build_s2l();
860
+ build_l2s_rights();
861
+ }
862
+
863
+ int oil_scale_init(struct oil_scale *os, int in_height, int out_height,
864
+ int in_width, int out_width, enum oil_colorspace cs)
865
+ {
866
+ if (!os || in_height > MAX_DIMENSION || out_height > MAX_DIMENSION ||
867
+ in_height < 1 || out_height < 1 ||
868
+ in_width > MAX_DIMENSION || out_width > MAX_DIMENSION ||
869
+ in_width < 1 || out_width < 1) {
870
+ return -1;
871
+ }
872
+
873
+ /* Lazy perform global init */
874
+ if (!s2l_map_f[128]) {
875
+ oil_global_init();
876
+ }
877
+
878
+ os->in_height = in_height;
879
+ os->out_height = out_height;
880
+ os->in_width = in_width;
881
+ os->out_width = out_width;
882
+ os->cs = cs;
883
+ os->in_pos = 0;
884
+ os->out_pos = 0;
885
+ os->taps = calc_taps(in_height, out_height);
886
+ os->target = yscaler_map_pos(os, &os->ty);
887
+ os->sl_len = out_width * OIL_CMP(cs);
888
+ os->coeffs_y = NULL;
889
+ os->coeffs_x = NULL;
890
+ os->borders = NULL;
891
+ os->rb = NULL;
892
+ os->virt = NULL;
893
+
894
+ /**
895
+ * If we are horizontally shrinking, then allocate & pre-calculate
896
+ * coefficients.
897
+ */
898
+ if (out_width <= in_width) {
899
+ os->coeffs_x = malloc(128 * in_width);
900
+ os->borders = malloc(sizeof(int) * out_width);
901
+ if (!os->coeffs_x || !os->borders) {
902
+ oil_scale_free(os);
903
+ return -2;
904
+ }
905
+ xscale_calc_coeffs(in_width, out_width, os->coeffs_x,
906
+ os->borders);
907
+ }
908
+
909
+ os->rb = malloc((long)os->sl_len * os->taps * sizeof(float));
910
+ os->virt = malloc(os->taps * sizeof(float*));
911
+ os->coeffs_y = malloc(os->taps * sizeof(float));
912
+ if (!os->rb || !os->virt || !os->coeffs_y) {
913
+ oil_scale_free(os);
914
+ return -2;
915
+ }
916
+
917
+ return 0;
918
+ }
919
+
920
+ void oil_scale_free(struct oil_scale *os)
921
+ {
922
+ if (!os) {
923
+ return;
924
+ }
925
+ if (os->virt) {
926
+ free(os->virt);
927
+ os->virt = NULL;
928
+ }
929
+ if (os->rb) {
930
+ free(os->rb);
931
+ os->rb = NULL;
932
+ }
933
+ if (os->coeffs_y) {
934
+ free(os->coeffs_y);
935
+ os->coeffs_y = NULL;
936
+ }
937
+
938
+ if (os->coeffs_x) {
939
+ free(os->coeffs_x);
940
+ os->coeffs_x = NULL;
941
+ }
942
+ if (os->borders) {
943
+ free(os->borders);
944
+ os->borders = NULL;
945
+ }
946
+ }
947
+
948
+ int oil_scale_slots(struct oil_scale *ys)
949
+ {
950
+ int tmp, safe_target;
951
+ tmp = ys->target + 1;
952
+ safe_target = tmp > ys->in_height ? ys->in_height : tmp;
953
+ return safe_target - ys->in_pos;
954
+ }
955
+
956
+ void oil_scale_in(struct oil_scale *os, unsigned char *in)
957
+ {
958
+ float *tmp;
959
+
960
+ tmp = os->rb + (os->in_pos % os->taps) * os->sl_len;
961
+ os->in_pos++;
962
+ if (os->coeffs_x) {
963
+ oil_xscale_down(in, os->in_width, tmp, os->out_width, os->cs,
964
+ os->coeffs_x, os->borders);
965
+ } else {
966
+ oil_xscale_up(in, os->in_width, tmp, os->out_width, os->cs);
967
+ }
968
+ }
969
+
970
+ void oil_scale_out(struct oil_scale *ys, unsigned char *out)
971
+ {
972
+ int i, idx;
973
+
974
+ if (!ys || !out) {
975
+ return;
976
+ }
977
+
978
+ for (i=0; i<ys->taps; i++) {
979
+ idx = oil_yscaler_safe_idx(ys, i);
980
+ ys->virt[i] = ys->rb + (idx % ys->taps) * ys->sl_len;
981
+ }
982
+ strip_scale(ys->virt, ys->taps, ys->sl_len, out, ys->coeffs_y, ys->ty,
983
+ ys->cs);
984
+ ys->out_pos++;
985
+ ys->target = yscaler_map_pos(ys, &ys->ty);
986
+ }
987
+
988
+ int oil_fix_ratio(int src_width, int src_height, int *out_width,
989
+ int *out_height)
990
+ {
991
+ double width_ratio, height_ratio, tmp;
992
+ int *adjust_dim;
993
+
994
+ if (src_width < 1 || src_height < 1 || *out_width < 1 || *out_height < 1) {
995
+ return -1; // bad argument
996
+ }
997
+
998
+ width_ratio = *out_width / (double)src_width;
999
+ height_ratio = *out_height / (double)src_height;
1000
+ if (width_ratio < height_ratio) {
1001
+ tmp = round(width_ratio * src_height);
1002
+ adjust_dim = out_height;
1003
+ } else {
1004
+ tmp = round(height_ratio * src_width);
1005
+ adjust_dim = out_width;
1006
+ }
1007
+ if (tmp > INT_MAX) {
1008
+ return -2; // adjusted dimension out of range
1009
+ }
1010
+ *adjust_dim = tmp ? tmp : 1;
1011
+ return 0;
1012
+ }