webp-ffi 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +1 -0
- data/.travis.yml +3 -1
- data/README.md +57 -1
- data/Rakefile +2 -0
- data/ext/webp_ffi/Rakefile +5 -2
- data/ext/webp_ffi/util.c +259 -1
- data/ext/webp_ffi/util.h +19 -2
- data/ext/webp_ffi/webp_ffi.c +137 -15
- data/ext/webp_ffi/webp_ffi.h +8 -1
- data/lib/webp_ffi.rb +1 -0
- data/lib/webp_ffi/c.rb +12 -14
- data/lib/webp_ffi/libc.rb +20 -0
- data/lib/webp_ffi/version.rb +1 -1
- data/lib/webp_ffi/webp_ffi.rb +10 -11
- data/spec/webp_ffi_spec.rb +29 -14
- data/webp-ffi.gemspec +0 -1
- metadata +5 -20
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -7,6 +7,27 @@ Ruby wrapper for libwebp.
|
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
|
10
|
+
### Requirements
|
11
|
+
|
12
|
+
First of all you should have install libraries: libpng, libjpeg and libwebp (for libwebp need libpng and libjpeg).
|
13
|
+
|
14
|
+
For ubuntu, debian:
|
15
|
+
|
16
|
+
sudo apt-get install libjpeg62-dev
|
17
|
+
sudo apt-get install libpng12-dev
|
18
|
+
|
19
|
+
For Mac OS:
|
20
|
+
|
21
|
+
sudo port install jpeg libpng
|
22
|
+
|
23
|
+
or:
|
24
|
+
|
25
|
+
brew install libjpg libpng
|
26
|
+
|
27
|
+
Next, you should [install libwebp](https://developers.google.com/speed/webp/docs/compiling).
|
28
|
+
|
29
|
+
### Final part
|
30
|
+
|
10
31
|
Add this line to your application's Gemfile:
|
11
32
|
|
12
33
|
gem 'webp-ffi'
|
@@ -21,7 +42,42 @@ Or install it yourself as:
|
|
21
42
|
|
22
43
|
## Usage
|
23
44
|
|
24
|
-
|
45
|
+
Basic info about libwebp (encoder and decoder versions):
|
46
|
+
|
47
|
+
$ irb
|
48
|
+
2.0.0p0 :001 > require 'webp_ffi'
|
49
|
+
=> true
|
50
|
+
2.0.0p0 :002 > WebpFfi.decoder_version
|
51
|
+
=> "0.2.0"
|
52
|
+
2.0.0p0 :003 > WebpFfi.encoder_version
|
53
|
+
=> "0.2.0"
|
54
|
+
|
55
|
+
Get size (width and height) from webp image:
|
56
|
+
|
57
|
+
2.0.0p0 :001 > require 'webp_ffi'
|
58
|
+
=> true
|
59
|
+
2.0.0p0 :006 > filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
|
60
|
+
2.0.0p0 :007 > WebpFfi.webp_size(data = File.open(filename, "rb").read)
|
61
|
+
=> [2000, 2353]
|
62
|
+
|
63
|
+
Decode webp image to png:
|
64
|
+
|
65
|
+
2.0.0p0 :008 > filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
|
66
|
+
=> "/Users/leo/programs/projects/webp-ffi/spec/factories/4.webp"
|
67
|
+
2.0.0p0 :009 > out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.png"))
|
68
|
+
=> "/Users/leo/programs/projects/webp-ffi/tmp/4.png"
|
69
|
+
2.0.0p0 :010 > WebpFfi.decode(filename, out_filename)
|
70
|
+
Saved file /Users/leo/programs/projects/webp-ffi/tmp/4.png
|
71
|
+
=> 0
|
72
|
+
|
73
|
+
Encode png image to webp:
|
74
|
+
|
75
|
+
2.0.0p0 :011 > filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.png"))
|
76
|
+
=> "/Users/leo/programs/projects/webp-ffi/spec/factories/4.png"
|
77
|
+
2.0.0p0 :012 > out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.webp"))
|
78
|
+
=> "/Users/leo/programs/projects/webp-ffi/tmp/4.webp"
|
79
|
+
2.0.0p0 :013 > WebpFfi.encode(filename, out_filename)
|
80
|
+
=> 0
|
25
81
|
|
26
82
|
## Contributing
|
27
83
|
|
data/Rakefile
CHANGED
@@ -16,6 +16,8 @@ namespace "ffi-compiler" do
|
|
16
16
|
c.have_func?('WebPDecoderConfig')
|
17
17
|
c.have_func?('WebPGetInfo')
|
18
18
|
c.have_library?('webp')
|
19
|
+
c.have_library?('png')
|
20
|
+
c.have_library?('jpeg')
|
19
21
|
c.cflags << "-arch x86_64" if c.platform.mac?
|
20
22
|
c.ldflags << "-arch x86_64" if c.platform.mac?
|
21
23
|
end
|
data/ext/webp_ffi/Rakefile
CHANGED
@@ -10,7 +10,10 @@ FFI::Compiler::CompileTask.new('webp_ffi') do |c|
|
|
10
10
|
c.have_func?('WebPDecoderConfig')
|
11
11
|
c.have_func?('WebPGetInfo')
|
12
12
|
c.have_library?('webp')
|
13
|
+
# libs
|
14
|
+
c.have_library?('png')
|
15
|
+
c.have_library?('jpeg')
|
13
16
|
# compiler flags
|
14
|
-
c.cflags << "-arch x86_64
|
15
|
-
c.ldflags << "-arch x86_64
|
17
|
+
c.cflags << "-arch x86_64" if c.platform.mac?
|
18
|
+
c.ldflags << "-arch x86_64" if c.platform.mac?
|
16
19
|
end
|
data/ext/webp_ffi/util.c
CHANGED
@@ -1,15 +1,273 @@
|
|
1
1
|
#include "./util.h"
|
2
|
+
#include <assert.h>
|
2
3
|
#include <stdio.h>
|
3
4
|
#include <stdlib.h>
|
5
|
+
#include <string.h>
|
6
|
+
|
7
|
+
#include <png.h>
|
8
|
+
|
9
|
+
#include <setjmp.h> // note: this must be included *after* png.h
|
10
|
+
#include <jpeglib.h>
|
11
|
+
|
12
|
+
#include "webp/decode.h"
|
13
|
+
#include "webp/encode.h"
|
4
14
|
|
5
15
|
#if defined(__cplusplus) || defined(c_plusplus)
|
6
16
|
extern "C" {
|
7
17
|
#endif
|
8
18
|
|
19
|
+
static int UtilReadYUV(FILE* in_file, WebPPicture* const pic) {
|
20
|
+
const int use_argb = pic->use_argb;
|
21
|
+
const int uv_width = (pic->width + 1) / 2;
|
22
|
+
const int uv_height = (pic->height + 1) / 2;
|
23
|
+
int y;
|
24
|
+
int ok = 0;
|
25
|
+
|
26
|
+
pic->use_argb = 0;
|
27
|
+
if (!WebPPictureAlloc(pic)) return ok;
|
28
|
+
|
29
|
+
for (y = 0; y < pic->height; ++y) {
|
30
|
+
if (fread(pic->y + y * pic->y_stride, pic->width, 1, in_file) != 1) {
|
31
|
+
goto End;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
for (y = 0; y < uv_height; ++y) {
|
35
|
+
if (fread(pic->u + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
36
|
+
goto End;
|
37
|
+
}
|
38
|
+
for (y = 0; y < uv_height; ++y) {
|
39
|
+
if (fread(pic->v + y * pic->uv_stride, uv_width, 1, in_file) != 1)
|
40
|
+
goto End;
|
41
|
+
}
|
42
|
+
ok = 1;
|
43
|
+
if (use_argb) ok = WebPPictureYUVAToARGB(pic);
|
44
|
+
|
45
|
+
End:
|
46
|
+
return ok;
|
47
|
+
}
|
48
|
+
|
49
|
+
static void PNGAPI error_function(png_structp png, png_const_charp dummy) {
|
50
|
+
(void)dummy; // remove variable-unused warning
|
51
|
+
longjmp(png_jmpbuf(png), 1);
|
52
|
+
}
|
53
|
+
|
54
|
+
static int UtilReadPNG(FILE* in_file, WebPPicture* const pic, int keep_alpha) {
|
55
|
+
png_structp png;
|
56
|
+
png_infop info;
|
57
|
+
int color_type, bit_depth, interlaced;
|
58
|
+
int has_alpha;
|
59
|
+
int num_passes;
|
60
|
+
int p;
|
61
|
+
int ok = 0;
|
62
|
+
png_uint_32 width, height, y;
|
63
|
+
int stride;
|
64
|
+
uint8_t* rgb = NULL;
|
65
|
+
|
66
|
+
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
|
67
|
+
if (png == NULL) {
|
68
|
+
goto End;
|
69
|
+
}
|
70
|
+
|
71
|
+
png_set_error_fn(png, 0, error_function, NULL);
|
72
|
+
if (setjmp(png_jmpbuf(png))) {
|
73
|
+
Error:
|
74
|
+
png_destroy_read_struct(&png, NULL, NULL);
|
75
|
+
free(rgb);
|
76
|
+
goto End;
|
77
|
+
}
|
78
|
+
|
79
|
+
info = png_create_info_struct(png);
|
80
|
+
if (info == NULL) goto Error;
|
81
|
+
|
82
|
+
png_init_io(png, in_file);
|
83
|
+
png_read_info(png, info);
|
84
|
+
if (!png_get_IHDR(png, info,
|
85
|
+
&width, &height, &bit_depth, &color_type, &interlaced,
|
86
|
+
NULL, NULL)) goto Error;
|
87
|
+
|
88
|
+
png_set_strip_16(png);
|
89
|
+
png_set_packing(png);
|
90
|
+
if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png);
|
91
|
+
if (color_type == PNG_COLOR_TYPE_GRAY ||
|
92
|
+
color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
93
|
+
if (bit_depth < 8) {
|
94
|
+
png_set_expand_gray_1_2_4_to_8(png);
|
95
|
+
}
|
96
|
+
png_set_gray_to_rgb(png);
|
97
|
+
}
|
98
|
+
if (png_get_valid(png, info, PNG_INFO_tRNS)) {
|
99
|
+
png_set_tRNS_to_alpha(png);
|
100
|
+
has_alpha = 1;
|
101
|
+
} else {
|
102
|
+
has_alpha = !!(color_type & PNG_COLOR_MASK_ALPHA);
|
103
|
+
}
|
104
|
+
|
105
|
+
if (!keep_alpha) {
|
106
|
+
png_set_strip_alpha(png);
|
107
|
+
has_alpha = 0;
|
108
|
+
}
|
109
|
+
|
110
|
+
num_passes = png_set_interlace_handling(png);
|
111
|
+
png_read_update_info(png, info);
|
112
|
+
stride = (has_alpha ? 4 : 3) * width * sizeof(*rgb);
|
113
|
+
rgb = (uint8_t*)malloc(stride * height);
|
114
|
+
if (rgb == NULL) goto Error;
|
115
|
+
for (p = 0; p < num_passes; ++p) {
|
116
|
+
for (y = 0; y < height; ++y) {
|
117
|
+
png_bytep row = rgb + y * stride;
|
118
|
+
png_read_rows(png, &row, NULL, 1);
|
119
|
+
}
|
120
|
+
}
|
121
|
+
png_read_end(png, info);
|
122
|
+
png_destroy_read_struct(&png, &info, NULL);
|
123
|
+
|
124
|
+
pic->width = width;
|
125
|
+
pic->height = height;
|
126
|
+
ok = has_alpha ? WebPPictureImportRGBA(pic, rgb, stride)
|
127
|
+
: WebPPictureImportRGB(pic, rgb, stride);
|
128
|
+
free(rgb);
|
129
|
+
|
130
|
+
if (ok && has_alpha && keep_alpha == 2) {
|
131
|
+
WebPCleanupTransparentArea(pic);
|
132
|
+
}
|
133
|
+
|
134
|
+
End:
|
135
|
+
return ok;
|
136
|
+
}
|
137
|
+
|
138
|
+
static int UtilWritePNG(FILE* out_file, const WebPDecBuffer* const buffer) {
|
139
|
+
const uint32_t width = buffer->width;
|
140
|
+
const uint32_t height = buffer->height;
|
141
|
+
unsigned char* const rgb = buffer->u.RGBA.rgba;
|
142
|
+
const int stride = buffer->u.RGBA.stride;
|
143
|
+
const int has_alpha = (buffer->colorspace == MODE_RGBA);
|
144
|
+
png_structp png;
|
145
|
+
png_infop info;
|
146
|
+
png_uint_32 y;
|
147
|
+
|
148
|
+
png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
149
|
+
NULL, error_function, NULL);
|
150
|
+
if (png == NULL) {
|
151
|
+
return 0;
|
152
|
+
}
|
153
|
+
info = png_create_info_struct(png);
|
154
|
+
if (info == NULL) {
|
155
|
+
png_destroy_write_struct(&png, NULL);
|
156
|
+
return 0;
|
157
|
+
}
|
158
|
+
if (setjmp(png_jmpbuf(png))) {
|
159
|
+
png_destroy_write_struct(&png, &info);
|
160
|
+
return 0;
|
161
|
+
}
|
162
|
+
png_init_io(png, out_file);
|
163
|
+
png_set_IHDR(png, info, width, height, 8,
|
164
|
+
has_alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB,
|
165
|
+
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
|
166
|
+
PNG_FILTER_TYPE_DEFAULT);
|
167
|
+
png_write_info(png, info);
|
168
|
+
for (y = 0; y < height; ++y) {
|
169
|
+
png_bytep row = rgb + y * stride;
|
170
|
+
png_write_rows(png, &row, 1);
|
171
|
+
}
|
172
|
+
png_write_end(png, info);
|
173
|
+
png_destroy_write_struct(&png, &info);
|
174
|
+
return 1;
|
175
|
+
}
|
176
|
+
|
177
|
+
static InputFileFormat GetImageType(FILE* in_file) {
|
178
|
+
InputFileFormat format = UNSUPPORTED;
|
179
|
+
unsigned int magic;
|
180
|
+
unsigned char buf[4];
|
181
|
+
|
182
|
+
if ((fread(&buf[0], 4, 1, in_file) != 1) ||
|
183
|
+
(fseek(in_file, 0, SEEK_SET) != 0)) {
|
184
|
+
return format;
|
185
|
+
}
|
186
|
+
|
187
|
+
magic = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
|
188
|
+
if (magic == 0x89504E47U) {
|
189
|
+
format = PNG_;
|
190
|
+
} else if (magic >= 0xFFD8FF00U && magic <= 0xFFD8FFFFU) {
|
191
|
+
format = JPEG_;
|
192
|
+
} else if (magic == 0x49492A00 || magic == 0x4D4D002A) {
|
193
|
+
format = TIFF_;
|
194
|
+
}
|
195
|
+
return format;
|
196
|
+
}
|
197
|
+
|
198
|
+
int UtilReadPicture(const char* const filename, WebPPicture* const pic,
|
199
|
+
int keep_alpha) {
|
200
|
+
int ok = 0;
|
201
|
+
FILE* in_file = fopen(filename, "rb");
|
202
|
+
if (in_file == NULL) {
|
203
|
+
fprintf(stderr, "Error! Cannot open input file '%s'\n", filename);
|
204
|
+
return ok;
|
205
|
+
}
|
206
|
+
|
207
|
+
if (pic->width == 0 || pic->height == 0) {
|
208
|
+
// If no size specified, try to decode it as PNG/JPEG (as appropriate).
|
209
|
+
const InputFileFormat format = GetImageType(in_file);
|
210
|
+
if (format == PNG_) {
|
211
|
+
ok = UtilReadPNG(in_file, pic, keep_alpha);
|
212
|
+
}
|
213
|
+
/*
|
214
|
+
} else if (format == JPEG_) {
|
215
|
+
ok = ReadJPEG(in_file, pic);
|
216
|
+
} else if (format == TIFF_) {
|
217
|
+
ok = ReadTIFF(filename, pic, keep_alpha);
|
218
|
+
}
|
219
|
+
*/
|
220
|
+
} else {
|
221
|
+
// If image size is specified, infer it as YUV format.
|
222
|
+
ok = UtilReadYUV(in_file, pic);
|
223
|
+
}
|
224
|
+
if (!ok) {
|
225
|
+
fprintf(stderr, "Error! Could not process file %s\n", filename);
|
226
|
+
}
|
227
|
+
|
228
|
+
fclose(in_file);
|
229
|
+
return ok;
|
230
|
+
}
|
231
|
+
|
232
|
+
void UtilSaveOutput(const WebPDecBuffer* const buffer,
|
233
|
+
OutputFileFormat format, const char* const out_file) {
|
234
|
+
FILE* fout = NULL;
|
235
|
+
int ok = 1;
|
236
|
+
|
237
|
+
fout = fopen(out_file, "wb");
|
238
|
+
if (!fout) {
|
239
|
+
fprintf(stderr, "Error opening output file %s\n", out_file);
|
240
|
+
return;
|
241
|
+
}
|
242
|
+
|
243
|
+
if (format == PNG) {
|
244
|
+
ok &= UtilWritePNG(fout, buffer);
|
245
|
+
}
|
246
|
+
/*
|
247
|
+
} else if (format == PAM) {
|
248
|
+
ok &= WritePPM(fout, buffer, 1);
|
249
|
+
} else if (format == PPM) {
|
250
|
+
ok &= WritePPM(fout, buffer, 0);
|
251
|
+
} else if (format == PGM) {
|
252
|
+
ok &= WritePGM(fout, buffer);
|
253
|
+
} else if (format == ALPHA_PLANE_ONLY) {
|
254
|
+
ok &= WriteAlphaPlane(fout, buffer);
|
255
|
+
}
|
256
|
+
*/
|
257
|
+
if (fout) {
|
258
|
+
fclose(fout);
|
259
|
+
}
|
260
|
+
if (ok) {
|
261
|
+
printf("Saved file %s\n", out_file);
|
262
|
+
} else {
|
263
|
+
fprintf(stderr, "Error writing file %s !!\n", out_file);
|
264
|
+
}
|
265
|
+
}
|
266
|
+
|
9
267
|
// -----------------------------------------------------------------------------
|
10
268
|
// File I/O
|
11
269
|
|
12
|
-
int
|
270
|
+
int UtilReadFile(const char* const file_name,
|
13
271
|
const uint8_t** data, size_t* data_size) {
|
14
272
|
int ok;
|
15
273
|
void* file_data;
|
data/ext/webp_ffi/util.h
CHANGED
@@ -1,13 +1,30 @@
|
|
1
|
-
#include "webp/
|
1
|
+
#include "webp/decode.h"
|
2
|
+
#include "webp/encode.h"
|
2
3
|
|
3
4
|
#if defined(__cplusplus) || defined(c_plusplus)
|
4
5
|
extern "C" {
|
5
6
|
#endif
|
6
7
|
|
8
|
+
typedef enum {
|
9
|
+
PNG = 0,
|
10
|
+
PAM,
|
11
|
+
PPM,
|
12
|
+
PGM,
|
13
|
+
ALPHA_PLANE_ONLY // this is for experimenting only
|
14
|
+
} OutputFileFormat;
|
15
|
+
|
16
|
+
typedef enum {
|
17
|
+
PNG_ = 0,
|
18
|
+
JPEG_,
|
19
|
+
TIFF_, // 'TIFF' clashes with libtiff
|
20
|
+
UNSUPPORTED
|
21
|
+
} InputFileFormat;
|
22
|
+
|
23
|
+
void UtilSaveOutput(const WebPDecBuffer* const buffer, OutputFileFormat format, const char* const out_file);
|
7
24
|
// Allocates storage for entire file 'file_name' and returns contents and size
|
8
25
|
// in 'data' and 'data_size'. Returns 1 on success, 0 otherwise. '*data' should
|
9
26
|
// be deleted using free().
|
10
|
-
int
|
27
|
+
int UtilReadFile(const char* const file_name,
|
11
28
|
const uint8_t** data, size_t* data_size);
|
12
29
|
|
13
30
|
#if defined(__cplusplus) || defined(c_plusplus)
|
data/ext/webp_ffi/webp_ffi.c
CHANGED
@@ -10,23 +10,24 @@
|
|
10
10
|
#include "./util.h"
|
11
11
|
#include "./webp_ffi.h"
|
12
12
|
|
13
|
-
#ifdef WEBP_HAVE_PNG
|
14
|
-
#include <png.h>
|
15
|
-
#endif
|
16
13
|
|
17
|
-
#
|
18
|
-
|
19
|
-
#include <jpeglib.h>
|
14
|
+
#if defined(__cplusplus) || defined(c_plusplus)
|
15
|
+
extern "C" {
|
20
16
|
#endif
|
21
17
|
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
static int EncodeWriter(const uint8_t* data, size_t data_size,
|
19
|
+
const WebPPicture* const pic) {
|
20
|
+
FILE* const out = (FILE*)pic->custom_ptr;
|
21
|
+
return data_size ? (fwrite(data, data_size, 1, out) == 1) : 1;
|
22
|
+
}
|
25
23
|
|
24
|
+
static void AllocExtraInfo(WebPPicture* const pic) {
|
25
|
+
const int mb_w = (pic->width + 15) / 16;
|
26
|
+
const int mb_h = (pic->height + 15) / 16;
|
27
|
+
pic->extra_info = (uint8_t*)malloc(mb_w * mb_h * sizeof(*pic->extra_info));
|
28
|
+
}
|
26
29
|
|
27
|
-
|
28
|
-
extern "C" {
|
29
|
-
#endif
|
30
|
+
// main functions
|
30
31
|
|
31
32
|
void decoder_version(char *version) {
|
32
33
|
int v = WebPGetDecoderVersion();
|
@@ -41,11 +42,132 @@ void encoder_version(char *version) {
|
|
41
42
|
}
|
42
43
|
|
43
44
|
int webp_get_info(const uint8_t* data, size_t data_size, int* width, int* height) {
|
44
|
-
|
45
|
+
if (WebPGetInfo(data, data_size, width, height) == 1) {
|
46
|
+
return 0;
|
47
|
+
} else {
|
48
|
+
return 1;
|
49
|
+
}
|
45
50
|
}
|
46
51
|
|
47
|
-
|
48
|
-
|
52
|
+
|
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
|
+
int webp_encode(const char *in_file, const char *out_file) {
|
118
|
+
int return_value = -1;
|
119
|
+
FILE *out = NULL;
|
120
|
+
int keep_alpha = 1;
|
121
|
+
WebPPicture picture;
|
122
|
+
WebPConfig config;
|
123
|
+
|
124
|
+
|
125
|
+
if (!WebPPictureInit(&picture) ||
|
126
|
+
!WebPConfigInit(&config)) {
|
127
|
+
fprintf(stderr, "Error! Version mismatch!\n");
|
128
|
+
return 1;
|
129
|
+
}
|
130
|
+
|
131
|
+
if (!WebPValidateConfig(&config)) {
|
132
|
+
fprintf(stderr, "Error! Invalid configuration.\n");
|
133
|
+
return_value = 2;
|
134
|
+
goto Error;
|
135
|
+
}
|
136
|
+
|
137
|
+
if (!UtilReadPicture(in_file, &picture, keep_alpha)) {
|
138
|
+
fprintf(stderr, "Error! Cannot read input picture file '%s'\n", in_file);
|
139
|
+
return_value = 3;
|
140
|
+
goto Error;
|
141
|
+
}
|
142
|
+
|
143
|
+
out = fopen(out_file, "wb");
|
144
|
+
if (out == NULL) {
|
145
|
+
fprintf(stderr, "Error! Cannot open output file '%s'\n", out_file);
|
146
|
+
return_value = 4;
|
147
|
+
goto Error;
|
148
|
+
}
|
149
|
+
picture.writer = EncodeWriter;
|
150
|
+
picture.custom_ptr = (void*)out;
|
151
|
+
|
152
|
+
if (picture.extra_info_type > 0) {
|
153
|
+
AllocExtraInfo(&picture);
|
154
|
+
}
|
155
|
+
|
156
|
+
if (!WebPEncode(&config, &picture)) {
|
157
|
+
fprintf(stderr, "Error! Cannot encode picture as WebP\n");
|
158
|
+
return_value = 5;
|
159
|
+
goto Error;
|
160
|
+
}
|
161
|
+
return_value = 0;
|
162
|
+
|
163
|
+
Error:
|
164
|
+
free(picture.extra_info);
|
165
|
+
WebPPictureFree(&picture);
|
166
|
+
if (out != NULL) {
|
167
|
+
fclose(out);
|
168
|
+
}
|
169
|
+
|
170
|
+
return return_value;
|
49
171
|
}
|
50
172
|
|
51
173
|
// test
|
data/ext/webp_ffi/webp_ffi.h
CHANGED
@@ -5,10 +5,17 @@
|
|
5
5
|
extern "C" {
|
6
6
|
#endif
|
7
7
|
|
8
|
+
typedef struct {
|
9
|
+
int lossless; // Lossless encoding (0=lossy(default), 1=lossless).
|
10
|
+
float quality; // between 0 (smallest file) and 100 (biggest)
|
11
|
+
int method; // quality/speed trade-off (0=fast, 6=slower-better)
|
12
|
+
} FfiWebpConfig;
|
13
|
+
|
8
14
|
void decoder_version(char *version);
|
9
15
|
void encoder_version(char *version);
|
10
16
|
int webp_get_info(const uint8_t* data, size_t data_size, int* width, int* height);
|
11
|
-
|
17
|
+
int webp_decode(const char *in_file, const char *out_file);
|
18
|
+
int webp_encode(const char *in_file, const char *out_file);
|
12
19
|
int test(int n);
|
13
20
|
|
14
21
|
#if defined(__cplusplus) || defined(c_plusplus)
|
data/lib/webp_ffi.rb
CHANGED
data/lib/webp_ffi/c.rb
CHANGED
@@ -1,26 +1,24 @@
|
|
1
1
|
module WebpFfi
|
2
2
|
module C
|
3
3
|
# enum
|
4
|
-
|
5
|
-
:
|
6
|
-
:
|
7
|
-
:
|
8
|
-
:
|
9
|
-
:vp8_status_suspended,
|
10
|
-
:vp8_status_user_abort,
|
11
|
-
:vp8_status_not_enought_data)
|
4
|
+
WebpImageHint = enum(:webp_hint_default, 0,
|
5
|
+
:webp_hint_picture,
|
6
|
+
:webp_hint_photo,
|
7
|
+
:webp_hint_graph,
|
8
|
+
:webp_hint_last)
|
12
9
|
# struct
|
13
|
-
class
|
14
|
-
layout :
|
15
|
-
:
|
16
|
-
:
|
10
|
+
class FfiWebpConfig < FFI::Struct
|
11
|
+
layout :lossless, :int,
|
12
|
+
:quality, :float,
|
13
|
+
:method, :int
|
17
14
|
end
|
18
|
-
|
15
|
+
|
19
16
|
# webp lib functions
|
20
17
|
attach_function :decoder_version, [:pointer], :void
|
21
18
|
attach_function :encoder_version, [:pointer], :void
|
22
19
|
attach_function :webp_get_info, [:pointer, :size_t, :pointer, :pointer], :int
|
23
|
-
attach_function :
|
20
|
+
attach_function :webp_decode, [:string, :string], :int
|
21
|
+
attach_function :webp_encode, [:string, :string], :int
|
24
22
|
|
25
23
|
attach_function :test, [:int], :int
|
26
24
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module WebpFfi
|
2
|
+
module LibC
|
3
|
+
extend FFI::Library
|
4
|
+
# figures out the correct libc for each platform including Windows
|
5
|
+
library = ffi_lib(FFI::Library::LIBC).first
|
6
|
+
|
7
|
+
# Size_t not working properly on Windows
|
8
|
+
find_type(:size_t) rescue typedef(:ulong, :size_t)
|
9
|
+
|
10
|
+
# memory allocators
|
11
|
+
attach_function :malloc, [:size_t], :pointer
|
12
|
+
attach_function :free, [:pointer], :void
|
13
|
+
|
14
|
+
# get a pointer to the free function
|
15
|
+
Free = library.find_symbol('free')
|
16
|
+
|
17
|
+
# memory movers
|
18
|
+
attach_function :memcpy, [:pointer, :pointer, :size_t], :pointer
|
19
|
+
end # module LibC
|
20
|
+
end
|
data/lib/webp_ffi/version.rb
CHANGED
data/lib/webp_ffi/webp_ffi.rb
CHANGED
@@ -18,12 +18,12 @@ module WebpFfi
|
|
18
18
|
# get webp image size
|
19
19
|
def webp_size(data)
|
20
20
|
return nil if data.nil?
|
21
|
+
size = data.respond_to?(:bytesize) ? data.bytesize : data.size
|
21
22
|
width_ptr = FFI::MemoryPointer.new(:int, 2)
|
22
23
|
height_ptr = FFI::MemoryPointer.new(:int, 2)
|
23
|
-
size = data.respond_to?(:bytesize) ? data.bytesize : data.size
|
24
24
|
memBuf = FFI::MemoryPointer.new(:char, size)
|
25
25
|
memBuf.put_bytes(0, data)
|
26
|
-
if C.webp_get_info(memBuf, size, width_ptr, height_ptr) ==
|
26
|
+
if C.webp_get_info(memBuf, size, width_ptr, height_ptr) == 0
|
27
27
|
[width_ptr.null? ? nil : width_ptr.read_int, height_ptr.null? ? nil : height_ptr.read_int]
|
28
28
|
else
|
29
29
|
raise InvalidImageFormatError, "invalid webp image"
|
@@ -31,15 +31,14 @@ module WebpFfi
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
pointer.null? ? nil : pointer.read_string()
|
34
|
+
# decode
|
35
|
+
def decode(input_file, output_file, options = {})
|
36
|
+
C.webp_decode(input_file, output_file)
|
37
|
+
end
|
38
|
+
|
39
|
+
# encode
|
40
|
+
def encode(input_file, output_file, options = {})
|
41
|
+
C.webp_encode(input_file, output_file)
|
43
42
|
end
|
44
43
|
|
45
44
|
end
|
data/spec/webp_ffi_spec.rb
CHANGED
@@ -2,29 +2,31 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe WebpFfi do
|
4
4
|
factories = {
|
5
|
-
webp: ["1
|
5
|
+
webp: ["1", "2", "3", "4", "5", "6"],
|
6
|
+
png: ["1", "2", "3", "4"],
|
7
|
+
jpg: ["5", "6"],
|
6
8
|
info: {
|
7
|
-
"1
|
9
|
+
"1" => {
|
8
10
|
size: [400, 301],
|
9
11
|
has_alpha: true
|
10
12
|
},
|
11
|
-
"2
|
13
|
+
"2" => {
|
12
14
|
size: [386, 395],
|
13
15
|
has_alpha: true
|
14
16
|
},
|
15
|
-
"3
|
17
|
+
"3" => {
|
16
18
|
size: [300, 300],
|
17
19
|
has_alpha: true
|
18
20
|
},
|
19
|
-
"4
|
21
|
+
"4" => {
|
20
22
|
size: [2000, 2353],
|
21
23
|
has_alpha: true
|
22
24
|
},
|
23
|
-
"5
|
25
|
+
"5" => {
|
24
26
|
size: [550, 368],
|
25
27
|
has_alpha: false
|
26
28
|
},
|
27
|
-
"6
|
29
|
+
"6" => {
|
28
30
|
size: [1024, 772],
|
29
31
|
has_alpha: false
|
30
32
|
}
|
@@ -49,7 +51,7 @@ describe WebpFfi do
|
|
49
51
|
context "webp_size" do
|
50
52
|
factories[:webp].each do |image|
|
51
53
|
it "#{image} image size == #{factories[:info][image][:size]}" do
|
52
|
-
filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}"))
|
54
|
+
filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
|
53
55
|
data = File.open(filename, "rb").read
|
54
56
|
info = WebpFfi.webp_size(data)
|
55
57
|
info.class.should == Array
|
@@ -64,13 +66,26 @@ describe WebpFfi do
|
|
64
66
|
end
|
65
67
|
end
|
66
68
|
|
67
|
-
context "
|
69
|
+
context "decode" do
|
68
70
|
factories[:webp].each do |image|
|
69
|
-
it "#{image}
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
it "#{image}.webp image" do
|
72
|
+
out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
|
73
|
+
Dir.mkdir(out_dir) unless File.exists?(out_dir)
|
74
|
+
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
|
75
|
+
out_filename = File.expand_path(File.join(out_dir, "#{image}.webp.png"))
|
76
|
+
WebpFfi.decode(in_filename, out_filename)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "encode" do
|
82
|
+
factories[:png].each do |image|
|
83
|
+
it "#{image}.png image" do
|
84
|
+
out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
|
85
|
+
Dir.mkdir(out_dir) unless File.exists?(out_dir)
|
86
|
+
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
|
87
|
+
out_filename = File.expand_path(File.join(out_dir, "#{image}.png.webp"))
|
88
|
+
WebpFfi.encode(in_filename, out_filename)
|
74
89
|
end
|
75
90
|
end
|
76
91
|
end
|
data/webp-ffi.gemspec
CHANGED
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.0
|
4
|
+
version: 0.1.0
|
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-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -75,22 +75,6 @@ dependencies:
|
|
75
75
|
- - '>='
|
76
76
|
- !ruby/object:Gem::Version
|
77
77
|
version: '0'
|
78
|
-
- !ruby/object:Gem::Dependency
|
79
|
-
name: vagrant
|
80
|
-
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
|
-
requirements:
|
83
|
-
- - '>='
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: '0'
|
86
|
-
type: :development
|
87
|
-
prerelease: false
|
88
|
-
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - '>='
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: '0'
|
94
78
|
description: Ruby wrapper for libwebp
|
95
79
|
email:
|
96
80
|
- leopard.not.a@gmail.com
|
@@ -115,6 +99,7 @@ files:
|
|
115
99
|
- lib/webp_ffi.rb
|
116
100
|
- lib/webp_ffi/c.rb
|
117
101
|
- lib/webp_ffi/error.rb
|
102
|
+
- lib/webp_ffi/libc.rb
|
118
103
|
- lib/webp_ffi/version.rb
|
119
104
|
- lib/webp_ffi/webp_ffi.rb
|
120
105
|
- spec/bindings.md
|
@@ -149,7 +134,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
149
134
|
version: '0'
|
150
135
|
segments:
|
151
136
|
- 0
|
152
|
-
hash:
|
137
|
+
hash: -112599722798268677
|
153
138
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
154
139
|
none: false
|
155
140
|
requirements:
|
@@ -158,7 +143,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
158
143
|
version: '0'
|
159
144
|
segments:
|
160
145
|
- 0
|
161
|
-
hash:
|
146
|
+
hash: -112599722798268677
|
162
147
|
requirements: []
|
163
148
|
rubyforge_project:
|
164
149
|
rubygems_version: 1.8.25
|