diff options
author | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-05 18:00:28 +0000 |
---|---|---|
committer | agl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-02-05 18:00:28 +0000 |
commit | 937a4583a290d621f50c1788346584f79aab5a29 (patch) | |
tree | 789028cbf29f2fb1967ff3dc65412e6b2f9dcd95 /chrome/browser | |
parent | c66a0ebbcbd5792cf58ffd4944cc493f1ffe3ea9 (diff) | |
download | chromium_src-937a4583a290d621f50c1788346584f79aab5a29.zip chromium_src-937a4583a290d621f50c1788346584f79aab5a29.tar.gz chromium_src-937a4583a290d621f50c1788346584f79aab5a29.tar.bz2 |
POSIX: Backing store scrolling.
In the long term, we plan to get rid of all this code and "do things
properly". However, for now we are implementing several shortcuts to
get something which can render. Part one of this was BitmapWireData
(r9065) which transported updates from the renderer over the IPC
channel. This patch implements the fast-scrolling path, slowly.
It might seem that this is something which Skia should be doing.
However, copying from one bitmap to the same bitmap needs to be
handled very differently from bitblitting from one to another to save
writing to locations that you'll need to read from later on. I can't
see any indication in the Skia code that it handles this case so, in
order to get something working quickly, we write our own, small,
bitblitter.
Since this code hasn't been tested, it's almost certainly buggy.
However, we don't get to test it until we get a little more of the
browser up and running.
Review URL: http://codereview.chromium.org/21050
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@9223 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/renderer_host/backing_store_posix.cc | 134 |
1 files changed, 132 insertions, 2 deletions
diff --git a/chrome/browser/renderer_host/backing_store_posix.cc b/chrome/browser/renderer_host/backing_store_posix.cc index a21b235..0407ba4 100644 --- a/chrome/browser/renderer_host/backing_store_posix.cc +++ b/chrome/browser/renderer_host/backing_store_posix.cc @@ -4,6 +4,11 @@ #include "chrome/browser/renderer_host/backing_store.h" +#include "base/logging.h" +#include "skia/ext/platform_canvas.h" +#include "skia/include/SkBitmap.h" +#include "skia/include/SkCanvas.h" + BackingStore::BackingStore(const gfx::Size& size) : size_(size) { if (!canvas_.initialize(size.width(), size.height(), true)) @@ -26,12 +31,137 @@ bool BackingStore::PaintRect(base::ProcessHandle process, return true; } +// Return the given value, clipped to 0..max (inclusive) +static int RangeClip(int value, int max) { + return std::min(max, std::max(value, 0)); +} + void BackingStore::ScrollRect(base::ProcessHandle process, BitmapWireData bitmap, const gfx::Rect& bitmap_rect, int dx, int dy, const gfx::Rect& clip_rect, const gfx::Size& view_size) { - // TODO(port): implement scrolling - NOTIMPLEMENTED(); + // WARNING: this is temporary code until a real solution is found for Mac and + // Linux. + // + // On Windows, there's a ScrollDC call which performs horiz and vertical + // scrolling + // + // clip_rect: MSDN says "The only bits that will be painted are the + // bits that remain inside this rectangle after the scroll operation has been + // completed." + // + // The Windows code always sets the whole backing store as the source of the + // scroll. Thus, we only have to worry about pixels which will end up inside + // the clipping rectangle. (Note that the clipping rectangle is not + // translated by the scrol.) + + // We only support scrolling in one direction at a time. + DCHECK(dx == 0 || dy == 0); + + if (bitmap.width() != bitmap_rect.width() || + bitmap.height() != bitmap_rect.height() || + bitmap.config() != SkBitmap::kARGB_8888_Config) { + return; + } + + // We assume that |clip_rect| is within the backing store. + + const SkBitmap &backing_bitmap = canvas_.getDevice()->accessBitmap(true); + const int stride = backing_bitmap.rowBytes(); + uint8_t* x = static_cast<uint8_t*>(backing_bitmap.getPixels()); + + if (dx) { + // Horizontal scroll. Positive values of |dx| scroll right. + + // We know that |clip_rect| is the destination rectangle. The source + // rectangle, in an infinite world, would be the destination rectangle + // translated by -dx. However, we can't pull pixels from beyound the + // edge of the backing store. By truncating the source rectangle to the + // backing store, we might have made it thinner, thus we find the final + // dest rectangle by translating the resulting source rectangle back. + const int bs_width = canvas_.getDevice()->width(); + const int src_rect_left = RangeClip(clip_rect.x() - dx, bs_width); + const int src_rect_right = RangeClip(clip_rect.right() - dx, bs_width); + + const int dest_rect_left = RangeClip(src_rect_left + dx, bs_width); + const int dest_rect_right = RangeClip(src_rect_right + dx, bs_width); + + DCHECK(dest_rect_right >= dest_rect_left); + DCHECK(src_rect_right >= src_rect_left); + DCHECK(dest_rect_right - dest_rect_left == + src_rect_right - src_rect_left); + + // This is the number of bytes to move per line at 4 bytes per pixel. + const int len = (dest_rect_right - dest_rect_left) * 4; + + // Position the pixel data pointer at the top-left corner of the dest rect + x += clip_rect.y() * stride; + x += clip_rect.x() * 4; + + // This is the number of bytes to reach left in order to find the source + // rectangle. (Will be negative for a left scroll.) + const int left_reach = dest_rect_left - src_rect_left; + + for (int y = clip_rect.y(); y < clip_rect.bottom(); ++y) { + // Note that overlapping regions requires memmove, not memcpy. + memmove(x, x + left_reach, len); + x += stride; + } + } else { + // Vertical scroll. Positive values of |dy| scroll down. + + // The logic is very similar to the horizontal case, above. + const int bs_height = canvas_.getDevice()->height(); + const int src_rect_top = RangeClip(clip_rect.y() - dy, bs_height); + const int src_rect_bottom = RangeClip(clip_rect.bottom() - dy, bs_height); + + const int dest_rect_top = RangeClip(src_rect_top + dy, bs_height); + const int dest_rect_bottom = RangeClip(src_rect_bottom + dy, bs_height); + + const int len = clip_rect.width() * 4; + + DCHECK(dest_rect_bottom >= dest_rect_top); + DCHECK(src_rect_bottom >= src_rect_top); + + // We need to consider the two directions separately here because the order + // of copying rows must vary to avoid writing data which we later need to + // read. + if (dy > 0) { + // Scrolling down; dest rect is above src rect. + + // Position the pixel data pointer at the top-left corner of the dest + // rect. + x += dest_rect_top * stride; + x += clip_rect.x() * 4; + + DCHECK(dest_rect_top <= src_rect_top); + const int down_reach_bytes = stride * (src_rect_top - dest_rect_top); + + for (int y = dest_rect_top; y < dest_rect_bottom; ++y) { + memcpy(x, x + down_reach_bytes, len); + x += stride; + } + } else { + // Scrolling up; dest rect is below src rect. + + // Position the pixel data pointer at the bottom-left corner of the dest + // rect. + x += (dest_rect_bottom - 1) * stride; + x += clip_rect.x() * 4; + + DCHECK(src_rect_top <= dest_rect_top); + const int up_reach_bytes = stride * (dest_rect_bottom - src_rect_bottom); + + for (int y = dest_rect_bottom - 1; y >= dest_rect_top; --y) { + memcpy(x, x - up_reach_bytes, len); + x -= stride; + } + } + } + + // Now paint the new bitmap data. + canvas_.drawBitmap(bitmap, bitmap_rect.x(), bitmap_rect.y()); + return; } |