<rdar://problem/10891801> BackingStore::scroll() unnecessarily copies pixels around
[WebKit-https.git] / Source / WebKit2 / UIProcess / mac / BackingStoreMac.mm
1 /*
2  * Copyright (C) 2011 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "BackingStore.h"
28
29 #import "CGUtilities.h"
30 #import "ShareableBitmap.h"
31 #import "UpdateInfo.h"
32 #import "WebPageProxy.h"
33 #import <WebCore/GraphicsContext.h>
34 #import <WebCore/Region.h>
35
36 using namespace WebCore;
37
38 namespace WebKit {
39
40 void BackingStore::performWithScrolledRectTransform(const IntRect& rect, void (^block)(const IntRect&, const IntSize&))
41 {
42     if (m_scrolledRect.isEmpty() || m_scrolledRectOffset.isZero() || !m_scrolledRect.intersects(rect)) {
43         block(rect, IntSize());
44         return;
45     }
46
47     // The part of rect that's outside the scrolled rect is not translated.
48     Region untranslatedRegion = rect;
49     untranslatedRegion.subtract(m_scrolledRect);
50     Vector<IntRect> untranslatedRects = untranslatedRegion.rects();
51     for (size_t i = 0; i < untranslatedRects.size(); ++i)
52         block(untranslatedRects[i], IntSize());
53
54     // The part of rect that intersects the scrolled rect comprises up to four parts, each subject
55     // to a different translation (all translations are equivalent modulo the dimensions of the
56     // scrolled rect to the scroll offset).
57     IntRect intersection = rect;
58     intersection.intersect(m_scrolledRect);
59
60     IntRect scrolledRect = m_scrolledRect;
61     IntSize offset = m_scrolledRectOffset;
62     scrolledRect.move(-offset);
63
64     IntRect part = intersection;
65     part.intersect(scrolledRect);
66     if (!part.isEmpty())
67         block(part, offset);
68
69     part = intersection;
70     offset += IntSize(0, -m_scrolledRect.height());
71     scrolledRect.move(IntSize(0, m_scrolledRect.height()));
72     part.intersect(scrolledRect);
73     if (!part.isEmpty())
74         block(part, offset);
75
76     part = intersection;
77     offset += IntSize(-m_scrolledRect.width(), 0);
78     scrolledRect.move(IntSize(m_scrolledRect.width(), 0));
79     part.intersect(scrolledRect);
80     if (!part.isEmpty())
81         block(part, offset);
82
83     part = intersection;
84     offset += IntSize(0, m_scrolledRect.height());
85     scrolledRect.move(IntSize(0, -m_scrolledRect.height()));
86     part.intersect(scrolledRect);
87     if (!part.isEmpty())
88         block(part, offset);
89 }
90
91 void BackingStore::resetScrolledRect()
92 {
93     ASSERT(!m_scrolledRect.isEmpty());
94
95     if (m_scrolledRectOffset.isZero()) {
96         m_scrolledRect = IntRect();
97         return;
98     }
99
100     RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
101     RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(0, m_scrolledRect.size().width(), m_scrolledRect.size().height(), 8, m_scrolledRect.size().width() * 4, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
102
103     CGContextTranslateCTM(context.get(), -m_scrolledRect.location().x(), -m_scrolledRect.location().y());
104     CGContextTranslateCTM(context.get(), 0, m_scrolledRect.size().height());
105     CGContextScaleCTM(context.get(), 1, -1);
106     paint(context.get(), m_scrolledRect);
107
108     IntRect sourceRect(IntPoint(), m_scrolledRect.size());
109     paintBitmapContext(backingStoreContext(), context.get(), m_scrolledRect.location(), sourceRect);
110
111     m_scrolledRect = IntRect();
112     m_scrolledRectOffset = IntSize();
113 }
114
115 void BackingStore::paint(PlatformGraphicsContext context, const IntRect& rect)
116 {
117     // FIXME: This is defined outside the block to work around bugs in llvm-gcc 4.2.
118     __block CGRect source;
119     performWithScrolledRectTransform(rect, ^(const IntRect& part, const IntSize& offset) {
120         if (m_cgLayer) {
121             CGContextSaveGState(context);
122             CGContextClipToRect(context, part);
123
124             CGContextScaleCTM(context, 1, -1);
125             CGContextDrawLayerAtPoint(context, CGPointMake(-offset.width(), offset.height() - m_size.height()), m_cgLayer.get());
126
127             CGContextRestoreGState(context);
128             return;
129         }
130
131         ASSERT(m_bitmapContext);
132         source = part;
133         source.origin.x += offset.width();
134         source.origin.y += offset.height();
135         paintBitmapContext(context, m_bitmapContext.get(), part.location(), source);
136     });
137 }
138
139 CGContextRef BackingStore::backingStoreContext()
140 {
141     if (m_cgLayer)
142         return CGLayerGetContext(m_cgLayer.get());
143
144     // Try to create a layer.
145     if (CGContextRef containingWindowContext = m_webPageProxy->containingWindowGraphicsContext()) {
146         m_cgLayer.adoptCF(CGLayerCreateWithContext(containingWindowContext, NSSizeToCGSize(m_size), 0));
147         CGContextRef layerContext = CGLayerGetContext(m_cgLayer.get());
148         
149         CGContextSetBlendMode(layerContext, kCGBlendModeCopy);
150
151         // We want the origin to be in the top left corner so flip the backing store context.
152         CGContextTranslateCTM(layerContext, 0, m_size.height());
153         CGContextScaleCTM(layerContext, 1, -1);
154
155         if (m_bitmapContext) {
156             // Paint the contents of the bitmap into the layer context.
157             paintBitmapContext(layerContext, m_bitmapContext.get(), CGPointZero, CGRectMake(0, 0, m_size.width(), m_size.height()));
158             m_bitmapContext = nullptr;
159         }
160
161         return layerContext;
162     }
163
164     if (!m_bitmapContext) {
165         RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
166         
167         m_bitmapContext.adoptCF(CGBitmapContextCreate(0, m_size.width(), m_size.height(), 8, m_size.width() * 4, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
168
169         CGContextSetBlendMode(m_bitmapContext.get(), kCGBlendModeCopy);
170
171         // We want the origin to be in the top left corner so flip the backing store context.
172         CGContextTranslateCTM(m_bitmapContext.get(), 0, m_size.height());
173         CGContextScaleCTM(m_bitmapContext.get(), 1, -1);
174     }
175
176     return m_bitmapContext.get();
177 }
178
179 void BackingStore::incorporateUpdate(ShareableBitmap* bitmap, const UpdateInfo& updateInfo)
180 {
181     CGContextRef context = backingStoreContext();
182
183     scroll(updateInfo.scrollRect, updateInfo.scrollOffset);
184
185     IntPoint updateRectLocation = updateInfo.updateRectBounds.location();
186
187     GraphicsContext ctx(context);
188     __block GraphicsContext* graphicsContext = &ctx;
189
190     // Paint all update rects.
191     for (size_t i = 0; i < updateInfo.updateRects.size(); ++i) {
192         IntRect updateRect = updateInfo.updateRects[i];
193         IntRect srcRect = updateRect;
194         // FIXME: This is defined outside the block to work around bugs in llvm-gcc 4.2.
195         __block IntRect srcPart;
196         performWithScrolledRectTransform(srcRect, ^(const IntRect& part, const IntSize& offset) {
197             srcPart = part;
198             srcPart.move(-updateRectLocation.x(), -updateRectLocation.y());
199             bitmap->paint(*graphicsContext, updateInfo.deviceScaleFactor, part.location() + offset, srcPart);
200         });
201     }
202 }
203
204 void BackingStore::scroll(const IntRect& scrollRect, const IntSize& scrollOffset)
205 {
206     if (scrollOffset.isZero())
207         return;
208
209     if (!m_scrolledRect.isEmpty() && m_scrolledRect != scrollRect)
210         resetScrolledRect();
211
212     m_scrolledRect = scrollRect;
213
214     int width = (m_scrolledRectOffset.width() - scrollOffset.width()) % m_scrolledRect.width();
215     if (width < 0)
216         width += m_scrolledRect.width();
217     m_scrolledRectOffset.setWidth(width);
218
219     int height = (m_scrolledRectOffset.height() - scrollOffset.height()) % m_scrolledRect.height();
220     if (height < 0)
221         height += m_scrolledRect.height();
222     m_scrolledRectOffset.setHeight(height);
223 }
224
225 } // namespace WebKit