reed@android.com | 8a1c16f | 2008-12-17 15:59:43 +0000 | [diff] [blame^] | 1 | #include "SkBitmap.h" |
| 2 | #include "SkRegion.h" |
| 3 | |
| 4 | bool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy, |
| 5 | SkRegion* inval) const |
| 6 | { |
| 7 | if (NULL != subset) { |
| 8 | SkBitmap tmp; |
| 9 | |
| 10 | return this->extractSubset(&tmp, *subset) && |
| 11 | // now call again with no rectangle |
| 12 | tmp.scrollRect(NULL, dx, dy, inval); |
| 13 | } |
| 14 | |
| 15 | int shift; |
| 16 | |
| 17 | switch (this->config()) { |
| 18 | case kIndex8_Config: |
| 19 | case kA8_Config: |
| 20 | shift = 0; |
| 21 | break; |
| 22 | case kARGB_4444_Config: |
| 23 | case kRGB_565_Config: |
| 24 | shift = 1; |
| 25 | break; |
| 26 | case kARGB_8888_Config: |
| 27 | shift = 2; |
| 28 | break; |
| 29 | default: |
| 30 | // can't scroll this config |
| 31 | return false; |
| 32 | } |
| 33 | |
| 34 | int width = this->width(); |
| 35 | int height = this->height(); |
| 36 | |
| 37 | // check if there's nothing to do |
| 38 | if ((dx | dy) == 0 || width <= 0 || height <= 0) { |
| 39 | if (NULL != inval) { |
| 40 | inval->setEmpty(); |
| 41 | } |
| 42 | return true; |
| 43 | } |
| 44 | |
| 45 | // compute the inval region now, before we see if there are any pixels |
| 46 | if (NULL != inval) { |
| 47 | SkIRect r; |
| 48 | |
| 49 | r.set(0, 0, width, height); |
| 50 | // initial the region with the entire bounds |
| 51 | inval->setRect(r); |
| 52 | // do the "scroll" |
| 53 | r.offset(dx, dy); |
| 54 | |
| 55 | // check if we scrolled completely away |
| 56 | if (!SkIRect::Intersects(r, inval->getBounds())) { |
| 57 | // inval has already been updated... |
| 58 | return true; |
| 59 | } |
| 60 | |
| 61 | // compute the dirty area |
| 62 | inval->op(r, SkRegion::kDifference_Op); |
| 63 | } |
| 64 | |
| 65 | SkAutoLockPixels alp(*this); |
| 66 | // if we have no pixels, just return (inval is already updated) |
| 67 | // don't call readyToDraw(), since we don't require a colortable per se |
| 68 | if (this->getPixels() == NULL) { |
| 69 | return true; |
| 70 | } |
| 71 | |
| 72 | // if we get this far, then we need to shift the pixels |
| 73 | |
| 74 | char* dst = (char*)this->getPixels(); |
| 75 | const char* src = dst; |
| 76 | int rowBytes = this->rowBytes(); // need rowBytes to be signed |
| 77 | |
| 78 | if (dy <= 0) { |
| 79 | src -= dy * rowBytes; |
| 80 | height += dy; |
| 81 | } else { |
| 82 | dst += dy * rowBytes; |
| 83 | height -= dy; |
| 84 | // now jump src/dst to the last scanline |
| 85 | src += (height - 1) * rowBytes; |
| 86 | dst += (height - 1) * rowBytes; |
| 87 | // now invert rowbytes so we copy backwards in the loop |
| 88 | rowBytes = -rowBytes; |
| 89 | } |
| 90 | |
| 91 | if (dx <= 0) { |
| 92 | src -= dx << shift; |
| 93 | width += dx; |
| 94 | } else { |
| 95 | dst += dx << shift; |
| 96 | width -= dx; |
| 97 | } |
| 98 | |
| 99 | width <<= shift; // now width is the number of bytes to move per line |
| 100 | while (--height >= 0) { |
| 101 | memmove(dst, src, width); |
| 102 | dst += rowBytes; |
| 103 | src += rowBytes; |
| 104 | } |
| 105 | return true; |
| 106 | } |
| 107 | |