Spaces:
Running
Running
/* Smalltalk from Squeak4.5 with VMMaker 4.13.6 translated as JS source on 14 November 2014 12:21:50 am */ | |
/* Automatically generated by | |
JSPluginCodeGenerator VMMakerJS-bf.17 uuid: 399be48b-95d8-4722-bdcc-39a94a12c486 | |
from | |
BalloonEnginePlugin VMMaker-bf.353 uuid: 8ae25e7e-8d2c-451e-8277-598b30e9c002 | |
*/ | |
(function B2DPlugin() { | |
; | |
var VM_PROXY_MAJOR = 1; | |
var VM_PROXY_MINOR = 11; | |
/*** Functions ***/ | |
function CLASSOF(obj) { return typeof obj === "number" ? interpreterProxy.classSmallInteger() : obj.sqClass } | |
function SIZEOF(obj) { return obj.pointers ? obj.pointers.length : obj.words ? obj.words.length : obj.bytes ? obj.bytes.length : 0 } | |
function BYTESIZEOF(obj) { return obj.bytes ? obj.bytes.length : obj.words ? obj.words.length * 4 : 0 } | |
function DIV(a, b) { return Math.floor(a / b) | 0; } // integer division | |
function MOD(a, b) { return a - DIV(a, b) * b | 0; } // signed modulus | |
function SHL(a, b) { return b > 31 ? 0 : a << b; } // fix JS shift | |
function SHR(a, b) { return b > 31 ? 0 : a >>> b; } // fix JS shift | |
function SHIFT(a, b) { return b < 0 ? (b < -31 ? 0 : a >>> (0-b) ) : (b > 31 ? 0 : a << b); } | |
function PTR_ADD(p, n) { return new Int32Array(p.buffer, p.byteOffset + n * 4); } | |
function FPTR_ADD(p, n) { return new Float32Array(p.buffer, p.byteOffset + n * 4); } | |
/*** Constants ***/ | |
var BEBalloonEngineSize = 12; | |
var BEBitBltIndex = 2; | |
var BEFormsIndex = 3; | |
var BESpanIndex = 1; | |
var BEWorkBufferIndex = 0; | |
var ETBalloonEdgeDataSize = 6; | |
var ETIndexIndex = 0; | |
var ETLinesIndex = 4; | |
var ETXValueIndex = 1; | |
var ETYValueIndex = 2; | |
var ETZValueIndex = 3; | |
var FTBalloonFillDataSize = 6; | |
var FTIndexIndex = 0; | |
var FTMaxXIndex = 2; | |
var FTMinXIndex = 1; | |
var FTYValueIndex = 3; | |
var GBBaseSize = 16; | |
var GBBitmapDepth = 12; | |
var GBBitmapHeight = 11; | |
var GBBitmapRaster = 14; | |
var GBBitmapSize = 13; | |
var GBBitmapWidth = 10; | |
var GBColormapOffset = 18; | |
var GBColormapSize = 15; | |
var GBEndX = 14; | |
var GBEndY = 15; | |
var GBFinalX = 21; | |
var GBMBaseSize = 18; | |
var GBTileFlag = 16; | |
var GBUpdateDDX = 4; | |
var GBUpdateDDY = 5; | |
var GBUpdateDX = 2; | |
var GBUpdateDY = 3; | |
var GBUpdateData = 10; | |
var GBUpdateX = 0; | |
var GBUpdateY = 1; | |
var GBViaX = 12; | |
var GBViaY = 13; | |
var GBWideEntry = 18; | |
var GBWideExit = 19; | |
var GBWideExtent = 20; | |
var GBWideFill = 16; | |
var GBWideSize = 28; | |
var GBWideUpdateData = 22; | |
var GBWideWidth = 17; | |
var GEBaseEdgeSize = 10; | |
var GEBaseFillSize = 4; | |
var GEEdgeFillsInvalid = 65536; | |
var GEFAlreadyFailed = 100; | |
var GEFBadPoint = 121; | |
var GEFBitBltLoadFailed = 122; | |
var GEFClassMismatch = 114; | |
var GEFEdgeDataTooSmall = 112; | |
var GEFEngineIsInteger = 101; | |
var GEFEngineIsWords = 102; | |
var GEFEngineStopped = 104; | |
var GEFEngineTooSmall = 103; | |
var GEFEntityCheckFailed = 120; | |
var GEFEntityLoadFailed = 119; | |
var GEFFillDataTooSmall = 113; | |
var GEFFormLoadFailed = 123; | |
var GEFSizeMismatch = 115; | |
var GEFWorkBufferBadMagic = 108; | |
var GEFWorkBufferIsInteger = 105; | |
var GEFWorkBufferIsPointers = 106; | |
var GEFWorkBufferStartWrong = 110; | |
var GEFWorkBufferTooSmall = 107; | |
var GEFWorkBufferWrongSize = 109; | |
var GEFWorkTooBig = 111; | |
var GEFWrongEdge = 118; | |
var GEFWrongFill = 117; | |
var GEFWrongState = 116; | |
var GEFillIndexLeft = 8; | |
var GEFillIndexRight = 9; | |
var GENumLines = 7; | |
var GEObjectIndex = 2; | |
var GEObjectLength = 1; | |
var GEObjectType = 0; | |
var GEPrimitiveBezier = 6; | |
var GEPrimitiveClippedBitmapFill = 1024; | |
var GEPrimitiveEdge = 2; | |
var GEPrimitiveEdgeMask = 255; | |
var GEPrimitiveFill = 256; | |
var GEPrimitiveFillMask = 65280; | |
var GEPrimitiveLine = 4; | |
var GEPrimitiveLinearGradientFill = 512; | |
var GEPrimitiveRadialGradientFill = 768; | |
var GEPrimitiveTypeMask = 65535; | |
var GEPrimitiveWide = 1; | |
var GEPrimitiveWideBezier = 7; | |
var GEPrimitiveWideLine = 5; | |
var GEPrimitiveWideMask = 254; | |
var GEStateAddingFromGET = 1; | |
var GEStateBlitBuffer = 5; | |
var GEStateCompleted = 8; | |
var GEStateScanningAET = 3; | |
var GEStateUnlocked = 0; | |
var GEStateUpdateEdges = 6; | |
var GEStateWaitingChange = 7; | |
var GEStateWaitingForEdge = 2; | |
var GEStateWaitingForFill = 4; | |
var GEXValue = 4; | |
var GEYValue = 5; | |
var GEZValue = 6; | |
var GErrorAETEntry = 6; | |
var GErrorBadState = 2; | |
var GErrorFillEntry = 5; | |
var GErrorGETEntry = 4; | |
var GErrorNeedFlush = 3; | |
var GErrorNoMoreSpace = 1; | |
var GFDirectionX = 6; | |
var GFDirectionY = 7; | |
var GFNormalX = 8; | |
var GFNormalY = 9; | |
var GFOriginX = 4; | |
var GFOriginY = 5; | |
var GFRampLength = 10; | |
var GFRampOffset = 12; | |
var GGBaseSize = 12; | |
var GLBaseSize = 16; | |
var GLEndX = 14; | |
var GLEndY = 15; | |
var GLError = 13; | |
var GLErrorAdjDown = 15; | |
var GLErrorAdjUp = 14; | |
var GLWideEntry = 18; | |
var GLWideExit = 19; | |
var GLWideExtent = 20; | |
var GLWideFill = 16; | |
var GLWideSize = 21; | |
var GLWideWidth = 17; | |
var GLXDirection = 10; | |
var GLXIncrement = 12; | |
var GLYDirection = 11; | |
var GWAAColorMask = 51; | |
var GWAAColorShift = 50; | |
var GWAAHalfPixel = 53; | |
var GWAALevel = 48; | |
var GWAAScanMask = 52; | |
var GWAAShift = 49; | |
var GWAETStart = 13; | |
var GWAETUsed = 14; | |
var GWBezierHeightSubdivisions = 109; | |
var GWBezierLineConversions = 111; | |
var GWBezierMonotonSubdivisions = 108; | |
var GWBezierOverflowSubdivisions = 110; | |
var GWBufferTop = 10; | |
var GWClearSpanBuffer = 69; | |
var GWClipMaxX = 43; | |
var GWClipMaxY = 45; | |
var GWClipMinX = 42; | |
var GWClipMinY = 44; | |
var GWColorTransform = 24; | |
var GWCountAddAETEntry = 97; | |
var GWCountChangeAETEntry = 107; | |
var GWCountDisplaySpan = 103; | |
var GWCountFinishTest = 93; | |
var GWCountInitializing = 91; | |
var GWCountMergeFill = 101; | |
var GWCountNextAETEntry = 105; | |
var GWCountNextFillEntry = 99; | |
var GWCountNextGETEntry = 95; | |
var GWCurrentY = 88; | |
var GWCurrentZ = 113; | |
var GWDestOffsetX = 46; | |
var GWDestOffsetY = 47; | |
var GWEdgeTransform = 18; | |
var GWFillMaxX = 37; | |
var GWFillMaxY = 39; | |
var GWFillMinX = 36; | |
var GWFillMinY = 38; | |
var GWGETStart = 11; | |
var GWGETUsed = 12; | |
var GWHasColorTransform = 17; | |
var GWHasEdgeTransform = 16; | |
var GWHeaderSize = 128; | |
var GWLastExportedEdge = 65; | |
var GWLastExportedFill = 66; | |
var GWLastExportedLeftX = 67; | |
var GWLastExportedRightX = 68; | |
var GWMagicIndex = 0; | |
var GWMagicNumber = 1097753705; | |
var GWMinimalSize = 256; | |
var GWNeedsFlush = 63; | |
var GWObjStart = 8; | |
var GWObjUsed = 9; | |
var GWPoint1 = 80; | |
var GWPoint2 = 82; | |
var GWPoint3 = 84; | |
var GWPoint4 = 86; | |
var GWSize = 1; | |
var GWSpanEnd = 34; | |
var GWSpanEndAA = 35; | |
var GWSpanSize = 33; | |
var GWSpanStart = 32; | |
var GWState = 2; | |
var GWStopReason = 64; | |
var GWTimeAddAETEntry = 96; | |
var GWTimeChangeAETEntry = 106; | |
var GWTimeDisplaySpan = 102; | |
var GWTimeFinishTest = 92; | |
var GWTimeInitializing = 90; | |
var GWTimeMergeFill = 100; | |
var GWTimeNextAETEntry = 104; | |
var GWTimeNextFillEntry = 98; | |
var GWTimeNextGETEntry = 94; | |
var PrimErrBadArgument = 3; | |
var PrimErrBadNumArgs = 5; | |
/*** Variables ***/ | |
var aetBuffer = null; | |
var bbPluginName = "BitBltPlugin"; | |
var copyBitsFn = null; | |
var dispatchReturnValue = 0; | |
var dispatchedValue = 0; | |
var doProfileStats = 0; | |
var engine = 0; | |
var engineStopped = 0; | |
var formArray = 0; | |
var geProfileTime = 0; | |
var getBuffer = null; | |
var interpreterProxy = null; | |
var loadBBFn = null; | |
var moduleName = "B2DPlugin 14 November 2014 (e)"; | |
var objBuffer = null; | |
var objUsed = 0; | |
var spanBuffer = null; | |
var workBuffer = null; | |
function aaColorMaskGet() { | |
return workBuffer[GWAAColorMask]; | |
} | |
function aaColorMaskPut(value) { | |
return workBuffer[GWAAColorMask] = value; | |
} | |
function aaColorShiftGet() { | |
return workBuffer[GWAAColorShift]; | |
} | |
function aaColorShiftPut(value) { | |
return workBuffer[GWAAColorShift] = value; | |
} | |
/* Common function to compute the first full pixel for AA drawing */ | |
function aaFirstPixelFromto(leftX, rightX) { | |
var firstPixel; | |
firstPixel = ((leftX + aaLevelGet()) - 1) & ~(aaLevelGet() - 1); | |
if (firstPixel > rightX) { | |
return rightX; | |
} else { | |
return firstPixel; | |
} | |
} | |
function aaHalfPixelPut(value) { | |
return workBuffer[GWAAHalfPixel] = value; | |
} | |
/* Common function to compute the last full pixel for AA drawing */ | |
function aaLastPixelFromto(leftX, rightX) { | |
return (rightX - 1) & ~(aaLevelGet() - 1); | |
} | |
function aaLevelGet() { | |
return workBuffer[GWAALevel]; | |
} | |
function aaLevelPut(value) { | |
return workBuffer[GWAALevel] = value; | |
} | |
function aaScanMaskGet() { | |
return workBuffer[GWAAScanMask]; | |
} | |
function aaScanMaskPut(value) { | |
return workBuffer[GWAAScanMask] = value; | |
} | |
function aaShiftGet() { | |
return workBuffer[GWAAShift]; | |
} | |
function aaShiftPut(value) { | |
return workBuffer[GWAAShift] = value; | |
} | |
/* Compute the squared value of a 8.24 number with 0.0 <= value < 1.0, | |
e.g., compute (value * value) bitShift: -24 */ | |
function absoluteSquared8Dot24(value) { | |
var word1; | |
var word2; | |
word1 = value & 65535; | |
word2 = (value >>> 16) & 255; | |
return ((((word1 * word1) >>> 16) + ((word1 * word2) * 2)) + ((word2 * word2) << 16)) >>> 8; | |
} | |
/* Return the accurate length of the vector described by deltaX and deltaY */ | |
function accurateLengthOfwith(deltaX, deltaY) { | |
var length2; | |
if (deltaX === 0) { | |
if (deltaY < 0) { | |
return 0 - deltaY; | |
} else { | |
return deltaY; | |
} | |
} | |
if (deltaY === 0) { | |
if (deltaX < 0) { | |
return 0 - deltaX; | |
} else { | |
return deltaX; | |
} | |
} | |
length2 = (deltaX * deltaX) + (deltaY * deltaY); | |
return computeSqrt(length2); | |
} | |
function addEdgeToGET(edge) { | |
if (!allocateGETEntry(1)) { | |
return 0; | |
} | |
getBuffer[getUsedGet()] = edge; | |
getUsedPut(getUsedGet() + 1); | |
} | |
/* Adjust the wide bezier curve (dx < 0) to start/end at the right point */ | |
function adjustWideBezierLeftwidthoffsetendX(bezier, lineWidth, lineOffset, endX) { | |
var lastX; | |
var lastY; | |
bezierUpdateDataOf(bezier)[GBUpdateX] = (bezierUpdateDataOf(bezier)[GBUpdateX] - (lineOffset * 256)); | |
lastX = wideBezierUpdateDataOf(bezier)[GBUpdateX]; | |
wideBezierUpdateDataOf(bezier)[GBUpdateX] = (lastX + ((lineWidth - lineOffset) * 256)); | |
lastY = wideBezierUpdateDataOf(bezier)[GBUpdateY]; | |
wideBezierUpdateDataOf(bezier)[GBUpdateY] = (lastY + (lineWidth * 256)); | |
bezierFinalXOfput(bezier, endX - lineOffset); | |
} | |
/* Adjust the wide bezier curve (dx >= 0) to start/end at the right point */ | |
function adjustWideBezierRightwidthoffsetendX(bezier, lineWidth, lineOffset, endX) { | |
var lastX; | |
var lastY; | |
bezierUpdateDataOf(bezier)[GBUpdateX] = (bezierUpdateDataOf(bezier)[GBUpdateX] + (lineOffset * 256)); | |
lastX = wideBezierUpdateDataOf(bezier)[GBUpdateX]; | |
wideBezierUpdateDataOf(bezier)[GBUpdateX] = (lastX - ((lineWidth - lineOffset) * 256)); | |
/* Set lineWidth pixels down */ | |
lastY = wideBezierUpdateDataOf(bezier)[GBUpdateY]; | |
wideBezierUpdateDataOf(bezier)[GBUpdateY] = (lastY + (lineWidth * 256)); | |
bezierFinalXOfput(bezier, (endX - lineOffset) + lineWidth); | |
} | |
/* Adjust the wide line after it has been stepped from lastX to nextX. | |
Special adjustments of line width and start position are made here | |
to simulate a rectangular brush */ | |
function adjustWideLineafterSteppingFromto(line, lastX, nextX) { | |
var baseWidth; | |
var deltaX; | |
var lineOffset; | |
var lineWidth; | |
var xDir; | |
var yEntry; | |
var yExit; | |
/* Don't inline this */ | |
/* Fetch the values the adjustment decisions are based on */ | |
yEntry = wideLineEntryOf(line); | |
yExit = wideLineExitOf(line); | |
baseWidth = wideLineExtentOf(line); | |
lineOffset = offsetFromWidth(baseWidth); | |
lineWidth = wideLineWidthOf(line); | |
xDir = lineXDirectionOf(line); | |
/* Adjust the start of the line to fill an entire rectangle */ | |
deltaX = nextX - lastX; | |
if (yEntry < baseWidth) { | |
if (xDir < 0) { | |
/* effectively adding */ | |
lineWidth -= deltaX; | |
} else { | |
lineWidth += deltaX; | |
edgeXValueOfput(line, lastX); | |
} | |
} | |
if ((yExit + lineOffset) === 0) { | |
if (xDir > 0) { | |
lineWidth -= lineXIncrementOf(line); | |
} else { | |
/* effectively subtracting */ | |
lineWidth += lineXIncrementOf(line); | |
edgeXValueOfput(line, lastX); | |
} | |
} | |
if ((yExit + lineOffset) > 0) { | |
if (xDir < 0) { | |
/* effectively subtracting */ | |
lineWidth += deltaX; | |
edgeXValueOfput(line, lastX); | |
} else { | |
lineWidth -= deltaX; | |
} | |
} | |
wideLineWidthOfput(line, lineWidth); | |
} | |
function aetStartGet() { | |
return workBuffer[GWAETStart]; | |
} | |
function aetStartPut(value) { | |
return workBuffer[GWAETStart] = value; | |
} | |
function aetUsedGet() { | |
return workBuffer[GWAETUsed]; | |
} | |
function aetUsedPut(value) { | |
return workBuffer[GWAETUsed] = value; | |
} | |
/* Allocate n slots in the active edge table */ | |
function allocateAETEntry(nSlots) { | |
return needAvailableSpace(nSlots); | |
} | |
function allocateBezier() { | |
var bezier; | |
if (!allocateObjEntry(GBBaseSize)) { | |
return 0; | |
} | |
bezier = objUsed; | |
objUsed = bezier + GBBaseSize; | |
objectTypeOfput(bezier, GEPrimitiveBezier); | |
objectIndexOfput(bezier, 0); | |
objectLengthOfput(bezier, GBBaseSize); | |
return bezier; | |
} | |
function allocateBezierStackEntry() { | |
wbStackPush(6); | |
return wbStackSize(); | |
} | |
function allocateBitmapFillcolormap(cmSize, cmBits) { | |
var cm; | |
var fill; | |
var fillSize; | |
var i; | |
fillSize = GBMBaseSize + cmSize; | |
if (!allocateObjEntry(fillSize)) { | |
return 0; | |
} | |
fill = objUsed; | |
objUsed = fill + fillSize; | |
objectTypeOfput(fill, GEPrimitiveClippedBitmapFill); | |
objectIndexOfput(fill, 0); | |
objectLengthOfput(fill, fillSize); | |
cm = colormapOf(fill); | |
if (hasColorTransform()) { | |
for (i = 0; i <= (cmSize - 1); i++) { | |
cm[i] = transformColor(cmBits[i]); | |
} | |
} else { | |
for (i = 0; i <= (cmSize - 1); i++) { | |
cm[i] = cmBits[i]; | |
} | |
} | |
bitmapCmSizeOfput(fill, cmSize); | |
return fill; | |
} | |
/* Allocate n slots in the global edge table */ | |
function allocateGETEntry(nSlots) { | |
var dstIndex; | |
var i; | |
var srcIndex; | |
var iLimiT; | |
/* First allocate nSlots in the AET */ | |
if (!allocateAETEntry(nSlots)) { | |
return false; | |
} | |
if (aetUsedGet() !== 0) { | |
/* Then move the AET upwards */ | |
srcIndex = aetUsedGet(); | |
dstIndex = aetUsedGet() + nSlots; | |
for (i = 1, iLimiT = aetUsedGet(); i <= iLimiT; i++) { | |
aetBuffer[(--dstIndex)] = aetBuffer[(--srcIndex)]; | |
} | |
} | |
aetBuffer = PTR_ADD(aetBuffer, nSlots); | |
return true; | |
} | |
function allocateGradientFillrampWidthisRadial(ramp, rampWidth, isRadial) { | |
var fill; | |
var fillSize; | |
var i; | |
var rampPtr; | |
fillSize = GGBaseSize + rampWidth; | |
if (!allocateObjEntry(fillSize)) { | |
return 0; | |
} | |
fill = objUsed; | |
objUsed = fill + fillSize; | |
if (isRadial) { | |
objectTypeOfput(fill, GEPrimitiveRadialGradientFill); | |
} else { | |
objectTypeOfput(fill, GEPrimitiveLinearGradientFill); | |
} | |
objectIndexOfput(fill, 0); | |
objectLengthOfput(fill, fillSize); | |
rampPtr = gradientRampOf(fill); | |
if (hasColorTransform()) { | |
for (i = 0; i <= (rampWidth - 1); i++) { | |
rampPtr[i] = transformColor(ramp[i]); | |
} | |
} else { | |
for (i = 0; i <= (rampWidth - 1); i++) { | |
rampPtr[i] = ramp[i]; | |
} | |
} | |
gradientRampLengthOfput(fill, rampWidth); | |
return fill; | |
} | |
function allocateLine() { | |
var line; | |
if (!allocateObjEntry(GLBaseSize)) { | |
return 0; | |
} | |
line = objUsed; | |
objUsed = line + GLBaseSize; | |
objectTypeOfput(line, GEPrimitiveLine); | |
objectIndexOfput(line, 0); | |
objectLengthOfput(line, GLBaseSize); | |
return line; | |
} | |
/* Allocate n slots in the object buffer */ | |
function allocateObjEntry(nSlots) { | |
var dstIndex; | |
var i; | |
var srcIndex; | |
var iLimiT; | |
/* First allocate nSlots in the GET */ | |
if (!allocateGETEntry(nSlots)) { | |
return false; | |
} | |
if (getUsedGet() !== 0) { | |
/* Then move the GET upwards */ | |
srcIndex = getUsedGet(); | |
dstIndex = getUsedGet() + nSlots; | |
for (i = 1, iLimiT = getUsedGet(); i <= iLimiT; i++) { | |
getBuffer[(--dstIndex)] = getBuffer[(--srcIndex)]; | |
} | |
} | |
getBuffer = PTR_ADD(getBuffer, nSlots); | |
return true; | |
} | |
/* AET and Stack allocation are symmetric */ | |
function allocateStackEntry(nSlots) { | |
return needAvailableSpace(nSlots); | |
} | |
function allocateStackFillEntry() { | |
return wbStackPush(stackFillEntryLength()); | |
} | |
function allocateWideBezier() { | |
var bezier; | |
if (!allocateObjEntry(GBWideSize)) { | |
return 0; | |
} | |
bezier = objUsed; | |
objUsed = bezier + GBWideSize; | |
objectTypeOfput(bezier, GEPrimitiveWideBezier); | |
objectIndexOfput(bezier, 0); | |
objectLengthOfput(bezier, GBWideSize); | |
return bezier; | |
} | |
function allocateWideLine() { | |
var line; | |
if (!allocateObjEntry(GLWideSize)) { | |
return 0; | |
} | |
line = objUsed; | |
objUsed = line + GLWideSize; | |
objectTypeOfput(line, GEPrimitiveWideLine); | |
objectIndexOfput(line, 0); | |
objectLengthOfput(line, GLWideSize); | |
return line; | |
} | |
function areEdgeFillsValid(edge) { | |
return (objectHeaderOf(edge) & GEEdgeFillsInvalid) === 0; | |
} | |
/* Make sure that val1 is between val2 and val3. */ | |
function assureValuebetweenand(val1, val2, val3) { | |
if (val2 > val3) { | |
if (val1 > val2) { | |
return val2; | |
} | |
if (val1 < val3) { | |
return val3; | |
} | |
} else { | |
if (val1 < val2) { | |
return val2; | |
} | |
if (val1 > val3) { | |
return val3; | |
} | |
} | |
return val1; | |
} | |
function bezierEndXOf(bezier) { | |
return objat(bezier, GBEndX); | |
} | |
function bezierEndXOfput(bezier, value) { | |
return objatput(bezier, GBEndX, value); | |
} | |
function bezierEndYOf(bezier) { | |
return objat(bezier, GBEndY); | |
} | |
function bezierEndYOfput(bezier, value) { | |
return objatput(bezier, GBEndY, value); | |
} | |
function bezierFinalXOf(bezier) { | |
return objat(bezier, GBFinalX); | |
} | |
function bezierFinalXOfput(bezier, value) { | |
return objatput(bezier, GBFinalX, value); | |
} | |
function bezierUpdateDataOf(bezier) { | |
return PTR_ADD(objBuffer, bezier + GBUpdateData); | |
} | |
function bezierViaXOf(bezier) { | |
return objat(bezier, GBViaX); | |
} | |
function bezierViaXOfput(bezier, value) { | |
return objatput(bezier, GBViaX, value); | |
} | |
function bezierViaYOf(bezier) { | |
return objat(bezier, GBViaY); | |
} | |
function bezierViaYOfput(bezier, value) { | |
return objatput(bezier, GBViaY, value); | |
} | |
function bitmapCmSizeOf(bmFill) { | |
return objat(bmFill, GBColormapSize); | |
} | |
function bitmapCmSizeOfput(bmFill, value) { | |
return objatput(bmFill, GBColormapSize, value); | |
} | |
function bitmapDepthOf(bmFill) { | |
return objat(bmFill, GBBitmapDepth); | |
} | |
function bitmapDepthOfput(bmFill, value) { | |
return objatput(bmFill, GBBitmapDepth, value); | |
} | |
function bitmapHeightOf(bmFill) { | |
return objat(bmFill, GBBitmapHeight); | |
} | |
function bitmapHeightOfput(bmFill, value) { | |
return objatput(bmFill, GBBitmapHeight, value); | |
} | |
function bitmapRasterOf(bmFill) { | |
return objat(bmFill, GBBitmapRaster); | |
} | |
function bitmapRasterOfput(bmFill, value) { | |
return objatput(bmFill, GBBitmapRaster, value); | |
} | |
function bitmapSizeOf(bmFill) { | |
return objat(bmFill, GBBitmapSize); | |
} | |
function bitmapSizeOfput(bmFill, value) { | |
return objatput(bmFill, GBBitmapSize, value); | |
} | |
function bitmapTileFlagOf(bmFill) { | |
return objat(bmFill, GBTileFlag); | |
} | |
function bitmapTileFlagOfput(bmFill, value) { | |
return objatput(bmFill, GBTileFlag, value); | |
} | |
function bitmapValuebitsatXy(bmFill, bits, xp, yp) { | |
var a; | |
var b; | |
var bmDepth; | |
var bmRaster; | |
var cMask; | |
var g; | |
var r; | |
var rShift; | |
var value; | |
bmDepth = bitmapDepthOf(bmFill); | |
bmRaster = bitmapRasterOf(bmFill); | |
if (bmDepth === 32) { | |
value = (bits[(bmRaster * yp) + xp]|0); | |
if ((value !== 0) && ((value & 4278190080) === 0)) { | |
value = value | 4278190080; | |
} | |
return uncheckedTransformColor(value); | |
} | |
rShift = rShiftTable()[bmDepth]; | |
/* cMask - mask out the pixel from the word */ | |
value = bits[(bmRaster * yp) + (SHR(xp, rShift))]; | |
/* rShift - shift value to move the pixel in the word to the lowest bit position */ | |
cMask = (SHL(1, bmDepth)) - 1; | |
rShift = (32 - bmDepth) - ((xp & ((SHL(1, rShift)) - 1)) * bmDepth); | |
value = (SHR(value, rShift)) & cMask; | |
if (bmDepth === 16) { | |
/* Must convert by expanding bits */ | |
if (value !== 0) { | |
b = (value & 31) << 3; | |
b += b >>> 5; | |
g = ((value >>> 5) & 31) << 3; | |
g += g >>> 5; | |
r = ((value >>> 10) & 31) << 3; | |
r += r >>> 5; | |
a = 255; | |
value = ((b + (g << 8)) + (r << 16)) + (a << 24); | |
} | |
} else { | |
/* Must convert by using color map */ | |
if (bitmapCmSizeOf(bmFill) === 0) { | |
value = 0; | |
} else { | |
value = colormapOf(bmFill)[value]; | |
} | |
} | |
return uncheckedTransformColor(value); | |
} | |
function bitmapWidthOf(bmFill) { | |
return objat(bmFill, GBBitmapWidth); | |
} | |
function bitmapWidthOfput(bmFill, value) { | |
return objatput(bmFill, GBBitmapWidth, value); | |
} | |
function bzEndX(index) { | |
return wbStackValue((wbStackSize() - index) + 4); | |
} | |
function bzEndXput(index, value) { | |
return wbStackValueput((wbStackSize() - index) + 4, value); | |
} | |
function bzEndY(index) { | |
return wbStackValue((wbStackSize() - index) + 5); | |
} | |
function bzEndYput(index, value) { | |
return wbStackValueput((wbStackSize() - index) + 5, value); | |
} | |
function bzStartX(index) { | |
return wbStackValue((wbStackSize() - index) + 0); | |
} | |
function bzStartXput(index, value) { | |
return wbStackValueput((wbStackSize() - index) + 0, value); | |
} | |
function bzStartY(index) { | |
return wbStackValue((wbStackSize() - index) + 1); | |
} | |
function bzStartYput(index, value) { | |
return wbStackValueput((wbStackSize() - index) + 1, value); | |
} | |
function bzViaX(index) { | |
return wbStackValue((wbStackSize() - index) + 2); | |
} | |
function bzViaXput(index, value) { | |
return wbStackValueput((wbStackSize() - index) + 2, value); | |
} | |
function bzViaY(index) { | |
return wbStackValue((wbStackSize() - index) + 3); | |
} | |
function bzViaYput(index, value) { | |
return wbStackValueput((wbStackSize() - index) + 3, value); | |
} | |
/* Check the fill indexes in the run-length encoded fillList */ | |
function checkCompressedFillIndexListmaxsegments(fillList, maxIndex, nSegs) { | |
var fillPtr; | |
var i; | |
var length; | |
var nFills; | |
var runLength; | |
var runValue; | |
length = SIZEOF(fillList); | |
fillPtr = fillList.wordsAsInt32Array(); | |
nFills = 0; | |
for (i = 0; i <= (length - 1); i++) { | |
runLength = shortRunLengthAtfrom(i, fillPtr); | |
runValue = shortRunValueAtfrom(i, fillPtr); | |
if (!((runValue >= 0) && (runValue <= maxIndex))) { | |
return false; | |
} | |
nFills += runLength; | |
} | |
return nFills === nSegs; | |
} | |
/* Check if the indexList (containing fill handles) is okay. */ | |
function checkCompressedFills(indexList) { | |
var fillIndex; | |
var fillPtr; | |
var i; | |
var length; | |
/* First check if the oops have the right format */ | |
if (!interpreterProxy.isWords(indexList)) { | |
return false; | |
} | |
length = SIZEOF(indexList); | |
fillPtr = indexList.wordsAsInt32Array(); | |
for (i = 0; i <= (length - 1); i++) { | |
/* Make sure the fill is okay */ | |
fillIndex = fillPtr[i]; | |
if (!isFillOkay(fillIndex)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/* Check the run-length encoded lineWidthList matches nSegments */ | |
function checkCompressedLineWidthssegments(lineWidthList, nSegments) { | |
var i; | |
var length; | |
var nItems; | |
var ptr; | |
var runLength; | |
length = SIZEOF(lineWidthList); | |
ptr = lineWidthList.wordsAsInt32Array(); | |
nItems = 0; | |
for (i = 0; i <= (length - 1); i++) { | |
runLength = shortRunLengthAtfrom(i, ptr); | |
nItems += runLength; | |
} | |
return nItems === nSegments; | |
} | |
/* Check if the given point array can be handled by the engine. */ | |
function checkCompressedPointssegments(points, nSegments) { | |
var pSize; | |
if (!interpreterProxy.isWords(points)) { | |
return false; | |
} | |
/* The points must be either in PointArray format or ShortPointArray format. | |
Also, we currently handle only quadratic segments (e.g., 3 points each) and thus either | |
pSize = nSegments * 3, for ShortPointArrays or, | |
pSize = nSegments * 6, for PointArrays */ | |
pSize = SIZEOF(points); | |
if (!((pSize === (nSegments * 3)) || (pSize === (nSegments * 6)))) { | |
return false; | |
} | |
return true; | |
} | |
/* Check if the given shape can be handled by the engine. | |
Since there are a number of requirements this is an extra method. */ | |
function checkCompressedShapesegmentsleftFillsrightFillslineWidthslineFillsfillIndexList(points, nSegments, leftFills, rightFills, lineWidths, lineFills, fillIndexList) { | |
var maxFillIndex; | |
if (!checkCompressedPointssegments(points, nSegments)) { | |
return false; | |
} | |
if (!checkCompressedFills(fillIndexList)) { | |
return false; | |
} | |
maxFillIndex = SIZEOF(fillIndexList); | |
if (!checkCompressedFillIndexListmaxsegments(leftFills, maxFillIndex, nSegments)) { | |
return false; | |
} | |
if (!checkCompressedFillIndexListmaxsegments(rightFills, maxFillIndex, nSegments)) { | |
return false; | |
} | |
if (!checkCompressedFillIndexListmaxsegments(lineFills, maxFillIndex, nSegments)) { | |
return false; | |
} | |
if (!checkCompressedLineWidthssegments(lineWidths, nSegments)) { | |
return false; | |
} | |
return true; | |
} | |
/* Add the bezier to the global edge table if it intersects the clipping region */ | |
function checkedAddBezierToGET(bezier) { | |
var lineWidth; | |
if (isWide(bezier)) { | |
lineWidth = wideBezierExtentOf(bezier); | |
} else { | |
lineWidth = 0; | |
} | |
if ((bezierEndYOf(bezier) + lineWidth) < fillMinYGet()) { | |
return 0; | |
} | |
if (((edgeXValueOf(bezier) - lineWidth) >= fillMaxXGet()) && ((bezierEndXOf(bezier) - lineWidth) >= fillMaxXGet())) { | |
return 0; | |
} | |
addEdgeToGET(bezier); | |
} | |
/* Add the edge to the global edge table. | |
For known edge types, check if the edge intersects the visible region */ | |
function checkedAddEdgeToGET(edge) { | |
if (isLine(edge)) { | |
return checkedAddLineToGET(edge); | |
} | |
if (isBezier(edge)) { | |
return checkedAddBezierToGET(edge); | |
} | |
addEdgeToGET(edge); | |
} | |
/* Add the line to the global edge table if it intersects the clipping region */ | |
function checkedAddLineToGET(line) { | |
var lineWidth; | |
if (isWide(line)) { | |
lineWidth = wideLineExtentOf(line); | |
} else { | |
lineWidth = 0; | |
} | |
if ((lineEndYOf(line) + lineWidth) < fillMinYGet()) { | |
return 0; | |
} | |
if (((edgeXValueOf(line) - lineWidth) >= fillMaxXGet()) && ((lineEndXOf(line) - lineWidth) >= fillMaxXGet())) { | |
return 0; | |
} | |
addEdgeToGET(line); | |
} | |
function circleCosTable() { | |
var theTable = | |
[1.0, 0.98078528040323, 0.923879532511287, 0.831469612302545, | |
0.7071067811865475, 0.555570233019602, 0.38268343236509, 0.1950903220161286, | |
0.0, -0.1950903220161283, -0.3826834323650896, -0.555570233019602, | |
-0.707106781186547, -0.831469612302545, -0.9238795325112865, -0.98078528040323, | |
-1.0, -0.98078528040323, -0.923879532511287, -0.831469612302545, | |
-0.707106781186548, -0.555570233019602, -0.3826834323650903, -0.1950903220161287, | |
0.0, 0.1950903220161282, 0.38268343236509, 0.555570233019602, | |
0.707106781186547, 0.831469612302545, 0.9238795325112865, 0.98078528040323, | |
1.0 ]; | |
return theTable; | |
} | |
function circleSinTable() { | |
var theTable = | |
[0.0, 0.1950903220161282, 0.3826834323650897, 0.555570233019602, | |
0.707106781186547, 0.831469612302545, 0.923879532511287, 0.98078528040323, | |
1.0, 0.98078528040323, 0.923879532511287, 0.831469612302545, | |
0.7071067811865475, 0.555570233019602, 0.38268343236509, 0.1950903220161286, | |
0.0, -0.1950903220161283, -0.3826834323650896, -0.555570233019602, | |
-0.707106781186547, -0.831469612302545, -0.9238795325112865, -0.98078528040323, | |
-1.0, -0.98078528040323, -0.923879532511287, -0.831469612302545, | |
-0.707106781186548, -0.555570233019602, -0.3826834323650903, -0.1950903220161287, | |
0.0 ]; | |
return theTable; | |
} | |
function clampValuemax(value, maxValue) { | |
if (value < 0) { | |
return 0; | |
} else { | |
if (value >= maxValue) { | |
return maxValue - 1; | |
} else { | |
return value; | |
} | |
} | |
} | |
/* Clear the current span buffer. | |
The span buffer is only cleared in the area that has been used by the previous scan line. */ | |
function clearSpanBuffer() { | |
var x0; | |
var x1; | |
x0 = SHR(spanStartGet(), aaShiftGet()); | |
x1 = (SHR(spanEndGet(), aaShiftGet())) + 1; | |
if (x0 < 0) { | |
x0 = 0; | |
} | |
if (x1 > spanSizeGet()) { | |
x1 = spanSizeGet(); | |
} | |
while (x0 < x1) { | |
spanBuffer[x0] = 0; | |
++x0; | |
} | |
spanStartPut(spanSizeGet()); | |
spanEndPut(0); | |
} | |
function clearSpanBufferGet() { | |
return workBuffer[GWClearSpanBuffer]; | |
} | |
function clearSpanBufferPut(value) { | |
return workBuffer[GWClearSpanBuffer] = value; | |
} | |
function clipMaxXGet() { | |
return workBuffer[GWClipMaxX]; | |
} | |
function clipMaxXPut(value) { | |
return workBuffer[GWClipMaxX] = value; | |
} | |
function clipMaxYGet() { | |
return workBuffer[GWClipMaxY]; | |
} | |
function clipMaxYPut(value) { | |
return workBuffer[GWClipMaxY] = value; | |
} | |
function clipMinXGet() { | |
return workBuffer[GWClipMinX]; | |
} | |
function clipMinXPut(value) { | |
return workBuffer[GWClipMinX] = value; | |
} | |
function clipMinYGet() { | |
return workBuffer[GWClipMinY]; | |
} | |
function clipMinYPut(value) { | |
return workBuffer[GWClipMinY] = value; | |
} | |
function colorTransform() { | |
return FPTR_ADD(workBuffer, GWColorTransform); | |
} | |
function colormapOf(bmFill) { | |
return PTR_ADD(objBuffer, bmFill + GBColormapOffset); | |
} | |
/* Split the bezier curve at the given parametric value. | |
Note: Since this method is only invoked to make non-monoton | |
beziers monoton we must check for the resulting y values | |
to be *really* between the start and end value. */ | |
function computeBeziersplitAt(index, param) { | |
var endX; | |
var endY; | |
var leftViaX; | |
var leftViaY; | |
var newIndex; | |
var rightViaX; | |
var rightViaY; | |
var sharedX; | |
var sharedY; | |
var startX; | |
var startY; | |
var viaX; | |
var viaY; | |
leftViaX = (startX = bzStartX(index)); | |
leftViaY = (startY = bzStartY(index)); | |
rightViaX = (viaX = bzViaX(index)); | |
rightViaY = (viaY = bzViaY(index)); | |
endX = bzEndX(index); | |
/* Compute intermediate points */ | |
endY = bzEndY(index); | |
sharedX = (leftViaX += (((viaX - startX) * param)|0)); | |
sharedY = (leftViaY += (((viaY - startY) * param)|0)); | |
rightViaX += (((endX - viaX) * param)|0); | |
/* Compute new shared point */ | |
rightViaY += (((endY - viaY) * param)|0); | |
sharedX += (((rightViaX - leftViaX) * param)|0); | |
/* Check the new via points */ | |
sharedY += (((rightViaY - leftViaY) * param)|0); | |
leftViaY = assureValuebetweenand(leftViaY, startY, sharedY); | |
rightViaY = assureValuebetweenand(rightViaY, sharedY, endY); | |
newIndex = allocateBezierStackEntry(); | |
if (engineStopped) { | |
return 0; | |
} | |
bzViaXput(index, leftViaX); | |
bzViaYput(index, leftViaY); | |
bzEndXput(index, sharedX); | |
bzEndYput(index, sharedY); | |
bzStartXput(newIndex, sharedX); | |
bzStartYput(newIndex, sharedY); | |
bzViaXput(newIndex, rightViaX); | |
bzViaYput(newIndex, rightViaY); | |
bzEndXput(newIndex, endX); | |
bzEndYput(newIndex, endY); | |
return newIndex; | |
} | |
/* Split the bezier curve at 0.5. */ | |
function computeBezierSplitAtHalf(index) { | |
var endX; | |
var endY; | |
var leftViaX; | |
var leftViaY; | |
var newIndex; | |
var rightViaX; | |
var rightViaY; | |
var sharedX; | |
var sharedY; | |
var startX; | |
var startY; | |
var viaX; | |
var viaY; | |
newIndex = allocateBezierStackEntry(); | |
if (engineStopped) { | |
return 0; | |
} | |
leftViaX = (startX = bzStartX(index)); | |
leftViaY = (startY = bzStartY(index)); | |
rightViaX = (viaX = bzViaX(index)); | |
rightViaY = (viaY = bzViaY(index)); | |
endX = bzEndX(index); | |
/* Compute intermediate points */ | |
endY = bzEndY(index); | |
leftViaX += (viaX - startX) >> 1; | |
leftViaY += (viaY - startY) >> 1; | |
sharedX = (rightViaX += (endX - viaX) >> 1); | |
/* Compute new shared point */ | |
sharedY = (rightViaY += (endY - viaY) >> 1); | |
sharedX += (leftViaX - rightViaX) >> 1; | |
/* Store the first part back */ | |
sharedY += (leftViaY - rightViaY) >> 1; | |
bzViaXput(index, leftViaX); | |
bzViaYput(index, leftViaY); | |
bzEndXput(index, sharedX); | |
bzEndYput(index, sharedY); | |
bzStartXput(newIndex, sharedX); | |
bzStartYput(newIndex, sharedY); | |
bzViaXput(newIndex, rightViaX); | |
bzViaYput(newIndex, rightViaY); | |
bzEndXput(newIndex, endX); | |
bzEndYput(newIndex, endY); | |
return newIndex; | |
} | |
/* Get both values from the two boundaries of the given bezier | |
and compute the actual position/width of the line */ | |
function computeFinalWideBezierValueswidth(bezier, lineWidth) { | |
var leftX; | |
var rightX; | |
var temp; | |
leftX = bezierUpdateDataOf(bezier)[GBUpdateX] >> 8; | |
rightX = wideBezierUpdateDataOf(bezier)[GBUpdateX] >> 8; | |
if (leftX > rightX) { | |
temp = leftX; | |
leftX = rightX; | |
rightX = temp; | |
} | |
edgeXValueOfput(bezier, leftX); | |
if ((rightX - leftX) > lineWidth) { | |
wideBezierWidthOfput(bezier, rightX - leftX); | |
} else { | |
wideBezierWidthOfput(bezier, lineWidth); | |
} | |
} | |
function computeSqrt(length2) { | |
if (length2 < 32) { | |
return smallSqrtTable()[length2]; | |
} else { | |
return ((Math.sqrt(length2) + 0.5)|0); | |
} | |
} | |
function copyBitsFromtoat(x0, x1, yValue) { | |
if (!copyBitsFn) { | |
/* We need copyBits here so try to load it implicitly */ | |
if (!initialiseModule()) { | |
return false; | |
} | |
} | |
return copyBitsFn(x0, x1, yValue); | |
} | |
/* Create the global edge table */ | |
function createGlobalEdgeTable() { | |
var end; | |
var object; | |
object = 0; | |
end = objUsed; | |
while (object < end) { | |
/* Note: addEdgeToGET: may fail on insufficient space but that's not a problem here */ | |
if (isEdge(object)) { | |
/* Check if the edge starts below fillMaxY. */ | |
if (!(edgeYValueOf(object) >= fillMaxYGet())) { | |
checkedAddEdgeToGET(object); | |
} | |
} | |
object += objectLengthOf(object); | |
} | |
} | |
function currentYGet() { | |
return workBuffer[GWCurrentY]; | |
} | |
function currentYPut(value) { | |
return workBuffer[GWCurrentY] = value; | |
} | |
function currentZGet() { | |
return workBuffer[GWCurrentZ]; | |
} | |
function currentZPut(value) { | |
return workBuffer[GWCurrentZ] = value; | |
} | |
function destOffsetXGet() { | |
return workBuffer[GWDestOffsetX]; | |
} | |
function destOffsetXPut(value) { | |
return workBuffer[GWDestOffsetX] = value; | |
} | |
function destOffsetYGet() { | |
return workBuffer[GWDestOffsetY]; | |
} | |
function destOffsetYPut(value) { | |
return workBuffer[GWDestOffsetY] = value; | |
} | |
/* Display the span buffer at the current scan line. */ | |
function displaySpanBufferAt(y) { | |
var targetX0; | |
var targetX1; | |
var targetY; | |
/* self aaLevelGet > 1 ifTrue:[self adjustAALevel]. */ | |
targetX0 = SHR(spanStartGet(), aaShiftGet()); | |
if (targetX0 < clipMinXGet()) { | |
targetX0 = clipMinXGet(); | |
} | |
targetX1 = SHR(((spanEndGet() + aaLevelGet()) - 1), aaShiftGet()); | |
if (targetX1 > clipMaxXGet()) { | |
targetX1 = clipMaxXGet(); | |
} | |
targetY = SHR(y, aaShiftGet()); | |
if ((targetY < clipMinYGet()) || ((targetY >= clipMaxYGet()) || ((targetX1 < clipMinXGet()) || (targetX0 >= clipMaxXGet())))) { | |
return 0; | |
} | |
copyBitsFromtoat(targetX0, targetX1, targetY); | |
} | |
function edgeFillsInvalidate(edge) { | |
return objectTypeOfput(edge, objectTypeOf(edge) | GEEdgeFillsInvalid); | |
} | |
function edgeFillsValidate(edge) { | |
return objectTypeOfput(edge, objectTypeOf(edge) & ~GEEdgeFillsInvalid); | |
} | |
function edgeLeftFillOf(edge) { | |
return objat(edge, GEFillIndexLeft); | |
} | |
function edgeLeftFillOfput(edge, value) { | |
return objatput(edge, GEFillIndexLeft, value); | |
} | |
function edgeNumLinesOf(edge) { | |
return objat(edge, GENumLines); | |
} | |
function edgeNumLinesOfput(edge, value) { | |
return objatput(edge, GENumLines, value); | |
} | |
function edgeRightFillOf(edge) { | |
return objat(edge, GEFillIndexRight); | |
} | |
function edgeRightFillOfput(edge, value) { | |
return objatput(edge, GEFillIndexRight, value); | |
} | |
function edgeTransform() { | |
return FPTR_ADD(workBuffer, GWEdgeTransform); | |
} | |
/* Return the edge type (e.g., witout the wide edge flag) */ | |
function edgeTypeOf(edge) { | |
return objectTypeOf(edge) >>> 1; | |
} | |
function edgeXValueOf(edge) { | |
return objat(edge, GEXValue); | |
} | |
function edgeXValueOfput(edge, value) { | |
return objatput(edge, GEXValue, value); | |
} | |
function edgeYValueOf(edge) { | |
return objat(edge, GEYValue); | |
} | |
function edgeYValueOfput(edge, value) { | |
return objatput(edge, GEYValue, value); | |
} | |
function edgeZValueOf(edge) { | |
return objat(edge, GEZValue); | |
} | |
function edgeZValueOfput(edge, value) { | |
return objatput(edge, GEZValue, value); | |
} | |
/* Ignore dispatch errors when translating to C | |
(since we have no entry point for #error in the VM proxy) */ | |
function errorWrongIndex() { | |
; | |
} | |
/* Fill the span buffer from leftX to rightX with the given fill. */ | |
function fillAllFromto(leftX, rightX) { | |
var fill; | |
var startX; | |
var stopX; | |
fill = topFill(); | |
startX = leftX; | |
stopX = topRightX(); | |
while (stopX < rightX) { | |
fill = topFill(); | |
if (fill !== 0) { | |
if (fillSpanfromto(fill, startX, stopX)) { | |
return true; | |
} | |
} | |
quickRemoveInvalidFillsAt(stopX); | |
startX = stopX; | |
stopX = topRightX(); | |
} | |
fill = topFill(); | |
if (fill !== 0) { | |
return fillSpanfromto(fill, startX, rightX); | |
} | |
return false; | |
} | |
function fillBitmapSpan() { | |
return fillBitmapSpanfromtoat(lastExportedFillGet(), lastExportedLeftXGet(), lastExportedRightXGet(), currentYGet()); | |
} | |
/* Fill the span buffer between leftEdge and rightEdge using the given bits. | |
Note: We always start from zero - this avoids using huge bitmap buffers if the bitmap is to be displayed at the very far right hand side and also gives us a chance of using certain bitmaps (e.g., those with depth 32) directly. */ | |
function fillBitmapSpanfromto(bits, leftX, rightX) { | |
var baseShift; | |
var bitX; | |
var colorMask; | |
var colorShift; | |
var fillValue; | |
var x; | |
var x0; | |
var x1; | |
x0 = leftX; | |
x1 = rightX; | |
/* Hack for pre-increment */ | |
bitX = -1; | |
if (aaLevelGet() === 1) { | |
/* Speedy version for no anti-aliasing */ | |
while (x0 < x1) { | |
fillValue = (bits[(++bitX)]|0); | |
spanBuffer[x0] = fillValue; | |
++x0; | |
} | |
} else { | |
/* Generic version with anti-aliasing */ | |
colorMask = aaColorMaskGet(); | |
colorShift = aaColorShiftGet(); | |
baseShift = aaShiftGet(); | |
while (x0 < x1) { | |
x = SHR(x0, baseShift); | |
fillValue = (bits[(++bitX)]|0); | |
fillValue = SHR((fillValue & colorMask), colorShift); | |
spanBuffer[x] = (spanBuffer[x] + fillValue); | |
++x0; | |
} | |
} | |
if (x1 > spanEndGet()) { | |
spanEndPut(x1); | |
} | |
if (x1 > spanEndAAGet()) { | |
spanEndAAPut(x1); | |
} | |
} | |
function fillBitmapSpanfromtoat(bmFill, leftX, rightX, yValue) { | |
var bits; | |
var bmHeight; | |
var bmWidth; | |
var deltaX; | |
var deltaY; | |
var ds; | |
var dsX; | |
var dt; | |
var dtX; | |
var fillValue; | |
var tileFlag; | |
var x; | |
var x1; | |
var xp; | |
var yp; | |
if (aaLevelGet() !== 1) { | |
return fillBitmapSpanAAfromtoat(bmFill, leftX, rightX, yValue); | |
} | |
bits = loadBitsFrom(bmFill); | |
if (!bits) { | |
return null; | |
} | |
bmWidth = bitmapWidthOf(bmFill); | |
bmHeight = bitmapHeightOf(bmFill); | |
tileFlag = bitmapTileFlagOf(bmFill) === 1; | |
deltaX = leftX - fillOriginXOf(bmFill); | |
deltaY = yValue - fillOriginYOf(bmFill); | |
dsX = fillDirectionXOf(bmFill); | |
dtX = fillNormalXOf(bmFill); | |
ds = (deltaX * dsX) + (deltaY * fillDirectionYOf(bmFill)); | |
dt = (deltaX * dtX) + (deltaY * fillNormalYOf(bmFill)); | |
x = leftX; | |
x1 = rightX; | |
while (x < x1) { | |
if (tileFlag) { | |
ds = repeatValuemax(ds, bmWidth << 16); | |
dt = repeatValuemax(dt, bmHeight << 16); | |
} | |
xp = ds >> 16; | |
yp = dt >> 16; | |
if (!tileFlag) { | |
xp = clampValuemax(xp, bmWidth); | |
yp = clampValuemax(yp, bmHeight); | |
} | |
if ((xp >= 0) && ((yp >= 0) && ((xp < bmWidth) && (yp < bmHeight)))) { | |
fillValue = bitmapValuebitsatXy(bmFill, bits, xp, yp); | |
spanBuffer[x] = fillValue; | |
} | |
ds += dsX; | |
dt += dtX; | |
++x; | |
} | |
} | |
function fillBitmapSpanAAfromtoat(bmFill, leftX, rightX, yValue) { | |
var aaLevel; | |
var baseShift; | |
var bits; | |
var bmHeight; | |
var bmWidth; | |
var cMask; | |
var cShift; | |
var deltaX; | |
var deltaY; | |
var ds; | |
var dsX; | |
var dt; | |
var dtX; | |
var fillValue; | |
var firstPixel; | |
var idx; | |
var lastPixel; | |
var tileFlag; | |
var x; | |
var xp; | |
var yp; | |
bits = loadBitsFrom(bmFill); | |
if (!bits) { | |
return null; | |
} | |
bmWidth = bitmapWidthOf(bmFill); | |
bmHeight = bitmapHeightOf(bmFill); | |
tileFlag = bitmapTileFlagOf(bmFill) === 1; | |
deltaX = leftX - fillOriginXOf(bmFill); | |
deltaY = yValue - fillOriginYOf(bmFill); | |
dsX = fillDirectionXOf(bmFill); | |
dtX = fillNormalXOf(bmFill); | |
ds = (deltaX * dsX) + (deltaY * fillDirectionYOf(bmFill)); | |
dt = (deltaX * dtX) + (deltaY * fillNormalYOf(bmFill)); | |
aaLevel = aaLevelGet(); | |
firstPixel = aaFirstPixelFromto(leftX, rightX); | |
lastPixel = aaLastPixelFromto(leftX, rightX); | |
baseShift = aaShiftGet(); | |
cMask = aaColorMaskGet(); | |
cShift = aaColorShiftGet(); | |
x = leftX; | |
while (x < firstPixel) { | |
if (tileFlag) { | |
ds = repeatValuemax(ds, bmWidth << 16); | |
dt = repeatValuemax(dt, bmHeight << 16); | |
} | |
xp = ds >> 16; | |
yp = dt >> 16; | |
if (!tileFlag) { | |
xp = clampValuemax(xp, bmWidth); | |
yp = clampValuemax(yp, bmHeight); | |
} | |
if ((xp >= 0) && ((yp >= 0) && ((xp < bmWidth) && (yp < bmHeight)))) { | |
fillValue = bitmapValuebitsatXy(bmFill, bits, xp, yp); | |
fillValue = SHR((fillValue & cMask), cShift); | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + fillValue); | |
} | |
ds += dsX; | |
dt += dtX; | |
++x; | |
} | |
cMask = (SHR(aaColorMaskGet(), aaShiftGet())) | 4042322160; | |
cShift = aaShiftGet(); | |
while (x < lastPixel) { | |
if (tileFlag) { | |
ds = repeatValuemax(ds, bmWidth << 16); | |
dt = repeatValuemax(dt, bmHeight << 16); | |
} | |
xp = ds >> 16; | |
yp = dt >> 16; | |
if (!tileFlag) { | |
xp = clampValuemax(xp, bmWidth); | |
yp = clampValuemax(yp, bmHeight); | |
} | |
if ((xp >= 0) && ((yp >= 0) && ((xp < bmWidth) && (yp < bmHeight)))) { | |
fillValue = bitmapValuebitsatXy(bmFill, bits, xp, yp); | |
fillValue = SHR((fillValue & cMask), cShift); | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + fillValue); | |
} | |
ds += SHL(dsX, cShift); | |
dt += SHL(dtX, cShift); | |
x += aaLevel; | |
} | |
cMask = aaColorMaskGet(); | |
cShift = aaColorShiftGet(); | |
while (x < rightX) { | |
if (tileFlag) { | |
ds = repeatValuemax(ds, bmWidth << 16); | |
dt = repeatValuemax(dt, bmHeight << 16); | |
} | |
xp = ds >> 16; | |
yp = dt >> 16; | |
if (!tileFlag) { | |
xp = clampValuemax(xp, bmWidth); | |
yp = clampValuemax(yp, bmHeight); | |
} | |
if ((xp >= 0) && ((yp >= 0) && ((xp < bmWidth) && (yp < bmHeight)))) { | |
fillValue = bitmapValuebitsatXy(bmFill, bits, xp, yp); | |
fillValue = SHR((fillValue & cMask), cShift); | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + fillValue); | |
} | |
ds += dsX; | |
dt += dtX; | |
++x; | |
} | |
} | |
/* Fill the span buffer between leftEdge and rightEdge with the given pixel value. */ | |
function fillColorSpanfromto(pixelValue32, leftX, rightX) { | |
var x0; | |
var x1; | |
/* Use a unrolled version for anti-aliased fills... */ | |
if (aaLevelGet() !== 1) { | |
return fillColorSpanAAx0x1(pixelValue32, leftX, rightX); | |
} | |
x0 = leftX; | |
/* Unroll the inner loop four times, since we're only storing data. */ | |
x1 = rightX; | |
while ((x0 + 4) < x1) { | |
spanBuffer[x0] = pixelValue32; | |
spanBuffer[x0 + 1] = pixelValue32; | |
spanBuffer[x0 + 2] = pixelValue32; | |
spanBuffer[x0 + 3] = pixelValue32; | |
x0 += 4; | |
} | |
while (x0 < x1) { | |
spanBuffer[x0] = pixelValue32; | |
++x0; | |
} | |
} | |
/* This is the inner loop for solid color fills with anti-aliasing. | |
This loop has been unrolled for speed and quality into three parts: | |
a) copy all pixels that fall into the first full pixel. | |
b) copy aaLevel pixels between the first and the last full pixel | |
c) copy all pixels that fall in the last full pixel */ | |
function fillColorSpanAAx0x1(pixelValue32, leftX, rightX) { | |
var aaLevel; | |
var baseShift; | |
var colorMask; | |
var firstPixel; | |
var idx; | |
var lastPixel; | |
var pv32; | |
var x; | |
/* Not now -- maybe later */ | |
/* Compute the pixel boundaries. */ | |
firstPixel = aaFirstPixelFromto(leftX, rightX); | |
lastPixel = aaLastPixelFromto(leftX, rightX); | |
aaLevel = aaLevelGet(); | |
baseShift = aaShiftGet(); | |
/* Part a: Deal with the first n sub-pixels */ | |
x = leftX; | |
if (x < firstPixel) { | |
pv32 = SHR((pixelValue32 & aaColorMaskGet()), aaColorShiftGet()); | |
while (x < firstPixel) { | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + pv32); | |
++x; | |
} | |
} | |
if (x < lastPixel) { | |
colorMask = (SHR(aaColorMaskGet(), aaShiftGet())) | 4042322160; | |
pv32 = SHR((pixelValue32 & colorMask), aaShiftGet()); | |
while (x < lastPixel) { | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + pv32); | |
x += aaLevel; | |
} | |
} | |
if (x < rightX) { | |
pv32 = SHR((pixelValue32 & aaColorMaskGet()), aaColorShiftGet()); | |
while (x < rightX) { | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + pv32); | |
++x; | |
} | |
} | |
} | |
function fillDirectionXOf(fill) { | |
return objat(fill, GFDirectionX); | |
} | |
function fillDirectionXOfput(fill, value) { | |
return objatput(fill, GFDirectionX, value); | |
} | |
function fillDirectionYOf(fill) { | |
return objat(fill, GFDirectionY); | |
} | |
function fillDirectionYOfput(fill, value) { | |
return objatput(fill, GFDirectionY, value); | |
} | |
function fillLinearGradient() { | |
return fillLinearGradientfromtoat(lastExportedFillGet(), lastExportedLeftXGet(), lastExportedRightXGet(), currentYGet()); | |
} | |
/* Draw a linear gradient fill. */ | |
function fillLinearGradientfromtoat(fill, leftX, rightX, yValue) { | |
var ds; | |
var dsX; | |
var ramp; | |
var rampIndex; | |
var rampSize; | |
var x; | |
var x0; | |
var x1; | |
ramp = gradientRampOf(fill); | |
rampSize = gradientRampLengthOf(fill); | |
dsX = fillDirectionXOf(fill); | |
ds = ((leftX - fillOriginXOf(fill)) * dsX) + ((yValue - fillOriginYOf(fill)) * fillDirectionYOf(fill)); | |
x = (x0 = leftX); | |
/* Note: The inner loop has been divided into three parts for speed */ | |
/* Part one: Fill everything outside the left boundary */ | |
x1 = rightX; | |
while (((((rampIndex = ds >> 16)) < 0) || (rampIndex >= rampSize)) && (x < x1)) { | |
++x; | |
ds += dsX; | |
} | |
if (x > x0) { | |
if (rampIndex < 0) { | |
rampIndex = 0; | |
} | |
if (rampIndex >= rampSize) { | |
rampIndex = rampSize - 1; | |
} | |
fillColorSpanfromto(ramp[rampIndex], x0, x); | |
} | |
if (aaLevelGet() === 1) { | |
/* Fast version w/o anti-aliasing */ | |
while (((((rampIndex = ds >> 16)) < rampSize) && (rampIndex >= 0)) && (x < x1)) { | |
spanBuffer[x] = ramp[rampIndex]; | |
++x; | |
ds += dsX; | |
} | |
} else { | |
x = fillLinearGradientAArampdsdsXfromto(fill, ramp, ds, dsX, x, rightX); | |
} | |
if (x < x1) { | |
if (rampIndex < 0) { | |
rampIndex = 0; | |
} | |
if (rampIndex >= rampSize) { | |
rampIndex = rampSize - 1; | |
} | |
fillColorSpanfromto(ramp[rampIndex], x, x1); | |
} | |
} | |
/* This is the AA version of linear gradient filling. */ | |
function fillLinearGradientAArampdsdsXfromto(fill, ramp, deltaS, dsX, leftX, rightX) { | |
var aaLevel; | |
var baseShift; | |
var colorMask; | |
var colorShift; | |
var ds; | |
var firstPixel; | |
var idx; | |
var lastPixel; | |
var rampIndex; | |
var rampSize; | |
var rampValue; | |
var x; | |
aaLevel = aaLevelGet(); | |
baseShift = aaShiftGet(); | |
rampSize = gradientRampLengthOf(fill); | |
ds = deltaS; | |
x = leftX; | |
rampIndex = ds >> 16; | |
firstPixel = aaFirstPixelFromto(leftX, rightX); | |
/* Deal with the first n sub-pixels */ | |
lastPixel = aaLastPixelFromto(leftX, rightX); | |
colorMask = aaColorMaskGet(); | |
colorShift = aaColorShiftGet(); | |
while ((x < firstPixel) && ((rampIndex < rampSize) && (rampIndex >= 0))) { | |
rampValue = ramp[rampIndex]; | |
/* Copy as many pixels as possible */ | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while ((x < firstPixel) && ((ds >> 16) === rampIndex)) { | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + rampValue); | |
++x; | |
ds += dsX; | |
} | |
rampIndex = ds >> 16; | |
} | |
colorMask = (SHR(aaColorMaskGet(), aaShiftGet())) | 4042322160; | |
colorShift = aaShiftGet(); | |
while ((x < lastPixel) && ((rampIndex < rampSize) && (rampIndex >= 0))) { | |
rampValue = ramp[rampIndex]; | |
/* Copy as many pixels as possible */ | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while ((x < lastPixel) && ((ds >> 16) === rampIndex)) { | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + rampValue); | |
x += aaLevel; | |
ds += SHL(dsX, colorShift); | |
} | |
rampIndex = ds >> 16; | |
} | |
colorMask = aaColorMaskGet(); | |
colorShift = aaColorShiftGet(); | |
while ((x < rightX) && ((rampIndex < rampSize) && (rampIndex >= 0))) { | |
rampValue = ramp[rampIndex]; | |
/* Copy as many pixels as possible */ | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while ((x < rightX) && ((ds >> 16) === rampIndex)) { | |
idx = SHR(x, baseShift); | |
spanBuffer[idx] = (spanBuffer[idx] + rampValue); | |
++x; | |
ds += dsX; | |
} | |
rampIndex = ds >> 16; | |
} | |
return x; | |
} | |
function fillMaxXGet() { | |
return workBuffer[GWFillMaxX]; | |
} | |
function fillMaxXPut(value) { | |
return workBuffer[GWFillMaxX] = value; | |
} | |
function fillMaxYGet() { | |
return workBuffer[GWFillMaxY]; | |
} | |
function fillMaxYPut(value) { | |
return workBuffer[GWFillMaxY] = value; | |
} | |
function fillMinXGet() { | |
return workBuffer[GWFillMinX]; | |
} | |
function fillMinXPut(value) { | |
return workBuffer[GWFillMinX] = value; | |
} | |
function fillMinYGet() { | |
return workBuffer[GWFillMinY]; | |
} | |
function fillMinYPut(value) { | |
return workBuffer[GWFillMinY] = value; | |
} | |
function fillNormalXOf(fill) { | |
return objat(fill, GFNormalX); | |
} | |
function fillNormalXOfput(fill, value) { | |
return objatput(fill, GFNormalX, value); | |
} | |
function fillNormalYOf(fill) { | |
return objat(fill, GFNormalY); | |
} | |
function fillNormalYOfput(fill, value) { | |
return objatput(fill, GFNormalY, value); | |
} | |
function fillOriginXOf(fill) { | |
return objat(fill, GFOriginX); | |
} | |
function fillOriginXOfput(fill, value) { | |
return objatput(fill, GFOriginX, value); | |
} | |
function fillOriginYOf(fill) { | |
return objat(fill, GFOriginY); | |
} | |
function fillOriginYOfput(fill, value) { | |
return objatput(fill, GFOriginY, value); | |
} | |
/* Part 2a) Compute the decreasing part of the ramp */ | |
function fillRadialDecreasingrampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, leftX, rightX) { | |
var ds; | |
var dt; | |
var length2; | |
var nextLength; | |
var rampIndex; | |
var rampValue; | |
var x; | |
var x1; | |
ds = (deltaST[0]|0); | |
dt = (deltaST[1]|0); | |
rampIndex = accurateLengthOfwith(ds >> 16, dt >> 16); | |
rampValue = ramp[rampIndex]; | |
length2 = (rampIndex - 1) * (rampIndex - 1); | |
x = leftX; | |
x1 = rightX; | |
if (x1 > fillOriginXOf(fill)) { | |
x1 = fillOriginXOf(fill); | |
} | |
while (x < x1) { | |
/* Try to copy the current value more than just once */ | |
while ((x < x1) && (squaredLengthOfwith(ds >> 16, dt >> 16) >= length2)) { | |
spanBuffer[x] = rampValue; | |
++x; | |
ds += dsX; | |
dt += dtX; | |
} | |
nextLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (nextLength < length2) { | |
--rampIndex; | |
rampValue = ramp[rampIndex]; | |
length2 = (rampIndex - 1) * (rampIndex - 1); | |
} | |
} | |
deltaST[0] = ds; | |
deltaST[1] = dt; | |
return x; | |
} | |
/* Part 2a) Compute the decreasing part of the ramp */ | |
function fillRadialDecreasingAArampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, leftX, rightX) { | |
var aaLevel; | |
var baseShift; | |
var colorMask; | |
var colorShift; | |
var ds; | |
var dt; | |
var firstPixel; | |
var index; | |
var lastPixel; | |
var length2; | |
var nextLength; | |
var rampIndex; | |
var rampValue; | |
var x; | |
var x1; | |
ds = (deltaST[0]|0); | |
dt = (deltaST[1]|0); | |
aaLevel = aaLevelGet(); | |
baseShift = aaShiftGet(); | |
rampIndex = accurateLengthOfwith(ds >> 16, dt >> 16); | |
length2 = (rampIndex - 1) * (rampIndex - 1); | |
x = leftX; | |
x1 = fillOriginXOf(fill); | |
if (x1 > rightX) { | |
x1 = rightX; | |
} | |
firstPixel = aaFirstPixelFromto(leftX, x1); | |
/* Deal with the first n sub-pixels */ | |
lastPixel = aaLastPixelFromto(leftX, x1); | |
if (x < firstPixel) { | |
colorMask = aaColorMaskGet(); | |
colorShift = aaColorShiftGet(); | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while (x < firstPixel) { | |
/* Try to copy the current value more than just once */ | |
while ((x < firstPixel) && (squaredLengthOfwith(ds >> 16, dt >> 16) >= length2)) { | |
index = SHR(x, baseShift); | |
spanBuffer[index] = (spanBuffer[index] + rampValue); | |
++x; | |
ds += dsX; | |
dt += dtX; | |
} | |
nextLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (nextLength < length2) { | |
--rampIndex; | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
length2 = (rampIndex - 1) * (rampIndex - 1); | |
} | |
} | |
} | |
if (x < lastPixel) { | |
colorMask = (SHR(aaColorMaskGet(), aaShiftGet())) | 4042322160; | |
colorShift = aaShiftGet(); | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while (x < lastPixel) { | |
/* Try to copy the current value more than just once */ | |
while ((x < lastPixel) && (squaredLengthOfwith(ds >> 16, dt >> 16) >= length2)) { | |
index = SHR(x, baseShift); | |
spanBuffer[index] = (spanBuffer[index] + rampValue); | |
x += aaLevel; | |
ds += SHL(dsX, colorShift); | |
dt += SHL(dtX, colorShift); | |
} | |
nextLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (nextLength < length2) { | |
--rampIndex; | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
length2 = (rampIndex - 1) * (rampIndex - 1); | |
} | |
} | |
} | |
if (x < x1) { | |
colorMask = aaColorMaskGet(); | |
colorShift = aaColorShiftGet(); | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while (x < x1) { | |
/* Try to copy the current value more than just once */ | |
while ((x < x1) && (squaredLengthOfwith(ds >> 16, dt >> 16) >= length2)) { | |
index = SHR(x, baseShift); | |
spanBuffer[index] = (spanBuffer[index] + rampValue); | |
++x; | |
ds += dsX; | |
dt += dtX; | |
} | |
nextLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (nextLength < length2) { | |
--rampIndex; | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
length2 = (rampIndex - 1) * (rampIndex - 1); | |
} | |
} | |
} | |
deltaST[0] = ds; | |
deltaST[1] = dt; | |
return x; | |
} | |
function fillRadialGradient() { | |
return fillRadialGradientfromtoat(lastExportedFillGet(), lastExportedLeftXGet(), lastExportedRightXGet(), currentYGet()); | |
} | |
/* Draw a radial gradient fill. */ | |
function fillRadialGradientfromtoat(fill, leftX, rightX, yValue) { | |
var deltaST; | |
var deltaX; | |
var deltaY; | |
var ds; | |
var dsX; | |
var dt; | |
var dtX; | |
var length2; | |
var ramp; | |
var rampSize; | |
var x; | |
var x1; | |
ramp = gradientRampOf(fill); | |
rampSize = gradientRampLengthOf(fill); | |
deltaX = leftX - fillOriginXOf(fill); | |
deltaY = yValue - fillOriginYOf(fill); | |
dsX = fillDirectionXOf(fill); | |
dtX = fillNormalXOf(fill); | |
ds = (deltaX * dsX) + (deltaY * fillDirectionYOf(fill)); | |
dt = (deltaX * dtX) + (deltaY * fillNormalYOf(fill)); | |
x = leftX; | |
/* Note: The inner loop has been divided into three parts for speed */ | |
/* Part one: Fill everything outside the left boundary */ | |
x1 = rightX; | |
/* This is the upper bound */ | |
length2 = (rampSize - 1) * (rampSize - 1); | |
while ((squaredLengthOfwith(ds >> 16, dt >> 16) >= length2) && (x < x1)) { | |
++x; | |
ds += dsX; | |
dt += dtX; | |
} | |
if (x > leftX) { | |
fillColorSpanfromto(ramp[rampSize - 1], leftX, x); | |
} | |
deltaST = point1Get(); | |
deltaST[0] = ds; | |
deltaST[1] = dt; | |
if (x < fillOriginXOf(fill)) { | |
/* Draw the decreasing part */ | |
if (aaLevelGet() === 1) { | |
x = fillRadialDecreasingrampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, x, x1); | |
} else { | |
x = fillRadialDecreasingAArampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, x, x1); | |
} | |
} | |
if (x < x1) { | |
/* Draw the increasing part */ | |
if (aaLevelGet() === 1) { | |
x = fillRadialIncreasingrampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, x, x1); | |
} else { | |
x = fillRadialIncreasingAArampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, x, x1); | |
} | |
} | |
if (x < rightX) { | |
fillColorSpanfromto(ramp[rampSize - 1], x, rightX); | |
} | |
} | |
/* Part 2b) Compute the increasing part of the ramp */ | |
function fillRadialIncreasingrampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, leftX, rightX) { | |
var ds; | |
var dt; | |
var lastLength; | |
var length2; | |
var nextLength; | |
var rampIndex; | |
var rampSize; | |
var rampValue; | |
var x; | |
var x1; | |
ds = (deltaST[0]|0); | |
dt = (deltaST[1]|0); | |
rampIndex = accurateLengthOfwith(ds >> 16, dt >> 16); | |
rampValue = ramp[rampIndex]; | |
rampSize = gradientRampLengthOf(fill); | |
/* This is the upper bound */ | |
length2 = (rampSize - 1) * (rampSize - 1); | |
nextLength = (rampIndex + 1) * (rampIndex + 1); | |
lastLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
x = leftX; | |
x1 = rightX; | |
while ((x < x1) && (lastLength < length2)) { | |
/* Try to copy the current value more than once */ | |
while ((x < x1) && (squaredLengthOfwith(ds >> 16, dt >> 16) <= nextLength)) { | |
spanBuffer[x] = rampValue; | |
++x; | |
ds += dsX; | |
dt += dtX; | |
} | |
lastLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (lastLength > nextLength) { | |
++rampIndex; | |
rampValue = ramp[rampIndex]; | |
nextLength = (rampIndex + 1) * (rampIndex + 1); | |
} | |
} | |
deltaST[0] = ds; | |
deltaST[1] = dt; | |
return x; | |
} | |
/* Part 2b) Compute the increasing part of the ramp */ | |
function fillRadialIncreasingAArampdeltaSTdsXdtXfromto(fill, ramp, deltaST, dsX, dtX, leftX, rightX) { | |
var aaLevel; | |
var baseShift; | |
var colorMask; | |
var colorShift; | |
var ds; | |
var dt; | |
var firstPixel; | |
var index; | |
var lastLength; | |
var lastPixel; | |
var length2; | |
var nextLength; | |
var rampIndex; | |
var rampSize; | |
var rampValue; | |
var x; | |
ds = (deltaST[0]|0); | |
dt = (deltaST[1]|0); | |
aaLevel = aaLevelGet(); | |
baseShift = aaShiftGet(); | |
rampIndex = accurateLengthOfwith(ds >> 16, dt >> 16); | |
rampSize = gradientRampLengthOf(fill); | |
/* This is the upper bound */ | |
length2 = (rampSize - 1) * (rampSize - 1); | |
nextLength = (rampIndex + 1) * (rampIndex + 1); | |
lastLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
x = leftX; | |
firstPixel = aaFirstPixelFromto(leftX, rightX); | |
/* Deal with the first n subPixels */ | |
lastPixel = aaLastPixelFromto(leftX, rightX); | |
if ((x < firstPixel) && (lastLength < length2)) { | |
colorMask = aaColorMaskGet(); | |
colorShift = aaColorShiftGet(); | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while ((x < firstPixel) && (lastLength < length2)) { | |
/* Try to copy the current value more than once */ | |
while ((x < firstPixel) && (squaredLengthOfwith(ds >> 16, dt >> 16) <= nextLength)) { | |
index = SHR(x, baseShift); | |
spanBuffer[index] = (spanBuffer[index] + rampValue); | |
++x; | |
ds += dsX; | |
dt += dtX; | |
} | |
lastLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (lastLength > nextLength) { | |
++rampIndex; | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
nextLength = (rampIndex + 1) * (rampIndex + 1); | |
} | |
} | |
} | |
if ((x < lastPixel) && (lastLength < length2)) { | |
colorMask = (SHR(aaColorMaskGet(), aaShiftGet())) | 4042322160; | |
colorShift = aaShiftGet(); | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while ((x < lastPixel) && (lastLength < length2)) { | |
/* Try to copy the current value more than once */ | |
while ((x < lastPixel) && (squaredLengthOfwith(ds >> 16, dt >> 16) <= nextLength)) { | |
index = SHR(x, baseShift); | |
spanBuffer[index] = (spanBuffer[index] + rampValue); | |
x += aaLevel; | |
ds += SHL(dsX, colorShift); | |
dt += SHL(dtX, colorShift); | |
} | |
lastLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (lastLength > nextLength) { | |
++rampIndex; | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
nextLength = (rampIndex + 1) * (rampIndex + 1); | |
} | |
} | |
} | |
if ((x < rightX) && (lastLength < length2)) { | |
colorMask = aaColorMaskGet(); | |
colorShift = aaColorShiftGet(); | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
while ((x < rightX) && (lastLength < length2)) { | |
/* Try to copy the current value more than once */ | |
while ((x < rightX) && (squaredLengthOfwith(ds >> 16, dt >> 16) <= nextLength)) { | |
index = SHR(x, baseShift); | |
spanBuffer[index] = (spanBuffer[index] + rampValue); | |
++x; | |
ds += dsX; | |
dt += dtX; | |
} | |
lastLength = squaredLengthOfwith(ds >> 16, dt >> 16); | |
while (lastLength > nextLength) { | |
++rampIndex; | |
rampValue = ramp[rampIndex]; | |
rampValue = SHR((rampValue & colorMask), colorShift); | |
nextLength = (rampIndex + 1) * (rampIndex + 1); | |
} | |
} | |
} | |
deltaST[0] = ds; | |
deltaST[1] = dt; | |
return x; | |
} | |
/* Return true if fillEntry1 should be drawn before fillEntry2 */ | |
function fillSortsbefore(fillEntry1, fillEntry2) { | |
var diff; | |
/* First check the depth value */ | |
diff = stackFillDepth(fillEntry1) - stackFillDepth(fillEntry2); | |
if (diff !== 0) { | |
return diff > 0; | |
} | |
return (stackFillValue(fillEntry1)>>>0) < (stackFillValue(fillEntry2)>>>0); | |
} | |
/* Fill the span buffer from leftX to rightX with the given fill. | |
Clip before performing any operations. Return true if the fill must | |
be handled by some Smalltalk code. */ | |
function fillSpanfromto(fill, leftX, rightX) { | |
var type; | |
var x0; | |
var x1; | |
if (fill === 0) { | |
return false; | |
} | |
if (leftX < spanEndAAGet()) { | |
x0 = spanEndAAGet(); | |
} else { | |
x0 = leftX; | |
} | |
if (rightX > (SHL(spanSizeGet(), aaShiftGet()))) { | |
x1 = SHL(spanSizeGet(), aaShiftGet()); | |
} else { | |
x1 = rightX; | |
} | |
if (x0 < fillMinXGet()) { | |
x0 = fillMinXGet(); | |
} | |
if (x1 > fillMaxXGet()) { | |
x1 = fillMaxXGet(); | |
} | |
if (x0 < spanStartGet()) { | |
spanStartPut(x0); | |
} | |
if (x1 > spanEndGet()) { | |
spanEndPut(x1); | |
} | |
if (x1 > spanEndAAGet()) { | |
spanEndAAPut(x1); | |
} | |
if (x0 >= x1) { | |
return false; | |
} | |
if (isFillColor(fill)) { | |
fillColorSpanfromto(fill, x0, x1); | |
} else { | |
/* Store the values for the dispatch */ | |
lastExportedFillPut(fill); | |
lastExportedLeftXPut(x0); | |
lastExportedRightXPut(x1); | |
type = fillTypeOf(fill); | |
if (type <= 1) { | |
return true; | |
} | |
switch (type) { | |
case 0: | |
case 1: | |
errorWrongIndex(); | |
break; | |
case 2: | |
fillLinearGradient(); | |
break; | |
case 3: | |
fillRadialGradient(); | |
break; | |
case 4: | |
case 5: | |
fillBitmapSpan(); | |
break; | |
} | |
} | |
return false; | |
} | |
function fillTypeOf(fill) { | |
return (objectTypeOf(fill) & GEPrimitiveFillMask) >>> 8; | |
} | |
/* Check the global edge table for any entries that cannot be handled by the engine itself. | |
If there are any, return true. Otherwise, initialize the the edge and add it to the AET */ | |
function findNextExternalEntryFromGET() { | |
var edge; | |
var type; | |
var yValue; | |
/* As long as we have entries in the GET */ | |
yValue = currentYGet(); | |
while (getStartGet() < getUsedGet()) { | |
edge = getBuffer[getStartGet()]; | |
if (edgeYValueOf(edge) > yValue) { | |
return false; | |
} | |
type = objectTypeOf(edge); | |
if ((type & GEPrimitiveWideMask) === GEPrimitiveEdge) { | |
return true; | |
} | |
if (!needAvailableSpace(1)) { | |
return false; | |
} | |
switch (type) { | |
case 0: | |
case 1: | |
case 2: | |
case 3: | |
errorWrongIndex(); | |
break; | |
case 4: | |
stepToFirstLine(); | |
break; | |
case 5: | |
stepToFirstWideLine(); | |
break; | |
case 6: | |
stepToFirstBezier(); | |
break; | |
case 7: | |
stepToFirstWideBezier(); | |
break; | |
} | |
insertEdgeIntoAET(edge); | |
getStartPut(getStartGet() + 1); | |
} | |
return false; | |
} | |
/* Scan the active edge table. If there is any fill that cannot be handled by the engine itself, return true. Otherwise handle the fills and return false. */ | |
/* self currentYGet >= 680 ifTrue:[ | |
self printAET. | |
self halt. | |
]. */ | |
function findNextExternalFillFromAET() { | |
var leftEdge; | |
var leftX; | |
var rightEdge; | |
var rightX; | |
leftX = (rightX = fillMaxXGet()); | |
while (aetStartGet() < aetUsedGet()) { | |
/* TODO: We should check if leftX from last operation | |
is greater than leftX from next edge. | |
Currently, we rely here on spanEndAA | |
from the span buffer fill. */ | |
leftEdge = (rightEdge = aetBuffer[aetStartGet()]); | |
leftX = (rightX = edgeXValueOf(leftEdge)); | |
if (leftX >= fillMaxXGet()) { | |
return false; | |
} | |
quickRemoveInvalidFillsAt(leftX); | |
if (isWide(leftEdge)) { | |
toggleWideFillOf(leftEdge); | |
} | |
if (areEdgeFillsValid(leftEdge)) { | |
toggleFillsOf(leftEdge); | |
if (engineStopped) { | |
return false; | |
} | |
} | |
aetStartPut(aetStartGet() + 1); | |
if (aetStartGet() < aetUsedGet()) { | |
rightEdge = aetBuffer[aetStartGet()]; | |
rightX = edgeXValueOf(rightEdge); | |
if (rightX >= fillMinXGet()) { | |
/* This is the visible portion */ | |
fillAllFromto(leftX, rightX); | |
} | |
} | |
} | |
if (rightX < fillMaxXGet()) { | |
fillAllFromto(rightX, fillMaxXGet()); | |
} | |
return false; | |
} | |
/* Check the active edge table for any entries that cannot be handled by the engine itself. | |
If there are any, return true. Otherwise, step the the edge to the next y value. */ | |
function findNextExternalUpdateFromAET() { | |
var count; | |
var edge; | |
var type; | |
while (aetStartGet() < aetUsedGet()) { | |
edge = aetBuffer[aetStartGet()]; | |
count = edgeNumLinesOf(edge) - 1; | |
if (count === 0) { | |
/* Edge at end -- remove it */ | |
removeFirstAETEntry(); | |
} else { | |
/* Store remaining lines back */ | |
edgeNumLinesOfput(edge, count); | |
type = objectTypeOf(edge); | |
if ((type & GEPrimitiveWideMask) === GEPrimitiveEdge) { | |
return true; | |
} | |
switch (type) { | |
case 0: | |
case 1: | |
case 2: | |
case 3: | |
errorWrongIndex(); | |
break; | |
case 4: | |
stepToNextLine(); | |
break; | |
case 5: | |
stepToNextWideLine(); | |
break; | |
case 6: | |
stepToNextBezier(); | |
break; | |
case 7: | |
stepToNextWideBezier(); | |
break; | |
} | |
resortFirstAETEntry(); | |
aetStartPut(aetStartGet() + 1); | |
} | |
} | |
return false; | |
} | |
function findStackFilldepth(fillIndex, depth) { | |
var index; | |
index = 0; | |
while ((index < stackFillSize()) && ((stackFillValue(index) !== fillIndex) || (stackFillDepth(index) !== depth))) { | |
index += stackFillEntryLength(); | |
} | |
if (index >= stackFillSize()) { | |
return -1; | |
} else { | |
return index; | |
} | |
} | |
/* Return true if processing is finished */ | |
function finishedProcessing() { | |
return stateGet() === GEStateCompleted; | |
} | |
function freeStackFillEntry() { | |
wbStackPop(stackFillEntryLength()); | |
} | |
/* Note: This is hardcoded so it can be run from Squeak. | |
The module name is used for validating a module *after* | |
it is loaded to check if it does really contain the module | |
we're thinking it contains. This is important! */ | |
function getModuleName() { | |
return moduleName; | |
} | |
/* Return true if the edge at index i should sort before the edge at index j. */ | |
function getSortsbefore(edge1, edge2) { | |
var diff; | |
if (edge1 === edge2) { | |
return true; | |
} | |
diff = edgeYValueOf(edge1) - edgeYValueOf(edge2); | |
if (diff !== 0) { | |
return diff < 0; | |
} | |
diff = edgeXValueOf(edge1) - edgeXValueOf(edge2); | |
return diff < 0; | |
} | |
function getStartGet() { | |
return workBuffer[GWGETStart]; | |
} | |
function getStartPut(value) { | |
return workBuffer[GWGETStart] = value; | |
} | |
function getUsedGet() { | |
return workBuffer[GWGETUsed]; | |
} | |
function getUsedPut(value) { | |
return workBuffer[GWGETUsed] = value; | |
} | |
function gradientRampLengthOf(fill) { | |
return objat(fill, GFRampLength); | |
} | |
function gradientRampLengthOfput(fill, value) { | |
return objatput(fill, GFRampLength, value); | |
} | |
function gradientRampOf(fill) { | |
return PTR_ADD(objBuffer, fill + GFRampOffset); | |
} | |
function halt() { | |
; | |
} | |
function hasColorTransform() { | |
return hasColorTransformGet() !== 0; | |
} | |
function hasColorTransformGet() { | |
return workBuffer[GWHasColorTransform]; | |
} | |
function hasColorTransformPut(value) { | |
return workBuffer[GWHasColorTransform] = value; | |
} | |
function hasEdgeTransform() { | |
return hasEdgeTransformGet() !== 0; | |
} | |
function hasEdgeTransformGet() { | |
return workBuffer[GWHasEdgeTransform]; | |
} | |
function hasEdgeTransformPut(value) { | |
return workBuffer[GWHasEdgeTransform] = value; | |
} | |
/* Make the fill style with the given index invisible */ | |
function hideFilldepth(fillIndex, depth) { | |
var index; | |
var newDepth; | |
var newRightX; | |
var newTop; | |
var newTopIndex; | |
index = findStackFilldepth(fillIndex, depth); | |
if (index === -1) { | |
return false; | |
} | |
if (index === 0) { | |
freeStackFillEntry(); | |
return true; | |
} | |
stackFillValueput(index, stackFillValue(0)); | |
stackFillDepthput(index, stackFillDepth(0)); | |
stackFillRightXput(index, stackFillRightX(0)); | |
freeStackFillEntry(); | |
if (stackFillSize() <= stackFillEntryLength()) { | |
return true; | |
} | |
newTopIndex = 0; | |
index = stackFillEntryLength(); | |
while (index < stackFillSize()) { | |
if (fillSortsbefore(index, newTopIndex)) { | |
newTopIndex = index; | |
} | |
index += stackFillEntryLength(); | |
} | |
if ((newTopIndex + stackFillEntryLength()) === stackFillSize()) { | |
return true; | |
} | |
newTop = stackFillValue(newTopIndex); | |
stackFillValueput(newTopIndex, topFillValue()); | |
topFillValuePut(newTop); | |
newDepth = stackFillDepth(newTopIndex); | |
stackFillDepthput(newTopIndex, topFillDepth()); | |
topFillDepthPut(newDepth); | |
newRightX = stackFillRightX(newTopIndex); | |
stackFillRightXput(newTopIndex, topFillRightX()); | |
topFillRightXPut(newRightX); | |
return true; | |
} | |
function incrementStatby(statIndex, value) { | |
return workBuffer[statIndex] = (workBuffer[statIndex] + value); | |
} | |
/* Find insertion point for the given edge in the AET */ | |
function indexForInsertingIntoAET(edge) { | |
var index; | |
var initialX; | |
initialX = edgeXValueOf(edge); | |
index = 0; | |
while ((index < aetUsedGet()) && (edgeXValueOf(aetBuffer[index]) < initialX)) { | |
++index; | |
} | |
while ((index < aetUsedGet()) && ((edgeXValueOf(aetBuffer[index]) === initialX) && (getSortsbefore(aetBuffer[index], edge)))) { | |
++index; | |
} | |
return index; | |
} | |
function initColorTransform() { | |
var transform; | |
transform = colorTransform(); | |
transform[0] = 1.0; | |
transform[1] = 0.0; | |
transform[2] = 1.0; | |
transform[3] = 0.0; | |
transform[4] = 1.0; | |
transform[5] = 0.0; | |
transform[6] = 1.0; | |
transform[7] = 0.0; | |
hasColorTransformPut(0); | |
} | |
function initEdgeTransform() { | |
var transform; | |
transform = edgeTransform(); | |
transform[0] = 1.0; | |
transform[1] = 0.0; | |
transform[2] = 0.0; | |
transform[3] = 0.0; | |
transform[4] = 1.0; | |
transform[5] = 0.0; | |
hasEdgeTransformPut(0); | |
} | |
function initialiseModule() { | |
loadBBFn = interpreterProxy.ioLoadFunctionFrom("loadBitBltFrom", bbPluginName); | |
copyBitsFn = interpreterProxy.ioLoadFunctionFrom("copyBitsFromtoat", bbPluginName); | |
return (!!loadBBFn) && (!!copyBitsFn); | |
} | |
/* Initialization stuff that needs to be done before any processing can take place. */ | |
/* Make sure aaLevel is initialized */ | |
function initializeGETProcessing() { | |
setAALevel(aaLevelGet()); | |
if (clipMinXGet() < 0) { | |
clipMinXPut(0); | |
} | |
if (clipMaxXGet() > spanSizeGet()) { | |
clipMaxXPut(spanSizeGet()); | |
} | |
fillMinXPut(SHL(clipMinXGet(), aaShiftGet())); | |
fillMinYPut(SHL(clipMinYGet(), aaShiftGet())); | |
fillMaxXPut(SHL(clipMaxXGet(), aaShiftGet())); | |
fillMaxYPut(SHL(clipMaxYGet(), aaShiftGet())); | |
getUsedPut(0); | |
aetUsedPut(0); | |
getBuffer = PTR_ADD(objBuffer, objUsed); | |
/* Create the global edge table */ | |
aetBuffer = PTR_ADD(objBuffer, objUsed); | |
createGlobalEdgeTable(); | |
if (engineStopped) { | |
return null; | |
} | |
if (getUsedGet() === 0) { | |
/* Nothing to do */ | |
currentYPut(fillMaxYGet()); | |
return 0; | |
} | |
sortGlobalEdgeTable(); | |
currentYPut(edgeYValueOf(getBuffer[0])); | |
if (currentYGet() < fillMinYGet()) { | |
currentYPut(fillMinYGet()); | |
} | |
spanStartPut(0); | |
spanEndPut((SHL(spanSizeGet(), aaShiftGet())) - 1); | |
clearSpanBuffer(); | |
} | |
/* Insert the edge with the given index from the global edge table into the active edge table. | |
The edge has already been stepped to the initial yValue -- thus remainingLines and rasterX | |
are both set. */ | |
function insertEdgeIntoAET(edge) { | |
var index; | |
/* Check for the number of lines remaining */ | |
if (edgeNumLinesOf(edge) <= 0) { | |
return null; | |
} | |
/* And insert edge */ | |
index = indexForInsertingIntoAET(edge); | |
insertToAETbeforeIndex(edge, index); | |
} | |
/* Insert the given edge into the AET. */ | |
function insertToAETbeforeIndex(edge, index) { | |
var i; | |
/* Make sure we have space in the AET */ | |
if (!allocateAETEntry(1)) { | |
return null; | |
} | |
i = aetUsedGet() - 1; | |
while (!(i < index)) { | |
aetBuffer[i + 1] = aetBuffer[i]; | |
--i; | |
} | |
aetBuffer[index] = edge; | |
aetUsedPut(aetUsedGet() + 1); | |
} | |
function isBezier(bezier) { | |
return (objectTypeOf(bezier) & GEPrimitiveWideMask) === GEPrimitiveBezier; | |
} | |
function isEdge(edge) { | |
var type; | |
type = objectTypeOf(edge); | |
if (type > GEPrimitiveEdgeMask) { | |
return false; | |
} | |
return (objectTypeOf(edge) & GEPrimitiveEdgeMask) !== 0; | |
} | |
function isFill(fill) { | |
return isFillColor(fill) || (isRealFill(fill)); | |
} | |
function isFillColor(fill) { | |
return (fill & 4278190080) !== 0; | |
} | |
function isFillOkay(fill) { | |
return (fill === 0) || (isFillColor(fill) || (isObject(fill) && (isFill(fill)))); | |
} | |
function isLine(line) { | |
return (objectTypeOf(line) & GEPrimitiveWideMask) === GEPrimitiveLine; | |
} | |
function isObject(obj) { | |
return (obj >= 0) && (obj < objUsed); | |
} | |
function isRealFill(fill) { | |
return (objectTypeOf(fill) & GEPrimitiveFillMask) !== 0; | |
} | |
function isWide(object) { | |
return (objectTypeOf(object) & GEPrimitiveWide) !== 0; | |
} | |
function lastExportedEdgeGet() { | |
return workBuffer[GWLastExportedEdge]; | |
} | |
function lastExportedEdgePut(value) { | |
return workBuffer[GWLastExportedEdge] = value; | |
} | |
function lastExportedFillGet() { | |
return workBuffer[GWLastExportedFill]; | |
} | |
function lastExportedFillPut(value) { | |
return workBuffer[GWLastExportedFill] = value; | |
} | |
function lastExportedLeftXGet() { | |
return workBuffer[GWLastExportedLeftX]; | |
} | |
function lastExportedLeftXPut(value) { | |
return workBuffer[GWLastExportedLeftX] = value; | |
} | |
function lastExportedRightXGet() { | |
return workBuffer[GWLastExportedRightX]; | |
} | |
function lastExportedRightXPut(value) { | |
return workBuffer[GWLastExportedRightX] = value; | |
} | |
function lineEndXOf(line) { | |
return objat(line, GLEndX); | |
} | |
function lineEndXOfput(line, value) { | |
return objatput(line, GLEndX, value); | |
} | |
function lineEndYOf(line) { | |
return objat(line, GLEndY); | |
} | |
function lineEndYOfput(line, value) { | |
return objatput(line, GLEndY, value); | |
} | |
function lineErrorAdjDownOf(line) { | |
return objat(line, GLErrorAdjDown); | |
} | |
function lineErrorAdjDownOfput(line, value) { | |
return objatput(line, GLErrorAdjDown, value); | |
} | |
function lineErrorAdjUpOf(line) { | |
return objat(line, GLErrorAdjUp); | |
} | |
function lineErrorAdjUpOfput(line, value) { | |
return objatput(line, GLErrorAdjUp, value); | |
} | |
function lineErrorOf(line) { | |
return objat(line, GLError); | |
} | |
function lineErrorOfput(line, value) { | |
return objatput(line, GLError, value); | |
} | |
function lineXDirectionOf(line) { | |
return objat(line, GLXDirection); | |
} | |
function lineXDirectionOfput(line, value) { | |
return objatput(line, GLXDirection, value); | |
} | |
function lineXIncrementOf(line) { | |
return objat(line, GLXIncrement); | |
} | |
function lineXIncrementOfput(line, value) { | |
return objatput(line, GLXIncrement, value); | |
} | |
function lineYDirectionOfput(line, value) { | |
return objatput(line, GLYDirection, value); | |
} | |
/* Load and subdivide the bezier curve from point1/point2/point3. | |
If wideFlag is set then make sure the curve is monoton in X. */ | |
function loadAndSubdivideBezierFromviatoisWide(point1, point2, point3, wideFlag) { | |
var bz1; | |
var bz2; | |
var index; | |
var index1; | |
var index2; | |
bz1 = allocateBezierStackEntry(); | |
if (engineStopped) { | |
return 0; | |
} | |
bzStartXput(bz1, point1[0]); | |
bzStartYput(bz1, point1[1]); | |
bzViaXput(bz1, point2[0]); | |
bzViaYput(bz1, point2[1]); | |
bzEndXput(bz1, point3[0]); | |
bzEndYput(bz1, point3[1]); | |
index2 = (bz2 = subdivideToBeMonotoninX(bz1, wideFlag)); | |
for (index = bz1; index <= bz2; index += 6) { | |
index1 = subdivideBezierFrom(index); | |
if (index1 > index2) { | |
index2 = index1; | |
} | |
if (engineStopped) { | |
return 0; | |
} | |
} | |
return DIV(index2, 6); | |
} | |
function loadArrayPolygonnPointsfilllineWidthlineFill(points, nPoints, fillIndex, lineWidth, lineFill) { | |
var i; | |
var x0; | |
var x1; | |
var y0; | |
var y1; | |
loadPointfrom(point1Get(), interpreterProxy.fetchPointerofObject(0, points)); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
x0 = point1Get()[0]; | |
y0 = point1Get()[1]; | |
for (i = 1; i <= (nPoints - 1); i++) { | |
loadPointfrom(point1Get(), interpreterProxy.fetchPointerofObject(i, points)); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
x1 = point1Get()[0]; | |
y1 = point1Get()[1]; | |
point1Get()[0] = x0; | |
point1Get()[1] = y0; | |
point2Get()[0] = x1; | |
point2Get()[1] = y1; | |
transformPoints(2); | |
loadWideLinefromtolineFillleftFillrightFill(lineWidth, point1Get(), point2Get(), lineFill, fillIndex, 0); | |
if (engineStopped) { | |
return null; | |
} | |
x0 = x1; | |
y0 = y1; | |
} | |
} | |
function loadArrayShapenSegmentsfilllineWidthlineFill(points, nSegments, fillIndex, lineWidth, lineFill) { | |
var i; | |
var pointOop; | |
var segs; | |
var x0; | |
var x1; | |
var x2; | |
var y0; | |
var y1; | |
var y2; | |
for (i = 0; i <= (nSegments - 1); i++) { | |
pointOop = interpreterProxy.fetchPointerofObject(i * 3, points); | |
loadPointfrom(point1Get(), pointOop); | |
pointOop = interpreterProxy.fetchPointerofObject((i * 3) + 1, points); | |
loadPointfrom(point2Get(), pointOop); | |
pointOop = interpreterProxy.fetchPointerofObject((i * 3) + 2, points); | |
loadPointfrom(point3Get(), pointOop); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
transformPoints(3); | |
x0 = point1Get()[0]; | |
y0 = point1Get()[1]; | |
x1 = point2Get()[0]; | |
y1 = point2Get()[1]; | |
x2 = point3Get()[0]; | |
/* Check if we can use a line */ | |
y2 = point3Get()[1]; | |
if (((x0 === y0) && (x1 === y1)) || ((x1 === x2) && (y1 === y2))) { | |
loadWideLinefromtolineFillleftFillrightFill(lineWidth, point1Get(), point3Get(), lineFill, fillIndex, 0); | |
} else { | |
/* Need bezier */ | |
segs = loadAndSubdivideBezierFromviatoisWide(point1Get(), point2Get(), point3Get(), (lineWidth !== 0) && (lineFill !== 0)); | |
if (engineStopped) { | |
return null; | |
} | |
loadWideBezierlineFillleftFillrightFilln(lineWidth, lineFill, fillIndex, 0, segs); | |
} | |
if (engineStopped) { | |
return null; | |
} | |
} | |
} | |
/* Load a transformation from the given array. */ | |
function loadArrayTransformFromintolength(transformOop, destPtr, n) { | |
var i; | |
var value; | |
for (i = 0; i <= (n - 1); i++) { | |
value = interpreterProxy.fetchPointerofObject(i, transformOop); | |
if (!(typeof value === "number" || (value.isFloat))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (typeof value === "number") { | |
destPtr[i] = value; | |
} else { | |
destPtr[i] = interpreterProxy.floatValueOf(value); | |
} | |
} | |
} | |
/* Initialize the bezier segment stored on the stack */ | |
function loadBeziersegmentleftFillrightFilloffset(bezier, index, leftFillIndex, rightFillIndex, yOffset) { | |
if (bzEndY(index) >= bzStartY(index)) { | |
/* Top to bottom */ | |
edgeXValueOfput(bezier, bzStartX(index)); | |
edgeYValueOfput(bezier, bzStartY(index) - yOffset); | |
bezierViaXOfput(bezier, bzViaX(index)); | |
bezierViaYOfput(bezier, bzViaY(index) - yOffset); | |
bezierEndXOfput(bezier, bzEndX(index)); | |
bezierEndYOfput(bezier, bzEndY(index) - yOffset); | |
} else { | |
edgeXValueOfput(bezier, bzEndX(index)); | |
edgeYValueOfput(bezier, bzEndY(index) - yOffset); | |
bezierViaXOfput(bezier, bzViaX(index)); | |
bezierViaYOfput(bezier, bzViaY(index) - yOffset); | |
bezierEndXOfput(bezier, bzStartX(index)); | |
bezierEndYOfput(bezier, bzStartY(index) - yOffset); | |
} | |
edgeZValueOfput(bezier, currentZGet()); | |
edgeLeftFillOfput(bezier, leftFillIndex); | |
edgeRightFillOfput(bezier, rightFillIndex); | |
} | |
function loadBitBltFrom(bbObj) { | |
if (!loadBBFn) { | |
/* We need copyBits here so try to load it implicitly */ | |
if (!initialiseModule()) { | |
return false; | |
} | |
} | |
return loadBBFn(bbObj); | |
} | |
/* Load the bitmap fill. */ | |
function loadBitmapFillcolormaptilefromalongnormalxIndex(formOop, cmOop, tileFlag, point1, point2, point3, xIndex) { | |
var bmBits; | |
var bmBitsSize; | |
var bmDepth; | |
var bmFill; | |
var bmHeight; | |
var bmRaster; | |
var bmWidth; | |
var cmBits; | |
var cmSize; | |
var ppw; | |
if (cmOop.isNil) { | |
cmSize = 0; | |
cmBits = null; | |
} else { | |
if (CLASSOF(cmOop) !== interpreterProxy.classBitmap()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
cmSize = SIZEOF(cmOop); | |
cmBits = cmOop.wordsAsInt32Array(); | |
} | |
if (typeof formOop === "number") { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (!interpreterProxy.isPointers(formOop)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (SIZEOF(formOop) < 5) { | |
return interpreterProxy.primitiveFail(); | |
} | |
bmBits = interpreterProxy.fetchPointerofObject(0, formOop); | |
if (CLASSOF(bmBits) !== interpreterProxy.classBitmap()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
bmBitsSize = SIZEOF(bmBits); | |
bmWidth = interpreterProxy.fetchIntegerofObject(1, formOop); | |
bmHeight = interpreterProxy.fetchIntegerofObject(2, formOop); | |
bmDepth = interpreterProxy.fetchIntegerofObject(3, formOop); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if (!((bmWidth >= 0) && (bmHeight >= 0))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (!((((((bmDepth === 32) || (bmDepth === 8)) || (bmDepth === 16)) || (bmDepth === 1)) || (bmDepth === 2)) || (bmDepth === 4))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (!((cmSize === 0) || (cmSize === (SHL(1, bmDepth))))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
ppw = DIV(32, bmDepth); | |
bmRaster = DIV((bmWidth + (ppw - 1)), ppw); | |
if (bmBitsSize !== (bmRaster * bmHeight)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
bmFill = allocateBitmapFillcolormap(cmSize, cmBits); | |
if (engineStopped) { | |
return null; | |
} | |
bitmapWidthOfput(bmFill, bmWidth); | |
bitmapHeightOfput(bmFill, bmHeight); | |
bitmapDepthOfput(bmFill, bmDepth); | |
bitmapRasterOfput(bmFill, bmRaster); | |
bitmapSizeOfput(bmFill, bmBitsSize); | |
bitmapTileFlagOfput(bmFill, tileFlag); | |
objectIndexOfput(bmFill, xIndex); | |
loadFillOrientationfromalongnormalwidthheight(bmFill, point1, point2, point3, bmWidth, bmHeight); | |
return bmFill; | |
} | |
/* Note: Assumes that the contents of formArray has been checked before */ | |
function loadBitsFrom(bmFill) { | |
var bitsLen; | |
var bitsOop; | |
var formOop; | |
var xIndex; | |
xIndex = objectIndexOf(bmFill); | |
if (xIndex > SIZEOF(formArray)) { | |
return null; | |
} | |
formOop = interpreterProxy.fetchPointerofObject(xIndex, formArray); | |
bitsOop = interpreterProxy.fetchPointerofObject(0, formOop); | |
bitsLen = SIZEOF(bitsOop); | |
if (bitsLen !== bitmapSizeOf(bmFill)) { | |
return null; | |
} | |
return bitsOop.wordsAsInt32Array(); | |
} | |
/* Load a 2x3 transformation matrix from the given oop. | |
Return true if the matrix is not nil, false otherwise */ | |
function loadColorTransformFrom(transformOop) { | |
var okay; | |
var transform; | |
transform = colorTransform(); | |
hasColorTransformPut(0); | |
okay = loadTransformFromintolength(transformOop, transform, 8); | |
if (!okay) { | |
return false; | |
} | |
hasColorTransformPut(1); | |
transform[1] = (transform[1] * 256.0); | |
transform[3] = (transform[3] * 256.0); | |
transform[5] = (transform[5] * 256.0); | |
transform[7] = (transform[7] * 256.0); | |
return okay; | |
} | |
/* Load the compressed segment identified by segment index */ | |
function loadCompressedSegmentfromshortleftFillrightFilllineWidthlineColor(segmentIndex, points, pointsShort, leftFill, rightFill, lineWidth, lineFill) { | |
var index; | |
var segs; | |
var x0; | |
var x1; | |
var x2; | |
var y0; | |
var y1; | |
var y2; | |
/* Check if have anything to do at all */ | |
if ((leftFill === rightFill) && ((lineWidth === 0) || (lineFill === 0))) { | |
return null; | |
} | |
/* 3 points with x/y each */ | |
index = segmentIndex * 6; | |
if (pointsShort) { | |
/* Load short points */ | |
x0 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[index + 0]; | |
y0 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[index + 1]; | |
x1 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[index + 2]; | |
y1 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[index + 3]; | |
x2 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[index + 4]; | |
y2 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[index + 5]; | |
} else { | |
x0 = (points[(index + 0)]|0); | |
y0 = (points[(index + 1)]|0); | |
x1 = (points[(index + 2)]|0); | |
y1 = (points[(index + 3)]|0); | |
x2 = (points[(index + 4)]|0); | |
y2 = (points[(index + 5)]|0); | |
} | |
if (((x0 === x1) && (y0 === y1)) || ((x1 === x2) && (y1 === y2))) { | |
/* We can use a line from x0/y0 to x2/y2 */ | |
if ((x0 === x2) && (y0 === y2)) { | |
return null; | |
} | |
point1Get()[0] = x0; | |
point1Get()[1] = y0; | |
point2Get()[0] = x2; | |
point2Get()[1] = y2; | |
transformPoints(2); | |
return loadWideLinefromtolineFillleftFillrightFill(lineWidth, point1Get(), point2Get(), lineFill, leftFill, rightFill); | |
} | |
point1Get()[0] = x0; | |
point1Get()[1] = y0; | |
point2Get()[0] = x1; | |
point2Get()[1] = y1; | |
point3Get()[0] = x2; | |
point3Get()[1] = y2; | |
transformPoints(3); | |
segs = loadAndSubdivideBezierFromviatoisWide(point1Get(), point2Get(), point3Get(), (lineWidth !== 0) && (lineFill !== 0)); | |
if (engineStopped) { | |
return null; | |
} | |
loadWideBezierlineFillleftFillrightFilln(lineWidth, lineFill, leftFill, rightFill, segs); | |
} | |
/* Load a compressed shape into the engine. | |
WARNING: THIS METHOD NEEDS THE FULL FRAME SIZE!!!! | |
*/ | |
function loadCompressedShapesegmentsleftFillsrightFillslineWidthslineFillsfillIndexListpointShort(points, nSegments, leftFills, rightFills, lineWidths, lineFills, fillIndexList, pointsShort) { | |
var i; | |
var leftLength; | |
var leftRun; | |
var leftValue; | |
var lineFillLength; | |
var lineFillRun; | |
var lineFillValue; | |
var rightLength; | |
var rightRun; | |
var rightValue; | |
var widthLength; | |
var widthRun; | |
var widthValue; | |
if (nSegments === 0) { | |
return 0; | |
} | |
leftRun = (rightRun = (widthRun = (lineFillRun = -1))); | |
leftLength = (rightLength = (widthLength = (lineFillLength = 1))); | |
leftValue = (rightValue = (widthValue = (lineFillValue = 0))); | |
for (i = 1; i <= nSegments; i++) { | |
/* Decrement current run length and load new stuff */ | |
if (((--leftLength)) <= 0) { | |
++leftRun; | |
leftLength = shortRunLengthAtfrom(leftRun, leftFills); | |
leftValue = shortRunValueAtfrom(leftRun, leftFills); | |
if (leftValue !== 0) { | |
leftValue = fillIndexList[leftValue - 1]; | |
leftValue = transformColor(leftValue); | |
if (engineStopped) { | |
return null; | |
} | |
} | |
} | |
if (((--rightLength)) <= 0) { | |
++rightRun; | |
rightLength = shortRunLengthAtfrom(rightRun, rightFills); | |
rightValue = shortRunValueAtfrom(rightRun, rightFills); | |
if (rightValue !== 0) { | |
rightValue = fillIndexList[rightValue - 1]; | |
rightValue = transformColor(rightValue); | |
} | |
} | |
if (((--widthLength)) <= 0) { | |
++widthRun; | |
widthLength = shortRunLengthAtfrom(widthRun, lineWidths); | |
widthValue = shortRunValueAtfrom(widthRun, lineWidths); | |
if (widthValue !== 0) { | |
widthValue = transformWidth(widthValue); | |
} | |
} | |
if (((--lineFillLength)) <= 0) { | |
++lineFillRun; | |
lineFillLength = shortRunLengthAtfrom(lineFillRun, lineFills); | |
lineFillValue = shortRunValueAtfrom(lineFillRun, lineFills); | |
if (lineFillValue !== 0) { | |
lineFillValue = fillIndexList[lineFillValue - 1]; | |
} | |
} | |
loadCompressedSegmentfromshortleftFillrightFilllineWidthlineColor(i - 1, points, pointsShort, leftValue, rightValue, widthValue, lineFillValue); | |
if (engineStopped) { | |
return null; | |
} | |
} | |
} | |
function loadEdgeStateFrom(edgeOop) { | |
var edge; | |
edge = lastExportedEdgeGet(); | |
if (SIZEOF(edgeOop) < ETBalloonEdgeDataSize) { | |
return null; | |
} | |
edgeXValueOfput(edge, interpreterProxy.fetchIntegerofObject(ETXValueIndex, edgeOop)); | |
edgeYValueOfput(edge, interpreterProxy.fetchIntegerofObject(ETYValueIndex, edgeOop)); | |
edgeZValueOfput(edge, interpreterProxy.fetchIntegerofObject(ETZValueIndex, edgeOop)); | |
edgeNumLinesOfput(edge, interpreterProxy.fetchIntegerofObject(ETLinesIndex, edgeOop)); | |
return edge; | |
} | |
/* Load a 2x3 transformation matrix from the given oop. | |
Return true if the matrix is not nil, false otherwise */ | |
function loadEdgeTransformFrom(transformOop) { | |
var okay; | |
var transform; | |
hasEdgeTransformPut(0); | |
transform = edgeTransform(); | |
okay = loadTransformFromintolength(transformOop, transform, 6); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if (!okay) { | |
return false; | |
} | |
hasEdgeTransformPut(1); | |
transform[2] = (transform[2] + destOffsetXGet()); | |
transform[5] = (transform[5] + destOffsetYGet()); | |
return true; | |
} | |
/* Transform the points */ | |
function loadFillOrientationfromalongnormalwidthheight(fill, point1, point2, point3, fillWidth, fillHeight) { | |
var dirX; | |
var dirY; | |
var dsLength2; | |
var dsX; | |
var dsY; | |
var dtLength2; | |
var dtX; | |
var dtY; | |
var nrmX; | |
var nrmY; | |
point2[0] = (point2[0] + point1[0]); | |
point2[1] = (point2[1] + point1[1]); | |
point3[0] = (point3[0] + point1[0]); | |
point3[1] = (point3[1] + point1[1]); | |
transformPoint(point1); | |
transformPoint(point2); | |
transformPoint(point3); | |
dirX = point2[0] - point1[0]; | |
dirY = point2[1] - point1[1]; | |
nrmX = point3[0] - point1[0]; | |
/* Compute the scale from direction/normal into ramp size */ | |
nrmY = point3[1] - point1[1]; | |
dsLength2 = (dirX * dirX) + (dirY * dirY); | |
if (dsLength2 > 0) { | |
dsX = ((((dirX * fillWidth) * 65536.0) / dsLength2)|0); | |
dsY = ((((dirY * fillWidth) * 65536.0) / dsLength2)|0); | |
} else { | |
dsX = 0; | |
dsY = 0; | |
} | |
dtLength2 = (nrmX * nrmX) + (nrmY * nrmY); | |
if (dtLength2 > 0) { | |
dtX = ((((nrmX * fillHeight) * 65536.0) / dtLength2)|0); | |
dtY = ((((nrmY * fillHeight) * 65536.0) / dtLength2)|0); | |
} else { | |
dtX = 0; | |
dtY = 0; | |
} | |
fillOriginXOfput(fill, point1[0]); | |
fillOriginYOfput(fill, point1[1]); | |
fillDirectionXOfput(fill, dsX); | |
fillDirectionYOfput(fill, dsY); | |
fillNormalXOfput(fill, dtX); | |
fillNormalYOfput(fill, dtY); | |
} | |
/* Check all the forms from arrayOop. */ | |
function loadFormsFrom(arrayOop) { | |
var bmBits; | |
var bmBitsSize; | |
var bmDepth; | |
var bmHeight; | |
var bmRaster; | |
var bmWidth; | |
var formOop; | |
var i; | |
var ppw; | |
if (!interpreterProxy.isArray(arrayOop)) { | |
return false; | |
} | |
formArray = arrayOop; | |
for (i = 0; i <= (SIZEOF(formArray) - 1); i++) { | |
formOop = interpreterProxy.fetchPointerofObject(i, formArray); | |
if (typeof formOop === "number") { | |
return false; | |
} | |
if (!interpreterProxy.isPointers(formOop)) { | |
return false; | |
} | |
if (SIZEOF(formOop) < 5) { | |
return false; | |
} | |
bmBits = interpreterProxy.fetchPointerofObject(0, formOop); | |
if (CLASSOF(bmBits) !== interpreterProxy.classBitmap()) { | |
return false; | |
} | |
bmBitsSize = SIZEOF(bmBits); | |
bmWidth = interpreterProxy.fetchIntegerofObject(1, formOop); | |
bmHeight = interpreterProxy.fetchIntegerofObject(2, formOop); | |
bmDepth = interpreterProxy.fetchIntegerofObject(3, formOop); | |
if (interpreterProxy.failed()) { | |
return false; | |
} | |
if (!((bmWidth >= 0) && (bmHeight >= 0))) { | |
return false; | |
} | |
ppw = DIV(32, bmDepth); | |
bmRaster = DIV((bmWidth + (ppw - 1)), ppw); | |
if (bmBitsSize !== (bmRaster * bmHeight)) { | |
return false; | |
} | |
} | |
return true; | |
} | |
/* Load the gradient fill as defined by the color ramp. */ | |
function loadGradientFillfromalongnormalisRadial(rampOop, point1, point2, point3, isRadial) { | |
var fill; | |
var rampWidth; | |
if (CLASSOF(rampOop) !== interpreterProxy.classBitmap()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
rampWidth = SIZEOF(rampOop); | |
fill = allocateGradientFillrampWidthisRadial(rampOop.wordsAsInt32Array(), rampWidth, isRadial); | |
if (engineStopped) { | |
return null; | |
} | |
loadFillOrientationfromalongnormalwidthheight(fill, point1, point2, point3, rampWidth, rampWidth); | |
return fill; | |
} | |
/* Load the line defined by point1 and point2. */ | |
function loadLinefromtooffsetleftFillrightFill(line, point1, point2, yOffset, leftFill, rightFill) { | |
var p1; | |
var p2; | |
var yDir; | |
if (point1[1] <= point2[1]) { | |
p1 = point1; | |
p2 = point2; | |
yDir = 1; | |
} else { | |
p1 = point2; | |
p2 = point1; | |
yDir = -1; | |
} | |
edgeXValueOfput(line, p1[0]); | |
edgeYValueOfput(line, p1[1] - yOffset); | |
edgeZValueOfput(line, currentZGet()); | |
edgeLeftFillOfput(line, leftFill); | |
edgeRightFillOfput(line, rightFill); | |
lineEndXOfput(line, p2[0]); | |
lineEndYOfput(line, p2[1] - yOffset); | |
lineYDirectionOfput(line, yDir); | |
} | |
/* Load a rectangular oval currently defined by point1/point2 */ | |
function loadOvallineFillleftFillrightFill(lineWidth, lineFill, leftFill, rightFill) { | |
var cx; | |
var cy; | |
var h; | |
var i; | |
var nSegments; | |
var w; | |
w = (point2Get()[0] - point1Get()[0]) >> 1; | |
h = (point2Get()[1] - point1Get()[1]) >> 1; | |
cx = (point2Get()[0] + point1Get()[0]) >> 1; | |
cy = (point2Get()[1] + point1Get()[1]) >> 1; | |
for (i = 0; i <= 15; i++) { | |
loadOvalSegmentwhcxcy(i, w, h, cx, cy); | |
transformPoints(3); | |
nSegments = loadAndSubdivideBezierFromviatoisWide(point1Get(), point2Get(), point3Get(), (lineWidth !== 0) && (lineFill !== 0)); | |
if (engineStopped) { | |
return null; | |
} | |
loadWideBezierlineFillleftFillrightFilln(lineWidth, lineFill, leftFill, rightFill, nSegments); | |
if (engineStopped) { | |
return null; | |
} | |
} | |
} | |
function loadOvalSegmentwhcxcy(seg, w, h, cx, cy) { | |
var x0; | |
var x1; | |
var x2; | |
var y0; | |
var y1; | |
var y2; | |
/* Load start point of segment */ | |
x0 = (((circleCosTable()[(seg * 2) + 0] * w) + cx)|0); | |
y0 = (((circleSinTable()[(seg * 2) + 0] * h) + cy)|0); | |
point1Get()[0] = x0; | |
point1Get()[1] = y0; | |
x2 = (((circleCosTable()[(seg * 2) + 2] * w) + cx)|0); | |
y2 = (((circleSinTable()[(seg * 2) + 2] * h) + cy)|0); | |
point3Get()[0] = x2; | |
point3Get()[1] = y2; | |
x1 = (((circleCosTable()[(seg * 2) + 1] * w) + cx)|0); | |
/* NOTE: The intermediate point is the point ON the curve | |
and not yet the control point (which is OFF the curve) */ | |
y1 = (((circleSinTable()[(seg * 2) + 1] * h) + cy)|0); | |
x1 = (x1 * 2) - ((x0 + x2) >> 1); | |
y1 = (y1 * 2) - ((y0 + y2) >> 1); | |
point2Get()[0] = x1; | |
point2Get()[1] = y1; | |
} | |
/* Load the contents of pointOop into pointArray */ | |
function loadPointfrom(pointArray, pointOop) { | |
var value; | |
if (CLASSOF(pointOop) !== interpreterProxy.classPoint()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
value = interpreterProxy.fetchPointerofObject(0, pointOop); | |
if (!(typeof value === "number" || (value.isFloat))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (typeof value === "number") { | |
pointArray[0] = value; | |
} else { | |
pointArray[0] = (interpreterProxy.floatValueOf(value)|0); | |
} | |
value = interpreterProxy.fetchPointerofObject(1, pointOop); | |
if (!(typeof value === "number" || (value.isFloat))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (typeof value === "number") { | |
pointArray[1] = value; | |
} else { | |
pointArray[1] = (interpreterProxy.floatValueOf(value)|0); | |
} | |
} | |
function loadPolygonnPointsfilllineWidthlineFillpointsShort(points, nPoints, fillIndex, lineWidth, lineFill, isShort) { | |
var i; | |
var x0; | |
var x1; | |
var y0; | |
var y1; | |
if (isShort) { | |
x0 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[0]; | |
y0 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[1]; | |
} else { | |
x0 = (points[0]|0); | |
y0 = (points[1]|0); | |
} | |
for (i = 1; i <= (nPoints - 1); i++) { | |
if (isShort) { | |
x1 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[i * 2]; | |
y1 = (points.int16Array || (points.int16Array = new Int16Array(points.buffer, points.byteOffset)))[(i * 2) + 1]; | |
} else { | |
x1 = (points[(i * 2)]|0); | |
y1 = (points[((i * 2) + 1)]|0); | |
} | |
point1Get()[0] = x0; | |
point1Get()[1] = y0; | |
point2Get()[0] = x1; | |
point2Get()[1] = y1; | |
transformPoints(2); | |
loadWideLinefromtolineFillleftFillrightFill(lineWidth, point1Get(), point2Get(), lineFill, fillIndex, 0); | |
if (engineStopped) { | |
return null; | |
} | |
x0 = x1; | |
y0 = y1; | |
} | |
} | |
/* Load a rectangle currently defined by point1-point4 */ | |
function loadRectanglelineFillleftFillrightFill(lineWidth, lineFill, leftFill, rightFill) { | |
loadWideLinefromtolineFillleftFillrightFill(lineWidth, point1Get(), point2Get(), lineFill, leftFill, rightFill); | |
loadWideLinefromtolineFillleftFillrightFill(lineWidth, point2Get(), point3Get(), lineFill, leftFill, rightFill); | |
loadWideLinefromtolineFillleftFillrightFill(lineWidth, point3Get(), point4Get(), lineFill, leftFill, rightFill); | |
loadWideLinefromtolineFillleftFillrightFill(lineWidth, point4Get(), point1Get(), lineFill, leftFill, rightFill); | |
} | |
/* Load the entire state from the interpreter for the rendering primitives. | |
Answer 0 on success or a non-zero failure code on failure. */ | |
function loadRenderingState() { | |
var edgeOop; | |
var failCode; | |
var fillOop; | |
var state; | |
if (interpreterProxy.methodArgumentCount() !== 2) { | |
return PrimErrBadNumArgs; | |
} | |
if (((failCode = quickLoadEngineFrom(interpreterProxy.stackValue(2)))) !== 0) { | |
return failCode; | |
} | |
fillOop = interpreterProxy.stackObjectValue(0); | |
edgeOop = interpreterProxy.stackObjectValue(1); | |
if (interpreterProxy.failed()) { | |
return PrimErrBadArgument; | |
} | |
if (((failCode = loadSpanBufferFrom(interpreterProxy.fetchPointerofObject(BESpanIndex, engine)))) !== 0) { | |
return failCode; | |
} | |
if (!loadBitBltFrom(interpreterProxy.fetchPointerofObject(BEBitBltIndex, engine))) { | |
return GEFBitBltLoadFailed; | |
} | |
if (!loadFormsFrom(interpreterProxy.fetchPointerofObject(BEFormsIndex, engine))) { | |
return GEFFormLoadFailed; | |
} | |
if (SIZEOF(edgeOop) < ETBalloonEdgeDataSize) { | |
return GEFEdgeDataTooSmall; | |
} | |
if (SIZEOF(fillOop) < FTBalloonFillDataSize) { | |
return GEFFillDataTooSmall; | |
} | |
state = stateGet(); | |
if ((state === GEStateWaitingForEdge) || ((state === GEStateWaitingForFill) || (state === GEStateWaitingChange))) { | |
return GEFWrongState; | |
} | |
return 0; | |
} | |
function loadShapenSegmentsfilllineWidthlineFillpointsShort(points, nSegments, fillIndex, lineWidth, lineFill, pointsShort) { | |
var i; | |
for (i = 1; i <= nSegments; i++) { | |
loadCompressedSegmentfromshortleftFillrightFilllineWidthlineColor(i - 1, points, pointsShort, fillIndex, 0, lineWidth, lineFill); | |
if (engineStopped) { | |
return null; | |
} | |
} | |
} | |
/* Load the span buffer from the given oop. | |
Answer 0 on success or a non-zero failure code on failure. */ | |
function loadSpanBufferFrom(spanOop) { | |
if (CLASSOF(spanOop) !== interpreterProxy.classBitmap()) { | |
return GEFClassMismatch; | |
} | |
/* Leave last entry unused to avoid complications */ | |
spanBuffer = spanOop.words; | |
spanSizePut(SIZEOF(spanOop) - 1); | |
return 0; | |
} | |
/* Load a transformation from transformOop into the float array | |
defined by destPtr. The transformation is assumed to be either | |
an array or a FloatArray of length n. */ | |
function loadTransformFromintolength(transformOop, destPtr, n) { | |
if (transformOop.isNil) { | |
return false; | |
} | |
if (typeof transformOop === "number") { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (SIZEOF(transformOop) !== n) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (interpreterProxy.isWords(transformOop)) { | |
loadWordTransformFromintolength(transformOop, destPtr, n); | |
} else { | |
loadArrayTransformFromintolength(transformOop, destPtr, n); | |
} | |
return true; | |
} | |
/* Load the (possibly wide) bezier from the segments currently on the bezier stack. */ | |
function loadWideBezierlineFillleftFillrightFilln(lineWidth, lineFill, leftFill, rightFill, nSegments) { | |
var bezier; | |
var index; | |
var offset; | |
var wide; | |
if ((lineWidth === 0) || (lineFill === 0)) { | |
wide = false; | |
offset = 0; | |
} else { | |
wide = true; | |
offset = offsetFromWidth(lineWidth); | |
} | |
index = nSegments * 6; | |
while (index > 0) { | |
if (wide) { | |
bezier = allocateWideBezier(); | |
} else { | |
bezier = allocateBezier(); | |
} | |
if (engineStopped) { | |
return 0; | |
} | |
loadBeziersegmentleftFillrightFilloffset(bezier, index, leftFill, rightFill, offset); | |
if (wide) { | |
wideBezierFillOfput(bezier, lineFill); | |
wideBezierWidthOfput(bezier, lineWidth); | |
wideBezierExtentOfput(bezier, lineWidth); | |
} | |
index -= 6; | |
} | |
wbStackClear(); | |
} | |
/* Load a (possibly wide) line defined by the points p1 and p2 */ | |
function loadWideLinefromtolineFillleftFillrightFill(lineWidth, p1, p2, lineFill, leftFill, rightFill) { | |
var line; | |
var offset; | |
if ((lineWidth === 0) || (lineFill === 0)) { | |
line = allocateLine(); | |
offset = 0; | |
} else { | |
line = allocateWideLine(); | |
offset = offsetFromWidth(lineWidth); | |
} | |
if (engineStopped) { | |
return 0; | |
} | |
loadLinefromtooffsetleftFillrightFill(line, p1, p2, offset, leftFill, rightFill); | |
if (isWide(line)) { | |
wideLineFillOfput(line, lineFill); | |
wideLineWidthOfput(line, lineWidth); | |
wideLineExtentOfput(line, lineWidth); | |
} | |
} | |
/* Load a float array transformation from the given oop */ | |
function loadWordTransformFromintolength(transformOop, destPtr, n) { | |
var i; | |
var srcPtr; | |
srcPtr = transformOop.wordsAsFloat32Array(); | |
for (i = 0; i <= (n - 1); i++) { | |
destPtr[i] = srcPtr[i]; | |
} | |
} | |
/* Load the working buffer from the given oop */ | |
function loadWorkBufferFrom(wbOop) { | |
if (typeof wbOop === "number") { | |
return GEFWorkBufferIsInteger; | |
} | |
if (!interpreterProxy.isWords(wbOop)) { | |
return GEFWorkBufferIsPointers; | |
} | |
if (SIZEOF(wbOop) < GWMinimalSize) { | |
return GEFWorkBufferTooSmall; | |
} | |
workBufferPut(wbOop); | |
if (magicNumberGet() !== GWMagicNumber) { | |
return GEFWorkBufferBadMagic; | |
} | |
if (wbSizeGet() !== SIZEOF(wbOop)) { | |
return GEFWorkBufferWrongSize; | |
} | |
if (objStartGet() !== GWHeaderSize) { | |
return GEFWorkBufferStartWrong; | |
} | |
objBuffer = PTR_ADD(workBuffer, objStartGet()); | |
getBuffer = PTR_ADD(objBuffer, objUsedGet()); | |
/* Make sure we don't exceed the work buffer */ | |
aetBuffer = PTR_ADD(getBuffer, getUsedGet()); | |
if ((((GWHeaderSize + objUsedGet()) + getUsedGet()) + aetUsedGet()) > wbSizeGet()) { | |
return GEFWorkTooBig; | |
} | |
return 0; | |
} | |
function magicNumberGet() { | |
return workBuffer[GWMagicIndex]; | |
} | |
function magicNumberPut(value) { | |
return workBuffer[GWMagicIndex] = value; | |
} | |
/* The module with the given name was just unloaded. | |
Make sure we have no dangling references. */ | |
function moduleUnloaded(aModuleName) { | |
if (strcmp(aModuleName, bbPluginName) === 0) { | |
/* BitBlt just shut down. How nasty. */ | |
loadBBFn = 0; | |
copyBitsFn = 0; | |
} | |
} | |
/* The entry at index is not in the right position of the AET. | |
Move it to the left until the position is okay. */ | |
function moveAETEntryFromedgex(index, edge, xValue) { | |
var newIndex; | |
newIndex = index; | |
while ((newIndex > 0) && (edgeXValueOf(aetBuffer[newIndex - 1]) > xValue)) { | |
aetBuffer[newIndex] = aetBuffer[newIndex - 1]; | |
--newIndex; | |
} | |
aetBuffer[newIndex] = edge; | |
} | |
/* Check if we have n slots available */ | |
function needAvailableSpace(nSlots) { | |
if (((((GWHeaderSize + objUsed) + getUsedGet()) + aetUsedGet()) + nSlots) > wbTopGet()) { | |
stopBecauseOf(GErrorNoMoreSpace); | |
return false; | |
} | |
return true; | |
} | |
function needsFlush() { | |
return needsFlushGet() !== 0; | |
} | |
function needsFlushGet() { | |
return workBuffer[GWNeedsFlush]; | |
} | |
function needsFlushPut(value) { | |
return workBuffer[GWNeedsFlush] = value; | |
} | |
function objat(object, index) { | |
return objBuffer[object + index]; | |
} | |
function objatput(object, index, value) { | |
return objBuffer[object + index] = value; | |
} | |
function objStartGet() { | |
return workBuffer[GWObjStart]; | |
} | |
function objStartPut(value) { | |
return workBuffer[GWObjStart] = value; | |
} | |
function objUsedGet() { | |
return workBuffer[GWObjUsed]; | |
} | |
function objUsedPut(value) { | |
return workBuffer[GWObjUsed] = value; | |
} | |
function objectHeaderOf(obj) { | |
return objat(obj, GEObjectType); | |
} | |
function objectIndexOf(obj) { | |
return objat(obj, GEObjectIndex); | |
} | |
function objectIndexOfput(obj, value) { | |
return objatput(obj, GEObjectIndex, value); | |
} | |
function objectLengthOf(obj) { | |
return objat(obj, GEObjectLength); | |
} | |
function objectLengthOfput(obj, value) { | |
return objatput(obj, GEObjectLength, value); | |
} | |
function objectTypeOf(obj) { | |
return objat(obj, GEObjectType) & GEPrimitiveTypeMask; | |
} | |
function objectTypeOfput(obj, value) { | |
return objatput(obj, GEObjectType, value); | |
} | |
/* Common function so that we don't compute that wrong in any place | |
and can easily find all the places where we deal with one-pixel offsets. */ | |
function offsetFromWidth(lineWidth) { | |
return lineWidth >> 1; | |
} | |
function point1Get() { | |
return PTR_ADD(workBuffer, GWPoint1); | |
} | |
function point2Get() { | |
return PTR_ADD(workBuffer, GWPoint2); | |
} | |
function point3Get() { | |
return PTR_ADD(workBuffer, GWPoint3); | |
} | |
function point4Get() { | |
return PTR_ADD(workBuffer, GWPoint4); | |
} | |
/* We have just blitted a scan line to the screen. | |
Do whatever seems to be a good idea here. */ | |
/* Note: In the future we may check the time needed for this scan line and interrupt processing to give the Smalltalk code a chance to run at a certain time. */ | |
/* Check if there is any more work to do. */ | |
function postDisplayAction() { | |
if ((getStartGet() >= getUsedGet()) && (aetUsedGet() === 0)) { | |
/* No more entries to process */ | |
statePut(GEStateCompleted); | |
} | |
if (currentYGet() >= fillMaxYGet()) { | |
/* Out of clipping range */ | |
statePut(GEStateCompleted); | |
} | |
} | |
function primitiveAbortProcessing() { | |
var failureCode; | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(0)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
statePut(GEStateCompleted); | |
storeEngineStateInto(engine); | |
} | |
/* Note: No need to load either bitBlt or spanBuffer */ | |
function primitiveAddActiveEdgeEntry() { | |
var edge; | |
var edgeOop; | |
var failureCode; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateWaitingForEdge))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
edgeOop = interpreterProxy.stackObjectValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
edge = loadEdgeStateFrom(edgeOop); | |
if (!edge) { | |
return interpreterProxy.primitiveFailFor(GEFEdgeDataTooSmall); | |
} | |
if (!needAvailableSpace(1)) { | |
return interpreterProxy.primitiveFailFor(GEFWorkTooBig); | |
} | |
if (edgeNumLinesOf(edge) > 0) { | |
insertEdgeIntoAET(edge); | |
} | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
statePut(GEStateAddingFromGET); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
if (doProfileStats) { | |
incrementStatby(GWCountAddAETEntry, 1); | |
incrementStatby(GWTimeAddAETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
function primitiveAddBezier() { | |
var endOop; | |
var failureCode; | |
var leftFill; | |
var nSegments; | |
var rightFill; | |
var startOop; | |
var viaOop; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 5) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
rightFill = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(0)); | |
leftFill = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(1)); | |
viaOop = interpreterProxy.stackObjectValue(2); | |
endOop = interpreterProxy.stackObjectValue(3); | |
startOop = interpreterProxy.stackObjectValue(4); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(5), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (!(isFillOkay(leftFill) && (isFillOkay(rightFill)))) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
if ((leftFill === rightFill) && false) { | |
return interpreterProxy.pop(6); | |
} | |
loadPointfrom(point1Get(), startOop); | |
loadPointfrom(point2Get(), viaOop); | |
loadPointfrom(point3Get(), endOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
transformPoints(3); | |
nSegments = loadAndSubdivideBezierFromviatoisWide(point1Get(), point2Get(), point3Get(), false); | |
needAvailableSpace(nSegments * GBBaseSize); | |
if (!engineStopped) { | |
leftFill = transformColor(leftFill); | |
rightFill = transformColor(rightFill); | |
} | |
if (!engineStopped) { | |
loadWideBezierlineFillleftFillrightFilln(0, 0, leftFill, rightFill, nSegments); | |
} | |
if (engineStopped) { | |
/* Make sure the stack is okay */ | |
wbStackClear(); | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(5); | |
} | |
function primitiveAddBezierShape() { | |
var failureCode; | |
var fillIndex; | |
var length; | |
var lineFill; | |
var lineWidth; | |
var nSegments; | |
var points; | |
var pointsIsArray; | |
var segSize; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 5) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
lineFill = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(0)); | |
lineWidth = interpreterProxy.stackIntegerValue(1); | |
fillIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(2)); | |
nSegments = interpreterProxy.stackIntegerValue(3); | |
points = interpreterProxy.stackObjectValue(4); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(5), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
length = SIZEOF(points); | |
if (interpreterProxy.isWords(points)) { | |
/* Either PointArray or ShortPointArray */ | |
pointsIsArray = false; | |
if (!((length === (nSegments * 3)) || (length === (nSegments * 6)))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
} else { | |
/* Must be Array of points */ | |
if (!interpreterProxy.isArray(points)) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (length !== (nSegments * 3)) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
pointsIsArray = true; | |
} | |
if ((lineWidth === 0) || (lineFill === 0)) { | |
segSize = GLBaseSize; | |
} else { | |
segSize = GLWideSize; | |
} | |
if (!needAvailableSpace(segSize * nSegments)) { | |
return interpreterProxy.primitiveFailFor(GEFWorkTooBig); | |
} | |
if (!(isFillOkay(lineFill) && (isFillOkay(fillIndex)))) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
lineFill = transformColor(lineFill); | |
fillIndex = transformColor(fillIndex); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (((lineFill === 0) || (lineWidth === 0)) && (fillIndex === 0)) { | |
return interpreterProxy.pop(5); | |
} | |
if (lineWidth !== 0) { | |
lineWidth = transformWidth(lineWidth); | |
if (lineWidth < 1) { | |
lineWidth = 1; | |
} | |
} | |
if (pointsIsArray) { | |
loadArrayShapenSegmentsfilllineWidthlineFill(points, nSegments, fillIndex, lineWidth, lineFill); | |
} else { | |
loadShapenSegmentsfilllineWidthlineFillpointsShort(points.wordsAsInt32Array(), nSegments, fillIndex, lineWidth, lineFill, (nSegments * 3) === length); | |
} | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
needsFlushPut(1); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(5); | |
} | |
function primitiveAddBitmapFill() { | |
var cmOop; | |
var dirOop; | |
var failureCode; | |
var fill; | |
var formOop; | |
var nrmOop; | |
var originOop; | |
var tileFlag; | |
var xIndex; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 7) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
xIndex = interpreterProxy.stackIntegerValue(0); | |
if (xIndex <= 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
nrmOop = interpreterProxy.stackObjectValue(1); | |
dirOop = interpreterProxy.stackObjectValue(2); | |
originOop = interpreterProxy.stackObjectValue(3); | |
tileFlag = interpreterProxy.booleanValueOf(interpreterProxy.stackValue(4)); | |
cmOop = interpreterProxy.stackObjectValue(5); | |
formOop = interpreterProxy.stackObjectValue(6); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(7), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
loadPointfrom(point1Get(), originOop); | |
loadPointfrom(point2Get(), dirOop); | |
loadPointfrom(point3Get(), nrmOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFBadPoint); | |
} | |
fill = loadBitmapFillcolormaptilefromalongnormalxIndex(formOop, cmOop, (tileFlag | |
? 1 | |
: 0), point1Get(), point2Get(), point3Get(), xIndex - 1); | |
if (engineStopped) { | |
/* Make sure the stack is okay */ | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.popthenPush(8, interpreterProxy.positive32BitIntegerFor(fill)); | |
} | |
function primitiveAddCompressedShape() { | |
var failureCode; | |
var fillIndexList; | |
var leftFills; | |
var lineFills; | |
var lineWidths; | |
var nSegments; | |
var points; | |
var pointsShort; | |
var rightFills; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 7) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
fillIndexList = interpreterProxy.stackObjectValue(0); | |
lineFills = interpreterProxy.stackObjectValue(1); | |
lineWidths = interpreterProxy.stackObjectValue(2); | |
rightFills = interpreterProxy.stackObjectValue(3); | |
leftFills = interpreterProxy.stackObjectValue(4); | |
nSegments = interpreterProxy.stackIntegerValue(5); | |
points = interpreterProxy.stackObjectValue(6); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(7), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (!checkCompressedShapesegmentsleftFillsrightFillslineWidthslineFillsfillIndexList(points, nSegments, leftFills, rightFills, lineWidths, lineFills, fillIndexList)) { | |
return interpreterProxy.primitiveFailFor(GEFEntityCheckFailed); | |
} | |
if (!needAvailableSpace(Math.max(GBBaseSize, GLBaseSize) * nSegments)) { | |
return interpreterProxy.primitiveFailFor(GEFWorkTooBig); | |
} | |
/* Then actually load the compressed shape */ | |
pointsShort = SIZEOF(points) === (nSegments * 3); | |
loadCompressedShapesegmentsleftFillsrightFillslineWidthslineFillsfillIndexListpointShort(points.wordsAsInt32Array(), nSegments, leftFills.wordsAsInt32Array(), rightFills.wordsAsInt32Array(), lineWidths.wordsAsInt32Array(), lineFills.wordsAsInt32Array(), fillIndexList.wordsAsInt32Array(), pointsShort); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
needsFlushPut(1); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(7); | |
} | |
function primitiveAddGradientFill() { | |
var dirOop; | |
var failureCode; | |
var fill; | |
var isRadial; | |
var nrmOop; | |
var originOop; | |
var rampOop; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 5) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
isRadial = interpreterProxy.booleanValueOf(interpreterProxy.stackValue(0)); | |
nrmOop = interpreterProxy.stackValue(1); | |
dirOop = interpreterProxy.stackValue(2); | |
originOop = interpreterProxy.stackValue(3); | |
rampOop = interpreterProxy.stackValue(4); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(5), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
loadPointfrom(point1Get(), originOop); | |
loadPointfrom(point2Get(), dirOop); | |
loadPointfrom(point3Get(), nrmOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFBadPoint); | |
} | |
fill = loadGradientFillfromalongnormalisRadial(rampOop, point1Get(), point2Get(), point3Get(), isRadial); | |
if (engineStopped) { | |
/* Make sure the stack is okay */ | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.popthenPush(6, interpreterProxy.positive32BitIntegerFor(fill)); | |
} | |
function primitiveAddLine() { | |
var endOop; | |
var failureCode; | |
var leftFill; | |
var rightFill; | |
var startOop; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 4) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
rightFill = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(0)); | |
leftFill = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(1)); | |
endOop = interpreterProxy.stackObjectValue(2); | |
startOop = interpreterProxy.stackObjectValue(3); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(4), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (!(isFillOkay(leftFill) && (isFillOkay(rightFill)))) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
loadPointfrom(point1Get(), startOop); | |
loadPointfrom(point2Get(), endOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFBadPoint); | |
} | |
transformPoints(2); | |
leftFill = transformColor(leftFill); | |
rightFill = transformColor(rightFill); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
loadWideLinefromtolineFillleftFillrightFill(0, point1Get(), point2Get(), 0, leftFill, rightFill); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(4); | |
} | |
function primitiveAddOval() { | |
var borderIndex; | |
var borderWidth; | |
var endOop; | |
var failureCode; | |
var fillIndex; | |
var startOop; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 5) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
borderIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(0)); | |
borderWidth = interpreterProxy.stackIntegerValue(1); | |
fillIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(2)); | |
endOop = interpreterProxy.stackObjectValue(3); | |
startOop = interpreterProxy.stackObjectValue(4); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(5), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (!(isFillOkay(borderIndex) && (isFillOkay(fillIndex)))) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
fillIndex = transformColor(fillIndex); | |
borderIndex = transformColor(borderIndex); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if ((fillIndex === 0) && ((borderIndex === 0) || (borderWidth <= 0))) { | |
return interpreterProxy.pop(5); | |
} | |
if (!needAvailableSpace(16 * GBBaseSize)) { | |
return interpreterProxy.primitiveFailFor(GEFWorkTooBig); | |
} | |
if ((borderWidth > 0) && (borderIndex !== 0)) { | |
borderWidth = transformWidth(borderWidth); | |
} else { | |
borderWidth = 0; | |
} | |
loadPointfrom(point1Get(), startOop); | |
loadPointfrom(point2Get(), endOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFBadPoint); | |
} | |
loadOvallineFillleftFillrightFill(borderWidth, borderIndex, 0, fillIndex); | |
if (engineStopped) { | |
wbStackClear(); | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
needsFlushPut(1); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(5); | |
} | |
function primitiveAddPolygon() { | |
var failureCode; | |
var fillIndex; | |
var length; | |
var lineFill; | |
var lineWidth; | |
var nPoints; | |
var points; | |
var pointsIsArray; | |
var segSize; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 5) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
lineFill = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(0)); | |
lineWidth = interpreterProxy.stackIntegerValue(1); | |
fillIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(2)); | |
nPoints = interpreterProxy.stackIntegerValue(3); | |
points = interpreterProxy.stackObjectValue(4); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(5), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
length = SIZEOF(points); | |
if (interpreterProxy.isWords(points)) { | |
/* Either PointArray or ShortPointArray */ | |
pointsIsArray = false; | |
if (!((length === nPoints) || ((nPoints * 2) === length))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
} else { | |
/* Must be Array of points */ | |
if (!interpreterProxy.isArray(points)) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (length !== nPoints) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
pointsIsArray = true; | |
} | |
if ((lineWidth === 0) || (lineFill === 0)) { | |
segSize = GLBaseSize; | |
} else { | |
segSize = GLWideSize; | |
} | |
if (!needAvailableSpace(segSize * nPoints)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (!(isFillOkay(lineFill) && (isFillOkay(fillIndex)))) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
lineFill = transformColor(lineFill); | |
fillIndex = transformColor(fillIndex); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (((lineFill === 0) || (lineWidth === 0)) && (fillIndex === 0)) { | |
return interpreterProxy.pop(5); | |
} | |
if (lineWidth !== 0) { | |
lineWidth = transformWidth(lineWidth); | |
} | |
if (pointsIsArray) { | |
loadArrayPolygonnPointsfilllineWidthlineFill(points, nPoints, fillIndex, lineWidth, lineFill); | |
} else { | |
loadPolygonnPointsfilllineWidthlineFillpointsShort(points.wordsAsInt32Array(), nPoints, fillIndex, lineWidth, lineFill, nPoints === length); | |
} | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
needsFlushPut(1); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(5); | |
} | |
function primitiveAddRect() { | |
var borderIndex; | |
var borderWidth; | |
var endOop; | |
var failureCode; | |
var fillIndex; | |
var startOop; | |
/* Fail if we have the wrong number of arguments */ | |
if (interpreterProxy.methodArgumentCount() !== 5) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
borderIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(0)); | |
borderWidth = interpreterProxy.stackIntegerValue(1); | |
fillIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(2)); | |
endOop = interpreterProxy.stackObjectValue(3); | |
startOop = interpreterProxy.stackObjectValue(4); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(5), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (!(isFillOkay(borderIndex) && (isFillOkay(fillIndex)))) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
borderIndex = transformColor(borderIndex); | |
fillIndex = transformColor(fillIndex); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if ((fillIndex === 0) && ((borderIndex === 0) || (borderWidth === 0))) { | |
return interpreterProxy.pop(5); | |
} | |
if (!needAvailableSpace(4 * GLBaseSize)) { | |
return interpreterProxy.primitiveFailFor(GEFWorkTooBig); | |
} | |
if ((borderWidth > 0) && (borderIndex !== 0)) { | |
borderWidth = transformWidth(borderWidth); | |
} else { | |
borderWidth = 0; | |
} | |
loadPointfrom(point1Get(), startOop); | |
loadPointfrom(point3Get(), endOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFBadPoint); | |
} | |
point2Get()[0] = point3Get()[0]; | |
point2Get()[1] = point1Get()[1]; | |
point4Get()[0] = point1Get()[0]; | |
point4Get()[1] = point3Get()[1]; | |
transformPoints(4); | |
loadRectanglelineFillleftFillrightFill(borderWidth, borderIndex, 0, fillIndex); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
needsFlushPut(1); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(5); | |
} | |
/* Note: No need to load either bitBlt or spanBuffer */ | |
function primitiveChangedActiveEdgeEntry() { | |
var edge; | |
var edgeOop; | |
var failureCode; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateWaitingChange))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
edgeOop = interpreterProxy.stackObjectValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
edge = loadEdgeStateFrom(edgeOop); | |
if (!edge) { | |
return interpreterProxy.primitiveFailFor(GEFEdgeDataTooSmall); | |
} | |
if (edgeNumLinesOf(edge) === 0) { | |
removeFirstAETEntry(); | |
} else { | |
resortFirstAETEntry(); | |
aetStartPut(aetStartGet() + 1); | |
} | |
statePut(GEStateUpdateEdges); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
if (doProfileStats) { | |
incrementStatby(GWCountChangeAETEntry, 1); | |
incrementStatby(GWTimeChangeAETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
function primitiveCopyBuffer() { | |
var buf1; | |
var buf2; | |
var diff; | |
var dst; | |
var failCode; | |
var i; | |
var src; | |
var iLimiT; | |
if (interpreterProxy.methodArgumentCount() !== 2) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
buf2 = interpreterProxy.stackValue(0); | |
/* Make sure the old buffer is properly initialized */ | |
buf1 = interpreterProxy.stackValue(1); | |
if (((failCode = loadWorkBufferFrom(buf1))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failCode); | |
} | |
if (CLASSOF(buf1) !== CLASSOF(buf2)) { | |
return interpreterProxy.primitiveFailFor(GEFClassMismatch); | |
} | |
diff = SIZEOF(buf2) - SIZEOF(buf1); | |
if (diff < 0) { | |
return interpreterProxy.primitiveFailFor(GEFSizeMismatch); | |
} | |
src = workBuffer; | |
dst = buf2.wordsAsInt32Array(); | |
for (i = 0, iLimiT = (wbTopGet() - 1); i <= iLimiT; i++) { | |
dst[i] = src[i]; | |
} | |
dst[GWBufferTop] = (wbTopGet() + diff); | |
dst[GWSize] = (wbSizeGet() + diff); | |
src = PTR_ADD(src, wbTopGet()); | |
dst = PTR_ADD(dst, wbTopGet() + diff); | |
for (i = 0, iLimiT = ((wbSizeGet() - wbTopGet()) - 1); i <= iLimiT; i++) { | |
dst[i] = src[i]; | |
} | |
if (((failCode = loadWorkBufferFrom(buf2))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failCode); | |
} | |
interpreterProxy.pop(2); | |
} | |
/* Note: Must load bitBlt and spanBuffer */ | |
function primitiveDisplaySpanBuffer() { | |
var failureCode; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(0), GEStateBlitBuffer))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (((failureCode = loadSpanBufferFrom(interpreterProxy.fetchPointerofObject(BESpanIndex, engine)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (!loadBitBltFrom(interpreterProxy.fetchPointerofObject(BEBitBltIndex, engine))) { | |
return interpreterProxy.primitiveFailFor(GEFBitBltLoadFailed); | |
} | |
if ((currentYGet() & aaScanMaskGet()) === aaScanMaskGet()) { | |
displaySpanBufferAt(currentYGet()); | |
postDisplayAction(); | |
} | |
if (!finishedProcessing()) { | |
aetStartPut(0); | |
currentYPut(currentYGet() + 1); | |
statePut(GEStateUpdateEdges); | |
} | |
storeEngineStateInto(engine); | |
if (doProfileStats) { | |
incrementStatby(GWCountDisplaySpan, 1); | |
incrementStatby(GWTimeDisplaySpan, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
/* Turn on/off profiling. Return the old value of the flag. */ | |
function primitiveDoProfileStats() { | |
var newValue; | |
var oldValue; | |
oldValue = doProfileStats; | |
newValue = interpreterProxy.stackObjectValue(0); | |
newValue = interpreterProxy.booleanValueOf(newValue); | |
if (!interpreterProxy.failed()) { | |
doProfileStats = newValue; | |
interpreterProxy.pop(2); | |
interpreterProxy.pushBool(oldValue); | |
} | |
} | |
function primitiveFinishedProcessing() { | |
var failureCode; | |
var finished; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(0)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
finished = finishedProcessing(); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
interpreterProxy.pushBool(finished); | |
if (doProfileStats) { | |
incrementStatby(GWCountFinishTest, 1); | |
incrementStatby(GWTimeFinishTest, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
function primitiveGetAALevel() { | |
var failureCode; | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(0)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
interpreterProxy.pop(1); | |
interpreterProxy.pushInteger(aaLevelGet()); | |
} | |
function primitiveGetBezierStats() { | |
var failureCode; | |
var statOop; | |
var stats; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(1)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
statOop = interpreterProxy.stackObjectValue(0); | |
if (!(!interpreterProxy.failed() && (interpreterProxy.isWords(statOop) && (SIZEOF(statOop) >= 4)))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
stats = statOop.wordsAsInt32Array(); | |
stats[0] = (stats[0] + workBuffer[GWBezierMonotonSubdivisions]); | |
stats[1] = (stats[1] + workBuffer[GWBezierHeightSubdivisions]); | |
stats[2] = (stats[2] + workBuffer[GWBezierOverflowSubdivisions]); | |
stats[3] = (stats[3] + workBuffer[GWBezierLineConversions]); | |
interpreterProxy.pop(1); | |
} | |
function primitiveGetClipRect() { | |
var failureCode; | |
var pointOop; | |
var rectOop; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(1)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
rectOop = interpreterProxy.stackObjectValue(0); | |
if (!(!interpreterProxy.failed() && (interpreterProxy.isPointers(rectOop) && (SIZEOF(rectOop) >= 2)))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
interpreterProxy.pushRemappableOop(rectOop); | |
pointOop = interpreterProxy.makePointwithxValueyValue(clipMinXGet(), clipMinYGet()); | |
interpreterProxy.storePointerofObjectwithValue(0, interpreterProxy.topRemappableOop(), pointOop); | |
pointOop = interpreterProxy.makePointwithxValueyValue(clipMaxXGet(), clipMaxYGet()); | |
rectOop = interpreterProxy.popRemappableOop(); | |
interpreterProxy.storePointerofObjectwithValue(1, rectOop, pointOop); | |
interpreterProxy.popthenPush(2, rectOop); | |
} | |
function primitiveGetCounts() { | |
var failureCode; | |
var statOop; | |
var stats; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(1)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
statOop = interpreterProxy.stackObjectValue(0); | |
if (!(!interpreterProxy.failed() && (interpreterProxy.isWords(statOop) && (SIZEOF(statOop) >= 9)))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
stats = statOop.wordsAsInt32Array(); | |
stats[0] = (stats[0] + workBuffer[GWCountInitializing]); | |
stats[1] = (stats[1] + workBuffer[GWCountFinishTest]); | |
stats[2] = (stats[2] + workBuffer[GWCountNextGETEntry]); | |
stats[3] = (stats[3] + workBuffer[GWCountAddAETEntry]); | |
stats[4] = (stats[4] + workBuffer[GWCountNextFillEntry]); | |
stats[5] = (stats[5] + workBuffer[GWCountMergeFill]); | |
stats[6] = (stats[6] + workBuffer[GWCountDisplaySpan]); | |
stats[7] = (stats[7] + workBuffer[GWCountNextAETEntry]); | |
stats[8] = (stats[8] + workBuffer[GWCountChangeAETEntry]); | |
interpreterProxy.pop(1); | |
} | |
function primitiveGetDepth() { | |
var failureCode; | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(0)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
interpreterProxy.pop(1); | |
interpreterProxy.pushInteger(currentZGet()); | |
} | |
/* Return the reason why the last operation failed. */ | |
function primitiveGetFailureReason() { | |
var failCode; | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
/* Note -- don't call loadEngineFrom here because this will override the stopReason with Zero */ | |
engine = interpreterProxy.stackValue(0); | |
if (typeof engine === "number") { | |
return interpreterProxy.primitiveFailFor(GEFEngineIsInteger); | |
} | |
if (!interpreterProxy.isPointers(engine)) { | |
return interpreterProxy.primitiveFailFor(GEFEngineIsWords); | |
} | |
if (SIZEOF(engine) < BEBalloonEngineSize) { | |
return interpreterProxy.primitiveFailFor(GEFEngineTooSmall); | |
} | |
if (((failCode = loadWorkBufferFrom(interpreterProxy.fetchPointerofObject(BEWorkBufferIndex, engine)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failCode); | |
} | |
interpreterProxy.pop(1); | |
interpreterProxy.pushInteger(stopReasonGet()); | |
} | |
function primitiveGetOffset() { | |
var failureCode; | |
var pointOop; | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(0)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
pointOop = interpreterProxy.makePointwithxValueyValue(destOffsetXGet(), destOffsetYGet()); | |
interpreterProxy.popthenPush(1, pointOop); | |
} | |
function primitiveGetTimes() { | |
var failureCode; | |
var statOop; | |
var stats; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(1)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
statOop = interpreterProxy.stackObjectValue(0); | |
if (!(!interpreterProxy.failed() && (interpreterProxy.isWords(statOop) && (SIZEOF(statOop) >= 9)))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
stats = statOop.wordsAsInt32Array(); | |
stats[0] = (stats[0] + workBuffer[GWTimeInitializing]); | |
stats[1] = (stats[1] + workBuffer[GWTimeFinishTest]); | |
stats[2] = (stats[2] + workBuffer[GWTimeNextGETEntry]); | |
stats[3] = (stats[3] + workBuffer[GWTimeAddAETEntry]); | |
stats[4] = (stats[4] + workBuffer[GWTimeNextFillEntry]); | |
stats[5] = (stats[5] + workBuffer[GWTimeMergeFill]); | |
stats[6] = (stats[6] + workBuffer[GWTimeDisplaySpan]); | |
stats[7] = (stats[7] + workBuffer[GWTimeNextAETEntry]); | |
stats[8] = (stats[8] + workBuffer[GWTimeChangeAETEntry]); | |
interpreterProxy.pop(1); | |
} | |
function primitiveInitializeBuffer() { | |
var size; | |
var wbOop; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFail(); | |
} | |
wbOop = interpreterProxy.stackObjectValue(0); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if (!interpreterProxy.isWords(wbOop)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (((size = SIZEOF(wbOop))) < GWMinimalSize) { | |
return interpreterProxy.primitiveFail(); | |
} | |
workBufferPut(wbOop); | |
objBuffer = PTR_ADD(workBuffer, GWHeaderSize); | |
magicNumberPut(GWMagicNumber); | |
wbSizePut(size); | |
wbTopPut(size); | |
statePut(GEStateUnlocked); | |
objStartPut(GWHeaderSize); | |
objUsedPut(4); | |
objectTypeOfput(0, GEPrimitiveFill); | |
objectLengthOfput(0, 4); | |
objectIndexOfput(0, 0); | |
getStartPut(0); | |
getUsedPut(0); | |
aetStartPut(0); | |
aetUsedPut(0); | |
stopReasonPut(0); | |
needsFlushPut(0); | |
clipMinXPut(0); | |
clipMaxXPut(0); | |
clipMinYPut(0); | |
clipMaxYPut(0); | |
currentZPut(0); | |
resetGraphicsEngineStats(); | |
initEdgeTransform(); | |
initColorTransform(); | |
interpreterProxy.pop(2); | |
interpreterProxy.push(wbOop); | |
} | |
/* Note: No need to load bitBlt but must load spanBuffer */ | |
function primitiveInitializeProcessing() { | |
var failureCode; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(0), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (((failureCode = loadSpanBufferFrom(interpreterProxy.fetchPointerofObject(BESpanIndex, engine)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
initializeGETProcessing(); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
statePut(GEStateAddingFromGET); | |
if (!interpreterProxy.failed()) { | |
storeEngineStateInto(engine); | |
} | |
if (doProfileStats) { | |
incrementStatby(GWCountInitializing, 1); | |
incrementStatby(GWTimeInitializing, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
/* Note: No need to load bitBlt but must load spanBuffer */ | |
function primitiveMergeFillFrom() { | |
var bitsOop; | |
var failureCode; | |
var fillOop; | |
var value; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 2) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(2), GEStateWaitingForFill))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (((failureCode = loadSpanBufferFrom(interpreterProxy.fetchPointerofObject(BESpanIndex, engine)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
fillOop = interpreterProxy.stackObjectValue(0); | |
/* Check bitmap */ | |
bitsOop = interpreterProxy.stackObjectValue(1); | |
if (!(!interpreterProxy.failed() && (CLASSOF(bitsOop) === interpreterProxy.classBitmap()))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (SIZEOF(fillOop) < FTBalloonFillDataSize) { | |
return interpreterProxy.primitiveFailFor(GEFFillDataTooSmall); | |
} | |
value = interpreterProxy.fetchIntegerofObject(FTIndexIndex, fillOop); | |
if (objectIndexOf(lastExportedFillGet()) !== value) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
value = interpreterProxy.fetchIntegerofObject(FTMinXIndex, fillOop); | |
if (lastExportedLeftXGet() !== value) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
value = interpreterProxy.fetchIntegerofObject(FTMaxXIndex, fillOop); | |
if (lastExportedRightXGet() !== value) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
if (SIZEOF(bitsOop) < (lastExportedRightXGet() - lastExportedLeftXGet())) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
fillBitmapSpanfromto(bitsOop.wordsAsInt32Array(), lastExportedLeftXGet(), lastExportedRightXGet()); | |
statePut(GEStateScanningAET); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(2); | |
if (doProfileStats) { | |
incrementStatby(GWCountMergeFill, 1); | |
incrementStatby(GWTimeMergeFill, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
function primitiveNeedsFlush() { | |
var failureCode; | |
var needFlush; | |
if (interpreterProxy.methodArgumentCount() !== 0) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(0)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
needFlush = needsFlush(); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
interpreterProxy.pushBool(needFlush); | |
} | |
function primitiveNeedsFlushPut() { | |
var failureCode; | |
var needFlush; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFrom(interpreterProxy.stackValue(1)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
needFlush = interpreterProxy.booleanValueOf(interpreterProxy.stackValue(0)); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (needFlush === true) { | |
needsFlushPut(1); | |
} else { | |
needsFlushPut(0); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
} | |
/* Note: No need to load either bitBlt or spanBuffer */ | |
function primitiveNextActiveEdgeEntry() { | |
var edge; | |
var edgeOop; | |
var failureCode; | |
var hasEdge; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredStateor(interpreterProxy.stackValue(1), GEStateUpdateEdges, GEStateCompleted))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
edgeOop = interpreterProxy.stackObjectValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
hasEdge = false; | |
if (stateGet() !== GEStateCompleted) { | |
hasEdge = findNextExternalUpdateFromAET(); | |
if (hasEdge) { | |
edge = aetBuffer[aetStartGet()]; | |
storeEdgeStateFrominto(edge, edgeOop); | |
statePut(GEStateWaitingChange); | |
} else { | |
statePut(GEStateAddingFromGET); | |
} | |
} | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(2); | |
interpreterProxy.pushBool(!hasEdge); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextAETEntry, 1); | |
incrementStatby(GWTimeNextAETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
/* Note: No need to load bitBlt but must load spanBuffer */ | |
function primitiveNextFillEntry() { | |
var failureCode; | |
var fillOop; | |
var hasFill; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateScanningAET))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (((failureCode = loadSpanBufferFrom(interpreterProxy.fetchPointerofObject(BESpanIndex, engine)))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
if (!loadFormsFrom(interpreterProxy.fetchPointerofObject(BEFormsIndex, engine))) { | |
return interpreterProxy.primitiveFailFor(GEFFormLoadFailed); | |
} | |
if (clearSpanBufferGet() !== 0) { | |
if ((currentYGet() & aaScanMaskGet()) === 0) { | |
clearSpanBuffer(); | |
} | |
clearSpanBufferPut(0); | |
} | |
fillOop = interpreterProxy.stackObjectValue(0); | |
hasFill = findNextExternalFillFromAET(); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (hasFill) { | |
storeFillStateInto(fillOop); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
if (hasFill) { | |
statePut(GEStateWaitingForFill); | |
} else { | |
wbStackClear(); | |
spanEndAAPut(0); | |
statePut(GEStateBlitBuffer); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(2); | |
interpreterProxy.pushBool(!hasFill); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextFillEntry, 1); | |
incrementStatby(GWTimeNextFillEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
/* Note: No need to load either bitBlt or spanBuffer */ | |
function primitiveNextGlobalEdgeEntry() { | |
var edge; | |
var edgeOop; | |
var failureCode; | |
var hasEdge; | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateAddingFromGET))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
edgeOop = interpreterProxy.stackObjectValue(0); | |
hasEdge = findNextExternalEntryFromGET(); | |
if (hasEdge) { | |
edge = getBuffer[getStartGet()]; | |
storeEdgeStateFrominto(edge, edgeOop); | |
getStartPut(getStartGet() + 1); | |
} | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFWrongEdge); | |
} | |
if (hasEdge) { | |
statePut(GEStateWaitingForEdge); | |
} else { | |
/* Start scanning the AET */ | |
statePut(GEStateScanningAET); | |
clearSpanBufferPut(1); | |
aetStartPut(0); | |
wbStackClear(); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(2); | |
interpreterProxy.pushBool(!hasEdge); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextGETEntry, 1); | |
incrementStatby(GWTimeNextGETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
} | |
function primitiveRegisterExternalEdge() { | |
var edge; | |
var failureCode; | |
var index; | |
var initialX; | |
var initialY; | |
var initialZ; | |
var leftFillIndex; | |
var rightFillIndex; | |
if (interpreterProxy.methodArgumentCount() !== 6) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(6), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
rightFillIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(0)); | |
leftFillIndex = interpreterProxy.positive32BitValueOf(interpreterProxy.stackValue(1)); | |
initialZ = interpreterProxy.stackIntegerValue(2); | |
initialY = interpreterProxy.stackIntegerValue(3); | |
initialX = interpreterProxy.stackIntegerValue(4); | |
index = interpreterProxy.stackIntegerValue(5); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
if (!allocateObjEntry(GEBaseEdgeSize)) { | |
return interpreterProxy.primitiveFailFor(GEFWorkTooBig); | |
} | |
if (!(isFillOkay(leftFillIndex) && (isFillOkay(rightFillIndex)))) { | |
return interpreterProxy.primitiveFailFor(GEFWrongFill); | |
} | |
edge = objUsed; | |
/* Install type and length */ | |
objUsed = edge + GEBaseEdgeSize; | |
objectTypeOfput(edge, GEPrimitiveEdge); | |
objectLengthOfput(edge, GEBaseEdgeSize); | |
objectIndexOfput(edge, index); | |
edgeXValueOfput(edge, initialX); | |
edgeYValueOfput(edge, initialY); | |
edgeZValueOfput(edge, initialZ); | |
edgeLeftFillOfput(edge, transformColor(leftFillIndex)); | |
edgeRightFillOfput(edge, transformColor(rightFillIndex)); | |
if (engineStopped) { | |
return interpreterProxy.primitiveFailFor(GEFEngineStopped); | |
} | |
if (!interpreterProxy.failed()) { | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(6); | |
} | |
} | |
function primitiveRegisterExternalFill() { | |
var failureCode; | |
var fill; | |
var index; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
index = interpreterProxy.stackIntegerValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
fill = 0; | |
while (fill === 0) { | |
if (!allocateObjEntry(GEBaseEdgeSize)) { | |
return interpreterProxy.primitiveFailFor(GEFWorkTooBig); | |
} | |
fill = objUsed; | |
/* Install type and length */ | |
objUsed = fill + GEBaseFillSize; | |
objectTypeOfput(fill, GEPrimitiveFill); | |
objectLengthOfput(fill, GEBaseFillSize); | |
objectIndexOfput(fill, index); | |
} | |
if (!interpreterProxy.failed()) { | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(2); | |
interpreterProxy.pushInteger(fill); | |
} | |
} | |
/* Start/Proceed rendering the entire image */ | |
function primitiveRenderImage() { | |
var failCode; | |
if (((failCode = loadRenderingState())) !== 0) { | |
return interpreterProxy.primitiveFailFor(failCode); | |
} | |
proceedRenderingScanline(); | |
if (engineStopped) { | |
return storeRenderingState(); | |
} | |
proceedRenderingImage(); | |
storeRenderingState(); | |
} | |
/* Start rendering the entire image */ | |
function primitiveRenderScanline() { | |
var failCode; | |
if (((failCode = loadRenderingState())) !== 0) { | |
return interpreterProxy.primitiveFailFor(failCode); | |
} | |
proceedRenderingScanline(); | |
storeRenderingState(); | |
} | |
function primitiveSetAALevel() { | |
var failureCode; | |
var level; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
level = interpreterProxy.stackIntegerValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
setAALevel(level); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
} | |
/* Primitive. Set the BitBlt plugin to use. */ | |
function primitiveSetBitBltPlugin() { | |
var i; | |
var length; | |
var needReload; | |
var pluginName; | |
var ptr; | |
/* Must be string to work */ | |
pluginName = interpreterProxy.stackValue(0); | |
if (!interpreterProxy.isBytes(pluginName)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
length = BYTESIZEOF(pluginName); | |
if (length >= 256) { | |
return interpreterProxy.primitiveFail(); | |
} | |
ptr = pluginName.bytes; | |
needReload = false; | |
// JS hack: can't copy bytes as in the C version | |
var newPluginName = pluginName.bytesAsString(); | |
if (newPluginName !== bbPluginName) { | |
bbPluginName = newPluginName; | |
needReload = true; | |
} | |
if (needReload) { | |
if (!initialiseModule()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
} | |
interpreterProxy.pop(1); | |
} | |
function primitiveSetClipRect() { | |
var failureCode; | |
var rectOop; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
rectOop = interpreterProxy.stackObjectValue(0); | |
if (!(!interpreterProxy.failed() && (interpreterProxy.isPointers(rectOop) && (SIZEOF(rectOop) >= 2)))) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
loadPointfrom(point1Get(), interpreterProxy.fetchPointerofObject(0, rectOop)); | |
loadPointfrom(point2Get(), interpreterProxy.fetchPointerofObject(1, rectOop)); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
clipMinXPut(point1Get()[0]); | |
clipMinYPut(point1Get()[1]); | |
clipMaxXPut(point2Get()[0]); | |
clipMaxYPut(point2Get()[1]); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
} | |
function primitiveSetColorTransform() { | |
var failureCode; | |
var transformOop; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
transformOop = interpreterProxy.stackObjectValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
loadColorTransformFrom(transformOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(GEFEntityLoadFailed); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
} | |
function primitiveSetDepth() { | |
var depth; | |
var failureCode; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
depth = interpreterProxy.stackIntegerValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
currentZPut(depth); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
} | |
function primitiveSetEdgeTransform() { | |
var failureCode; | |
var transformOop; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
transformOop = interpreterProxy.stackObjectValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
loadEdgeTransformFrom(transformOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
} | |
function primitiveSetOffset() { | |
var failureCode; | |
var pointOop; | |
if (interpreterProxy.methodArgumentCount() !== 1) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadNumArgs); | |
} | |
if (((failureCode = quickLoadEngineFromrequiredState(interpreterProxy.stackValue(1), GEStateUnlocked))) !== 0) { | |
return interpreterProxy.primitiveFailFor(failureCode); | |
} | |
pointOop = interpreterProxy.stackValue(0); | |
if (CLASSOF(pointOop) !== interpreterProxy.classPoint()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
loadPointfrom(point1Get(), pointOop); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFailFor(PrimErrBadArgument); | |
} | |
destOffsetXPut(point1Get()[0]); | |
destOffsetYPut(point1Get()[1]); | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(1); | |
} | |
/* This is the main rendering entry */ | |
function proceedRenderingImage() { | |
var external; | |
while (!(finishedProcessing())) { | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
external = findNextExternalEntryFromGET(); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextGETEntry, 1); | |
incrementStatby(GWTimeNextGETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateAddingFromGET); | |
} | |
if (external) { | |
statePut(GEStateWaitingForEdge); | |
return stopBecauseOf(GErrorGETEntry); | |
} | |
aetStartPut(0); | |
wbStackClear(); | |
clearSpanBufferPut(1); | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if ((clearSpanBufferGet() !== 0) && ((currentYGet() & aaScanMaskGet()) === 0)) { | |
clearSpanBuffer(); | |
} | |
clearSpanBufferPut(0); | |
external = findNextExternalFillFromAET(); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextFillEntry, 1); | |
incrementStatby(GWTimeNextFillEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateScanningAET); | |
} | |
if (external) { | |
statePut(GEStateWaitingForFill); | |
return stopBecauseOf(GErrorFillEntry); | |
} | |
wbStackClear(); | |
spanEndAAPut(0); | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if ((currentYGet() & aaScanMaskGet()) === aaScanMaskGet()) { | |
displaySpanBufferAt(currentYGet()); | |
postDisplayAction(); | |
} | |
if (doProfileStats) { | |
incrementStatby(GWCountDisplaySpan, 1); | |
incrementStatby(GWTimeDisplaySpan, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateBlitBuffer); | |
} | |
if (finishedProcessing()) { | |
return 0; | |
} | |
aetStartPut(0); | |
currentYPut(currentYGet() + 1); | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
external = findNextExternalUpdateFromAET(); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextAETEntry, 1); | |
incrementStatby(GWTimeNextAETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateUpdateEdges); | |
} | |
if (external) { | |
statePut(GEStateWaitingChange); | |
return stopBecauseOf(GErrorAETEntry); | |
} | |
} | |
} | |
/* Proceed rendering the current scan line. | |
This method may be called after some Smalltalk code has been executed inbetween. */ | |
/* This is the main rendering entry */ | |
function proceedRenderingScanline() { | |
var external; | |
var state; | |
state = stateGet(); | |
if (state === GEStateUnlocked) { | |
initializeGETProcessing(); | |
if (engineStopped) { | |
return 0; | |
} | |
state = GEStateAddingFromGET; | |
} | |
if (state === GEStateAddingFromGET) { | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
external = findNextExternalEntryFromGET(); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextGETEntry, 1); | |
incrementStatby(GWTimeNextGETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateAddingFromGET); | |
} | |
if (external) { | |
statePut(GEStateWaitingForEdge); | |
return stopBecauseOf(GErrorGETEntry); | |
} | |
aetStartPut(0); | |
wbStackClear(); | |
clearSpanBufferPut(1); | |
state = GEStateScanningAET; | |
} | |
if (state === GEStateScanningAET) { | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if ((clearSpanBufferGet() !== 0) && ((currentYGet() & aaScanMaskGet()) === 0)) { | |
clearSpanBuffer(); | |
} | |
clearSpanBufferPut(0); | |
external = findNextExternalFillFromAET(); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextFillEntry, 1); | |
incrementStatby(GWTimeNextFillEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateScanningAET); | |
} | |
if (external) { | |
statePut(GEStateWaitingForFill); | |
return stopBecauseOf(GErrorFillEntry); | |
} | |
state = GEStateBlitBuffer; | |
wbStackClear(); | |
spanEndAAPut(0); | |
} | |
if (state === GEStateBlitBuffer) { | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
if ((currentYGet() & aaScanMaskGet()) === aaScanMaskGet()) { | |
displaySpanBufferAt(currentYGet()); | |
postDisplayAction(); | |
} | |
if (doProfileStats) { | |
incrementStatby(GWCountDisplaySpan, 1); | |
incrementStatby(GWTimeDisplaySpan, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateBlitBuffer); | |
} | |
if (finishedProcessing()) { | |
return 0; | |
} | |
state = GEStateUpdateEdges; | |
aetStartPut(0); | |
currentYPut(currentYGet() + 1); | |
} | |
if (state === GEStateUpdateEdges) { | |
if (doProfileStats) { | |
geProfileTime = interpreterProxy.ioMicroMSecs(); | |
} | |
external = findNextExternalUpdateFromAET(); | |
if (doProfileStats) { | |
incrementStatby(GWCountNextAETEntry, 1); | |
incrementStatby(GWTimeNextAETEntry, interpreterProxy.ioMicroMSecs() - geProfileTime); | |
} | |
if (engineStopped) { | |
return statePut(GEStateUpdateEdges); | |
} | |
if (external) { | |
statePut(GEStateWaitingChange); | |
return stopBecauseOf(GErrorAETEntry); | |
} | |
statePut(GEStateAddingFromGET); | |
} | |
} | |
/* Load the minimal required state from the engineOop, e.g., just the work buffer. | |
Answer 0 on success or non-zero a failure code on failure */ | |
function quickLoadEngineFrom(engineOop) { | |
var failCode; | |
if (interpreterProxy.failed()) { | |
return GEFAlreadyFailed; | |
} | |
if (typeof engineOop === "number") { | |
return GEFEngineIsInteger; | |
} | |
if (!interpreterProxy.isPointers(engineOop)) { | |
return GEFEngineIsWords; | |
} | |
if (SIZEOF(engineOop) < BEBalloonEngineSize) { | |
return GEFEngineTooSmall; | |
} | |
engine = engineOop; | |
if (((failCode = loadWorkBufferFrom(interpreterProxy.fetchPointerofObject(BEWorkBufferIndex, engineOop)))) !== 0) { | |
return failCode; | |
} | |
stopReasonPut(0); | |
objUsed = objUsedGet(); | |
engineStopped = false; | |
return 0; | |
} | |
function quickLoadEngineFromrequiredState(oop, requiredState) { | |
var failureCode; | |
if (((failureCode = quickLoadEngineFrom(oop))) !== 0) { | |
return failureCode; | |
} | |
if (stateGet() === requiredState) { | |
return 0; | |
} | |
stopReasonPut(GErrorBadState); | |
return GEFWrongState; | |
} | |
function quickLoadEngineFromrequiredStateor(oop, requiredState, alternativeState) { | |
var failureCode; | |
if (((failureCode = quickLoadEngineFrom(oop))) !== 0) { | |
return failureCode; | |
} | |
if (stateGet() === requiredState) { | |
return 0; | |
} | |
if (stateGet() === alternativeState) { | |
return 0; | |
} | |
stopReasonPut(GErrorBadState); | |
return GEFWrongState; | |
} | |
/* Remove any top fills if they have become invalid. */ | |
function quickRemoveInvalidFillsAt(leftX) { | |
if (stackFillSize() === 0) { | |
return null; | |
} | |
while (topRightX() <= leftX) { | |
hideFilldepth(topFill(), topDepth()); | |
if (stackFillSize() === 0) { | |
return null; | |
} | |
} | |
} | |
/* Sort elements i through j of self to be nondescending according to | |
sortBlock. */ | |
/* Note: The original loop has been heavily re-written for C translation */ | |
function quickSortGlobalEdgeTablefromto(array, i, j) { | |
var again; | |
var before; | |
var di; | |
var dij; | |
var dj; | |
var ij; | |
var k; | |
var l; | |
var n; | |
var tmp; | |
var tt; | |
/* The prefix d means the data at that index. */ | |
if (((n = (j + 1) - i)) <= 1) { | |
return 0; | |
} | |
di = array[i]; | |
dj = array[j]; | |
/* i.e., should di precede dj? */ | |
before = getSortsbefore(di, dj); | |
if (!before) { | |
tmp = array[i]; | |
array[i] = array[j]; | |
array[j] = tmp; | |
tt = di; | |
di = dj; | |
dj = tt; | |
} | |
if (n <= 2) { | |
return 0; | |
} | |
/* ij is the midpoint of i and j. */ | |
ij = (i + j) >> 1; | |
/* Sort di,dij,dj. Make dij be their median. */ | |
dij = array[ij]; | |
/* i.e. should di precede dij? */ | |
before = getSortsbefore(di, dij); | |
if (before) { | |
/* i.e., should dij precede dj? */ | |
before = getSortsbefore(dij, dj); | |
if (!before) { | |
/* i.e., should dij precede dj? */ | |
tmp = array[j]; | |
array[j] = array[ij]; | |
array[ij] = tmp; | |
dij = dj; | |
} | |
} else { | |
/* i.e. di should come after dij */ | |
tmp = array[i]; | |
array[i] = array[ij]; | |
array[ij] = tmp; | |
dij = di; | |
} | |
if (n <= 3) { | |
return 0; | |
} | |
k = i; | |
l = j; | |
again = true; | |
while (again) { | |
before = true; | |
while (before) { | |
if (k <= ((--l))) { | |
tmp = array[l]; | |
before = getSortsbefore(dij, tmp); | |
} else { | |
before = false; | |
} | |
} | |
before = true; | |
while (before) { | |
if (((++k)) <= l) { | |
tmp = array[k]; | |
before = getSortsbefore(tmp, dij); | |
} else { | |
before = false; | |
} | |
} | |
again = k <= l; | |
if (again) { | |
tmp = array[k]; | |
array[k] = array[l]; | |
array[l] = tmp; | |
} | |
} | |
quickSortGlobalEdgeTablefromto(array, i, l); | |
quickSortGlobalEdgeTablefromto(array, k, j); | |
} | |
function rShiftTable() { | |
var theTable = | |
[0, 5, 4, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]; | |
return theTable; | |
} | |
function removeFirstAETEntry() { | |
var index; | |
index = aetStartGet(); | |
aetUsedPut(aetUsedGet() - 1); | |
while (index < aetUsedGet()) { | |
aetBuffer[index] = aetBuffer[index + 1]; | |
++index; | |
} | |
} | |
function repeatValuemax(delta, maxValue) { | |
var newDelta; | |
newDelta = delta; | |
while (newDelta < 0) { | |
newDelta += maxValue; | |
} | |
while (newDelta >= maxValue) { | |
newDelta -= maxValue; | |
} | |
return newDelta; | |
} | |
function resetGraphicsEngineStats() { | |
workBuffer[GWTimeInitializing] = 0; | |
workBuffer[GWTimeFinishTest] = 0; | |
workBuffer[GWTimeNextGETEntry] = 0; | |
workBuffer[GWTimeAddAETEntry] = 0; | |
workBuffer[GWTimeNextFillEntry] = 0; | |
workBuffer[GWTimeMergeFill] = 0; | |
workBuffer[GWTimeDisplaySpan] = 0; | |
workBuffer[GWTimeNextAETEntry] = 0; | |
workBuffer[GWTimeChangeAETEntry] = 0; | |
workBuffer[GWCountInitializing] = 0; | |
workBuffer[GWCountFinishTest] = 0; | |
workBuffer[GWCountNextGETEntry] = 0; | |
workBuffer[GWCountAddAETEntry] = 0; | |
workBuffer[GWCountNextFillEntry] = 0; | |
workBuffer[GWCountMergeFill] = 0; | |
workBuffer[GWCountDisplaySpan] = 0; | |
workBuffer[GWCountNextAETEntry] = 0; | |
workBuffer[GWCountChangeAETEntry] = 0; | |
workBuffer[GWBezierMonotonSubdivisions] = 0; | |
workBuffer[GWBezierHeightSubdivisions] = 0; | |
workBuffer[GWBezierOverflowSubdivisions] = 0; | |
workBuffer[GWBezierLineConversions] = 0; | |
} | |
function resortFirstAETEntry() { | |
var edge; | |
var leftEdge; | |
var xValue; | |
if (aetStartGet() === 0) { | |
return null; | |
} | |
edge = aetBuffer[aetStartGet()]; | |
xValue = edgeXValueOf(edge); | |
leftEdge = aetBuffer[aetStartGet() - 1]; | |
if (edgeXValueOf(leftEdge) <= xValue) { | |
return null; | |
} | |
moveAETEntryFromedgex(aetStartGet(), edge, xValue); | |
} | |
function returnWideBezierFill() { | |
return (dispatchReturnValue = wideBezierFillOf(dispatchedValue)); | |
} | |
function returnWideBezierWidth() { | |
return (dispatchReturnValue = wideBezierWidthOf(dispatchedValue)); | |
} | |
/* Return the fill of the (wide) line - this method is called from a case. */ | |
function returnWideLineFill() { | |
return (dispatchReturnValue = wideLineFillOf(dispatchedValue)); | |
} | |
/* Return the width of the (wide) line - this method is called from a case. */ | |
function returnWideLineWidth() { | |
return (dispatchReturnValue = wideLineWidthOf(dispatchedValue)); | |
} | |
/* Set the anti-aliasing level. Three levels are supported: | |
1 - No antialiasing | |
2 - 2x2 unweighted anti-aliasing | |
4 - 4x4 unweighted anti-aliasing. | |
*/ | |
function setAALevel(level) { | |
var aaLevel; | |
if (level >= 4) { | |
aaLevel = 4; | |
} | |
if ((level >= 2) && (level < 4)) { | |
aaLevel = 2; | |
} | |
if (level < 2) { | |
aaLevel = 1; | |
} | |
aaLevelPut(aaLevel); | |
if (aaLevel === 1) { | |
aaShiftPut(0); | |
aaColorMaskPut(4294967295); | |
aaScanMaskPut(0); | |
} | |
if (aaLevel === 2) { | |
aaShiftPut(1); | |
aaColorMaskPut(4244438268); | |
aaScanMaskPut(1); | |
} | |
if (aaLevel === 4) { | |
aaShiftPut(2); | |
aaColorMaskPut(4042322160); | |
aaScanMaskPut(3); | |
} | |
aaColorShiftPut(aaShiftGet() * 2); | |
aaHalfPixelPut(aaShiftGet()); | |
} | |
/* Note: This is coded so that is can be run from Squeak. */ | |
function setInterpreter(anInterpreter) { | |
var ok; | |
interpreterProxy = anInterpreter; | |
ok = interpreterProxy.majorVersion() == VM_PROXY_MAJOR; | |
if (ok === false) { | |
return false; | |
} | |
ok = interpreterProxy.minorVersion() >= VM_PROXY_MINOR; | |
return ok; | |
} | |
/* Return the run-length value from the given ShortRunArray. */ | |
function shortRunLengthAtfrom(i, runArray) { | |
return (runArray[i]|0) >>> 16; | |
} | |
/* Return the run-length value from the given ShortRunArray. | |
Note: We don't need any coercion to short/int here, since | |
we deal basically only with unsigned values. */ | |
function shortRunValueAtfrom(i, runArray) { | |
return (runArray[i]|0) & 65535; | |
} | |
function showFilldepthrightX(fillIndex, depth, rightX) { | |
if (!allocateStackFillEntry()) { | |
return null; | |
} | |
stackFillValueput(0, fillIndex); | |
stackFillDepthput(0, depth); | |
stackFillRightXput(0, rightX); | |
if (stackFillSize() === stackFillEntryLength()) { | |
return null; | |
} | |
if (fillSortsbefore(0, stackFillSize() - stackFillEntryLength())) { | |
/* New top fill */ | |
stackFillValueput(0, topFillValue()); | |
stackFillDepthput(0, topFillDepth()); | |
stackFillRightXput(0, topFillRightX()); | |
topFillValuePut(fillIndex); | |
topFillDepthPut(depth); | |
topFillRightXPut(rightX); | |
} | |
} | |
function smallSqrtTable() { | |
var theTable = | |
[0, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6]; | |
return theTable; | |
} | |
/* Sort the entire global edge table */ | |
function sortGlobalEdgeTable() { | |
quickSortGlobalEdgeTablefromto(getBuffer, 0, getUsedGet() - 1); | |
} | |
function spanEndAAGet() { | |
return workBuffer[GWSpanEndAA]; | |
} | |
function spanEndAAPut(value) { | |
return workBuffer[GWSpanEndAA] = value; | |
} | |
function spanEndGet() { | |
return workBuffer[GWSpanEnd]; | |
} | |
function spanEndPut(value) { | |
return workBuffer[GWSpanEnd] = value; | |
} | |
function spanSizeGet() { | |
return workBuffer[GWSpanSize]; | |
} | |
function spanSizePut(value) { | |
return workBuffer[GWSpanSize] = value; | |
} | |
function spanStartGet() { | |
return workBuffer[GWSpanStart]; | |
} | |
function spanStartPut(value) { | |
return workBuffer[GWSpanStart] = value; | |
} | |
function squaredLengthOfwith(deltaX, deltaY) { | |
return (deltaX * deltaX) + (deltaY * deltaY); | |
} | |
function stackFillDepth(index) { | |
return wbStackValue(index + 1); | |
} | |
function stackFillDepthput(index, value) { | |
return wbStackValueput(index + 1, value); | |
} | |
function stackFillEntryLength() { | |
return 3; | |
} | |
function stackFillRightX(index) { | |
return wbStackValue(index + 2); | |
} | |
function stackFillRightXput(index, value) { | |
return wbStackValueput(index + 2, value); | |
} | |
function stackFillSize() { | |
return wbStackSize(); | |
} | |
function stackFillValue(index) { | |
return wbStackValue(index); | |
} | |
function stackFillValueput(index, value) { | |
return wbStackValueput(index, value); | |
} | |
function stateGet() { | |
return workBuffer[GWState]; | |
} | |
function statePut(value) { | |
return workBuffer[GWState] = value; | |
} | |
/* Initialize the current entry in the GET by stepping to the current scan line */ | |
function stepToFirstBezier() { | |
return stepToFirstBezierInat(getBuffer[getStartGet()], currentYGet()); | |
} | |
/* Initialize the bezier at yValue. | |
TODO: Check if reducing maxSteps from 2*deltaY to deltaY | |
brings a *significant* performance improvement. | |
In theory this should make for double step performance | |
but will cost in quality. Might be that the AA stuff will | |
compensate for this - but I'm not really sure. */ | |
function stepToFirstBezierInat(bezier, yValue) { | |
var deltaY; | |
var endX; | |
var endY; | |
var fwDDx; | |
var fwDDy; | |
var fwDx; | |
var fwDy; | |
var fwX1; | |
var fwX2; | |
var fwY1; | |
var fwY2; | |
var maxSteps; | |
var scaledStepSize; | |
var squaredStepSize; | |
var startX; | |
var startY; | |
var updateData; | |
var viaX; | |
var viaY; | |
/* Do a quick check if there is anything at all to do */ | |
if (!isWide(bezier) && (yValue >= bezierEndYOf(bezier))) { | |
return edgeNumLinesOfput(bezier, 0); | |
} | |
startX = edgeXValueOf(bezier); | |
startY = edgeYValueOf(bezier); | |
viaX = bezierViaXOf(bezier); | |
viaY = bezierViaYOf(bezier); | |
endX = bezierEndXOf(bezier); | |
endY = bezierEndYOf(bezier); | |
/* Initialize integer forward differencing */ | |
deltaY = endY - startY; | |
fwX1 = (viaX - startX) * 2; | |
fwX2 = (startX + endX) - (viaX * 2); | |
fwY1 = (viaY - startY) * 2; | |
fwY2 = (startY + endY) - (viaY * 2); | |
maxSteps = deltaY * 2; | |
if (maxSteps < 2) { | |
maxSteps = 2; | |
} | |
scaledStepSize = DIV(16777216, maxSteps); | |
squaredStepSize = absoluteSquared8Dot24(scaledStepSize); | |
fwDx = fwX1 * scaledStepSize; | |
fwDDx = (fwX2 * squaredStepSize) * 2; | |
fwDx += fwDDx >> 1; | |
fwDy = fwY1 * scaledStepSize; | |
fwDDy = (fwY2 * squaredStepSize) * 2; | |
/* Store the values */ | |
fwDy += fwDDy >> 1; | |
edgeNumLinesOfput(bezier, deltaY); | |
updateData = bezierUpdateDataOf(bezier); | |
updateData[GBUpdateX] = (startX * 256); | |
updateData[GBUpdateY] = (startY * 256); | |
updateData[GBUpdateDX] = fwDx; | |
updateData[GBUpdateDY] = fwDy; | |
updateData[GBUpdateDDX] = fwDDx; | |
updateData[GBUpdateDDY] = fwDDy; | |
if (((startY = edgeYValueOf(bezier))) !== yValue) { | |
stepToNextBezierInat(bezier, yValue); | |
edgeNumLinesOfput(bezier, deltaY - (yValue - startY)); | |
} | |
} | |
/* Initialize the current entry in the GET by stepping to the current scan line */ | |
function stepToFirstLine() { | |
return stepToFirstLineInat(getBuffer[getStartGet()], currentYGet()); | |
} | |
/* Initialize the line at yValue */ | |
function stepToFirstLineInat(line, yValue) { | |
var deltaX; | |
var deltaY; | |
var error; | |
var errorAdjUp; | |
var i; | |
var startY; | |
var widthX; | |
var xDir; | |
var xInc; | |
/* Do a quick check if there is anything at all to do */ | |
if (!isWide(line) && (yValue >= lineEndYOf(line))) { | |
return edgeNumLinesOfput(line, 0); | |
} | |
deltaX = lineEndXOf(line) - edgeXValueOf(line); | |
/* Check if edge goes left to right */ | |
deltaY = lineEndYOf(line) - edgeYValueOf(line); | |
if (deltaX >= 0) { | |
xDir = 1; | |
widthX = deltaX; | |
error = 0; | |
} else { | |
xDir = -1; | |
widthX = 0 - deltaX; | |
error = 1 - deltaY; | |
} | |
if (deltaY === 0) { | |
/* No error for horizontal edges */ | |
error = 0; | |
/* Encodes width and direction */ | |
xInc = deltaX; | |
errorAdjUp = 0; | |
} else { | |
/* Check if edge is y-major */ | |
if (deltaY > widthX) { | |
/* Note: The '>' instead of '>=' could be important here... */ | |
xInc = 0; | |
errorAdjUp = widthX; | |
} else { | |
xInc = (DIV(widthX, deltaY)) * xDir; | |
errorAdjUp = MOD(widthX, deltaY); | |
} | |
} | |
edgeNumLinesOfput(line, deltaY); | |
lineXDirectionOfput(line, xDir); | |
lineXIncrementOfput(line, xInc); | |
lineErrorOfput(line, error); | |
lineErrorAdjUpOfput(line, errorAdjUp); | |
lineErrorAdjDownOfput(line, deltaY); | |
if (((startY = edgeYValueOf(line))) !== yValue) { | |
for (i = startY; i <= (yValue - 1); i++) { | |
stepToNextLineInat(line, i); | |
} | |
edgeNumLinesOfput(line, deltaY - (yValue - startY)); | |
} | |
} | |
/* Initialize the current entry in the GET by stepping to the current scan line */ | |
function stepToFirstWideBezier() { | |
return stepToFirstWideBezierInat(getBuffer[getStartGet()], currentYGet()); | |
} | |
/* Initialize the bezier at yValue */ | |
function stepToFirstWideBezierInat(bezier, yValue) { | |
var endX; | |
var i; | |
var lineOffset; | |
var lineWidth; | |
var nLines; | |
var startY; | |
var xDir; | |
var yEntry; | |
var yExit; | |
/* Get some values */ | |
lineWidth = wideBezierExtentOf(bezier); | |
/* Compute the incremental values of the bezier */ | |
lineOffset = offsetFromWidth(lineWidth); | |
endX = bezierEndXOf(bezier); | |
startY = edgeYValueOf(bezier); | |
stepToFirstBezierInat(bezier, startY); | |
/* Copy the incremental update data */ | |
nLines = edgeNumLinesOf(bezier); | |
for (i = 0; i <= 5; i++) { | |
wideBezierUpdateDataOf(bezier)[i] = bezierUpdateDataOf(bezier)[i]; | |
} | |
xDir = bezierUpdateDataOf(bezier)[GBUpdateDX]; | |
if (xDir === 0) { | |
bezierUpdateDataOf(bezier)[GBUpdateDDX]; | |
} | |
if (xDir >= 0) { | |
xDir = 1; | |
} else { | |
xDir = -1; | |
} | |
if (xDir < 0) { | |
adjustWideBezierLeftwidthoffsetendX(bezier, lineWidth, lineOffset, endX); | |
} else { | |
adjustWideBezierRightwidthoffsetendX(bezier, lineWidth, lineOffset, endX); | |
} | |
if (nLines === 0) { | |
bezierUpdateDataOf(bezier)[GBUpdateX] = (bezierFinalXOf(bezier) * 256); | |
} | |
edgeNumLinesOfput(bezier, nLines + lineWidth); | |
/* turned on at lineOffset */ | |
yEntry = 0; | |
/* turned off at zero */ | |
yExit = (0 - nLines) - lineOffset; | |
wideBezierEntryOfput(bezier, yEntry); | |
wideBezierExitOfput(bezier, yExit); | |
if ((yEntry >= lineOffset) && (yExit < 0)) { | |
edgeFillsValidate(bezier); | |
} else { | |
edgeFillsInvalidate(bezier); | |
} | |
computeFinalWideBezierValueswidth(bezier, lineWidth); | |
if (startY !== yValue) { | |
/* Note: Must single step here so that entry/exit works */ | |
for (i = startY; i <= (yValue - 1); i++) { | |
stepToNextWideBezierInat(bezier, i); | |
} | |
edgeNumLinesOfput(bezier, edgeNumLinesOf(bezier) - (yValue - startY)); | |
} | |
} | |
/* Initialize the current entry in the GET by stepping to the current scan line */ | |
function stepToFirstWideLine() { | |
return stepToFirstWideLineInat(getBuffer[getStartGet()], currentYGet()); | |
} | |
/* Initialize the wide line at yValue. */ | |
function stepToFirstWideLineInat(line, yValue) { | |
var i; | |
var lineOffset; | |
var lineWidth; | |
var nLines; | |
var startX; | |
var startY; | |
var xDir; | |
var yEntry; | |
var yExit; | |
/* Get some values */ | |
lineWidth = wideLineExtentOf(line); | |
/* Compute the incremental values of the line */ | |
lineOffset = offsetFromWidth(lineWidth); | |
startX = edgeXValueOf(line); | |
startY = edgeYValueOf(line); | |
stepToFirstLineInat(line, startY); | |
nLines = edgeNumLinesOf(line); | |
/* Adjust the line to start at the correct X position */ | |
xDir = lineXDirectionOf(line); | |
edgeXValueOfput(line, startX - lineOffset); | |
edgeNumLinesOfput(line, nLines + lineWidth); | |
if (xDir > 0) { | |
wideLineWidthOfput(line, lineXIncrementOf(line) + lineWidth); | |
} else { | |
wideLineWidthOfput(line, lineWidth - lineXIncrementOf(line)); | |
edgeXValueOfput(line, edgeXValueOf(line) + lineXIncrementOf(line)); | |
} | |
/* turned on at lineOffset */ | |
yEntry = 0; | |
/* turned off at zero */ | |
yExit = (0 - nLines) - lineOffset; | |
wideLineEntryOfput(line, yEntry); | |
wideLineExitOfput(line, yExit); | |
if ((yEntry >= lineOffset) && (yExit < 0)) { | |
edgeFillsValidate(line); | |
} else { | |
edgeFillsInvalidate(line); | |
} | |
if (startY !== yValue) { | |
for (i = startY; i <= (yValue - 1); i++) { | |
stepToNextWideLineInat(line, i); | |
} | |
edgeNumLinesOfput(line, edgeNumLinesOf(line) - (yValue - startY)); | |
} | |
} | |
/* Process the current entry in the AET by stepping to the next scan line */ | |
function stepToNextBezier() { | |
return stepToNextBezierInat(aetBuffer[aetStartGet()], currentYGet()); | |
} | |
/* Incrementally step to the next scan line in the given bezier update data. */ | |
function stepToNextBezierForwardat(updateData, yValue) { | |
var fwDx; | |
var fwDy; | |
var lastX; | |
var lastY; | |
var minY; | |
lastX = updateData[GBUpdateX]; | |
lastY = updateData[GBUpdateY]; | |
fwDx = updateData[GBUpdateDX]; | |
fwDy = updateData[GBUpdateDY]; | |
/* Step as long as we haven't yet reached minY and also | |
as long as fwDy is greater than zero thus stepping down. | |
Note: The test for fwDy should not be necessary in theory | |
but is a good insurance in practice. */ | |
minY = yValue * 256; | |
while ((minY > lastY) && (fwDy >= 0)) { | |
lastX += (fwDx + 32768) >> 16; | |
lastY += (fwDy + 32768) >> 16; | |
fwDx += updateData[GBUpdateDDX]; | |
fwDy += updateData[GBUpdateDDY]; | |
} | |
updateData[GBUpdateX] = lastX; | |
updateData[GBUpdateY] = lastY; | |
updateData[GBUpdateDX] = fwDx; | |
updateData[GBUpdateDY] = fwDy; | |
return lastX >> 8; | |
} | |
/* Incrementally step to the next scan line in the given bezier */ | |
function stepToNextBezierInat(bezier, yValue) { | |
var xValue; | |
xValue = stepToNextBezierForwardat(bezierUpdateDataOf(bezier), yValue); | |
edgeXValueOfput(bezier, xValue); | |
} | |
/* Process the current entry in the AET by stepping to the next scan line */ | |
function stepToNextLine() { | |
return stepToNextLineInat(aetBuffer[aetStartGet()], currentYGet()); | |
} | |
/* Incrementally step to the next scan line in the given line */ | |
function stepToNextLineInat(line, yValue) { | |
var err; | |
var x; | |
x = edgeXValueOf(line) + lineXIncrementOf(line); | |
err = lineErrorOf(line) + lineErrorAdjUpOf(line); | |
if (err > 0) { | |
x += lineXDirectionOf(line); | |
err -= lineErrorAdjDownOf(line); | |
} | |
lineErrorOfput(line, err); | |
edgeXValueOfput(line, x); | |
} | |
/* Initialize the current entry in the GET by stepping to the current scan line */ | |
function stepToNextWideBezier() { | |
stepToNextWideBezierInat(aetBuffer[aetStartGet()], currentYGet()); | |
} | |
/* Incrementally step to the next scan line in the given wide bezier */ | |
function stepToNextWideBezierInat(bezier, yValue) { | |
var lineOffset; | |
var lineWidth; | |
var yEntry; | |
var yExit; | |
/* Don't inline this */ | |
lineWidth = wideBezierExtentOf(bezier); | |
lineOffset = offsetFromWidth(lineWidth); | |
yEntry = wideBezierEntryOf(bezier) + 1; | |
yExit = wideBezierExitOf(bezier) + 1; | |
wideBezierEntryOfput(bezier, yEntry); | |
wideBezierExitOfput(bezier, yExit); | |
if (yEntry >= lineOffset) { | |
edgeFillsValidate(bezier); | |
} | |
if (yExit >= 0) { | |
edgeFillsInvalidate(bezier); | |
} | |
if ((yExit + lineOffset) < 0) { | |
stepToNextBezierForwardat(bezierUpdateDataOf(bezier), yValue); | |
} else { | |
/* Adjust the last x value to the final x recorded previously */ | |
bezierUpdateDataOf(bezier)[GBUpdateX] = (bezierFinalXOf(bezier) * 256); | |
} | |
stepToNextBezierForwardat(wideBezierUpdateDataOf(bezier), yValue); | |
computeFinalWideBezierValueswidth(bezier, lineWidth); | |
} | |
/* Process the current entry in the AET by stepping to the next scan line */ | |
function stepToNextWideLine() { | |
return stepToNextWideLineInat(aetBuffer[aetStartGet()], currentYGet()); | |
} | |
/* Incrementally step to the next scan line in the given wide line */ | |
function stepToNextWideLineInat(line, yValue) { | |
var lastX; | |
var lineOffset; | |
var lineWidth; | |
var nextX; | |
var yEntry; | |
var yExit; | |
/* Adjust entry/exit values */ | |
yEntry = wideLineEntryOf(line) + 1; | |
yExit = wideLineExitOf(line) + 1; | |
wideLineEntryOfput(line, yEntry); | |
wideLineExitOfput(line, yExit); | |
lineWidth = wideLineExtentOf(line); | |
lineOffset = offsetFromWidth(lineWidth); | |
if (yEntry >= lineOffset) { | |
edgeFillsValidate(line); | |
} | |
if (yExit >= 0) { | |
edgeFillsInvalidate(line); | |
} | |
lastX = edgeXValueOf(line); | |
stepToNextLineInat(line, yValue); | |
/* Check for special start/end adjustments */ | |
nextX = edgeXValueOf(line); | |
if ((yEntry <= lineWidth) || ((yExit + lineOffset) >= 0)) { | |
/* Yes, need an update */ | |
adjustWideLineafterSteppingFromto(line, lastX, nextX); | |
} | |
} | |
function stopBecauseOf(stopReason) { | |
stopReasonPut(stopReason); | |
engineStopped = true; | |
} | |
function stopReasonGet() { | |
return workBuffer[GWStopReason]; | |
} | |
function stopReasonPut(value) { | |
return workBuffer[GWStopReason] = value; | |
} | |
function storeEdgeStateFrominto(edge, edgeOop) { | |
if (SIZEOF(edgeOop) < ETBalloonEdgeDataSize) { | |
return interpreterProxy.primitiveFail(); | |
} | |
interpreterProxy.storeIntegerofObjectwithValue(ETIndexIndex, edgeOop, objectIndexOf(edge)); | |
interpreterProxy.storeIntegerofObjectwithValue(ETXValueIndex, edgeOop, edgeXValueOf(edge)); | |
interpreterProxy.storeIntegerofObjectwithValue(ETYValueIndex, edgeOop, currentYGet()); | |
interpreterProxy.storeIntegerofObjectwithValue(ETZValueIndex, edgeOop, edgeZValueOf(edge)); | |
interpreterProxy.storeIntegerofObjectwithValue(ETLinesIndex, edgeOop, edgeNumLinesOf(edge)); | |
lastExportedEdgePut(edge); | |
} | |
function storeEngineStateInto(oop) { | |
objUsedPut(objUsed); | |
} | |
function storeFillStateInto(fillOop) { | |
var fillIndex; | |
var leftX; | |
var rightX; | |
fillIndex = lastExportedFillGet(); | |
leftX = lastExportedLeftXGet(); | |
rightX = lastExportedRightXGet(); | |
if (SIZEOF(fillOop) < FTBalloonFillDataSize) { | |
return interpreterProxy.primitiveFail(); | |
} | |
interpreterProxy.storeIntegerofObjectwithValue(FTIndexIndex, fillOop, objectIndexOf(fillIndex)); | |
interpreterProxy.storeIntegerofObjectwithValue(FTMinXIndex, fillOop, leftX); | |
interpreterProxy.storeIntegerofObjectwithValue(FTMaxXIndex, fillOop, rightX); | |
interpreterProxy.storeIntegerofObjectwithValue(FTYValueIndex, fillOop, currentYGet()); | |
} | |
function storeRenderingState() { | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if (engineStopped) { | |
/* Check the stop reason and store the required information */ | |
storeStopStateIntoEdgefill(interpreterProxy.stackObjectValue(1), interpreterProxy.stackObjectValue(0)); | |
} | |
storeEngineStateInto(engine); | |
interpreterProxy.pop(3); | |
interpreterProxy.pushInteger(stopReasonGet()); | |
} | |
function storeStopStateIntoEdgefill(edgeOop, fillOop) { | |
var edge; | |
var reason; | |
reason = stopReasonGet(); | |
if (reason === GErrorGETEntry) { | |
edge = getBuffer[getStartGet()]; | |
storeEdgeStateFrominto(edge, edgeOop); | |
getStartPut(getStartGet() + 1); | |
} | |
if (reason === GErrorFillEntry) { | |
storeFillStateInto(fillOop); | |
} | |
if (reason === GErrorAETEntry) { | |
edge = aetBuffer[aetStartGet()]; | |
storeEdgeStateFrominto(edge, edgeOop); | |
} | |
} | |
/* Subdivide the given bezier curve if necessary */ | |
function subdivideBezier(index) { | |
var deltaX; | |
var deltaY; | |
var endX; | |
var endY; | |
var startX; | |
var startY; | |
startY = bzStartY(index); | |
/* If the receiver is horizontal, don't do anything */ | |
endY = bzEndY(index); | |
if (endY === startY) { | |
return index; | |
} | |
deltaY = endY - startY; | |
if (deltaY < 0) { | |
deltaY = 0 - deltaY; | |
} | |
if (deltaY > 255) { | |
incrementStatby(GWBezierHeightSubdivisions, 1); | |
return computeBezierSplitAtHalf(index); | |
} | |
startX = bzStartX(index); | |
endX = bzEndX(index); | |
deltaX = endX - startX; | |
if (deltaX < 0) { | |
deltaX = 0 - deltaX; | |
} | |
if ((deltaY * 32) < deltaX) { | |
incrementStatby(GWBezierOverflowSubdivisions, 1); | |
return computeBezierSplitAtHalf(index); | |
} | |
return index; | |
} | |
/* Recursively subdivide the curve on the bezier stack. */ | |
function subdivideBezierFrom(index) { | |
var index1; | |
var index2; | |
var otherIndex; | |
otherIndex = subdivideBezier(index); | |
if (otherIndex !== index) { | |
index1 = subdivideBezierFrom(index); | |
if (engineStopped) { | |
return 0; | |
} | |
index2 = subdivideBezierFrom(otherIndex); | |
if (engineStopped) { | |
return 0; | |
} | |
if (index1 >= index2) { | |
return index1; | |
} else { | |
return index2; | |
} | |
} | |
return index; | |
} | |
/* Check if the given bezier curve is monoton in Y, and, if desired in X. | |
If not, subdivide it */ | |
function subdivideToBeMonotoninX(base, doTestX) { | |
var base2; | |
var index1; | |
var index2; | |
base2 = (index1 = (index2 = subdivideToBeMonotonInY(base))); | |
if (doTestX) { | |
index1 = subdivideToBeMonotonInX(base); | |
} | |
if (index1 > index2) { | |
index2 = index1; | |
} | |
if ((base !== base2) && (doTestX)) { | |
index1 = subdivideToBeMonotonInX(base2); | |
} | |
if (index1 > index2) { | |
index2 = index1; | |
} | |
return index2; | |
} | |
/* Check if the given bezier curve is monoton in X. If not, subdivide it */ | |
function subdivideToBeMonotonInX(index) { | |
var denom; | |
var dx1; | |
var dx2; | |
var endX; | |
var num; | |
var startX; | |
var viaX; | |
startX = bzStartX(index); | |
viaX = bzViaX(index); | |
endX = bzEndX(index); | |
dx1 = viaX - startX; | |
dx2 = endX - viaX; | |
if ((dx1 * dx2) >= 0) { | |
return index; | |
} | |
incrementStatby(GWBezierMonotonSubdivisions, 1); | |
denom = dx2 - dx1; | |
num = dx1; | |
if (num < 0) { | |
num = 0 - num; | |
} | |
if (denom < 0) { | |
denom = 0 - denom; | |
} | |
return computeBeziersplitAt(index, num / denom); | |
} | |
/* Check if the given bezier curve is monoton in Y. If not, subdivide it */ | |
function subdivideToBeMonotonInY(index) { | |
var denom; | |
var dy1; | |
var dy2; | |
var endY; | |
var num; | |
var startY; | |
var viaY; | |
startY = bzStartY(index); | |
viaY = bzViaY(index); | |
endY = bzEndY(index); | |
dy1 = viaY - startY; | |
dy2 = endY - viaY; | |
if ((dy1 * dy2) >= 0) { | |
return index; | |
} | |
incrementStatby(GWBezierMonotonSubdivisions, 1); | |
denom = dy2 - dy1; | |
num = dy1; | |
if (num < 0) { | |
num = 0 - num; | |
} | |
if (denom < 0) { | |
denom = 0 - denom; | |
} | |
return computeBeziersplitAt(index, num / denom); | |
} | |
/* Make the fill style with the given index either visible or invisible */ | |
function toggleFilldepthrightX(fillIndex, depth, rightX) { | |
var hidden; | |
if (stackFillSize() === 0) { | |
if (allocateStackFillEntry()) { | |
topFillValuePut(fillIndex); | |
topFillDepthPut(depth); | |
topFillRightXPut(rightX); | |
} | |
} else { | |
hidden = hideFilldepth(fillIndex, depth); | |
if (!hidden) { | |
showFilldepthrightX(fillIndex, depth, rightX); | |
} | |
} | |
} | |
function toggleFillsOf(edge) { | |
var depth; | |
var fillIndex; | |
if (!needAvailableSpace(stackFillEntryLength() * 2)) { | |
return null; | |
} | |
depth = edgeZValueOf(edge) << 1; | |
fillIndex = edgeLeftFillOf(edge); | |
if (fillIndex !== 0) { | |
toggleFilldepthrightX(fillIndex, depth, 999999999); | |
} | |
fillIndex = edgeRightFillOf(edge); | |
if (fillIndex !== 0) { | |
toggleFilldepthrightX(fillIndex, depth, 999999999); | |
} | |
quickRemoveInvalidFillsAt(edgeXValueOf(edge)); | |
} | |
function toggleWideFillOf(edge) { | |
var depth; | |
var fill; | |
var index; | |
var lineWidth; | |
var rightX; | |
var type; | |
type = edgeTypeOf(edge); | |
dispatchedValue = edge; | |
switch (type) { | |
case 0: | |
case 1: | |
errorWrongIndex(); | |
break; | |
case 2: | |
returnWideLineWidth(); | |
break; | |
case 3: | |
returnWideBezierWidth(); | |
break; | |
} | |
lineWidth = dispatchReturnValue; | |
switch (type) { | |
case 0: | |
case 1: | |
errorWrongIndex(); | |
break; | |
case 2: | |
returnWideLineFill(); | |
break; | |
case 3: | |
returnWideBezierFill(); | |
break; | |
} | |
fill = dispatchReturnValue; | |
if (fill === 0) { | |
return null; | |
} | |
if (!needAvailableSpace(stackFillEntryLength())) { | |
return null; | |
} | |
/* So lines sort before interior fills */ | |
depth = (edgeZValueOf(edge) << 1) + 1; | |
rightX = edgeXValueOf(edge) + lineWidth; | |
index = findStackFilldepth(fill, depth); | |
if (index === -1) { | |
showFilldepthrightX(fill, depth, rightX); | |
} else { | |
if (stackFillRightX(index) < rightX) { | |
stackFillRightXput(index, rightX); | |
} | |
} | |
quickRemoveInvalidFillsAt(edgeXValueOf(edge)); | |
} | |
function topDepth() { | |
if (stackFillSize() === 0) { | |
return -1; | |
} else { | |
return topFillDepth(); | |
} | |
} | |
function topFill() { | |
if (stackFillSize() === 0) { | |
return 0; | |
} else { | |
return topFillValue(); | |
} | |
} | |
function topFillDepth() { | |
return stackFillDepth(stackFillSize() - stackFillEntryLength()); | |
} | |
function topFillDepthPut(value) { | |
return stackFillDepthput(stackFillSize() - stackFillEntryLength(), value); | |
} | |
function topFillRightX() { | |
return stackFillRightX(stackFillSize() - stackFillEntryLength()); | |
} | |
function topFillRightXPut(value) { | |
return stackFillRightXput(stackFillSize() - stackFillEntryLength(), value); | |
} | |
function topFillValue() { | |
return stackFillValue(stackFillSize() - stackFillEntryLength()); | |
} | |
function topFillValuePut(value) { | |
return stackFillValueput(stackFillSize() - stackFillEntryLength(), value); | |
} | |
function topRightX() { | |
if (stackFillSize() === 0) { | |
return 999999999; | |
} else { | |
return topFillRightX(); | |
} | |
} | |
function transformColor(fillIndex) { | |
var a; | |
var alphaScale; | |
var b; | |
var g; | |
var r; | |
var transform; | |
if (!((fillIndex === 0) || (isFillColor(fillIndex)))) { | |
return fillIndex; | |
} | |
b = fillIndex & 255; | |
g = (fillIndex >>> 8) & 255; | |
r = (fillIndex >>> 16) & 255; | |
a = (fillIndex >>> 24) & 255; | |
if (hasColorTransform()) { | |
transform = colorTransform(); | |
alphaScale = ((a * transform[6]) + transform[7]) / a; | |
r = ((((r * transform[0]) + transform[1]) * alphaScale)|0); | |
g = ((((g * transform[2]) + transform[3]) * alphaScale)|0); | |
b = ((((b * transform[4]) + transform[5]) * alphaScale)|0); | |
a = a * alphaScale|0; | |
r = Math.max(r, 0); | |
r = Math.min(r, 255); | |
g = Math.max(g, 0); | |
g = Math.min(g, 255); | |
b = Math.max(b, 0); | |
b = Math.min(b, 255); | |
a = Math.max(a, 0); | |
a = Math.min(a, 255); | |
} | |
if (a < 1) { | |
return 0; | |
} | |
if ((a < 255) && (needsFlush())) { | |
stopBecauseOf(GErrorNeedFlush); | |
} | |
return ((b + (g << 8)) + (r << 16)) + (a << 24); | |
} | |
/* Transform the given point. If haveMatrix is true then use the current transformation. */ | |
function transformPoint(point) { | |
if (hasEdgeTransform()) { | |
/* Note: AA adjustment is done in #transformPoint: for higher accuracy */ | |
transformPointinto(point, point); | |
} else { | |
/* Multiply each component by aaLevel and add a half pixel */ | |
point[0] = ((point[0] + destOffsetXGet()) * aaLevelGet()); | |
point[1] = ((point[1] + destOffsetYGet()) * aaLevelGet()); | |
} | |
} | |
/* Transform srcPoint into dstPoint by using the currently loaded matrix */ | |
/* Note: This method has been rewritten so that inlining works (e.g., removing | |
the declarations and adding argument coercions at the appropriate points) */ | |
function transformPointinto(srcPoint, dstPoint) { | |
transformPointXyinto((srcPoint[0]|0), (srcPoint[1]|0), dstPoint); | |
} | |
/* Transform srcPoint into dstPoint by using the currently loaded matrix */ | |
/* Note: This should be rewritten so that inlining works (e.g., removing | |
the declarations and adding argument coercions at the appropriate points) */ | |
function transformPointXyinto(xValue, yValue, dstPoint) { | |
var transform; | |
var x; | |
var y; | |
transform = edgeTransform(); | |
x = (((((transform[0] * xValue) + (transform[1] * yValue)) + transform[2]) * aaLevelGet())|0); | |
y = (((((transform[3] * xValue) + (transform[4] * yValue)) + transform[5]) * aaLevelGet())|0); | |
dstPoint[0] = x; | |
dstPoint[1] = y; | |
} | |
/* Transform n (n=1,2,3) points. | |
If haveMatrix is true then the matrix contains the actual transformation. */ | |
function transformPoints(n) { | |
if (n > 0) { | |
transformPoint(point1Get()); | |
} | |
if (n > 1) { | |
transformPoint(point2Get()); | |
} | |
if (n > 2) { | |
transformPoint(point3Get()); | |
} | |
if (n > 3) { | |
transformPoint(point4Get()); | |
} | |
} | |
/* Transform the given width */ | |
function transformWidth(w) { | |
var deltaX; | |
var deltaY; | |
var dstWidth; | |
var dstWidth2; | |
if (w === 0) { | |
return 0; | |
} | |
point1Get()[0] = 0; | |
point1Get()[1] = 0; | |
point2Get()[0] = (w * 256); | |
point2Get()[1] = 0; | |
point3Get()[0] = 0; | |
point3Get()[1] = (w * 256); | |
transformPoints(3); | |
deltaX = (point2Get()[0] - point1Get()[0]); | |
deltaY = (point2Get()[1] - point1Get()[1]); | |
dstWidth = ((Math.sqrt((deltaX * deltaX) + (deltaY * deltaY))|0) + 128) >> 8; | |
deltaX = (point3Get()[0] - point1Get()[0]); | |
deltaY = (point3Get()[1] - point1Get()[1]); | |
dstWidth2 = ((Math.sqrt((deltaX * deltaX) + (deltaY * deltaY))|0) + 128) >> 8; | |
if (dstWidth2 < dstWidth) { | |
dstWidth = dstWidth2; | |
} | |
if (dstWidth === 0) { | |
return 1; | |
} else { | |
return dstWidth; | |
} | |
} | |
function uncheckedTransformColor(fillIndex) { | |
var a; | |
var b; | |
var g; | |
var r; | |
var transform; | |
if (!hasColorTransform()) { | |
return fillIndex; | |
} | |
b = fillIndex & 255; | |
g = (fillIndex >>> 8) & 255; | |
r = (fillIndex >>> 16) & 255; | |
a = (fillIndex >>> 24) & 255; | |
transform = colorTransform(); | |
r = (((r * transform[0]) + transform[1])|0); | |
g = (((g * transform[2]) + transform[3])|0); | |
b = (((b * transform[4]) + transform[5])|0); | |
a = (((a * transform[6]) + transform[7])|0); | |
r = Math.max(r, 0); | |
r = Math.min(r, 255); | |
g = Math.max(g, 0); | |
g = Math.min(g, 255); | |
b = Math.max(b, 0); | |
b = Math.min(b, 255); | |
a = Math.max(a, 0); | |
a = Math.min(a, 255); | |
if (a < 16) { | |
return 0; | |
} | |
return ((b + (g << 8)) + (r << 16)) + (a << 24); | |
} | |
function wbSizeGet() { | |
return workBuffer[GWSize]; | |
} | |
function wbSizePut(value) { | |
return workBuffer[GWSize] = value; | |
} | |
function wbStackClear() { | |
wbTopPut(wbSizeGet()); | |
} | |
function wbStackPop(nItems) { | |
wbTopPut(wbTopGet() + nItems); | |
} | |
function wbStackPush(nItems) { | |
if (!allocateStackEntry(nItems)) { | |
return false; | |
} | |
wbTopPut(wbTopGet() - nItems); | |
return true; | |
} | |
function wbStackSize() { | |
return wbSizeGet() - wbTopGet(); | |
} | |
function wbStackValue(index) { | |
return workBuffer[wbTopGet() + index]; | |
} | |
function wbStackValueput(index, value) { | |
return workBuffer[wbTopGet() + index] = value; | |
} | |
function wbTopGet() { | |
return workBuffer[GWBufferTop]; | |
} | |
function wbTopPut(value) { | |
return workBuffer[GWBufferTop] = value; | |
} | |
function wideBezierEntryOf(line) { | |
return objat(line, GBWideEntry); | |
} | |
function wideBezierEntryOfput(line, value) { | |
return objatput(line, GBWideEntry, value); | |
} | |
function wideBezierExitOf(line) { | |
return objat(line, GBWideExit); | |
} | |
function wideBezierExitOfput(line, value) { | |
return objatput(line, GBWideExit, value); | |
} | |
function wideBezierExtentOf(bezier) { | |
return objat(bezier, GBWideExtent); | |
} | |
function wideBezierExtentOfput(bezier, value) { | |
return objatput(bezier, GBWideExtent, value); | |
} | |
function wideBezierFillOf(bezier) { | |
return objat(bezier, GBWideFill); | |
} | |
function wideBezierFillOfput(bezier, value) { | |
return objatput(bezier, GBWideFill, value); | |
} | |
function wideBezierUpdateDataOf(bezier) { | |
return PTR_ADD(objBuffer, bezier + GBWideUpdateData); | |
} | |
function wideBezierWidthOf(line) { | |
return objat(line, GBWideWidth); | |
} | |
function wideBezierWidthOfput(line, value) { | |
return objatput(line, GBWideWidth, value); | |
} | |
function wideLineEntryOf(line) { | |
return objat(line, GLWideEntry); | |
} | |
function wideLineEntryOfput(line, value) { | |
return objatput(line, GLWideEntry, value); | |
} | |
function wideLineExitOf(line) { | |
return objat(line, GLWideExit); | |
} | |
function wideLineExitOfput(line, value) { | |
return objatput(line, GLWideExit, value); | |
} | |
function wideLineExtentOf(line) { | |
return objat(line, GLWideExtent); | |
} | |
function wideLineExtentOfput(line, value) { | |
return objatput(line, GLWideExtent, value); | |
} | |
function wideLineFillOf(line) { | |
return objat(line, GLWideFill); | |
} | |
function wideLineFillOfput(line, value) { | |
return objatput(line, GLWideFill, value); | |
} | |
function wideLineWidthOf(line) { | |
return objat(line, GLWideWidth); | |
} | |
function wideLineWidthOfput(line, value) { | |
return objatput(line, GLWideWidth, value); | |
} | |
function workBufferPut(wbOop) { | |
workBuffer = wbOop.wordsAsInt32Array(); | |
} | |
function registerPlugin() { | |
if (typeof Squeak === "object" && Squeak.registerExternalModule) { | |
Squeak.registerExternalModule("B2DPlugin", { | |
primitiveMergeFillFrom: primitiveMergeFillFrom, | |
primitiveCopyBuffer: primitiveCopyBuffer, | |
primitiveAddRect: primitiveAddRect, | |
primitiveAddGradientFill: primitiveAddGradientFill, | |
primitiveSetClipRect: primitiveSetClipRect, | |
initialiseModule: initialiseModule, | |
primitiveSetBitBltPlugin: primitiveSetBitBltPlugin, | |
primitiveRegisterExternalEdge: primitiveRegisterExternalEdge, | |
primitiveGetClipRect: primitiveGetClipRect, | |
primitiveAddBezier: primitiveAddBezier, | |
primitiveInitializeProcessing: primitiveInitializeProcessing, | |
primitiveRenderImage: primitiveRenderImage, | |
primitiveGetOffset: primitiveGetOffset, | |
primitiveSetDepth: primitiveSetDepth, | |
primitiveAddBezierShape: primitiveAddBezierShape, | |
primitiveSetEdgeTransform: primitiveSetEdgeTransform, | |
getModuleName: getModuleName, | |
primitiveGetTimes: primitiveGetTimes, | |
primitiveNextActiveEdgeEntry: primitiveNextActiveEdgeEntry, | |
primitiveAddBitmapFill: primitiveAddBitmapFill, | |
primitiveGetDepth: primitiveGetDepth, | |
primitiveAbortProcessing: primitiveAbortProcessing, | |
primitiveNextGlobalEdgeEntry: primitiveNextGlobalEdgeEntry, | |
primitiveGetFailureReason: primitiveGetFailureReason, | |
primitiveDisplaySpanBuffer: primitiveDisplaySpanBuffer, | |
moduleUnloaded: moduleUnloaded, | |
primitiveGetCounts: primitiveGetCounts, | |
primitiveChangedActiveEdgeEntry: primitiveChangedActiveEdgeEntry, | |
primitiveRenderScanline: primitiveRenderScanline, | |
primitiveGetBezierStats: primitiveGetBezierStats, | |
primitiveFinishedProcessing: primitiveFinishedProcessing, | |
setInterpreter: setInterpreter, | |
primitiveNeedsFlush: primitiveNeedsFlush, | |
primitiveAddLine: primitiveAddLine, | |
primitiveSetOffset: primitiveSetOffset, | |
primitiveNextFillEntry: primitiveNextFillEntry, | |
primitiveInitializeBuffer: primitiveInitializeBuffer, | |
primitiveDoProfileStats: primitiveDoProfileStats, | |
primitiveAddActiveEdgeEntry: primitiveAddActiveEdgeEntry, | |
primitiveSetAALevel: primitiveSetAALevel, | |
primitiveNeedsFlushPut: primitiveNeedsFlushPut, | |
primitiveAddCompressedShape: primitiveAddCompressedShape, | |
primitiveSetColorTransform: primitiveSetColorTransform, | |
primitiveAddOval: primitiveAddOval, | |
primitiveRegisterExternalFill: primitiveRegisterExternalFill, | |
primitiveAddPolygon: primitiveAddPolygon, | |
primitiveGetAALevel: primitiveGetAALevel, | |
}); | |
} else self.setTimeout(registerPlugin, 100); | |
} | |
registerPlugin(); | |
})(); // Register module/plugin | |