Class: Libpng::SimplifiedDecoder

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

Overview

Decodes a PNG byte buffer into raw pixels via libpng's "simplified" read API (png_image_begin_read_from_memory + png_image_finish_read).

Also parses the byte buffer via ChunkWalker to populate DecodedImage#bit_depth, #color_type, #interlace (from IHDR), #text (from tEXt/zTXt/iTXt), and #color (from gAMA/cHRM/sRGB/iCCP). The simplified API itself drops these ancillary chunks on read -- walking them separately is the only way to surface their content.

One instance per decode call. Ractor-safe.

Instance Method Summary collapse

Constructor Details

#initialize(png, pixel_format: 'RGBA') ⇒ SimplifiedDecoder

Returns a new instance of SimplifiedDecoder.

Raises:



15
16
17
18
19
20
21
22
# File 'lib/libpng/simplified_decoder.rb', line 15

def initialize(png, pixel_format: 'RGBA')
  raise Error, 'input PNG buffer is nil' if png.nil?
  raise Error, 'input PNG buffer must be a String' unless png.is_a?(String)

  @png = png
  @pixel_format = pixel_format
  raise Error, "unknown pixel_format #{@pixel_format.inspect}" unless format_value
end

Instance Method Details

#callObject

Returns a Libpng::DecodedImage.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/libpng/simplified_decoder.rb', line 25

def call
  fmt = format_value
  img = FFI::MemoryPointer.new(:uint8, PNG_IMAGE_SIZE)
  img.clear
  img.put_uint32(PNG_IMAGE_OFF_VERSION, PNG_IMAGE_VERSION)

  FFI::MemoryPointer.new(:uint8, @png.bytesize) do |in_buf|
    in_buf.write_bytes(@png)
    ok = Libpng::Binding.png_image_begin_read_from_memory(img, in_buf, @png.bytesize)
    raise Error, "png_image_begin_read_from_memory failed: #{read_message(img)}" if ok.zero?

    img.put_uint32(PNG_IMAGE_OFF_FORMAT, fmt)
    width = img.get_uint32(PNG_IMAGE_OFF_WIDTH)
    height = img.get_uint32(PNG_IMAGE_OFF_HEIGHT)
    stride = width * BytesPerPixel.for_format(fmt)
    out_size = stride * height

    FFI::MemoryPointer.new(:uint8, out_size) do |out_buf|
      ok = Libpng::Binding.png_image_finish_read(img, nil, out_buf, stride, nil)
      raise Error, "png_image_finish_read failed: #{read_message(img)}" if ok.zero?

      return DecodedImage.new(
        width: width,
        height: height,
        format: @pixel_format.to_s.upcase,
        pixels: out_buf.read_bytes(out_size),
        **
      )
    end
  end
ensure
  Libpng::Binding.png_image_free(img) unless img.nil? || img.null?
end