summaryrefslogtreecommitdiffstats
path: root/chrome/browser/renderer_host/backing_store_mac.mm
blob: 6a5a177aa71eb09b3b504ed773e1a406703e1dcc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <Cocoa/Cocoa.h>

#include "chrome/browser/renderer_host/backing_store.h"
#include "chrome/browser/renderer_host/render_widget_host.h"
#include "chrome/browser/renderer_host/render_widget_host_view.h"

#include "base/logging.h"
#include "base/mac_util.h"
#include "chrome/common/transport_dib.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "third_party/skia/include/core/SkCanvas.h"

// Mac Backing Stores:
//
// Since backing stores are only ever written to or drawn into windows, we keep
// our backing store in a CGLayer that can get cached in GPU memory.  This
// allows acclerated drawing into the layer and lets scrolling and such happen
// all or mostly on the GPU, which is good for performance.

BackingStore::BackingStore(RenderWidgetHost* widget, const gfx::Size& size)
    : render_widget_host_(widget),
      size_(size),
      cg_layer_(NULL) {
  // We want our CGLayer to be optimized for drawing into our containing
  // window, so extract a CGContext corresponding to that window that we can
  // pass to CGLayerCreateWithContext.
  NSWindow* containing_window = [widget->view()->GetNativeView() window];
  if (!containing_window) {
    // If we are not in a containing window yet, create a CGBitmapContext
    // to use as a stand-in for the layer.
    cg_bitmap_.reset(CGBitmapContextCreate(NULL, size.width(), size.height(),
        8, size.width() * 8, mac_util::GetSystemColorSpace(),
        kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
  } else {
    CGContextRef context = static_cast<CGContextRef>(
        [[containing_window graphicsContext] graphicsPort]);
    CGLayerRef layer = CGLayerCreateWithContext(context, size.ToCGSize(), NULL);
    cg_layer_.reset(layer);
  }
}

BackingStore::~BackingStore() {
}

size_t BackingStore::MemorySize() {
  // Estimate memory usage as 4 bytes per pixel.
  return size_.GetArea() * 4;
}

// Paint the contents of a TransportDIB into a rectangle of our CGLayer
void BackingStore::PaintRect(base::ProcessHandle process,
                             TransportDIB* bitmap,
                             const gfx::Rect& bitmap_rect) {
  scoped_cftyperef<CGDataProviderRef> data_provider(
      CGDataProviderCreateWithData(NULL, bitmap->memory(),
      bitmap_rect.width() * bitmap_rect.height() * 4, NULL));
  scoped_cftyperef<CGImageRef> image(
      CGImageCreate(bitmap_rect.width(), bitmap_rect.height(), 8, 32,
          4 * bitmap_rect.width(), mac_util::GetSystemColorSpace(),
          kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host,
          data_provider, NULL, false, kCGRenderingIntentDefault));

  if (!cg_layer()) {
    // we don't have a CGLayer yet, so see if we can create one.
    NSWindow* containing_window =
        [render_widget_host()->view()->GetNativeView() window];
    if (containing_window) {
      CGContextRef context = static_cast<CGContextRef>(
          [[containing_window graphicsContext] graphicsPort]);
      CGLayerRef layer =
          CGLayerCreateWithContext(context, size().ToCGSize(), NULL);
      cg_layer_.reset(layer);
      // now that we have a layer, copy the cached image into it
      scoped_cftyperef<CGImageRef> bitmap_image(
          CGBitmapContextCreateImage(cg_bitmap_));
      CGContextDrawImage(CGLayerGetContext(layer),
                         CGRectMake(0, 0, size().width(), size().height()),
                         bitmap_image);
      // Discard the cache bitmap, since we no longer need it.
      cg_bitmap_.reset(NULL);
    }
  }

  if (cg_layer()) {
    // The CGLayer's origin is in the lower left, but flipping the CTM would
    // cause the image to get drawn upside down.  So we move the rectangle
    // to the right position before drawing the image.
    CGContextRef layer = CGLayerGetContext(cg_layer());
    gfx::Rect paint_rect = bitmap_rect;
    paint_rect.set_y(size_.height() - bitmap_rect.bottom());
    CGContextDrawImage(layer, paint_rect.ToCGRect(), image);
  } else {
    // The layer hasn't been created yet, so draw into the cache bitmap.
    gfx::Rect paint_rect = bitmap_rect;
    paint_rect.set_y(size_.height() - bitmap_rect.bottom());
    CGContextDrawImage(cg_bitmap_, paint_rect.ToCGRect(), image);
  }
}

// Scroll the contents of our CGLayer
void BackingStore::ScrollRect(base::ProcessHandle process,
                              TransportDIB* bitmap,
                              const gfx::Rect& bitmap_rect,
                              int dx, int dy,
                              const gfx::Rect& clip_rect,
                              const gfx::Size& view_size) {
  // "Scroll" the contents of the layer by creating a new CGLayer,
  // copying the contents of the old one into the new one offset by the scroll
  // amount, swapping in the new CGLayer, and then painting in the new data.
  //
  // 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 scroll.)

  // We assume |clip_rect| is contained within the backing store.
  CGSize layer_size = CGLayerGetSize(cg_layer());
  DCHECK(clip_rect.bottom() <= layer_size.height);
  DCHECK(clip_rect.right() <= layer_size.width);

  if ((dx && abs(dx) < layer_size.width) ||
      (dy && abs(dy) < layer_size.height)) {
    if (cg_layer()) {
      CGContextRef context = CGLayerGetContext(cg_layer());
      scoped_cftyperef<CGLayerRef> new_layer(
          CGLayerCreateWithContext(context, layer_size, NULL));
      CGContextRef layer = CGLayerGetContext(new_layer);
      CGContextDrawLayerAtPoint(layer, CGPointMake(0, 0), cg_layer());
      CGContextSaveGState(layer);
      CGContextClipToRect(layer, CGRectMake(clip_rect.x(),
                                            size_.height() - clip_rect.bottom(),
                                            clip_rect.width(),
                                            clip_rect.height()));
      CGContextDrawLayerAtPoint(layer, CGPointMake(dx, -dy), cg_layer());
      CGContextRestoreGState(layer);
      cg_layer_.swap(new_layer);
    } else {
      // We don't have a layer, so scroll the contents of the CGBitmapContext.
      scoped_cftyperef<CGContextRef> new_bitmap(
          CGBitmapContextCreate(NULL, size_.width(), size_.height(), 8,
              size_.width() * 8, mac_util::GetSystemColorSpace(),
              kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
      scoped_cftyperef<CGImageRef> bitmap_image(
          CGBitmapContextCreateImage(cg_bitmap_));
      CGContextDrawImage(new_bitmap,
                         CGRectMake(0, 0, size_.width(), size_.height()),
                         bitmap_image);
      CGContextSaveGState(new_bitmap);
      CGContextClipToRect(new_bitmap,
                          CGRectMake(clip_rect.x(),
                                     size_.height() - clip_rect.bottom(),
                                     clip_rect.width(),
                                     clip_rect.height()));
      CGContextDrawImage(new_bitmap,
                         CGRectMake(dx, -dy, size_.width(), size_.height()),
                         bitmap_image);
      CGContextRestoreGState(new_bitmap);
      cg_bitmap_.swap(new_bitmap);
    }
  }
  // Now paint the new bitmap data
  PaintRect(process, bitmap, bitmap_rect);
  return;
}