| /* | |
| * 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; | |
| } |