oil 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }