Module: Quake::Debug::PngWriter

Defined in:
lib/quake/debug/png_writer.rb

Overview

Minimal PNG encoder using zlib (no external dependencies). Writes 8-bit RGB PNGs.

Constant Summary collapse

SIGNATURE =
"\x89PNG\r\n\x1a\n".b

Class Method Summary collapse

Class Method Details

.chunk(type, data) ⇒ Object

Build a PNG chunk: length, type, data, CRC



40
41
42
43
44
# File 'lib/quake/debug/png_writer.rb', line 40

def self.chunk(type, data)
  type_data = type.b + data.b
  crc = Zlib.crc32(type_data)
  [data.bytesize].pack("N") + type_data + [crc].pack("N")
end

.encode(width, height, rgb_data) ⇒ Object



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/quake/debug/png_writer.rb', line 17

def self.encode(width, height, rgb_data)
  bytes_per_row = width * 3

  # PNG requires a filter byte at the start of each row
  filtered = String.new(capacity: (bytes_per_row + 1) * height)
  height.times do |y|
    filtered << "\x00".b # filter type 0 = None
    filtered << rgb_data[y * bytes_per_row, bytes_per_row]
  end

  compressed = Zlib::Deflate.deflate(filtered)

  out = String.new(capacity: 64 + compressed.bytesize)
  out << SIGNATURE
  # IHDR: width, height, bit_depth, color_type, compression, filter, interlace
  ihdr = [width, height].pack("NN") + [8, 2, 0, 0, 0].pack("CCCCC")
  out << chunk("IHDR", ihdr)
  out << chunk("IDAT", compressed)
  out << chunk("IEND", "".b)
  out
end

.flip_rows(rgb_data, width, height) ⇒ Object

Flip RGB data vertically (glReadPixels returns bottom-up rows, PNG expects top-down).



48
49
50
51
52
53
54
55
# File 'lib/quake/debug/png_writer.rb', line 48

def self.flip_rows(rgb_data, width, height)
  bytes_per_row = width * 3
  out = String.new(capacity: rgb_data.bytesize)
  (height - 1).downto(0) do |y|
    out << rgb_data[y * bytes_per_row, bytes_per_row]
  end
  out
end

.write(filename, width, height, rgb_data) ⇒ Object

Write an RGB PNG. rgb_data is W*H*3 bytes, top-to-bottom rows.



13
14
15
# File 'lib/quake/debug/png_writer.rb', line 13

def self.write(filename, width, height, rgb_data)
  File.binwrite(filename, encode(width, height, rgb_data))
end