~
BEGIN
Device: TYPE ~ ImagerDevice.Device;
PixelArray: TYPE ~ ImagerPixelArray.PixelArray;
Transformation:
TYPE ~ ImagerTransformation.Transformation;
Data: TYPE ~ REF DataRep;
DataRep:
TYPE ~
RECORD [
bitmap: ImagerSample.SampleMap ¬ NIL,
deviceColorData: ImagerPrintColor.DeviceColorData
];
defaultDotShape: REAL ¬ 0.48;
defaultScreenAngle:
ARRAY PrintColor.Toner[black..yellow]
OF
REAL ¬ [
black: 45,
cyan: 75,
magenta: 105,
yellow: 90
];
MakeHalftoneProperties:
PROC [logicalDevice: PrintColor.LogicalDevice, pixelsPerHalftoneDot:
REAL, toners: PrintColor.TonerUniverse]
RETURNS [h: PrintColor.HalftoneProperties ¬
NIL] ~ {
angle: ARRAY PrintColor.Toner[black..yellow] OF REAL ¬ defaultScreenAngle;
IF toners = [black: TRUE] THEN angle[black] ← 45;
FOR t: PrintColor.Toner
IN PrintColor.Toner[black..yellow]
DO
IF toners[t]
THEN {
angle: REAL ~ defaultScreenAngle[t];
brick: ImagerBrick.Brick ~ ImagerBrick.BrickFromDotScreen[pixelsPerDot: pixelsPerHalftoneDot, degrees: angle, shape: defaultDotShape];
h ¬ CONS[[type: NIL, toner: t, brick: brick], h];
};
ENDLOOP;
};
SimpleCreate:
PUBLIC
PROC [deviceSpaceSize:
SF.Vec, scanMode: ImagerTransformation.ScanMode, surfaceUnitsPerInch: Vector2.
VEC, logicalDevice: PrintColor.LogicalDevice, pixelsPerHalftoneDot:
REAL, toners: PrintColor.TonerUniverse]
RETURNS [Imager.Context] ~ {
halftoneProperties: PrintColor.HalftoneProperties ~ MakeHalftoneProperties[logicalDevice, pixelsPerHalftoneDot, toners];
RETURN [Create[deviceSpaceSize: deviceSpaceSize, scanMode: scanMode, surfaceUnitsPerInch: surfaceUnitsPerInch, logicalDevice: logicalDevice, halftoneProperties: halftoneProperties, correction: NIL, interpolate: FALSE]];
};
Create:
PUBLIC
PROC [deviceSpaceSize:
SF.Vec, scanMode: ImagerTransformation.ScanMode, surfaceUnitsPerInch: Vector2.
VEC, logicalDevice: PrintColor.LogicalDevice, halftoneProperties: PrintColor.HalftoneProperties, correction: PrintColor.ColorCorrection, interpolate:
BOOL, fontCacheName:
ATOM ¬ $Print, parameters: ImagerMaskCache.Parameters ¬
NIL]
RETURNS [Imager.Context] ~ {
data: Data ~ NEW[DataRep ¬ [bitmap: NIL, deviceColorData: ImagerPrintColor.NewDeviceColorData[logicalDevice: logicalDevice, halftoneProperties: halftoneProperties, correction: correction, interpolate: interpolate]]];
deviceParm: ImagerDevice.DeviceParm ~ ImagerDevice.MakeDeviceParm[
class: deviceClass,
sSize: deviceSpaceSize.s,
fSize: deviceSpaceSize.f,
scanMode: scanMode,
surfaceUnitsPerInch: surfaceUnitsPerInch,
surfaceUnitsPerPixel: 1,
fontCache: ImagerMaskCache.GetNamedCache[fontCacheName],
parameters: parameters
];
context: Imager.Context ~ ImagerRaster.Create[class: contextClass, deviceClass: deviceClass, deviceParm: deviceParm, data: data, pixelUnits: FALSE];
device: Device ~ ImagerRaster.GetDevice[context];
ImagerPrintColorPrivate.SetDeviceColorControl[data.deviceColorData, ImagerDeviceColor.GetDeviceColorControl[device]];
device.works ¬ worksClass;
ImagerRaster.SetDeviceClipBox[context, [[0,0], [0,0]]];
RETURN [context]
};
IsPrintDevice:
PUBLIC
PROC [device: Device]
RETURNS [
BOOL] ~ {
WITH device.data
SELECT
FROM
data: Data => RETURN [TRUE];
ENDCASE => RETURN [FALSE];
};
GetDeviceBitmap:
PUBLIC
PROC [device: Device]
RETURNS [ImagerSample.SampleMap] ~ {
data: Data ~ NARROW[device.data];
RETURN [data.bitmap]
};
SetDeviceBitmap:
PUBLIC
PROC [device: Device, bitmap: ImagerSample.SampleMap]
RETURNS [clipperNeedsFixing:
BOOL] ~ {
data: Data ~ NARROW[device.data];
box: SF.Box = IF bitmap = NIL THEN [] ELSE ImagerSample.GetBox[bitmap];
clipper: ImagerDevice.DeviceClipper = device.worksState.clipper;
clipperNeedsFixing ¬ NOT SF.Inside[box, device.state.bounds];
device.state.bounds ¬ box;
clipper.clipMask ¬ ImagerManhattan.DestructiveClip[clipper.clipMask, box];
clipper.clipBox ¬ ImagerManhattan.BoundingBox[clipper.clipMask];
data.bitmap ¬ bitmap;
ValidateAllow[device, data];
};
GetColorData:
PUBLIC
PROC [device: Device]
RETURNS [ImagerPrintColor.DeviceColorData] ~ {
data: Data ~ NARROW[device.data];
RETURN [data.deviceColorData]
};
ValidateAllow:
PROC [device: Device, data: Data] ~ {
realRaster: BOOL ~ ISTYPE[data.bitmap, ImagerSample.RasterSampleMap];
device.state.allow ¬ [
unorderedBoxes: (data.deviceColorData.case < sampledColor),
multipleCoverage: (data.deviceColorData.case < sampledColor),
regionFill: data.deviceColorData.case = constant AND realRaster,
bitmap: data.deviceColorData.case = constant,
rawBitmaps: (data.deviceColorData.case = constant AND realRaster)
];
};
SetColorData:
PUBLIC PROC [device: Device, deviceColorData: ImagerPrintColor.DeviceColorData] ~ {
data: Data ~ NARROW[device.data];
data.deviceColorData ¬ data.deviceColorData;
ValidateAllow[device, data];
};
SetBitmap:
PUBLIC PROC [context: Imager.Context, bitmap: ImagerSample.SampleMap] ~ {
device: Device = ImagerRaster.GetDevice[context];
IF SetDeviceBitmap[device, bitmap].clipperNeedsFixing
THEN {
ImagerRaster.SetDeviceClipBox[context, device.state.bounds];
};
};
SetSeparation:
PUBLIC PROC [context: Imager.Context, toner: PrintColor.Toner] ~ {
data: Data ~ NARROW[ImagerRaster.GetDevice[context].data];
ImagerPrintColor.SetSeparation[data.deviceColorData, toner];
};
MySetColor:
PROC [device: Device, color: ImagerColor.Color, viewToDevice: ImagerTransformation.Transformation] ~ {
data: Data ~ NARROW[device.data];
ImagerPrintColor.SetDeviceColorData[data.deviceColorData, color, viewToDevice];
ValidateAllow[device, data];
};
TranslateHalftoneProperties:
PROC [h: ImagerBrick.HalftoneProperties]
RETURNS [PrintColor.HalftoneProperties] ~ {
Eliminate this when PrintColor.HalftoneProperties becomes the same as ImagerBrick.HalftoneProperties
RETURN[
IF h =
NIL
THEN NIL
ELSE
CONS[
[type: h.first.type, toner: LOOPHOLE[h.first.toner], brick: h.first.brick],
TranslateHalftoneProperties[h.rest]]]
};
MySetHalftoneProperties:
PROC [device: Device, halftoneProperties: ImagerBrick.HalftoneProperties] ~ {
data: Data ~ NARROW[device.data];
data.deviceColorData.halftoneProperties ¬ TranslateHalftoneProperties[halftoneProperties];
ImagerPrintColor.SetSeparation[data.deviceColorData, data.deviceColorData.toner]; -- validate deviceColorData.brick
Note that a SetColor should be done before further masking.
};
MyMaskBoxes:
PROC [device: Device, bounds:
SF.Box, boxes:
SF.BoxGenerator] ~ {
data: Data ~ NARROW[device.data];
ImagerPrintColor.MaskBoxes[data.bitmap, data.deviceColorData, bounds, boxes];
};
MyMaskBitmap:
PROC [device: Device, bitmap: ImagerSample.SampleMap, delta:
SF.Vec, bounds:
SF.Box, boxes:
SF.BoxGenerator] ~ {
data: Data ~ NARROW[device.data];
function: ImagerSample.Function ¬ [null, null];
IF data.deviceColorData.case # constant THEN ERROR;
SELECT data.deviceColorData.ink
FROM
nop => RETURN;
set => function ¬ [or, null];
remove => function ¬ [and, complement];
ENDCASE => ERROR;
ImagerSample.TransferBoxes[dst: data.bitmap, src: bitmap, delta: delta, boxes: boxes, function: function];
};
MyMaskRegion:
PROC [device: Device, bounds:
SF.Box, edgeGenerator:
PROC [ImagerSample.EdgeAction]] ~ {
data: Data ~ NARROW[device.data];
value: CARDINAL ¬ 1;
IF data.deviceColorData.case # constant THEN ERROR;
SELECT data.deviceColorData.ink
FROM
nop => RETURN;
set => value ¬ 1;
remove => value ¬ 0;
ENDCASE => ERROR;
ImagerSample.RegionFill[dst: NARROW[data.bitmap], edgeGenerator: edgeGenerator, value: value, function: [null, null]];
};
MyMaskRawBitmaps:
PROC [device: Device, list:
LIST
OF ImagerSample.RawDescriptor] ~ {
data: Data ~ NARROW[device.data];
function: ImagerSample.Function ¬ [null, null];
IF data.deviceColorData.case # constant THEN ERROR;
SELECT data.deviceColorData.ink
FROM
nop => RETURN;
set => function ¬ [or, null];
remove => function ¬ [and, complement];
ENDCASE => ERROR;
ImagerSample.RawListTransfer[dst: NARROW[data.bitmap], src: list, function: function];
};
GetSimpleClip:
PROC [device: Device]
RETURNS [
LIST
OF
SF.Box] ~
INLINE {
Returns a singleton list iff the clipping region is a non-empty rectangle.
b: LIST OF SF.Box ~ device.worksState.clipper.clipMask;
IF b = NIL OR b.rest = NIL THEN RETURN[b] ELSE RETURN [NIL];
};
WMaskPixelArray:
PROC [device: Device, bitmap: PixelArray, clientToDevice: Transformation] ~ {
This is a fast case that avoids the banding steps that happen in the normal path.
clip: LIST OF SF.Box ~ GetSimpleClip[device];
maskToDevice: Transformation ¬ ImagerTransformation.Concat[bitmap.m, clientToDevice];
form: INT ~ maskToDevice.form;
integerTrans: BOOL ~ maskToDevice.integerTrans;
s0: INT ~ maskToDevice.tx;
f0: INT ~ maskToDevice.ty;
ImagerTransformation.Destroy[maskToDevice];
maskToDevice ¬ NIL;
IF form = 3
AND integerTrans
AND clip #
NIL
THEN {
data: Data ~ NARROW[device.data];
IF data.deviceColorData.case = constant
THEN {
function: ImagerSample.Function ¬ [null, null];
SELECT data.deviceColorData.ink
FROM
nop => RETURN;
set => function ¬ [or, null];
remove => function ¬ [and, complement];
ENDCASE;
IF function # [null, null]
THEN {
sMin: INT ~ MAX[clip.first.min.s, s0];
fMin: INT ~ MAX[clip.first.min.f, f0];
sMax: INT ~ MIN[clip.first.max.s, s0 + bitmap.sSize];
fMax: INT ~ MIN[clip.first.max.f, f0 + bitmap.fSize];
IF sMin < sMax
AND fMin < fMax
THEN {
ImagerPixelArray.Transfer[pa: bitmap, i: 0, s: sMin-s0, f: fMin-f0,
dst: data.bitmap, dstMin: [sMin, fMin], size: [sMax-sMin, fMax-fMin],
function: function];
};
RETURN;
};
};
};
ImagerDeviceWorks.StandardMaskPixelArray[device, bitmap, clientToDevice];
};
deviceClass: ImagerDevice.DeviceClass ~
NEW[ImagerDevice.DeviceClassRep ¬ [
SetColor: MySetColor,
SetHalftoneProperties: MySetHalftoneProperties,
MaskBoxes: MyMaskBoxes,
MaskRegion: MyMaskRegion,
MaskBitmap: MyMaskBitmap,
MaskRawBitmaps: MyMaskRawBitmaps
]];
worksClass: ImagerDevice.WorksClass ~
NEW[ImagerDevice.WorksClassRep ¬ [
Clip: ImagerDeviceWorks.StandardClip,
MaskFill: ImagerDeviceWorks.StandardMaskFill,
MaskRectangle: ImagerDeviceWorks.StandardMaskRectangle,
MaskStroke: ImagerDeviceWorks.StandardMaskStroke,
MaskVector: ImagerDeviceWorks.StandardMaskVector,
MaskDashedStroke: ImagerDeviceWorks.StandardMaskDashedStroke,
MaskBitmap: ImagerDeviceWorks.StandardMaskBitmap,
MaskPixelArray: WMaskPixelArray,
MaskBoxes: ImagerDeviceWorks.StandardMaskBoxes,
MaskCharMask: ImagerDeviceWorks.StandardMaskCharMask,
Show: ImagerDeviceWorks.StandardShow
]];
contextClass: ImagerPrivate.Class ~ ImagerRaster.CreateClass[type: $Print];
SetMaskCacheTradeoffs:
PUBLIC
PROC [fontCacheName:
ATOM] ~ {
x: ImagerMaskCache.MaskCache ~ ImagerMaskCache.GetNamedCache[fontCacheName];
changed: BOOL ¬ FALSE;
Val:
PROC [from:
REAL, to:
REAL]
RETURNS [
REAL] ~
INLINE {
IF from # to THEN changed ¬ TRUE;
RETURN [to]
};
Action:
PROC [p: ImagerMaskCache.Parameters] ~ {
p.rasterCost.slope ¬ Val[p.rasterCost.slope, 1];
p.rasterCost.offset ¬ Val[p.rasterCost.offset, 0];
p.runsCost.slope ¬ Val[p.runsCost.slope, 2];
p.runsCost.offset ¬ Val[p.runsCost.offset, 3];
};
ImagerMaskCache.UpdateParameters[x, Action];
IF changed THEN ImagerMaskCache.Flush[x];
};
SetMaskCacheTradeoffs[$Print];