Merge m-c to inbound.
[gecko.git] / gfx / 2d / SourceSurfaceCG.cpp
blobe4ad395cdf5a5963ffaab409217be91c115b707a
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "SourceSurfaceCG.h"
7 #include "DrawTargetCG.h"
9 #include "MacIOSurface.h"
10 #include "Tools.h"
12 namespace mozilla {
13 namespace gfx {
16 SourceSurfaceCG::~SourceSurfaceCG()
18 CGImageRelease(mImage);
21 IntSize
22 SourceSurfaceCG::GetSize() const
24 IntSize size;
25 size.width = CGImageGetWidth(mImage);
26 size.height = CGImageGetHeight(mImage);
27 return size;
30 SurfaceFormat
31 SourceSurfaceCG::GetFormat() const
33 return mFormat;
36 TemporaryRef<DataSourceSurface>
37 SourceSurfaceCG::GetDataSurface()
39 //XXX: we should be more disciplined about who takes a reference and where
40 CGImageRetain(mImage);
41 RefPtr<DataSourceSurfaceCG> dataSurf =
42 new DataSourceSurfaceCG(mImage);
43 return dataSurf;
46 static void releaseCallback(void *info, const void *data, size_t size) {
47 free(info);
50 static CGImageRef
51 CreateCGImage(void *aInfo,
52 const void *aData,
53 const IntSize &aSize,
54 int32_t aStride,
55 SurfaceFormat aFormat)
57 //XXX: we should avoid creating this colorspace everytime
58 CGColorSpaceRef colorSpace = nullptr;
59 CGBitmapInfo bitinfo = 0;
60 int bitsPerComponent = 0;
61 int bitsPerPixel = 0;
63 switch (aFormat) {
64 case FORMAT_B8G8R8A8:
65 colorSpace = CGColorSpaceCreateDeviceRGB();
66 bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
67 bitsPerComponent = 8;
68 bitsPerPixel = 32;
69 break;
71 case FORMAT_B8G8R8X8:
72 colorSpace = CGColorSpaceCreateDeviceRGB();
73 bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
74 bitsPerComponent = 8;
75 bitsPerPixel = 32;
76 break;
78 case FORMAT_A8:
79 // XXX: why don't we set a colorspace here?
80 bitsPerComponent = 8;
81 bitsPerPixel = 8;
82 break;
84 default:
85 MOZ_CRASH();
88 CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo,
89 aData,
90 aSize.height * aStride,
91 releaseCallback);
93 CGImageRef image;
94 if (aFormat == FORMAT_A8) {
95 CGFloat decode[] = {1.0, 0.0};
96 image = CGImageMaskCreate (aSize.width, aSize.height,
97 bitsPerComponent,
98 bitsPerPixel,
99 aStride,
100 dataProvider,
101 decode,
102 true);
103 } else {
104 image = CGImageCreate (aSize.width, aSize.height,
105 bitsPerComponent,
106 bitsPerPixel,
107 aStride,
108 colorSpace,
109 bitinfo,
110 dataProvider,
111 nullptr,
112 true,
113 kCGRenderingIntentDefault);
116 CGDataProviderRelease(dataProvider);
117 CGColorSpaceRelease(colorSpace);
119 return image;
122 bool
123 SourceSurfaceCG::InitFromData(unsigned char *aData,
124 const IntSize &aSize,
125 int32_t aStride,
126 SurfaceFormat aFormat)
128 assert(aSize.width >= 0 && aSize.height >= 0);
130 void *data = malloc(aStride * aSize.height);
131 // Copy all the data except the stride padding on the very last
132 // row since we can't guarantee that is readable.
133 memcpy(data, aData, aStride * (aSize.height - 1) + (aSize.width * BytesPerPixel(aFormat)));
135 mFormat = aFormat;
136 mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
138 return mImage != nullptr;
141 DataSourceSurfaceCG::~DataSourceSurfaceCG()
143 CGImageRelease(mImage);
144 free(CGBitmapContextGetData(mCg));
145 CGContextRelease(mCg);
148 IntSize
149 DataSourceSurfaceCG::GetSize() const
151 IntSize size;
152 size.width = CGImageGetWidth(mImage);
153 size.height = CGImageGetHeight(mImage);
154 return size;
157 bool
158 DataSourceSurfaceCG::InitFromData(unsigned char *aData,
159 const IntSize &aSize,
160 int32_t aStride,
161 SurfaceFormat aFormat)
163 if (aSize.width <= 0 || aSize.height <= 0) {
164 return false;
167 void *data = malloc(aStride * aSize.height);
168 memcpy(data, aData, aStride * (aSize.height - 1) + (aSize.width * BytesPerPixel(aFormat)));
170 mFormat = aFormat;
171 mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
173 if (!mImage) {
174 free(data);
175 return false;
178 return true;
181 CGContextRef CreateBitmapContextForImage(CGImageRef image)
183 CGColorSpaceRef colorSpace;
185 size_t width = CGImageGetWidth(image);
186 size_t height = CGImageGetHeight(image);
188 int bitmapBytesPerRow = (width * 4);
189 int bitmapByteCount = (bitmapBytesPerRow * height);
191 void *data = calloc(bitmapByteCount, 1);
192 //XXX: which color space should we be using here?
193 colorSpace = CGColorSpaceCreateDeviceRGB();
194 assert(colorSpace);
196 // we'd like to pass nullptr as the first parameter
197 // to let Quartz manage this memory for us. However,
198 // on 10.5 and older CGBitmapContextGetData will return
199 // nullptr instead of the associated buffer so we need
200 // to manage it ourselves.
201 CGContextRef cg = CGBitmapContextCreate(data,
202 width,
203 height,
205 bitmapBytesPerRow,
206 colorSpace,
207 kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
208 assert(cg);
210 CGColorSpaceRelease(colorSpace);
212 return cg;
215 DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage)
217 mFormat = FORMAT_B8G8R8A8;
218 mImage = aImage;
219 mCg = CreateBitmapContextForImage(aImage);
220 if (mCg == nullptr) {
221 // error creating context
222 return;
225 // Get image width, height. We'll use the entire image.
226 CGFloat w = CGImageGetWidth(aImage);
227 CGFloat h = CGImageGetHeight(aImage);
228 CGRect rect = {{0,0},{w,h}};
230 // Draw the image to the bitmap context. Once we draw, the memory
231 // allocated for the context for rendering will then contain the
232 // raw image data in the specified color space.
233 CGContextDrawImage(mCg, rect, aImage);
235 // Now we can get a pointer to the image data associated with the bitmap
236 // context.
237 mData = CGBitmapContextGetData(mCg);
238 assert(mData);
241 unsigned char *
242 DataSourceSurfaceCG::GetData()
244 // See http://developer.apple.com/library/mac/#qa/qa1509/_index.html
245 // the following only works on 10.5+, the Q&A above suggests a method
246 // that can be used for earlier versions
247 //CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
248 //unsigned char *dataPtr = CFDataGetBytePtr(data);
249 //CFDataRelease(data);
250 // unfortunately the the method above only works for read-only access and
251 // we need read-write for DataSourceSurfaces
252 return (unsigned char*)mData;
255 SourceSurfaceCGBitmapContext::SourceSurfaceCGBitmapContext(DrawTargetCG *aDrawTarget)
257 mDrawTarget = aDrawTarget;
258 mFormat = aDrawTarget->GetFormat();
259 mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT);
260 if (!mCg)
261 abort();
263 mSize.width = CGBitmapContextGetWidth(mCg);
264 mSize.height = CGBitmapContextGetHeight(mCg);
265 mStride = CGBitmapContextGetBytesPerRow(mCg);
266 mData = CGBitmapContextGetData(mCg);
268 mImage = nullptr;
271 void SourceSurfaceCGBitmapContext::EnsureImage() const
273 // Instead of using CGBitmapContextCreateImage we create
274 // a CGImage around the data associated with the CGBitmapContext
275 // we do this to avoid the vm_copy that CGBitmapContextCreateImage.
276 // vm_copy tends to cause all sorts of unexpected performance problems
277 // because of the mm tricks that vm_copy does. Using a regular
278 // memcpy when the bitmap context is modified gives us more predictable
279 // performance characteristics.
280 if (!mImage) {
281 void *info;
282 if (mCg) {
283 // if we have an mCg than it owns the data
284 // and we don't want to tranfer ownership
285 // to the CGDataProviderCreateWithData
286 info = nullptr;
287 } else {
288 // otherwise we transfer ownership to
289 // the dataProvider
290 info = mData;
293 if (!mData) abort();
295 mImage = CreateCGImage(info, mData, mSize, mStride, mFormat);
299 IntSize
300 SourceSurfaceCGBitmapContext::GetSize() const
302 return mSize;
305 void
306 SourceSurfaceCGBitmapContext::DrawTargetWillChange()
308 if (mDrawTarget) {
309 // This will break the weak reference we hold to mCg
310 size_t stride = CGBitmapContextGetBytesPerRow(mCg);
311 size_t height = CGBitmapContextGetHeight(mCg);
313 //XXX: infalliable malloc?
314 mData = malloc(stride * height);
316 // copy out the data from the CGBitmapContext
317 // we'll maintain ownership of mData until
318 // we transfer it to mImage
319 memcpy(mData, CGBitmapContextGetData(mCg), stride*height);
321 // drop the current image for the data associated with the CGBitmapContext
322 if (mImage)
323 CGImageRelease(mImage);
324 mImage = nullptr;
326 mCg = nullptr;
327 mDrawTarget = nullptr;
331 SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
333 if (!mImage && !mCg) {
334 // neither mImage or mCg owns the data
335 free(mData);
337 if (mImage)
338 CGImageRelease(mImage);
341 SourceSurfaceCGIOSurfaceContext::SourceSurfaceCGIOSurfaceContext(DrawTargetCG *aDrawTarget)
343 CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NATIVE_SURFACE_CGCONTEXT_ACCELERATED);
345 RefPtr<MacIOSurface> surf = MacIOSurface::IOSurfaceContextGetSurface(cg);
347 mFormat = aDrawTarget->GetFormat();
348 mSize.width = surf->GetWidth();
349 mSize.height = surf->GetHeight();
351 // TODO use CreateImageFromIOSurfaceContext instead of reading back the surface
352 //mImage = MacIOSurface::CreateImageFromIOSurfaceContext(cg);
353 mImage = nullptr;
355 aDrawTarget->Flush();
356 surf->Lock();
357 size_t bytesPerRow = surf->GetBytesPerRow();
358 size_t ioHeight = surf->GetHeight();
359 void* ioData = surf->GetBaseAddress();
360 // XXX If the width is much less then the stride maybe
361 // we should repack the image?
362 mData = malloc(ioHeight*bytesPerRow);
363 memcpy(mData, ioData, ioHeight*(bytesPerRow));
364 mStride = bytesPerRow;
365 surf->Unlock();
368 void SourceSurfaceCGIOSurfaceContext::EnsureImage() const
370 // TODO Use CreateImageFromIOSurfaceContext and remove this
372 // Instead of using CGBitmapContextCreateImage we create
373 // a CGImage around the data associated with the CGBitmapContext
374 // we do this to avoid the vm_copy that CGBitmapContextCreateImage.
375 // vm_copy tends to cause all sorts of unexpected performance problems
376 // because of the mm tricks that vm_copy does. Using a regular
377 // memcpy when the bitmap context is modified gives us more predictable
378 // performance characteristics.
379 if (!mImage) {
380 mImage = CreateCGImage(mData, mData, mSize, mStride, FORMAT_B8G8R8A8);
385 IntSize
386 SourceSurfaceCGIOSurfaceContext::GetSize() const
388 return mSize;
391 void
392 SourceSurfaceCGIOSurfaceContext::DrawTargetWillChange()
396 SourceSurfaceCGIOSurfaceContext::~SourceSurfaceCGIOSurfaceContext()
398 if (mImage)
399 CGImageRelease(mImage);
400 else
401 free(mData);
404 unsigned char*
405 SourceSurfaceCGIOSurfaceContext::GetData()
407 return (unsigned char*)mData;