[Win] Support High DPI drawing with CACFLayers
[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, Format pixelFormat)
55 {
56     // YUV422 IOSurfaces do not go in the pool.
57     // FIXME: Want pooling of RGB10, RGB10A8.
58     if (pixelFormat == Format::RGBA) {
59         if (auto cachedSurface = surfaceFromPool(size, size, colorSpace))
60             return cachedSurface;
61     }
62
63     return std::unique_ptr<IOSurface>(new IOSurface(size, colorSpace, pixelFormat));
64 }
65
66 std::unique_ptr<IOSurface> IOSurface::create(IntSize size, IntSize contextSize, ColorSpace colorSpace)
67 {
68     if (auto cachedSurface = surfaceFromPool(size, contextSize, colorSpace))
69         return cachedSurface;
70     return std::unique_ptr<IOSurface>(new IOSurface(size, contextSize, colorSpace));
71 }
72
73 std::unique_ptr<IOSurface> IOSurface::createFromSendRight(const MachSendRight& sendRight, ColorSpace colorSpace)
74 {
75     auto surface = adoptCF(IOSurfaceLookupFromMachPort(sendRight.sendRight()));
76     return IOSurface::createFromSurface(surface.get(), colorSpace);
77 }
78
79 std::unique_ptr<IOSurface> IOSurface::createFromSurface(IOSurfaceRef surface, ColorSpace colorSpace)
80 {
81     return std::unique_ptr<IOSurface>(new IOSurface(surface, colorSpace));
82 }
83
84 std::unique_ptr<IOSurface> IOSurface::createFromImage(CGImageRef image)
85 {
86     if (!image)
87         return nullptr;
88
89     size_t width = CGImageGetWidth(image);
90     size_t height = CGImageGetHeight(image);
91
92     auto surface = IOSurface::create(IntSize(width, height), ColorSpaceSRGB);
93     auto surfaceContext = surface->ensurePlatformContext();
94     CGContextDrawImage(surfaceContext, CGRectMake(0, 0, width, height), image);
95     CGContextFlush(surfaceContext);
96
97     return surface;
98 }
99
100 IOSurface::IOSurface(IntSize size, ColorSpace colorSpace, Format format)
101     : m_colorSpace(colorSpace)
102     , m_size(size)
103     , m_contextSize(size)
104 {
105     unsigned pixelFormat;
106     unsigned bytesPerPixel;
107     unsigned bytesPerElement;
108
109     int width = size.width();
110     int height = size.height();
111
112     NSDictionary *options;
113     
114     if (format == Format::RGB10A8) {
115         pixelFormat = 'b3a8';
116         
117         // RGB plane (10-10-10)
118         bytesPerPixel = 4;
119         bytesPerElement = 4;
120
121         size_t rgbPlaneBytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
122         size_t rgbPlaneTotalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * rgbPlaneBytesPerRow);
123
124         // Alpha plane (8)
125         bytesPerElement = 1;
126         size_t alphaPlaneBytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerElement);
127         size_t alphaPlaneTotalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * alphaPlaneBytesPerRow);
128         
129         m_totalBytes = rgbPlaneTotalBytes + alphaPlaneTotalBytes;
130
131         NSArray *planeInfo = @[
132             @{
133                 (id)kIOSurfacePlaneWidth: @(width),
134                 (id)kIOSurfacePlaneHeight: @(height),
135                 (id)kIOSurfacePlaneBytesPerRow: @(rgbPlaneBytesPerRow),
136                 (id)kIOSurfacePlaneOffset: @(0),
137                 (id)kIOSurfacePlaneSize: @(rgbPlaneTotalBytes)
138             },
139             @{
140                 (id)kIOSurfacePlaneWidth: @(width),
141                 (id)kIOSurfacePlaneHeight: @(height),
142                 (id)kIOSurfacePlaneBytesPerRow: @(alphaPlaneBytesPerRow),
143                 (id)kIOSurfacePlaneOffset: @(rgbPlaneTotalBytes),
144                 (id)kIOSurfacePlaneSize: @(alphaPlaneTotalBytes)
145             }
146         ];
147
148         options = @{
149             (id)kIOSurfaceWidth: @(width),
150             (id)kIOSurfaceHeight: @(height),
151             (id)kIOSurfacePixelFormat: @(pixelFormat),
152             (id)kIOSurfaceAllocSize: @(m_totalBytes),
153 #if PLATFORM(IOS)
154             (id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache),
155 #endif
156             (id)kIOSurfacePlaneInfo: planeInfo,
157         };
158     } else {
159         unsigned elementWidth;
160
161         switch (format) {
162         case Format::RGBA:
163             pixelFormat = 'BGRA';
164             bytesPerPixel = 4;
165             bytesPerElement = 4;
166             elementWidth = 1;
167             break;
168         case Format::YUV422:
169             pixelFormat = 'yuvf';
170             bytesPerPixel = 2;
171             bytesPerElement = 4;
172             elementWidth = 2;
173             break;
174         case Format::RGB10:
175             pixelFormat = 'w30r';
176             bytesPerPixel = 4;
177             bytesPerElement = 4;
178             elementWidth = 1;
179             break;
180         case Format::RGB10A8:
181             ASSERT_NOT_REACHED();
182             pixelFormat = 'b3a8';
183             bytesPerPixel = 1;
184             bytesPerElement = 1;
185             elementWidth = 1;
186             break;
187         }
188
189         size_t bytesPerRow = IOSurfaceAlignProperty(kIOSurfaceBytesPerRow, width * bytesPerPixel);
190         ASSERT(bytesPerRow);
191
192         m_totalBytes = IOSurfaceAlignProperty(kIOSurfaceAllocSize, height * bytesPerRow);
193         ASSERT(m_totalBytes);
194
195         options = @{
196             (id)kIOSurfaceWidth: @(width),
197             (id)kIOSurfaceHeight: @(height),
198             (id)kIOSurfacePixelFormat: @(pixelFormat),
199             (id)kIOSurfaceBytesPerElement: @(bytesPerElement),
200             (id)kIOSurfaceBytesPerRow: @(bytesPerRow),
201             (id)kIOSurfaceAllocSize: @(m_totalBytes),
202 #if PLATFORM(IOS)
203             (id)kIOSurfaceCacheMode: @(kIOMapWriteCombineCache),
204 #endif
205             (id)kIOSurfaceElementWidth: @(elementWidth),
206             (id)kIOSurfaceElementHeight: @(1)
207         };
208     }
209     
210     m_surface = adoptCF(IOSurfaceCreate((CFDictionaryRef)options));
211     if (!m_surface)
212         NSLog(@"Surface creation failed for options %@", options);
213 }
214
215 IOSurface::IOSurface(IntSize size, IntSize contextSize, ColorSpace colorSpace)
216     : IOSurface(size, colorSpace, Format::RGBA)
217 {
218     ASSERT(contextSize.width() <= size.width());
219     ASSERT(contextSize.height() <= size.height());
220     m_contextSize = contextSize;
221 }
222
223 IOSurface::IOSurface(IOSurfaceRef surface, ColorSpace colorSpace)
224     : m_colorSpace(colorSpace)
225     , m_surface(surface)
226 {
227     m_size = IntSize(IOSurfaceGetWidth(surface), IOSurfaceGetHeight(surface));
228     m_totalBytes = IOSurfaceGetAllocSize(surface);
229 }
230
231 IntSize IOSurface::maximumSize()
232 {
233     return IntSize(IOSurfaceGetPropertyMaximum(kIOSurfaceWidth), IOSurfaceGetPropertyMaximum(kIOSurfaceHeight));
234 }
235
236 MachSendRight IOSurface::createSendRight() const
237 {
238     return MachSendRight::adopt(IOSurfaceCreateMachPort(m_surface.get()));
239 }
240
241 RetainPtr<CGImageRef> IOSurface::createImage()
242 {
243     return adoptCF(CGIOSurfaceContextCreateImage(ensurePlatformContext()));
244 }
245
246 void IOSurface::setContextSize(IntSize contextSize)
247 {
248     if (contextSize == m_contextSize)
249         return;
250
251     // Release the graphics context and update the context size. Next time the graphics context is
252     // accessed, we will construct it again with the right size.
253     releaseGraphicsContext();
254     m_contextSize = contextSize;
255 }
256
257 CGContextRef IOSurface::ensurePlatformContext()
258 {
259     if (m_cgContext)
260         return m_cgContext.get();
261
262     CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
263
264     size_t bitsPerComponent = 8;
265     size_t bitsPerPixel = 32;
266     
267     switch (format()) {
268     case Format::RGBA:
269         break;
270     case Format::RGB10:
271         bitsPerComponent = 10;
272         bitsPerPixel = 32;
273         break;
274     case Format::RGB10A8:
275         // FIXME: This doesn't take the two-plane format into account.
276         bitsPerComponent = 10;
277         bitsPerPixel = 32;
278         break;
279     case Format::YUV422:
280         ASSERT_NOT_REACHED();
281         break;
282     }
283     
284     m_cgContext = adoptCF(CGIOSurfaceContextCreate(m_surface.get(), m_contextSize.width(), m_contextSize.height(), bitsPerComponent, bitsPerPixel, cachedCGColorSpace(m_colorSpace), bitmapInfo));
285
286     return m_cgContext.get();
287 }
288
289 GraphicsContext& IOSurface::ensureGraphicsContext()
290 {
291     if (m_graphicsContext)
292         return *m_graphicsContext;
293
294     m_graphicsContext = std::make_unique<GraphicsContext>(ensurePlatformContext());
295     m_graphicsContext->setIsAcceleratedContext(true);
296
297     return *m_graphicsContext;
298 }
299
300 IOSurface::SurfaceState IOSurface::state() const
301 {
302     uint32_t previousState = 0;
303     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
304     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
305     return previousState == kIOSurfacePurgeableEmpty ? IOSurface::SurfaceState::Empty : IOSurface::SurfaceState::Valid;
306 }
307
308 bool IOSurface::isVolatile() const
309 {
310     uint32_t previousState = 0;
311     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), kIOSurfacePurgeableKeepCurrent, &previousState);
312     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
313     return previousState != kIOSurfacePurgeableNonVolatile;
314 }
315
316 IOSurface::SurfaceState IOSurface::setIsVolatile(bool isVolatile)
317 {
318     uint32_t previousState = 0;
319     IOReturn ret = IOSurfaceSetPurgeable(m_surface.get(), isVolatile ? kIOSurfacePurgeableVolatile : kIOSurfacePurgeableNonVolatile, &previousState);
320     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
321
322     if (previousState == kIOSurfacePurgeableEmpty)
323         return IOSurface::SurfaceState::Empty;
324
325     return IOSurface::SurfaceState::Valid;
326 }
327
328 IOSurface::Format IOSurface::format() const
329 {
330     unsigned pixelFormat = IOSurfaceGetPixelFormat(m_surface.get());
331     if (pixelFormat == 'BGRA')
332         return Format::RGBA;
333
334     if (pixelFormat == 'w30r')
335         return Format::RGB10;
336
337     if (pixelFormat == 'b3a8')
338         return Format::RGB10A8;
339
340     if (pixelFormat == 'yuvf')
341         return Format::YUV422;
342
343     ASSERT_NOT_REACHED();
344     return Format::RGBA;
345 }
346
347 bool IOSurface::isInUse() const
348 {
349     return IOSurfaceIsInUse(m_surface.get());
350 }
351
352 void IOSurface::releaseGraphicsContext()
353 {
354     m_graphicsContext = nullptr;
355     m_cgContext = nullptr;
356 }
357
358 #if PLATFORM(IOS)
359 WEBCORE_EXPORT void IOSurface::copyToSurface(IOSurface& destSurface)
360 {
361     if (destSurface.format() != format()) {
362         WTFLogAlways("Trying to copy IOSurface to another surface with a different format");
363         return;
364     }
365
366     if (destSurface.size() != size()) {
367         WTFLogAlways("Trying to copy IOSurface to another surface with a different size");
368         return;
369     }
370
371     static IOSurfaceAcceleratorRef accelerator;
372     if (!accelerator)
373         IOSurfaceAcceleratorCreate(nullptr, nullptr, &accelerator);
374
375     IOReturn ret = IOSurfaceAcceleratorTransformSurface(accelerator, m_surface.get(), destSurface.surface(), nullptr, nullptr, nullptr, nullptr, nullptr);
376     if (ret)
377         WTFLogAlways("IOSurfaceAcceleratorTransformSurface %p to %p failed with error %d", m_surface.get(), destSurface.surface(), ret);
378 }
379
380 void IOSurface::convertToFormat(std::unique_ptr<WebCore::IOSurface>&& inSurface, Format format, std::function<void(std::unique_ptr<WebCore::IOSurface>)> callback)
381 {
382     static IOSurfaceAcceleratorRef accelerator;
383     if (!accelerator) {
384         IOSurfaceAcceleratorCreate(nullptr, nullptr, &accelerator);
385
386         auto runLoopSource = IOSurfaceAcceleratorGetRunLoopSource(accelerator);
387         CFRunLoopAddSource(CFRunLoopGetMain(), runLoopSource, kCFRunLoopDefaultMode);
388     }
389
390     if (inSurface->format() == format) {
391         callback(WTF::move(inSurface));
392         return;
393     }
394
395     auto destinationSurface = IOSurface::create(inSurface->size(), inSurface->colorSpace(), format);
396     IOSurfaceRef destinationIOSurfaceRef = destinationSurface->surface();
397
398     IOSurfaceAcceleratorCompletion completion;
399     completion.completionRefCon = new std::function<void(std::unique_ptr<IOSurface>)> (WTF::move(callback));
400     completion.completionRefCon2 = destinationSurface.release();
401     completion.completionCallback = [](void *completionRefCon, IOReturn, void * completionRefCon2) {
402         auto* callback = static_cast<std::function<void(std::unique_ptr<WebCore::IOSurface>)>*>(completionRefCon);
403         auto destinationSurface = std::unique_ptr<IOSurface>(static_cast<IOSurface*>(completionRefCon2));
404         
405         (*callback)(WTF::move(destinationSurface));
406         delete callback;
407     };
408
409     IOReturn ret = IOSurfaceAcceleratorTransformSurface(accelerator, inSurface->surface(), destinationIOSurfaceRef, nullptr, nullptr, &completion, nullptr, nullptr);
410     ASSERT_UNUSED(ret, ret == kIOReturnSuccess);
411 }
412 #endif // PLATFORM(IOS)
413
414 #endif // USE(IOSURFACE)