moduleChunkyPNGclassCanvasmoduleOperationsdefcompose(new_foreground,dx=0,dy=0)check_size_constraints!(new_foreground,dx,dy)foryin0...new_foreground.heightdoforxin0...new_foreground.widthdoself[x+dx,y+dy]=ChunkyPNG::Color.compose(new_foreground[x,y],self[x+dx,y+dy])endendselfenddefreplace(other,offset_x=0,offset_y=0)check_size_constraints!(other,offset_x,offset_y)foryin0...other.heightdopixels[(y+offset_y)*width+offset_x,other.width]=other.pixels[y*other.width,other.width]endselfenddefcrop(x,y,crop_width,crop_height)new_pixels=[]forcyin0...crop_heightdonew_pixels+=pixels.slice((cy+y)*width+x,crop_width)endChunkyPNG::Canvas.new(crop_width,crop_height,new_pixels)enddefchange_theme_color!(old_theme_color,new_theme_color,bg_color=ChunkyPNG::Color::WHITE,tolerance=5)base,mask=extract_mask(old_theme_color,bg_color,tolerance)mask.change_mask_color!(new_theme_color)self.replace(base.compose(mask))enddefextract_mask(mask_color,bg_color,tolerance=5)base_pixels=[]mask_pixels=[]pixels.eachdo|pixel|ifChunkyPNG::Color.alpha_decomposable?(pixel,mask_color,bg_color,tolerance)mask_pixels<<ChunkyPNG::Color.decompose_color(pixel,mask_color,bg_color,tolerance)base_pixels<<bg_colorelsemask_pixels<<(mask_color&0xffffff00)base_pixels<<pixelendend[self.class.new(width,height,base_pixels),self.class.new(width,height,mask_pixels)]enddefchange_mask_color!(new_color)raise"This is not a mask image!"ifpalette.opaque_palette.size!=1pixels.map!{|pixel|(new_color&0xffffff00)|ChunkyPNG::Color.a(pixel)}endprotecteddefcheck_size_constraints!(other,offset_x,offset_y)raise"Background image width is too small!"ifwidth<other.width+offset_xraise"Background image height is too small!"ifheight<other.height+offset_yendendendend