gfx/thebes/gfxImageSurface.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     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/. */
     7 #include "mozilla/MemoryReporting.h"
     8 #if defined(HAVE_POSIX_MEMALIGN)
     9 #include "gfxAlphaRecovery.h"
    10 #endif
    11 #include "gfxImageSurface.h"
    13 #include "cairo.h"
    14 #include "mozilla/gfx/2D.h"
    15 #include "gfx2DGlue.h"
    16 #include <algorithm>
    18 using namespace mozilla;
    19 using namespace mozilla::gfx;
    21 gfxImageSurface::gfxImageSurface()
    22   : mSize(0, 0),
    23     mOwnsData(false),
    24     mFormat(gfxImageFormat::Unknown),
    25     mStride(0)
    26 {
    27 }
    29 void
    30 gfxImageSurface::InitFromSurface(cairo_surface_t *csurf)
    31 {
    32     mSize.width = cairo_image_surface_get_width(csurf);
    33     mSize.height = cairo_image_surface_get_height(csurf);
    34     mData = cairo_image_surface_get_data(csurf);
    35     mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf);
    36     mOwnsData = false;
    37     mStride = cairo_image_surface_get_stride(csurf);
    39     Init(csurf, true);
    40 }
    42 gfxImageSurface::gfxImageSurface(unsigned char *aData, const gfxIntSize& aSize,
    43                                  long aStride, gfxImageFormat aFormat)
    44 {
    45     InitWithData(aData, aSize, aStride, aFormat);
    46 }
    48 void
    49 gfxImageSurface::MakeInvalid()
    50 {
    51     mSize = gfxIntSize(-1, -1);
    52     mData = nullptr;
    53     mStride = 0;
    54 }
    56 void
    57 gfxImageSurface::InitWithData(unsigned char *aData, const gfxIntSize& aSize,
    58                               long aStride, gfxImageFormat aFormat)
    59 {
    60     mSize = aSize;
    61     mOwnsData = false;
    62     mData = aData;
    63     mFormat = aFormat;
    64     mStride = aStride;
    66     if (!CheckSurfaceSize(aSize))
    67         MakeInvalid();
    69     cairo_surface_t *surface =
    70         cairo_image_surface_create_for_data((unsigned char*)mData,
    71                                             (cairo_format_t)(int)mFormat,
    72                                             mSize.width,
    73                                             mSize.height,
    74                                             mStride);
    76     // cairo_image_surface_create_for_data can return a 'null' surface
    77     // in out of memory conditions. The gfxASurface::Init call checks
    78     // the surface it receives to see if there is an error with the
    79     // surface and handles it appropriately. That is why there is
    80     // no check here.
    81     Init(surface);
    82 }
    84 static void*
    85 TryAllocAlignedBytes(size_t aSize)
    86 {
    87     // Use fallible allocators here
    88 #if defined(HAVE_POSIX_MEMALIGN)
    89     void* ptr;
    90     // Try to align for fast alpha recovery.  This should only help
    91     // cairo too, can't hurt.
    92     return moz_posix_memalign(&ptr,
    93                               1 << gfxAlphaRecovery::GoodAlignmentLog2(),
    94                               aSize) ?
    95              nullptr : ptr;
    96 #else
    97     // Oh well, hope that luck is with us in the allocator
    98     return moz_malloc(aSize);
    99 #endif
   100 }
   102 gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format, bool aClear)
   103  : mSize(size), mData(nullptr), mFormat(format)
   104 {
   105     AllocateAndInit(0, 0, aClear);
   106 }
   108 void 
   109 gfxImageSurface::AllocateAndInit(long aStride, int32_t aMinimalAllocation,
   110                                  bool aClear)
   111 {
   112     // The callers should set mSize and mFormat.
   113     MOZ_ASSERT(!mData);
   114     mData = nullptr;
   115     mOwnsData = false;
   117     mStride = aStride > 0 ? aStride : ComputeStride();
   118     if (aMinimalAllocation < mSize.height * mStride)
   119         aMinimalAllocation = mSize.height * mStride;
   121     if (!CheckSurfaceSize(mSize))
   122         MakeInvalid();
   124     // if we have a zero-sized surface, just leave mData nullptr
   125     if (mSize.height * mStride > 0) {
   127         // This can fail to allocate memory aligned as we requested,
   128         // or it can fail to allocate any memory at all.
   129         mData = (unsigned char *) TryAllocAlignedBytes(aMinimalAllocation);
   130         if (!mData)
   131             return;
   132         if (aClear)
   133             memset(mData, 0, aMinimalAllocation);
   134     }
   136     mOwnsData = true;
   138     cairo_surface_t *surface =
   139         cairo_image_surface_create_for_data((unsigned char*)mData,
   140                                             (cairo_format_t)(int)mFormat,
   141                                             mSize.width,
   142                                             mSize.height,
   143                                             mStride);
   145     Init(surface);
   147     if (mSurfaceValid) {
   148         RecordMemoryUsed(mSize.height * ComputeStride() +
   149                          sizeof(gfxImageSurface));
   150     }
   151 }
   153 gfxImageSurface::gfxImageSurface(const gfxIntSize& size, gfxImageFormat format,
   154                                  long aStride, int32_t aExtraBytes, bool aClear)
   155  : mSize(size), mData(nullptr), mFormat(format)
   156 {
   157     AllocateAndInit(aStride, aExtraBytes, aClear);
   158 }
   160 gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
   161 {
   162     mSize.width = cairo_image_surface_get_width(csurf);
   163     mSize.height = cairo_image_surface_get_height(csurf);
   164     mData = cairo_image_surface_get_data(csurf);
   165     mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf);
   166     mOwnsData = false;
   167     mStride = cairo_image_surface_get_stride(csurf);
   169     Init(csurf, true);
   170 }
   172 gfxImageSurface::~gfxImageSurface()
   173 {
   174     if (mOwnsData)
   175         free(mData);
   176 }
   178 /*static*/ long
   179 gfxImageSurface::ComputeStride(const gfxIntSize& aSize, gfxImageFormat aFormat)
   180 {
   181     long stride;
   183     if (aFormat == gfxImageFormat::ARGB32)
   184         stride = aSize.width * 4;
   185     else if (aFormat == gfxImageFormat::RGB24)
   186         stride = aSize.width * 4;
   187     else if (aFormat == gfxImageFormat::RGB16_565)
   188         stride = aSize.width * 2;
   189     else if (aFormat == gfxImageFormat::A8)
   190         stride = aSize.width;
   191     else if (aFormat == gfxImageFormat::A1) {
   192         stride = (aSize.width + 7) / 8;
   193     } else {
   194         NS_WARNING("Unknown format specified to gfxImageSurface!");
   195         stride = aSize.width * 4;
   196     }
   198     stride = ((stride + 3) / 4) * 4;
   200     return stride;
   201 }
   203 size_t
   204 gfxImageSurface::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   205 {
   206     size_t n = gfxASurface::SizeOfExcludingThis(aMallocSizeOf);
   207     if (mOwnsData) {
   208         n += aMallocSizeOf(mData);
   209     }
   210     return n;
   211 }
   213 size_t
   214 gfxImageSurface::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
   215 {
   216     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   217 }
   219 bool
   220 gfxImageSurface::SizeOfIsMeasured() const
   221 {
   222     return true;
   223 }
   225 // helper function for the CopyFrom methods
   226 static void
   227 CopyForStride(unsigned char* aDest, unsigned char* aSrc, const gfxIntSize& aSize, long aDestStride, long aSrcStride)
   228 {
   229     if (aDestStride == aSrcStride) {
   230         memcpy (aDest, aSrc, aSrcStride * aSize.height);
   231     } else {
   232         int lineSize = std::min(aDestStride, aSrcStride);
   233         for (int i = 0; i < aSize.height; i++) {
   234             unsigned char* src = aSrc + aSrcStride * i;
   235             unsigned char* dst = aDest + aDestStride * i;
   237             memcpy (dst, src, lineSize);
   238         }
   239     }
   240 }
   242 // helper function for the CopyFrom methods
   243 static bool
   244 FormatsAreCompatible(gfxImageFormat a1, gfxImageFormat a2)
   245 {
   246     if (a1 != a2 &&
   247         !(a1 == gfxImageFormat::ARGB32 &&
   248           a2 == gfxImageFormat::RGB24) &&
   249         !(a1 == gfxImageFormat::RGB24 &&
   250           a2 == gfxImageFormat::ARGB32)) {
   251         return false;
   252     }
   254     return true;
   255 }
   257 bool
   258 gfxImageSurface::CopyFrom (SourceSurface *aSurface)
   259 {
   260     mozilla::RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
   262     if (!data) {
   263         return false;
   264     }
   266     gfxIntSize size(data->GetSize().width, data->GetSize().height);
   267     if (size != mSize) {
   268         return false;
   269     }
   271     if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
   272                               mFormat)) {
   273         return false;
   274     }
   276     CopyForStride(mData, data->GetData(), size, mStride, data->Stride());
   278     return true;
   279 }
   282 bool
   283 gfxImageSurface::CopyFrom(gfxImageSurface *other)
   284 {
   285     if (other->mSize != mSize) {
   286         return false;
   287     }
   289     if (!FormatsAreCompatible(other->mFormat, mFormat)) {
   290         return false;
   291     }
   293     CopyForStride(mData, other->mData, mSize, mStride, other->mStride);
   295     return true;
   296 }
   298 bool
   299 gfxImageSurface::CopyTo(SourceSurface *aSurface) {
   300     mozilla::RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
   302     if (!data) {
   303         return false;
   304     }
   306     gfxIntSize size(data->GetSize().width, data->GetSize().height);
   307     if (size != mSize) {
   308         return false;
   309     }
   311     if (!FormatsAreCompatible(SurfaceFormatToImageFormat(aSurface->GetFormat()),
   312                               mFormat)) {
   313         return false;
   314     }
   316     CopyForStride(data->GetData(), mData, size, data->Stride(), mStride);
   318     return true;
   319 }
   321 TemporaryRef<DataSourceSurface>
   322 gfxImageSurface::CopyToB8G8R8A8DataSourceSurface()
   323 {
   324   RefPtr<DataSourceSurface> dataSurface =
   325     Factory::CreateDataSourceSurface(IntSize(GetSize().width, GetSize().height),
   326                                      SurfaceFormat::B8G8R8A8);
   327   if (dataSurface) {
   328     CopyTo(dataSurface);
   329   }
   330   return dataSurface.forget();
   331 }
   333 already_AddRefed<gfxSubimageSurface>
   334 gfxImageSurface::GetSubimage(const gfxRect& aRect)
   335 {
   336     gfxRect r(aRect);
   337     r.Round();
   338     MOZ_ASSERT(gfxRect(0, 0, mSize.width, mSize.height).Contains(r));
   340     gfxImageFormat format = Format();
   342     unsigned char* subData = Data() +
   343         (Stride() * (int)r.Y()) +
   344         (int)r.X() * gfxASurface::BytePerPixelFromFormat(Format());
   346     if (format == gfxImageFormat::ARGB32 &&
   347         GetOpaqueRect().Contains(aRect)) {
   348         format = gfxImageFormat::RGB24;
   349     }
   351     nsRefPtr<gfxSubimageSurface> image =
   352         new gfxSubimageSurface(this, subData,
   353                                gfxIntSize((int)r.Width(), (int)r.Height()),
   354                                format);
   356     return image.forget();
   357 }
   359 gfxSubimageSurface::gfxSubimageSurface(gfxImageSurface* aParent,
   360                                        unsigned char* aData,
   361                                        const gfxIntSize& aSize,
   362                                        gfxImageFormat aFormat)
   363   : gfxImageSurface(aData, aSize, aParent->Stride(), aFormat)
   364   , mParent(aParent)
   365 {
   366 }
   368 already_AddRefed<gfxImageSurface>
   369 gfxImageSurface::GetAsImageSurface()
   370 {
   371   nsRefPtr<gfxImageSurface> surface = this;
   372   return surface.forget();
   373 }
   375 void
   376 gfxImageSurface::MovePixels(const nsIntRect& aSourceRect,
   377                             const nsIntPoint& aDestTopLeft)
   378 {
   379     const nsIntRect bounds(0, 0, mSize.width, mSize.height);
   380     nsIntPoint offset = aDestTopLeft - aSourceRect.TopLeft(); 
   381     nsIntRect clippedSource = aSourceRect;
   382     clippedSource.IntersectRect(clippedSource, bounds);
   383     nsIntRect clippedDest = clippedSource + offset;
   384     clippedDest.IntersectRect(clippedDest, bounds);
   385     const nsIntRect dest = clippedDest;
   386     const nsIntRect source = dest - offset;
   387     // NB: this relies on IntersectRect() and operator+/- preserving
   388     // x/y for empty rectangles
   389     NS_ABORT_IF_FALSE(bounds.Contains(dest) && bounds.Contains(source) &&
   390                       aSourceRect.Contains(source) &&
   391                       nsIntRect(aDestTopLeft, aSourceRect.Size()).Contains(dest) &&
   392                       source.Size() == dest.Size() &&
   393                       offset == (dest.TopLeft() - source.TopLeft()),
   394                       "Messed up clipping, crash or corruption will follow");
   395     if (source.IsEmpty() || source.IsEqualInterior(dest)) {
   396         return;
   397     }
   399     long naturalStride = ComputeStride(mSize, mFormat);
   400     if (mStride == naturalStride && dest.width == bounds.width) {
   401         // Fast path: this is a vertical shift of some rows in a
   402         // "normal" image surface.  We can directly memmove and
   403         // hopefully stay in SIMD land.
   404         unsigned char* dst = mData + dest.y * mStride;
   405         const unsigned char* src = mData + source.y * mStride;
   406         size_t nBytes = dest.height * mStride;
   407         memmove(dst, src, nBytes);
   408         return;
   409     }
   411     // Slow(er) path: have to move row-by-row.
   412     const int32_t bpp = BytePerPixelFromFormat(mFormat);
   413     const size_t nRowBytes = dest.width * bpp;
   414     // dstRow points at the first pixel within the current destination
   415     // row, and similarly for srcRow.  endSrcRow is one row beyond the
   416     // last row we need to copy.  stride is either +mStride or
   417     // -mStride, depending on which direction we're copying.
   418     unsigned char* dstRow;
   419     unsigned char* srcRow;
   420     unsigned char* endSrcRow;   // NB: this may point outside the image
   421     long stride;
   422     if (dest.y > source.y) {
   423         // We're copying down from source to dest, so walk backwards
   424         // starting from the last rows to avoid stomping pixels we
   425         // need.
   426         stride = -mStride;
   427         dstRow = mData + dest.x * bpp + (dest.YMost() - 1) * mStride;
   428         srcRow = mData + source.x * bpp + (source.YMost() - 1) * mStride;
   429         endSrcRow = mData + source.x * bpp + (source.y - 1) * mStride;
   430     } else {
   431         stride = mStride;
   432         dstRow = mData + dest.x * bpp + dest.y * mStride;
   433         srcRow = mData + source.x * bpp + source.y * mStride;
   434         endSrcRow = mData + source.x * bpp + source.YMost() * mStride;
   435     }
   437     for (; srcRow != endSrcRow; dstRow += stride, srcRow += stride) {
   438         memmove(dstRow, srcRow, nRowBytes);
   439     }
   440 }

mercurial