gfx/thebes/gfxASurface.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/. */
     6 #include "nsIMemoryReporter.h"
     7 #include "nsMemory.h"
     8 #include "mozilla/ArrayUtils.h"
     9 #include "mozilla/Base64.h"
    10 #include "mozilla/CheckedInt.h"
    11 #include "mozilla/Attributes.h"
    12 #include "mozilla/MemoryReporting.h"
    13 #include "nsISupportsImpl.h"
    14 #include "mozilla/gfx/2D.h"
    15 #include "gfx2DGlue.h"
    17 #include "gfxASurface.h"
    18 #include "gfxContext.h"
    19 #include "gfxImageSurface.h"
    20 #include "gfxPlatform.h"
    21 #include "gfxRect.h"
    23 #include "cairo.h"
    24 #include <algorithm>
    26 #ifdef CAIRO_HAS_WIN32_SURFACE
    27 #include "gfxWindowsSurface.h"
    28 #endif
    29 #ifdef CAIRO_HAS_D2D_SURFACE
    30 #include "gfxD2DSurface.h"
    31 #endif
    33 #ifdef MOZ_X11
    34 #include "gfxXlibSurface.h"
    35 #endif
    37 #ifdef CAIRO_HAS_QUARTZ_SURFACE
    38 #include "gfxQuartzSurface.h"
    39 #include "gfxQuartzImageSurface.h"
    40 #endif
    42 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
    43 #include "gfxQPainterSurface.h"
    44 #endif
    46 #include <stdio.h>
    47 #include <limits.h>
    49 #include "imgIEncoder.h"
    50 #include "nsComponentManagerUtils.h"
    51 #include "nsISupportsUtils.h"
    52 #include "nsCOMPtr.h"
    53 #include "nsServiceManagerUtils.h"
    54 #include "nsString.h"
    55 #include "nsIClipboardHelper.h"
    57 using namespace mozilla;
    58 using namespace mozilla::gfx;
    60 static cairo_user_data_key_t gfxasurface_pointer_key;
    62 gfxASurface::gfxASurface()
    63  : mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0),
    64    mSurfaceValid(false), mAllowUseAsSource(true)
    65 {
    66     MOZ_COUNT_CTOR(gfxASurface);
    67 }
    69 gfxASurface::~gfxASurface()
    70 {
    71     RecordMemoryFreed();
    73     MOZ_COUNT_DTOR(gfxASurface);
    74 }
    76 // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
    77 // refcount mismatch issues.
    78 nsrefcnt
    79 gfxASurface::AddRef(void)
    80 {
    81     if (mSurfaceValid) {
    82         if (mFloatingRefs) {
    83             // eat a floating ref
    84             mFloatingRefs--;
    85         } else {
    86             cairo_surface_reference(mSurface);
    87         }
    89         return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
    90     } else {
    91         // the surface isn't valid, but we still need to refcount
    92         // the gfxASurface
    93         return ++mFloatingRefs;
    94     }
    95 }
    97 nsrefcnt
    98 gfxASurface::Release(void)
    99 {
   100     if (mSurfaceValid) {
   101         NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!");
   103         // Note that there is a destructor set on user data for mSurface,
   104         // which will delete this gfxASurface wrapper when the surface's refcount goes
   105         // out of scope.
   106         nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
   107         cairo_surface_destroy(mSurface);
   109         // |this| may not be valid any more, don't use it!
   111         return --refcnt;
   112     } else {
   113         if (--mFloatingRefs == 0) {
   114             delete this;
   115             return 0;
   116         }
   118         return mFloatingRefs;
   119     }
   120 }
   122 nsrefcnt
   123 gfxASurface::AddRefExternal(void)
   124 {
   125   return AddRef();
   126 }
   128 nsrefcnt
   129 gfxASurface::ReleaseExternal(void)
   130 {
   131   return Release();
   132 }
   134 void
   135 gfxASurface::SurfaceDestroyFunc(void *data) {
   136     gfxASurface *surf = (gfxASurface*) data;
   137     // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
   138     delete surf;
   139 }
   141 gfxASurface*
   142 gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf)
   143 {
   144     if (!csurf)
   145         return nullptr;
   146     return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key);
   147 }
   149 void
   150 gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
   151 {
   152     if (!csurf)
   153         return;
   154     cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
   155 }
   157 already_AddRefed<gfxASurface>
   158 gfxASurface::Wrap (cairo_surface_t *csurf, const gfxIntSize& aSize)
   159 {
   160     nsRefPtr<gfxASurface> result;
   162     /* Do we already have a wrapper for this surface? */
   163     result = GetSurfaceWrapper(csurf);
   164     if (result) {
   165         // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
   166         return result.forget();
   167     }
   169     /* No wrapper; figure out the surface type and create it */
   170     cairo_surface_type_t stype = cairo_surface_get_type(csurf);
   172     if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
   173         result = new gfxImageSurface(csurf);
   174     }
   175 #ifdef CAIRO_HAS_WIN32_SURFACE
   176     else if (stype == CAIRO_SURFACE_TYPE_WIN32 ||
   177              stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
   178         result = new gfxWindowsSurface(csurf);
   179     }
   180 #endif
   181 #ifdef CAIRO_HAS_D2D_SURFACE
   182     else if (stype == CAIRO_SURFACE_TYPE_D2D) {
   183         result = new gfxD2DSurface(csurf);
   184     }
   185 #endif
   186 #ifdef MOZ_X11
   187     else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
   188         result = new gfxXlibSurface(csurf);
   189     }
   190 #endif
   191 #ifdef CAIRO_HAS_QUARTZ_SURFACE
   192     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
   193         result = new gfxQuartzSurface(csurf, aSize);
   194     }
   195     else if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
   196         result = new gfxQuartzImageSurface(csurf);
   197     }
   198 #endif
   199 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
   200     else if (stype == CAIRO_SURFACE_TYPE_QT) {
   201         result = new gfxQPainterSurface(csurf);
   202     }
   203 #endif
   204     else {
   205         result = new gfxUnknownSurface(csurf, aSize);
   206     }
   208     // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
   210     return result.forget();
   211 }
   213 void
   214 gfxASurface::Init(cairo_surface_t* surface, bool existingSurface)
   215 {
   216     SetSurfaceWrapper(surface, this);
   218     mSurface = surface;
   219     mSurfaceValid = surface && !cairo_surface_status(surface);
   221     if (existingSurface || !mSurfaceValid) {
   222         mFloatingRefs = 0;
   223     } else {
   224         mFloatingRefs = 1;
   225 #ifdef MOZ_TREE_CAIRO
   226         if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
   227             cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
   228         }
   229 #endif
   230     }
   231 }
   233 gfxSurfaceType
   234 gfxASurface::GetType() const
   235 {
   236     if (!mSurfaceValid)
   237         return (gfxSurfaceType)-1;
   239     return (gfxSurfaceType)cairo_surface_get_type(mSurface);
   240 }
   242 gfxContentType
   243 gfxASurface::GetContentType() const
   244 {
   245     if (!mSurfaceValid)
   246         return (gfxContentType)-1;
   248     return (gfxContentType)cairo_surface_get_content(mSurface);
   249 }
   251 void
   252 gfxASurface::SetDeviceOffset(const gfxPoint& offset)
   253 {
   254     if (!mSurfaceValid)
   255         return;
   256     cairo_surface_set_device_offset(mSurface,
   257                                     offset.x, offset.y);
   258 }
   260 gfxPoint
   261 gfxASurface::GetDeviceOffset() const
   262 {
   263     if (!mSurfaceValid)
   264         return gfxPoint(0.0, 0.0);
   265     gfxPoint pt;
   266     cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y);
   267     return pt;
   268 }
   270 void
   271 gfxASurface::Flush() const
   272 {
   273     if (!mSurfaceValid)
   274         return;
   275     cairo_surface_flush(mSurface);
   276     gfxPlatform::ClearSourceSurfaceForSurface(const_cast<gfxASurface*>(this));
   277 }
   279 void
   280 gfxASurface::MarkDirty()
   281 {
   282     if (!mSurfaceValid)
   283         return;
   284     cairo_surface_mark_dirty(mSurface);
   285     gfxPlatform::ClearSourceSurfaceForSurface(this);
   286 }
   288 void
   289 gfxASurface::MarkDirty(const gfxRect& r)
   290 {
   291     if (!mSurfaceValid)
   292         return;
   293     cairo_surface_mark_dirty_rectangle(mSurface,
   294                                        (int) r.X(), (int) r.Y(),
   295                                        (int) r.Width(), (int) r.Height());
   296     gfxPlatform::ClearSourceSurfaceForSurface(this);
   297 }
   299 void
   300 gfxASurface::SetData(const cairo_user_data_key_t *key,
   301                      void *user_data,
   302                      thebes_destroy_func_t destroy)
   303 {
   304     if (!mSurfaceValid)
   305         return;
   306     cairo_surface_set_user_data(mSurface, key, user_data, destroy);
   307 }
   309 void *
   310 gfxASurface::GetData(const cairo_user_data_key_t *key)
   311 {
   312     if (!mSurfaceValid)
   313         return nullptr;
   314     return cairo_surface_get_user_data(mSurface, key);
   315 }
   317 void
   318 gfxASurface::Finish()
   319 {
   320     // null surfaces are allowed here
   321     cairo_surface_finish(mSurface);
   322 }
   324 already_AddRefed<gfxASurface>
   325 gfxASurface::CreateSimilarSurface(gfxContentType aContent,
   326                                   const nsIntSize& aSize)
   327 {
   328     if (!mSurface || !mSurfaceValid) {
   329       return nullptr;
   330     }
   332     cairo_surface_t *surface =
   333         cairo_surface_create_similar(mSurface, cairo_content_t(int(aContent)),
   334                                      aSize.width, aSize.height);
   335     if (cairo_surface_status(surface)) {
   336         cairo_surface_destroy(surface);
   337         return nullptr;
   338     }
   340     nsRefPtr<gfxASurface> result = Wrap(surface, aSize);
   341     cairo_surface_destroy(surface);
   342     return result.forget();
   343 }
   345 already_AddRefed<gfxImageSurface>
   346 gfxASurface::GetAsReadableARGB32ImageSurface()
   347 {
   348     nsRefPtr<gfxImageSurface> imgSurface = GetAsImageSurface();
   349     if (!imgSurface || imgSurface->Format() != gfxImageFormat::ARGB32) {
   350       imgSurface = CopyToARGB32ImageSurface();
   351     }
   352     return imgSurface.forget();
   353 }
   355 already_AddRefed<gfxImageSurface>
   356 gfxASurface::CopyToARGB32ImageSurface()
   357 {
   358     if (!mSurface || !mSurfaceValid) {
   359       return nullptr;
   360     }
   362     const nsIntSize size = GetSize();
   363     nsRefPtr<gfxImageSurface> imgSurface =
   364         new gfxImageSurface(size, gfxImageFormat::ARGB32);
   366     RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
   367     RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
   369     dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
   371     return imgSurface.forget();
   372 }
   374 int
   375 gfxASurface::CairoStatus()
   376 {
   377     if (!mSurfaceValid)
   378         return -1;
   380     return cairo_surface_status(mSurface);
   381 }
   383 /* static */
   384 bool
   385 gfxASurface::CheckSurfaceSize(const nsIntSize& sz, int32_t limit)
   386 {
   387     if (sz.width < 0 || sz.height < 0) {
   388         NS_WARNING("Surface width or height < 0!");
   389         return false;
   390     }
   392     // reject images with sides bigger than limit
   393     if (limit && (sz.width > limit || sz.height > limit)) {
   394         NS_WARNING("Surface size too large (exceeds caller's limit)!");
   395         return false;
   396     }
   398 #if defined(XP_MACOSX)
   399     // CoreGraphics is limited to images < 32K in *height*,
   400     // so clamp all surfaces on the Mac to that height
   401     if (sz.height > SHRT_MAX) {
   402         NS_WARNING("Surface size too large (exceeds CoreGraphics limit)!");
   403         return false;
   404     }
   405 #endif
   407     // make sure the surface area doesn't overflow a int32_t
   408     CheckedInt<int32_t> tmp = sz.width;
   409     tmp *= sz.height;
   410     if (!tmp.isValid()) {
   411         NS_WARNING("Surface size too large (would overflow)!");
   412         return false;
   413     }
   415     // assuming 4-byte stride, make sure the allocation size
   416     // doesn't overflow a int32_t either
   417     tmp *= 4;
   418     if (!tmp.isValid()) {
   419         NS_WARNING("Allocation too large (would overflow)!");
   420         return false;
   421     }
   423     return true;
   424 }
   426 /* static */
   427 int32_t
   428 gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width)
   429 {
   430     return cairo_format_stride_for_width((cairo_format_t)(int)format, (int)width);
   431 }
   433 nsresult
   434 gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
   435 {
   436     return NS_OK;
   437 }
   439 nsresult
   440 gfxASurface::EndPrinting()
   441 {
   442     return NS_OK;
   443 }
   445 nsresult
   446 gfxASurface::AbortPrinting()
   447 {
   448     return NS_OK;
   449 }
   451 nsresult
   452 gfxASurface::BeginPage()
   453 {
   454     return NS_OK;
   455 }
   457 nsresult
   458 gfxASurface::EndPage()
   459 {
   460     return NS_OK;
   461 }
   463 gfxContentType
   464 gfxASurface::ContentFromFormat(gfxImageFormat format)
   465 {
   466     switch (format) {
   467         case gfxImageFormat::ARGB32:
   468             return gfxContentType::COLOR_ALPHA;
   469         case gfxImageFormat::RGB24:
   470         case gfxImageFormat::RGB16_565:
   471             return gfxContentType::COLOR;
   472         case gfxImageFormat::A8:
   473         case gfxImageFormat::A1:
   474             return gfxContentType::ALPHA;
   476         case gfxImageFormat::Unknown:
   477         default:
   478             return gfxContentType::COLOR;
   479     }
   480 }
   482 void
   483 gfxASurface::SetSubpixelAntialiasingEnabled(bool aEnabled)
   484 {
   485 #ifdef MOZ_TREE_CAIRO
   486     if (!mSurfaceValid)
   487         return;
   488     cairo_surface_set_subpixel_antialiasing(mSurface,
   489         aEnabled ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
   490 #endif
   491 }
   493 bool
   494 gfxASurface::GetSubpixelAntialiasingEnabled()
   495 {
   496     if (!mSurfaceValid)
   497       return false;
   498 #ifdef MOZ_TREE_CAIRO
   499     return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
   500 #else
   501     return true;
   502 #endif
   503 }
   505 gfxMemoryLocation
   506 gfxASurface::GetMemoryLocation() const
   507 {
   508     return gfxMemoryLocation::IN_PROCESS_HEAP;
   509 }
   511 int32_t
   512 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
   513 {
   514     switch (format) {
   515         case gfxImageFormat::ARGB32:
   516         case gfxImageFormat::RGB24:
   517             return 4;
   518         case gfxImageFormat::RGB16_565:
   519             return 2;
   520         case gfxImageFormat::A8:
   521             return 1;
   522         default:
   523             NS_WARNING("Unknown byte per pixel value for Image format");
   524     }
   525     return 0;
   526 }
   528 void
   529 gfxASurface::FastMovePixels(const nsIntRect& aSourceRect,
   530                             const nsIntPoint& aDestTopLeft)
   531 {
   532     // Used when the backend can internally handle self copies.
   533     nsIntRect dest(aDestTopLeft, aSourceRect.Size());
   535     nsRefPtr<gfxContext> ctx = new gfxContext(this);
   536     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
   537     nsIntPoint srcOrigin = dest.TopLeft() - aSourceRect.TopLeft();
   538     ctx->SetSource(this, gfxPoint(srcOrigin.x, srcOrigin.y));
   539     ctx->Rectangle(gfxRect(dest.x, dest.y, dest.width, dest.height));
   540     ctx->Fill();
   541 }
   543 void
   544 gfxASurface::MovePixels(const nsIntRect& aSourceRect,
   545                         const nsIntPoint& aDestTopLeft)
   546 {
   547     // Assume the backend can't handle self copying well and allocate
   548     // a temporary surface instead.
   549     nsRefPtr<gfxASurface> tmp = 
   550       CreateSimilarSurface(GetContentType(), 
   551                            nsIntSize(aSourceRect.width, aSourceRect.height));
   552     // CreateSimilarSurface can return nullptr if the current surface is
   553     // in an error state. This isn't good, but its better to carry
   554     // on with the error surface instead of crashing.
   555     NS_WARN_IF_FALSE(tmp, "Must have temporary surface to move pixels!");
   556     if (!tmp) {
   557         return;
   558     }
   559     nsRefPtr<gfxContext> ctx = new gfxContext(tmp);
   560     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
   561     ctx->SetSource(this, gfxPoint(-aSourceRect.x, -aSourceRect.y));
   562     ctx->Paint();
   564     ctx = new gfxContext(this);
   565     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
   566     ctx->SetSource(tmp, gfxPoint(aDestTopLeft.x, aDestTopLeft.y));
   567     ctx->Rectangle(gfxRect(aDestTopLeft.x, 
   568                            aDestTopLeft.y, 
   569                            aSourceRect.width, 
   570                            aSourceRect.height));
   571     ctx->Fill();
   572 }
   574 /** Memory reporting **/
   576 static const char *sDefaultSurfaceDescription =
   577     "Memory used by gfx surface of the given type.";
   579 struct SurfaceMemoryReporterAttrs {
   580   const char *path;
   581   const char *description;
   582 };
   584 static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = {
   585     {"gfx-surface-image", nullptr},
   586     {"gfx-surface-pdf", nullptr},
   587     {"gfx-surface-ps", nullptr},
   588     {"gfx-surface-xlib",
   589      "Memory used by xlib surfaces to store pixmaps. This memory lives in "
   590      "the X server's process rather than in this application, so the bytes "
   591      "accounted for here aren't counted in vsize, resident, explicit, or any of "
   592      "the other measurements on this page."},
   593     {"gfx-surface-xcb", nullptr},
   594     {"gfx-surface-glitz???", nullptr},       // should never be used
   595     {"gfx-surface-quartz", nullptr},
   596     {"gfx-surface-win32", nullptr},
   597     {"gfx-surface-beos", nullptr},
   598     {"gfx-surface-directfb???", nullptr},    // should never be used
   599     {"gfx-surface-svg", nullptr},
   600     {"gfx-surface-os2", nullptr},
   601     {"gfx-surface-win32printing", nullptr},
   602     {"gfx-surface-quartzimage", nullptr},
   603     {"gfx-surface-script", nullptr},
   604     {"gfx-surface-qpainter", nullptr},
   605     {"gfx-surface-recording", nullptr},
   606     {"gfx-surface-vg", nullptr},
   607     {"gfx-surface-gl", nullptr},
   608     {"gfx-surface-drm", nullptr},
   609     {"gfx-surface-tee", nullptr},
   610     {"gfx-surface-xml", nullptr},
   611     {"gfx-surface-skia", nullptr},
   612     {"gfx-surface-subsurface", nullptr},
   613     {"gfx-surface-d2d", nullptr},
   614 };
   616 PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) ==
   617                  size_t(gfxSurfaceType::Max));
   618 #ifdef CAIRO_HAS_D2D_SURFACE
   619 PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_TYPE_D2D) ==
   620                  uint32_t(gfxSurfaceType::D2D));
   621 #endif
   622 PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_TYPE_SKIA) ==
   623                  uint32_t(gfxSurfaceType::Skia));
   625 /* Surface size memory reporting */
   627 static int64_t gSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)] = { 0 };
   629 class SurfaceMemoryReporter MOZ_FINAL : public nsIMemoryReporter
   630 {
   631 public:
   632     NS_DECL_ISUPPORTS
   634     NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCb,
   635                               nsISupports *aClosure)
   636     {
   637         const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs);
   638         for (size_t i = 0; i < len; i++) {
   639             int64_t amount = gSurfaceMemoryUsed[i];
   641             if (amount != 0) {
   642                 const char *path = sSurfaceMemoryReporterAttrs[i].path;
   643                 const char *desc = sSurfaceMemoryReporterAttrs[i].description;
   644                 if (!desc) {
   645                     desc = sDefaultSurfaceDescription;
   646                 }
   648                 nsresult rv = aCb->Callback(EmptyCString(), nsCString(path),
   649                                             KIND_OTHER, UNITS_BYTES,
   650                                             gSurfaceMemoryUsed[i],
   651                                             nsCString(desc), aClosure);
   652                 NS_ENSURE_SUCCESS(rv, rv);
   653             }
   654         }
   656         return NS_OK;
   657     }
   658 };
   660 NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter)
   662 void
   663 gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType,
   664                                             int32_t aBytes)
   665 {
   666     if (int(aType) < 0 || aType >= gfxSurfaceType::Max) {
   667         NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
   668         return;
   669     }
   671     static bool registered = false;
   672     if (!registered) {
   673         RegisterStrongMemoryReporter(new SurfaceMemoryReporter());
   674         registered = true;
   675     }
   677     gSurfaceMemoryUsed[size_t(aType)] += aBytes;
   678 }
   680 void
   681 gfxASurface::RecordMemoryUsed(int32_t aBytes)
   682 {
   683     RecordMemoryUsedForSurfaceType(GetType(), aBytes);
   684     mBytesRecorded += aBytes;
   685 }
   687 void
   688 gfxASurface::RecordMemoryFreed()
   689 {
   690     if (mBytesRecorded) {
   691         RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
   692         mBytesRecorded = 0;
   693     }
   694 }
   696 size_t
   697 gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   698 {
   699     // We don't measure mSurface because cairo doesn't allow it.
   700     return 0;
   701 }
   703 size_t
   704 gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
   705 {
   706     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   707 }
   709 /* static */ uint8_t
   710 gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat)
   711 {
   712   switch (aImageFormat) {
   713     case gfxImageFormat::ARGB32:
   714       return 4;
   715     case gfxImageFormat::RGB24:
   716       return 4;
   717     case gfxImageFormat::RGB16_565:
   718       return 2;
   719     case gfxImageFormat::A8:
   720       return 1;
   721     case gfxImageFormat::A1:
   722       return 1; // Close enough
   723     case gfxImageFormat::Unknown:
   724     default:
   725       NS_NOTREACHED("Not really sure what you want me to say here");
   726       return 0;
   727   }
   728 }
   730 void
   731 gfxASurface::WriteAsPNG(const char* aFile)
   732 {
   733     FILE *file = fopen(aFile, "wb");
   734     if (file) {
   735       WriteAsPNG_internal(file, true);
   736       fclose(file);
   737     } else {
   738       NS_WARNING("Failed to create file!\n");
   739     }
   740 }
   742 void
   743 gfxASurface::DumpAsDataURL(FILE* aOutput)
   744 {
   745   WriteAsPNG_internal(aOutput, false);
   746 }
   748 void
   749 gfxASurface::PrintAsDataURL()
   750 {
   751   WriteAsPNG_internal(stdout, false);
   752   fprintf(stdout, "\n");
   753 }
   755 void
   756 gfxASurface::CopyAsDataURL()
   757 {
   758   WriteAsPNG_internal(nullptr, false);
   759 }
   761 /**
   762  * Write to a PNG file. If aBinary is true, then it is written
   763  * as binary, otherwise as a data URL. If no file is specified then
   764  * data is copied to the clipboard (must not be binary!).
   765  */
   766 void
   767 gfxASurface::WriteAsPNG_internal(FILE* aFile, bool aBinary)
   768 {
   769   nsRefPtr<gfxImageSurface> imgsurf = GetAsImageSurface();
   770   nsIntSize size;
   772   // FIXME/bug 831898: hack r5g6b5 for now.
   773   if (!imgsurf || imgsurf->Format() == gfxImageFormat::RGB16_565) {
   774     size = GetSize();
   775     if (size.width == -1 && size.height == -1) {
   776       printf("Could not determine surface size\n");
   777       return;
   778     }
   780     imgsurf =
   781       new gfxImageSurface(nsIntSize(size.width, size.height),
   782                           gfxImageFormat::ARGB32);
   784     if (!imgsurf || imgsurf->CairoStatus()) {
   785       printf("Could not allocate image surface\n");
   786       return;
   787     }
   789     nsRefPtr<gfxContext> ctx = new gfxContext(imgsurf);
   790     if (!ctx || ctx->HasError()) {
   791       printf("Could not allocate image context\n");
   792       return;
   793     }
   795     ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
   796     ctx->SetSource(this, gfxPoint(0, 0));
   797     ctx->Paint();
   798   }
   799   size = imgsurf->GetSize();
   801   nsCOMPtr<imgIEncoder> encoder =
   802     do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
   803   if (!encoder) {
   804     int32_t w = std::min(size.width, 8);
   805     int32_t h = std::min(size.height, 8);
   806     printf("Could not create encoder. Printing %dx%d pixels.\n", w, h);
   807     for (int32_t y = 0; y < h; ++y) {
   808       for (int32_t x = 0; x < w; ++x) {
   809         printf("%x ", reinterpret_cast<uint32_t*>(imgsurf->Data())[y*imgsurf->Stride()+ x]);
   810       }
   811     }
   812     return;
   813   }
   815   nsresult rv = encoder->InitFromData(imgsurf->Data(),
   816                                       size.width * size.height * 4,
   817                                       size.width,
   818                                       size.height,
   819                                       imgsurf->Stride(),
   820                                       imgIEncoder::INPUT_FORMAT_HOSTARGB,
   821                                       NS_LITERAL_STRING(""));
   822   if (NS_FAILED(rv))
   823     return;
   825   nsCOMPtr<nsIInputStream> imgStream;
   826   CallQueryInterface(encoder.get(), getter_AddRefs(imgStream));
   827   if (!imgStream)
   828     return;
   830   uint64_t bufSize64;
   831   rv = imgStream->Available(&bufSize64);
   832   if (NS_FAILED(rv))
   833     return;
   835   if (bufSize64 > UINT32_MAX - 16)
   836     return;
   838   uint32_t bufSize = (uint32_t)bufSize64;
   840   // ...leave a little extra room so we can call read again and make sure we
   841   // got everything. 16 bytes for better padding (maybe)
   842   bufSize += 16;
   843   uint32_t imgSize = 0;
   844   char* imgData = (char*)moz_malloc(bufSize);
   845   if (!imgData)
   846     return;
   847   uint32_t numReadThisTime = 0;
   848   while ((rv = imgStream->Read(&imgData[imgSize],
   849                                bufSize - imgSize,
   850                                &numReadThisTime)) == NS_OK && numReadThisTime > 0)
   851   {
   852     imgSize += numReadThisTime;
   853     if (imgSize == bufSize) {
   854       // need a bigger buffer, just double
   855       bufSize *= 2;
   856       char* newImgData = (char*)moz_realloc(imgData, bufSize);
   857       if (!newImgData) {
   858         moz_free(imgData);
   859         return;
   860       }
   861       imgData = newImgData;
   862     }
   863   }
   865   if (aBinary) {
   866     if (aFile) {
   867       fwrite(imgData, 1, imgSize, aFile);
   868     } else {
   869       NS_WARNING("Can't write binary image data without a file!");
   870     }
   871     return;
   872   }
   874   // base 64, result will be null-terminated
   875   nsCString encodedImg;
   876   rv = Base64Encode(Substring(imgData, imgSize), encodedImg);
   877   moz_free(imgData);
   878   if (NS_FAILED(rv)) // not sure why this would fail
   879     return;
   881   nsCString string("data:image/png;base64,");
   882   string.Append(encodedImg);
   884   if (aFile) {
   885 #ifdef ANDROID
   886      if (aFile == stdout || aFile == stderr) {
   887        // ADB logcat cuts off long strings so we will break it down
   888        const char* cStr = string.BeginReading();
   889        size_t len = strlen(cStr);
   890        while (true) {
   891          printf_stderr("IMG: %.140s\n", cStr);
   892          if (len <= 140)
   893            break;
   894          len -= 140;
   895          cStr += 140;
   896        }
   897      }
   898 #endif
   899     fprintf(aFile, "%s", string.BeginReading());
   900   } else {
   901     nsCOMPtr<nsIClipboardHelper> clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv));
   902     if (clipboard) {
   903       clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr);
   904     }
   905   }
   907   return;
   908 }
   910 void
   911 gfxASurface::SetOpaqueRect(const gfxRect& aRect)
   912 {
   913     if (aRect.IsEmpty()) {
   914         mOpaqueRect = nullptr;
   915     } else if (!!mOpaqueRect) {
   916         *mOpaqueRect = aRect;
   917     } else {
   918         mOpaqueRect = new gfxRect(aRect);
   919     }
   920 }
   922 /* static */const gfxRect&
   923 gfxASurface::GetEmptyOpaqueRect()
   924 {
   925   static const gfxRect empty(0, 0, 0, 0);
   926   return empty;
   927 }
   929 const nsIntSize
   930 gfxASurface::GetSize() const
   931 {
   932   return nsIntSize(-1, -1);
   933 }
   935 already_AddRefed<gfxImageSurface>
   936 gfxASurface::GetAsImageSurface()
   937 {
   938   return nullptr;
   939 }

mercurial