<> <> <> <> 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 { <> <<-- test against viewClipBox -->> 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.