[pypy-commit] lang-smalltalk rbitblt: merge upstream

timfel noreply at buildbot.pypy.org
Fri Jan 3 16:45:41 CET 2014


Author: Tim Felgentreff <timfelgentreff at gmail.com>
Branch: rbitblt
Changeset: r548:2bd14f5f88a1
Date: 2014-01-03 09:50 +0100
http://bitbucket.org/pypy/lang-smalltalk/changeset/2bd14f5f88a1/

Log:	merge upstream

diff --git a/spyvm/model.py b/spyvm/model.py
--- a/spyvm/model.py
+++ b/spyvm/model.py
@@ -599,14 +599,6 @@
         from spyvm.shadow import ObserveeShadow
         return self.as_special_get_shadow(space, ObserveeShadow)
 
-    def as_bitblt_get_shadow(self, space):
-        from spyvm.shadow import BitBltShadow
-        return self.as_special_get_shadow(space, BitBltShadow)
-
-    def as_form_get_shadow(self, space):
-        from spyvm.shadow import FormShadow
-        return self.as_special_get_shadow(space, FormShadow)
-
     def has_shadow(self):
         return self._shadow is not None
 
diff --git a/spyvm/plugins/bitblt.py b/spyvm/plugins/bitblt.py
new file mode 100644
--- /dev/null
+++ b/spyvm/plugins/bitblt.py
@@ -0,0 +1,633 @@
+from spyvm import model
+from spyvm.error import PrimitiveFailedError
+from spyvm.shadow import AbstractCachingShadow
+from spyvm.plugins.plugin import Plugin
+
+from rpython.rlib import rarithmetic, jit
+
+
+BitBltPlugin = Plugin()
+
+ at BitBltPlugin.expose_primitive(unwrap_spec=[object], clean_stack=False)
+def primitiveCopyBits(interp, s_frame, w_rcvr):
+    from spyvm.interpreter import Return
+    if not isinstance(w_rcvr, model.W_PointersObject) or w_rcvr.size() < 15:
+        raise PrimitiveFailedError
+
+    # only allow combinationRules 0-41
+    combinationRule = interp.space.unwrap_positive_32bit_int(w_rcvr.fetch(interp.space, 3))
+    if combinationRule > 41:
+        raise PrimitiveFailedError
+
+    space = interp.space
+    s_bitblt = w_rcvr.as_special_get_shadow(space, BitBltShadow)
+    s_bitblt.copyBits()
+
+    w_dest_form = w_rcvr.fetch(space, 0)
+    if (combinationRule == 22 or combinationRule == 32):
+        s_frame.pop() # pops the next value under BitBlt
+        s_frame.push(interp.space.wrap_int(s_bitblt.bitCount))
+    elif w_dest_form.is_same_object(space.objtable['w_display']):
+        w_bitmap = w_dest_form.fetch(space, 0)
+        assert isinstance(w_bitmap, model.W_DisplayBitmap)
+        w_bitmap.flush_to_screen()
+    return w_rcvr
+
+    def as_bitblt_get_shadow(self, space):
+        return
+
+
+class BitBltShadow(AbstractCachingShadow):
+    WordSize = 32
+    MaskTable = [rarithmetic.r_uint(0)]
+    for i in xrange(WordSize):
+        MaskTable.append(rarithmetic.r_uint((2 ** (i + 1)) - 1))
+    AllOnes = rarithmetic.r_uint(0xFFFFFFFF)
+
+    def sync_cache(self):
+        self.loadBitBlt()
+
+    def intOrIfNil(self, w_int, i):
+        if w_int is self.space.w_nil:
+            return i
+        else:
+            return self.space.unwrap_int(w_int)
+
+    def loadForm(self, w_form):
+        try:
+            if not isinstance(w_form, model.W_PointersObject):
+                raise PrimitiveFailedError()
+            s_form = w_form.as_special_get_shadow(self.space, FormShadow)
+            if not isinstance(s_form, FormShadow):
+                raise PrimitiveFailedError()
+            return s_form
+        except PrimitiveFailedError, e:
+            w_self = self.w_self()
+            assert isinstance(w_self, model.W_PointersObject)
+            w_self._shadow = None
+            raise e
+
+    def loadHalftone(self, w_halftone_form):
+        if w_halftone_form is self.space.w_nil:
+            return None
+        elif isinstance(w_halftone_form, model.W_WordsObject):
+            # Already a bitmap
+            return w_halftone_form.words
+        else:
+            assert isinstance(w_halftone_form, model.W_PointersObject)
+            w_bits = w_halftone_form.as_special_get_shadow(self.space, FormShadow).w_bits
+            assert isinstance(w_bits, model.W_WordsObject)
+            return w_bits.words
+
+    def loadColorMap(self, w_color_map):
+        if isinstance(w_color_map, model.W_WordsObject):
+            self.w_cmLookupTable = w_color_map
+            self.cmMask = self.w_cmLookupTable.size() - 1
+        else:
+            self.w_cmLookupTable = None
+
+    def loadBitBlt(self):
+        self.success = True
+        self.w_destForm = self.fetch(0)
+        self.dest = self.loadForm(self.w_destForm)
+        self.w_sourceForm = self.fetch(1)
+        if self.w_sourceForm is not self.space.w_nil:
+            self.source = self.loadForm(self.w_sourceForm)
+        else:
+            self.source = None
+        self.halftone = self.loadHalftone(self.fetch(2))
+        self.combinationRule = self.space.unwrap_int(self.fetch(3))
+        self.destX = self.intOrIfNil(self.fetch(4), 0)
+        self.destY = self.intOrIfNil(self.fetch(5), 0)
+        self.width = self.intOrIfNil(self.fetch(6), self.dest.width)
+        self.height = self.intOrIfNil(self.fetch(7), self.dest.height)
+        self.clipX = self.intOrIfNil(self.fetch(10), 0)
+        self.clipY = self.intOrIfNil(self.fetch(11), 0)
+        self.clipW = self.intOrIfNil(self.fetch(12), self.width)
+        self.clipH = self.intOrIfNil(self.fetch(13), self.height)
+        if not self.source:
+            self.sourceX = 0
+            self.sourceY = 0
+        else:
+            self.loadColorMap(self.fetch(14))
+            self.sourceX = self.intOrIfNil(self.fetch(8), 0)
+            self.sourceY = self.intOrIfNil(self.fetch(9), 0)
+
+    def copyBits(self):
+        self.bitCount = 0
+        self.clipRange()
+        if (self.bbW <= 0 or self.bbH <= 0):
+            return
+        self.destMaskAndPointerInit()
+        if not self.source:
+            self.copyLoopNoSource()
+        else:
+            self.checkSourceOverlap()
+            if self.source.depth != self.dest.depth:
+                self.copyLoopPixMap()
+            else:
+                self.sourceSkewAndPointerInit()
+                self.copyLoop()
+
+    def checkSourceOverlap(self):
+        if (self.w_sourceForm is self.w_destForm and self.dy >= self.sy):
+            if (self.dy > self.sy):
+                self.vDir = -1
+                self.sy = (self.sy + self.bbH) - 1
+                self.dy = (self.dy + self.bbH) - 1
+            else:
+                if (self.dy == self.sy and self.dx > self.sx):
+                    self.hDir = -1
+                    self.sx = (self.sx + self.bbW) - 1 # start at right
+                    self.dx = (self.dx + self.bbW) - 1
+                    if (self.nWords > 1):
+                        t = self.mask1 # and fix up masks
+                        self.mask1 = self.mask2
+                        self.mask2 = t
+            self.destIndex = (self.dy * self.dest.pitch) + (self.dx / self.dest.pixPerWord | 0) # recompute since dx, dy change
+            self.destDelta = (self.dest.pitch * self.vDir) - (self.nWords * self.hDir)
+
+    def sourceSkewAndPointerInit(self):
+        pixPerM1 = self.dest.pixPerWord - 1 # Pix per word is power of two, so self makes a mask
+        sxLowBits = self.sx & pixPerM1
+        dxLowBits = self.dx & pixPerM1
+        # check if need to preload buffer
+        # (i.e., two words of source needed for first word of destination)
+        dWid = -1
+        if (self.hDir > 0):
+            if self.bbW < (self.dest.pixPerWord - dxLowBits):
+                dWid = self.bbW
+            else:
+                dWid = self.dest.pixPerWord - dxLowBits
+            self.preload = (sxLowBits + dWid) > pixPerM1
+        else:
+            if self.bbW < (dxLowBits + 1):
+                dWid = self.bbW
+            else:
+                dWid = dxLowBits + 1
+            self.preload = ((sxLowBits - dWid) + 1) < 0
+
+        if self.source.msb:
+            self.skew = (sxLowBits - dxLowBits) * self.dest.depth
+        else:
+            self.skew = (dxLowBits - sxLowBits) * self.dest.depth
+        if (self.preload):
+            if (self.skew < 0):
+                self.skew += 32
+            else:
+                self.skew -= 32
+        # calculate increments from end of one line to start of next
+        self.sourceIndex = (self.sy * self.source.pitch) + (self.sx / (32 / self.source.depth) |0)
+        self.sourceDelta = (self.source.pitch * self.vDir) - (self.nWords * self.hDir)
+        if (self.preload):
+            self.sourceDelta -= self.hDir
+
+    def clipRange(self):
+        # intersect with destForm bounds
+        if self.clipX < 0:
+            self.clipW += self.clipX
+            self.clipX = 0
+        if self.clipY < 0:
+            self.clipH += self.clipY
+            self.clipY = 0
+        if self.clipX + self.clipW > self.dest.width:
+            self.clipW = self.dest.width - self.clipX
+        if self.clipY + self.clipH > self.dest.height:
+            self.clipH = self.dest.height - self.clipY
+        # intersect with clipRect
+        leftOffset = max(self.clipX - self.destX, 0)
+        self.sx = self.sourceX + leftOffset
+        self.dx = self.destX + leftOffset
+        self.bbW = self.width - leftOffset
+        rightOffset = (self.dx + self.bbW) - (self.clipX + self.clipW)
+        if rightOffset > 0:
+            self.bbW -= rightOffset
+        topOffset = max(self.clipY - self.destY, 0)
+        self.sy = self.sourceY + topOffset
+        self.dy = self.destY + topOffset
+        self.bbH = self.height - topOffset
+        bottomOffset = (self.dy + self.bbH) - (self.clipY + self.clipH)
+        if bottomOffset > 0:
+            self.bbH -= bottomOffset
+        # intersect with sourceForm bounds
+        if not self.source:
+            return
+        if self.sx < 0:
+            self.dx -= self.sx
+            self.bbW += self.sx
+            self.sx = 0
+        if (self.sx + self.bbW) > self.source.width:
+            self.bbW -= (self.sx + self.bbW) - self.source.width
+        if self.sy < 0:
+            self.dy -= self.sy
+            self.bbH += self.sy
+            self.sy = 0
+        if (self.sy + self.bbH) > self.source.height:
+            self.bbH -= (self.sy + self.bbH) - self.source.height
+
+    def rshift(self, val, n):
+        # return rarithmetic.r_uint(val >> n if val >= 0 else (val + 0x100000000) >> n)
+        return rarithmetic.r_uint(rarithmetic.r_uint(val) >> n & BitBltShadow.AllOnes)
+
+    def destMaskAndPointerInit(self):
+        pixPerM1 = self.dest.pixPerWord - 1 # pixPerWord is power-of-two, so this makes a mask
+        startBits = self.dest.pixPerWord - (self.dx & pixPerM1) # how many px in 1st word
+        endBits = (((self.dx + self.bbW) - 1) & pixPerM1) + 1
+        if self.dest.msb:
+            self.mask1 = self.rshift(BitBltShadow.AllOnes, (32 - (startBits * self.dest.depth)))
+            self.mask2 = BitBltShadow.AllOnes << (32 - (endBits * self.dest.depth))
+        else:
+            self.mask1 = BitBltShadow.AllOnes << (32 - (startBits * self.dest.depth))
+            self.mask2 = self.rshift(BitBltShadow.AllOnes, (32 - (endBits * self.dest.depth)))
+        if self.bbW < startBits:
+            self.mask1 = self.mask1 & self.mask2
+            self.mask2 = 0
+            self.nWords = 1
+        else:
+            self.nWords = (((self.bbW - startBits) + pixPerM1) / self.dest.pixPerWord | 0) + 1
+        self.hDir = 1
+        self.vDir = 1
+        self.destIndex = (self.dy * self.dest.pitch) + (self.dx / self.dest.pixPerWord | 0)
+        self.destDelta = (self.dest.pitch * self.vDir) - (self.nWords * self.hDir)
+
+    def copyLoopNoSource(self):
+        halftoneWord = BitBltShadow.AllOnes
+        for i in range(self.bbH):
+            if self.halftone:
+                halftoneWord = rarithmetic.r_uint(self.halftone[(self.dy + i) % len(self.halftone)])
+            # first word in row is masked
+            destMask = self.mask1
+            destWord = self.dest.w_bits.getword(self.destIndex)
+            mergeWord = self.mergeFn(halftoneWord, destWord)
+            destWord = (destMask & mergeWord) | (destWord & (~destMask))
+            self.dest.w_bits.setword(self.destIndex, destWord)
+            self.destIndex += 1
+            destMask = BitBltShadow.AllOnes
+            # the central horizontal loop requires no store masking
+            if self.combinationRule == 3: # store rule requires no dest merging
+                for word in range(2, self.nWords):
+                    self.dest.w_bits.setword(self.destIndex, halftoneWord)
+                    self.destIndex += 1
+            else:
+                for word in range(2, self.nWords):
+                    destWord = self.dest.w_bits.getword(self.destIndex)
+                    mergeWord = self.mergeFn(halftoneWord, destWord)
+                    self.dest.w_bits.setword(self.destIndex, mergeWord)
+                    self.destIndex += 1
+            # last word in row is masked
+            if self.nWords > 1:
+                destMask = self.mask2
+                destWord = self.dest.w_bits.getword(self.destIndex)
+                mergeWord = self.mergeFn(halftoneWord, destWord)
+                destWord = (destMask & mergeWord) | (destWord & (~destMask))
+                self.dest.w_bits.setword(self.destIndex, destWord)
+                self.destIndex += 1
+            self.destIndex += self.destDelta
+
+    def copyLoopPixMap(self):
+        # 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.
+        #
+        # 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.
+        sourcePixMask = BitBltShadow.MaskTable[self.source.depth]
+        destPixMask = BitBltShadow.MaskTable[self.dest.depth]
+        self.sourceIndex = (self.sy * self.source.pitch) + (self.sx / self.source.pixPerWord | 0)
+        scrStartBits = self.source.pixPerWord - (self.sx & (self.source.pixPerWord - 1))
+        if self.bbW < scrStartBits:
+            nSourceIncs = 0
+        else:
+            nSourceIncs = ((self.bbW - scrStartBits) / self.source.pixPerWord | 0) + 1
+        # Note following two items were already calculated in destmask setup!
+        self.sourceDelta = self.source.pitch - nSourceIncs
+        startBits = self.dest.pixPerWord - (self.dx & (self.dest.pixPerWord - 1))
+        endBits = (((self.dx + self.bbW) - 1) & (self.dest.pixPerWord - 1)) + 1
+        if self.bbW < startBits:
+            startBits = self.bbW # ?!
+        srcShift = (self.sx & (self.source.pixPerWord - 1)) * self.source.depth
+        dstShift = (self.dx & (self.dest.pixPerWord - 1)) * self.dest.depth
+        srcShiftInc = self.source.depth
+        dstShiftInc = self.dest.depth
+        dstShiftLeft = 0
+        if (self.source.msb):
+            srcShift = (32 - self.source.depth) - srcShift
+            srcShiftInc = -srcShiftInc
+
+        if (self.dest.msb):
+            dstShift = (32 - self.dest.depth) - dstShift
+            dstShiftInc = -dstShiftInc
+            dstShiftLeft = 32 - self.dest.depth
+
+        for i in range(self.bbH):
+            if self.halftone:
+                halftoneWord = rarithmetic.r_uint(self.halftone[(self.dy + i) % len(self.halftone)])
+            else:
+                halftoneWord = BitBltShadow.AllOnes
+            self.srcBitShift = srcShift
+            self.dstBitShift = dstShift
+            self.destMask = self.mask1
+            nPix = startBits
+            words = self.nWords
+            # Here is the horizontal loop...
+            for word in range(words):
+                skewWord = self.pickSourcePixels(nPix, sourcePixMask, destPixMask, srcShiftInc, dstShiftInc)
+                # align next word to leftmost pixel
+                self.dstBitShift = dstShiftLeft
+                if self.destMask == BitBltShadow.AllOnes: # avoid read-modify-write
+                    self.dest.w_bits.setword(
+                        self.destIndex,
+                        self.mergeFn(skewWord & halftoneWord, self.dest.w_bits.getword(self.destIndex))
+                    )
+                else: # General version using dest masking
+                    destWord = self.dest.w_bits.getword(self.destIndex)
+                    mergeWord = self.mergeFn(skewWord & halftoneWord, destWord & self.destMask)
+                    destWord = (self.destMask & mergeWord) | (destWord & (~self.destMask))
+                    self.dest.w_bits.setword(self.destIndex, destWord)
+
+                self.destIndex += 1
+                if (words == 2): # is the next word the last word?
+                    self.destMask = self.mask2
+                    nPix = endBits
+                else: # use fullword mask for inner loop
+                    self.destMask = BitBltShadow.AllOnes
+                    nPix = self.dest.pixPerWord
+            self.sourceIndex += self.sourceDelta
+            self.destIndex += self.destDelta
+
+    def pickSourcePixels(self, nPixels, srcMask, dstMask, srcShiftInc, dstShiftInc):
+        # Pick nPix pixels starting at srcBitIndex from the source, map by the
+        # color map, and justify them according to dstBitIndex in the resulting destWord.
+        sourceWord = rarithmetic.r_uint(self.source.w_bits.getword(self.sourceIndex))
+        destWord = 0
+        srcShift = self.srcBitShift # put into temp for speed
+        dstShift = self.dstBitShift
+        nPix = nPixels
+        # always > 0 so we can use do { } while(--nPix);
+        if (self.w_cmLookupTable): # a little optimization for (pretty crucial) blits using indexed lookups only
+            for px in range(nPix):
+                sourcePix = self.rshift(rarithmetic.r_uint(sourceWord), srcShift) & srcMask
+                destPix = self.w_cmLookupTable.getword(rarithmetic.intmask(sourcePix & self.cmMask))
+                # adjust dest pix index
+                destWord = destWord | ((destPix & dstMask) << dstShift)
+                # adjust source pix index
+                dstShift += dstShiftInc
+                srcShift += srcShiftInc
+                if srcShift & rarithmetic.r_uint(0xFFFFFFE0):
+                    if (self.source.msb):
+                        srcShift += 32
+                    else:
+                        srcShift -= 32
+                    self.sourceIndex += 1
+                    sourceWord = self.source.w_bits.getword(self.sourceIndex)
+        else:
+            raise PrimitiveFailedError()
+        self.srcBitShift = srcShift # Store back
+        return destWord
+
+    def rotate32bit(self, thisWord, prevWord, skewMask, notSkewMask, unskew):
+        if unskew < 0:
+            rotated = self.rshift(rarithmetic.r_uint(prevWord & notSkewMask), -unskew)
+        else:
+            rotated = (prevWord & notSkewMask) << unskew
+        if self.skew < 0:
+            rotated = rotated | self.rshift(rarithmetic.r_uint(thisWord & skewMask), -self.skew)
+        else:
+            rotated = rotated | (thisWord & skewMask) << self.skew
+        return rotated
+
+    def copyLoop(self):
+        # self version of the inner loop assumes we do have a source
+        sourceLimit = self.source.w_bits.size()
+        hInc = self.hDir
+        # init skew (the difference in word alignment of source and dest)
+        if (self.skew == -32):
+            self.skew = unskew = 0
+            skewMask = rarithmetic.r_uint(0)
+        else:
+            if (self.skew < 0):
+                unskew = self.skew + 32
+                skewMask = rarithmetic.r_uint(BitBltShadow.AllOnes << -self.skew)
+            else:
+                if (self.skew == 0):
+                    unskew = 0
+                    skewMask = BitBltShadow.AllOnes
+                else:
+                    unskew = self.skew - 32
+                    skewMask = self.rshift(BitBltShadow.AllOnes, self.skew)
+        notSkewMask = rarithmetic.r_uint(~skewMask)
+
+        # init halftones
+        if (self.halftone):
+            halftoneWord = rarithmetic.r_uint(self.halftone[0])
+            halftoneHeight = len(self.halftone)
+        else:
+            halftoneWord = BitBltShadow.AllOnes
+            halftoneHeight = 0
+
+        # now loop over all lines
+        y = self.dy
+        for i in range(1, self.bbH + 1):
+            if (halftoneHeight > 1):
+                halftoneWord = rarithmetic.r_uint(self.halftone[y % halftoneHeight])
+                y += self.vDir
+
+            if (self.preload):
+                prevWord = rarithmetic.r_uint(self.source.w_bits.getword(self.sourceIndex))
+                self.sourceIndex += hInc
+            else:
+                prevWord = rarithmetic.r_uint(0)
+
+            destMask = self.mask1
+            # pick up next word
+            thisWord = rarithmetic.r_uint(self.source.w_bits.getword(self.sourceIndex))
+            self.sourceIndex += hInc
+            skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
+            prevWord = thisWord
+            destWord = self.dest.w_bits.getword(self.destIndex)
+            mergeWord = self.mergeFn(skewWord & halftoneWord, destWord)
+            destWord = (destMask & mergeWord) | (destWord & (~destMask))
+            self.dest.w_bits.setword(self.destIndex, destWord)
+            # The central horizontal loop requires no store masking
+            self.destIndex += hInc
+            destMask = BitBltShadow.AllOnes
+            if (self.combinationRule == 3): # Store mode avoids dest merge function
+                if ((self.skew == 0) and (halftoneWord == BitBltShadow.AllOnes)):
+                    # Non-skewed with no halftone
+                    if (self.hDir == -1):
+                        for word in range(2, self.nWords):
+                            thisWord = self.source.w_bits.getword(self.sourceIndex)
+                            self.dest.w_bits.setword(self.destIndex, thisWord)
+                            self.sourceIndex += hInc
+                            self.destIndex += hInc
+                    else:
+                        for word in range(2, self.nWords):
+                            self.dest.w_bits.setword(self.destIndex, prevWord)
+                            prevWord = self.source.w_bits.getword(self.sourceIndex)
+                            self.destIndex += hInc
+                            self.sourceIndex += hInc
+                else:
+                    # skewed and/or halftoned
+                    for word in range(2, self.nWords):
+                        thisWord = self.source.w_bits.getword(self.sourceIndex)
+                        self.sourceIndex += hInc
+                        skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
+                        prevWord = thisWord
+                        self.dest.w_bits.setword(self.destIndex, skewWord & halftoneWord)
+                        self.destIndex += hInc
+            else: # Dest merging here...
+                for word in range(2, self.nWords):
+                    thisWord = self.source.w_bits.getword(self.sourceIndex) # pick up next word
+                    self.sourceIndex += hInc
+                    skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
+                    prevWord = thisWord
+                    mergeWord = self.mergeFn(skewWord & halftoneWord, self.dest.w_bits.getword(self.destIndex))
+                    self.dest.w_bits.setword(self.destIndex, mergeWord)
+                    self.destIndex += hInc
+            # last word with masking and all
+            if (self.nWords > 1):
+                destMask = self.mask2
+                if (self.sourceIndex >= 0 and self.sourceIndex < sourceLimit):
+                    # NOTE: we are currently overrunning source bits in some cases
+                    # self test makes up for it.
+                    thisWord = self.source.w_bits.getword(self.sourceIndex) # pick up next word
+                self.sourceIndex += hInc
+                skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
+                destWord = self.dest.w_bits.getword(self.destIndex)
+                mergeWord = self.mergeFn(skewWord & halftoneWord, destWord)
+                destWord = (destMask & mergeWord) | (destWord & (~destMask))
+                self.dest.w_bits.setword(self.destIndex, destWord)
+                self.destIndex += hInc
+            self.sourceIndex += self.sourceDelta
+            self.destIndex += self.destDelta
+
+    def mergeFn(self, src, dest):
+        return rarithmetic.r_uint(self.merge(
+            rarithmetic.r_uint(src),
+            rarithmetic.r_uint(dest)
+        ))
+
+    def merge(self, source_word, dest_word):
+        assert isinstance(source_word, rarithmetic.r_uint) and isinstance(dest_word, rarithmetic.r_uint)
+        if self.combinationRule == 0:
+            return 0
+        elif self.combinationRule == 1:
+            return source_word & dest_word
+        elif self.combinationRule == 2:
+            return source_word & ~dest_word
+        elif self.combinationRule == 3:
+            return source_word
+        elif self.combinationRule == 4:
+            return ~source_word & dest_word
+        elif self.combinationRule == 5:
+            return dest_word
+        elif self.combinationRule == 6:
+            return source_word ^ dest_word
+        elif self.combinationRule == 7:
+            return source_word | dest_word
+        elif self.combinationRule == 8:
+            return ~source_word & ~dest_word
+        elif self.combinationRule == 9:
+            return ~source_word ^ dest_word
+        elif self.combinationRule == 10:
+            return ~dest_word
+        elif self.combinationRule == 11:
+            return source_word | ~dest_word
+        elif self.combinationRule == 12:
+            return ~source_word
+        elif self.combinationRule == 13:
+            return ~source_word | dest_word
+        elif self.combinationRule == 14:
+            return ~source_word | ~dest_word
+        elif self.combinationRule >= 15 and self.combinationRule <= 17:
+            return dest_word
+        elif self.combinationRule == 18:
+            return source_word + dest_word
+        elif self.combinationRule == 19:
+            return source_word - dest_word
+        elif self.combinationRule >= 20 and self.combinationRule <= 24:
+            return source_word
+        elif self.combinationRule == 25:
+            if source_word == 0:
+                return dest_word
+            else:
+                return self.partitionedANDtonBitsnPartitions(
+                    ~source_word,
+                    dest_word,
+                    self.dest.depth,
+                    self.dest.pixPerWord
+                )
+        elif self.combinationRule == 26:
+            return self.partitionedANDtonBitsnPartitions(
+                ~source_word,
+                dest_word,
+                self.dest.depth,
+                self.dest.pixPerWord
+            )
+        elif 26 < self.combinationRule <= 41:
+            return dest_word
+        else:
+            raise PrimitiveFailedError()
+
+    def partitionedANDtonBitsnPartitions(self, word1, word2, nBits, nParts):
+        # partition mask starts at the right
+        mask = BitBltShadow.MaskTable[nBits]
+        result = 0
+        for i in range(1, nParts + 1):
+            if ((word1 & mask) == mask):
+                result = result | (word2 & mask)
+            # slide left to next partition
+            mask = mask << nBits
+        return result
+
+    def as_string(bb):
+        return 'aBitBlt (destX: %d, destY: %d, sx: %d, sy: %d, dx: %d, dy: %d, w: %d, h: %d, hDir: %d, vDir: %d, sDelta: %d, dDelta: %d, skew: %d, sI: %d, dI: %d)' % (
+            bb.dest_x, bb.dest_y, bb.sx, bb.sy, bb.dx, bb.dy, bb.w, bb.h, bb.h_dir, bb.v_dir, bb.source_delta, bb.dest_delta, bb.skew, bb.source_index, bb.dest_index)
+            # "dest_raster", "source_raster",
+            # "halftone_bits", "mask1", "mask2", "skew_mask",
+            # "n_words", "preload"
+
+
+class FormShadow(AbstractCachingShadow):
+    _attrs_ = ["w_bits", "width", "height", "depth", "offsetX",
+               "offsetY", "msb", "pixPerWord", "pitch"]
+
+    def sync_cache(self):
+        if self.size() < 5:
+            w_self = self.w_self()
+            assert isinstance(w_self, model.W_PointersObject)
+            w_self._shadow = None
+            raise PrimitiveFailedError
+        self.w_bits = self.fetch(0)
+        if self.w_bits is self.space.w_nil:
+            return
+        if not (isinstance(self.w_bits, model.W_WordsObject) or isinstance(self.w_bits, model.W_DisplayBitmap)):
+            w_self = self.w_self()
+            assert isinstance(w_self, model.W_PointersObject)
+            w_self._shadow = None
+            raise PrimitiveFailedError
+        self.width = self.space.unwrap_int(self.fetch(1))
+        self.height = self.space.unwrap_int(self.fetch(2))
+        self.depth = self.space.unwrap_int(self.fetch(3))
+        if self.width < 0 or self.height < 0:
+            raise PrimitiveFailedError()
+        self.msb = self.depth > 0
+        if self.depth < 0:
+            self.depth = -self.depth
+        if self.depth == 0:
+            raise PrimitiveFailedError()
+        w_offset = self.fetch(4)
+        assert isinstance(w_offset, model.W_PointersObject)
+        if not w_offset is self.space.w_nil:
+            self.offsetX = self.space.unwrap_int(w_offset._fetch(0))
+            self.offsetY = self.space.unwrap_int(w_offset._fetch(1))
+        self.pixPerWord = 32 / self.depth
+        self.pitch = (self.width + (self.pixPerWord - 1)) / self.pixPerWord | 0
+        if self.w_bits.size() != (self.pitch * self.height):
+            # raise error.PrimitiveFailedError()
+            pass # - we'll be updated again
diff --git a/spyvm/primitives.py b/spyvm/primitives.py
--- a/spyvm/primitives.py
+++ b/spyvm/primitives.py
@@ -629,31 +629,10 @@
 def func(interp, s_frame, w_rcvr, w_into):
     raise PrimitiveNotYetWrittenError()
 
- at expose_primitive(BITBLT_COPY_BITS, unwrap_spec=[object], clean_stack=False)
-def func(interp, s_frame, w_rcvr):
-    from spyvm.interpreter import Return
-    if not isinstance(w_rcvr, model.W_PointersObject) or w_rcvr.size() < 15:
-        raise PrimitiveFailedError
-
-    # only allow combinationRules 0-41
-    combinationRule = interp.space.unwrap_positive_32bit_int(w_rcvr.fetch(interp.space, 3))
-    if combinationRule > 41:
-        raise PrimitiveFailedError
-
-    space = interp.space
-
-    s_bitblt = w_rcvr.as_bitblt_get_shadow(space)
-    s_bitblt.copyBits()
-
-    w_dest_form = w_rcvr.fetch(space, 0)
-    if (combinationRule == 22 or combinationRule == 32):
-        s_frame.pop() # pops the next value under BitBlt
-        s_frame.push(interp.space.wrap_int(s_bitblt.bitCount))
-    elif w_dest_form.is_same_object(space.objtable['w_display']):
-        w_bitmap = w_dest_form.fetch(space, 0)
-        assert isinstance(w_bitmap, model.W_DisplayBitmap)
-        w_bitmap.flush_to_screen()
-    return w_rcvr
+ at expose_primitive(BITBLT_COPY_BITS, clean_stack=False, no_result=True, compiled_method=True)
+def func(interp, s_frame, argcount, s_method):
+    from spyvm.plugins.bitblt import BitBltPlugin
+    return BitBltPlugin.call("primitiveCopyBits", interp, s_frame, argcount, s_method)
 
 @expose_primitive(BE_CURSOR)
 def func(interp, s_frame, argcount):
@@ -877,9 +856,10 @@
         raise PrimitiveFailedError
     signature = (w_modulename.as_string(), w_functionname.as_string())
 
-    if signature == ('BitBltPlugin', 'primitiveCopyBits'):
-        return prim_holder.prim_table[BITBLT_COPY_BITS](interp, s_frame, argcount, s_method)
-    if signature[0] == "SocketPlugin":
+    if signature == 'BitBltPlugin':
+        from spyvm.plugins.bitblt import BitBltPlugin
+        return BitBltPlugin.call(signature[1], interp, s_frame, argcount, s_method)
+    elif signature[0] == "SocketPlugin":
         from spyvm.plugins.socket import SocketPlugin
         return SocketPlugin.call(signature[1], interp, s_frame, argcount, s_method)
     elif signature[0] == "FilePlugin":
@@ -889,6 +869,7 @@
         from spyvm.plugins.vmdebugging import DebuggingPlugin
         return DebuggingPlugin.call(signature[1], interp, s_frame, argcount, s_method)
     else:
+        print signature
         from spyvm.interpreter_proxy import IProxy
         return IProxy.call(signature, interp, s_frame, argcount, s_method)
     raise PrimitiveFailedError
diff --git a/spyvm/shadow.py b/spyvm/shadow.py
--- a/spyvm/shadow.py
+++ b/spyvm/shadow.py
@@ -1125,610 +1125,3 @@
         self.dependent = dependent
 
     def update(self): pass
-
-
-class BitBltShadow(AbstractCachingShadow):
-    WordSize = 32
-    MaskTable = [rarithmetic.r_uint(0)]
-    for i in xrange(WordSize):
-        MaskTable.append(rarithmetic.r_uint((2 ** (i + 1)) - 1))
-    AllOnes = rarithmetic.r_uint(0xFFFFFFFF)
-
-    def sync_cache(self):
-        self.loadBitBlt()
-
-    def intOrIfNil(self, w_int, i):
-        if w_int is self.space.w_nil:
-            return i
-        else:
-            return self.space.unwrap_int(w_int)
-
-    def loadForm(self, w_form):
-        try:
-            if not isinstance(w_form, model.W_PointersObject):
-                raise error.PrimitiveFailedError()
-            s_form = w_form.as_form_get_shadow(self.space)
-            if not isinstance(s_form, FormShadow):
-                raise error.PrimitiveFailedError()
-            return s_form
-        except error.PrimitiveFailedError, e:
-            w_self = self.w_self()
-            assert isinstance(w_self, model.W_PointersObject)
-            w_self._shadow = None
-            raise e
-
-    def loadHalftone(self, w_halftone_form):
-        if w_halftone_form is self.space.w_nil:
-            return None
-        elif isinstance(w_halftone_form, model.W_WordsObject):
-            # Already a bitmap
-            return w_halftone_form.words
-        else:
-            assert isinstance(w_halftone_form, model.W_PointersObject)
-            w_bits = w_halftone_form.as_form_get_shadow(self.space).w_bits
-            assert isinstance(w_bits, model.W_WordsObject)
-            return w_bits.words
-
-    def loadColorMap(self, w_color_map):
-        if isinstance(w_color_map, model.W_WordsObject):
-            self.w_cmLookupTable = w_color_map
-            self.cmMask = self.w_cmLookupTable.size() - 1
-        else:
-            self.w_cmLookupTable = None
-
-    def loadBitBlt(self):
-        self.success = True
-        self.w_destForm = self.fetch(0)
-        self.dest = self.loadForm(self.w_destForm)
-        self.w_sourceForm = self.fetch(1)
-        if self.w_sourceForm is not self.space.w_nil:
-            self.source = self.loadForm(self.w_sourceForm)
-        else:
-            self.source = None
-        self.halftone = self.loadHalftone(self.fetch(2))
-        self.combinationRule = self.space.unwrap_int(self.fetch(3))
-        self.destX = self.intOrIfNil(self.fetch(4), 0)
-        self.destY = self.intOrIfNil(self.fetch(5), 0)
-        self.width = self.intOrIfNil(self.fetch(6), self.dest.width)
-        self.height = self.intOrIfNil(self.fetch(7), self.dest.height)
-        self.clipX = self.intOrIfNil(self.fetch(10), 0)
-        self.clipY = self.intOrIfNil(self.fetch(11), 0)
-        self.clipW = self.intOrIfNil(self.fetch(12), self.width)
-        self.clipH = self.intOrIfNil(self.fetch(13), self.height)
-        if not self.source:
-            self.sourceX = 0
-            self.sourceY = 0
-        else:
-            self.loadColorMap(self.fetch(14))
-            self.sourceX = self.intOrIfNil(self.fetch(8), 0)
-            self.sourceY = self.intOrIfNil(self.fetch(9), 0)
-
-    def copyBits(self):
-        self.bitCount = 0
-        self.clipRange()
-        if (self.bbW <= 0 or self.bbH <= 0):
-            return
-        self.destMaskAndPointerInit()
-        if not self.source:
-            self.copyLoopNoSource()
-        else:
-            self.checkSourceOverlap()
-            if self.source.depth != self.dest.depth:
-                self.copyLoopPixMap()
-            else:
-                self.sourceSkewAndPointerInit()
-                self.copyLoop()
-
-    def checkSourceOverlap(self):
-        if (self.w_sourceForm is self.w_destForm and self.dy >= self.sy):
-            if (self.dy > self.sy):
-                self.vDir = -1
-                self.sy = (self.sy + self.bbH) - 1
-                self.dy = (self.dy + self.bbH) - 1
-            else:
-                if (self.dy == self.sy and self.dx > self.sx):
-                    self.hDir = -1
-                    self.sx = (self.sx + self.bbW) - 1 # start at right
-                    self.dx = (self.dx + self.bbW) - 1
-                    if (self.nWords > 1):
-                        t = self.mask1 # and fix up masks
-                        self.mask1 = self.mask2
-                        self.mask2 = t
-            self.destIndex = (self.dy * self.dest.pitch) + (self.dx / self.dest.pixPerWord | 0) # recompute since dx, dy change
-            self.destDelta = (self.dest.pitch * self.vDir) - (self.nWords * self.hDir)
-
-    def sourceSkewAndPointerInit(self):
-        pixPerM1 = self.dest.pixPerWord - 1 # Pix per word is power of two, so self makes a mask
-        sxLowBits = self.sx & pixPerM1
-        dxLowBits = self.dx & pixPerM1
-        # check if need to preload buffer
-        # (i.e., two words of source needed for first word of destination)
-        dWid = -1
-        if (self.hDir > 0):
-            if self.bbW < (self.dest.pixPerWord - dxLowBits):
-                dWid = self.bbW
-            else:
-                dWid = self.dest.pixPerWord - dxLowBits
-            self.preload = (sxLowBits + dWid) > pixPerM1
-        else:
-            if self.bbW < (dxLowBits + 1):
-                dWid = self.bbW
-            else:
-                dWid = dxLowBits + 1
-            self.preload = ((sxLowBits - dWid) + 1) < 0
-
-        if self.source.msb:
-            self.skew = (sxLowBits - dxLowBits) * self.dest.depth
-        else:
-            self.skew = (dxLowBits - sxLowBits) * self.dest.depth
-        if (self.preload):
-            if (self.skew < 0):
-                self.skew += 32
-            else:
-                self.skew -= 32
-        # calculate increments from end of one line to start of next
-        self.sourceIndex = (self.sy * self.source.pitch) + (self.sx / (32 / self.source.depth) |0)
-        self.sourceDelta = (self.source.pitch * self.vDir) - (self.nWords * self.hDir)
-        if (self.preload):
-            self.sourceDelta -= self.hDir
-
-    def clipRange(self):
-        # intersect with destForm bounds
-        if self.clipX < 0:
-            self.clipW += self.clipX
-            self.clipX = 0
-        if self.clipY < 0:
-            self.clipH += self.clipY
-            self.clipY = 0
-        if self.clipX + self.clipW > self.dest.width:
-            self.clipW = self.dest.width - self.clipX
-        if self.clipY + self.clipH > self.dest.height:
-            self.clipH = self.dest.height - self.clipY
-        # intersect with clipRect
-        leftOffset = max(self.clipX - self.destX, 0)
-        self.sx = self.sourceX + leftOffset
-        self.dx = self.destX + leftOffset
-        self.bbW = self.width - leftOffset
-        rightOffset = (self.dx + self.bbW) - (self.clipX + self.clipW)
-        if rightOffset > 0:
-            self.bbW -= rightOffset
-        topOffset = max(self.clipY - self.destY, 0)
-        self.sy = self.sourceY + topOffset
-        self.dy = self.destY + topOffset
-        self.bbH = self.height - topOffset
-        bottomOffset = (self.dy + self.bbH) - (self.clipY + self.clipH)
-        if bottomOffset > 0:
-            self.bbH -= bottomOffset
-        # intersect with sourceForm bounds
-        if not self.source:
-            return
-        if self.sx < 0:
-            self.dx -= self.sx
-            self.bbW += self.sx
-            self.sx = 0
-        if (self.sx + self.bbW) > self.source.width:
-            self.bbW -= (self.sx + self.bbW) - self.source.width
-        if self.sy < 0:
-            self.dy -= self.sy
-            self.bbH += self.sy
-            self.sy = 0
-        if (self.sy + self.bbH) > self.source.height:
-            self.bbH -= (self.sy + self.bbH) - self.source.height
-
-    def rshift(self, val, n):
-        # return rarithmetic.r_uint(val >> n if val >= 0 else (val + 0x100000000) >> n)
-        return rarithmetic.r_uint(rarithmetic.r_uint(val) >> n & BitBltShadow.AllOnes)
-
-    def destMaskAndPointerInit(self):
-        pixPerM1 = self.dest.pixPerWord - 1 # pixPerWord is power-of-two, so this makes a mask
-        startBits = self.dest.pixPerWord - (self.dx & pixPerM1) # how many px in 1st word
-        endBits = (((self.dx + self.bbW) - 1) & pixPerM1) + 1
-        if self.dest.msb:
-            self.mask1 = self.rshift(BitBltShadow.AllOnes, (32 - (startBits * self.dest.depth)))
-            self.mask2 = BitBltShadow.AllOnes << (32 - (endBits * self.dest.depth))
-        else:
-            self.mask1 = BitBltShadow.AllOnes << (32 - (startBits * self.dest.depth))
-            self.mask2 = self.rshift(BitBltShadow.AllOnes, (32 - (endBits * self.dest.depth)))
-        if self.bbW < startBits:
-            self.mask1 = self.mask1 & self.mask2
-            self.mask2 = 0
-            self.nWords = 1
-        else:
-            self.nWords = (((self.bbW - startBits) + pixPerM1) / self.dest.pixPerWord | 0) + 1
-        self.hDir = 1
-        self.vDir = 1
-        self.destIndex = (self.dy * self.dest.pitch) + (self.dx / self.dest.pixPerWord | 0)
-        self.destDelta = (self.dest.pitch * self.vDir) - (self.nWords * self.hDir)
-
-    def copyLoopNoSource(self):
-        halftoneWord = BitBltShadow.AllOnes
-        for i in range(self.bbH):
-            if self.halftone:
-                halftoneWord = rarithmetic.r_uint(self.halftone[(self.dy + i) % len(self.halftone)])
-            # first word in row is masked
-            destMask = self.mask1
-            destWord = self.dest.w_bits.getword(self.destIndex)
-            mergeWord = self.mergeFn(halftoneWord, destWord)
-            destWord = (destMask & mergeWord) | (destWord & (~destMask))
-            self.dest.w_bits.setword(self.destIndex, destWord)
-            self.destIndex += 1
-            destMask = BitBltShadow.AllOnes
-            # the central horizontal loop requires no store masking
-            if self.combinationRule == 3: # store rule requires no dest merging
-                for word in range(2, self.nWords):
-                    self.dest.w_bits.setword(self.destIndex, halftoneWord)
-                    self.destIndex += 1
-            else:
-                for word in range(2, self.nWords):
-                    destWord = self.dest.w_bits.getword(self.destIndex)
-                    mergeWord = self.mergeFn(halftoneWord, destWord)
-                    self.dest.w_bits.setword(self.destIndex, mergeWord)
-                    self.destIndex += 1
-            # last word in row is masked
-            if self.nWords > 1:
-                destMask = self.mask2
-                destWord = self.dest.w_bits.getword(self.destIndex)
-                mergeWord = self.mergeFn(halftoneWord, destWord)
-                destWord = (destMask & mergeWord) | (destWord & (~destMask))
-                self.dest.w_bits.setword(self.destIndex, destWord)
-                self.destIndex += 1
-            self.destIndex += self.destDelta
-
-    def copyLoopPixMap(self):
-        # 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.
-        #
-        # 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.
-        sourcePixMask = BitBltShadow.MaskTable[self.source.depth]
-        destPixMask = BitBltShadow.MaskTable[self.dest.depth]
-        self.sourceIndex = (self.sy * self.source.pitch) + (self.sx / self.source.pixPerWord | 0)
-        scrStartBits = self.source.pixPerWord - (self.sx & (self.source.pixPerWord - 1))
-        if self.bbW < scrStartBits:
-            nSourceIncs = 0
-        else:
-            nSourceIncs = ((self.bbW - scrStartBits) / self.source.pixPerWord | 0) + 1
-        # Note following two items were already calculated in destmask setup!
-        self.sourceDelta = self.source.pitch - nSourceIncs
-        startBits = self.dest.pixPerWord - (self.dx & (self.dest.pixPerWord - 1))
-        endBits = (((self.dx + self.bbW) - 1) & (self.dest.pixPerWord - 1)) + 1
-        if self.bbW < startBits:
-            startBits = self.bbW # ?!
-        srcShift = (self.sx & (self.source.pixPerWord - 1)) * self.source.depth
-        dstShift = (self.dx & (self.dest.pixPerWord - 1)) * self.dest.depth
-        srcShiftInc = self.source.depth
-        dstShiftInc = self.dest.depth
-        dstShiftLeft = 0
-        if (self.source.msb):
-            srcShift = (32 - self.source.depth) - srcShift
-            srcShiftInc = -srcShiftInc
-
-        if (self.dest.msb):
-            dstShift = (32 - self.dest.depth) - dstShift
-            dstShiftInc = -dstShiftInc
-            dstShiftLeft = 32 - self.dest.depth
-
-        for i in range(self.bbH):
-            if self.halftone:
-                halftoneWord = rarithmetic.r_uint(self.halftone[(self.dy + i) % len(self.halftone)])
-            else:
-                halftoneWord = BitBltShadow.AllOnes
-            self.srcBitShift = srcShift
-            self.dstBitShift = dstShift
-            self.destMask = self.mask1
-            nPix = startBits
-            words = self.nWords
-            # Here is the horizontal loop...
-            for word in range(words):
-                skewWord = self.pickSourcePixels(nPix, sourcePixMask, destPixMask, srcShiftInc, dstShiftInc)
-                # align next word to leftmost pixel
-                self.dstBitShift = dstShiftLeft
-                if self.destMask == BitBltShadow.AllOnes: # avoid read-modify-write
-                    self.dest.w_bits.setword(
-                        self.destIndex,
-                        self.mergeFn(skewWord & halftoneWord, self.dest.w_bits.getword(self.destIndex))
-                    )
-                else: # General version using dest masking
-                    destWord = self.dest.w_bits.getword(self.destIndex)
-                    mergeWord = self.mergeFn(skewWord & halftoneWord, destWord & self.destMask)
-                    destWord = (self.destMask & mergeWord) | (destWord & (~self.destMask))
-                    self.dest.w_bits.setword(self.destIndex, destWord)
-
-                self.destIndex += 1
-                if (words == 2): # is the next word the last word?
-                    self.destMask = self.mask2
-                    nPix = endBits
-                else: # use fullword mask for inner loop
-                    self.destMask = BitBltShadow.AllOnes
-                    nPix = self.dest.pixPerWord
-            self.sourceIndex += self.sourceDelta
-            self.destIndex += self.destDelta
-
-    def pickSourcePixels(self, nPixels, srcMask, dstMask, srcShiftInc, dstShiftInc):
-        # Pick nPix pixels starting at srcBitIndex from the source, map by the
-        # color map, and justify them according to dstBitIndex in the resulting destWord.
-        sourceWord = rarithmetic.r_uint(self.source.w_bits.getword(self.sourceIndex))
-        destWord = 0
-        srcShift = self.srcBitShift # put into temp for speed
-        dstShift = self.dstBitShift
-        nPix = nPixels
-        # always > 0 so we can use do { } while(--nPix);
-        if (self.w_cmLookupTable): # a little optimization for (pretty crucial) blits using indexed lookups only
-            for px in range(nPix):
-                sourcePix = self.rshift(rarithmetic.r_uint(sourceWord), srcShift) & srcMask
-                destPix = self.w_cmLookupTable.getword(rarithmetic.intmask(sourcePix & self.cmMask))
-                # adjust dest pix index
-                destWord = destWord | ((destPix & dstMask) << dstShift)
-                # adjust source pix index
-                dstShift += dstShiftInc
-                srcShift += srcShiftInc
-                if srcShift & rarithmetic.r_uint(0xFFFFFFE0):
-                    if (self.source.msb):
-                        srcShift += 32
-                    else:
-                        srcShift -= 32
-                    self.sourceIndex += 1
-                    sourceWord = self.source.w_bits.getword(self.sourceIndex)
-        else:
-            raise error.PrimitiveFailedError()
-        self.srcBitShift = srcShift # Store back
-        return destWord
-
-    def rotate32bit(self, thisWord, prevWord, skewMask, notSkewMask, unskew):
-        if unskew < 0:
-            rotated = self.rshift(rarithmetic.r_uint(prevWord & notSkewMask), -unskew)
-        else:
-            rotated = (prevWord & notSkewMask) << unskew
-        if self.skew < 0:
-            rotated = rotated | self.rshift(rarithmetic.r_uint(thisWord & skewMask), -self.skew)
-        else:
-            rotated = rotated | (thisWord & skewMask) << self.skew
-        return rotated
-
-    def copyLoop(self):
-        # self version of the inner loop assumes we do have a source
-        sourceLimit = self.source.w_bits.size()
-        hInc = self.hDir
-        # init skew (the difference in word alignment of source and dest)
-        if (self.skew == -32):
-            self.skew = unskew = 0
-            skewMask = rarithmetic.r_uint(0)
-        else:
-            if (self.skew < 0):
-                unskew = self.skew + 32
-                skewMask = rarithmetic.r_uint(BitBltShadow.AllOnes << -self.skew)
-            else:
-                if (self.skew == 0):
-                    unskew = 0
-                    skewMask = BitBltShadow.AllOnes
-                else:
-                    unskew = self.skew - 32
-                    skewMask = self.rshift(BitBltShadow.AllOnes, self.skew)
-        notSkewMask = rarithmetic.r_uint(~skewMask)
-
-        # init halftones
-        if (self.halftone):
-            halftoneWord = rarithmetic.r_uint(self.halftone[0])
-            halftoneHeight = len(self.halftone)
-        else:
-            halftoneWord = BitBltShadow.AllOnes
-            halftoneHeight = 0
-
-        # now loop over all lines
-        y = self.dy
-        for i in range(1, self.bbH + 1):
-            if (halftoneHeight > 1):
-                halftoneWord = rarithmetic.r_uint(self.halftone[y % halftoneHeight])
-                y += self.vDir
-
-            if (self.preload):
-                prevWord = rarithmetic.r_uint(self.source.w_bits.getword(self.sourceIndex))
-                self.sourceIndex += hInc
-            else:
-                prevWord = rarithmetic.r_uint(0)
-
-            destMask = self.mask1
-            # pick up next word
-            thisWord = rarithmetic.r_uint(self.source.w_bits.getword(self.sourceIndex))
-            self.sourceIndex += hInc
-            skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
-            prevWord = thisWord
-            destWord = self.dest.w_bits.getword(self.destIndex)
-            mergeWord = self.mergeFn(skewWord & halftoneWord, destWord)
-            destWord = (destMask & mergeWord) | (destWord & (~destMask))
-            self.dest.w_bits.setword(self.destIndex, destWord)
-            # The central horizontal loop requires no store masking
-            self.destIndex += hInc
-            destMask = BitBltShadow.AllOnes
-            if (self.combinationRule == 3): # Store mode avoids dest merge function
-                if ((self.skew == 0) and (halftoneWord == BitBltShadow.AllOnes)):
-                    # Non-skewed with no halftone
-                    if (self.hDir == -1):
-                        for word in range(2, self.nWords):
-                            thisWord = self.source.w_bits.getword(self.sourceIndex)
-                            self.dest.w_bits.setword(self.destIndex, thisWord)
-                            self.sourceIndex += hInc
-                            self.destIndex += hInc
-                    else:
-                        for word in range(2, self.nWords):
-                            self.dest.w_bits.setword(self.destIndex, prevWord)
-                            prevWord = self.source.w_bits.getword(self.sourceIndex)
-                            self.destIndex += hInc
-                            self.sourceIndex += hInc
-                else:
-                    # skewed and/or halftoned
-                    for word in range(2, self.nWords):
-                        thisWord = self.source.w_bits.getword(self.sourceIndex)
-                        self.sourceIndex += hInc
-                        skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
-                        prevWord = thisWord
-                        self.dest.w_bits.setword(self.destIndex, skewWord & halftoneWord)
-                        self.destIndex += hInc
-            else: # Dest merging here...
-                for word in range(2, self.nWords):
-                    thisWord = self.source.w_bits.getword(self.sourceIndex) # pick up next word
-                    self.sourceIndex += hInc
-                    skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
-                    prevWord = thisWord
-                    mergeWord = self.mergeFn(skewWord & halftoneWord, self.dest.w_bits.getword(self.destIndex))
-                    self.dest.w_bits.setword(self.destIndex, mergeWord)
-                    self.destIndex += hInc
-            # last word with masking and all
-            if (self.nWords > 1):
-                destMask = self.mask2
-                if (self.sourceIndex >= 0 and self.sourceIndex < sourceLimit):
-                    # NOTE: we are currently overrunning source bits in some cases
-                    # self test makes up for it.
-                    thisWord = self.source.w_bits.getword(self.sourceIndex) # pick up next word
-                self.sourceIndex += hInc
-                skewWord = self.rotate32bit(thisWord, prevWord, skewMask, notSkewMask, unskew)
-                destWord = self.dest.w_bits.getword(self.destIndex)
-                mergeWord = self.mergeFn(skewWord & halftoneWord, destWord)
-                destWord = (destMask & mergeWord) | (destWord & (~destMask))
-                self.dest.w_bits.setword(self.destIndex, destWord)
-                self.destIndex += hInc
-            self.sourceIndex += self.sourceDelta
-            self.destIndex += self.destDelta
-
-    def mergeFn(self, src, dest):
-        return rarithmetic.r_uint(self.merge(
-            rarithmetic.r_uint(src),
-            rarithmetic.r_uint(dest)
-        ))
-
-    def merge(self, source_word, dest_word):
-        assert isinstance(source_word, rarithmetic.r_uint) and isinstance(dest_word, rarithmetic.r_uint)
-        if self.combinationRule == 0:
-            return 0
-        elif self.combinationRule == 1:
-            return source_word & dest_word
-        elif self.combinationRule == 2:
-            return source_word & ~dest_word
-        elif self.combinationRule == 3:
-            return source_word
-        elif self.combinationRule == 4:
-            return ~source_word & dest_word
-        elif self.combinationRule == 5:
-            return dest_word
-        elif self.combinationRule == 6:
-            return source_word ^ dest_word
-        elif self.combinationRule == 7:
-            return source_word | dest_word
-        elif self.combinationRule == 8:
-            return ~source_word & ~dest_word
-        elif self.combinationRule == 9:
-            return ~source_word ^ dest_word
-        elif self.combinationRule == 10:
-            return ~dest_word
-        elif self.combinationRule == 11:
-            return source_word | ~dest_word
-        elif self.combinationRule == 12:
-            return ~source_word
-        elif self.combinationRule == 13:
-            return ~source_word | dest_word
-        elif self.combinationRule == 14:
-            return ~source_word | ~dest_word
-        elif self.combinationRule >= 15 and self.combinationRule <= 17:
-            return dest_word
-        elif self.combinationRule == 18:
-            return source_word + dest_word
-        elif self.combinationRule == 19:
-            return source_word - dest_word
-        elif self.combinationRule >= 20 and self.combinationRule <= 24:
-            return source_word
-        elif self.combinationRule == 25:
-            if source_word == 0:
-                return dest_word
-            else:
-                return self.partitionedANDtonBitsnPartitions(
-                    ~source_word,
-                    dest_word,
-                    self.dest.depth,
-                    self.dest.pixPerWord
-                )
-        elif self.combinationRule == 26:
-            return self.partitionedANDtonBitsnPartitions(
-                ~source_word,
-                dest_word,
-                self.dest.depth,
-                self.dest.pixPerWord
-            )
-        elif 26 < self.combinationRule <= 41:
-            return dest_word
-        else:
-            raise error.PrimitiveFailedError()
-
-    def partitionedANDtonBitsnPartitions(self, word1, word2, nBits, nParts):
-        # partition mask starts at the right
-        mask = BitBltShadow.MaskTable[nBits]
-        result = 0
-        for i in range(1, nParts + 1):
-            if ((word1 & mask) == mask):
-                result = result | (word2 & mask)
-            # slide left to next partition
-            mask = mask << nBits
-        return result
-
-    def as_string(bb):
-        return 'aBitBlt (destX: %d, destY: %d, sx: %d, sy: %d, dx: %d, dy: %d, w: %d, h: %d, hDir: %d, vDir: %d, sDelta: %d, dDelta: %d, skew: %d, sI: %d, dI: %d)' % (
-            bb.dest_x, bb.dest_y, bb.sx, bb.sy, bb.dx, bb.dy, bb.w, bb.h, bb.h_dir, bb.v_dir, bb.source_delta, bb.dest_delta, bb.skew, bb.source_index, bb.dest_index)
-            # "dest_raster", "source_raster",
-            # "halftone_bits", "mask1", "mask2", "skew_mask",
-            # "n_words", "preload"
-
-class FormShadow(AbstractCachingShadow):
-    _attrs_ = ["w_bits", "width", "height", "depth", "offsetX",
-               "offsetY", "msb", "pixPerWord", "pitch"]
-
-    def sync_cache(self):
-        if self.size() < 5:
-            w_self = self.w_self()
-            assert isinstance(w_self, model.W_PointersObject)
-            w_self._shadow = None
-            raise error.PrimitiveFailedError
-        self.w_bits = self.fetch(0)
-        if self.w_bits is self.space.w_nil:
-            return
-        if not (isinstance(self.w_bits, model.W_WordsObject) or isinstance(self.w_bits, model.W_DisplayBitmap)):
-            w_self = self.w_self()
-            assert isinstance(w_self, model.W_PointersObject)
-            w_self._shadow = None
-            raise error.PrimitiveFailedError
-        self.width = self.space.unwrap_int(self.fetch(1))
-        self.height = self.space.unwrap_int(self.fetch(2))
-        self.depth = self.space.unwrap_int(self.fetch(3))
-        if self.width < 0 or self.height < 0:
-            raise error.PrimitiveFailedError()
-        self.msb = self.depth > 0
-        if self.depth < 0:
-            self.depth = -self.depth
-        if self.depth == 0:
-            raise error.PrimitiveFailedError()
-        w_offset = self.fetch(4)
-        assert isinstance(w_offset, model.W_PointersObject)
-        if not w_offset is self.space.w_nil:
-            self.offsetX = self.space.unwrap_int(w_offset._fetch(0))
-            self.offsetY = self.space.unwrap_int(w_offset._fetch(1))
-        self.pixPerWord = 32 / self.depth
-        self.pitch = (self.width + (self.pixPerWord - 1)) / self.pixPerWord | 0
-        if self.w_bits.size() != (self.pitch * self.height):
-            # raise error.PrimitiveFailedError()
-            pass # - we'll be updated again
-
-    # def replace_bits(self):
-    #     w_bits = self.w_bits
-    #     if isinstance(w_bits, model.W_WordsObject):
-    #         pass
-    #     elif isinstance(w_bits, model.W_DisplayBitmap):
-    #         w_bits.update_from_buffer()
-    #     else:
-    #         w_self = self.w_self()
-    #         assert isinstance(w_self, model.W_PointersObject)
-    #         w_self._shadow = None
-    #         raise error.PrimitiveFailedError


More information about the pypy-commit mailing list