FontTuneImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Michael Plass, November 27, 1985 6:17:44 pm PST
DIRECTORY ImagerRaster, FontTune, Basics, Process, DynamicBits, Imager, ImagerPixelMap, ImagerTransformation, Real, Rope, GridModulation, FontTuningParameters, ImagerPixelSeq, ImagerMaskCapture, ImagerBackdoor, ImagerOps;
FontTuneImpl: CEDAR PROGRAM
IMPORTS Process, DynamicBits, Imager, ImagerPixelMap, ImagerTransformation, Real, Rope, GridModulation, FontTuningParameters, ImagerPixelSeq, ImagerMaskCapture, ImagerBackdoor, ImagerOps
EXPORTS FontTune
~ 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
];
maxBitSize: NAT ← 600;
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: BOOLTRUE;
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: REFNIL;
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];
};
END.