<<>> <> <> <> <> <<>> DIRECTORY <> Basics, FunctionCache, Imager, ImagerBrick, ImagerColor, ImagerColorPrivate, ImagerDevice, ImagerDeviceProcs, ImagerError, ImagerHighlightContext, ImagerHighlightContextBackdoor, ImagerMaskCache, ImagerPixel, ImagerPixelArray, ImagerPrivate, ImagerRaster, ImagerSample, ImagerTransformation, MaskWithColor, RasterBasics, Real, RealInline, Rope, SF, ImagerSwitches; ImagerHighlightContextImpl: CEDAR PROGRAM IMPORTS <> Basics, FunctionCache, Imager, ImagerBrick, ImagerColor, ImagerColorPrivate, ImagerDevice, ImagerDeviceProcs, ImagerError, ImagerMaskCache, ImagerPixel, ImagerPixelArray, ImagerRaster, ImagerSample, ImagerTransformation, MaskWithColor, Real, RealInline, SF, ImagerSwitches EXPORTS ImagerHighlightContext, ImagerHighlightContextBackdoor ~ BEGIN bpw: NAT = BITS[WORD]; BitOffset: TYPE = [0..bpw); Box: TYPE ~ SF.Box; Context: TYPE ~ Imager.Context; Device: TYPE ~ ImagerDevice.Device; classCode: PUBLIC ATOM ¬ $Highlight; SampledColor: TYPE ~ ImagerColor.SampledColor; OpConstantColor: TYPE ~ ImagerColor.OpConstantColor; SpecialColor: TYPE ~ ImagerColor.SpecialColor; SampledBlack: TYPE ~ ImagerColor.SampledBlack; Transformation: TYPE ~ ImagerTransformation.Transformation; PixelArray: TYPE ~ ImagerPixelArray.PixelArray; ColorOperator: TYPE ~ ImagerColor.ColorOperator; PixelProc: TYPE ~ ImagerPixelArray.PixelProc; PixelBuffer: TYPE ~ ImagerPixelArray.PixelBuffer; PixelMap: TYPE ~ ImagerPixel.PixelMap; RawBytesPtr: TYPE = POINTER TO Basics.RawBytes; RawWordsPtr: TYPE = POINTER TO Basics.RawWords; SampleMap: TYPE = ImagerSample.SampleMap; SampleBuffer: TYPE = ImagerSample.SampleBuffer; RasterSampleMap: TYPE = ImagerSample.RasterSampleMap; ColorLookupTable: TYPE ~ ImagerHighlightContextBackdoor.ColorLookupTable; Case: TYPE ~ { nil, constant, tile, pixelarray, sampled, sampledBlack, clearSampledBlack }; Data: TYPE ~ REF DataRep; DataRep: TYPE ~ RECORD [ sampleMap: ImagerSample.SampleMap, highlight: ImagerColor.ConstantColor ¬ NIL, -- note: we assume base color is white colorLookupTable: ColorLookupTable ¬ NIL, -- takes precedence over highlight mapping hY, hE, hS: REAL ¬ 0.0, hE2hS2Sqr: REAL ¬ 0.0, -- precomputed (data.hE**2 + data.hS**2)**2 fastColorOperator: ImagerColor.ColorOperator, colorTransform: ImagerColorPrivate.ColorTransform ¬ NIL, brick: ImagerBrick.Brick ¬ [0, NIL, 0], interpolate: BOOL ¬ FALSE, -- controls sampling; it would be nice to enable this, but we have some trouble with images that come in bands that way. case: Case ¬ nil, -- what type of color <>> constant: WORD ¬ 0, colorWord: WORD ¬ 0, -- the constant, replicated to fill a word. <>> tile: ImagerSample.SampleMap ¬ NIL, -- sampleMap for case=tile <>> pixelarray: ImagerPixelArray.PixelArray ¬ NIL, <>> sharedSource: BOOL ¬ FALSE, -- If TRUE, don't recycle source. paToDevice: ImagerTransformation.Transformation ¬ NIL, <> source: PixelMap ¬ NIL <> <<1 => intensity,>> <<2 => (black, highlight)>> ]; SampledColorData: TYPE ~ REF SampledColorDataRep; SampledColorDataRep: TYPE ~ RECORD [ pa: ImagerPixelArray.PixelArray, colorOperator: ImagerColor.ColorOperator, highlight: ImagerColor.ConstantColor ¬ NIL, colorLookupTable: ColorLookupTable ¬ NIL, source: PixelMap ]; Create: PUBLIC PROC [deviceSpaceSize: SF.Vec, scanMode: Imager.ScanMode, surfaceUnitsPerInch: Imager.VEC, pixelUnits: BOOL ¬ FALSE, fontCacheName: ATOM ¬ NIL, highlight: Imager.ConstantColor ¬ NIL] RETURNS [Context] ~ { data: Data ~ NEW[DataRep ¬ [ paToDevice: ImagerTransformation.Scale[1] ]]; deviceParm: ImagerDevice.DeviceParm ¬ ImagerDevice.MakeDeviceParm[ class: deviceClass, sSize: deviceSpaceSize.s, fSize: deviceSpaceSize.f, scanMode: scanMode, surfaceUnitsPerInch: surfaceUnitsPerInch, surfaceUnitsPerPixel: 1, fontCache: IF fontCacheName = NIL THEN NIL ELSE ImagerMaskCache.GetNamedCache[fontCacheName] ]; context: Context ~ ImagerRaster.Create[class: contextClass, deviceClass: deviceClass, deviceParm: deviceParm, data: data, pixelUnits: pixelUnits]; SetDataHighlight[data, highlight]; SetDataBrick[data, ImagerBrick.BrickFromDotScreen[pixelsPerDot: 5.656, degrees: 45, shape: 0.48]]; ImagerRaster.SetDeviceClipBox[context, [[0,0], [0,0]]]; RETURN [context]; }; SetSampleMap: PUBLIC PROC [context: Context, sampleMap: Imager.SampleMap] ~ { data: Data ~ NARROW[ImagerRaster.GetDevice[context].data]; IF sampleMap.GetBitsPerSample # 2 THEN ImagerError.Error[[$bounds, "sampleMap must have 2 bits per sample for a Highlight context"]]; data.sampleMap ¬ sampleMap; ImagerRaster.SetDeviceClipBox[context, ImagerSample.GetBox[sampleMap]]; }; GetSampleMap: PUBLIC PROC [context: Context] RETURNS [Imager.SampleMap] ~ { data: Data ~ NARROW[ImagerRaster.GetDevice[context].data]; RETURN [data.sampleMap]; }; SetColorLookupTable: PUBLIC<> PROC [context: Context, colorLookupTable: ColorLookupTable] ~ { WITH ImagerRaster.GetDevice[context].data SELECT FROM data: Data => { data.colorLookupTable ¬ colorLookupTable; data.colorTransform ¬ NIL }; ENDCASE; }; SetDataHighlight: PROC [data: Data, highlight: Imager.ConstantColor] ~ { data.highlight ¬ highlight; [[data.hY, data.hE, data.hS]] ¬ ImagerColor.YESFromColor[ImagerColor.NarrowToOpConstantColor[highlight]]; data.hE2hS2Sqr ¬ (data.hE**2 + data.hS**2)**2; data.fastColorOperator ¬ NewColorOperatorHighlight[highlight]; }; SetDataBrick: PROC [data: Data, brick: ImagerBrick.Brick] ~ { IF brick.maxSample # 255 THEN ERROR; -- temporary(?) restriction. data.brick ¬ brick; data.tile ¬ NIL; }; NewColorOperatorHighlight: PROC [highlight: Imager.ConstantColor] RETURNS [co: ImagerColor.ColorOperator] ~ { mapper: PROC [s: ImagerSample.Sample] RETURNS [ImagerColor.ConstantColor] = { RETURN [SELECT s FROM 0 => Imager.white, 1 => highlight, ENDCASE => Imager.black] }; co ¬ ImagerColor.NewColorOperatorMap[3, mapper]; }; HiliteGetBufferColorOperator: PROC [device: Device] RETURNS [ColorOperator] ~ { WITH device.data SELECT FROM data: Data => { RETURN [data.fastColorOperator] }; ENDCASE => RETURN [NIL]; }; me: REF TEXT ~ "HIGHLITE"; <> PixelMapWords: PROC [pm: PixelMap] RETURNS [words: INT ¬ 0] ~ { FOR i: NAT IN [0..pm.samplesPerPixel) DO words ¬ words + ImagerSample.WordsForMap[size: ImagerSample.GetSize[pm[0]], bitsPerSample: ImagerSample.GetBitsPerSample[pm[0]]]; ENDLOOP; }; ignoreHighlightSetScreen: CHAR['h..'h] ~ ImagerSwitches.Define['h, $ignorehighlightsetscreen, "Ignore setscreen on highlight color device.", NIL]; HiliteSetHalftoneProperties: PROC [device: ImagerDevice.Device, halftoneProperties: ImagerBrick.HalftoneProperties] ~ { data: Data ~ NARROW[device.data]; brick: ImagerBrick.Brick ¬ data.brick; IF ImagerSwitches.BoolValue[ignoreHighlightSetScreen] THEN RETURN; IF halftoneProperties # NIL THEN brick ¬ halftoneProperties.first.brick; FOR tail: ImagerBrick.HalftoneProperties ¬ halftoneProperties, tail.rest UNTIL tail = NIL DO IF tail.first.toner = black THEN { brick ¬ tail.first.brick; EXIT }; ENDLOOP; SetDataBrick[data, brick]; }; HiliteSetColor: PROC [device: Device, color: Imager.Color, viewToDevice: Imager.Transformation] ~ { data: Data ~ NARROW [device.data]; sampled: BOOL ¬ FALSE; allowed: ImagerDevice.AllowedMasks ¬ [ unorderedBoxes: TRUE, multipleCoverage: TRUE, regionFill: FALSE, bitmap: FALSE, rawBitmaps: FALSE, runGroupChar: FALSE, rasterChar: FALSE]; data.case ¬ nil; IF data.source # NIL AND NOT data.sharedSource THEN ImagerSample.ReleaseScratchMap[data.source[0]]; data.source ¬ NIL; WITH color SELECT FROM color: ImagerColor.OpConstantColor => { transform: ImagerColorPrivate.ColorTransform ~ GetColorTransform[data, color.colorOperator]; point: ImagerColorPrivate.ColorPoint ~ ImagerColorPrivate.ColorPointFromColor[color, transform]; black: REAL ~ point[0]; hilite: REAL ~ IF point.dim < 2 THEN 0.0 ELSE point[1]; threshold: REAL ~ 0.5; blackBit: BOOL ~ black > maxSample-threshold; blackSolid: BOOL ~ blackBit OR (black < threshold); hiliteBit: BOOL ~ hilite > maxSample-threshold; hiliteSolid: BOOL ~ hiliteBit OR (hilite < threshold); IF blackSolid AND hiliteSolid THEN TRUSTED { data.case ¬ constant; data.constant _ ORD[blackBit] * 2 + ORD[hiliteBit]; data.colorWord ¬ MaskWithColor.MakeColorWord[data.constant, lgTable[ImagerSample.GetBitsPerSample[data.sampleMap]]]; } ELSE { box: SF.Box ~ ImagerSample.GetBox[data.brick.sampleMap]; b: ImagerPixel.PixelBuffer ~ ImagerPixel.ObtainScratchPixels[samplesPerPixel: 2, length: box.max.f-box.min.f]; Gen: PROC [action: PROC[pixels: ImagerPixel.PixelBuffer, min: SF.Vec]] ~ { FOR s: INTEGER IN [box.min.s..box.max.s) DO action[b, [s, box.min.f]]; ENDLOOP; }; ImagerSample.FillSamples[b[0], Real.Round[black]]; ImagerSample.FillSamples[b[1], Real.Round[hilite]]; data.case ¬ tile; IF data.tile = NIL THEN { data.tile ¬ ImagerSample.NewSampleMap[box: ImagerSample.GetBox[data.brick.sampleMap], bitsPerSample: 2]; }; HiliteHalftone[dst: data.tile, src: Gen, bounds: box, brick: data.brick]; ImagerPixel.ReleaseScratchPixels[b]; }; }; color: SampledColor => { cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[]; pa: PixelArray ~ color.pa; um: Transformation ~ color.um; colorOperator: ColorOperator ~ color.colorOperator; data.paToDevice ¬ ImagerTransformation.Cat[pa.m, color.um, viewToDevice]; IF colorOperator = data.fastColorOperator AND data.paToDevice.form = 3 AND data.paToDevice.integerTrans THEN { <> data.case ¬ pixelarray; data.pixelarray ¬ pa; allowed.multipleCoverage ¬ FALSE; device.state.allow ¬ allowed; RETURN; }; data.case ¬ $sampled; sampled ¬ TRUE; IF pa.immutable THEN { Compare: FunctionCache.CompareProc ~ { WITH argument SELECT FROM scd: SampledColorData => RETURN [scd.pa = pa AND scd.colorOperator = color.colorOperator AND scd.highlight = data.highlight AND scd.colorLookupTable = data.colorLookupTable]; ENDCASE => RETURN [FALSE] }; scd: SampledColorData ¬ NARROW[FunctionCache.Lookup[cache, Compare, me].value]; IF scd = NIL THEN { pixelMap: PixelMap ~ ComputeSource[data, color, color.colorOperator]; words: INT ~ PixelMapWords[pixelMap]; scd ¬ NEW [SampledColorDataRep ¬ [pa: pa, colorOperator: color.colorOperator, highlight: data.highlight, source: pixelMap]]; FunctionCache.Insert[x: cache, argument: scd, value: scd, size: words, clientID: me]; }; data.source ¬ scd.source; data.sharedSource ¬ TRUE; } ELSE { data.source ¬ ComputeSource[data, color, color.colorOperator]; data.sharedSource ¬ FALSE; }; }; color: SampledBlack => { pa: PixelArray ~ color.pa; data.case ¬ IF color.clear THEN clearSampledBlack ELSE sampledBlack; sampled ¬ TRUE; data.paToDevice ¬ ImagerTransformation.Cat[pa.m, color.um, viewToDevice]; data.source ¬ ImagerPixel.MakePixelMap[ImagerSample.ObtainScratchMap[box: [max: [pa.sSize, pa.fSize]], bitsPerSample: 1]]; ImagerPixelArray.Transfer[pa: pa, dst: data.source[0]]; data.sharedSource ¬ FALSE; }; color: ImagerColor.SpecialColor => { SELECT color.type FROM ENDCASE => { IF color.substitute = NIL THEN ERROR ImagerError.Error[[code: $unknownSpecialColor, explanation: "Unknown special color has no substitute value"]]; HiliteSetColor[device, color.substitute, viewToDevice]; }; }; ENDCASE => ERROR; -- unknown color variant IF data.case = constant THEN { allowed.rawBitmaps ¬ allowed.bitmap ¬ TRUE; }; IF sampled THEN allowed.multipleCoverage ¬ allowed.unorderedBoxes ¬ FALSE; device.state.allow ¬ allowed; }; colorSpaceCMYK: ImagerColorPrivate.ColorSpace ~ ImagerColorPrivate.DefineProcessSpace[ImagerColor.NewColorOperatorCMYK[255]]; allowedColorSpaces: LIST OF ImagerColorPrivate.ColorSpace ~ LIST[$Highlight, $YES, $RGB, $Y]; allowedColorSpaces2: LIST OF ImagerColorPrivate.ColorSpace ~ LIST[$Highlight, colorSpaceCMYK, $RGB, $Y]; GetColorTransform: PROC [data: Data, colorOperator: ColorOperator] RETURNS [ImagerColorPrivate.ColorTransform] ~ { allow: LIST OF ImagerColorPrivate.ColorSpace ~ IF data.colorLookupTable # NIL THEN allowedColorSpaces2 ELSE allowedColorSpaces; colorSpace: ImagerColorPrivate.ColorSpace ~ ImagerColorPrivate.ChooseColorSpace[colorOperator, allow]; IF data.colorTransform = NIL OR colorSpace # data.colorTransform.domain THEN { domain: ImagerColorPrivate.ColorSpace ~ colorSpace; dim: NAT ~ IF domain = $Y THEN 1 ELSE 2; rangeMax: ImagerColorPrivate.ColorPoint ~ ImagerColorPrivate.MakeColorPoint[dim, data.brick.maxSample]; ct: ImagerColorPrivate.ColorTransform ~ NEW[ImagerColorPrivate.ColorTransformRep ¬ [ domain: domain, rangeMax: rangeMax, proc: SELECT domain FROM $Highlight => HighlightTransform, $RGB => IF data.colorLookupTable # NIL THEN TableRGBTransform ELSE PictorialTransform, $YES => PictorialTransform, $Y => IntensityTransform, colorSpaceCMYK => CMYKTransform, ENDCASE => ERROR, data: data]]; data.colorTransform ¬ ct; }; RETURN [data.colorTransform] }; <> <> <> <> <> <<};>> HighlightTransform: PROC [self: ImagerColorPrivate.ColorTransform, in: ImagerColorPrivate.ColorPoint, out: ImagerColorPrivate.ColorPoint] ~ { b: REAL ¬ (-in[0]-in[1]+1.0)*maxSample; h: REAL ¬ (in[1])*maxSample; outOfGamut: BOOL ¬ FALSE; IF b < 0.0 THEN {outOfGamut ¬ TRUE; b ¬ 0.0}; IF b > maxSample THEN {outOfGamut ¬ TRUE; b ¬ maxSample}; IF h < 0.0 THEN {outOfGamut ¬ TRUE; h ¬ 0.0}; IF h > maxSample-b THEN {outOfGamut ¬ TRUE; h ¬ maxSample-b}; out[0] ¬ b; out[1] ¬ h; out.outOfGamut ¬ outOfGamut; < [%g, %g] outOfGamut: %g\n", LIST[>> <<[real[in[0]]],>> <<[real[in[1]]],>> <<[real[out[0]]],>> <<[real[out[1]]],>> <<[boolean[out.outOfGamut]]>> <<]];>> }; CMYKTransform: PROC [self: ImagerColorPrivate.ColorTransform, in: ImagerColorPrivate.ColorPoint, out: ImagerColorPrivate.ColorPoint] ~ TRUSTED { data: Data ~ NARROW[self.data]; p: POINTER TO ImagerHighlightContextBackdoor.ColorLookupArray ~ data.colorLookupTable.tablePointer; C: REAL ~ in[0]/255.0; M: REAL ~ in[1]/255.0; Y: REAL ~ in[2]/255.0; K: REAL ~ in[3]/255.0; R: REAL ~ 1.0 - (C+K); G: REAL ~ 1.0 - (M+K); B: REAL ~ 1.0 - (Y+K); outOfGamut: BOOL ¬ FALSE; max: ImagerHighlightContextBackdoor.RedIndex ~ ImagerHighlightContextBackdoor.RedIndex.LAST; r: [0..max] ~ MIN[MAX[RealInline.MCRound[R*max], 0], max]; g: [0..max] ~ MIN[MAX[RealInline.MCRound[G*max], 0], max]; b: [0..max] ~ MIN[MAX[RealInline.MCRound[B*max], 0], max]; black, highlight: BYTE; [highlight: highlight, black: black] ¬ p[r][g][b]; { b: REAL ¬ black; h: REAL ¬ highlight; outOfGamut: BOOL ¬ FALSE; IF b < 0.0 THEN {outOfGamut ¬ TRUE; b ¬ 0.0}; IF b > maxSample THEN {outOfGamut ¬ TRUE; b ¬ maxSample}; IF h < 0.0 THEN {outOfGamut ¬ TRUE; h ¬ 0.0}; IF h > maxSample-b THEN {outOfGamut ¬ TRUE; h ¬ maxSample-b}; out[0] ¬ b; out[1] ¬ h; out.outOfGamut ¬ outOfGamut; }; < [%g, %g]\n", LIST[>> <<[real[in[0]]],>> <<[real[in[1]]],>> <<[real[in[2]]],>> <<[real[in[3]]],>> <<[real[out[0]]],>> <<[real[out[1]]]>> <<]];>> }; maxSample: INT ~ 255; IntensityTransform: PROC [self: ImagerColorPrivate.ColorTransform, in: ImagerColorPrivate.ColorPoint, out: ImagerColorPrivate.ColorPoint] ~ { scaled: REAL ~ (1.0-in[0])*maxSample; clipped: REAL ~ IF scaled < 0.0 THEN 0.0 ELSE IF scaled > maxSample THEN maxSample ELSE scaled; out[0] ¬ clipped; out.outOfGamut ¬ clipped # scaled; < [%g] outOfGamut: %g\n", LIST[>> <<[real[in[0]]],>> <<[real[out[0]]],>> <<[boolean[out.outOfGamut]]>> <<]];>> }; YESFromRGB: PROC [r, g, b: REAL] RETURNS [y, e, s: REAL] ~ INLINE { y ¬ 0.253*r + 0.684*g + 0.063*b; e ¬ (r - g) / 2; s ¬ ((r + g) / 4) - b/2; }; Store: PROC [out: ImagerColorPrivate.ColorPoint, i: NAT, x: REAL, limit: INTEGER] RETURNS [INTEGER] ~ { <> <> scaled: INT ~ RealInline.MCRound[x*maxSample]; clipped: INT ~ IF scaled < 0 THEN 0 ELSE IF scaled > limit THEN limit ELSE scaled; out[i] ¬ clipped; IF clipped # scaled THEN out.outOfGamut ¬ TRUE; RETURN [clipped] }; PictorialTransform: PROC [self: ImagerColorPrivate.ColorTransform, in: ImagerColorPrivate.ColorPoint, out: ImagerColorPrivate.ColorPoint] ~ { <> <> <> data: Data ~ NARROW[self.data]; y, e, s: REAL; IF self.domain = $RGB THEN { [y, e, s] ¬ YESFromRGB[r: in[0], g: in[1], b: in[2]] } ELSE { y ¬ in[0]; e ¬ in[1]; s ¬ in[2] }; { denom: REAL ~ ((e**2 + s**2) * data.hE2hS2Sqr); h: REAL ~ IF denom = 0.0 THEN 0.0 ELSE MAX[0, (((data.hE * e) + (data.hS * s))**3) / denom]; w: REAL ~ MIN[1.0-h, y - (h * data.hY)]; out.outOfGamut ¬ FALSE; [] ¬ Store[out, 1, h, maxSample - Store[out, 0, 1.0-h-w, maxSample]]; }; < [%g, %g]\n", LIST[>> <<[real[in[0]]],>> <<[real[in[1]]],>> <<[real[in[2]]],>> <<[real[out[0]]],>> <<[real[out[1]]]>> <<]];>> }; TableRGBTransform: PROC [self: ImagerColorPrivate.ColorTransform, in: ImagerColorPrivate.ColorPoint, out: ImagerColorPrivate.ColorPoint] ~ TRUSTED { <> data: Data ~ NARROW[self.data]; p: POINTER TO ImagerHighlightContextBackdoor.ColorLookupArray ~ data.colorLookupTable.tablePointer; max: ImagerHighlightContextBackdoor.RedIndex ~ ImagerHighlightContextBackdoor.RedIndex.LAST; r: [0..max] ~ MIN[MAX[RealInline.MCRound[in[0]*max], 0], max]; g: [0..max] ~ MIN[MAX[RealInline.MCRound[in[1]*max], 0], max]; b: [0..max] ~ MIN[MAX[RealInline.MCRound[in[2]*max], 0], max]; black, highlight: BYTE; [highlight: highlight, black: black] ¬ p[r][g][b]; { b: REAL ¬ black; h: REAL ¬ highlight; outOfGamut: BOOL ¬ FALSE; IF b < 0.0 THEN {outOfGamut ¬ TRUE; b ¬ 0.0}; IF b > maxSample THEN {outOfGamut ¬ TRUE; b ¬ maxSample}; IF h < 0.0 THEN {outOfGamut ¬ TRUE; h ¬ 0.0}; IF h > maxSample-b THEN {outOfGamut ¬ TRUE; h ¬ maxSample-b}; out[0] ¬ b; out[1] ¬ h; out.outOfGamut ¬ outOfGamut; }; < [%g, %g] => [%g, %g] outOfGamut: %g\n", LIST[>> <<[real[in[0]]],>> <<[real[in[1]]],>> <<[real[in[2]]],>> <<[real[black]],>> <<[real[highlight]],>> <<[real[out[0]]],>> <<[real[out[1]]],>> <<[boolean[out.outOfGamut]]>> <<]];>> }; ComputeSource: PROC [data: Data, sampledColor: ImagerColor.SampledColor, colorOperator: ImagerColor.ColorOperator] RETURNS [PixelMap] ~ { pa: PixelArray ~ sampledColor.pa; transform: ImagerColorPrivate.ColorTransform ~ GetColorTransform[data, colorOperator]; pixelMap: PixelMap ~ ImagerColorPrivate.Translate[colorOperator: colorOperator, transform: transform, pa: pa]; RETURN [pixelMap]; }; HiliteHalftone: PROC [dst: SampleMap, src: PROC[PROC[pixels: ImagerPixel.PixelBuffer, min: SF.Vec]], bounds: Box, brick: ImagerBrick.Brick] ~ TRUSTED { thresholdBuffer: SampleBuffer ~ ImagerSample.ObtainScratchSamples[SF.SizeF[bounds]]; zeroBuffer: SampleBuffer ¬ NIL; GetHBuffer: SAFE PROC [pixels: ImagerPixel.PixelBuffer] RETURNS [SampleBuffer] ~ TRUSTED { <> IF pixels.samplesPerPixel < 2 THEN { IF zeroBuffer = NIL THEN { zeroBuffer ¬ ImagerSample.ObtainScratchSamples[SF.SizeF[bounds]]; ImagerSample.ClearSamples[zeroBuffer]; }; RETURN[zeroBuffer]; } ELSE { RETURN [pixels[1]] }; }; BitBuffer: TYPE ~ PACKED ARRAY [0..8*16) OF [0..2**16); bitsPerBuffer: NAT ~ BITS[BitBuffer]; pixelsPerBuffer: NAT ~ bitsPerBuffer/2; bitBuffer: BitBuffer; bitBufferMap: SampleMap ~ ImagerSample.ObtainUnsafeDescriptor[size: [s: 1, f: bitsPerBuffer/2], bitsPerSample: 2, bitsPerLine: bitsPerBuffer, base: [word: LOOPHOLE[@bitBuffer], bit: 0], ref: NIL, words: WORDS[BitBuffer]]; Action: SAFE PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ TRUSTED { blackBuffer: SampleBuffer ~ pixels[0]; hiliteBuffer: SampleBuffer ~ GetHBuffer[pixels]; count: NAT ~ pixels.length; index: SF.Vec ¬ min; residual: NAT ¬ thresholdBuffer.length ¬ count; SrcChunk: TYPE ~ ARRAY [0..8) OF WORD; t: POINTER TO SrcChunk ¬ LOOPHOLE[ImagerSample.PointerToSamples[buffer: thresholdBuffer, start: 0, count: count]]; b: POINTER TO SrcChunk ¬ LOOPHOLE[ImagerSample.PointerToSamples[buffer: blackBuffer, start: 0, count: count]]; h: POINTER TO SrcChunk ¬ LOOPHOLE[ImagerSample.PointerToSamples[buffer: hiliteBuffer, start: 0, count: count]]; unit: CARDINAL ¬ maxSample + 1; -- try to get this in a register BH: UNSAFE PROC [j: [0..8)] RETURNS [WORD] ~ UNCHECKED INLINE { thresh: CARDINAL ~ t[j]; <<-- Next two lines changed to fix IPD AR 3093>> <<-- Changed again (May 27, 1993) - too light at the white end.>> <<-- black: CARDINAL ~ Basics.BITRSHIFT[(thresh+CARDINAL[1])-b[j], bpw-1];>> black: CARDINAL ~ Basics.BITRSHIFT[(thresh)-b[j], bpw-1]; hilite: CARDINAL ~ Basics.BITRSHIFT[(unit-thresh)-h[j], bpw-1]; RETURN [black + Basics.BITOR[black, hilite]] -- just a little tricky }; ImagerSample.GetTileSamples[tile: brick.sampleMap, initIndex: min, buffer: thresholdBuffer, phase: brick.phase]; WHILE residual # 0 DO chunkSize: NAT ~ MIN[pixelsPerBuffer, residual]; bitBufferIndex: NAT ¬ 0; chunkResidual: CARDINAL ¬ chunkSize; WHILE chunkResidual >= 8 DO bitBuffer[bitBufferIndex] ¬ (((((((BH[0]*4)+BH[1])*4+BH[2])*4+BH[3])*4+BH[4])*4+BH[5])*4+BH[6])*4+BH[7]; bitBufferIndex ¬ bitBufferIndex + 1; chunkResidual ¬ chunkResidual - 8; t ¬ t + SIZE[SrcChunk]; b ¬ b + SIZE[SrcChunk]; h ¬ h + SIZE[SrcChunk]; ENDLOOP; IF chunkResidual # 0 THEN { w: CARDINAL ¬ 0; checkedResidual: [0..8) ~ chunkResidual; IF chunkSize # residual THEN ERROR; -- must be last time through FOR i: [0..8) IN [0..checkedResidual) DO w ¬ w*4 + BH[i]; ENDLOOP; bitBuffer[bitBufferIndex] ¬ Basics.BITLSHIFT[w, 16-(checkedResidual*2)]; }; ImagerSample.BasicTransfer[dst: dst, src: bitBufferMap, dstMin: index, size: [s: 1, f: chunkSize]]; index.f ¬ index.f + chunkSize; residual ¬ residual - chunkSize; ENDLOOP; }; src[Action]; ImagerSample.ReleaseScratchSamples[thresholdBuffer]; IF zeroBuffer # NIL THEN ImagerSample.ReleaseScratchSamples[zeroBuffer]; ImagerSample.ReleaseDescriptor[bitBufferMap]; }; HiliteMaskBoxes: PROC [device: Device, bounds: Box, boxes: SF.BoxGenerator] ~ { data: Data ~ NARROW[device.data]; SELECT data.case FROM nil => ERROR; -- color not initialized constant => { ImagerSample.FillBoxes[map: data.sampleMap, boxes: boxes, value: data.constant]; }; tile => { ImagerSample.TileBoxes[map: data.sampleMap, boxes: boxes, tile: data.tile, phase: data.brick.phase]; }; sampled => { Src: PROC [action: PROC[pixels: ImagerPixel.PixelBuffer, min: SF.Vec]] ~ { ImagerPixel.Resample[self: data.source, m: data.paToDevice, interpolate: data.interpolate, boxes: boxes, bounds: bounds, action: action]; }; HiliteHalftone[dst: data.sampleMap, src: Src, bounds: bounds, brick: data.brick]; }; pixelarray => { paBox: Box ~ ImagerTransformation.EasyTransformBox[m: data.paToDevice, x: 0, y: 0, w: data.pixelarray.sSize, h: data.pixelarray.fSize, clip: SF.maxBox]; BoxAction: PROC [box: Box] ~ { ImagerPixelArray.Transfer[pa: data.pixelarray, i: 0, s: box.min.s-paBox.min.s, f: box.min.f-paBox.min.f, dst: data.sampleMap, dstMin: box.min, size: SF.Size[box]]; }; IF SF.Inside[bounds, paBox] THEN boxes[BoxAction] ELSE { bpp: NAT ~ lgTable[ImagerPixelArray.MaxSampleValue[data.pixelarray, 0]+1]; tile: SampleMap ~ ImagerSample.ObtainScratchMap[box: paBox, bitsPerSample: bpp]; ImagerPixelArray.Transfer[pa: data.pixelarray, dst: tile, dstMin: paBox.min, size: SF.Size[paBox]]; ImagerSample.TileBoxes[map: data.sampleMap, boxes: boxes, tile: tile, phase: 0]; ImagerSample.ReleaseScratchMap[tile]; }; }; sampledBlack, clearSampledBlack => { BlackAction: PROC [pixels: ImagerPixel.PixelBuffer, min: SF.Vec] ~ TRUSTED { count: NAT ~ pixels.length; buffer: SampleBuffer ~ pixels[0]; bufferPointer: LONG POINTER TO Basics.RawWords ~ ImagerSample.PointerToSamples[buffer: pixels[0], start: 0, count: count]; SELECT data.case FROM sampledBlack => { white: WORD ~ 0; black: WORD ~ 2; FOR i: NAT IN [0..count) DO <> bufferPointer[i] ¬ IF bufferPointer[i] = 0 THEN white ELSE black; ENDLOOP; ImagerSample.PutSamples[map: data.sampleMap, initIndex: min, buffer: buffer, start: 0, count: count]; }; clearSampledBlack => { black: WORD ~ 2; FOR i: NAT IN [0..count) DO <> bufferPointer[i] ¬ CARDINAL[0] - bufferPointer[i]; ENDLOOP; ImagerSample.PutSamples[map: data.sampleMap, initIndex: min, buffer: buffer, start: 0, count: count, function: [and, complement]]; FOR i: NAT IN [0..count) DO <> bufferPointer[i] ¬ Basics.BITAND[bufferPointer[i], black]; ENDLOOP; ImagerSample.PutSamples[map: data.sampleMap, initIndex: min, buffer: buffer, start: 0, count: count, function: [or, null]]; }; ENDCASE => ERROR; }; ImagerPixel.Resample[self: data.source, m: data.paToDevice, interpolate: FALSE, boxes: boxes, bounds: bounds, action: BlackAction]; }; ENDCASE => ERROR; }; HiliteMaskBitmap: PROC [device: Device, bitmap: SampleMap, delta: SF.Vec, bounds: Box, boxes: SF.BoxGenerator] ~ TRUSTED { data: Data ~ NARROW[device.data]; WITH bitmap SELECT FROM srcRast: RasterSampleMap => { srcBitsPerLine: NAT = srcRast.GetBitsPerLine; IF srcRast.GetBitsPerSample = 1 AND srcBitsPerLine MOD BITS[WORD] = 0 THEN { WITH data.sampleMap SELECT FROM dstRast: RasterSampleMap => { dstBitsPerLine: NAT = dstRast.GetBitsPerLine; dstBitsPerSample: [2..2] = dstRast.GetBitsPerSample; logBitsPerSample: NAT = 1; IF dstBitsPerLine MOD BITS[WORD] = 0 THEN { <> dstBox: Box ~ dstRast.GetBox; srcBox: Box ~ SF.Displace[srcRast.GetBox, delta]; dstWpl: CARDINAL ~ dstBitsPerLine / BITS[WORD]; maskWpl: CARDINAL ~ srcBitsPerLine / BITS[WORD]; FastTransfer: SF.BoxAction ~ TRUSTED { sSize: INTEGER = box.max.s-box.min.s; fSize: INTEGER = box.max.f-box.min.f; IF sSize > 0 AND fSize > 0 THEN { dstBitIndex: CARD ~ dstRast.GetBase.bit + CARD[box.min.s-dstBox.min.s]*dstBitsPerLine + Basics.BITLSHIFT[CARD[box.min.f-dstBox.min.f], logBitsPerSample]; srcBitIndex: CARD ~ srcRast.GetBase.bit + CARD[box.min.s-srcBox.min.s]*srcBitsPerLine + CARD[box.min.f-srcBox.min.f]*1; StoreWithColorMask2[ maskBase: LOOPHOLE[srcRast.GetBase.word], maskOffset: srcBitIndex, maskWpl: maskWpl, dstBase: LOOPHOLE[dstRast.GetBase.word], dstOffset: Basics.BITRSHIFT[dstBitIndex, logBitsPerSample], dstWpl: dstWpl, height: sSize, width: fSize, colorWord: data.colorWord] }; }; IF dstRast.GetBitsPerSample # 2 THEN ERROR; -- should not have happened. boxes[FastTransfer]; RETURN; }; }; ENDCASE; }; }; ENDCASE; ImagerDeviceProcs.DMaskBitmap[device, bitmap, delta, bounds, boxes]; }; HiliteMaskRawBitmaps: <> PROC [device: Device, list: LIST OF ImagerSample.RawDescriptor] ~ TRUSTED { data: Data ~ NARROW[device.data]; rast: RasterSampleMap = NARROW[data.sampleMap]; dstPtr: LONG POINTER TO WORD = LOOPHOLE[rast.GetBase.word]; dstOff: NAT = rast.GetBase.bit; dstBitsPerLine: NAT = rast.GetBitsPerLine; dstWpl: CARDINAL = dstBitsPerLine / BITS[WORD]; dstBitsPerSample: [2..2] = rast.GetBitsPerSample; logBitsPerSample: NAT = 1; colorWord: WORD = data.colorWord; WHILE list # NIL DO r: POINTER TO ImagerSample.RawDescriptor = @(list.first); sSize: INTEGER = list.first.box.max.s-list.first.box.min.s; fSize: INTEGER = list.first.box.max.f-list.first.box.min.f; IF sSize > 0 AND fSize > 0 THEN { dstBitIndex: CARD ~ dstOff + CARD[r.box.min.s-rast.GetBox.min.s]*dstBitsPerLine + Basics.BITLSHIFT[CARD[r.box.min.f-rast.GetBox.min.f], logBitsPerSample]; StoreWithColorMask2[ maskBase: LOOPHOLE[r.basePointer], maskOffset: 0, maskWpl: r.bitsPerLine / BITS[WORD], dstBase: LOOPHOLE[dstPtr], dstOffset: Basics.BITRSHIFT[dstBitIndex, logBitsPerSample], dstWpl: dstWpl, height: sSize, width: fSize, colorWord: colorWord] }; list ¬ list.rest; ENDLOOP; }; ExpandArray2: TYPE = PACKED ARRAY BYTE OF CARD16; expandArray2: REF ExpandArray2 ¬ MakeExpandArray2[]; MakeExpandArray2: PROC RETURNS [REF ExpandArray2] = { new: REF ExpandArray2 ¬ NEW[ExpandArray2 ¬ ALL[0]]; FOR b: BYTE IN BYTE DO elem: WORD ¬ 3; word: WORD ¬ 0; x: BYTE ¬ b; WHILE x # 0 DO IF x MOD 2 = 1 THEN word ¬ word + elem; elem ¬ elem * 4; x ¬ x / 2; ENDLOOP; new[b] ¬ word; ENDLOOP; RETURN [new]; }; StoreWithColorMask2: UNSAFE PROC [ maskBase: RawWordsPtr, maskOffset: CARDINAL, maskWpl: CARDINAL, dstBase: RawWordsPtr, dstOffset: CARDINAL, dstWpl: CARDINAL, height: CARDINAL, width: CARDINAL, colorWord: WORD ] = UNCHECKED { <> IF bpw # 32 -- a compile-time decision, presumably! THEN { MaskWithColor.StoreWithColorMask[ maskBase: LOOPHOLE[maskBase], maskOffset: maskOffset, maskWpl: maskWpl, dstBase: LOOPHOLE[dstBase], dstOffset: dstOffset, dstWpl: dstWpl, height: height, width: width, logDepth: 1, colorWord: colorWord]; } ELSE { <> expand: REF ExpandArray2 ¬ expandArray2; dstMod: BitOffset ¬ (dstOffset ¬ dstOffset*2) MOD bpw; maskMod: BitOffset ¬ maskOffset MOD bpw; tailMask: WORD ¬ Basics.BITNOT[Basics.BITRSHIFT[WORD.LAST, width MOD bpw]]; dstBase ¬ dstBase + (dstOffset / bpw)*SIZE[WORD]; maskBase ¬ maskBase + (maskOffset / bpw)*SIZE[WORD]; IF tailMask = 0 THEN tailMask ¬ WORD.LAST; IF width <= bpw AND maskMod = 0 THEN { <> DO <> maskW: WORD ¬ Basics.BITAND[tailMask, maskBase[0]]; IF maskW # 0 THEN { b0: BYTE = Basics.BITRSHIFT[maskW, 24] MOD 256; b1: BYTE = Basics.BITRSHIFT[maskW, 16] MOD 256; b2: BYTE = Basics.BITRSHIFT[maskW, 8] MOD 256; b3: BYTE = maskW MOD 256; dstMask0: WORD ¬ Basics.BITLSHIFT[expand[b0], 16] + expand[b1]; dstMask1: WORD ¬ Basics.BITLSHIFT[expand[b2], 16] + expand[b3]; dstMask2: WORD ¬ 0; IF dstMod # 0 THEN { dmc: BitOffset ¬ WORD[bpw - dstMod] MOD bpw; dstMask2 ¬ Basics.BITLSHIFT[dstMask1, dmc]; dstMask1 ¬ Basics.BITLSHIFT[dstMask0, dmc] + Basics.BITRSHIFT[dstMask1, dstMod]; dstMask0 ¬ Basics.BITRSHIFT[dstMask0, dstMod]; }; IF dstMask0 # 0 THEN dstBase[0] ¬ Basics.BITOR[ Basics.BITAND[Basics.BITNOT[dstMask0], dstBase[0]], Basics.BITAND[dstMask0, colorWord]]; IF dstMask1 # 0 THEN dstBase[1] ¬ Basics.BITOR[ Basics.BITAND[Basics.BITNOT[dstMask1], dstBase[1]], Basics.BITAND[dstMask1, colorWord]]; IF dstMask2 # 0 THEN dstBase[2] ¬ Basics.BITOR[ Basics.BITAND[Basics.BITNOT[dstMask2], dstBase[2]], Basics.BITAND[dstMask2, colorWord]]; }; IF height = 1 THEN EXIT; maskBase ¬ maskBase + maskWpl*SIZE[WORD]; dstBase ¬ dstBase + dstWpl*SIZE[WORD]; height ¬ height - 1; ENDLOOP; } ELSE { <> maskMask: WORD ¬ Basics.BITNOT[Basics.BITRSHIFT[WORD.LAST, width MOD bpw]]; maskModC: BitOffset = WORD[bpw-maskMod] MOD bpw; DO <> dstPtr: RawWordsPtr ¬ dstBase; maskPtr: RawWordsPtr ¬ maskBase; rem: CARDINAL ¬ width; DO maskW: WORD ¬ maskPtr[0]; -- AR3036 fixed - this line. IF maskMod # 0 THEN { <> maskW ¬ Basics.BITLSHIFT[maskW, maskMod]; IF rem > maskModC THEN <> maskW ¬ maskW + Basics.BITRSHIFT[maskPtr[1], maskModC]; }; IF rem < bpw THEN maskW ¬ Basics.BITAND[maskW, tailMask]; <> IF maskW # 0 THEN { b0: BYTE = Basics.BITRSHIFT[maskW, 24] MOD 256; b1: BYTE = Basics.BITRSHIFT[maskW, 16] MOD 256; b2: BYTE = Basics.BITRSHIFT[maskW, 8] MOD 256; b3: BYTE = maskW MOD 256; dstMask0: WORD ¬ Basics.BITLSHIFT[expand[b0], 16] + expand[b1]; dstMask1: WORD ¬ Basics.BITLSHIFT[expand[b2], 16] + expand[b3]; dstMask2: WORD ¬ 0; IF dstMod # 0 THEN { dmc: BitOffset ¬ WORD[bpw - dstMod] MOD bpw; dstMask2 ¬ Basics.BITLSHIFT[dstMask1, dmc]; dstMask1 ¬ Basics.BITLSHIFT[dstMask0, dmc] + Basics.BITRSHIFT[dstMask1, dstMod]; dstMask0 ¬ Basics.BITRSHIFT[dstMask0, dstMod]; }; IF dstMask0 # 0 THEN dstPtr[0] ¬ Basics.BITOR[ Basics.BITAND[Basics.BITNOT[dstMask0], dstPtr[0]], Basics.BITAND[dstMask0, colorWord]]; IF dstMask1 # 0 THEN dstPtr[1] ¬ Basics.BITOR[ Basics.BITAND[Basics.BITNOT[dstMask1], dstPtr[1]], Basics.BITAND[dstMask1, colorWord]]; IF dstMask2 # 0 THEN dstPtr[2] ¬ Basics.BITOR[ Basics.BITAND[Basics.BITNOT[dstMask2], dstPtr[2]], Basics.BITAND[dstMask2, colorWord]]; }; IF rem <= bpw THEN EXIT; rem ¬ rem - bpw; dstPtr ¬ dstPtr + SIZE[WORD]*2; maskPtr ¬ maskPtr + SIZE[WORD]; ENDLOOP; IF height = 1 THEN EXIT; maskBase ¬ maskBase + maskWpl*SIZE[WORD]; dstBase ¬ dstBase + dstWpl*SIZE[WORD]; height ¬ height - 1; ENDLOOP; }; }; }; lgTable: PACKED ARRAY [0..32] OF BYTE = [ 0, 0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5]; deviceClass: ImagerDevice.DeviceClass ~ NEW[ImagerDevice.DeviceClassRep ¬ [ SetColor: HiliteSetColor, SetPriority: NIL, SetHalftoneProperties: HiliteSetHalftoneProperties, MaskBoxes: HiliteMaskBoxes, MaskBitmap: HiliteMaskBitmap, MaskRawBitmaps: HiliteMaskRawBitmaps, GetBufferColorOperator: HiliteGetBufferColorOperator ]]; contextClass: ImagerPrivate.Class ~ CreateClass[]; CreateClass: PROC RETURNS [class: ImagerPrivate.Class] ~ { class ¬ ImagerRaster.CreateClass[type: classCode]; }; END.