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