oil 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/Rakefile +7 -3
- data/ext/oil/jpeg.c +25 -49
- data/ext/oil/oil.c +4 -3
- data/ext/oil/oil_libjpeg.c +104 -0
- data/ext/oil/oil_libjpeg.h +57 -0
- data/ext/oil/oil_libpng.c +161 -0
- data/ext/oil/oil_libpng.h +58 -0
- data/ext/oil/oil_resample.c +1012 -0
- data/ext/oil/oil_resample.h +149 -0
- data/ext/oil/png.c +22 -100
- data/lib/oil.rb +1 -1
- metadata +9 -6
- data/ext/oil/resample.c +0 -938
- data/ext/oil/resample.h +0 -119
@@ -0,0 +1,149 @@
|
|
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_RESAMPLE_H
|
23
|
+
#define OIL_RESAMPLE_H
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Color spaces currently supported by oil.
|
27
|
+
*/
|
28
|
+
enum oil_colorspace {
|
29
|
+
// error
|
30
|
+
OIL_CS_UNKNOWN = 0,
|
31
|
+
|
32
|
+
// greyscale - no color space conversions
|
33
|
+
OIL_CS_G = 0x0001,
|
34
|
+
|
35
|
+
// greyscale w/ alpha - uses premultiplied alpha
|
36
|
+
OIL_CS_GA = 0x0002,
|
37
|
+
|
38
|
+
// sRGB - input will be converted to linear RGB during processing
|
39
|
+
OIL_CS_RGB = 0x0003,
|
40
|
+
|
41
|
+
// sRGB w/ padding - same as OIL_CS_RGB, but padded with an extra byte
|
42
|
+
OIL_CS_RGBX = 0x0004, // sRGB w/ padding
|
43
|
+
|
44
|
+
// sRGB w/ alpha - sRGB to linear conversion and premultiplied alpha
|
45
|
+
OIL_CS_RGBA = 0x0104, // sRGB w/ alpha
|
46
|
+
|
47
|
+
// CMYK - no color space conversions
|
48
|
+
OIL_CS_CMYK = 0x0204, // four color CMYK
|
49
|
+
};
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Macro to get the number of components from an oil color space.
|
53
|
+
*/
|
54
|
+
#define OIL_CMP(x) ((x)&0xFF)
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Struct to hold state for scaling.
|
58
|
+
*/
|
59
|
+
struct oil_scale {
|
60
|
+
int in_height; // input image height.
|
61
|
+
int out_height; // output image height.
|
62
|
+
int in_width; // input image width.
|
63
|
+
int out_width; // output image height.
|
64
|
+
enum oil_colorspace cs; // color space of input & output.
|
65
|
+
int in_pos; // current row of input image.
|
66
|
+
int out_pos; // current row of output image.
|
67
|
+
int taps; // number of taps required to perform scaling.
|
68
|
+
int target; // where the ring buffer should be on next scaling.
|
69
|
+
int sl_len; // length in bytes of a row.
|
70
|
+
float ty; // sub-pixel offset for next scaling.
|
71
|
+
float *coeffs_y; // buffer for holding temporary y-coefficients.
|
72
|
+
float *coeffs_x; // buffer for holding precalculated coefficients.
|
73
|
+
int *borders; // holds precalculated coefficient rotation points.
|
74
|
+
float *rb; // ring buffer holding scanlines.
|
75
|
+
float **virt; // space to provide scanline pointers for scaling.
|
76
|
+
};
|
77
|
+
|
78
|
+
/**
|
79
|
+
* Initialize static, pre-calculated tables. This only needs to be called once.
|
80
|
+
* A call to oil_scale_init() will initialize these tables if not already done,
|
81
|
+
* so explicityly calling oil_global_init() is only needed if there are
|
82
|
+
* concurrency concerns.
|
83
|
+
*/
|
84
|
+
void oil_global_init();
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Initialize an oil scaler struct.
|
88
|
+
* @os: Pointer to the scaler struct to be initialized.
|
89
|
+
* @in_height: Height, in pixels, of the input image.
|
90
|
+
* @out_height: Height, in pixels, of the output image.
|
91
|
+
* @in_width: Width, in pixels, of the input image.
|
92
|
+
* @out_width: Width, in pixels, of the output image.
|
93
|
+
* @cs: Color space of the input/output images.
|
94
|
+
*
|
95
|
+
* Returns 0 on success.
|
96
|
+
* Returns -1 if an argument is bad.
|
97
|
+
* Returns -2 if unable to allocate memory.
|
98
|
+
*/
|
99
|
+
int oil_scale_init(struct oil_scale *os, int in_height, int out_height,
|
100
|
+
int in_width, int out_width, enum oil_colorspace cs);
|
101
|
+
|
102
|
+
/**
|
103
|
+
* Free heap allocations associated with a yscaler struct.
|
104
|
+
* @ys: Pointer to the yscaler struct to be freed.
|
105
|
+
*/
|
106
|
+
void oil_scale_free(struct oil_scale *os);
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Get a pointer to the next scanline to be filled in the ring buffer.
|
110
|
+
* @ys: Pointer to the yscaler struct to advance.
|
111
|
+
*
|
112
|
+
* Returns 0 if no more input lines are needed to produce the next output line.
|
113
|
+
* Otherwise, returns the number of input lines that are needed.
|
114
|
+
*/
|
115
|
+
int oil_scale_slots(struct oil_scale *ys);
|
116
|
+
|
117
|
+
/**
|
118
|
+
* Ingest & buffer an input scanline. Input is unsigned chars.
|
119
|
+
* @os: Pointer to the scaler struct.
|
120
|
+
* @in: Pointer to the input buffer containing a scanline.
|
121
|
+
*/
|
122
|
+
void oil_scale_in(struct oil_scale *os, unsigned char *in);
|
123
|
+
|
124
|
+
/**
|
125
|
+
* Scale previously ingested & buffered contents to produce the next scaled output
|
126
|
+
* scanline.
|
127
|
+
* @ys: Pointer to the scaler struct.
|
128
|
+
* @out: Pointer to the buffer where the output scanline will be written.
|
129
|
+
*/
|
130
|
+
void oil_scale_out(struct oil_scale *ys, unsigned char *out);
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Calculate an output ratio that preserves the input aspect ratio.
|
134
|
+
* @src_width: Width, in pixels, of the input image.
|
135
|
+
* @src_height: Height, in pixels, of the input image.
|
136
|
+
* @out_width: Width, in pixels, of the output bounding box.
|
137
|
+
* @out_height: Height, in pixels, of the output bounding box.
|
138
|
+
*
|
139
|
+
* The out_width and out_height parameters will be modified, if necessary, to
|
140
|
+
* maintain the input aspect ratio while staying within the given bounding box.
|
141
|
+
*
|
142
|
+
* Returns 0 on success.
|
143
|
+
* Returns -1 if an argument is bad.
|
144
|
+
* Returns -3 if an adjusted dimension would be out of range.
|
145
|
+
*/
|
146
|
+
int oil_fix_ratio(int src_width, int src_height, int *out_width,
|
147
|
+
int *out_height);
|
148
|
+
|
149
|
+
#endif
|
data/ext/oil/png.c
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
#include <ruby.h>
|
2
2
|
#include <png.h>
|
3
|
-
#include "
|
3
|
+
#include "oil_libpng.h"
|
4
4
|
|
5
5
|
static ID id_read;
|
6
6
|
|
@@ -8,8 +8,8 @@ struct readerdata {
|
|
8
8
|
png_structp png;
|
9
9
|
png_infop info;
|
10
10
|
VALUE source_io;
|
11
|
-
|
12
|
-
|
11
|
+
int scale_width;
|
12
|
+
int scale_height;
|
13
13
|
int locked;
|
14
14
|
};
|
15
15
|
|
@@ -210,69 +210,26 @@ struct each_args {
|
|
210
210
|
struct readerdata *reader;
|
211
211
|
png_structp wpng;
|
212
212
|
png_infop winfo;
|
213
|
-
unsigned char *inwidthbuf;
|
214
213
|
unsigned char *outwidthbuf;
|
215
|
-
|
216
|
-
struct yscaler ys;
|
217
|
-
struct preprocess_xscaler xs;
|
214
|
+
struct oil_libpng ol;
|
218
215
|
};
|
219
216
|
|
220
|
-
static VALUE
|
217
|
+
static VALUE each2(struct each_args *args)
|
221
218
|
{
|
222
219
|
struct readerdata *reader;
|
223
|
-
unsigned char
|
224
|
-
|
225
|
-
|
226
|
-
struct yscaler *ys;
|
227
|
-
struct preprocess_xscaler *xs;
|
228
|
-
|
229
|
-
reader = args->reader;
|
230
|
-
xs = &args->xs;
|
231
|
-
ys = &args->ys;
|
232
|
-
scanlines = args->scanlines;
|
233
|
-
outwidthbuf = args->outwidthbuf;
|
234
|
-
scaley = reader->scale_height;
|
235
|
-
|
236
|
-
png_write_info(args->wpng, args->winfo);
|
237
|
-
png_read_image(args->reader->png, (png_bytepp)args->scanlines);
|
238
|
-
|
239
|
-
n = 0;
|
240
|
-
for(i=0; i<scaley; i++) {
|
241
|
-
while ((yinbuf = yscaler_next(ys))) {
|
242
|
-
preprocess_xscaler_scale(xs, scanlines[n++], yinbuf);
|
243
|
-
}
|
244
|
-
yscaler_scale(ys, outwidthbuf, i);
|
245
|
-
png_write_row(args->wpng, outwidthbuf);
|
246
|
-
}
|
247
|
-
|
248
|
-
png_write_end(args->wpng, args->winfo);
|
249
|
-
return Qnil;
|
250
|
-
}
|
251
|
-
|
252
|
-
static VALUE each_interlace_none(struct each_args *args)
|
253
|
-
{
|
254
|
-
struct readerdata *reader;
|
255
|
-
unsigned char *inwidthbuf, *outwidthbuf;
|
256
|
-
uint16_t *yinbuf;
|
257
|
-
struct preprocess_xscaler *xs;
|
258
|
-
struct yscaler *ys;
|
259
|
-
uint32_t i, scaley;
|
220
|
+
unsigned char *outwidthbuf;
|
221
|
+
struct oil_libpng *ol;
|
222
|
+
int i, scaley;
|
260
223
|
|
261
224
|
reader = args->reader;
|
262
|
-
xs = &args->xs;
|
263
|
-
inwidthbuf = args->inwidthbuf;
|
264
225
|
outwidthbuf = args->outwidthbuf;
|
265
|
-
|
226
|
+
ol = &args->ol;
|
266
227
|
scaley = reader->scale_height;
|
267
228
|
|
268
229
|
png_write_info(args->wpng, args->winfo);
|
269
230
|
|
270
231
|
for(i=0; i<scaley; i++) {
|
271
|
-
|
272
|
-
png_read_row(reader->png, inwidthbuf, NULL);
|
273
|
-
preprocess_xscaler_scale(xs, inwidthbuf, yinbuf);
|
274
|
-
}
|
275
|
-
yscaler_scale(ys, outwidthbuf, i);
|
232
|
+
oil_libpng_read_scanline(ol, outwidthbuf);
|
276
233
|
png_write_row(args->wpng, outwidthbuf);
|
277
234
|
}
|
278
235
|
|
@@ -280,21 +237,6 @@ static VALUE each_interlace_none(struct each_args *args)
|
|
280
237
|
return Qnil;
|
281
238
|
}
|
282
239
|
|
283
|
-
static enum oil_colorspace png_cs_to_oil(png_byte cs)
|
284
|
-
{
|
285
|
-
switch(cs) {
|
286
|
-
case PNG_COLOR_TYPE_GRAY:
|
287
|
-
return OIL_CS_G;
|
288
|
-
case PNG_COLOR_TYPE_GA:
|
289
|
-
return OIL_CS_GA;
|
290
|
-
case PNG_COLOR_TYPE_RGB:
|
291
|
-
return OIL_CS_RGB;
|
292
|
-
case PNG_COLOR_TYPE_RGBA:
|
293
|
-
return OIL_CS_RGBA;
|
294
|
-
}
|
295
|
-
rb_raise(rb_eRuntimeError, "Color space not recognized.");
|
296
|
-
}
|
297
|
-
|
298
240
|
/*
|
299
241
|
* call-seq:
|
300
242
|
* reader.each(opts, &block) -> self
|
@@ -315,13 +257,9 @@ static VALUE each(int argc, VALUE *argv, VALUE self)
|
|
315
257
|
png_infop winfo;
|
316
258
|
png_structp wpng;
|
317
259
|
VALUE opts;
|
318
|
-
int cmp, state;
|
260
|
+
int cmp, state, ret;
|
319
261
|
struct each_args args;
|
320
|
-
uint32_t i, height, width;
|
321
262
|
png_byte ctype;
|
322
|
-
unsigned char **scanlines;
|
323
|
-
size_t row_bytes;
|
324
|
-
enum oil_colorspace cs;
|
325
263
|
|
326
264
|
rb_scan_args(argc, argv, "01", &opts);
|
327
265
|
|
@@ -332,7 +270,6 @@ static VALUE each(int argc, VALUE *argv, VALUE self)
|
|
332
270
|
|
333
271
|
cmp = png_get_channels(reader->png, reader->info);
|
334
272
|
ctype = png_get_color_type(reader->png, reader->info);
|
335
|
-
cs = png_cs_to_oil(ctype);
|
336
273
|
|
337
274
|
wpng = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
|
338
275
|
(png_error_ptr)error, (png_error_ptr)warning);
|
@@ -343,39 +280,24 @@ static VALUE each(int argc, VALUE *argv, VALUE self)
|
|
343
280
|
ctype, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
344
281
|
PNG_FILTER_TYPE_DEFAULT);
|
345
282
|
|
346
|
-
width = png_get_image_width(reader->png, reader->info);
|
347
|
-
height = png_get_image_height(reader->png, reader->info);
|
348
|
-
row_bytes = png_get_rowbytes(reader->png, reader->info);
|
349
|
-
|
350
283
|
args.reader = reader;
|
351
284
|
args.wpng = wpng;
|
352
285
|
args.winfo = winfo;
|
353
286
|
args.outwidthbuf = malloc(reader->scale_width * cmp);
|
287
|
+
if (!args.outwidthbuf) {
|
288
|
+
rb_raise(rb_eRuntimeError, "Unable to allocate memory.");
|
289
|
+
}
|
354
290
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
rb_protect((VALUE(*)(VALUE))each_interlace_none, (VALUE)&args, &state);
|
361
|
-
free(args.inwidthbuf);
|
362
|
-
} else {
|
363
|
-
scanlines = malloc(height * sizeof(unsigned char *));
|
364
|
-
for (i=0; i<height; i++) {
|
365
|
-
scanlines[i] = malloc(row_bytes);
|
366
|
-
}
|
367
|
-
|
368
|
-
args.scanlines = scanlines;
|
369
|
-
rb_protect((VALUE(*)(VALUE))each_interlace, (VALUE)&args, &state);
|
370
|
-
|
371
|
-
for (i=0; i<height; i++) {
|
372
|
-
free(scanlines[i]);
|
373
|
-
}
|
374
|
-
free(scanlines);
|
291
|
+
ret = oil_libpng_init(&args.ol, reader->png, reader->info,
|
292
|
+
reader->scale_width, reader->scale_height);
|
293
|
+
if (ret!=0) {
|
294
|
+
free(args.outwidthbuf);
|
295
|
+
rb_raise(rb_eRuntimeError, "Unable to allocate memory.");
|
375
296
|
}
|
376
297
|
|
377
|
-
|
378
|
-
|
298
|
+
rb_protect((VALUE(*)(VALUE))each2, (VALUE)&args, &state);
|
299
|
+
|
300
|
+
oil_libpng_free(&args.ol);
|
379
301
|
free(args.outwidthbuf);
|
380
302
|
png_destroy_write_struct(&wpng, &winfo);
|
381
303
|
|
data/lib/oil.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oil
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Timothy Elliott
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Resize JPEG and PNG images, aiming for fast performance and low memory
|
14
14
|
use.
|
@@ -25,9 +25,13 @@ files:
|
|
25
25
|
- ext/oil/extconf.rb
|
26
26
|
- ext/oil/jpeg.c
|
27
27
|
- ext/oil/oil.c
|
28
|
+
- ext/oil/oil_libjpeg.c
|
29
|
+
- ext/oil/oil_libjpeg.h
|
30
|
+
- ext/oil/oil_libpng.c
|
31
|
+
- ext/oil/oil_libpng.h
|
32
|
+
- ext/oil/oil_resample.c
|
33
|
+
- ext/oil/oil_resample.h
|
28
34
|
- ext/oil/png.c
|
29
|
-
- ext/oil/resample.c
|
30
|
-
- ext/oil/resample.h
|
31
35
|
- lib/oil.rb
|
32
36
|
- test/helper.rb
|
33
37
|
- test/test_jpeg.rb
|
@@ -51,8 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
55
|
- !ruby/object:Gem::Version
|
52
56
|
version: '0'
|
53
57
|
requirements: []
|
54
|
-
|
55
|
-
rubygems_version: 2.7.6
|
58
|
+
rubygems_version: 3.0.2
|
56
59
|
signing_key:
|
57
60
|
specification_version: 4
|
58
61
|
summary: Resize JPEG and PNG images.
|
data/ext/oil/resample.c
DELETED
@@ -1,938 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* Copyright (c) 2014-2016 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 "resample.h"
|
23
|
-
#include <stdint.h>
|
24
|
-
#include <math.h>
|
25
|
-
#include <stdlib.h>
|
26
|
-
#include <string.h>
|
27
|
-
#include <stdio.h>
|
28
|
-
|
29
|
-
/**
|
30
|
-
* Bicubic interpolation. 2 base taps on either side.
|
31
|
-
*/
|
32
|
-
#define TAPS 4
|
33
|
-
|
34
|
-
/**
|
35
|
-
* 64-bit type that uses 1 bit for signedness, 33 bits for the integer, and 30
|
36
|
-
* bits for the fraction.
|
37
|
-
*
|
38
|
-
* 0-29: fraction, 30-62: integer, 63: sign.
|
39
|
-
*
|
40
|
-
* Useful for storing the product of a fix1_30 type and an unsigned char.
|
41
|
-
*/
|
42
|
-
typedef int64_t fix33_30;
|
43
|
-
|
44
|
-
/**
|
45
|
-
* We add this to a fix33_30 value in order to bump up rounding errors.
|
46
|
-
*
|
47
|
-
* The best possible value was determined by comparing to a reference
|
48
|
-
* implementation and comparing values for the minimal number of errors.
|
49
|
-
*/
|
50
|
-
#define TOPOFF 8192
|
51
|
-
|
52
|
-
/**
|
53
|
-
* Signed type that uses 1 bit for signedness, 1 bit for the integer, and 30
|
54
|
-
* bits for the fraction.
|
55
|
-
*
|
56
|
-
* 0-29: fraction, 30: integer, 31: sign.
|
57
|
-
*
|
58
|
-
* Useful for storing coefficients.
|
59
|
-
*/
|
60
|
-
typedef int32_t fix1_30;
|
61
|
-
#define ONE_FIX1_30 (1<<30)
|
62
|
-
|
63
|
-
/**
|
64
|
-
* Calculate the greatest common denominator between a and b.
|
65
|
-
*/
|
66
|
-
static uint32_t gcd(uint32_t a, uint32_t b)
|
67
|
-
{
|
68
|
-
uint32_t c;
|
69
|
-
while (a != 0) {
|
70
|
-
c = a;
|
71
|
-
a = b%a;
|
72
|
-
b = c;
|
73
|
-
}
|
74
|
-
return b;
|
75
|
-
}
|
76
|
-
|
77
|
-
/**
|
78
|
-
* Round and clamp a fix33_30 value between 0 and 255. Returns an unsigned char.
|
79
|
-
*/
|
80
|
-
static uint16_t clamp(fix33_30 x)
|
81
|
-
{
|
82
|
-
x += 1 << 29;
|
83
|
-
x = x < 0 ? 0 : (x > (65535L << 30) ? (65535L << 30) : x);
|
84
|
-
return x >> 30;
|
85
|
-
}
|
86
|
-
|
87
|
-
static uint8_t clamp8(fix33_30 x)
|
88
|
-
{
|
89
|
-
return (clamp(x) + (1 << 7)) / 257;
|
90
|
-
}
|
91
|
-
|
92
|
-
/**
|
93
|
-
* Given input and output dimensions and an output position, return the
|
94
|
-
* corresponding input position and put the sub-pixel remainder in rest.
|
95
|
-
*
|
96
|
-
* Map from a discreet dest coordinate to a continuous source coordinate.
|
97
|
-
* The resulting coordinate can range from -0.5 to the maximum of the
|
98
|
-
* destination image dimension.
|
99
|
-
*/
|
100
|
-
int32_t split_map(uint32_t dim_in, uint32_t dim_out, uint32_t pos, float *rest)
|
101
|
-
{
|
102
|
-
double smp;
|
103
|
-
int32_t smp_i;
|
104
|
-
|
105
|
-
smp = (pos + 0.5) * ((double)dim_in / dim_out) - 0.5;
|
106
|
-
smp_i = smp < 0 ? -1 : smp;
|
107
|
-
*rest = smp - smp_i;
|
108
|
-
return smp_i;
|
109
|
-
}
|
110
|
-
|
111
|
-
/**
|
112
|
-
* Given input and output dimension, calculate the total number of taps that
|
113
|
-
* will be needed to calculate an output sample.
|
114
|
-
*
|
115
|
-
* When we reduce an image by a factor of two, we need to scale our resampling
|
116
|
-
* function by two as well in order to avoid aliasing.
|
117
|
-
*/
|
118
|
-
uint64_t calc_taps(uint32_t dim_in, uint32_t dim_out)
|
119
|
-
{
|
120
|
-
uint64_t tmp;
|
121
|
-
if (dim_out > dim_in) {
|
122
|
-
return TAPS;
|
123
|
-
}
|
124
|
-
tmp = (uint64_t)TAPS * dim_in / dim_out;
|
125
|
-
return tmp - (tmp & 1);
|
126
|
-
}
|
127
|
-
|
128
|
-
/**
|
129
|
-
* Catmull-Rom interpolator.
|
130
|
-
*/
|
131
|
-
static float catrom(float x)
|
132
|
-
{
|
133
|
-
if (x<1) {
|
134
|
-
return ((3*x - 5)*x*x + 2) / 2;
|
135
|
-
}
|
136
|
-
return (((5 - x)*x - 8)*x + 4) / 2;
|
137
|
-
}
|
138
|
-
|
139
|
-
/**
|
140
|
-
* Convert a single-precision float to a fix1_30 fixed point int. x must be
|
141
|
-
* between 0 and 1.
|
142
|
-
*/
|
143
|
-
static fix1_30 f_to_fix1_30(float x)
|
144
|
-
{
|
145
|
-
return x * ONE_FIX1_30;
|
146
|
-
}
|
147
|
-
|
148
|
-
/**
|
149
|
-
* Given an offset tx, calculate TAPS * tap_mult coefficients.
|
150
|
-
*
|
151
|
-
* The coefficients are stored as fix1_30 fixed point ints in coeffs.
|
152
|
-
*/
|
153
|
-
static void calc_coeffs(fix1_30 *coeffs, float tx, uint32_t taps)
|
154
|
-
{
|
155
|
-
uint32_t i;
|
156
|
-
float tmp, tap_mult, fudge;
|
157
|
-
fix1_30 tmp_fixed;
|
158
|
-
|
159
|
-
tap_mult = (float)taps / TAPS;
|
160
|
-
tx = 1 - tx - taps / 2;
|
161
|
-
fudge = 1.0;
|
162
|
-
|
163
|
-
for (i=0; i<taps; i++) {
|
164
|
-
tmp = catrom(fabsf(tx) / tap_mult) / tap_mult;
|
165
|
-
fudge -= tmp;
|
166
|
-
tmp_fixed = f_to_fix1_30(tmp);
|
167
|
-
coeffs[i] = tmp_fixed;
|
168
|
-
tx += 1;
|
169
|
-
}
|
170
|
-
coeffs[taps / 2] += f_to_fix1_30(fudge);
|
171
|
-
}
|
172
|
-
|
173
|
-
/* bicubic y-scaler */
|
174
|
-
|
175
|
-
static uint8_t linear_sample_to_srgb(uint16_t in)
|
176
|
-
{
|
177
|
-
double in_f, s1, s2, s3;
|
178
|
-
if (in <= 248) {
|
179
|
-
return (in * 3295 + 32768) >> 16;
|
180
|
-
}
|
181
|
-
in_f = in / 65535.0;
|
182
|
-
s1 = sqrt(in_f);
|
183
|
-
s2 = sqrt(s1);
|
184
|
-
s3 = sqrt(s2);
|
185
|
-
return (0.0427447 + 0.547242 * s1 + 0.928361 * s2 - 0.518123 * s3) * 255 + 0.5;
|
186
|
-
}
|
187
|
-
|
188
|
-
static void strip_scale_rgbx(uint16_t **in, uint32_t strip_height, size_t len,
|
189
|
-
uint8_t *out, fix1_30 *coeffs)
|
190
|
-
{
|
191
|
-
size_t i;
|
192
|
-
uint32_t j;
|
193
|
-
fix33_30 coeff, sum[3];
|
194
|
-
|
195
|
-
for (i=0; i<len; i+=4) {
|
196
|
-
sum[0] = sum[1] = sum[2] = 0;
|
197
|
-
for (j=0; j<strip_height; j++) {
|
198
|
-
coeff = coeffs[j];
|
199
|
-
sum[0] += coeff * in[j][i];
|
200
|
-
sum[1] += coeff * in[j][i + 1];
|
201
|
-
sum[2] += coeff * in[j][i + 2];
|
202
|
-
}
|
203
|
-
out[0] = linear_sample_to_srgb(clamp(sum[0]));
|
204
|
-
out[1] = linear_sample_to_srgb(clamp(sum[1]));
|
205
|
-
out[2] = linear_sample_to_srgb(clamp(sum[2]));
|
206
|
-
out[3] = 0;
|
207
|
-
out += 4;
|
208
|
-
}
|
209
|
-
}
|
210
|
-
|
211
|
-
static void strip_scale_rgb(uint16_t **in, uint32_t strip_height, size_t len,
|
212
|
-
uint8_t *out, fix1_30 *coeffs)
|
213
|
-
{
|
214
|
-
size_t i;
|
215
|
-
uint32_t j;
|
216
|
-
fix33_30 coeff, sum[3];
|
217
|
-
|
218
|
-
for (i=0; i<len; i+=4) {
|
219
|
-
sum[0] = sum[1] = sum[2] = 0;
|
220
|
-
for (j=0; j<strip_height; j++) {
|
221
|
-
coeff = coeffs[j];
|
222
|
-
sum[0] += coeff * in[j][i];
|
223
|
-
sum[1] += coeff * in[j][i + 1];
|
224
|
-
sum[2] += coeff * in[j][i + 2];
|
225
|
-
}
|
226
|
-
out[0] = linear_sample_to_srgb(clamp(sum[0]));
|
227
|
-
out[1] = linear_sample_to_srgb(clamp(sum[1]));
|
228
|
-
out[2] = linear_sample_to_srgb(clamp(sum[2]));
|
229
|
-
out += 3;
|
230
|
-
}
|
231
|
-
}
|
232
|
-
|
233
|
-
static void strip_scale_g(uint16_t **in, uint32_t strip_height, size_t len,
|
234
|
-
uint8_t *out, fix1_30 *coeffs)
|
235
|
-
{
|
236
|
-
size_t i;
|
237
|
-
uint32_t j;
|
238
|
-
fix33_30 sum;
|
239
|
-
|
240
|
-
for (i=0; i<len; i++) {
|
241
|
-
sum = 0;
|
242
|
-
for (j=0; j<strip_height; j++) {
|
243
|
-
sum += (fix33_30)coeffs[j] * in[j][i];
|
244
|
-
}
|
245
|
-
out[i] = clamp8(sum);
|
246
|
-
}
|
247
|
-
}
|
248
|
-
|
249
|
-
static void strip_scale_rgba(uint16_t **in, uint32_t strip_height, size_t len,
|
250
|
-
uint8_t *out, fix1_30 *coeffs)
|
251
|
-
{
|
252
|
-
size_t i;
|
253
|
-
uint32_t j;
|
254
|
-
fix33_30 coeff, sum[4];
|
255
|
-
|
256
|
-
for (i=0; i<len; i+=4) {
|
257
|
-
sum[0] = sum[1] = sum[2] = sum[3] = 0;
|
258
|
-
for (j=0; j<strip_height; j++) {
|
259
|
-
coeff = coeffs[j];
|
260
|
-
sum[0] += coeff * in[j][i];
|
261
|
-
sum[1] += coeff * in[j][i + 1];
|
262
|
-
sum[2] += coeff * in[j][i + 2];
|
263
|
-
sum[3] += coeff * in[j][i + 3];
|
264
|
-
}
|
265
|
-
out[0] = linear_sample_to_srgb(clamp(sum[0]));
|
266
|
-
out[1] = linear_sample_to_srgb(clamp(sum[1]));
|
267
|
-
out[2] = linear_sample_to_srgb(clamp(sum[2]));
|
268
|
-
out[3] = clamp8(sum[3]);
|
269
|
-
out += 4;
|
270
|
-
}
|
271
|
-
}
|
272
|
-
|
273
|
-
static void strip_scale_cmyk(uint16_t **in, uint32_t strip_height, size_t len,
|
274
|
-
uint8_t *out, fix1_30 *coeffs)
|
275
|
-
{
|
276
|
-
size_t i;
|
277
|
-
uint32_t j;
|
278
|
-
fix33_30 coeff, sum[4];
|
279
|
-
|
280
|
-
for (i=0; i<len; i+=4) {
|
281
|
-
sum[0] = sum[1] = sum[2] = sum[3] = 0;
|
282
|
-
for (j=0; j<strip_height; j++) {
|
283
|
-
coeff = coeffs[j];
|
284
|
-
sum[0] += coeff * in[j][i];
|
285
|
-
sum[1] += coeff * in[j][i + 1];
|
286
|
-
sum[2] += coeff * in[j][i + 2];
|
287
|
-
sum[3] += coeff * in[j][i + 3];
|
288
|
-
}
|
289
|
-
out[0] = clamp8(sum[0]);
|
290
|
-
out[1] = clamp8(sum[1]);
|
291
|
-
out[2] = clamp8(sum[2]);
|
292
|
-
out[3] = clamp8(sum[3]);
|
293
|
-
out += 4;
|
294
|
-
}
|
295
|
-
}
|
296
|
-
|
297
|
-
int strip_scale(uint16_t **in, uint32_t strip_height, size_t len, uint8_t *out,
|
298
|
-
float ty, enum oil_colorspace cs)
|
299
|
-
{
|
300
|
-
fix1_30 *coeffs;
|
301
|
-
|
302
|
-
coeffs = malloc(strip_height * sizeof(fix1_30));
|
303
|
-
if (!coeffs) {
|
304
|
-
return -2; // unable to allocate
|
305
|
-
}
|
306
|
-
calc_coeffs(coeffs, ty, strip_height);
|
307
|
-
|
308
|
-
switch(cs) {
|
309
|
-
case OIL_CS_G:
|
310
|
-
case OIL_CS_GA:
|
311
|
-
strip_scale_g(in, strip_height, len, out, coeffs);
|
312
|
-
break;
|
313
|
-
case OIL_CS_RGB:
|
314
|
-
strip_scale_rgb(in, strip_height, len, out, coeffs);
|
315
|
-
break;
|
316
|
-
case OIL_CS_RGBX:
|
317
|
-
strip_scale_rgbx(in, strip_height, len, out, coeffs);
|
318
|
-
break;
|
319
|
-
case OIL_CS_RGBA:
|
320
|
-
strip_scale_rgba(in, strip_height, len, out, coeffs);
|
321
|
-
break;
|
322
|
-
case OIL_CS_CMYK:
|
323
|
-
strip_scale_cmyk(in, strip_height, len, out, coeffs);
|
324
|
-
break;
|
325
|
-
}
|
326
|
-
|
327
|
-
free(coeffs);
|
328
|
-
return 0;
|
329
|
-
}
|
330
|
-
|
331
|
-
/* Bicubic x scaler */
|
332
|
-
|
333
|
-
static void sample_generic(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
|
334
|
-
uint16_t *out, uint8_t cmp)
|
335
|
-
{
|
336
|
-
uint8_t i;
|
337
|
-
uint32_t j;
|
338
|
-
fix33_30 total, coeff;
|
339
|
-
|
340
|
-
for (i=0; i<cmp; i++) {
|
341
|
-
total = 0;
|
342
|
-
for (j=0; j<taps; j++){
|
343
|
-
coeff = coeffs[j];
|
344
|
-
total += coeff * in[j * cmp + i];
|
345
|
-
}
|
346
|
-
out[i] = clamp(total);
|
347
|
-
}
|
348
|
-
}
|
349
|
-
|
350
|
-
static void sample_rgba(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
|
351
|
-
uint16_t *out)
|
352
|
-
{
|
353
|
-
uint32_t i;
|
354
|
-
fix33_30 sum[4], coeff;
|
355
|
-
|
356
|
-
sum[0] = sum[1] = sum[2] = sum[3] = 0;
|
357
|
-
for (i=0; i<taps; i++) {
|
358
|
-
coeff = coeffs[i];
|
359
|
-
sum[0] += coeff * in[0];
|
360
|
-
sum[1] += coeff * in[1];
|
361
|
-
sum[2] += coeff * in[2];
|
362
|
-
sum[3] += coeff * in[3];
|
363
|
-
in += 4;
|
364
|
-
}
|
365
|
-
out[0] = clamp(sum[0]);
|
366
|
-
out[1] = clamp(sum[1]);
|
367
|
-
out[2] = clamp(sum[2]);
|
368
|
-
out[3] = clamp(sum[3]);
|
369
|
-
}
|
370
|
-
|
371
|
-
static void sample_rgbx(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
|
372
|
-
uint16_t *out)
|
373
|
-
{
|
374
|
-
uint32_t i;
|
375
|
-
fix33_30 sum[3], coeff;
|
376
|
-
|
377
|
-
sum[0] = sum[1] = sum[2] = 0;
|
378
|
-
for (i=0; i<taps; i++) {
|
379
|
-
coeff = coeffs[i];
|
380
|
-
sum[0] += coeff * in[0];
|
381
|
-
sum[1] += coeff * in[1];
|
382
|
-
sum[2] += coeff * in[2];
|
383
|
-
in += 4;
|
384
|
-
}
|
385
|
-
out[0] = clamp(sum[0]);
|
386
|
-
out[1] = clamp(sum[1]);
|
387
|
-
out[2] = clamp(sum[2]);
|
388
|
-
out[3] = 0;
|
389
|
-
}
|
390
|
-
|
391
|
-
static void xscale_set_sample(uint32_t taps, fix1_30 *coeffs, uint16_t *in,
|
392
|
-
uint16_t *out, enum oil_colorspace cs)
|
393
|
-
{
|
394
|
-
switch(cs) {
|
395
|
-
case OIL_CS_G:
|
396
|
-
case OIL_CS_GA:
|
397
|
-
case OIL_CS_RGB:
|
398
|
-
sample_generic(taps, coeffs, in, out, CS_TO_CMP(cs));
|
399
|
-
break;
|
400
|
-
case OIL_CS_RGBX:
|
401
|
-
sample_rgbx(taps, coeffs, in, out);
|
402
|
-
break;
|
403
|
-
case OIL_CS_RGBA:
|
404
|
-
case OIL_CS_CMYK:
|
405
|
-
sample_rgba(taps, coeffs, in, out);
|
406
|
-
break;
|
407
|
-
}
|
408
|
-
}
|
409
|
-
|
410
|
-
void padded_sl_extend_edges(uint16_t *buf, uint32_t width, size_t pad_len,
|
411
|
-
uint8_t cmp)
|
412
|
-
{
|
413
|
-
uint16_t *pad_right;
|
414
|
-
size_t i;
|
415
|
-
pad_right = buf + pad_len + (size_t)width * cmp;
|
416
|
-
for (i=0; i<pad_len; i++) {
|
417
|
-
buf[i] = (buf + pad_len)[i % cmp];
|
418
|
-
pad_right[i] = (pad_right - cmp)[i % cmp];
|
419
|
-
}
|
420
|
-
}
|
421
|
-
|
422
|
-
size_t padded_sl_len_offset(uint32_t in_width, uint32_t out_width,
|
423
|
-
uint8_t cmp, size_t *offset)
|
424
|
-
{
|
425
|
-
uint64_t taps;
|
426
|
-
taps = calc_taps(in_width, out_width);
|
427
|
-
*offset = (taps / 2 + 1) * cmp;
|
428
|
-
return ((size_t)in_width * cmp + *offset * 2) * sizeof(uint16_t);
|
429
|
-
}
|
430
|
-
|
431
|
-
int xscale_padded(uint16_t *in, uint32_t in_width, uint16_t *out,
|
432
|
-
uint32_t out_width, enum oil_colorspace cs)
|
433
|
-
{
|
434
|
-
float tx;
|
435
|
-
fix1_30 *coeffs;
|
436
|
-
uint32_t i, j, in_chunk, out_chunk, scale_gcd;
|
437
|
-
int32_t xsmp_i;
|
438
|
-
uint64_t taps;
|
439
|
-
uint16_t *out_pos, *tmp;
|
440
|
-
uint8_t cmp;
|
441
|
-
|
442
|
-
if (!in_width || !out_width) {
|
443
|
-
return -1; // bad input parameter
|
444
|
-
}
|
445
|
-
|
446
|
-
cmp = CS_TO_CMP(cs);
|
447
|
-
taps = calc_taps(in_width, out_width);
|
448
|
-
coeffs = malloc(taps * sizeof(fix1_30));
|
449
|
-
if (!coeffs) {
|
450
|
-
return -2; // unable to allocate space for coefficients
|
451
|
-
}
|
452
|
-
|
453
|
-
scale_gcd = gcd(in_width, out_width);
|
454
|
-
in_chunk = in_width / scale_gcd;
|
455
|
-
out_chunk = out_width / scale_gcd;
|
456
|
-
|
457
|
-
for (i=0; i<out_chunk; i++) {
|
458
|
-
xsmp_i = split_map(in_width, out_width, i, &tx);
|
459
|
-
calc_coeffs(coeffs, tx, taps);
|
460
|
-
|
461
|
-
xsmp_i += 1 - taps / 2;
|
462
|
-
out_pos = out + i * cmp;
|
463
|
-
for (j=0; j<scale_gcd; j++) {
|
464
|
-
tmp = in + xsmp_i * cmp;
|
465
|
-
xscale_set_sample(taps, coeffs, tmp, out_pos, cs);
|
466
|
-
out_pos += out_chunk * cmp;
|
467
|
-
xsmp_i += in_chunk;
|
468
|
-
}
|
469
|
-
}
|
470
|
-
|
471
|
-
free(coeffs);
|
472
|
-
return 0;
|
473
|
-
}
|
474
|
-
|
475
|
-
/* scanline ring buffer */
|
476
|
-
|
477
|
-
int sl_rbuf_init(struct sl_rbuf *rb, uint32_t height, size_t sl_len)
|
478
|
-
{
|
479
|
-
rb->height = height;
|
480
|
-
rb->count = 0;
|
481
|
-
rb->length = sl_len;
|
482
|
-
rb->buf = malloc(sl_len * height * sizeof(uint16_t));
|
483
|
-
if (!rb->buf) {
|
484
|
-
return -2;
|
485
|
-
}
|
486
|
-
rb->virt = malloc(sizeof(uint8_t *) * height);
|
487
|
-
if (!rb->virt) {
|
488
|
-
free(rb->buf);
|
489
|
-
return -2;
|
490
|
-
}
|
491
|
-
return 0;
|
492
|
-
}
|
493
|
-
|
494
|
-
void sl_rbuf_free(struct sl_rbuf *rb)
|
495
|
-
{
|
496
|
-
free(rb->buf);
|
497
|
-
free(rb->virt);
|
498
|
-
}
|
499
|
-
|
500
|
-
uint16_t *sl_rbuf_next(struct sl_rbuf *rb)
|
501
|
-
{
|
502
|
-
return rb->buf + (rb->count++ % rb->height) * rb->length;
|
503
|
-
}
|
504
|
-
|
505
|
-
uint16_t **sl_rbuf_virt(struct sl_rbuf *rb, uint32_t last_target)
|
506
|
-
{
|
507
|
-
uint32_t i, safe, height, last_idx;
|
508
|
-
height = rb->height;
|
509
|
-
last_idx = rb->count - 1;
|
510
|
-
|
511
|
-
// Make sure we have the 1st scanline if extending upwards
|
512
|
-
if (last_target < last_idx && last_idx > height - 1) {
|
513
|
-
return 0;
|
514
|
-
}
|
515
|
-
|
516
|
-
for (i=0; i<height; i++) {
|
517
|
-
safe = last_target < i ? 0 : last_target - i;
|
518
|
-
safe = safe > last_idx ? last_idx : safe;
|
519
|
-
rb->virt[height - i - 1] = rb->buf + (safe % height) * rb->length;
|
520
|
-
}
|
521
|
-
return rb->virt;
|
522
|
-
}
|
523
|
-
|
524
|
-
/* xscaler */
|
525
|
-
|
526
|
-
int xscaler_init(struct xscaler *xs, uint32_t width_in, uint32_t width_out,
|
527
|
-
enum oil_colorspace cs)
|
528
|
-
{
|
529
|
-
size_t psl_len, psl_offset;
|
530
|
-
uint16_t *psl_buf;
|
531
|
-
|
532
|
-
psl_len = padded_sl_len_offset(width_in, width_out, CS_TO_CMP(cs), &psl_offset);
|
533
|
-
psl_buf = malloc(psl_len);
|
534
|
-
if (!psl_buf) {
|
535
|
-
return -2;
|
536
|
-
}
|
537
|
-
|
538
|
-
xs->psl_buf = psl_buf;
|
539
|
-
xs->psl_offset = psl_offset;
|
540
|
-
xs->psl_pos0 = psl_buf + psl_offset;
|
541
|
-
xs->width_in = width_in;
|
542
|
-
xs->width_out = width_out;
|
543
|
-
xs->cs = cs;
|
544
|
-
|
545
|
-
return 0;
|
546
|
-
}
|
547
|
-
|
548
|
-
void xscaler_free(struct xscaler *xs)
|
549
|
-
{
|
550
|
-
free(xs->psl_buf);
|
551
|
-
}
|
552
|
-
|
553
|
-
void xscaler_scale(struct xscaler *xs, uint16_t *out_buf)
|
554
|
-
{
|
555
|
-
padded_sl_extend_edges(xs->psl_buf, xs->width_in, xs->psl_offset, CS_TO_CMP(xs->cs));
|
556
|
-
xscale_padded(xs->psl_pos0, xs->width_in, out_buf, xs->width_out, xs->cs);
|
557
|
-
}
|
558
|
-
|
559
|
-
/* yscaler */
|
560
|
-
|
561
|
-
static void yscaler_map_pos(struct yscaler *ys, uint32_t pos)
|
562
|
-
{
|
563
|
-
long target;
|
564
|
-
target = split_map(ys->in_height, ys->out_height, pos, &ys->ty);
|
565
|
-
ys->target = target + ys->rb.height / 2;
|
566
|
-
}
|
567
|
-
|
568
|
-
int yscaler_init(struct yscaler *ys, uint32_t in_height, uint32_t out_height,
|
569
|
-
uint32_t width, enum oil_colorspace cs)
|
570
|
-
{
|
571
|
-
uint8_t cmp;
|
572
|
-
int ret;
|
573
|
-
uint32_t taps;
|
574
|
-
taps = calc_taps(in_height, out_height);
|
575
|
-
ys->in_height = in_height;
|
576
|
-
ys->out_height = out_height;
|
577
|
-
ys->width = width;
|
578
|
-
ys->cs = cs;
|
579
|
-
cmp = CS_TO_CMP(cs);
|
580
|
-
if (cs == OIL_CS_RGB) {
|
581
|
-
cmp = 4;
|
582
|
-
}
|
583
|
-
ret = sl_rbuf_init(&ys->rb, taps, width * cmp);
|
584
|
-
yscaler_map_pos(ys, 0);
|
585
|
-
return ret;
|
586
|
-
}
|
587
|
-
|
588
|
-
void yscaler_free(struct yscaler *ys)
|
589
|
-
{
|
590
|
-
sl_rbuf_free(&ys->rb);
|
591
|
-
}
|
592
|
-
|
593
|
-
uint16_t *yscaler_next(struct yscaler *ys)
|
594
|
-
{
|
595
|
-
if (ys->rb.count == ys->in_height || ys->rb.count > ys->target) {
|
596
|
-
return 0;
|
597
|
-
}
|
598
|
-
return sl_rbuf_next(&ys->rb);
|
599
|
-
}
|
600
|
-
|
601
|
-
int yscaler_scale(struct yscaler *ys, uint8_t *out, uint32_t pos)
|
602
|
-
{
|
603
|
-
int ret;
|
604
|
-
uint16_t **virt;
|
605
|
-
virt = sl_rbuf_virt(&ys->rb, ys->target);
|
606
|
-
ret = strip_scale(virt, ys->rb.height, ys->rb.length, out, ys->ty, ys->cs);
|
607
|
-
yscaler_map_pos(ys, pos + 1);
|
608
|
-
return ret;
|
609
|
-
}
|
610
|
-
|
611
|
-
/* Color Space Helpers */
|
612
|
-
|
613
|
-
#define EXPAND8(X) ((X<<8) + X)
|
614
|
-
|
615
|
-
static uint16_t srgb_sample_to_linear(uint8_t x)
|
616
|
-
{
|
617
|
-
static const uint16_t s2l_map[256] = {
|
618
|
-
0x0000, 0x0014, 0x0028, 0x003c, 0x0050, 0x0063, 0x0077, 0x008b,
|
619
|
-
0x009f, 0x00b3, 0x00c7, 0x00db, 0x00f1, 0x0108, 0x0120, 0x0139,
|
620
|
-
0x0154, 0x016f, 0x018c, 0x01ab, 0x01ca, 0x01eb, 0x020e, 0x0232,
|
621
|
-
0x0257, 0x027d, 0x02a5, 0x02ce, 0x02f9, 0x0325, 0x0353, 0x0382,
|
622
|
-
0x03b3, 0x03e5, 0x0418, 0x044d, 0x0484, 0x04bc, 0x04f6, 0x0532,
|
623
|
-
0x056f, 0x05ad, 0x05ed, 0x062f, 0x0673, 0x06b8, 0x06fe, 0x0747,
|
624
|
-
0x0791, 0x07dd, 0x082a, 0x087a, 0x08ca, 0x091d, 0x0972, 0x09c8,
|
625
|
-
0x0a20, 0x0a79, 0x0ad5, 0x0b32, 0x0b91, 0x0bf2, 0x0c55, 0x0cba,
|
626
|
-
0x0d20, 0x0d88, 0x0df2, 0x0e5e, 0x0ecc, 0x0f3c, 0x0fae, 0x1021,
|
627
|
-
0x1097, 0x110e, 0x1188, 0x1203, 0x1280, 0x1300, 0x1381, 0x1404,
|
628
|
-
0x1489, 0x1510, 0x159a, 0x1625, 0x16b2, 0x1741, 0x17d3, 0x1866,
|
629
|
-
0x18fb, 0x1993, 0x1a2c, 0x1ac8, 0x1b66, 0x1c06, 0x1ca7, 0x1d4c,
|
630
|
-
0x1df2, 0x1e9a, 0x1f44, 0x1ff1, 0x20a0, 0x2150, 0x2204, 0x22b9,
|
631
|
-
0x2370, 0x242a, 0x24e5, 0x25a3, 0x2664, 0x2726, 0x27eb, 0x28b1,
|
632
|
-
0x297b, 0x2a46, 0x2b14, 0x2be3, 0x2cb6, 0x2d8a, 0x2e61, 0x2f3a,
|
633
|
-
0x3015, 0x30f2, 0x31d2, 0x32b4, 0x3399, 0x3480, 0x3569, 0x3655,
|
634
|
-
0x3742, 0x3833, 0x3925, 0x3a1a, 0x3b12, 0x3c0b, 0x3d07, 0x3e06,
|
635
|
-
0x3f07, 0x400a, 0x4110, 0x4218, 0x4323, 0x4430, 0x453f, 0x4651,
|
636
|
-
0x4765, 0x487c, 0x4995, 0x4ab1, 0x4bcf, 0x4cf0, 0x4e13, 0x4f39,
|
637
|
-
0x5061, 0x518c, 0x52b9, 0x53e9, 0x551b, 0x5650, 0x5787, 0x58c1,
|
638
|
-
0x59fe, 0x5b3d, 0x5c7e, 0x5dc2, 0x5f09, 0x6052, 0x619e, 0x62ed,
|
639
|
-
0x643e, 0x6591, 0x66e8, 0x6840, 0x699c, 0x6afa, 0x6c5b, 0x6dbe,
|
640
|
-
0x6f24, 0x708d, 0x71f8, 0x7366, 0x74d7, 0x764a, 0x77c0, 0x7939,
|
641
|
-
0x7ab4, 0x7c32, 0x7db3, 0x7f37, 0x80bd, 0x8246, 0x83d1, 0x855f,
|
642
|
-
0x86f0, 0x8884, 0x8a1b, 0x8bb4, 0x8d50, 0x8eef, 0x9090, 0x9235,
|
643
|
-
0x93dc, 0x9586, 0x9732, 0x98e2, 0x9a94, 0x9c49, 0x9e01, 0x9fbb,
|
644
|
-
0xa179, 0xa339, 0xa4fc, 0xa6c2, 0xa88b, 0xaa56, 0xac25, 0xadf6,
|
645
|
-
0xafca, 0xb1a1, 0xb37b, 0xb557, 0xb737, 0xb919, 0xbaff, 0xbce7,
|
646
|
-
0xbed2, 0xc0c0, 0xc2b1, 0xc4a5, 0xc69c, 0xc895, 0xca92, 0xcc91,
|
647
|
-
0xce94, 0xd099, 0xd2a1, 0xd4ad, 0xd6bb, 0xd8cc, 0xdae0, 0xdcf7,
|
648
|
-
0xdf11, 0xe12e, 0xe34e, 0xe571, 0xe797, 0xe9c0, 0xebec, 0xee1b,
|
649
|
-
0xf04d, 0xf282, 0xf4ba, 0xf6f5, 0xf933, 0xfb74, 0xfdb8, 0xffff,
|
650
|
-
};
|
651
|
-
return s2l_map[x];
|
652
|
-
}
|
653
|
-
|
654
|
-
static void srgbx_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
|
655
|
-
{
|
656
|
-
uint32_t i, j;
|
657
|
-
uint32_t sums[3];
|
658
|
-
for (i=0; i<in_width/n; i++) {
|
659
|
-
sums[0] = sums[1] = sums[2] = 0;
|
660
|
-
for (j=0; j<n; j++) {
|
661
|
-
sums[0] += srgb_sample_to_linear(in[0]);
|
662
|
-
sums[1] += srgb_sample_to_linear(in[1]);
|
663
|
-
sums[2] += srgb_sample_to_linear(in[2]);
|
664
|
-
in += 4;
|
665
|
-
}
|
666
|
-
out[0] = sums[0] / n;
|
667
|
-
out[1] = sums[1] / n;
|
668
|
-
out[2] = sums[2] / n;
|
669
|
-
out[3] = 0;
|
670
|
-
out += 4;
|
671
|
-
}
|
672
|
-
}
|
673
|
-
|
674
|
-
static void srgba_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
|
675
|
-
{
|
676
|
-
uint32_t i, j;
|
677
|
-
uint32_t sums[4];
|
678
|
-
for (i=0; i<in_width/n; i++) {
|
679
|
-
sums[0] = sums[1] = sums[2] = sums[3] = 0;
|
680
|
-
for (j=0; j<n; j++) {
|
681
|
-
sums[0] += srgb_sample_to_linear(in[0]);
|
682
|
-
sums[1] += srgb_sample_to_linear(in[1]);
|
683
|
-
sums[2] += srgb_sample_to_linear(in[2]);
|
684
|
-
sums[3] += EXPAND8(in[3]);
|
685
|
-
in += 4;
|
686
|
-
}
|
687
|
-
out[0] = sums[0] / n;
|
688
|
-
out[1] = sums[1] / n;
|
689
|
-
out[2] = sums[2] / n;
|
690
|
-
out[3] = sums[3] / n;
|
691
|
-
out += 4;
|
692
|
-
}
|
693
|
-
}
|
694
|
-
|
695
|
-
static void srgb_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
|
696
|
-
{
|
697
|
-
uint32_t i, j;
|
698
|
-
uint32_t sums[3];
|
699
|
-
for (i=0; i<in_width/n; i++) {
|
700
|
-
sums[0] = sums[1] = sums[2] = 0;
|
701
|
-
for (j=0; j<n; j++) {
|
702
|
-
sums[0] += srgb_sample_to_linear(in[0]);
|
703
|
-
sums[1] += srgb_sample_to_linear(in[1]);
|
704
|
-
sums[2] += srgb_sample_to_linear(in[2]);
|
705
|
-
in += 3;
|
706
|
-
}
|
707
|
-
out[0] = sums[0] / n;
|
708
|
-
out[1] = sums[1] / n;
|
709
|
-
out[2] = sums[2] / n;
|
710
|
-
out[3] = 0;
|
711
|
-
out += 4;
|
712
|
-
}
|
713
|
-
}
|
714
|
-
|
715
|
-
static void g_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
|
716
|
-
{
|
717
|
-
uint32_t i, j;
|
718
|
-
uint32_t sum;
|
719
|
-
for (i=0; i<in_width/n; i++) {
|
720
|
-
sum = 0;
|
721
|
-
for (j=0; j<n; j++) {
|
722
|
-
sum += EXPAND8(in[0]);
|
723
|
-
in++;
|
724
|
-
}
|
725
|
-
out[0] = sum / n;
|
726
|
-
out++;
|
727
|
-
}
|
728
|
-
}
|
729
|
-
|
730
|
-
static void ga_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
|
731
|
-
{
|
732
|
-
uint32_t i, j;
|
733
|
-
uint32_t sums[2];
|
734
|
-
for (i=0; i<in_width/n; i++) {
|
735
|
-
sums[0] = sums[1] = 0;
|
736
|
-
for (j=0; j<n; j++) {
|
737
|
-
sums[0] += EXPAND8(in[0]);
|
738
|
-
sums[1] += EXPAND8(in[1]);
|
739
|
-
in += 2;
|
740
|
-
}
|
741
|
-
out[0] = sums[0] / n;
|
742
|
-
out[1] = sums[1] / n;
|
743
|
-
out += 2;
|
744
|
-
}
|
745
|
-
}
|
746
|
-
|
747
|
-
static void cmyk_preprocess_nx(uint8_t *in, uint16_t *out, uint32_t in_width, uint32_t n)
|
748
|
-
{
|
749
|
-
uint32_t i, j;
|
750
|
-
uint32_t sums[4];
|
751
|
-
for (i=0; i<in_width/n; i++) {
|
752
|
-
sums[0] = sums[1] = sums[2] = sums[3] = 0;
|
753
|
-
for (j=0; j<n; j++) {
|
754
|
-
sums[0] += EXPAND8(in[0]);
|
755
|
-
sums[1] += EXPAND8(in[1]);
|
756
|
-
sums[2] += EXPAND8(in[2]);
|
757
|
-
sums[3] += EXPAND8(in[3]);
|
758
|
-
in += 4;
|
759
|
-
}
|
760
|
-
out[0] = sums[0] / n;
|
761
|
-
out[1] = sums[1] / n;
|
762
|
-
out[2] = sums[2] / n;
|
763
|
-
out[3] = sums[3] / n;
|
764
|
-
out += 4;
|
765
|
-
}
|
766
|
-
}
|
767
|
-
|
768
|
-
static uint32_t calc_pre_shrink(uint32_t dim_in, uint32_t dim_out)
|
769
|
-
{
|
770
|
-
uint32_t max;
|
771
|
-
max = (2 * dim_in) / dim_out / 3;
|
772
|
-
if (max >= 4 && (dim_in % 4) == 0) {
|
773
|
-
return 4;
|
774
|
-
}
|
775
|
-
if (max >= 2 && (dim_in % 2) == 0) {
|
776
|
-
return 2;
|
777
|
-
}
|
778
|
-
return 1;
|
779
|
-
}
|
780
|
-
|
781
|
-
int preprocess_xscaler_init(struct preprocess_xscaler *pxs, uint32_t width_in,
|
782
|
-
uint32_t width_out, enum oil_colorspace cs_in)
|
783
|
-
{
|
784
|
-
enum oil_colorspace cs_out;
|
785
|
-
|
786
|
-
pxs->width_in = width_in;
|
787
|
-
pxs->cs_in = cs_in;
|
788
|
-
pxs->scale_factor = calc_pre_shrink(width_in, width_out);
|
789
|
-
|
790
|
-
/* Auto promote rgb components to rgbx for performance */
|
791
|
-
cs_out = cs_in == OIL_CS_RGB ? OIL_CS_RGBX : cs_in;
|
792
|
-
return xscaler_init(&pxs->xs, width_in / pxs->scale_factor, width_out, cs_out);
|
793
|
-
}
|
794
|
-
|
795
|
-
void preprocess_xscaler_free(struct preprocess_xscaler *pxs)
|
796
|
-
{
|
797
|
-
xscaler_free(&pxs->xs);
|
798
|
-
}
|
799
|
-
|
800
|
-
static void pre_convert_g(uint8_t *in, uint16_t *out,
|
801
|
-
uint32_t width_in, uint32_t scale_factor)
|
802
|
-
{
|
803
|
-
switch(scale_factor) {
|
804
|
-
case 1:
|
805
|
-
g_preprocess_nx(in, out, width_in, 1);
|
806
|
-
break;
|
807
|
-
case 2:
|
808
|
-
g_preprocess_nx(in, out, width_in, 2);
|
809
|
-
break;
|
810
|
-
case 4:
|
811
|
-
g_preprocess_nx(in, out, width_in, 4);
|
812
|
-
break;
|
813
|
-
}
|
814
|
-
}
|
815
|
-
|
816
|
-
static void pre_convert_ga(uint8_t *in, uint16_t *out,
|
817
|
-
uint32_t width_in, uint32_t scale_factor)
|
818
|
-
{
|
819
|
-
switch(scale_factor) {
|
820
|
-
case 1:
|
821
|
-
ga_preprocess_nx(in, out, width_in, 1);
|
822
|
-
break;
|
823
|
-
case 2:
|
824
|
-
ga_preprocess_nx(in, out, width_in, 2);
|
825
|
-
break;
|
826
|
-
case 4:
|
827
|
-
ga_preprocess_nx(in, out, width_in, 4);
|
828
|
-
break;
|
829
|
-
}
|
830
|
-
}
|
831
|
-
|
832
|
-
static void pre_convert_cmyk(uint8_t *in, uint16_t *out,
|
833
|
-
uint32_t width_in, uint32_t scale_factor)
|
834
|
-
{
|
835
|
-
switch(scale_factor) {
|
836
|
-
case 1:
|
837
|
-
cmyk_preprocess_nx(in, out, width_in, 1);
|
838
|
-
break;
|
839
|
-
case 2:
|
840
|
-
cmyk_preprocess_nx(in, out, width_in, 2);
|
841
|
-
break;
|
842
|
-
case 4:
|
843
|
-
cmyk_preprocess_nx(in, out, width_in, 4);
|
844
|
-
break;
|
845
|
-
}
|
846
|
-
}
|
847
|
-
|
848
|
-
static void pre_convert_rgbx(uint8_t *in, uint16_t *out,
|
849
|
-
uint32_t width_in, uint32_t scale_factor)
|
850
|
-
{
|
851
|
-
switch(scale_factor) {
|
852
|
-
case 1:
|
853
|
-
srgbx_preprocess_nx(in, out, width_in, 1);
|
854
|
-
break;
|
855
|
-
case 2:
|
856
|
-
srgbx_preprocess_nx(in, out, width_in, 2);
|
857
|
-
break;
|
858
|
-
case 4:
|
859
|
-
srgbx_preprocess_nx(in, out, width_in, 4);
|
860
|
-
break;
|
861
|
-
}
|
862
|
-
}
|
863
|
-
|
864
|
-
static void pre_convert_rgba(uint8_t *in, uint16_t *out,
|
865
|
-
uint32_t width_in, uint32_t scale_factor)
|
866
|
-
{
|
867
|
-
switch(scale_factor) {
|
868
|
-
case 1:
|
869
|
-
srgba_preprocess_nx(in, out, width_in, 1);
|
870
|
-
break;
|
871
|
-
case 2:
|
872
|
-
srgba_preprocess_nx(in, out, width_in, 2);
|
873
|
-
break;
|
874
|
-
case 4:
|
875
|
-
srgba_preprocess_nx(in, out, width_in, 4);
|
876
|
-
break;
|
877
|
-
}
|
878
|
-
}
|
879
|
-
|
880
|
-
static void pre_convert_rgb(uint8_t *in, uint16_t *out,
|
881
|
-
uint32_t width_in, uint32_t scale_factor)
|
882
|
-
{
|
883
|
-
switch(scale_factor) {
|
884
|
-
case 1:
|
885
|
-
srgb_preprocess_nx(in, out, width_in, 1);
|
886
|
-
break;
|
887
|
-
case 2:
|
888
|
-
srgb_preprocess_nx(in, out, width_in, 2);
|
889
|
-
break;
|
890
|
-
case 4:
|
891
|
-
srgb_preprocess_nx(in, out, width_in, 4);
|
892
|
-
break;
|
893
|
-
}
|
894
|
-
}
|
895
|
-
|
896
|
-
void preprocess_xscaler_scale(struct preprocess_xscaler *pxs, uint8_t *in,
|
897
|
-
uint16_t *out)
|
898
|
-
{
|
899
|
-
switch(pxs->cs_in) {
|
900
|
-
case OIL_CS_G:
|
901
|
-
pre_convert_g(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
|
902
|
-
break;
|
903
|
-
case OIL_CS_GA:
|
904
|
-
pre_convert_ga(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
|
905
|
-
break;
|
906
|
-
case OIL_CS_RGB:
|
907
|
-
pre_convert_rgb(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
|
908
|
-
break;
|
909
|
-
case OIL_CS_RGBX:
|
910
|
-
pre_convert_rgbx(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
|
911
|
-
break;
|
912
|
-
case OIL_CS_RGBA:
|
913
|
-
pre_convert_rgba(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
|
914
|
-
break;
|
915
|
-
case OIL_CS_CMYK:
|
916
|
-
pre_convert_cmyk(in, pxs->xs.psl_pos0, pxs->width_in, pxs->scale_factor);
|
917
|
-
break;
|
918
|
-
}
|
919
|
-
xscaler_scale(&pxs->xs, out);
|
920
|
-
}
|
921
|
-
|
922
|
-
|
923
|
-
/* Utility helpers */
|
924
|
-
void fix_ratio(uint32_t src_width, uint32_t src_height, uint32_t *out_width,
|
925
|
-
uint32_t *out_height)
|
926
|
-
{
|
927
|
-
double width_ratio, height_ratio;
|
928
|
-
|
929
|
-
width_ratio = *out_width / (double)src_width;
|
930
|
-
height_ratio = *out_height / (double)src_height;
|
931
|
-
if (width_ratio < height_ratio) {
|
932
|
-
*out_height = round(width_ratio * src_height);
|
933
|
-
*out_height = *out_height ? *out_height : 1;
|
934
|
-
} else {
|
935
|
-
*out_width = round(height_ratio * src_width);
|
936
|
-
*out_width = *out_width ? *out_width : 1;
|
937
|
-
}
|
938
|
-
}
|