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