# frozen-string-literal: truemoduleChunkyPNGclassCanvas# The ChunkyPNG::Canvas::Resampling module defines methods to perform image resampling to# a {ChunkyPNG::Canvas}.## Currently, only the nearest neighbor algorithm is implemented. Bilinear and cubic# algorithms may be added later on.## @see ChunkyPNG::CanvasmoduleResampling# Integer Interpolation between two values## Used for generating indicies for interpolation (eg, nearest# neighbour).## @param [Integer] width The width of the source# @param [Integer] new_width The width of the destination# @return [Array<Integer>] An Array of Integer indiciesdefsteps(width,new_width)indicies,residues=steps_residues(width,new_width)foriin1..new_widthindicies[i-1]=(indicies[i-1]+(residues[i-1]+127)/255)endindiciesend# Fractional Interpolation between two values## Used for generating values for interpolation (eg, bilinear).# Produces both the indices and the interpolation factors (residues).## @param [Integer] width The width of the source# @param [Integer] new_width The width of the destination# @return [Array<Integer>, Array<Integer>] Two arrays of indicies and residuesdefsteps_residues(width,new_width)indicies=Array.new(new_width,nil)residues=Array.new(new_width,nil)# This works by accumulating the fractional error and# overflowing when necessary.# We use mixed number arithmetic with a denominator of# 2 * new_widthbase_step=width/new_widtherr_step=(width%new_width)<<1denominator=new_width<<1# Initial pixelindex=(width-new_width)/denominatorerr=(width-new_width)%denominatorforiin1..new_widthindicies[i-1]=indexresidues[i-1]=(255.0*err.to_f/denominator.to_f).roundindex+=base_steperr+=err_stepiferr>=denominatorindex+=1err-=denominatorendend[indicies,residues]end# Resamples the canvas using nearest neighbor interpolation.# @param [Integer] new_width The width of the resampled canvas.# @param [Integer] new_height The height of the resampled canvas.# @return [ChunkyPNG::Canvas] A new canvas instance with the resampled pixels.defresample_nearest_neighbor!(new_width,new_height)steps_x=steps(width,new_width)steps_y=steps(height,new_height)pixels=Array.new(new_width*new_height)i=0foryinsteps_yforxinsteps_xpixels[i]=get_pixel(x,y)i+=1endendreplace_canvas!(new_width.to_i,new_height.to_i,pixels)enddefresample_nearest_neighbor(new_width,new_height)dup.resample_nearest_neighbor!(new_width,new_height)end# Resamples the canvas with bilinear interpolation.# @param [Integer] new_width The width of the resampled canvas.# @param [Integer] new_height The height of the resampled canvas.# @return [ChunkyPNG::Canvas] A new canvas instance with the resampled pixels.defresample_bilinear!(new_width,new_height)index_x,interp_x=steps_residues(width,new_width)index_y,interp_y=steps_residues(height,new_height)pixels=Array.new(new_width*new_height)i=0foryin1..new_height# Clamp the indicies to the edges of the imagey1=[index_y[y-1],0].maxy2=[index_y[y-1]+1,height-1].miny_residue=interp_y[y-1]forxin1..new_width# Clamp the indicies to the edges of the imagex1=[index_x[x-1],0].maxx2=[index_x[x-1]+1,width-1].minx_residue=interp_x[x-1]pixel_11=get_pixel(x1,y1)pixel_21=get_pixel(x2,y1)pixel_12=get_pixel(x1,y2)pixel_22=get_pixel(x2,y2)# Interpolate by Rowpixel_top=ChunkyPNG::Color.interpolate_quick(pixel_21,pixel_11,x_residue)pixel_bot=ChunkyPNG::Color.interpolate_quick(pixel_22,pixel_12,x_residue)# Interpolate by Columnpixels[i]=ChunkyPNG::Color.interpolate_quick(pixel_bot,pixel_top,y_residue)i+=1endendreplace_canvas!(new_width.to_i,new_height.to_i,pixels)enddefresample_bilinear(new_width,new_height)dup.resample_bilinear!(new_width,new_height)endaliasresampleresample_nearest_neighboraliasresizeresampleendendend