1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/2d/SourceSurfaceCG.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,425 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "SourceSurfaceCG.h" 1.10 +#include "DrawTargetCG.h" 1.11 +#include "DataSourceSurfaceWrapper.h" 1.12 +#include "DataSurfaceHelpers.h" 1.13 +#include "mozilla/Types.h" // for decltype 1.14 + 1.15 +#include "MacIOSurface.h" 1.16 +#include "Tools.h" 1.17 + 1.18 +namespace mozilla { 1.19 +namespace gfx { 1.20 + 1.21 + 1.22 +SourceSurfaceCG::~SourceSurfaceCG() 1.23 +{ 1.24 + CGImageRelease(mImage); 1.25 +} 1.26 + 1.27 +IntSize 1.28 +SourceSurfaceCG::GetSize() const 1.29 +{ 1.30 + IntSize size; 1.31 + size.width = CGImageGetWidth(mImage); 1.32 + size.height = CGImageGetHeight(mImage); 1.33 + return size; 1.34 +} 1.35 + 1.36 +SurfaceFormat 1.37 +SourceSurfaceCG::GetFormat() const 1.38 +{ 1.39 + return mFormat; 1.40 +} 1.41 + 1.42 +TemporaryRef<DataSourceSurface> 1.43 +SourceSurfaceCG::GetDataSurface() 1.44 +{ 1.45 + //XXX: we should be more disciplined about who takes a reference and where 1.46 + CGImageRetain(mImage); 1.47 + RefPtr<DataSourceSurface> dataSurf = new DataSourceSurfaceCG(mImage); 1.48 + 1.49 + // We also need to make sure that the returned surface has 1.50 + // surface->GetType() == SurfaceType::DATA. 1.51 + dataSurf = new DataSourceSurfaceWrapper(dataSurf); 1.52 + 1.53 + return dataSurf; 1.54 +} 1.55 + 1.56 +static void releaseCallback(void *info, const void *data, size_t size) { 1.57 + free(info); 1.58 +} 1.59 + 1.60 +CGImageRef 1.61 +CreateCGImage(void *aInfo, 1.62 + const void *aData, 1.63 + const IntSize &aSize, 1.64 + int32_t aStride, 1.65 + SurfaceFormat aFormat) 1.66 +{ 1.67 + //XXX: we should avoid creating this colorspace everytime 1.68 + CGColorSpaceRef colorSpace = nullptr; 1.69 + CGBitmapInfo bitinfo = 0; 1.70 + int bitsPerComponent = 0; 1.71 + int bitsPerPixel = 0; 1.72 + 1.73 + switch (aFormat) { 1.74 + case SurfaceFormat::B8G8R8A8: 1.75 + colorSpace = CGColorSpaceCreateDeviceRGB(); 1.76 + bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host; 1.77 + bitsPerComponent = 8; 1.78 + bitsPerPixel = 32; 1.79 + break; 1.80 + 1.81 + case SurfaceFormat::B8G8R8X8: 1.82 + colorSpace = CGColorSpaceCreateDeviceRGB(); 1.83 + bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host; 1.84 + bitsPerComponent = 8; 1.85 + bitsPerPixel = 32; 1.86 + break; 1.87 + 1.88 + case SurfaceFormat::A8: 1.89 + // XXX: why don't we set a colorspace here? 1.90 + bitsPerComponent = 8; 1.91 + bitsPerPixel = 8; 1.92 + break; 1.93 + 1.94 + default: 1.95 + MOZ_CRASH(); 1.96 + } 1.97 + 1.98 + size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); 1.99 + if (bufLen == 0) { 1.100 + return nullptr; 1.101 + } 1.102 + CGDataProviderRef dataProvider = CGDataProviderCreateWithData(aInfo, 1.103 + aData, 1.104 + bufLen, 1.105 + releaseCallback); 1.106 + 1.107 + CGImageRef image; 1.108 + if (aFormat == SurfaceFormat::A8) { 1.109 + CGFloat decode[] = {1.0, 0.0}; 1.110 + image = CGImageMaskCreate (aSize.width, aSize.height, 1.111 + bitsPerComponent, 1.112 + bitsPerPixel, 1.113 + aStride, 1.114 + dataProvider, 1.115 + decode, 1.116 + true); 1.117 + } else { 1.118 + image = CGImageCreate (aSize.width, aSize.height, 1.119 + bitsPerComponent, 1.120 + bitsPerPixel, 1.121 + aStride, 1.122 + colorSpace, 1.123 + bitinfo, 1.124 + dataProvider, 1.125 + nullptr, 1.126 + true, 1.127 + kCGRenderingIntentDefault); 1.128 + } 1.129 + 1.130 + CGDataProviderRelease(dataProvider); 1.131 + CGColorSpaceRelease(colorSpace); 1.132 + 1.133 + return image; 1.134 +} 1.135 + 1.136 +bool 1.137 +SourceSurfaceCG::InitFromData(unsigned char *aData, 1.138 + const IntSize &aSize, 1.139 + int32_t aStride, 1.140 + SurfaceFormat aFormat) 1.141 +{ 1.142 + assert(aSize.width >= 0 && aSize.height >= 0); 1.143 + 1.144 + size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); 1.145 + if (bufLen == 0) { 1.146 + mImage = nullptr; 1.147 + return false; 1.148 + } 1.149 + 1.150 + void *data = malloc(bufLen); 1.151 + // Copy all the data except the stride padding on the very last 1.152 + // row since we can't guarantee that is readable. 1.153 + memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat))); 1.154 + 1.155 + mFormat = aFormat; 1.156 + mImage = CreateCGImage(data, data, aSize, aStride, aFormat); 1.157 + 1.158 + return mImage != nullptr; 1.159 +} 1.160 + 1.161 +DataSourceSurfaceCG::~DataSourceSurfaceCG() 1.162 +{ 1.163 + CGImageRelease(mImage); 1.164 + free(CGBitmapContextGetData(mCg)); 1.165 + CGContextRelease(mCg); 1.166 +} 1.167 + 1.168 +IntSize 1.169 +DataSourceSurfaceCG::GetSize() const 1.170 +{ 1.171 + IntSize size; 1.172 + size.width = CGImageGetWidth(mImage); 1.173 + size.height = CGImageGetHeight(mImage); 1.174 + return size; 1.175 +} 1.176 + 1.177 +bool 1.178 +DataSourceSurfaceCG::InitFromData(unsigned char *aData, 1.179 + const IntSize &aSize, 1.180 + int32_t aStride, 1.181 + SurfaceFormat aFormat) 1.182 +{ 1.183 + if (aSize.width <= 0 || aSize.height <= 0) { 1.184 + return false; 1.185 + } 1.186 + 1.187 + size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height); 1.188 + if (bufLen == 0) { 1.189 + mImage = nullptr; 1.190 + return false; 1.191 + } 1.192 + 1.193 + void *data = malloc(bufLen); 1.194 + memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat))); 1.195 + 1.196 + mFormat = aFormat; 1.197 + mImage = CreateCGImage(data, data, aSize, aStride, aFormat); 1.198 + 1.199 + if (!mImage) { 1.200 + free(data); 1.201 + return false; 1.202 + } 1.203 + 1.204 + return true; 1.205 +} 1.206 + 1.207 +CGContextRef CreateBitmapContextForImage(CGImageRef image) 1.208 +{ 1.209 + CGColorSpaceRef colorSpace; 1.210 + 1.211 + size_t width = CGImageGetWidth(image); 1.212 + size_t height = CGImageGetHeight(image); 1.213 + 1.214 + int bitmapBytesPerRow = (width * 4); 1.215 + int bitmapByteCount = (bitmapBytesPerRow * height); 1.216 + 1.217 + void *data = calloc(bitmapByteCount, 1); 1.218 + //XXX: which color space should we be using here? 1.219 + colorSpace = CGColorSpaceCreateDeviceRGB(); 1.220 + assert(colorSpace); 1.221 + 1.222 + // we'd like to pass nullptr as the first parameter 1.223 + // to let Quartz manage this memory for us. However, 1.224 + // on 10.5 and older CGBitmapContextGetData will return 1.225 + // nullptr instead of the associated buffer so we need 1.226 + // to manage it ourselves. 1.227 + CGContextRef cg = CGBitmapContextCreate(data, 1.228 + width, 1.229 + height, 1.230 + 8, 1.231 + bitmapBytesPerRow, 1.232 + colorSpace, 1.233 + kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst); 1.234 + assert(cg); 1.235 + 1.236 + CGColorSpaceRelease(colorSpace); 1.237 + 1.238 + return cg; 1.239 +} 1.240 + 1.241 +DataSourceSurfaceCG::DataSourceSurfaceCG(CGImageRef aImage) 1.242 +{ 1.243 + mFormat = SurfaceFormat::B8G8R8A8; 1.244 + mImage = aImage; 1.245 + mCg = CreateBitmapContextForImage(aImage); 1.246 + if (mCg == nullptr) { 1.247 + // error creating context 1.248 + return; 1.249 + } 1.250 + 1.251 + // Get image width, height. We'll use the entire image. 1.252 + CGFloat w = CGImageGetWidth(aImage); 1.253 + CGFloat h = CGImageGetHeight(aImage); 1.254 + CGRect rect = {{0,0},{w,h}}; 1.255 + 1.256 + // Draw the image to the bitmap context. Once we draw, the memory 1.257 + // allocated for the context for rendering will then contain the 1.258 + // raw image data in the specified color space. 1.259 + CGContextDrawImage(mCg, rect, aImage); 1.260 + 1.261 + // Now we can get a pointer to the image data associated with the bitmap 1.262 + // context. 1.263 + mData = CGBitmapContextGetData(mCg); 1.264 + assert(mData); 1.265 +} 1.266 + 1.267 +unsigned char * 1.268 +DataSourceSurfaceCG::GetData() 1.269 +{ 1.270 + // See http://developer.apple.com/library/mac/#qa/qa1509/_index.html 1.271 + // the following only works on 10.5+, the Q&A above suggests a method 1.272 + // that can be used for earlier versions 1.273 + //CFDataRef data = CGDataProviderCopyData(CGImageGetDataProvider(cgImage)); 1.274 + //unsigned char *dataPtr = CFDataGetBytePtr(data); 1.275 + //CFDataRelease(data); 1.276 + // unfortunately the the method above only works for read-only access and 1.277 + // we need read-write for DataSourceSurfaces 1.278 + return (unsigned char*)mData; 1.279 +} 1.280 + 1.281 +SourceSurfaceCGBitmapContext::SourceSurfaceCGBitmapContext(DrawTargetCG *aDrawTarget) 1.282 +{ 1.283 + mDrawTarget = aDrawTarget; 1.284 + mFormat = aDrawTarget->GetFormat(); 1.285 + mCg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT); 1.286 + if (!mCg) 1.287 + abort(); 1.288 + 1.289 + mSize.width = CGBitmapContextGetWidth(mCg); 1.290 + mSize.height = CGBitmapContextGetHeight(mCg); 1.291 + mStride = CGBitmapContextGetBytesPerRow(mCg); 1.292 + mData = CGBitmapContextGetData(mCg); 1.293 + 1.294 + mImage = nullptr; 1.295 +} 1.296 + 1.297 +void SourceSurfaceCGBitmapContext::EnsureImage() const 1.298 +{ 1.299 + // Instead of using CGBitmapContextCreateImage we create 1.300 + // a CGImage around the data associated with the CGBitmapContext 1.301 + // we do this to avoid the vm_copy that CGBitmapContextCreateImage. 1.302 + // vm_copy tends to cause all sorts of unexpected performance problems 1.303 + // because of the mm tricks that vm_copy does. Using a regular 1.304 + // memcpy when the bitmap context is modified gives us more predictable 1.305 + // performance characteristics. 1.306 + if (!mImage) { 1.307 + if (!mData) abort(); 1.308 + mImage = CreateCGImage(nullptr, mData, mSize, mStride, mFormat); 1.309 + } 1.310 +} 1.311 + 1.312 +IntSize 1.313 +SourceSurfaceCGBitmapContext::GetSize() const 1.314 +{ 1.315 + return mSize; 1.316 +} 1.317 + 1.318 +void 1.319 +SourceSurfaceCGBitmapContext::DrawTargetWillChange() 1.320 +{ 1.321 + if (mDrawTarget) { 1.322 + // This will break the weak reference we hold to mCg 1.323 + size_t stride = CGBitmapContextGetBytesPerRow(mCg); 1.324 + size_t height = CGBitmapContextGetHeight(mCg); 1.325 + 1.326 + size_t bufLen = BufferSizeFromStrideAndHeight(stride, height); 1.327 + if (bufLen == 0) { 1.328 + mDataHolder.Dealloc(); 1.329 + mData = nullptr; 1.330 + } else { 1.331 + static_assert(sizeof(decltype(mDataHolder[0])) == 1, 1.332 + "mDataHolder.Realloc() takes an object count, so its objects must be 1-byte sized if we use bufLen"); 1.333 + mDataHolder.Realloc(/* actually an object count */ bufLen); 1.334 + mData = mDataHolder; 1.335 + 1.336 + // copy out the data from the CGBitmapContext 1.337 + // we'll maintain ownership of mData until 1.338 + // we transfer it to mImage 1.339 + memcpy(mData, CGBitmapContextGetData(mCg), bufLen); 1.340 + } 1.341 + 1.342 + // drop the current image for the data associated with the CGBitmapContext 1.343 + if (mImage) 1.344 + CGImageRelease(mImage); 1.345 + mImage = nullptr; 1.346 + 1.347 + mCg = nullptr; 1.348 + mDrawTarget = nullptr; 1.349 + } 1.350 +} 1.351 + 1.352 +SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext() 1.353 +{ 1.354 + if (mImage) 1.355 + CGImageRelease(mImage); 1.356 +} 1.357 + 1.358 +SourceSurfaceCGIOSurfaceContext::SourceSurfaceCGIOSurfaceContext(DrawTargetCG *aDrawTarget) 1.359 +{ 1.360 + CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT_ACCELERATED); 1.361 + 1.362 + RefPtr<MacIOSurface> surf = MacIOSurface::IOSurfaceContextGetSurface(cg); 1.363 + 1.364 + mFormat = aDrawTarget->GetFormat(); 1.365 + mSize.width = surf->GetWidth(); 1.366 + mSize.height = surf->GetHeight(); 1.367 + 1.368 + // TODO use CreateImageFromIOSurfaceContext instead of reading back the surface 1.369 + //mImage = MacIOSurface::CreateImageFromIOSurfaceContext(cg); 1.370 + mImage = nullptr; 1.371 + 1.372 + aDrawTarget->Flush(); 1.373 + surf->Lock(); 1.374 + size_t bytesPerRow = surf->GetBytesPerRow(); 1.375 + size_t ioHeight = surf->GetHeight(); 1.376 + void* ioData = surf->GetBaseAddress(); 1.377 + // XXX If the width is much less then the stride maybe 1.378 + // we should repack the image? 1.379 + mData = malloc(ioHeight*bytesPerRow); 1.380 + memcpy(mData, ioData, ioHeight*(bytesPerRow)); 1.381 + mStride = bytesPerRow; 1.382 + surf->Unlock(); 1.383 +} 1.384 + 1.385 +void SourceSurfaceCGIOSurfaceContext::EnsureImage() const 1.386 +{ 1.387 + // TODO Use CreateImageFromIOSurfaceContext and remove this 1.388 + 1.389 + // Instead of using CGBitmapContextCreateImage we create 1.390 + // a CGImage around the data associated with the CGBitmapContext 1.391 + // we do this to avoid the vm_copy that CGBitmapContextCreateImage. 1.392 + // vm_copy tends to cause all sorts of unexpected performance problems 1.393 + // because of the mm tricks that vm_copy does. Using a regular 1.394 + // memcpy when the bitmap context is modified gives us more predictable 1.395 + // performance characteristics. 1.396 + if (!mImage) { 1.397 + mImage = CreateCGImage(mData, mData, mSize, mStride, SurfaceFormat::B8G8R8A8); 1.398 + } 1.399 + 1.400 +} 1.401 + 1.402 +IntSize 1.403 +SourceSurfaceCGIOSurfaceContext::GetSize() const 1.404 +{ 1.405 + return mSize; 1.406 +} 1.407 + 1.408 +void 1.409 +SourceSurfaceCGIOSurfaceContext::DrawTargetWillChange() 1.410 +{ 1.411 +} 1.412 + 1.413 +SourceSurfaceCGIOSurfaceContext::~SourceSurfaceCGIOSurfaceContext() 1.414 +{ 1.415 + if (mImage) 1.416 + CGImageRelease(mImage); 1.417 + else 1.418 + free(mData); 1.419 +} 1.420 + 1.421 +unsigned char* 1.422 +SourceSurfaceCGIOSurfaceContext::GetData() 1.423 +{ 1.424 + return (unsigned char*)mData; 1.425 +} 1.426 + 1.427 +} 1.428 +}