<> <> <> <<>> DIRECTORY Basics, FunctionCache, II, IIBackdoor, IIBitmapContext, IIColor, IIColorPrivate, IIDevice, IIFont, IIMaskCache, IIPixel, IIPixelArray, IIPrivate, IIRaster, IIRasterShow, IISample, IIState, IITransformation, Real, Scaled, SF, PrincOps, PrincOpsUtils; IIBitmapContextImpl: CEDAR PROGRAM IMPORTS Basics, FunctionCache, II, IIBackdoor, IIColorPrivate, IIMaskCache, IIPixel, IIPixelArray, IIPrivate, IIRaster, IIRasterShow, IISample, IITransformation, Real, Scaled, SF, PrincOpsUtils EXPORTS IIBitmapContext, II SHARES IISample ~ BEGIN <> Context: TYPE ~ II.Context; ScaledVec: TYPE ~ IIMaskCache.ScaledVec; ShowData: TYPE ~ IIRasterShow.ShowData; classCode: PUBLIC ATOM _ $Bitmap; fontCacheMaxSize: NAT _ 4000; Case: TYPE ~ {constant, tile, sampled, sampledBlack}; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ bitmap: IISample.SampleMap _ NIL, case: Case _ constant, constant: [0..1] _ 1, function: IISample.Function _ [null, null], maskBitmapFunction: IISample.Function _ [null, null], tile: IIBitmapContext.Brick _ [0, NIL, 0], -- bitmap for case=tile separation: ATOM, brick: IIBitmapContext.Brick _ [0, NIL, 0], -- thresholds scratchTileMap: IISample.SampleMap _ NIL, -- scratch storage for constant colors scratchStippleMap: IISample.SampleMap _ NIL, -- scratch storage for stipples paToDevice: IITransformation.Transformation, source: IIPixel.PixelMap _ NIL, sharedSource: BOOL _ FALSE ]; <> Create: PUBLIC PROC [deviceSpaceSize: SF.Vec, scanMode: II.ScanMode, surfaceUnitsPerInch: II.VEC, pixelUnits: BOOL, fontCacheName: ATOM, fontTuner: IIDevice.FontTuner] RETURNS [Context] ~ { data: Data ~ NEW[DataRep _ [paToDevice: IITransformation.Scale[1]]]; deviceParm: IIDevice.DeviceParm _ NEW[IIDevice.DeviceParmRep _ [ class: deviceClass, sSize: deviceSpaceSize.s, fSize: deviceSpaceSize.f, scanMode: scanMode, surfaceUnitsPerInch: surfaceUnitsPerInch, surfaceUnitsPerPixel: 1, fontTuner: fontTuner, fontCache: IF fontCacheName = NIL THEN NIL ELSE IIMaskCache.GetNamedCache[fontCacheName, fontCacheMaxSize], rastWeight: 2.0 ]]; context: Context ~ IIRaster.Create[class: contextClass, deviceParm: deviceParm, data: data, pixelUnits: pixelUnits]; data.brick _ DefaultBrick[]; IIRaster.SetDeviceClipBox[context, [[0,0], [0,0]]]; RETURN [context]; }; SetBitmap: PUBLIC PROC [context: Context, bitmap: II.SampleMap] ~ { data: Data ~ NARROW[context.data]; data.bitmap _ bitmap; IIRaster.SetDeviceClipBox[context, IISample.GetBox[bitmap]]; }; GetBitmap: PUBLIC PROC [context: Context] RETURNS [II.SampleMap] ~ { data: Data ~ NARROW[context.data]; RETURN [data.bitmap]; }; <> DefaultBrick: PROC RETURNS [IIBitmapContext.Brick] ~ { b: II.SampleMap ~ IISample.NewSampleMap[box: [max: [4, 4]], bitsPerSample: 8]; b.Put[[0, 0], 00]; b.Put[[0, 1], 01]; b.Put[[0, 2], 13]; b.Put[[0, 3], 14]; b.Put[[1, 0], 08]; b.Put[[1, 1], 02]; b.Put[[1, 2], 03]; b.Put[[1, 3], 15]; b.Put[[2, 0], 09]; b.Put[[2, 1], 10]; b.Put[[2, 2], 04]; b.Put[[2, 3], 05]; b.Put[[3, 0], 07]; b.Put[[3, 1], 11]; b.Put[[3, 2], 12]; b.Put[[3, 3], 06]; RETURN [[maxSample: 15, sampleMap: b, phase: 0]] }; SetHalftoneProperties: PUBLIC PROC [context: II.Context, separation: ATOM, brick: IIBitmapContext.Brick] ~ { WITH context.data SELECT FROM data: Data => { data.brick _ brick; data.separation _ separation; II.SetColor[context, IIBackdoor.GetColor[context]]; -- to re-validate the color }; ENDCASE => NULL; }; MakeTile: PROC [f: REAL, brick: IIBitmapContext.Brick, data: Data] RETURNS [IIBitmapContext.Brick] ~ { box: SF.Box ~ IISample.GetBox[brick.sampleMap]; sample: CARDINAL ~ Real.Round[f*(brick.maxSample+1)]; b: IISample.SampleBuffer ~ IISample.ObtainScratchSamples[SF.SizeF[box]]; sampleMap: IISample.SampleMap ~ IF data.scratchTileMap # NIL AND IISample.GetBox[data.scratchTileMap] = box THEN data.scratchTileMap ELSE (data.scratchTileMap _ IISample.NewSampleMap[box]); FOR s: INTEGER IN [box.min.s..box.max.s) DO IISample.GetSamples[map: brick.sampleMap, initIndex: [s, box.min.f], buffer: b]; FOR j: INTEGER IN [0..b.length) DO b[j] _ IF sample > b[j] THEN 1 ELSE 0; ENDLOOP; IISample.PutSamples[map: sampleMap, initIndex: [s, box.min.f], buffer: b]; ENDLOOP; IISample.ReleaseScratchSamples[b]; RETURN [[maxSample: 1, sampleMap: sampleMap, phase: brick.phase]]; }; FractionFromConstantColor: PROC [data: Data, c: IIColor.ConstantColor] RETURNS [REAL] ~ { f: REAL ~ IIColorPrivate.GrayFromColor[c]; <> RETURN [f] }; SampledColorData: TYPE ~ REF SampledColorDataRep; SampledColorDataRep: TYPE ~ RECORD [ pa: IIPixelArray.PixelArray, colorOperator: IIColor.ColorOperator, separation: ATOM, maxSample: CARDINAL, filterDiameter: NAT, source: IIPixel.PixelMap ]; me: REF TEXT ~ "Bitmap"; -- a globally unique REF for use as a clientID in the global cache. BitmapSetColor: PROC [context: Context, color: II.Color, viewToDevice: II.Transformation] RETURNS [IIDevice.AllowedMasks] ~ { data: Data ~ NARROW [context.data]; allowBitmaps: BOOL; IF NOT data.sharedSource THEN data.source _ NIL; WITH color SELECT FROM c: IIColor.ConstantColor => { f: REAL ~ FractionFromConstantColor[data, c]; data.case _ constant; data.function _ [null, null]; SELECT f FROM < 0.00001 => { data.case _ constant; data.constant _ 0 }; > 0.99999 => { data.case _ constant; data.constant _ 1 }; ENDCASE => { data.tile _ MakeTile[f, data.brick, data]; data.case _ tile; }; }; sampledColor: IIColor.SampledColor => { BitmapSetSampledColor[context: context, sampledColor: sampledColor, viewToDevice: viewToDevice]; }; s: IIColor.SampledBlack => { pa: IIPixelArray.PixelArray ~ s.pa; IITransformation.ApplyCat[data.paToDevice, pa.m, s.um, viewToDevice]; data.function _ IF s.clear THEN [or, null] ELSE [null, null]; IF data.paToDevice.form = 3 AND data.paToDevice.integerTrans AND pa.sSize*pa.fSize <= 16384 THEN { <> min: SF.Vec ~ [s: data.paToDevice.tx, f: data.paToDevice.ty]; max: SF.Vec ~ [s: min.s + pa.sSize, f: min.f + pa.fSize]; data.case _ tile; data.tile.maxSample _ 1; data.tile.sampleMap _ IISample.NewSampleMap[box: [min: min, max: max]]; data.tile.phase _ 0; IIPixelArray.Transfer[self: pa, dst: data.tile.sampleMap, dstMin: min]; } ELSE { One: IIPixel.PixelProc ~ {RETURN [1]}; data.case _ sampledBlack; data.source _ IIPixel.NewPixelMap[samplesPerPixel: 1, maxSample: One, box: [max: [pa.sSize, pa.fSize]]]; IIPixelArray.Transfer[self: pa, dst: data.source[0]]; }; }; s: IIColor.SpecialColor => { IF s.type = $Stipple THEN { stippleData: IIColorPrivate.StippleData ~ NARROW[s.data]; data.function _ stippleData.function; SELECT stippleData.word FROM 00000H => { data.case _ constant; data.constant _ 0 }; 0FFFFH => { data.case _ constant; data.constant _ 1 }; ENDCASE => { data.case _ tile; data.tile.sampleMap _ data.scratchStippleMap _ IISample.TileFromStipple[stipple: stippleData.word, scratch: data.scratchStippleMap]; data.tile.phase _ 0; }; } ELSE IF s.substitute # NIL THEN { [] _ BitmapSetColor[context, s.substitute, viewToDevice] } ELSE { ERROR II.Error[[code: $unknownSpecialColor, explanation: "Unknown special color has no substitute value"]] }; }; ENDCASE => ERROR; data.maskBitmapFunction _ [null, null]; IF data.case = constant THEN SELECT data.function FROM [null, null] => { SELECT data.constant FROM 0 => data.maskBitmapFunction _ [and, complement]; 1 => data.maskBitmapFunction _ [or, null]; ENDCASE => NULL; }; [xor, null] => { IF data.constant = 1 THEN data.maskBitmapFunction _ [xor, null]; }; ENDCASE => NULL; allowBitmaps _ (data.maskBitmapFunction # [null, null]); RETURN [[unorderedBoxes: data.function.dstFunc # xor, regionFill: data.case = constant, bitmap: allowBitmaps, rawBitmaps: allowBitmaps, runGroupChar: FALSE, rasterChar: FALSE]]; }; ComputeSource: PROC [sampledColor: IIColor.SampledColor, colorOperator: IIColor.ColorOperator, maxOut: PROC [i: NAT] RETURNS [CARDINAL], filterDiameter: NAT, separation: ATOM] RETURNS [IIPixel.PixelMap] ~ { colorOutput: IIColorPrivate.ColorOutput ~ NEW[IIColorPrivate.ColorOutputRep _ [type: $Y, samplesPerPixelOut: 1, impl: NIL]]; pixelMap: IIPixel.PixelMap ~ IIColorPrivate.Translate[self: sampledColor.colorOperator, output: colorOutput, pa: sampledColor.pa, maxOut: maxOut]; <> RETURN [pixelMap] }; BitmapSetSampledColor: PROC [context: Context, sampledColor: IIColor.SampledColor, viewToDevice: II.Transformation] ~ { data: Data ~ NARROW [context.data]; pa: IIPixelArray.PixelArray ~ sampledColor.pa; largerPaSize: NAT ~ MIN[pa.sSize, pa.fSize] + 1; cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[]; filterDiameter: NAT _ 1; -- for low-pass filtering scd: SampledColorData _ NIL; compare: FunctionCache.CompareProc ~ { WITH argument SELECT FROM scd: SampledColorData => RETURN [scd.pa = pa AND scd.colorOperator = sampledColor.colorOperator AND scd.separation = data.separation AND scd.maxSample = data.brick.maxSample+1 AND scd.filterDiameter = filterDiameter]; ENDCASE => RETURN [FALSE] }; maxOut: PROC [i: NAT] RETURNS [CARDINAL] ~ { RETURN [data.brick.maxSample+1] }; IITransformation.ApplyCat[data.paToDevice, pa.m, sampledColor.um, viewToDevice]; filterDiameter _ MIN[MAX[Real.Round[1.0/MAX[IITransformation.SingularValues[data.paToDevice].y, 1.0/largerPaSize]], 1], 255]; data.case _ sampled; data.function _ [null, null]; IF pa.immutable THEN { scd _ NARROW[FunctionCache.Lookup[cache, compare, me].value]; IF scd = NIL THEN { colorOutput: IIColorPrivate.ColorOutput ~ NEW[IIColorPrivate.ColorOutputRep _ [type: $Y, samplesPerPixelOut: 1, impl: NIL]]; pixelMap: IIPixel.PixelMap ~ ComputeSource[sampledColor, sampledColor.colorOperator, maxOut, filterDiameter, data.separation]; scd _ NEW [SampledColorDataRep _ [pa: pa, colorOperator: sampledColor.colorOperator, separation: data.separation, maxSample: data.brick.maxSample+1, filterDiameter: filterDiameter, source: pixelMap]]; FunctionCache.Insert[x: cache, argument: scd, value: scd, size: IISample.WordsForMap[size: IISample.GetSize[pixelMap[0]], bitsPerSample: IISample.GetBitsPerSample[pixelMap[0]]], clientID: me]; }; data.source _ scd.source; data.sharedSource _ TRUE; } ELSE { data.source _ ComputeSource[sampledColor, sampledColor.colorOperator, maxOut, filterDiameter, data.separation]; data.sharedSource _ FALSE; }; }; <> interpolate: BOOL _ TRUE; BitmapMaskBoxes: PROC [context: Context, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { data: Data ~ NARROW[context.data]; SELECT data.case FROM constant => { IISample.FillBoxes[map: data.bitmap, boxes: boxes, value: data.constant, function: data.function]; }; tile => { IISample.TileBoxes[map: data.bitmap, boxes: boxes, tile: data.tile.sampleMap, phase: data.tile.phase, function: data.function]; }; sampled => TRUSTED { buffer: IISample.SampleBuffer ~ IISample.ObtainScratchSamples[SF.SizeF[bounds]]; Action: SAFE PROC [pixels: IIPixel.PixelBuffer, min: SF.Vec] ~ TRUSTED { buffer.length _ pixels.length; IISample.GetTileSamples[tile: data.brick.sampleMap, initIndex: min, buffer: buffer, phase: data.brick.phase]; IISample.Halftone[map: data.bitmap, min: min, sampleBuffer: pixels[0], thresholdBuffer: buffer, function: data.function]; }; IIPixel.Resample[self: data.source, m: data.paToDevice, interpolate: interpolate, boxes: boxes, bounds: bounds, action: Action]; IISample.ReleaseScratchSamples[buffer]; }; sampledBlack => { Action: PROC [pixels: IIPixel.PixelBuffer, min: SF.Vec] ~ { IISample.PutSamples[map: data.bitmap, initIndex: min, buffer: pixels[0], start: 0, count: pixels.length, function: data.function]; }; IIPixel.Resample[self: data.source, m: data.paToDevice, interpolate: FALSE, boxes: boxes, bounds: bounds, action: Action]; }; ENDCASE => ERROR; }; BitmapMaskRegion: PROC [context: Context, bounds: SF.Box, edgeGenerator: PROC [IISample.EdgeAction]] ~ { data: Data ~ NARROW[context.data]; IF data.case # constant THEN ERROR; IISample.RegionFill[dst: data.bitmap, edgeGenerator: edgeGenerator, value: data.constant, function: data.function]; }; BitmapMaskBitmap: PROC [context: Context, bitmap: IISample.SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { data: Data ~ NARROW[context.data]; IISample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes, function: data.maskBitmapFunction]; }; rawArraySize: NAT ~ IISample.rawArraySize; RawArray: TYPE ~ IISample.RawArray; BitmapMaskRawBitmaps: PROC [context: Context, n: [0..rawArraySize], a: POINTER TO RawArray] ~ { data: Data ~ NARROW[context.data]; IISample.MultipleTransfer[dst: data.bitmap, n: n, a: a, function: data.maskBitmapFunction]; }; BitmapDrawBitmap: PROC [context: Context, bitmap: IISample.SampleMap, delta: SF.Vec, bounds: SF.Box, boxes: SF.BoxGenerator] ~ { data: Data ~ NARROW[context.data]; IISample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes]; }; BitmapMoveBox: PROC [context: Context, dstMin, srcMin, size: SF.Vec] ~ { data: Data ~ NARROW[context.data]; IISample.Move[map: data.bitmap, dstMin: dstMin, srcMin: srcMin, size: size]; }; BitmapDoBuffered: PROC [context: Context, bounds: SF.Box, copy: BOOL, action: PROC] ~ { data: Data ~ NARROW[context.data]; buffer: IISample.SampleMap ~ IISample.ObtainScratchMap[box: bounds, bitsPerSample: 1]; bitmap: IISample.SampleMap ~ data.bitmap; IF copy THEN IISample.Transfer[dst: buffer, src: bitmap]; data.bitmap _ buffer; action[ ! UNWIND => data.bitmap _ bitmap ]; IISample.Transfer[dst: bitmap, src: buffer]; data.bitmap _ bitmap; IISample.ReleaseScratchMap[buffer]; }; <> State: TYPE ~ IIState.State; StateRep: PUBLIC TYPE ~ IIState.StateRep; -- export to II.StateRep XChar: TYPE ~ IIFont.XChar; RasterMask: TYPE ~ REF IIMaskCache.CharMaskRep.raster; RawDescriptor: TYPE ~ IISample.RawDescriptor; bitsPerWord: NAT ~ Basics.bitsPerWord; worryNat: NAT ~ LAST[NAT]/2-1; worryReal: REAL _ worryNat; nullBitBltTable: PrincOps.BitBltTable ~ [ dst: [word: NIL, bit: 0], dstBpl: 0, src: [word: NIL, bit: 0], srcDesc: [srcBpl[0]], width: 0, height: 0, flags: [] ]; CommonChar: TYPE ~ IIRasterShow.CommonChar; Ord: PROC [char: XChar] RETURNS [CARDINAL] ~ --INLINE-- {RETURN [LOOPHOLE[char]]}; BPLFromFSize: PROC [i: INTEGER] RETURNS [INTEGER] ~ --INLINE-- { bplMask: CARDINAL ~ LAST[CARDINAL]-(bitsPerWord-1); RETURN [LOOPHOLE[Basics.BITAND[LOOPHOLE[i+(bitsPerWord-1)], bplMask]]] }; Trust: PROC [i: INTEGER] RETURNS [CARDINAL] ~ --INLINE-- { RETURN [LOOPHOLE[i]] }; BitmapFastShow: PUBLIC PROC [context: Context, string: IIFont.XStringProc, xrel: BOOL] ~ { state: State ~ context.state; data: Data ~ NARROW[context.data]; showData: ShowData ~ IIRasterShow.GetShowData[context]; easyMetrics: BOOL ~ (state.np.amplifySpace = 1.0 AND state.np.correctPass = 0); box: SF.Box _ []; TryScaled: PROC RETURNS [BOOL] ~ --INLINE-- { IIRasterShow.TryFastState[state, showData]; RETURN [showData.valid]; }; IF NOT xrel AND showData.allow.rawBitmaps AND state.np.correctPass = 0 AND state.np.noImage = 0 AND showData.fontCache # NIL AND TryScaled[] AND data.bitmap.bitsPerLine MOD bitsPerWord = 0 THEN TRUSTED { charArray: REF ARRAY CommonChar OF RasterMask _ IIRasterShow.GetCharArray[showData.fontAtom]; bbTableSpace: PrincOps.BBTableSpace; bbPtr: PrincOps.BBptr ~ PrincOpsUtils.AlignedBBTable[@bbTableSpace]; dstWpl: CARDINAL ~ data.bitmap.bitsPerLine / bitsPerWord; dstBase: LONG POINTER ~ data.bitmap.base.word; dstBox: SF.Box ~ data.bitmap.box; FastCharAction: SAFE PROC [char: XChar] ~ TRUSTED { m: RasterMask _ NIL; charBox: SF.Box; QuickLook: SAFE PROC RETURNS [ok: BOOL] ~ CHECKED --INLINE-- { IF Ord[char] IN CommonChar THEN { m _ charArray[Ord[char]]; IF m = NIL THEN { m _ IIRasterShow.CommonCharLookup[showData, charArray, char]; IF m = NIL THEN RETURN [FALSE] }; charBox _ SF.Displace[m.box, [showData.cp.s.integerPart, showData.cp.f.integerPart]]; RETURN [TRUE]; }; RETURN [FALSE] }; IF showData.valid AND QuickLook[] THEN { SELECT TRUE FROM SF.Inside[charBox, box] OR SF.Inside[charBox, box _ IIRaster.GetContainingBox[context, charBox.min]] => { fSize: INTEGER ~ charBox.max.f-charBox.min.f; IF fSize > 0 THEN TRUSTED { bb: PrincOps.BBptr ~ bbPtr; f0: CARDINAL ~ Trust[charBox.min.f-dstBox.min.f]; bb.dst.word _ dstBase + Basics.LongMult[Trust[charBox.min.s-dstBox.min.s], dstWpl] + f0/bitsPerWord; bb.dst.bit _ f0 MOD bitsPerWord; bb.src.word _ @(m[0]); bb.srcDesc _ [srcBpl[BPLFromFSize[fSize]]]; bb.width _ Trust[fSize]; bb.height _ Trust[charBox.max.s-charBox.min.s]; PrincOpsUtils.BITBLT[bb]; }; IF easyMetrics THEN { <> showData.cp.s _ showData.cp.s.PLUS[m.escapement.s]; showData.cp.f _ showData.cp.f.PLUS[m.escapement.f]; RETURN }; }; SF.Empty[SF.Intersect[charBox, showData.clipBounds]] => { NULL }; ENDCASE => { IIRasterShow.FlushState[state, showData]; IF NOT IIRasterShow.MaskCharMask[context, m] THEN ERROR; -- should never fail showData.valid _ TRUE; -- Everything is really still valid }; IIRasterShow.DoCharMetrics[state, showData, m]; RETURN; }; IIRasterShow.FlushState[state, showData]; IIRasterShow.CachedShowChar[context, char]; IIRasterShow.TryFastState[state, showData]; }; IF NOT SF.Inside[box, data.bitmap.box] THEN ERROR; bbPtr^ _ nullBitBltTable; bbPtr.dstBpl _ data.bitmap.bitsPerLine; bbPtr.flags _ [disjoint: TRUE, disjointItems: TRUE, gray: FALSE]; bbPtr.flags.srcFunc _ data.maskBitmapFunction.srcFunc; bbPtr.flags.dstFunc _ data.maskBitmapFunction.dstFunc; string[FastCharAction]; IIRasterShow.FlushState[state, showData]; } ELSE IIRasterShow.FastShow[context, string, xrel]; }; <> deviceClass: IIDevice.DeviceClass ~ NEW[IIDevice.DeviceClassRep _ [ SetColor: BitmapSetColor, SetPriority: NIL, MaskBoxes: BitmapMaskBoxes, MaskRegion: BitmapMaskRegion, MaskBitmap: BitmapMaskBitmap, MaskRawBitmaps: BitmapMaskRawBitmaps, DrawBitmap: BitmapDrawBitmap, MaskChar: NIL, MoveBox: BitmapMoveBox, DoBuffered: BitmapDoBuffered ]]; contextClass: IIPrivate.Class ~ CreateClass[]; CreateClass: PROC RETURNS [class: IIPrivate.Class] ~ --INLINE-- { class _ IIRaster.CreateClass[type: classCode, deviceClass: deviceClass]; class.Show _ BitmapFastShow; class.DrawObject _ IIPrivate.RasterDrawObject; }; END.