%!PS % PS Bitmap "Swings and Tilts" View Camera Effects % ================================================ % by Don Lancaster % copyright c 2005 by Don Lancaster and Synergetics 3860 West First % Street, Box 809 Thatcher AZ, 85552. (928) 428-4073. don@tinaja.com % http://www.tinaja.com ALL commercial and web rights FULLY reserved. %%%%%%%%%%%%%%%%%%%%%%%%% ENTER DATA HERE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /rootfilename (nukeyt01.bmp) store /diskfileheader (C:\\Documents and Settings\\don\\Desktop\\aarawpix\\) store /oldtopleft 85 store % define two top points that shift /newtopleft 140 store /oldtopright 463 store /newtopright 384 store /dowhitefill true store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Copyright c 2003 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 1.5 % PostScript-as-language utility reads a .BMP bitmap file and provides "view camera" % swing and tilt image distortion effects. Such as making edges truly vertical. % This TILTER91.PSL combines swings & tilts with nowhite and a simplified input. % It is an improvement over nutilt01.psl in that quad numeric entry is used for % a pair of top line shift points. This reduces the number of approximations % and improves perspective lettering reversibility. It may also improve linearity. % To use, modify filenames and shift points above. Resave and send % to Acrobat Distiller. Always run at highest possible resolution, preferably % before any other post processing. % Presently works on horizontal scan lines only. For horizontal distortion % correction, pre-rotate image by 90 degrees, correct, and rotate back. % A typical 600 x 600 bitmap may take ten seconds or so to process. % One left pixel column and two right pixel columns are lost during processing % and replaced by neighbor values. Pre-expand image if edge values are critical. % PRELIMINARY CODE!!! Please report any bugs or enhancements to don@tinaja.com % Target filename presently is original filename with SIXTH letter from the % left changed to "K" as in "keystoned". stuff01.bmp becomes stuffk1.bmp % A "no Acrobat file produced" error is normal and expected. % ========= % IMPORTANT NOTE: Don Lancaster's file gonzo.ps is optional for this program. % After obvious location mods, uncomment ONE of the following two lines for use: % (C:\\Documents and Settings\\don\\Desktop\\gonzo\\gonzo.ps) run % use internal gonzo % (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, 2001 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 % activate gonzo utilities if used % gonzo excerpt 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 % gonzo excerpt 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 % preprocess and early report to find slope and offset. Currently not deferred.. /leftdelta newtopleft oldtopleft sub store % REVERSE because .BMP top=bottom! /rightdelta newtopright oldtopright sub store (leftdelta is --->) print leftdelta == (rightdelta is --->) print rightdelta == /neutralaxis newtopright newtopleft sub leftdelta dup rightdelta neg add div mul newtopleft add store /gain leftdelta neutralaxis newtopleft sub div store (neutralaxis is ----> ) print neutralaxis == (gain is -----------> ) print gain == % check: (\nleftdeltacheck ----------------> ) print neutralaxis newtopleft sub gain mul newtopleft add == (rightdeltacheck ----------------> ) print neutralaxis newtopright sub gain mul newtopright add == /whitecount 0 store % ========= % dink with filenames rootfilename (.) search {dup length string cvs /pre exch store /gotcha exch store /post exch store pre dup length 2 sub (k) putinterval pre gotcha mergestr post mergestr /wfilename exch store }{mistook_in_filename} ifelse % input name of bmp file to be swung and tilted here... % /diskfilesourcename (decrpt01.bmp) store % source .BMP % /diskfiletargetname (decrptt1.bmp) store /diskfilesourcename rootfilename store /diskfiletargetname wfilename store % example .BMP files available at http://www.tinaja.com/images/bargs/ . /sourcefilename diskfileheader diskfilesourcename mergestr store /targetfilename diskfileheader diskfiletargetname mergestr store /readfile sourcefilename (r) file store % establish input read file /writefile targetfilename (w+) file store % establish output target file /bitsperpixelposition 28 store % These are .BMP header offset params /datastartposition 10 store /horizontalpixels 18 store /verticalpixels 22 store %%%%%%%%% CHECK FOR VALID 24-BIT UNCOMPRESSED BITMAP %%%%%%%%%%%%% % BM check: readfile (XX) readstring pop % error message if file not two chars long (BM) eq {(\nFile 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 % Datastart: readfile datastartposition setfileposition % access 4 {readfile read pop} repeat % get bitmap width bytes error if not present 3 {256 mul add} repeat % calculate data start dup /datastart exch def 10 string cvs % make string (Datastart is ) exch 10 string cvs mergestr ( bytes.\n\n) mergestr print flush % 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 Height: 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 %%%%%%%%%%%%%%%% COPY HEADER TO TARGET FILE %%%%%%%%%%%%%%%%%% % The entire old header gets reused. % 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 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 datastart setfileposition %%%%%%%%%%%%%% DISTORTION CORRECTION ROUTINES %%%%%%%%%%%%%%% %% Derivation of swing tilt adjustment scheme: %% %% add stuff here %% this must then be restricted to the original x range of 1 to hres -3. %% edge pixels are lost because of the "overreach" of the cubic spline interpolator. %% formula for a = -1 basis interpolation is %% (pix-1)*(basis-1) + (pix)*(basis) + (pix+1)*(basis+1) + (pix+2)*(basis+2) + % interpolation uses a bicubic lookup value and a string of % (r-1 g-1 b-1 r g b r+1 g+1 b+1 r+2 g+2 b+2)and writes new triad % doing a cubic interpolation. %% enter with x'= hpos; exit with RGB x placed in targetstring % findcurrentpixel interpolates pixel on stack top, usually moves it. /findcurrentpixel { /st linestring % preplace avoids exch % next calculate the needed gain... % /neededgain hposn curgainslope mul curyintercept add store % y = mx + b % /soughtpixel hposn neededgain mul store neutralaxis hposn sub gain mul vgainfract mul hposn add /soughtpixel exch store false { (curvline --> ) print curvline == (hposn --> ) print hposn == (spxl----> ) print soughtpixel == (vgainfract->) print vgainfract == } if soughtpixel dup 1 le {pop 1} if % but never less than one dup hresminus3 ge {pop hresminus3} if % or more than hres-3 dup dup floor sub 20 mul floor cvi % change fractional offset to 0-20 % will automagically be 0 at line ends. /ct exch store % table lookup cspline coefficients cvi 3 mul 3 sub % back up one pixel for -1 12 getinterval % grab twelve linestring RGB pixels false {dup == (\n) print } if store % store as /st % calc interpol via cubic spline targetstring hposn3 2 add % preposition put info to avoid exch targetstring hposn3 1 add targetstring hposn3 st 0 get b0 ct get mul % new red pixel using bicubic interpolation st 3 get b1 ct get mul add st 6 get b2 ct get mul add st 9 get b3 ct get mul add cvi dup 0 lt {pop 0} if dup 255 ge { pop nowhite {255}{254} ifelse % correct possible over/under /whitecount whitecount 1 add store } if % and fix whitefill if wanted put st 1 get b0 ct get mul % new green pixel st 4 get b1 ct get mul add st 7 get b2 ct get mul add st 10 get b3 ct get mul add cvi dup 0 lt {pop 0} if dup 255 gt {pop 255} if put st 2 get b0 ct get mul % new blue pixel st 5 get b1 ct get mul add st 8 get b2 ct get mul add st 11 get b3 ct get mul add cvi dup 0 lt {pop 0} if dup 255 gt {pop 255} if put } bind def /nowhite true store % Twelve data values are needed for interpolation: % [r-1 g-1 b-1 r g b r+1 b+1 g+1 r+2 b+2 g+2] % see http://www.tinaja.com/glib/basis.pdf at http://www.tinaja.com/gurgrm01.asp % /basistab is a cubic spline interpolation lookup table. % [pix-1 + pix + pix+1 + pix+2] coeffients /basistab % from http://www.tinaja.com/psutils/imtable.psl reduced to 20 lowres values [ [ 0.000 1.000 0.00 -0.000 ] [ -0.007 0.999 0.007 -0.000 ] [ -0.026 0.998 0.028 -0.001 ] [ -0.053 0.992 0.064 -0.003 ] [ -0.083 0.979 0.113 -0.009 ] [ -0.111 0.954 0.176 -0.020 ] [ -0.132 0.916 0.252 -0.036 ] [ -0.145 0.863 0.338 -0.057 ] [ -0.147 0.795 0.432 -0.080 ] [ -0.140 0.715 0.529 -0.103 ] [ -0.125 0.625 0.625 -0.125 ] [ -0.103 0.529 0.715 -0.140 ] [ -0.080 0.432 0.795 -0.147 ] [ -0.057 0.338 0.863 -0.145 ] [ -0.036 0.252 0.916 -0.132 ] [ -0.020 0.176 0.954 -0.111 ] [ -0.009 0.113 0.979 -0.083 ] [ -0.003 0.064 0.992 -0.053 ] [ -0.000 0.028 0.998 -0.026 ] [ -0.000 0.007 0.999 -0.007 ] [ -0.000 0.000 1.000 -0.000 ] ] store /b0 % extracted from above [0.000 -0.007 -0.026 -0.053 -0.083 -0.111 -0.132 -0.145 -0.147 -0.140 -0.125 -0.103 -0.080 -0.057 -0.036 -0.020 -0.009 -0.003 -0.000 -0.000 -0.000 ] store /b1 % extracted from above [1.000 0.999 0.998 0.992 0.979 0.954 0.916 0.863 0.795 0.715 0.625 0.529 0.432 0.338 0.252 0.176 0.113 0.064 0.028 0.007 0.000 ] store /b2 % extracted from above [0.00 0.007 0.028 0.064 0.113 0.176 0.252 0.338 0.432 0.529 0.625 0.715 0.795 0.863 0.916 0.954 0.979 0.992 0.998 0.999 1.000 ] store /b3 % extracted from above [-0.000 -0.000 -0.001 -0.003 -0.009 -0.020 -0.036 -0.057 -0.080 -0.103 -0.125 -0.140 -0.147 -0.145 -0.132 -0.111 -0.083 -0.053 -0.026 -0.007 -0.000 ] store %% See Gurugram #04 for further details http://www.tinaja.com/gurgrm01.asp %%%%%%%%%%%%%%%% MODIFY AND WRITE PIXELS %%%%%%%%%%%%%%%%%% /linestring hres 3 mul padding add string store % read file line buffer /targetstring hres 3 mul padding add string store % write file line buffer targetstring dup length 1 sub 0 put % zero max padding targetstring dup length 2 sub 0 put % some may overwrite targetstring dup length 3 sub 0 put /hresminus3 hres 3 sub store % slight speedup trick /mainloop { readfile datastart setfileposition % position read file 0 1 vres 1 sub { /curvline exch store % for each horizontal line curvline 24 mod 0 eq % pretty print progress {(.) print flush} if /vgainfract curvline vres 1 sub %% huh??? div % keystone correction vertical 1 sub neg 2 mul 1 sub store true { curvline == vgainfract == (\n) print } if readfile linestring readstring % read current line not {big_time_read_error} if % should always read pop % rowstart string has changed! 0 1 hres 1 sub % start line pixel loop {dup /hposn exch store % pixel position 3 mul /hposn3 exch store % hposn3 pixel in bmp color space findcurrentpixel % calculate & write new pixel } for % next line MUST start on an even 32-bit boundary writefile targetstring writestring } for % (\npadding is ) padding 12 string cvs mergestr (.\n) mergestr print % debug } bind def stopwatchon mainloop % this does it all stopwatchoff (\nA total of ) whitecount 12 string cvs mergestr ( white pixels have been substituted\n\n) mergestr print flush (\nFinished.\n) print % report % EOF