René Nyffenegger's collection of things on the web | |
René Nyffenegger on Oracle - Most wanted - Feedback
- Follow @renenyffenegger
|
November 22, 2005: On creating bitmaps with pure PL/SQL only | ||
Out of idle curiosity, I wanted to know if it is possible to create bitmaps (BMPs) with pure PL/SQL alone. And it is! The following package
is the result of this endeavor.
The procedure init must be called first in order to use the package and create a bitmap with the specified width and height.
The three other parameters r, g and b must be between 0 and 255 and specify the rgb-value of the bitmap's background. Obviously,
these three parameters can be omitted in which case they default to 0 which makes a black background.
The three parameters r, g and b are present in the other procedures as well and have the same meaning as in init.
PixelAt plots a simple pixel at the coordinates x/y. Line draws a line from
xFrom/yFrom to xTo/yTo. Circle draws a circle with the specified radius around x/y.
Finally, the function AsBlob returns the drawn bitmap as a blob.
create package bmp as procedure Init (width pls_integer, height pls_integer, r pls_integer := 0, g pls_integer := 0, b pls_integer := 0); procedure PixelAt(x pls_integer, y pls_integer, r pls_integer, g pls_integer, b pls_integer); procedure Line (xFrom pls_integer, yFrom pls_integer, xTo pls_integer, yTo pls_integer, r pls_integer, g pls_integer, b pls_integer); procedure Circle (x pls_integer, y pls_integer, radius pls_integer, r pls_integer, g pls_integer, b pls_integer); function AsBlob return blob; end bmp; / create package body bmp as headersize constant pls_integer := 14; infosize constant pls_integer := 40; offset constant pls_integer := infosize + headersize; bmpWidth pls_integer; bmpHeight pls_integer; line_len pls_integer; filesize pls_integer; output_file utl_file.file_type; the_bits blob; function unsigned_short(s in pls_integer) return raw is ret raw(2); v pls_integer; r pls_integer; begin v := trunc (s/256 ); r := s-v; ret := utl_raw.cast_to_raw(chr(v)); v := trunc (s ); r := s-v; ret := utl_raw.cast_to_raw(chr(v)) || ret; return ret; end unsigned_short; function unsigned_rgb(r in pls_integer, g in pls_integer, b in pls_integer) return raw is ret raw(3); begin ret := utl_raw.cast_to_raw(chr(r)); ret := utl_raw.cast_to_raw(chr(g)) || ret; ret := utl_raw.cast_to_raw(chr(b)) || ret; return ret; end unsigned_rgb; function unsigned_int(i in pls_integer) return raw is /* i = ret(4) * 256*256*256 + ret(3) * 256*256 + ret(2) * 256 + ret(1) */ ret raw(4); v pls_integer; r pls_integer; begin v := trunc (i/256/256/256); r := i-v; ret := utl_raw.cast_to_raw(chr(v)); v := trunc (i/256/256 ); r := i-v; ret := utl_raw.cast_to_raw(chr(v)) || ret; v := trunc (i/256 ); r := i-v; ret := utl_raw.cast_to_raw(chr(v)) || ret; v := trunc (i ); r := i-v; ret := utl_raw.cast_to_raw(chr(v)) || ret; return ret; end unsigned_int; procedure WriteHeader is imagesize pls_integer; begin imagesize := bmpHeight * line_len; filesize := imagesize + offset; -- Header dbms_lob.append(the_bits, utl_raw.cast_to_raw('BM')); -- Pos 0 dbms_lob.append(the_bits, unsigned_int(filesize)); -- Pos 2 dbms_lob.append(the_bits, unsigned_short(0)); -- Pos 6, reserved 1 dbms_lob.append(the_bits, unsigned_short(0)); -- Pos 8, reserved 2 dbms_lob.append(the_bits, unsigned_int(offset)); -- Pos 10, offset to image -- Information dbms_lob.append(the_bits, unsigned_int(infosize)); -- Pos 14 dbms_lob.append(the_bits, unsigned_int(bmpWidth)); -- Pos 18 dbms_lob.append(the_bits, unsigned_int(bmpHeight)); -- Pos 22 dbms_lob.append(the_bits, unsigned_short( 1)); -- Pos 26, planes dbms_lob.append(the_bits, unsigned_short(24)); -- Pos 28, bits per pixel dbms_lob.append(the_bits, unsigned_int ( 0)); -- Pos 30, no compression dbms_lob.append(the_bits, unsigned_int (imagesize)); -- Pos 34 dbms_lob.append(the_bits, unsigned_int (7874)); -- Pos 38, x pixels/meter (???) dbms_lob.append(the_bits, unsigned_int (7874)); -- Pos 42, y pixels/meter (???) dbms_lob.append(the_bits, unsigned_int (0)); -- Pos 46, Number of colors dbms_lob.append(the_bits, unsigned_int (0)); -- Pos 50, Important colors end WriteHeader; procedure Init(width pls_integer, height pls_integer, r in pls_integer, g in pls_integer, b in pls_integer) is bgColor raw(3); begin bmpWidth := width; bmpHeight := height; -- line_len must be divisible by 4 line_len := 4*ceil(3*bmpWidth/4); bgColor := unsigned_rgb(r,g,b); the_bits := empty_blob(); dbms_lob.createTemporary(the_bits, true); dbms_lob.open(the_bits, dbms_lob.lob_readwrite); WriteHeader; for x in 0 .. bmpWidth-1 loop for Y in 0 .. bmpHeight-1 loop dbms_lob.append(the_bits, bgColor); end loop; end loop; end Init; function AsBlob return blob is begin return the_bits; end AsBlob; procedure PixelAt(x in pls_integer, y in pls_integer, rgb in raw) is begin if x < 0 or y < 0 or x >= bmpWidth or y >= bmpHeight then return; end if; dbms_lob.write(the_bits, 3, 1+offset+ (bmpHeight-y-1)*line_len + x*3, rgb); end PixelAt; procedure PixelAt(x pls_integer, y pls_integer, r pls_integer, g pls_integer, b pls_integer) is rgb raw(3); begin rgb := unsigned_rgb(r,g,b); PixelAt(x, y, rgb); end; procedure Line (xFrom pls_integer, yFrom pls_integer, xTo pls_integer, yTo pls_integer, r pls_integer, g pls_integer, b pls_integer) is rgb raw(3); c pls_integer; m pls_integer; x pls_integer; y pls_integer; D pls_integer; HX pls_integer; HY pls_integer; xInc pls_integer; yInc pls_integer; begin rgb := unsigned_rgb(r,g,b); x := xFrom; y := yFrom; D := 0; HX := xTo - xfrom; HY := yTo - yfrom; xInc := 1; yInc := 1; if HX < 0 then xInc := -1; HX := -HX; end if; if HY < 0 then yInc := -1; HY := -HY; end if; if HY <= HX then c := 2*HX; M := 2*HY; loop PixelAt(x, y, rgb); exit when x = xTo; x := x + xInc; D := D + M; if D > HX then y := y+yInc; D := D-c; end if; end loop; else c := 2*HY; M := 2*HX; loop PixelAt(x, y, rgb); exit when y = yTo; y := y + yInc; D := D + M; if D > HY then x := x + xInc; D := D - c; end if; end loop; end if; end Line; procedure Circle_(x pls_integer, y pls_integer, xx pls_integer, yy pls_integer, rgb raw) is begin if xx = 0 then PixelAt(x , y + yy , rgb); PixelAt(x , y - yy , rgb); PixelAt(x + yy, y , rgb); PixelAt(x - yy, y , rgb); elsif xx = yy then PixelAt(x + xx , y + yy , rgb); PixelAt(x - xx , y + yy , rgb); PixelAt(x + xx , y - yy , rgb); PixelAt(x - xx , y - yy , rgb); elsif xx < yy then PixelAt(x + xx , y + yy , rgb); PixelAt(x - xx , y + yy , rgb); PixelAt(x + xx , y - yy , rgb); PixelAt(x - xx , y - yy , rgb); PixelAt(x + yy , y + xx , rgb); PixelAt(x - yy , y + xx , rgb); PixelAt(x + yy , y - xx , rgb); PixelAt(x - yy , y - xx , rgb); end if; end Circle_; procedure Circle (x pls_integer, y pls_integer, radius pls_integer, r pls_integer, g pls_integer, b pls_integer) is xx pls_integer := 0; yy pls_integer := radius; pp pls_integer := (5-radius*4)/4; rgb raw(3); begin rgb := unsigned_rgb(r,g,b); Circle_(x, y, xx, yy, rgb); while xx < yy loop xx := xx+1; if pp < 0 then pp := pp + 2*xx+1; else yy := yy - 1; pp := pp + 2*(xx-yy) + 1; end if; Circle_(x, y, xx, yy, rgb); end loop; end Circle; end bmp; / Testing the package
I am going to save the bitmap returned from AsBlob into a file. Therefore, I need to create a directory object that points to the
directory in which I want to save the bitmap:
create directory temp_dir as 'c:\temp';
This anonymous block actually creates the bitmap. First, I set the width, height and background color with Init, then I draw a
rectangle around the bitmap followed by 36 lines and lastly a circle.
The blob I get with AsBlob is saved using blob_wrapper which is a package which I have
presented two days ago.
begin bmp.Init(300, 200, 238, 238, 204); bmp.Line( 0, 0, 0, 199, 66, 166, 194); bmp.Line( 0, 199, 299, 199, 66, 166, 194); bmp.Line(299, 199, 299, 0, 66, 166, 194); bmp.Line(299, 0, 0, 0, 66, 166, 194); for i in 1 .. 36 loop bmp.Line(150, 100, 150+sin(i/18*3.141)* 80, 100+cos(i/18*3.141)*80, 55, 0, 180); end loop; bmp.Circle(150, 100, 80, 255, 0, 0); blob_wrapper.to_file('TEMP_DIR', 'test.bmp', bmp.AsBlob); end; /
This is the created bitmap:
Cleaning up:
drop directory temp_dir; Update January 9, 2006
Maxim Demenko sends me an email regarding the function
unsigned_int :
I want to thank Maxim for sharing his feedback here.
Links
The following links proved useful to create this package:
More on OracleThis is an on Oracle article. The most current articles of this series can be found here.
|