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