michael@0: michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: #include "SkBitmap.h" michael@0: #include "SkRegion.h" michael@0: michael@0: bool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy, michael@0: SkRegion* inval) const michael@0: { michael@0: if (this->isImmutable() || kUnknown_SkColorType == this->colorType()) { michael@0: return false; michael@0: } michael@0: michael@0: if (NULL != subset) { michael@0: SkBitmap tmp; michael@0: michael@0: return this->extractSubset(&tmp, *subset) && michael@0: // now call again with no rectangle michael@0: tmp.scrollRect(NULL, dx, dy, inval); michael@0: } michael@0: michael@0: int shift = this->bytesPerPixel() >> 1; michael@0: int width = this->width(); michael@0: int height = this->height(); michael@0: michael@0: // check if there's nothing to do michael@0: if ((dx | dy) == 0 || width <= 0 || height <= 0) { michael@0: if (NULL != inval) { michael@0: inval->setEmpty(); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // compute the inval region now, before we see if there are any pixels michael@0: if (NULL != inval) { michael@0: SkIRect r; michael@0: michael@0: r.set(0, 0, width, height); michael@0: // initial the region with the entire bounds michael@0: inval->setRect(r); michael@0: // do the "scroll" michael@0: r.offset(dx, dy); michael@0: michael@0: // check if we scrolled completely away michael@0: if (!SkIRect::Intersects(r, inval->getBounds())) { michael@0: // inval has already been updated... michael@0: return true; michael@0: } michael@0: michael@0: // compute the dirty area michael@0: inval->op(r, SkRegion::kDifference_Op); michael@0: } michael@0: michael@0: SkAutoLockPixels alp(*this); michael@0: // if we have no pixels, just return (inval is already updated) michael@0: // don't call readyToDraw(), since we don't require a colortable per se michael@0: if (this->getPixels() == NULL) { michael@0: return true; michael@0: } michael@0: michael@0: char* dst = (char*)this->getPixels(); michael@0: const char* src = dst; michael@0: int rowBytes = (int)this->rowBytes(); // need rowBytes to be signed michael@0: michael@0: if (dy <= 0) { michael@0: src -= dy * rowBytes; michael@0: height += dy; michael@0: } else { michael@0: dst += dy * rowBytes; michael@0: height -= dy; michael@0: // now jump src/dst to the last scanline michael@0: src += (height - 1) * rowBytes; michael@0: dst += (height - 1) * rowBytes; michael@0: // now invert rowbytes so we copy backwards in the loop michael@0: rowBytes = -rowBytes; michael@0: } michael@0: michael@0: if (dx <= 0) { michael@0: src -= dx << shift; michael@0: width += dx; michael@0: } else { michael@0: dst += dx << shift; michael@0: width -= dx; michael@0: } michael@0: michael@0: // If the X-translation would push it completely beyond the region, michael@0: // then there's nothing to draw. michael@0: if (width <= 0) { michael@0: return true; michael@0: } michael@0: michael@0: width <<= shift; // now width is the number of bytes to move per line michael@0: while (--height >= 0) { michael@0: memmove(dst, src, width); michael@0: dst += rowBytes; michael@0: src += rowBytes; michael@0: } michael@0: michael@0: this->notifyPixelsChanged(); michael@0: return true; michael@0: }