Source file : gid-decoding_png.adb
-- A PNG stream is made of several "chunks" (see type PNG_Chunk_tag).
-- The image itself is contained in the IDAT chunk(s).
--
-- Steps for decoding an image (step numbers are from the ISO standard):
--
-- 10: Inflate deflated data; at each output buffer (slide),
-- process with step 9.
-- 9: Read filter code (row begin), or unfilter bytes, go with step 8
-- 8: Display pixels these bytes represent;
-- eventually, locate the interlaced image current point
--
-- Reference: Portable Network Graphics (PNG) Specification (Second Edition)
-- ISO/IEC 15948:2003 (E)
-- W3C Recommendation 10 November 2003
-- http://www.w3.org/TR/PNG/
--
with GID.Buffering, GID.Decoding_PNG.Huffman;
with Ada.Text_IO;
package body GID.Decoding_PNG is
generic
type Number is mod <>;
procedure Big_endian_number (
from : in out Input_buffer;
n : out Number
);
pragma Inline (Big_endian_number);
procedure Big_endian_number (
from : in out Input_buffer;
n : out Number
)
is
b : U8;
begin
n := 0;
for i in 1 .. Number'Size / 8 loop
Buffering.Get_Byte (from, b);
n := n * 256 + Number (b);
end loop;
end Big_endian_number;
procedure Big_endian is new Big_endian_number (U32);
----------
-- Read --
----------
procedure Read (image : in out Image_descriptor; ch : out Chunk_head) is
str4 : String (1 .. 4);
b : U8;
begin
Big_endian (image.buffer, ch.length);
for i in str4'Range loop
Buffering.Get_Byte (image.buffer, b);
str4 (i) := Character'Val (b);
end loop;
begin
ch.kind := PNG_Chunk_tag'Value (str4);
if some_trace then
Ada.Text_IO.Put_Line (
"Chunk [" & str4 &
"], length:" & U32'Image (ch.length)
);
end if;
exception
when Constraint_Error =>
raise error_in_image_data with
"PNG chunk unknown: " &
Integer'Image (Character'Pos (str4 (1))) &
Integer'Image (Character'Pos (str4 (2))) &
Integer'Image (Character'Pos (str4 (3))) &
Integer'Image (Character'Pos (str4 (4))) &
" (" & str4 & ')';
end;
end Read;
package CRC32 is
procedure Init (CRC : out Unsigned_32);
function Final (CRC : Unsigned_32) return Unsigned_32;
procedure Update (CRC : in out Unsigned_32; InBuf : Byte_array);
pragma Inline (Update);
end CRC32;
package body CRC32 is
CRC32_Table : array (Unsigned_32'(0) .. 255) of Unsigned_32;
procedure Prepare_table is
-- CRC-32 algorithm, ISO-3309
Seed : constant := 16#EDB88320#;
l : Unsigned_32;
begin
for i in CRC32_Table'Range loop
l := i;
for bit in 0 .. 7 loop
if (l and 1) = 0 then
l := Shift_Right (l, 1);
else
l := Shift_Right (l, 1) xor Seed;
end if;
end loop;
CRC32_Table (i) := l;
end loop;
end Prepare_table;
procedure Update (CRC : in out Unsigned_32; InBuf : Byte_array) is
local_CRC : Unsigned_32;
begin
local_CRC := CRC;
for i in InBuf'Range loop
local_CRC :=
CRC32_Table (16#FF# and (local_CRC xor Unsigned_32 (InBuf (i))))
xor
Shift_Right (local_CRC, 8);
end loop;
CRC := local_CRC;
end Update;
table_empty : Boolean := True;
procedure Init (CRC : out Unsigned_32) is
begin
if table_empty then
Prepare_table;
table_empty := False;
end if;
CRC := 16#FFFF_FFFF#;
end Init;
function Final (CRC : Unsigned_32) return Unsigned_32 is
begin
return not CRC;
end Final;
end CRC32;
----------
-- Load --
----------
procedure Load (image : in out Image_descriptor) is
----------------------
-- Load_specialized --
----------------------
generic
-- These values are invariant through the whole picture,
-- so we can make them generic parameters. As a result, all
-- "if", "case", etc. using them at the center of the decoding
-- are optimized out at compile-time.
interlaced : Boolean;
png_bits_per_pixel : Positive;
bytes_to_unfilter : Positive;
-- ^ amount of bytes to unfilter at a time
-- = Integer'Max(1, bits_per_pixel / 8);
subformat_id : Natural;
procedure Load_specialized;
--
procedure Load_specialized is
use GID.Buffering;
subtype Mem_row_bytes_array is Byte_array (0 .. Integer (image.width) * 8);
--
mem_row_bytes : array (0 .. 1) of Mem_row_bytes_array;
-- We need to memorize two image rows, for un-filtering
curr_row : Natural := 1;
-- either current is 1 and old is 0, or the reverse
subtype X_range is Integer range -1 .. Integer (image.width) - 1;
subtype Y_range is Integer range 0 .. Integer (image.height) - 1;
-- X position -1 is for the row's filter methode code
x : X_range := X_range'First;
y : Y_range := Y_range'First;
x_max : X_range; -- for non-interlaced images: = X_range'Last
y_max : Y_range; -- for non-interlaced images: = Y_range'Last
pass : Positive range 1 .. 7 := 1;
--------------------------
-- ** 9: Unfiltering ** --
--------------------------
-- http://www.w3.org/TR/PNG/#9Filters
type Filter_method_0 is (None, Sub, Up, Average, Paeth);
current_filter : Filter_method_0;
procedure Unfilter_bytes (
f : in Byte_array; -- filtered
u : out Byte_array -- unfiltered
)
is
pragma Inline (Unfilter_bytes);
-- Byte positions (f is the byte to be unfiltered):
--
-- c b
-- a f
a, b, c, p, pa, pb, pc, pr : Integer;
j : Integer := 0;
begin
if full_trace and then x = 0 then
if y = 0 then
Ada.Text_IO.New_Line;
end if;
Ada.Text_IO.Put_Line (
"row" & Integer'Image (y) & ": filter= " &
Filter_method_0'Image (current_filter)
);
end if;
--
-- !! find a way to have f99n0g04.png decoded correctly...
-- seems a filter issue.
--
case current_filter is
when None =>
-- Recon(x) = Filt(x)
u := f;
when Sub =>
-- Recon(x) = Filt(x) + Recon(a)
if x > 0 then
for i in f'Range loop
u (u'First + j) := f (i) + mem_row_bytes (curr_row)((x - 1) * bytes_to_unfilter + j);
j := j + 1;
end loop;
else
u := f;
end if;
when Up =>
-- Recon(x) = Filt(x) + Recon(b)
if y > 0 then
for i in f'Range loop
u (u'First + j) := f (i) + mem_row_bytes (1 - curr_row)(x * bytes_to_unfilter + j);
j := j + 1;
end loop;
else
u := f;
end if;
when Average =>
-- Recon(x) = Filt(x) + floor((Recon(a) + Recon(b)) / 2)
for i in f'Range loop
if x > 0 then
a := Integer (mem_row_bytes (curr_row)((x - 1) * bytes_to_unfilter + j));
else
a := 0;
end if;
if y > 0 then
b := Integer (mem_row_bytes (1 - curr_row)(x * bytes_to_unfilter + j));
else
b := 0;
end if;
u (u'First + j) := U8 ((Integer (f (i)) + (a + b) / 2) mod 256);
j := j + 1;
end loop;
when Paeth =>
-- Recon(x) = Filt(x) + PaethPredictor(Recon(a), Recon(b), Recon(c))
for i in f'Range loop
if x > 0 then
a := Integer (mem_row_bytes (curr_row)((x - 1) * bytes_to_unfilter + j));
else
a := 0;
end if;
if y > 0 then
b := Integer (mem_row_bytes (1 - curr_row)(x * bytes_to_unfilter + j));
else
b := 0;
end if;
if x > 0 and y > 0 then
c := Integer (mem_row_bytes (1 - curr_row)((x - 1) * bytes_to_unfilter + j));
else
c := 0;
end if;
p := a + b - c;
pa := abs (p - a);
pb := abs (p - b);
pc := abs (p - c);
if pa <= pb and then pa <= pc then
pr := a;
elsif pb <= pc then
pr := b;
else
pr := c;
end if;
u (u'First + j) := f (i) + U8 (pr);
j := j + 1;
end loop;
end case;
j := 0;
for i in u'Range loop
mem_row_bytes (curr_row)(x * bytes_to_unfilter + j) := u (i);
j := j + 1;
end loop;
-- if u'Length /= bytes_to_unfilter then
-- raise Constraint_Error;
-- end if;
end Unfilter_bytes;
filter_stat : array (Filter_method_0) of Natural := (others => 0);
----------------------------------------------
-- ** 8: Interlacing and pass extraction ** --
----------------------------------------------
-- http://www.w3.org/TR/PNG/#8Interlace
-- Output bytes from decompression
--
procedure Output_uncompressed (
data : in Byte_array;
reject : out Natural
-- amount of bytes to be resent here next time,
-- in order to have a full multi-byte pixel
)
is
-- Display of pixels coded on 8 bits per channel in the PNG stream
procedure Out_Pixel_8 (br, bg, bb, ba : U8) is
pragma Inline (Out_Pixel_8);
function Times_257 (x : Primary_color_range) return Primary_color_range is
pragma Inline (Times_257);
begin
return 16 * (16 * x) + x; -- this is 257 * x, = 16#0101# * x
-- Numbers 8-bit -> no OA warning at instanciation. Returns x if type Primary_color_range is mod 2**8.
end Times_257;
begin
case Primary_color_range'Modulus is
when 256 =>
Put_Pixel (
Primary_color_range (br),
Primary_color_range (bg),
Primary_color_range (bb),
Primary_color_range (ba)
);
when 65_536 =>
Put_Pixel (
Times_257 (Primary_color_range (br)),
Times_257 (Primary_color_range (bg)),
Times_257 (Primary_color_range (bb)),
Times_257 (Primary_color_range (ba))
-- Times_257 makes max intensity FF go to FFFF
);
when others =>
raise invalid_primary_color_range with "PNG: color range not supported";
end case;
end Out_Pixel_8;
procedure Out_Pixel_Palette (ix : U8) is
pragma Inline (Out_Pixel_Palette);
color_idx : constant Natural := Integer (ix);
begin
Out_Pixel_8 (
image.palette (color_idx).red,
image.palette (color_idx).green,
image.palette (color_idx).blue,
255
);
end Out_Pixel_Palette;
-- Display of pixels coded on 16 bits per channel in the PNG stream
procedure Out_Pixel_16 (br, bg, bb, ba : U16) is
pragma Inline (Out_Pixel_16);
begin
case Primary_color_range'Modulus is
when 256 =>
Put_Pixel (
Primary_color_range (br / 256),
Primary_color_range (bg / 256),
Primary_color_range (bb / 256),
Primary_color_range (ba / 256)
);
when 65_536 =>
Put_Pixel (
Primary_color_range (br),
Primary_color_range (bg),
Primary_color_range (bb),
Primary_color_range (ba)
);
when others =>
raise invalid_primary_color_range with "PNG: color range not supported";
end case;
end Out_Pixel_16;
procedure Inc_XY is
pragma Inline (Inc_XY);
xm, ym : Integer;
begin
if x < x_max then
x := x + 1;
if interlaced then
-- Position of pixels depending on pass:
--
-- 1 6 4 6 2 6 4 6
-- 7 7 7 7 7 7 7 7
-- 5 6 5 6 5 6 5 6
-- 7 7 7 7 7 7 7 7
-- 3 6 4 6 3 6 4 6
-- 7 7 7 7 7 7 7 7
-- 5 6 5 6 5 6 5 6
-- 7 7 7 7 7 7 7 7
case pass is
when 1 =>
Set_X_Y (x * 8, Y_range'Last - y * 8);
when 2 =>
Set_X_Y (4 + x * 8, Y_range'Last - y * 8);
when 3 =>
Set_X_Y (x * 4, Y_range'Last - 4 - y * 8);
when 4 =>
Set_X_Y (2 + x * 4, Y_range'Last - y * 4);
when 5 =>
Set_X_Y (x * 2, Y_range'Last - 2 - y * 4);
when 6 =>
Set_X_Y (1 + x * 2, Y_range'Last - y * 2);
when 7 =>
null; -- nothing to to, pixel are contiguous
end case;
end if;
else
x := X_range'First; -- New row
if y < y_max then
y := y + 1;
curr_row := 1 - curr_row; -- swap row index for filtering
if not interlaced then
Feedback ((y * 100) / Integer (image.height));
end if;
elsif interlaced then -- last row has beed displayed
while pass < 7 loop
pass := pass + 1;
y := 0;
case pass is
when 1 =>
null;
when 2 =>
xm := (Integer (image.width) + 3) / 8 - 1;
ym := (Integer (image.height) + 7) / 8 - 1;
when 3 =>
xm := (Integer (image.width) + 3) / 4 - 1;
ym := (Integer (image.height) + 3) / 8 - 1;
when 4 =>
xm := (Integer (image.width) + 1) / 4 - 1;
ym := (Integer (image.height) + 3) / 4 - 1;
when 5 =>
xm := (Integer (image.width) + 1) / 2 - 1;
ym := (Integer (image.height) + 1) / 4 - 1;
when 6 =>
xm := (Integer (image.width)) / 2 - 1;
ym := (Integer (image.height) + 1) / 2 - 1;
when 7 =>
xm := Integer (image.width) - 1;
ym := Integer (image.height) / 2 - 1;
end case;
if xm >= 0 and xm <= X_range'Last and ym in Y_range then
-- This pass is not empty (otherwise, we will continue
-- to the next one, if any).
x_max := xm;
y_max := ym;
exit;
end if;
end loop;
end if;
end if;
end Inc_XY;
uf : Byte_array (0 .. 15); -- unfiltered bytes for a pixel
w1, w2 : U16;
i : Integer;
begin
if some_trace then
Ada.Text_IO.Put ("[UO]");
end if;
-- Depending on the row size, bpp, etc., we can have
-- several rows, or less than one, being displayed
-- with the present uncompressed data batch.
--
i := data'First;
if i > data'Last then
reject := 0;
return; -- data is empty, do nothing
end if;
--
-- Main loop over data
--
loop
if x = X_range'First then -- pseudo-column for filter method
exit when i > data'Last;
begin
current_filter := Filter_method_0'Val (data (i));
if some_trace then
filter_stat (current_filter) := filter_stat (current_filter) + 1;
end if;
exception
when Constraint_Error =>
raise error_in_image_data with
"PNG: wrong filter code, row #" &
Integer'Image (y) & " code:" & U8'Image (data (i));
end;
if interlaced then
case pass is
when 1 .. 6 =>
null; -- Set_X_Y for each pixel
when 7 =>
Set_X_Y (0, Y_range'Last - 1 - y * 2);
end case;
else
Set_X_Y (0, Y_range'Last - y);
end if;
i := i + 1;
else -- normal pixel
--
-- We quit the loop if all data has been used (except for an
-- eventual incomplete pixel)
exit when i > data'Last - (bytes_to_unfilter - 1);
-- NB, for per-channel bpp < 8:
-- 7.2 Scanlines - some low-order bits of the
-- last byte of a scanline may go unused.
case subformat_id is
when 0 =>
-----------------------
-- Type 0: Greyscale --
-----------------------
case png_bits_per_pixel is
when 1 | 2 | 4 =>
Unfilter_bytes (data (i .. i), uf (0 .. 0));
i := i + 1;
declare
b : U8;
shift : Integer := 8 - png_bits_per_pixel;
max : constant U8 := U8 (Shift_Left (Unsigned_32'(1), png_bits_per_pixel) - 1);
-- Scaling factor to obtain the correct color value on a 0..255 range.
-- The division is exact in all cases (bpp=8,4,2,1),
-- since 255 = 3 * 5 * 17 and max = 255, 15, 3 or 1.
-- This factor ensures: 0 -> 0, max -> 255
factor : constant U8 := 255 / max;
begin
-- loop through the number of pixels in this byte:
for k in reverse 1 .. 8 / png_bits_per_pixel loop
b := (max and U8 (Shift_Right (Unsigned_8 (uf (0)), shift))) * factor;
shift := shift - png_bits_per_pixel;
Out_Pixel_8 (b, b, b, 255);
exit when x >= x_max or k = 1;
Inc_XY;
end loop;
end;
when 8 =>
-- NB: with bpp as generic param, this case could be merged
-- into the general 1,2,4[,8] case without loss of performance
-- if the compiler is smart enough to simplify the code, given
-- the value of bits_per_pixel.
-- But we let it here for two reasons:
-- 1) a compiler might be not smart enough
-- 2) it is a very simple case, perhaps helpful for
-- understanding the algorithm.
Unfilter_bytes (data (i .. i), uf (0 .. 0));
i := i + 1;
Out_Pixel_8 (uf (0), uf (0), uf (0), 255);
when 16 =>
Unfilter_bytes (data (i .. i + 1), uf (0 .. 1));
i := i + 2;
w1 := U16 (uf (0)) * 256 + U16 (uf (1));
Out_Pixel_16 (w1, w1, w1, 65535);
when others =>
null; -- undefined in PNG standard
end case;
when 2 =>
-----------------
-- Type 2: RGB --
-----------------
case png_bits_per_pixel is
when 24 =>
Unfilter_bytes (data (i .. i + 2), uf (0 .. 2));
i := i + 3;
Out_Pixel_8 (uf (0), uf (1), uf (2), 255);
when 48 =>
Unfilter_bytes (data (i .. i + 5), uf (0 .. 5));
i := i + 6;
Out_Pixel_16 (
U16 (uf (0)) * 256 + U16 (uf (1)),
U16 (uf (2)) * 256 + U16 (uf (3)),
U16 (uf (4)) * 256 + U16 (uf (5)),
65_535
);
when others =>
null;
end case;
when 3 =>
------------------------------
-- Type 3: RGB with palette --
------------------------------
Unfilter_bytes (data (i .. i), uf (0 .. 0));
i := i + 1;
case png_bits_per_pixel is
when 1 | 2 | 4 =>
declare
shift : Integer := 8 - png_bits_per_pixel;
max : constant U8 := U8 (Shift_Left (Unsigned_32'(1), png_bits_per_pixel) - 1);
begin
-- loop through the number of pixels in this byte:
for k in reverse 1 .. 8 / png_bits_per_pixel loop
Out_Pixel_Palette (max and U8 (Shift_Right (Unsigned_8 (uf (0)), shift)));
shift := shift - png_bits_per_pixel;
exit when x >= x_max or k = 1;
Inc_XY;
end loop;
end;
when 8 =>
-- Same remark for this case (8bpp) as
-- within Image Type 0 / Greyscale above
Out_Pixel_Palette (uf (0));
when others =>
null;
end case;
when 4 =>
-------------------------------
-- Type 4: Greyscale & Alpha --
-------------------------------
case png_bits_per_pixel is
when 16 =>
Unfilter_bytes (data (i .. i + 1), uf (0 .. 1));
i := i + 2;
Out_Pixel_8 (uf (0), uf (0), uf (0), uf (1));
when 32 =>
Unfilter_bytes (data (i .. i + 3), uf (0 .. 3));
i := i + 4;
w1 := U16 (uf (0)) * 256 + U16 (uf (1));
w2 := U16 (uf (2)) * 256 + U16 (uf (3));
Out_Pixel_16 (w1, w1, w1, w2);
when others =>
null; -- undefined in PNG standard
end case;
when 6 =>
------------------
-- Type 6: RGBA --
------------------
case png_bits_per_pixel is
when 32 =>
Unfilter_bytes (data (i .. i + 3), uf (0 .. 3));
i := i + 4;
Out_Pixel_8 (uf (0), uf (1), uf (2), uf (3));
when 64 =>
Unfilter_bytes (data (i .. i + 7), uf (0 .. 7));
i := i + 8;
Out_Pixel_16 (
U16 (uf (0)) * 256 + U16 (uf (1)),
U16 (uf (2)) * 256 + U16 (uf (3)),
U16 (uf (4)) * 256 + U16 (uf (5)),
U16 (uf (6)) * 256 + U16 (uf (7))
);
when others =>
null;
end case;
when others =>
null; -- Unknown - exception already raised at header level
end case;
end if;
Inc_XY;
end loop;
-- i is between data'Last-(bytes_to_unfilter-2) and data'Last+1
reject := (data'Last + 1) - i;
if reject > 0 then
if some_trace then
Ada.Text_IO.Put ("[rj" & Integer'Image (reject) & ']');
end if;
end if;
end Output_uncompressed;
ch : Chunk_head;
-- Out of some intelligent design, there might be an IDAT chunk
-- boundary anywhere inside the zlib compressed block...
procedure Jump_IDAT is
dummy : U32;
begin
Big_endian (image.buffer, dummy); -- ending chunk's CRC
-- New chunk begins here.
loop
Read (image, ch);
exit when ch.kind /= IDAT or ch.length > 0;
end loop;
if ch.kind /= IDAT then
raise error_in_image_data with "PNG: additional data chunk must be an IDAT";
end if;
end Jump_IDAT;
---------------------------------------------------------------------
-- ** 10: Decompression ** --
-- Excerpt and simplification from UnZip.Decompress (Inflate only) --
---------------------------------------------------------------------
-- http://www.w3.org/TR/PNG/#10Compression
-- Size of sliding dictionary and circular output buffer
wsize : constant := 16#10000#;
--------------------------------------
-- Specifications of UnZ_* packages --
--------------------------------------
package UnZ_Glob is
-- I/O Buffers
-- > Sliding dictionary for unzipping, and output buffer as well
slide : Byte_array (0 .. wsize);
slide_index : Integer := 0; -- Current Position in slide
Zip_EOF : constant Boolean := False;
crc32val : Unsigned_32; -- crc calculated from data
end UnZ_Glob;
package UnZ_IO is
procedure Init_Buffers;
procedure Read_raw_byte (bt : out U8);
pragma Inline (Read_raw_byte);
package Bit_buffer is
procedure Init;
-- Read at least n bits into the bit buffer, returns the n first bits
function Read (n : Natural) return Integer;
pragma Inline (Read);
function Read_U32 (n : Natural) return Unsigned_32;
pragma Inline (Read_U32);
-- Dump n bits no longer needed from the bit buffer
procedure Dump (n : Natural);
pragma Inline (Dump);
procedure Dump_to_byte_boundary;
function Read_and_dump (n : Natural) return Integer;
pragma Inline (Read_and_dump);
function Read_and_dump_U32 (n : Natural) return Unsigned_32;
pragma Inline (Read_and_dump_U32);
end Bit_buffer;
procedure Flush (x : Natural); -- directly from slide to output stream
procedure Flush_if_full (W : in out Integer);
pragma Inline (Flush_if_full);
procedure Copy (
distance, length : Natural;
index : in out Natural);
pragma Inline (Copy);
end UnZ_IO;
package UnZ_Meth is
deflate_e_mode : constant Boolean := False;
procedure Inflate;
end UnZ_Meth;
------------------------------
-- Bodies of UnZ_* packages --
------------------------------
package body UnZ_IO is
procedure Init_Buffers is
begin
UnZ_Glob.slide_index := 0;
Bit_buffer.Init;
CRC32.Init (UnZ_Glob.crc32val);
end Init_Buffers;
procedure Read_raw_byte (bt : out U8) is
begin
if ch.length = 0 then
-- We hit the end of a PNG 'IDAT' chunk, so we go to the next one
-- - in petto, it's strange design, but well...
-- This "feature" has taken some time (and nerves) to be addressed.
-- Incidentally, to solve the mystery, I have reprogrammed the
-- whole Huffman decoding, and looked at many other wrong places!
Jump_IDAT;
end if;
Buffering.Get_Byte (image.buffer, bt);
ch.length := ch.length - 1;
end Read_raw_byte;
package body Bit_buffer is
B : Unsigned_32;
K : Integer;
procedure Init is
begin
B := 0;
K := 0;
end Init;
procedure Need (n : Natural) is
pragma Inline (Need);
bt : U8;
begin
while K < n loop
Read_raw_byte (bt);
B := B or Shift_Left (Unsigned_32 (bt), K);
K := K + 8;
end loop;
end Need;
procedure Dump (n : Natural) is
begin
B := Shift_Right (B, n);
K := K - n;
end Dump;
procedure Dump_to_byte_boundary is
begin
Dump (K mod 8);
end Dump_to_byte_boundary;
function Read_U32 (n : Natural) return Unsigned_32 is
begin
Need (n);
return B and (Shift_Left (1, n) - 1);
end Read_U32;
function Read (n : Natural) return Integer is
begin
return Integer (Read_U32 (n));
end Read;
function Read_and_dump (n : Natural) return Integer is
res : Integer;
begin
res := Read (n);
Dump (n);
return res;
end Read_and_dump;
function Read_and_dump_U32 (n : Natural) return Unsigned_32 is
res : Unsigned_32;
begin
res := Read_U32 (n);
Dump (n);
return res;
end Read_and_dump_U32;
end Bit_buffer;
old_bytes : Natural := 0;
-- how many bytes to be resent from last Inflate output
byte_mem : Byte_array (1 .. 8);
procedure Flush (x : Natural) is
begin
if full_trace then
Ada.Text_IO.Put ("[Flush..." & Integer'Image (x));
end if;
CRC32.Update (UnZ_Glob.crc32val, UnZ_Glob.slide (0 .. x - 1));
if old_bytes > 0 then
declare
app : constant Byte_array :=
byte_mem (1 .. old_bytes) & UnZ_Glob.slide (0 .. x - 1);
begin
Output_uncompressed (app, old_bytes);
-- In extreme cases (x very small), we might have some of
-- the rejected bytes from byte_mem.
if old_bytes > 0 then
byte_mem (1 .. old_bytes) := app (app'Last - (old_bytes - 1) .. app'Last);
end if;
end;
else
Output_uncompressed (UnZ_Glob.slide (0 .. x - 1), old_bytes);
if old_bytes > 0 then
byte_mem (1 .. old_bytes) := UnZ_Glob.slide (x - old_bytes .. x - 1);
end if;
end if;
if full_trace then
Ada.Text_IO.Put_Line ("finished]");
end if;
end Flush;
procedure Flush_if_full (W : in out Integer) is
begin
if W = wsize then
Flush (wsize);
W := 0;
end if;
end Flush_if_full;
----------------------------------------------------
-- Reproduction of sequences in the output slide. --
----------------------------------------------------
-- Internal:
procedure Adjust_to_Slide (
source : in out Integer;
remain : in out Natural;
part : out Integer;
index : Integer)
is
pragma Inline (Adjust_to_Slide);
begin
source := source mod wsize;
-- source and index are now in 0..WSize-1
if source > index then
part := wsize - source;
else
part := wsize - index;
end if;
-- NB: part is in 1..WSize (part cannot be 0)
if part > remain then
part := remain;
end if;
-- Now part <= remain
remain := remain - part;
-- NB: remain cannot be < 0
end Adjust_to_Slide;
procedure Copy_range (source, index : in out Natural; amount : Positive) is
pragma Inline (Copy_range);
begin
if abs (index - source) < amount then
-- if source >= index, the effect of copy is
-- just like the non-overlapping case
for count in reverse 1 .. amount loop
UnZ_Glob.slide (index) := UnZ_Glob.slide (source);
index := index + 1;
source := source + 1;
end loop;
else -- non-overlapping -> copy slice
UnZ_Glob.slide (index .. index + amount - 1) :=
UnZ_Glob.slide (source .. source + amount - 1);
index := index + amount;
source := source + amount;
end if;
end Copy_range;
-- The copying routines:
procedure Copy (
distance, length : Natural;
index : in out Natural)
is
source, part, remain : Integer;
begin
source := index - distance;
remain := length;
loop
Adjust_to_Slide (source, remain, part, index);
Copy_range (source, index, part);
Flush_if_full (index);
exit when remain = 0;
end loop;
end Copy;
end UnZ_IO;
package body UnZ_Meth is
use GID.Decoding_PNG.Huffman;
--------[ Method: Inflate ]--------
procedure Inflate_Codes (Tl, Td : p_Table_list; Bl, Bd : Integer) is
CT : p_HufT_table; -- current table
CT_idx : Integer; -- current table index
length : Natural;
E : Integer; -- table entry flag/number of extra bits
W : Integer := UnZ_Glob.slide_index;
-- more local variable for slide index
begin
if full_trace then
Ada.Text_IO.Put_Line ("Begin Inflate_codes");
end if;
-- inflate the coded data
main_loop :
while not UnZ_Glob.Zip_EOF loop
CT := Tl.table;
CT_idx := UnZ_IO.Bit_buffer.Read (Bl);
loop
E := CT (CT_idx).extra_bits;
exit when E <= 16;
if E = invalid then
raise error_in_image_data with "PNG: invalid code in Deflate compression";
end if;
-- then it's a literal
UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);
E := E - 16;
CT := CT (CT_idx).next_table;
CT_idx := UnZ_IO.Bit_buffer.Read (E);
end loop;
UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);
case E is
when 16 => -- CTE.N is a Litteral
UnZ_Glob.slide (W) := U8 (CT (CT_idx).n);
W := W + 1;
UnZ_IO.Flush_if_full (W);
when 15 => -- End of block (EOB)
if full_trace then
Ada.Text_IO.Put_Line ("Exit Inflate_codes, e=15 EOB");
end if;
exit main_loop;
when others => -- We have a length/distance
-- Get length of block to copy:
length := CT (CT_idx).n + UnZ_IO.Bit_buffer.Read_and_dump (E);
-- Decode distance of block to copy:
CT := Td.table;
CT_idx := UnZ_IO.Bit_buffer.Read (Bd);
loop
E := CT (CT_idx).extra_bits;
exit when E <= 16;
if E = invalid then
raise error_in_image_data
with "PNG: invalid code in Deflate compression (LZ distance)";
end if;
UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);
E := E - 16;
CT := CT (CT_idx).next_table;
CT_idx := UnZ_IO.Bit_buffer.Read (E);
end loop;
UnZ_IO.Bit_buffer.Dump (CT (CT_idx).bits);
UnZ_IO.Copy (
distance => CT (CT_idx).n + UnZ_IO.Bit_buffer.Read_and_dump (E),
length => length,
index => W
);
end case;
end loop main_loop;
UnZ_Glob.slide_index := W;
if full_trace then
Ada.Text_IO.Put_Line ("End Inflate_codes");
end if;
end Inflate_Codes;
procedure Inflate_stored_block is -- Actually, nothing to inflate
N : Integer;
begin
if full_trace then
Ada.Text_IO.Put_Line ("Begin Inflate_stored_block");
end if;
UnZ_IO.Bit_buffer.Dump_to_byte_boundary;
-- Get the block length and its complement
N := UnZ_IO.Bit_buffer.Read_and_dump (16);
if N /= Integer (
(not UnZ_IO.Bit_buffer.Read_and_dump_U32 (16))
and 16#ffff#)
then
raise error_in_image_data with "PNG: invalid check code in Deflate stored block";
end if;
while N > 0 and then not UnZ_Glob.Zip_EOF loop
-- Read and output the non-compressed data
N := N - 1;
UnZ_Glob.slide (UnZ_Glob.slide_index) :=
U8 (UnZ_IO.Bit_buffer.Read_and_dump (8));
UnZ_Glob.slide_index := UnZ_Glob.slide_index + 1;
UnZ_IO.Flush_if_full (UnZ_Glob.slide_index);
end loop;
if full_trace then
Ada.Text_IO.Put_Line ("End Inflate_stored_block");
end if;
end Inflate_stored_block;
-- Copy lengths for literal codes 257..285
copy_lengths_literal : Length_array (0 .. 30) :=
(3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0);
-- Extra bits for literal codes 257..285
extra_bits_literal : Length_array (0 .. 30) :=
(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, invalid, invalid);
-- Copy offsets for distance codes 0..29 (30..31: deflate_e)
copy_offset_distance : constant Length_array (0 .. 31) :=
(1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
8193, 12289, 16385, 24577, 32769, 49153);
-- Extra bits for distance codes
extra_bits_distance : constant Length_array (0 .. 31) :=
(0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14);
max_dist : Integer := 29; -- changed to 31 for deflate_e
procedure Inflate_fixed_block is
Tl, -- literal/length code table
Td : p_Table_list; -- distance code table
Bl, Bd : Integer; -- lookup bits for tl/bd
huft_incomplete : Boolean;
-- length list for HufT_build (literal table)
L : constant Length_array (0 .. 287) :=
(0 .. 143 => 8, 144 .. 255 => 9, 256 .. 279 => 7, 280 .. 287 => 8);
begin
if full_trace then
Ada.Text_IO.Put_Line ("Begin Inflate_fixed_block");
end if;
-- make a complete, but wrong code set
Bl := 7;
HufT_build (
L, 257, copy_lengths_literal, extra_bits_literal,
Tl, Bl, huft_incomplete
);
-- Make an incomplete code set
Bd := 5;
begin
HufT_build (
(0 .. max_dist => 5), 0,
copy_offset_distance, extra_bits_distance,
Td, Bd, huft_incomplete
);
if huft_incomplete then
if full_trace then
Ada.Text_IO.Put_Line (
"td is incomplete, pointer=null: " &
Boolean'Image (Td = null)
);
end if;
end if;
exception
when huft_out_of_memory | huft_error =>
HufT_free (Tl);
raise error_in_image_data
with "PNG: error in Deflate compression (Huffman #1)";
end;
Inflate_Codes (Tl, Td, Bl, Bd);
HufT_free (Tl);
HufT_free (Td);
if full_trace then
Ada.Text_IO.Put_Line ("End Inflate_fixed_block");
end if;
end Inflate_fixed_block;
procedure Inflate_dynamic_block is
bit_order : constant array (0 .. 18) of Natural :=
(16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15);
Lbits : constant := 9;
Dbits : constant := 6;
current_length : Natural := 0;
defined, number_of_lengths : Natural;
Tl, -- literal/length code tables
Td : p_Table_list; -- distance code tables
CT_dyn_idx : Integer; -- current table element
Bl, Bd : Integer; -- lookup bits for tl/bd
Nb : Natural; -- number of bit length codes
Nl : Natural; -- number of literal length codes
Nd : Natural; -- number of distance codes
-- literal/length and distance code lengths
Ll : Length_array (0 .. 288 + 32 - 1) := (others => 0);
huft_incomplete : Boolean;
procedure Repeat_length_code (amount : Natural) is
begin
if defined + amount > number_of_lengths then
raise error_in_image_data
with "PNG: invalid data in Deflate dynamic compression structure (#1)";
end if;
for c in reverse 1 .. amount loop
Ll (defined) := Natural_M32 (current_length);
defined := defined + 1;
end loop;
end Repeat_length_code;
begin
if full_trace then
Ada.Text_IO.Put_Line ("Begin Inflate_dynamic_block");
end if;
-- Read in table lengths
Nl := 257 + UnZ_IO.Bit_buffer.Read_and_dump (5);
Nd := 1 + UnZ_IO.Bit_buffer.Read_and_dump (5);
Nb := 4 + UnZ_IO.Bit_buffer.Read_and_dump (4);
if Nl > 288 or else Nd > 32 then
raise error_in_image_data
with "PNG: invalid data in Deflate dynamic compression structure (#2)";
end if;
-- Read in bit-length-code lengths.
-- The rest, Ll( Bit_Order( Nb .. 18 ) ), is already = 0
for J in 0 .. Nb - 1 loop
Ll (bit_order (J)) := Natural_M32 (UnZ_IO.Bit_buffer.Read_and_dump (3));
end loop;
-- Build decoding table for trees--single level, 7 bit lookup
Bl := 7;
begin
HufT_build (
Ll (0 .. 18), 19, empty, empty, Tl, Bl, huft_incomplete
);
if huft_incomplete then
HufT_free (Tl);
raise error_in_image_data
with "PNG: error in Deflate compression (Huffman #2)";
end if;
exception
when others =>
raise error_in_image_data
with "PNG: error in Deflate compression (Huffman #3)";
end;
-- Read in literal and distance code lengths
number_of_lengths := Nl + Nd;
defined := 0;
current_length := 0;
while defined < number_of_lengths loop
CT_dyn_idx := UnZ_IO.Bit_buffer.Read (Bl);
UnZ_IO.Bit_buffer.Dump (Tl.table (CT_dyn_idx).bits);
case Tl.table (CT_dyn_idx).n is
when 0 .. 15 => -- length of code in bits (0..15)
current_length := Tl.table (CT_dyn_idx).n;
Ll (defined) := Natural_M32 (current_length);
defined := defined + 1;
when 16 => -- repeat last length 3 to 6 times
Repeat_length_code (3 + UnZ_IO.Bit_buffer.Read_and_dump (2));
when 17 => -- 3 to 10 zero length codes
current_length := 0;
Repeat_length_code (3 + UnZ_IO.Bit_buffer.Read_and_dump (3));
when 18 => -- 11 to 138 zero length codes
current_length := 0;
Repeat_length_code (11 + UnZ_IO.Bit_buffer.Read_and_dump (7));
when others =>
if full_trace then
Ada.Text_IO.Put_Line (
"Illegal length code: " &
Integer'Image (Tl.table (CT_dyn_idx).n)
);
end if;
end case;
end loop;
HufT_free (Tl); -- free decoding table for trees
-- Build the decoding tables for literal/length codes
Bl := Lbits;
begin
HufT_build (
Ll (0 .. Nl - 1), 257,
copy_lengths_literal, extra_bits_literal,
Tl, Bl, huft_incomplete
);
if huft_incomplete then
HufT_free (Tl);
raise error_in_image_data
with "PNG: error in Deflate compression (Huffman #4)";
end if;
exception
when others =>
raise error_in_image_data
with "PNG: error in Deflate compression (Huffman #5)";
end;
-- Build the decoding tables for distance codes
Bd := Dbits;
begin
HufT_build (
Ll (Nl .. Nl + Nd - 1), 0,
copy_offset_distance, extra_bits_distance,
Td, Bd, huft_incomplete
);
if huft_incomplete then -- do nothing!
if full_trace then
Ada.Text_IO.Put_Line ("PKZIP 1.93a bug workaround");
end if;
end if;
exception
when huft_out_of_memory | huft_error =>
HufT_free (Tl);
raise error_in_image_data
with "PNG: error in Deflate compression (Huffman #6)";
end;
-- Decompress until an end-of-block code
Inflate_Codes (Tl, Td, Bl, Bd);
HufT_free (Tl);
HufT_free (Td);
if full_trace then
Ada.Text_IO.Put_Line ("End Inflate_dynamic_block");
end if;
end Inflate_dynamic_block;
procedure Inflate_Block (last_block : out Boolean) is
begin
last_block := Boolean'Val (UnZ_IO.Bit_buffer.Read_and_dump (1));
case UnZ_IO.Bit_buffer.Read_and_dump (2) is -- Block type = 0,1,2,3
when 0 => Inflate_stored_block;
when 1 => Inflate_fixed_block;
when 2 => Inflate_dynamic_block;
when others =>
raise error_in_image_data with
"PNG: error in Deflate compression: bad block type (3)";
end case;
end Inflate_Block;
procedure Inflate is
is_last_block : Boolean;
blocks : Positive := 1;
begin
if deflate_e_mode then
copy_lengths_literal (28) := 3; -- instead of 258
extra_bits_literal (28) := 16; -- instead of 0
max_dist := 31;
end if;
loop
Inflate_Block (is_last_block);
exit when is_last_block;
blocks := blocks + 1;
end loop;
UnZ_IO.Flush (UnZ_Glob.slide_index);
UnZ_Glob.slide_index := 0;
if some_trace then
Ada.Text_IO.Put ("# blocks:" & Integer'Image (blocks));
end if;
UnZ_Glob.crc32val := CRC32.Final (UnZ_Glob.crc32val);
end Inflate;
end UnZ_Meth;
--------------------------------------------------------------------
-- End of the Decompression part, and of UnZip.Decompress excerpt --
--------------------------------------------------------------------
b : U8;
z_crc, dummy : U32;
begin -- Load_specialized
--
-- For optimization reasons, bytes_to_unfilter is passed as a
-- generic parameter but should be always as below right to "/=" :
--
if bytes_to_unfilter /= Integer'Max (1, png_bits_per_pixel / 8) then
raise Program_Error;
end if;
if interlaced then
x_max := (Integer (image.width) + 7) / 8 - 1;
y_max := (Integer (image.height) + 7) / 8 - 1;
else
x_max := X_range'Last;
y_max := Y_range'Last;
end if;
main_chunk_loop :
loop
loop
Read (image, ch);
exit when ch.kind = IEND or ch.length > 0;
end loop;
case ch.kind is
when IEND => -- 11.2.5 IEND Image trailer
exit main_chunk_loop;
when IDAT => -- 11.2.4 IDAT Image data
--
-- NB: the compressed data may hold on several IDAT chunks.
-- It means that right in the middle of compressed data, you
-- can have a chunk crc, and a new IDAT header!...
--
UnZ_IO.Read_raw_byte (b); -- zlib compression method/flags code
UnZ_IO.Read_raw_byte (b); -- Additional flags/check bits
--
UnZ_IO.Init_Buffers;
-- ^ we indicate that we have a byte reserve of chunk's length,
-- minus both zlib header bytes.
UnZ_Meth.Inflate;
z_crc := 0;
for i in 1 .. 4 loop
begin
UnZ_IO.Read_raw_byte (b);
exception
when error_in_image_data =>
-- vicious IEND at the wrong place
-- basi4a08.png test image (corrupt, imho)
exit main_chunk_loop;
end;
z_crc := z_crc * 256 + U32 (b);
end loop;
-- z_crc : zlib Check value
-- if z_crc /= U32(UnZ_Glob.crc32val) then
-- ada.text_io.put(z_crc 'img & UnZ_Glob.crc32val'img);
-- raise
-- error_in_image_data with
-- "PNG: deflate stream corrupt";
-- end if;
-- ** Mystery: this check fails even with images which decompress perfectly
-- ** Is CRC init value different between zip and zlib ? Is it Adler32 ?
Big_endian (image.buffer, dummy); -- chunk's CRC
-- last IDAT chunk's CRC (then, on compressed data)
--
when tEXt => -- 11.3.4.3 tEXt Textual data
for i in 1 .. ch.length loop
Get_Byte (image.buffer, b);
if some_trace then
if b = 0 then -- separates keyword from message
Ada.Text_IO.New_Line;
else
Ada.Text_IO.Put (Character'Val (b));
end if;
end if;
end loop;
Big_endian (image.buffer, dummy); -- chunk's CRC
when others =>
-- Skip chunk data and CRC
for i in 1 .. ch.length + 4 loop
Get_Byte (image.buffer, b);
end loop;
end case;
end loop main_chunk_loop;
if some_trace then
for f in Filter_method_0 loop
Ada.Text_IO.Put_Line (
"Filter: " &
Filter_method_0'Image (f) &
Integer'Image (filter_stat (f))
);
end loop;
end if;
Feedback (100);
end Load_specialized;
-- Instances of Load_specialized, with hard-coded parameters.
-- They may take an insane amount of time to compile, and bloat the
-- .o code , but are significantly faster since they make the
-- compiler skip corresponding tests at pixel level.
-- These instances are for most current PNG sub-formats.
procedure Load_interlaced_1pal is new Load_specialized (True, 1, 1, 3);
procedure Load_interlaced_2pal is new Load_specialized (True, 2, 1, 3);
procedure Load_interlaced_4pal is new Load_specialized (True, 4, 1, 3);
procedure Load_interlaced_8pal is new Load_specialized (True, 8, 1, 3);
procedure Load_interlaced_24 is new Load_specialized (True, 24, 3, 2);
procedure Load_interlaced_32 is new Load_specialized (True, 32, 4, 6);
--
procedure Load_straight_1pal is new Load_specialized (False, 1, 1, 3);
procedure Load_straight_2pal is new Load_specialized (False, 2, 1, 3);
procedure Load_straight_4pal is new Load_specialized (False, 4, 1, 3);
procedure Load_straight_8pal is new Load_specialized (False, 8, 1, 3);
procedure Load_straight_24 is new Load_specialized (False, 24, 3, 2);
procedure Load_straight_32 is new Load_specialized (False, 32, 4, 6);
--
-- For unusual sub-formats, we prefer to fall back to the
-- slightly slower, general version, where parameters values
-- are not known at compile-time:
--
procedure Load_general is new
Load_specialized (
interlaced => image.interlaced,
png_bits_per_pixel => image.bits_per_pixel,
bytes_to_unfilter => Integer'Max (1, image.bits_per_pixel / 8),
subformat_id => image.subformat_id
);
begin -- Load
--
-- All these case tests are better done at the picture
-- level than at the pixel level.
--
case image.subformat_id is
when 2 => -- RGB
case image.bits_per_pixel is
when 24 =>
if image.interlaced then
Load_interlaced_24;
else
Load_straight_24;
end if;
when others =>
Load_general;
end case;
when 3 => -- Palette
case image.bits_per_pixel is
when 1 =>
if image.interlaced then
Load_interlaced_1pal;
else
Load_straight_1pal;
end if;
when 2 =>
if image.interlaced then
Load_interlaced_2pal;
else
Load_straight_2pal;
end if;
when 4 =>
if image.interlaced then
Load_interlaced_4pal;
else
Load_straight_4pal;
end if;
when 8 =>
if image.interlaced then
Load_interlaced_8pal;
else
Load_straight_8pal;
end if;
when others =>
Load_general;
end case;
when 6 => -- RGBA
case image.bits_per_pixel is
when 32 =>
if image.interlaced then
Load_interlaced_32;
else
Load_straight_32;
end if;
when others =>
Load_general;
end case;
when others =>
Load_general;
end case;
end Load;
end GID.Decoding_PNG;
Ada PDF Writer: Ada package for writing PDF files (.pdf).
Ada programming.
Some news about Ada PDF Writer and other Ada projects
on Gautier's blog.