Class: ZplRender::Canvas

Inherits:
Object
  • Object
show all
Defined in:
lib/zpl_render/canvas.rb

Overview

A 1-bit monochrome dot canvas at printer resolution. Pixel value 1 = black dot (ink), 0 = white. Stored one byte per pixel for fast random access via String#getbyte/#setbyte.

Constant Summary collapse

ROTATIONS =
%i[n r i b].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(width, height) ⇒ Canvas

Returns a new instance of Canvas.

Raises:

  • (ArgumentError)


12
13
14
15
16
17
18
# File 'lib/zpl_render/canvas.rb', line 12

def initialize(width, height)
  raise ArgumentError, "canvas size must be positive (#{width}x#{height})" if width < 1 || height < 1

  @width = width
  @height = height
  @bits = ("\x00" * (width * height)).b
end

Instance Attribute Details

#heightObject (readonly)

Returns the value of attribute height.



8
9
10
# File 'lib/zpl_render/canvas.rb', line 8

def height
  @height
end

#widthObject (readonly)

Returns the value of attribute width.



8
9
10
# File 'lib/zpl_render/canvas.rb', line 8

def width
  @width
end

Class Method Details

.rotated_size(w, h, rotation) ⇒ Object

Dimensions of src after rotation, as [w, h].



88
89
90
# File 'lib/zpl_render/canvas.rb', line 88

def self.rotated_size(w, h, rotation)
  %i[r b].include?(rotation) ? [h, w] : [w, h]
end

Instance Method Details

#blit(src, x, y, rotation: :n, mode: :paint) ⇒ Object

Blit src onto self with its (rotated) top-left corner at (x, y). Rotation is one of :n (0), :r (90 CW), :i (180), :b (270 CW). mode :paint -> src ink paints black, src background is transparent mode :erase -> src ink paints white (used for ^GB/^GC color W) mode :xor -> src ink inverts the canvas (ZPL ^FR field reverse)



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/zpl_render/canvas.rb', line 64

def blit(src, x, y, rotation: :n, mode: :paint)
  sw = src.width
  sh = src.height
  (0...sh).each do |sy|
    (0...sw).each do |sx|
      next unless src.get(sx, sy)

      tx, ty = case rotation
               when :n then [x + sx, y + sy]
               when :r then [x + (sh - 1 - sy), y + sx]
               when :i then [x + (sw - 1 - sx), y + (sh - 1 - sy)]
               when :b then [x + sy, y + (sw - 1 - sx)]
               else [x + sx, y + sy]
               end
      case mode
      when :xor then toggle(tx, ty)
      when :erase then set(tx, ty, false)
      else set(tx, ty, true)
      end
    end
  end
end

#fill_rect(x, y, w, h, color: :black, xor: false) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/zpl_render/canvas.rb', line 39

def fill_rect(x, y, w, h, color: :black, xor: false)
  x0 = [x, 0].max
  y0 = [y, 0].max
  x1 = [x + w, @width].min
  y1 = [y + h, @height].min
  return if x0 >= x1 || y0 >= y1

  value = color == :black ? 1 : 0
  (y0...y1).each do |yy|
    row = yy * @width
    (x0...x1).each do |xx|
      if xor
        @bits.setbyte(row + xx, @bits.getbyte(row + xx) ^ 1)
      else
        @bits.setbyte(row + xx, value)
      end
    end
  end
end

#get(x, y) ⇒ Object



33
34
35
36
37
# File 'lib/zpl_render/canvas.rb', line 33

def get(x, y)
  return false if x.negative? || y.negative? || x >= @width || y >= @height

  @bits.getbyte(y * @width + x) == 1
end

#packed_rowsObject

Packed rows, 8 pixels per byte, MSB first (for PNG/PDF encoders).



93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/zpl_render/canvas.rb', line 93

def packed_rows
  Array.new(@height) do |y|
    row_offset = y * @width
    bytes = ("\x00" * ((@width + 7) / 8)).b
    (0...@width).each do |x|
      next unless @bits.getbyte(row_offset + x) == 1

      bytes.setbyte(x >> 3, bytes.getbyte(x >> 3) | (0x80 >> (x & 7)))
    end
    bytes
  end
end

#set(x, y, on = true) ⇒ Object



20
21
22
23
24
# File 'lib/zpl_render/canvas.rb', line 20

def set(x, y, on = true)
  return if x.negative? || y.negative? || x >= @width || y >= @height

  @bits.setbyte(y * @width + x, on ? 1 : 0)
end

#to_ascii(on: "#", off: ".") ⇒ Object



106
107
108
109
110
# File 'lib/zpl_render/canvas.rb', line 106

def to_ascii(on: "#", off: ".")
  Array.new(@height) do |y|
    Array.new(@width) { |x| get(x, y) ? on : off }.join
  end.join("\n")
end

#toggle(x, y) ⇒ Object



26
27
28
29
30
31
# File 'lib/zpl_render/canvas.rb', line 26

def toggle(x, y)
  return if x.negative? || y.negative? || x >= @width || y >= @height

  idx = y * @width + x
  @bits.setbyte(idx, @bits.getbyte(idx) ^ 1)
end