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