module ChunkyPNG::Canvas::Resampling

def resample_bilinear(new_width, new_height)

def resample_bilinear(new_width, new_height)
  dup.resample_bilinear!(new_width, new_height)
end

def resample_bilinear!(new_width, new_height)

Returns:
  • (ChunkyPNG::Canvas) - A new canvas instance with the resampled pixels.

Parameters:
  • new_height (Integer) -- The height of the resampled canvas.
  • new_width (Integer) -- The width of the resampled canvas.
def resample_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 = 0
  for y in 1..new_height
    # Clamp the indicies to the edges of the image
    y1 = [index_y[y-1], 0].max
    y2 = [index_y[y-1] + 1, height - 1].min
    y_residue = interp_y[y-1]
    for x in 1..new_width
      # Clamp the indicies to the edges of the image
      x1 = [index_x[x-1], 0].max
      x2 = [index_x[x-1] + 1, width - 1].min
      x_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 Row
      pixel_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 Column
      pixels[i] = ChunkyPNG::Color.interpolate_quick(pixel_bot, pixel_top, y_residue)
      i += 1
    end
  end
  replace_canvas!(new_width.to_i, new_height.to_i, pixels)
end

def resample_nearest_neighbor(new_width, new_height)

def resample_nearest_neighbor(new_width, new_height)
  dup.resample_nearest_neighbor!(new_width, new_height)
end

def resample_nearest_neighbor!(new_width, new_height)

Returns:
  • (ChunkyPNG::Canvas) - A new canvas instance with the resampled pixels.

Parameters:
  • new_height (Integer) -- The height of the resampled canvas.
  • new_width (Integer) -- The width of the resampled canvas.
def resample_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 = 0
  for y in steps_y
    for x in steps_x
      pixels[i] = get_pixel(x, y)
      i += 1
    end
  end
  
  replace_canvas!(new_width.to_i, new_height.to_i, pixels)
end

def steps(width, new_width)

Returns:
  • (Array) - An Array of Integer indicies

Parameters:
  • new_width (Integer) -- The width of the destination
  • width (Integer) -- The width of the source
def steps(width, new_width)
  indicies, residues = steps_residues(width, new_width)
  
  for i in 1..new_width
    indicies[i-1] = (indicies[i-1] + (residues[i-1] + 127)/255)
  end
  return indicies
end

def steps_residues(width, new_width)

Returns:
  • (Array, Array) - Two arrays of indicies and residues

Parameters:
  • new_width (Integer) -- The width of the destination
  • width (Integer) -- The width of the source
def steps_residues(width, new_width)
  indicies = Array.new(new_width, obj=nil)
  residues = Array.new(new_width, obj=nil)
  
  # This works by accumulating the fractional error and
  # overflowing when necessary.
  # We use mixed number arithmetic with a denominator of
  # 2 * new_width
  base_step = width / new_width
  err_step = (width % new_width) << 1
  denominator = (new_width) << 1
          
  # Initial pixel
  index = (width - new_width) / denominator
  err = (width - new_width) % denominator
  for i in 1..new_width
    indicies[i-1] = index
    residues[i-1] = (255.0 * err.to_f / denominator.to_f).round
    index += base_step
    err += err_step
    if err >= denominator
      index += 1
      err -= denominator
    end
  end
  return indicies, residues
end