Class: PureJPEG::Decoder
- Inherits:
-
Object
- Object
- PureJPEG::Decoder
- Defined in:
- lib/pure_jpeg/decoder.rb
Overview
Baseline JPEG decoder.
Decodes baseline DCT (SOF0) JPEGs with 1 or 3 components, any chroma subsampling factor, and restart markers.
Use read for a convenient entry point.
Class Method Summary collapse
-
.decode(path_or_data) ⇒ Image
Decode a JPEG from a file path or binary string.
Instance Method Summary collapse
- #decode ⇒ Object
-
#initialize(data) ⇒ Decoder
constructor
A new instance of Decoder.
Constructor Details
#initialize(data) ⇒ Decoder
Returns a new instance of Decoder.
24 25 26 |
# File 'lib/pure_jpeg/decoder.rb', line 24 def initialize(data) @data = data end |
Class Method Details
.decode(path_or_data) ⇒ Image
Decode a JPEG from a file path or binary string.
15 16 17 18 19 20 21 22 |
# File 'lib/pure_jpeg/decoder.rb', line 15 def self.decode(path_or_data) data = if path_or_data.is_a?(String) && !path_or_data.start_with?("\xFF\xD8".b) && File.exist?(path_or_data) File.binread(path_or_data) else path_or_data.b end new(data).decode end |
Instance Method Details
#decode ⇒ Object
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 58 59 60 61 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 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/pure_jpeg/decoder.rb', line 28 def decode jfif = JFIFReader.new(@data) @icc_profile = jfif.icc_profile validate_dimensions!(jfif.width, jfif.height) return decode_progressive(jfif) if jfif.progressive width = jfif.width height = jfif.height # Build Huffman decode tables dc_tables = {} ac_tables = {} jfif.huffman_tables.each do |(table_class, table_id), info| table = Huffman::DecodeTable.new(info[:bits], info[:values]) if table_class == 0 dc_tables[table_id] = table else ac_tables[table_id] = table end end # Map component IDs to their info comp_info = {} jfif.components.each { |c| comp_info[c.id] = c } # Determine max sampling factors max_h = jfif.components.map(&:h_sampling).max max_v = jfif.components.map(&:v_sampling).max # MCU dimensions in pixels mcu_px_w = max_h * 8 mcu_px_h = max_v * 8 mcus_x = (width + mcu_px_w - 1) / mcu_px_w mcus_y = (height + mcu_px_h - 1) / mcu_px_h # Allocate channel buffers (full padded size) padded_w = mcus_x * mcu_px_w padded_h = mcus_y * mcu_px_h channels = {} jfif.components.each do |c| ch_w = (padded_w * c.h_sampling) / max_h ch_h = (padded_h * c.v_sampling) / max_v channels[c.id] = { data: Array.new(ch_w * ch_h, 0), width: ch_w, height: ch_h } end # Decode scan data reader = BitReader.new(jfif.scan_data) prev_dc = Hash.new(0) restart_interval = jfif.restart_interval mcu_count = 0 # Reusable buffers zigzag = Array.new(64, 0) raster = Array.new(64, 0) dequant = Array.new(64, 0) mcus_y.times do |mcu_row| mcus_x.times do |mcu_col| # Handle restart interval if restart_interval > 0 && mcu_count > 0 && (mcu_count % restart_interval) == 0 reader.reset prev_dc.clear end jfif.scan_components.each do |sc| comp, dc_tab, ac_tab = resolve_scan_references!(sc, comp_info, dc_tables, ac_tables) qt = fetch_quant_table!(jfif, comp) ch = channels[comp.id] comp.v_sampling.times do |bv| comp.h_sampling.times do |bh| # Decode one 8x8 block decode_block(reader, dc_tab, ac_tab, prev_dc, sc.id, zigzag) # Inverse pipeline: unzigzag -> dequantize -> IDCT -> level shift Zigzag.unreorder!(zigzag, raster) Quantization.dequantize!(raster, qt, dequant) DCT.inverse!(dequant) # Write block into channel buffer bx = (mcu_col * comp.h_sampling + bh) * 8 by = (mcu_row * comp.v_sampling + bv) * 8 write_block(dequant, ch[:data], ch[:width], bx, by) end end end mcu_count += 1 end end # Assemble pixels num_components = jfif.components.length if num_components == 1 assemble_grayscale(width, height, channels, jfif.components[0]) elsif num_components == 3 assemble_color(width, height, channels, jfif.components, max_h, max_v) else raise DecodeError, "Unsupported number of components: #{num_components}" end end |