GENERIC INTERFACE Texture2D (Pixel); (* WHERE /\ IN Pixel /\ TYPE T /\ CONST N: CARDINAL (* Number of coordinates *) /\ TYPE RealT = ARRAY [0..N-1] OF REAL /\ TYPE LongRealT = ARRAY [0..N-1] OF LONGREAL *) IMPORT Random, Wr; TYPE PixelMatrix = ARRAY OF ARRAY OF Pixel.T; RealPixelMatrix = ARRAY OF ARRAY OF Pixel.RealT; TYPE T = RECORD NX: CARDINAL; (* Number of columns *) NY: CARDINAL; (* Number of rows *) dxdy: REAL; (* Displacement between cell rows is NY*dxdy *) absError: Pixel.LongRealT; (* Absolute error per component *) relError: Pixel.LongRealT; (* Relative error per component *) st: REF PixelMatrix; (* Texels *) END; (* A "Texture.T" is a periodic texture with period vectors "(NX,0)" and "(dxdy,NY)". The "st" matrix contains the texture samples centerd in the integer cells of its canonical period rectangle "[0 _ NX)" x "[0 _ NY)". Element "[y,x]" of the matrix contains the sample for the grid cell whose lower left corner is "(x,y)". Intuitively, the Y axis points up, and the X axis points left, as in mathematics. Note that when the matrix is written out as na image file, texel "st[0,0]" is the *bottom* left corner, and "st[0,NX-1]" is the last pixel in the file. The "st" matrix actually has "NY+2" rows and "NX+2" columns; the extra rows and columns are used internally by some routines, as scratch areas. *) PROCEDURE NewTexture( NX, NY: CARDINAL; DYDX: REAL; READONLY absError, relError: Pixel.LongRealT; ): T; (* Allocates a new texture of width NX and height NY *) PROCEDURE PrintParms(wr: Wr.T; READONLY t: T); (* Prints to "wr" the parameters of "t". *) PROCEDURE NRandom(VAR t: T; rnd: Random.T; avg, dev: Pixel.LongRealT); (* Fills "t" with random pixels. The "k"th component will have a Gaussian distribution with mean "avg[k]" and standard deviation "dev[k]". *) PROCEDURE URandom(VAR t: T; rnd: Random.T; min, max: Pixel.LongRealT); (* Fills "t" with random pixels. The "k"th component will have uniform distribution in the range "[min[k] __ max[k])". *) PROCEDURE GetTexel(READONLY t: T; x, y: CARDINAL; VAR s: Pixel.T); PROCEDURE SetTexel(VAR t: T; x, y: CARDINAL; READONLY s: Pixel.T); (* Extracts/Sets the texel with coordinates "(x,y)". Note that it is element "[y,x]" in the array. We must have "x IN [0..t.NX-1]" and "y IN [0..t.NY-1]". *) PROCEDURE Reduce(READONLY t: T; VAR x, y: REAL); (* Reduced the point "(x,y)" modulo the period vectors of "t" so that it ends up in the canonical cell "[0 __ NX) x [0 __ NY)". *) PROCEDURE FillTexture(VAR t: T; READONLY s: Pixel.T); (* Fills "t" with "s", including the safety borders. *) PROCEDURE ZeroTexture(VAR t: T); (* Fills "t" with zero pixels, including the safety borders. *) PROCEDURE ModifyTexture(VAR t: T; alpha: LONGREAL; READONLY f: T): BOOLEAN; (* Modifies the texture "t" by adding to it the the texture "f" scaled by "alpha". Returns TRUE if the texture changed. *) (******************************** SCALING *******************************) PROCEDURE TextureInfNorm(READONLY t: T): LONGREAL; (* Returns the infinity norm of the texture, that is, the maximum absolute value of all texels in all layers. *) PROCEDURE TextureMeanAndVariance(READONLY t: T; VAR a, v: Pixel.LongRealT); (* Computes the mean "a" and variance "v" of all texels in "t", separately for each component. *) PROCEDURE ScaleTexture(VAR t: T; shift, scale: Pixel.LongRealT); (* Shifts and rescales the texels in each layer of texture "t" by the formula "t[y,x][k] := (t[y,x][k] - shift[k])*scale[k]". *) (******************************** I/O *******************************) PROCEDURE ReadTexture( name: TEXT; dxdy: REAL; READONLY a, s: Pixel.LongRealT; ): T; (* Reads an image file and returns the contents as a periodic texture with specified "dxdy". The file name is "name" with the extension and format defined by the "Pixel" module. Each component "[k]" of the image pixels will be shifted and rescaled from its maximum range in the file to the real range "[a[k]-s[k] .. a[k]+s[k]]" *) PROCEDURE WriteTexture( name: TEXT; READONLY t: T; READONLY a, s: Pixel.LongRealT; ); (* Writes a texture to an image file. The "Pixel" module defines the file format and appends an appropriate extension to the file "name" given. The "dxdy" field of "t" is ignored. The pixel coordinates are mapped linearly to pixel values, from the real range "[a[k]-s[k] .. a[k]+s[k]]" to the maximum pixel range. Values outside these ranges are clipped. *) (*************************** NEIGHBORHOODS ************************) PROCEDURE DuplicateBorder(READONLY t: T); (* Fills the two extra rows and two extra columns of "t" with appropriately shifted copies of the first two, so that we can access the neighborhood of any texel without MODs. *) PROCEDURE FoldBorder(READONLY t: T); (* Adds back the 2 extra border rows and columns into the first two, appropriately shifted. *) TYPE PixelNeighborhood = ARRAY [-1..+1] OF ARRAY [-1..+1] OF Pixel.T; (* A 3x3 neighborhood of a texel. Indices are "[y,x]", as in the whole texture. *) PROCEDURE GetNeighborhood(READONLY t: T; x, y: CARDINAL; VAR n: PixelNeighborhood); (* Extracts the neighborhood of texel "(x,y)". Be sure to call "DuplicateBorder(t)" before calling this procedure! *) TYPE NeighborhoodVisitProc = PROCEDURE(x, y: CARDINAL; READONLY n: PixelNeighborhood); (* A procedure that processes a texel neighborhood. *) PROCEDURE EnumerateNeighborhoods( READONLY t: T; visit: NeighborhoodVisitProc; periodic: BOOLEAN := TRUE; ); (* Applies "visit(x, y, n)" to the neighborhood "n" of every pixel "[y,x]" in "t". If "periodic = FALSE", visits only neighbohoods that are entirely contained in the canonical period; that is, the indices are restricted to "[1..NX-2]" and "[1..NY-2]". This is useful when "t" is not really an infinite pattern, but just n ordinary bounded image. If "periodic = TRUE", the indices range over "[1..NX]" and "[1..NY]", respectively. See the "FoldBorder" procedure above... *) TYPE NeighborhoodMapProc = PROCEDURE( x, y: CARDINAL; READONLY tn: PixelNeighborhood; VAR (*OUT*) fn: PixelNeighborhood; ); (* A procedure that maps a pixel neighborhood "tn" into a pixel neighborhood "fn". *) PROCEDURE MapNeighborhoods( READONLY t: T; (* The input texture *) VAR f: T; (* The output texture *) map: NeighborhoodMapProc; (* The mapping procedure *) periodic: BOOLEAN := TRUE; ); (* Given two textures "t" and "f" with same shape and size, ppplies "map(x, y, tn, fn)" to the neighborhood "tn" of every pixel "[y,x]" of "t", and splats the resulting neighborhood "fn" into the "f" texture. The "periodic" flag has the same meaning as in "EnumerateNeighborhoods". The "f" texture is initialized with zeros and properly folded at the end. *) (************************* SHRINKING AND EXPANDING *********************) PROCEDURE ShrinkTexture( READONLY t: T; (* Texture to shrink *) VAR noiseVar: Pixel.LongRealT; (* Out: variance of lost detail *) VAR r: T; (* Out: shrunk texture *) ); (* Reduces the texture "t" by a factor of 2 in each direction and stores the result in "r" (which must have the correct size). The shrinking is done by averaging each 2x2 subblock. *) PROCEDURE ExpandTexture( READONLY r: T; (* Texture to expand *) READONLY noiseVar: Pixel.LongRealT; (* variance of detail to add *) rnd: Random.T; (* Random number source *) VAR t: T; (* Out: expanded texture *) ); (* Expands the texture "r" by a factor of 2 in each direction, and stores the result in "t" (which must have the correct size). Each texel of "r" is replicated into a 2x2 block, and noise with mean zero and variance "noiseVar" is added to every pixel. *) END Texture2D.