DIRECTORY Atom USING [GetPropFromList], Basics USING [bitsPerWord, LongMult], Imager USING [black, Box, ClassRep, Color, ColorOperator, Context, ContextRep, DoSave, DoSaveAll, Error, MaskBits, MaskRectangle, MaskRectangleI, metersPerInch, PixelArray, SetColor, SetXY, StateRep, Trans, white], ImagerBackdoor USING [Clipper, Visibility], ImagerBackdoorPrivate USING [Class], ImagerBox USING [BoxFromRect], ImagerCache USING [Ref], ImagerDevice USING [CharMask, Class, Device, DeviceBox, RunProc], ImagerFont USING [Font, FontImplRep, XChar, XStringProc], ImagerFontPrivate USING [FontImpl, FontImplRep, MakeFontAtom], ImagerManhattan USING [BoundingBox, Copy, CreateFromBox, Destroy, Polygon], ImagerManhattanExtras USING [DestructiveClip, DestructiveDifference, DestructiveIntersection], ImagerMask USING [RunsFromBits], ImagerPath USING [ArcToConics, ConicToCurves, MapOutline, PathProc], ImagerPixelArray USING [MaxSampleValue, PixelArray, UnsafeGetBits], ImagerPixelMap USING [DeviceRectangle, Intersect], ImagerPrivate USING [Class, ClassRep], ImagerRaster USING [FontTuner], ImagerRasterPrivate USING [AndFlags, CharArrayRep, Data, DataRep, Flags, NotFlags, OrFlags, RasterShow, RasterShowText], ImagerSample USING [GetPointSamples, ObtainScratchBuffer, ReleaseScratchBuffer, Sample, SampleBuffer, Sampler, SamplerRep, SetSamplerIncrements, SetSamplerPosition], ImagerScanConverter USING [BoundingBox, ConvertToManhattanPolygon, ConvertToRuns, CreatePath, DevicePath], ImagerState USING [ChangeFlags, notChanged, State, StateClip, StateClipRectangle, StateClipRectangleI, StateConcatT, StateCorrect, StateCorrectMask, StateCorrectSpace, StateDontCorrect, StateDoSave, StateGetClipper, StateGetColor, StateGetCP, StateGetFont, StateGetInt, StateGetReal, StateGetT, StateMaskUnderline, StateMove, StateRep, StateRotateT, StateScale2T, StateSetClipper, StateSetColor, StateSetCorrectMeasure, StateSetCorrectTolerance, StateSetFont, StateSetGray, StateSetInt, StateSetReal, StateSetSampledBlack, StateSetSampledColor, StateSetT, StateSetXY, StateSetXYRel, StateSpace, StateStartUnderline, StateTranslateT], ImagerStroke USING [PathFromStroke], ImagerTransformation USING [ApplyCat, ApplyPostConcat, ApplyPostTranslate, ApplyPreConcat, ApplyPreTranslate, ApplyTranslateTo, InverseTransform, InverseTransformRectangle, Rectangle, Rotate, Scale, Scale2, Transform, Transformation, TransformationRep], Process USING [CheckForAbort], Real USING [Fix, FScale, LargestNumber, Round], RealFns USING [AlmostEqual, SqRt], Rope USING [ROPE], Vector2 USING [Add, Div, Length, Mul, Square, Sub, VEC], VM USING [AddressForPageNumber, Free, Interval, PagesForWords, SimpleAllocate]; ImagerRasterImpl: CEDAR PROGRAM IMPORTS Atom, Basics, Imager, ImagerBox, ImagerFontPrivate, ImagerManhattan, ImagerManhattanExtras, ImagerMask, ImagerPath, ImagerPixelArray, ImagerPixelMap, ImagerRasterPrivate, ImagerSample, ImagerScanConverter, ImagerState, ImagerStroke, ImagerTransformation, Process, Real, RealFns, Vector2, VM EXPORTS Imager, ImagerBackdoor, ImagerFont, ImagerRaster, ImagerRasterPrivate ~ BEGIN OPEN ImagerState, ImagerRasterPrivate; Class: TYPE ~ ImagerPrivate.Class; ClassRep: PUBLIC TYPE ~ ImagerPrivate.ClassRep; -- export to Imager.ClassRep State: TYPE ~ ImagerState.State; StateRep: PUBLIC TYPE ~ ImagerState.StateRep; -- export to Imager.StateRep ROPE: TYPE ~ Rope.ROPE; CharMask: TYPE ~ ImagerDevice.CharMask; Clipper: TYPE ~ ImagerBackdoor.Clipper; Color: TYPE ~ Imager.Color; ColorOperator: TYPE ~ Imager.ColorOperator; Context: TYPE ~ Imager.Context; Device: TYPE ~ ImagerDevice.Device; DeviceBox: TYPE ~ ImagerDevice.DeviceBox; RunProc: TYPE ~ ImagerDevice.RunProc; DevicePath: TYPE ~ ImagerScanConverter.DevicePath; DeviceRectangle: TYPE ~ ImagerPixelMap.DeviceRectangle; Font: TYPE ~ ImagerFont.Font; ManhattanPolygon: TYPE ~ ImagerManhattan.Polygon; PathProc: TYPE ~ ImagerPath.PathProc; PixelArray: TYPE ~ Imager.PixelArray; Rectangle: TYPE ~ ImagerTransformation.Rectangle; Transformation: TYPE ~ ImagerTransformation.Transformation; TransformationRep: TYPE ~ ImagerTransformation.TransformationRep; VEC: TYPE ~ Vector2.VEC; XChar: TYPE ~ ImagerFont.XChar; XStringProc: TYPE ~ ImagerFont.XStringProc; FontImpl: TYPE ~ ImagerFontPrivate.FontImpl; FontImplRep: PUBLIC TYPE ~ ImagerFontPrivate.FontImplRep; -- export to to ImagerFont DeviceBoxFromDeviceRectangle: PROC [r: DeviceRectangle] RETURNS [DeviceBox] ~ { RETURN[[smin: r.sMin, smax: r.sMin+r.sSize, fmin: r.fMin, fmax: r.fMin+r.fSize]]; }; DeviceRectangleFromDeviceBox: PROC [b: DeviceBox] RETURNS [DeviceRectangle] ~ { RETURN[[sMin: b.smin, fMin: b.fmin, sSize: b.smax-b.smin, fSize: b.fmax-b.fmin]]; }; Create: PUBLIC PROC [device: Device, pixelUnits: BOOL, fontCache: ImagerCache.Ref, rastWeight: REAL, fontTuner: ImagerRaster.FontTuner, class: Class ] RETURNS [Context] ~ { data: Data ~ NEW[DataRep _ [device: device]]; state: State ~ NEW[StateRep _ []]; metersToSurface: VEC ~ device.surfaceUnitsPerInch.Div[Imager.metersPerInch]; data.rastWeight _ rastWeight; data.fontTuner _ fontTuner; data.viewToDevice _ ImagerTransformation.Scale[1]; data.clientToDevice _ ImagerTransformation.Scale[1]; data.maskToDevice _ ImagerTransformation.Scale[1]; data.charToDevice _ ImagerTransformation.Scale[1]; data.fontCache _ fontCache; data.charArray _ NEW[CharArrayRep _ ALL[NIL]]; DViewReset[data]; IF pixelUnits THEN state.T _ ImagerTransformation.Scale[1] ELSE state.T _ ImagerTransformation.Scale2[metersToSurface]; state.color _ Imager.black; IF class=NIL THEN class _ rasterClass; RETURN[NEW[Imager.ContextRep _ [class: class, state: state, data: data]]]; }; CreateClass: PUBLIC PROC [type: ATOM] RETURNS [Class] ~ { class: Class ~ NEW[ClassRep _ rasterClass^]; class.type _ type; RETURN[class]; }; ValidateClientToDevice: PROC [data: Data, state: State] ~ { data.clientToDevice.ApplyCat[state.T, data.viewToDevice]; data.valid.clientToDevice _ TRUE; }; ValidateFontInfo: PROC [data: Data, state: State] ~ { font: Font ~ state.font; IF font = NIL THEN ERROR Imager.Error[[code: $noFont, explanation: "Failed to set font before calling Show."]] ELSE { impl: FontImpl ~ font.impl; IF NOT data.valid.clientToDevice THEN ValidateClientToDevice[data, state]; data.charToDevice^ _ data.clientToDevice^; data.charToDevice.ApplyTranslateTo[[0, 0]]; data.charToDevice.ApplyPreConcat[font.charToClient]; data.fontAtom _ ImagerFontPrivate.MakeFontAtom[impl.typeface, data.charToDevice]; data.valid.fontInfo _ TRUE; }; }; ValidateClientClipper: PROC [data: Data, state: State] ~ { cc: ImagerManhattan.Polygon _ NIL; ImagerManhattan.Destroy[data.clientClipMask]; cc _ ImagerManhattan.Copy[data.viewClipMask]; IF data.specialClipPresent THEN { cc _ ImagerManhattanExtras.DestructiveClip[cc, data.specialClipRect]; }; FOR each: Clipper _ state.clipper, each.rest UNTIL each=NIL DO path: PathProc ~ { ImagerPath.MapOutline[outline: each.first.outline, moveTo: moveTo, lineTo: lineTo, curveTo: curveTo, conicTo: conicTo, arcTo: arcTo] }; this: ManhattanPolygon; data.devicePath _ ImagerScanConverter.CreatePath[path: path, transformation: data.viewToDevice, clipBox: data.viewClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath, clipBox: data.viewClipBox, parityFill: each.first.parity]; cc _ CombineManhattan[cc, this, each.first.exclude]; ImagerManhattan.Destroy[this]; ENDLOOP; data.clientClipMask _ cc; data.clientClipRect _ ImagerManhattan.BoundingBox[cc]; data.clientClipBox _ DeviceBoxFromDeviceRectangle[data.clientClipRect]; data.clientClipBoxOnly _ (cc=NIL OR cc.rest=NIL); data.valid.clientClipper _ TRUE; }; ValidateColor: PROC [data: Data, state: State] ~ { device: Device ~ data.device; device.class.SetColor[device, state.color, data.viewToDevice]; data.valid.deviceColor _ TRUE; }; ValidatePriority: PROC [data: Data, state: State] ~ { device: Device ~ data.device; device.class.SetPriority[device, state.np.priorityImportant#0]; data.valid.devicePriority _ TRUE; }; NoteStateChanges: PUBLIC PROC [data: Data, state: State] ~ { changed: ImagerState.ChangeFlags ~ state.changed; state.changed _ ImagerState.notChanged; IF changed.T THEN data.valid.clientToDevice _ data.valid.fontInfo _ FALSE; IF changed.color THEN data.valid.deviceColor _ FALSE; IF changed.priority THEN data.valid.devicePriority _ FALSE; IF changed.clipper THEN data.valid.clientClipper _ FALSE; IF changed.font THEN data.valid.fontInfo _ FALSE; }; ValidateIfNeeded: PUBLIC PROC [data: Data, state: State, needs: Flags] ~ { fix: Flags ~ AndFlags[needs, NotFlags[data.valid]]; IF fix.clientToDevice THEN ValidateClientToDevice[data, state]; IF fix.deviceColor THEN ValidateColor[data, state]; IF fix.devicePriority THEN ValidatePriority[data, state]; IF fix.clientClipper THEN ValidateClientClipper[data, state]; IF fix.fontInfo THEN ValidateFontInfo[data, state]; IF AndFlags[data.valid, needs]#needs THEN ERROR; }; Validate: PROC [data: Data, state: State, needs: Flags] ~ INLINE { IF state.changed#ImagerState.notChanged THEN NoteStateChanges[data, state]; IF AndFlags[data.valid, needs]#needs THEN ValidateIfNeeded[data, state, needs]; }; Verify: PROC [data: Data, needs: Flags] ~ INLINE { IF AndFlags[data.valid, needs]#needs THEN ERROR; }; runsFromPathNeeds: Flags ~ [clientClipper: TRUE]; RunsFromPath: PROC [data: Data, path: PathProc, parity: BOOL _ FALSE, transformation: Transformation _ NIL, action: PROC [bounds: DeviceBox, runs: PROC[RunProc]] ] ~ { clipBox: DeviceRectangle ~ data.clientClipRect; clipMask: ManhattanPolygon ~ data.clientClipMask; devicePath: DevicePath ~ ImagerScanConverter.CreatePath[path: path, transformation: transformation, clipBox: clipBox, scratch: data.devicePath]; rect: DeviceRectangle ~ ImagerScanConverter.BoundingBox[devicePath]; bounds: DeviceBox ~ DeviceBoxFromDeviceRectangle[rect.Intersect[clipBox]]; runs: PROC [run: RunProc] ~ { IF clipMask#NIL AND clipMask.rest=NIL THEN { ImagerScanConverter.ConvertToRuns[devicePath: devicePath, runProc: run, clipBox: clipMask.first, parityFill: parity]; } ELSE { rem: ManhattanPolygon _ clipMask; clip: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { WHILE rem#NIL AND rem.first.sMin+rem.first.sSize<=sMin DO rem _ rem.rest ENDLOOP; FOR l: LIST OF DeviceRectangle _ rem, l.rest UNTIL l=NIL OR l.first.sMin>sMin DO fMinRun: INTEGER ~ MAX[l.first.fMin, fMin]; fMaxRun: INTEGER ~ MIN[l.first.fMin+l.first.fSize, fMin+fSize]; IF fMaxRun > fMinRun THEN run[sMin, fMinRun, fMaxRun-fMinRun]; ENDLOOP; }; ImagerScanConverter.ConvertToRuns[devicePath: devicePath, runProc: clip, clipBox: clipBox, parityFill: parity]; }; }; Verify[data, runsFromPathNeeds]; Process.CheckForAbort[]; action[bounds, runs]; data.devicePath _ devicePath; }; maskRunsNeeds: Flags ~ OrFlags[runsFromPathNeeds, [deviceColor: TRUE, devicePriority: TRUE]]; MaskRuns: PROC [data: Data, path: PathProc, parity: BOOL _ FALSE, transformation: Transformation _ NIL] ~ { maskRunsAction: PROC [bounds: DeviceBox, runs: PROC[RunProc]] ~ { device: Device ~ data.device; device.class.MaskRuns[device: device, bounds: bounds, runs: runs]; }; Verify[data, maskRunsNeeds]; RunsFromPath[data: data, path: path, parity: parity, transformation: transformation, action: maskRunsAction]; }; ClipBoxToMask: PUBLIC PROC [box: DeviceBox, mask: ManhattanPolygon, action: PROC[DeviceBox]] ~ { FOR list: LIST OF DeviceRectangle _ mask, list.rest UNTIL list=NIL DO c: DeviceBox ~ DeviceBoxFromDeviceRectangle[list.first]; IF box.sminc.smin AND box.fminc.fmin THEN action[[smin: MAX[box.smin, c.smin], smax: MIN[box.smax, c.smax], fmin: MAX[box.fmin, c.fmin], fmax: MIN[box.fmax, c.fmax]]]; ENDLOOP; }; maskNeeds: Flags ~ OrFlags[maskRunsNeeds, [clientToDevice: TRUE]]; RasterMaskFill: PROC [context: Context, path: PathProc, oddWrap: BOOL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; Validate[data, state, maskNeeds]; MaskRuns[data: data, path: path, parity: oddWrap, transformation: data.clientToDevice]; }; }; specialRectangles: BOOL _ TRUE; RasterMaskRectangle: PROC [context: Context, r: Rectangle] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; device: Device ~ data.device; Validate[data, state, maskNeeds]; IF specialRectangles AND data.clientToDevice.form # 0 AND device.class.MaskBoxes#NIL THEN { clientToDevice: Transformation ~ data.clientToDevice; clip: DeviceBox ~ data.clientClipBox; p0: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x, r.y]]; p1: VEC ~ ImagerTransformation.Transform[clientToDevice, [r.x+r.w, r.y+r.h]]; s0: INT _ Real.Fix[MAX[MIN[p0.x+0.5, clip.smax], clip.smin]]; s1: INT _ Real.Fix[MAX[MIN[p1.x+0.5, clip.smax], clip.smin]]; f0: INT _ Real.Fix[MAX[MIN[p0.y+0.5, clip.fmax], clip.fmin]]; f1: INT _ Real.Fix[MAX[MIN[p1.y+0.5, clip.fmax], clip.fmin]]; Process.CheckForAbort[]; IF s1box.smin AND fminbox.fmin THEN { IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MaskBoxes[device: device, bounds: box, boxes: boxes]; }; } ELSE RasterMaskRectangle[context, [x: x, y: y, w: w, h: h]]; }; }; RasterMaskUnderline: PROC [context: Context, dy, h: REAL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { end: VEC ~ state.T.InverseTransform[state.p.cp]; -- current position (client space) start: VEC ~ [state.np.underlineStart, end.y-dy-h]; -- corner of underline (client space) underline: PROC ~ { Imager.SetXY[context, start]; Imager.Trans[context]; Imager.MaskRectangle[context, [0, 0, end.x-start.x, h]]; }; Imager.DoSaveAll[context, underline]; }; }; RasterMaskStroke: PROC [context: Context, path: PathProc, closed: BOOL] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; strokePath: PathProc ~ { ImagerStroke.PathFromStroke[path: path, closed: closed, m: data.clientToDevice, width: state.np.strokeWidth, end: state.np.strokeEnd, joint: state.np.strokeJoint, moveTo: moveTo, lineTo: lineTo, conicTo: conicTo, curveTo: curveTo]; }; Validate[data, state, maskNeeds]; MaskRuns[data: data, path: strokePath]; }; }; RasterMaskVector: PROC [context: Context, p1, p2: VEC] ~ { vectorPath: PathProc ~ { moveTo[p1]; lineTo[p2] }; RasterMaskStroke[context: context, path: vectorPath, closed: FALSE]; }; RasterMaskDashedStroke: PROC [context: Context, path: PathProc, patternLen: NAT, pattern: PROC [i: NAT] RETURNS [REAL], offset, length: REAL] ~ { Bezier: TYPE ~ ARRAY [0..4) OF VEC; RealPlusOrMinus: TYPE ~ RECORD [value: REAL, error: REAL]; nPts: NAT _ 6; AverageSpeed: PROC [p: Bezier] RETURNS [RealPlusOrMinus] ~ { nReal: REAL ~ nPts; min: REAL _ Real.LargestNumber; max: REAL _ 0; delta: ARRAY [0..3) OF VEC ~ [Vector2.Sub[p[1], p[0]], Vector2.Sub[p[2], p[1]], Vector2.Sub[p[3], p[2]]]; FOR i: NAT IN [0..nPts] DO t: REAL ~ i/nReal; s: REAL ~ 1.0-t; d01: VEC _ Vector2.Add[Vector2.Mul[delta[0], t], Vector2.Mul[delta[1], s]]; d12: VEC _ Vector2.Add[Vector2.Mul[delta[1], t], Vector2.Mul[delta[2], s]]; d012: VEC _ Vector2.Add[Vector2.Mul[d01, t], Vector2.Mul[d12, s]]; sqr: REAL ~ Vector2.Square[d012]; IF sqr > max THEN max _ sqr; IF sqr < min THEN min _ sqr; ENDLOOP; max _ RealFns.SqRt[max]; min _ RealFns.SqRt[min]; RETURN [[(max+min)*1.5, (max-min)*1.5]] }; sigBits: NAT _ 5; DivideUntilSmooth: PROC [p: Bezier, piece: PROC [p: Bezier, speed: REAL], fullScale: REAL] ~ { a: RealPlusOrMinus _ AverageSpeed[p]; UNTIL Real.FScale[a.error, sigBits] <= fullScale DO Mid: PROC [a,b: VEC] RETURNS [VEC] ~ INLINE {RETURN [[Real.FScale[a.x+b.x, -1], Real.FScale[a.y+b.y, -1]]]}; p01: VEC ~ Mid[p[0],p[1]]; p12: VEC ~ Mid[p[1],p[2]]; p23: VEC ~ Mid[p[2],p[3]]; p012: VEC ~ Mid[p01,p12]; p123: VEC ~ Mid[p12,p23]; p0123: VEC ~ Mid[p012,p123]; DivideUntilSmooth[[p[0], p01, p012, p0123], piece, fullScale]; p _ [p0123, p123, p23, p[3]]; a _ AverageSpeed[p]; ENDLOOP; piece[p, a.value]; }; SubDivide: PROC [b: Bezier, t: REAL, hi: BOOL] RETURNS [Bezier] ~ { s: REAL ~ 1-t; Interpolate: PROC [a, b: VEC] RETURNS [VEC] ~ INLINE { RETURN [[a.x*s+b.x*t, a.y*s+b.y*t]] }; q1 : VEC ~ Interpolate[b[0], b[1]]; q2: VEC ~ Interpolate[b[1], b[2]]; q3: VEC ~ Interpolate[b[2], b[3]]; qp1: VEC ~ Interpolate[q1, q2]; qp2: VEC ~ Interpolate[q2, q3]; q: VEC ~ Interpolate[qp1, qp2]; RETURN [IF hi THEN [q, qp2, q3, b[3]] ELSE [b[0], q1, qp1, q]] }; SubPiece: PROC [b: Bezier, t0, t1: REAL] RETURNS [Bezier] ~ { IF t1 # 1.0 THEN { b _ SubDivide[b, t1, FALSE]; t0 _ t0/t1; t1 _ 1; }; IF t0 # 0.0 THEN b _ SubDivide[b, t0, TRUE]; RETURN [b]; }; MeasurePath: PROC [path: PathProc] RETURNS [sum: REAL _ 0.0] ~ { lp: VEC _ [0,0]; move: PROC [p0: VEC] ~ { lp _ p0; }; line: PROC [p1: VEC] ~ { sum _ sum + Vector2.Length[Vector2.Sub[p1, lp]]; lp _ p1; }; piece: PROC [p: Bezier, speed: REAL] ~ { sum _ sum + speed; }; curve: PROC [p1, p2, p3: VEC] ~ TRUSTED { p: Bezier ~ [lp, p1, p2, p3]; fullScale: REAL ~ AverageSpeed[p].value; IF fullScale > 0.0 THEN DivideUntilSmooth[p, piece, fullScale]; lp _ p3; }; conic: PROC [p1, p2: VEC, r: REAL] ~ { ImagerPath.ConicToCurves[lp, p1, p2, r, curve] }; arc: PROC [p1, p2: VEC] ~ {ImagerPath.ArcToConics[lp, p1, p2, conic]}; path[moveTo: move, lineTo: line, curveTo: curve, conicTo: conic, arcTo: arc]; }; pathUnits: REAL ~ MeasurePath[path]; stretch: REAL ~ IF length<=0.0 OR pathUnits=0.0 THEN 1.0 ELSE pathUnits/length; lp: VEC _ [0,0]; index: INT _ 0; -- index of currently active pattern element. residual: REAL _ pattern[0]*stretch; -- remaining part of current pattern element, in master units. used: REAL _ 0.0; -- amount of pattern used, in master units. on: BOOL _ TRUE; Advance: PROC [patternOffset: REAL, startAction, stopAction: PROC _ NIL] ~ { UNTIL used >= patternOffset DO IF residual > 0.0 THEN { IF used+residual <= patternOffset THEN {used _ used+residual; residual _ 0.0} ELSE {residual _ used+residual - patternOffset; used _ patternOffset}; } ELSE { IF on AND stopAction#NIL THEN stopAction[]; index _ index + 1; IF index = patternLen THEN index _ 0; residual _ pattern[index]*stretch; on _ NOT on; IF on AND startAction#NIL THEN startAction[]; }; ENDLOOP; }; dashedPath: PathProc ~ { move: PROC [p0: VEC] ~ {lp _ p0; IF on THEN moveTo[lp]}; line: PROC [p1: VEC] ~ { delta: VEC ~ Vector2.Sub[p1, lp]; d: REAL ~ Vector2.Length[delta]; segmentStart: REAL ~ used; segmentEnd: REAL ~ used + d; start: PROC ~ { s: REAL _ (used-segmentStart)/d; moveTo[Vector2.Add[lp, Vector2.Mul[delta, s]]]; }; stop: PROC ~ { s: REAL _ (used-segmentStart)/d; lineTo[Vector2.Add[lp, Vector2.Mul[delta, s]]]; }; Advance[segmentEnd, start, stop]; IF on THEN lineTo[p1]; lp _ p1; }; curve: PROC [p1, p2, p3: VEC] ~ { piece: PROC [p: Bezier, speed: REAL] ~ { segmentStart: REAL ~ used; segmentEnd: REAL ~ used + speed; dashStartParam: REAL _ 0; needMove: BOOL _ FALSE; start: PROC ~ { dashStartParam _ (used-segmentStart)/speed; needMove _ TRUE; }; stop: PROC ~ { dashEndParam: REAL _ (used-segmentStart)/speed; b: Bezier _ SubPiece[p, dashStartParam, dashEndParam]; IF needMove THEN moveTo[b[0]]; curveTo[b[1], b[2], b[3]]; needMove _ FALSE; }; Advance[segmentEnd, start, stop]; IF on THEN stop[]; }; p: Bezier ~ [lp, p1, p2, p3]; fullScale: REAL ~ AverageSpeed[p].value; IF fullScale > 0.0 THEN DivideUntilSmooth[p, piece, fullScale]; lp _ p3; }; conic: PROC [p1, p2: VEC, r: REAL] ~ { ImagerPath.ConicToCurves[lp, p1, p2, r, curve] }; arc: PROC [p1, p2: VEC] ~ {ImagerPath.ArcToConics[lp, p1, p2, conic]}; path[moveTo: move, lineTo: line, curveTo: curve, conicTo: conic, arcTo: arc]; }; Advance[offset*stretch]; RasterMaskStroke[context, dashedPath, FALSE]; }; bitsPerWord: NAT ~ Basics.bitsPerWord; SMul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ INLINE {RETURN [IF a#0 THEN Smul[a,b] ELSE 0]}; Smul: PROC [a: INTEGER, b: INTEGER] RETURNS [INT] ~ { HighBit: PROC [a: INTEGER] RETURNS [[0..1]] ~ INLINE { RETURN [LOOPHOLE[a, PACKED ARRAY [0..Basics.bitsPerWord) OF [0..1]][0]]; }; Card: PROC [i: INTEGER] RETURNS [NAT] ~ INLINE {RETURN [LOOPHOLE[i]]}; SELECT HighBit[a]*2+HighBit[b] FROM 0 => RETURN [Basics.LongMult[Card[a], Card[b]]]; 1 => RETURN [-Basics.LongMult[Card[a], Card[-b]]]; 2 => RETURN [-Basics.LongMult[Card[-a], Card[b]]]; 3 => RETURN [Basics.LongMult[Card[-a], Card[-b]]]; ENDCASE => ERROR; }; worryReal: REAL _ LAST[NAT]*0.5; IsAllInteger: PROC [m: Transformation] RETURNS [BOOL] ~ { Is: PROC [r: REAL] RETURNS [BOOL] ~ { IF r IN [-worryReal..worryReal] THEN { ir: INT ~ Real.Round[r]; IF r = ir THEN RETURN [TRUE]; IF RealFns.AlmostEqual[r, Real.Round[r], -18] THEN RETURN [TRUE]; }; RETURN [FALSE]; }; RETURN [Is[m.a] AND Is[m.b] AND Is[m.c] AND Is[m.d] AND Is[m.e] AND Is[m.f]]; }; Ceiling: PROC [r: REAL] RETURNS [i: INT] ~ { i _ Real.Round[r]; IF i < r THEN i _ i + 1; }; SF: TYPE ~ RECORD [s, f: INT]; ClippedBounds: PROC [clipBox: DeviceBox, p0, p1: SF] RETURNS [DeviceBox] ~ { IF p0.s > p1.s THEN {t: INT _ p0.s; p0.s _ p1.s; p1.s _ t}; IF p0.f > p1.f THEN {t: INT _ p0.f; p0.f _ p1.f; p1.f _ t}; p0.s _ MAX[p0.s, clipBox.smin]; p0.f _ MAX[p0.f, clipBox.fmin]; p1.s _ MIN[p1.s, clipBox.smax]; p1.f _ MIN[p1.f, clipBox.fmax]; IF p0.s < p1.s AND p0.f < p1.f THEN RETURN [[smin: p0.s, fmin: p0.f, smax: p1.s, fmax: p1.f]] ELSE RETURN [[smin: clipBox.smin, fmin: clipBox.fmin, smax: clipBox.smin, fmax: clipBox.fmin]]; }; specialMaskBits: BOOL _ TRUE; MaskSampledBits: PROC [data: Data, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT, sSize, fSize: NAT, maskToDevice: Transformation, constant: BOOL] ~ { device: Device ~ data.device; SELECT TRUE FROM maskToDevice.form=3 AND maskToDevice.integerTrans AND device.class.MaskBits#NIL => { smin: INT ~ maskToDevice.tx; smax: INT ~ smin+sSize; fmin: INT ~ maskToDevice.ty; fmax: INT ~ fmin+fSize; box: DeviceBox _ data.clientClipBox; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.clientClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MaskBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine, ts: smin-sMin, tf: fmin-fMin, boxes: boxes]; }; }; specialMaskBits AND maskToDevice.form#0 AND IsAllInteger[maskToDevice] AND constant AND device.class.MaskBoxes#NIL => { a: INTEGER ~ Real.Round[maskToDevice.a]; b: INTEGER ~ Real.Round[maskToDevice.b]; c: INTEGER ~ maskToDevice.tx; d: INTEGER ~ Real.Round[maskToDevice.d]; e: INTEGER ~ Real.Round[maskToDevice.e]; f: INTEGER ~ maskToDevice.ty; SF: TYPE ~ RECORD [s, f: INT]; Map: PROC [x, y: INTEGER] RETURNS [SF] ~ { RETURN [[SMul[a, x] + SMul[b, y] + c, SMul[d, x] + SMul[e, y] + f]]; }; box: DeviceBox ~ ClippedBounds[data.clientClipBox, [c, f], Map[sSize, fSize]]; boxes: PROC[action: PROC[DeviceBox]] ~ { myAction: PROC[clipBox: DeviceBox] ~ { run: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { clippedBox: DeviceBox ~ ClippedBounds[clipBox, Map[sMin, fMin], Map[sMin+1, fMin+fSize]]; IF clippedBox.smin # clippedBox.smax THEN action[clippedBox]; }; sb: Imager.Box ~ ImagerBox.BoxFromRect[ ImagerTransformation.InverseTransformRectangle[maskToDevice, [ x: clipBox.smin, y: clipBox.fmin, w: clipBox.smax-clipBox.smin, h: clipBox.fmax-clipBox.fmin ]] ]; srcBox: DeviceBox ~ ClippedBounds[ clipBox: [smin: 0, fmin: 0, smax: sSize, fmax: fSize], p0: [Real.Fix[sb.xmin], Real.Fix[sb.ymin]], p1: [Ceiling[sb.xmax], Ceiling[sb.ymax]] ]; ImagerMask.RunsFromBits[base: base, wordsPerLine: wordsPerLine, sBits: srcBox.smin+sMin, fBits: srcBox.fmin+fMin, sSize: srcBox.smax-srcBox.smin, fSize: srcBox.fmax-srcBox.fmin, sRuns: srcBox.smin, fRuns: srcBox.fmin, run: run]; }; ClipBoxToMask[box: box, mask: data.clientClipMask, action: myAction]; }; device.class.MaskBoxes[device: device, bounds: box, boxes: boxes]; }; ENDCASE => HardMaskSampledBits[data, base, wordsPerLine, sMin, fMin, sSize, fSize, maskToDevice]; }; HardMaskSampledBits: PROC [data: Data, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT, sSize, fSize: NAT, maskToDevice: Transformation] ~ { sampler: ImagerSample.Sampler ~ NEW[ImagerSample.SamplerRep _ []]; maskBitsAction: PROC [bounds: DeviceBox, runs: PROC [RunProc]] ~ { device: Device ~ data.device; buffer: ImagerSample.SampleBuffer ~ ImagerSample.ObtainScratchBuffer[ iSize: 1, jSize: bounds.fmax-bounds.fmin]; maskBitsRuns: PROC [run: RunProc] ~ { maskBitsRun: PROC [sMin, fMin: INTEGER, fSize: NAT] ~ { state: {off, on} _ off; f0: NAT _ 0; ImagerSample.GetPointSamples[sampler: sampler, s: sMin, f: fMin, buffer: buffer, bi: 0, bj: 0, count: fSize]; FOR f: NAT IN[0..fSize) DO x: ImagerSample.Sample ~ buffer[f]; SELECT state FROM off => IF x#0 THEN { f0 _ f; state _ on }; on => IF x=0 THEN { run[sMin: sMin, fMin: fMin+f0, fSize: f-f0]; state _ off }; ENDCASE => ERROR; ENDLOOP; IF state=on THEN run[sMin: sMin, fMin: fMin+f0, fSize: fSize-f0]; }; runs[maskBitsRun]; }; ImagerSample.SetSamplerPosition[sampler: sampler, m: maskToDevice, s: bounds.smin, f: bounds.fmin]; device.class.MaskRuns[device: device, bounds: bounds, runs: maskBitsRuns]; ImagerSample.ReleaseScratchBuffer[buffer]; }; maskBoundary: PathProc ~ { moveTo[[0, 0]]; lineTo[[sSize, 0]]; lineTo[[sSize, fSize]]; lineTo[[0, fSize]]; }; sampler.base _ base; sampler.wordsPerLine _ wordsPerLine; sampler.bitsPerSample _ 1; sampler.sMin _ sMin; sampler.fMin _ fMin; sampler.sSize _ sSize; sampler.fSize _ fSize; ImagerSample.SetSamplerIncrements[sampler, maskToDevice]; RunsFromPath[data: data, path: maskBoundary, transformation: maskToDevice, action: maskBitsAction]; }; bitsToClient: Transformation ~ ImagerTransformation.Rotate[-90]; RasterMaskBits: PROC [context: Context, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT _ 0, sSize, fSize: NAT, tx, ty: INTEGER] ~ { state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; maskToDevice: Transformation ~ data.maskToDevice; Validate[data, state, maskNeeds]; maskToDevice^ _ bitsToClient^; maskToDevice.ApplyPostTranslate[[tx, ty]]; maskToDevice.ApplyPostConcat[data.clientToDevice]; MaskSampledBits[data: data, base: base, wordsPerLine: wordsPerLine, sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize, maskToDevice: maskToDevice, constant: state.color.tag = constant]; }; }; RasterDrawBits: PROC [context: Context, base: LONG POINTER, wordsPerLine: NAT, sMin, fMin: NAT _ 0, sSize, fSize: NAT, tx, ty: INTEGER] ~ { drawBitsAction: PROC ~ { Imager.SetColor[context: context, color: Imager.white]; Imager.MaskRectangleI[context: context, x: tx, y: ty, w: fSize, h: -sSize]; Imager.SetColor[context: context, color: Imager.black]; Imager.MaskBits[context: context, base: base, wordsPerLine: wordsPerLine, sMin: sMin, fMin: fMin, sSize: sSize, fSize: fSize, tx: tx, ty: ty]; }; state: State ~ context.state; IF state.np.noImage=0 THEN { data: Data ~ NARROW[context.data]; maskToDevice: Transformation ~ data.maskToDevice; Validate[data, state, maskNeeds]; maskToDevice^ _ bitsToClient^; maskToDevice.ApplyPostTranslate[[tx, ty]]; maskToDevice.ApplyPostConcat[data.clientToDevice]; IF maskToDevice.form=3 AND maskToDevice.integerTrans AND data.device.class.DrawBits # NIL THEN { smin: INT ~ maskToDevice.tx; smax: INT ~ smin+sSize; fmin: INT ~ maskToDevice.ty; fmax: INT ~ fmin+fSize; box: DeviceBox _ data.clientClipBox; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.clientClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { device: Device ~ data.device; IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.DrawBits[device: device, srcBase: base, srcWordsPerLine: wordsPerLine, ts: smin-sMin, tf: fmin-fMin, boxes: boxes]; }; } ELSE Imager.DoSave[context, drawBitsAction]; }; }; RasterMaskPixel: PROC [context: Context, pa: PixelArray] ~ { state: State ~ context.state; IF pa.samplesPerPixel=1 THEN NULL ELSE ERROR Imager.Error[[code: $illegalPixelMask, explanation: "MaskPixel argument must have one sample per pixel."]]; IF ImagerPixelArray.MaxSampleValue[pa, 0]=1 THEN NULL ELSE ERROR Imager.Error[[code: $illegalPixelMask, explanation: "MaskPixel argument must have one bit per sample."]]; IF state.np.noImage=0 THEN TRUSTED { rast: NAT ~ (pa.fSize+bitsPerWord)/bitsPerWord; words: INT ~ Basics.LongMult[pa.sSize, rast]; vm: VM.Interval ~ VM.SimpleAllocate[VM.PagesForWords[words]]; pointer: LONG POINTER ~ VM.AddressForPageNumber[vm.page]; DoMask: UNSAFE PROC ~ { data: Data ~ NARROW[context.data]; maskToDevice: Transformation ~ data.maskToDevice; Validate[data, state, maskNeeds]; maskToDevice.ApplyCat[pa.m, data.clientToDevice]; ImagerPixelArray.UnsafeGetBits[pa: pa, i: 0, s: 0, f: 0, dst: [word: pointer, bit: 0], dstBpl: rast*bitsPerWord, width: pa.fSize, height: pa.sSize]; MaskSampledBits[data: data, base: pointer, wordsPerLine: rast, sMin: 0, fMin: 0, sSize: pa.sSize, fSize: pa.fSize, maskToDevice: maskToDevice, constant: state.color.tag = constant]; }; DoMask[ ! UNWIND => VM.Free[vm]]; VM.Free[vm]; }; }; RasterGetBounds: PROC [context: Context] RETURNS [Rectangle] ~ { state: State ~ context.state; data: Data ~ NARROW[context.data]; Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]]; RETURN[data.clientToDevice.InverseTransformRectangle[[ x: data.clientClipRect.sMin, y: data.clientClipRect.fMin, w: data.clientClipRect.sSize, h: data.clientClipRect.fSize ]]]; }; DViewReset: PROC [data: Data] ~ { data.viewToDevice^ _ data.device.surfaceToDevice^; data.viewClipBox _ DeviceRectangleFromDeviceBox[data.device.box]; ImagerManhattan.Destroy[data.viewClipMask]; data.viewClipMask _ ImagerManhattan.CreateFromBox[data.viewClipBox]; data.valid.clientToDevice _ FALSE; data.valid.clientClipper _ FALSE; data.valid.fontInfo _ FALSE; data.valid.deviceColor _ FALSE; }; DViewTranslateI: PROC [data: Data, x, y: INTEGER] ~ { data.viewToDevice.ApplyPreTranslate[[x, y]]; data.valid.clientToDevice _ FALSE; data.valid.clientClipper _ FALSE; data.valid.deviceColor _ FALSE; }; DViewClipRectangleI: PROC [data: Data, x, y, w, h: INTEGER, exclude: BOOL] ~ { viewToDevice: Transformation ~ data.viewToDevice; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { old: ManhattanPolygon ~ data.viewClipMask; smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h]; fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w]; rect: DeviceRectangle ~ [sMin: smin, fMin: fmin, sSize: ABS[h], fSize: ABS[w]]; IF exclude THEN { t: ManhattanPolygon ~ ImagerManhattan.CreateFromBox[rect]; data.viewClipMask _ ImagerManhattanExtras.DestructiveDifference[data.viewClipMask, t]; ImagerManhattan.Destroy[t]; } ELSE data.viewClipMask _ ImagerManhattanExtras.DestructiveClip[data.viewClipMask, rect]; data.viewClipBox _ ImagerManhattan.BoundingBox[data.viewClipMask]; data.valid.clientClipper _ FALSE; } ELSE { path: PathProc ~ { moveTo[[x, y]]; lineTo[[x+w, y]]; lineTo[[x+w, y+h]]; lineTo[[x, y+h]]; }; DViewClip[data: data, path: path, parity: FALSE, exclude: exclude]; }; }; CombineManhattan: PROC [a, b: ManhattanPolygon, exclude: BOOL] RETURNS [ManhattanPolygon] ~ { IF exclude THEN RETURN [ImagerManhattanExtras.DestructiveDifference[a, b]] ELSE RETURN [ImagerManhattanExtras.DestructiveIntersection[a, b]]; }; DViewClip: PROC [data: Data, path: PathProc, parity: BOOL, exclude: BOOL] ~ { this: ManhattanPolygon; data.devicePath _ ImagerScanConverter.CreatePath[path: path, transformation: data.viewToDevice, clipBox: data.viewClipBox, scratch: data.devicePath]; this _ ImagerScanConverter.ConvertToManhattanPolygon[devicePath: data.devicePath, clipBox: data.viewClipBox, parityFill: parity]; data.viewClipMask _ CombineManhattan[data.viewClipMask, this, exclude]; ImagerManhattan.Destroy[this]; data.viewClipBox _ ImagerManhattan.BoundingBox[data.viewClipMask]; data.valid.clientClipper _ FALSE; }; GetViewerClass: PROC [context: Context] RETURNS [ImagerBackdoorPrivate.Class] ~ { class: Class ~ context.class; val: REF ~ Atom.GetPropFromList[class.propList, $viewOperations]; WITH val SELECT FROM x: ImagerBackdoorPrivate.Class => RETURN[x]; ENDCASE => RETURN[NIL]; }; ViewReset: PUBLIC PROC [context: Context] ~ { WITH context.data SELECT FROM data: Data => DViewReset[data]; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN viewerClass.ViewReset[context] ELSE ERROR Imager.Error[[$unimplemented, "ViewReset not implemented"]]; }; }; ViewTranslateI: PUBLIC PROC [context: Context, x, y: INTEGER] ~ { WITH context.data SELECT FROM data: Data => DViewTranslateI[data, x, y]; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN viewerClass.ViewTranslateI[context, x, y] ELSE ERROR Imager.Error[[$unimplemented, "ViewTranslate not implemented"]]; }; }; ViewClip: PUBLIC PROC [context: Context, path: PathProc, parity: BOOL, exclude: BOOL] ~ { WITH context.data SELECT FROM data: Data => DViewClip[data: data, path: path, parity: parity, exclude: exclude]; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN viewerClass.ViewClip[context: context, path: path, parity: parity, exclude: exclude] ELSE ERROR Imager.Error[[$unimplemented, "ViewClip not implemented"]]; }; }; ViewClipRectangleI: PUBLIC PROC [context: Context, x, y, w, h: INTEGER, exclude: BOOL] ~ { WITH context.data SELECT FROM data: Data => DViewClipRectangleI[data: data, x: x, y: y, w: w, h: h, exclude: exclude]; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN viewerClass.ViewClipRectangleI[context: context, x: x, y: y, w: w, h: h, exclude: exclude] ELSE ERROR Imager.Error[[$unimplemented, "ViewClipRectangleI not implemented"]]; }; }; ViewFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { state: State ~ context.state; IF state#NIL THEN RETURN[state.T.Transform[p]] ELSE { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN RETURN[viewerClass.ViewFromClient[context: context, p: p]] }; ERROR Imager.Error[[$unimplemented, "ViewFromClient not implemented"]]; }; ClientFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { state: State ~ context.state; IF state#NIL THEN RETURN[state.T.InverseTransform[p]] ELSE { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN RETURN[viewerClass.ClientFromView[context: context, p: p]] }; ERROR Imager.Error[[$unimplemented, "ClientFromView not implemented"]]; }; DeviceFromView: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => RETURN[data.viewToDevice.Transform[p]]; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN RETURN[viewerClass.DeviceFromView[context: context, p: p]] ELSE ERROR Imager.Error[[$unimplemented, "DeviceFromView not implemented"]]; }; }; ViewFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => RETURN[data.viewToDevice.InverseTransform[p]]; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN RETURN[viewerClass.ViewFromDevice[context: context, p: p]] ELSE ERROR Imager.Error[[$unimplemented, "ViewFromDevice not implemented"]]; }; }; DeviceFromClient: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => { Validate[data, context.state, [clientToDevice: TRUE]]; RETURN[data.clientToDevice.Transform[p]]; }; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN RETURN[viewerClass.DeviceFromClient[context: context, p: p]] ELSE ERROR Imager.Error[[$unimplemented, "DeviceFromClient not implemented"]]; }; }; ClientFromDevice: PUBLIC PROC [context: Context, p: VEC] RETURNS [VEC] ~ { WITH context.data SELECT FROM data: Data => { Validate[data, context.state, [clientToDevice: TRUE]]; RETURN[data.clientToDevice.InverseTransform[p]]; }; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN RETURN[viewerClass.ClientFromDevice[context: context, p: p]] ELSE ERROR Imager.Error[[$unimplemented, "ClientFromDevice not implemented"]]; }; }; MoveViewRectangle: PUBLIC PROC [context: Context, width, height, fromX, fromY, toX, toY: INTEGER] ~ { WITH context.data SELECT FROM data: Data => { state: State ~ context.state; Validate[data, state, maskNeeds]; DMoveViewRectangle[data: data, width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY]; }; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN viewerClass.MoveViewRectangle[context: context, width: width, height: height, fromX: fromX, fromY: fromY, toX: toX, toY: toY] ELSE ERROR Imager.Error[[$unimplemented, "MoveViewRectangle not implemented"]]; }; }; DMoveViewRectangle: PROC [data: Data, width, height, fromX, fromY, toX, toY: INTEGER] ~ { viewToDevice: Transformation ~ data.viewToDevice; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { smax: INT _ INT[viewToDevice.tx]-toY; smin: INT _ smax-height; fmin: INT _ INT[viewToDevice.ty]+toX; fmax: INT _ fmin+width; box: DeviceBox _ DeviceBoxFromDeviceRectangle[data.viewClipBox]; boxes: PROC[action: PROC[DeviceBox]] ~ { ClipBoxToMask[box: box, mask: data.viewClipMask, action: action]; }; IF sminbox.smin AND fminbox.fmin THEN { device: Device ~ data.device; IF box.sminsmax THEN box.smax _ smax; IF box.fminfmax THEN box.fmax _ fmax; device.class.MoveBoxes[device: device, ts: fromY-toY, tf: toX-fromX, boxes: boxes]; }; } ELSE ERROR; -- hard case }; Visibility: TYPE ~ ImagerBackdoor.Visibility; TestViewRectangle: PUBLIC PROC [context: Imager.Context, x, y, w, h: INTEGER] RETURNS [Visibility] ~ { WITH context.data SELECT FROM data: Data => { state: State ~ context.state; Validate[data, state, [clientToDevice: TRUE, clientClipper: TRUE]]; RETURN[DTestViewRectangle[data: data, x: x, y: y, w: w, h: h]]; }; ENDCASE => { viewerClass: ImagerBackdoorPrivate.Class ~ GetViewerClass[context]; IF viewerClass#NIL THEN RETURN[viewerClass.TestViewRectangle[context: context, x: x, y: y, w: w, h: h]] ELSE ERROR Imager.Error[[$unimplemented, "TestRectangleI not implemented"]]; }; }; DTestViewRectangle: PROC [data: Data, x, y, w, h: INTEGER] RETURNS [Visibility] ~ { viewToDevice: Transformation ~ data.viewToDevice; viewClipMask: ManhattanPolygon ~ data.viewClipMask; IF viewClipMask=NIL THEN RETURN[none]; IF viewToDevice.form=9 AND viewToDevice.integerTrans THEN { clip: DeviceRectangle ~ data.viewClipBox; csmin: INTEGER ~ clip.sMin; csmax: INTEGER ~ csmin+clip.sSize; cfmin: INTEGER ~ clip.fMin; cfmax: INTEGER ~ cfmin+clip.fSize; smin: INTEGER ~ viewToDevice.tx-MAX[y, y+h]; smax: INTEGER ~ smin+ABS[h]; fmin: INTEGER ~ viewToDevice.ty+MIN[x, x+w]; fmax: INTEGER ~ fmin+ABS[w]; IF fmin>=cfmax OR smin>=csmax OR fmax<=cfmin OR smax<=csmin THEN RETURN[none]; IF viewClipMask.rest#NIL THEN RETURN[part]; IF fmincfmax OR smax>csmax THEN RETURN[part]; RETURN[all]; } ELSE { RETURN[part]; }; }; RasterGetClipper: PUBLIC PROC[context: Context] RETURNS[clipper: Clipper] ~ { data: Data ~ NARROW[context.data]; IF data.specialClipPresent THEN { ERROR Imager.Error[[$unimplemented, "GetClipper during ImagerOps.DoWithBuffer"]]; }; RETURN [ImagerState.StateGetClipper[context]]; }; rasterClass: Class ~ NEW[ClassRep _ [ type: $Raster, DoSave: ImagerState.StateDoSave, SetInt: ImagerState.StateSetInt, SetReal: ImagerState.StateSetReal, SetT: ImagerState.StateSetT, SetFont: ImagerState.StateSetFont, SetColor: ImagerState.StateSetColor, SetClipper: ImagerState.StateSetClipper, GetInt: ImagerState.StateGetInt, GetReal: ImagerState.StateGetReal, GetT: ImagerState.StateGetT, GetFont: ImagerState.StateGetFont, GetColor: ImagerState.StateGetColor, GetClipper: RasterGetClipper, ConcatT: ImagerState.StateConcatT, Scale2T: ImagerState.StateScale2T, RotateT: ImagerState.StateRotateT, TranslateT: ImagerState.StateTranslateT, Move: ImagerState.StateMove, SetXY: ImagerState.StateSetXY, SetXYRel: ImagerState.StateSetXYRel, Show: ImagerRasterPrivate.RasterShow, ShowText: ImagerRasterPrivate.RasterShowText, StartUnderline: ImagerState.StateStartUnderline, MaskUnderline: ImagerState.StateMaskUnderline, CorrectMask: ImagerState.StateCorrectMask, CorrectSpace: ImagerState.StateCorrectSpace, Space: ImagerState.StateSpace, SetCorrectMeasure: ImagerState.StateSetCorrectMeasure, SetCorrectTolerance: ImagerState.StateSetCorrectTolerance, Correct: ImagerState.StateCorrect, DontCorrect: ImagerState.StateDontCorrect, SetGray: ImagerState.StateSetGray, SetSampledColor: ImagerState.StateSetSampledColor, SetSampledBlack: ImagerState.StateSetSampledBlack, MaskFill: RasterMaskFill, MaskStroke: RasterMaskStroke, MaskDashedStroke: RasterMaskDashedStroke, MaskRectangle: RasterMaskRectangle, MaskRectangleI: RasterMaskRectangleI, MaskVector: RasterMaskVector, MaskPixel: RasterMaskPixel, MaskBits: RasterMaskBits, DrawBits: RasterDrawBits, Clip: ImagerState.StateClip, ClipRectangle: ImagerState.StateClipRectangle, ClipRectangleI: ImagerState.StateClipRectangleI, GetCP: ImagerState.StateGetCP, GetBoundingRectangle: RasterGetBounds, propList: NIL ]]; END.  ImagerRasterImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Michael Plass, November 1, 1985 9:31:41 am PST Doug Wyatt, September 29, 1986 11:57:15 am PDT USING [T, color, clipper, underlineStart, noImage, priorityImportant] USING [T, color, clipper, strokeWidth, strokeStyle, noImage, priorityImportant] Computes an approximation to the average speed of the point moving along the curve as a function of its parameter, along with estimated error bounds. Since the curve is parameterized from zero to one, its average speed is numerically equal to its arc length. The deltas form a Bezier description of the velocity profile. We want its average magnitude. Still have part of the current piece to use up. The current piece is all used up; go on to the next. Overflow cannot happen. Computes bounding box of p0 and p1, clipped against clipBox. Note that translation doesn't invalidate charToDevice rect: Rectangle ~ viewToDevice.TransformRectangle[[x, y, w, h]]; -- test against viewClipBox -- Κ,<˜codešœ™Kšœ Οmœ=™HK™.Kšœ.™.—K˜šΟk ˜ Kšœžœ˜Kšœžœ˜%KšœžœΚ˜ΦKšœžœ˜+Kšœžœ ˜$Kšœ žœ˜Kšœ žœ˜Kšœ žœ/˜AKšœ žœ)˜9Kšœžœ'˜>Kšœžœ6˜KKšœžœC˜^Kšœ žœ˜ Kšœ žœ4˜DKšœžœ-˜CKšœžœ˜2Kšœžœ˜&Kšœ žœ ˜Kšœžœ_˜xKšœ žœ“˜₯KšœžœQ˜jKšœ žœθ˜ωKšœ žœ˜$Kšœžœγ˜ύKšœžœ˜Kšœžœ%˜/Kšœžœ˜"Kšœžœžœ˜Kšœžœ&žœ˜8KšžœžœG˜O—K˜KšΠblœžœž˜Kšžœ‘ž˜ͺKšžœF˜MKšœžœžœ"˜.K˜Kšœžœ˜"Kšœ žœžœΟc˜LK˜Kšœžœ˜ Kšœ žœžœ ˜JK˜šžœžœžœ˜K˜—Kšœ žœ˜'Kšœ žœ˜'Kšœžœ˜Kšœžœ˜+Kšœ žœ˜Kšœžœ˜#Kšœ žœ˜)Kšœ žœ˜%Kšœ žœ"˜2Kšœžœ"˜7Kšœžœ˜Kšœžœ˜1Kšœ žœ˜%Kšœ žœ˜%Kšœ žœ"˜1Kšœžœ'˜;Kšœžœ*˜AKšžœžœ žœ˜Kšœžœ˜Kšœ žœ˜+Kšœ žœ˜,Kšœ žœžœ" ˜TK˜šΟnœžœžœ˜OKšžœK˜QK˜K˜—š‘œžœžœ˜OKšžœK˜QK˜K˜—K˜š ‘œžœžœžœ+žœ4žœ˜­Kšœ žœ˜-Kšœžœ˜"Kšœžœ8˜LKšœ˜Kšœ˜Kšœ2˜2Kšœ4˜4Kšœ2˜2Kšœ2˜2Kšœ˜Kšœžœžœžœ˜.K˜Kšžœ žœ(˜:Kšžœ8˜Kšœœ˜œKšœ˜Kšœ—˜—Kšœ˜Kšœ4˜4Kšœ˜Kšžœ˜—Kšœ˜Kšœ6˜6KšœG˜GKšœžœžœ žœ˜1Kšœžœ˜ K˜K˜—š‘ œžœ˜2K˜K˜>Kšœžœ˜K˜K˜—š‘œžœ˜5K˜K˜?Kšœžœ˜!K˜K˜—š‘œžœžœ˜Kšž˜—Kšœ˜—Kšœp˜pK˜—K˜—Kšœ ˜ K˜K˜K˜K˜K˜—šœ@žœžœ˜]K˜—š ‘œžœ'žœžœ#žœ˜lšœžœžœ˜AK˜KšœB˜BK˜—Kšœ˜Kšœn˜nK˜K˜—š‘ œž œ2žœ˜`š žœžœžœ#žœžœž˜EKšœ8˜8š žœžœžœžœž˜SKš œžœžœžœžœ˜~—Kšžœ˜—K˜K˜—K˜šœ;žœ˜BK˜—š‘œžœ-žœ˜JK˜šžœžœ˜Kšœ žœ˜"Kšœ!˜!KšœW˜WK˜—K˜K˜—šœžœžœ˜K˜—š‘œžœ%˜>Kšœ˜šžœžœ˜Kšœ žœ˜"J˜Kšœ!˜!š žœžœžœžœžœ˜[Jšœ5˜5Jšœ%˜%Jšœžœ>˜EJšœžœF˜MJšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=Jšœžœ žœžœ#˜=K˜Kšžœžœ žœ˜5Kšžœžœ žœ˜5šžœžœžœ˜Jšœ:˜:šœžœ žœ˜(KšœC˜CKšœ˜—JšœB˜BJšœ˜—Jšœ˜—šžœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—KšœO˜OJšœ˜—K˜—Kšœ˜K˜—š‘œžœ žœ˜FKšœ˜šžœžœ˜Kšœ žœ˜"K˜Kšœ˜Kšœ!˜!Kšœ%˜%š žœžœžœžœžœ˜^Kšœžœžœžœ ˜9Kšœžœžœžœ ˜9Kšœ$˜$šœžœ žœ˜(KšœC˜CKšœ˜—K˜Kšžœ žœ žœ$˜AKšžœ žœ žœ$˜Aš žœžœžœžœžœ˜MJšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœB˜BK˜—K˜—Kšžœ8˜˜>Kšœ˜Kšœ˜Kšžœ˜—Kšœ˜Kšœ˜—š ‘ œžœžœžœžœ ˜CKšœžœ˜š ‘ œžœžœžœžœžœ˜6Kšžœ˜#Kšœ˜—Kšœžœ˜#Kšœžœ˜"Kšœžœ˜"Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšžœžœžœžœ˜>Kšœ˜—š‘œžœžœžœ ˜=šžœ žœ˜Kšœžœ˜Kšœ ˜ Kšœ˜Kšœ˜—Kšžœ žœžœ˜,Kšžœ˜ Kšœ˜—š‘ œžœžœžœ ˜@Kšœžœ ˜šœžœžœ˜Kšœ˜Kšœ˜—šœžœžœ˜Kšœ0˜0Kšœ˜Kšœ˜—šœžœžœ˜(Kšœ˜Kšœ˜—šœžœžœžœ˜)Kšœ˜Kšœ žœ˜(Kšžœžœ(˜?Kšœ˜Kšœ˜—šœžœ žœžœ˜&Kšœ.˜.Kšœ˜—Kšœžœ žœ0˜FKšœM˜MKšœ˜—Kšœ žœ˜$Kš œ žœžœ žœžœžœ˜OKšœžœ ˜Kšœžœ -˜=Kšœ žœ >˜cKšœžœ +˜=Kšœžœžœ˜š ‘œžœžœžœžœ˜Lšžœž˜šžœ˜šžœ˜K™/šžœ˜!Kšžœ'˜+KšžœB˜F—Kšœ˜—šžœ˜K™4Kšžœžœ žœžœ˜+Kšœžœžœ ˜8Kšœ"˜"Kšœžœ˜ Kšžœžœ žœžœ˜-Kšœ˜——Kšžœ˜—Kšœ˜—šœ˜Kš œžœžœžœžœ ˜8šœžœžœ˜Kšœžœ˜!Kšœžœ˜ Kšœžœ˜Kšœ žœ ˜šœžœ˜Kšœžœ˜ Kšœ/˜/Kšœ˜—šœžœ˜Kšœžœ˜ Kšœ/˜/Kšœ˜—Kšœ!˜!Kšžœžœ ˜Kšœ˜Kšœ˜—šœžœžœ˜!šœžœžœ˜(Kšœžœ˜Kšœ žœ˜ Kšœžœ˜Kšœ žœžœ˜šœžœ˜Kšœ+˜+Kšœ žœ˜Kšœ˜—šœžœ˜Kšœžœ˜/Kšœ6˜6Kšžœ žœ˜Kšœ˜Kšœ žœ˜Kšœ˜—Kšœ!˜!Kšžœžœ˜Kšœ˜—Kšœ˜Kšœ žœ˜(Kšžœžœ(˜?Kšœ˜Kšœ˜—šœžœ žœžœ˜&Kšœ.˜.Kšœ˜—Kšœžœ žœ0˜FKšœM˜MKšœ˜—Kšœ˜Kšœ&žœ˜-Kšœ˜K˜—K˜šœ žœ˜&K˜—Kš‘œžœžœžœžœžœžœžœžœžœ žœ˜cš ‘œžœžœžœžœžœ˜5J™š ‘œžœžœžœ žœ˜6Kš žœžœžœžœžœ ˜HKšœ˜—Kš‘œžœžœžœžœžœžœžœ˜Fšžœž˜#Kšœžœ%˜0Kšœžœ'˜2Kšœžœ'˜2Kšœžœ'˜2Kšžœžœ˜—Jšœ˜J˜—šœ žœžœžœ˜ J˜—š‘ œžœžœžœ˜9š ‘œžœžœžœžœ˜%šžœžœžœ˜&Kšœžœ˜Kšžœžœžœžœ˜Kšžœ,žœžœžœ˜AKšœ˜—Kšžœžœ˜Kšœ˜—Kš žœ žœ žœ žœ žœ žœ ˜MJšœ˜J˜—š ‘œžœžœžœžœ˜,J˜Jšžœžœ ˜Jšœ˜J˜—Kšžœžœžœžœ˜š‘ œžœžœžœ˜LKšœ<™˜DKšœ˜—KšœN˜Nšœžœ žœ˜(šœ žœ˜&šœžœžœ žœ˜/KšœY˜YKšžœ#žœ˜=Kšœ˜—šœ'˜'šœ>˜>Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—J˜—•StartOfExpansion€[base: LONG POINTER, wordsPerLine: NAT, sBits: NAT, fBits: NAT, sSize: NAT, fSize: NAT, sRuns: INTEGER, fRuns: INTEGER, run: ImagerDevice.RunProc]šœ"˜"Kšœ6˜6Kšœ+˜+Kšœ(˜(Kšœ˜—Kšœδ˜δKšœ˜—KšœE˜EKšœ˜—KšœB˜BKšœ˜—KšžœZ˜a—K˜K˜—š‘œžœžœžœžœžœžœ#˜”Kšœ žœ˜Bšœžœžœ˜BK˜Kšœp˜pšœžœ˜%šœ žœžœ žœ˜7K˜Kšœžœ˜ Kšœn˜nšžœžœžœ ž˜Kšœ#˜#šžœž˜Kšœžœžœ˜*Kšœžœžœ>˜OKšžœžœ˜—Kšžœ˜—Kšžœ žœ1˜AK˜—K˜K˜—Kšœd˜dK˜JKšœ*˜*K˜—šœ˜KšœO˜OKšœ˜—Kšœ˜Kšœ$˜$Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ9˜9Kšœd˜dK˜K˜—šœ@˜@K˜—š‘œžœžœžœžœžœžœ žœ˜ŒKšœ˜šžœžœ˜Kšœ žœ˜"Kšœ1˜1Kšœ!˜!Kšœ˜Kšœ*˜*Kšœ2˜2Kšœ»˜»K˜—K˜K˜—š‘œžœžœžœžœžœžœ žœ˜Œšœžœ˜Kšœ7˜7KšœK˜KKšœ7˜7Kšœ˜Kšœ˜—Kšœ˜šžœžœ˜Kšœ žœ˜"Kšœ1˜1Kšœ!˜!Kšœ˜Kšœ*˜*Kšœ2˜2š žœžœžœžœžœ˜`Kšœžœžœ˜4Kšœžœžœ˜4Kšœ$˜$šœžœ žœ˜(KšœC˜CKšœ˜—š žœžœžœžœžœ˜MK˜Jšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœ€˜€K˜—Kšœ˜—Kšžœ(˜,K˜—K˜K˜—š‘œžœ'˜Kšœžœžœžœ˜=Kšœ@˜@šœžœ žœ˜(KšœA˜AKšœ˜—š žœžœžœžœžœ˜MK˜Jšžœžœžœžœ˜MJšžœžœžœžœ˜MKšœS˜SK˜—K˜—Kšžœžœ  ˜K˜K˜—Kšœ žœ˜-K˜š ‘œžœžœ'žœžœ˜gšžœžœž˜šœ˜Kšœ˜Kšœ'žœžœ˜CKšžœ9˜?K˜—šžœ˜ KšœC˜Cšžœ žœž˜KšžœI˜O—šž˜KšžœB˜G—K˜——K˜—K˜š‘œžœžœžœ˜SK˜1K˜3Kšžœžœžœžœ˜&šžœžœžœ˜;K˜)Kšœžœ ˜Kšœžœ˜"Kšœžœ ˜Kšœžœ˜"Kšœžœžœ ˜,Kšœžœžœ˜Kšœžœžœ ˜,Kšœžœžœ˜Kš žœ žœ žœ žœ žœžœ˜NKšžœžœžœžœ˜+Kš žœ žœ žœ žœ žœžœ˜JKšžœ˜ K˜—šžœ˜K™@Kš ™Kšžœ˜ K˜—K˜K˜—š‘œžœžœžœ˜MKšœ žœ˜"šžœžœ˜!KšžœL˜QKšœ˜—Kšžœ(˜.K˜K˜—šœžœ ˜%Kšœ˜Kšœ ˜ Kšœ ˜ Kšœ"˜"Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ(˜(Kšœ ˜ Kšœ"˜"Kšœ˜Kšœ"˜"Kšœ$˜$Kšœ˜Kšœ"˜"Kšœ"˜"Kšœ"˜"Kšœ(˜(Kšœ˜Kšœ˜Kšœ$˜$Kšœ%˜%Kšœ-˜-Kšœ0˜0Kšœ.˜.Kšœ*˜*Kšœ,˜,Kšœ˜Kšœ6˜6Kšœ:˜:Kšœ"˜"Kšœ*˜*Kšœ"˜"Kšœ2˜2Kšœ2˜2Kšœ˜Kšœ˜Kšœ)˜)Kšœ#˜#Kšœ%˜%Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ.˜.Kšœ0˜0Kšœ˜Kšœ&˜&Kšœ ž˜ K˜K˜—K˜Kšžœ˜—…—¨–Ψή