class PNG::Canvas

A canvas used for drawing images. Origin is 0, 0 in the bottom left corner.

Attributes

data[R]

Raw data

height[R]

Height of the canvas

width[R]

Width of the canvas

Public Class Methods

new(width, height, background = Color::Background) click to toggle source
# File lib/png.rb, line 419
def initialize width, height, background = Color::Background
  @width = width
  @height = height
  @data = Array.new(@height) { Array.new(@width, background) }
end

Public Instance Methods

[](x, y) click to toggle source

Retrieves the color of the pixel at (x, y).

# File lib/png.rb, line 428
def [] x, y
  raise "bad x value #{x} >= #{@width}" if x >= @width
  raise "bad y value #{y} >= #{@height}" if y >= @height
  @data[@height-y-1][x]
end
[]=(x, y, color) click to toggle source

Sets the color of the pixel at (x, y) to color.

# File lib/png.rb, line 437
def []= x, y, color
  raise "bad x value #{x} >= #{@width}" if x >= @width
  raise "bad y value #{y} >= #{@height}"  if y >= @height
  raise "bad color #{color.inspect}" unless color.kind_of? PNG::Color
  @data[@height-y-1][x] = color
end
annotate(string, x, y, font = PNG::Font.default, align = :left, style = :overwrite) click to toggle source

Write a string at [x, y] with font, optionally specifying a font, an alignment of :left, :center, or :right and the style to draw the annotation (see composite).

require 'png/font'
# File lib/png/font.rb, line 52
def annotate string, x, y,
             font = PNG::Font.default, align = :left, style = :overwrite
  case align
  when :left then
    # do nothing
  when :center then
    x -= string.length * font.width / 2
  when :right then
    x -= string.length * font.width
  else
    raise ArgumentError, "Unknown align: #{align.inspect}"
  end

  x_offset, width = 0, font.width

  string.split(//).each do |char|
    self.composite font[char], x + x_offset, y
    x_offset += width
  end
end
composite(canvas, x, y, style = :overwrite) click to toggle source

Composites another canvas onto self at the given (bottom left) coordinates.

# File lib/png.rb, line 447
def composite canvas, x, y, style = :overwrite
  canvas.each do |x1, y1, color|
    case style
    when :overwrite then
      self[x+x1, y+y1] = color
    when :add, :underlay then
      self[x+x1, y+y1] = self[x+x1, y+y1] | color
    when :overlay then
      self[x+x1, y+y1] = color | self[x+x1, y+y1]
    when :blend then
      self.point x+x1, y+y1, color
    else
      raise "unknown style for composite: #{style.inspect}"
    end
  end
end
each() { |x, y, color| ... } click to toggle source

Iterates over the canvas yielding x, y, and color.

# File lib/png.rb, line 467
def each
  data.reverse.each_with_index do |row, y|
    row.each_with_index do |color, x|
      yield x, y, color
    end
  end
end
extract(x0, y0, x1, y1) click to toggle source

Create a new canvas copying a region of the current canvas

# File lib/png.rb, line 478
def extract x0, y0, x1, y1
  canvas = Canvas.new(x1-x0+1, y1-y0+1)

  (x0..x1).each_with_index do |x2, x3|
    (y0..y1).each_with_index do |y2, y3|
      canvas[x3, y3] = self[x2, y2]
    end
  end

  canvas
end
line(x0, y0, x1, y1, color) click to toggle source

Draws a line using Xiaolin Wu's antialiasing technique.

en.wikipedia.org/wiki/Xiaolin_Wu’s_line_algorithm

# File lib/png.rb, line 506
def line x0, y0, x1, y1, color
  y0, y1, x0, x1 = y1, y0, x1, x0 if y0 > y1
  dx = x1 - x0
  sx = dx < 0 ? -1 : 1
  dx *= sx
  dy = y1 - y0

  # 'easy' cases
  if dy == 0 then
    Range.new(*[x0, x1].sort).each do |x|
      point(x, y0, color)
    end
    return
  end

  if dx == 0 then
    (y0..y1).each do |y|
      point(x0, y, color)
    end
    return
  end

  if dx == dy then
    x0.step(x1, sx) do |x|
      point(x, y0, color)
      y0 += 1
    end
    return
  end

  # main loop
  point(x0, y0, color)
  e_acc = 0
  if dy > dx then # vertical displacement
    e = (dx << 16) / dy
    (y0...y1-1).each do
      e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
      x0 += sx if (e_acc <= e_acc_temp)
      w = 0xFF-(e_acc >> 8)
      point(x0, y0, color.intensity(w))
      y0 += 1
      point(x0 + sx, y0, color.intensity(0xFF-w))
    end
    point(x1, y1, color)
    return
  end

  # horizontal displacement
  e = (dy << 16) / dx
  (dx - 1).downto(0) do
    e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
    y0 += 1 if (e_acc <= e_acc_temp)
    w = 0xFF-(e_acc >> 8)
    point(x0, y0, color.intensity(w))
    x0 += sx
    point(x0, y0 + 1, color.intensity(0xFF-w))
  end
  point(x1, y1, color)
end
point(x, y, color) click to toggle source

Blends color onto the color at point (x, y).

# File lib/png.rb, line 497
def point x, y, color
  self[x, y] = self[x, y].blend(color)
end
to_s() click to toggle source

Returns an ASCII representation of this image

# File lib/png.rb, line 569
def to_s
  image = []
  scale = (@width / 39) + 1

  @data.each_with_index do |row, x|
    next if x % scale != 0
    row.each_with_index do |color, y|
      next if y % scale != 0
      image << color.to_ascii
    end
    image << "\n"
  end

  image.join
end