ext/png_stitcher/png_stitcher.c
// Adapted from OilyPng's methods for decoding and reencoding a PNG.
// See:
//
//   https://github.com/wvanbergen/oily_png/blob/v1.2.1/ext/oily_png/png_decoding.c#L318-L374
//   https://github.com/wvanbergen/oily_png/blob/v1.2.1/ext/oily_png/png_encoding.c#L242-L303
//
Adapted from OilyPng's methods for decoding and reencoding a PNG.
See.


Adapted from OilyPng ' s methods for decoding and reencoding a PNG . See .
VALUE method_stitch(VALUE self, VALUE datastreams, VALUE rb_width, VALUE rb_height) {
  long i;
  long length = RARRAY_LEN(datastreams);
  VALUE datastream, header, imagedata;
  long width = FIX2LONG(rb_width);
  long height = FIX2LONG(rb_height);
  long line_size = (8 + (width + 7)) >> 3;
  long part_height, pass_size, y, line_start, x;
  long p = 0;
  BYTE* pixels = ALLOC_N(BYTE, (width * height));
  for (i = 0; i < length; i++) {
    datastream = rb_ary_entry(datastreams, i);
    header = rb_funcall(datastream, rb_intern("header_chunk"), 0);
    imagedata = rb_funcall(datastream, rb_intern("imagedata"), 0);
    part_height = FIX2LONG(rb_funcall(header, rb_intern("height"), 0));
    pass_size = line_size * part_height;
    if (RSTRING_LEN(imagedata) < pass_size) {
      rb_raise(rb_eRuntimeError, "The length of the stream is too short to contain the image!");
    }
    BYTE* bytes = ALLOC_N(BYTE, pass_size);
    memcpy(bytes, RSTRING_PTR(imagedata), pass_size);
    for (y = 0; y < part_height; y++) {
      line_start = y * line_size;
      switch (bytes[line_start]) {
        case OILY_PNG_FILTER_NONE:    break;
        case OILY_PNG_FILTER_SUB:     oily_png_decode_filter_sub(     bytes, line_start, line_size, 1); break;
        case OILY_PNG_FILTER_UP:      oily_png_decode_filter_up(      bytes, line_start, line_size, 1); break;
        case OILY_PNG_FILTER_AVERAGE: oily_png_decode_filter_average( bytes, line_start, line_size, 1); break;
        case OILY_PNG_FILTER_PAETH:   oily_png_decode_filter_paeth(   bytes, line_start, line_size, 1); break;
        default: rb_raise(rb_eRuntimeError, "Filter type not supported: %d", bytes[line_start]);
      }
      bytes[line_start] = (BYTE) 0;
      for (x = 0; x < width; x++) {
        pixels[p] = oily_png_extract_1bit_element(bytes, line_start, x);
        p++;
      }
    }
    xfree(bytes);
  }
  pass_size = line_size * height;
  BYTE* bytes = ALLOC_N(BYTE, pass_size);
  for (y = height - 1; y >= 0; y--) {
    line_start = y * line_size;
    bytes[line_start] = (BYTE) 0; 
    encode_scanline_indexed_1bit(bytes + line_start + 1, pixels, y, width);
  }
  xfree(pixels);
  VALUE result = rb_str_new((char*) bytes, pass_size);
  xfree(bytes);
  return result;
}
VALUE method_stitch ( VALUE self , VALUE datastreams , VALUE rb_width , VALUE rb_height ) { long i ; long length = RARRAY_LEN ( datastreams ) ; VALUE datastream , header , imagedata ; long width = FIX2LONG ( rb_width ) ; long height = FIX2LONG ( rb_height ) ; long line_size = ( 8 + ( width + 7 ) ) >> 3 ; long part_height , pass_size , y , line_start , x ; long p = 0 ; BYTE * pixels = ALLOC_N ( BYTE , ( width * height ) ) ; for ( i = 0 ; i < length ; i ++ ) { datastream = rb_ary_entry ( datastreams , i ) ; header = rb_funcall ( datastream , rb_intern ( " " ) , 0 ) ; imagedata = rb_funcall ( datastream , rb_intern ( " " ) , 0 ) ; part_height = FIX2LONG ( rb_funcall ( header , rb_intern ( " " ) , 0 ) ) ; pass_size = line_size * part_height ; if ( RSTRING_LEN ( imagedata ) < pass_size ) { rb_raise ( rb_eRuntimeError , " " ) ; } BYTE * bytes = ALLOC_N ( BYTE , pass_size ) ; memcpy ( bytes , RSTRING_PTR ( imagedata ) , pass_size ) ; for ( y = 0 ; y < part_height ; y ++ ) { line_start = y * line_size ; switch ( bytes [ line_start ] ) { case OILY_PNG_FILTER_NONE : break ; case OILY_PNG_FILTER_SUB : oily_png_decode_filter_sub ( bytes , line_start , line_size , 1 ) ; break ; case OILY_PNG_FILTER_UP : oily_png_decode_filter_up ( bytes , line_start , line_size , 1 ) ; break ; case OILY_PNG_FILTER_AVERAGE : oily_png_decode_filter_average ( bytes , line_start , line_size , 1 ) ; break ; case OILY_PNG_FILTER_PAETH : oily_png_decode_filter_paeth ( bytes , line_start , line_size , 1 ) ; break ; default : rb_raise ( rb_eRuntimeError , " " , bytes [ line_start ] ) ; } bytes [ line_start ] = ( BYTE ) 0 ; for ( x = 0 ; x < width ; x ++ ) { pixels [ p ] = oily_png_extract_1bit_element ( bytes , line_start , x ) ; p ++ ; } } xfree ( bytes ) ; } pass_size = line_size * height ; BYTE * bytes = ALLOC_N ( BYTE , pass_size ) ; for ( y = height - 1 ; y >= 0 ; y -- ) { line_start = y * line_size ; bytes [ line_start ] = ( BYTE ) 0 ; encode_scanline_indexed_1bit ( bytes + line_start + 1 , pixels , y , width ) ; } xfree ( pixels ) ; VALUE result = rb_str_new ( ( char * ) bytes , pass_size ) ; xfree ( bytes ) ; return result ; }
Adapted from OilyPng's methods for decoding and reencoding a PNG.
Adapted from OilyPng ' s methods for decoding and reencoding a PNG .
// Make sure that the stream is large enough to contain our pass. // Copy the bytes for this pass from the stream to a separate location // so we can work on this byte array directly. // Apply filering to the line // Set the filter byte to 0 because the bytearray is now unfiltered. // Allocate memory for the byte array. // no filtering