1033 lines
34 KiB
Ada
1033 lines
34 KiB
Ada
-- Steps for decoding a JPEG image
|
|
--
|
|
-- 1. Huffman decompression
|
|
-- 2. Inverse quantization
|
|
-- 3. Inverse cosine transform
|
|
-- 4. Upsampling
|
|
-- 5. Color transformation
|
|
-- 6. Image reconstruction
|
|
--
|
|
-- The JPEG decoder is largely inspired
|
|
-- by the NanoJPEG code by Martin J. Fiedler
|
|
-- http://keyj.s2000.ws/?p=137
|
|
-- With the author's permission. Many thanks!
|
|
--
|
|
-- Other informations:
|
|
-- http://en.wikipedia.org/wiki/JPEG
|
|
|
|
-- !! ** Some optimizations to consider **
|
|
-- !! ssx, ssy ,ssxmax, ssymax
|
|
-- as generic parameters + specialized instances
|
|
-- !! consider only power-of-two upsampling factors ?
|
|
-- !! simplify upsampling loops in case of power-of-two upsampling factors
|
|
-- using Shift_Right
|
|
-- !! Col_IDCT output direct to "flat", or something similar to NanoJPEG
|
|
|
|
with GID.Buffering;
|
|
with Ada.Text_IO, Ada.Exceptions, Ada.IO_Exceptions;
|
|
|
|
package body GID.Decoding_JPG is
|
|
|
|
use GID.Buffering;
|
|
use Ada.Text_IO, Ada.Exceptions;
|
|
|
|
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
|
|
Get_Byte(from, b);
|
|
n:= n * 256 + Number(b);
|
|
end loop;
|
|
end Big_endian_number;
|
|
|
|
procedure Big_endian is new Big_endian_number( U16 );
|
|
|
|
procedure Read( image: in out image_descriptor; sh: out Segment_head) is
|
|
b: U8;
|
|
id: constant array(JPEG_marker) of U8:=
|
|
( SOI => 16#D8#,
|
|
--
|
|
SOF_0 => 16#C0#, SOF_1 => 16#C1#, SOF_2 => 16#C2#, SOF_3 => 16#C3#,
|
|
SOF_5 => 16#C5#, SOF_6 => 16#C6#, SOF_7 => 16#C7#, SOF_8 => 16#C8#,
|
|
SOF_9 => 16#C9#, SOF_10 => 16#CA#, SOF_11 => 16#CB#, SOF_13 => 16#CD#,
|
|
SOF_14 => 16#CE#, SOF_15 => 16#CF#,
|
|
--
|
|
DHT => 16#C4#,
|
|
DAC => 16#CC#,
|
|
DQT => 16#DB#,
|
|
DRI => 16#DD#,
|
|
--
|
|
APP_0 => 16#E0#, APP_1 => 16#E1#, APP_2 => 16#E2#, APP_3 => 16#E3#,
|
|
APP_4 => 16#E4#, APP_5 => 16#E5#, APP_6 => 16#E6#, APP_7 => 16#E7#,
|
|
APP_8 => 16#E8#, APP_9 => 16#E9#, APP_10 => 16#EA#, APP_11 => 16#EB#,
|
|
APP_12 => 16#EC#, APP_13 => 16#ED#, APP_14 => 16#EE#,
|
|
--
|
|
COM => 16#FE#,
|
|
SOS => 16#DA#,
|
|
EOI => 16#D9#
|
|
);
|
|
begin
|
|
Get_Byte(image.buffer, b);
|
|
if b /= 16#FF# then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: expected marker here"
|
|
);
|
|
end if;
|
|
Get_Byte(image.buffer, b);
|
|
for m in id'Range loop
|
|
if id(m)= b then
|
|
sh.kind:= m;
|
|
Big_endian(image.buffer, sh.length);
|
|
sh.length:= sh.length - 2;
|
|
-- We consider length of contents, without the FFxx marker.
|
|
if some_trace then
|
|
Put_Line(
|
|
"Segment [" & JPEG_marker'Image(sh.kind) &
|
|
"], length:" & U16'Image(sh.length));
|
|
end if;
|
|
return;
|
|
end if;
|
|
end loop;
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: unknown marker here: FF, " & U8'Image(b)
|
|
);
|
|
end Read;
|
|
|
|
shift_arg: constant array(0..15) of Integer:=
|
|
(1 => 0, 2 => 1, 4 => 2, 8 => 3, others => -1);
|
|
|
|
-- SOF - Start Of Frame (the real header)
|
|
procedure Read_SOF(image: in out Image_descriptor; sh: Segment_head) is
|
|
use Bounded_255;
|
|
b, bits_pp_primary: U8;
|
|
w, h: U16;
|
|
compo: JPEG_defs.Component;
|
|
begin
|
|
case sh.kind is
|
|
when SOF_0 =>
|
|
image.detailed_format:= To_Bounded_String("JPEG, Baseline DCT (SOF_0)");
|
|
when SOF_2 =>
|
|
image.detailed_format:= To_Bounded_String("JPEG, Progressive DCT (SOF_2)");
|
|
image.interlaced:= True;
|
|
when others =>
|
|
Raise_exception(
|
|
unsupported_image_subformat'Identity,
|
|
"JPEG: image type not yet supported: " & JPEG_marker'Image(sh.kind)
|
|
);
|
|
end case;
|
|
Get_Byte(image.buffer, bits_pp_primary);
|
|
if bits_pp_primary /= 8 then
|
|
Raise_exception(
|
|
unsupported_image_subformat'Identity,
|
|
"Bits per primary color=" & U8'Image(bits_pp_primary)
|
|
);
|
|
end if;
|
|
image.bits_per_pixel:= 3 * Positive(bits_pp_primary);
|
|
Big_endian(image.buffer, h);
|
|
Big_endian(image.buffer, w);
|
|
image.width:= Natural(w);
|
|
image.height:= Natural(h);
|
|
-- number of components:
|
|
Get_Byte(image.buffer, b);
|
|
image.subformat_id:= Integer(b);
|
|
--
|
|
image.JPEG_stuff.max_samples_hor:= 0;
|
|
image.JPEG_stuff.max_samples_ver:= 0;
|
|
-- for each component: 3 bytes
|
|
for i in 1..image.subformat_id loop
|
|
-- component id (1 = Y, 2 = Cb, 3 = Cr, 4 = I, 5 = Q)
|
|
Get_Byte(image.buffer, b);
|
|
compo:= JPEG_defs.Component'Val(b - 1);
|
|
image.JPEG_stuff.components(compo):= True;
|
|
declare
|
|
stuff: JPEG_stuff_type renames image.JPEG_stuff;
|
|
info: JPEG_defs.info_per_component_A renames stuff.info(compo);
|
|
begin
|
|
-- sampling factors (bit 0-3 vert., 4-7 hor.)
|
|
Get_Byte(image.buffer, b);
|
|
info.samples_ver:= Natural(b mod 16);
|
|
info.samples_hor:= Natural(b / 16);
|
|
stuff.max_samples_hor:=
|
|
Integer'Max(stuff.max_samples_hor, info.samples_hor);
|
|
stuff.max_samples_ver:=
|
|
Integer'Max(stuff.max_samples_ver, info.samples_ver);
|
|
-- quantization table number
|
|
Get_Byte(image.buffer, b);
|
|
info.qt_assoc:= Natural(b);
|
|
end;
|
|
end loop;
|
|
for c in Component loop
|
|
if image.JPEG_stuff.components(c) then
|
|
declare
|
|
stuff: JPEG_stuff_type renames image.JPEG_stuff;
|
|
info: JPEG_defs.info_per_component_A renames stuff.info(c);
|
|
begin
|
|
info.up_factor_x:= stuff.max_samples_hor / info.samples_hor;
|
|
info.up_factor_y:= stuff.max_samples_ver / info.samples_ver;
|
|
info.shift_x:= shift_arg(info.up_factor_x);
|
|
info.shift_y:= shift_arg(info.up_factor_y);
|
|
end;
|
|
end if;
|
|
end loop;
|
|
if Natural(sh.length) < 6 + 3 * image.subformat_id then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: SOF segment too short"
|
|
);
|
|
end if;
|
|
if some_trace then
|
|
Put_Line("Frame has following components:");
|
|
for c in JPEG_defs.component loop
|
|
Put_Line(
|
|
JPEG_defs.Component'Image(c) & " -> " &
|
|
Boolean'Image(image.JPEG_stuff.components(c))
|
|
);
|
|
end loop;
|
|
end if;
|
|
if image.JPEG_stuff.components = YCbCr_set then
|
|
image.JPEG_stuff.color_space:= YCbCr;
|
|
elsif image.JPEG_stuff.components = Y_Grey_set then
|
|
image.JPEG_stuff.color_space:= Y_Grey;
|
|
image.greyscale:= True;
|
|
elsif image.JPEG_stuff.components = CMYK_set then
|
|
image.JPEG_stuff.color_space:= CMYK;
|
|
else
|
|
Raise_exception(
|
|
unsupported_image_subformat'Identity,
|
|
"JPEG: only YCbCr, Y_Grey and CMYK color spaces are currently supported"
|
|
);
|
|
end if;
|
|
image.detailed_format:= image.detailed_format & ", " &
|
|
JPEG_defs.Supported_color_space'Image(image.JPEG_stuff.color_space);
|
|
if some_trace then
|
|
Put_Line(
|
|
"Color space: " &
|
|
JPEG_defs.Supported_color_space'Image(image.JPEG_stuff.color_space)
|
|
);
|
|
end if;
|
|
if image.JPEG_stuff.color_space = CMYK then
|
|
Raise_exception(
|
|
unsupported_image_subformat'Identity,
|
|
"JPEG: CMYK color space is currently not properly decoded"
|
|
);
|
|
end if;
|
|
end Read_SOF;
|
|
|
|
procedure Read_DHT(image: in out Image_descriptor; data_length: Natural) is
|
|
remaining: Integer:= data_length; -- data remaining in segment
|
|
b: U8;
|
|
ht_idx: Natural;
|
|
kind: AC_DC;
|
|
counts: array(1..16) of Natural;
|
|
spread, currcnt, idx: Natural;
|
|
remain_vlc: Integer;
|
|
begin
|
|
multi_tables:
|
|
loop
|
|
Get_Byte(image.buffer, b);
|
|
remaining:= remaining - 1;
|
|
if b >= 8 then
|
|
kind:= AC;
|
|
else
|
|
kind:= DC;
|
|
end if;
|
|
ht_idx:= Natural(b and 7);
|
|
if some_trace then
|
|
Put_Line(
|
|
"Huffman Table (HT) #" &
|
|
Natural'Image(ht_idx) & ", " & AC_DC'Image(kind)
|
|
);
|
|
end if;
|
|
if image.JPEG_stuff.vlc_defs(kind, ht_idx) = null then
|
|
image.JPEG_stuff.vlc_defs(kind, ht_idx):= new VLC_table;
|
|
end if;
|
|
for i in counts'range loop
|
|
Get_Byte(image.buffer, b);
|
|
remaining:= remaining - 1;
|
|
counts(i):= Natural(b);
|
|
end loop;
|
|
remain_vlc:= 65_536;
|
|
spread:= 65_536;
|
|
idx:= 0;
|
|
for codelen in counts'Range loop
|
|
spread:= spread / 2;
|
|
currcnt:= counts(codelen);
|
|
if currcnt > 0 then
|
|
if remaining < currcnt then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: DHT data too short"
|
|
);
|
|
end if;
|
|
remain_vlc:= remain_vlc - currcnt * spread;
|
|
if remain_vlc < 0 then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: DHT table too short for data"
|
|
);
|
|
end if;
|
|
for i in reverse 1..currcnt loop
|
|
Get_Byte(image.buffer, b);
|
|
for j in reverse 1..spread loop
|
|
image.JPEG_stuff.vlc_defs(kind, ht_idx)(idx):=
|
|
(bits => U8(codelen), code => b);
|
|
idx:= idx + 1;
|
|
end loop;
|
|
end loop;
|
|
remaining:= remaining - currcnt;
|
|
end if;
|
|
end loop;
|
|
while remain_vlc > 0 loop
|
|
remain_vlc:= remain_vlc - 1;
|
|
image.JPEG_stuff.vlc_defs(kind, ht_idx)(idx).bits:= 0;
|
|
idx:= idx + 1;
|
|
end loop;
|
|
exit when remaining <= 0;
|
|
end loop multi_tables;
|
|
end Read_DHT;
|
|
|
|
procedure Read_DQT(image: in out Image_descriptor; data_length: Natural) is
|
|
remaining: Integer:= data_length; -- data remaining in segment
|
|
b, q8: U8; q16: U16;
|
|
qt_idx: Natural;
|
|
high_prec: Boolean;
|
|
begin
|
|
multi_tables:
|
|
loop
|
|
Get_Byte(image.buffer, b);
|
|
remaining:= remaining - 1;
|
|
high_prec:= b >= 8;
|
|
qt_idx:= Natural(b and 7);
|
|
if some_trace then
|
|
Put_Line("Quantization Table (QT) #" & U8'Image(b));
|
|
end if;
|
|
for i in QT'Range loop
|
|
if high_prec then
|
|
Big_endian(image.buffer, q16);
|
|
remaining:= remaining - 2;
|
|
image.JPEG_stuff.qt_list(qt_idx)(i):= Natural(q16);
|
|
else
|
|
Get_Byte(image.buffer, q8);
|
|
remaining:= remaining - 1;
|
|
image.JPEG_stuff.qt_list(qt_idx)(i):= Natural(q8);
|
|
end if;
|
|
end loop;
|
|
exit when remaining <= 0;
|
|
end loop multi_tables;
|
|
end Read_DQT;
|
|
|
|
procedure Read_DRI(image: in out Image_descriptor) is
|
|
ri: U16;
|
|
begin
|
|
Big_endian(image.buffer, ri);
|
|
if some_trace then
|
|
Put_Line(" Restart interval set to:" & U16'Image(ri));
|
|
end if;
|
|
image.JPEG_stuff.restart_interval:= Natural(ri);
|
|
end Read_DRI;
|
|
|
|
procedure Read_EXIF(image: in out Image_descriptor; data_length: Natural) is
|
|
b, orientation_value: U8;
|
|
x: Natural;
|
|
Exif_signature: constant String:= "Exif" & ASCII.NUL & ASCII.NUL;
|
|
signature: String(1..6);
|
|
IFD_tag: U16;
|
|
begin
|
|
if some_trace then
|
|
Put_Line("APP1");
|
|
end if;
|
|
if data_length < 6 then
|
|
-- Skip segment data
|
|
for i in 1..data_length loop
|
|
Get_Byte(image.buffer, b);
|
|
end loop;
|
|
else
|
|
for i in 1..6 loop
|
|
Get_Byte(image.buffer, b);
|
|
signature(i):= Character'Val(b);
|
|
end loop;
|
|
if signature /= Exif_signature then
|
|
for i in 7..data_length loop
|
|
Get_Byte(image.buffer, b);
|
|
end loop;
|
|
if some_trace then
|
|
Put_Line("APP1 is not Exif");
|
|
end if;
|
|
return;
|
|
end if;
|
|
if some_trace then
|
|
Put_Line("APP1 is Exif");
|
|
end if;
|
|
for i in 7..16 loop -- TIFF 6.0 header + IFD0 entries
|
|
Get_Byte(image.buffer, b);
|
|
end loop;
|
|
x:= 17;
|
|
while x <= data_length - 12 loop
|
|
Get_Byte(image.buffer, b);
|
|
IFD_tag:= U16(b);
|
|
Get_Byte(image.buffer, b);
|
|
IFD_tag:= IFD_tag + 16#100# * U16(b);
|
|
for i in 3..8 loop
|
|
Get_Byte(image.buffer, b);
|
|
end loop;
|
|
Get_Byte(image.buffer, orientation_value);
|
|
for i in 10..12 loop
|
|
Get_Byte(image.buffer, b);
|
|
end loop;
|
|
x:= x + 12;
|
|
if IFD_tag = 16#112# then
|
|
case orientation_value is
|
|
when 1 =>
|
|
image.display_orientation:= Unchanged;
|
|
when 8 =>
|
|
image.display_orientation:= Rotation_90;
|
|
when 3 =>
|
|
image.display_orientation:= Rotation_180;
|
|
when 6 =>
|
|
image.display_orientation:= Rotation_270;
|
|
when others =>
|
|
image.display_orientation:= Unchanged;
|
|
end case;
|
|
if some_trace then
|
|
Put_Line(
|
|
"IFD tag: Orientation set to: " &
|
|
Orientation'Image(image.display_orientation)
|
|
);
|
|
end if;
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
-- Skip rest of data
|
|
for i in x..data_length loop
|
|
Get_Byte(image.buffer, b);
|
|
end loop;
|
|
end if;
|
|
end Read_EXIF;
|
|
|
|
--------------------
|
|
-- Image decoding --
|
|
--------------------
|
|
|
|
procedure Load (
|
|
image : in out Image_descriptor;
|
|
next_frame: out Ada.Calendar.Day_Duration
|
|
)
|
|
is
|
|
--
|
|
-- Bit buffer
|
|
--
|
|
buf: U32:= 0;
|
|
bufbits: Natural:= 0;
|
|
|
|
function Show_bits(bits: Natural) return Natural is
|
|
newbyte, marker: U8;
|
|
begin
|
|
if bits=0 then
|
|
return 0;
|
|
end if;
|
|
while bufbits < bits loop
|
|
begin
|
|
Get_Byte(image.buffer, newbyte);
|
|
bufbits:= bufbits + 8;
|
|
buf:= buf * 256 + U32(newbyte);
|
|
if newbyte = 16#FF# then
|
|
Get_Byte(image.buffer, marker);
|
|
case marker is
|
|
when 0 =>
|
|
null;
|
|
when 16#D9# =>
|
|
null; -- !! signal end
|
|
when 16#D0# .. 16#D7# =>
|
|
bufbits:= bufbits + 8;
|
|
buf:= buf * 256 + U32(marker);
|
|
when others =>
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: Invalid code (bit buffer)"
|
|
);
|
|
end case;
|
|
end if;
|
|
exception
|
|
when Ada.IO_Exceptions.End_Error =>
|
|
newbyte:= 16#FF#;
|
|
bufbits:= bufbits + 8;
|
|
buf:= buf * 256 + U32(newbyte);
|
|
end;
|
|
end loop;
|
|
return Natural(
|
|
Shift_Right(buf, bufbits - bits)
|
|
and
|
|
(Shift_Left(1, bits)-1)
|
|
);
|
|
end Show_bits;
|
|
|
|
procedure Skip_bits(bits: Natural) is
|
|
pragma Inline(Skip_bits);
|
|
dummy: Integer;
|
|
pragma Warnings(off, dummy);
|
|
begin
|
|
if bufbits < bits then
|
|
dummy:= Show_bits(bits);
|
|
end if;
|
|
bufbits:= bufbits - bits;
|
|
end Skip_bits;
|
|
|
|
function Get_bits(bits: Natural) return Integer is
|
|
pragma Inline(Get_bits);
|
|
res: constant Integer:= Show_bits(bits);
|
|
begin
|
|
Skip_bits(bits);
|
|
return res;
|
|
end Get_bits;
|
|
|
|
--
|
|
|
|
type Info_per_component_B is record
|
|
ht_idx_AC : Natural;
|
|
ht_idx_DC : Natural;
|
|
width, height, stride: Natural;
|
|
dcpred: Integer:= 0;
|
|
end record;
|
|
|
|
info_A: Component_info_A renames image.JPEG_stuff.info;
|
|
info_B: array(Component) of Info_per_component_B;
|
|
|
|
procedure Get_VLC(
|
|
vlc: VLC_table;
|
|
code: out U8;
|
|
value_ret: out Integer
|
|
)
|
|
is
|
|
-- Step 1 happens here: Huffman decompression
|
|
value: Integer:= Show_bits(16);
|
|
bits : Natural:= Natural(vlc(value).bits);
|
|
begin
|
|
if bits = 0 then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: VLC table: bits = 0"
|
|
);
|
|
end if;
|
|
Skip_bits(bits);
|
|
value:= Integer(vlc(value).code);
|
|
code:= U8(value);
|
|
bits:= Natural(U32(value) and 15);
|
|
value_ret:= 0;
|
|
if bits /= 0 then
|
|
value:= Get_bits(bits);
|
|
if value < Integer(Shift_Left(U32'(1), bits - 1)) then
|
|
value:= value + 1 - Integer(Shift_Left(U32'(1), bits));
|
|
end if;
|
|
value_ret:= value;
|
|
end if;
|
|
end Get_VLC;
|
|
|
|
function Clip(x: Integer) return Integer is
|
|
pragma Inline(Clip);
|
|
begin
|
|
if x < 0 then
|
|
return 0;
|
|
elsif x > 255 then
|
|
return 255;
|
|
else
|
|
return x;
|
|
end if;
|
|
end Clip;
|
|
|
|
type Block_8x8 is array(0..63) of Integer;
|
|
|
|
-- Ordering within a 8x8 block, in zig-zag
|
|
zig_zag: constant Block_8x8:=
|
|
( 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18,
|
|
11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20,
|
|
13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43,
|
|
36, 29, 22, 15, 23, 30, 37, 44, 51, 58, 59, 52, 45,
|
|
38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 );
|
|
|
|
procedure Decode_Block(c: Component; block: in out Block_8x8) is
|
|
value, coef: Integer;
|
|
code: U8;
|
|
qt: JPEG_defs.QT renames image.JPEG_stuff.qt_list(info_A(c).qt_assoc);
|
|
--
|
|
W1: constant:= 2841;
|
|
W2: constant:= 2676;
|
|
W3: constant:= 2408;
|
|
W5: constant:= 1609;
|
|
W6: constant:= 1108;
|
|
W7: constant:= 565;
|
|
--
|
|
procedure Row_IDCT(start: Integer) is
|
|
pragma Inline(Row_IDCT);
|
|
x0, x1, x2, x3, x4, x5, x6, x7, x8, val: Integer;
|
|
begin
|
|
x1:= block(start + 4) * 2**11;
|
|
x2:= block(start + 6);
|
|
x3:= block(start + 2);
|
|
x4:= block(start + 1);
|
|
x5:= block(start + 7);
|
|
x6:= block(start + 5);
|
|
x7:= block(start + 3);
|
|
if x1=0 and x2=0 and x3=0 and x4=0 and x5=0 and x6=0 and x7=0 then
|
|
val:= block(start + 0) * 8;
|
|
block(start + 0 .. start + 7):= (others => val);
|
|
else
|
|
x0:= (block(start + 0) * 2**11) + 128;
|
|
x8:= W7 * (x4 + x5);
|
|
x4:= x8 + (W1 - W7) * x4;
|
|
x5:= x8 - (W1 + W7) * x5;
|
|
x8:= W3 * (x6 + x7);
|
|
x6:= x8 - (W3 - W5) * x6;
|
|
x7:= x8 - (W3 + W5) * x7;
|
|
x8:= x0 + x1;
|
|
x0:= x0 - x1;
|
|
x1:= W6 * (x3 + x2);
|
|
x2:= x1 - (W2 + W6) * x2;
|
|
x3:= x1 + (W2 - W6) * x3;
|
|
x1:= x4 + x6;
|
|
x4:= x4 - x6;
|
|
x6:= x5 + x7;
|
|
x5:= x5 - x7;
|
|
x7:= x8 + x3;
|
|
x8:= x8 - x3;
|
|
x3:= x0 + x2;
|
|
x0:= x0 - x2;
|
|
x2:= (181 * (x4 + x5) + 128) / 256;
|
|
x4:= (181 * (x4 - x5) + 128) / 256;
|
|
block(start + 0):= (x7 + x1) / 256;
|
|
block(start + 1):= (x3 + x2) / 256;
|
|
block(start + 2):= (x0 + x4) / 256;
|
|
block(start + 3):= (x8 + x6) / 256;
|
|
block(start + 4):= (x8 - x6) / 256;
|
|
block(start + 5):= (x0 - x4) / 256;
|
|
block(start + 6):= (x3 - x2) / 256;
|
|
block(start + 7):= (x7 - x1) / 256;
|
|
end if;
|
|
end Row_IDCT;
|
|
|
|
procedure Col_IDCT(start: Integer) is
|
|
pragma Inline(Col_IDCT);
|
|
x0, x1, x2, x3, x4, x5, x6, x7, x8, val: Integer;
|
|
begin
|
|
x1:= block(start + 8*4) * 256;
|
|
x2:= block(start + 8*6);
|
|
x3:= block(start + 8*2);
|
|
x4:= block(start + 8*1);
|
|
x5:= block(start + 8*7);
|
|
x6:= block(start + 8*5);
|
|
x7:= block(start + 8*3);
|
|
if x1=0 and x2=0 and x3=0 and x4=0 and x5=0 and x6=0 and x7=0 then
|
|
val:= Clip(((block(start) + 32) / 2**6) + 128);
|
|
for row in reverse 0..7 loop
|
|
block(start + row * 8):= val;
|
|
end loop;
|
|
else
|
|
x0:= (block(start) * 256) + 8192;
|
|
x8:= W7 * (x4 + x5) + 4;
|
|
x4:= (x8 + (W1 - W7) * x4) / 8;
|
|
x5:= (x8 - (W1 + W7) * x5) / 8;
|
|
x8:= W3 * (x6 + x7) + 4;
|
|
x6:= (x8 - (W3 - W5) * x6) / 8;
|
|
x7:= (x8 - (W3 + W5) * x7) / 8;
|
|
x8:= x0 + x1;
|
|
x0:= x0 - x1;
|
|
x1:= W6 * (x3 + x2) + 4;
|
|
x2:= (x1 - (W2 + W6) * x2) / 8;
|
|
x3:= (x1 + (W2 - W6) * x3) / 8;
|
|
x1:= x4 + x6;
|
|
x4:= x4 - x6;
|
|
x6:= x5 + x7;
|
|
x5:= x5 - x7;
|
|
x7:= x8 + x3;
|
|
x8:= x8 - x3;
|
|
x3:= x0 + x2;
|
|
x0:= x0 - x2;
|
|
x2:= (181 * (x4 + x5) + 128) / 256;
|
|
x4:= (181 * (x4 - x5) + 128) / 256;
|
|
block(start + 8*0):= Clip(((x7 + x1) / 2**14) + 128);
|
|
block(start + 8*1):= Clip(((x3 + x2) / 2**14) + 128);
|
|
block(start + 8*2):= Clip(((x0 + x4) / 2**14) + 128);
|
|
block(start + 8*3):= Clip(((x8 + x6) / 2**14) + 128);
|
|
block(start + 8*4):= Clip(((x8 - x6) / 2**14) + 128);
|
|
block(start + 8*5):= Clip(((x0 - x4) / 2**14) + 128);
|
|
block(start + 8*6):= Clip(((x3 - x2) / 2**14) + 128);
|
|
block(start + 8*7):= Clip(((x7 - x1) / 2**14) + 128);
|
|
end if;
|
|
end Col_IDCT;
|
|
|
|
begin -- Decode_Block
|
|
--
|
|
-- Step 2 happens here: Inverse quantization
|
|
Get_VLC(image.JPEG_stuff.vlc_defs(DC, info_B(c).ht_idx_DC).all, code, value);
|
|
-- First value in block (0: top left) uses a predictor.
|
|
info_B(c).dcpred:= info_B(c).dcpred + value;
|
|
block:= (0 => info_B(c).dcpred * qt(0), others => 0);
|
|
coef:= 0;
|
|
loop
|
|
Get_VLC(image.JPEG_stuff.vlc_defs(AC, info_B(c).ht_idx_AC).all, code, value);
|
|
exit when code = 0; -- EOB
|
|
if (code and 16#0F#) = 0 and code /= 16#F0# then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: error in VLC AC code for de-quantization"
|
|
);
|
|
end if;
|
|
coef:= coef + Integer(Shift_Right(code, 4)) + 1;
|
|
if coef > 63 then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: coefficient for de-quantization is > 63"
|
|
);
|
|
end if;
|
|
block(zig_zag(coef)):= value * qt(coef);
|
|
exit when coef = 63;
|
|
end loop;
|
|
-- Step 3 happens here: Inverse cosine transform
|
|
for row in 0..7 loop
|
|
Row_IDCT(row * 8);
|
|
end loop;
|
|
for col in 0..7 loop
|
|
Col_IDCT(col);
|
|
end loop;
|
|
end Decode_Block;
|
|
|
|
type Macro_block is array(
|
|
Component range <>, -- component
|
|
Positive range <>, -- x sample range
|
|
Positive range <> -- y sample range
|
|
) of Block_8x8;
|
|
|
|
procedure Out_Pixel_8(br, bg, bb: U8) is
|
|
pragma Inline(Out_Pixel_8);
|
|
ba: constant:= 255;
|
|
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(
|
|
16#101# * Primary_color_range(br),
|
|
16#101# * Primary_color_range(bg),
|
|
16#101# * Primary_color_range(bb),
|
|
16#101# * Primary_color_range(ba)
|
|
-- 16#101# because max intensity FF goes to FFFF
|
|
);
|
|
when others =>
|
|
raise invalid_primary_color_range;
|
|
end case;
|
|
end Out_Pixel_8;
|
|
|
|
-- !! might be generic parameters
|
|
ssxmax: constant Natural:= image.JPEG_stuff.max_samples_hor;
|
|
ssymax: constant Natural:= image.JPEG_stuff.max_samples_ver;
|
|
|
|
procedure Upsampling_and_output(
|
|
m: Macro_block;
|
|
x0, y0: Natural
|
|
)
|
|
is
|
|
flat: array(Component, 0..8*ssxmax-1, 0..8*ssymax-1) of Integer;
|
|
|
|
generic
|
|
color_space: Supported_color_space;
|
|
procedure Color_transformation_and_output;
|
|
--
|
|
procedure Color_transformation_and_output is
|
|
y_val, cb_val, cr_val, c_val, m_val, w_val: Integer;
|
|
y_val_8: U8;
|
|
begin
|
|
for ymb in flat'Range(3) loop
|
|
exit when y0+ymb >= image.height;
|
|
Set_X_Y(x0, image.height-1-(y0+ymb));
|
|
for xmb in flat'Range(2) loop
|
|
exit when x0+xmb >= image.width;
|
|
case color_space is
|
|
when YCbCR =>
|
|
y_val := flat(Y, xmb, ymb) * 256;
|
|
cb_val:= flat(Cb, xmb, ymb) - 128;
|
|
cr_val:= flat(Cr, xmb, ymb) - 128;
|
|
Out_pixel_8(
|
|
br => U8(Clip((y_val + 359 * cr_val + 128) / 256)),
|
|
bg => U8(Clip((y_val - 88 * cb_val - 183 * cr_val + 128) / 256)),
|
|
bb => U8(Clip((y_val + 454 * cb_val + 128) / 256))
|
|
);
|
|
when Y_Grey =>
|
|
y_val_8:= U8(flat(Y, xmb, ymb));
|
|
Out_pixel_8(y_val_8, y_val_8, y_val_8);
|
|
when CMYK =>
|
|
-- !! find a working conversion formula.
|
|
-- perhaps it is more complicated (APP_2
|
|
-- color profile must be used ?)
|
|
c_val:= flat(Y, xmb, ymb);
|
|
m_val:= flat(Cb, xmb, ymb);
|
|
y_val:= flat(Cr, xmb, ymb);
|
|
w_val:= flat(I, xmb, ymb)-255;
|
|
Out_pixel_8(
|
|
br => U8(255-Clip(c_val+w_val)),
|
|
bg => U8(255-Clip(m_val+w_val)),
|
|
bb => U8(255-Clip(y_val+w_val))
|
|
);
|
|
end case;
|
|
end loop;
|
|
end loop;
|
|
end Color_transformation_and_output;
|
|
--
|
|
procedure Ct_YCbCr is new Color_transformation_and_output(YCbCr);
|
|
procedure Ct_Y_Grey is new Color_transformation_and_output(Y_Grey);
|
|
procedure Ct_CMYK is new Color_transformation_and_output(CMYK);
|
|
|
|
blk_idx: Integer;
|
|
upsx, upsy: Natural;
|
|
begin
|
|
-- Step 4 happens here: Upsampling
|
|
for c in Component loop
|
|
if image.JPEG_stuff.components(c) then
|
|
upsx:= info_A(c).up_factor_x;
|
|
upsy:= info_A(c).up_factor_y;
|
|
for x in reverse 1..info_A(c).samples_hor loop
|
|
for y in reverse 1..info_A(c).samples_ver loop
|
|
-- We are at the 8x8 block level
|
|
blk_idx:= 63;
|
|
for y8 in reverse 0..7 loop
|
|
for x8 in reverse 0..7 loop
|
|
declare
|
|
val: constant Integer:= m(c,x,y)(blk_idx);
|
|
big_pixel_x: constant Natural:= upsx * (x8 + 8*(x-1));
|
|
big_pixel_y: constant Natural:= upsy * (y8 + 8*(y-1));
|
|
begin
|
|
-- Repeat pixels for component c, sample (x,y),
|
|
-- position (x8,y8).
|
|
for rx in reverse 0..upsx-1 loop
|
|
for ry in reverse 0..upsy-1 loop
|
|
flat(c, rx + big_pixel_x, ry + big_pixel_y):= val;
|
|
end loop;
|
|
end loop;
|
|
end;
|
|
blk_idx:= blk_idx - 1;
|
|
end loop;
|
|
end loop;
|
|
end loop;
|
|
end loop;
|
|
end if;
|
|
end loop;
|
|
-- Step 5 and 6 happen here: Color transformation and output
|
|
case image.JPEG_stuff.color_space is
|
|
when YCbCr =>
|
|
Ct_YCbCr;
|
|
when Y_Grey =>
|
|
Ct_Y_Grey;
|
|
when CMYK =>
|
|
Ct_CMYK;
|
|
end case;
|
|
end Upsampling_and_output;
|
|
|
|
-- Start Of Scan (and image data which follow)
|
|
--
|
|
procedure Read_SOS is
|
|
components, b: U8;
|
|
compo: Component;
|
|
mbx, mby: Natural:= 0;
|
|
mbsizex, mbsizey, mbwidth, mbheight: Natural;
|
|
rstcount: Natural:= image.JPEG_stuff.restart_interval;
|
|
nextrst: U16:= 0;
|
|
w: U16;
|
|
start_spectral_selection,
|
|
end_spectral_selection,
|
|
successive_approximation: U8;
|
|
begin
|
|
Get_Byte(image.buffer, components);
|
|
if some_trace then
|
|
Put_Line(
|
|
"Start of Scan (SOS), with" & U8'Image(components) & " components"
|
|
);
|
|
end if;
|
|
if image.subformat_id /= Natural(components) then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: components mismatch in Scan segment"
|
|
);
|
|
end if;
|
|
for i in 1..components loop
|
|
Get_Byte(image.buffer, b);
|
|
compo:= Component'Val(b - 1);
|
|
if not image.JPEG_stuff.components(compo) then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: component " & Component'Image(compo) &
|
|
" has not been defined in the header (SOF) segment"
|
|
);
|
|
end if;
|
|
-- Huffman table selection
|
|
Get_Byte(image.buffer, b);
|
|
info_B(compo).ht_idx_AC:= Natural(b mod 16);
|
|
info_B(compo).ht_idx_DC:= Natural(b / 16);
|
|
end loop;
|
|
-- Parameters for progressive display format (SOF_2)
|
|
Get_Byte(image.buffer, start_spectral_selection);
|
|
Get_Byte(image.buffer, end_spectral_selection);
|
|
Get_Byte(image.buffer, successive_approximation);
|
|
--
|
|
-- End of SOS segment, image data follow.
|
|
--
|
|
mbsizex:= ssxmax * 8; -- pixels in a row of a macro-block
|
|
mbsizey:= ssymax * 8; -- pixels in a column of a macro-block
|
|
mbwidth := (image.width + mbsizex - 1) / mbsizex;
|
|
-- width in macro-blocks
|
|
mbheight:= (image.height + mbsizey - 1) / mbsizey;
|
|
-- height in macro-blocks
|
|
if some_trace then
|
|
Put_Line(" mbsizex = " & Integer'Image(mbsizex));
|
|
Put_Line(" mbsizey = " & Integer'Image(mbsizey));
|
|
Put_Line(" mbwidth = " & Integer'Image(mbwidth));
|
|
Put_Line(" mbheight = " & Integer'Image(mbheight));
|
|
end if;
|
|
for c in Component loop
|
|
if image.JPEG_stuff.components(c) then
|
|
info_B(c).width := (image.width * info_A(c).samples_hor + ssxmax - 1) / ssxmax;
|
|
info_B(c).height:= (image.height * info_A(c).samples_ver + ssymax - 1) / ssymax;
|
|
info_B(c).stride:= (mbwidth * mbsizex * info_A(c).samples_hor) / ssxmax;
|
|
if some_trace then
|
|
Put_Line(" Details for component " & Component'Image(c));
|
|
Put_Line(" samples in x " & Integer'Image(info_A(c).samples_hor));
|
|
Put_Line(" samples in y " & Integer'Image(info_A(c).samples_ver));
|
|
Put_Line(" width " & Integer'Image(info_B(c).width));
|
|
Put_Line(" height " & Integer'Image(info_B(c).height));
|
|
Put_Line(" stride " & Integer'Image(info_B(c).stride));
|
|
Put_Line(
|
|
" AC/DC table index " &
|
|
Integer'Image(info_B(compo).ht_idx_AC) & ", " &
|
|
Integer'Image(info_B(compo).ht_idx_DC)
|
|
);
|
|
end if;
|
|
if (info_B(c).width < 3 and info_A(c).samples_hor /= ssxmax) or
|
|
(info_B(c).height < 3 and info_A(c).samples_ver /= ssymax)
|
|
then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: component " & Component'Image(c) &
|
|
": sample dimension mismatch"
|
|
);
|
|
end if;
|
|
end if;
|
|
end loop;
|
|
--
|
|
if image.interlaced then
|
|
Raise_exception(
|
|
unsupported_image_subformat'Identity,
|
|
"JPEG: progressive format not yet functional"
|
|
);
|
|
end if;
|
|
declare
|
|
mb: Macro_block(Component, 1..ssxmax, 1..ssymax);
|
|
x0, y0: Integer:= 0;
|
|
begin
|
|
macro_blocks_loop:
|
|
loop
|
|
components_loop:
|
|
for c in Component loop
|
|
if image.JPEG_stuff.components(c) then
|
|
samples_y_loop:
|
|
for sby in 1..info_A(c).samples_ver loop
|
|
samples_x_loop:
|
|
for sbx in 1..info_A(c).samples_hor loop
|
|
Decode_Block(c, mb(c, sbx, sby));
|
|
end loop samples_x_loop;
|
|
end loop samples_y_loop;
|
|
end if;
|
|
end loop components_loop;
|
|
-- All components of the current macro-block are decoded.
|
|
-- Step 4, 5, 6 happen here: Upsampling, color transformation, output
|
|
Upsampling_and_output(mb, x0, y0);
|
|
--
|
|
mbx:= mbx + 1;
|
|
x0:= x0 + ssxmax * 8;
|
|
if mbx >= mbwidth then
|
|
mbx:= 0;
|
|
x0:= 0;
|
|
mby:= mby + 1;
|
|
y0:= y0 + ssymax * 8;
|
|
Feedback((100*mby)/mbheight);
|
|
exit macro_blocks_loop when mby >= mbheight;
|
|
end if;
|
|
if image.JPEG_stuff.restart_interval > 0 then
|
|
rstcount:= rstcount - 1;
|
|
if rstcount = 0 then
|
|
-- Here begins the restart.
|
|
bufbits:= Natural(U32(bufbits) and 16#F8#); -- byte alignment
|
|
-- Now the restart marker. We expect a
|
|
w:= U16(Get_bits(16));
|
|
if some_trace then
|
|
Put_Line(
|
|
" Restart #" & U16'Image(nextrst) &
|
|
" Code " & U16'Image(w) &
|
|
" after" & Natural'Image(image.JPEG_stuff.restart_interval) &
|
|
" macro blocks"
|
|
);
|
|
end if;
|
|
if w not in 16#FFD0# .. 16#FFD7# or (w and 7) /= nextrst then
|
|
Raise_exception(
|
|
error_in_image_data'Identity,
|
|
"JPEG: expected RST (restart) marker Nb " & U16'Image(nextrst)
|
|
);
|
|
end if;
|
|
nextrst:= (nextrst + 1) and 7;
|
|
rstcount:= image.JPEG_stuff.restart_interval;
|
|
-- Block-to-block predictor variables are reset.
|
|
for c in Component loop
|
|
info_B(c).dcpred:= 0;
|
|
end loop;
|
|
end if;
|
|
end if;
|
|
end loop macro_blocks_loop;
|
|
end;
|
|
end Read_SOS;
|
|
|
|
--
|
|
sh: Segment_head;
|
|
b: U8;
|
|
begin -- Load
|
|
loop
|
|
Read(image, sh);
|
|
case sh.kind is
|
|
when DQT => -- Quantization Table
|
|
Read_DQT(image, Natural(sh.length));
|
|
when DHT => -- Huffman Table
|
|
Read_DHT(image, Natural(sh.length));
|
|
when DRI => -- Restart Interval
|
|
Read_DRI(image);
|
|
when EOI => -- End Of Input
|
|
exit;
|
|
when SOS => -- Start Of Scan
|
|
Read_SOS;
|
|
exit;
|
|
when others =>
|
|
-- Skip segment data
|
|
for i in 1..sh.length loop
|
|
Get_Byte(image.buffer, b);
|
|
end loop;
|
|
end case;
|
|
end loop;
|
|
next_frame:= 0.0; -- still picture
|
|
end Load;
|
|
|
|
end GID.Decoding_JPG;
|