gfx/2d/SourceSurfaceCG.cpp

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:46841cf90939
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/. */
5
6 #include "SourceSurfaceCG.h"
7 #include "DrawTargetCG.h"
8 #include "DataSourceSurfaceWrapper.h"
9 #include "DataSurfaceHelpers.h"
10 #include "mozilla/Types.h" // for decltype
11
12 #include "MacIOSurface.h"
13 #include "Tools.h"
14
15 namespace mozilla {
16 namespace gfx {
17
18
19 SourceSurfaceCG::~SourceSurfaceCG()
20 {
21 CGImageRelease(mImage);
22 }
23
24 IntSize
25 SourceSurfaceCG::GetSize() const
26 {
27 IntSize size;
28 size.width = CGImageGetWidth(mImage);
29 size.height = CGImageGetHeight(mImage);
30 return size;
31 }
32
33 SurfaceFormat
34 SourceSurfaceCG::GetFormat() const
35 {
36 return mFormat;
37 }
38
39 TemporaryRef<DataSourceSurface>
40 SourceSurfaceCG::GetDataSurface()
41 {
42 //XXX: we should be more disciplined about who takes a reference and where
43 CGImageRetain(mImage);
44 RefPtr<DataSourceSurface> dataSurf = new DataSourceSurfaceCG(mImage);
45
46 // We also need to make sure that the returned surface has
47 // surface->GetType() == SurfaceType::DATA.
48 dataSurf = new DataSourceSurfaceWrapper(dataSurf);
49
50 return dataSurf;
51 }
52
53 static void releaseCallback(void *info, const void *data, size_t size) {
54 free(info);
55 }
56
57 CGImageRef
58 CreateCGImage(void *aInfo,
59 const void *aData,
60 const IntSize &aSize,
61 int32_t aStride,
62 SurfaceFormat aFormat)
63 {
64 //XXX: we should avoid creating this colorspace everytime
65 CGColorSpaceRef colorSpace = nullptr;
66 CGBitmapInfo bitinfo = 0;
67 int bitsPerComponent = 0;
68 int bitsPerPixel = 0;
69
70 switch (aFormat) {
71 case SurfaceFormat::B8G8R8A8:
72 colorSpace = CGColorSpaceCreateDeviceRGB();
73 bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
74 bitsPerComponent = 8;
75 bitsPerPixel = 32;
76 break;
77
78 case SurfaceFormat::B8G8R8X8:
79 colorSpace = CGColorSpaceCreateDeviceRGB();
80 bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
81 bitsPerComponent = 8;
82 bitsPerPixel = 32;
83 break;
84
85 case SurfaceFormat::A8:
86 // XXX: why don't we set a colorspace here?
87 bitsPerComponent = 8;
88 bitsPerPixel = 8;
89 break;
90
91 default:
92 MOZ_CRASH();
93 }
94
95 size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
96 if (bufLen == 0) {
97 return nullptr;
98 }
99 CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo,
100 aData,
101 bufLen,
102 releaseCallback);
103
104 CGImageRef image;
105 if (aFormat == SurfaceFormat::A8) {
106 CGFloat decode[] = {1.0, 0.0};
107 image = CGImageMaskCreate (aSize.width, aSize.height,
108 bitsPerComponent,
109 bitsPerPixel,
110 aStride,
111 dataProvider,
112 decode,
113 true);
114 } else {
115 image = CGImageCreate (aSize.width, aSize.height,
116 bitsPerComponent,
117 bitsPerPixel,
118 aStride,
119 colorSpace,
120 bitinfo,
121 dataProvider,
122 nullptr,
123 true,
124 kCGRenderingIntentDefault);
125 }
126
127 CGDataProviderRelease(dataProvider);
128 CGColorSpaceRelease(colorSpace);
129
130 return image;
131 }
132
133 bool
134 SourceSurfaceCG::InitFromData(unsigned char *aData,
135 const IntSize &aSize,
136 int32_t aStride,
137 SurfaceFormat aFormat)
138 {
139 assert(aSize.width >= 0 && aSize.height >= 0);
140
141 size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
142 if (bufLen == 0) {
143 mImage = nullptr;
144 return false;
145 }
146
147 void *data = malloc(bufLen);
148 // Copy all the data except the stride padding on the very last
149 // row since we can't guarantee that is readable.
150 memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat)));
151
152 mFormat = aFormat;
153 mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
154
155 return mImage != nullptr;
156 }
157
158 DataSourceSurfaceCG::~DataSourceSurfaceCG()
159 {
160 CGImageRelease(mImage);
161 free(CGBitmapContextGetData(mCg));
162 CGContextRelease(mCg);
163 }
164
165 IntSize
166 DataSourceSurfaceCG::GetSize() const
167 {
168 IntSize size;
169 size.width = CGImageGetWidth(mImage);
170 size.height = CGImageGetHeight(mImage);
171 return size;
172 }
173
174 bool
175 DataSourceSurfaceCG::InitFromData(unsigned char *aData,
176 const IntSize &aSize,
177 int32_t aStride,
178 SurfaceFormat aFormat)
179 {
180 if (aSize.width <= 0 || aSize.height <= 0) {
181 return false;
182 }
183
184 size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
185 if (bufLen == 0) {
186 mImage = nullptr;
187 return false;
188 }
189
190 void *data = malloc(bufLen);
191 memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat)));
192
193 mFormat = aFormat;
194 mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
195
196 if (!mImage) {
197 free(data);
198 return false;
199 }
200
201 return true;
202 }
203
204 CGContextRef CreateBitmapContextForImage(CGImageRef image)
205 {
206 CGColorSpaceRef colorSpace;
207
208 size_t width = CGImageGetWidth(image);
209 size_t height = CGImageGetHeight(image);
210
211 int bitmapBytesPerRow = (width * 4);
212 int bitmapByteCount = (bitmapBytesPerRow * height);
213
214 void *data = calloc(bitmapByteCount, 1);
215 //XXX: which color space should we be using here?
216 colorSpace = CGColorSpaceCreateDeviceRGB();
217 assert(colorSpace);
218
219 // we'd like to pass nullptr as the first parameter
220 // to let Quartz manage this memory for us. However,
221 // on 10.5 and older CGBitmapContextGetData will return
222 // nullptr instead of the associated buffer so we need
223 // to manage it ourselves.
224 CGContextRef cg = CGBitmapContextCreate(data,
225 width,
226 height,
227 8,
228 bitmapBytesPerRow,
229 colorSpace,
230 kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
231 assert(cg);
232
233 CGColorSpaceRelease(colorSpace);
234
235 return cg;
236 }
237
238 DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage)
239 {
240 mFormat = SurfaceFormat::B8G8R8A8;
241 mImage = aImage;
242 mCg = CreateBitmapContextForImage(aImage);
243 if (mCg == nullptr) {
244 // error creating context
245 return;
246 }
247
248 // Get image width, height. We'll use the entire image.
249 CGFloat w = CGImageGetWidth(aImage);
250 CGFloat h = CGImageGetHeight(aImage);
251 CGRect rect = {{0,0},{w,h}};
252
253 // Draw the image to the bitmap context. Once we draw, the memory
254 // allocated for the context for rendering will then contain the
255 // raw image data in the specified color space.
256 CGContextDrawImage(mCg, rect, aImage);
257
258 // Now we can get a pointer to the image data associated with the bitmap
259 // context.
260 mData = CGBitmapContextGetData(mCg);
261 assert(mData);
262 }
263
264 unsigned char *
265 DataSourceSurfaceCG::GetData()
266 {
267 // See http://developer.apple.com/library/mac/#qa/qa1509/_index.html
268 // the following only works on 10.5+, the Q&A above suggests a method
269 // that can be used for earlier versions
270 //CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
271 //unsigned char *dataPtr = CFDataGetBytePtr(data);
272 //CFDataRelease(data);
273 // unfortunately the the method above only works for read-only access and
274 // we need read-write for DataSourceSurfaces
275 return (unsigned char*)mData;
276 }
277
278 SourceSurfaceCGBitmapContext::SourceSurfaceCGBitmapContext(DrawTargetCG *aDrawTarget)
279 {
280 mDrawTarget = aDrawTarget;
281 mFormat = aDrawTarget->GetFormat();
282 mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT);
283 if (!mCg)
284 abort();
285
286 mSize.width = CGBitmapContextGetWidth(mCg);
287 mSize.height = CGBitmapContextGetHeight(mCg);
288 mStride = CGBitmapContextGetBytesPerRow(mCg);
289 mData = CGBitmapContextGetData(mCg);
290
291 mImage = nullptr;
292 }
293
294 void SourceSurfaceCGBitmapContext::EnsureImage() const
295 {
296 // Instead of using CGBitmapContextCreateImage we create
297 // a CGImage around the data associated with the CGBitmapContext
298 // we do this to avoid the vm_copy that CGBitmapContextCreateImage.
299 // vm_copy tends to cause all sorts of unexpected performance problems
300 // because of the mm tricks that vm_copy does. Using a regular
301 // memcpy when the bitmap context is modified gives us more predictable
302 // performance characteristics.
303 if (!mImage) {
304 if (!mData) abort();
305 mImage = CreateCGImage(nullptr, mData, mSize, mStride, mFormat);
306 }
307 }
308
309 IntSize
310 SourceSurfaceCGBitmapContext::GetSize() const
311 {
312 return mSize;
313 }
314
315 void
316 SourceSurfaceCGBitmapContext::DrawTargetWillChange()
317 {
318 if (mDrawTarget) {
319 // This will break the weak reference we hold to mCg
320 size_t stride = CGBitmapContextGetBytesPerRow(mCg);
321 size_t height = CGBitmapContextGetHeight(mCg);
322
323 size_t bufLen = BufferSizeFromStrideAndHeight(stride, height);
324 if (bufLen == 0) {
325 mDataHolder.Dealloc();
326 mData = nullptr;
327 } else {
328 static_assert(sizeof(decltype(mDataHolder[0])) == 1,
329 "mDataHolder.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen");
330 mDataHolder.Realloc(/* actually an object count */ bufLen);
331 mData = mDataHolder;
332
333 // copy out the data from the CGBitmapContext
334 // we'll maintain ownership of mData until
335 // we transfer it to mImage
336 memcpy(mData, CGBitmapContextGetData(mCg), bufLen);
337 }
338
339 // drop the current image for the data associated with the CGBitmapContext
340 if (mImage)
341 CGImageRelease(mImage);
342 mImage = nullptr;
343
344 mCg = nullptr;
345 mDrawTarget = nullptr;
346 }
347 }
348
349 SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
350 {
351 if (mImage)
352 CGImageRelease(mImage);
353 }
354
355 SourceSurfaceCGIOSurfaceContext::SourceSurfaceCGIOSurfaceContext(DrawTargetCG *aDrawTarget)
356 {
357 CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT_ACCELERATED);
358
359 RefPtr<MacIOSurface> surf = MacIOSurface::IOSurfaceContextGetSurface(cg);
360
361 mFormat = aDrawTarget->GetFormat();
362 mSize.width = surf->GetWidth();
363 mSize.height = surf->GetHeight();
364
365 // TODO use CreateImageFromIOSurfaceContext instead of reading back the surface
366 //mImage = MacIOSurface::CreateImageFromIOSurfaceContext(cg);
367 mImage = nullptr;
368
369 aDrawTarget->Flush();
370 surf->Lock();
371 size_t bytesPerRow = surf->GetBytesPerRow();
372 size_t ioHeight = surf->GetHeight();
373 void* ioData = surf->GetBaseAddress();
374 // XXX If the width is much less then the stride maybe
375 // we should repack the image?
376 mData = malloc(ioHeight*bytesPerRow);
377 memcpy(mData, ioData, ioHeight*(bytesPerRow));
378 mStride = bytesPerRow;
379 surf->Unlock();
380 }
381
382 void SourceSurfaceCGIOSurfaceContext::EnsureImage() const
383 {
384 // TODO Use CreateImageFromIOSurfaceContext and remove this
385
386 // Instead of using CGBitmapContextCreateImage we create
387 // a CGImage around the data associated with the CGBitmapContext
388 // we do this to avoid the vm_copy that CGBitmapContextCreateImage.
389 // vm_copy tends to cause all sorts of unexpected performance problems
390 // because of the mm tricks that vm_copy does. Using a regular
391 // memcpy when the bitmap context is modified gives us more predictable
392 // performance characteristics.
393 if (!mImage) {
394 mImage = CreateCGImage(mData, mData, mSize, mStride, SurfaceFormat::B8G8R8A8);
395 }
396
397 }
398
399 IntSize
400 SourceSurfaceCGIOSurfaceContext::GetSize() const
401 {
402 return mSize;
403 }
404
405 void
406 SourceSurfaceCGIOSurfaceContext::DrawTargetWillChange()
407 {
408 }
409
410 SourceSurfaceCGIOSurfaceContext::~SourceSurfaceCGIOSurfaceContext()
411 {
412 if (mImage)
413 CGImageRelease(mImage);
414 else
415 free(mData);
416 }
417
418 unsigned char*
419 SourceSurfaceCGIOSurfaceContext::GetData()
420 {
421 return (unsigned char*)mData;
422 }
423
424 }
425 }

mercurial