tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

gfxImageSurface.cpp (9840B)


      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 "mozilla/MemoryReporting.h"
      7 #if defined(HAVE_POSIX_MEMALIGN)
      8 #  include "gfxAlphaRecovery.h"
      9 #endif
     10 #include "gfxImageSurface.h"
     11 
     12 #include "cairo.h"
     13 #include "mozilla/gfx/2D.h"
     14 #include "mozilla/gfx/HelpersCairo.h"
     15 #include "gfx2DGlue.h"
     16 #include <algorithm>
     17 
     18 using namespace mozilla;
     19 using namespace mozilla::gfx;
     20 
     21 gfxImageSurface::gfxImageSurface()
     22    : mSize(0, 0),
     23      mOwnsData(false),
     24      mData(nullptr),
     25      mFormat(SurfaceFormat::UNKNOWN),
     26      mStride(0) {}
     27 
     28 void gfxImageSurface::InitFromSurface(cairo_surface_t* csurf) {
     29  if (!csurf || cairo_surface_status(csurf)) {
     30    MakeInvalid();
     31    return;
     32  }
     33 
     34  mSize.width = cairo_image_surface_get_width(csurf);
     35  mSize.height = cairo_image_surface_get_height(csurf);
     36  mData = cairo_image_surface_get_data(csurf);
     37  mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
     38  mOwnsData = false;
     39  mStride = cairo_image_surface_get_stride(csurf);
     40 
     41  Init(csurf, true);
     42 }
     43 
     44 gfxImageSurface::gfxImageSurface(unsigned char* aData, const IntSize& aSize,
     45                                 long aStride, gfxImageFormat aFormat) {
     46  InitWithData(aData, aSize, aStride, aFormat);
     47 }
     48 
     49 void gfxImageSurface::MakeInvalid() {
     50  mSize = IntSize(-1, -1);
     51  mData = nullptr;
     52  mStride = 0;
     53 }
     54 
     55 void gfxImageSurface::InitWithData(unsigned char* aData, const IntSize& aSize,
     56                                   long aStride, gfxImageFormat aFormat) {
     57  mSize = aSize;
     58  mOwnsData = false;
     59  mData = aData;
     60  mFormat = aFormat;
     61  mStride = aStride;
     62 
     63  if (!Factory::CheckSurfaceSize(aSize)) MakeInvalid();
     64 
     65  cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
     66  cairo_surface_t* surface = cairo_image_surface_create_for_data(
     67      (unsigned char*)mData, cformat, mSize.width, mSize.height, mStride);
     68 
     69  // cairo_image_surface_create_for_data can return a 'null' surface
     70  // in out of memory conditions. The gfxASurface::Init call checks
     71  // the surface it receives to see if there is an error with the
     72  // surface and handles it appropriately. That is why there is
     73  // no check here.
     74  Init(surface);
     75 }
     76 
     77 static void* TryAllocAlignedBytes(size_t aSize) {
     78  // Use fallible allocators here
     79 #if defined(HAVE_POSIX_MEMALIGN)
     80  void* ptr;
     81  // Try to align for fast alpha recovery.  This should only help
     82  // cairo too, can't hurt.
     83  return posix_memalign(&ptr, 1 << gfxAlphaRecovery::GoodAlignmentLog2(), aSize)
     84             ? nullptr
     85             : ptr;
     86 #else
     87  // Oh well, hope that luck is with us in the allocator
     88  return malloc(aSize);
     89 #endif
     90 }
     91 
     92 gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format,
     93                                 bool aClear)
     94    : mSize(size), mData(nullptr), mFormat(format) {
     95  AllocateAndInit(0, 0, aClear);
     96 }
     97 
     98 void gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
     99                                      bool aClear) {
    100  // The callers should set mSize and mFormat.
    101  MOZ_ASSERT(!mData);
    102  mData = nullptr;
    103  mOwnsData = false;
    104 
    105  mStride = aStride > 0 ? aStride : ComputeStride();
    106  if (aMinimalAllocation < mSize.height * mStride)
    107    aMinimalAllocation = mSize.height * mStride;
    108 
    109  if (!Factory::CheckSurfaceSize(mSize)) MakeInvalid();
    110 
    111  // if we have a zero-sized surface, just leave mData nullptr
    112  if (mSize.height * mStride > 0) {
    113    // This can fail to allocate memory aligned as we requested,
    114    // or it can fail to allocate any memory at all.
    115    mData = (unsigned char*)TryAllocAlignedBytes(aMinimalAllocation);
    116    if (!mData) return;
    117    if (aClear) memset(mData, 0, aMinimalAllocation);
    118  }
    119 
    120  mOwnsData = true;
    121 
    122  cairo_format_t cformat = GfxFormatToCairoFormat(mFormat);
    123  cairo_surface_t* surface = cairo_image_surface_create_for_data(
    124      (unsigned char*)mData, cformat, mSize.width, mSize.height, mStride);
    125 
    126  Init(surface);
    127 
    128  if (mSurfaceValid) {
    129    RecordMemoryUsed(mSize.height * ComputeStride() + sizeof(gfxImageSurface));
    130  }
    131 }
    132 
    133 gfxImageSurface::gfxImageSurface(const IntSize& size, gfxImageFormat format,
    134                                 long aStride, int32_t aExtraBytes, bool aClear)
    135    : mSize(size), mData(nullptr), mFormat(format) {
    136  AllocateAndInit(aStride, aExtraBytes, aClear);
    137 }
    138 
    139 gfxImageSurface::gfxImageSurface(cairo_surface_t* csurf) {
    140  mSize.width = cairo_image_surface_get_width(csurf);
    141  mSize.height = cairo_image_surface_get_height(csurf);
    142  mData = cairo_image_surface_get_data(csurf);
    143  mFormat = CairoFormatToGfxFormat(cairo_image_surface_get_format(csurf));
    144  mOwnsData = false;
    145  mStride = cairo_image_surface_get_stride(csurf);
    146 
    147  Init(csurf, true);
    148 }
    149 
    150 gfxImageSurface::~gfxImageSurface() {
    151  if (mOwnsData) free(mData);
    152 }
    153 
    154 /*static*/
    155 long gfxImageSurface::ComputeStride(const IntSize& aSize,
    156                                    gfxImageFormat aFormat) {
    157  long stride;
    158 
    159  if (aFormat == SurfaceFormat::A8R8G8B8_UINT32)
    160    stride = aSize.width * 4;
    161  else if (aFormat == SurfaceFormat::X8R8G8B8_UINT32)
    162    stride = aSize.width * 4;
    163  else if (aFormat == SurfaceFormat::R5G6B5_UINT16)
    164    stride = aSize.width * 2;
    165  else if (aFormat == SurfaceFormat::A8)
    166    stride = aSize.width;
    167  else {
    168    NS_WARNING("Unknown format specified to gfxImageSurface!");
    169    stride = aSize.width * 4;
    170  }
    171 
    172  stride = ((stride + 3) / 4) * 4;
    173 
    174  return stride;
    175 }
    176 
    177 size_t gfxImageSurface::SizeOfExcludingThis(
    178    mozilla::MallocSizeOf aMallocSizeOf) const {
    179  size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf);
    180  if (mOwnsData) {
    181    n += aMallocSizeOf(mData);
    182  }
    183  return n;
    184 }
    185 
    186 size_t gfxImageSurface::SizeOfIncludingThis(
    187    mozilla::MallocSizeOf aMallocSizeOf) const {
    188  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    189 }
    190 
    191 bool gfxImageSurface::SizeOfIsMeasured() const { return true; }
    192 
    193 // helper function for the CopyFrom methods
    194 static void CopyForStride(unsigned char* aDest, unsigned char* aSrc,
    195                          const IntSize& aSize, long aDestStride,
    196                          long aSrcStride) {
    197  if (aDestStride == aSrcStride) {
    198    memcpy(aDest, aSrc, aSrcStride * aSize.height);
    199  } else {
    200    int lineSize = std::min(aDestStride, aSrcStride);
    201    for (int i = 0; i < aSize.height; i++) {
    202      unsigned char* src = aSrc + aSrcStride * i;
    203      unsigned char* dst = aDest + aDestStride * i;
    204 
    205      memcpy(dst, src, lineSize);
    206    }
    207  }
    208 }
    209 
    210 // helper function for the CopyFrom methods
    211 static bool FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2) {
    212  if (a1 != a2 &&
    213      !(a1 == SurfaceFormat::A8R8G8B8_UINT32 &&
    214        a2 == SurfaceFormat::X8R8G8B8_UINT32) &&
    215      !(a1 == SurfaceFormat::X8R8G8B8_UINT32 &&
    216        a2 == SurfaceFormat::A8R8G8B8_UINT32)) {
    217    return false;
    218  }
    219 
    220  return true;
    221 }
    222 
    223 bool gfxImageSurface::CopyFrom(SourceSurface* aSurface) {
    224  RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
    225 
    226  if (!data) {
    227    return false;
    228  }
    229 
    230  IntSize size(data->GetSize().width, data->GetSize().height);
    231  if (size != mSize) {
    232    return false;
    233  }
    234 
    235  if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
    236                            mFormat)) {
    237    return false;
    238  }
    239 
    240  DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
    241  CopyForStride(mData, map.GetData(), size, mStride, map.GetStride());
    242 
    243  return true;
    244 }
    245 
    246 bool gfxImageSurface::CopyFrom(gfxImageSurface* other) {
    247  if (other->mSize != mSize) {
    248    return false;
    249  }
    250 
    251  if (!FormatsAreCompatible(other->mFormat, mFormat)) {
    252    return false;
    253  }
    254 
    255  CopyForStride(mData, other->mData, mSize, mStride, other->mStride);
    256 
    257  return true;
    258 }
    259 
    260 bool gfxImageSurface::CopyTo(SourceSurface* aSurface) {
    261  RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
    262 
    263  if (!data) {
    264    return false;
    265  }
    266 
    267  IntSize size(data->GetSize().width, data->GetSize().height);
    268  if (size != mSize) {
    269    return false;
    270  }
    271 
    272  if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
    273                            mFormat)) {
    274    return false;
    275  }
    276 
    277  DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ_WRITE);
    278  CopyForStride(map.GetData(), mData, size, map.GetStride(), mStride);
    279 
    280  return true;
    281 }
    282 
    283 already_AddRefed<DataSourceSurface>
    284 gfxImageSurface::CopyToB8G8R8A8DataSourceSurface() {
    285  RefPtr<DataSourceSurface> dataSurface = Factory::CreateDataSourceSurface(
    286      IntSize(GetSize().width, GetSize().height), SurfaceFormat::B8G8R8A8);
    287  if (dataSurface) {
    288    CopyTo(dataSurface);
    289  }
    290  return dataSurface.forget();
    291 }
    292 
    293 already_AddRefed<gfxSubimageSurface> gfxImageSurface::GetSubimage(
    294    const gfxRect& aRect) {
    295  gfxRect r(aRect);
    296  r.Round();
    297  MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r));
    298 
    299  gfxImageFormat format = Format();
    300 
    301  unsigned char* subData =
    302      Data() + (Stride() * (int)r.Y()) +
    303      (int)r.X() * gfxASurface::BytePerPixelFromFormat(Format());
    304 
    305  if (format == SurfaceFormat::A8R8G8B8_UINT32 &&
    306      GetOpaqueRect().Contains(aRect)) {
    307    format = SurfaceFormat::X8R8G8B8_UINT32;
    308  }
    309 
    310  RefPtr<gfxSubimageSurface> image = new gfxSubimageSurface(
    311      this, subData, IntSize((int)r.Width(), (int)r.Height()), format);
    312 
    313  return image.forget();
    314 }
    315 
    316 gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent,
    317                                       unsigned char* aData,
    318                                       const IntSize& aSize,
    319                                       gfxImageFormat aFormat)
    320    : gfxImageSurface(aData, aSize, aParent->Stride(), aFormat),
    321      mParent(aParent) {}
    322 
    323 already_AddRefed<gfxImageSurface> gfxImageSurface::GetAsImageSurface() {
    324  RefPtr<gfxImageSurface> surface = this;
    325  return surface.forget();
    326 }