|
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 } |