Class: PNG::Canvas

Inherits:
Object
  • Object
show all
Defined in:
lib/png.rb,
lib/png/font.rb

Overview

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

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(width, height, background = Color::Background) ⇒ Canvas

Returns a new instance of Canvas.



418
419
420
421
422
# File 'lib/png.rb', line 418

def initialize width, height, background = Color::Background
  @width = width
  @height = height
  @data = Array.new(@height) { |x| Array.new(@width, background) }
end

Instance Attribute Details

#dataObject (readonly)

Raw data



416
417
418
# File 'lib/png.rb', line 416

def data
  @data
end

#heightObject (readonly)

Height of the canvas



406
407
408
# File 'lib/png.rb', line 406

def height
  @height
end

#widthObject (readonly)

Width of the canvas



411
412
413
# File 'lib/png.rb', line 411

def width
  @width
end

Instance Method Details

#[](x, y) ⇒ Object

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



427
428
429
430
431
# File 'lib/png.rb', line 427

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) ⇒ Object

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



436
437
438
439
440
441
# File 'lib/png.rb', line 436

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) ⇒ Object

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'


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/png/font.rb', line 53

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) ⇒ Object

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



446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/png.rb', line 446

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

#eachObject

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



466
467
468
469
470
471
472
# File 'lib/png.rb', line 466

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) ⇒ Object

Create a new canvas copying a region of the current canvas



477
478
479
480
481
482
483
484
485
486
487
# File 'lib/png.rb', line 477

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

#inspectObject

:nodoc:



489
490
491
# File 'lib/png.rb', line 489

def inspect # :nodoc:
  '#<%s %dx%d>' % [self.class, @width, @height]
end

#line(x0, y0, x1, y1, color) ⇒ Object

Draws a line using Xiaolin Wu’s antialiasing technique.

en.wikipedia.org/wiki/Xiaolin_Wu’s_line_algorithm



505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
# File 'lib/png.rb', line 505

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 |i|
      e_acc_temp, e_acc = e_acc, (e_acc + e) & 0xFFFF
      x0 = x0 + sx if (e_acc <= e_acc_temp)
      w = 0xFF-(e_acc >> 8)
      point(x0, y0, color.intensity(w))
      y0 = 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 |i|
    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) ⇒ Object

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



496
497
498
# File 'lib/png.rb', line 496

def point(x, y, color)
  self[x,y] = self[x,y].blend(color)
end

#to_sObject

Returns an ASCII representation of this image



568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
# File 'lib/png.rb', line 568

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

  return image.join
end