| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import <Cocoa/Cocoa.h> | |
| 6 | |
| 7 #include "chrome/browser/renderer_host/backing_store_mac.h" | |
| 8 | |
| 9 #include "app/surface/transport_dib.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/mac/mac_util.h" | |
| 12 #include "base/mac/scoped_cftyperef.h" | |
| 13 #include "base/sys_info.h" | |
| 14 #include "chrome/browser/renderer_host/render_process_host.h" | |
| 15 #include "chrome/browser/renderer_host/render_widget_host.h" | |
| 16 #include "chrome/browser/renderer_host/render_widget_host_view.h" | |
| 17 #include "skia/ext/platform_canvas.h" | |
| 18 #include "third_party/skia/include/core/SkBitmap.h" | |
| 19 #include "third_party/skia/include/core/SkCanvas.h" | |
| 20 #include "ui/gfx/rect.h" | |
| 21 | |
| 22 // Mac Backing Stores: | |
| 23 // | |
| 24 // Since backing stores are only ever written to or drawn into windows, we keep | |
| 25 // our backing store in a CGLayer that can get cached in GPU memory. This | |
| 26 // allows acclerated drawing into the layer and lets scrolling and such happen | |
| 27 // all or mostly on the GPU, which is good for performance. | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 // Returns whether this version of OS X has broken CGLayers, see | |
| 32 // http://crbug.com/45553 , comments 5 and 6. | |
| 33 bool NeedsLayerWorkaround() { | |
| 34 int32 os_major, os_minor, os_bugfix; | |
| 35 base::SysInfo::OperatingSystemVersionNumbers( | |
| 36 &os_major, &os_minor, &os_bugfix); | |
| 37 return os_major == 10 && os_minor == 5; | |
| 38 } | |
| 39 | |
| 40 } // namespace | |
| 41 | |
| 42 BackingStoreMac::BackingStoreMac(RenderWidgetHost* widget, | |
| 43 const gfx::Size& size) | |
| 44 : BackingStore(widget, size) { | |
| 45 cg_layer_.reset(CreateCGLayer()); | |
| 46 if (!cg_layer_) { | |
| 47 // The view isn't in a window yet. Use a CGBitmapContext for now. | |
| 48 cg_bitmap_.reset(CreateCGBitmapContext()); | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 BackingStoreMac::~BackingStoreMac() { | |
| 53 } | |
| 54 | |
| 55 void BackingStoreMac::PaintToBackingStore( | |
| 56 RenderProcessHost* process, | |
| 57 TransportDIB::Id bitmap, | |
| 58 const gfx::Rect& bitmap_rect, | |
| 59 const std::vector<gfx::Rect>& copy_rects) { | |
| 60 DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap())); | |
| 61 | |
| 62 TransportDIB* dib = process->GetTransportDIB(bitmap); | |
| 63 if (!dib) | |
| 64 return; | |
| 65 | |
| 66 base::mac::ScopedCFTypeRef<CGDataProviderRef> data_provider( | |
| 67 CGDataProviderCreateWithData(NULL, dib->memory(), | |
| 68 bitmap_rect.width() * bitmap_rect.height() * 4, NULL)); | |
| 69 | |
| 70 base::mac::ScopedCFTypeRef<CGImageRef> bitmap_image( | |
| 71 CGImageCreate(bitmap_rect.width(), bitmap_rect.height(), 8, 32, | |
| 72 4 * bitmap_rect.width(), base::mac::GetSystemColorSpace(), | |
| 73 kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, | |
| 74 data_provider, NULL, false, kCGRenderingIntentDefault)); | |
| 75 | |
| 76 for (size_t i = 0; i < copy_rects.size(); i++) { | |
| 77 const gfx::Rect& copy_rect = copy_rects[i]; | |
| 78 | |
| 79 // Only the subpixels given by copy_rect have pixels to copy. | |
| 80 base::mac::ScopedCFTypeRef<CGImageRef> image( | |
| 81 CGImageCreateWithImageInRect(bitmap_image, CGRectMake( | |
| 82 copy_rect.x() - bitmap_rect.x(), | |
| 83 copy_rect.y() - bitmap_rect.y(), | |
| 84 copy_rect.width(), | |
| 85 copy_rect.height()))); | |
| 86 | |
| 87 if (!cg_layer()) { | |
| 88 // The view may have moved to a window. Try to get a CGLayer. | |
| 89 cg_layer_.reset(CreateCGLayer()); | |
| 90 if (cg_layer()) { | |
| 91 // now that we have a layer, copy the cached image into it | |
| 92 base::mac::ScopedCFTypeRef<CGImageRef> bitmap_image( | |
| 93 CGBitmapContextCreateImage(cg_bitmap_)); | |
| 94 CGContextDrawImage(CGLayerGetContext(cg_layer()), | |
| 95 CGRectMake(0, 0, size().width(), size().height()), | |
| 96 bitmap_image); | |
| 97 // Discard the cache bitmap, since we no longer need it. | |
| 98 cg_bitmap_.reset(NULL); | |
| 99 } | |
| 100 } | |
| 101 | |
| 102 DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap())); | |
| 103 | |
| 104 if (cg_layer()) { | |
| 105 // The CGLayer's origin is in the lower left, but flipping the CTM would | |
| 106 // cause the image to get drawn upside down. So we move the rectangle | |
| 107 // to the right position before drawing the image. | |
| 108 CGContextRef layer = CGLayerGetContext(cg_layer()); | |
| 109 gfx::Rect paint_rect = copy_rect; | |
| 110 paint_rect.set_y(size().height() - copy_rect.bottom()); | |
| 111 CGContextDrawImage(layer, paint_rect.ToCGRect(), image); | |
| 112 } else { | |
| 113 // The layer hasn't been created yet, so draw into the cache bitmap. | |
| 114 gfx::Rect paint_rect = copy_rect; | |
| 115 paint_rect.set_y(size().height() - copy_rect.bottom()); | |
| 116 CGContextDrawImage(cg_bitmap_, paint_rect.ToCGRect(), image); | |
| 117 } | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 bool BackingStoreMac::CopyFromBackingStore(const gfx::Rect& rect, | |
| 122 skia::PlatformCanvas* output) { | |
| 123 if (!output->initialize(rect.width(), rect.height(), true)) | |
| 124 return false; | |
| 125 | |
| 126 CGContextRef temp_context = output->beginPlatformPaint(); | |
| 127 CGContextSaveGState(temp_context); | |
| 128 CGContextTranslateCTM(temp_context, 0.0, size().height()); | |
| 129 CGContextScaleCTM(temp_context, 1.0, -1.0); | |
| 130 CGContextDrawLayerAtPoint(temp_context, CGPointMake(rect.x(), rect.y()), | |
| 131 cg_layer()); | |
| 132 CGContextRestoreGState(temp_context); | |
| 133 output->endPlatformPaint(); | |
| 134 return true; | |
| 135 } | |
| 136 | |
| 137 // Scroll the contents of our CGLayer | |
| 138 void BackingStoreMac::ScrollBackingStore(int dx, int dy, | |
| 139 const gfx::Rect& clip_rect, | |
| 140 const gfx::Size& view_size) { | |
| 141 DCHECK_NE(static_cast<bool>(cg_layer()), static_cast<bool>(cg_bitmap())); | |
| 142 | |
| 143 // "Scroll" the contents of the layer by creating a new CGLayer, | |
| 144 // copying the contents of the old one into the new one offset by the scroll | |
| 145 // amount, swapping in the new CGLayer, and then painting in the new data. | |
| 146 // | |
| 147 // The Windows code always sets the whole backing store as the source of the | |
| 148 // scroll. Thus, we only have to worry about pixels which will end up inside | |
| 149 // the clipping rectangle. (Note that the clipping rectangle is not | |
| 150 // translated by the scroll.) | |
| 151 | |
| 152 // We assume |clip_rect| is contained within the backing store. | |
| 153 DCHECK(clip_rect.bottom() <= size().height()); | |
| 154 DCHECK(clip_rect.right() <= size().width()); | |
| 155 | |
| 156 if ((dx || dy) && abs(dx) < size().width() && abs(dy) < size().height()) { | |
| 157 if (cg_layer()) { | |
| 158 // See http://crbug.com/45553 , comments 5 and 6. | |
| 159 static bool needs_layer_workaround = NeedsLayerWorkaround(); | |
| 160 | |
| 161 base::mac::ScopedCFTypeRef<CGLayerRef> new_layer; | |
| 162 CGContextRef layer; | |
| 163 | |
| 164 if (needs_layer_workaround) { | |
| 165 new_layer.reset(CreateCGLayer()); | |
| 166 // If the current view is in a window, the replacement must be too. | |
| 167 DCHECK(new_layer); | |
| 168 | |
| 169 layer = CGLayerGetContext(new_layer); | |
| 170 CGContextDrawLayerAtPoint(layer, CGPointMake(0, 0), cg_layer()); | |
| 171 } else { | |
| 172 layer = CGLayerGetContext(cg_layer()); | |
| 173 } | |
| 174 | |
| 175 CGContextSaveGState(layer); | |
| 176 CGContextClipToRect(layer, | |
| 177 CGRectMake(clip_rect.x(), | |
| 178 size().height() - clip_rect.bottom(), | |
| 179 clip_rect.width(), | |
| 180 clip_rect.height())); | |
| 181 CGContextDrawLayerAtPoint(layer, CGPointMake(dx, -dy), cg_layer()); | |
| 182 CGContextRestoreGState(layer); | |
| 183 | |
| 184 if (needs_layer_workaround) | |
| 185 cg_layer_.swap(new_layer); | |
| 186 } else { | |
| 187 // We don't have a layer, so scroll the contents of the CGBitmapContext. | |
| 188 base::mac::ScopedCFTypeRef<CGImageRef> bitmap_image( | |
| 189 CGBitmapContextCreateImage(cg_bitmap_)); | |
| 190 CGContextSaveGState(cg_bitmap_); | |
| 191 CGContextClipToRect(cg_bitmap_, | |
| 192 CGRectMake(clip_rect.x(), | |
| 193 size().height() - clip_rect.bottom(), | |
| 194 clip_rect.width(), | |
| 195 clip_rect.height())); | |
| 196 CGContextDrawImage(cg_bitmap_, | |
| 197 CGRectMake(dx, -dy, size().width(), size().height()), | |
| 198 bitmap_image); | |
| 199 CGContextRestoreGState(cg_bitmap_); | |
| 200 } | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 CGLayerRef BackingStoreMac::CreateCGLayer() { | |
| 205 // The CGLayer should be optimized for drawing into the containing window, | |
| 206 // so extract a CGContext corresponding to the window to be passed to | |
| 207 // CGLayerCreateWithContext. | |
| 208 NSWindow* window = [render_widget_host()->view()->GetNativeView() window]; | |
| 209 if ([window windowNumber] <= 0) { | |
| 210 // This catches a nil |window|, as well as windows that exist but that | |
| 211 // aren't yet connected to WindowServer. | |
| 212 return NULL; | |
| 213 } | |
| 214 | |
| 215 NSGraphicsContext* ns_context = [window graphicsContext]; | |
| 216 DCHECK(ns_context); | |
| 217 | |
| 218 CGContextRef cg_context = static_cast<CGContextRef>( | |
| 219 [ns_context graphicsPort]); | |
| 220 DCHECK(cg_context); | |
| 221 | |
| 222 CGLayerRef layer = CGLayerCreateWithContext(cg_context, | |
| 223 size().ToCGSize(), | |
| 224 NULL); | |
| 225 DCHECK(layer); | |
| 226 | |
| 227 return layer; | |
| 228 } | |
| 229 | |
| 230 CGContextRef BackingStoreMac::CreateCGBitmapContext() { | |
| 231 // A CGBitmapContext serves as a stand-in for the layer before the view is | |
| 232 // in a containing window. | |
| 233 CGContextRef context = CGBitmapContextCreate(NULL, | |
| 234 size().width(), size().height(), | |
| 235 8, size().width() * 4, | |
| 236 base::mac::GetSystemColorSpace(), | |
| 237 kCGImageAlphaPremultipliedFirst | | |
| 238 kCGBitmapByteOrder32Host); | |
| 239 DCHECK(context); | |
| 240 | |
| 241 return context; | |
| 242 } | |
| OLD | NEW |