############################################################################ # # File: putpixel.icn # # Subject: Procedure to write quantized, processed pixel # # Author: Gregg M. Townsend # # Date: August 14, 1996 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # These procedures assist pixel-by-pixel image construction. # # PutPixel(W, x, y, k) draws a single pixel after applying # dithering, color quantization, and # gamma correction. # # PixInit(gamma, cquant, gquant, drandom) # initializes parameters for PutPixel(). # ############################################################################ # # PutPixel([win,] x, y, colr) sets the pixel at (x,y) to the given color # after applying dithering, color quantization, and gamma correction. # It is designed for constructing images a pixel at a time. The window's # foreground color is left set to the adjusted color. # # Colr can be any value acceptable to Fg. Mutable colors are not # dithered, quantized, or gamma-corrected. # # PixInit(gamma, cquant, gquant, drandom) may be called before PutPixel # to establish non-default parameters. The default gamma value is 1.0 # (that is, no correction beyond Icon's usual gamma correction). # cquant and gquant specify the number of color and grayscale quantization # steps; the defaults are 6 and 16 respectively. If gquant + cquant ^ 3 # exceeds 256 there is a potential for running out of colors. drandom # is the fraction (0 to 1) of the dithering to be done randomly; the # default is zero. # ############################################################################ # # Requires: Version 9 graphics # ############################################################################ global XPP_qtab, XPP_gtab, XPP_dtab, XPP_rtab, XPP_gadjust # PixInit -- set parameters and build tables procedure PixInit(gamma, cquant, gquant, drandom) #: initialize pixel processing local PIXRANGE, NRANDOM, cstep, gstep, indx, appx, gcor, i /gamma := 1.0 # gamma correction factor /cquant := 6 # color quantization steps /gquant := 16 # grayscale quantization /drandom := 0.0 # fraction of dithering to do randomly NRANDOM := 500 # size of random number table PIXRANGE := 255 # pixel value range 0..255 if gamma < 0.01 then # ensure legal values gamma := 2.5 cquant <:= 2 gquant <:= 2 drandom <:= 0.0 drandom >:= 1.0 cstep := (PIXRANGE / (cquant-1.0)) # color step size gstep := (PIXRANGE / (gquant-1.0)) # grayscale step size # build 4 x 4 dither table (choose one) # XPP_dtab := [0,8,2,10,12,4,14,6,3,11,1,9,15,7,13,5] # ordered dither XPP_dtab := [0,6,9,15,11,13,2,4,7,1,14,8,12,10,5,3] # magic square dither every i := 1 to 16 do # normalize XPP_dtab[i] := (XPP_dtab[i]/15.0 - 0.5) * (cstep - 3) * (1.0 - drandom) # build list of scaled random numbers for dithering XPP_rtab := list(NRANDOM) every !XPP_rtab := (?0 - 0.5) * 2 * (cstep - 3) * drandom # build table for combined quantization and gamma correction XPP_qtab := list(PIXRANGE+1) every i := 0 to PIXRANGE do { indx := integer((i + cstep / 2) / cstep) appx := cstep * indx gcor := PIXRANGE * ((real(appx) / real(PIXRANGE)) ^ (1.0 / gamma)) XPP_qtab[i+1] := integer(gcor + 0.5) } # build similar table for grayscale XPP_gtab := list(PIXRANGE+1) every i := 0 to PIXRANGE do { indx := integer((i + gstep / 2) / gstep) appx := gstep * indx gcor := PIXRANGE * ((real(appx) / real(PIXRANGE)) ^ (1.0 / gamma)) XPP_gtab[i+1] := integer(gcor + 0.5) } # grayscale adjustment for different quantization XPP_gadjust := (gstep - 3) / (cstep - 3) return end # PutPixel -- write a pixel procedure PutPixel(win, x, y, color) #: write pixel local i, r, g, b initial if /XPP_qtab then PixInit() # default win to &window if omitted if type(win) ~== "window" then { win :=: x :=: y :=: color win := &window } # convert color to 8-bit r, g, b if type(color) == "integer" then { # mutable -- don't quantize Fg(win, color) DrawPoint(win, x, y) return } (color | ColorValue(color) | fail) ? ( (r := tab(many(&digits))) & move(1) & (g := tab(many(&digits))) & move(1) & (b := tab(many(&digits))) ) # convert three 0..65535 ints to 0..255 r := (r + 255) / 257 g := (g + 255) / 257 b := (b + 255) / 257 # get dither table index based on coordinates i := iand(x, 3) + 4 * iand(y, 3) + 1 if r = g = b then { g := integer(g + XPP_gadjust * (XPP_dtab[i] + ?XPP_rtab)) (g <:= 1) | (g >:= 256) r := g := b := 257 * XPP_gtab[g] } else { r := integer(r + XPP_dtab[i] + ?XPP_rtab + 1.5) g := integer(g - XPP_dtab[i] + ?XPP_rtab + 1.5) b := integer(b + XPP_dtab[i] + ?XPP_rtab + 1.5) (r <:= 1) | (r >:= 256) (g <:= 1) | (g >:= 256) (b <:= 1) | (b >:= 256) r := 257 * XPP_qtab[r] g := 257 * XPP_qtab[g] b := 257 * XPP_qtab[b] } # finally, put the pixel on the screen Fg(win, r || "," || g || "," || b) DrawPoint(win, x, y) return end