<> <> <> <<>> DIRECTORY Imager, ImagerOps, ImagerPixelMap, ImagerPixelArray, ImagerTransformation, Terminal, ImagerBitmapDevicePrivate, ImagerDitheredDevicePrivate, ImagerColor24DevicePrivate, ImagerRasterPrivate, ImagerDevice, ImagerState, Vector2, ImagerManhattan, ImagerManhattanExtras, ImagerBackdoor, ImagerPixelArrayDefs, ImagerSample, UnsafeStorage, ImagerPixelArrayPrivate, PrincOps, Basics, PrincOpsUtils; ImagerOpsImpl: CEDAR MONITOR IMPORTS Imager, ImagerPixelMap, ImagerTransformation, ImagerRasterPrivate, ImagerManhattan, ImagerManhattanExtras, ImagerBackdoor, ImagerSample, UnsafeStorage, Basics, PrincOpsUtils, ImagerPixelArray, Terminal EXPORTS ImagerOps, Imager, ImagerPixelArrayDefs ~ BEGIN Context: TYPE ~ Imager.Context; VEC: TYPE ~ Vector2.VEC; Color: TYPE ~ Imager.Color; PixelArray: TYPE ~ ImagerPixelArray.PixelArray; PixelMap: TYPE ~ ImagerPixelMap.PixelMap; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; Transformation: TYPE ~ ImagerTransformation.Transformation; Rectangle: TYPE ~ ImagerTransformation.Rectangle; FrameBuffer: TYPE ~ Terminal.FrameBuffer; Virtual: TYPE ~ Terminal.Virtual; Kind: TYPE ~ ATOM; State: TYPE ~ ImagerState.State; StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep PixelArrayFromPixelMaps: PUBLIC PROC [pms: LIST OF PixelMap, um: Transformation] RETURNS [PixelArray] ~ { w: DeviceRectangle ~ ImagerPixelMap.BoundedWindow[pms.first]; samplesPerPixel: INT _ 0; FOR p: LIST OF PixelMap _ pms.rest, p.rest UNTIL p=NIL DO IF w # ImagerPixelMap.BoundedWindow[p.first] THEN ERROR Imager.Error[[$specification, "Pixel map windows do not match."]]; ENDLOOP; FOR p: LIST OF PixelMap _ pms, p.rest UNTIL p=NIL DO samplesPerPixel _ samplesPerPixel + 1; p.first _ ImagerPixelMap.ShiftMap[p.first, -w.sMin, -w.fMin]; ENDLOOP; RETURN [NEW[ImagerPixelArrayDefs.PixelArrayRep _ [ samplesPerPixel: samplesPerPixel, sSize: w.sSize, fSize: w.fSize, m: ImagerTransformation.PostTranslate[um, [w.sMin, w.fMin]], impl: NIL, class: pixelArrayClass, data: pms ]]]; }; PixelArrayClass: TYPE ~ ImagerPixelArrayPrivate.PixelArrayClass; PixelArrayClassRep: PUBLIC TYPE ~ ImagerPixelArrayPrivate.PixelArrayClassRep; pixelArrayClass: PixelArrayClass ~ NEW[PixelArrayClassRep _ [ type: $PixelMaps, MaxSampleValue: PmsMaxSampleValue, UnsafeGetSamples: PmsUnsafeGetSamples, UnsafeGetBits: PmsUnsafeGetBits ]]; GetPM: PROC [pa: PixelArray, i: NAT] RETURNS [PixelMap] ~ { pms: LIST OF PixelMap _ NARROW[pa.data]; THROUGH [0..Basics.BoundsCheck[i, pa.samplesPerPixel]) DO pms _ pms.rest ENDLOOP; RETURN [pms.first]; }; PmsMaxSampleValue: PROC [pa: PixelArray, i: NAT] RETURNS [ImagerPixelArray.Sample] ~ { pm: PixelMap ~ GetPM[pa, i]; RETURN [Basics.BITSHIFT[1, Basics.BITSHIFT[1, pm.refRep.lgBitsPerPixel]]-1]; }; PmsUnsafeGetSamples: UNSAFE PROC [pa: PixelArray, i: NAT, s, f: INT, samples: ImagerSample.UnsafeSamples, count: NAT] ~ UNCHECKED { pm: PixelMap ~ GetPM[pa, i]; ImagerSample.UnsafeGetF[samples: samples, count: count, base: pm.refRep.pointer, wordsPerLine: pm.refRep.rast, bitsPerSample: Basics.BITSHIFT[1, pm.refRep.lgBitsPerPixel], s: s-pm.sOrigin, f: f-pm.fOrigin]; }; bitsPerWord: NAT ~ Basics.bitsPerWord; PmsUnsafeGetBits: UNSAFE PROC [pa: PixelArray, i: NAT _ 0, s, f: INT, dst: PrincOps.BitAddress, dstBpl: INTEGER, width, height: CARDINAL, srcFunc: PrincOps.SrcFunc _ null, dstFunc: PrincOps.DstFunc _ null] ~ UNCHECKED { source: PixelMap ~ GetPM[pa, i]; sStartSource: NAT ~ s-source.sOrigin; fMinSource: NAT ~ f-source.fOrigin; bbTableSpace: PrincOps.BBTableSpace; bb: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; [] _ Basics.BoundsCheck[width, source.fSize+1]; [] _ Basics.BoundsCheck[height, source.sSize+1]; [] _ Basics.BoundsCheck[source.refRep.lgBitsPerPixel, 1]; bb^ _ [ dstBpl: dstBpl, srcDesc: [srcBpl[source.refRep.rast*bitsPerWord]], height: height, width: width, flags: [direction: forward, disjoint: TRUE, disjointItems: TRUE, gray: FALSE, srcFunc: srcFunc, dstFunc: dstFunc], dst: dst, src: [word: (source.refRep.pointer + Basics.LongMult[sStartSource, source.refRep.rast] + (fMinSource/bitsPerWord)), bit: fMinSource MOD bitsPerWord] ]; PrincOpsUtils.BITBLT[bb]; }; PixelMapFromPixelArray: PUBLIC PROC [pa: PixelArray, i: NAT] RETURNS [PixelMap] ~ { IF pa.class = pixelArrayClass THEN RETURN [GetPM[pa, i]] ELSE { max: INT ~ ImagerPixelArray.MaxSampleValue[pa, i]; bitsPerSample: NAT ~ Lg[max+1]; pm: PixelMap ~ ImagerPixelMap.Create[Lg[bitsPerSample], [0, 0, pa.sSize, pa.fSize]]; ImagerPixelMap.Clear[pm]; IF bitsPerSample = 1 THEN TRUSTED { ImagerPixelArray.UnsafeGetBits[pa: pa, i: i, s: 0, f: 0, dst: [word: pm.refRep.pointer, bit: 0], dstBpl: pm.refRep.rast*bitsPerWord, width: pm.fSize, height: pm.sSize, srcFunc: null, dstFunc: null] } ELSE TRUSTED { tZone: UNCOUNTED ZONE ~ UnsafeStorage.GetTransientPageUZone[]; buf: ImagerSample.UnsafeSamples _ tZone.NEW[ImagerSample.RawSamples[pm.fSize]]; base: LONG POINTER ~ pm.refRep.pointer; wpl: NAT ~ pm.refRep.rast; FOR line: NAT IN [0..pm.sSize) DO ImagerPixelArray.UnsafeGetSamples[pa: pa, i: i, s: line, f: 0, samples: buf, count: pm.fSize]; ImagerSample.UnsafePutF[samples: buf, count: pm.fSize, s: line, f: 0, base: base, wordsPerLine: wpl, bitsPerSample: bitsPerSample, srcFunc: null, dstFunc: null]; ENDLOOP; tZone.FREE[@buf]; }; RETURN [pm]; }; }; KindOf: PUBLIC PROC [context: Context] RETURNS [Kind] ~ { WITH context.data SELECT FROM rasterData: ImagerRasterPrivate.Data => { device: ImagerDevice.Device ~ rasterData.device; WITH device.data SELECT FROM bitmap: ImagerBitmapDevicePrivate.Data => RETURN [$Bitmap]; dithered: ImagerDitheredDevicePrivate.Data => RETURN [$Dithered]; fullColor: ImagerColor24DevicePrivate.Data => RETURN [$Color24]; ENDCASE => NULL; }; ENDCASE => NULL; RETURN [NIL] }; SurfaceToDevice: PUBLIC PROC [context: Context] RETURNS [Transformation] ~ { WITH context.data SELECT FROM rasterData: ImagerRasterPrivate.Data => { device: ImagerDevice.Device ~ rasterData.device; RETURN [device.surfaceToDevice]; }; ENDCASE => NULL; RETURN [NIL]; }; knownKinds: LIST OF Kind _ LIST [$Bitmap, $Dithered, $Color24]; KnownKinds: PUBLIC PROC RETURNS [LIST OF Kind] ~ {RETURN [knownKinds]}; TerminalFromContext: PUBLIC PROC [context: Context] RETURNS [vt: Virtual, color: BOOL] ~ { WITH context.data SELECT FROM rasterData: ImagerRasterPrivate.Data => { device: ImagerDevice.Device ~ rasterData.device; WITH device.data SELECT FROM bitmap: ImagerBitmapDevicePrivate.Data => { vt: Virtual ~ Terminal.Current[]; fb: Terminal.FrameBuffer ~ Terminal.GetBWFrameBuffer[vt]; IF fb = NIL OR fb.base # bitmap.frame.pointer THEN RETURN [NIL, FALSE] ELSE RETURN [vt, FALSE] }; dithered: ImagerDitheredDevicePrivate.Data => RETURN [dithered.terminal, TRUE]; fullColor: ImagerColor24DevicePrivate.Data => RETURN [fullColor.terminal, TRUE]; ENDCASE => NULL; }; ENDCASE => NULL; RETURN [NIL, FALSE]; }; Lg: PROC [n: INT] RETURNS [lg: NAT _ 0] ~ { nn: LONG CARDINAL ~ n; k: LONG CARDINAL _ 1; UNTIL k=0 OR k>= nn DO lg _ lg + 1; k _ k + k; ENDLOOP; }; PixelMapFromFrameBuffer: PUBLIC PROC [frameBuffer: FrameBuffer] RETURNS [PixelMap] ~ { refRep: REF ImagerPixelMap.PixelMapRep ~ NEW[ImagerPixelMap.PixelMapRep _ [ ref: frameBuffer, pointer: frameBuffer.base, words: INT[frameBuffer.wordsPerLine]*INT[frameBuffer.height], lgBitsPerPixel: Lg[frameBuffer.bitsPerPixel], rast: frameBuffer.wordsPerLine, lines: frameBuffer.height ]]; pixelMap: ImagerPixelMap.PixelMap ~ [ sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: frameBuffer.height, fSize: frameBuffer.width, refRep: refRep ]; RETURN[pixelMap]; }; PixelMapsFromContext: PUBLIC PROC [context: Context] RETURNS [LIST OF PixelMap] ~ { WITH context.data SELECT FROM rasterData: ImagerRasterPrivate.Data => { device: ImagerDevice.Device ~ rasterData.device; WITH device.data SELECT FROM bitmap: ImagerBitmapDevicePrivate.Data => RETURN [LIST[[sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: bitmap.sSizeFrame, fSize: bitmap.fSizeFrame, refRep: bitmap.frame]]]; dithered: ImagerDitheredDevicePrivate.Data => RETURN [LIST[dithered.frame]]; fullColor: ImagerColor24DevicePrivate.Data => RETURN [LIST[PixelMapFromFrameBuffer[fullColor.frame[A]], PixelMapFromFrameBuffer[fullColor.frame[B]]]]; ENDCASE => NULL; }; ENDCASE => NULL; RETURN [NIL]; }; Validate: PROC [context: Context, data: ImagerRasterPrivate.Data, needs: ImagerRasterPrivate.Flags] ~ INLINE { state: ImagerState.State ~ context.state; IF state.changed#ImagerState.notChanged THEN ImagerRasterPrivate.NoteStateChanges[data, state]; IF ImagerRasterPrivate.AndFlags[data.valid, needs]#needs THEN ImagerRasterPrivate.ValidateIfNeeded[data, state, needs]; }; DoSaveClipRectangleI: PROC [context: Context, action: PROC, x, y, w, h: INTEGER] ~ { clippedAction: PROC ~ { Imager.ClipRectangleI[context, x, y, w, h]; action[]; }; WITH context.data SELECT FROM rasterData: ImagerRasterPrivate.Data => { clientToDevice: Transformation; Validate[context, rasterData, [clientToDevice: TRUE]]; clientToDevice _ rasterData.clientToDevice; IF clientToDevice.form=9 AND clientToDevice.integerTrans AND NOT rasterData.specialClipPresent THEN { smin: INT _ INT[clientToDevice.tx]-y; smax: INT _ smin-h; fmin: INT _ INT[clientToDevice.ty]+x; fmax: INT _ fmin+w; IF smax rasterData.specialClipPresent _ rasterData.valid.clientClipper _ FALSE]; rasterData.specialClipPresent _ rasterData.valid.clientClipper _ FALSE; RETURN; }; }; ENDCASE => NULL; Imager.DoSave[context, clippedAction]; }; DoWithBuffer: PUBLIC PROC [context: Context, action: PROC, x, y, w, h: INTEGER, backgroundColor: Color _ NIL] ~ { paintBack: PROC ~ { Imager.SetColor[context, backgroundColor]; Imager.MaskRectangleI[context, x, y, w, h]; }; clippedAction: PROC ~ { IF bufferingOn THEN WITH context.data SELECT FROM rasterData: ImagerRasterPrivate.Data => { Validate[context, rasterData, [clientClipper: TRUE]]; IF rasterData.clientClipBoxOnly THEN { device: ImagerDevice.Device ~ rasterData.device; WITH device.data SELECT FROM bitmapData: ImagerBitmapDevicePrivate.Data => { IF DoWithBitmapBuffer[context, action, backgroundColor, rasterData, bitmapData] THEN RETURN; }; ditheredData: ImagerDitheredDevicePrivate.Data => { IF DoWithDitheredBuffer[context, action, backgroundColor, rasterData, ditheredData] THEN RETURN; }; fullColorData: ImagerColor24DevicePrivate.Data => { }; ENDCASE => NULL; }; }; ENDCASE => NULL; IF backgroundColor # NIL THEN Imager.DoSave[context, paintBack]; action[]; }; DoSaveClipRectangleI[context, clippedAction, x, y, w, h]; }; DeviceBoxFromDeviceRectangle: PROC [r: ImagerPixelMap.DeviceRectangle] RETURNS [ImagerDevice.DeviceBox] ~ { RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]]; }; DoWithShift: PROC [rasterData: ImagerRasterPrivate.Data, action: PROC, sDelta, fDelta: INTEGER] ~ { <> delta: VEC ~ [sDelta, fDelta]; minusDelta: VEC ~ [-delta.x, -delta.y]; true: BOOL[TRUE..TRUE] ~ rasterData.valid.clientClipper; savedClipMask: ImagerManhattan.Polygon ~ rasterData.viewClipMask; savedDeviceBox: ImagerDevice.DeviceBox ~ rasterData.device.box; Restore: PROC ~ { minusDelta: VEC ~ [-delta.x, -delta.y]; ImagerTransformation.ApplyPostTranslate[rasterData.viewToDevice, minusDelta]; ImagerTransformation.ApplyPostTranslate[rasterData.device.surfaceToDevice, minusDelta]; rasterData.device.box _ savedDeviceBox; ImagerManhattan.Destroy[rasterData.viewClipMask]; rasterData.viewClipMask _ savedClipMask; rasterData.viewClipBox _ ImagerManhattan.BoundingBox[rasterData.viewClipMask]; rasterData.specialClipRect.sMin _ rasterData.specialClipRect.sMin-sDelta; rasterData.specialClipRect.fMin _ rasterData.specialClipRect.fMin-fDelta; rasterData.valid.clientClipper _ FALSE; rasterData.valid.clientToDevice _ FALSE; }; ImagerTransformation.ApplyPostTranslate[rasterData.viewToDevice, delta]; ImagerTransformation.ApplyPostTranslate[rasterData.device.surfaceToDevice, delta]; rasterData.viewClipMask _ ImagerManhattanExtras.DestructiveClip[ ImagerManhattan.Copy[rasterData.viewClipMask], rasterData.specialClipRect ]; ImagerManhattan.Shift[rasterData.viewClipMask, sDelta, fDelta]; rasterData.viewClipBox _ ImagerManhattan.BoundingBox[rasterData.viewClipMask]; rasterData.device.box _ DeviceBoxFromDeviceRectangle[rasterData.viewClipBox]; rasterData.specialClipRect.sMin _ rasterData.specialClipRect.sMin+sDelta; rasterData.specialClipRect.fMin _ rasterData.specialClipRect.fMin+fDelta; rasterData.valid.clientClipper _ FALSE; rasterData.valid.clientToDevice _ FALSE; action[ ! UNWIND => Restore[]]; Restore[]; }; nScratch: NAT ~ 2; scratch: ARRAY [0..nScratch) OF REF ImagerPixelMap.PixelMapRep _ ALL[NIL]; ObtainScratch: ENTRY PROC RETURNS [r: REF ImagerPixelMap.PixelMapRep] ~ { FOR i: NAT IN [0..nScratch) DO IF (r _ scratch[i]) # NIL THEN {scratch[i] _ NIL; RETURN}; ENDLOOP; RETURN [NIL]; }; ReleaseScratch: ENTRY PROC [r: REF ImagerPixelMap.PixelMapRep] ~ { FOR i: NAT IN [0..nScratch) DO IF scratch[i] = NIL THEN {scratch[i] _ r; RETURN} ENDLOOP; }; DoWithBitmapBuffer: PROC [context: Context, action: PROC, backgroundColor: Color, rasterData: ImagerRasterPrivate.Data, bitmapData: ImagerBitmapDevicePrivate.Data] RETURNS [ok: BOOL _ TRUE] ~ { tightBox: ImagerDevice.DeviceBox ~ rasterData.clientClipBox; box: ImagerDevice.DeviceBox ~ [ smin: tightBox.smin/4*4, fmin: tightBox.fmin/4*4, smax: tightBox.smax, fmax: tightBox.fmax]; display: PixelMap ~ [sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: bitmapData.sSizeFrame, fSize: bitmapData.fSizeFrame, refRep: bitmapData.frame]; buffer: PixelMap ~ ImagerPixelMap.Reshape[ObtainScratch[], 0, [box.smin, box.fmin, box.smax-box.smin, box.fmax-box.fmin]].Clip[[tightBox.smin, tightBox.fmin, tightBox.smax-tightBox.smin, tightBox.fmax-tightBox.fmin]]; frameAction: PROC ~ { bitmapData.sSizeFrame _ buffer.sMin+buffer.sSize; bitmapData.fSizeFrame _ buffer.fMin+buffer.fSize; bitmapData.frame _ buffer.refRep; IF backgroundColor # NIL THEN { state: ImagerState.State ~ context.state; oldColor: Color ~ state.color; Imager.SetColor[context, backgroundColor]; Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]]; Imager.SetColor[context, oldColor]; }; Imager.DoSave[context, action ! UNWIND => { bitmapData.sSizeFrame _ display.sSize; bitmapData.fSizeFrame _ display.fSize; bitmapData.frame _ display.refRep; }]; display.Transfer[buffer]; bitmapData.sSizeFrame _ display.sSize; bitmapData.fSizeFrame _ display.fSize; bitmapData.frame _ display.refRep; }; IF backgroundColor = NIL THEN { buffer.Transfer[display]; } ELSE IF backgroundColor = Imager.white THEN { ImagerPixelMap.Clear[buffer]; backgroundColor _ NIL; }; DoWithShift[rasterData, frameAction, -buffer.sOrigin, -buffer.fOrigin]; ReleaseScratch[buffer.refRep]; }; DoWithDitheredBuffer: PROC [context: Context, action: PROC, backgroundColor: Color, rasterData: ImagerRasterPrivate.Data, ditheredData: ImagerDitheredDevicePrivate.Data] RETURNS [ok: BOOL _ TRUE] ~ { tightBox: ImagerDevice.DeviceBox ~ rasterData.clientClipBox; box: ImagerDevice.DeviceBox ~ [ smin: tightBox.smin/12*12, fmin: tightBox.fmin/12*12, smax: tightBox.smax, fmax: tightBox.fmax]; terminal: Terminal.Virtual ~ ditheredData.terminal; display: PixelMap ~ ditheredData.frame; buffer: PixelMap ~ ImagerPixelMap.Reshape[ObtainScratch[], display.refRep.lgBitsPerPixel, [box.smin, box.fmin, box.smax-box.smin, box.fmax-box.fmin]].Clip[[tightBox.smin, tightBox.fmin, tightBox.smax-tightBox.smin, tightBox.fmax-tightBox.fmin]]; frameAction: PROC ~ { ditheredData.frame _ buffer; ditheredData.frame.sOrigin _ ditheredData.frame.fOrigin _ 0; IF backgroundColor # NIL THEN { state: ImagerState.State ~ context.state; oldColor: Color ~ state.color; Imager.SetColor[context, backgroundColor]; Imager.MaskRectangle[context, ImagerBackdoor.GetBounds[context]]; Imager.SetColor[context, oldColor]; }; Imager.DoSave[context, action ! UNWIND => { ditheredData.frame _ display; }]; IF terminal = NIL THEN display.Transfer[buffer] ELSE { action: PROC ~ {display.Transfer[buffer]}; Terminal.ModifyColorFrame[vt: terminal, action: action, xmin: box.fmin, ymin: box.smin, xmax: box.fmax, ymax: box.smax]; }; display.Transfer[buffer]; ditheredData.frame _ display; }; IF backgroundColor = NIL THEN { IF terminal = NIL THEN buffer.Transfer[display] ELSE { action: PROC ~ {buffer.Transfer[display]}; Terminal.ModifyColorFrame[vt: terminal, action: action, xmin: box.fmin, ymin: box.smin, xmax: box.fmax, ymax: box.smax]; }; buffer.Transfer[display]; }; ditheredData.terminal _ NIL; DoWithShift[rasterData, frameAction, -buffer.sOrigin, -buffer.fOrigin ! UNWIND => {ditheredData.terminal _ terminal}]; ditheredData.terminal _ terminal; ReleaseScratch[buffer.refRep]; }; bufferingOn: BOOL _ TRUE; END.