~
BEGIN
PixelMap: TYPE ~ ImagerPixelMap.PixelMap;
DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle;
FontTuner: TYPE ~ ImagerRaster.FontTuner;
Transformation: TYPE ~ ImagerTransformation.Transformation;
Data: TYPE ~ REF DataRep;
DataRep:
TYPE ~
RECORD [
param: FontTuningParameters.Ref,
model: DynamicBits.Model,
runSizeMap: GridModulation.RunSizeMap,
antialiasingFilter: PixelMap,
pmScratch: REF ImagerPixelMap.PixelMapRep ← NIL
];
CreateFontTuner:
PUBLIC
PROC [name: Rope.
ROPE]
RETURNS [FontTuner] ~ {
param: FontTuningParameters.Ref ~ FontTuningParameters.Load[Rope.Concat[name, ".fontTune"]];
printerModel: DynamicBits.PrinterModel ~ {
intensity: REAL ← param.intensity[encoding];
scaledIntensity:
REAL ←
(intensity-minMeanIntensity) / (maxMeanIntensity-minMeanIntensity);
RETURN [Real.RoundLI[scaledIntensity*DynamicBits.Intensity.LAST], Real.RoundLI[param.noiseWeight*param.noisePenalty[encoding]]]
};
minMeanIntensity: REAL ← 9999999999.9;
maxMeanIntensity: REAL ← 0;
FOR c:
NAT
IN [0..param.intensity.length)
DO
minMeanIntensity ← MIN[minMeanIntensity, param.intensity[c]];
maxMeanIntensity ← MAX[maxMeanIntensity, param.intensity[c]];
ENDLOOP;
RETURN [
NEW[ImagerRaster.FontTunerRep ← [
proc: MyFontTunerProc,
data:
NEW[DataRep ← [
param: param,
model: DynamicBits.CreatePrinterModel[param.printerModelNeighborhood, printerModel, param.comparisonKernel],
runSizeMap: GridModulation.SimpleRunSizeMap[maxBitSize, param.gridReductionFactor],
antialiasingFilter: GridModulation.ComputeConvolutionKernel[param.gridReductionFactor]
]],
propList: NIL
]]]
};
MyFontTunerProc: ImagerRaster.FontTunerProc ~ {
[self: ImagerRaster.FontTuner, charProc: PROC [...], context: Imager.Context]
data: Data ~ NARROW[self.data];
param: FontTuningParameters.Ref ~ data.param;
T: Transformation ~ ImagerTransformation.TranslateTo[ImagerBackdoor.GetT[context], [0,0]];
bigT: Transformation ~ ImagerTransformation.PreScale[T, param.gridReductionFactor];
bigChar: PixelMap ~ ImagerMaskCapture.CaptureBitmap[charProc, bigT];
IF bigChar.sSize > maxBitSize OR bigChar.fSize > maxBitSize THEN {charProc[context]}
ELSE {
smallChar: PixelMap ~ ConvertPixelMap[bigChar, data];
pa: Imager.PixelArray ~ ImagerOps.PixelArrayFromPixelMaps[LIST[smallChar], NIL];
mask:
PROC ~ {
Imager.ConcatT[context, ImagerTransformation.Invert[T]];
Imager.MaskPixel[context, pa];
};
metrics:
PROC ~ {
Imager.SetNoImage[context, TRUE];
charProc[context]; -- ugh. Do not need this if the 3.0 font stuff is right.
};
Imager.DoSave[context, mask];
Imager.DoSave[context, metrics];
};
};
VideoInvert:
PROC [pm: PixelMap]
RETURNS [PixelMap] ~ {
pm.Fill[pm.Window, CARDINAL.LAST, [xor, null]];
RETURN [pm];
};
Threshold:
PROC [pm: PixelMap]
RETURNS [PixelMap] ~ {
bb: DeviceRectangle ← pm.Window;
new: PixelMap ← ImagerPixelMap.Create[0, bb];
seq: ImagerPixelSeq.PixelSeq ← ImagerPixelSeq.ObtainScratch[bb.fSize];
FOR s:
INTEGER
IN [bb.sMin..bb.sMin+bb.sSize)
DO
seq.LoadF[s, bb.fMin, bb.fSize, pm];
FOR j:
NAT
IN [0..bb.fSize)
DO
seq[j] ← seq[j] / 128;
ENDLOOP;
seq.StoreF[s, bb.fMin, bb.fSize, new];
ENDLOOP;
ImagerPixelSeq.ReleaseScratch[seq];
RETURN [new];
};
alternating: BOOL ← TRUE;
ConvertPixelMap:
PROC [pixelMap: PixelMap, data: Data]
RETURNS [PixelMap] ~ {
param: FontTuningParameters.Ref ~ data.param;
antialiasingFilter: PixelMap ~ GridModulation.ComputeConvolutionKernel[param.gridReductionFactor];
sScratch: GridModulation.EdgeProjection ← GridModulation.CreateEdgeProjection[TRUE, 0, pixelMap.sSize, param.gridReductionFactor];
fScratch: GridModulation.EdgeProjection ← GridModulation.CreateEdgeProjection[FALSE, 0, pixelMap.fSize, param.gridReductionFactor];
model: DynamicBits.Model ~ data.model;
rawGray: PixelMap ← GridModulation.ConvertGrayPixelMap[
pixelMap: pixelMap,
reductionFactor: param.gridReductionFactor,
param: param.gridParam,
runSizeMap: data.runSizeMap,
kernel: antialiasingFilter,
sScratch: sScratch,
fScratch: fScratch,
pmScratch: data.pmScratch
];
gray: PixelMap ← VideoInvert[DynamicBits.AddBorder[rawGray.Trim[0], 2, 0]];
new: PixelMap ← Threshold[gray];
fixedBits: PixelMap;
scratch: REF ← NIL;
DoTestPass:
PROC [passNumber:
NAT] ~ {
w: DeviceRectangle ~ gray.Window;
swath: DeviceRectangle ← [w.sMin, w.fMin, w.sSize, param.swathWidth];
IF alternating
AND passNumber
MOD 2 = 1
THEN {
FOR f:
INT
DECREASING
IN [w.fMin..w.fMin+w.fSize-param.swathWidth)
DO
swath: DeviceRectangle ← [w.sMin, f, w.sSize, param.swathWidth];
scratch ← DynamicBits.TuneSwath[blurred, new, fixedBits, swath, model, scratch];
Process.CheckForAbort[];
ENDLOOP;
}
ELSE {
FOR f:
INT
IN [w.fMin..w.fMin+w.fSize-param.swathWidth)
DO
swath: DeviceRectangle ← [w.sMin, f, w.sSize, param.swathWidth];
scratch ← DynamicBits.TuneSwath[blurred, new, fixedBits, swath, model, scratch];
Process.CheckForAbort[];
ENDLOOP;
};
};
blurred: PixelMap ← gray.Copy;
DynamicBits.Convolve[blurred, model.kernel, 255];
fixedBits ← DynamicBits.FindFixedBits[blurred, -model.neighborhood.sMin-model.kernel.sOrigin];
FOR i:
INT
IN [0..param.tuningPasses)
DO
DoTestPass[i];
ENDLOOP;
new ← VideoInvert[new];
RETURN [new];
};