1/*
2 * Copyright (C) 2009 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. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CACFLayerTreeHost.h"
28
29#if USE(ACCELERATED_COMPOSITING)
30
31#include "CACFLayerTreeHostClient.h"
32#include "DefWndProcWindowClass.h"
33#include "LayerChangesFlusher.h"
34#include "LegacyCACFLayerTreeHost.h"
35#include "PlatformCALayer.h"
36#include "WKCACFViewLayerTreeHost.h"
37#include "WebCoreInstanceHandle.h"
38#include <limits.h>
39#include <QuartzCore/CABase.h>
40#include <wtf/CurrentTime.h>
41#include <wtf/OwnArrayPtr.h>
42
43#ifdef DEBUG_ALL
44#pragma comment(lib, "QuartzCore_debug")
45#else
46#pragma comment(lib, "QuartzCore")
47#endif
48
49inline static CGRect winRectToCGRect(RECT rc)
50{
51    return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top));
52}
53
54inline static CGRect winRectToCGRect(RECT rc, RECT relativeToRect)
55{
56    return CGRectMake(rc.left, (relativeToRect.bottom-rc.bottom), (rc.right - rc.left), (rc.bottom - rc.top));
57}
58
59namespace WebCore {
60
61bool CACFLayerTreeHost::acceleratedCompositingAvailable()
62{
63    static bool available;
64    static bool tested;
65
66    if (tested)
67        return available;
68
69    tested = true;
70
71    // Initialize available to true since this function will be called from a
72    // propagation within createRenderer(). We want to be able to return true
73    // when that happens so that the test can continue.
74    available = true;
75
76    HMODULE library = LoadLibrary(TEXT("d3d9.dll"));
77    if (!library) {
78        available = false;
79        return available;
80    }
81
82    FreeLibrary(library);
83#ifdef DEBUG_ALL
84    library = LoadLibrary(TEXT("QuartzCore_debug.dll"));
85#else
86    library = LoadLibrary(TEXT("QuartzCore.dll"));
87#endif
88    if (!library) {
89        available = false;
90        return available;
91    }
92
93    FreeLibrary(library);
94
95    // Make a dummy HWND.
96    HWND testWindow = ::CreateWindow(defWndProcWindowClassName(), L"CoreAnimationTesterWindow", WS_POPUP, -500, -500, 20, 20, 0, 0, 0, 0);
97
98    if (!testWindow) {
99        available = false;
100        return available;
101    }
102
103    RefPtr<CACFLayerTreeHost> host = CACFLayerTreeHost::create();
104    host->setWindow(testWindow);
105    available = host->createRenderer();
106    host->setWindow(0);
107    ::DestroyWindow(testWindow);
108
109    return available;
110}
111
112PassRefPtr<CACFLayerTreeHost> CACFLayerTreeHost::create()
113{
114    if (!acceleratedCompositingAvailable())
115        return 0;
116    RefPtr<CACFLayerTreeHost> host = WKCACFViewLayerTreeHost::create();
117    if (!host)
118        host = LegacyCACFLayerTreeHost::create();
119    host->initialize();
120    return host.release();
121}
122
123CACFLayerTreeHost::CACFLayerTreeHost()
124    : m_client(0)
125    , m_rootLayer(PlatformCALayer::create(PlatformCALayer::LayerTypeRootLayer, 0))
126    , m_window(0)
127    , m_shouldFlushPendingGraphicsLayerChanges(false)
128    , m_isFlushingLayerChanges(false)
129#if !ASSERT_DISABLED
130    , m_state(WindowNotSet)
131#endif
132{
133}
134
135void CACFLayerTreeHost::initialize()
136{
137    // Point the CACFContext to this
138    initializeContext(this, m_rootLayer.get());
139
140    // Under the root layer, we have a clipping layer to clip the content,
141    // that contains a scroll layer that we use for scrolling the content.
142    // The root layer is the size of the client area of the window.
143    // The clipping layer is the size of the WebView client area (window less the scrollbars).
144    // The scroll layer is the size of the root child layer.
145    // Resizing the window will change the bounds of the rootLayer and the clip layer and will not
146    // cause any repositioning.
147    // Scrolling will affect only the position of the scroll layer without affecting the bounds.
148
149    m_rootLayer->setName("CACFLayerTreeHost rootLayer");
150    m_rootLayer->setAnchorPoint(FloatPoint3D(0, 0, 0));
151    m_rootLayer->setGeometryFlipped(true);
152
153#ifndef NDEBUG
154    CGColorRef debugColor = CGColorCreateGenericRGB(1, 0, 0, 0.8);
155    m_rootLayer->setBackgroundColor(debugColor);
156    CGColorRelease(debugColor);
157#endif
158}
159
160CACFLayerTreeHost::~CACFLayerTreeHost()
161{
162    ASSERT_WITH_MESSAGE(m_state != WindowSet, "Must call setWindow(0) before destroying CACFLayerTreeHost");
163}
164
165void CACFLayerTreeHost::setWindow(HWND window)
166{
167    if (window == m_window)
168        return;
169
170#if !ASSERT_DISABLED
171    switch (m_state) {
172    case WindowNotSet:
173        ASSERT_ARG(window, window);
174        ASSERT(!m_window);
175        m_state = WindowSet;
176        break;
177    case WindowSet:
178        ASSERT_ARG(window, !window);
179        ASSERT(m_window);
180        m_state = WindowCleared;
181        break;
182    case WindowCleared:
183        ASSERT_NOT_REACHED();
184        break;
185    }
186#endif
187
188    if (m_window)
189        destroyRenderer();
190
191    m_window = window;
192}
193
194PlatformCALayer* CACFLayerTreeHost::rootLayer() const
195{
196    return m_rootLayer.get();
197}
198
199void CACFLayerTreeHost::addPendingAnimatedLayer(PassRefPtr<PlatformCALayer> layer)
200{
201    m_pendingAnimatedLayers.add(layer);
202}
203
204void CACFLayerTreeHost::setRootChildLayer(PlatformCALayer* layer)
205{
206    m_rootLayer->removeAllSublayers();
207    m_rootChildLayer = layer;
208    if (m_rootChildLayer)
209        m_rootLayer->appendSublayer(m_rootChildLayer.get());
210}
211
212void CACFLayerTreeHost::layerTreeDidChange()
213{
214    if (m_isFlushingLayerChanges) {
215        // The layer tree is changing as a result of flushing GraphicsLayer changes to their
216        // underlying PlatformCALayers. We'll flush those changes to the context as part of that
217        // process, so there's no need to schedule another flush here.
218        return;
219    }
220
221    // The layer tree is changing as a result of someone modifying a PlatformCALayer that doesn't
222    // have a corresponding GraphicsLayer. Schedule a flush since we won't schedule one through the
223    // normal GraphicsLayer mechanisms.
224    LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
225}
226
227void CACFLayerTreeHost::destroyRenderer()
228{
229    m_rootLayer = 0;
230    m_rootChildLayer = 0;
231    LayerChangesFlusher::shared().cancelPendingFlush(this);
232}
233
234static void getDirtyRects(HWND window, Vector<CGRect>& outRects)
235{
236    ASSERT_ARG(outRects, outRects.isEmpty());
237
238    RECT clientRect;
239    if (!GetClientRect(window, &clientRect))
240        return;
241
242    OwnPtr<HRGN> region = adoptPtr(CreateRectRgn(0, 0, 0, 0));
243    int regionType = GetUpdateRgn(window, region.get(), false);
244    if (regionType != COMPLEXREGION) {
245        RECT dirtyRect;
246        if (GetUpdateRect(window, &dirtyRect, false))
247            outRects.append(winRectToCGRect(dirtyRect, clientRect));
248        return;
249    }
250
251    DWORD dataSize = GetRegionData(region.get(), 0, 0);
252    OwnArrayPtr<unsigned char> regionDataBuffer = adoptArrayPtr(new unsigned char[dataSize]);
253    RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get());
254    if (!GetRegionData(region.get(), dataSize, regionData))
255        return;
256
257    outRects.resize(regionData->rdh.nCount);
258
259    RECT* rect = reinterpret_cast<RECT*>(regionData->Buffer);
260    for (size_t i = 0; i < outRects.size(); ++i, ++rect)
261        outRects[i] = winRectToCGRect(*rect, clientRect);
262}
263
264void CACFLayerTreeHost::paint()
265{
266    Vector<CGRect> dirtyRects;
267    getDirtyRects(m_window, dirtyRects);
268    render(dirtyRects);
269}
270
271void CACFLayerTreeHost::flushPendingGraphicsLayerChangesSoon()
272{
273    m_shouldFlushPendingGraphicsLayerChanges = true;
274    LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this);
275}
276
277void CACFLayerTreeHost::setShouldInvertColors(bool)
278{
279}
280
281void CACFLayerTreeHost::flushPendingLayerChangesNow()
282{
283    // Calling out to the client could cause our last reference to go away.
284    RefPtr<CACFLayerTreeHost> protector(this);
285
286    m_isFlushingLayerChanges = true;
287
288    // Flush changes stored up in GraphicsLayers to their underlying PlatformCALayers, if
289    // requested.
290    if (m_client && m_shouldFlushPendingGraphicsLayerChanges) {
291        m_shouldFlushPendingGraphicsLayerChanges = false;
292        m_client->flushPendingGraphicsLayerChanges();
293    }
294
295    // Flush changes stored up in PlatformCALayers to the context so they will be rendered.
296    flushContext();
297
298    m_isFlushingLayerChanges = false;
299}
300
301void CACFLayerTreeHost::contextDidChange()
302{
303    // All pending animations will have been started with the flush. Fire the animationStarted calls.
304    notifyAnimationsStarted();
305}
306
307void CACFLayerTreeHost::notifyAnimationsStarted()
308{
309    // Send currentTime to the pending animations. This function is called by CACF in a callback
310    // which occurs after the drawInContext calls. So currentTime is very close to the time
311    // the animations actually start
312    double currentTime = WTF::currentTime();
313
314    HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end();
315    for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it)
316        (*it)->animationStarted(currentTime);
317
318    m_pendingAnimatedLayers.clear();
319}
320
321CGRect CACFLayerTreeHost::bounds() const
322{
323    RECT clientRect;
324    GetClientRect(m_window, &clientRect);
325
326    return winRectToCGRect(clientRect);
327}
328
329}
330
331#endif // USE(ACCELERATED_COMPOSITING)
332