Class: BitmapWindow

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

Overview

A third implementation of the drawing interface WindowAdapter targets (alongside the X11 Window and the harness’s VirtualWindow): it rasterises real glyphs with skrift and composites them into an in-memory RGB buffer. Wrapped by WindowAdapter it is a full “bitmap backend” - the same Term core, rendered to a pixel buffer with no X server - useful for headless visual testing and for embedding the terminal anywhere a bitmap can go.

win = BitmapWindow.new(80, 24)
adapter = WindowAdapter.new(win, host)   # host: term_width/blink_state...
... feed the terminal ...
win.save_png("screen.png")

Defined Under Namespace

Classes: ColourDelegate

Constant Summary collapse

DEFAULT_FONT =
"/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf"
DEFAULT_EMOJI =
"/usr/share/fonts/truetype/noto/NotoColorEmoji.ttf"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(cols, rows, font: DEFAULT_FONT, size: 16, fg: 0xcccccc, bg: 0x000000, emoji: DEFAULT_EMOJI) ⇒ BitmapWindow

Returns a new instance of BitmapWindow.



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/bitmapwindow.rb', line 36

def initialize(cols, rows, font: DEFAULT_FONT, size: 16,
               fg: 0xcccccc, bg: 0x000000, emoji: DEFAULT_EMOJI)
  # The glyph pipeline (rasterise + cache + metrics) lives in skrift's
  # GlyphCache; colour emoji come from an optional colour delegate.
  @cache    = Skrift::GlyphCache.new(font, x_scale: size, y_scale: size,
                                     color: colour_delegate(emoji, size))
  @char_w   = @cache.cell_width
  @char_h   = @cache.cell_height
  @baseline = @cache.baseline
  @cols, @rows = cols, rows
  @fg, @bg = fg, bg
  resize(cols * @char_w, rows * @char_h)
end

Instance Attribute Details

#heightObject (readonly)

Returns the value of attribute height.



24
25
26
# File 'lib/bitmapwindow.rb', line 24

def height
  @height
end

#pixelsObject (readonly)

Returns the value of attribute pixels.



24
25
26
# File 'lib/bitmapwindow.rb', line 24

def pixels
  @pixels
end

#widthObject (readonly)

Returns the value of attribute width.



24
25
26
# File 'lib/bitmapwindow.rb', line 24

def width
  @width
end

Instance Method Details

#char_hObject



51
# File 'lib/bitmapwindow.rb', line 51

def char_h = @char_h

#char_wObject



50
# File 'lib/bitmapwindow.rb', line 50

def char_w = @char_w

#clear(x, y, w, h) ⇒ Object



78
# File 'lib/bitmapwindow.rb', line 78

def clear(x, y, w, h)  = fillrect(x, y, w, h, @bg)

#copy_bufferObject



63
# File 'lib/bitmapwindow.rb', line 63

def copy_buffer   = nil

#dirty!Object

Live-loop hooks: a bitmap has no separate front buffer / event channel.



61
# File 'lib/bitmapwindow.rb', line 61

def dirty!        = nil

#draw(x, y, str, fg, bg, _lineattrs = nil) ⇒ Object

x,y are pixel coordinates (WindowAdapter has already multiplied by the cell size). lineattrs (double width/height) is rendered as normal width for now - it does not affect correctness of the text, only its scale.



84
85
86
87
88
89
90
91
# File 'lib/bitmapwindow.rb', line 84

def draw(x, y, str, fg, bg, _lineattrs = nil)
  fillrect(x, y, str.length * @char_w, @char_h, bg)
  str.each_char.with_index do |ch, i|
    cp = ch.ord
    next if cp == 32 || cp == CharWidth::WIDE_SPACER # space / wide-glyph tail
    blit_glyph(cp, x + i * @char_w, y, fg)
  end
end

#draw_line(x, y, w, col) ⇒ Object



79
# File 'lib/bitmapwindow.rb', line 79

def draw_line(x, y, w, col) = fillrect(x, y, w, 1, col)

#fillrect(x, y, w, h, col) ⇒ Object



69
70
71
72
73
74
75
76
# File 'lib/bitmapwindow.rb', line 69

def fillrect(x, y, w, h, col)
  x0 = x.clamp(0, @width); x1 = (x + w).clamp(0, @width)
  y0 = y.clamp(0, @height); y1 = (y + h).clamp(0, @height)
  (y0...y1).each do |py|
    base = py * @width
    (x0...x1).each { |px| @pixels[base + px] = col }
  end
end

#flushObject



62
# File 'lib/bitmapwindow.rb', line 62

def flush         = nil

#map_windowObject



64
# File 'lib/bitmapwindow.rb', line 64

def map_window    = nil

#resize(w, h) ⇒ Object



53
54
55
56
# File 'lib/bitmapwindow.rb', line 53

def resize(w, h)
  @width, @height = w, h
  @pixels = Array.new(@width * @height, @bg)
end

#save_png(path) ⇒ Object

# Output



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/bitmapwindow.rb', line 107

def save_png(path)
  raw = +"".b
  @height.times do |y|
    raw << "\0"               # filter: none
    base = y * @width
    @width.times do |x|
      p = @pixels[base + x]
      raw << ((p >> 16) & 0xff).chr << ((p >> 8) & 0xff).chr << (p & 0xff).chr
    end
  end
  png = +"\x89PNG\r\n\x1a\n".b
  png << png_chunk("IHDR", [@width, @height, 8, 2, 0, 0, 0].pack("NNC5"))
  png << png_chunk("IDAT", Zlib::Deflate.deflate(raw))
  png << png_chunk("IEND", "")
  File.binwrite(path, png)
  path
end

#scroll_down(srcy, w, h, step) ⇒ Object



100
101
102
103
# File 'lib/bitmapwindow.rb', line 100

def scroll_down(srcy, w, h, step)
  move_rows(srcy, srcy + step, h)
  clear(0, srcy, @width, step)
end

#scroll_up(srcy, w, h, step) ⇒ Object

Mirror Window#scroll_up / #scroll_down: move a block of pixel rows and clear the vacated strip (geometry comes from WindowAdapter).



95
96
97
98
# File 'lib/bitmapwindow.rb', line 95

def scroll_up(srcy, w, h, step)
  move_rows(srcy, srcy - step, h)
  clear(0, srcy + h - step, @width, step + 1)
end

#scrollback_countObject



67
# File 'lib/bitmapwindow.rb', line 67

def scrollback_count = 0

#scrollback_modeObject



66
# File 'lib/bitmapwindow.rb', line 66

def scrollback_mode  = false

#set_buffer(_) ⇒ Object



65
# File 'lib/bitmapwindow.rb', line 65

def set_buffer(_) = nil