1ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com
2ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com/*
3ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Copyright 2011 Google Inc.
4ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com *
5ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * Use of this source code is governed by a BSD-style license that can be
6ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com * found in the LICENSE file.
7ec3ed6a5ebf6f2c406d7bcf94b6bc34fcaeb976eepoger@google.com */
88a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkBitmap.h"
98a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com#include "SkRegion.h"
108a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
118a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.combool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy,
128a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                          SkRegion* inval) const
138a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com{
14900ecf2f1579d42c9d2959831787af0346320f86reed@google.com    if (this->isImmutable() || kUnknown_SkColorType == this->colorType()) {
156a9368d41154890b6c316fdae704bf56ca4c70ccscroggo@google.com        return false;
166a9368d41154890b6c316fdae704bf56ca4c70ccscroggo@google.com    }
176a9368d41154890b6c316fdae704bf56ca4c70ccscroggo@google.com
188a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (NULL != subset) {
198a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkBitmap tmp;
208a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
218a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return  this->extractSubset(&tmp, *subset) &&
228a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                // now call again with no rectangle
238a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com                tmp.scrollRect(NULL, dx, dy, inval);
248a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
258a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
26900ecf2f1579d42c9d2959831787af0346320f86reed@google.com    int shift = this->bytesPerPixel() >> 1;
278a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    int width = this->width();
28fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com    int height = this->height();
29fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
308a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // check if there's nothing to do
318a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if ((dx | dy) == 0 || width <= 0 || height <= 0) {
328a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (NULL != inval) {
338a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            inval->setEmpty();
348a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
358a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return true;
368a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
378a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
388a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // compute the inval region now, before we see if there are any pixels
398a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (NULL != inval) {
408a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        SkIRect r;
41fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
428a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        r.set(0, 0, width, height);
438a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // initial the region with the entire bounds
448a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        inval->setRect(r);
458a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // do the "scroll"
468a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        r.offset(dx, dy);
47fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
488a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // check if we scrolled completely away
498a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        if (!SkIRect::Intersects(r, inval->getBounds())) {
508a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            // inval has already been updated...
518a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com            return true;
528a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        }
53fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
548a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // compute the dirty area
558a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        inval->op(r, SkRegion::kDifference_Op);
568a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
57fbfcd5602128ec010c82cb733c9cdc0a3254f9f3rmistry@google.com
588a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    SkAutoLockPixels    alp(*this);
598a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // if we have no pixels, just return (inval is already updated)
608a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    // don't call readyToDraw(), since we don't require a colortable per se
618a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (this->getPixels() == NULL) {
628a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        return true;
638a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
648a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
658a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    char*       dst = (char*)this->getPixels();
668a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    const char* src = dst;
67e5f48243bdbed2662be7a31be0888abc273b09e8scroggo@google.com    int         rowBytes = (int)this->rowBytes();    // need rowBytes to be signed
688a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
698a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (dy <= 0) {
708a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        src -= dy * rowBytes;
718a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        height += dy;
728a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
738a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst += dy * rowBytes;
748a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        height -= dy;
758a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // now jump src/dst to the last scanline
768a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        src += (height - 1) * rowBytes;
778a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst += (height - 1) * rowBytes;
788a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        // now invert rowbytes so we copy backwards in the loop
798a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        rowBytes = -rowBytes;
808a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
81d4af56c5f24a7bf0200e20a591d55f8c82fb9627epoger@google.com
828a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    if (dx <= 0) {
838a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        src -= dx << shift;
848a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        width += dx;
858a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    } else {
868a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst += dx << shift;
878a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        width -= dx;
888a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
898a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com
90d4af56c5f24a7bf0200e20a591d55f8c82fb9627epoger@google.com    // If the X-translation would push it completely beyond the region,
91d4af56c5f24a7bf0200e20a591d55f8c82fb9627epoger@google.com    // then there's nothing to draw.
92d4af56c5f24a7bf0200e20a591d55f8c82fb9627epoger@google.com    if (width <= 0) {
93d4af56c5f24a7bf0200e20a591d55f8c82fb9627epoger@google.com        return true;
94d4af56c5f24a7bf0200e20a591d55f8c82fb9627epoger@google.com    }
95d4af56c5f24a7bf0200e20a591d55f8c82fb9627epoger@google.com
968a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    width <<= shift;    // now width is the number of bytes to move per line
978a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    while (--height >= 0) {
988a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        memmove(dst, src, width);
998a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        dst += rowBytes;
1008a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com        src += rowBytes;
1018a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    }
1026a9368d41154890b6c316fdae704bf56ca4c70ccscroggo@google.com
1036a9368d41154890b6c316fdae704bf56ca4c70ccscroggo@google.com    this->notifyPixelsChanged();
1048a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com    return true;
1058a1c16ff38322f0210116fa7293eb8817c7e477ereed@android.com}
106