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