webp-ffi 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +1 -2
- data/CHANGELOG.md +12 -0
- data/Gemfile +1 -3
- data/README.md +38 -5
- data/Rakefile +1 -0
- data/ext/webp_ffi/util.c +4 -7
- data/ext/webp_ffi/webp_ffi.c +115 -80
- data/ext/webp_ffi/webp_ffi.h +28 -3
- data/lib/webp_ffi/c.rb +14 -2
- data/lib/webp_ffi/error.rb +12 -0
- data/lib/webp_ffi/options.rb +7 -4
- data/lib/webp_ffi/version.rb +1 -1
- data/lib/webp_ffi/webp_ffi.rb +22 -14
- data/spec/webp_ffi_spec.rb +36 -24
- data/webp-ffi.gemspec +1 -1
- metadata +21 -4
data/.travis.yml
CHANGED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -45,6 +45,8 @@ Or install it yourself as:
|
|
45
45
|
|
46
46
|
## Usage
|
47
47
|
|
48
|
+
### Encode end Decoder versions
|
49
|
+
|
48
50
|
Basic info about libwebp (encoder and decoder versions):
|
49
51
|
|
50
52
|
$ irb
|
@@ -54,6 +56,8 @@ Basic info about libwebp (encoder and decoder versions):
|
|
54
56
|
=> "0.2.0"
|
55
57
|
2.0.0p0 :003 > WebpFfi.encoder_version
|
56
58
|
=> "0.2.0"
|
59
|
+
|
60
|
+
### WebP size (width and height)
|
57
61
|
|
58
62
|
Get size (width and height) from webp image:
|
59
63
|
|
@@ -61,11 +65,7 @@ Get size (width and height) from webp image:
|
|
61
65
|
WebpFfi.webp_size(File.open(filename, "rb").read)
|
62
66
|
=> [2000, 2353]
|
63
67
|
|
64
|
-
|
65
|
-
|
66
|
-
filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
|
67
|
-
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.png"))
|
68
|
-
WebpFfi.decode(filename, out_filename)
|
68
|
+
### Encode png, jpg or tiff image to WebP image
|
69
69
|
|
70
70
|
Encode png, jpg or tiff image to webp:
|
71
71
|
|
@@ -77,6 +77,39 @@ Encode png, jpg or tiff image to webp (with options):
|
|
77
77
|
|
78
78
|
WebpFfi.encode(filename, out_filename, quality: 50, resize_w: 100, resize_h: 200)
|
79
79
|
WebpFfi.encode(filename, out_filename, quality: 75, crop_x: 0, cropt_y: 0, crop_w: 100, crop_h: 100)
|
80
|
+
|
81
|
+
Possible encode options:
|
82
|
+
|
83
|
+
* **lossless** (int) - Lossless encoding (0=lossy(default), 1=lossless)
|
84
|
+
* **quality** (float) - between 0 (smallest file) and 100 (biggest)
|
85
|
+
* **method** (int) - quality/speed trade-off (0=fast, 6=slower-better)
|
86
|
+
* **target\_size** (int) - if non-zero, set the desired target size in bytes. Takes precedence over the 'compression' parameter
|
87
|
+
* **target\_PSNR** (float) - if non-zero, specifies the minimal distortion to try to achieve. Takes precedence over target\_size
|
88
|
+
* **segments** (int) - maximum number of segments to use, in [1..4]
|
89
|
+
* **sns_strength** (int) - Spatial Noise Shaping. 0=off, 100=maximum
|
90
|
+
* **filter\_strength** (int) - range: [0 = off .. 100 = strongest]
|
91
|
+
* **filter\_sharpness** (int) - range: [0 = off .. 7 = least sharp]
|
92
|
+
* **filter\_type** (int) - filtering type: 0 = simple, 1 = strong (only used if filter\_strength > 0 or autofilter > 0)
|
93
|
+
* **autofilter** (int) - Auto adjust filter's strength [0 = off, 1 = on]
|
94
|
+
* **alpha\_compression** (int) - Algorithm for encoding the alpha plane (0 = none, 1 = compressed with WebP lossless). Default is 1
|
95
|
+
* **alpha\_filtering** (int) - Predictive filtering method for alpha plane. 0: none, 1: fast, 2: best. Default if 1
|
96
|
+
* **alpha\_quality** (int) - Between 0 (smallest size) and 100 (lossless). Default is 100
|
97
|
+
* **pass** (int) - number of entropy-analysis passes (in [1..10])
|
98
|
+
* **show\_compressed** (int) - if true, export the compressed picture back. In-loop filtering is not applied
|
99
|
+
* **preprocessing** (int) - preprocessing filter (0=none, 1=segment-smooth)
|
100
|
+
* **partitions** (int) - log2(number of token partitions) in [0..3]. Default is set to 0 for easier progressive decoding
|
101
|
+
* **partition\_limit** (int) - quality degradation allowed to fit the 512k limit on prediction modes coding (0: no degradation, 100: maximum possible degradation)
|
102
|
+
* **width** (int), **height** (int) - Input size (width x height) for YUV
|
103
|
+
* **crop\_x** (int), **crop\_y** (int), **crop\_w** (int), **crop\_h** (int) - crop picture with the given rectangle
|
104
|
+
* **resize\_w** (int), **resize\_h** (int) - resize picture (after any cropping)
|
105
|
+
|
106
|
+
### Decode WebP image to png image
|
107
|
+
|
108
|
+
Decode webp image to png:
|
109
|
+
|
110
|
+
filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
|
111
|
+
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.png"))
|
112
|
+
WebpFfi.decode(filename, out_filename)
|
80
113
|
|
81
114
|
## Contributing
|
82
115
|
|
data/Rakefile
CHANGED
data/ext/webp_ffi/util.c
CHANGED
@@ -267,7 +267,7 @@ static int UtilReadTIFF(const char* const filename,
|
|
267
267
|
int dircount = 1;
|
268
268
|
|
269
269
|
if (tif == NULL) {
|
270
|
-
fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename);
|
270
|
+
//fprintf(stderr, "Error! Cannot open TIFF file '%s'\n", filename);
|
271
271
|
return 0;
|
272
272
|
}
|
273
273
|
|
@@ -298,7 +298,7 @@ static int UtilReadTIFF(const char* const filename,
|
|
298
298
|
}
|
299
299
|
_TIFFfree(raster);
|
300
300
|
} else {
|
301
|
-
fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
|
301
|
+
//fprintf(stderr, "Error allocating TIFF RGBA memory!\n");
|
302
302
|
}
|
303
303
|
|
304
304
|
if (ok && keep_alpha == 2) {
|
@@ -335,7 +335,7 @@ int UtilReadPicture(const char* const filename, WebPPicture* const pic,
|
|
335
335
|
int ok = 0;
|
336
336
|
FILE* in_file = fopen(filename, "rb");
|
337
337
|
if (in_file == NULL) {
|
338
|
-
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
|
338
|
+
//fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
|
339
339
|
return ok;
|
340
340
|
}
|
341
341
|
|
@@ -353,9 +353,6 @@ int UtilReadPicture(const char* const filename, WebPPicture* const pic,
|
|
353
353
|
// If image size is specified, infer it as YUV format.
|
354
354
|
ok = UtilReadYUV(in_file, pic);
|
355
355
|
}
|
356
|
-
if (!ok) {
|
357
|
-
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
358
|
-
}
|
359
356
|
|
360
357
|
fclose(in_file);
|
361
358
|
return ok;
|
@@ -369,7 +366,7 @@ int UtilSaveOutput(const WebPDecBuffer* const buffer,
|
|
369
366
|
fout = fopen(out_file, "wb");
|
370
367
|
if (!fout) {
|
371
368
|
fprintf(stderr, "Error opening output file %s\n", out_file);
|
372
|
-
return;
|
369
|
+
return 0;
|
373
370
|
}
|
374
371
|
|
375
372
|
if (format == PNG) {
|
data/ext/webp_ffi/webp_ffi.c
CHANGED
@@ -51,69 +51,6 @@ int webp_get_info(const uint8_t* data, size_t data_size, int* width, int* height
|
|
51
51
|
|
52
52
|
|
53
53
|
|
54
|
-
int webp_decode(const char *in_file, const char *out_file) {
|
55
|
-
int return_value = -1;
|
56
|
-
WebPDecoderConfig config;
|
57
|
-
WebPDecBuffer* const output_buffer = &config.output;
|
58
|
-
WebPBitstreamFeatures* const bitstream = &config.input;
|
59
|
-
OutputFileFormat format = PNG;
|
60
|
-
|
61
|
-
if (!WebPInitDecoderConfig(&config)) {
|
62
|
-
fprintf(stderr, "Library version mismatch!\n");
|
63
|
-
return 1;
|
64
|
-
}
|
65
|
-
|
66
|
-
VP8StatusCode status = VP8_STATUS_OK;
|
67
|
-
size_t data_size = 0;
|
68
|
-
const uint8_t* data = NULL;
|
69
|
-
|
70
|
-
if (!UtilReadFile(in_file, &data, &data_size)) return -1;
|
71
|
-
|
72
|
-
status = WebPGetFeatures(data, data_size, bitstream);
|
73
|
-
if (status != VP8_STATUS_OK) {
|
74
|
-
fprintf(stderr, "This is invalid webp image!\n");
|
75
|
-
return_value = 2;
|
76
|
-
goto Error;
|
77
|
-
}
|
78
|
-
|
79
|
-
switch (format) {
|
80
|
-
case PNG:
|
81
|
-
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
|
82
|
-
break;
|
83
|
-
case PAM:
|
84
|
-
output_buffer->colorspace = MODE_RGBA;
|
85
|
-
break;
|
86
|
-
case PPM:
|
87
|
-
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
|
88
|
-
break;
|
89
|
-
case PGM:
|
90
|
-
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
|
91
|
-
break;
|
92
|
-
case ALPHA_PLANE_ONLY:
|
93
|
-
output_buffer->colorspace = MODE_YUVA;
|
94
|
-
break;
|
95
|
-
default:
|
96
|
-
free((void*)data);
|
97
|
-
return 3;
|
98
|
-
}
|
99
|
-
status = WebPDecode(data, data_size, &config);
|
100
|
-
|
101
|
-
if (status != VP8_STATUS_OK) {
|
102
|
-
fprintf(stderr, "Decoding of %s failed.\n", in_file);
|
103
|
-
return_value = 4;
|
104
|
-
goto Error;
|
105
|
-
}
|
106
|
-
UtilSaveOutput(output_buffer, format, out_file);
|
107
|
-
return_value = 0;
|
108
|
-
|
109
|
-
Error:
|
110
|
-
free((void*)data);
|
111
|
-
WebPFreeDecBuffer(output_buffer);
|
112
|
-
return return_value;
|
113
|
-
}
|
114
|
-
|
115
|
-
|
116
|
-
|
117
54
|
int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeConfig *encode_config) {
|
118
55
|
int return_value = -1;
|
119
56
|
FILE *out = NULL;
|
@@ -121,56 +58,90 @@ int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeCo
|
|
121
58
|
WebPPicture picture;
|
122
59
|
WebPConfig config;
|
123
60
|
// config
|
124
|
-
if (encode_config->lossless
|
61
|
+
if (encode_config->lossless == 0 || encode_config->lossless == 1){
|
125
62
|
config.lossless = encode_config->lossless;
|
126
63
|
}
|
127
|
-
if (encode_config->quality){
|
64
|
+
if (encode_config->quality >= 0 && encode_config->quality <= 100){
|
128
65
|
config.quality = encode_config->quality;
|
129
66
|
}
|
130
67
|
if (encode_config->method >= 0 && encode_config->method <= 6){
|
131
68
|
config.method = encode_config->method;
|
132
69
|
}
|
133
|
-
if (encode_config->
|
70
|
+
if (encode_config->target_size > 0){
|
71
|
+
config.target_size = encode_config->target_size;
|
72
|
+
}
|
73
|
+
if (encode_config->target_PSNR > 0){
|
74
|
+
config.target_PSNR = encode_config->target_PSNR;
|
75
|
+
}
|
76
|
+
if (encode_config->segments >= 0 && encode_config->segments <= 4){
|
134
77
|
config.segments = encode_config->segments;
|
135
78
|
}
|
136
|
-
if (encode_config->sns_strength){
|
79
|
+
if (encode_config->sns_strength >= 0 && encode_config->sns_strength <= 100){
|
137
80
|
config.sns_strength = encode_config->sns_strength;
|
138
81
|
}
|
139
|
-
if (encode_config->
|
140
|
-
config.
|
82
|
+
if (encode_config->filter_strength >= 0 && encode_config->filter_strength <= 100){
|
83
|
+
config.filter_strength = encode_config->filter_strength;
|
84
|
+
}
|
85
|
+
if (encode_config->filter_sharpness >= 0 && encode_config->filter_sharpness <= 7){
|
86
|
+
config.filter_sharpness = encode_config->filter_sharpness;
|
141
87
|
}
|
142
|
-
if (encode_config->
|
88
|
+
if (encode_config->filter_type == 0 || encode_config->filter_type == 1){
|
89
|
+
config.filter_type = encode_config->filter_type;
|
90
|
+
}
|
91
|
+
if (encode_config->autofilter == 0 || encode_config->autofilter == 1){
|
92
|
+
config.autofilter = encode_config->autofilter;
|
93
|
+
}
|
94
|
+
if (encode_config->alpha_compression == 0 || encode_config->alpha_compression == 1){
|
143
95
|
config.alpha_compression = encode_config->alpha_compression;
|
144
96
|
}
|
145
|
-
if (encode_config->alpha_filtering){
|
97
|
+
if (encode_config->alpha_filtering >= 0 && encode_config->alpha_filtering <= 2){
|
146
98
|
config.alpha_filtering = encode_config->alpha_filtering;
|
147
99
|
}
|
148
|
-
if (encode_config->
|
100
|
+
if (encode_config->alpha_quality >= 0 && encode_config->alpha_quality <= 100){
|
101
|
+
config.alpha_quality = encode_config->alpha_quality;
|
102
|
+
}
|
103
|
+
if (encode_config->pass >= 0 && encode_config->pass <= 10){
|
104
|
+
config.pass = encode_config->pass;
|
105
|
+
}
|
106
|
+
if (encode_config->show_compressed >= 0){
|
107
|
+
config.show_compressed = encode_config->show_compressed;
|
108
|
+
}
|
109
|
+
if (encode_config->preprocessing == 0 || encode_config->preprocessing == 1){
|
110
|
+
config.preprocessing = encode_config->preprocessing;
|
111
|
+
}
|
112
|
+
if (encode_config->partitions >= 0 && encode_config->partitions <= 3){
|
113
|
+
config.partitions = encode_config->partitions;
|
114
|
+
}
|
115
|
+
if (encode_config->partition_limit >= 0 && encode_config->partition_limit <= 100){
|
116
|
+
config.partition_limit = encode_config->partition_limit;
|
117
|
+
}
|
118
|
+
|
119
|
+
if ((encode_config->width | encode_config->height) > 0){
|
149
120
|
picture.width = encode_config->width;
|
150
121
|
picture.height = encode_config->height;
|
151
122
|
}
|
152
123
|
|
153
124
|
if (!WebPPictureInit(&picture) ||
|
154
125
|
!WebPConfigInit(&config)) {
|
155
|
-
fprintf(stderr, "Error! Version mismatch!\n");
|
126
|
+
//fprintf(stderr, "Error! Version mismatch!\n");
|
156
127
|
return 1;
|
157
128
|
}
|
158
129
|
|
159
130
|
if (!WebPValidateConfig(&config)) {
|
160
|
-
fprintf(stderr, "Error! Invalid configuration.\n");
|
131
|
+
//fprintf(stderr, "Error! Invalid configuration.\n");
|
161
132
|
return_value = 2;
|
162
133
|
goto Error;
|
163
134
|
}
|
164
135
|
|
165
136
|
if (!UtilReadPicture(in_file, &picture, keep_alpha)) {
|
166
|
-
fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
|
137
|
+
//fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
|
167
138
|
return_value = 3;
|
168
139
|
goto Error;
|
169
140
|
}
|
170
141
|
|
171
142
|
out = fopen(out_file, "wb");
|
172
143
|
if (out == NULL) {
|
173
|
-
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
|
144
|
+
//fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
|
174
145
|
return_value = 4;
|
175
146
|
goto Error;
|
176
147
|
}
|
@@ -179,7 +150,7 @@ int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeCo
|
|
179
150
|
|
180
151
|
if ((encode_config->crop_w | encode_config->crop_h) > 0){
|
181
152
|
if (!WebPPictureView(&picture, encode_config->crop_x, encode_config->crop_y, encode_config->crop_w, encode_config->crop_h, &picture)) {
|
182
|
-
fprintf(stderr, "Error! Cannot crop picture\n");
|
153
|
+
//fprintf(stderr, "Error! Cannot crop picture\n");
|
183
154
|
return_value = 5;
|
184
155
|
goto Error;
|
185
156
|
}
|
@@ -187,7 +158,7 @@ int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeCo
|
|
187
158
|
|
188
159
|
if ((encode_config->resize_w | encode_config->resize_h) > 0) {
|
189
160
|
if (!WebPPictureRescale(&picture, encode_config->resize_w, encode_config->resize_h)) {
|
190
|
-
fprintf(stderr, "Error! Cannot resize picture\n");
|
161
|
+
//fprintf(stderr, "Error! Cannot resize picture\n");
|
191
162
|
return_value = 6;
|
192
163
|
goto Error;
|
193
164
|
}
|
@@ -198,8 +169,8 @@ int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeCo
|
|
198
169
|
}
|
199
170
|
|
200
171
|
if (!WebPEncode(&config, &picture)) {
|
201
|
-
fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
202
|
-
return_value =
|
172
|
+
//fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
173
|
+
return_value = 7;
|
203
174
|
goto Error;
|
204
175
|
}
|
205
176
|
return_value = 0;
|
@@ -214,6 +185,70 @@ Error:
|
|
214
185
|
return return_value;
|
215
186
|
}
|
216
187
|
|
188
|
+
|
189
|
+
|
190
|
+
int webp_decode(const char *in_file, const char *out_file) {
|
191
|
+
int return_value = -1;
|
192
|
+
WebPDecoderConfig config;
|
193
|
+
WebPDecBuffer* const output_buffer = &config.output;
|
194
|
+
WebPBitstreamFeatures* const bitstream = &config.input;
|
195
|
+
OutputFileFormat format = PNG;
|
196
|
+
|
197
|
+
if (!WebPInitDecoderConfig(&config)) {
|
198
|
+
fprintf(stderr, "Library version mismatch!\n");
|
199
|
+
return 1;
|
200
|
+
}
|
201
|
+
|
202
|
+
VP8StatusCode status = VP8_STATUS_OK;
|
203
|
+
size_t data_size = 0;
|
204
|
+
const uint8_t* data = NULL;
|
205
|
+
|
206
|
+
if (!UtilReadFile(in_file, &data, &data_size)) return -1;
|
207
|
+
|
208
|
+
status = WebPGetFeatures(data, data_size, bitstream);
|
209
|
+
if (status != VP8_STATUS_OK) {
|
210
|
+
fprintf(stderr, "This is invalid webp image!\n");
|
211
|
+
return_value = 2;
|
212
|
+
goto Error;
|
213
|
+
}
|
214
|
+
|
215
|
+
switch (format) {
|
216
|
+
case PNG:
|
217
|
+
output_buffer->colorspace = bitstream->has_alpha ? MODE_RGBA : MODE_RGB;
|
218
|
+
break;
|
219
|
+
case PAM:
|
220
|
+
output_buffer->colorspace = MODE_RGBA;
|
221
|
+
break;
|
222
|
+
case PPM:
|
223
|
+
output_buffer->colorspace = MODE_RGB; // drops alpha for PPM
|
224
|
+
break;
|
225
|
+
case PGM:
|
226
|
+
output_buffer->colorspace = bitstream->has_alpha ? MODE_YUVA : MODE_YUV;
|
227
|
+
break;
|
228
|
+
case ALPHA_PLANE_ONLY:
|
229
|
+
output_buffer->colorspace = MODE_YUVA;
|
230
|
+
break;
|
231
|
+
default:
|
232
|
+
free((void*)data);
|
233
|
+
return 3;
|
234
|
+
}
|
235
|
+
status = WebPDecode(data, data_size, &config);
|
236
|
+
|
237
|
+
if (status != VP8_STATUS_OK) {
|
238
|
+
fprintf(stderr, "Decoding of %s failed.\n", in_file);
|
239
|
+
return_value = 4;
|
240
|
+
goto Error;
|
241
|
+
}
|
242
|
+
UtilSaveOutput(output_buffer, format, out_file);
|
243
|
+
return_value = 0;
|
244
|
+
|
245
|
+
Error:
|
246
|
+
free((void*)data);
|
247
|
+
WebPFreeDecBuffer(output_buffer);
|
248
|
+
return return_value;
|
249
|
+
}
|
250
|
+
|
251
|
+
|
217
252
|
// test
|
218
253
|
int test_c(int n) {
|
219
254
|
return n + 100;
|
data/ext/webp_ffi/webp_ffi.h
CHANGED
@@ -9,11 +9,35 @@ extern "C" {
|
|
9
9
|
int lossless; // Lossless encoding (0=lossy(default), 1=lossless).
|
10
10
|
float quality; // between 0 (smallest file) and 100 (biggest)
|
11
11
|
int method; // quality/speed trade-off (0=fast, 6=slower-better)
|
12
|
+
|
13
|
+
// Parameters related to lossy compression only:
|
14
|
+
int target_size; // if non-zero, set the desired target size in bytes.
|
15
|
+
// Takes precedence over the 'compression' parameter.
|
16
|
+
float target_PSNR; // if non-zero, specifies the minimal distortion to
|
17
|
+
// try to achieve. Takes precedence over target_size.
|
12
18
|
int segments; // maximum number of segments to use, in [1..4]
|
13
19
|
int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum.
|
14
|
-
int
|
15
|
-
int
|
20
|
+
int filter_strength; // range: [0 = off .. 100 = strongest]
|
21
|
+
int filter_sharpness; // range: [0 = off .. 7 = least sharp]
|
22
|
+
int filter_type; // filtering type: 0 = simple, 1 = strong (only used
|
23
|
+
// if filter_strength > 0 or autofilter > 0)
|
24
|
+
int autofilter; // Auto adjust filter's strength [0 = off, 1 = on]
|
25
|
+
int alpha_compression; // Algorithm for encoding the alpha plane (0 = none,
|
26
|
+
// 1 = compressed with WebP lossless). Default is 1.
|
16
27
|
int alpha_filtering; // Predictive filtering method for alpha plane.
|
28
|
+
// 0: none, 1: fast, 2: best. Default if 1.
|
29
|
+
int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
|
30
|
+
// Default is 100.
|
31
|
+
int pass; // number of entropy-analysis passes (in [1..10]).
|
32
|
+
|
33
|
+
int show_compressed; // if true, export the compressed picture back.
|
34
|
+
// In-loop filtering is not applied.
|
35
|
+
int preprocessing; // preprocessing filter (0=none, 1=segment-smooth)
|
36
|
+
int partitions; // log2(number of token partitions) in [0..3]
|
37
|
+
// Default is set to 0 for easier progressive decoding.
|
38
|
+
int partition_limit; // quality degradation allowed to fit the 512k limit on
|
39
|
+
// prediction modes coding (0: no degradation,
|
40
|
+
// 100: maximum possible degradation).
|
17
41
|
int width;
|
18
42
|
int height;
|
19
43
|
int crop_x;
|
@@ -23,12 +47,13 @@ extern "C" {
|
|
23
47
|
int resize_w;
|
24
48
|
int resize_h;
|
25
49
|
} FfiWebpEncodeConfig;
|
50
|
+
|
26
51
|
|
27
52
|
void decoder_version(char *version);
|
28
53
|
void encoder_version(char *version);
|
29
54
|
int webp_get_info(const uint8_t* data, size_t data_size, int* width, int* height);
|
30
|
-
int webp_decode(const char *in_file, const char *out_file);
|
31
55
|
int webp_encode(const char *in_file, const char *out_file, const FfiWebpEncodeConfig *encode_config);
|
56
|
+
int webp_decode(const char *in_file, const char *out_file);
|
32
57
|
int test_c(int n);
|
33
58
|
|
34
59
|
#if defined(__cplusplus) || defined(c_plusplus)
|
data/lib/webp_ffi/c.rb
CHANGED
@@ -5,11 +5,23 @@ module WebpFfi
|
|
5
5
|
layout :lossless, :int,
|
6
6
|
:quality, :float,
|
7
7
|
:method, :int,
|
8
|
+
:target_size, :int,
|
9
|
+
:target_PSNR, :float,
|
8
10
|
:segments, :int,
|
9
11
|
:sns_strength, :int,
|
10
|
-
:
|
12
|
+
:filter_strength, :int,
|
13
|
+
:filter_sharpness, :int,
|
14
|
+
:filter_type, :int,
|
15
|
+
:autofilter, :int,
|
11
16
|
:alpha_compression, :int,
|
12
17
|
:alpha_filtering, :int,
|
18
|
+
:alpha_quality, :int,
|
19
|
+
:pass, :int,
|
20
|
+
:show_compressed, :int,
|
21
|
+
:preprocessing, :int,
|
22
|
+
:partitions, :int,
|
23
|
+
:partition_limit, :int,
|
24
|
+
# image
|
13
25
|
:width, :int,
|
14
26
|
:height, :int,
|
15
27
|
:crop_x, :int,
|
@@ -24,8 +36,8 @@ module WebpFfi
|
|
24
36
|
attach_function :decoder_version, [:pointer], :void
|
25
37
|
attach_function :encoder_version, [:pointer], :void
|
26
38
|
attach_function :webp_get_info, [:pointer, :size_t, :pointer, :pointer], :int
|
27
|
-
attach_function :webp_decode, [:string, :string], :int
|
28
39
|
attach_function :webp_encode, [:string, :string, :pointer], :int
|
40
|
+
attach_function :webp_decode, [:string, :string], :int
|
29
41
|
|
30
42
|
attach_function :test_c, [:int], :int
|
31
43
|
end
|
data/lib/webp_ffi/error.rb
CHANGED
@@ -1,3 +1,15 @@
|
|
1
1
|
module WebpFfi
|
2
|
+
|
3
|
+
ENCODER_ERRORS = [
|
4
|
+
"Version mismatch",
|
5
|
+
"Invalid configuration",
|
6
|
+
"Cannot read input picture file",
|
7
|
+
"Cannot open output file",
|
8
|
+
"Cannot crop picture",
|
9
|
+
"Cannot resize picture",
|
10
|
+
"Cannot encode picture as WebP"]
|
11
|
+
|
2
12
|
class InvalidImageFormatError < StandardError; end
|
13
|
+
class EncoderError < StandardError; end
|
14
|
+
class DecoderError < StandardError; end
|
3
15
|
end
|
data/lib/webp_ffi/options.rb
CHANGED
@@ -14,10 +14,13 @@ module WebpFfi
|
|
14
14
|
options_struct[:crop_w] = options_struct[:crop_h] = 0
|
15
15
|
options_struct[:resize_w] = options_struct[:resize_h] = 0
|
16
16
|
# users
|
17
|
-
[:lossless, :quality, :method, :
|
18
|
-
:
|
19
|
-
:
|
20
|
-
|
17
|
+
[:lossless, :quality, :method, :target_size, :target_PSNR,
|
18
|
+
:segments, :sns_strength, :filter_strength, :filter_sharpness,
|
19
|
+
:filter_type, :autofilter, :alpha_compression, :alpha_filtering,
|
20
|
+
:alpha_quality, :pass, :show_compressed, :preprocessing, :partitions,
|
21
|
+
:partition_limit, :width, :height, :crop_x, :crop_y, :crop_w,
|
22
|
+
:crop_h, :resize_w, :resize_h].each do |key|
|
23
|
+
options_struct[key] = @user_options[key] if @user_options[key]
|
21
24
|
end
|
22
25
|
options_pointer
|
23
26
|
end
|
data/lib/webp_ffi/version.rb
CHANGED
data/lib/webp_ffi/webp_ffi.rb
CHANGED
@@ -19,28 +19,36 @@ module WebpFfi
|
|
19
19
|
# get webp image size
|
20
20
|
def webp_size(data)
|
21
21
|
return nil if data.nil?
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
memBuf = FFI::MemoryPointer.new(:char, size)
|
26
|
-
memBuf.put_bytes(0, data)
|
27
|
-
if C.webp_get_info(memBuf, size, width_ptr, height_ptr) == 0
|
28
|
-
[width_ptr.null? ? nil : width_ptr.read_int, height_ptr.null? ? nil : height_ptr.read_int]
|
22
|
+
pointers = get_pointers_for_webp_size(data)
|
23
|
+
if 0 == C.webp_get_info(pointers[:data], pointers[:data_size], pointers[:width], pointers[:height])
|
24
|
+
[(pointers[:width].null? ? nil : pointers[:width].read_int), (pointers[:height].null? ? nil : pointers[:height].read_int)]
|
29
25
|
else
|
30
|
-
raise InvalidImageFormatError, "invalid
|
31
|
-
return nil
|
26
|
+
raise InvalidImageFormatError, "invalid WebP image data"
|
32
27
|
end
|
33
28
|
end
|
34
29
|
|
30
|
+
# encode
|
31
|
+
def encode(input_file, output_file, options = {})
|
32
|
+
options_obj = Options.new options
|
33
|
+
res = C.webp_encode(input_file, output_file, options_obj.encode_pointer)
|
34
|
+
raise EncoderError, ENCODER_ERRORS[res - 1] unless 0 == res
|
35
|
+
return true
|
36
|
+
end
|
37
|
+
|
35
38
|
# decode
|
36
39
|
def decode(input_file, output_file, options = {})
|
37
|
-
C.webp_decode(input_file, output_file)
|
40
|
+
res = C.webp_decode(input_file, output_file)
|
41
|
+
return true
|
38
42
|
end
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
+
private
|
45
|
+
|
46
|
+
def get_pointers_for_webp_size(data)
|
47
|
+
pointers = Hash.new
|
48
|
+
pointers[:data_size] = data.respond_to?(:bytesize) ? data.bytesize : data.size
|
49
|
+
pointers[:width], pointers[:height] = FFI::MemoryPointer.new(:int, 2), FFI::MemoryPointer.new(:int, 2)
|
50
|
+
pointers[:data] = FFI::MemoryPointer.new(:char, pointers[:data_size]).put_bytes(0, data)
|
51
|
+
pointers
|
44
52
|
end
|
45
53
|
|
46
54
|
end
|
data/spec/webp_ffi_spec.rb
CHANGED
@@ -37,8 +37,13 @@ describe WebpFfi do
|
|
37
37
|
}
|
38
38
|
}
|
39
39
|
}
|
40
|
+
|
41
|
+
before :all do
|
42
|
+
@out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
|
43
|
+
Dir.mkdir(@out_dir) unless File.exists?(@out_dir)
|
44
|
+
end
|
40
45
|
|
41
|
-
it "calculate plus 100 by
|
46
|
+
it "calculate plus 100 by test_c (verify C)" do
|
42
47
|
WebpFfi::C.test_c(100).should == 200
|
43
48
|
WebpFfi::C.test_c(150).should == 250
|
44
49
|
end
|
@@ -71,51 +76,38 @@ describe WebpFfi do
|
|
71
76
|
end
|
72
77
|
end
|
73
78
|
|
74
|
-
context "decode" do
|
75
|
-
factories[:webp].each do |image|
|
76
|
-
it "#{image}.webp image" do
|
77
|
-
out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
|
78
|
-
Dir.mkdir(out_dir) unless File.exists?(out_dir)
|
79
|
-
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
|
80
|
-
out_filename = File.expand_path(File.join(out_dir, "#{image}.webp.png"))
|
81
|
-
WebpFfi.decode(in_filename, out_filename)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
79
|
context "encode" do
|
87
|
-
before :all do
|
88
|
-
@out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
|
89
|
-
Dir.mkdir(@out_dir) unless File.exists?(@out_dir)
|
90
|
-
end
|
91
80
|
factories[:png].each do |image|
|
92
81
|
it "#{image}.png image" do
|
93
82
|
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
|
94
83
|
out_filename = File.expand_path(File.join(@out_dir, "#{image}.png.webp"))
|
95
|
-
WebpFfi.encode(in_filename, out_filename)
|
84
|
+
WebpFfi.encode(in_filename, out_filename).should be_true
|
96
85
|
end
|
97
86
|
end
|
98
87
|
factories[:jpg].each do |image|
|
99
88
|
it "#{image}.jpg image" do
|
100
89
|
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.jpg"))
|
101
90
|
out_filename = File.expand_path(File.join(@out_dir, "#{image}.jpg.webp"))
|
102
|
-
WebpFfi.encode(in_filename, out_filename)
|
91
|
+
WebpFfi.encode(in_filename, out_filename).should be_true
|
103
92
|
end
|
104
93
|
end
|
105
94
|
factories[:tiff].each do |image|
|
106
95
|
it "#{image}.tif image" do
|
107
96
|
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.tif"))
|
108
97
|
out_filename = File.expand_path(File.join(@out_dir, "#{image}.tif.webp"))
|
109
|
-
WebpFfi.encode(in_filename, out_filename)
|
98
|
+
WebpFfi.encode(in_filename, out_filename).should be_true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
factories[:webp].each do |image|
|
102
|
+
it "raise EncoderError on #{image}.webp image" do
|
103
|
+
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
|
104
|
+
out_filename = File.expand_path(File.join(@out_dir, "#{image}invalid.webp.png"))
|
105
|
+
expect { WebpFfi.encode(in_filename, out_filename) }.to raise_error WebpFfi::EncoderError
|
110
106
|
end
|
111
107
|
end
|
112
108
|
end
|
113
109
|
|
114
110
|
context "encode with options" do
|
115
|
-
before :all do
|
116
|
-
@out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
|
117
|
-
Dir.mkdir(@out_dir) unless File.exists?(@out_dir)
|
118
|
-
end
|
119
111
|
factories[:png].each do |image|
|
120
112
|
it "#{image}.png image" do
|
121
113
|
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
|
@@ -124,5 +116,25 @@ describe WebpFfi do
|
|
124
116
|
end
|
125
117
|
end
|
126
118
|
end
|
119
|
+
|
120
|
+
context "raise EncoderError on invalid crop options" do
|
121
|
+
factories[:png].each do |image|
|
122
|
+
it "#{image}.png image" do
|
123
|
+
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
|
124
|
+
out_filename = File.expand_path(File.join(@out_dir, "#{image}.50png.webp"))
|
125
|
+
expect { WebpFfi.encode(in_filename, out_filename, crop_w: 30000) }.to raise_error WebpFfi::EncoderError
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "decode" do
|
131
|
+
factories[:webp].each do |image|
|
132
|
+
it "#{image}.webp image" do
|
133
|
+
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
|
134
|
+
out_filename = File.expand_path(File.join(@out_dir, "#{image}.webp.png"))
|
135
|
+
WebpFfi.decode(in_filename, out_filename).should be_true
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
127
139
|
|
128
140
|
end
|
data/webp-ffi.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.extensions << 'ext/webp_ffi/Rakefile'
|
21
21
|
|
22
22
|
spec.add_runtime_dependency "ffi", "~> 1.4.0"
|
23
|
-
|
23
|
+
spec.add_runtime_dependency "ffi-compiler", "~> 0.1.2"
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler", ">= 1.2"
|
26
26
|
spec.add_development_dependency "rake"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: webp-ffi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-03-
|
12
|
+
date: 2013-03-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 1.4.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: ffi-compiler
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.1.2
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.1.2
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: bundler
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,6 +102,7 @@ files:
|
|
86
102
|
- .gitignore
|
87
103
|
- .rvmrc
|
88
104
|
- .travis.yml
|
105
|
+
- CHANGELOG.md
|
89
106
|
- Gemfile
|
90
107
|
- LICENSE.txt
|
91
108
|
- README.md
|
@@ -136,7 +153,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
136
153
|
version: '0'
|
137
154
|
segments:
|
138
155
|
- 0
|
139
|
-
hash:
|
156
|
+
hash: 2298624109850622941
|
140
157
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
141
158
|
none: false
|
142
159
|
requirements:
|
@@ -145,7 +162,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
145
162
|
version: '0'
|
146
163
|
segments:
|
147
164
|
- 0
|
148
|
-
hash:
|
165
|
+
hash: 2298624109850622941
|
149
166
|
requirements: []
|
150
167
|
rubyforge_project:
|
151
168
|
rubygems_version: 1.8.25
|