%!PS % PostScript Array to Image Conversion Ap: AIRBRUSH I % ==================================== % by Don Lancaster %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Copyright c 2008 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 % ========= % revised for usehsb and hueshift april 08 % IMPORTANT NOTE: Don Lancaster's file gonzo.ps is recommended for the demos in % this program. The actual utilities can stand alone as they include Gonzo excerpts. % After obvious location mods, uncomment ONE of the following two lines: % (C:\\Program Files\\Gonzo\\gonzo.ps) run % use internal gonzo % A:\\gonzo.ps) run % use external gonzo % NOTE THAT ALL PS FILENAME STRINGS !!!DEMAND!!! DOUBLE REVERSE SLASHES. % Also note that Acrobat 8.1 or higher defaults to prevent most PS disk access. % The workaround is to prerun Distiller from the Windows command line using "acrodist -F". % Any .PSL file can then be dragged and dropped into any Distiller on or under the desktop. % 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. % ===================================== % The PostScript string operator has a number of advantages over a normal % array in that it stores info more compactly, can be read and written to as a % file, has more operators available to it, and still behaves as an array % with such operators as get, put, or {} forall. % A data structure of an array-of-strings can be a potent means of storing % and manipulating x-y data that ultimately comes from or becomes a PS image % or a conventional .BMP data file. % A group of utilities that simplify and ease the use of PostScript % array-of-strings is presented here. % A complete tutorial appears as http://www.tinaja.com/glib/bmp2psa.pdf % Its sourcecode is available as http://www.tinaja.com/glib/bmp2psa.psl %%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% START GONZO EXCERPTS. %%%%%%%%%%%%%%%%% % Complete Gonzo code appears at http://www.tinaja.com/psutils/gonzoutils.psl % Gonzo use tutorial appears at http://www.tinaja.com/glib/gonzotut.pdf % 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 /random {rand 65536 div 32768 div mul cvi} def % as in -- 6 random -- % 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 %%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% START BITMAP TO ARRAY OF STRINGS CORE UTILITIES %%%%%%%%%%%%%%%%% % file variable names have been improved %%%%%%%%%% (A) MERGE STRING UTILITY %%%%%%%%%%%%%%%%%%%%%% % Excerpted from the Gonzo Utilities at http://www.tinaja.com/glib/gonzotut.pdf % Useful in combining filenames and as a general tool. /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 % uncomment for demo % (Hello ) (there.) mergestr % should return (Hello there.) %%%%%%%%%% (B) STRING TO ARRAY CONVERTER %%%%%%%%%%%%%%%%%%%%%% /string2array {mark exch { } forall ] } store % uncomment for demo % (Hello!) string2array % should return [72 101 108 108 111 33]. %%%%%%%%%% (C) ARRAY TO STRING CONVERTER %%%%%%%%%%%%%%%%%%%%%% /array2string {dup length string dup /NullEncode filter 3 -1 roll {1 index exch write} forall pop } def % uncomment for demo % [72 105 32 116 104 101 114 101] array2string % should return a (Hi there). %%%%%%%%%%%% (D) STRING READ FROM AS A FILE %%%%%%%%%%%%%%%%%%%%%%% /str1 (This is a test) store /file1 str1 0 (%EOF) /SubFileDecode filter store % uncomment for demo % 6 { file1 read pop == } repeat % should return 84 104 105 115 32 105. %%%%%%%%%%%% (E) STRING WRITTEN TO AS A FILE %%%%%%%%%%%%%%%%%%%%%%% /str2 (zzzzzzzzzzzzzzzzzzz) store /file2 str2 /NullEncode filter store % uncomment for demo % file2 65 write file2 66 write file2 67 write % should return (ABCzzzzzzzzzzzzzzzz). %%%%%%%%%% (F) CREATE DEMO ARRAY-OF-STRINGS FILES %%%%%%%%%%%%% % these workfiles can be useful for developing and testing the utilities that follow % data shown creates a 6 pixel wide by 3 pixel high image with a rainbow on the bottom % row, gray elsewhere except for an upper right red pixel. /redAOS [ [250 250 0 0 0 250 ] array2string [100 100 100 100 100 100 ] array2string [100 100 100 100 100 250 ] array2string ] store /greenAOS [ [ 0 250 250 250 0 0 ] array2string [100 100 100 100 100 100 ] array2string [100 100 100 100 100 0 ] array2string ] store /blueAOS [ [ 0 0 0 250 250 250 ] array2string [100 100 100 100 100 100 ] array2string [100 100 100 100 100 0 ] array2string ] store %%%%%%%%%%%% (G) CONVERT ARRAYS-OF-STRINGS TO A POSTSCRIPT IMAGE %%%%%%%%%%% % three AOS string gathering procs needed by image... % /xx {dup {==} forall (\n) print } store /getredAOSrow {redAOS AOSrowcount get } store /getgreenAOSrow {greenAOS AOSrowcount get } store /getblueAOSrow {blueAOS AOSrowcount get /AOSrowcount AOSrowcount 1 add store } store % increment row counter for all gets % the AOS to image converter maps the AOS into a unit square. Scaling to final size and shape % must be done by CTM mods... /convertAOStoPSimage { /AOSrowcount 0 store << /ImageType 1 /Width redAOS 0 get length /Height redAOS length /ImageMatrix [redAOS 0 get length 0 0 redAOS length 0 0 ] % /MultipleDataSources true /DataSource [ {getredAOSrow} {getgreenAOSrow} {getblueAOSrow}] /BitsPerComponent 8 /Decode [ 0 1 0 1 0 1 ] /Interpolate false /AOSrowcount 0 % custom proc added here >> dup begin image end } bind store % true for demo false { 0 0 0 setrgbcolor 50 50 10 setgrid % requires Gonzo 20 20 showgrid % requires Gonzo convertAOStoPSimage showpage } if %%%%%%%%%%%% (H) CONVERT ARRAY-OF-STRINGS TO A .BMP BITMAP %%%%%%%%%%% % requires bmpoutfileprefix path and bmpoutfilename filenames defined. % Also valid data in redAOS, greenAOS, and blueAOS. /convertAOS2BMPimage { /bmpoutfile bmpoutfileprefix % make bmp write file bmpoutfilename mergestr (w) file store /xpixels redAOS 0 get length store % calc bmp data /ypixels redAOS length store /padcount xpixels 4 mod store % calc bmp padding simplified? /totalbmpsize xpixels padcount % calc file length add ypixels mul 54 add store bmpoutfile 66 write % BEGIN HEADER bmpoutfile 77 write % with ASCII "BM". totalbmpsize 256 3 exp cvi idiv % calc length byte /byte4 exch store totalbmpsize 256 3 exp cvi mod /res3 exch store res3 65536 idiv /byte3 exch store res3 65536 mod /res2 exch store res2 256 idiv /byte2 exch store res2 256 mod /byte1 exch store bmpoutfile byte1 write % write length as modulo 256 bmpoutfile byte2 write bmpoutfile byte3 write bmpoutfile byte4 write 4 {bmpoutfile 0 write} repeat % four reserved nulls bmpoutfile 54 write % start of data offset bmpoutfile 0 write bmpoutfile 0 write bmpoutfile 0 write bmpoutfile 40 write % write data header size bmpoutfile 0 write bmpoutfile 0 write bmpoutfile 0 write bmpoutfile xpixels 256 mod write % write x size lsbs bmpoutfile xpixels 256 idiv write bmpoutfile 0 write % assume < 65K bmpoutfile 0 write % (\n\nypixels are --------------------> ) print flush ypixels == bmpoutfile ypixels 256 mod write % write y size lsbs bmpoutfile ypixels 256 idiv write bmpoutfile 0 write % assume < 65K bmpoutfile 0 write bmpoutfile 1 write % number of bit planes bmpoutfile 0 write bmpoutfile 24 write % write color mode bmpoutfile 0 write 4 {bmpoutfile 0 write} repeat % compression mode 4 {bmpoutfile 0 write} repeat % pixel data size 4 {bmpoutfile 0 write} repeat % width resolution 4 {bmpoutfile 0 write} repeat % height resolution 4 {bmpoutfile 0 write} repeat % colors used 4 {bmpoutfile 0 write} repeat % important colors 0 1 ypixels 1 sub {/currow exch store % start data write % (\n processing row ---> ) print currow == /curred redAOS currow get store % grab RGB row data /curgreen greenAOS currow get store /curblue blueAOS currow get store 0 1 xpixels 1 sub {/curpos exch store % BGR pixel write bmpoutfile curblue curpos get write bmpoutfile curgreen curpos get write bmpoutfile curred curpos get write } for % finish row padcount {bmpoutfile 0 write } repeat % add row padding } for % finish image bmpoutfile closefile % close file } bind store % and exit % true for demo false { /bmpoutfileprefix (C:\\Program Files\\Gonzo\\) store /bmpoutfilename (bmpout1.bmp) store convertAOS2BMPimage } if %%%%%%%%%%%% (I) CONVERT A .BMP BITMAP TO A PS ARRAY-OF-STRINGS %%%%%%%%%%% % requires a predefined bmpinfileprefix and bmpinfilename /inputbitmap2AOS { % START ANALYSIS /bmpinfile bmpinfileprefix % create .BMP read file bmpinfilename mergestr (r) file store bmpinfile read % check for ok format pop 66 eq % "B" as first character? bmpinfile read pop 77 eq and % "M" as second char? bmpinfile 26 setfileposition % one color plane? bmpinfile read pop 1 eq and not {NOT_A_BITMAP_FILE!} if % report bitmap error bmpinfile 28 setfileposition % report color message bmpinfile read pop 24 eq not {NOT_24_BIT_COLOR!} if bmpinfile 10 setfileposition % find the offset bmpinfile read pop bmpinfile read pop 256 mul add /offset exch store bmpinfile 18 setfileposition % find the xpixels bmpinfile read pop bmpinfile read pop 256 mul add /xpixels exch store bmpinfile 22 setfileposition % find the ypixels bmpinfile read pop bmpinfile read pop 256 mul add /ypixels exch store /padcount xpixels 4 mod store % calculate padding simplified? /redAOS ypixels array store % empty AOS arrays /greenAOS ypixels array store /blueAOS ypixels array store 0 1 ypixels 1 sub % 1 sub % temp skip last line {/rowcount exch store % GRAB DATA by rows % (rowcount ---> ) print rowcount 10 string cvs == bmpinfile offset % go to row start rowcount xpixels 3 mul % X3 for triads padcount add mul % plus padding add setfileposition % set row start /redAOSrow xpixels string store % create rows /greenAOSrow xpixels string store /blueAOSrow xpixels string store 0 1 xpixels 1 sub {/posn exch store % for each pixel triad blueAOSrow posn bmpinfile read pop put % blue greenAOSrow posn bmpinfile read pop put % green redAOSrow posn bmpinfile read pop put % red } for % till row done redAOS rowcount redAOSrow put % place strings greenAOS rowcount greenAOSrow put blueAOS rowcount blueAOSrow put } for % for each row } bind store % completing proc % true for round trip demo BMP ----- PS AOS ----> BMP and PS AOS ---> PS IMAGE % requires preplaced .BMP file data source or name change false { /bmpinfileprefix (C:\\Program Files\\Gonzo\\) store /bmpinfilename (small.bmp) store /bmpoutfilename (smallz.bmp) store inputbitmap2AOS convertAOS2BMPimage gsave 0 0 0 setrgbcolor 50 50 translate 372 354 scale convertAOStoPSimage grestore showpage } if %%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% END BITMAP TO ARRAY OF STRINGS CORE UTILITIES %%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% START POSSIBLY EXPORTABLE GLOBAL UTLILITIES %%%%%%%%%%%%%%%%% % PS LINEAR EQUATION SOLVER FOR N=4 LINEQ04.PS % ============================================ % Copyright c 1999 by Don Lancaster and Synergetics, Box 809, Thatcher, AZ, 85552 % (520) 428-4073 don@tinaja.com http://www.tinaja.com % Consulting services available per http://www.tinaja.com/info01.html % All commercial rights and all electronic media rights fully reserved. % Personal use permitted provided header and entire file remains intact. % Linking is welcome. Reposting expressly forbidden. % This utility demo shows how to use PostScript to solve linear algabraic equations % by use of Gaussian elimination. It is easily extended to higher orders. % Additional details in http://www.tinaja.com/glib/muse142.pdf % This code currently includes temporary test and reporting utils. % temp report utility /aaa {20 string cvs print ( ) print} def /rrr {(\n\n) print w0 aaa x0 aaa y0 aaa z0 aaa ( ) print a0 aaa (\n) print w1 aaa x1 aaa y1 aaa z1 aaa ( ) print a1 aaa (\n) print w2 aaa x2 aaa y2 aaa z2 aaa ( ) print a2 aaa (\n) print w3 aaa x3 aaa y3 aaa z3 aaa ( ) print a3 aaa (\n) print } def % Define or capture your data. To avoid any div0 problems, preplace your largest % absolute values on your principle diagonals... false { % dummy data set for testing /w0 -3.997 store /x0 2.075 store /y0 -0.997 store /z0 1.436 store /a0 29.223 store /w1 2.345 store /x1 -0.654 store /y1 -8.231 store /z1 1.234 store /a1 -13.491 store /w2 -3.224 store /x2 12.223 store /y2 -1.06 store /z2 4.987 store /a2 1.342 store /w3 0.334 store /x3 -1.653 store /y3 2.724 store /z3 -7.003 store /a3 -13.365 store } if /solven04 { % (\n\nraw equations) == rrr % normalize w0 to unity... /a0 a0 w0 div store /z0 z0 w0 div store /y0 y0 w0 div store /x0 x0 w0 div store /w0 1.000 store % (\n\nforcew0 to unity) == rrr % force w1 to zero... /a1 a1 a0 w1 mul sub store /z1 z1 z0 w1 mul sub store /y1 y1 y0 w1 mul sub store /x1 x1 x0 w1 mul sub store /w1 w1 w0 w1 mul sub store % check % /w1 0 store % (\n\nforce w1 to zero)== rrr % normalize x1 to unity... /a1 a1 x1 div store /z1 z1 x1 div store /y1 y1 x1 div store /x1 x1 x1 div store % /x1 1.000 store % (\n\nnormalize x1 to zero) == rrr % force w2 to zero /a2 a2 w2 a0 mul sub store /z2 z2 w2 z0 mul sub store /y2 y2 w2 y0 mul sub store /x2 x2 w2 x0 mul sub store /w2 w2 w2 w0 mul sub store % /w2 0 store % (\n\nforce w2 to zero) == rrr % force x2 to zero... /a2 a2 a1 x2 mul sub store /z2 z2 z1 x2 mul sub store /y2 y2 y1 x2 mul sub store /x2 x2 x1 x2 mul sub store % /x2 0 store % (\n\nforce x2 to zero) == rrr % normalize y2 to unity... /a2 a2 y2 div store /z2 z2 y2 div store /y2 y2 y2 div store % /y2 1.000 store % (\n\nnormalize y2 to unity) == rrr % force w3 to zero (w3 - w0/w3) /a3 a3 a0 w3 mul sub store /z3 z3 z0 w3 mul sub store /y3 y3 y0 w3 mul sub store /x3 x3 x0 w3 mul sub store /w3 w3 w0 w3 mul sub store % /w3 0 store % (\n\n force w3 to zero) == rrr % force x3 to zero /a3 a3 a1 x3 mul sub store /z3 z3 z1 x3 mul sub store /y3 y3 y1 x3 mul sub store /x3 x3 x1 x3 mul sub store % /x3 0 store % (\n\nforce x3 to zero) == rrr % force y3 to zero /a3 a3 a2 y3 mul sub store /z3 z3 z2 y3 mul sub store /y3 y3 y2 y3 mul sub store % /y3 0 store % (\n\nforce y3 to zero) == rrr % normalize y3 to unity (solving for z) /z a3 z3 div store % solve for y by back substitution... /y a2 z2 z mul sub store % solve for x by back substitution... /x a1 z1 z mul sub y1 y mul sub store % solve for w by back substitution... /w a0 z0 z mul sub y0 y mul sub x0 x mul sub store % report the results false { (w = ) print w 20 string print (\n) print (x = ) print x 20 string cvs print (\n) print (y = ) print y 20 string cvs print (\n) print (z = ) print z 20 string cvs print (\n) print } if } store %%%%%%%%%%%%% end %%%%%%%%% bilineal interpolator for unit square /dobilin1 { /yfract exch store /xfract exch store 1 yfract sub 1 xfract sub mul w mul 1 yfract sub xfract mul x mul add yfract 1 xfract sub mul y mul add yfract xfract mul z mul add } bind store % /reportbmpasintegers lets you look at actual bytes in a .BMP file as 0-255 integers. % This can be handy for resolving code issues. bmpinfileprefix and bmpinfilename % must be predefined. A fancier version might pad one and two digit results. /reportbmpasintegers { /bmpinfile bmpinfileprefix % create .BMP read file bmpinfilename mergestr (r) file store bmpinfile 0 setfileposition % verify start at beginning 1000 {bmpinfile read {==} if % number of bytes as needed } repeat } store %%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%% START AIRBRUSH I APPLICATION %%%%%%%%%%%%%%%%% % this is an improved version that allows hsb or rgb blends and hue shifts /airbrushquad { grabhsbpoints % capture rectangular corners inputbitmap2AOS % read input bitmap calchsbcorners % find hsb corner values writenewhsb % write new values convertAOS2BMPimage % convert to output bitmap } store %%%%%%%%%% utility subprocs %%%%%%%% % /grabhsbpoints captures the corner data. MUST build clockwise from lower left. /grabhsbpoints { airbrushboundaries aload pop /lry exch store % grab quadralateral values /lrx exch store /ury exch store /urx exch store /uly exch store /ulx exch store /lly exch store /llx exch store /bblx llx ulx 2 copy gt {exch} if pop % find rect bounding box lrx urx 2 copy gt {exch} if pop 2 copy gt {exch} if pop store /bbux llx ulx 2 copy lt {exch} if pop lrx urx 2 copy lt {exch} if pop 2 copy lt {exch} if pop store /bbly lly uly 2 copy gt {exch} if pop lry ury 2 copy gt {exch} if pop 2 copy gt {exch} if pop store /bbuy lly uly 2 copy lt {exch} if pop lry ury 2 copy lt {exch} if pop 2 copy lt {exch} if pop store /xdel bbux bblx sub store % deltas for normalization /ydel bbuy bbly sub store false { (\n\nbounding box is ---> )print flush [bblx bbly bbux bbuy ] {==} forall } if } store % /calchsbcorners finds the corner hsb values. It does this by finding what % the bounding box values WOULD HAVE TO BE to produce the KNOWN quadralateral % corner values. It starts with this "forward" unity normalized bilineal transform... % newxyvalue = (1-xf)(1-yf)w + (1-xf)(yf)x + (xf)(yf)y + (xf)(1-yf)z % where xf and yf are the fractional distances to be interpolated inside the unit square. % Normally w, x, y, and z corner values are already known and the newxyvalue % is unknown. In our case, we have four KNOWN different newxyvalues which we can call % a0 = f(ll) a1 = f(ul) a2 = f(ur) and a3 = f(lr). And are seeking UNKNOWNS w thru z. % a0 = WO*w + X0*x + Y0*y + Z0*z % a1 = W1*w + X1*x + Y1*y + Z1*z % a2 = W2*w + X2*x + Y2*y + Z2*z % a3 = W3*w + X3*x + Y3*y + Z3*z % The W-Z constants are the fractional distances for each quadralateral point % relative to the bounding box. % These equations can be solved by Gaussian elimination, repeated for H, S, and B. /calchsbcorners { /a0r redAOS lly get llx get 256 div store % grab known red values /a1r redAOS uly get ulx get 256 div store /a2r redAOS ury get urx get 256 div store /a3r redAOS lry get lrx get 256 div store /a0g greenAOS lly get llx get 256 div store % grab known green values /a1g greenAOS uly get ulx get 256 div store /a2g greenAOS ury get urx get 256 div store /a3g greenAOS lry get lrx get 256 div store /a0b blueAOS lly get llx get 256 div store % grab known blue values /a1b blueAOS uly get ulx get 256 div store /a2b blueAOS ury get urx get 256 div store /a3b blueAOS lry get lrx get 256 div store usehsb { a0r a0g a0b setrgbcolor % convert RGB to HSB currenthsbcolor /ab0 exch store /as0 exch store /ah0 exch hue+ store a1r a1g a1b setrgbcolor currenthsbcolor /ab1 exch store /as1 exch store /ah1 exch hue+ store a2r a2g a2b setrgbcolor currenthsbcolor /ab2 exch store /as2 exch store /ah2 exch hue+ store a3r a3g a3b setrgbcolor currenthsbcolor /ab3 exch store /as3 exch store /ah3 exch hue+ store } { /ab0 a0b store % inefficient but only used once /ab1 a1b store /ab2 a2b store /ab3 a3b store /as0 a0g store /as1 a1g store /as2 a2g store /as3 a3g store /ah0 a0r store /ah1 a1r store /ah2 a2r store /ah3 a3r store } ifelse % begin Gaussian solutions /a0 ab0 store % set known b values /a1 ab1 store /a2 ab2 store /a3 ab3 store grabwzcoeffs % grab the w0-z3 values false { [a0 a1 a2 a3 w0 w1 w2 w3 x0 x1 x2 x3 y0 y1 y2 y3 z0 z1 z2 z3] {==} forall } if solven04 % solve for unknown bright false { (\n\n\non exiting solven04 bright --->) print flush [w x y z] {==} forall } if /bbbll w store /bbbul x store /bbbur y store /bbblr z store /a0 as0 store % set known s values /a1 as1 store /a2 as2 store /a3 as3 store grabwzcoeffs % grab the w0-z3 values solven04 % solve for unknown sat false { (\n\n\non exiting solven04 sat --->) print flush [w x y z] {==} forall } if /bbsll w store /bbsul x store /bbsur y store /bbslr z store /a0 ah0 store % set known h values /a1 ah1 store /a2 ah2 store /a3 ah3 store grabwzcoeffs % grab the w0-z3 values false { (\n\n\n\nsolving HUE ======================================================\n\n) print flush } if solven04 % solve for unknown hue false { (\n\n\non exiting solven04 hue --->) print flush [w x y z] {==} forall } if /bbhll w store /bbhul x store /bbhur y store /bbhlr z store false { [ (lower left hsb ----> ) bbhll bbsll bbbll (upper left hsb ----> ) bbhul bbsul bbbul (upper right hsb ---> ) bbhur bbbur bbbur (lower right hsb ---> ) bbhlr bbslr bbblr ] {==} forall } if } store % /grabwzcoeffs finds the constants for the Gaussian solution. Has to be % repeated for each HSB solution because of internal Gaussian operations. /grabwzcoeffs { /w0 { 1 llx bblx sub xdel div sub 1 lly bbly sub ydel div sub mul } store /x0 { 1 llx bblx sub xdel div sub lly bbly sub ydel div mul } store /y0 { llx bblx sub xdel div lly bbly sub ydel div mul } store /z0 { llx bblx sub xdel div 1 lly bbly sub ydel div sub mul } store /w1 { 1 ulx bblx sub xdel div sub 1 uly bbly sub ydel div sub mul } store /x1 { 1 ulx bblx sub xdel div sub uly bbly sub ydel div mul } store /y1 { ulx bblx sub xdel div uly bbly sub ydel div mul } store /z1 { ulx bblx sub xdel div 1 uly bbly sub ydel div sub mul } store /w2 { 1 urx bblx sub xdel div sub 1 ury bbly sub ydel div sub mul } store /x2 { 1 urx bblx sub xdel div sub ury bbly sub ydel div mul } store /y2 { urx bblx sub xdel div ury bbly sub ydel div mul } store /z2 { urx bblx sub xdel div 1 ury bbly sub ydel div sub mul } store /w3 { 1 lrx bblx sub xdel div sub 1 lry bbly sub ydel div sub mul } store /x3 { 1 lrx bblx sub xdel div sub lry bbly sub ydel div mul } store /y3 { lrx bblx sub xdel div lry bbly sub ydel div mul } store /z3 { lrx bblx sub xdel div 1 lry bbly sub ydel div sub mul } store false { (\nwx to zx coeffs---------------------------> : ) print flush (\n wx ----> ) [ w0 w1 w2 w3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n xx ----> ) [ x0 x1 x2 x3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n yy ----> ) [ y0 y1 y2 y3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n zz ----> ) [ z0 z1 z2 z3 ] {20 string cvs mergestr ( ) mergestr} forall print flush } if } store % /hbilin... are hsb specific bilineal interpolators..s /hblin { 1 xfract sub 1 yfract sub mul bbhll mul 1 xfract sub yfract mul bbhul mul add xfract yfract mul bbhur mul add xfract 1 yfract sub mul bbhlr mul add usehsb {hue- } if % only if hsb!!!! } store /sblin { 1 xfract sub 1 yfract sub mul bbsll mul 1 xfract sub yfract mul bbsul mul add xfract yfract mul bbsur mul add xfract 1 yfract sub mul bbslr mul add } store /bblin { 1 xfract sub 1 yfract sub mul bbbll mul 1 xfract sub yfract mul bbbul mul add xfract yfract mul bbbur mul add xfract 1 yfract sub mul bbblr mul add } store % /writenewhsb writes the new HSB values /writenewhsb { newpath % create PS path for infill llx lly moveto ulx uly lineto urx ury lineto lrx lry lineto closepath bbly 1 bbuy {/cury exch store bblx 1 bbux {/curx exch store curx cury infill { /xfract curx bblx sub xdel div store /yfract cury bbly sub ydel div store hblin % interpolate hsb sblin bblin false {3 copy == == == (\n\n) print flush} if usehsb { % shift color space? sethsbcolor currentrgbcolor } if 255 mul round cvi texture blueAOS cury get exch curx exch put % and place RGB 255 mul round cvi texture greenAOS cury get exch curx exch put 255 mul round cvi texture redAOS cury get exch curx exch put } if % only if inside infill } for % all row values } for % for all rows } store % /texture adds selected randomness to eliminate a "pure" blend. % The assumption is made that there is not much difference between RGB and HSB variance. /textureflag false store /textdepth 10 store /texture { textureflag { textdepth random dup 2 div sub add cvi } if dup 255 gt {pop 255} if % range check dup 0 lt {pop 0 } if } store % /reportall is a complete diagnostic useful for degubbing. /reportall {(\n\n\nAll values are...\n) print flush (\nairbrushboundaries ----> ) airbrushboundaries {20 string cvs mergestr ( ) mergestr} forall print flush (\nbounding box ----------> ) [ bblx bbly bblx bbuy bbux bbuy bbux bbly] {20 string cvs mergestr ( ) mergestr} forall print flush (\nxdelta and ydelta -----> ) [xdel ydel] {20 string cvs mergestr ( ) mergestr} forall print flush grabwzcoeffs % have to repeat because w and z changes with gaussian (\nwx to zx coeffs : ) print flush (\n wx ----> ) [ w0 w1 w2 w3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n xx ----> ) [ x0 x1 x2 x3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n yy ----> ) [ y0 y1 y2 y3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n zz ----> ) [ z0 z1 z2 z3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\nw to z coeff sums check ----> ) [ w0 x0 add y0 add z0 add w1 x1 add y1 add z1 add w2 x2 add y2 add z2 add w3 x3 add y3 add z3 add ] {20 string cvs mergestr ( ) mergestr} forall print flush (\nquad corner hsb's: ) print flush (\n lower left ----> ) [ ah0 as0 ab0 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n upper left ----> ) [ ah1 as1 ab1 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n upper right ---> ) [ ah2 as2 ab2 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n lower right ---> ) [ ah3 as3 ab3 ] {20 string cvs mergestr ( ) mergestr} forall print flush (\nbb corner hsb's: ) print flush (\n lower left ----> ) [ bbhll bbsll bbbll ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n upper left ----> ) [ bbhul bbsul bbbul ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n upper right ---> ) [ bbhur bbsur bbbur ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n lower right ---> ) [ bbhlr bbslr bbblr ] {20 string cvs mergestr ( ) mergestr} forall print flush (\n\n\n) print flush } store %%%%%%%%%%% add on routines for blend options %%%%%%%%%%%%%%%% % Certain blends may go the "wrong way" around the color circle. The hue % near true gray may become ambiguous and create color fringing. These % addon routines attempt to correct these second order problems. % A hsb blend moves the colors around the hue circle. % It is best suited for brighter colors. % A rgb blend moves the colors more through gray. % It is best suited when blending to or near gray. % A hue shift can sometimes prevent a hsb blend from going % "the wrong way" around the color circle. It should be % rarely needed and reserved for specific problems. Extreme % multiple blends can still create issues if a 1/0 hue transition remains. % Default hueshift is 0 and blends through aqua. problem color is red % A hue shift of -0.16 blends through blue problem color is orange % A hue shift of +0.16 blends through green problem color is purple % A hue shift of +0.5 blends through red. problem color is aqua % code is not yet speed optimized. % default controls for mode /usehsb true store % true for hsb blend false for rgb blend /hueshift 0 store % hue shift to prevent "wrong way" blend % /hue+ and hue- shifts the phase of the hue so the 0/1 discontinuity % takes place on a color other than red. /hue+ { hueshift add % add positive phase for interpolation dup 1 gt {1 sub} if dup 0 lt {1 add} if } store /hue- { hueshift sub % remove positive phase for restoration dup 1 gt {1 sub} if dup 0 lt {1 add} if } store %%%%%%%%%%%%%% demos -- remove or alter before reuse %%%%%%%%%%%% % The source files will have to be relocated to your host for your demo to work properly. % Distilling MUST be PRINT QUALITY for best results! % /airbrushboundaries go clockwise from lower left [ xll yll xul yul xur yur xlr ylr ] % Exact sequence is critical! % Note that Paint verticals need inverted. Bitmaps build from bottom up. ll = 0.0 % original demos % Our first airbrush demo eliminates a bad highlight burn /bmpinfilename (glareabd.bmp) store /bmpinfileprefix (C:\\Documents and Settings\\don\\Desktop\\aarawpix\\) store /bmpoutfilename (glareabx.bmp) store /bmpoutfileprefix {bmpinfileprefix} store % if same /pmax 234 store % paint vertical invert for this image /airbrushboundaries [127 pmax 203 sub 139 pmax 102 sub 214 pmax 80 sub 240 pmax 167 sub ] store /textureflag true store /textdepth 8 store stopwatchon airbrushquad stopwatchoff reportall % Our second airbrush demo on the same file eliminates some scanner dirt /bmpinfilename (glareabx.bmp) store /airbrushboundaries [12 pmax 84 sub 9 pmax 9 sub 91 pmax 5 sub 96 pmax 53 sub ] store stopwatchon airbrushquad stopwatchoff reportall % EOF