gfx/2d/SourceSurfaceCG.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial