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() || kUnknown_SkColorType == this->colorType()) {
15        return false;
16    }
17
18    if (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 = this->bytesPerPixel() >> 1;
27    int width = this->width();
28    int height = this->height();
29
30    // check if there's nothing to do
31    if ((dx | dy) == 0 || width <= 0 || height <= 0) {
32        if (inval) {
33            inval->setEmpty();
34        }
35        return true;
36    }
37
38    // compute the inval region now, before we see if there are any pixels
39    if (inval) {
40        SkIRect r;
41
42        r.set(0, 0, width, height);
43        // initial the region with the entire bounds
44        inval->setRect(r);
45        // do the "scroll"
46        r.offset(dx, dy);
47
48        // check if we scrolled completely away
49        if (!SkIRect::Intersects(r, inval->getBounds())) {
50            // inval has already been updated...
51            return true;
52        }
53
54        // compute the dirty area
55        inval->op(r, SkRegion::kDifference_Op);
56    }
57
58    SkAutoLockPixels    alp(*this);
59    // if we have no pixels, just return (inval is already updated)
60    // don't call readyToDraw(), since we don't require a colortable per se
61    if (this->getPixels() == NULL) {
62        return true;
63    }
64
65    char*       dst = (char*)this->getPixels();
66    const char* src = dst;
67    int         rowBytes = (int)this->rowBytes();    // need rowBytes to be signed
68
69    if (dy <= 0) {
70        src -= dy * rowBytes;
71        height += dy;
72    } else {
73        dst += dy * rowBytes;
74        height -= dy;
75        // now jump src/dst to the last scanline
76        src += (height - 1) * rowBytes;
77        dst += (height - 1) * rowBytes;
78        // now invert rowbytes so we copy backwards in the loop
79        rowBytes = -rowBytes;
80    }
81
82    if (dx <= 0) {
83        src -= dx << shift;
84        width += dx;
85    } else {
86        dst += dx << shift;
87        width -= dx;
88    }
89
90    // If the X-translation would push it completely beyond the region,
91    // then there's nothing to draw.
92    if (width <= 0) {
93        return true;
94    }
95
96    width <<= shift;    // now width is the number of bytes to move per line
97    while (--height >= 0) {
98        memmove(dst, src, width);
99        dst += rowBytes;
100        src += rowBytes;
101    }
102
103    this->notifyPixelsChanged();
104    return true;
105}
106