scratch0-5 / plugins /BitBltPlugin.js
soiz1's picture
Upload folder using huggingface_hub
8f3f8db verified
/* 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() {
"use strict";
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