alexandria-plsql-utils/ora/image_util_pkg.pkb
2015-02-20 19:43:42 +01:00

578 lines
17 KiB
Plaintext
Executable File

create or replace package body image_util_pkg
as
/*
Purpose: Package handles images
Remarks: Based on image parsing code from Anton Scheffer's AS_PDF3 package
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
type tp_pls_tab is table of pls_integer index by pls_integer;
function blob2num (p_blob blob,
p_len integer,
p_pos integer) return number
is
begin
return to_number( rawtohex( dbms_lob.substr( p_blob, p_len, p_pos ) ), 'xxxxxxxx' );
end blob2num;
function raw2num (p_value raw) return number
is
begin
return to_number( rawtohex( p_value ), 'XXXXXXXX' );
end raw2num;
function raw2num (p_value raw,
p_pos pls_integer,
p_len pls_integer) return pls_integer
is
begin
return to_number( rawtohex( utl_raw.substr( p_value, p_pos, p_len ) ), 'XXXXXXXX' );
end raw2num;
function adler32 (p_src in blob) return varchar2
is
s1 pls_integer := 1;
s2 pls_integer := 0;
n pls_integer;
step_size number;
tmp varchar2(32766);
c65521 constant pls_integer := 65521;
begin
step_size := trunc( 16383 / dbms_lob.getchunksize( p_src ) ) * dbms_lob.getchunksize( p_src );
for j in 0 .. trunc( ( dbms_lob.getlength( p_src ) - 1 ) / step_size )
loop
tmp := rawtohex( dbms_lob.substr( p_src, step_size, j * step_size + 1 ) );
for i in 1 .. length( tmp ) / 2
loop
n := to_number( substr( tmp, i * 2 - 1, 2 ), 'xx' );
s1 := s1 + n;
if s1 >= c65521
then
s1 := s1 - c65521;
end if;
s2 := s2 + s1;
if s2 >= c65521
then
s2 := s2 - c65521;
end if;
end loop;
end loop;
return to_char( s2, 'fm0XXX' ) || to_char( s1, 'fm0XXX' );
end adler32;
function parse_jpg (p_img_blob blob) return t_image_info
is
buf raw(4);
t_img t_image_info;
t_ind integer;
begin
/*
Purpose: Parse JPG
Remarks: From Anton Scheffer's AS_PDF3 package
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
if ( dbms_lob.substr( p_img_blob, 2, 1 ) != hextoraw( 'FFD8' ) -- SOI Start of Image
or dbms_lob.substr( p_img_blob, 2, dbms_lob.getlength( p_img_blob ) - 1 ) != hextoraw( 'FFD9' ) -- EOI End of Image
)
then -- this is not a jpg I can handle
return null;
end if;
--
t_img.pixels := p_img_blob;
t_img.type := 'jpg';
if dbms_lob.substr( t_img.pixels, 2, 3 ) in ( hextoraw( 'FFE0' ) -- a APP0 jpg
, hextoraw( 'FFE1' ) -- a APP1 jpg
)
then
t_img.color_res := 8;
t_img.height := 1;
t_img.width := 1;
--
t_ind := 3;
t_ind := t_ind + 2 + blob2num( t_img.pixels, 2, t_ind + 2 );
loop
buf := dbms_lob.substr( t_img.pixels, 2, t_ind );
exit when buf = hextoraw( 'FFDA' ); -- SOS Start of Scan
exit when buf = hextoraw( 'FFD9' ); -- EOI End Of Image
exit when substr( rawtohex( buf ), 1, 2 ) != 'FF';
if rawtohex( buf ) in ( 'FFD0' -- RSTn
, 'FFD1', 'FFD2', 'FFD3', 'FFD4', 'FFD5', 'FFD6', 'FFD7', 'FF01' -- TEM
)
then
t_ind := t_ind + 2;
else
if buf = hextoraw( 'FFC0' ) -- SOF0 (Start Of Frame 0) marker
then
t_img.color_res := blob2num( t_img.pixels, 1, t_ind + 4 );
t_img.height := blob2num( t_img.pixels, 2, t_ind + 5 );
t_img.width := blob2num( t_img.pixels, 2, t_ind + 7 );
end if;
t_ind := t_ind + 2 + blob2num( t_img.pixels, 2, t_ind + 2 );
end if;
end loop;
end if;
--
return t_img;
end parse_jpg;
function parse_png (p_img_blob blob) return t_image_info
is
t_img t_image_info;
buf raw(32767);
len integer;
ind integer;
color_type pls_integer;
begin
/*
Purpose: Parse PNG
Remarks: From Anton Scheffer's AS_PDF3 package
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
if rawtohex( dbms_lob.substr( p_img_blob, 8, 1 ) ) != '89504E470D0A1A0A' -- not the right signature
then
return null;
end if;
dbms_lob.createtemporary( t_img.pixels, true );
ind := 9;
loop
len := blob2num( p_img_blob, 4, ind ); -- length
exit when len is null or ind > dbms_lob.getlength( p_img_blob );
case utl_raw.cast_to_varchar2( dbms_lob.substr( p_img_blob, 4, ind + 4 ) ) -- Chunk type
when 'IHDR'
then
t_img.width := blob2num( p_img_blob, 4, ind + 8 );
t_img.height := blob2num( p_img_blob, 4, ind + 12 );
t_img.color_res := blob2num( p_img_blob, 1, ind + 16 );
color_type := blob2num( p_img_blob, 1, ind + 17 );
t_img.greyscale := color_type in ( 0, 4 );
when 'PLTE'
then
t_img.color_tab := dbms_lob.substr( p_img_blob, len, ind + 8 );
when 'IDAT'
then
dbms_lob.copy( t_img.pixels, p_img_blob, len, dbms_lob.getlength( t_img.pixels ) + 1, ind + 8 );
when 'IEND'
then
exit;
else
null;
end case;
ind := ind + 4 + 4 + len + 4; -- Length + Chunk type + Chunk data + CRC
end loop;
--
t_img.type := g_format_png;
t_img.nr_colors := case color_type
when 0 then 1
when 2 then 3
when 3 then 1
when 4 then 2
else 4
end;
--
return t_img;
end parse_png;
function lzw_decompress (p_blob blob,
p_bits pls_integer) return blob
is
powers tp_pls_tab;
--
g_lzw_ind pls_integer;
g_lzw_bits pls_integer;
g_lzw_buffer pls_integer;
g_lzw_bits_used pls_integer;
--
type tp_lzw_dict is table of raw(1000) index by pls_integer;
t_lzw_dict tp_lzw_dict;
t_clr_code pls_integer;
t_nxt_code pls_integer;
t_new_code pls_integer;
t_old_code pls_integer;
t_blob blob;
--
function get_lzw_code
return pls_integer
is
t_rv pls_integer;
begin
while g_lzw_bits_used < g_lzw_bits
loop
g_lzw_ind := g_lzw_ind + 1;
g_lzw_buffer := blob2num( p_blob, 1, g_lzw_ind ) * powers( g_lzw_bits_used ) + g_lzw_buffer;
g_lzw_bits_used := g_lzw_bits_used + 8;
end loop;
t_rv := bitand( g_lzw_buffer, powers( g_lzw_bits ) - 1 );
g_lzw_bits_used := g_lzw_bits_used - g_lzw_bits;
g_lzw_buffer := trunc( g_lzw_buffer / powers( g_lzw_bits ) );
return t_rv;
end;
--
begin
/*
Purpose: LZW decompression
Remarks: From Anton Scheffer's AS_PDF3 package
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
for i in 0 .. 30
loop
powers( i ) := power( 2, i );
end loop;
--
t_clr_code := powers( p_bits - 1 );
t_nxt_code := t_clr_code + 2;
for i in 0 .. least( t_clr_code - 1, 255 )
loop
t_lzw_dict( i ) := hextoraw( to_char( i, 'fm0X' ) );
end loop;
dbms_lob.createtemporary( t_blob, true );
g_lzw_ind := 0;
g_lzw_bits := p_bits;
g_lzw_buffer := 0;
g_lzw_bits_used := 0;
--
t_old_code := null;
t_new_code := get_lzw_code( );
loop
case nvl( t_new_code, t_clr_code + 1 )
when t_clr_code + 1
then
exit;
when t_clr_code
then
t_new_code := null;
g_lzw_bits := p_bits;
t_nxt_code := t_clr_code + 2;
else
if t_new_code = t_nxt_code
then
t_lzw_dict( t_nxt_code ) :=
utl_raw.concat( t_lzw_dict( t_old_code )
, utl_raw.substr( t_lzw_dict( t_old_code ), 1, 1 )
);
dbms_lob.append( t_blob, t_lzw_dict( t_nxt_code ) );
t_nxt_code := t_nxt_code + 1;
elsif t_new_code > t_nxt_code
then
exit;
else
dbms_lob.append( t_blob, t_lzw_dict( t_new_code ) );
if t_old_code is not null
then
t_lzw_dict( t_nxt_code ) := utl_raw.concat( t_lzw_dict( t_old_code )
, utl_raw.substr( t_lzw_dict( t_new_code ), 1, 1 )
);
t_nxt_code := t_nxt_code + 1;
end if;
end if;
if bitand( t_nxt_code, powers( g_lzw_bits ) - 1 ) = 0
and g_lzw_bits < 12
then
g_lzw_bits := g_lzw_bits + 1;
end if;
end case;
t_old_code := t_new_code;
t_new_code := get_lzw_code( );
end loop;
t_lzw_dict.delete;
--
return t_blob;
end lzw_decompress;
function parse_gif (p_img_blob blob) return t_image_info
is
img t_image_info;
buf raw(4000);
ind integer;
t_len pls_integer;
begin
/*
Purpose: Parse GIF
Remarks: From Anton Scheffer's AS_PDF3 package
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
if dbms_lob.substr( p_img_blob, 3, 1 ) != utl_raw.cast_to_raw( 'GIF' )
then
return null;
end if;
ind := 7;
buf := dbms_lob.substr( p_img_blob, 7, 7 ); -- Logical Screen Descriptor
ind := ind + 7;
img.color_res := raw2num( utl_raw.bit_and( utl_raw.substr( buf, 5, 1 ), hextoraw( '70' ) ) ) / 16 + 1;
img.color_res := 8;
if raw2num( buf, 5, 1 ) > 127
then
t_len := 3 * power( 2, raw2num( utl_raw.bit_and( utl_raw.substr( buf, 5, 1 ), hextoraw( '07' ) ) ) + 1 );
img.color_tab := dbms_lob.substr( p_img_blob, t_len, ind ); -- Global Color Table
ind := ind + t_len;
end if;
--
loop
case dbms_lob.substr( p_img_blob, 1, ind )
when hextoraw( '3B' ) -- trailer
then
exit;
when hextoraw( '21' ) -- extension
then
if dbms_lob.substr( p_img_blob, 1, ind + 1 ) = hextoraw( 'F9' )
then -- Graphic Control Extension
if utl_raw.bit_and( dbms_lob.substr( p_img_blob, 1, ind + 3 ), hextoraw( '01' ) ) = hextoraw( '01' )
then -- Transparent Color Flag set
img.transparency_index := blob2num( p_img_blob, 1, ind + 6 );
end if;
end if;
ind := ind + 2; -- skip sentinel + label
loop
t_len := blob2num( p_img_blob, 1, ind ); -- Block Size
exit when t_len = 0;
ind := ind + 1 + t_len; -- skip Block Size + Data Sub-block
end loop;
ind := ind + 1; -- skip last Block Size
when hextoraw( '2C' ) -- image
then
declare
img_blob blob;
min_code_size pls_integer;
code_size pls_integer;
flags raw(1);
begin
img.width := utl_raw.cast_to_binary_integer( dbms_lob.substr( p_img_blob, 2, ind + 5 )
, utl_raw.little_endian
);
img.height := utl_raw.cast_to_binary_integer( dbms_lob.substr( p_img_blob, 2, ind + 7 )
, utl_raw.little_endian
);
img.greyscale := false;
ind := ind + 1 + 8; -- skip sentinel + img sizes
flags := dbms_lob.substr( p_img_blob, 1, ind );
if utl_raw.bit_and( flags, hextoraw( '80' ) ) = hextoraw( '80' )
then
t_len := 3 * power( 2, raw2num( utl_raw.bit_and( flags, hextoraw( '07' ) ) ) + 1 );
img.color_tab := dbms_lob.substr( p_img_blob, t_len, ind + 1 ); -- Local Color Table
end if;
ind := ind + 1; -- skip image Flags
min_code_size := blob2num( p_img_blob, 1, ind );
ind := ind + 1; -- skip LZW Minimum Code Size
dbms_lob.createtemporary( img_blob, true );
loop
t_len := blob2num( p_img_blob, 1, ind ); -- Block Size
exit when t_len = 0;
dbms_lob.append( img_blob, dbms_lob.substr( p_img_blob, t_len, ind + 1 ) ); -- Data Sub-block
ind := ind + 1 + t_len; -- skip Block Size + Data Sub-block
end loop;
ind := ind + 1; -- skip last Block Size
img.pixels := lzw_decompress( img_blob, min_code_size + 1 );
--
if utl_raw.bit_and( flags, hextoraw( '40' ) ) = hextoraw( '40' )
then -- interlaced
declare
pass pls_integer;
pass_ind tp_pls_tab;
l_mod number;
begin
dbms_lob.createtemporary( img_blob, true );
pass_ind( 1 ) := 1;
pass_ind( 2 ) := trunc( ( img.height - 1 ) / 8 ) + 1;
pass_ind( 3 ) := pass_ind( 2 ) + trunc( ( img.height + 3 ) / 8 );
pass_ind( 4 ) := pass_ind( 3 ) + trunc( ( img.height + 1 ) / 4 );
pass_ind( 2 ) := pass_ind( 2 ) * img.width + 1;
pass_ind( 3 ) := pass_ind( 3 ) * img.width + 1;
pass_ind( 4 ) := pass_ind( 4 ) * img.width + 1;
for i in 0 .. img.height - 1
loop
l_mod := mod( i, 8 );
pass := case l_mod
when 0 then 1
when 4 then 2
when 2 then 3
when 6 then 3
else 4
end;
dbms_lob.append( img_blob, dbms_lob.substr( img.pixels, img.width, pass_ind( pass ) ) );
pass_ind( pass ) := pass_ind( pass ) + img.width;
end loop;
img.pixels := img_blob;
end;
end if;
--
dbms_lob.freetemporary( img_blob );
end;
else
exit;
end case;
end loop;
--
img.type := g_format_gif;
return img;
end parse_gif;
function parse_img (p_blob in blob,
p_adler32 in varchar2 := null,
p_type in varchar2 := null) return t_image_info
is
t_img t_image_info;
begin
/*
Purpose: Parse image file
Remarks: From Anton Scheffer's AS_PDF3 package
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
t_img.type := p_type;
if t_img.type is null
then
if rawtohex( dbms_lob.substr( p_blob, 8, 1 ) ) = '89504E470D0A1A0A'
then
t_img.type := g_format_png;
elsif dbms_lob.substr( p_blob , 3, 1 ) = utl_raw.cast_to_raw( 'GIF' )
then
t_img.type := g_format_gif;
else
t_img.type := g_format_jpg;
end if;
end if;
--
t_img := case lower( t_img.type )
when g_format_gif then parse_gif( p_blob )
when g_format_png then parse_png( p_blob )
when g_format_jpg then parse_jpg( p_blob )
else null
end;
--
if t_img.type is not null
then
t_img.adler32 := coalesce( p_adler32, adler32( p_blob ) );
end if;
return t_img;
end parse_img;
function is_image (p_file in blob,
p_format in varchar2 := null) return boolean
as
l_info t_image_info;
l_returnvalue boolean := false;
begin
/*
Purpose: returns true if blob is image
Remarks:
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
l_info := get_image_info (p_file);
if l_info.type is not null then
if p_format is null then
l_returnvalue := l_info.type in (g_format_jpg, g_format_png, g_format_gif);
else
l_returnvalue := l_info.type = p_format;
end if;
end if;
return l_returnvalue;
end is_image;
function get_image_info (p_file in blob) return t_image_info
as
l_returnvalue t_image_info;
begin
/*
Purpose: get image information
Remarks:
Who Date Description
------ ---------- --------------------------------
MBR 23.06.2012 Created
*/
l_returnvalue := parse_img (p_file);
return l_returnvalue;
end get_image_info;
end image_util_pkg;
/