Spaces:
Running
Running
/* Smalltalk from Squeak4.5 with VMMaker 4.13.6 translated as JS source on 3 November 2014 1:52:20 pm */ | |
/* Automatically generated by | |
JSSmartSyntaxPluginCodeGenerator VMMakerJS-bf.15 uuid: fd4e10f2-3773-4e80-8bb5-c4b471a014e5 | |
from | |
BitBltSimulation VMMaker-bf.353 uuid: 8ae25e7e-8d2c-451e-8277-598b30e9c002 | |
*/ | |
(function BitBltPlugin() { | |
; | |
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); } | |
/*** Constants ***/ | |
var AllOnes = 4294967295; | |
var AlphaIndex = 3; | |
var BBClipHeightIndex = 13; | |
var BBClipWidthIndex = 12; | |
var BBClipXIndex = 10; | |
var BBClipYIndex = 11; | |
var BBColorMapIndex = 14; | |
var BBDestFormIndex = 0; | |
var BBDestXIndex = 4; | |
var BBDestYIndex = 5; | |
var BBHalftoneFormIndex = 2; | |
var BBHeightIndex = 7; | |
var BBRuleIndex = 3; | |
var BBSourceFormIndex = 1; | |
var BBSourceXIndex = 8; | |
var BBSourceYIndex = 9; | |
var BBWarpBase = 15; | |
var BBWidthIndex = 6; | |
var BinaryPoint = 14; | |
var BlueIndex = 2; | |
var ColorMapFixedPart = 2; | |
var ColorMapIndexedPart = 4; | |
var ColorMapNewStyle = 8; | |
var ColorMapPresent = 1; | |
var FixedPt1 = 16384; | |
var FormBitsIndex = 0; | |
var FormDepthIndex = 3; | |
var FormHeightIndex = 2; | |
var FormWidthIndex = 1; | |
var GreenIndex = 1; | |
var OpTableSize = 43; | |
var RedIndex = 0; | |
/*** Variables ***/ | |
var affectedB = 0; | |
var affectedL = 0; | |
var affectedR = 0; | |
var affectedT = 0; | |
var bbH = 0; | |
var bbW = 0; | |
var bitBltOop = 0; | |
var bitCount = 0; | |
var clipHeight = 0; | |
var clipWidth = 0; | |
var clipX = 0; | |
var clipY = 0; | |
var cmBitsPerColor = 0; | |
var cmFlags = 0; | |
var cmLookupTable = null; | |
var cmMask = 0; | |
var cmMaskTable = null; | |
var cmShiftTable = null; | |
var combinationRule = 0; | |
var componentAlphaModeAlpha = 0; | |
var componentAlphaModeColor = 0; | |
var destBits = 0; | |
var destDelta = 0; | |
var destDepth = 0; | |
var destForm = 0; | |
var destHeight = 0; | |
var destIndex = 0; | |
var destMSB = 0; | |
var destMask = 0; | |
var destPPW = 0; | |
var destPitch = 0; | |
var destWidth = 0; | |
var destX = 0; | |
var destY = 0; | |
var dither8Lookup = new Array(4096); | |
var ditherMatrix4x4 = [ | |
0, 8, 2, 10, | |
12, 4, 14, 6, | |
3, 11, 1, 9, | |
15, 7, 13, 5 | |
]; | |
var ditherThresholds16 = [ 0, 2, 4, 6, 8, 12, 14, 16 ]; | |
var ditherValues16 = [ | |
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | |
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 | |
]; | |
var dstBitShift = 0; | |
var dx = 0; | |
var dy = 0; | |
var gammaLookupTable = null; | |
var hDir = 0; | |
var halftoneBase = 0; | |
var halftoneForm = 0; | |
var halftoneHeight = 0; | |
var hasSurfaceLock = 0; | |
var height = 0; | |
var interpreterProxy = null; | |
var isWarping = 0; | |
var lockSurfaceFn = null; | |
var mask1 = 0; | |
var mask2 = 0; | |
var maskTable = [ | |
0, 1, 3, 0, 15, 31, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 65535, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 | |
]; | |
var moduleName = "BitBltPlugin 3 November 2014 (e)"; | |
var nWords = 0; | |
var noHalftone = 0; | |
var noSource = 0; | |
var opTable = new Array(43); | |
var preload = 0; | |
var querySurfaceFn = null; | |
var skew = 0; | |
var sourceAlpha = 0; | |
var sourceBits = 0; | |
var sourceDelta = 0; | |
var sourceDepth = 0; | |
var sourceForm = 0; | |
var sourceHeight = 0; | |
var sourceIndex = 0; | |
var sourceMSB = 0; | |
var sourcePPW = 0; | |
var sourcePitch = 0; | |
var sourceWidth = 0; | |
var sourceX = 0; | |
var sourceY = 0; | |
var srcBitShift = 0; | |
var sx = 0; | |
var sy = 0; | |
var ungammaLookupTable = null; | |
var unlockSurfaceFn = null; | |
var vDir = 0; | |
var warpAlignMask = 0; | |
var warpAlignShift = 0; | |
var warpBitShiftTable = new Array(32); | |
var warpSrcMask = 0; | |
var warpSrcShift = 0; | |
var width = 0; | |
/* Subract the pixels in the source and destination, color by color, | |
and return the sum of the absolute value of all the differences. | |
For non-rgb, XOR the two and return the number of differing pixels. | |
Note that the region is not clipped to bit boundaries, but only to the | |
nearest (enclosing) word. This is because copyLoop does not do | |
pre-merge masking. For accurate results, you must subtract the | |
values obtained from the left and right fringes. */ | |
function OLDrgbDiffwith(sourceWord, destinationWord) { | |
var diff; | |
var pixMask; | |
if (destDepth < 16) { | |
/* Just xor and count differing bits if not RGB */ | |
diff = sourceWord ^ destinationWord; | |
pixMask = maskTable[destDepth]; | |
while (!(diff === 0)) { | |
if ((diff & pixMask) !== 0) { | |
++bitCount; | |
} | |
diff = SHR(diff, destDepth); | |
} | |
return destinationWord; | |
} | |
if (destDepth === 16) { | |
diff = partitionedSubfromnBitsnPartitions(sourceWord, destinationWord, 5, 3); | |
bitCount = ((bitCount + (diff & 31)) + ((diff >>> 5) & 31)) + ((diff >>> 10) & 31); | |
diff = partitionedSubfromnBitsnPartitions(sourceWord >>> 16, destinationWord >>> 16, 5, 3); | |
bitCount = ((bitCount + (diff & 31)) + ((diff >>> 5) & 31)) + ((diff >>> 10) & 31); | |
} else { | |
diff = partitionedSubfromnBitsnPartitions(sourceWord, destinationWord, 8, 3); | |
bitCount = ((bitCount + (diff & 255)) + ((diff >>> 8) & 255)) + ((diff >>> 16) & 255); | |
} | |
return destinationWord; | |
} | |
/* Tally pixels into the color map. Note that the source should be | |
specified = destination, in order for the proper color map checks | |
to be performed at setup. | |
Note that the region is not clipped to bit boundaries, but only to the | |
nearest (enclosing) word. This is because copyLoop does not do | |
pre-merge masking. For accurate results, you must subtract the | |
values obtained from the left and right fringes. */ | |
function OLDtallyIntoMapwith(sourceWord, destinationWord) { | |
var pixMask; | |
var mapIndex; | |
var i; | |
var shiftWord; | |
if ((cmFlags & (ColorMapPresent | ColorMapIndexedPart)) !== (ColorMapPresent | ColorMapIndexedPart)) { | |
return destinationWord; | |
} | |
if (destDepth < 16) { | |
/* loop through all packed pixels. */ | |
pixMask = maskTable[destDepth] & cmMask; | |
shiftWord = destinationWord; | |
for (i = 1; i <= destPPW; i++) { | |
mapIndex = shiftWord & pixMask; | |
tallyMapAtput(mapIndex, tallyMapAt(mapIndex) + 1); | |
shiftWord = SHR(shiftWord, destDepth); | |
} | |
return destinationWord; | |
} | |
if (destDepth === 16) { | |
/* Two pixels Tally the right half... */ | |
mapIndex = rgbMapfromto(destinationWord & 65535, 5, cmBitsPerColor); | |
tallyMapAtput(mapIndex, tallyMapAt(mapIndex) + 1); | |
mapIndex = rgbMapfromto(destinationWord >>> 16, 5, cmBitsPerColor); | |
tallyMapAtput(mapIndex, tallyMapAt(mapIndex) + 1); | |
} else { | |
/* Just one pixel. */ | |
mapIndex = rgbMapfromto(destinationWord, 8, cmBitsPerColor); | |
tallyMapAtput(mapIndex, tallyMapAt(mapIndex) + 1); | |
} | |
return destinationWord; | |
} | |
function addWordwith(sourceWord, destinationWord) { | |
return sourceWord + destinationWord; | |
} | |
/* Blend sourceWord with destinationWord, assuming both are 32-bit pixels. | |
The source is assumed to have 255*alpha in the high 8 bits of each pixel, | |
while the high 8 bits of the destinationWord will be ignored. | |
The blend produced is alpha*source + (1-alpha)*dest, with | |
the computation being performed independently on each color | |
component. The high byte of the result will be 0. */ | |
function alphaBlendwith(sourceWord, destinationWord) { | |
var unAlpha; | |
var blendRB; | |
var blendAG; | |
var result; | |
var alpha; | |
/* High 8 bits of source pixel */ | |
alpha = sourceWord >>> 24; | |
if (alpha === 0) { | |
return destinationWord; | |
} | |
if (alpha === 255) { | |
return sourceWord; | |
} | |
unAlpha = 255 - alpha; | |
/* blend red and blue */ | |
blendRB = (((sourceWord & 16711935) * alpha) + ((destinationWord & 16711935) * unAlpha)) + 16711935; | |
/* blend alpha and green */ | |
blendAG = (((((sourceWord >>> 8) | 16711680) & 16711935) * alpha) + (((destinationWord >>> 8) & 16711935) * unAlpha)) + 16711935; | |
/* divide by 255 */ | |
blendRB = ((blendRB + (((blendRB - 65537) >>> 8) & 16711935)) >>> 8) & 16711935; | |
blendAG = ((blendAG + (((blendAG - 65537) >>> 8) & 16711935)) >>> 8) & 16711935; | |
result = blendRB | (blendAG << 8); | |
return result; | |
} | |
function alphaBlendConstwith(sourceWord, destinationWord) { | |
return alphaBlendConstwithpaintMode(sourceWord, destinationWord, false); | |
} | |
/* Blend sourceWord with destinationWord using a constant alpha. | |
Alpha is encoded as 0 meaning 0.0, and 255 meaning 1.0. | |
The blend produced is alpha*source + (1.0-alpha)*dest, with the | |
computation being performed independently on each color component. | |
This function could eventually blend into any depth destination, | |
using the same color averaging and mapping as warpBlt. | |
paintMode = true means do nothing if the source pixel value is zero. */ | |
/* This first implementation works with dest depths of 16 and 32 bits only. | |
Normal color mapping will allow sources of lower depths in this case, | |
and results can be mapped directly by truncation, so no extra color maps are needed. | |
To allow storing into any depth will require subsequent addition of two other | |
colormaps, as is the case with WarpBlt. */ | |
function alphaBlendConstwithpaintMode(sourceWord, destinationWord, paintMode) { | |
var rgbMask; | |
var pixMask; | |
var pixBlend; | |
var j; | |
var sourceShifted; | |
var result; | |
var shift; | |
var sourcePixVal; | |
var i; | |
var unAlpha; | |
var destPixVal; | |
var blendRB; | |
var blendAG; | |
var bitsPerColor; | |
var blend; | |
var destShifted; | |
var maskShifted; | |
if (destDepth < 16) { | |
return destinationWord; | |
} | |
unAlpha = 255 - sourceAlpha; | |
result = destinationWord; | |
if (destPPW === 1) { | |
/* 32bpp blends include alpha */ | |
if (!(paintMode && (sourceWord === 0))) { | |
/* painting a transparent pixel */ | |
/* blendRB red and blue */ | |
blendRB = (((sourceWord & 16711935) * sourceAlpha) + ((destinationWord & 16711935) * unAlpha)) + 16711935; | |
/* blendRB alpha and green */ | |
blendAG = ((((sourceWord >>> 8) & 16711935) * sourceAlpha) + (((destinationWord >>> 8) & 16711935) * unAlpha)) + 16711935; | |
/* divide by 255 */ | |
blendRB = ((blendRB + (((blendRB - 65537) >>> 8) & 16711935)) >>> 8) & 16711935; | |
blendAG = ((blendAG + (((blendAG - 65537) >>> 8) & 16711935)) >>> 8) & 16711935; | |
result = blendRB | (blendAG << 8); | |
} | |
} else { | |
pixMask = maskTable[destDepth]; | |
bitsPerColor = 5; | |
rgbMask = 31; | |
maskShifted = destMask; | |
destShifted = destinationWord; | |
sourceShifted = sourceWord; | |
for (j = 1; j <= destPPW; j++) { | |
sourcePixVal = sourceShifted & pixMask; | |
if (!(((maskShifted & pixMask) === 0) || (paintMode && (sourcePixVal === 0)))) { | |
destPixVal = destShifted & pixMask; | |
pixBlend = 0; | |
for (i = 1; i <= 3; i++) { | |
shift = (i - 1) * bitsPerColor; | |
blend = (DIV((((((SHR(sourcePixVal, shift)) & rgbMask) * sourceAlpha) + (((SHR(destPixVal, shift)) & rgbMask) * unAlpha)) + 254), 255)) & rgbMask; | |
pixBlend = pixBlend | (SHL(blend, shift)); | |
} | |
result = (result & ~(SHL(pixMask, ((j - 1) * 16)))) | (SHL(pixBlend, ((j - 1) * 16))); | |
} | |
maskShifted = SHR(maskShifted, destDepth); | |
sourceShifted = SHR(sourceShifted, destDepth); | |
destShifted = SHR(destShifted, destDepth); | |
} | |
} | |
return result; | |
} | |
/* Blend sourceWord with destinationWord using the alpha value from sourceWord. | |
Alpha is encoded as 0 meaning 0.0, and 255 meaning 1.0. | |
In contrast to alphaBlend:with: the color produced is | |
srcColor + (1-srcAlpha) * dstColor | |
e.g., it is assumed that the source color is already scaled. */ | |
function alphaBlendScaledwith(sourceWord, destinationWord) { | |
var unAlpha; | |
var rb; | |
var ag; | |
/* Do NOT inline this into optimized loops */ | |
/* High 8 bits of source pixel is source opacity (ARGB format) */ | |
unAlpha = 255 - (sourceWord >>> 24); | |
/* blend red and blue components */ | |
rb = ((((destinationWord & 16711935) * unAlpha) >>> 8) & 16711935) + (sourceWord & 16711935); | |
/* blend alpha and green components */ | |
ag = (((((destinationWord >>> 8) & 16711935) * unAlpha) >>> 8) & 16711935) + ((sourceWord >>> 8) & 16711935); | |
/* saturate red and blue components if there is a carry */ | |
rb = (rb & 16711935) | (((rb & 16777472) * 255) >>> 8); | |
/* saturate alpha and green components if there is a carry */ | |
ag = ((ag & 16711935) << 8) | ((ag & 16777472) * 255); | |
return ag | rb; | |
} | |
function alphaPaintConstwith(sourceWord, destinationWord) { | |
if (sourceWord === 0) { | |
return destinationWord; | |
} | |
return alphaBlendConstwithpaintMode(sourceWord, destinationWord, true); | |
} | |
/* This version assumes | |
combinationRule = 34 | |
sourcePixSize = 32 | |
destPixSize = 16 | |
sourceForm ~= destForm. | |
*/ | |
function alphaSourceBlendBits16() { | |
var ditherBase; | |
var ditherThreshold; | |
var srcShift; | |
var sourceWord; | |
var srcIndex; | |
var deltaX; | |
var dstIndex; | |
var srcAlpha; | |
var dstMask; | |
var deltaY; | |
var srcY; | |
var destWord; | |
var dstY; | |
var ditherIndex; | |
/* This particular method should be optimized in itself */ | |
/* So we can pre-decrement */ | |
deltaY = bbH + 1; | |
srcY = sy; | |
dstY = dy; | |
srcShift = (dx & 1) * 16; | |
if (destMSB) { | |
srcShift = 16 - srcShift; | |
} | |
/* This is the outer loop */ | |
mask1 = SHL(65535, (16 - srcShift)); | |
while (((--deltaY)) !== 0) { | |
srcIndex = ((srcY * sourcePitch)) + (sx * 4); | |
dstIndex = ((dstY * destPitch)) + ((dx >> 1) * 4); | |
ditherBase = (dstY & 3) * 4; | |
/* For pre-increment */ | |
ditherIndex = (sx & 3) - 1; | |
/* So we can pre-decrement */ | |
deltaX = bbW + 1; | |
dstMask = mask1; | |
if (dstMask === 65535) { | |
srcShift = 16; | |
} else { | |
srcShift = 0; | |
} | |
while (((--deltaX)) !== 0) { | |
ditherThreshold = ditherMatrix4x4[ditherBase + ((ditherIndex = (ditherIndex + 1) & 3))]; | |
sourceWord = sourceBits[srcIndex >>> 2]; | |
srcAlpha = sourceWord >>> 24; | |
if (srcAlpha === 255) { | |
/* Dither from 32 to 16 bit */ | |
sourceWord = dither32To16threshold(sourceWord, ditherThreshold); | |
if (sourceWord === 0) { | |
sourceWord = SHL(1, srcShift); | |
} else { | |
sourceWord = SHL(sourceWord, srcShift); | |
} | |
dstLongAtputmask(dstIndex, sourceWord, dstMask); | |
} else { | |
/* srcAlpha ~= 255 */ | |
if (srcAlpha !== 0) { | |
/* 0 < srcAlpha < 255 */ | |
/* If we have to mix colors then just copy a single word */ | |
destWord = destBits[dstIndex >>> 2]; | |
destWord = destWord & ~dstMask; | |
/* Expand from 16 to 32 bit by adding zero bits */ | |
destWord = SHR(destWord, srcShift); | |
/* Mix colors */ | |
destWord = (((destWord & 31744) << 9) | ((destWord & 992) << 6)) | (((destWord & 31) << 3) | 4278190080); | |
/* And dither */ | |
sourceWord = alphaBlendScaledwith(sourceWord, destWord); | |
sourceWord = dither32To16threshold(sourceWord, ditherThreshold); | |
if (sourceWord === 0) { | |
sourceWord = SHL(1, srcShift); | |
} else { | |
sourceWord = SHL(sourceWord, srcShift); | |
} | |
dstLongAtputmask(dstIndex, sourceWord, dstMask); | |
} | |
} | |
srcIndex += 4; | |
if (destMSB) { | |
if (srcShift === 0) { | |
dstIndex += 4; | |
} | |
} else { | |
if (srcShift !== 0) { | |
dstIndex += 4; | |
} | |
} | |
/* Toggle between 0 and 16 */ | |
srcShift = srcShift ^ 16; | |
dstMask = ~dstMask; | |
} | |
++srcY; | |
++dstY; | |
} | |
} | |
/* This version assumes | |
combinationRule = 34 | |
sourcePixSize = destPixSize = 32 | |
sourceForm ~= destForm. | |
Note: The inner loop has been optimized for dealing | |
with the special cases of srcAlpha = 0.0 and srcAlpha = 1.0 | |
*/ | |
function alphaSourceBlendBits32() { | |
var sourceWord; | |
var srcIndex; | |
var deltaX; | |
var dstIndex; | |
var srcAlpha; | |
var deltaY; | |
var srcY; | |
var destWord; | |
var dstY; | |
/* This particular method should be optimized in itself */ | |
/* Give the compile a couple of hints */ | |
/* The following should be declared as pointers so the compiler will | |
notice that they're used for accessing memory locations | |
(good to know on an Intel architecture) but then the increments | |
would be different between ST code and C code so must hope the | |
compiler notices what happens (MS Visual C does) */ | |
/* So we can pre-decrement */ | |
deltaY = bbH + 1; | |
srcY = sy; | |
/* This is the outer loop */ | |
dstY = dy; | |
while (((--deltaY)) !== 0) { | |
srcIndex = ((srcY * sourcePitch)) + (sx * 4); | |
dstIndex = ((dstY * destPitch)) + (dx * 4); | |
/* So we can pre-decrement */ | |
/* This is the inner loop */ | |
deltaX = bbW + 1; | |
while (((--deltaX)) !== 0) { | |
sourceWord = sourceBits[srcIndex >>> 2]; | |
srcAlpha = sourceWord >>> 24; | |
if (srcAlpha === 255) { | |
destBits[dstIndex >>> 2] = sourceWord; | |
srcIndex += 4; | |
/* Now copy as many words as possible with alpha = 255 */ | |
dstIndex += 4; | |
while ((((--deltaX)) !== 0) && ((((sourceWord = sourceBits[srcIndex >>> 2])) >>> 24) === 255)) { | |
destBits[dstIndex >>> 2] = sourceWord; | |
srcIndex += 4; | |
dstIndex += 4; | |
} | |
++deltaX; | |
} else { | |
/* srcAlpha ~= 255 */ | |
if (srcAlpha === 0) { | |
srcIndex += 4; | |
/* Now skip as many words as possible, */ | |
dstIndex += 4; | |
while ((((--deltaX)) !== 0) && ((((sourceWord = sourceBits[srcIndex >>> 2])) >>> 24) === 0)) { | |
srcIndex += 4; | |
dstIndex += 4; | |
} | |
++deltaX; | |
} else { | |
/* 0 < srcAlpha < 255 */ | |
/* If we have to mix colors then just copy a single word */ | |
destWord = destBits[dstIndex >>> 2]; | |
destWord = alphaBlendScaledwith(sourceWord, destWord); | |
destBits[dstIndex >>> 2] = destWord; | |
srcIndex += 4; | |
dstIndex += 4; | |
} | |
} | |
} | |
++srcY; | |
++dstY; | |
} | |
} | |
/* This version assumes | |
combinationRule = 34 | |
sourcePixSize = 32 | |
destPixSize = 8 | |
sourceForm ~= destForm. | |
Note: This is not real blending since we don't have the source colors available. | |
*/ | |
function alphaSourceBlendBits8() { | |
var srcShift; | |
var sourceWord; | |
var srcIndex; | |
var deltaX; | |
var mappingTable; | |
var dstIndex; | |
var adjust; | |
var mapperFlags; | |
var srcAlpha; | |
var dstMask; | |
var deltaY; | |
var srcY; | |
var destWord; | |
var dstY; | |
mappingTable = default8To32Table(); | |
mapperFlags = cmFlags & ~ColorMapNewStyle; | |
/* So we can pre-decrement */ | |
deltaY = bbH + 1; | |
srcY = sy; | |
dstY = dy; | |
mask1 = (dx & 3) * 8; | |
if (destMSB) { | |
mask1 = 24 - mask1; | |
} | |
mask2 = AllOnes ^ (SHL(255, mask1)); | |
if ((dx & 1) === 0) { | |
adjust = 0; | |
} else { | |
adjust = 522133279; | |
} | |
if ((dy & 1) === 0) { | |
adjust = adjust ^ 522133279; | |
} | |
while (((--deltaY)) !== 0) { | |
adjust = adjust ^ 522133279; | |
srcIndex = ((srcY * sourcePitch)) + (sx * 4); | |
dstIndex = ((dstY * destPitch)) + ((dx >> 2) * 4); | |
/* So we can pre-decrement */ | |
deltaX = bbW + 1; | |
srcShift = mask1; | |
/* This is the inner loop */ | |
dstMask = mask2; | |
while (((--deltaX)) !== 0) { | |
sourceWord = (sourceBits[srcIndex >>> 2] & ~adjust) + adjust; | |
srcAlpha = sourceWord >>> 24; | |
if (srcAlpha > 31) { | |
/* Everything below 31 is transparent */ | |
if (srcAlpha < 224) { | |
/* Everything above 224 is opaque */ | |
destWord = destBits[dstIndex >>> 2]; | |
destWord = destWord & ~dstMask; | |
destWord = SHR(destWord, srcShift); | |
destWord = mappingTable[destWord]; | |
sourceWord = alphaBlendScaledwith(sourceWord, destWord); | |
} | |
sourceWord = mapPixelflags(sourceWord, mapperFlags); | |
/* Store back */ | |
sourceWord = SHL(sourceWord, srcShift); | |
dstLongAtputmask(dstIndex, sourceWord, dstMask); | |
} | |
srcIndex += 4; | |
if (destMSB) { | |
if (srcShift === 0) { | |
dstIndex += 4; | |
srcShift = 24; | |
dstMask = 16777215; | |
} else { | |
srcShift -= 8; | |
dstMask = (dstMask >>> 8) | 4278190080; | |
} | |
} else { | |
if (srcShift === 24) { | |
dstIndex += 4; | |
srcShift = 0; | |
dstMask = 4294967040; | |
} else { | |
srcShift += 8; | |
dstMask = (dstMask << 8) | 255; | |
} | |
} | |
adjust = adjust ^ 522133279; | |
} | |
++srcY; | |
++dstY; | |
} | |
} | |
function bitAndwith(sourceWord, destinationWord) { | |
return sourceWord & destinationWord; | |
} | |
function bitAndInvertwith(sourceWord, destinationWord) { | |
return sourceWord & ~destinationWord; | |
} | |
function bitInvertAndwith(sourceWord, destinationWord) { | |
return ~sourceWord & destinationWord; | |
} | |
function bitInvertAndInvertwith(sourceWord, destinationWord) { | |
return ~sourceWord & ~destinationWord; | |
} | |
function bitInvertDestinationwith(sourceWord, destinationWord) { | |
return ~destinationWord; | |
} | |
function bitInvertOrwith(sourceWord, destinationWord) { | |
return ~sourceWord | destinationWord; | |
} | |
function bitInvertOrInvertwith(sourceWord, destinationWord) { | |
return ~sourceWord | ~destinationWord; | |
} | |
function bitInvertSourcewith(sourceWord, destinationWord) { | |
return ~sourceWord; | |
} | |
function bitInvertXorwith(sourceWord, destinationWord) { | |
return ~sourceWord ^ destinationWord; | |
} | |
function bitOrwith(sourceWord, destinationWord) { | |
return sourceWord | destinationWord; | |
} | |
function bitOrInvertwith(sourceWord, destinationWord) { | |
return sourceWord | ~destinationWord; | |
} | |
function bitXorwith(sourceWord, destinationWord) { | |
return sourceWord ^ destinationWord; | |
} | |
/* check for possible overlap of source and destination */ | |
/* ar 10/19/1999: This method requires surfaces to be locked. */ | |
function checkSourceOverlap() { | |
var t; | |
if ((sourceForm === destForm) && (dy >= sy)) { | |
if (dy > sy) { | |
/* have to start at bottom */ | |
vDir = -1; | |
sy = (sy + bbH) - 1; | |
dy = (dy + bbH) - 1; | |
} else { | |
if ((dy === sy) && (dx > sx)) { | |
/* y's are equal, but x's are backward */ | |
hDir = -1; | |
/* start at right */ | |
sx = (sx + bbW) - 1; | |
/* and fix up masks */ | |
dx = (dx + bbW) - 1; | |
if (nWords > 1) { | |
t = mask1; | |
mask1 = mask2; | |
mask2 = t; | |
} | |
} | |
} | |
destIndex = ((dy * destPitch)) + ((DIV(dx, destPPW)) * 4); | |
destDelta = (destPitch * vDir) - (4 * (nWords * hDir)); | |
} | |
} | |
function clearWordwith(source, destination) { | |
return 0; | |
} | |
/* clip and adjust source origin and extent appropriately */ | |
/* first in x */ | |
function clipRange() { | |
if (destX >= clipX) { | |
sx = sourceX; | |
dx = destX; | |
bbW = width; | |
} else { | |
sx = sourceX + (clipX - destX); | |
bbW = width - (clipX - destX); | |
dx = clipX; | |
} | |
if ((dx + bbW) > (clipX + clipWidth)) { | |
bbW -= (dx + bbW) - (clipX + clipWidth); | |
} | |
if (destY >= clipY) { | |
sy = sourceY; | |
dy = destY; | |
bbH = height; | |
} else { | |
sy = (sourceY + clipY) - destY; | |
bbH = height - (clipY - destY); | |
dy = clipY; | |
} | |
if ((dy + bbH) > (clipY + clipHeight)) { | |
bbH -= (dy + bbH) - (clipY + clipHeight); | |
} | |
if (noSource) { | |
return null; | |
} | |
if (sx < 0) { | |
dx -= sx; | |
bbW += sx; | |
sx = 0; | |
} | |
if ((sx + bbW) > sourceWidth) { | |
bbW -= (sx + bbW) - sourceWidth; | |
} | |
if (sy < 0) { | |
dy -= sy; | |
bbH += sy; | |
sy = 0; | |
} | |
if ((sy + bbH) > sourceHeight) { | |
bbH -= (sy + bbH) - sourceHeight; | |
} | |
} | |
/* This function is exported for the Balloon engine */ | |
function copyBits() { | |
clipRange(); | |
if ((bbW <= 0) || (bbH <= 0)) { | |
/* zero width or height; noop */ | |
affectedL = (affectedR = (affectedT = (affectedB = 0))); | |
return null; | |
} | |
if (!lockSurfaces()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
// skipping ifdef ENABLE_FAST_BLT | |
copyBitsLockedAndClipped(); | |
unlockSurfaces(); | |
} | |
/* Recover from the fast path specialised code saying Help-I-cant-cope */ | |
function copyBitsFallback(op, flags) { | |
var done; | |
// skipping ifdef ENABLE_FAST_BLT | |
} | |
/* Perform the actual copyBits operation using the fast path specialised code; fail some cases by falling back to normal code. | |
Assume: Surfaces have been locked and clipping was performed. */ | |
function copyBitsFastPathSpecialised() { | |
// skipping ifdef ENABLE_FAST_BLT | |
} | |
/* Support for the balloon engine. */ | |
function copyBitsFromtoat(startX, stopX, yValue) { | |
destX = startX; | |
destY = yValue; | |
sourceX = startX; | |
width = stopX - startX; | |
copyBits(); | |
showDisplayBits(); | |
} | |
/* Perform the actual copyBits operation. | |
Assume: Surfaces have been locked and clipping was performed. */ | |
function copyBitsLockedAndClipped() { | |
var done; | |
copyBitsRule41Test(); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
done = tryCopyingBitsQuickly(); | |
if (done) { | |
return null; | |
} | |
if ((combinationRule === 30) || (combinationRule === 31)) { | |
/* Check and fetch source alpha parameter for alpha blend */ | |
if (interpreterProxy.methodArgumentCount() === 1) { | |
sourceAlpha = interpreterProxy.stackIntegerValue(0); | |
if (!(!interpreterProxy.failed() && ((sourceAlpha >= 0) && (sourceAlpha <= 255)))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
} else { | |
return interpreterProxy.primitiveFail(); | |
} | |
} | |
/* Choose and perform the actual copy loop. */ | |
bitCount = 0; | |
performCopyLoop(); | |
if ((combinationRule === 22) || (combinationRule === 32)) { | |
/* zero width and height; return the count */ | |
affectedL = (affectedR = (affectedT = (affectedB = 0))); | |
} | |
if (hDir > 0) { | |
affectedL = dx; | |
affectedR = dx + bbW; | |
} else { | |
affectedL = (dx - bbW) + 1; | |
affectedR = dx + 1; | |
} | |
if (vDir > 0) { | |
affectedT = dy; | |
affectedB = dy + bbH; | |
} else { | |
affectedT = (dy - bbH) + 1; | |
affectedB = dy + 1; | |
} | |
} | |
/* Test possible use of rule 41, rgbComponentAlpha:with: Nothing to return, just set up some variables */ | |
function copyBitsRule41Test() { | |
var ungammaLookupTableOop; | |
var gammaLookupTableOop; | |
if (combinationRule === 41) { | |
/* fetch the forecolor into componentAlphaModeColor. */ | |
componentAlphaModeAlpha = 255; | |
componentAlphaModeColor = 16777215; | |
gammaLookupTable = null; | |
ungammaLookupTable = null; | |
if (interpreterProxy.methodArgumentCount() >= 2) { | |
componentAlphaModeAlpha = interpreterProxy.stackIntegerValue(interpreterProxy.methodArgumentCount() - 2); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
componentAlphaModeColor = interpreterProxy.stackIntegerValue(interpreterProxy.methodArgumentCount() - 1); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (interpreterProxy.methodArgumentCount() === 4) { | |
gammaLookupTableOop = interpreterProxy.stackObjectValue(1); | |
if (interpreterProxy.isBytes(gammaLookupTableOop)) { | |
gammaLookupTable = gammaLookupTableOop.bytes; | |
} | |
ungammaLookupTableOop = interpreterProxy.stackObjectValue(0); | |
if (interpreterProxy.isBytes(ungammaLookupTableOop)) { | |
ungammaLookupTable = ungammaLookupTableOop.bytes; | |
} | |
} | |
} else { | |
if (interpreterProxy.methodArgumentCount() === 1) { | |
componentAlphaModeColor = interpreterProxy.stackIntegerValue(0); | |
if (interpreterProxy.failed()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
} else { | |
return interpreterProxy.primitiveFail(); | |
} | |
} | |
} | |
} | |
/* This version of the inner loop assumes noSource = false. */ | |
function copyLoop() { | |
var mergeWord; | |
var skewWord; | |
var skewMask; | |
var halftoneWord; | |
var unskew; | |
var mergeFnwith; | |
var hInc; | |
var destWord; | |
var word; | |
var prevWord; | |
var y; | |
var i; | |
var thisWord; | |
var notSkewMask; | |
mergeFnwith = opTable[combinationRule + 1]; | |
mergeFnwith; | |
/* Byte delta */ | |
/* degenerate skew fixed for Sparc. 10/20/96 ikp */ | |
hInc = hDir * 4; | |
if (skew === -32) { | |
skew = (unskew = (skewMask = 0)); | |
} else { | |
if (skew < 0) { | |
unskew = skew + 32; | |
skewMask = SHL(AllOnes, (0 - skew)); | |
} else { | |
if (skew === 0) { | |
unskew = 0; | |
skewMask = AllOnes; | |
} else { | |
unskew = skew - 32; | |
skewMask = SHR(AllOnes, skew); | |
} | |
} | |
} | |
notSkewMask = ~skewMask; | |
if (noHalftone) { | |
halftoneWord = AllOnes; | |
halftoneHeight = 0; | |
} else { | |
halftoneWord = halftoneAt(0); | |
} | |
y = dy; | |
for (i = 1; i <= bbH; i++) { | |
/* here is the vertical loop */ | |
if (halftoneHeight > 1) { | |
/* Otherwise, its always the same */ | |
halftoneWord = halftoneAt(y); | |
y += vDir; | |
} | |
if (preload) { | |
/* load the 64-bit shifter */ | |
prevWord = sourceBits[sourceIndex >>> 2]; | |
sourceIndex += hInc; | |
} else { | |
prevWord = 0; | |
} | |
destMask = mask1; | |
/* pick up next word */ | |
thisWord = sourceBits[sourceIndex >>> 2]; | |
sourceIndex += hInc; | |
/* 32-bit rotate */ | |
skewWord = (SHIFT((prevWord & notSkewMask), unskew)) | (SHIFT((thisWord & skewMask), skew)); | |
prevWord = thisWord; | |
destWord = destBits[destIndex >>> 2]; | |
mergeWord = mergeFnwith(skewWord & halftoneWord, destWord); | |
destWord = (destMask & mergeWord) | (destWord & ~destMask); | |
destBits[destIndex >>> 2] = destWord; | |
/* This central horizontal loop requires no store masking */ | |
destIndex += hInc; | |
destMask = AllOnes; | |
if (combinationRule === 3) { | |
if ((skew === 0) && (halftoneWord === AllOnes)) { | |
/* Very special inner loop for STORE mode with no skew -- just move words */ | |
if (hDir === -1) { | |
/* Woeful patch: revert to older code for hDir = -1 */ | |
for (word = 2; word <= (nWords - 1); word++) { | |
thisWord = sourceBits[sourceIndex >>> 2]; | |
sourceIndex += hInc; | |
destBits[destIndex >>> 2] = thisWord; | |
destIndex += hInc; | |
} | |
} else { | |
for (word = 2; word <= (nWords - 1); word++) { | |
/* Note loop starts with prevWord loaded (due to preload) */ | |
destBits[destIndex >>> 2] = prevWord; | |
destIndex += hInc; | |
prevWord = sourceBits[sourceIndex >>> 2]; | |
sourceIndex += hInc; | |
} | |
} | |
} else { | |
/* Special inner loop for STORE mode -- no need to call merge */ | |
for (word = 2; word <= (nWords - 1); word++) { | |
thisWord = sourceBits[sourceIndex >>> 2]; | |
sourceIndex += hInc; | |
/* 32-bit rotate */ | |
skewWord = (SHIFT((prevWord & notSkewMask), unskew)) | (SHIFT((thisWord & skewMask), skew)); | |
prevWord = thisWord; | |
destBits[destIndex >>> 2] = skewWord & halftoneWord; | |
destIndex += hInc; | |
} | |
} | |
} else { | |
for (word = 2; word <= (nWords - 1); word++) { | |
/* Normal inner loop does merge: */ | |
/* pick up next word */ | |
thisWord = sourceBits[sourceIndex >>> 2]; | |
sourceIndex += hInc; | |
/* 32-bit rotate */ | |
skewWord = (SHIFT((prevWord & notSkewMask), unskew)) | (SHIFT((thisWord & skewMask), skew)); | |
prevWord = thisWord; | |
mergeWord = mergeFnwith(skewWord & halftoneWord, destBits[destIndex >>> 2]); | |
destBits[destIndex >>> 2] = mergeWord; | |
destIndex += hInc; | |
} | |
} | |
if (nWords > 1) { | |
destMask = mask2; | |
/* pick up next word */ | |
thisWord = sourceBits[sourceIndex >>> 2]; | |
sourceIndex += hInc; | |
/* 32-bit rotate */ | |
skewWord = (SHIFT((prevWord & notSkewMask), unskew)) | (SHIFT((thisWord & skewMask), skew)); | |
destWord = destBits[destIndex >>> 2]; | |
mergeWord = mergeFnwith(skewWord & halftoneWord, destWord); | |
destWord = (destMask & mergeWord) | (destWord & ~destMask); | |
destBits[destIndex >>> 2] = destWord; | |
destIndex += hInc; | |
} | |
sourceIndex += sourceDelta; | |
destIndex += destDelta; | |
} | |
} | |
/* Faster copyLoop when source not used. hDir and vDir are both | |
positive, and perload and skew are unused */ | |
function copyLoopNoSource() { | |
var mergeWord; | |
var halftoneWord; | |
var mergeFnwith; | |
var destWord; | |
var word; | |
var i; | |
mergeFnwith = opTable[combinationRule + 1]; | |
mergeFnwith; | |
for (i = 1; i <= bbH; i++) { | |
/* here is the vertical loop */ | |
if (noHalftone) { | |
halftoneWord = AllOnes; | |
} else { | |
halftoneWord = halftoneAt((dy + i) - 1); | |
} | |
destMask = mask1; | |
destWord = destBits[destIndex >>> 2]; | |
mergeWord = mergeFnwith(halftoneWord, destWord); | |
destWord = (destMask & mergeWord) | (destWord & ~destMask); | |
destBits[destIndex >>> 2] = destWord; | |
/* This central horizontal loop requires no store masking */ | |
destIndex += 4; | |
destMask = AllOnes; | |
if (combinationRule === 3) { | |
/* Special inner loop for STORE */ | |
destWord = halftoneWord; | |
for (word = 2; word <= (nWords - 1); word++) { | |
destBits[destIndex >>> 2] = destWord; | |
destIndex += 4; | |
} | |
} else { | |
/* Normal inner loop does merge */ | |
for (word = 2; word <= (nWords - 1); word++) { | |
/* Normal inner loop does merge */ | |
destWord = destBits[destIndex >>> 2]; | |
mergeWord = mergeFnwith(halftoneWord, destWord); | |
destBits[destIndex >>> 2] = mergeWord; | |
destIndex += 4; | |
} | |
} | |
if (nWords > 1) { | |
destMask = mask2; | |
destWord = destBits[destIndex >>> 2]; | |
mergeWord = mergeFnwith(halftoneWord, destWord); | |
destWord = (destMask & mergeWord) | (destWord & ~destMask); | |
destBits[destIndex >>> 2] = destWord; | |
destIndex += 4; | |
} | |
destIndex += destDelta; | |
} | |
} | |
/* This version of the inner loop maps source pixels | |
to a destination form with different depth. Because it is already | |
unweildy, the loop is not unrolled as in the other versions. | |
Preload, skew and skewMask are all overlooked, since pickSourcePixels | |
delivers its destination word already properly aligned. | |
Note that pickSourcePixels could be copied in-line at the top of | |
the horizontal loop, and some of its inits moved out of the loop. */ | |
/* ar 12/7/1999: | |
The loop has been rewritten to use only one pickSourcePixels call. | |
The idea is that the call itself could be inlined. If we decide not | |
to inline pickSourcePixels we could optimize the loop instead. */ | |
function copyLoopPixMap() { | |
var mapperFlags; | |
var srcShiftInc; | |
var dstShiftLeft; | |
var sourcePixMask; | |
var nSourceIncs; | |
var skewWord; | |
var words; | |
var destWord; | |
var startBits; | |
var mergeFnwith; | |
var dstShift; | |
var i; | |
var halftoneWord; | |
var mergeWord; | |
var destPixMask; | |
var dstShiftInc; | |
var srcShift; | |
var endBits; | |
var nPix; | |
var scrStartBits; | |
mergeFnwith = opTable[combinationRule + 1]; | |
mergeFnwith; | |
sourcePPW = DIV(32, sourceDepth); | |
sourcePixMask = maskTable[sourceDepth]; | |
destPixMask = maskTable[destDepth]; | |
mapperFlags = cmFlags & ~ColorMapNewStyle; | |
sourceIndex = ((sy * sourcePitch)) + ((DIV(sx, sourcePPW)) * 4); | |
scrStartBits = sourcePPW - (sx & (sourcePPW - 1)); | |
if (bbW < scrStartBits) { | |
nSourceIncs = 0; | |
} else { | |
nSourceIncs = (DIV((bbW - scrStartBits), sourcePPW)) + 1; | |
} | |
/* Note following two items were already calculated in destmask setup! */ | |
sourceDelta = sourcePitch - (nSourceIncs * 4); | |
startBits = destPPW - (dx & (destPPW - 1)); | |
endBits = (((dx + bbW) - 1) & (destPPW - 1)) + 1; | |
if (bbW < startBits) { | |
startBits = bbW; | |
} | |
srcShift = (sx & (sourcePPW - 1)) * sourceDepth; | |
dstShift = (dx & (destPPW - 1)) * destDepth; | |
srcShiftInc = sourceDepth; | |
dstShiftInc = destDepth; | |
dstShiftLeft = 0; | |
if (sourceMSB) { | |
srcShift = (32 - sourceDepth) - srcShift; | |
srcShiftInc = 0 - srcShiftInc; | |
} | |
if (destMSB) { | |
dstShift = (32 - destDepth) - dstShift; | |
dstShiftInc = 0 - dstShiftInc; | |
dstShiftLeft = 32 - destDepth; | |
} | |
for (i = 1; i <= bbH; i++) { | |
/* here is the vertical loop */ | |
/* *** is it possible at all that noHalftone == false? *** */ | |
if (noHalftone) { | |
halftoneWord = AllOnes; | |
} else { | |
halftoneWord = halftoneAt((dy + i) - 1); | |
} | |
srcBitShift = srcShift; | |
dstBitShift = dstShift; | |
destMask = mask1; | |
/* Here is the horizontal loop... */ | |
nPix = startBits; | |
words = nWords; | |
do { | |
/* pick up the word */ | |
/* align next word to leftmost pixel */ | |
skewWord = pickSourcePixelsflagssrcMaskdestMasksrcShiftIncdstShiftInc(nPix, mapperFlags, sourcePixMask, destPixMask, srcShiftInc, dstShiftInc); | |
dstBitShift = dstShiftLeft; | |
if (destMask === AllOnes) { | |
/* avoid read-modify-write */ | |
mergeWord = mergeFnwith(skewWord & halftoneWord, destBits[destIndex >>> 2]); | |
destBits[destIndex >>> 2] = destMask & mergeWord; | |
} else { | |
/* General version using dest masking */ | |
destWord = destBits[destIndex >>> 2]; | |
mergeWord = mergeFnwith(skewWord & halftoneWord, destWord & destMask); | |
destWord = (destMask & mergeWord) | (destWord & ~destMask); | |
destBits[destIndex >>> 2] = destWord; | |
} | |
destIndex += 4; | |
if (words === 2) { | |
/* e.g., is the next word the last word? */ | |
/* set mask for last word in this row */ | |
destMask = mask2; | |
nPix = endBits; | |
} else { | |
/* use fullword mask for inner loop */ | |
destMask = AllOnes; | |
nPix = destPPW; | |
} | |
} while(!(((--words)) === 0)); | |
sourceIndex += sourceDelta; | |
destIndex += destDelta; | |
} | |
} | |
/* Return the default translation table from 1..8 bit indexed colors to 32bit */ | |
/* The table has been generated by the following statements */ | |
/* | pvs hex | | |
String streamContents:[:s| | |
s nextPutAll:'static unsigned int theTable[256] = { '. | |
pvs := (Color colorMapIfNeededFrom: 8 to: 32) asArray. | |
1 to: pvs size do:[:i| | |
i > 1 ifTrue:[s nextPutAll:', ']. | |
(i-1 \\ 8) = 0 ifTrue:[s cr]. | |
s nextPutAll:'0x'. | |
hex := (pvs at: i) printStringBase: 16. | |
s nextPutAll: (hex copyFrom: 4 to: hex size). | |
]. | |
s nextPutAll:'};'. | |
]. */ | |
function default8To32Table() { | |
var theTable = [ | |
0x0, 0xFF000001, 0xFFFFFFFF, 0xFF808080, 0xFFFF0000, 0xFF00FF00, 0xFF0000FF, 0xFF00FFFF, | |
0xFFFFFF00, 0xFFFF00FF, 0xFF202020, 0xFF404040, 0xFF606060, 0xFF9F9F9F, 0xFFBFBFBF, 0xFFDFDFDF, | |
0xFF080808, 0xFF101010, 0xFF181818, 0xFF282828, 0xFF303030, 0xFF383838, 0xFF484848, 0xFF505050, | |
0xFF585858, 0xFF686868, 0xFF707070, 0xFF787878, 0xFF878787, 0xFF8F8F8F, 0xFF979797, 0xFFA7A7A7, | |
0xFFAFAFAF, 0xFFB7B7B7, 0xFFC7C7C7, 0xFFCFCFCF, 0xFFD7D7D7, 0xFFE7E7E7, 0xFFEFEFEF, 0xFFF7F7F7, | |
0xFF000001, 0xFF003300, 0xFF006600, 0xFF009900, 0xFF00CC00, 0xFF00FF00, 0xFF000033, 0xFF003333, | |
0xFF006633, 0xFF009933, 0xFF00CC33, 0xFF00FF33, 0xFF000066, 0xFF003366, 0xFF006666, 0xFF009966, | |
0xFF00CC66, 0xFF00FF66, 0xFF000099, 0xFF003399, 0xFF006699, 0xFF009999, 0xFF00CC99, 0xFF00FF99, | |
0xFF0000CC, 0xFF0033CC, 0xFF0066CC, 0xFF0099CC, 0xFF00CCCC, 0xFF00FFCC, 0xFF0000FF, 0xFF0033FF, | |
0xFF0066FF, 0xFF0099FF, 0xFF00CCFF, 0xFF00FFFF, 0xFF330000, 0xFF333300, 0xFF336600, 0xFF339900, | |
0xFF33CC00, 0xFF33FF00, 0xFF330033, 0xFF333333, 0xFF336633, 0xFF339933, 0xFF33CC33, 0xFF33FF33, | |
0xFF330066, 0xFF333366, 0xFF336666, 0xFF339966, 0xFF33CC66, 0xFF33FF66, 0xFF330099, 0xFF333399, | |
0xFF336699, 0xFF339999, 0xFF33CC99, 0xFF33FF99, 0xFF3300CC, 0xFF3333CC, 0xFF3366CC, 0xFF3399CC, | |
0xFF33CCCC, 0xFF33FFCC, 0xFF3300FF, 0xFF3333FF, 0xFF3366FF, 0xFF3399FF, 0xFF33CCFF, 0xFF33FFFF, | |
0xFF660000, 0xFF663300, 0xFF666600, 0xFF669900, 0xFF66CC00, 0xFF66FF00, 0xFF660033, 0xFF663333, | |
0xFF666633, 0xFF669933, 0xFF66CC33, 0xFF66FF33, 0xFF660066, 0xFF663366, 0xFF666666, 0xFF669966, | |
0xFF66CC66, 0xFF66FF66, 0xFF660099, 0xFF663399, 0xFF666699, 0xFF669999, 0xFF66CC99, 0xFF66FF99, | |
0xFF6600CC, 0xFF6633CC, 0xFF6666CC, 0xFF6699CC, 0xFF66CCCC, 0xFF66FFCC, 0xFF6600FF, 0xFF6633FF, | |
0xFF6666FF, 0xFF6699FF, 0xFF66CCFF, 0xFF66FFFF, 0xFF990000, 0xFF993300, 0xFF996600, 0xFF999900, | |
0xFF99CC00, 0xFF99FF00, 0xFF990033, 0xFF993333, 0xFF996633, 0xFF999933, 0xFF99CC33, 0xFF99FF33, | |
0xFF990066, 0xFF993366, 0xFF996666, 0xFF999966, 0xFF99CC66, 0xFF99FF66, 0xFF990099, 0xFF993399, | |
0xFF996699, 0xFF999999, 0xFF99CC99, 0xFF99FF99, 0xFF9900CC, 0xFF9933CC, 0xFF9966CC, 0xFF9999CC, | |
0xFF99CCCC, 0xFF99FFCC, 0xFF9900FF, 0xFF9933FF, 0xFF9966FF, 0xFF9999FF, 0xFF99CCFF, 0xFF99FFFF, | |
0xFFCC0000, 0xFFCC3300, 0xFFCC6600, 0xFFCC9900, 0xFFCCCC00, 0xFFCCFF00, 0xFFCC0033, 0xFFCC3333, | |
0xFFCC6633, 0xFFCC9933, 0xFFCCCC33, 0xFFCCFF33, 0xFFCC0066, 0xFFCC3366, 0xFFCC6666, 0xFFCC9966, | |
0xFFCCCC66, 0xFFCCFF66, 0xFFCC0099, 0xFFCC3399, 0xFFCC6699, 0xFFCC9999, 0xFFCCCC99, 0xFFCCFF99, | |
0xFFCC00CC, 0xFFCC33CC, 0xFFCC66CC, 0xFFCC99CC, 0xFFCCCCCC, 0xFFCCFFCC, 0xFFCC00FF, 0xFFCC33FF, | |
0xFFCC66FF, 0xFFCC99FF, 0xFFCCCCFF, 0xFFCCFFFF, 0xFFFF0000, 0xFFFF3300, 0xFFFF6600, 0xFFFF9900, | |
0xFFFFCC00, 0xFFFFFF00, 0xFFFF0033, 0xFFFF3333, 0xFFFF6633, 0xFFFF9933, 0xFFFFCC33, 0xFFFFFF33, | |
0xFFFF0066, 0xFFFF3366, 0xFFFF6666, 0xFFFF9966, 0xFFFFCC66, 0xFFFFFF66, 0xFFFF0099, 0xFFFF3399, | |
0xFFFF6699, 0xFFFF9999, 0xFFFFCC99, 0xFFFFFF99, 0xFFFF00CC, 0xFFFF33CC, 0xFFFF66CC, 0xFFFF99CC, | |
0xFFFFCCCC, 0xFFFFFFCC, 0xFFFF00FF, 0xFFFF33FF, 0xFFFF66FF, 0xFFFF99FF, 0xFFFFCCFF, 0xFFFFFFFF];; | |
return theTable; | |
} | |
/* Utility routine for computing Warp increments. */ | |
function deltaFromtonSteps(x1, x2, n) { | |
if (x2 > x1) { | |
return (DIV(((x2 - x1) + FixedPt1), (n + 1))) + 1; | |
} else { | |
if (x2 === x1) { | |
return 0; | |
} | |
return 0 - ((DIV(((x1 - x2) + FixedPt1), (n + 1))) + 1); | |
} | |
} | |
/* Compute masks for left and right destination words */ | |
function destMaskAndPointerInit() { | |
var endBits; | |
var startBits; | |
var pixPerM1; | |
/* A mask, assuming power of two */ | |
/* how many pixels in first word */ | |
pixPerM1 = destPPW - 1; | |
startBits = destPPW - (dx & pixPerM1); | |
if (destMSB) { | |
mask1 = SHR(AllOnes, (32 - (startBits * destDepth))); | |
} else { | |
mask1 = SHL(AllOnes, (32 - (startBits * destDepth))); | |
} | |
endBits = (((dx + bbW) - 1) & pixPerM1) + 1; | |
if (destMSB) { | |
mask2 = SHL(AllOnes, (32 - (endBits * destDepth))); | |
} else { | |
mask2 = SHR(AllOnes, (32 - (endBits * destDepth))); | |
} | |
if (bbW < startBits) { | |
mask1 = mask1 & mask2; | |
mask2 = 0; | |
nWords = 1; | |
} else { | |
nWords = (DIV(((bbW - startBits) + pixPerM1), destPPW)) + 1; | |
} | |
/* defaults for no overlap with source */ | |
/* calculate byte addr and delta, based on first word of data */ | |
/* Note pitch is bytes and nWords is longs, not bytes */ | |
hDir = (vDir = 1); | |
destIndex = ((dy * destPitch)) + ((DIV(dx, destPPW)) * 4); | |
destDelta = (destPitch * vDir) - (4 * (nWords * hDir)); | |
} | |
function destinationWordwith(sourceWord, destinationWord) { | |
return destinationWord; | |
} | |
/* Dither the given 32bit word to 16 bit. Ignore alpha. */ | |
function dither32To16threshold(srcWord, ditherValue) { | |
var addThreshold; | |
/* You bet */ | |
addThreshold = ditherValue << 8; | |
return ((dither8Lookup[addThreshold + ((srcWord >>> 16) & 255)] << 10) + (dither8Lookup[addThreshold + ((srcWord >>> 8) & 255)] << 5)) + dither8Lookup[addThreshold + (srcWord & 255)]; | |
} | |
/* This is the primitive implementation of the line-drawing loop. | |
See the comments in BitBlt>>drawLoopX:Y: */ | |
function drawLoopXY(xDelta, yDelta) { | |
var P; | |
var affT; | |
var dx1; | |
var px; | |
var affR; | |
var affL; | |
var py; | |
var i; | |
var affB; | |
var dy1; | |
if (xDelta > 0) { | |
dx1 = 1; | |
} else { | |
if (xDelta === 0) { | |
dx1 = 0; | |
} else { | |
dx1 = -1; | |
} | |
} | |
if (yDelta > 0) { | |
dy1 = 1; | |
} else { | |
if (yDelta === 0) { | |
dy1 = 0; | |
} else { | |
dy1 = -1; | |
} | |
} | |
px = Math.abs(yDelta); | |
py = Math.abs(xDelta); | |
/* init null rectangle */ | |
affL = (affT = 9999); | |
affR = (affB = -9999); | |
if (py > px) { | |
/* more horizontal */ | |
P = py >> 1; | |
for (i = 1; i <= py; i++) { | |
destX += dx1; | |
if (((P -= px)) < 0) { | |
destY += dy1; | |
P += py; | |
} | |
if (i < py) { | |
copyBits(); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if ((affectedL < affectedR) && (affectedT < affectedB)) { | |
/* Affected rectangle grows along the line */ | |
affL = Math.min(affL, affectedL); | |
affR = Math.max(affR, affectedR); | |
affT = Math.min(affT, affectedT); | |
affB = Math.max(affB, affectedB); | |
if (((affR - affL) * (affB - affT)) > 4000) { | |
/* If affected rectangle gets large, update it in chunks */ | |
affectedL = affL; | |
affectedR = affR; | |
affectedT = affT; | |
affectedB = affB; | |
showDisplayBits(); | |
/* init null rectangle */ | |
affL = (affT = 9999); | |
affR = (affB = -9999); | |
} | |
} | |
} | |
} | |
} else { | |
/* more vertical */ | |
P = px >> 1; | |
for (i = 1; i <= px; i++) { | |
destY += dy1; | |
if (((P -= py)) < 0) { | |
destX += dx1; | |
P += px; | |
} | |
if (i < px) { | |
copyBits(); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if ((affectedL < affectedR) && (affectedT < affectedB)) { | |
/* Affected rectangle grows along the line */ | |
affL = Math.min(affL, affectedL); | |
affR = Math.max(affR, affectedR); | |
affT = Math.min(affT, affectedT); | |
affB = Math.max(affB, affectedB); | |
if (((affR - affL) * (affB - affT)) > 4000) { | |
/* If affected rectangle gets large, update it in chunks */ | |
affectedL = affL; | |
affectedR = affR; | |
affectedT = affT; | |
affectedB = affB; | |
showDisplayBits(); | |
/* init null rectangle */ | |
affL = (affT = 9999); | |
affR = (affB = -9999); | |
} | |
} | |
} | |
} | |
} | |
affectedL = affL; | |
affectedR = affR; | |
affectedT = affT; | |
/* store destX, Y back */ | |
affectedB = affB; | |
interpreterProxy.storeIntegerofObjectwithValue(BBDestXIndex, bitBltOop, destX); | |
interpreterProxy.storeIntegerofObjectwithValue(BBDestYIndex, bitBltOop, destY); | |
} | |
/* Store the given value back into destination form, using dstMask | |
to mask out the bits to be modified. This is an essiantial | |
read-modify-write operation on the destination form. */ | |
function dstLongAtputmask(idx, srcValue, dstMask) { | |
var dstValue; | |
dstValue = destBits[idx >>> 2]; | |
dstValue = dstValue & dstMask; | |
dstValue = dstValue | srcValue; | |
destBits[idx >>> 2] = dstValue; | |
} | |
/* Dither the given 32bit word to 16 bit. Ignore alpha. */ | |
function expensiveDither32To16threshold(srcWord, ditherValue) { | |
var pv; | |
var threshold; | |
var value; | |
var out; | |
/* You bet */ | |
pv = srcWord & 255; | |
threshold = ditherThresholds16[pv & 7]; | |
value = ditherValues16[pv >>> 3]; | |
if (ditherValue < threshold) { | |
out = value + 1; | |
} else { | |
out = value; | |
} | |
pv = (srcWord >>> 8) & 255; | |
threshold = ditherThresholds16[pv & 7]; | |
value = ditherValues16[pv >>> 3]; | |
if (ditherValue < threshold) { | |
out = out | ((value + 1) << 5); | |
} else { | |
out = out | (value << 5); | |
} | |
pv = (srcWord >>> 16) & 255; | |
threshold = ditherThresholds16[pv & 7]; | |
value = ditherValues16[pv >>> 3]; | |
if (ditherValue < threshold) { | |
out = out | ((value + 1) << 10); | |
} else { | |
out = out | (value << 10); | |
} | |
return out; | |
} | |
/* Return the integer value of the given field of the given object. If the field contains a Float, truncate it and return its integral part. Fail if the given field does not contain a small integer or Float, or if the truncated Float is out of the range of small integers. */ | |
function fetchIntOrFloatofObject(fieldIndex, objectPointer) { | |
var floatValue; | |
var fieldOop; | |
fieldOop = interpreterProxy.fetchPointerofObject(fieldIndex, objectPointer); | |
if (typeof fieldOop === "number") { | |
return fieldOop; | |
} | |
floatValue = interpreterProxy.floatValueOf(fieldOop); | |
if (!((-2.147483648e9 <= floatValue) && (floatValue <= 2.147483647e9))) { | |
interpreterProxy.primitiveFail(); | |
return 0; | |
} | |
return (floatValue|0); | |
} | |
/* Return the integer value of the given field of the given object. If the field contains a Float, truncate it and return its integral part. Fail if the given field does not contain a small integer or Float, or if the truncated Float is out of the range of small integers. */ | |
function fetchIntOrFloatofObjectifNil(fieldIndex, objectPointer, defaultValue) { | |
var floatValue; | |
var fieldOop; | |
fieldOop = interpreterProxy.fetchPointerofObject(fieldIndex, objectPointer); | |
if (typeof fieldOop === "number") { | |
return fieldOop; | |
} | |
if (fieldOop.isNil) { | |
return defaultValue; | |
} | |
floatValue = interpreterProxy.floatValueOf(fieldOop); | |
if (!((-2.147483648e9 <= floatValue) && (floatValue <= 2.147483647e9))) { | |
interpreterProxy.primitiveFail(); | |
return 0; | |
} | |
return (floatValue|0); | |
} | |
/* For any non-zero pixel value in destinationWord with zero alpha channel take the alpha from sourceWord and fill it in. Intended for fixing alpha channels left at zero during 16->32 bpp conversions. */ | |
function fixAlphawith(sourceWord, destinationWord) { | |
if (destDepth !== 32) { | |
return destinationWord; | |
} | |
if (destinationWord === 0) { | |
return 0; | |
} | |
if ((destinationWord & 4278190080) !== 0) { | |
return destinationWord; | |
} | |
return destinationWord | (sourceWord & 4278190080); | |
} | |
/* 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 a value from the halftone pattern. */ | |
function halftoneAt(idx) { | |
return halftoneBase[MOD(idx, halftoneHeight)]; | |
} | |
function halt() { | |
; | |
} | |
function ignoreSourceOrHalftone(formPointer) { | |
if (formPointer.isNil) { | |
return true; | |
} | |
if (combinationRule === 0) { | |
return true; | |
} | |
if (combinationRule === 5) { | |
return true; | |
} | |
if (combinationRule === 10) { | |
return true; | |
} | |
if (combinationRule === 15) { | |
return true; | |
} | |
return false; | |
} | |
function initBBOpTable() { | |
opTable[0+1] = clearWordwith; | |
opTable[1+1] = bitAndwith; | |
opTable[2+1] = bitAndInvertwith; | |
opTable[3+1] = sourceWordwith; | |
opTable[4+1] = bitInvertAndwith; | |
opTable[5+1] = destinationWordwith; | |
opTable[6+1] = bitXorwith; | |
opTable[7+1] = bitOrwith; | |
opTable[8+1] = bitInvertAndInvertwith; | |
opTable[9+1] = bitInvertXorwith; | |
opTable[10+1] = bitInvertDestinationwith; | |
opTable[11+1] = bitOrInvertwith; | |
opTable[12+1] = bitInvertSourcewith; | |
opTable[13+1] = bitInvertOrwith; | |
opTable[14+1] = bitInvertOrInvertwith; | |
opTable[15+1] = destinationWordwith; | |
opTable[16+1] = destinationWordwith; | |
opTable[17+1] = destinationWordwith; | |
opTable[18+1] = addWordwith; | |
opTable[19+1] = subWordwith; | |
opTable[20+1] = rgbAddwith; | |
opTable[21+1] = rgbSubwith; | |
opTable[22+1] = OLDrgbDiffwith; | |
opTable[23+1] = OLDtallyIntoMapwith; | |
opTable[24+1] = alphaBlendwith; | |
opTable[25+1] = pixPaintwith; | |
opTable[26+1] = pixMaskwith; | |
opTable[27+1] = rgbMaxwith; | |
opTable[28+1] = rgbMinwith; | |
opTable[29+1] = rgbMinInvertwith; | |
opTable[30+1] = alphaBlendConstwith; | |
opTable[31+1] = alphaPaintConstwith; | |
opTable[32+1] = rgbDiffwith; | |
opTable[33+1] = tallyIntoMapwith; | |
opTable[34+1] = alphaBlendScaledwith; | |
opTable[35+1] = alphaBlendScaledwith; | |
opTable[36+1] = alphaBlendScaledwith; | |
opTable[37+1] = rgbMulwith; | |
opTable[38+1] = pixSwapwith; | |
opTable[39+1] = pixClearwith; | |
opTable[40+1] = fixAlphawith; | |
opTable[41+1] = rgbComponentAlphawith; | |
} | |
function initDither8Lookup() { | |
var t; | |
var b; | |
var value; | |
for (b = 0; b <= 255; b++) { | |
for (t = 0; t <= 15; t++) { | |
value = expensiveDither32To16threshold(b, t); | |
dither8Lookup[(t << 8) + b] = value; | |
} | |
} | |
} | |
function initialiseModule() { | |
initBBOpTable(); | |
initDither8Lookup(); | |
// skipping ifdef ENABLE_FAST_BLT | |
return true; | |
} | |
/* Return true if shiftTable/maskTable define an identity mapping. */ | |
function isIdentityMapwith(shifts, masks) { | |
if ((!shifts) || (!masks)) { | |
return true; | |
} | |
if ((shifts[RedIndex] === 0) && ((shifts[GreenIndex] === 0) && ((shifts[BlueIndex] === 0) && ((shifts[AlphaIndex] === 0) && ((masks[RedIndex] === 16711680) && ((masks[GreenIndex] === 65280) && ((masks[BlueIndex] === 255) && (masks[AlphaIndex] === 4278190080)))))))) { | |
return true; | |
} | |
return false; | |
} | |
/* Load the dest form for BitBlt. Return false if anything is wrong, true otherwise. */ | |
function loadBitBltDestForm() { | |
var destBitsSize; | |
destBits = interpreterProxy.fetchPointerofObject(FormBitsIndex, destForm); | |
destWidth = interpreterProxy.fetchIntegerofObject(FormWidthIndex, destForm); | |
destHeight = interpreterProxy.fetchIntegerofObject(FormHeightIndex, destForm); | |
if (!((destWidth >= 0) && (destHeight >= 0))) { | |
return false; | |
} | |
destDepth = interpreterProxy.fetchIntegerofObject(FormDepthIndex, destForm); | |
destMSB = destDepth > 0; | |
if (destDepth < 0) { | |
destDepth = 0 - destDepth; | |
} | |
if (typeof destBits === "number") { | |
/* Query for actual surface dimensions */ | |
if (!queryDestSurface(destBits)) { | |
return false; | |
} | |
destPPW = DIV(32, destDepth); | |
destBits = (destPitch = 0); | |
} else { | |
destPPW = DIV(32, destDepth); | |
destPitch = (DIV((destWidth + (destPPW - 1)), destPPW)) * 4; | |
destBitsSize = BYTESIZEOF(destBits); | |
if (!(interpreterProxy.isWordsOrBytes(destBits) && (destBitsSize === (destPitch * destHeight)))) { | |
if (interpreterProxy.isWordsOrBytes(destBits) && (destBitsSize > (destPitch * destHeight))) { | |
interpreterProxy.vm.warnOnce("BitBlt>>loadBitBltDestForm: destBitsSize != destPitch * destHeight, expected " + | |
destPitch + "*" + destHeight + "=" + (destPitch * destHeight) + ", got " + destBitsSize); | |
} else { | |
return false; | |
} | |
} | |
destBits = destBits.wordsOrBytes(); | |
} | |
return true; | |
} | |
/* Load BitBlt from the oop. | |
This function is exported for the Balloon engine. */ | |
function loadBitBltFrom(bbObj) { | |
return loadBitBltFromwarping(bbObj, false); | |
} | |
/* Load context from BitBlt instance. Return false if anything is amiss */ | |
/* NOTE this should all be changed to minX/maxX coordinates for simpler clipping | |
-- once it works! */ | |
function loadBitBltFromwarping(bbObj, aBool) { | |
var ok; | |
bitBltOop = bbObj; | |
isWarping = aBool; | |
combinationRule = interpreterProxy.fetchIntegerofObject(BBRuleIndex, bitBltOop); | |
if (interpreterProxy.failed() || ((combinationRule < 0) || (combinationRule > (OpTableSize - 2)))) { | |
return false; | |
} | |
if ((combinationRule >= 16) && (combinationRule <= 17)) { | |
return false; | |
} | |
sourceForm = interpreterProxy.fetchPointerofObject(BBSourceFormIndex, bitBltOop); | |
noSource = ignoreSourceOrHalftone(sourceForm); | |
halftoneForm = interpreterProxy.fetchPointerofObject(BBHalftoneFormIndex, bitBltOop); | |
noHalftone = ignoreSourceOrHalftone(halftoneForm); | |
destForm = interpreterProxy.fetchPointerofObject(BBDestFormIndex, bbObj); | |
if (!(interpreterProxy.isPointers(destForm) && (SIZEOF(destForm) >= 4))) { | |
return false; | |
} | |
ok = loadBitBltDestForm(); | |
if (!ok) { | |
return false; | |
} | |
destX = fetchIntOrFloatofObjectifNil(BBDestXIndex, bitBltOop, 0); | |
destY = fetchIntOrFloatofObjectifNil(BBDestYIndex, bitBltOop, 0); | |
width = fetchIntOrFloatofObjectifNil(BBWidthIndex, bitBltOop, destWidth); | |
height = fetchIntOrFloatofObjectifNil(BBHeightIndex, bitBltOop, destHeight); | |
if (interpreterProxy.failed()) { | |
return false; | |
} | |
if (noSource) { | |
sourceX = (sourceY = 0); | |
} else { | |
if (!(interpreterProxy.isPointers(sourceForm) && (SIZEOF(sourceForm) >= 4))) { | |
return false; | |
} | |
ok = loadBitBltSourceForm(); | |
if (!ok) { | |
return false; | |
} | |
ok = loadColorMap(); | |
if (!ok) { | |
return false; | |
} | |
if ((cmFlags & ColorMapNewStyle) === 0) { | |
setupColorMasks(); | |
} | |
sourceX = fetchIntOrFloatofObjectifNil(BBSourceXIndex, bitBltOop, 0); | |
sourceY = fetchIntOrFloatofObjectifNil(BBSourceYIndex, bitBltOop, 0); | |
} | |
ok = loadHalftoneForm(); | |
if (!ok) { | |
return false; | |
} | |
clipX = fetchIntOrFloatofObjectifNil(BBClipXIndex, bitBltOop, 0); | |
clipY = fetchIntOrFloatofObjectifNil(BBClipYIndex, bitBltOop, 0); | |
clipWidth = fetchIntOrFloatofObjectifNil(BBClipWidthIndex, bitBltOop, destWidth); | |
clipHeight = fetchIntOrFloatofObjectifNil(BBClipHeightIndex, bitBltOop, destHeight); | |
if (interpreterProxy.failed()) { | |
return false; | |
} | |
if (clipX < 0) { | |
clipWidth += clipX; | |
clipX = 0; | |
} | |
if (clipY < 0) { | |
clipHeight += clipY; | |
clipY = 0; | |
} | |
if ((clipX + clipWidth) > destWidth) { | |
clipWidth = destWidth - clipX; | |
} | |
if ((clipY + clipHeight) > destHeight) { | |
clipHeight = destHeight - clipY; | |
} | |
return true; | |
} | |
/* Load the source form for BitBlt. Return false if anything is wrong, true otherwise. */ | |
function loadBitBltSourceForm() { | |
var sourceBitsSize; | |
sourceBits = interpreterProxy.fetchPointerofObject(FormBitsIndex, sourceForm); | |
sourceWidth = fetchIntOrFloatofObject(FormWidthIndex, sourceForm); | |
sourceHeight = fetchIntOrFloatofObject(FormHeightIndex, sourceForm); | |
if (!((sourceWidth >= 0) && (sourceHeight >= 0))) { | |
return false; | |
} | |
sourceDepth = interpreterProxy.fetchIntegerofObject(FormDepthIndex, sourceForm); | |
sourceMSB = sourceDepth > 0; | |
if (sourceDepth < 0) { | |
sourceDepth = 0 - sourceDepth; | |
} | |
if (typeof sourceBits === "number") { | |
/* Query for actual surface dimensions */ | |
if (!querySourceSurface(sourceBits)) { | |
return false; | |
} | |
sourcePPW = DIV(32, sourceDepth); | |
sourceBits = (sourcePitch = 0); | |
} else { | |
sourcePPW = DIV(32, sourceDepth); | |
sourcePitch = (DIV((sourceWidth + (sourcePPW - 1)), sourcePPW)) * 4; | |
sourceBitsSize = BYTESIZEOF(sourceBits); | |
if (!(interpreterProxy.isWordsOrBytes(sourceBits) && (sourceBitsSize === (sourcePitch * sourceHeight)))) { | |
if (interpreterProxy.isWordsOrBytes(sourceBits) && (sourceBitsSize > (sourcePitch * sourceHeight))) { | |
interpreterProxy.vm.warnOnce("BitBlt>>loadBitBltSourceForm: sourceBitsSize != sourcePitch * sourceHeight, expected " + | |
sourcePitch + "*" + sourceHeight + "=" + (sourcePitch * sourceHeight) + ", got " + sourceBitsSize); | |
} else { | |
return false; | |
} | |
} | |
sourceBits = sourceBits.wordsOrBytes(); | |
} | |
return true; | |
} | |
/* ColorMap, if not nil, must be longWords, and | |
2^N long, where N = sourceDepth for 1, 2, 4, 8 bits, | |
or N = 9, 12, or 15 (3, 4, 5 bits per color) for 16 or 32 bits. */ | |
function loadColorMap() { | |
var oop; | |
var cmOop; | |
var cmSize; | |
var oldStyle; | |
cmFlags = (cmMask = (cmBitsPerColor = 0)); | |
cmShiftTable = null; | |
cmMaskTable = null; | |
cmLookupTable = null; | |
cmOop = interpreterProxy.fetchPointerofObject(BBColorMapIndex, bitBltOop); | |
if (cmOop.isNil) { | |
return true; | |
} | |
/* even if identity or somesuch - may be cleared later */ | |
cmFlags = ColorMapPresent; | |
oldStyle = false; | |
if (interpreterProxy.isWords(cmOop)) { | |
/* This is an old-style color map (indexed only, with implicit RGBA conversion) */ | |
cmSize = SIZEOF(cmOop); | |
cmLookupTable = cmOop.words; | |
oldStyle = true; | |
; | |
} else { | |
/* A new-style color map (fully qualified) */ | |
if (!(interpreterProxy.isPointers(cmOop) && (SIZEOF(cmOop) >= 3))) { | |
return false; | |
} | |
cmShiftTable = loadColorMapShiftOrMaskFrom(interpreterProxy.fetchPointerofObject(0, cmOop)); | |
cmMaskTable = loadColorMapShiftOrMaskFrom(interpreterProxy.fetchPointerofObject(1, cmOop)); | |
oop = interpreterProxy.fetchPointerofObject(2, cmOop); | |
if (oop.isNil) { | |
cmSize = 0; | |
} else { | |
if (!interpreterProxy.isWords(oop)) { | |
return false; | |
} | |
cmSize = SIZEOF(oop); | |
cmLookupTable = oop.words; | |
} | |
cmFlags = cmFlags | ColorMapNewStyle; | |
; | |
} | |
if ((cmSize & (cmSize - 1)) !== 0) { | |
return false; | |
} | |
cmMask = cmSize - 1; | |
cmBitsPerColor = 0; | |
if (cmSize === 512) { | |
cmBitsPerColor = 3; | |
} | |
if (cmSize === 4096) { | |
cmBitsPerColor = 4; | |
} | |
if (cmSize === 32768) { | |
cmBitsPerColor = 5; | |
} | |
if (cmSize === 0) { | |
cmLookupTable = null; | |
cmMask = 0; | |
} else { | |
cmFlags = cmFlags | ColorMapIndexedPart; | |
} | |
if (oldStyle) { | |
/* needs implicit conversion */ | |
setupColorMasks(); | |
} | |
if (isIdentityMapwith(cmShiftTable, cmMaskTable)) { | |
cmMaskTable = null; | |
cmShiftTable = null; | |
} else { | |
cmFlags = cmFlags | ColorMapFixedPart; | |
} | |
return true; | |
} | |
function loadColorMapShiftOrMaskFrom(mapOop) { | |
if (mapOop.isNil) { | |
return null; | |
} | |
if (typeof mapOop === "number") { | |
interpreterProxy.primitiveFail(); | |
return null; | |
} | |
if (!(interpreterProxy.isWords(mapOop) && (SIZEOF(mapOop) === 4))) { | |
interpreterProxy.primitiveFail(); | |
return null; | |
} | |
// hand-edited generated code: shifts needs to be signed! | |
return mapOop.wordsAsInt32Array(); | |
} | |
/* Load the halftone form */ | |
function loadHalftoneForm() { | |
var halftoneBits; | |
if (noHalftone) { | |
halftoneBase = null; | |
return true; | |
} | |
if (interpreterProxy.isPointers(halftoneForm) && (SIZEOF(halftoneForm) >= 4)) { | |
/* Old-style 32xN monochrome halftone Forms */ | |
halftoneBits = interpreterProxy.fetchPointerofObject(FormBitsIndex, halftoneForm); | |
halftoneHeight = interpreterProxy.fetchIntegerofObject(FormHeightIndex, halftoneForm); | |
if (!interpreterProxy.isWords(halftoneBits)) { | |
noHalftone = true; | |
} | |
} else { | |
/* New spec accepts, basically, a word array */ | |
if (!(!interpreterProxy.isPointers(halftoneForm) && (interpreterProxy.isWords(halftoneForm)))) { | |
return false; | |
} | |
halftoneBits = halftoneForm; | |
halftoneHeight = SIZEOF(halftoneBits); | |
} | |
halftoneBase = halftoneBits.wordsOrBytes(); | |
return true; | |
} | |
/* Load the surface support plugin */ | |
function loadSurfacePlugin() { | |
querySurfaceFn = interpreterProxy.ioLoadFunctionFrom("ioGetSurfaceFormat", "SurfacePlugin"); | |
lockSurfaceFn = interpreterProxy.ioLoadFunctionFrom("ioLockSurface", "SurfacePlugin"); | |
unlockSurfaceFn = interpreterProxy.ioLoadFunctionFrom("ioUnlockSurface", "SurfacePlugin"); | |
return (!!querySurfaceFn) && ((!!lockSurfaceFn) && (!!unlockSurfaceFn)); | |
} | |
function loadWarpBltFrom(bbObj) { | |
return loadBitBltFromwarping(bbObj, true); | |
} | |
/* Get a pointer to the bits of any OS surfaces. */ | |
/* Notes: | |
* For equal source/dest handles only one locking operation is performed. | |
This is to prevent locking of overlapping areas which does not work with | |
certain APIs (as an example, DirectDraw prevents locking of overlapping areas). | |
A special case for non-overlapping but equal source/dest handle would | |
be possible but we would have to transfer this information over to | |
unlockSurfaces somehow (currently, only one unlock operation is | |
performed for equal source and dest handles). Also, this would require | |
a change in the notion of ioLockSurface() which is right now interpreted | |
as a hint and not as a requirement to lock only the specific portion of | |
the surface. | |
* The arguments in ioLockSurface() provide the implementation with | |
an explicit hint what area is affected. It can be very useful to | |
know the max. affected area beforehand if getting the bits requires expensive | |
copy operations (e.g., like a roundtrip to the X server or a glReadPixel op). | |
However, the returned pointer *MUST* point to the virtual origin of the surface | |
and not to the beginning of the rectangle. The promise made by BitBlt | |
is to never access data outside the given rectangle (aligned to 4byte boundaries!) | |
so it is okay to return a pointer to the virtual origin that is actually outside | |
the valid memory area. | |
* The area provided in ioLockSurface() is already clipped (e.g., it will always | |
be inside the source and dest boundingBox) but it is not aligned to word boundaries | |
yet. It is up to the support code to compute accurate alignment if necessary. | |
* Warping always requires the entire source surface to be locked because | |
there is no beforehand knowledge about what area will actually be traversed. | |
*/ | |
function lockSurfaces() { | |
var destHandle; | |
var sourceHandle; | |
var t; | |
var fn; | |
var r; | |
var b; | |
var l; | |
hasSurfaceLock = false; | |
if (destBits === 0) { | |
/* Blitting *to* OS surface */ | |
if (!lockSurfaceFn) { | |
if (!loadSurfacePlugin()) { | |
return null; | |
} | |
} | |
fn = lockSurfaceFn; | |
destHandle = interpreterProxy.fetchIntegerofObject(FormBitsIndex, destForm); | |
if ((sourceBits === 0) && (!noSource)) { | |
/* Handle the special case of equal source and dest handles */ | |
sourceHandle = interpreterProxy.fetchIntegerofObject(FormBitsIndex, sourceForm); | |
if (sourceHandle === destHandle) { | |
/* If we have overlapping source/dest we lock the entire area | |
so that there is only one area transmitted */ | |
if (isWarping) { | |
/* Otherwise use overlapping area */ | |
l = Math.min(sx, dx); | |
r = Math.max(sx, dx) + bbW; | |
t = Math.min(sy, dy); | |
b = Math.max(sy, dy) + bbH; | |
sourceBits = fn(sourceHandle, function(p){sourcePitch = p}, l, t, r-l, b-t); | |
} else { | |
/* When warping we always need the entire surface for the source */ | |
sourceBits = fn(sourceHandle, function(p){sourcePitch = p}, 0,0, sourceWidth, sourceHeight); | |
} | |
destBits = sourceBits; | |
destPitch = sourcePitch; | |
hasSurfaceLock = true; | |
return destBits !== 0; | |
} | |
} | |
destBits = fn(destHandle, function(p){destPitch = p}, dx, dy, bbW, bbH); | |
hasSurfaceLock = true; | |
} | |
if ((sourceBits === 0) && (!noSource)) { | |
/* Blitting *from* OS surface */ | |
sourceHandle = interpreterProxy.fetchIntegerofObject(FormBitsIndex, sourceForm); | |
if (!lockSurfaceFn) { | |
if (!loadSurfacePlugin()) { | |
return null; | |
} | |
} | |
/* Warping requiring the entire surface */ | |
fn = lockSurfaceFn; | |
if (isWarping) { | |
sourceBits = fn(sourceHandle, function(p){sourcePitch = p}, 0, 0, sourceWidth, sourceHeight); | |
} else { | |
sourceBits = fn(sourceHandle, function(p){sourcePitch = p}, sx, sy, bbW, bbH); | |
} | |
hasSurfaceLock = true; | |
} | |
return (destBits !== 0) && ((sourceBits !== 0) || (noSource)); | |
} | |
/* Color map the given source pixel. */ | |
function mapPixelflags(sourcePixel, mapperFlags) { | |
var pv; | |
pv = sourcePixel; | |
if ((mapperFlags & ColorMapPresent) !== 0) { | |
if ((mapperFlags & ColorMapFixedPart) !== 0) { | |
/* avoid introducing transparency by color reduction */ | |
pv = rgbMapPixelflags(sourcePixel, mapperFlags); | |
if ((pv === 0) && (sourcePixel !== 0)) { | |
pv = 1; | |
} | |
} | |
if ((mapperFlags & ColorMapIndexedPart) !== 0) { | |
pv = cmLookupTable[pv & cmMask]; | |
} | |
} | |
return pv; | |
} | |
/* The module with the given name was just unloaded. | |
Make sure we have no dangling references. */ | |
function moduleUnloaded(aModuleName) { | |
if (strcmp(aModuleName, "SurfacePlugin") === 0) { | |
/* The surface plugin just shut down. How nasty. */ | |
querySurfaceFn = (lockSurfaceFn = (unlockSurfaceFn = 0)); | |
} | |
} | |
/* AND word1 to word2 as nParts partitions of nBits each. | |
Any field of word1 not all-ones is treated as all-zeroes. | |
Used for erasing, eg, brush shapes prior to ORing in a color */ | |
function partitionedANDtonBitsnPartitions(word1, word2, nBits, nParts) { | |
var result; | |
var i; | |
var mask; | |
/* partition mask starts at the right */ | |
mask = maskTable[nBits]; | |
result = 0; | |
for (i = 1; i <= nParts; i++) { | |
if ((word1 & mask) === mask) { | |
result = result | (word2 & mask); | |
} | |
/* slide left to next partition */ | |
mask = SHL(mask, nBits); | |
} | |
return result; | |
} | |
/* Add word1 to word2 as nParts partitions of nBits each. | |
This is useful for packed pixels, or packed colors */ | |
/* Use unsigned int everywhere because it has a well known arithmetic model without undefined behavior w.r.t. overflow and shifts */ | |
function partitionedAddtonBitscomponentMaskcarryOverflowMask(word1, word2, nBits, componentMask, carryOverflowMask) { | |
var w2; | |
var carryOverflow; | |
var sum; | |
var w1; | |
/* mask to remove high bit of each component */ | |
w1 = word1 & carryOverflowMask; | |
w2 = word2 & carryOverflowMask; | |
/* sum without high bit to avoid overflowing over next component */ | |
sum = (word1 ^ w1) + (word2 ^ w2); | |
/* detect overflow condition for saturating */ | |
carryOverflow = (w1 & w2) | ((w1 | w2) & sum); | |
return ((sum ^ w1) ^ w2) | ((SHR(carryOverflow, (nBits - 1))) * componentMask); | |
} | |
/* Max word1 to word2 as nParts partitions of nBits each */ | |
/* In C, most arithmetic operations answer the same bit pattern regardless of the operands being signed or unsigned ints | |
(this is due to the way 2's complement numbers work). However, comparisions might fail. Add the proper declaration of | |
words as unsigned int in those cases where comparisions are done (jmv) */ | |
function partitionedMaxwithnBitsnPartitions(word1, word2, nBits, nParts) { | |
var result; | |
var i; | |
var mask; | |
/* partition mask starts at the right */ | |
mask = maskTable[nBits]; | |
result = 0; | |
for (i = 1; i <= nParts; i++) { | |
result = result | Math.max((word2 & mask), (word1 & mask)); | |
/* slide left to next partition */ | |
mask = SHL(mask, nBits); | |
} | |
return result; | |
} | |
/* Min word1 to word2 as nParts partitions of nBits each */ | |
/* In C, most arithmetic operations answer the same bit pattern regardless of the operands being signed or unsigned ints | |
(this is due to the way 2's complement numbers work). However, comparisions might fail. Add the proper declaration of | |
words as unsigned int in those cases where comparisions are done (jmv) */ | |
function partitionedMinwithnBitsnPartitions(word1, word2, nBits, nParts) { | |
var result; | |
var i; | |
var mask; | |
/* partition mask starts at the right */ | |
mask = maskTable[nBits]; | |
result = 0; | |
for (i = 1; i <= nParts; i++) { | |
result = result | Math.min((word2 & mask), (word1 & mask)); | |
/* slide left to next partition */ | |
mask = SHL(mask, nBits); | |
} | |
return result; | |
} | |
/* Multiply word1 with word2 as nParts partitions of nBits each. | |
This is useful for packed pixels, or packed colors. | |
Bug in loop version when non-white background */ | |
/* In C, integer multiplication might answer a wrong value if the unsigned values are declared as signed. | |
This problem does not affect this method, because the most significant bit (i.e. the sign bit) will | |
always be zero (jmv) */ | |
function partitionedMulwithnBitsnPartitions(word1, word2, nBits, nParts) { | |
var dMask; | |
var result; | |
var product; | |
var sMask; | |
/* partition mask starts at the right */ | |
sMask = maskTable[nBits]; | |
dMask = SHL(sMask, nBits); | |
/* optimized first step */ | |
result = SHR((((((word1 & sMask) + 1) * ((word2 & sMask) + 1)) - 1) & dMask), nBits); | |
if (nParts === 1) { | |
return result; | |
} | |
product = (((((SHR(word1, nBits)) & sMask) + 1) * (((SHR(word2, nBits)) & sMask) + 1)) - 1) & dMask; | |
result = result | product; | |
if (nParts === 2) { | |
return result; | |
} | |
product = (((((SHR(word1, (2 * nBits))) & sMask) + 1) * (((SHR(word2, (2 * nBits))) & sMask) + 1)) - 1) & dMask; | |
result = result | (SHL(product, nBits)); | |
if (nParts === 3) { | |
return result; | |
} | |
product = (((((SHR(word1, (3 * nBits))) & sMask) + 1) * (((SHR(word2, (3 * nBits))) & sMask) + 1)) - 1) & dMask; | |
result = result | (SHL(product, (2 * nBits))); | |
return result; | |
} | |
function partitionedRgbComponentAlphadestnBitsnPartitions(sourceWord, destWord, nBits, nParts) { | |
var p2; | |
var result; | |
var p1; | |
var i; | |
var v; | |
var mask; | |
/* partition mask starts at the right */ | |
mask = maskTable[nBits]; | |
result = 0; | |
for (i = 1; i <= nParts; i++) { | |
p1 = SHR((sourceWord & mask), ((i - 1) * nBits)); | |
p2 = SHR((destWord & mask), ((i - 1) * nBits)); | |
if (nBits !== 32) { | |
if (nBits === 16) { | |
p1 = rgbMap16To32(p1) | 4278190080; | |
p2 = rgbMap16To32(p2) | 4278190080; | |
} else { | |
p1 = rgbMapfromto(p1, nBits, 32) | 4278190080; | |
p2 = rgbMapfromto(p2, nBits, 32) | 4278190080; | |
} | |
} | |
v = rgbComponentAlpha32with(p1, p2); | |
if (nBits !== 32) { | |
v = rgbMapfromto(v, 32, nBits); | |
} | |
result = result | (SHL(v, ((i - 1) * nBits))); | |
/* slide left to next partition */ | |
mask = SHL(mask, nBits); | |
} | |
return result; | |
} | |
/* Subtract word1 from word2 as nParts partitions of nBits each. | |
This is useful for packed pixels, or packed colors */ | |
/* In C, most arithmetic operations answer the same bit pattern regardless of the operands being signed or unsigned ints | |
(this is due to the way 2's complement numbers work). However, comparisions might fail. Add the proper declaration of | |
words as unsigned int in those cases where comparisions are done (jmv) */ | |
function partitionedSubfromnBitsnPartitions(word1, word2, nBits, nParts) { | |
var p2; | |
var result; | |
var p1; | |
var i; | |
var mask; | |
/* partition mask starts at the right */ | |
mask = maskTable[nBits]; | |
result = 0; | |
for (i = 1; i <= nParts; i++) { | |
p1 = word1 & mask; | |
p2 = word2 & mask; | |
if (p1 < p2) { | |
/* result is really abs value of thedifference */ | |
result = result | (p2 - p1); | |
} else { | |
result = result | (p1 - p2); | |
} | |
/* slide left to next partition */ | |
mask = SHL(mask, nBits); | |
} | |
return result; | |
} | |
/* Based on the values provided during setup choose and | |
perform the appropriate inner loop function. */ | |
/* Should be inlined into caller for speed */ | |
function performCopyLoop() { | |
destMaskAndPointerInit(); | |
if (noSource) { | |
/* Simple fill loop */ | |
copyLoopNoSource(); | |
} else { | |
/* Loop using source and dest */ | |
checkSourceOverlap(); | |
if ((sourceDepth !== destDepth) || ((cmFlags !== 0) || (sourceMSB !== destMSB))) { | |
/* If we must convert between pixel depths or use | |
color lookups or swap pixels use the general version */ | |
copyLoopPixMap(); | |
} else { | |
/* Otherwise we simple copy pixels and can use a faster version */ | |
sourceSkewAndPointerInit(); | |
copyLoop(); | |
} | |
} | |
} | |
/* Pick nPix pixels starting at srcBitIndex from the source, map by the | |
color map, and justify them according to dstBitIndex in the resulting destWord. */ | |
function pickSourcePixelsflagssrcMaskdestMasksrcShiftIncdstShiftInc(nPixels, mapperFlags, srcMask, dstMask, srcShiftInc, dstShiftInc) { | |
var sourcePix; | |
var srcShift; | |
var sourceWord; | |
var dstShift; | |
var destPix; | |
var nPix; | |
var destWord; | |
/* oh please */ | |
sourceWord = sourceBits[sourceIndex >>> 2]; | |
destWord = 0; | |
/* Hint: Keep in register */ | |
srcShift = srcBitShift; | |
/* Hint: Keep in register */ | |
dstShift = dstBitShift; | |
/* always > 0 so we can use do { } while(--nPix); */ | |
nPix = nPixels; | |
if (mapperFlags === (ColorMapPresent | ColorMapIndexedPart)) { | |
/* a little optimization for (pretty crucial) blits using indexed lookups only */ | |
/* grab, colormap and mix in pixel */ | |
do { | |
sourcePix = (SHR(sourceWord, srcShift)) & srcMask; | |
destPix = cmLookupTable[sourcePix & cmMask]; | |
/* adjust dest pix index */ | |
destWord = destWord | (SHL((destPix & dstMask), dstShift)); | |
/* adjust source pix index */ | |
dstShift += dstShiftInc; | |
if ((((srcShift += srcShiftInc)) & 4294967264) !== 0) { | |
if (sourceMSB) { | |
srcShift += 32; | |
} else { | |
srcShift -= 32; | |
} | |
sourceWord = sourceBits[(sourceIndex += 4) >>> 2]; | |
} | |
} while(!(((--nPix)) === 0)); | |
} else { | |
/* grab, colormap and mix in pixel */ | |
do { | |
sourcePix = (SHR(sourceWord, srcShift)) & srcMask; | |
destPix = mapPixelflags(sourcePix, mapperFlags); | |
/* adjust dest pix index */ | |
destWord = destWord | (SHL((destPix & dstMask), dstShift)); | |
/* adjust source pix index */ | |
dstShift += dstShiftInc; | |
if ((((srcShift += srcShiftInc)) & 4294967264) !== 0) { | |
if (sourceMSB) { | |
srcShift += 32; | |
} else { | |
srcShift -= 32; | |
} | |
sourceWord = sourceBits[(sourceIndex += 4) >>> 2]; | |
} | |
} while(!(((--nPix)) === 0)); | |
} | |
/* Store back */ | |
srcBitShift = srcShift; | |
return destWord; | |
} | |
/* Pick a single pixel from the source for WarpBlt. | |
Note: This method is crucial for WarpBlt speed w/o smoothing | |
and still relatively important when smoothing is used. */ | |
function pickWarpPixelAtXy(xx, yy) { | |
var sourcePix; | |
var sourceWord; | |
var srcIndex; | |
var x; | |
var y; | |
/* *please* */ | |
/* note: it would be much faster if we could just | |
avoid these stupid tests for being inside sourceForm. */ | |
if ((xx < 0) || ((yy < 0) || ((((x = xx >>> 14)) >= sourceWidth) || (((y = yy >>> 14)) >= sourceHeight)))) { | |
return 0; | |
} | |
srcIndex = ((y * sourcePitch)) + ((SHR(x, warpAlignShift)) * 4); | |
/* Extract pixel from word */ | |
sourceWord = sourceBits[srcIndex >>> 2]; | |
srcBitShift = warpBitShiftTable[x & warpAlignMask]; | |
sourcePix = (SHR(sourceWord, srcBitShift)) & warpSrcMask; | |
return sourcePix; | |
} | |
/* Clear all pixels in destinationWord for which the pixels of sourceWord have the same values. Used to clear areas of some constant color to zero. */ | |
function pixClearwith(sourceWord, destinationWord) { | |
var pv; | |
var nBits; | |
var result; | |
var i; | |
var mask; | |
if (destDepth === 32) { | |
if (sourceWord === destinationWord) { | |
return 0; | |
} else { | |
return destinationWord; | |
} | |
} | |
nBits = destDepth; | |
/* partition mask starts at the right */ | |
mask = maskTable[nBits]; | |
result = 0; | |
for (i = 1; i <= destPPW; i++) { | |
pv = destinationWord & mask; | |
if ((sourceWord & mask) === pv) { | |
pv = 0; | |
} | |
result = result | pv; | |
/* slide left to next partition */ | |
mask = SHL(mask, nBits); | |
} | |
return result; | |
} | |
function pixMaskwith(sourceWord, destinationWord) { | |
return partitionedANDtonBitsnPartitions(~sourceWord, destinationWord, destDepth, destPPW); | |
} | |
function pixPaintwith(sourceWord, destinationWord) { | |
if (sourceWord === 0) { | |
return destinationWord; | |
} | |
return sourceWord | partitionedANDtonBitsnPartitions(~sourceWord, destinationWord, destDepth, destPPW); | |
} | |
/* Swap the pixels in destWord */ | |
function pixSwapwith(sourceWord, destWord) { | |
var result; | |
var shift; | |
var lowMask; | |
var highMask; | |
var i; | |
if (destPPW === 1) { | |
return destWord; | |
} | |
result = 0; | |
/* mask low pixel */ | |
lowMask = (SHL(1, destDepth)) - 1; | |
/* mask high pixel */ | |
highMask = SHL(lowMask, ((destPPW - 1) * destDepth)); | |
shift = 32 - destDepth; | |
result = result | ((SHL((destWord & lowMask), shift)) | (SHR((destWord & highMask), shift))); | |
if (destPPW <= 2) { | |
return result; | |
} | |
for (i = 2; i <= (destPPW >> 1); i++) { | |
lowMask = SHL(lowMask, destDepth); | |
highMask = SHR(highMask, destDepth); | |
shift -= destDepth * 2; | |
result = result | ((SHL((destWord & lowMask), shift)) | (SHR((destWord & highMask), shift))); | |
} | |
return result; | |
} | |
/* Invoke the copyBits primitive. If the destination is the display, then copy it to the screen. */ | |
function primitiveCopyBits() { | |
var rcvr; | |
rcvr = interpreterProxy.stackValue(interpreterProxy.methodArgumentCount()); | |
if (!loadBitBltFrom(rcvr)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
copyBits(); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
showDisplayBits(); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
interpreterProxy.pop(interpreterProxy.methodArgumentCount()); | |
if ((combinationRule === 22) || (combinationRule === 32)) { | |
interpreterProxy.pop(1); | |
return interpreterProxy.pushInteger(bitCount); | |
} | |
} | |
function primitiveDisplayString() { | |
var charIndex; | |
var sourcePtr; | |
var stopIndex; | |
var bbObj; | |
var xTable; | |
var maxGlyph; | |
var quickBlt; | |
var glyphIndex; | |
var glyphMap; | |
var left; | |
var kernDelta; | |
var startIndex; | |
var ascii; | |
var sourceString; | |
if (interpreterProxy.methodArgumentCount() !== 6) { | |
return interpreterProxy.primitiveFail(); | |
} | |
kernDelta = interpreterProxy.stackIntegerValue(0); | |
xTable = interpreterProxy.stackObjectValue(1); | |
glyphMap = interpreterProxy.stackObjectValue(2); | |
if (!((CLASSOF(xTable) === interpreterProxy.classArray()) && (CLASSOF(glyphMap) === interpreterProxy.classArray()))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (SIZEOF(glyphMap) !== 256) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
maxGlyph = SIZEOF(xTable) - 2; | |
stopIndex = interpreterProxy.stackIntegerValue(3); | |
startIndex = interpreterProxy.stackIntegerValue(4); | |
sourceString = interpreterProxy.stackObjectValue(5); | |
if (!interpreterProxy.isBytes(sourceString)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (!((startIndex > 0) && ((stopIndex > 0) && (stopIndex <= BYTESIZEOF(sourceString))))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
bbObj = interpreterProxy.stackObjectValue(6); | |
if (!loadBitBltFrom(bbObj)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if ((combinationRule === 30) || (combinationRule === 31)) { | |
/* needs extra source alpha */ | |
return interpreterProxy.primitiveFail(); | |
} | |
quickBlt = (destBits !== 0) && ((sourceBits !== 0) && ((noSource === false) && ((sourceForm !== destForm) && ((cmFlags !== 0) || ((sourceMSB !== destMSB) || (sourceDepth !== destDepth)))))); | |
left = destX; | |
sourcePtr = sourceString.bytes; | |
for (charIndex = startIndex; charIndex <= stopIndex; charIndex++) { | |
ascii = sourcePtr[charIndex - 1]; | |
glyphIndex = interpreterProxy.fetchIntegerofObject(ascii, glyphMap); | |
if ((glyphIndex < 0) || (glyphIndex > maxGlyph)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
sourceX = interpreterProxy.fetchIntegerofObject(glyphIndex, xTable); | |
width = interpreterProxy.fetchIntegerofObject(glyphIndex + 1, xTable) - sourceX; | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
clipRange(); | |
if ((bbW > 0) && (bbH > 0)) { | |
if (quickBlt) { | |
destMaskAndPointerInit(); | |
copyLoopPixMap(); | |
affectedL = dx; | |
affectedR = dx + bbW; | |
affectedT = dy; | |
affectedB = dy + bbH; | |
} else { | |
copyBits(); | |
} | |
} | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
destX = (destX + width) + kernDelta; | |
} | |
affectedL = left; | |
showDisplayBits(); | |
interpreterProxy.storeIntegerofObjectwithValue(BBDestXIndex, bbObj, destX); | |
interpreterProxy.pop(6); | |
} | |
/* Invoke the line drawing primitive. */ | |
function primitiveDrawLoop() { | |
var yDelta; | |
var rcvr; | |
var xDelta; | |
rcvr = interpreterProxy.stackValue(2); | |
xDelta = interpreterProxy.stackIntegerValue(1); | |
yDelta = interpreterProxy.stackIntegerValue(0); | |
if (!loadBitBltFrom(rcvr)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
if (!interpreterProxy.failed()) { | |
drawLoopXY(xDelta, yDelta); | |
showDisplayBits(); | |
} | |
if (!interpreterProxy.failed()) { | |
interpreterProxy.pop(2); | |
} | |
} | |
/* returns the single pixel at x@y. | |
It does not handle LSB bitmaps right now. | |
If x or y are < 0, return 0 to indicate transparent (cf BitBlt>bitPeekerFromForm: usage). | |
Likewise if x>width or y>depth. | |
Fail if the rcvr doesn't seem to be a Form, or x|y seem wrong */ | |
function primitivePixelValueAt() { | |
var pixel; | |
var rcvr; | |
var shift; | |
var depth; | |
var bitmap; | |
var ppW; | |
var word; | |
var stride; | |
var bitsSize; | |
var mask; | |
var xVal; | |
var yVal; | |
var _return_value; | |
xVal = interpreterProxy.stackIntegerValue(1); | |
yVal = interpreterProxy.stackIntegerValue(0); | |
rcvr = interpreterProxy.stackValue(2); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if ((xVal < 0) || (yVal < 0)) { | |
_return_value = 0; | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
interpreterProxy.popthenPush(3, _return_value); | |
return null; | |
} | |
rcvr = interpreterProxy.stackValue(interpreterProxy.methodArgumentCount()); | |
if (!(interpreterProxy.isPointers(rcvr) && (SIZEOF(rcvr) >= 4))) { | |
interpreterProxy.primitiveFail(); | |
return null; | |
} | |
bitmap = interpreterProxy.fetchPointerofObject(FormBitsIndex, rcvr); | |
if (!interpreterProxy.isWordsOrBytes(bitmap)) { | |
interpreterProxy.primitiveFail(); | |
return null; | |
} | |
width = interpreterProxy.fetchIntegerofObject(FormWidthIndex, rcvr); | |
height = interpreterProxy.fetchIntegerofObject(FormHeightIndex, rcvr); | |
/* if width/height/depth are not integer, fail */ | |
depth = interpreterProxy.fetchIntegerofObject(FormDepthIndex, rcvr); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
if ((xVal >= width) || (yVal >= height)) { | |
_return_value = 0; | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
interpreterProxy.popthenPush(3, _return_value); | |
return null; | |
} | |
if (depth < 0) { | |
interpreterProxy.primitiveFail(); | |
return null; | |
} | |
/* pixels in each word */ | |
ppW = DIV(32, depth); | |
/* how many words per row of pixels */ | |
stride = DIV((width + (ppW - 1)), ppW); | |
bitsSize = BYTESIZEOF(bitmap); | |
if (bitsSize !== ((stride * height) * 4)) { | |
/* bytes per word */ | |
interpreterProxy.primitiveFail(); | |
return null; | |
} | |
/* load the word that contains our target */ | |
word = interpreterProxy.fetchLong32ofObject((yVal * stride) + (DIV(xVal, ppW)), bitmap); | |
/* make a mask to isolate the pixel within that word */ | |
mask = SHR(4294967295, (32 - depth)); | |
/* this is the tricky MSB part - we mask the xVal to find how far into the word we need, then add 1 for the pixel we're looking for, then * depth to get the bit shift */ | |
shift = 32 - (((xVal & (ppW - 1)) + 1) * depth); | |
/* shift, mask and dim the lights */ | |
pixel = (SHR(word, shift)) & mask; | |
_return_value = interpreterProxy.positive32BitIntegerFor(pixel); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
interpreterProxy.popthenPush(3, _return_value); | |
return null; | |
} | |
/* Invoke the warpBits primitive. If the destination is the display, then copy it to the screen. */ | |
function primitiveWarpBits() { | |
var rcvr; | |
rcvr = interpreterProxy.stackValue(interpreterProxy.methodArgumentCount()); | |
if (!loadWarpBltFrom(rcvr)) { | |
return interpreterProxy.primitiveFail(); | |
} | |
warpBits(); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
showDisplayBits(); | |
if (interpreterProxy.failed()) { | |
return null; | |
} | |
interpreterProxy.pop(interpreterProxy.methodArgumentCount()); | |
} | |
/* Query the dimension of an OS surface. | |
This method is provided so that in case the inst vars of the | |
source form are broken, *actual* values of the OS surface | |
can be obtained. This might, for instance, happen if the user | |
resizes the main window. | |
Note: Moved to a separate function for better inlining of the caller. */ | |
function queryDestSurface(handle) { | |
if (!querySurfaceFn) { | |
if (!loadSurfacePlugin()) { | |
return false; | |
} | |
} | |
return querySurfaceFn(handle, function(w, h, d, m){destWidth = w; destHeight = h; destDepth = d; destMSB = m; }); | |
} | |
/* Query the dimension of an OS surface. | |
This method is provided so that in case the inst vars of the | |
source form are broken, *actual* values of the OS surface | |
can be obtained. This might, for instance, happen if the user | |
resizes the main window. | |
Note: Moved to a separate function for better inlining of the caller. */ | |
function querySourceSurface(handle) { | |
if (!querySurfaceFn) { | |
if (!loadSurfacePlugin()) { | |
return false; | |
} | |
} | |
return querySurfaceFn(handle, function(w, h, d, m){sourceWidth = w; sourceHeight = h; sourceDepth = d; sourceMSB = m; }); | |
} | |
function rgbAddwith(sourceWord, destinationWord) { | |
var carryOverflowMask; | |
var componentMask; | |
if (destDepth < 16) { | |
/* Add each pixel separately */ | |
componentMask = (SHL(1, destDepth)) - 1; | |
carryOverflowMask = SHL((DIV(4294967295, componentMask)), (destDepth - 1)); | |
return partitionedAddtonBitscomponentMaskcarryOverflowMask(sourceWord, destinationWord, destDepth, componentMask, carryOverflowMask); | |
} | |
if (destDepth === 16) { | |
/* Add RGB components of each pixel separately */ | |
componentMask = 31; | |
carryOverflowMask = 1108361744; | |
return partitionedAddtonBitscomponentMaskcarryOverflowMask(sourceWord & 2147450879, destinationWord & 2147450879, 5, componentMask, carryOverflowMask); | |
} else { | |
/* Add RGBA components of the pixel separately */ | |
componentMask = 255; | |
carryOverflowMask = 2155905152; | |
return partitionedAddtonBitscomponentMaskcarryOverflowMask(sourceWord, destinationWord, 8, componentMask, carryOverflowMask); | |
} | |
} | |
/* This version assumes | |
combinationRule = 41 | |
sourcePixSize = 32 | |
destPixSize = 16 | |
sourceForm ~= destForm. | |
*/ | |
/* This particular method should be optimized in itself */ | |
function rgbComponentAlpha16() { | |
var ditherBase; | |
var ditherThreshold; | |
var srcShift; | |
var sourceWord; | |
var srcIndex; | |
var deltaX; | |
var dstIndex; | |
var srcAlpha; | |
var dstMask; | |
var deltaY; | |
var srcY; | |
var destWord; | |
var dstY; | |
var ditherIndex; | |
/* So we can pre-decrement */ | |
deltaY = bbH + 1; | |
srcY = sy; | |
dstY = dy; | |
srcShift = (dx & 1) * 16; | |
if (destMSB) { | |
srcShift = 16 - srcShift; | |
} | |
/* This is the outer loop */ | |
mask1 = SHL(65535, (16 - srcShift)); | |
while (((--deltaY)) !== 0) { | |
srcIndex = ((srcY * sourcePitch)) + (sx * 4); | |
dstIndex = ((dstY * destPitch)) + ((dx >> 1) * 4); | |
ditherBase = (dstY & 3) * 4; | |
/* For pre-increment */ | |
ditherIndex = (sx & 3) - 1; | |
/* So we can pre-decrement */ | |
deltaX = bbW + 1; | |
dstMask = mask1; | |
if (dstMask === 65535) { | |
srcShift = 16; | |
} else { | |
srcShift = 0; | |
} | |
while (((--deltaX)) !== 0) { | |
ditherThreshold = ditherMatrix4x4[ditherBase + ((ditherIndex = (ditherIndex + 1) & 3))]; | |
sourceWord = sourceBits[srcIndex >>> 2]; | |
srcAlpha = sourceWord & 16777215; | |
if (srcAlpha !== 0) { | |
/* 0 < srcAlpha */ | |
/* If we have to mix colors then just copy a single word */ | |
destWord = destBits[dstIndex >>> 2]; | |
destWord = destWord & ~dstMask; | |
/* Expand from 16 to 32 bit by adding zero bits */ | |
destWord = SHR(destWord, srcShift); | |
/* Mix colors */ | |
destWord = (((destWord & 31744) << 9) | ((destWord & 992) << 6)) | (((destWord & 31) << 3) | 4278190080); | |
/* And dither */ | |
sourceWord = rgbComponentAlpha32with(sourceWord, destWord); | |
sourceWord = dither32To16threshold(sourceWord, ditherThreshold); | |
if (sourceWord === 0) { | |
sourceWord = SHL(1, srcShift); | |
} else { | |
sourceWord = SHL(sourceWord, srcShift); | |
} | |
dstLongAtputmask(dstIndex, sourceWord, dstMask); | |
} | |
srcIndex += 4; | |
if (destMSB) { | |
if (srcShift === 0) { | |
dstIndex += 4; | |
} | |
} else { | |
if (srcShift !== 0) { | |
dstIndex += 4; | |
} | |
} | |
/* Toggle between 0 and 16 */ | |
srcShift = srcShift ^ 16; | |
dstMask = ~dstMask; | |
} | |
++srcY; | |
++dstY; | |
} | |
} | |
/* This version assumes | |
combinationRule = 41 | |
sourcePixSize = destPixSize = 32 | |
sourceForm ~= destForm. | |
Note: The inner loop has been optimized for dealing | |
with the special case of aR = aG = aB = 0 | |
*/ | |
function rgbComponentAlpha32() { | |
var sourceWord; | |
var srcIndex; | |
var deltaX; | |
var dstIndex; | |
var srcAlpha; | |
var deltaY; | |
var srcY; | |
var destWord; | |
var dstY; | |
/* This particular method should be optimized in itself */ | |
/* Give the compile a couple of hints */ | |
/* The following should be declared as pointers so the compiler will | |
notice that they're used for accessing memory locations | |
(good to know on an Intel architecture) but then the increments | |
would be different between ST code and C code so must hope the | |
compiler notices what happens (MS Visual C does) */ | |
/* So we can pre-decrement */ | |
deltaY = bbH + 1; | |
srcY = sy; | |
/* This is the outer loop */ | |
dstY = dy; | |
while (((--deltaY)) !== 0) { | |
srcIndex = ((srcY * sourcePitch)) + (sx * 4); | |
dstIndex = ((dstY * destPitch)) + (dx * 4); | |
/* So we can pre-decrement */ | |
/* This is the inner loop */ | |
deltaX = bbW + 1; | |
while (((--deltaX)) !== 0) { | |
sourceWord = sourceBits[srcIndex >>> 2]; | |
srcAlpha = sourceWord & 16777215; | |
if (srcAlpha === 0) { | |
srcIndex += 4; | |
/* Now skip as many words as possible, */ | |
dstIndex += 4; | |
while ((((--deltaX)) !== 0) && ((((sourceWord = sourceBits[srcIndex >>> 2])) & 16777215) === 0)) { | |
srcIndex += 4; | |
dstIndex += 4; | |
} | |
++deltaX; | |
} else { | |
/* 0 < srcAlpha */ | |
/* If we have to mix colors then just copy a single word */ | |
destWord = destBits[dstIndex >>> 2]; | |
destWord = rgbComponentAlpha32with(sourceWord, destWord); | |
destBits[dstIndex >>> 2] = destWord; | |
srcIndex += 4; | |
dstIndex += 4; | |
} | |
} | |
++srcY; | |
++dstY; | |
} | |
} | |
/* | |
componentAlphaModeColor is the color, | |
sourceWord contains an alpha value for each component of RGB | |
each of which is encoded as0 meaning 0.0 and 255 meaning 1.0 . | |
the rule is... | |
color = componentAlphaModeColor. | |
colorAlpha = componentAlphaModeAlpha. | |
mask = sourceWord. | |
dst.A = colorAlpha + (1 - colorAlpha) * dst.A | |
dst.R = color.R * mask.R * colorAlpha + (1 - (mask.R * colorAlpha)) * dst.R | |
dst.G = color.G * mask.G * colorAlpha + (1 - (mask.G* colorAlpha)) * dst.G | |
dst.B = color.B * mask.B * colorAlpha + (1 - (mask.B* colorAlpha)) * dst.B | |
*/ | |
/* Do NOT inline this into optimized loops */ | |
function rgbComponentAlpha32with(sourceWord, destinationWord) { | |
var g; | |
var srcColor; | |
var aG; | |
var d; | |
var a; | |
var aA; | |
var aR; | |
var dstMask; | |
var srcAlpha; | |
var r; | |
var b; | |
var aB; | |
var alpha; | |
var answer; | |
var s; | |
alpha = sourceWord; | |
if (alpha === 0) { | |
return destinationWord; | |
} | |
srcColor = componentAlphaModeColor; | |
srcAlpha = componentAlphaModeAlpha & 255; | |
aB = alpha & 255; | |
alpha = alpha >>> 8; | |
aG = alpha & 255; | |
alpha = alpha >>> 8; | |
aR = alpha & 255; | |
alpha = alpha >>> 8; | |
aA = alpha & 255; | |
if (srcAlpha !== 255) { | |
aA = (aA * srcAlpha) >>> 8; | |
aR = (aR * srcAlpha) >>> 8; | |
aG = (aG * srcAlpha) >>> 8; | |
aB = (aB * srcAlpha) >>> 8; | |
} | |
dstMask = destinationWord; | |
d = dstMask & 255; | |
s = srcColor & 255; | |
if (!!ungammaLookupTable) { | |
d = ungammaLookupTable[d]; | |
s = ungammaLookupTable[s]; | |
} | |
b = ((d * (255 - aB)) >>> 8) + ((s * aB) >>> 8); | |
if (b > 255) { | |
b = 255; | |
} | |
if (!!gammaLookupTable) { | |
b = gammaLookupTable[b]; | |
} | |
dstMask = dstMask >>> 8; | |
srcColor = srcColor >>> 8; | |
d = dstMask & 255; | |
s = srcColor & 255; | |
if (!!ungammaLookupTable) { | |
d = ungammaLookupTable[d]; | |
s = ungammaLookupTable[s]; | |
} | |
g = ((d * (255 - aG)) >>> 8) + ((s * aG) >>> 8); | |
if (g > 255) { | |
g = 255; | |
} | |
if (!!gammaLookupTable) { | |
g = gammaLookupTable[g]; | |
} | |
dstMask = dstMask >>> 8; | |
srcColor = srcColor >>> 8; | |
d = dstMask & 255; | |
s = srcColor & 255; | |
if (!!ungammaLookupTable) { | |
d = ungammaLookupTable[d]; | |
s = ungammaLookupTable[s]; | |
} | |
r = ((d * (255 - aR)) >>> 8) + ((s * aR) >>> 8); | |
if (r > 255) { | |
r = 255; | |
} | |
if (!!gammaLookupTable) { | |
r = gammaLookupTable[r]; | |
} | |
dstMask = dstMask >>> 8; | |
srcColor = srcColor >>> 8; | |
/* no need to gamma correct alpha value ? */ | |
a = (((dstMask & 255) * (255 - aA)) >>> 8) + aA; | |
if (a > 255) { | |
a = 255; | |
} | |
answer = (((((a << 8) + r) << 8) + g) << 8) + b; | |
return answer; | |
} | |
/* This version assumes | |
combinationRule = 41 | |
sourcePixSize = 32 | |
destPixSize = 8 | |
sourceForm ~= destForm. | |
Note: This is not real blending since we don't have the source colors available. | |
*/ | |
function rgbComponentAlpha8() { | |
var srcShift; | |
var sourceWord; | |
var srcIndex; | |
var deltaX; | |
var mappingTable; | |
var dstIndex; | |
var adjust; | |
var mapperFlags; | |
var srcAlpha; | |
var dstMask; | |
var deltaY; | |
var srcY; | |
var destWord; | |
var dstY; | |
/* This particular method should be optimized in itself */ | |
mappingTable = default8To32Table(); | |
mapperFlags = cmFlags & ~ColorMapNewStyle; | |
/* So we can pre-decrement */ | |
deltaY = bbH + 1; | |
srcY = sy; | |
dstY = dy; | |
mask1 = (dx & 3) * 8; | |
if (destMSB) { | |
mask1 = 24 - mask1; | |
} | |
mask2 = AllOnes ^ (SHL(255, mask1)); | |
if ((dx & 1) === 0) { | |
adjust = 0; | |
} else { | |
adjust = 522133279; | |
} | |
if ((dy & 1) === 0) { | |
adjust = adjust ^ 522133279; | |
} | |
while (((--deltaY)) !== 0) { | |
adjust = adjust ^ 522133279; | |
srcIndex = ((srcY * sourcePitch)) + (sx * 4); | |
dstIndex = ((dstY * destPitch)) + ((dx >> 2) * 4); | |
/* So we can pre-decrement */ | |
deltaX = bbW + 1; | |
srcShift = mask1; | |
/* This is the inner loop */ | |
dstMask = mask2; | |
while (((--deltaX)) !== 0) { | |
sourceWord = (sourceBits[srcIndex >>> 2] & ~adjust) + adjust; | |
/* set srcAlpha to the average of the 3 separate aR,Ag,AB values */ | |
srcAlpha = sourceWord & 16777215; | |
srcAlpha = DIV((((srcAlpha >>> 16) + ((srcAlpha >>> 8) & 255)) + (srcAlpha & 255)), 3); | |
if (srcAlpha > 31) { | |
/* Everything below 31 is transparent */ | |
if (srcAlpha > 224) { | |
/* treat everything above 224 as opaque */ | |
sourceWord = 4294967295; | |
} | |
destWord = destBits[dstIndex >>> 2]; | |
destWord = destWord & ~dstMask; | |
destWord = SHR(destWord, srcShift); | |
destWord = mappingTable[destWord]; | |
sourceWord = rgbComponentAlpha32with(sourceWord, destWord); | |
sourceWord = mapPixelflags(sourceWord, mapperFlags); | |
/* Store back */ | |
sourceWord = SHL(sourceWord, srcShift); | |
dstLongAtputmask(dstIndex, sourceWord, dstMask); | |
} | |
srcIndex += 4; | |
if (destMSB) { | |
if (srcShift === 0) { | |
dstIndex += 4; | |
srcShift = 24; | |
dstMask = 16777215; | |
} else { | |
srcShift -= 8; | |
dstMask = (dstMask >>> 8) | 4278190080; | |
} | |
} else { | |
if (srcShift === 32) { | |
dstIndex += 4; | |
srcShift = 0; | |
dstMask = 4294967040; | |
} else { | |
srcShift += 8; | |
dstMask = (dstMask << 8) | 255; | |
} | |
} | |
adjust = adjust ^ 522133279; | |
} | |
++srcY; | |
++dstY; | |
} | |
} | |
/* | |
componentAlphaModeColor is the color, | |
sourceWord contains an alpha value for each component of RGB | |
each of which is encoded as0 meaning 0.0 and 255 meaning 1.0 . | |
the rule is... | |
color = componentAlphaModeColor. | |
colorAlpha = componentAlphaModeAlpha. | |
mask = sourceWord. | |
dst.A = colorAlpha + (1 - colorAlpha) * dst.A | |
dst.R = color.R * mask.R * colorAlpha + (1 - (mask.R * colorAlpha)) * dst.R | |
dst.G = color.G * mask.G * colorAlpha + (1 - (mask.G* colorAlpha)) * dst.G | |
dst.B = color.B * mask.B * colorAlpha + (1 - (mask.B* colorAlpha)) * dst.B | |
*/ | |
/* Do NOT inline this into optimized loops */ | |
function rgbComponentAlphawith(sourceWord, destinationWord) { | |
var alpha; | |
alpha = sourceWord; | |
if (alpha === 0) { | |
return destinationWord; | |
} | |
return partitionedRgbComponentAlphadestnBitsnPartitions(sourceWord, destinationWord, destDepth, destPPW); | |
} | |
/* Subract the pixels in the source and destination, color by color, | |
and return the sum of the absolute value of all the differences. | |
For non-rgb, return the number of differing pixels. */ | |
function rgbDiffwith(sourceWord, destinationWord) { | |
var sourcePixVal; | |
var bitsPerColor; | |
var diff; | |
var sourceShifted; | |
var pixMask; | |
var rgbMask; | |
var destShifted; | |
var i; | |
var maskShifted; | |
var destPixVal; | |
pixMask = maskTable[destDepth]; | |
if (destDepth === 16) { | |
bitsPerColor = 5; | |
rgbMask = 31; | |
} else { | |
bitsPerColor = 8; | |
rgbMask = 255; | |
} | |
maskShifted = destMask; | |
destShifted = destinationWord; | |
sourceShifted = sourceWord; | |
for (i = 1; i <= destPPW; i++) { | |
if ((maskShifted & pixMask) > 0) { | |
/* Only tally pixels within the destination rectangle */ | |
destPixVal = destShifted & pixMask; | |
sourcePixVal = sourceShifted & pixMask; | |
if (destDepth < 16) { | |
if (sourcePixVal === destPixVal) { | |
diff = 0; | |
} else { | |
diff = 1; | |
} | |
} else { | |
diff = partitionedSubfromnBitsnPartitions(sourcePixVal, destPixVal, bitsPerColor, 3); | |
diff = ((diff & rgbMask) + ((SHR(diff, bitsPerColor)) & rgbMask)) + ((SHR((SHR(diff, bitsPerColor)), bitsPerColor)) & rgbMask); | |
} | |
bitCount += diff; | |
} | |
maskShifted = SHR(maskShifted, destDepth); | |
sourceShifted = SHR(sourceShifted, destDepth); | |
destShifted = SHR(destShifted, destDepth); | |
} | |
return destinationWord; | |
} | |
/* Convert the given 16bit pixel value to a 32bit RGBA value. | |
Note: This method is intended to deal with different source formats. */ | |
function rgbMap16To32(sourcePixel) { | |
return (((sourcePixel & 31) << 3) | ((sourcePixel & 992) << 6)) | ((sourcePixel & 31744) << 9); | |
} | |
/* Convert the given 32bit pixel value to a 32bit RGBA value. | |
Note: This method is intended to deal with different source formats. */ | |
function rgbMap32To32(sourcePixel) { | |
return sourcePixel; | |
} | |
/* Convert the given pixel value with nBitsIn bits for each color component to a pixel value with nBitsOut bits for each color component. Typical values for nBitsIn/nBitsOut are 3, 5, or 8. */ | |
function rgbMapfromto(sourcePixel, nBitsIn, nBitsOut) { | |
var d; | |
var destPix; | |
var srcPix; | |
var mask; | |
if (((d = nBitsOut - nBitsIn)) > 0) { | |
/* Expand to more bits by zero-fill */ | |
/* Transfer mask */ | |
mask = (SHL(1, nBitsIn)) - 1; | |
srcPix = SHL(sourcePixel, d); | |
mask = SHL(mask, d); | |
destPix = srcPix & mask; | |
mask = SHL(mask, nBitsOut); | |
srcPix = SHL(srcPix, d); | |
return (destPix + (srcPix & mask)) + ((SHL(srcPix, d)) & (SHL(mask, nBitsOut))); | |
} else { | |
/* Compress to fewer bits by truncation */ | |
if (d === 0) { | |
if (nBitsIn === 5) { | |
/* Sometimes called with 16 bits, though pixel is 15, | |
but we must never return more than 15. */ | |
return sourcePixel & 32767; | |
} | |
if (nBitsIn === 8) { | |
/* Sometimes called with 32 bits, though pixel is 24, | |
but we must never return more than 24. */ | |
return sourcePixel & 16777215; | |
} | |
return sourcePixel; | |
} | |
if (sourcePixel === 0) { | |
return sourcePixel; | |
} | |
d = nBitsIn - nBitsOut; | |
/* Transfer mask */ | |
mask = (SHL(1, nBitsOut)) - 1; | |
srcPix = SHR(sourcePixel, d); | |
destPix = srcPix & mask; | |
mask = SHL(mask, nBitsOut); | |
srcPix = SHR(srcPix, d); | |
destPix = (destPix + (srcPix & mask)) + ((SHR(srcPix, d)) & (SHL(mask, nBitsOut))); | |
if (destPix === 0) { | |
return 1; | |
} | |
return destPix; | |
} | |
} | |
/* Perform the RGBA conversion for the given source pixel */ | |
function rgbMapPixelflags(sourcePixel, mapperFlags) { | |
var val; | |
val = SHIFT((sourcePixel & cmMaskTable[0]), cmShiftTable[0]); | |
val = val | (SHIFT((sourcePixel & cmMaskTable[1]), cmShiftTable[1])); | |
val = val | (SHIFT((sourcePixel & cmMaskTable[2]), cmShiftTable[2])); | |
return val | (SHIFT((sourcePixel & cmMaskTable[3]), cmShiftTable[3])); | |
} | |
function rgbMaxwith(sourceWord, destinationWord) { | |
if (destDepth < 16) { | |
/* Max each pixel separately */ | |
return partitionedMaxwithnBitsnPartitions(sourceWord, destinationWord, destDepth, destPPW); | |
} | |
if (destDepth === 16) { | |
/* Max RGB components of each pixel separately */ | |
return partitionedMaxwithnBitsnPartitions(sourceWord, destinationWord, 5, 3) + (partitionedMaxwithnBitsnPartitions(sourceWord >>> 16, destinationWord >>> 16, 5, 3) << 16); | |
} else { | |
/* Max RGBA components of the pixel separately */ | |
return partitionedMaxwithnBitsnPartitions(sourceWord, destinationWord, 8, 4); | |
} | |
} | |
function rgbMinwith(sourceWord, destinationWord) { | |
if (destDepth < 16) { | |
/* Min each pixel separately */ | |
return partitionedMinwithnBitsnPartitions(sourceWord, destinationWord, destDepth, destPPW); | |
} | |
if (destDepth === 16) { | |
/* Min RGB components of each pixel separately */ | |
return partitionedMinwithnBitsnPartitions(sourceWord, destinationWord, 5, 3) + (partitionedMinwithnBitsnPartitions(sourceWord >>> 16, destinationWord >>> 16, 5, 3) << 16); | |
} else { | |
/* Min RGBA components of the pixel separately */ | |
return partitionedMinwithnBitsnPartitions(sourceWord, destinationWord, 8, 4); | |
} | |
} | |
function rgbMinInvertwith(wordToInvert, destinationWord) { | |
var sourceWord; | |
sourceWord = ~wordToInvert; | |
if (destDepth < 16) { | |
/* Min each pixel separately */ | |
return partitionedMinwithnBitsnPartitions(sourceWord, destinationWord, destDepth, destPPW); | |
} | |
if (destDepth === 16) { | |
/* Min RGB components of each pixel separately */ | |
return partitionedMinwithnBitsnPartitions(sourceWord, destinationWord, 5, 3) + (partitionedMinwithnBitsnPartitions(sourceWord >>> 16, destinationWord >>> 16, 5, 3) << 16); | |
} else { | |
/* Min RGBA components of the pixel separately */ | |
return partitionedMinwithnBitsnPartitions(sourceWord, destinationWord, 8, 4); | |
} | |
} | |
function rgbMulwith(sourceWord, destinationWord) { | |
if (destDepth < 16) { | |
/* Mul each pixel separately */ | |
return partitionedMulwithnBitsnPartitions(sourceWord, destinationWord, destDepth, destPPW); | |
} | |
if (destDepth === 16) { | |
/* Mul RGB components of each pixel separately */ | |
return partitionedMulwithnBitsnPartitions(sourceWord, destinationWord, 5, 3) + (partitionedMulwithnBitsnPartitions(sourceWord >>> 16, destinationWord >>> 16, 5, 3) << 16); | |
} else { | |
/* Mul RGBA components of the pixel separately */ | |
return partitionedMulwithnBitsnPartitions(sourceWord, destinationWord, 8, 4); | |
} | |
} | |
function rgbSubwith(sourceWord, destinationWord) { | |
if (destDepth < 16) { | |
/* Sub each pixel separately */ | |
return partitionedSubfromnBitsnPartitions(sourceWord, destinationWord, destDepth, destPPW); | |
} | |
if (destDepth === 16) { | |
/* Sub RGB components of each pixel separately */ | |
return partitionedSubfromnBitsnPartitions(sourceWord, destinationWord, 5, 3) + (partitionedSubfromnBitsnPartitions(sourceWord >>> 16, destinationWord >>> 16, 5, 3) << 16); | |
} else { | |
/* Sub RGBA components of the pixel separately */ | |
return partitionedSubfromnBitsnPartitions(sourceWord, destinationWord, 8, 4); | |
} | |
} | |
/* 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; | |
} | |
/* WARNING: For WarpBlt w/ smoothing the source depth is wrong here! */ | |
function setupColorMasks() { | |
var bits; | |
var targetBits; | |
bits = (targetBits = 0); | |
if (sourceDepth <= 8) { | |
return null; | |
} | |
if (sourceDepth === 16) { | |
bits = 5; | |
} | |
if (sourceDepth === 32) { | |
bits = 8; | |
} | |
if (cmBitsPerColor === 0) { | |
/* Convert to destDepth */ | |
if (destDepth <= 8) { | |
return null; | |
} | |
if (destDepth === 16) { | |
targetBits = 5; | |
} | |
if (destDepth === 32) { | |
targetBits = 8; | |
} | |
} else { | |
targetBits = cmBitsPerColor; | |
} | |
setupColorMasksFromto(bits, targetBits); | |
} | |
/* Setup color masks for converting an incoming RGB pixel value from srcBits to targetBits. */ | |
function setupColorMasksFromto(srcBits, targetBits) { | |
var shifts = [0, 0, 0, 0]; | |
var masks = [0, 0, 0, 0]; | |
var deltaBits; | |
var mask; | |
; | |
deltaBits = targetBits - srcBits; | |
if (deltaBits === 0) { | |
return 0; | |
} | |
if (deltaBits <= 0) { | |
/* Mask for extracting a color part of the source */ | |
mask = (SHL(1, targetBits)) - 1; | |
masks[RedIndex] = (SHL(mask, ((srcBits * 2) - deltaBits))); | |
masks[GreenIndex] = (SHL(mask, (srcBits - deltaBits))); | |
masks[BlueIndex] = (SHL(mask, (0 - deltaBits))); | |
masks[AlphaIndex] = 0; | |
} else { | |
/* Mask for extracting a color part of the source */ | |
mask = (SHL(1, srcBits)) - 1; | |
masks[RedIndex] = (SHL(mask, (srcBits * 2))); | |
masks[GreenIndex] = (SHL(mask, srcBits)); | |
masks[BlueIndex] = mask; | |
} | |
shifts[RedIndex] = (deltaBits * 3); | |
shifts[GreenIndex] = (deltaBits * 2); | |
shifts[BlueIndex] = deltaBits; | |
shifts[AlphaIndex] = 0; | |
cmShiftTable = shifts; | |
cmMaskTable = masks; | |
cmFlags = cmFlags | (ColorMapPresent | ColorMapFixedPart); | |
} | |
function showDisplayBits() { | |
interpreterProxy.showDisplayBitsLeftTopRightBottom(destForm, affectedL, affectedT, affectedR, affectedB); | |
} | |
/* This is only used when source and dest are same depth, | |
ie, when the barrel-shift copy loop is used. */ | |
function sourceSkewAndPointerInit() { | |
var dxLowBits; | |
var sxLowBits; | |
var dWid; | |
var pixPerM1; | |
/* A mask, assuming power of two */ | |
pixPerM1 = destPPW - 1; | |
sxLowBits = sx & pixPerM1; | |
/* check if need to preload buffer | |
(i.e., two words of source needed for first word of destination) */ | |
dxLowBits = dx & pixPerM1; | |
if (hDir > 0) { | |
/* n Bits stored in 1st word of dest */ | |
dWid = Math.min(bbW, (destPPW - dxLowBits)); | |
preload = (sxLowBits + dWid) > pixPerM1; | |
} else { | |
dWid = Math.min(bbW, (dxLowBits + 1)); | |
preload = ((sxLowBits - dWid) + 1) < 0; | |
} | |
if (sourceMSB) { | |
skew = (sxLowBits - dxLowBits) * destDepth; | |
} else { | |
skew = (dxLowBits - sxLowBits) * destDepth; | |
} | |
if (preload) { | |
if (skew < 0) { | |
skew += 32; | |
} else { | |
skew -= 32; | |
} | |
} | |
/* calculate increments from end of 1 line to start of next */ | |
sourceIndex = ((sy * sourcePitch)) + ((DIV(sx, (DIV(32, sourceDepth)))) * 4); | |
sourceDelta = (sourcePitch * vDir) - (4 * (nWords * hDir)); | |
if (preload) { | |
/* Compensate for extra source word fetched */ | |
sourceDelta -= 4 * hDir; | |
} | |
} | |
function sourceWordwith(sourceWord, destinationWord) { | |
return sourceWord; | |
} | |
function subWordwith(sourceWord, destinationWord) { | |
return sourceWord - destinationWord; | |
} | |
/* Tally pixels into the color map. Those tallied are exactly those | |
in the destination rectangle. Note that the source should be | |
specified == destination, in order for the proper color map checks | |
to be performed at setup. */ | |
function tallyIntoMapwith(sourceWord, destinationWord) { | |
var pixMask; | |
var mapIndex; | |
var destShifted; | |
var i; | |
var maskShifted; | |
var pixVal; | |
if ((cmFlags & (ColorMapPresent | ColorMapIndexedPart)) !== (ColorMapPresent | ColorMapIndexedPart)) { | |
return destinationWord; | |
} | |
pixMask = maskTable[destDepth]; | |
destShifted = destinationWord; | |
maskShifted = destMask; | |
for (i = 1; i <= destPPW; i++) { | |
if ((maskShifted & pixMask) !== 0) { | |
/* Only tally pixels within the destination rectangle */ | |
pixVal = destShifted & pixMask; | |
if (destDepth < 16) { | |
mapIndex = pixVal; | |
} else { | |
if (destDepth === 16) { | |
mapIndex = rgbMapfromto(pixVal, 5, cmBitsPerColor); | |
} else { | |
mapIndex = rgbMapfromto(pixVal, 8, cmBitsPerColor); | |
} | |
} | |
tallyMapAtput(mapIndex, tallyMapAt(mapIndex) + 1); | |
} | |
maskShifted = SHR(maskShifted, destDepth); | |
destShifted = SHR(destShifted, destDepth); | |
} | |
return destinationWord; | |
} | |
/* Return the word at position idx from the colorMap */ | |
function tallyMapAt(idx) { | |
return cmLookupTable[idx & cmMask]; | |
} | |
/* Store the word at position idx in the colorMap */ | |
function tallyMapAtput(idx, value) { | |
return cmLookupTable[idx & cmMask] = value; | |
} | |
/* Shortcut for stuff that's being run from the balloon engine. | |
Since we do this at each scan line we should avoid the expensive | |
setup for source and destination. */ | |
/* We need a source. */ | |
function tryCopyingBitsQuickly() { | |
if (noSource) { | |
return false; | |
} | |
if (!((combinationRule === 34) || (combinationRule === 41))) { | |
return false; | |
} | |
if (sourceDepth !== 32) { | |
return false; | |
} | |
if (sourceForm === destForm) { | |
return false; | |
} | |
if (combinationRule === 41) { | |
if (destDepth === 32) { | |
rgbComponentAlpha32(); | |
affectedL = dx; | |
affectedR = dx + bbW; | |
affectedT = dy; | |
affectedB = dy + bbH; | |
return true; | |
} | |
if (destDepth === 16) { | |
rgbComponentAlpha16(); | |
affectedL = dx; | |
affectedR = dx + bbW; | |
affectedT = dy; | |
affectedB = dy + bbH; | |
return true; | |
} | |
if (destDepth === 8) { | |
rgbComponentAlpha8(); | |
affectedL = dx; | |
affectedR = dx + bbW; | |
affectedT = dy; | |
affectedB = dy + bbH; | |
return true; | |
} | |
return false; | |
} | |
if (destDepth < 8) { | |
return false; | |
} | |
if ((destDepth === 8) && ((cmFlags & ColorMapPresent) === 0)) { | |
return false; | |
} | |
if (destDepth === 32) { | |
alphaSourceBlendBits32(); | |
} | |
if (destDepth === 16) { | |
alphaSourceBlendBits16(); | |
} | |
if (destDepth === 8) { | |
alphaSourceBlendBits8(); | |
} | |
affectedL = dx; | |
affectedR = dx + bbW; | |
affectedT = dy; | |
affectedB = dy + bbH; | |
return true; | |
} | |
/* Unlock the bits of any OS surfaces. */ | |
/* See the comment in lockSurfaces. Similar rules apply. That is, the area provided in ioUnlockSurface can be used to determine the dirty region after drawing. If a source is unlocked, then the area will be (0,0,0,0) to indicate that no portion is dirty. */ | |
function unlockSurfaces() { | |
var destHandle; | |
var sourceHandle; | |
var fn; | |
var destLocked; | |
if (hasSurfaceLock) { | |
if (!unlockSurfaceFn) { | |
if (!loadSurfacePlugin()) { | |
return null; | |
} | |
} | |
fn = unlockSurfaceFn; | |
destLocked = false; | |
destHandle = interpreterProxy.fetchPointerofObject(FormBitsIndex, destForm); | |
if (typeof destHandle === "number") { | |
/* The destBits are always assumed to be dirty */ | |
destHandle = destHandle; | |
fn(destHandle, affectedL, affectedT, affectedR-affectedL, affectedB-affectedT); | |
destBits = (destPitch = 0); | |
destLocked = true; | |
} | |
if (!noSource) { | |
sourceHandle = interpreterProxy.fetchPointerofObject(FormBitsIndex, sourceForm); | |
if (typeof sourceHandle === "number") { | |
/* Only unlock sourceHandle if different from destHandle */ | |
sourceHandle = sourceHandle; | |
if (!(destLocked && (sourceHandle === destHandle))) { | |
fn(sourceHandle, 0, 0, 0, 0); | |
} | |
sourceBits = (sourcePitch = 0); | |
} | |
} | |
hasSurfaceLock = false; | |
} | |
} | |
function warpBits() { | |
var ns; | |
ns = noSource; | |
noSource = true; | |
clipRange(); | |
noSource = ns; | |
if (noSource || ((bbW <= 0) || (bbH <= 0))) { | |
/* zero width or height; noop */ | |
affectedL = (affectedR = (affectedT = (affectedB = 0))); | |
return null; | |
} | |
if (!lockSurfaces()) { | |
return interpreterProxy.primitiveFail(); | |
} | |
destMaskAndPointerInit(); | |
warpLoop(); | |
if (hDir > 0) { | |
affectedL = dx; | |
affectedR = dx + bbW; | |
} else { | |
affectedL = (dx - bbW) + 1; | |
affectedR = dx + 1; | |
} | |
if (vDir > 0) { | |
affectedT = dy; | |
affectedB = dy + bbH; | |
} else { | |
affectedT = (dy - bbH) + 1; | |
affectedB = dy + 1; | |
} | |
unlockSurfaces(); | |
} | |
/* This version of the inner loop traverses an arbirary quadrilateral | |
source, thus producing a general affine transformation. */ | |
function warpLoop() { | |
var mapperFlags; | |
var dstShiftLeft; | |
var words; | |
var skewWord; | |
var nSteps; | |
var deltaP43y; | |
var destWord; | |
var startBits; | |
var mergeFnwith; | |
var deltaP43x; | |
var pBy; | |
var i; | |
var yDelta; | |
var halftoneWord; | |
var mergeWord; | |
var pAy; | |
var dstShiftInc; | |
var pBx; | |
var sourceMapOop; | |
var xDelta; | |
var pAx; | |
var deltaP12y; | |
var endBits; | |
var nPix; | |
var deltaP12x; | |
var smoothingCount; | |
mergeFnwith = opTable[combinationRule + 1]; | |
mergeFnwith; | |
if (!(SIZEOF(bitBltOop) >= (BBWarpBase + 12))) { | |
return interpreterProxy.primitiveFail(); | |
} | |
nSteps = height - 1; | |
if (nSteps <= 0) { | |
nSteps = 1; | |
} | |
pAx = fetchIntOrFloatofObject(BBWarpBase, bitBltOop); | |
words = fetchIntOrFloatofObject(BBWarpBase + 3, bitBltOop); | |
deltaP12x = deltaFromtonSteps(pAx, words, nSteps); | |
if (deltaP12x < 0) { | |
pAx = words - (nSteps * deltaP12x); | |
} | |
pAy = fetchIntOrFloatofObject(BBWarpBase + 1, bitBltOop); | |
words = fetchIntOrFloatofObject(BBWarpBase + 4, bitBltOop); | |
deltaP12y = deltaFromtonSteps(pAy, words, nSteps); | |
if (deltaP12y < 0) { | |
pAy = words - (nSteps * deltaP12y); | |
} | |
pBx = fetchIntOrFloatofObject(BBWarpBase + 9, bitBltOop); | |
words = fetchIntOrFloatofObject(BBWarpBase + 6, bitBltOop); | |
deltaP43x = deltaFromtonSteps(pBx, words, nSteps); | |
if (deltaP43x < 0) { | |
pBx = words - (nSteps * deltaP43x); | |
} | |
pBy = fetchIntOrFloatofObject(BBWarpBase + 10, bitBltOop); | |
words = fetchIntOrFloatofObject(BBWarpBase + 7, bitBltOop); | |
deltaP43y = deltaFromtonSteps(pBy, words, nSteps); | |
if (deltaP43y < 0) { | |
pBy = words - (nSteps * deltaP43y); | |
} | |
if (interpreterProxy.failed()) { | |
return false; | |
} | |
if (interpreterProxy.methodArgumentCount() === 2) { | |
smoothingCount = interpreterProxy.stackIntegerValue(1); | |
sourceMapOop = interpreterProxy.stackValue(0); | |
if (sourceMapOop.isNil) { | |
if (sourceDepth < 16) { | |
/* color map is required to smooth non-RGB dest */ | |
return interpreterProxy.primitiveFail(); | |
} | |
} else { | |
if (SIZEOF(sourceMapOop) < (SHL(1, sourceDepth))) { | |
/* sourceMap must be long enough for sourceDepth */ | |
return interpreterProxy.primitiveFail(); | |
} | |
sourceMapOop = sourceMapOop.wordsOrBytes(); | |
} | |
} else { | |
smoothingCount = 1; | |
sourceMapOop = interpreterProxy.nilObject(); | |
} | |
nSteps = width - 1; | |
if (nSteps <= 0) { | |
nSteps = 1; | |
} | |
startBits = destPPW - (dx & (destPPW - 1)); | |
endBits = (((dx + bbW) - 1) & (destPPW - 1)) + 1; | |
if (bbW < startBits) { | |
startBits = bbW; | |
} | |
if (destY < clipY) { | |
/* Advance increments if there was clipping in y */ | |
pAx += (clipY - destY) * deltaP12x; | |
pAy += (clipY - destY) * deltaP12y; | |
pBx += (clipY - destY) * deltaP43x; | |
pBy += (clipY - destY) * deltaP43y; | |
} | |
warpLoopSetup(); | |
if ((smoothingCount > 1) && ((cmFlags & ColorMapNewStyle) === 0)) { | |
if (!cmLookupTable) { | |
if (destDepth === 16) { | |
setupColorMasksFromto(8, 5); | |
} | |
} else { | |
setupColorMasksFromto(8, cmBitsPerColor); | |
} | |
} | |
mapperFlags = cmFlags & ~ColorMapNewStyle; | |
if (destMSB) { | |
dstShiftInc = 0 - destDepth; | |
dstShiftLeft = 32 - destDepth; | |
} else { | |
dstShiftInc = destDepth; | |
dstShiftLeft = 0; | |
} | |
for (i = 1; i <= bbH; i++) { | |
/* here is the vertical loop... */ | |
xDelta = deltaFromtonSteps(pAx, pBx, nSteps); | |
if (xDelta >= 0) { | |
sx = pAx; | |
} else { | |
sx = pBx - (nSteps * xDelta); | |
} | |
yDelta = deltaFromtonSteps(pAy, pBy, nSteps); | |
if (yDelta >= 0) { | |
sy = pAy; | |
} else { | |
sy = pBy - (nSteps * yDelta); | |
} | |
if (destMSB) { | |
dstBitShift = 32 - (((dx & (destPPW - 1)) + 1) * destDepth); | |
} else { | |
dstBitShift = (dx & (destPPW - 1)) * destDepth; | |
} | |
if (destX < clipX) { | |
/* Advance increments if there was clipping in x */ | |
sx += (clipX - destX) * xDelta; | |
sy += (clipX - destX) * yDelta; | |
} | |
if (noHalftone) { | |
halftoneWord = AllOnes; | |
} else { | |
halftoneWord = halftoneAt((dy + i) - 1); | |
} | |
destMask = mask1; | |
/* Here is the inner loop... */ | |
nPix = startBits; | |
words = nWords; | |
do { | |
/* pick up word */ | |
if (smoothingCount === 1) { | |
/* Faster if not smoothing */ | |
skewWord = warpPickSourcePixelsxDeltahyDeltahxDeltavyDeltavdstShiftIncflags(nPix, xDelta, yDelta, deltaP12x, deltaP12y, dstShiftInc, mapperFlags); | |
} else { | |
/* more difficult with smoothing */ | |
skewWord = warpPickSmoothPixelsxDeltahyDeltahxDeltavyDeltavsourceMapsmoothingdstShiftInc(nPix, xDelta, yDelta, deltaP12x, deltaP12y, sourceMapOop, smoothingCount, dstShiftInc); | |
} | |
dstBitShift = dstShiftLeft; | |
if (destMask === AllOnes) { | |
/* avoid read-modify-write */ | |
mergeWord = mergeFnwith(skewWord & halftoneWord, destBits[destIndex >>> 2]); | |
destBits[destIndex >>> 2] = destMask & mergeWord; | |
} else { | |
/* General version using dest masking */ | |
destWord = destBits[destIndex >>> 2]; | |
mergeWord = mergeFnwith(skewWord & halftoneWord, destWord & destMask); | |
destWord = (destMask & mergeWord) | (destWord & ~destMask); | |
destBits[destIndex >>> 2] = destWord; | |
} | |
destIndex += 4; | |
if (words === 2) { | |
/* e.g., is the next word the last word? */ | |
/* set mask for last word in this row */ | |
destMask = mask2; | |
nPix = endBits; | |
} else { | |
/* use fullword mask for inner loop */ | |
destMask = AllOnes; | |
nPix = destPPW; | |
} | |
} while(!(((--words)) === 0)); | |
pAx += deltaP12x; | |
pAy += deltaP12y; | |
pBx += deltaP43x; | |
pBy += deltaP43y; | |
destIndex += destDelta; | |
} | |
} | |
/* Setup values for faster pixel fetching. */ | |
function warpLoopSetup() { | |
var i; | |
var words; | |
/* warpSrcShift = log2(sourceDepth) */ | |
warpSrcShift = 0; | |
/* recycle temp */ | |
words = sourceDepth; | |
while (!(words === 1)) { | |
++warpSrcShift; | |
words = words >>> 1; | |
} | |
/* warpAlignShift: Shift for aligning x position to word boundary */ | |
warpSrcMask = maskTable[sourceDepth]; | |
/* warpAlignMask: Mask for extracting the pixel position from an x position */ | |
warpAlignShift = 5 - warpSrcShift; | |
/* Setup the lookup table for source bit shifts */ | |
/* warpBitShiftTable: given an sub-word x value what's the bit shift? */ | |
warpAlignMask = (SHL(1, warpAlignShift)) - 1; | |
for (i = 0; i <= warpAlignMask; i++) { | |
if (sourceMSB) { | |
warpBitShiftTable[i] = (32 - (SHL((i + 1), warpSrcShift))); | |
} else { | |
warpBitShiftTable[i] = (SHL(i, warpSrcShift)); | |
} | |
} | |
} | |
/* Pick n (sub-) pixels from the source form, mapped by sourceMap, | |
average the RGB values, map by colorMap and return the new word. | |
This version is only called from WarpBlt with smoothingCount > 1 */ | |
function warpPickSmoothPixelsxDeltahyDeltahxDeltavyDeltavsourceMapsmoothingdstShiftInc(nPixels, xDeltah, yDeltah, xDeltav, yDeltav, sourceMap, n, dstShiftInc) { | |
var k; | |
var destWord; | |
var xdh; | |
var j; | |
var ydh; | |
var i; | |
var xdv; | |
var dstMask; | |
var ydv; | |
var rgb; | |
var y; | |
var b; | |
var yy; | |
var g; | |
var x; | |
var a; | |
var r; | |
var nPix; | |
var xx; | |
/* nope - too much stuff in here */ | |
dstMask = maskTable[destDepth]; | |
destWord = 0; | |
if (n === 2) { | |
/* Try avoiding divides for most common n (divide by 2 is generated as shift) */ | |
xdh = xDeltah >> 1; | |
ydh = yDeltah >> 1; | |
xdv = xDeltav >> 1; | |
ydv = yDeltav >> 1; | |
} else { | |
xdh = DIV(xDeltah, n); | |
ydh = DIV(yDeltah, n); | |
xdv = DIV(xDeltav, n); | |
ydv = DIV(yDeltav, n); | |
} | |
i = nPixels; | |
do { | |
x = sx; | |
y = sy; | |
/* Pick and average n*n subpixels */ | |
a = (r = (g = (b = 0))); | |
/* actual number of pixels (not clipped and not transparent) */ | |
nPix = 0; | |
j = n; | |
do { | |
xx = x; | |
yy = y; | |
k = n; | |
do { | |
/* get a single subpixel */ | |
rgb = pickWarpPixelAtXy(xx, yy); | |
if (!((combinationRule === 25) && (rgb === 0))) { | |
/* If not clipped and not transparent, then tally rgb values */ | |
++nPix; | |
if (sourceDepth < 16) { | |
/* Get RGBA values from sourcemap table */ | |
rgb = sourceMap[rgb]; | |
} else { | |
/* Already in RGB format */ | |
if (sourceDepth === 16) { | |
rgb = rgbMap16To32(rgb); | |
} else { | |
rgb = rgbMap32To32(rgb); | |
} | |
} | |
b += rgb & 255; | |
g += (rgb >>> 8) & 255; | |
r += (rgb >>> 16) & 255; | |
a += rgb >>> 24; | |
} | |
xx += xdh; | |
yy += ydh; | |
} while(!(((--k)) === 0)); | |
x += xdv; | |
y += ydv; | |
} while(!(((--j)) === 0)); | |
if ((nPix === 0) || ((combinationRule === 25) && (nPix < ((n * n) >> 1)))) { | |
/* All pixels were 0, or most were transparent */ | |
rgb = 0; | |
} else { | |
/* normalize rgba sums */ | |
if (nPix === 4) { | |
/* Try to avoid divides for most common n */ | |
r = r >>> 2; | |
g = g >>> 2; | |
b = b >>> 2; | |
a = a >>> 2; | |
} else { | |
r = DIV(r, nPix); | |
g = DIV(g, nPix); | |
b = DIV(b, nPix); | |
a = DIV(a, nPix); | |
} | |
/* map the pixel */ | |
rgb = (((a << 24) + (r << 16)) + (g << 8)) + b; | |
if (rgb === 0) { | |
/* only generate zero if pixel is really transparent */ | |
if ((((r + g) + b) + a) > 0) { | |
rgb = 1; | |
} | |
} | |
rgb = mapPixelflags(rgb, cmFlags); | |
} | |
destWord = destWord | (SHL((rgb & dstMask), dstBitShift)); | |
dstBitShift += dstShiftInc; | |
sx += xDeltah; | |
sy += yDeltah; | |
} while(!(((--i)) === 0)); | |
return destWord; | |
} | |
/* Pick n pixels from the source form, | |
map by colorMap and return aligned by dstBitShift. | |
This version is only called from WarpBlt with smoothingCount = 1 */ | |
function warpPickSourcePixelsxDeltahyDeltahxDeltavyDeltavdstShiftIncflags(nPixels, xDeltah, yDeltah, xDeltav, yDeltav, dstShiftInc, mapperFlags) { | |
var sourcePix; | |
var nPix; | |
var destPix; | |
var dstMask; | |
var destWord; | |
/* Yepp - this should go into warpLoop */ | |
dstMask = maskTable[destDepth]; | |
destWord = 0; | |
nPix = nPixels; | |
if (mapperFlags === (ColorMapPresent | ColorMapIndexedPart)) { | |
/* a little optimization for (pretty crucial) blits using indexed lookups only */ | |
/* grab, colormap and mix in pixel */ | |
do { | |
sourcePix = pickWarpPixelAtXy(sx, sy); | |
destPix = cmLookupTable[sourcePix & cmMask]; | |
destWord = destWord | (SHL((destPix & dstMask), dstBitShift)); | |
dstBitShift += dstShiftInc; | |
sx += xDeltah; | |
sy += yDeltah; | |
} while(!(((--nPix)) === 0)); | |
} else { | |
/* grab, colormap and mix in pixel */ | |
do { | |
sourcePix = pickWarpPixelAtXy(sx, sy); | |
destPix = mapPixelflags(sourcePix, mapperFlags); | |
destWord = destWord | (SHL((destPix & dstMask), dstBitShift)); | |
dstBitShift += dstShiftInc; | |
sx += xDeltah; | |
sy += yDeltah; | |
} while(!(((--nPix)) === 0)); | |
} | |
return destWord; | |
} | |
function registerPlugin() { | |
if (typeof Squeak === "object" && Squeak.registerExternalModule) { | |
Squeak.registerExternalModule("BitBltPlugin", { | |
primitiveCopyBits: primitiveCopyBits, | |
copyBits: copyBits, | |
moduleUnloaded: moduleUnloaded, | |
primitiveDrawLoop: primitiveDrawLoop, | |
primitiveDisplayString: primitiveDisplayString, | |
initialiseModule: initialiseModule, | |
loadBitBltFrom: loadBitBltFrom, | |
setInterpreter: setInterpreter, | |
primitiveWarpBits: primitiveWarpBits, | |
getModuleName: getModuleName, | |
primitivePixelValueAt: primitivePixelValueAt, | |
copyBitsFromtoat: copyBitsFromtoat, | |
}); | |
} else self.setTimeout(registerPlugin, 100); | |
} | |
registerPlugin(); | |
})(); // Register module/plugin | |