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