Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Bilinear resampling #5

Merged
merged 4 commits into from

2 participants

@natemueller
Collaborator

No description provided.

@wvanbergen
Owner

Thanks for contributing. The build seems to be failing because of long overflows: https://travis-ci.org/wvanbergen/oily_png/builds/4809541

@natemueller
Collaborator

Hmm. Passes on my test machines but they're all 64-bit. I'll check it out.

@wvanbergen
Owner

One tidbit that may have caused this:

ChunkyPNG/OilyPNG use 32bit integers to store pixels. This would normally work fine on 32-bit machines, but a ruby Fixnum uses the first two bits for type information, so in practice a FIxnum only has 30 bits before it overflows to a Bignum.

So when you convert a long back into a Ruby type, it could be both a Fixnum or a Bignum. You may want to play around with ruby.h'sFIX2INT,NUM2INT,FIX2LONG, andNUM2LONG` macros to make it work.

(I didn't actually look at your code to see if this is the problem for you ;)

@natemueller natemueller Store pixels in uint32_t instead of long
Saves space and works on 32-bit systems
1c21a9a
@natemueller
Collaborator

Yea, that was it. Thanks for pointing me in the right direction.

@wvanbergen wvanbergen merged commit 4495023 into wvanbergen:master
@wvanbergen
Owner

Released as version 1.1.0. Thanks for contributing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 14, 2013
  1. @natemueller

    Bilinear resampling in C

    natemueller authored
  2. @natemueller

    Removed debugging printf

    natemueller authored
Commits on Feb 15, 2013
  1. @natemueller
  2. @natemueller

    Store pixels in uint32_t instead of long

    natemueller authored
    Saves space and works on 32-bit systems
This page is out of date. Refresh to see the latest.
View
21 ext/oily_png/color.c
@@ -13,7 +13,22 @@ PIXEL oily_png_compose_color(PIXEL fg, PIXEL bg) {
new_g = INT8_MULTIPLY(A_BYTE(fg), G_BYTE(fg)) + INT8_MULTIPLY(a_com, G_BYTE(bg));
new_b = INT8_MULTIPLY(A_BYTE(fg), B_BYTE(fg)) + INT8_MULTIPLY(a_com, B_BYTE(bg));
new_a = A_BYTE(fg) + a_com;
-
+
+ return BUILD_PIXEL(new_r, new_g, new_b, new_a);
+}
+
+PIXEL oily_png_color_interpolate_quick(PIXEL fg, PIXEL bg, int alpha) {
+ BYTE a_com, new_r, new_g, new_b, new_a;
+
+ if (alpha >= 255) return fg;
+ if (alpha <= 0) return bg;
+
+ a_com = 255 - alpha;
+ new_r = INT8_MULTIPLY(alpha, R_BYTE(fg)) + INT8_MULTIPLY(a_com, R_BYTE(bg));
+ new_g = INT8_MULTIPLY(alpha, G_BYTE(fg)) + INT8_MULTIPLY(a_com, G_BYTE(bg));
+ new_b = INT8_MULTIPLY(alpha, B_BYTE(fg)) + INT8_MULTIPLY(a_com, B_BYTE(bg));
+ new_a = INT8_MULTIPLY(alpha, A_BYTE(fg)) + INT8_MULTIPLY(a_com, A_BYTE(bg));
+
return BUILD_PIXEL(new_r, new_g, new_b, new_a);
}
@@ -23,12 +38,12 @@ VALUE oily_png_color_compose_quick(VALUE self, VALUE fg_color, VALUE bg_color) {
}
VALUE oily_png_color_r(VALUE self, VALUE value) {
- UNUSED_PARAMETER(self);
+ UNUSED_PARAMETER(self);
return INT2FIX(R_BYTE(NUM2UINT(value)));
}
VALUE oily_png_color_g(VALUE self, VALUE value) {
- UNUSED_PARAMETER(self);
+ UNUSED_PARAMETER(self);
return INT2FIX(G_BYTE(NUM2UINT(value)));
}
View
5 ext/oily_png/color.h
@@ -9,15 +9,16 @@
#define BUILD_PIXEL(r, g, b, a) (((PIXEL) (r) << 24) + ((PIXEL) (g) << 16) + ((PIXEL) (b) << 8) + (PIXEL) (a))
#define INT8_MULTIPLY(a, b) (((((a) * (b) + 0x80) >> 8) + ((a) * (b) + 0x80)) >> 8)
-/*
+/*
Ruby replacement method for color composition using alpha transparency.
-
+
This method should replace ChunkyPNG::Color.compose_quick
*/
VALUE oily_png_color_compose_quick(VALUE self, VALUE fg_color, VALUE bg_color);
/* Color composition using alpha transparency. */
PIXEL oily_png_compose_color(PIXEL fg, PIXEL bg);
+PIXEL oily_png_color_interpolate_quick(PIXEL fg, PIXEL bg, int alpha);
/* Accessors */
VALUE oily_png_color_r(VALUE self, VALUE pixel);
View
7 ext/oily_png/oily_png_ext.c
@@ -7,15 +7,16 @@ void Init_oily_png() {
rb_define_private_method(OilyPNG_Canvas, "steps_residues", oily_png_canvas_steps_residues, 2);
rb_define_private_method(OilyPNG_Canvas, "steps", oily_png_canvas_steps, 2);
rb_define_method(OilyPNG_Canvas, "resample_nearest_neighbor!", oily_png_canvas_resample_nearest_neighbor_bang, 2);
+ rb_define_method(OilyPNG_Canvas, "resample_bilinear!", oily_png_canvas_resample_bilinear_bang, 2);
// Setup decoding module
VALUE OilyPNG_PNGDecoding = rb_define_module_under(OilyPNG, "PNGDecoding");
rb_define_method(OilyPNG_PNGDecoding, "decode_png_image_pass", oily_png_decode_png_image_pass, 6);
-
+
// Setup encoding module
VALUE OilyPNG_PNGEncoding = rb_define_module_under(OilyPNG, "PNGEncoding");
rb_define_method(OilyPNG_PNGEncoding, "encode_png_image_pass_to_stream", oily_png_encode_png_image_pass_to_stream, 4);
-
+
// Setup Color module
VALUE OilyPNG_Color = rb_define_module_under(OilyPNG, "Color");
rb_define_method(OilyPNG_Color, "compose_quick", oily_png_color_compose_quick, 2);
@@ -23,7 +24,7 @@ void Init_oily_png() {
rb_define_method(OilyPNG_Color, "g", oily_png_color_g, 1);
rb_define_method(OilyPNG_Color, "b", oily_png_color_b, 1);
rb_define_method(OilyPNG_Color, "a", oily_png_color_a, 1);
-
+
// Setup Operations module
VALUE OilyPNG_Operations = rb_define_module_under(OilyPNG, "Operations");
rb_define_method(OilyPNG_Operations, "compose!", oily_png_compose_bang, -1);
View
70 ext/oily_png/resampling.c
@@ -75,10 +75,10 @@ VALUE oily_png_canvas_steps_residues(VALUE self, VALUE v_width, VALUE v_new_widt
long width = NUM2LONG(v_width);
long new_width = NUM2LONG(v_new_width);
-
+
VALUE ret_steps = rb_ary_new2(new_width);
VALUE ret_residues = rb_ary_new2(new_width);
-
+
long *steps = ALLOC_N(long, new_width);
long *residues = ALLOC_N(long, new_width);
@@ -91,7 +91,7 @@ VALUE oily_png_canvas_steps_residues(VALUE self, VALUE v_width, VALUE v_new_widt
rb_ary_store(ret_steps, i, LONG2FIX(steps[i]));
rb_ary_store(ret_residues, i, LONG2FIX(residues[i]));
}
-
+
/* This is an unprotected allocation; it will leak on exception.
* However, rb_ary_store should not generate one as we have
* pre-allocated the array.
@@ -116,7 +116,7 @@ VALUE oily_png_canvas_resample_nearest_neighbor_bang(VALUE self, VALUE v_new_wid
long new_height = NUM2LONG(v_new_height);
long self_width = NUM2LONG(rb_funcall(self, rb_intern("width"), 0));
- long self_height = NUM2LONG(rb_funcall(self, rb_intern("height"), 0));
+ long self_height = NUM2LONG(rb_funcall(self, rb_intern("height"), 0));
VALUE pixels = rb_ary_new2(new_width*new_height);
VALUE source = rb_iv_get(self, "@pixels");
@@ -150,4 +150,64 @@ VALUE oily_png_canvas_resample_nearest_neighbor_bang(VALUE self, VALUE v_new_wid
rb_iv_set(self, "@height", LONG2NUM(new_height));
return self;
-}
+}
+
+VALUE oily_png_canvas_resample_bilinear_bang(VALUE self, VALUE v_new_width, VALUE v_new_height) {
+ long new_width = NUM2LONG(v_new_width);
+ long new_height = NUM2LONG(v_new_height);
+
+ long self_width = NUM2LONG(rb_funcall(self, rb_intern("width"), 0));
+ long self_height = NUM2LONG(rb_funcall(self, rb_intern("height"), 0));
+
+ VALUE pixels = rb_ary_new2(new_width*new_height);
+ VALUE source = rb_iv_get(self, "@pixels");
+
+ long *index_x = ALLOC_N(long, new_width);
+ long *index_y = ALLOC_N(long, new_height);
+ long *interp_x = ALLOC_N(long, new_width);
+ long *interp_y = ALLOC_N(long, new_height);
+
+ oily_png_generate_steps_residues(self_width, new_width, index_x, interp_x);
+ oily_png_generate_steps_residues(self_height, new_height, index_y, interp_y);
+
+ long index = 0;
+ long x, y;
+ long y1, y2, x1, x2;
+ uint32_t y_residue, x_residue;
+ uint32_t pixel_11, pixel_21, pixel_12, pixel_22;
+ uint32_t pixel_top, pixel_bot;
+ for (y = 0; y < new_height; y++) {
+ y1 = index_y[y] < 0 ? 0 : index_y[y];
+ y2 = y1+1 >= self_height ? self_height-1 : y1+1;
+ y_residue = interp_y[y];
+
+ for (x = 0; x < new_width; x++) {
+ x1 = index_x[x] < 0 ? 0 : index_x[x];
+ x2 = x1+1 >= self_width ? self_height-1 : x1+1;
+ x_residue = interp_x[x];
+
+ pixel_11 = NUM2UINT(rb_ary_entry(source, y1*self_width + x1));
+ pixel_21 = NUM2UINT(rb_ary_entry(source, y1*self_width + x2));
+ pixel_12 = NUM2UINT(rb_ary_entry(source, y2*self_width + x1));
+ pixel_22 = NUM2UINT(rb_ary_entry(source, y2*self_width + x2));
+
+ pixel_top = oily_png_color_interpolate_quick(pixel_21, pixel_11, x_residue);
+ pixel_bot = oily_png_color_interpolate_quick(pixel_22, pixel_12, x_residue);
+
+ rb_ary_store(pixels, index++, UINT2NUM(oily_png_color_interpolate_quick(pixel_bot, pixel_top, y_residue)));
+ }
+ }
+
+ xfree(index_x);
+ xfree(index_y);
+ xfree(interp_x);
+ xfree(interp_y);
+ interp_x = NULL;
+ interp_y = NULL;
+
+ rb_iv_set(self, "@pixels", pixels);
+ rb_iv_set(self, "@width", LONG2NUM(new_width));
+ rb_iv_set(self, "@height", LONG2NUM(new_height));
+
+ return self;
+}
View
4 ext/oily_png/resampling.h
@@ -8,7 +8,7 @@ void oily_png_generate_steps_residues(long width, long new_width, long *steps, l
/*
* Generates the interpolation steps through two values.
- *
+ *
* Returns a Ruby Array
*/
VALUE oily_png_canvas_steps_residues(VALUE self, VALUE width, VALUE new_width);
@@ -20,4 +20,6 @@ VALUE oily_png_canvas_steps(VALUE self, VALUE width, VALUE new_width);
*/
VALUE oily_png_canvas_resample_nearest_neighbor_bang(VALUE self, VALUE new_width, VALUE new_height);
+VALUE oily_png_canvas_resample_bilinear_bang(VALUE self, VALUE new_width, VALUE new_height);
+
#endif
View
16 spec/resampling_spec.rb
@@ -25,15 +25,27 @@
end
it "should resample [0,1,2,3] to 99x45 as ChunkyPNG does" do
- ChunkyPNG::Canvas.new(2,2,[0,1,2,3]).resample_nearest_neighbor(99,45).should == OilyPNG::Canvas.new(2,2,[0,1,2,3]).resample_nearest_neighbor(99,45)
+ ChunkyPNG::Canvas.new(2,2,[0,1,2,3]).resample_nearest_neighbor(99,45).should == OilyPNG::Canvas.new(2,2,[0,1,2,3]).resample_nearest_neighbor(99,45)
end
it "should resample an image to 10x20 as ChunkyPNG does" do
@reference.resample_nearest_neighbor(10,20).should == OilyPNG::Canvas.from_canvas(@reference).resample_nearest_neighbor(10,20)
end
-
+
it "should resample an image to 11x19 as ChunkyPNG does" do
@reference.resample_nearest_neighbor(11,19).should == OilyPNG::Canvas.from_canvas(@reference).resample_nearest_neighbor(11,19)
end
end
+
+ describe '#resample_bilinear!' do
+ before(:all) { @reference = ChunkyPNG::Canvas.from_file(resource_file('nonsquare.png'))}
+
+ it "should resample an image to 10x20 as ChunkyPNG does" do
+ @reference.resample_bilinear(10,20).should == OilyPNG::Canvas.from_canvas(@reference).resample_bilinear(10,20)
+ end
+
+ it "should resample an image to 11x19 as ChunkyPNG does" do
+ @reference.resample_bilinear(11,19).should == OilyPNG::Canvas.from_canvas(@reference).resample_bilinear(11,19)
+ end
+ end
end
Something went wrong with that request. Please try again.