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.



420
421
422
423
424
# File 'lib/png.rb', line 420

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



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

def data
  @data
end

#heightObject (readonly)

Height of the canvas



408
409
410
# File 'lib/png.rb', line 408

def height
  @height
end

#widthObject (readonly)

Width of the canvas



413
414
415
# File 'lib/png.rb', line 413

def width
  @width
end

Instance Method Details

#[](x, y) ⇒ Object

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



429
430
431
432
433
# File 'lib/png.rb', line 429

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.



438
439
440
441
442
443
# File 'lib/png.rb', line 438

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.



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

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.



468
469
470
471
472
473
474
# File 'lib/png.rb', line 468

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



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

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:



491
492
493
# File 'lib/png.rb', line 491

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



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
564
565
# File 'lib/png.rb', line 507

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).



498
499
500
# File 'lib/png.rb', line 498

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

#to_sObject

Returns an ASCII representation of this image



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

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