[CG] Have Canvas use the IOSurfacePool
[WebKit-https.git] / Source / WebCore / platform / graphics / cocoa / IOSurface.mm
1 /*
2  * Copyright (C) 2014 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 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 #import "config.h"
27 #import "IOSurface.h"
28
29 #if USE(IOSURFACE)
30
31 #import "GraphicsContextCG.h"
32 #import "IOSurfacePool.h"
33 #import "IOSurfaceSPI.h"
34 #import "MachSendRight.h"
35 #import <wtf/Assertions.h>
36
37 extern "C" {
38 CGContextRef CGIOSurfaceContextCreate(IOSurfaceRef, size_t, size_t, size_t, size_t, CGColorSpaceRef, CGBitmapInfo);
39 CGImageRef CGIOSurfaceContextCreateImage(CGContextRef);
40 }
41
42 using namespace WebCore;
43
44 inline std::unique_ptr<IOSurface> IOSurface::surfaceFromPool(IntSize size, IntSize contextSize, ColorSpace colorSpace)
45 {
46     auto cachedSurface = IOSurfacePool::sharedPool().takeSurface(size, colorSpace);
47     if (!cachedSurface)
48         return nullptr;
49
50     cachedSurface->setContextSize(contextSize);
51     return cachedSurface;
52 }
53
54 std::unique_ptr<IOSurface> IOSurface::create(IntSize size, ColorSpace colorSpace)
55 {
56     if (auto cachedSurface = surfaceFromPool(size, size, colorSpace))
57         return cachedSurface;
58     return std::unique_ptr<IOSurface>(new IOSurface(size, colorSpace));
59 }
60
61 std::unique_ptr<IOSurface> IOSurface::create(IntSize size, IntSize contextSize, ColorSpace colorSpace)
62 {
63     if (auto cachedSurface = surfaceFromPool(size, contextSize, colorSpace))
64         return cachedSurface;
65     return std::unique_ptr<IOSurface>(new IOSurface(size, contextSize, colorSpace));
66 }
67
68 std::unique_ptr<IOSurface> IOSurface::createFromSendRight(const MachSendRight& sendRight, ColorSpace colorSpace)
69 {
70     auto surface = adoptCF(IOSurfaceLookupFromMachPort(sendRight.sendRight()));
71     return IOSurface::createFromSurface(surface.get(), colorSpace);
72 }
73
74 std::unique_ptr<IOSurface> IOSurface::createFromSurface(IOSurfaceRef surface, ColorSpace colorSpace)
75 {
76     return std::unique_ptr<IOSurface>(new IOSurface(surface, colorSpace));
77 }
78
79 std::unique_ptr<IOSurface> IOSurface::createFromImage(CGImageRef image)
80 {
81     if (!image)
82         return nullptr;
83
84     size_t width = CGImageGetWidth(image);
85     size_t height = CGImageGetHeight(image);
86
87     auto surface = IOSurface::create(IntSize(width, height), ColorSpaceDeviceRGB);
88     auto surfaceContext = surface->ensurePlatformContext();
89     CGContextDrawImage(surfaceContext, CGRectMake(0, 0, width, height), image);
90     CGContextFlush(surfaceContext);
91
92     return surface;
93 }
94
95 IOSurface::IOSurface(IntSize size, ColorSpace colorSpace)
96     : m_colorSpace(colorSpace)
97     , m_size(size)
98     , m_contextSize(size)
99 {
100     unsigned pixelFormat = 'BGRA';
101     unsigned bytesPerElement = 4;
102     int width = size.width();
103     int height = size.height();
104
105     size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
106     ASSERT(bytesPerRow);
107
108     m_totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow);
109     ASSERT(m_totalBytes);
110
111     NSDictionary *options = @{
112         (id)kIOSurfaceWidth: @(width),
113         (id)kIOSurfaceHeight: @(height),
114         (id)kIOSurfacePixelFormat: @(pixelFormat),
115         (id)kIOSurfaceBytesPerElement: @(bytesPerElement),
116         (id)kIOSurfaceBytesPerRow: @(bytesPerRow),
117         (id)kIOSurfaceAllocSize: @(m_totalBytes),
118 #if PLATFORM(IOS)
119         (id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache)
120 #endif
121     };
122
123     m_surface = adoptCF(IOSurfaceCreate((CFDictionaryRef)options));
124 }
125
126 IOSurface::IOSurface(IntSize size, IntSize contextSize, ColorSpace colorSpace)
127     : IOSurface(size, colorSpace)
128 {
129     ASSERT(contextSize.width() <= size.width());
130     ASSERT(contextSize.height() <= size.height());
131     m_contextSize = contextSize;
132 }
133
134 IOSurface::IOSurface(IOSurfaceRef surface, ColorSpace colorSpace)
135     : m_colorSpace(colorSpace)
136     , m_surface(surface)
137 {
138     m_size = IntSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
139     m_totalBytes = IOSurfaceGetAllocSize(surface);
140 }
141
142 IntSize IOSurface::maximumSize()
143 {
144     return IntSize(IOSurfaceGetPropertyMaximum(kIOSurfaceWidth), IOSurfaceGetPropertyMaximum(kIOSurfaceHeight));
145 }
146
147 MachSendRight IOSurface::createSendRight() const
148 {
149     return MachSendRight::adopt(IOSurfaceCreateMachPort(m_surface.get()));
150 }
151
152 RetainPtr<CGImageRef> IOSurface::createImage()
153 {
154     return adoptCF(CGIOSurfaceContextCreateImage(ensurePlatformContext()));
155 }
156
157 void IOSurface::setContextSize(IntSize contextSize)
158 {
159     if (contextSize == m_contextSize)
160         return;
161
162     // Release the graphics context and update the context size. Next time the graphics context is
163     // accessed, we will construct it again with the right size.
164     releaseGraphicsContext();
165     m_contextSize = contextSize;
166 }
167
168 CGContextRef IOSurface::ensurePlatformContext()
169 {
170     if (m_cgContext)
171         return m_cgContext.get();
172
173     CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
174     size_t bitsPerComponent = 8;
175     size_t bitsPerPixel = 32;
176     m_cgContext = adoptCF(CGIOSurfaceContextCreate(m_surface.get(), m_contextSize.width(), m_contextSize.height(), bitsPerComponent, bitsPerPixel, cachedCGColorSpace(m_colorSpace), bitmapInfo));
177
178     return m_cgContext.get();
179 }
180
181 GraphicsContext& IOSurface::ensureGraphicsContext()
182 {
183     if (m_graphicsContext)
184         return *m_graphicsContext;
185
186     m_graphicsContext = adoptPtr(new GraphicsContext(ensurePlatformContext()));
187     m_graphicsContext->setIsAcceleratedContext(true);
188
189     return *m_graphicsContext;
190 }
191
192 IOSurface::SurfaceState IOSurface::state() const
193 {
194     uint32_t previousState = 0;
195     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
196     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
197     return previousState == kIOSurfacePurgeableEmpty ? IOSurface::SurfaceState::Empty : IOSurface::SurfaceState::Valid;
198 }
199
200 bool IOSurface::isVolatile() const
201 {
202     uint32_t previousState = 0;
203     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
204     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
205     return previousState != kIOSurfacePurgeableNonVolatile;
206 }
207
208 IOSurface::SurfaceState IOSurface::setIsVolatile(bool isVolatile)
209 {
210     uint32_t previousState = 0;
211     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), isVolatile ? kIOSurfacePurgeableVolatile : kIOSurfacePurgeableNonVolatile, &previousState);
212     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
213
214     if (previousState == kIOSurfacePurgeableEmpty)
215         return IOSurface::SurfaceState::Empty;
216
217     return IOSurface::SurfaceState::Valid;
218 }
219
220 bool IOSurface::isInUse() const
221 {
222     return IOSurfaceIsInUse(m_surface.get());
223 }
224
225 void IOSurface::releaseGraphicsContext()
226 {
227     m_graphicsContext = nullptr;
228     m_cgContext = nullptr;
229 }
230
231 #endif // USE(IOSURFACE)