DIRECTORY Basics, CtBasic, CtMap, CtMod, G2dContour, G2dBasic, G2dMatrix, G2dScan, G2dVector, ImagerColor, ImagerPixel, ImagerSample, ImagerTransformation, IO, MessageWindow, Process, Real, Rope, SF; CtModImpl: CEDAR PROGRAM IMPORTS Basics, CtBasic, CtMap, G2dContour, G2dMatrix, G2dScan, G2dVector, ImagerPixel, ImagerSample, ImagerTransformation, Process, Real, SF EXPORTS CtMod ~ BEGIN IntegerPair: TYPE ~ CtBasic.IntegerPair; PixelArray: TYPE ~ CtBasic.PixelArray; SampleMaps: TYPE ~ CtBasic.SampleMaps; Cmap: TYPE ~ CtMap.Cmap; Table: TYPE ~ CtMap.Table; CoonsControl: TYPE ~ CtMod.CoonsControl; TransformProc: TYPE ~ CtMod.TransformProc; Pair: TYPE ~ G2dBasic.Pair; PairSequence: TYPE ~ G2dBasic.PairSequence; Matrix: TYPE ~ G2dMatrix.Matrix; PixelMap: TYPE ~ ImagerPixel.PixelMap; Sample: TYPE ~ ImagerSample.Sample; SampleMap: TYPE ~ ImagerSample.SampleMap; SampleBuffer: TYPE ~ ImagerSample.SampleBuffer; Box: TYPE ~ ImagerSample.Box; Vec: TYPE ~ ImagerSample.Vec; Transformation: TYPE ~ ImagerTransformation.Transformation; ROPE: TYPE ~ Rope.ROPE; maxBox: Box ~ SF.maxBox; Error: PUBLIC SIGNAL [reason: ROPE] = CODE; TransformMap: PUBLIC PROC [map: SampleMap, transformation: Transformation] ~ { Action: ImagerPixel.ResampleAction ~ {ImagerSample.PutSamples[map, min,, pixels[0]]}; box: Box ฌ ImagerSample.GetBox[map]; pm: ImagerPixel.PixelMap ฌ ImagerPixel.MakePixelMap[ImagerSample.Copy[map,, box]]; boxes: SF.BoxGenerator ~ {boxAction[box]}; ImagerPixel.Resample[pm, transformation, TRUE, boxes, box, Action]; }; TransformMaps: PUBLIC PROC [maps: SampleMaps, transformation: Transformation] ~ { FOR i: NAT IN [0..maps.nChannels) DO TransformMap[maps[i].map, transformation]; ENDLOOP; }; ArbTransformMap: PUBLIC PROC [map: SampleMap, proc: TransformProc] ~ { Value: CtBasic.ValueProc ~ { pair: IntegerPair ฌ proc[[x, y]]; pair ฌ [MIN[xMax, MAX[pa.x, pair.x]], MIN[yMax, MAX[pa.y, pair.y]]]; value ฌ pa[pair.y][pair.x]; }; pa: PixelArray ฌ CtBasic.PixelArrayFromSampleMap[map]; xMax: INTEGER ฌ pa.x+pa.w-1; yMax: INTEGER ฌ pa.y+pa.h-1; CtBasic.PutBWFrame[map, Value]; }; ArbTransformMaps: PUBLIC PROC [maps: SampleMaps, proc: TransformProc] ~ { FOR i: NAT IN [0..maps.nChannels) DO ArbTransformMap[maps[i].map, proc]; ENDLOOP; }; Palette: PUBLIC PROC [maps: SampleMaps, nRows: NAT ฌ 5, smooth: BOOL ฌ FALSE] RETURNS [affectedRegion: Box] ~ { FillBox: PROC [x, y, w, h, val: INTEGER] ~ { affectedRegion ฌ [ [MIN[affectedRegion.min.s, y], MIN[affectedRegion.min.f, x]], [MAX[affectedRegion.max.s, y+h], MAX[affectedRegion.max.f, x+w]]]; CtBasic.PutBWBox[maps[0].map, x, y, x+w, y+h, val]; }; hSpace: NAT ~ 2; -- horizontal vSpace: NAT ~ 2; -- vertical i: NAT ฌ 0; vSize: NAT ฌ 7; vTot: NAT ฌ vSize+vSpace; nRows2: NAT ฌ MAX[1, MIN[60/vTot, nRows]]; nCols: NAT ฌ IF 255 MOD nRows2 = 0 THEN 255/nRows2 ELSE 255/nRows2+1; hSize: NAT ฌ MIN[14, (maps.size.f-2*20)/nCols-hSpace]; hTot: NAT ฌ hSize+hSpace; x: NAT ฌ (maps.size.f-nCols*hTot)/2; bot: NAT ฌ maps.y+2*maps.h/3; affectedRegion ฌ [[10000, 10000], [0, 0]]; IF smooth THEN FOR xx: NAT IN [x..x+hTot*nCols) DO val: CARDINAL ฌ Real.Round[255.0*REAL[xx-x]/REAL[hTot*nCols]]; FillBox[xx, bot, 1, vTot*nRows2, val]; ENDLOOP ELSE FOR row: NAT IN [0..nRows2) DO FOR col: NAT IN [0..nCols) DO xx: NAT ฌ x+col*hTot; yy: NAT ฌ bot+row*vTot; FillBox[xx, yy, hSize+1, vSize, i]; IF (i ฌ i+1) = 256 THEN EXIT; ENDLOOP; ENDLOOP; }; Indirect: PUBLIC PROC [map: SampleMap, table: Table, box: Box] ~ { Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED { FOR y: INT IN [box.min.s..box.max.s) DO Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, samples, 0, width]; FOR x: NAT IN [0..width) DO samples[x] ฌ table[samples[x]]; ENDLOOP; ImagerSample.PutSamples[map, [y, box.min.f],, samples, 0, width]; ENDLOOP; }; width: INTEGER ฌ SF.SizeF[box]; IF ImagerSample.GetBitsPerSample[map] = 8 THEN ImagerSample.DoWithScratchSamples[width, Action]; }; Lum24: PUBLIC PROC [maps: SampleMaps, cmap: Cmap, coeff: ImagerColor.RGB ฌ [.30, .59, .11]] ~ { IF maps.bpp = 24 THEN { MakeTable: PROC [cmap: Cmap, index: [0..3), coeff: REAL] RETURNS [tab: Table] ~ { tab ฌ NEW[CtMap.TableRep]; FOR v: NAT IN [0..256) DO tab[v] ฌ Real.Floor[cmap[index][v]*coeff]; ENDLOOP; }; bufs: ARRAY [0..4) OF ImagerSample.SampleBuffer; yMax: NAT ฌ maps.box.max.s-1; sc: INT ฌ 64; rtab: Table ฌ MakeTable[cmap, 0, coeff.R*sc]; gtab: Table ฌ MakeTable[cmap, 1, coeff.G*sc]; btab: Table ฌ MakeTable[cmap, 2, coeff.B*sc]; FOR i: INT IN [0..4) DO bufs[i] ฌ ImagerSample.ObtainScratchSamples[maps.size.f]; ENDLOOP; FOR y: INT IN [maps.box.min.s..maps.box.max.s) DO Process.CheckForAbort[]; FOR i: INT IN [0..3) DO ImagerSample.GetSamples[maps[0].map, [y, maps.box.min.f],, bufs[i], 0, maps.size.f]; ENDLOOP; FOR x: INT IN [0..maps.size.f) DO bufs[3][x] ฌ (rtab[bufs[0][x]]+gtab[bufs[1][x]]+btab[bufs[2][x]]+sc/2)/sc; -- normalize ENDLOOP; FOR i: INT IN [0..3) DO ImagerSample.PutSamples[maps[i].map, [y, maps.box.min.f],, bufs[3], 0, maps.size.f]; ENDLOOP; ENDLOOP; FOR i: INT IN [0..4) DO ImagerSample.ReleaseScratchSamples[bufs[i]]; ENDLOOP; }; }; Dif: PUBLIC PROC [map: SampleMap, x0, y0, x1, y1, x2, y2, w, h: NAT] ~ { src0: SampleMap ~ ImagerSample.Clip[map, [[y0, x0], [y0+h, x0+w]]]; src1: SampleMap ~ ImagerSample.Clip[map, [[y1, x1], [y1+h, x1+w]]]; dif: SampleMap ~ ImagerSample.Clip[map, [[y2, x2], [y2+h, x2+w]]]; box0: Box ฌ ImagerSample.GetBox[src0]; box1: Box ฌ ImagerSample.GetBox[src1]; boxd: Box ฌ ImagerSample.GetBox[dif]; FOR y: INT IN [0..h) DO FOR x: INT IN [0..w) DO src0Value: CARDINAL ~ CtBasic.GetBWPixel[src0, x+box0.min.f, y+box0.min.s]; src1Value: CARDINAL ~ CtBasic.GetBWPixel[src1, x+box1.min.f, y+box1.min.s]; CtBasic.PutBWPixel[dif, x+boxd.min.f, y+boxd.min.s, ABS[src0Value-src1Value]]; ENDLOOP; ENDLOOP; }; Up: PUBLIC PROC [map: SampleMap, shift: INTEGER] ~ { size: ImagerSample.Vec ฌ ImagerSample.GetSize[map]; tmpSize: INTEGER ฌ ABS[shift MOD size.s]; moveSize: INTEGER ฌ size.s-tmpSize; IF shift > 0 THEN { tmp: SampleMap ฌ ImagerSample.Copy[map, [0, 0], [[0, 0], [tmpSize, size.f]]]; ImagerSample.Move[map, [0, 0], [tmpSize, 0], [moveSize, size.f]]; ImagerSample.BasicTransfer[map, tmp, [moveSize, 0], [0, 0], [tmpSize, size.f]]; } ELSE { tmp: SampleMap ฌ ImagerSample.Copy[map, [-moveSize, 0], [[0, 0], [tmpSize, size.f]]]; ImagerSample.Move[map, [tmpSize, 0], [0, 0], [moveSize, size.f]]; ImagerSample.BasicTransfer[map, tmp, [0, 0], [0, 0], [tmpSize, size.f]]; }; }; Left: PUBLIC PROC [map: SampleMap, shift: INTEGER] ~ { size: ImagerSample.Vec ฌ ImagerSample.GetSize[map]; tmpSize: INTEGER ฌ ABS[shift MOD size.f]; moveSize: INTEGER ฌ size.f-tmpSize; IF shift > 0 THEN { tmp: SampleMap ฌ ImagerSample.Copy[map, [0, 0], [[0, 0], [size.s, tmpSize]]]; ImagerSample.Move[map, [0, 0], [0, tmpSize], [size.s, moveSize]]; ImagerSample.BasicTransfer[map, tmp, [0, moveSize], [0, 0], [size.s, tmpSize]]; } ELSE { tmp: SampleMap ฌ ImagerSample.Copy[map, [0, -moveSize], [[0, 0], [size.s, tmpSize]]]; ImagerSample.Move[map, [0, tmpSize], [0, 0], [size.s, moveSize]]; ImagerSample.BasicTransfer[map, tmp, [0, 0], [0, 0], [size.s, tmpSize]]; }; }; Negate: PUBLIC PROC [map: SampleMap] ~ { ImagerSample.Transfer[map, map, , [null, complement]]; }; ReflectH: PUBLIC PROC [map: SampleMap] ~ { box: Box ฌ ImagerSample.GetBox[map]; size: Vec ฌ ImagerSample.GetSize[map]; line1: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f]; line2: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchSamples[size.f]; FOR dy: NAT IN [0..size.s/2) DO y: NAT ฌ box.min.s+dy; yy: NAT ฌ box.max.s-1-dy; Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, line1, 0, size.f]; ImagerSample.GetSamples[map, [yy, box.min.f],, line2, 0, size.f]; ImagerSample.PutSamples[map, [y, box.min.f],, line2, 0, size.f]; ImagerSample.PutSamples[map, [yy, box.min.f],, line1, 0, size.f]; ENDLOOP; ImagerSample.ReleaseScratchSamples[line1]; ImagerSample.ReleaseScratchSamples[line2]; }; ReflectV: PUBLIC PROC [map: SampleMap] ~ { Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED { xRight: NAT ฌ size.f-1; FOR y: INT IN [box.min.s..box.max.s) DO Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, samples, 0, size.f]; FOR x: NAT IN [0..size.f/2) DO t: CARDINAL ฌ samples[x]; xx: CARDINAL ฌ xRight-x; samples[x] ฌ samples[xx]; samples[xx] ฌ t; ENDLOOP; ImagerSample.PutSamples[map, [y, box.min.f],, samples, 0, size.f]; ENDLOOP; }; box: Box ฌ ImagerSample.GetBox[map]; size: Vec ฌ ImagerSample.GetSize[map]; ImagerSample.DoWithScratchSamples[size.f, Action]; }; MirrorH: PUBLIC PROC [map: SampleMap, topToBottom: BOOL ฌ TRUE] ~ { Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED { y1, y2: NAT; FOR dy: NAT IN [0..size.s/2) DO Process.CheckForAbort[]; IF topToBottom THEN {y1 ฌ box.min.s+dy; y2 ฌ box.max.s-dy-1} ELSE {y1 ฌ box.max.s-dy-1; y2 ฌ box.min.s+dy}; ImagerSample.GetSamples[map, [y1, box.min.f], , samples, 0, size.f]; ImagerSample.PutSamples[map, [y2, box.min.f], , samples, 0, size.f]; ENDLOOP; }; box: Box ฌ ImagerSample.GetBox[map]; size: Vec ฌ ImagerSample.GetSize[map]; ImagerSample.DoWithScratchSamples[size.f, Action]; }; MirrorV: PUBLIC PROC [map: SampleMap, leftToRight: BOOL ฌ TRUE] ~ { Action: PROC [samples: ImagerSample.SampleBuffer] ~ TRUSTED { xRight: NAT ฌ size.f-1; FOR y: INT IN [box.min.s..box.max.s) DO Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, samples, 0, size.f]; IF leftToRight THEN FOR x: NAT IN [0..size.f/2) DO samples[xRight-x] ฌ samples[x]; ENDLOOP ELSE FOR x: NAT IN [0..size.f/2) DO samples[x] ฌ samples[xRight-x]; ENDLOOP; ImagerSample.PutSamples[map, [y, box.min.f],, samples, 0, size.f]; ENDLOOP; }; box: Box ฌ ImagerSample.GetBox[map]; size: Vec ฌ ImagerSample.GetSize[map]; ImagerSample.DoWithScratchSamples[size.f, Action]; }; Transpose: PUBLIC PROC [map: SampleMap] ~ { pa: PixelArray ฌ CtBasic.PixelArrayFromSampleMap[map]; FOR i: NAT IN [0..MIN[pa.w, pa.h]) DO x: NAT ฌ pa.x+i; Process.CheckForAbort[]; FOR y: NAT IN [pa.y..pa.y+i) DO temp: WORD ฌ pa[y][x]; pa[y][x] ฌ pa[x][y]; pa[x][y] ฌ temp; ENDLOOP; ENDLOOP; CtBasic.TransferPixelArrayToMap[pa, map]; }; Rotate: PUBLIC PROC [map: SampleMap, degrees: REAL] ~ { box: Box ฌ ImagerSample.GetBox[map]; w: INTEGER ฌ SF.SizeF[box]; h: INTEGER ฌ SF.SizeS[box]; TransformMap[map, ImagerTransformation.Cat[ ImagerTransformation.Translate[[-w/2, -h/2]], ImagerTransformation.Rotate[degrees], ImagerTransformation.Translate[[w/2, h/2]]]]; }; Add: PUBLIC PROC [dst, map: SampleMap, fbAlpha, fileAlpha: REAL] ~ { box: Box ฌ SF.Intersect[ImagerSample.GetBox[dst], ImagerSample.GetBox[map]]; width: NAT ฌ box.max.f-box.min.f; dstBuf: SampleBuffer ฌ ImagerSample.ObtainScratchSamples[width]; mapBuf: SampleBuffer ฌ ImagerSample.ObtainScratchSamples[width]; SELECT ImagerSample.GetBitsPerSample[map] FROM 8 => FOR y: NAT IN [box.min.s..box.max.s) DO Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width]; ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width]; FOR x: NAT IN [0..width) DO v: REAL ฌ REAL[mapBuf[x]]; pv: INTEGER ฌ Real.Round[(v*fbAlpha)+(fileAlpha*dstBuf[x])]; dstBuf[x] ฌ MIN[255, MAX[0, pv]]; ENDLOOP; ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width]; ENDLOOP; 16 => FOR y: NAT IN [box.min.s..box.max.s) DO Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width]; ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width]; FOR x: NAT IN [0..width) DO rgDst: CARDINAL ฌ dstBuf[x]; rgMap: CARDINAL ฌ mapBuf[x]; rDst: REAL ฌ REAL[rgDst/256]; rMap: REAL ฌ REAL[rgMap/256]; gDst: REAL ฌ REAL[rgDst MOD 256]; gMap: REAL ฌ REAL[rgMap MOD 256]; rv: INTEGER ฌ Real.Round[(rMap*fbAlpha)+(fileAlpha*rDst)]; gv: INTEGER ฌ Real.Round[(gMap*fbAlpha)+(fileAlpha*gDst)]; r: CARDINALฌ MIN[255, MAX[0, rv]]; g: CARDINAL ฌ MIN[255, MAX[0, gv]]; dstBuf[x] ฌ 256*r+g; ENDLOOP; ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width]; ENDLOOP; ENDCASE; ImagerSample.ReleaseScratchSamples[dstBuf]; ImagerSample.ReleaseScratchSamples[mapBuf]; }; Lerp: PUBLIC PROC [dst, map: SampleMap, alpha: REAL] ~ { box: Box ฌ SF.Intersect[ImagerSample.GetBox[dst], ImagerSample.GetBox[map]]; width: NAT ฌ box.max.f-box.min.f; dstBuf: SampleBuffer ฌ ImagerSample.ObtainScratchSamples[width]; mapBuf: SampleBuffer ฌ ImagerSample.ObtainScratchSamples[width]; SELECT ImagerSample.GetBitsPerSample[map] FROM 8 => FOR y: NAT IN [box.min.s..box.max.s) DO Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width]; ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width]; FOR x: NAT IN [0..width) DO v: REAL ฌ REAL[mapBuf[x]]; dstBuf[x] ฌ Real.Round[v+alpha*(REAL[dstBuf[x]]-v)]; ENDLOOP; ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width]; ENDLOOP; 16 => FOR y: NAT IN [box.min.s..box.max.s) DO Process.CheckForAbort[]; ImagerSample.GetSamples[map, [y, box.min.f],, mapBuf, 0, width]; ImagerSample.GetSamples[dst, [y, box.min.f],, dstBuf, 0, width]; FOR x: NAT IN [0..width) DO rgDst: CARDINAL ฌ dstBuf[x]; rgMap: CARDINAL ฌ mapBuf[x]; rDst: REAL ฌ REAL[rgDst/256]; rMap: REAL ฌ REAL[rgMap/256]; gDst: REAL ฌ REAL[rgDst MOD 256]; gMap: REAL ฌ REAL[rgMap MOD 256]; r: CARDINAL ฌ Real.Round[rMap+alpha*(rDst-rMap)]; g: CARDINAL ฌ Real.Round[gMap+alpha*(gDst-gMap)]; dstBuf[x] ฌ 256*r+g; ENDLOOP; ImagerSample.PutSamples[dst, [y, box.min.f],, dstBuf, 0, width]; ENDLOOP; ENDCASE; ImagerSample.ReleaseScratchSamples[dstBuf]; ImagerSample.ReleaseScratchSamples[mapBuf]; }; PVal: PUBLIC PROC [map: SampleMap, new: NAT, old: LIST OF NAT] ~ { table: Table ฌ NEW[CtMap.TableRep]; IF old = NIL THEN RETURN; FOR i: NAT IN [0..256) DO table[i] ฌ i; ENDLOOP; new ฌ MIN[255, MAX[0, INTEGER[new]]]; FOR o: LIST OF NAT ฌ old, o.rest WHILE o # NIL DO table[MIN[255, MAX[0, INTEGER[o.first]]]] ฌ new; ENDLOOP; Indirect[map, table, ImagerSample.GetBox[map]]; }; Gammatize: PUBLIC PROC [maps: SampleMaps, gamma: REAL, x, y, w, h: NAT] ~ { box: Box ฌ CtBasic.BoxFromXYWH[x, y, w, h]; table: Table ฌ CtMap.GetGammaTable[gamma]; FOR n: NAT IN [0..maps.nChannels) DO Indirect[maps[n].map, table, box]; ENDLOOP; }; PerspWarp: PUBLIC PROC [ texture: PixelArray ฌ NIL, dest: SampleMaps, smooth: BOOL ฌ FALSE, points: ARRAY [0..4) OF IntegerPair] RETURNS [transform: Matrix] ~ { PixelProc: G2dScan.PixelProc ~ { t: G2dBasic.Triple ฌ G2dMatrix.Transform[[x, y, 1.0], transform]; tx: REAL ฌ t.x/t.z; ty: REAL ฌ t.y/t.z; ix: NAT ฌ Real.Floor[tx]; iy: NAT ฌ Real.Floor[ty]; IF ix IN [texture.x..xmax] AND iy IN [texture.y..ymax] THEN CtBasic.PutBWPixel[dest[0].map, x, y, IF smooth THEN BiLerp[texture, tx, ty] ELSE texture[iy][ix]]; }; GetPair: PROC [ip: IntegerPair] RETURNS [Pair] ~ {RETURN[[REAL[ip.x], REAL[ip.y]]]}; xmax: INTEGER ฌ texture.x+texture.w-1; ymax: INTEGER ฌ texture.y+texture.h-1; transform ฌ G2dMatrix.QuadrilateralToRectangle[ GetPair[points[0]], GetPair[points[1]], GetPair[points[2]], GetPair[points[3]], [texture.x, texture.y, texture.w, texture.h]]; G2dScan.ScanTriangle[points[0], points[1], points[2], PixelProc]; G2dScan.ScanTriangle[points[0], points[2], points[3], PixelProc]; }; CoonsWarp: PUBLIC PROC [ pa1, pa2, pa3: PixelArray, -- source image (pa1 for bw, or pa1,2,3 for rgb) curves: ARRAY [0..4) OF PairSequence, -- outline curves dest: SampleMaps, -- destination window control: REF CoonsControl ฌ NIL] ~ { CoonsWarpInner: PROC ~ { rgb: BOOL ฌ pa2 # NIL AND pa3 # NIL; -- rgb or b&w? line1: SampleBuffer ฌ ImagerSample.ObtainScratchSamples[du+1]; -- bw or r line2: SampleBuffer ฌ ImagerSample.ObtainScratchSamples[du+1]; -- g or unused line3: SampleBuffer ฌ ImagerSample.ObtainScratchSamples[du+1]; -- b or unused qu0: PairSequence ฌ G2dContour.ResamplePairs[curves[0], du+1]; q1v: PairSequence ฌ G2dContour.ResamplePairs[curves[1], dv+1]; qu1: PairSequence ฌ G2dContour.ResamplePairs[curves[2], du+1]; q0v: PairSequence ฌ G2dContour.ResamplePairs[curves[3], dv+1]; pu0: Int32PairSequence ฌ NEW[Int32PairSequenceRep[du+1]]; pu1: Int32PairSequence ฌ NEW[Int32PairSequenceRep[du+1]]; p0v: Int32PairSequence ฌ NEW[Int32PairSequenceRep[dv+1]]; p1v: Int32PairSequence ฌ NEW[Int32PairSequenceRep[dv+1]]; buv: Int32PairSequence ฌ NEW[Int32PairSequenceRep[du+1]]; dbuv: Int32PairSequence ฌ NEW[Int32PairSequenceRep[du+1]]; c00, c10, c11, c01, c0v, c1v, dc0v, dc1v: Int32Pair; shift: NAT ~ 20; -- number of fractional bits scale: REAL ~ LOOPHOLE[Basics.BITLSHIFT[LOOPHOLE[INT32[1]], shift], INT32]; recipscale: REAL ~ 1./scale; [] ฌ G2dVector.ReverseSequence[qu1, qu1]; [] ฌ G2dVector.ReverseSequence[q0v, q0v]; FOR u: NAT IN [0..du] DO pu0[u] ฌ Int32ize[qu0[u], scale]; pu1[u] ฌ Int32ize[qu1[u], scale]; ENDLOOP; FOR v: NAT IN [0..dv] DO p0v[v] ฌ Int32ize[q0v[v], scale]; p1v[v] ฌ Int32ize[q1v[v], scale]; ENDLOOP; c00 ฌ Int32PairMidpoint[pu0[0], p0v[0]]; c10 ฌ Int32PairMidpoint[pu0[du], p1v[0]]; c11 ฌ Int32PairMidpoint[pu1[du], p1v[dv]]; c01 ฌ Int32PairMidpoint[pu1[0], p0v[dv]]; c0v ฌ c00; dc0v ฌ [(c01.x-c00.x)/dv, (c01.y-c00.y)/dv]; c1v ฌ c10; dc1v ฌ [(c11.x-c10.x)/dv, (c11.y-c10.y)/dv]; FOR u: NAT IN [0..du] DO buv[u] ฌ pu0[u]; dbuv[u] ฌ [(pu1[u].x-pu0[u].x)/dv, (pu1[u].y-pu0[u].y)/dv]; ENDLOOP; buv.length ฌ dbuv.length ฌ du+1; FOR v: NAT IN [0..dv] WHILE NOT control.abort DO TRUSTED { auv: Int32Pair ฌ [p0v[v].x-c0v.x, p0v[v].y-c0v.y]; dauv: Int32Pair ฌ [(p1v[v].x-c1v.x-auv.x)/du, (p1v[v].y-c1v.y-auv.y)/du]; buvp: LONG POINTER TO Int32Pair ฌ @buv[0]; dbuvp: LONG POINTER TO Int32Pair ฌ @dbuv[0]; lp1: LONG POINTER TO Sample ฌ @line1[0]; lp2: LONG POINTER TO Sample ฌ @line2[0]; lp3: LONG POINTER TO Sample ฌ @line3[0]; IF control.filter = point THEN -- point sample FOR u: NAT IN [0..du] DO x: INT32 ฌ LOOPHOLE[Basics.BITRSHIFT[LOOPHOLE[auv.x+buvp.x], shift]]; y: INT32 ฌ LOOPHOLE[Basics.BITRSHIFT[LOOPHOLE[auv.y+buvp.y], shift]]; IF x >= 0 AND x < dx AND y >= 0 AND y < dy THEN { lp1ญ ฌ pa1[y][x]; IF rgb THEN {lp2ญ ฌ pa2[y][x]; lp3ญ ฌ pa3[y][x]}; } ELSE { lp1ญ ฌ 0; IF rgb THEN {lp2ญ ฌ 0; lp3ญ ฌ 0}; }; auv ฌ [auv.x+dauv.x, auv.y+dauv.y]; buvpญ ฌ [buvp.x+dbuvp.x, buvp.y+dbuvp.y]; buvp ฌ buvp+SIZE[Int32Pair]; dbuvp ฌ dbuvp+SIZE[Int32Pair]; lp1 ฌ lp1+SIZE[Sample]; IF rgb THEN {lp2 ฌ lp2+SIZE[Sample]; lp3 ฌ lp3+SIZE[Sample]}; ENDLOOP ELSE -- bilinear interp FOR u: NAT IN [0..du] DO x: REAL ฌ (auv.x+buvp.x)*recipscale; y: REAL ฌ (auv.y+buvp.y)*recipscale; IF x >= 0 AND x < dx AND y >= 0 AND y < dy THEN { lp1ญ ฌ BiLerp[pa1, x, y]; IF rgb THEN {lp2ญ ฌ BiLerp[pa2, x, y]; lp3ญ ฌ BiLerp[pa3, x, y]}; } ELSE { lp1ญ ฌ 0; IF rgb THEN {lp2ญ ฌ 0; lp3ญ ฌ 0}; }; auv ฌ [auv.x+dauv.x, auv.y+dauv.y]; buvpญ ฌ [buvp.x+dbuvp.x, buvp.y+dbuvp.y]; buvp ฌ buvp+SIZE[Int32Pair]; dbuvp ฌ dbuvp+SIZE[Int32Pair]; lp1 ฌ lp1+SIZE[Sample]; IF rgb THEN {lp2 ฌ lp2+SIZE[Sample]; lp3 ฌ lp3+SIZE[Sample]}; ENDLOOP; ImagerSample.PutSamples[dest[0].map, [v, u0],, line1, 0, du+1]; IF rgb THEN { ImagerSample.PutSamples[dest[1].map, [v, u0],, line2, 0, du+1]; ImagerSample.PutSamples[dest[2].map, [v, u0],, line3, 0, du+1]; }; c0v ฌ [c0v.x+dc0v.x, c0v.y+dc0v.y]; c1v ฌ [c1v.x+dc1v.x, c1v.y+dc1v.y]; }; ENDLOOP; ImagerSample.ReleaseScratchSamples[line1]; ImagerSample.ReleaseScratchSamples[line2]; }; dx: NAT ฌ pa1.w; -- source window size dy: NAT ฌ pa1.h; du: NAT ฌ dest.size.f-1; -- dest window size dv: NAT ฌ dest.size.s-1; u0: NAT ฌ dest.box.min.f; -- noninclusive window: [u0..u1) x [v0..v1) v0: NAT ฌ dest.box.min.s; u1: NAT ฌ u0+du+1; v1: NAT ฌ v0+dv+1; IF control = NIL THEN control ฌ NEW[CoonsControl]; }; BiLerp: PUBLIC PROC [pa: PixelArray, x, y: REAL] RETURNS [CARDINAL] ~ { ix0: INT16 ฌ Real.Floor[x]; iy0: INT16 ฌ Real.Floor[y]; ix1: INT16 ฌ ix0+1; iy1: INT16 ฌ iy0+1; dx: REAL ฌ x-ix0; t00, t10: CARD; IF ix0 < pa.x THEN ix0 ฌ pa.x; IF iy0 < pa.y THEN iy0 ฌ pa.y; IF ix1 >= INT16[pa.x+pa.w] THEN ix1 ฌ pa.x+pa.w-1; IF iy1 >= INT16[pa.y+pa.h] THEN iy1 ฌ pa.y+pa.h-1; t00 ฌ pa[iy0][ix0]; t10 ฌ pa[iy1][ix0]; SELECT pa.bpp FROM 8 => RETURN[Real.Floor[BiLerpReal[pa, x, y]]]; 16 => { t01: CARD ฌ pa[iy0][ix1]; t11: CARD ฌ pa[iy1][ix1]; u00: INTEGER ฌ t00/256; -- high byte (red) u10: INTEGER ฌ t10/256; u0x: REAL ฌ u00+dx*(t01/256-u00); u1x: REAL ฌ u10+dx*(t11/256-u10); tyxr: CARD ฌ Real.Round[u0x+(y-iy0)*(u1x-u0x)]; tyxg: CARD; u00 ฌ t00 MOD 256; -- low byte (green) u10 ฌ t10 MOD 256; u0x ฌ u00+dx*(t01 MOD 256-u00); u1x ฌ u10+dx*(t11 MOD 256-u10); tyxg ฌ Real.Round[u0x+(y-iy0)*(u1x-u0x)]; RETURN[tyxr*256+tyxg]; }; ENDCASE => RETURN[0]; }; BiLerpReal: PUBLIC PROC [pa: PixelArray, x, y: REAL] RETURNS [REAL] ~ { ix0: INT16 ฌ Real.Floor[x]; iy0: INT16 ฌ Real.Floor[y]; ix1: INT16 ฌ ix0+1; iy1: INT16 ฌ iy0+1; dx: REAL ฌ x-ix0; t00, t10: CARD; IF ix0 < pa.x THEN ix0 ฌ pa.x; IF iy0 < pa.y THEN iy0 ฌ pa.y; IF ix1 >= INT16[pa.x+pa.w] THEN ix1 ฌ pa.x+pa.w-1; IF iy1 >= INT16[pa.y+pa.h] THEN iy1 ฌ pa.y+pa.h-1; t00 ฌ pa[iy0][ix0]; t10 ฌ pa[iy1][ix0]; SELECT pa.bpp FROM 8 => { t0x: REAL ฌ t00+dx*(REAL[pa[iy0][ix1]]-t00); t1x: REAL ฌ t10+dx*(REAL[pa[iy1][ix1]]-t10); RETURN[t0x+(y-iy0)*(t1x-t0x)]; }; ENDCASE => ERROR; }; Int16Pair: TYPE ~ G2dBasic.IntPair; Int16PairSequence: TYPE ~ G2dBasic.IntPairSequence; Int16PairMidpoint: PROC [v1, v2: Int16Pair] RETURNS [Int16Pair] ~ { RETURN [[Basics.BITSHIFT[v1.x+v2.x, -1], Basics.BITSHIFT[v1.y+v2.y, -1]]]; }; Int16ize: PROC [p: Pair, scale: REAL] RETURNS [Int16Pair] ~ { RETURN[[Real.Fix[scale*p.x], Real.Fix[scale*p.y]]]; }; Int32Pair: TYPE ~ RECORD [x, y: INT ฌ 0]; Int32PairSequence: TYPE ~ REF Int32PairSequenceRep; Int32PairSequenceRep: TYPE ~ RECORD [ length: CARDINAL ฌ 0, element: SEQUENCE maxLength: CARDINAL OF Int32Pair ]; Int32PairMidpoint: PROC [v1, v2: Int32Pair] RETURNS [Int32Pair] ~ { RETURN[[(v1.x+v2.x)/2, (v1.y+v2.y)/2]]; }; Int32ize: PROC [p: Pair, scale: REAL] RETURNS [Int32Pair] ~ { RETURN[[Real.Round[scale*p.x], Real.Round[scale*p.y]]]; }; END. .. /” CtModImpl.mesa Copyright ำ 1985, 1992 by Xerox Corporation. All rights reserved. Bloomenthal, April 24, 1993 10:59 am PDT Glassner, November 14, 1990 4:34 pm PST Types and Constants Raster Transformations Palette Procedures Color Map Indirection Multiply by sc for higher precision: IF y MOD 10 = 0 OR y = yMax THEN MessageWindow.Append[IO.PutFR["\t\tLine %g/%g", IO.int[y], IO.int[yMax]], TRUE]; Color Display Modification Procedures Rotate: PUBLIC PROC [map: SampleMap, ccw: BOOL] ~ { Rotate sample map by 90 degrees pa: PixelArray _ CtBasic.PixelArrayFromSampleMap[map]; s: NAT _ MIN[pa.w, pa.h]; d: NAT _ s-1; FOR i: NAT IN [0..s/2) DO -- vertical loop xi, xij: NAT _ pa.x+i; yi, yij: NAT _ pa.y+i; xdi, xdij: NAT _ pa.x+d-i; ydi, ydij: NAT _ pa.y+d-i; Process.CheckForAbort[]; FOR j: NAT IN [0..d-i-i) DO -- horizontal loop IF ccw THEN { temp: WORD _ pa[yi][xij]; pa[yi][xij] _ pa[yij][xdi]; -- upper left _ upper right pa[yij][xdi] _ pa[ydi][xdij]; -- upper right _ lower right pa[ydi][xdij] _ pa[ydij][xi]; -- lower right _ lower left pa[ydij][xi] _ temp; -- lower left _ upper left } ELSE { temp: WORD _ pa[ydij][xi]; pa[ydij][xi] _ pa[ydi][xdij]; -- lower right _ lower left pa[ydi][xdij] _ pa[yij][xdi]; -- upper right _ lower right pa[yij][xdi] _ pa[yi][xij]; -- upper left _ upper right pa[yi][xij] _ temp; }; xij _ xij+1; yij _ yij+1; xdij _ xdij-1; ydij _ ydij-1; ENDLOOP; ENDLOOP; CtBasic.TransferPixelArrayToMap[pa, map]; }; Warping By Paul Heckbert. See nonoptimized, more readable code at end of file. Highly optimized version (incremental and INT32) 640x480 timing tests: b&w without pointers: 22 sec pointsamp, 80 sec filtered b&w with pointers: 16 sec pointsamp, 77 sec filtered rgb with pointers: 32 sec pointsamp, 304 sec filtered Resample the outline curves with equal spacing at dest resolution Reverse the last two curves Quantize the real curves to integer curves Infer the four corner points (somewhat arbitrary since curves don't necessarily join) Setup for incremental inner loops Warp! Exclude cursor from dest window while drawing Terminal.ModifyColorFrame[Terminal.Current[], CoonsWarpInner, u0, v0, u1, v1]; Pixel Procedures Support Code Old Code BiLerp: PUBLIC PROC [pa: PixelArray, x, y: REAL] RETURNS [CARDINAL] ~ { val: REAL; ix: NAT _ Real.Floor[x]; iy: NAT _ Real.Floor[y]; IF ix = pa.x+pa.dx-1 THEN { IF iy = pa.y+pa.dy-1 THEN RETURN[pa[iy][ix]] ELSE { t1: INTEGER _ pa[iy][ix]; t2: INTEGER _ pa[iy+1][ix]; IF t1 = t2 THEN RETURN[t1] ELSE val _ t1+(y-iy)*(t2-t1); } } ELSE { IF iy = pa.y+pa.dy-1 THEN { t1: INTEGER _ pa[iy][ix]; t2: INTEGER _ pa[iy][ix+1]; IF t1 = t2 THEN RETURN[t1] ELSE val _ t1+(x-ix)*(t2-t1); } ELSE { dx: REAL _ x-ix; t1: INTEGER _ pa[iy][ix]; t2: INTEGER _ pa[iy+1][ix]; t3: INTEGER _ pa[iy][ix+1]; t4: INTEGER _ pa[iy+1][ix+1]; IF t1 = t2 AND t1 = t3 AND t1 = t4 THEN RETURN[t1] ELSE { val1: REAL _ t1+dx*(t3-t1); val2: REAL _ t2+dx*(t4-t2); val _ (val1)+(y-iy)*(val2-val1); }; }; }; RETURN[Real.Floor[val]]; }; Reflect: PUBLIC PROC [p: SampleMapCD] ~ { ImagerSampleMap.Transfer[p, ImagerSampleMap.ShiftMap[ImagerSampleMap.Reflect[p], p.sSize, 0]]; }; Lum: PUBLIC PROC [map: SampleMapCD] ~ { buf: ARRAY [0..256) OF NAT; cm: ColorMap.Cmap _ ColorMap.Read[]; By keeping buf outside of action, action runs ~30% faster because less stack mgt. necessary action: PROC [line: ImagerSample.SampleBuffer] ~ TRUSTED { sample: ImagerSample.UnsafeSamples ~ ImagerSample.GetPointer[line, 0, 0, map.fSize]; FOR y: INT IN [map.sMin..map.sMin+map.sSize) DO Process.CheckForAbort[]; SampleMapOps.GetF[map, y, map.fMin, line, 0, 0, map.fSize]; FOR x: NAT IN [0..map.fSize) DO sample[x] _ buf[sample[x]]; ENDLOOP; SampleMapOps.PutF[map, y, map.fMin, line, 0, 0, map.fSize, null, null]; ENDLOOP; }; FOR i: INT IN [0..256) DO buf[i] _ MIN[255, MAX[0, Real.RoundI[.30*cm[0][i] + .59*cm[1][i] + .11*cm[2][i]]]]; ENDLOOP; ColorMap.Write[ColorMap.Mono[]]; ImagerSample.DoWithScratchBuffer[1, map.fSize, action]; }; PerLine: PUBLIC PROC [perLineProc: ColorDisplayCase.PerLineProc] ~ { map: SampleMapCD; map _ ImagerOps.SampleMapFromFrameBuffer [Terminal.GetColorFrameBufferA[InterminalBackdoor.terminal]]; action: PROC [sampleBuffer: ImagerSample.SampleBuffer] ~ TRUSTED { bounds: ImagerSampleMap.DeviceRectangle ~ map.Window; sample: ImagerSample.UnsafeSamples ~ GetPointer[sampleBuffer, 0, 0, bounds.fSize]; maxVal: ImagerSample.Sample ~ CARDINAL[Basics.BITSHIFT[1, Basics.BITSHIFT[1, map.refRep.lgBitsPerPixel]]-1]; FOR s: INT IN [bounds.sMin..bounds.sMin+bounds.sSize) DO Process.CheckForAbort[]; SampleMapOps.GetF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize]; perLineProc[sample, bounds.fSize]; SampleMapOps.PutF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize, null, null]; ENDLOOP; }; ImagerSample.DoWithScratchBuffer[1, map.fSize, action]; }; PerPixel: PUBLIC PROC [perPixelProc: ColorDisplayCase.PerPixelProc] ~ { map: SampleMapCD; map: SampleMapCD _ ImagerOps.SampleMapFromFrameBuffer [Terminal.GetColorFrameBufferA[InterminalBackdoor.terminal]]; action: PROC [sampleBuffer: ImagerSample.SampleBuffer] ~ TRUSTED { bounds: ImagerSampleMap.DeviceRectangle ~ map.Window; sample: ImagerSample.UnsafeSamples ~ GetPointer[sampleBuffer, 0, 0, bounds.fSize]; maxVal: ImagerSample.Sample ~ CARDINAL[Basics.BITSHIFT[1, Basics.BITSHIFT[1, map.refRep.lgBitsPerPixel]]-1]; FOR s: INT IN [bounds.sMin..bounds.sMin+bounds.sSize) DO Process.CheckForAbort[]; SampleMapOps.GetF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize]; FOR j: NAT IN [0..bounds.fSize) DO sample[j] _ MAX[MIN[INT[perPixelProc[sample[j]]], maxVal], 0]; ENDLOOP; SampleMapOps.PutF[map, s, bounds.fMin, sampleBuffer, 0, 0, bounds.fSize, null, null]; ENDLOOP; }; ImagerSample.DoWithScratchBuffer[1, map.fSize, action]; }; ExpandReals: TYPE ~ RECORD [length: NAT _ 0, e: SEQUENCE maxLength: NAT OF REAL]; expandFactors: ARRAY [0..9) OF REAL _ [0./8., 5./8., 2./8., 7./8., 4./8., 1./8., 6./8., 3./8, 8./8.]; expandArray: ARRAY [0..9) OF REF ExpandReals; Expand: PUBLIC PROC [map: SampleMap, sourceBox: Box, report: BOOL _ TRUE] ~ { CheckAndReport: PROC [y: NAT] ~ { Process.CheckForAbort[]; IF report AND (y MOD 10 = 0 OR y > 700) THEN MessageWindow.Append[IO.PutFR["\t\tAt line %g (of 767)", IO.int[y]], TRUE]; }; sourceMap: SampleMap _ ImagerSample.ReIndex[map, , sourceBox]; s: PixelArray _ CtBasic.PixelArrayFromSampleMap[sourceMap]; d: PixelArray _ CtBasic.AllocatePixelArray[[[0, 0], [768, 1024]]]; IF sourceBox = [[0, 0], [480, 640]] THEN { -- optimized for the common case DoLine: PROC [reals0, reals1: REF ExpandReals] ~ { DoPixel: PROC [fX0Y0, fX1Y0, fX0Y1, fX1Y1: REAL] ~ INLINE { vX0Y0: NAT _ sLine0[sX0]; vX1Y0: NAT _ sLine0[sX1]; vX0Y1: NAT _ sLine1[sX0]; vX1Y1: NAT _ sLine1[sX1]; dLine[dX] _ IF vX0Y0=vX1Y0 AND vX0Y0=vX0Y1 AND vX0Y0=vX1Y1 THEN vX0Y0 ELSE Real.Round[fX0Y0*vX0Y0+fX1Y0*vX1Y0+fX0Y1*vX0Y1+fX1Y1*vX1Y1]; dX _ dX+1; }; dX, sX0, sX1: NAT _ 0; CheckAndReport[dY]; FOR n: NAT IN [0..128) DO sX0 _ sX1; sX1 _ sX1+1; DoPixel[reals0[8], reals0[0], reals1[8], reals1[0]]; DoPixel[reals0[7], reals0[1], reals1[7], reals1[1]]; sX0 _ sX1; sX1 _ sX1+1; DoPixel[reals0[6], reals0[2], reals1[6], reals1[2]]; DoPixel[reals0[5], reals0[3], reals1[5], reals1[3]]; sX0 _ sX1; sX1 _ sX1+1; DoPixel[reals0[4], reals0[4], reals1[4], reals1[4]]; sX0 _ sX1; sX1 _ sX1+1; DoPixel[reals0[3], reals0[5], reals1[3], reals1[5]]; DoPixel[reals0[2], reals0[6], reals1[2], reals1[6]]; sX0 _ sX1; IF sX1 < 639 THEN sX1 _ sX1+1; DoPixel[reals0[1], reals0[7], reals1[1], reals1[7]]; ENDLOOP; IF dY < 767 THEN dLine _ d[dY _ dY+1]; }; dY, sY1: NAT _ 0; dLine: SampleBuffer _ d[0]; sLine0: SampleBuffer _ s[0]; sLine1: SampleBuffer _ s[1]; FOR n: NAT IN [0..96) DO sLine0 _ sLine1; sLine1 _ s[sY1 _ sY1+1]; DoLine[expandArray[8], expandArray[0]]; DoLine[expandArray[7], expandArray[1]]; sLine0 _ sLine1; sLine1 _ s[sY1 _ sY1+1]; DoLine[expandArray[6], expandArray[2]]; DoLine[expandArray[5], expandArray[3]]; sLine0 _ sLine1; sLine1 _ s[sY1 _ sY1+1]; DoLine[expandArray[4], expandArray[4]]; sLine0 _ sLine1; sLine1 _ s[sY1 _ sY1+1]; DoLine[expandArray[3], expandArray[5]]; DoLine[expandArray[2], expandArray[6]]; sLine0 _ sLine1; IF sY1 < 479 THEN sLine1 _ s[sY1 _ sY1+1]; DoLine[expandArray[1], expandArray[7]]; ENDLOOP; } ELSE { maxX: NAT _ sourceBox.max.f-1; maxY: NAT _ sourceBox.max.s-1; xScale: REAL _ REAL[sourceBox.max.f-sourceBox.min.f]/1024.0; yScale: REAL _ REAL[sourceBox.max.s-sourceBox.min.s]/768.0; FOR y: NAT IN [0..768) DO yy: REAL _ REAL[y]*yScale; y0: NAT _ sourceBox.min.s+Real.Fix[yy]; y1: NAT _ IF y0 < maxY THEN y0+1 ELSE y0; dy: REAL _ yy-y0; my: REAL _ 1.0-dy; CheckAndReport[y]; FOR x: NAT IN [0..1024) DO xx: REAL _ REAL[x]*xScale; x0: NAT _ sourceBox.min.f+Real.Fix[xx]; x1: NAT _ IF x0 < maxX THEN x0+1 ELSE x0; dx: REAL _ xx-x0; mx: REAL _ 1.0-dx; vx0y0: CARDINAL _ s[y0][x0]; vx0y1: CARDINAL _ s[y1][x0]; vx1y0: CARDINAL _ s[y0][x1]; vx1y1: CARDINAL _ s[y1][x1]; d[y][x] _ IF vx0y0=vx0y1 AND vx0y0=vx1y0 AND vx0y0=vx1y1 THEN vx0y0 ELSE Real.Round[mx*my*vx0y0+dx*my*vx1y0+mx*dy*vx0y1+dx*dy*vx1y1]; ENDLOOP; ENDLOOP; }; ImagerSample.Transfer[map, CtBasic.SampleMapFromPixelArray[d]]; }; ExpandInit: PROC ~ { FOR n: NAT IN [0..9) DO v: REF ExpandReals _ expandArray[n] _ NEW[ExpandReals[9]]; f: REAL _ expandFactors[n]; FOR nn: NAT IN [0..9) DO v[nn] _ f*expandFactors[nn]; ENDLOOP; ENDLOOP; }; CoonsWarpInner1: PROC ~ { Nonoptimized (nonincremental) version, 130 sec for full screen: c00, c10, c11, c01: Pair; line: SampleBuffer _ ImagerSample.ObtainScratchSamples[du+1]; pu0: PairSequence _ G2dContour.ResamplePairs[curves[0], du+1]; p1v: PairSequence _ G2dContour.ResamplePairs[curves[1], dv+1]; pu1: PairSequence _ G2dContour.ResamplePairs[curves[2], du+1]; p0v: PairSequence _ G2dContour.ResamplePairs[curves[3], dv+1]; [] _ G2dContour.ReversePairs[pu1, pu1]; [] _ G2dContour.ReversePairs[p0v, p0v]; c00 _ G2dVector.Midpoint[pu0[0], p0v[0]]; c10 _ G2dVector.Midpoint[pu0[du], p1v[0]]; c11 _ G2dVector.Midpoint[pu1[du], p1v[dv]]; c01 _ G2dVector.Midpoint[pu1[0], p0v[dv]]; Given function F(u, v), with u, v IN [0..1]: FOR v: NAT IN [v0..v1) DO vf: REAL _ REAL[v-v0]/dv; FOR u: NAT IN [u0..u1) DO uf: REAL _ REAL[u-u0]/du; x: INT16 _ Real.Fix[ (1-vf)*pu0[u].x+ vf*pu1[u].x+ (1-uf)*p0v[v].x+ uf*p1v[v].x- (1-uf)*(1-vf)*c00.x- uf*(1-vf)*c10.x- (1-uf)*vf*c01.x- uf*vf*c11.x]; y: INT16 _ Real.Fix[ (1-vf)*pu0[u].y+ vf*pu1[u].y+ (1-uf)*p0v[v].y+ uf*p1v[v].y- (1-uf)*(1-vf)*c00.y- uf*(1-vf)*c10.y- (1-uf)*vf*c01.y- uf*vf*c11.y]; line[u] _ IF x >= 0 AND x < dx AND y >= 0 AND y < dy THEN pa1[y][x] ELSE 0; ENDLOOP; ImagerSample.PutSamples[dest.bw, [v, u0], , line, 0, du+1]; ENDLOOP; ImagerSample.ReleaseScratchSamples[line]; }; CoonsWarpInner2: PROC ~ { Moderately optimized version (mostly incremental yet REAL), 30 sec for full screen: c00, c10, c11, c01, c0v, c1v, dc0v, dc1v: Pair; line: SampleBuffer _ ImagerSample.ObtainScratchSamples[du+1]; pu0: PairSequence _ G2dContour.ResamplePairs[curves[0], du+1]; p1v: PairSequence _ G2dContour.ResamplePairs[curves[1], dv+1]; pu1: PairSequence _ G2dContour.ResamplePairs[curves[2], du+1]; p0v: PairSequence _ G2dContour.ResamplePairs[curves[3], dv+1]; [] _ G2dContour.ReversePairs[pu1, pu1]; [] _ G2dContour.ReversePairs[p0v, p0v]; c00 _ G2dVector.Midpoint[pu0[0], p0v[0]]; c10 _ G2dVector.Midpoint[pu0[du], p1v[0]]; c11 _ G2dVector.Midpoint[pu1[du], p1v[dv]]; c01 _ G2dVector.Midpoint[pu1[0], p0v[dv]]; c0v _ c00; dc0v _ [(c01.x-c00.x)/dv, (c01.y-c00.y)/dv]; c1v _ c10; dc1v _ [(c11.x-c10.x)/dv, (c11.y-c10.y)/dv]; FOR v: NAT IN [v0..v1) DO vf: REAL _ REAL[v-v0]/dv; puv: Pair _ [p0v[v].x-c0v.x, p0v[v].y-c0v.y]; dpuv: Pair _ [(p1v[v].x-c1v.x-puv.x)/du, (p1v[v].y-c1v.y-puv.y)/du]; FOR u: NAT IN [u0..u1) DO x: INT16 _ Real.Fix[puv.x + pu0[u].x+vf*(pu1[u].x-pu0[u].x)]; y: INT16 _ Real.Fix[puv.y + pu0[u].y+vf*(pu1[u].y-pu0[u].y)]; line[u] _ IF x >= 0 AND x < dx AND y >= 0 AND y < dy THEN pa1[y][x] ELSE 0; puv _ [puv.x+dpuv.x, puv.y+dpuv.y]; ENDLOOP; ImagerSample.PutSamples[dest.bw, [v, u0], , line, 0, du+1]; c0v _ [c0v.x+dc0v.x, c0v.y+dc0v.y]; c1v _ [c1v.x+dc1v.x, c1v.y+dc1v.y]; ENDLOOP; ImagerSample.ReleaseScratchSamples[line]; }; ExpandInit[]; ส6ไ–"cedarcode" style•NewlineDelimiter ™™Jšœ ฯeœ6™BJ™(J™'—J˜Jšฯk œพ˜วJ˜–0.320 0.931 0.362 textColoršะbl œะbkฯb ˜Jšžœ†˜Jšžœ˜ —J˜Jšœž˜head–0.0 24 .div 1 1 textColoršฯl™J• CharProps' Postfix16.0 24 .div 1 1 textColoršก œžœ˜)J–' Postfix16.0 24 .div 1 1 textColoršก œžœ˜(J–' Postfix16.0 24 .div 1 1 textColoršก œžœ˜'J–'Postfix16.0 24 .div 1 1 textColoršกœžœ˜J–'Postfix16.0 24 .div 1 1 textColoršกœžœ˜J–' Postfix16.0 24 .div 1 1 textColoršก œžœ˜)J–' Postfix16.0 24 .div 1 1 textColoršก œžœ˜*J–'Postfix16.0 24 .div 1 1 textColoršกœžœ˜J–' Postfix16.0 24 .div 1 1 textColor šก œžœ˜,J–'Postfix16.0 24 .div 1 1 textColoršกœžœ˜#J–'Postfix16.0 24 .div 1 1 textColor šกœžœ˜(J–'Postfix16.0 24 .div 1 1 textColor šกœžœ˜&J–' Postfix16.0 24 .div 1 1 textColor"šก œžœ˜+J–' Postfix16.0 24 .div 1 1 textColor$šก œžœ˜0J–'Postfix16.0 24 .div 1 1 textColoršกœžœ˜!J–'Postfix16.0 24 .div 1 1 textColoršกœžœ˜!J–'Postfix16.0 24 .div 1 1 textColor-šกœžœ'˜;–'Postfix16.0 24 .div 1 1 textColorš œžœžœ˜J˜—J–'Postfix16.0 24 .div 1 1 textColoršกœ žœ˜J˜J–&Postfix0.0 24 .div 1 1 textColor)š ฯnœžžœžœ žœžœ˜.—–0.0 24 .div 1 1 textColoršข™–( Postfix0.320 0.931 0.362 textColorBšะbn œžœžœ5˜NJ–(Postfix0.320 0.931 0.362 textColorO•StartOfExpansion€[map: ImagerSample.SampleMap, initIndex: SF.Vec _ [s: 0, f: 0], delta: SF.Vec _ [s: 0, f: 1], buffer: ImagerSample.SampleBuffer, start: NAT _ 0, count: NAT _ 32767, function: ImagerSample.Function _ [dstFunc: null, srcFunc: null]]šฃœO˜UJ˜$J˜RJšœžœ!˜*Jšœ)žœ˜CJ˜J™—–( Postfix0.320 0.931 0.362 textColorDšค œžœžœ7˜QJš žœžœžœžœ,žœ˜XJ˜J˜—–(Postfix0.320 0.931 0.362 textColor7šคœžœžœ*˜F–(Postfix0.320 0.931 0.362 textColoršฃœ˜J˜!Jš œžœžœžœžœ˜DJ˜J˜—J˜6Jšœžœ˜Jšœžœ˜J˜J˜J˜—–(Postfix0.320 0.931 0.362 textColor9šคœžœžœ,˜IJš žœžœžœžœ%žœ˜QJ˜——–0.0 24 .div 1 1 textColoršข™–(Postfix0.320 0.931 0.362 textColorFš คœžœžœžœžœžœ˜MJšžœ˜J˜–(Postfix0.320 0.931 0.362 textColor%šฃœžœžœ˜,˜Jšœžœžœ˜=Jšœžœžœ˜B—J˜3J˜—Jšœžœฯc ˜Jšœžœฅ ˜Jšœžœ˜ Jšœžœ˜Jšœžœ˜Jšœžœžœžœ˜*Jš œžœžœžœ žœ žœ˜FJšœžœžœ&˜7Jšœžœ˜Jšœžœ˜&Jšœžœ˜J˜*šžœ˜ š žœžœžœžœž˜(Jšœžœžœžœ˜>J˜&Jšž˜—šžœžœžœ ž˜#šžœžœžœ ž˜Jšœžœ˜Jšœžœ˜J˜#Jšžœžœžœ˜Jšžœ˜—Jšžœ˜——J˜——–0.0 24 .div 1 1 textColoršข™–(Postfix0.320 0.931 0.362 textColor:šฃœž œ-˜B–(Postfix0.320 0.931 0.362 textColor7šกœžœ(žœ˜=šžœžœžœž˜'J˜J˜AJš žœžœžœ žœ!žœ˜DJ˜AJšžœ˜—J˜—Jšœžœžœ ˜šžœ'˜)Jšžœ2˜6—J˜J˜—–(Postfix0.320 0.931 0.362 textColorVš ฃœžžžžžžœ3žœ˜[J˜šžœžœ˜–( Postfix0.320 0.931 0.362 textColorHšฃ œžœ$žœžœ˜QJšœžœ˜Jš žœžœžœ žœ,žœ˜MJ˜—Jšœžœžœ˜0Jšœžœ˜Jšœžžžœ˜ J–16.0 24 .div 1 1 textColor™$J˜-J˜-J˜-Jšžœฯsžฆžฆœฆžฆœฆœฆœ/ฆžœ˜Zšžœžœžœ"ž˜1J˜šžœžœžœ ž™ Jš œžœžœ žœ žœ™P—šžœžœžœž˜J˜TJšžœ˜—šžœžœžœž˜!J–'K Postfix16.0 24 .div 1 1 textColorš œ ฆœฆœ=ะdsฅัcdsฅ ˜WJšžœ˜—šžœžœžœž˜J˜TJšžœ˜—Jšžœ˜—Jš žœžœžœžœ.žœ˜MJ˜—J˜——–0.0 24 .div 1 1 textColoršข%™%–(Postfix0.320 0.931 0.362 textColorEšฃœžœžœ0žœ˜HJ˜CJ˜CJ˜CJ˜&J˜&J˜%šžœžœžœž˜šžœžœžœž˜Jšœ žœ8˜KJšœ žœ8˜KJšœ4žœ˜NJšžœ˜—Jšžœ˜—J˜J˜—–(Postfix0.320 0.931 0.362 textColor3šกœž œžœ˜5J˜3Jšœ žœžœžœ ˜)Jšœ žœ˜#šžœ ˜ šžœ˜J˜MJ˜AJ˜OJ˜—šžœ˜Jš œฆœฆœ ฆœ ฆœ˜UJ˜AJ˜HJ˜——J˜J˜—–(Postfix0.320 0.931 0.362 textColor3šกœž œžœ˜7J˜3Jšœ žœžœžœ ˜)Jšœ žœ˜#šžœ ˜ šžœ˜J˜MJ˜AJ˜OJ˜—šžœ˜Jš œฆœฆœฆœฆœ˜UJ˜AJ˜HJ˜——J˜J˜—–(Postfix0.320 0.931 0.362 textColor"šคœžœžœ˜(J˜6J˜J˜—–(Postfix0.320 0.931 0.362 textColor"šกœž œ˜*J˜$J˜&J˜MJ˜Mšžœžœžœž˜Jšœžœ˜Jšœžœ˜J˜J˜@J˜AJ˜@J˜AJšžœ˜—J˜*J˜*J˜J˜—–(Postfix0.320 0.931 0.362 textColor"šกœž œ˜*–(Postfix0.320 0.931 0.362 textColor7šกœžœ(žœ˜=Jšœžœ ˜šžœžœžœž˜'J˜J˜Bšžœžœžœž˜Jšœžœ˜Jšœžœ ˜J˜J˜Jšžœ˜—J˜BJšžœ˜—J˜—J˜$J˜&J˜2J˜J˜—–(Postfix0.320 0.931 0.362 textColor<š คœžœžœžœžœ˜C–(Postfix0.320 0.931 0.362 textColor7šกœžœ(žœ˜=Jšœžœ˜ šžœžœžœž˜J˜šžœ ˜Jšžœ)˜-Jšžœ*˜/—J˜DJ˜DJšžœ˜—J˜—J˜$J˜&J˜2J˜J˜—–(Postfix0.320 0.931 0.362 textColor<š คœžœžœžœžœ˜C–(Postfix0.320 0.931 0.362 textColor7šกœžœ(žœ˜=Jšœžœ ˜šžœžœžœž˜'J˜J˜Bšžœ ˜Jš žœžœžœžœžœ!ž˜KJš žœžœžœžœžœ!žœ˜L—J˜BJšžœ˜—J˜—J˜$J˜&J˜2J˜J˜—–( Postfix0.320 0.931 0.362 textColor"šฃ œž œ˜+J˜6š žœžœžœžœž˜%Jšœžœ ˜J˜šžœžœžœž˜Jšœžœ ˜J˜J˜Jšžœ˜—Jšžœ˜—J˜)J˜J˜—–(Postfix0.320 0.931 0.362 textColor1šฃœž œžœ˜7J˜$Jšœžœžœ ˜Jšœžœžœ ˜˜+J˜-J˜%J˜-—J˜J˜—–(Postfix0.320 0.931 0.362 textColor-šฃœžœžœžœ™3J™J™6Jšœžœžœ ™Jšœžœ™š žœžœžœ žœฅ™1Jšœžœ ™Jšœžœ ™Jšœ žœ ™Jšœ žœ ™J™š žœžœžœ žœฅ™2šžœ™šžœ™Jšœžœ™Jšœฅ™7Jšœฅ™:Jšœฅ™9Jšœฅ™1J™—šžœ™Jšœžœ™Jšœฅ™9Jšœฅ™:Jšœฅ™7Jšœ™J™——J™ J™ J™J™Jšž™—Jšžœ™—J™)J™J™—–(Postfix0.320 0.931 0.362 textColorAšฃœžœžœ+žœ˜DJšœ žœ?˜LJšœžœ˜!J˜@J˜@šžœ$ž˜.šœ˜šžœžœžœž˜'J˜Jšœ@˜@Jšœ@˜@šžœžœžœ ž˜Jšœžœžœ ˜Jšœžœ1˜š ฃœž œžœžœžœžœ˜BJšœžœ˜#Jšžœžœžœžœ˜Jš žœžœžœ žœžœ˜0Jšœžœžœžœ˜%š žœžœžœžœžœžœž˜1Jšœžœžœžœ˜0Jšžœ˜—J˜/J˜J˜—–( Postfix0.320 0.931 0.362 textColorBšฃ œž œžœžœ˜KJ˜+J˜*Jš žœžœžœžœ$žœ˜PJ˜——–0.0 24 .div 1 1 textColoršข™–( Postfix0.320 0.931 0.362 textColoršฃ œž œ˜Jšœžœ˜Jšœ˜Jšœ žœžœ˜Jšœ žœžœ ˜%Jšžœ˜Jšœ˜–( Postfix0.320 0.931 0.362 textColoršก œ˜ J˜AJšœžœ ˜Jšœžœ ˜Jšœžœ˜Jšœžœ˜š žœžœžœžœž˜;šœ&žœ˜/Jšžœ˜Jšžœ˜——J˜—J–(Postfix0.320 0.931 0.362 textColorMš ฃœžœžœ žœžœžœ ˜TJšœžœ˜&Jšœžœ˜&˜/J˜OJ˜.—J˜AJ˜AJ˜J˜—–( Postfix0.320 0.931 0.362 textColoršฃ œž œ˜Jšœฅ0˜OJšœžœžœฅ˜7Jšœฅ˜-Jšœ žœžœ˜ J˜J™Jšก4™4šฃœžœ˜Jšœ*ฆœ™0™J™7J™4J™5—Jš œžœ žœžœžœ ฅ˜J˜>J˜>J˜>Jšœžœ˜9Jšœžœ˜9Jšœžœ˜9Jšœžœ˜9Jšœžœ˜9Jšœžœ˜:J˜4Jšœžœฅ˜6Jš œžœžœž œžœžœžœ˜KJšœ žœ ˜J™J˜)J˜)J™*šžœžœžœ ž˜J˜!J˜!Jšžœ˜—šžœžœžœ ž˜J˜!J˜!Jšžœ˜—J™UJ˜(J˜)J˜*J˜)J™!J˜7J˜7šžœžœžœ ž˜J˜J˜;Jšžœ˜—J˜ J™š žœžœžœ žœžœž˜0šžœ˜ J˜2J˜IJšœžœžœžœ˜*Jšœžœžœžœ˜,Jšœžœžœžœ˜(Jšœžœžœžœ˜(Jšœžœžœžœ˜(šžœž˜šžœ ฅ˜ šžœžœžœ ž˜šœžœ˜ Jšžœž œžœ˜:—šœžœ˜ Jšžœž œžœ˜:—šžœžœžœžœ˜*šž˜J˜Jšžœžœ&˜1J˜—šžœ˜J˜ Jšžœžœ˜!J˜——J˜#J˜)Jšœ žœ ˜Jšœžœ ˜Jšœ žœ ˜Jšžœžœ žœžœ ˜=Jšž˜——šžœ ฅ˜#šžœžœžœ ž˜Jšœžœ˜$Jšœžœ˜$šžœžœžœžœ˜*šžœ˜J˜Jšžœžœ6˜AJ˜—šžœ˜J˜ Jšžœžœ˜!J˜——J˜#J˜)Jšœ žœ ˜Jšœžœ ˜Jšœ žœ ˜Jšžœžœ žœžœ ˜=Jšžœ˜———J˜?šžœžœ˜ Jšœ?˜?Jšœ?˜?J˜—J˜#J˜#J˜—Jšžœ˜—J˜*J˜*J˜—Jšœžœ ฅ˜)Jšœžœ ˜Jšœžœฅ˜-Jšœžœ˜Jšœžœฅ+˜FJšœžœ˜Jšœžœ ˜Jšœžœ ˜Jšžœ žœžœ žœ˜2J™-J™NJ˜——–0.0 24 .div 1 1 textColoršข™–(Postfix0.320 0.931 0.362 textColorAš ฃœžœžœžœžœžœ˜GJšœžœ˜Jšœžœ˜Jšœžœ ˜Jšœžœ ˜Jšœžœ ˜Jšœ žœ˜Jšžœ žœ ˜Jšžœ žœ ˜Jšžœžœ žœ˜2Jšžœžœ žœ˜2J˜J˜šžœž˜Jšœžœ#˜.˜Jšœžœ˜Jšœžœ˜Jšœžœฅ˜0Jšœžœ ˜Jšœžœ˜!Jšœžœ˜!Jšœžœ%˜/Jšœžœ˜ Jšœ žœฅ˜.Jšœ žœ˜Jšœžœ ˜Jšœžœ ˜J˜)Jšžœ˜J˜—Jšžœžœ˜—J˜J˜—–( Postfix0.320 0.931 0.362 textColor=š ฃ œžœžœžœžœžœ˜GJšœžœ˜Jšœžœ˜Jšœžœ ˜Jšœžœ ˜Jšœžœ ˜Jšœ žœ˜Jšžœ žœ ˜Jšžœ žœ ˜Jšžœžœ žœ˜2Jšžœžœ žœ˜2J˜J˜šžœž˜˜Jšœžœ žœ˜,Jšœžœ žœ˜,Jšžœ˜J˜—Jšžœžœ˜—J˜——–0.0 24 .div 1 1 textColoršข ™ J–' Postfix16.0 24 .div 1 1 textColoršก œž œ˜'–'Postfix16.0 24 .div 1 1 textColor#šกœžœ˜4J˜—–(Postfix0.320 0.931 0.362 textColor2šฃœžœžœ˜CJšžœ žœžœ˜JJ˜J˜—–(Postfix0.320 0.931 0.362 textColor5šฃœžœžœžœ˜=Jšžœ-˜3J˜J˜—J–' Postfix16.0 24 .div 1 1 textColor$š ก œžžžžžœžœžœ˜-J–'Postfix16.0 24 .div 1 1 textColor#šกœžœžœ˜4–'Postfix16.0 24 .div 1 1 textColoršกœžœžœ˜%Jš œžžžžžžžœ˜Jšœžœ žœžœ ˜9J˜J˜—–(Postfix0.320 0.931 0.362 textColor2šฃœžœžœ˜CJšžœ!˜'J˜J˜—–(Postfix0.320 0.931 0.362 textColor5šฃœžœžœžœ˜=Jšžœ1˜7J˜——J˜šžœ˜J˜J˜–0.0 24 .div 1 1 textColoršข™–(Postfix0.320 0.931 0.362 textColorAš ฃœžœžœžœžœžœ™GJšœžœ™ Jšœžœ™Jšœžœ™šžœ™šžœ™šžœ™Jšžœžœ ™šžœ™Jšœžœ™Jšœžœ™Jšžœ žœžœžœ™8J™——J™—šžœ™šžœ™šžœ™Jšœžœ™Jšœžœ™Jšžœ žœžœžœ™8J™—šžœ™Jšœžœ™Jšœžœ™Jšœžœ™Jšœžœ™Jšœžœ™šžœ žœ žœ™"Jšžœžœ™šžœ™Jšœžœ™Jšœžœ™J™ J™——J™——J™——Jšžœ™J™J™—–(Postfix0.320 0.931 0.362 textColor"šกœž œ™)J™^J™J™—–(Postfix0.320 0.931 0.362 textColor$šคœž œ™'Jšœžœ žœžœ™J™$J™[–(Postfix0.320 0.931 0.362 textColor4šกœžœ%žœ™:J™Tšžœžœžœ ž™/J™J™;Jš žœžœžœžœžœ™DJ™GJšžœ™—J™—šžœžœžœ ž™Jšœ žœžœ>™SJšžœ™—J™ J™7J™J™—–(Postfix0.320 0.931 0.362 textColor=šคœžœžœ0™DJ™J™f–(Postfix0.320 0.931 0.362 textColor<šกœžœ-žœ™BJ™5J™RJš œžœ žœžœ žœ#™lšžœžœžœ)ž™8J™J™IJ™"J™UJšžœ™—J™—J™7J™J™—–(Postfix0.320 0.931 0.362 textColor?šคœžœžœ2™GJ™J™s–(Postfix0.320 0.931 0.362 textColor<šกœžœ-žœ™BJ™5J™RJš œžœ žœžœ žœ#™lšžœžœžœ)ž™8J™J™Išžœžœžœž™"Jšœ žœžœžœ'™>Jšžœ™—J™UJšžœ™—J™—J™7J™J™Jšœ žœžœ žœ žœ žœžœžœ™QJšœžœžœžœB™eJšœ žœžœžœ ™-J™—–(Postfix0.320 0.931 0.362 textColorGš ฃœžœžœ*žœžœ™M–(Postfix0.320 0.931 0.362 textColoršฃœžœžœ™!J™š žœžœžœžœ ž™,Jšœžœ!žœ žœ™K—J™—J™>J™;J™Bšžœ!™#šžœฅ ™'–(Postfix0.320 0.931 0.362 textColor,šฃœžœžœ™2–(Postfix0.320 0.931 0.362 textColor4šฃœžœžœžœ™;Jšœžœ™Jšœžœ™Jšœžœ™Jšœžœ™Jš œ žœ žœ žœ žœžœ=™‡J™ J™—Jšœžœ™J™šžœžœžœ ž™J™J™4J™4J™J™4J™4J™J™4J™J™4J™4Jšœ žœ žœ ™)J™4Jšžœ™—Jšžœ žœ™&J™—Jšœ žœ™J™J™J™šžœžœžœ ž™J™)J™'J™'J™)J™'J™'J™)J™'J™)J™'J™'Jšœžœ žœ™;J™'Jšžœ™—J™—šžœ™Jšœžœ™Jšœžœ™Jšœžœžœ)™J™>J™>J™>J™'J™'J™)J™*J™+J™*Jšœ"ฆœ™,šžœžœžœ ž™Jšœžœžœ ™šžœžœžœ ž™Jšœžœžœ ™šœžœ ™J™J™ J™J™ J™J™J™J™ —šœžœ ™J™J™ J™J™ J™J™J™J™ —Jš œ žœžœžœžœžœ žœ™KJšžœ™—J™;Jšžœ™—J™)J™J™—–(Postfix0.320 0.931 0.362 textColor šฃœžœ™Jšœ5žœ™SJ™/J™=J™>J™>J™>J™>J™'J™'J™)J™*J™+J™*J™7J™7šžœžœžœ ž™Jšœžœžœ ™J™-J™Dšžœžœžœ ž™Jšœžœ5™=Jšœžœ5™=Jš œ žœžœžœžœžœ žœ™KJ™#Jšžœ™—J™;J™#J™#Jšžœ™—J™)J™J™—J™ ™J™———J˜—…—UฮผF