Class: Libpng::StandardEncoder
- Inherits:
-
Object
- Object
- Libpng::StandardEncoder
- Defined in:
- lib/libpng/standard_encoder.rb
Overview
Encodes raw pixel data via libpng's standard write API (png_create_write_struct -> png_set_IHDR -> png_set_rows -> png_write_png(PNG_TRANSFORM_IDENTITY)). This matches the byte output of the "classic" libpng write path used by libemf2svg's rgb2png, and avoids the sRGB/gAMA chunks the simplified API injects by default.
Options:
pixel_format: :gray, :ga, :rgb, :rgba, or :palette
filter: :default (adaptive), :none, :sub, :up, :avg,
:paeth, :all, :adaptive
compression_level: 0..9 (default 6 = Z_DEFAULT_COMPRESSION)
interlace: :none or :adam7 (default :none)
bit_depth: 8 or 16 (default 8; ignored for :palette which
is always 8)
palette: Array of [r, g, b] or [r, g, b, a] for
pixel_format: :palette. 1..256 entries.
Metadata options (forwarded to MetadataWriter; written between IHDR/PLTE and png_write_png):
text: Hash<String,String> of tEXt keyword -> value.
Values with non-ASCII bytes use iTXt (UTF-8).
gamma: Float file gamma (e.g. 0.45455 for sRGB).
srgb_intent: Integer 0..3 (perceptual, relative-colorimetric,
saturation, absolute-colorimetric).
chromaticities: Hash with :white_point_x/y, :red_x/y,
:green_x/y, :blue_x/y (each a Float 0..1).
icc_profile: Hash with :name (String) and :data (binary
String). libpng compresses via zlib internally.
phys: Hash with :pixels_per_unit_x, :pixels_per_unit_y,
and :unit (0 = unknown, 1 = meters).
One instance per encode call. Ractor-safe.
Defined Under Namespace
Classes: CallbackSet
Constant Summary collapse
- METADATA_KEYS =
%i[text gamma srgb_intent chromaticities icc_profile phys].freeze
Instance Method Summary collapse
-
#call ⇒ Object
Returns a binary String of PNG file bytes.
-
#initialize(width, height, pixels, pixel_format: 'RGBA', filter: :default, compression_level: 6, interlace: :none, bit_depth: 8, palette: nil, **metadata) ⇒ StandardEncoder
constructor
A new instance of StandardEncoder.
Constructor Details
#initialize(width, height, pixels, pixel_format: 'RGBA', filter: :default, compression_level: 6, interlace: :none, bit_depth: 8, palette: nil, **metadata) ⇒ StandardEncoder
Returns a new instance of StandardEncoder.
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# File 'lib/libpng/standard_encoder.rb', line 40 def initialize(width, height, pixels, pixel_format: 'RGBA', filter: :default, compression_level: 6, interlace: :none, bit_depth: 8, palette: nil, **) @width = width @height = height @pixels = pixels @pixel_format_sym = coerce_pixel_format(pixel_format) @filter_sym = filter.to_sym @compression_level = compression_level @interlace_sym = interlace.to_sym @bit_depth = bit_depth @palette = palette @metadata = validate! end |
Instance Method Details
#call ⇒ Object
Returns a binary String of PNG file bytes.
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/libpng/standard_encoder.rb', line 62 def call output = String.new.force_encoding('ASCII-8BIT') callbacks = CallbackSet.new(output) png_ptr = FFI::Pointer.new(0) info_ptr = FFI::Pointer.new(0) begin png_ptr = Libpng::Binding.png_create_write_struct(LIBPNG_VER_STRING_C, nil, callbacks.error_fn, nil) raise Error, 'png_create_write_struct returned NULL' if png_ptr.null? info_ptr = Libpng::Binding.png_create_info_struct(png_ptr) raise Error, 'png_create_info_struct returned NULL' if info_ptr.null? apply_compression(png_ptr) Libpng::Binding.png_set_write_fn(png_ptr, nil, callbacks.write_fn, nil) Libpng::Binding.png_set_IHDR(png_ptr, info_ptr, @width, @height, effective_bit_depth, color_type, interlace_method, COMPRESSION_TYPE_DEFAULT, FILTER_TYPE_DEFAULT) apply_filter(png_ptr) apply_palette(png_ptr, info_ptr) if palette_format? # Metadata writers run AFTER IHDR/PLTE and BEFORE png_write_png # (which is what calls png_write_info -> emits all queued chunks). MetadataWriter.apply(png_ptr, info_ptr, @metadata) FFI::MemoryPointer.new(:uint8, @pixels.bytesize) do |px| px.write_bytes(@pixels) FFI::MemoryPointer.new(:pointer, @height) do |rows| @height.times do |y| rows.put_pointer(y * FFI.type_size(:pointer), px + (y * stride)) end Libpng::Binding.png_set_rows(png_ptr, info_ptr, rows) Libpng::Binding.png_write_png(png_ptr, info_ptr, TRANSFORM_IDENTITY, nil) end end output ensure destroy(png_ptr, info_ptr) end end |