Skip to content

Commit

Permalink
decode with options
Browse files Browse the repository at this point in the history
  • Loading branch information
le0pard committed Mar 13, 2013
1 parent b6d007a commit f45feba
Show file tree
Hide file tree
Showing 16 changed files with 183 additions and 54 deletions.
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ Get size (width and height) from webp image:
WebP.webp_size(File.open(filename, "rb").read)
=> [2000, 2353]

### Encode png, jpg or tiff image to WebP image
### Encode WebP image

Encode png, jpg or tiff image to webp:

filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.png"))
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.webp"))
WebP.encode(filename, out_filename)
Encode png, jpg or tiff image to webp (with options):
Encode png, jpg or tiff image to webp with options:

WebP.encode(filename, out_filename, quality: 50, resize_w: 100, resize_h: 200)
WebP.encode(filename, out_filename, quality: 75, crop_x: 0, cropt_y: 0, crop_w: 100, crop_h: 100)
Expand Down Expand Up @@ -103,13 +103,34 @@ Possible encode options:
* **crop\_x** (int), **crop\_y** (int), **crop\_w** (int), **crop\_h** (int) - crop picture with the given rectangle
* **resize\_w** (int), **resize\_h** (int) - resize picture (after any cropping)

### Decode WebP image to png image
### Decode WebP image

Decode webp image to png:
Decode webp image (default format is png):

filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.png"))
WebP.decode(filename, out_filename)

Decode webp image to pam, ppm or pgm format of image:

filename = File.expand_path(File.join(File.dirname(__FILE__), "spec/factories/4.webp"))
out_filename = File.expand_path(File.join(File.dirname(__FILE__), "tmp/4.png"))
WebP.decode(filename, out_filename, output_format: :pam)
WebP.decode(filename, out_filename, output_format: :ppm)
WebP.decode(filename, out_filename, output_format: :pgm)

Decode webp image with options:

WebP.encode(filename, out_filename, resize_w: 100, resize_h: 200)
WebP.encode(filename, out_filename, crop_x: 0, cropt_y: 0, crop_w: 100, crop_h: 100)

Possible decode options:

* **bypass\_filtering** (bool) - disable in-loop filtering
* **no\_fancy\_upsampling** (bool) - don't use the fancy YUV420 upscaler
* **use\_threads** (bool) - use multi-threading
* **crop\_x** (int), **crop\_y** (int), **crop\_w** (int), **crop\_h** (int) - crop picture with the given rectangle
* **resize\_w** (int), **resize\_h** (int) - resize picture (after any cropping)

## Contributing

Expand Down
54 changes: 49 additions & 5 deletions ext/webp_ffi/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,52 @@ static int UtilWritePPM(FILE* fout, const WebPDecBuffer* const buffer, int alpha
return 1;
}

static int UtilWriteAlphaPlane(FILE* fout, const WebPDecBuffer* const buffer) {
const uint32_t width = buffer->width;
const uint32_t height = buffer->height;
const unsigned char* const a = buffer->u.YUVA.a;
const int a_stride = buffer->u.YUVA.a_stride;
uint32_t y;
assert(a != NULL);
fprintf(fout, "P5\n%d %d\n255\n", width, height);
for (y = 0; y < height; ++y) {
if (fwrite(a + y * a_stride, width, 1, fout) != 1) {
return 0;
}
}
return 1;
}

static int UtilWritePGM(FILE* fout, const WebPDecBuffer* const buffer) {
const int width = buffer->width;
const int height = buffer->height;
const WebPYUVABuffer* const yuv = &buffer->u.YUVA;
// Save a grayscale PGM file using the IMC4 layout
// (http://www.fourcc.org/yuv.php#IMC4). This is a very
// convenient format for viewing the samples, esp. for
// odd dimensions.
int ok = 1;
int y;
const int uv_width = (width + 1) / 2;
const int uv_height = (height + 1) / 2;
const int out_stride = (width + 1) & ~1;
const int a_height = yuv->a ? height : 0;
fprintf(fout, "P5\n%d %d\n255\n", out_stride, height + uv_height + a_height);
for (y = 0; ok && y < height; ++y) {
ok &= (fwrite(yuv->y + y * yuv->y_stride, width, 1, fout) == 1);
if (width & 1) fputc(0, fout); // padding byte
}
for (y = 0; ok && y < uv_height; ++y) {
ok &= (fwrite(yuv->u + y * yuv->u_stride, uv_width, 1, fout) == 1);
ok &= (fwrite(yuv->v + y * yuv->v_stride, uv_width, 1, fout) == 1);
}
for (y = 0; ok && y < a_height; ++y) {
ok &= (fwrite(yuv->a + y * yuv->a_stride, width, 1, fout) == 1);
if (width & 1) fputc(0, fout); // padding byte
}
return ok;
}

static int UtilReadTIFF(const char* const filename,
WebPPicture* const pic, int keep_alpha) {
TIFF* const tif = TIFFOpen(filename, "r");
Expand Down Expand Up @@ -397,14 +443,12 @@ int UtilSaveOutput(const WebPDecBuffer* const buffer,
ok &= UtilWritePPM(fout, buffer, 1);
} else if (format == PPM) {
ok &= UtilWritePPM(fout, buffer, 0);
}
/*
} else if (format == PGM) {
ok &= WritePGM(fout, buffer);
ok &= UtilWritePGM(fout, buffer);
} else if (format == ALPHA_PLANE_ONLY) {
ok &= WriteAlphaPlane(fout, buffer);
ok &= UtilWriteAlphaPlane(fout, buffer);
}
*/

if (fout) {
fclose(fout);
}
Expand Down
27 changes: 24 additions & 3 deletions ext/webp_ffi/webp_ffi.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,13 +195,34 @@ int webp_decode(const char *in_file, const char *out_file, const FfiWebpDecodeCo
OutputFileFormat format = PNG;

if (!WebPInitDecoderConfig(&config)) {
fprintf(stderr, "Library version mismatch!\n");
//fprintf(stderr, "Library version mismatch!\n");
return 1;
}

if (decode_config->output_format != format){
format = decode_config->output_format;
}
if (decode_config->no_fancy_upsampling > 0){
config.options.no_fancy_upsampling = 1;
}
if (decode_config->bypass_filtering > 0){
config.options.bypass_filtering = 1;
}
if (decode_config->use_threads > 0){
config.options.use_threads = 1;
}
if ((decode_config->crop_w | decode_config->crop_h) > 0){
config.options.use_cropping = 1;
config.options.crop_left = decode_config->crop_x;
config.options.crop_top = decode_config->crop_y;
config.options.crop_width = decode_config->crop_w;
config.options.crop_height = decode_config->crop_h;
}
if ((decode_config->resize_w | decode_config->resize_h) > 0){
config.options.use_scaling = 1;
config.options.scaled_width = decode_config->resize_w;
config.options.scaled_height = decode_config->resize_h;
}

VP8StatusCode status = VP8_STATUS_OK;
size_t data_size = 0;
Expand All @@ -211,7 +232,7 @@ int webp_decode(const char *in_file, const char *out_file, const FfiWebpDecodeCo

status = WebPGetFeatures(data, data_size, bitstream);
if (status != VP8_STATUS_OK) {
fprintf(stderr, "This is invalid webp image!\n");
//fprintf(stderr, "This is invalid webp image!\n");
return_value = 2;
goto Error;
}
Expand Down Expand Up @@ -239,7 +260,7 @@ int webp_decode(const char *in_file, const char *out_file, const FfiWebpDecodeCo
status = WebPDecode(data, data_size, &config);

if (status != VP8_STATUS_OK) {
fprintf(stderr, "Decoding of %s failed.\n", in_file);
//fprintf(stderr, "Decoding of %s failed.\n", in_file);
return_value = 4;
goto Error;
}
Expand Down
5 changes: 4 additions & 1 deletion lib/webp/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ module WebP
"Cannot encode picture as WebP"]

DECODER_ERRORS = [
""]
"Version mismatch",
"Invalid webp image",
"Invalid output format",
"Decoding failed"]

class InvalidImageFormatError < StandardError; end
class EncoderError < StandardError; end
Expand Down
19 changes: 16 additions & 3 deletions lib/webp/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,33 @@ def decode_pointer
if @user_options[:output_format] && [:png, :pam, :ppm, :pgm, :alpha_plane_only].include?(@user_options[:output_format])
options_struct[:output_format] = C::OutputFileFormat[@user_options[:output_format]]
end
[:bypass_filtering, :no_fancy_upsampling, :use_threads].each do |key|
options_struct[key] = 1 if @user_options[key] && true == @user_options[key]
end
[:crop_x, :crop_y, :crop_w,
:crop_h, :resize_w, :resize_h].each do |key|
options_struct[key] = @user_options[key] if @user_options[key]
end
options_pointer
end

private

def encode_default(options_struct)
options_struct[:quality] = 100
options_struct[:crop_x] = options_struct[:crop_y] = 0
options_struct[:crop_w] = options_struct[:crop_h] = 0
options_struct[:resize_w] = options_struct[:resize_h] = 0
similar_default(options_struct)
end

def decode_default(options_struct)
# default format is png
options_struct[:output_format] = C::OutputFileFormat[:png]
similar_default(options_struct)
end

def similar_default(options_struct)
options_struct[:crop_x] = options_struct[:crop_y] = 0
options_struct[:crop_w] = options_struct[:crop_h] = 0
options_struct[:resize_w] = options_struct[:resize_h] = 0
end

end
Expand Down
File renamed without changes
Binary file removed spec/factories/3.png
Binary file not shown.
Binary file modified spec/factories/3.webp
Binary file not shown.
File renamed without changes
Binary file removed spec/factories/4.png
Binary file not shown.
Binary file modified spec/factories/4.webp
Binary file not shown.
File renamed without changes.
Binary file modified spec/factories/5.webp
Binary file not shown.
Binary file removed spec/factories/6.webp
Binary file not shown.
Binary file removed spec/factories/7.webp
Binary file not shown.
103 changes: 65 additions & 38 deletions spec/webp_ffi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

describe WebP do
factories = {
webp: ["1", "2", "3", "4", "5", "6", "7"],
png: ["1", "2", "3", "4"],
jpg: ["5", "6"],
tiff: ["7"],
webp: ["1", "2", "3", "4", "5"],
png: ["1", "2"],
jpg: ["3", "4"],
tiff: ["5"],
info: {
"1" => {
size: [400, 301],
Expand All @@ -16,22 +16,14 @@
has_alpha: true
},
"3" => {
size: [300, 300],
has_alpha: true
},
"4" => {
size: [2000, 2353],
has_alpha: true
},
"5" => {
size: [550, 368],
has_alpha: false
},
"6" => {
"4" => {
size: [1024, 772],
has_alpha: false
},
"7" => {
"5" => {
size: [1419, 1001],
has_alpha: false
}
Expand All @@ -42,6 +34,12 @@
@out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
Dir.mkdir(@out_dir) unless File.exists?(@out_dir)
end
after :all do
@out_dir = File.expand_path(File.join(File.dirname(__FILE__), "../tmp/"))
#Dir["#{@out_dir}/*{.png,.webp}"].each do |file|
# File.delete(file) rescue nil
#end
end

it "calculate plus 100 by test_c (verify C)" do
WebP::C.test_c(100).should == 200
Expand Down Expand Up @@ -105,24 +103,22 @@
expect { WebP.encode(in_filename, out_filename) }.to raise_error WebP::EncoderError
end
end
end

context "encode with options" do
factories[:png].each do |image|
it "#{image}.png image" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.50png.webp"))
WebP.encode(in_filename, out_filename, quality: 50, method: 0, alpha_quality: 10, alpha_compression: 1)
context "with options" do
factories[:png].each do |image|
it "#{image}.png image" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.50png.webp"))
WebP.encode(in_filename, out_filename, quality: 50, method: 0, alpha_quality: 10, alpha_compression: 1)
end
end
end
end

context "raise EncoderError on invalid crop options" do
factories[:png].each do |image|
it "#{image}.png image" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.invpng.webp"))
expect { WebP.encode(in_filename, out_filename, crop_w: 30000) }.to raise_error WebP::EncoderError
context "raise EncoderError on invalid crop options" do
factories[:png].each do |image|
it "#{image}.png image" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.invpng.webp"))
expect { WebP.encode(in_filename, out_filename, crop_w: 30000) }.to raise_error WebP::EncoderError
end
end
end
end
Expand All @@ -135,15 +131,46 @@
WebP.decode(in_filename, out_filename).should be_true
end
end
end

context "decode with output_format" do
[:pam, :ppm].each do |output_format|
factories[:webp].each do |image|
it "#{image}.webp image to #{output_format}" do
context "with output_format" do
[:png, :pam, :ppm, :pgm, :alpha_plane_only].each do |output_format|
factories[:webp].take(2).each do |image|
it "#{image}.webp image to #{output_format}" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.#{output_format}.png"))
WebP.decode(in_filename, out_filename, output_format: output_format).should be_true
end
end
end
end
context "with options" do
factories[:webp].take(2).each do |image|
it "#{image}.webp image to png and crop" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.#{output_format}.png"))
WebP.decode(in_filename, out_filename, output_format: output_format).should be_true
out_filename = File.expand_path(File.join(@out_dir, "#{image}_crop.png"))
WebP.decode(in_filename, out_filename, crop_w: 200, crop_h: 200).should be_true
end
it "#{image}.webp image to png and scale" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.webp"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}_resize.png"))
WebP.decode(in_filename, out_filename, resize_w: 200, resize_h: 200).should be_true
end
end
end
context "raise DecoderError on invalid webp image" do
factories[:png].each do |image|
it "#{image}.png image" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.invpng.webp"))
expect { WebP.decode(in_filename, out_filename) }.to raise_error WebP::DecoderError
end
end
end
context "raise DecoderError on invalid options" do
factories[:png].each do |image|
it "#{image}.png image" do
in_filename = File.expand_path(File.join(File.dirname(__FILE__), "factories/#{image}.png"))
out_filename = File.expand_path(File.join(@out_dir, "#{image}.invpng.webp"))
expect { WebP.decode(in_filename, out_filename, crop_w: 30000) }.to raise_error WebP::DecoderError
end
end
end
Expand Down

0 comments on commit f45feba

Please sign in to comment.