oil 0.0.3 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +14 -46
- data/Rakefile +35 -31
- data/ext/{extconf.rb → oil/extconf.rb} +2 -12
- data/ext/oil/jpeg.c +861 -0
- data/ext/oil/oil.c +10 -0
- data/ext/oil/png.c +337 -0
- data/ext/oil/resample.c +464 -0
- data/ext/oil/resample.h +46 -0
- data/ext/oil/yscaler.c +138 -0
- data/ext/oil/yscaler.h +31 -0
- data/lib/oil.rb +77 -0
- data/test/helper.rb +84 -6
- data/test/test_jpeg.rb +114 -184
- data/test/test_png.rb +105 -169
- metadata +47 -49
- data/ext/oil.c +0 -692
- data/ext/oil.jar +0 -0
data/ext/oil/oil.c
ADDED
data/ext/oil/png.c
ADDED
@@ -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
|
+
}
|
data/ext/oil/resample.c
ADDED
@@ -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
|
+
}
|