%!PS % PS Bitmap Dodge and Burn utility % =================================== % by Don Lancaster % 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 2.1 % PostScript-as-language utility allows selective dodging and burning of any .BMP image. % Presently allowing both luminance, saturation, chroma, and gamma corrections. % Automatic upscaling of masks to exactly fit image size. % To use, load into wp or editor, change filenames and shading values below and % send to Distiller. A "no Acrobat PDF file produced" error is normal and expected. % Yes, you can do a lot of this with PhotoShop. But this code is free, flexible, and % easily controlled with all underlying algorithms immediately accessible. % Additional tutorials and examples at end of file. % ========= % IMPORTANT NOTE: Don Lancaster's file gonzo.ps is recommended for this program. % After obvious location mods, uncomment ONE of the following two lines: (C:\\windows\\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 % uncomment to activate gonzo utilities % ========= % define color weights here .11 .59 .30 is tv and PostScript standard. /redweight 0.30 store % red component of luminance /greenweight 0.59 store % green component of luminance /blueweight 0.11 store % blue component of luminance /dodgeburn {dbmap} store % current choices: dbluminance dbsaturation dbmap % dbgamma dbhue dbvignette dbgray /gamma [0 .25 .5 .75 1.0] store % default gamma /plotgammaflag false store % % don't plot % The image is split up into (n)^2 areas contolled by (n+1)^2 data points that get % expanded and filtered into 32*(n+1)^2 final data values. Each area % interpolates between its four corner data points. % /makeredchanges, etc.. lets you selectively change one or more colors only. /makeredchanges true store % false = do not change red /makegreenchanges true store /makebluechanges true store % /dbdata controls the effect of each area. A 10x10 matrix controls 81 areas. % 1.0 = normal less is darker or lower saturation, more is lighter % map can be any size above 3x3 and need not be square. /dbdata [ [1.00 1.00 1.00 1.00 0.88 0.76 0.64 0.52 0.40 0.28] % row at top of screen [1.00 1.00 1.00 1.00 1.00 0.88 0.76 0.64 0.52 0.40] [1.00 1.00 1.00 1.00 1.00 1.00 0.88 0.76 0.64 0.52] [1.00 1.00 1.00 1.00 1.00 1.00 1.00 0.88 0.76 0.64] [1.00 1.00 1.00 0.55 1.00 1.00 1.00 1.00 0.88 0.76] [1.00 1.00 0.55 0.55 1.00 1.00 1.00 1.00 1.00 0.88] [1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00] [1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00] [0.65 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00] [0.30 0.65 1.00 1.00 1.00 1.00 1.00 1.00 1.00 1.00] % row at bottom of screen ] store % dbdata arrays can be any length. /dbdata [ % example of full contrast white ul to black lr [1 0.75 0.5 ] [0.75 0.5 0.25] [0.5 0.25 0 ] ]store /dbdata [ % example of typical flash correction mask [0.5 0.6 0.7 ] [0.4 0.5 0.6 ] [0.3 0.4 0.5 ] ]store /dbdata [ % vignette example [0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5] [0.5 0.667 0.833 1 1 1 1 1 1 1 1 1 1 0.833 0.667 0.5] [0.5 0.833 1 1 1 1 1 1 1 1 1 1 1 1 0.833 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0.5] [0.5 0.833 1 1 1 1 1 1 1 1 1 1 1 1 0.833 0.5] [0.5 0.667 0.833 1 1 1 1 1 1 1 1 1 1 0.833 0.667 0.5] [0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5] ] store /dbdata [ % example of typical flash correction mask [0 0 0 0 0 0 0 ] [0 1 1 1 1 1 0 ] [0 0 0 0 0 0 0 ] ]store /dbdata [ % example of typical flash correction mask [0 0 0 ] [1 1 1 ] [1 0 1 ] [1 0 1 ] [1 0 1 ] [1 0 1 ] [1 0 1 ] [1 1 1 ] [1 0 1 ] [0 0 0 ] [0 1 0 ] [0 0 0 ] [1 0 1 ] [1 1 1 ] [ 0 0 0 ] ]store % input name of bmp file to be evaluated here... /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store /diskfilesourcename (terms7.bmp) store /diskfiletargetname (termd7.bmp) store /setupfiles { /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 currentdict /auxdiskfilesourcename known { % auxfile used for transparency /auxsourcefilename diskfileheader auxdiskfilesourcename mergestr store /auxreadfile auxsourcefilename (r) file store % establish aux input read file } if } def /dbmapscale 1.0 store % Use 0.5 if map values exceed 1.0 /whiteref (XXX) def % truewhite reference whiteref 0 255 put whiteref 1 255 put whiteref 2 255 put /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 %%%%%%%%%%%%% /checkvalidity { % 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 } store %%%%%%%%%%%%%%%% COPY HEADER TO TARGET FILE %%%%%%%%%%%%%%%%%% /writeheader { % 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 } store %%%%%%%%%%%%%% SERVICE UTILS %%%%%%%%%% % filterdbdata smooths the data to ease eye sensitivity to sudden changes. Current % algorithm is to double data points, then use (value + ((prev + post)/2))/2 /filterdbdata { mark dbdata {xdblarray xfiltdbarray} forall ] ydblarray ydbfilt } store % redoubling for further smoothing /dblfilterdbdata { mark filtdbdata {xdblarray xfiltdbarray} forall ] ydblarray ydbfilt } store /xdblarray {/curarray exch store mark 0 1 curarray length 2 sub {/caposn exch store curarray caposn get dup dup curarray caposn 1 add get exch sub 2 div add } for curarray dup length 1 sub get ] } store /ydblarray {/curarray exch store mark 0 1 curarray length 2 sub {/caposn exch store /wuz curarray caposn get store /wilby curarray caposn 1 add get store wuz % get previous then make new interpolated mark 0 1 wuz length 1 sub {/curele exch store wuz curele get dup wilby curele get exch sub 2 div add } for ] } for curarray dup length 1 sub get ] } store /ydbfilt { /curarray3 exch store mark curarray3 0 get % get first element 1 1 curarray3 length 2 sub {/curyposn1 exch store /prev curarray3 curyposn1 1 sub get store /curr curarray3 curyposn1 get store /post curarray3 curyposn1 1 add get store mark 0 1 curr length 1 sub {/yypos exch store curr yypos get prev yypos get post yypos get add 2 div add 2 div } for ] } for curarray3 dup length 1 sub get % get last element ] /filtdbdata exch store } store % xfiltdbarray low pass filters an array to value + (value + ((prev + post)/2))/2 % by expanding input dbarrays. This does the horizontal expansion and filtering. /xfiltdbarray {/curarray1 exch store mark curarray1 0 get 1 1 curarray1 length 2 sub {/caposn1 exch store curarray1 caposn1 get curarray1 caposn1 1 sub get curarray1 caposn1 1 add get add 2 div add 2 div } for curarray1 dup length 1 sub get ] } store % calcrowdbfiltdata interpolates array values for the selected line. /calcrowdbfiltdata { vline vres 1 sub div 1 exch sub filtdbdata length 2 sub 0.99999 add % as in 8.999 for 10 array values mul 0.00001 add /vfract exch store /abovedbdata filtdbdata vfract floor cvi get store /belowdbdata filtdbdata vfract ceiling cvi get store /vfractfract vfract dup floor sub store /rowdbdata mark 0 1 filtdbdata 0 get length 1 sub {/iii exch store % want single array length! abovedbdata iii get belowdbdata iii get sub vfractfract mul abovedbdata iii get exch sub } for ] def } def % calccurdatavalue interpolates the line rowdbdata for the current value. /calccurdatavalue { /hfract posn hres 1 sub 3 mul div rowdbdata length 2 sub 0.99999 add % as in 8.999 for 10 values mul store /hfractfract hfract dup floor sub store rowdbdata hfract floor cvi get dup rowdbdata hfract ceiling cvi get exch sub hfractfract mul add /curdatavalue exch store } store %%%%%%%% ACTUAL ROUTINES USED BY dodegburn %%%%%%%%%% % dbmap maps the data value as a gray. /dbmap {curpixelstring 0 curdatavalue 255 mul dbmapscale mul cvi clipcheck put curpixelstring 1 curdatavalue 255 mul dbmapscale mul cvi clipcheck put curpixelstring 2 curdatavalue 255 mul dbmapscale mul cvi clipcheck put } store % dbluminance brightens or darkens each pixel per the data map. /dbluminance { curpixelstring 2 get % get red, green blue curpixelstring 1 get curpixelstring 0 get 3 copy add add 765 eq % check for 256-256-256 pure white holdwhite and not { makebluechanges { curdatavalue mul % adjust blue luminance round cvi % change to integer clipcheck254 % restrict range curpixelstring exch 0 % and replace exch put }{pop} ifelse makegreenchanges { curdatavalue mul % adjust green luminance round cvi % change to integer clipcheck254 % restrict range curpixelstring exch 1 % and replace exch put }{pop} ifelse makeredchanges { curdatavalue mul % adjust red luminance round cvi % change to integer clipcheck254 % restrict range curpixelstring exch 2 % and replace exch put }{pop} ifelse } {pop pop pop} ifelse % no change if white } store % dbsaturation enhances or reduces color saturation. Also used to force NTSC gray /dbsaturation { curpixelstring 2 get % get red, green blue /curred exch store curpixelstring 1 get /curgreen exch store curpixelstring 0 get /curblue exch store curred redweight mul curgreen greenweight mul add curblue blueweight mul add % calculate current luminance /curlumin exch store /bluedelta curblue curlumin sub store /greendelta curgreen curlumin sub store /reddelta curred curlumin sub store makeredchanges { reddelta curdatavalue mul curlumin add cvi clipcheck curpixelstring exch 2 exch put } if makegreenchanges { greendelta curdatavalue mul curlumin add cvi clipcheck curpixelstring exch 1 exch put } if makebluechanges { bluedelta curdatavalue mul curlumin add cvi clipcheck curpixelstring exch 0 exch put } if } store % dbgray is a simpler variant on dbsaturation that forces a NTSC gray /dbgray { curpixelstring 2 get % get red, green blue /curred exch store curpixelstring 1 get /curgreen exch store curpixelstring 0 get /curblue exch store curred redweight mul curgreen greenweight mul add curblue blueweight mul add % calculate current luminance cvi /curlumin exch store curpixelstring 0 curlumin put curpixelstring 1 curlumin put curpixelstring 2 curlumin put } store % db gamma causes a progrmmable gamma correction... /dbgamma { curpixelstring 2 get % get red, green blue /curred exch store curpixelstring 1 get /curgreen exch store curpixelstring 0 get /curblue exch store makeredchanges { /gammawant gamma1 curred get store gammawant curred sub curdatavalue mul curred add cvi clipcheck curpixelstring exch 2 exch put } if makegreenchanges { /gammawant gamma1 curgreen get store gammawant curgreen sub curdatavalue mul curgreen add cvi clipcheck curpixelstring exch 1 exch put } if makebluechanges { /gammawant gamma1 curblue get store gammawant curblue sub curdatavalue mul curblue add cvi clipcheck curpixelstring exch 0 exch put } if } store % dbmask replaces everything under the mask value with black. Used to form a black mask. /dbmask { curpixelstring 2 get % get red, green blue /curred exch store curpixelstring 1 get /curgreen exch store curpixelstring 0 get /curblue exch store curred curdatavalue le {0 curpixelstring exch 2 exch put} if curgreen curdatavalue le {0 curpixelstring exch 1 exch put} if curblue curdatavalue le {0 curpixelstring exch 0 exch put} if } def % dbredsep creates a red separation. Same for dbgreensep and dbbluesep. % One underappreciated use is that certain colors may have more detail and % dynamic range when very dark or very light. /dbredsep { curpixelstring 0 0 put curpixelstring 1 0 put } def /dbgreensep { curpixelstring 0 0 put curpixelstring 2 0 put } def /dbbluesep { curpixelstring 1 0 put curpixelstring 2 0 put } def % dbtransblend overlays two similar sized images /dbtransblend { curpixelstring 2 get /curred exch store % get red, green blue curpixelstring 1 get /curgreen exch store curpixelstring 0 get /curblue exch store auxcurpixelstring 2 get /auxcurred exch store % get red, green blue auxcurpixelstring 1 get /auxcurgreen exch store auxcurpixelstring 0 get /auxcurblue exch store curblue auxcurblue exch sub curdatavalue mul curblue add round cvi curpixelstring exch 0 exch put curgreen auxcurgreen exch sub curdatavalue mul curgreen add round cvi curpixelstring exch 1 exch put curred auxcurred exch sub curdatavalue mul curred add round cvi curpixelstring exch 2 exch put } store % dbhue adjusts the hue in degrees of correction. There are six subprograms dbh0 to dbh5 % that calculate hsb, and six subprograms set0 to set300 that shift the colors. /dbhue { curpixelstring 2 get /curred exch store % get red, green blue curpixelstring 1 get /curgreen exch store curpixelstring 0 get /curblue exch store % 0 1 2 3 4 5 % red green green blue blue red max % green red blue green red blue % blue blue red red green green min curred curgreen ge {curred curblue ge {curblue curgreen ge {dbh5}{dbh0}ifelse} % cases 045 {dbh4} ifelse } {curred curblue ge {dbh1} % cases 123 {curblue curgreen ge {dbh3}{dbh2}ifelse} ifelse } ifelse /newhue curhue curdatavalue add dup 0 lt {360 add} if % if curdatavalue + curhue < 0 dup 360 ge {360 sub} if % if curdatavalue + curhue > 360 store % find new hue [{set0}{set60}{set120}{set180}{set240}{set300}] newhue 60 div floor cvi get exec } store /dbh0 {/brt curblue store % red to yellow /sat curred curblue sub store /curhue 0 curgreen brt sub sat div1 60 mul add store} store /dbh1 {/brt curblue store % yellow to green /sat curgreen curblue sub store /curhue 60 1 curred brt sub sat div1 sub 60 mul add store} store /dbh2 {/brt curred store % green to aqua /sat curgreen curred sub store /curhue 120 curblue brt sub sat div1 60 mul add store} store /dbh3 {/brt curred store % aqua to blue /sat curblue curred sub store /curhue 180 1 curgreen brt sub sat div1 sub 60 mul add store} store /dbh4 {/brt curgreen store % blue to violet /sat curblue curgreen sub store /curhue 240 curred brt sub sat div1 60 mul add store} store /dbh5 {/brt curgreen store % violet to red /sat curred curgreen sub store /curhue 300 1 curblue brt sub sat div1 sub 60 mul add store} store /div1 {dup 0 eq {pop pop 0}{div} ifelse} store % define gray as hue=0 % these set the new hue depending on which hexant they are in... /set0 { brt % blue to brightness for red to yel newhue 60 div sat mul brt add % green proportional sat brt add % full red stuffstring} store /set60 { brt cvi clipcheck % blue to brightness for yel to grn sat brt add % full green 1 newhue 60 sub 60 div sub sat mul brt add % red inverse proportional stuffstring} store /set120 {newhue 120 sub 60 div sat mul brt add % blue proportional for grn to aqua sat brt add % full green brt % red to brightness stuffstring} store /set180 { sat brt add % full blue for aqua to blue 1 newhue 180 sub 60 div sub sat mul brt add % green inverse proportional brt % red to brightness stuffstring} store /set240 { sat brt add % full blue for blue to violet brt % green to brightness newhue 240 sub 60 div sat mul brt add % red proportional stuffstring } store /set300 {1 newhue 300 sub 60 div sub sat mul brt add % blue inverse for violet to red brt % green to brightness sat brt add % full red stuffstring } store /stuffstring { makeredchanges {cvi clipcheck curpixelstring exch 2 exch put}{pop} ifelse makegreenchanges {cvi clipcheck curpixelstring exch 1 exch put}{pop} ifelse makebluechanges {cvi clipcheck curpixelstring exch 0 exch put}{pop} ifelse } store %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % expandgamma expands and filters the gamma curve to additional data points /gammatrips 5 store % number of filter trips /expandgamma { /gamma1 gamma store gammatrips { % multiply values by 32 mark 0 1 gamma1 length 2 sub {/gpos exch store % interpolate points gamma1 gpos get dup gamma1 gpos 1 add get add 2 div } for gamma1 dup length 1 sub get ] /gamma1 exch store mark gamma1 0 get 1 1 gamma1 length 2 sub {/gpos exch store % filter to (point + slope) /2 gamma1 gpos get gamma1 gpos 1 sub get dup gamma1 gpos 1 add get exch sub 2 div add add 2 div } for gamma1 dup length 1 sub get ] /gamma1 exch store } repeat mark % change to 256 element array 0 1 254 {255 div gamma1 length 1 sub mul /ggpos exch store /glow gamma1 ggpos floor cvi get store /ghi gamma1 ggpos ceiling cvi get store /gfract ggpos dup floor sub store { (ggpos -------------------------------> ) print ggpos == (glow ---> ) print glow == (ghi --> ) print ghi == (gfract -----------------> ) print gfract == } pop ghi glow sub gfract mul glow add % ( raw interpol --->) print dup == 255 mul round cvi (\n\n) print } for 255 ] /gamma1 exch store gamma1 == } store %%%%%%%%%%%%% % plotgamma makes a gamma plot for viewing... /plotgamma { save /gammasnap exch store [/CropBox [0 0 400 400] % set the wierd size /PAGES pdfmark [ {Catalog} << /ViewerPreferences << /FitWindow true /CenterWindow true >> /PageLayout /OneColumn % continous /Pagemode /UseNone % no thumbs /View [/XYZ null null 1 ] % force 100% >> /PUT pdfmark 100 100 10 setgrid 20 20 showgrid line1 0 0 mt 0 1 gamma1 length 1 sub {/ppos exch store ppos 20 mul gamma1 length div gamma1 ppos get 256 div 20 mul dup == lineto } for stroke 0 1 gamma length 1 sub {/gggposn exch store gggposn gamma length 1 sub div 20 mul gamma gggposn get 20 mul mt dot } for /cstretch 0.0 store /sstretch 0.0 store /font1 /Helvetica-Bold 1.05 gonzofont font1 10 23.3 (gamma curve for) cc 10 22 ([ ) gamma { 10 string cvs mergestr ( ) mergestr} forall (]) mergestr cc showpage gammasnap restore} store %%%%%%%%%%%%%%%%%%%%%%%%%%%% % clipcheck restricts color values to 0-255 range /clipcheck { dup 255 ge {pop 255} if dup 0 le {pop 0 } if } store % clipcheck254 restricts color values to 0-254 range. Used to preserve transparency. % / true store % do not change 255-255-255 for transparency? /clipthresh {holdwhite {254}{255} ifelse} store /clipcheck254 { dup clipthresh ge {pop clipthresh}if dup 0 le {pop 0 } if } store %%%%%%%%%%%%%%%% COPY DATA TO TARGET FILE, ADJUSTING AS NEEDED %%%%%%%%%%%%%%%%%% /filtertrips 4 store % used to smooth dbmaps. Reduce if db data > 20x20. /dodge&burn {12345 srand % make randoms repeat setupfiles % grab current filenames checkvalidity % make sure image is .BMP writeheader % write .BMP header /linestring hres 3 mul string store % set read file line buffer /targetstring hres 3 mul padding add string store % set write file line buffer targetstring dup length 1 sub 0 put % zero max padding targetstring dup length 2 sub 0 put targetstring dup length 3 sub 0 put /auxlinestring hres 3 mul string store % set aux read file line buffer /auxtargetstring hres 3 mul padding add string store % set aux write file line buffer auxtargetstring dup length 1 sub 0 put % zero max padding auxtargetstring dup length 2 sub 0 put auxtargetstring dup length 3 sub 0 put /oldcolor (ZZZ) def /whitecount 0 store filterdbdata % smooth mask filtertrips {dblfilterdbdata} repeat % experimental quad filtering makes 32x array expandgamma % expand gamma curve to additional filtered values plotgammaflag {plotgamma} if % start main loop here 0 1 vres 1 sub { /vline exch store % start of next hires line vline 24 mod 0 eq % pretty print progress {(.) print flush} if vline hres 3 mul padding add mul actualdatastart add dup dup % position read and write identically readfile exch setfileposition writefile exch setfileposition currentdict /auxreadfile known {auxreadfile exch setfileposition}{pop} ifelse readfile linestring readstring % grab a line of characters not {big_time_read_error} if currentdict /auxreadfile known { auxreadfile auxlinestring readstring % grab an aux line of characters not {big_time_aux_read_error} if /auxsourcestring exch store } if /sourcestring exch store calcrowdbfiltdata % interpolate filtered dbdata for line % start row line loop 0 3 hres 1 sub 3 mul { /posn exch store % start line pixel loop sourcestring posn 3 getinterval /curpixelstring exch store currentdict /auxreadfile known { auxsourcestring posn 3 getinterval /auxcurpixelstring exch store } if calccurdatavalue dodgeburn % does the selected mode curpixelstring targetstring exch posn exch putinterval } for % next line MUST start on an even 32-bit boundary writefile targetstring writestring } for % (\nA total of ) backcount 12 string cvs mergestr % ( pixels have been substituted\n\n) mergestr print flush (\nFinished.\n) print % report } bind def % end dodgeandburn %%%%%%%%%%%%%% TUTORIALS & EXAMPLES %%%%%%%%%%%%%%%%%%% % The key to the dodge and burn utilities is a CORRECTION MASK called ~dbmask~. % dbmask is defined as an array. They array can be of any size from 2x2 upwards. % A 3x3 array might be used for a simple shading correction. A 10x10 array might % be used for a serious dodge and burn. A 16x16 or larger array might be used % for a vignette. Arrays need not be square, but usually are. % The mask gets applied to one or more .BMP image files that get read. A MODE is % selected that makes some sort of pixel-by-pixel adjustment that appears in one % or more .BMP output files. Current MODES include... % (A) Show the Mode Map % (B) Adjust Luminance (classic dodge and burn) % (C) Adjust Color Saturation % (D) Convert to NTSC gray % (E) Localized blob burn % (F) Vingette Luminance per mask % (G) Adjust Hue % (H) Chroma Waterfall Background % (I) Adjust Gamma (raise lower contrast, blacks, etc...) % (J) Make Black Mask % (K) Do color separations % (L) Merge overlay with variable transparency % Modes, Masks, Input File(s) and Output File(s) are set early in the program or % per the examples below. After defining values, the Dodge & Burn utility is saved % AS AN ORDINARY ASCII TEXTFILE and then sent to Adobe Acrobat Distiller. % Each array value is a CORRECTION that gets used to do something. It might make % the current pixel brighter or darker, redder, bluer, or greener, or increase % or decrease the saturation per the mode selection. 1.0 is usually a "don't change" % mode value, while 0 is a "eliminate completely". Values greater than 1.0 will often % be "more" and less than 1.0 will be "less". Some modes use special array values. % The correction mask is expanded and filtered and then expanded again to exactly % fit the size of the image being adjusted. Filtering is used to attempt to reduce % certain visual artifacts. The current filter algorithm is to double the data % points and then replace each point with half its value plus half the average of % the previous "wuz" and post "wilby" points. Repeated 5X for a 32X array magnification. % Otherwise known as an 0.25 wuz + 0.5 is + 0.25 wilby approximate Gaussian(?) filter. % You are urged to use your own .BMP practice files. Default files TERMY7.BMP and % FLOWSWZ7.BMP are available via http://www.tinaja.com/images/bargs/termy7.bmp and % http://www.tinaja.com/images/bargs/flowswz7.bmp hese must be moved to a Distiller % locally accessible hard disk. % To run these demos, uncomment their dodge&burn and data selection commands ONE AT A TIME. % Typical run time is ten to fifteen seconds per demo. % For operator convenience, it is probably better to ultimately create one utility for each % needed operating mode, saved under a different filename and having filenames and other % needed parameters at the very top or bottom of the file. %%%%%%% EXAMPLE (A) CREATE AND VIEW A SIMPLE SHADING MASK %%%%%%%%% % Here is a typical mask to handle the usual flash problem of the lower left being % brighter... /burn0.5SW { % 50% brightness or saturation reduction /dbdata [ [0.75 0.825 1.0] [0.625 0.75 0.82 ] [0.5 0.625 0.75] ] store } store % An ~dbmapscale~ variable in mode A lets you view "whiter than whites" by setting % your adjustment to 0.5. Note that "whiter than whites" can lead to supersaturation % and is best avoided. It is better to burn than dodge, keeping adjustments less than % or equal to one. /dbmapscale 1 store % verify scale (usually 1.0) % Note that a valid .BMP read file is required, even though each and every pixel gets % overwritten in creating the map... /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (termy7.bmp) store % set source .BMP /diskfiletargetname (dbexamA1.bmp) store % set dest .BMP /dodgeburn {dbmap} store % select operating mode burn0.5SW % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform action % as variations, create stronger or weaker brightness corrections. % adjust map for northwest, northeast, and southeast too bright. % use dodge values >1 and adjust dbmapscale to 0.5 to view brightening. %%%%%%%%%%%%%% EXAMPLE (B) DO ACTUAL 0.5 LUMINANCE DODGE %%%%%%%%%%%% % This example applies the burn0.5 map of example (A) to correct the luminance of % a photo needing dodging in the lower left corner. % This variable lets you preserve true whites for possible later overlay use... /holdwhite true store % false lets 255-255-255 whites darken as any other color % The .BMP file to be corrected must be locally disk accessible. % This particular termy7.bmp example needs both a luminance and saturation correction. /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (termy7.bmp) store % set source .BMP /diskfiletargetname (dbexamB1.bmp) store % set dest .BMP % here is a somewhat gentler burn than example (A)... /burn0.2SW { % 20% brightness or saturation reduction /dbdata [ [0.9 0.95 1.0] [0.85 0.9 0.95 ] [0.8 0.85 0.9] ] store } store /dodgeburn {dbluminance} store % select operating mode burn0.2SW % create mask map % uncomment the start of the nextline to run this demo... % burn0.2SW % create mask map % dodge&burn % perform action % as variations, substitute burn0.5 to overcorrect % or holdwhite=true to drop background. %%%%%%%%%%%%%% EXAMPLE (C) DO 0.25 SATURATION DODGE %%%%%%%%%%%% % This example applies a new burn0.25NE map of to reduce the chrominance of % a photo needing correction in the upper right corner. % At present, the result of (B) is used as an input to provide both corrections... /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dbexamB1.bmp) store % set source .BMP /diskfiletargetname (dbexamC1.bmp) store % set dest .BMP /burn0.25NE { % 25% brightness or saturation reduction /dbdata [ [0.9 0.85 0.8] [0.95 0.9 0.85] [1.0 0.95 0.9] ] store } store /burn1.0NE { % 100% brightness or saturation reduction /dbdata [ [0.5 0.25 0] [0.75 0.5 0.25] [1.0 0.75 0.5] ] store } store /burn0.5NE { % 50% brightness or saturation reduction /dbdata [ [0.75 0.625 0.5] [0.875 0.75 0.625] [1.0 0.875 0.75] ] store } store /burn0.6NE { % 60% brightness or saturation reduction /dbdata [ [0.8 0.7 0.6] [0.9 0.8 0.7] [1.0 0.9 0.8] ] store } store % whites will be automatically held as they have no saturation % caution must be used on oversaturation > 1.0 /dodgeburn {dbsaturation} store % select operating mode burn0.6NE % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform action % as variations, do both a luminance and saturation correction to perfectlybalance % the image with a pure white background. %%%%%%%%%%%%%% EXAMPLE (D) CONVERT PICTURE TO NTSC GRAY %%%%%%%%%%%% % A NTSC gray is defined by the formula 0.30 red 0.59 green and 0.11 blue % any data map can be used as values are not used. /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dbexamB1.bmp) store % set source .BMP /diskfiletargetname (dbexamD1.bmp) store % set dest .BMP /dodgeburn {dbgray} store % select operating mode % uncomment the start of the next line to run this demo... % dodge&burn % perform action %%%%%%%%%%%%%% EXAMPLE (E) LOCALIZED BLOB BURN %%%%%%%%%%%% % specific hotspots can be adjusted to correct localized regions. % This example will cause a darker spot at image center. /hotspot1 { % localized burn example /dbdata [ [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 0 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] [1 1 1 1 1 1 1 1 1 1] ] store } store % we will turn off the red and green corrections for a blue burn here... % BE SURE TO RESET AFTER USE. Note that darkening red and green emphasizes blue. /makeredchanges true store % false = do not change /makegreenchanges true store /makebluechanges false store diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dbexamB1.bmp) store % set source .BMP /diskfiletargetname (dbxmapE1.bmp) store % set dest .BMP /dodgeburn {dbmap} store % select operating mode hotspot1 % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform action diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dbexamB1.bmp) store % set source .BMP /diskfiletargetname (dbexamE1.bmp) store % set dest .BMP /dodgeburn {dbluminance} store % select operating mode hotspot1 % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform action % Be sure to reset selective color burns! /makeredchanges true store % false = do not change /makegreenchanges true store /makebluechanges true store % variations: use this on a real image with a real problem; print the map % stronger effects are obtainable with more data points or by backing off on the db filtering. % add new routines for selective red, blue, or green dodge and burn. %%%%%%%%%%%%%% EXAMPLE (F) ADD VIGNETTE BORDER %%%%%%%%%%%% % first create a vignette border and view as a map... /vignette1 { % localized burn example /vv 0.55 store /dbdata [ [vv vv vv vv vv vv vv vv vv vv vv vv vv vv vv vv] [vv 0.7 1 1 1 1 1 1 1 1 1 1 1 1 0.7 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 1 1 1 1 1 1 1 1 1 1 1 1 1 1 vv] [vv 0.7 1 1 1 1 1 1 1 1 1 1 1 1 0.7 vv] [vv vv vv vv vv vv vv vv vv vv vv vv vv vv vv vv] ] store } store /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dintchf4.bmp) store % set source .BMP /diskfiletargetname (dintchv4.bmp) store % set dest .BMP /dodgeburn {dbmap} store % select operating mode vignette1 % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform mapping action /diskfiletargetname (contav1.bmp) store % set dest .BMP /dodgeburn {dbluminance} store % select operating mode vignette1 % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % do actual vignette %%%%%%%%%%%%%% EXAMPLE (G) CHANGE HUE %%%%%%%%%%%% % The dbmaps for hue changes are somewhat different in that they cover a % range of -360 to +360 degrees. They may NOT be viewed with dbmap. % Note that hue changes do NOT adjust for human vision. Doing so leads to supersaturation % issues. Thus, ~slight~ chroma adjustments will usually appear better than drastic ones. /shadedhuedemo20 { % 20 degree hue gradient /dbdata [ [0 5 10] [-5 0 5] [-10 -5 0 ] ] store } store /hue+10degrees { /dbdata [ [10 10] [10 10] ] store } store /shadedhuedemo { % 20 degree hue gradient /dbdata [ [0 100 200] [-100 0 100] [-200 -100 0 ] ] store } store /nullcheck { % 20 degree hue gradient /dbdata [ [0 0 0] [0 0 0] [0 0 0] ] store } store /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (fluke77f.bmp) store % set source .BMP /diskfiletargetname (fluke77v.bmp) store % set dest .BMP /dodgeburn {dbhue} store % select operating mode shadedhuedemo20 % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform mapping action %%%%%%%%%%%%%% EXAMPLE (H) CHROMA WATERFALL FOR BACKGROUND %%%%%%%%%%%% % Normally you would apply this to a background only to create smooth gradient effects... /waterdown40 { % 40 degree hue gradient /dbdata [ [40 40 40] [0 0 0] [-40 -40 -40] ] store } store /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (flowswz7.bmp) store % set source .BMP /diskfiletargetname (dbexamH1.bmp) store % set dest .BMP /dodgeburn {dbhue} store % select operating mode waterdown40 % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform mapping action %%%%%%%%%%%%%% EXAMPLE (I) SELECTIVE GAMMA CORRECTION %%%%%%%%%%%% % A Gamma curve can be used to selectively enhance or reduce contrast, favor or % disfavor bights or favor or disfavor darks, among other tricks. % The map determines which areas of the photo are to have their gamma altered. % 0 = no change; 1 = maximum alteration for selected gamma. % Here is a map that causes an overall correction to all pixels... /gammaall {/dbdata [ [1 1 1] [1 1 1] [1 1 1] ] store } store /gammacompare {/dbdata % a split to check contrast improvement [[0 0 0 0 0 0 0 ] [0 0 0 0 0 0 0] [1 1 1 1 1 1 1] [1 1 1 1 1 1 1] [1 1 1 1 1 1 1] [1 1 1 1 1 1 1]] store} store % Here is a contrasting enhancing gamma curve. Any number of equally spaced points from % 0 to 1 can be used. /gammacontrastraise [0 .05 .1 .5 .9 .95 1] store /gamma {gammacontrastraise} def /plotgammaflag true def /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dbexamB1.bmp) store % set source .BMP /diskfiletargetname (dbexamI1.bmp) store % set dest .BMP /dodgeburn {dbgamma} store % select operating mode % /dodgeburn {dbmap} store % select operating mode gammacompare % create mask map % uncomment the start of the next line to run this demo... % dodge&burn % perform mapping action %%%%%%%%%%%%%% EXAMPLE (J) EXTRACT MASK %%%%%%%%%%%% % converts everything under map value to black. 255 = white /maskallbutwhite {/dbdata [ [254 254 254] [254 254 254] [254 254 254] ] store } store /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dbexamB1.bmp) store % set source .BMP /diskfiletargetname (dbexamJ1.bmp) store % set dest .BMP /dodgeburn {dbmask} store % select operating mode maskallbutwhite % create mask % uncomment the start of the next line to run this demo... % dodge&burn % perform mapping action %%%%%%%%%%%%%% EXAMPLE (K) SEPARATE COLORS %%%%%%%%%%%% % creates new files of red, blue, or green only. Also very useful for very dark or very % light portions of images where one particular color may have a lot more detail or % luminance info. Presently does not use a mask. One can easily be added for special % effects. % Repeat process once as shown for each desired color. /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (dbexamB1.bmp) store % set source .BMP /diskfiletargetname (dbexrdK1.bmp) store % set dest .BMP /dodgeburn {dbredsep} store % select operating mode % uncomment the start of the next line to run the red separation demo... % dodge&burn % perform mapping action /diskfiletargetname (dbexgnK1.bmp) store % set dest .BMP /dodgeburn {dbgreensep} store % select operating mode % uncomment the start of the next line to run the green separation demo... % dodge&burn % perform mapping action /diskfiletargetname (dbexblK1.bmp) store % set dest .BMP /dodgeburn {dbbluesep} store % select operating mode % uncomment the start of the next line to run the blue separation demo... % dodge&burn % perform mapping action %%%%%%%%%%%%%% EXAMPLE (L) TRANSPARENT IMAGE MERGE %%%%%%%%%%%%%%%%%%%%%%%%%%%%% % creates one transparency overlaid image from two sources. Map 0 = background % only 0.5 = 0.5 transparency 1 = overlay only. Image sources MUST have the same % horizontal and vertical resolution at present. % File termy7x is a temporary resizing of termy7 for this demo only. /rightblend {/dbdata % merges left pix and right pix [ [0 0 0.5 1 1] [0 0 0.5 1 1] [0 0 0.5 1 1] ] store } store /dodgeburn {dbtransblend} store % select operating mode rightblend % set map /diskfileheader (C:\\WINDOWS\\Desktop\\aaraw_pix\\) store % set directories /diskfilesourcename (termy7x.bmp) store % set source .BMP /auxdiskfilesourcename (flowswz7.bmp) store % set source .BMP /diskfiletargetname (dbexrdL1.bmp) store % set dest .BMP % uncomment the start of the next line to run the demo... dodge&burn % perform mapping action %%%%%%%%%%%%%%%%%%%%% % EOF