#include "oily_png_ext.h" #include void oily_png_generate_steps_residues(long width, long new_width, long *steps, long *residues) { long base_step = width / new_width; long err_step = (width % new_width) << 1; long denominator = new_width << 1; long index; long err; /* We require an arithmetic modolus and rounding to the left of zero * This is standard Ruby behaviour (I hope!) but differs with how C/Java * typically handle integer division and modulo. But since we are workig * in mixed numbers, Ruby's convention is especially convienent */ if (width >= new_width) { index = (width - new_width) / denominator; err = (width - new_width) % denominator; } else { index = (width - new_width) / denominator - 1; err = denominator - ((new_width - width) % denominator); } long i; for (i=0; i < new_width; i++){ if (residues != NULL) { steps[i] = index; residues[i] = (long) round(255.0 * (float) err / (float) denominator); } else { /* If residues aren't requested, we round to the nearest pixel */ if (err < new_width) { steps[i] = index; } else { steps[i] = index + 1; } } index += base_step; err += err_step; if (err >= denominator) { index += 1; err -= denominator; } } } VALUE oily_png_canvas_steps(VALUE self, VALUE v_width, VALUE v_new_width) { long width = NUM2LONG(v_width); long new_width = NUM2LONG(v_new_width); long *steps = ALLOC_N(long, new_width); VALUE ret = rb_ary_new2(new_width); oily_png_generate_steps_residues(width, new_width, steps, NULL); long i; for (i=0; i < new_width; i++) { rb_ary_store(ret, i, LONG2FIX(steps[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. */ xfree(steps); steps = NULL; return ret; } VALUE oily_png_canvas_steps_residues(VALUE self, VALUE v_width, VALUE v_new_width) { 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); oily_png_generate_steps_residues(width, new_width, steps, residues); long i; for (i=0; i < new_width; i++) { 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. */ xfree(steps); steps = NULL; xfree(residues); residues = NULL; /* We return multiple values */ VALUE ret = rb_ary_new2(2); rb_ary_store(ret, 0, ret_steps); rb_ary_store(ret, 1, ret_residues); return ret; } VALUE oily_png_canvas_resample_nearest_neighbor_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 *steps_x = ALLOC_N(long, new_width); long *steps_y = ALLOC_N(long, new_height); oily_png_generate_steps_residues(self_width, new_width, steps_x, NULL); oily_png_generate_steps_residues(self_height, new_height, steps_y, NULL); long index = 0; long x, y; long src_index; for (y=0; y < new_height; y++) { for (x = 0; x < new_width; x++) { src_index = steps_y[y] * self_width + steps_x[x]; VALUE pixel = rb_ary_entry(source, src_index); rb_ary_store(pixels, index, pixel); index++; } } xfree(steps_x); steps_x = NULL; xfree(steps_y); steps_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; }