oil 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,10 @@
1
+ #include <ruby.h>
2
+
3
+ void Init_jpeg();
4
+ void Init_png();
5
+
6
+ void Init_oil()
7
+ {
8
+ Init_jpeg();
9
+ Init_png();
10
+ }
@@ -0,0 +1,337 @@
1
+ #include <ruby.h>
2
+ #include <png.h>
3
+ #include "resample.h"
4
+ #include "yscaler.h"
5
+
6
+ static ID id_read;
7
+
8
+ struct readerdata {
9
+ png_structp png;
10
+ png_infop info;
11
+ VALUE source_io;
12
+ uint32_t scale_width;
13
+ uint32_t scale_height;
14
+ int locked;
15
+ };
16
+
17
+ static void warning(png_structp png_ptr, png_const_charp message)
18
+ {
19
+ rb_warning("libpng: %s", message);
20
+ }
21
+
22
+ static void error(png_structp png_ptr, png_const_charp message)
23
+ {
24
+ rb_raise(rb_eRuntimeError, "libpng: %s", message);
25
+ }
26
+
27
+ static void read_data(png_structp png_ptr, png_bytep data, png_size_t length)
28
+ {
29
+ size_t out_len;
30
+ VALUE io, string;
31
+
32
+ io = (VALUE)png_get_io_ptr(png_ptr);
33
+ string = rb_funcall(io, id_read, 1, INT2FIX(length));
34
+ Check_Type(string, T_STRING);
35
+
36
+ out_len = RSTRING_LEN(string);
37
+ if (out_len != length) {
38
+ png_error(png_ptr, "IO returned wrong amount of data.");
39
+ return;
40
+ }
41
+
42
+ memcpy(data, RSTRING_PTR(string), length);
43
+ }
44
+
45
+ static void flush_data_fn(png_structp png_ptr) {}
46
+
47
+ static void write_data_fn(png_structp png_ptr, png_bytep data, png_size_t length)
48
+ {
49
+ rb_yield(rb_str_new((char *)data, length));
50
+ }
51
+
52
+ /* Ruby GC */
53
+
54
+ static void deallocate(struct readerdata *reader)
55
+ {
56
+ png_destroy_read_struct(&reader->png, &reader->info, NULL);
57
+ }
58
+
59
+ static void mark(struct readerdata *reader)
60
+ {
61
+ if (!NIL_P(reader->source_io)) {
62
+ rb_gc_mark(reader->source_io);
63
+ }
64
+ }
65
+
66
+ static VALUE allocate(VALUE klass)
67
+ {
68
+ struct readerdata *reader;
69
+ VALUE self;
70
+
71
+ self = Data_Make_Struct(klass, struct readerdata, mark, deallocate, reader);
72
+ reader->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)error, (png_error_ptr)warning);
73
+ return self;
74
+ }
75
+
76
+ /* Helper that raises an exception if the reader is locked. */
77
+
78
+ static void raise_if_locked(struct readerdata *reader)
79
+ {
80
+ if (reader->locked) {
81
+ rb_raise(rb_eRuntimeError, "Can't modify a Reader after decompress started.");
82
+ }
83
+ }
84
+
85
+ static VALUE initialize(VALUE self, VALUE io)
86
+ {
87
+ struct readerdata *reader;
88
+
89
+ Data_Get_Struct(self, struct readerdata, reader);
90
+
91
+ if (reader->source_io) {
92
+ png_destroy_read_struct(&reader->png, &reader->info, NULL);
93
+ reader->png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, (png_error_ptr)error, (png_error_ptr)warning);
94
+ reader->locked = 0;
95
+ }
96
+
97
+ reader->source_io = io;
98
+ reader->info = png_create_info_struct(reader->png);
99
+ png_set_read_fn(reader->png, (void*)io, read_data);
100
+ png_read_info(reader->png, reader->info);
101
+ return self;
102
+ }
103
+
104
+ /*
105
+ * call-seq:
106
+ * reader.width -> number
107
+ *
108
+ * Retrieve the width of the image.
109
+ */
110
+
111
+ static VALUE width(VALUE self)
112
+ {
113
+ struct readerdata *reader;
114
+ Data_Get_Struct(self, struct readerdata, reader);
115
+ return INT2FIX(png_get_image_width(reader->png, reader->info));
116
+ }
117
+
118
+ /*
119
+ * call-seq:
120
+ * reader.height -> number
121
+ *
122
+ * Retrieve the height of the image.
123
+ */
124
+
125
+ static VALUE height(VALUE self)
126
+ {
127
+ struct readerdata *reader;
128
+ Data_Get_Struct(self, struct readerdata, reader);
129
+ return INT2FIX(png_get_image_height(reader->png, reader->info));
130
+ }
131
+
132
+ /*
133
+ * call-seq:
134
+ * reader.scale_width -> number
135
+ *
136
+ * Retrieve the width to which the image will be resized after decompression. A
137
+ * width of 0 means the image will remain at original width.
138
+ */
139
+
140
+ static VALUE scale_width(VALUE self)
141
+ {
142
+ struct readerdata *reader;
143
+ Data_Get_Struct(self, struct readerdata, reader);
144
+ return INT2FIX(reader->scale_width);
145
+ }
146
+
147
+ /*
148
+ * call-seq:
149
+ * reader.scale_width = number
150
+ *
151
+ * Set the width to which the image will be resized after decompression. A
152
+ * width of 0 means the image will remain at original width.
153
+ */
154
+
155
+ static VALUE set_scale_width(VALUE self, VALUE scale_width)
156
+ {
157
+ struct readerdata *reader;
158
+ Data_Get_Struct(self, struct readerdata, reader);
159
+ raise_if_locked(reader);
160
+ reader->scale_width = NUM2INT(scale_width);
161
+ return scale_width;
162
+ }
163
+
164
+ /*
165
+ * call-seq:
166
+ * reader.scale_height -> number
167
+ *
168
+ * Retrieve the height to which the image will be resized after decompression. A
169
+ * height of 0 means the image will remain at original height.
170
+ */
171
+
172
+ static VALUE scale_height(VALUE self)
173
+ {
174
+ struct readerdata *reader;
175
+ Data_Get_Struct(self, struct readerdata, reader);
176
+ return INT2FIX(reader->scale_height);
177
+ }
178
+
179
+ /*
180
+ * call-seq:
181
+ * reader.scale_height = number
182
+ *
183
+ * Set the height to which the image will be resized after decompression. A
184
+ * height of 0 means the image will remain at original height.
185
+ */
186
+
187
+ static VALUE set_scale_height(VALUE self, VALUE scale_height)
188
+ {
189
+ struct readerdata *reader;
190
+ Data_Get_Struct(self, struct readerdata, reader);
191
+ raise_if_locked(reader);
192
+ reader->scale_height = NUM2INT(scale_height);
193
+ return scale_height;
194
+ }
195
+
196
+ struct write_png_args {
197
+ VALUE opts;
198
+ struct readerdata *reader;
199
+ unsigned char *inwidthbuf;
200
+ unsigned char *outwidthbuf;
201
+ struct yscaler *ys;
202
+ png_structp png;
203
+ png_infop info;
204
+ };
205
+
206
+ static VALUE each2(struct write_png_args *args)
207
+ {
208
+ png_structp png;
209
+ png_infop info;
210
+ png_byte ctype;
211
+ struct readerdata *reader;
212
+ unsigned char *inwidthbuf, *outwidthbuf, *yinbuf;
213
+ struct yscaler *ys;
214
+ uint32_t i, scalex, scaley;
215
+ int cmp;
216
+
217
+ reader = args->reader;
218
+ png = args->png;
219
+ info = args->info;
220
+ inwidthbuf = args->inwidthbuf;
221
+ outwidthbuf = args->outwidthbuf;
222
+ ys = args->ys;
223
+ scalex = args->reader->scale_width;
224
+ scaley = args->reader->scale_height;
225
+
226
+ cmp = png_get_channels(reader->png, reader->info);
227
+ png_set_write_fn(png, 0, write_data_fn, flush_data_fn);
228
+ ctype = png_get_color_type(reader->png, reader->info);
229
+
230
+ png_set_IHDR(png, info, scalex, scaley, 8, ctype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
231
+ png_write_info(png, info);
232
+
233
+ for(i=0; i<scaley; i++) {
234
+ while ((yinbuf = yscaler_next(ys))) {
235
+ png_read_row(reader->png, inwidthbuf, NULL);
236
+ xscale(inwidthbuf, png_get_image_width(reader->png, reader->info), yinbuf, scalex, cmp, 0);
237
+ }
238
+ yscaler_scale(ys, outwidthbuf, scalex, cmp, 0);
239
+ png_write_row(png, outwidthbuf);
240
+ }
241
+
242
+ png_write_end(png, info);
243
+
244
+ return Qnil;
245
+ }
246
+
247
+ /*
248
+ * call-seq:
249
+ * reader.each(opts, &block) -> self
250
+ *
251
+ * Yields a series of binary strings that make up the output JPEG image.
252
+ *
253
+ * Options is a hash which may have the following symbols:
254
+ *
255
+ * :quality - JPEG quality setting. Betweein 0 and 100.
256
+ * :markers - Custom markers to include in the output JPEG. Must be a hash where
257
+ * the keys are :APP[0-15] or :COM and the values are arrays of strings that
258
+ * will be inserted into the markers.
259
+ */
260
+
261
+ static VALUE each(int argc, VALUE *argv, VALUE self)
262
+ {
263
+ struct readerdata *reader;
264
+ int cmp, state;
265
+ struct write_png_args args;
266
+ unsigned char *inwidthbuf, *outwidthbuf;
267
+ struct yscaler ys;
268
+ VALUE opts;
269
+ png_structp png;
270
+ png_infop info;
271
+
272
+ rb_scan_args(argc, argv, "01", &opts);
273
+
274
+ Data_Get_Struct(self, struct readerdata, reader);
275
+
276
+ raise_if_locked(reader);
277
+ reader->locked = 1;
278
+
279
+ png_set_packing(reader->png);
280
+ png_set_strip_16(reader->png);
281
+ png_set_expand(reader->png);
282
+ png_read_update_info(reader->png, reader->info);
283
+
284
+ if (!reader->scale_width) {
285
+ reader->scale_width = png_get_image_width(reader->png, reader->info);
286
+ }
287
+ if (!reader->scale_height) {
288
+ reader->scale_height = png_get_image_height(reader->png, reader->info);
289
+ }
290
+
291
+ png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
292
+ (png_error_ptr)error, (png_error_ptr)warning);
293
+ info = png_create_info_struct(png);
294
+
295
+ inwidthbuf = malloc(png_get_rowbytes(reader->png, reader->info));
296
+ cmp = png_get_channels(reader->png, reader->info);
297
+ outwidthbuf = malloc(reader->scale_width * cmp);
298
+ yscaler_init(&ys, png_get_image_height(reader->png, reader->info),
299
+ reader->scale_height, reader->scale_width * cmp);
300
+
301
+ args.reader = reader;
302
+ args.opts = opts;
303
+ args.png = png;
304
+ args.info = info;
305
+ args.inwidthbuf = inwidthbuf;
306
+ args.outwidthbuf = outwidthbuf;
307
+ args.ys = &ys;
308
+ rb_protect((VALUE(*)(VALUE))each2, (VALUE)&args, &state);
309
+
310
+ yscaler_free(&ys);
311
+ free(inwidthbuf);
312
+ free(outwidthbuf);
313
+ png_destroy_write_struct(&png, &info);
314
+
315
+ if (state) {
316
+ rb_jump_tag(state);
317
+ }
318
+
319
+ return self;
320
+ }
321
+
322
+ void Init_png()
323
+ {
324
+ VALUE mOil, cPNGReader;
325
+ mOil = rb_const_get(rb_cObject, rb_intern("Oil"));
326
+ cPNGReader = rb_define_class_under(mOil, "PNGReader", rb_cObject);
327
+ rb_define_alloc_func(cPNGReader, allocate);
328
+ rb_define_method(cPNGReader, "initialize", initialize, 1);
329
+ rb_define_method(cPNGReader, "width", width, 0);
330
+ rb_define_method(cPNGReader, "height", height, 0);
331
+ rb_define_method(cPNGReader, "scale_width", scale_width, 0);
332
+ rb_define_method(cPNGReader, "scale_width=", set_scale_width, 1);
333
+ rb_define_method(cPNGReader, "scale_height", scale_height, 0);
334
+ rb_define_method(cPNGReader, "scale_height=", set_scale_height, 1);
335
+ rb_define_method(cPNGReader, "each", each, -1);
336
+ id_read = rb_intern("read");
337
+ }
@@ -0,0 +1,464 @@
1
+ #include "resample.h"
2
+ #include <stdint.h>
3
+ #include <math.h>
4
+ #include <stdlib.h>
5
+ #include <string.h>
6
+ #include <stdio.h>
7
+
8
+ #define TAPS 4
9
+
10
+ /**
11
+ * 64-bit type that uses 1 bit for signedness, 33 bits for the integer, and 30
12
+ * bits for the fraction.
13
+ *
14
+ * 0-29: fraction, 30-62: integer, 63: sign.
15
+ *
16
+ * Useful for storing the product of a fix1_30 type and an unsigned char.
17
+ */
18
+ typedef int64_t fix33_30;
19
+
20
+ /**
21
+ * We add this to a fix33_30 value in order to bump up rounding errors.
22
+ *
23
+ * The best possible value was determined by comparing to a reference
24
+ * implementation and comparing values for the minimal number of errors.
25
+ */
26
+ #define TOPOFF 4096
27
+
28
+ /**
29
+ * Signed type that uses 1 bit for signedness, 1 bit for the integer, and 30
30
+ * bits for the fraction.
31
+ *
32
+ * 0-29: fraction, 30: integer, 31: sign.
33
+ *
34
+ * Useful for storing coefficients.
35
+ */
36
+ typedef int32_t fix1_30;
37
+ #define ONE_FIX1_30 (1<<30)
38
+
39
+ /**
40
+ * Calculate the greatest common denominator between a and b.
41
+ */
42
+ static long gcd (long a, long b)
43
+ {
44
+ long c;
45
+ while (a != 0) {
46
+ c = a;
47
+ a = b%a;
48
+ b = c;
49
+ }
50
+ return b;
51
+ }
52
+
53
+ /**
54
+ * Round and clamp a fix33_30 value between 0 and 255. Returns an unsigned char.
55
+ */
56
+ static unsigned char clamp(fix33_30 x)
57
+ {
58
+ if (x < 0) {
59
+ return 0;
60
+ }
61
+
62
+ /* bump up rounding errors before truncating */
63
+ x += TOPOFF;
64
+
65
+ /* This is safe because we have the < 0 check above and a sample can't
66
+ * end up with a value over 512 */
67
+ if (x & (1l<<38)) {
68
+ return 255;
69
+ }
70
+
71
+ return x >> 30;
72
+ }
73
+
74
+ /**
75
+ * Map from a discreet dest coordinate to a continuous source coordinate.
76
+ * The resulting coordinate can range from -0.5 to the maximum of the
77
+ * destination image dimension.
78
+ */
79
+ static double map(long pos, double scale)
80
+ {
81
+ return (pos + 0.5) / scale - 0.5;
82
+ }
83
+
84
+ /**
85
+ * Given input and output dimensions and an output position, return the
86
+ * corresponding input position and put the sub-pixel remainder in rest.
87
+ */
88
+ long split_map(unsigned long dim_in, unsigned long dim_out, unsigned long pos,
89
+ float *rest)
90
+ {
91
+ double scale, smp;
92
+ long smp_i;
93
+
94
+ scale = dim_out / (double)dim_in;
95
+ smp = map(pos, scale);
96
+ smp_i = smp < 0 ? -1 : smp;
97
+ *rest = smp - smp_i;
98
+ return smp_i;
99
+ }
100
+
101
+ /**
102
+ * When we reduce an image by a factor of two, we need to scale our resampling
103
+ * function by two as well in order to avoid aliasing.
104
+ *
105
+ * Calculate the resampling scalar given input and output dimensions.
106
+ */
107
+ static long calc_tap_mult(long dim_in, long dim_out)
108
+ {
109
+ if (dim_out > dim_in) {
110
+ return 1;
111
+ }
112
+ return dim_in / dim_out;
113
+ }
114
+
115
+ /**
116
+ * Given input and output dimension, calculate the total number of taps that
117
+ * will be needed to calculate an output sample.
118
+ */
119
+ long calc_taps(long dim_in, long dim_out)
120
+ {
121
+ return calc_tap_mult(dim_in, dim_out) * TAPS;
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);
147
+ }
148
+
149
+ /**
150
+ * Catmull-Rom interpolator.
151
+ */
152
+ static float catrom(float x)
153
+ {
154
+ if (x<1)
155
+ return (3*x*x*x - 5*x*x + 2) / 2;
156
+ return (-1*x*x*x + 5*x*x - 8*x + 4) / 2;
157
+ }
158
+
159
+ /**
160
+ * Convert a single-precision float to a fix1_30 fixed point int. x must be
161
+ * between 0 and 1.
162
+ */
163
+ static fix1_30 f_to_fix1_30(float x)
164
+ {
165
+ return x * ONE_FIX1_30;
166
+ }
167
+
168
+ /**
169
+ * Given an offset tx, calculate TAPS * tap_mult coefficients.
170
+ *
171
+ * The coefficients are stored as fix1_30 fixed point ints in coeffs.
172
+ */
173
+ static void calc_coeffs(fix1_30 *coeffs, float tx, long tap_mult)
174
+ {
175
+ long i, taps, total;
176
+ float tmp;
177
+ fix1_30 tmp_fixed;
178
+
179
+ total = 0;
180
+
181
+ taps = tap_mult * TAPS;
182
+ tx = 1 - tx - taps / 2;
183
+
184
+ for (i=0; i<taps-1; i++) {
185
+ tmp = catrom(fabs(tx) / tap_mult) / tap_mult;
186
+ tmp_fixed = f_to_fix1_30(tmp);
187
+ coeffs[i] = tmp_fixed;
188
+ total += tmp_fixed;
189
+ tx += 1;
190
+ }
191
+
192
+ coeffs[taps-1] = ONE_FIX1_30 - total;
193
+ }
194
+
195
+ /**
196
+ * Generic yscaler, operates on arbitrary sized samples.
197
+ */
198
+ static void yscale_gen(long len, long height, fix1_30 *coeffs,
199
+ unsigned char **in, unsigned char *out)
200
+ {
201
+ long i, j;
202
+ fix33_30 coeff, total;
203
+
204
+ for (i=0; i<len; i++) {
205
+ total = 0;
206
+ for (j=0; j<height; j++) {
207
+ coeff = coeffs[j];
208
+ total += coeff * in[j][i];
209
+ }
210
+ out[i] = clamp(total);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * RGBA yscaler, fetches 32-bytes at a time from memory to improve mem read
216
+ * performance.
217
+ */
218
+ static void yscale_rgba(long width, long height, fix1_30 *coeffs,
219
+ uint32_t **sl_in, uint32_t *sl_out)
220
+ {
221
+ long i, j;
222
+ fix33_30 r, g, b, a, coeff;
223
+ uint32_t sample;
224
+
225
+ for (i=0; i<width; i++) {
226
+ r = g = b = a = 0;
227
+ for (j=0; j<height; j++) {
228
+ coeff = coeffs[j];
229
+ sample = sl_in[j][i];
230
+ r += coeff * rgba_r(sample);
231
+ g += coeff * rgba_g(sample);
232
+ b += coeff * rgba_b(sample);
233
+ a += coeff * rgba_a(sample);
234
+ }
235
+ sl_out[i] = fix33_30_to_rgba(r, g, b, a);
236
+ }
237
+ }
238
+
239
+ /**
240
+ * RGBX yscaler, fetches 32-bytes at a time from memory to improve mem read
241
+ * performance and ignores the last value.
242
+ */
243
+ static void yscale_rgbx(long width, long height, fix1_30 *coeffs,
244
+ uint32_t **sl_in, uint32_t *sl_out)
245
+ {
246
+ long i, j;
247
+ fix33_30 r, g, b, coeff;
248
+ uint32_t sample;
249
+
250
+ for (i=0; i<width; i++) {
251
+ r = g = b = 0;
252
+ for (j=0; j<height; j++) {
253
+ coeff = coeffs[j];
254
+ sample = sl_in[j][i];
255
+ r += coeff * rgba_r(sample);
256
+ g += coeff * rgba_g(sample);
257
+ b += coeff * rgba_b(sample);
258
+ }
259
+ sl_out[i] = fix33_30_to_rgbx(r, g, b);
260
+ }
261
+ }
262
+
263
+ void strip_scale(void **in, long strip_height, long width, void *out, float ty,
264
+ int cmp, int opts)
265
+ {
266
+ fix1_30 *coeffs;
267
+ long tap_mult;
268
+
269
+ tap_mult = strip_height / TAPS;
270
+ coeffs = malloc(strip_height * sizeof(fix1_30));
271
+ calc_coeffs(coeffs, ty, tap_mult);
272
+
273
+ if (cmp == 4 && (opts & OIL_FILLER)) {
274
+ yscale_rgbx(width, strip_height, coeffs, (uint32_t **)in,
275
+ (uint32_t *)out);
276
+ } else if (cmp == 4) {
277
+ yscale_rgba(width, strip_height, coeffs, (uint32_t **)in,
278
+ (uint32_t *)out);
279
+ } else {
280
+ yscale_gen(cmp * width, strip_height, coeffs,
281
+ (unsigned char **)in, (unsigned char *)out);
282
+ }
283
+
284
+ free(coeffs);
285
+ }
286
+
287
+ /* Bicubic x scaler */
288
+
289
+ static void sample_generic(int cmp, long taps, fix1_30 *coeffs,
290
+ unsigned char *in, unsigned char *out)
291
+ {
292
+ int i;
293
+ long j;
294
+ fix33_30 total, coeff;
295
+
296
+ for (i=0; i<cmp; i++) {
297
+ total = 0;
298
+ for (j=0; j<taps; j++){
299
+ coeff = coeffs[j];
300
+ total += coeff * in[j * cmp + i];
301
+ }
302
+ out[i] = clamp(total);
303
+ }
304
+ }
305
+
306
+ static uint32_t sample_rgba(long taps, fix1_30 *coeffs, uint32_t *in)
307
+ {
308
+ long i;
309
+ fix33_30 r, g, b, a, coeff;
310
+ uint32_t sample;
311
+
312
+ r = g = b = a = 0;
313
+ for (i=0; i<taps; i++) {
314
+ coeff = coeffs[i];
315
+ sample = in[i];
316
+ r += coeff * rgba_r(sample);
317
+ g += coeff * rgba_g(sample);
318
+ b += coeff * rgba_b(sample);
319
+ a += coeff * rgba_a(sample);
320
+ }
321
+ return fix33_30_to_rgba(r, g, b, a);
322
+ }
323
+
324
+ static uint32_t sample_rgbx(long taps, fix1_30 *coeffs, uint32_t *in)
325
+ {
326
+ long i;
327
+ fix33_30 r, g, b, coeff;
328
+ uint32_t sample;
329
+
330
+ r = g = b = 0;
331
+ for (i=0; i<taps; i++) {
332
+ coeff = coeffs[i];
333
+ sample = in[i];
334
+ r += coeff * rgba_r(sample);
335
+ g += coeff * rgba_g(sample);
336
+ b += coeff * rgba_b(sample);
337
+ }
338
+ return fix33_30_to_rgbx(r, g, b);
339
+ }
340
+
341
+ static void xscale_set_sample(long taps, fix1_30 *coeffs, void *in, void *out,
342
+ int cmp, int opts)
343
+ {
344
+ if (cmp == 4 && (opts & OIL_FILLER)) {
345
+ *(uint32_t *)out = sample_rgbx(taps, coeffs, (uint32_t *)in);
346
+ } else if (cmp == 4) {
347
+ *(uint32_t *)out = sample_rgba(taps, coeffs, (uint32_t *)in);
348
+ } else {
349
+ sample_generic(cmp, taps, coeffs, in, out);
350
+ }
351
+ }
352
+
353
+ /* padded scanline */
354
+
355
+ /**
356
+ * Scanline with extra space at the beginning and end. This allows us to extend
357
+ * a scanline to the left and right. This in turn allows resizing functions
358
+ * to operate past the edges of the scanline without having to check for
359
+ * boundaries.
360
+ */
361
+ struct padded_sl {
362
+ unsigned char *buf;
363
+ unsigned char *pad_left;
364
+ long inner_width;
365
+ long pad_width;
366
+ int cmp;
367
+ };
368
+
369
+ void padded_sl_init(struct padded_sl *psl, long inner_width, long pad_width,
370
+ int cmp)
371
+ {
372
+ psl->inner_width = inner_width;
373
+ psl->pad_width = pad_width;
374
+ psl->cmp = cmp;
375
+ psl->pad_left = malloc((inner_width + 2 * pad_width) * cmp);
376
+ psl->buf = psl->pad_left + pad_width * cmp;
377
+ }
378
+
379
+ void padded_sl_free(struct padded_sl *psl)
380
+ {
381
+ free(psl->pad_left);
382
+ }
383
+
384
+ /**
385
+ * pad points to the first byte in the pad area.
386
+ * src points to the sample that will be replicated in the pad area.
387
+ * width is the number of samples in the pad area.
388
+ * cmp is the number of components per sample.
389
+ */
390
+ static void padded_sl_pad(unsigned char *pad, unsigned char *src, int width,
391
+ int cmp)
392
+ {
393
+ int i, j;
394
+
395
+ for (i=0; i<width; i++)
396
+ for (j=0; j<cmp; j++)
397
+ pad[i * cmp + j] = src[j];
398
+ }
399
+
400
+ static void padded_sl_extend_edges(struct padded_sl *psl)
401
+ {
402
+ unsigned char *pad_right;
403
+
404
+ padded_sl_pad(psl->pad_left, psl->buf, psl->pad_width, psl->cmp);
405
+ pad_right = psl->buf + psl->inner_width * psl->cmp;
406
+ padded_sl_pad(pad_right, pad_right - psl->cmp, psl->pad_width, psl->cmp);
407
+ }
408
+
409
+ void xscale(unsigned char *in, long in_width, unsigned char *out,
410
+ long out_width, int cmp, int opts)
411
+ {
412
+ float tx;
413
+ fix1_30 *coeffs;
414
+ long i, j, xsmp_i, in_chunk, out_chunk, scale_gcd, taps, tap_mult;
415
+ unsigned char *out_pos, *rpadv, *tmp;
416
+ struct padded_sl psl;
417
+
418
+ tap_mult = calc_tap_mult(in_width, out_width);
419
+ taps = tap_mult * TAPS;
420
+ coeffs = malloc(taps * sizeof(fix1_30));
421
+
422
+ scale_gcd = gcd(in_width, out_width);
423
+ in_chunk = in_width / scale_gcd;
424
+ out_chunk = out_width / scale_gcd;
425
+
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
+ for (i=0; i<out_chunk; i++) {
441
+ xsmp_i = split_map(in_width, out_width, i, &tx);
442
+ calc_coeffs(coeffs, tx, tap_mult);
443
+
444
+ xsmp_i += 1 - taps / 2;
445
+ out_pos = out + i * cmp;
446
+ 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);
456
+ out_pos += out_chunk * cmp;
457
+
458
+ xsmp_i += in_chunk;
459
+ }
460
+ }
461
+
462
+ padded_sl_free(&psl);
463
+ free(coeffs);
464
+ }