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.

     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"
     8 #include "DataSourceSurfaceWrapper.h"
     9 #include "DataSurfaceHelpers.h"
    10 #include "mozilla/Types.h" // for decltype
    12 #include "MacIOSurface.h"
    13 #include "Tools.h"
    15 namespace mozilla {
    16 namespace gfx {
    19 SourceSurfaceCG::~SourceSurfaceCG()
    20 {
    21   CGImageRelease(mImage);
    22 }
    24 IntSize
    25 SourceSurfaceCG::GetSize() const
    26 {
    27   IntSize size;
    28   size.width = CGImageGetWidth(mImage);
    29   size.height = CGImageGetHeight(mImage);
    30   return size;
    31 }
    33 SurfaceFormat
    34 SourceSurfaceCG::GetFormat() const
    35 {
    36   return mFormat;
    37 }
    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);
    46   // We also need to make sure that the returned surface has
    47   // surface->GetType() == SurfaceType::DATA.
    48   dataSurf = new DataSourceSurfaceWrapper(dataSurf);
    50   return dataSurf;
    51 }
    53 static void releaseCallback(void *info, const void *data, size_t size) {
    54   free(info);
    55 }
    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;
    70   switch (aFormat) {
    71     case SurfaceFormat::B8G8R8A8:
    72       colorSpace = CGColorSpaceCreateDeviceRGB();
    73       bitinfo = kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host;
    74       bitsPerComponent = 8;
    75       bitsPerPixel = 32;
    76       break;
    78     case SurfaceFormat::B8G8R8X8:
    79       colorSpace = CGColorSpaceCreateDeviceRGB();
    80       bitinfo = kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host;
    81       bitsPerComponent = 8;
    82       bitsPerPixel = 32;
    83       break;
    85     case SurfaceFormat::A8:
    86       // XXX: why don't we set a colorspace here?
    87       bitsPerComponent = 8;
    88       bitsPerPixel = 8;
    89       break;
    91     default:
    92       MOZ_CRASH();
    93   }
    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);
   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   }
   127   CGDataProviderRelease(dataProvider);
   128   CGColorSpaceRelease(colorSpace);
   130   return image;
   131 }
   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);
   141   size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
   142   if (bufLen == 0) {
   143     mImage = nullptr;
   144     return false;
   145   }
   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)));
   152   mFormat = aFormat;
   153   mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
   155   return mImage != nullptr;
   156 }
   158 DataSourceSurfaceCG::~DataSourceSurfaceCG()
   159 {
   160   CGImageRelease(mImage);
   161   free(CGBitmapContextGetData(mCg));
   162   CGContextRelease(mCg);
   163 }
   165 IntSize
   166 DataSourceSurfaceCG::GetSize() const
   167 {
   168   IntSize size;
   169   size.width = CGImageGetWidth(mImage);
   170   size.height = CGImageGetHeight(mImage);
   171   return size;
   172 }
   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   }
   184   size_t bufLen = BufferSizeFromStrideAndHeight(aStride, aSize.height);
   185   if (bufLen == 0) {
   186     mImage = nullptr;
   187     return false;
   188   }
   190   void *data = malloc(bufLen);
   191   memcpy(data, aData, bufLen - aStride + (aSize.width * BytesPerPixel(aFormat)));
   193   mFormat = aFormat;
   194   mImage = CreateCGImage(data, data, aSize, aStride, aFormat);
   196   if (!mImage) {
   197     free(data);
   198     return false;
   199   }
   201   return true;
   202 }
   204 CGContextRef CreateBitmapContextForImage(CGImageRef image)
   205 {
   206   CGColorSpaceRef colorSpace;
   208   size_t width  = CGImageGetWidth(image);
   209   size_t height = CGImageGetHeight(image);
   211   int bitmapBytesPerRow = (width * 4);
   212   int bitmapByteCount   = (bitmapBytesPerRow * height);
   214   void *data = calloc(bitmapByteCount, 1);
   215   //XXX: which color space should we be using here?
   216   colorSpace = CGColorSpaceCreateDeviceRGB();
   217   assert(colorSpace);
   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);
   233   CGColorSpaceRelease(colorSpace);
   235   return cg;
   236 }
   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   }
   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}};
   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);
   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 }
   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 }
   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();
   286   mSize.width = CGBitmapContextGetWidth(mCg);
   287   mSize.height = CGBitmapContextGetHeight(mCg);
   288   mStride = CGBitmapContextGetBytesPerRow(mCg);
   289   mData = CGBitmapContextGetData(mCg);
   291   mImage = nullptr;
   292 }
   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 }
   309 IntSize
   310 SourceSurfaceCGBitmapContext::GetSize() const
   311 {
   312   return mSize;
   313 }
   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);
   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;
   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     }
   339     // drop the current image for the data associated with the CGBitmapContext
   340     if (mImage)
   341       CGImageRelease(mImage);
   342     mImage = nullptr;
   344     mCg = nullptr;
   345     mDrawTarget = nullptr;
   346   }
   347 }
   349 SourceSurfaceCGBitmapContext::~SourceSurfaceCGBitmapContext()
   350 {
   351   if (mImage)
   352     CGImageRelease(mImage);
   353 }
   355 SourceSurfaceCGIOSurfaceContext::SourceSurfaceCGIOSurfaceContext(DrawTargetCG *aDrawTarget)
   356 {
   357   CGContextRef cg = (CGContextRef)aDrawTarget->GetNativeSurface(NativeSurfaceType::CGCONTEXT_ACCELERATED);
   359   RefPtr<MacIOSurface> surf = MacIOSurface::IOSurfaceContextGetSurface(cg);
   361   mFormat = aDrawTarget->GetFormat();
   362   mSize.width = surf->GetWidth();
   363   mSize.height = surf->GetHeight();
   365   // TODO use CreateImageFromIOSurfaceContext instead of reading back the surface
   366   //mImage = MacIOSurface::CreateImageFromIOSurfaceContext(cg);
   367   mImage = nullptr;
   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 }
   382 void SourceSurfaceCGIOSurfaceContext::EnsureImage() const
   383 {
   384   // TODO Use CreateImageFromIOSurfaceContext and remove this
   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   }
   397 }
   399 IntSize
   400 SourceSurfaceCGIOSurfaceContext::GetSize() const
   401 {
   402   return mSize;
   403 }
   405 void
   406 SourceSurfaceCGIOSurfaceContext::DrawTargetWillChange()
   407 {
   408 }
   410 SourceSurfaceCGIOSurfaceContext::~SourceSurfaceCGIOSurfaceContext()
   411 {
   412   if (mImage)
   413     CGImageRelease(mImage);
   414   else
   415     free(mData);
   416 }
   418 unsigned char*
   419 SourceSurfaceCGIOSurfaceContext::GetData()
   420 {
   421   return (unsigned char*)mData;
   422 }
   424 }
   425 }

mercurial