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"
16 SourceSurfaceCG::~SourceSurfaceCG()
18 CGImageRelease(mImage
);
22 SourceSurfaceCG::GetSize() const
25 size
.width
= CGImageGetWidth(mImage
);
26 size
.height
= CGImageGetHeight(mImage
);
31 SourceSurfaceCG::GetFormat() const
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
);
46 static void releaseCallback(void *info
, const void *data
, size_t size
) {
51 CreateCGImage(void *aInfo
,
55 SurfaceFormat aFormat
)
57 //XXX: we should avoid creating this colorspace everytime
58 CGColorSpaceRef colorSpace
= nullptr;
59 CGBitmapInfo bitinfo
= 0;
60 int bitsPerComponent
= 0;
65 colorSpace
= CGColorSpaceCreateDeviceRGB();
66 bitinfo
= kCGImageAlphaPremultipliedFirst
| kCGBitmapByteOrder32Host
;
72 colorSpace
= CGColorSpaceCreateDeviceRGB();
73 bitinfo
= kCGImageAlphaNoneSkipFirst
| kCGBitmapByteOrder32Host
;
79 // XXX: why don't we set a colorspace here?
88 CGDataProviderRef dataProvider
= CGDataProviderCreateWithData(aInfo
,
90 aSize
.height
* aStride
,
94 if (aFormat
== FORMAT_A8
) {
95 CGFloat decode
[] = {1.0, 0.0};
96 image
= CGImageMaskCreate (aSize
.width
, aSize
.height
,
104 image
= CGImageCreate (aSize
.width
, aSize
.height
,
113 kCGRenderingIntentDefault
);
116 CGDataProviderRelease(dataProvider
);
117 CGColorSpaceRelease(colorSpace
);
123 SourceSurfaceCG::InitFromData(unsigned char *aData
,
124 const IntSize
&aSize
,
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
)));
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
);
149 DataSourceSurfaceCG::GetSize() const
152 size
.width
= CGImageGetWidth(mImage
);
153 size
.height
= CGImageGetHeight(mImage
);
158 DataSourceSurfaceCG::InitFromData(unsigned char *aData
,
159 const IntSize
&aSize
,
161 SurfaceFormat aFormat
)
163 if (aSize
.width
<= 0 || aSize
.height
<= 0) {
167 void *data
= malloc(aStride
* aSize
.height
);
168 memcpy(data
, aData
, aStride
* (aSize
.height
- 1) + (aSize
.width
* BytesPerPixel(aFormat
)));
171 mImage
= CreateCGImage(data
, data
, aSize
, aStride
, aFormat
);
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();
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
,
207 kCGBitmapByteOrder32Host
| kCGImageAlphaPremultipliedFirst
);
210 CGColorSpaceRelease(colorSpace
);
215 DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage
)
217 mFormat
= FORMAT_B8G8R8A8
;
219 mCg
= CreateBitmapContextForImage(aImage
);
220 if (mCg
== nullptr) {
221 // error creating context
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
237 mData
= CGBitmapContextGetData(mCg
);
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
);
263 mSize
.width
= CGBitmapContextGetWidth(mCg
);
264 mSize
.height
= CGBitmapContextGetHeight(mCg
);
265 mStride
= CGBitmapContextGetBytesPerRow(mCg
);
266 mData
= CGBitmapContextGetData(mCg
);
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.
283 // if we have an mCg than it owns the data
284 // and we don't want to tranfer ownership
285 // to the CGDataProviderCreateWithData
288 // otherwise we transfer ownership to
295 mImage
= CreateCGImage(info
, mData
, mSize
, mStride
, mFormat
);
300 SourceSurfaceCGBitmapContext::GetSize() const
306 SourceSurfaceCGBitmapContext::DrawTargetWillChange()
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
323 CGImageRelease(mImage
);
327 mDrawTarget
= nullptr;
331 SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
333 if (!mImage
&& !mCg
) {
334 // neither mImage or mCg owns the data
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);
355 aDrawTarget
->Flush();
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
;
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.
380 mImage
= CreateCGImage(mData
, mData
, mSize
, mStride
, FORMAT_B8G8R8A8
);
386 SourceSurfaceCGIOSurfaceContext::GetSize() const
392 SourceSurfaceCGIOSurfaceContext::DrawTargetWillChange()
396 SourceSurfaceCGIOSurfaceContext::~SourceSurfaceCGIOSurfaceContext()
399 CGImageRelease(mImage
);
405 SourceSurfaceCGIOSurfaceContext::GetData()
407 return (unsigned char*)mData
;