<> <> <> <> DIRECTORY FunctionCache USING [Cache, GlobalCache, CompareProc, Insert, Lookup], ImagerColor, ImagerDeviceColor24Private, ImagerDevice, ImagerRaster USING [], ImagerSample, ImagerTransformation, SF, Terminal USING [FrameBuffer, GetColorFrameBufferA, GetColorFrameBufferB, ModifyColorFrame, Virtual], Vector2 USING [VEC]; ImagerDeviceColor24Impl: CEDAR PROGRAM IMPORTS FunctionCache, ImagerColor, ImagerDevice, ImagerSample, ImagerTransformation, SF, Terminal EXPORTS ImagerRaster ~ BEGIN OPEN ImagerDevice, ImagerDeviceColor24Private; Transformation: TYPE ~ ImagerTransformation.Transformation; PixelProc: TYPE ~ ImagerSample.PixelProc; PixelBuffer: TYPE ~ ImagerSample.PixelBuffer; PixelMap: TYPE ~ ImagerSample.PixelMap; outputRGB: ImagerColor.ColorOutput _ NIL; classColor24: DeviceClass ~ NewClass[ type: $FullColorDisplay, SetColor: SetColorColor24, MaskBoxes: MaskBoxesColor24, MoveBoxes: MoveBoxesColor24 ]; NewColor24Device: PUBLIC PROC [terminal: Terminal.Virtual] RETURNS [Device] ~ { frameBufferA: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferA[terminal]; frameBufferB: Terminal.FrameBuffer ~ Terminal.GetColorFrameBufferB[terminal]; IF frameBufferA=NIL OR frameBufferA.bitsPerPixel#bitsPerPixelA OR frameBufferB=NIL OR frameBufferB.bitsPerPixel#bitsPerPixelB THEN RETURN[NIL] ELSE { frameA: SampleMap ~ ImagerSample.MapFromFrameBuffer[frameBufferA]; frameB: SampleMap ~ ImagerSample.MapFromFrameBuffer[frameBufferB]; data: Data ~ NEW[DataRep _ [terminal: terminal, frameA: frameA, frameB: frameB]]; pixelsPerInch: REAL ~ terminal.colorPixelsPerInch; surfaceToDevice: Transformation ~ ImagerTransformation.XYToSF[ scanMode: [slow: down, fast: right], sSize: frameA.size.s, fSize: frameA.size.f]; TRUSTED { data.frameRG _ ImagerSample.UnsafeNewSampleMap[ size: [s: frameA.size.s, f: frameA.size.f*2], bitsPerSample: frameA.bitsPerSample/2, bitsPerLine: frameA.bitsPerLine, base: frameA.base, ref: frameBufferA.vm, words: frameBufferA.vm.words] }; RETURN[NEW[ImagerDevice.DeviceRep _ [class: classColor24, box: [min: [s: 0, f: 0], max: frameA.size], surfaceToDevice: surfaceToDevice, surfaceUnitsPerInch: [pixelsPerInch, pixelsPerInch], surfaceUnitsPerPixel: 1, data: data]]]; }; }; me: REF TEXT ~ "DeviceColor24"; -- a globally unique REF for use a key in the global cache. SetColorColor24: SetColorProc ~ { data: Data ~ NARROW[self.data]; WITH color SELECT FROM color: ImagerColor.ConstantColor => { maxOut: PixelProc ~ { check: [0..3) ~ i; RETURN[255] }; pixelAction: PROC [pixelOut: PixelProc] ~ { data.valueA _ pixelOut[0]*256+pixelOut[1]; data.valueB _ pixelOut[2]; }; color.PixelFromColor[output: outputRGB, maxOut: maxOut, pixelAction: pixelAction]; data.function _ [null, null]; data.case _ fill; }; < {>> <> <> <> <> <> <> <> <<}>> <> <<};>> color: ImagerColor.SampledColor => { pm: ImagerSample.PixelMap _ NIL; cache: FunctionCache.Cache ~ FunctionCache.GlobalCache[]; compare: FunctionCache.CompareProc ~ { WITH argument SELECT FROM arg: ImagerColor.SampledColor => RETURN[arg.pa=color.pa AND arg.colorOperator=color.colorOperator]; ENDCASE => RETURN[FALSE]; }; WITH cache.Lookup[compare: compare, clientID: me].value SELECT FROM value: ImagerSample.PixelMap => pm _ value; ENDCASE => { maxOut: PixelProc ~ { check: [0..3) ~ i; RETURN[255] }; pm _ ImagerColor.Translate[self: color.colorOperator, output: outputRGB, pa: color.pa, maxOut: maxOut]; IF color.pa.immutable THEN cache.Insert[argument: color, value: pm, size: 3*INT[pm.size.s]*INT[pm.size.f]/2, clientID: me]; }; data.pm _ pm; data.pmToDevice.ApplyCat[color.pa.m, color.um, viewToDevice]; data.case _ sampledColor; }; color: ImagerColor.SpecialColor => SELECT color.type FROM $Invert => { data.valueA _ 377B*400B+377B; data.valueB _ 377B; data.function _ [xor, null]; data.case _ fill; }; <<$Stipple => {>> <> <> <> <> <> <<};>> ENDCASE => SetColorColor24[self, color.substitute, viewToDevice]; ENDCASE => ERROR; -- unknown color variant }; ModifyColorFrame: PROC [data: Data, action: PROC, bounds: Box] ~ INLINE { IF data.terminal=NIL THEN action[] ELSE Terminal.ModifyColorFrame[vt: data.terminal, action: action, xmin: bounds.min.f, ymin: bounds.min.s, xmax: bounds.max.f, ymax: bounds.max.s]; }; interpolate: BOOL _ TRUE; MaskBoxesColor24: MaskBoxesProc ~ { data: Data ~ NARROW[self.data]; actionMaskBoxesColor24: PROC ~ { SELECT data.case FROM nil => ERROR; -- color not initialized fill => { data.frameA.FillBoxes[boxes: boxes, value: data.valueA, function: data.function]; data.frameB.FillBoxes[boxes: boxes, value: data.valueB, function: data.function]; }; tile => { data.frameA.TileBoxes[boxes: boxes, tile: data.tileA, s0: data.s0, f0: data.f0, phase: data.phase, function: data.function]; data.frameB.TileBoxes[boxes: boxes, tile: data.tileB, s0: data.s0, f0: data.f0, phase: data.phase, function: data.function]; }; < {>> <> <> <<};>> <> <<};>> sampledColor => { sampledColorAction: PROC [pixels: PixelBuffer, min: Vec] ~ { s: NAT ~ min.s; f: NAT ~ min.f; data.frameRG.PutSamples[min: [s: s, f: f*2], delta: [s: 0, f: 2], samples: pixels[0], function: data.function]; data.frameRG.PutSamples[min: [s: s, f: f*2+1], delta: [s: 0, f: 2], samples: pixels[1], function: data.function]; data.frameB.PutSamples[min: [s: s, f: f], delta: [s: 0, f: 1], samples: pixels[2], function: data.function]; }; data.pm.Resample[m: data.pmToDevice, interpolate: interpolate, boxes: boxes, bounds: bounds, action: sampledColorAction]; }; ENDCASE => ERROR; -- bogus case }; ModifyColorFrame[data: data, action: actionMaskBoxesColor24, bounds: bounds]; }; MoveBoxesColor24: MoveBoxesProc ~ { data: Data ~ NARROW[self.data]; actionMoveBoxesColor24: PROC ~ { data.frameA.MoveBoxes[boxes: boxes, srcOffset: offset]; data.frameB.MoveBoxes[boxes: boxes, srcOffset: offset]; }; dmin: Vec ~ bounds.min; dmax: Vec ~ bounds.max; smin: Vec ~ dmin.Add[offset]; smax: Vec ~ dmax.Add[offset]; moveBounds: Box ~ [min: dmin.Min[smin], max: dmax.Max[smax]]; ModifyColorFrame[data: data, action: actionMoveBoxesColor24, bounds: moveBounds]; }; <<>> END.