webp-ffi 0.1.1 → 0.1.2
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.
- 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
|