%!PS % Universal Bitmap Image Manipulation Utilities % ============================================= % by Don Lancaster %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Copyright c 2005 by Don Lancaster & Synergetics, Box 809, Thatcher, AZ, 85552 % (928) 428-4073 Email: don@tinaja.com Website: http://www.tinaja.com % Consulting services available http://www.tinaja.com/info01.html % All commercial rights and all electronic media rights ~fully~ reserved. % Linking usually welcome. Reposting expressly forbidden. Version 2.1 % This is an expanding set of universal bitmap imaging utilities that % potentially offer ultra fancy calculated image manipulation possibilities. % Certain XY mnipulations may be 5X slower and increase with the resolution square. % A minimum of 250 megabytes of system RAM is recommended with no other programs active. % Image must be EXTERNALLY converted to/from .JPG or other non .BMP format. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /sourcefilename (utestk01.bmp) % pop (kkk01.bmp) pop (keystx01.bmp) def % short name of input .BMP file /sourcefilenameprefix % full source filename prefix to be appended (C:\\Documents and Settings\\don\\Desktop\\gurugrams\\Universal image 56\\) def /targetfilename (utestkx1.bmp) % pop (kkkx1.bmp) pop (keystx1.bmp) def % short name of output .BMP file /targetfilenameprefix % full target filename prefix to be appended (C:\\Documents and Settings\\don\\Desktop\\gurugrams\\Universal image 56\\) def %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % IMPORTANT NOTE: Don Lancaster's file gonzo.ps is recommended but not % required for this program. % After obvious location mods, uncomment ONE of the following two lines: % (C:\\Documents and Settings\\don\\Desktop\\gonzo\\gonzo.ps) run % use internal % (A:\\gonzo.ps) run % use external gonzo % NOTE THAT ALL PS FILENAME STRINGS !!!DEMAND!!! DOUBLE REVERSE SLASHES. % GONZO20A Guru Gonzo PostScript power tools (Interim release) % Includes gonzo justification and layout utilities. % Copyright c 1990, 1996, 2005 by Don Lancaster and Synergetics, Box 809, % Thatcher Arizona, 5552 (928) 428-4073 don@tinaja.com support % via http://www.tinaja.com All commercial rights and all electronic % media rights **FULLY** reserved. Reposting is expressly forbidden. % ======== /guru { gonzo begin ps.util.1 begin printerror nuisance begin} def % guru % optionally activate gonzo utilities % ========= % gonzo excerpts /mt {moveto} store /black {0 setgray} store % timing utilities. use stopwatchon and stopwatchoff for simple % one shot timing. For multiple time totals, use resettimer % starttimer stoptimer ... starttimer stoptimer reporttimer /stopwatchoff {stoptimer reporttimer} def % for single shots /stopwatchon {resettimer starttimer} def % for single shots /reporttimer {mytime 1000 div (\rElapsed time: ) print 20 string cvs print ( seconds.\r) print flush} def % to host /resettimer {/mytime 0 def} def % reset timer /starttimer {usertime /mytimenow exch def} def % add to time so far /stoptimer {usertime mytimenow sub /mytime exch mytime add def} def % for multiple timing intervals % mergestr merges the two top stack strings into one top stack string /mergestr {2 copy length exch length add string dup dup 4 3 roll 4 index length exch putinterval 3 1 roll exch 0 exch putinterval} def % [yourarray] makestring converts an array to a string... /makestring {dup length string dup % new string of right size /NullEncode filter % make a file out of string 3 -1 roll {1 index exch write} forall pop } def %%%%%%%%% end gonzo excerpts % bitmap and file manipulation basics... /wholesourcefilename sourcefilenameprefix sourcefilename mergestr store /wholetargetfilename targetfilenameprefix targetfilename mergestr store /readfile wholesourcefilename (r) file store % establish input read file /writefile wholetargetfilename (w+) file store % establish output target file /bitsperpixelposition 28 store % BMP header positioning info /datastartposition 10 store /horizontalpixels 18 store /verticalpixels 22 store /backcount 0 store %%%%%%%%% CHECK FOR VALID 24-BIT UNCOMPRESSED BITMAP %%%%%%%%%%%%% % BM check: readfile (XX) readstring pop % error message if file not two chars long (BM) eq {(File has correct "BM" as first two bytes.\n) print } { sourcefilename ( is not in .BMP format; further eval terminated.\n\n\n) mergestr print quit } ifelse % 24 bit check: readfile bitsperpixelposition setfileposition % access 2 {readfile read pop} repeat % get color planes error if not present 1 {256 mul add} repeat % calculate bits per pixel dup /bpp exch store % save for expanded analysis 10 string cvs % make string (24) eq {(File has correct 24-bit color format.\n) print } { sourcefilename ( is not in 24-bit format; further eval terminated.\n\n\n) mergestr print quit } ifelse % Compression mode: 4 {readfile read pop} repeat % get color planes error if not present 3 {256 mul add} repeat % calculate compression mode dup /cmm exch store % save for expanded analysis 10 string cvs % make string (0) eq {(File has correct 0 uncompressed format.\n) print } { sourcefilename ( is not in uncompressed format; further action terminated.\n\n\n) mergestr print quit } ifelse % Bitmap Width: readfile horizontalpixels setfileposition % access 4 {readfile read pop} repeat % get bitmap width bytes error if not present 3 {256 mul add} repeat % calculate data start dup /hres exch def 10 string cvs % make string (Bitmap width is ) exch mergestr ( pixels.\n) mergestr print flush % Bitmap Width: readfile verticalpixels setfileposition % access 4 {readfile read pop} repeat % get bitmap width bytes error if not present 3 {256 mul add} repeat % calculate data start dup /vres exch def 10 string cvs % make string (Bitmap height is ) exch mergestr ( pixels.\n) mergestr print flush % Find padding % .BMP rows MUST end on a 32-bit boundary! Zero, one, two, or three 00 % padding bits are required depending upon the actual width. /padding hres 3 mul cvi 4 mod % find start of next 32-byte block [ 0 3 2 1 ] exch get % TLU correction def (Padding 8-bit bytes per line are ) padding 10 string cvs mergestr ( .\n\n) mergestr print flush (Active storage 8-bit RGB bytes per line are ) hres 3 mul dup cvi /activestore exch store 10 string cvs mergestr ( .\n) mergestr print flush activestore 4 div ceiling cvi 4 mul /totalbytesperline exch store % find data start readfile datastartposition setfileposition % access 4 {readfile read pop} repeat % get data start bytes error if not present 3 {256 mul add} repeat % calculate data start /actualdatastart exch store % define read strings /linestring hres 3 mul string store % read file line buffer %%%%%%%%%%%%%%%% COPY HEADER TO TARGET FILE %%%%%%%%%%%%%%%%%% % The entire old header gets reused. outwidth and outlength is then % overwritten. /writeoutfileheader { readfile datastartposition setfileposition % access 4 {readfile read pop} repeat % get data start bytes error if not present 3 {256 mul add} repeat % calculate data start /actualdatastart exch store readfile 0 setfileposition readfile actualdatastart 1 sub string readstring not { sourcefilename ( has a short header; further action terminated.\n\n\n) mergestr print quit } if writefile exch writestring % write the entire header to new file writefile actualdatastart setfileposition % go to end of header } store % /correctoutlenwidth adjusts the output file size if it is different from the % original. As may happen in a crop or rotation. /correctoutlenwidth { writefile horizontalpixels setfileposition % file position t0 first horizontal posn byte 0 % assume highest byte is zero outwidth dup 65536 idiv % find next highest byte exch 65536 mod % find remainder dup 256 idiv % and final two bytes exch 256 mod 4 {writefile exch write} repeat % overwrite pixel width writefile verticalpixels setfileposition % file position t0 first vertical posn byte 0 % assume highest byte is zero outlength dup 65536 idiv % find next highest byte exch 65536 mod % find remainder dup 256 idiv % and final two bytes exch 256 mod 4 {writefile exch write} repeat % overwrite pixel width writefile actualdatastart setfileposition % go to end of header } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (A) BITMAP FILE CAPTURE ROUTINE /grabbitmap % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % /grabbitmap reads a disk file and converts its underlying bitmap to % a PostScript /instrarray image array of STRINGS. Each horizontal line % is one subarray, and the arrays build from the image BOTTOM UPWARDS... % In general, PostScript strings can be manipulated as arrays without % needing the overhead of string to array or array to string conversion. % The speed savings can be quite significant. % This is normally the FIRST step in any image manipulation. % Note that there is NO PADDING in instrarray. /grabbitmap { (\nReading initial bitmap from disk...) print flush /instrarray mark % start input data array 0 = bottom 0 1 vres 1 sub { % begin line reading loop /vline exch store % start of next hires line vline hres 3 mul padding add mul actualdatastart add % position read file readfile exch setfileposition readfile linestring readstring % grab a line of characters not {Error_reading_input_file_data} if % error trap dup length string cvs % must dereference } for ] store % complete instrarray definition } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (B) COPY STRING TO OUTPUT /instr2outstr % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % This is a simple string replicator that also does dereferencing. % Dereferencing allows the instrarray to be reused without surprises. /instr2outstr { -2 vmreclaim (\nCopying input to output...) print flush /outstrarray mark instrarray { dup length string cvs} forall ] store 0 vmreclaim } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (C) BITMAP FILE STORAGE ROUTINE /savebitmap % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % /savebitmap saves the internal PostScript /outarray to disk in % the .BMP format a chosen disk file. % This is normally the LAST step in any image manipulation. % Note that input and output files can be different sizes for certain % operations such as cropping or rotations. /savebitmap { (\nSaving processed bitmap image to disk...) print flush outstrarray length /outlength exch store % find output pixelslength outstrarray 0 get length 3 div cvi % find output pixels width /outwidth exch store % note three RGB bytes per pixel writeoutfileheader % write the outfile header correctoutlenwidth % correct the data size 0 1 outstrarray length 1 sub { % for each data line outstrarray exch get % get data line as string dup length 4 mod % add padding if needed dup 0 gt {[ (xxx) threepad twopad onepad] exch get mergestr}{pop}ifelse writefile exch writestring } for } store /onepad [ 0 ] makestring store % null padding strings used by /savebitmap /twopad [0 0] makestring store /threepad [ 0 0 0 ] makestring store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (D) RETANGULAR CROPPING /cropimage % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % /cropimage gathers an image subarray. Remember that there are % three bytes per pixel horizontally. There is currently no range % checking or error trapping. /cropimage { /ury exch store % upper right vertical count 3 mul cvi % upper right horizontal count /urx exch store /lly exch store % lower left vertical count 3 mul cvi % lower left horizontal count /llx exch store /outstrarray mark % begin new output array lly 1 ury { % begin loop instrarray exch get llx dup urx sub neg getinterval } for ] def % complete output array } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (E) FLIP IMAGE VERTICALLY /flipimagevertically % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % mirrors the image vertically. /flipimagevertically { /outstrarray [ % start output array instrarray length 1 sub -1 0 { % for each horizontal line instrarray exch get } for ] store % save new array } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (F) FLIP IMAGE HORIZONTALLY /flipimagehorizontally % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % mirrors the image horizontally by copying into second string. /flipimagehorizontally { 0 vmreclaim -2 vmreclaim (\nflipping image horizontally) print flush /outstrarray [ % start output array 0 1 instrarray length 1 sub { % for each horizontal line dup 200 mod 0 eq {(.) print flush % report progress 0 vmreclaim -2 vmreclaim } if instrarray exch get % get string to be reversed /curhstr exch store /csmax curhstr length 3 sub store % calculate maximum pixel position /tempstring csmax 3 add string store csmax -3 0 { /iii exch store % for each pixel group curhstr iii 3 getinterval % isolate rgbstring tempstring exch csmax iii sub % put into new string exch putinterval } for tempstring } for ] store % save new array 0 vmreclaim } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (G) ROTATE IMAGE 180 DEGREES /rotateimage180 % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /rotateimageby180 { 0 vmreclaim -2 vmreclaim (\nRotating image by 180 degrees) print flush /outstrarray [ % start output array instrarray length 1 sub -1 0 { % for each horizontal line dup 200 mod 0 eq {(.) print flush % report progress 0 vmreclaim -2 vmreclaim } if instrarray exch get % get string to be reversed /curhstr exch store /csmax curhstr length 3 sub store % calculate maximum pixel position /tempstring csmax 3 add string store csmax -3 0 { /iii exch store % for each pixel group curhstr iii 3 getinterval % isolate rgbstring tempstring exch csmax iii sub % put into new string exch putinterval } for tempstring } for ] store % save new array 0 vmreclaim } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (H) ROTATE IMAGE +90 DEGREES CLOCKWISE /rotateimageby90 % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % rotates the image by 90 degrees. /rotateimageby90 { 0 vmreclaim -2 vmreclaim (\nRotating image by 90 degrees) print flush /outstrarray [ % start output array /oldhlength instrarray 0 get length store % save old hlength oldhlength 3 sub -3 0 { % count old hline by threes dup 200 mod 0 eq {(.) print flush 0 vmreclaim -2 vmreclaim } if % show progress /oldhposn exch store % save old hposn /newhstring instrarray length 3 mul % create new output hstring string store instrarray length 1 sub -1 0{ % for each vertical position /oldvposn exch store instrarray oldvposn get oldhposn 3 getinterval % grab rgb triad newhstring exch oldvposn 3 mul cvi exch putinterval % and plut into newhstring } for newhstring } for % for each gathered line string ] store % save new array 0 vmreclaim } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (I) ROTATE 270 DEGREES CLOCKWISE /rotateimageby270 % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % rotates the image by 270 degrees (90 degrees counterclockwise). /rotateimageby270 { 0 vmreclaim -2 vmreclaim (\nRotating image by 270 degrees) print flush /outstrarray [ % start output array /oldhlength instrarray 0 get length store % save old hlength 0 3 oldhlength 3 sub { % count old hline by threes dup 200 mod 0 eq {(.) print flush 0 vmreclaim -2 vmreclaim } if % show progress /oldhposn exch store % save old hposn /newhstring instrarray length 3 mul % create new output hstring string store 0 1 instrarray length 1 sub { % for each vertical position /oldvposn exch store instrarray oldvposn get oldhposn 3 getinterval % grab rgb triad newhstring exch oldvposn 3 mul cvi exch putinterval % and plut into newhstring } for newhstring } for % for each gathered line string ] store % save new array 0 vmreclaim } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (J) REPLACE IMAGE WITH ITS NEGATIVE /negateimage % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % /negateimage replaces an entire image with its negative. Each color value of n % is replaced by abs (n-255). Since RGB are all treated the same, the entire % horizontal line can be processed at once % attempts to avoid time intensive makestring took longer so far. /negateimage { 0 vmreclaim -2 vmreclaim (\nConverting image to negative) print flush /outstrarray [ % start output array 0 1 instrarray length 1 sub { dup 200 mod 0 eq {(.) print flush 0 vmreclaim -2 vmreclaim } if instrarray exch get mark exch {256 sub abs} forall ] makestring } for ] store 0 vmreclaim } store % REWORK --- %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % (L) Background Eliminator /knockback % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Uses EWNS algorithm to replace everything from left margin to first % all white pixel string only. Then from right margin left. Then % top margin down. Then bottom margin up. % There MUST be ZERO breaks in the white outline! Diagonal whites are % permitted. Extra white pixels or thicker borders are permitted. % Random white pixels in the background should be preremoved. % Currently limited to 255 255 255 hardwire white replacement /triwhite [255 255 255] makestring store % whites used by top down /knockback { 0 vmreclaim -2 vmreclaim instr2outstr % replicate output string /spacestring1 % create white array string mark outstrarray 0 get length {255} repeat ] makestring store % begin left scan... 0 1 outstrarray length 1 sub {/vposn exch store % for each horizontal line outstrarray vposn get /curhstring exch store % get current string 0 3 curhstring length 3 sub { % get current posn /curhpos exch store curhstring curhpos get 255 eq { % scan rgb triads for white curhstring curhpos 1 add get 255 eq { curhstring curhpos 2 add get 255 eq {exit} if } if } if } for % for each triad spacestring1 0 curhpos getinterval % replace white to left outstrarray vposn get exch 0 exch putinterval } for % for each line % begin right scan... 0 1 outstrarray length 1 sub {/vposn exch store % for each horizontal line instrarray vposn get /curhstring exch store % get current string curhstring length 3 sub -3 0 { % get current posn /curhpos exch store curhstring curhpos get 255 eq { % scan rgb triads for white curhstring curhpos 1 add get 255 eq { curhstring curhpos 2 add get 255 eq {exit} if } if } if } for % for each triad spacestring1 dup length curhpos sub 0 exch getinterval outstrarray vposn get exch curhpos exch putinterval } for % for each line % begin slower top scan... 0 3 outstrarray 0 get length 4 sub {/hposn exch store % for each horizontal line outstrarray length 1 sub -1 0 { % scan downward /vposn exch store instrarray vposn get hposn get 255 eq { instrarray vposn get hposn 1 add get 255 eq { instrarray vposn get hposn 2 add get 255 eq {exit} if } if } if outstrarray vposn get hposn triwhite putinterval } for % for each vertical line } for % completing h sweep % begin slower bottom scan... 0 3 outstrarray 0 get length 4 sub {/hposn exch store % for each horizontal line 0 1 outstrarray length 1 sub { % scan upward /vposn exch store instrarray vposn get hposn get 255 eq { instrarray vposn get hposn 1 add get 255 eq { instrarray vposn get hposn 2 add get 255 eq {exit} if } if } if outstrarray vposn get hposn triwhite putinterval } for % for each vertical line } for % completing h swee 0 vmreclaim } store %%%%%%%%%%%%%%% demos -- remove or alter before reuse %%%%%%%%%%%%%%%%%%%%%%%%%%% % Uncomment ONE needed demo at a time! % Demo #1 -- simple unmodified image read and write (1 seconds for 1000x1000) % grabbitmap % read the input BMP file % instr2outstr % copy input to output array % savebitmap % save the output BMP file % Demo #2 -- crop image. Four data bytes are llx, lly, urx, ury % (1 seconds for 1000x1000 depends on crop tightness) % grabbitmap % 150 175 500 557 cropimage % crop image to positions shown % savebitmap % Demo #3 -- Flip image vertically. (1 seconds for 1000x1000) % grabbitmap % flipimagevertically % flip image vertically % savebitmap % Demo #4 -- Flip image horizontally. (3 seconds for 1000x1000) % grabbitmap % flipimagehorizontally % flip image vertically % savebitmap % Demo #5 -- Rotate image 180 degrees. (3 seconds for 1000x1000) % grabbitmap % rotateimageby180 % flip image vertically % savebitmap % Demo #6 -- Rotate image 90 degrees. (5 seconds for 1000x1000) % grabbitmap % rotateimageby90 % flip image by 90 degrees CW % savebitmap % Demo #7 -- Rotate image 270 degrees. (5 seconds for 1000x1000) % grabbitmap % rotateimageby270 % flip image by 270 degrees CW % savebitmap % Demo #8 -- Replace image with negative. (6 seconds for 1000x1000) % grabbitmap % negateimage % replace image with negative % savebitmap % Demo #9 -- quick background knockout to white stopwatchon grabbitmap knockback % quick background knockout savebitmap stopwatchoff %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % showpage % EOF