michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIMemoryReporter.h" michael@0: #include "nsMemory.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/Base64.h" michael@0: #include "mozilla/CheckedInt.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "nsISupportsImpl.h" michael@0: #include "mozilla/gfx/2D.h" michael@0: #include "gfx2DGlue.h" michael@0: michael@0: #include "gfxASurface.h" michael@0: #include "gfxContext.h" michael@0: #include "gfxImageSurface.h" michael@0: #include "gfxPlatform.h" michael@0: #include "gfxRect.h" michael@0: michael@0: #include "cairo.h" michael@0: #include michael@0: michael@0: #ifdef CAIRO_HAS_WIN32_SURFACE michael@0: #include "gfxWindowsSurface.h" michael@0: #endif michael@0: #ifdef CAIRO_HAS_D2D_SURFACE michael@0: #include "gfxD2DSurface.h" michael@0: #endif michael@0: michael@0: #ifdef MOZ_X11 michael@0: #include "gfxXlibSurface.h" michael@0: #endif michael@0: michael@0: #ifdef CAIRO_HAS_QUARTZ_SURFACE michael@0: #include "gfxQuartzSurface.h" michael@0: #include "gfxQuartzImageSurface.h" michael@0: #endif michael@0: michael@0: #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT) michael@0: #include "gfxQPainterSurface.h" michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "imgIEncoder.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsISupportsUtils.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsString.h" michael@0: #include "nsIClipboardHelper.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: michael@0: static cairo_user_data_key_t gfxasurface_pointer_key; michael@0: michael@0: gfxASurface::gfxASurface() michael@0: : mSurface(nullptr), mFloatingRefs(0), mBytesRecorded(0), michael@0: mSurfaceValid(false), mAllowUseAsSource(true) michael@0: { michael@0: MOZ_COUNT_CTOR(gfxASurface); michael@0: } michael@0: michael@0: gfxASurface::~gfxASurface() michael@0: { michael@0: RecordMemoryFreed(); michael@0: michael@0: MOZ_COUNT_DTOR(gfxASurface); michael@0: } michael@0: michael@0: // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid michael@0: // refcount mismatch issues. michael@0: nsrefcnt michael@0: gfxASurface::AddRef(void) michael@0: { michael@0: if (mSurfaceValid) { michael@0: if (mFloatingRefs) { michael@0: // eat a floating ref michael@0: mFloatingRefs--; michael@0: } else { michael@0: cairo_surface_reference(mSurface); michael@0: } michael@0: michael@0: return (nsrefcnt) cairo_surface_get_reference_count(mSurface); michael@0: } else { michael@0: // the surface isn't valid, but we still need to refcount michael@0: // the gfxASurface michael@0: return ++mFloatingRefs; michael@0: } michael@0: } michael@0: michael@0: nsrefcnt michael@0: gfxASurface::Release(void) michael@0: { michael@0: if (mSurfaceValid) { michael@0: NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!"); michael@0: michael@0: // Note that there is a destructor set on user data for mSurface, michael@0: // which will delete this gfxASurface wrapper when the surface's refcount goes michael@0: // out of scope. michael@0: nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface); michael@0: cairo_surface_destroy(mSurface); michael@0: michael@0: // |this| may not be valid any more, don't use it! michael@0: michael@0: return --refcnt; michael@0: } else { michael@0: if (--mFloatingRefs == 0) { michael@0: delete this; michael@0: return 0; michael@0: } michael@0: michael@0: return mFloatingRefs; michael@0: } michael@0: } michael@0: michael@0: nsrefcnt michael@0: gfxASurface::AddRefExternal(void) michael@0: { michael@0: return AddRef(); michael@0: } michael@0: michael@0: nsrefcnt michael@0: gfxASurface::ReleaseExternal(void) michael@0: { michael@0: return Release(); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::SurfaceDestroyFunc(void *data) { michael@0: gfxASurface *surf = (gfxASurface*) data; michael@0: // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data); michael@0: delete surf; michael@0: } michael@0: michael@0: gfxASurface* michael@0: gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf) michael@0: { michael@0: if (!csurf) michael@0: return nullptr; michael@0: return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf) michael@0: { michael@0: if (!csurf) michael@0: return; michael@0: cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc); michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxASurface::Wrap (cairo_surface_t *csurf, const gfxIntSize& aSize) michael@0: { michael@0: nsRefPtr result; michael@0: michael@0: /* Do we already have a wrapper for this surface? */ michael@0: result = GetSurfaceWrapper(csurf); michael@0: if (result) { michael@0: // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result); michael@0: return result.forget(); michael@0: } michael@0: michael@0: /* No wrapper; figure out the surface type and create it */ michael@0: cairo_surface_type_t stype = cairo_surface_get_type(csurf); michael@0: michael@0: if (stype == CAIRO_SURFACE_TYPE_IMAGE) { michael@0: result = new gfxImageSurface(csurf); michael@0: } michael@0: #ifdef CAIRO_HAS_WIN32_SURFACE michael@0: else if (stype == CAIRO_SURFACE_TYPE_WIN32 || michael@0: stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) { michael@0: result = new gfxWindowsSurface(csurf); michael@0: } michael@0: #endif michael@0: #ifdef CAIRO_HAS_D2D_SURFACE michael@0: else if (stype == CAIRO_SURFACE_TYPE_D2D) { michael@0: result = new gfxD2DSurface(csurf); michael@0: } michael@0: #endif michael@0: #ifdef MOZ_X11 michael@0: else if (stype == CAIRO_SURFACE_TYPE_XLIB) { michael@0: result = new gfxXlibSurface(csurf); michael@0: } michael@0: #endif michael@0: #ifdef CAIRO_HAS_QUARTZ_SURFACE michael@0: else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) { michael@0: result = new gfxQuartzSurface(csurf, aSize); michael@0: } michael@0: else if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) { michael@0: result = new gfxQuartzImageSurface(csurf); michael@0: } michael@0: #endif michael@0: #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT) michael@0: else if (stype == CAIRO_SURFACE_TYPE_QT) { michael@0: result = new gfxQPainterSurface(csurf); michael@0: } michael@0: #endif michael@0: else { michael@0: result = new gfxUnknownSurface(csurf, aSize); michael@0: } michael@0: michael@0: // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result); michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::Init(cairo_surface_t* surface, bool existingSurface) michael@0: { michael@0: SetSurfaceWrapper(surface, this); michael@0: michael@0: mSurface = surface; michael@0: mSurfaceValid = surface && !cairo_surface_status(surface); michael@0: michael@0: if (existingSurface || !mSurfaceValid) { michael@0: mFloatingRefs = 0; michael@0: } else { michael@0: mFloatingRefs = 1; michael@0: #ifdef MOZ_TREE_CAIRO michael@0: if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) { michael@0: cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED); michael@0: } michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: gfxSurfaceType michael@0: gfxASurface::GetType() const michael@0: { michael@0: if (!mSurfaceValid) michael@0: return (gfxSurfaceType)-1; michael@0: michael@0: return (gfxSurfaceType)cairo_surface_get_type(mSurface); michael@0: } michael@0: michael@0: gfxContentType michael@0: gfxASurface::GetContentType() const michael@0: { michael@0: if (!mSurfaceValid) michael@0: return (gfxContentType)-1; michael@0: michael@0: return (gfxContentType)cairo_surface_get_content(mSurface); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::SetDeviceOffset(const gfxPoint& offset) michael@0: { michael@0: if (!mSurfaceValid) michael@0: return; michael@0: cairo_surface_set_device_offset(mSurface, michael@0: offset.x, offset.y); michael@0: } michael@0: michael@0: gfxPoint michael@0: gfxASurface::GetDeviceOffset() const michael@0: { michael@0: if (!mSurfaceValid) michael@0: return gfxPoint(0.0, 0.0); michael@0: gfxPoint pt; michael@0: cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y); michael@0: return pt; michael@0: } michael@0: michael@0: void michael@0: gfxASurface::Flush() const michael@0: { michael@0: if (!mSurfaceValid) michael@0: return; michael@0: cairo_surface_flush(mSurface); michael@0: gfxPlatform::ClearSourceSurfaceForSurface(const_cast(this)); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::MarkDirty() michael@0: { michael@0: if (!mSurfaceValid) michael@0: return; michael@0: cairo_surface_mark_dirty(mSurface); michael@0: gfxPlatform::ClearSourceSurfaceForSurface(this); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::MarkDirty(const gfxRect& r) michael@0: { michael@0: if (!mSurfaceValid) michael@0: return; michael@0: cairo_surface_mark_dirty_rectangle(mSurface, michael@0: (int) r.X(), (int) r.Y(), michael@0: (int) r.Width(), (int) r.Height()); michael@0: gfxPlatform::ClearSourceSurfaceForSurface(this); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::SetData(const cairo_user_data_key_t *key, michael@0: void *user_data, michael@0: thebes_destroy_func_t destroy) michael@0: { michael@0: if (!mSurfaceValid) michael@0: return; michael@0: cairo_surface_set_user_data(mSurface, key, user_data, destroy); michael@0: } michael@0: michael@0: void * michael@0: gfxASurface::GetData(const cairo_user_data_key_t *key) michael@0: { michael@0: if (!mSurfaceValid) michael@0: return nullptr; michael@0: return cairo_surface_get_user_data(mSurface, key); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::Finish() michael@0: { michael@0: // null surfaces are allowed here michael@0: cairo_surface_finish(mSurface); michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxASurface::CreateSimilarSurface(gfxContentType aContent, michael@0: const nsIntSize& aSize) michael@0: { michael@0: if (!mSurface || !mSurfaceValid) { michael@0: return nullptr; michael@0: } michael@0: michael@0: cairo_surface_t *surface = michael@0: cairo_surface_create_similar(mSurface, cairo_content_t(int(aContent)), michael@0: aSize.width, aSize.height); michael@0: if (cairo_surface_status(surface)) { michael@0: cairo_surface_destroy(surface); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr result = Wrap(surface, aSize); michael@0: cairo_surface_destroy(surface); michael@0: return result.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxASurface::GetAsReadableARGB32ImageSurface() michael@0: { michael@0: nsRefPtr imgSurface = GetAsImageSurface(); michael@0: if (!imgSurface || imgSurface->Format() != gfxImageFormat::ARGB32) { michael@0: imgSurface = CopyToARGB32ImageSurface(); michael@0: } michael@0: return imgSurface.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxASurface::CopyToARGB32ImageSurface() michael@0: { michael@0: if (!mSurface || !mSurfaceValid) { michael@0: return nullptr; michael@0: } michael@0: michael@0: const nsIntSize size = GetSize(); michael@0: nsRefPtr imgSurface = michael@0: new gfxImageSurface(size, gfxImageFormat::ARGB32); michael@0: michael@0: RefPtr dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height)); michael@0: RefPtr source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this); michael@0: michael@0: dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint()); michael@0: michael@0: return imgSurface.forget(); michael@0: } michael@0: michael@0: int michael@0: gfxASurface::CairoStatus() michael@0: { michael@0: if (!mSurfaceValid) michael@0: return -1; michael@0: michael@0: return cairo_surface_status(mSurface); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: gfxASurface::CheckSurfaceSize(const nsIntSize& sz, int32_t limit) michael@0: { michael@0: if (sz.width < 0 || sz.height < 0) { michael@0: NS_WARNING("Surface width or height < 0!"); michael@0: return false; michael@0: } michael@0: michael@0: // reject images with sides bigger than limit michael@0: if (limit && (sz.width > limit || sz.height > limit)) { michael@0: NS_WARNING("Surface size too large (exceeds caller's limit)!"); michael@0: return false; michael@0: } michael@0: michael@0: #if defined(XP_MACOSX) michael@0: // CoreGraphics is limited to images < 32K in *height*, michael@0: // so clamp all surfaces on the Mac to that height michael@0: if (sz.height > SHRT_MAX) { michael@0: NS_WARNING("Surface size too large (exceeds CoreGraphics limit)!"); michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: // make sure the surface area doesn't overflow a int32_t michael@0: CheckedInt tmp = sz.width; michael@0: tmp *= sz.height; michael@0: if (!tmp.isValid()) { michael@0: NS_WARNING("Surface size too large (would overflow)!"); michael@0: return false; michael@0: } michael@0: michael@0: // assuming 4-byte stride, make sure the allocation size michael@0: // doesn't overflow a int32_t either michael@0: tmp *= 4; michael@0: if (!tmp.isValid()) { michael@0: NS_WARNING("Allocation too large (would overflow)!"); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* static */ michael@0: int32_t michael@0: gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width) michael@0: { michael@0: return cairo_format_stride_for_width((cairo_format_t)(int)format, (int)width); michael@0: } michael@0: michael@0: nsresult michael@0: gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: gfxASurface::EndPrinting() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: gfxASurface::AbortPrinting() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: gfxASurface::BeginPage() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: gfxASurface::EndPage() michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: gfxContentType michael@0: gfxASurface::ContentFromFormat(gfxImageFormat format) michael@0: { michael@0: switch (format) { michael@0: case gfxImageFormat::ARGB32: michael@0: return gfxContentType::COLOR_ALPHA; michael@0: case gfxImageFormat::RGB24: michael@0: case gfxImageFormat::RGB16_565: michael@0: return gfxContentType::COLOR; michael@0: case gfxImageFormat::A8: michael@0: case gfxImageFormat::A1: michael@0: return gfxContentType::ALPHA; michael@0: michael@0: case gfxImageFormat::Unknown: michael@0: default: michael@0: return gfxContentType::COLOR; michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxASurface::SetSubpixelAntialiasingEnabled(bool aEnabled) michael@0: { michael@0: #ifdef MOZ_TREE_CAIRO michael@0: if (!mSurfaceValid) michael@0: return; michael@0: cairo_surface_set_subpixel_antialiasing(mSurface, michael@0: aEnabled ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED); michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: gfxASurface::GetSubpixelAntialiasingEnabled() michael@0: { michael@0: if (!mSurfaceValid) michael@0: return false; michael@0: #ifdef MOZ_TREE_CAIRO michael@0: return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED; michael@0: #else michael@0: return true; michael@0: #endif michael@0: } michael@0: michael@0: gfxMemoryLocation michael@0: gfxASurface::GetMemoryLocation() const michael@0: { michael@0: return gfxMemoryLocation::IN_PROCESS_HEAP; michael@0: } michael@0: michael@0: int32_t michael@0: gfxASurface::BytePerPixelFromFormat(gfxImageFormat format) michael@0: { michael@0: switch (format) { michael@0: case gfxImageFormat::ARGB32: michael@0: case gfxImageFormat::RGB24: michael@0: return 4; michael@0: case gfxImageFormat::RGB16_565: michael@0: return 2; michael@0: case gfxImageFormat::A8: michael@0: return 1; michael@0: default: michael@0: NS_WARNING("Unknown byte per pixel value for Image format"); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: gfxASurface::FastMovePixels(const nsIntRect& aSourceRect, michael@0: const nsIntPoint& aDestTopLeft) michael@0: { michael@0: // Used when the backend can internally handle self copies. michael@0: nsIntRect dest(aDestTopLeft, aSourceRect.Size()); michael@0: michael@0: nsRefPtr ctx = new gfxContext(this); michael@0: ctx->SetOperator(gfxContext::OPERATOR_SOURCE); michael@0: nsIntPoint srcOrigin = dest.TopLeft() - aSourceRect.TopLeft(); michael@0: ctx->SetSource(this, gfxPoint(srcOrigin.x, srcOrigin.y)); michael@0: ctx->Rectangle(gfxRect(dest.x, dest.y, dest.width, dest.height)); michael@0: ctx->Fill(); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::MovePixels(const nsIntRect& aSourceRect, michael@0: const nsIntPoint& aDestTopLeft) michael@0: { michael@0: // Assume the backend can't handle self copying well and allocate michael@0: // a temporary surface instead. michael@0: nsRefPtr tmp = michael@0: CreateSimilarSurface(GetContentType(), michael@0: nsIntSize(aSourceRect.width, aSourceRect.height)); michael@0: // CreateSimilarSurface can return nullptr if the current surface is michael@0: // in an error state. This isn't good, but its better to carry michael@0: // on with the error surface instead of crashing. michael@0: NS_WARN_IF_FALSE(tmp, "Must have temporary surface to move pixels!"); michael@0: if (!tmp) { michael@0: return; michael@0: } michael@0: nsRefPtr ctx = new gfxContext(tmp); michael@0: ctx->SetOperator(gfxContext::OPERATOR_SOURCE); michael@0: ctx->SetSource(this, gfxPoint(-aSourceRect.x, -aSourceRect.y)); michael@0: ctx->Paint(); michael@0: michael@0: ctx = new gfxContext(this); michael@0: ctx->SetOperator(gfxContext::OPERATOR_SOURCE); michael@0: ctx->SetSource(tmp, gfxPoint(aDestTopLeft.x, aDestTopLeft.y)); michael@0: ctx->Rectangle(gfxRect(aDestTopLeft.x, michael@0: aDestTopLeft.y, michael@0: aSourceRect.width, michael@0: aSourceRect.height)); michael@0: ctx->Fill(); michael@0: } michael@0: michael@0: /** Memory reporting **/ michael@0: michael@0: static const char *sDefaultSurfaceDescription = michael@0: "Memory used by gfx surface of the given type."; michael@0: michael@0: struct SurfaceMemoryReporterAttrs { michael@0: const char *path; michael@0: const char *description; michael@0: }; michael@0: michael@0: static const SurfaceMemoryReporterAttrs sSurfaceMemoryReporterAttrs[] = { michael@0: {"gfx-surface-image", nullptr}, michael@0: {"gfx-surface-pdf", nullptr}, michael@0: {"gfx-surface-ps", nullptr}, michael@0: {"gfx-surface-xlib", michael@0: "Memory used by xlib surfaces to store pixmaps. This memory lives in " michael@0: "the X server's process rather than in this application, so the bytes " michael@0: "accounted for here aren't counted in vsize, resident, explicit, or any of " michael@0: "the other measurements on this page."}, michael@0: {"gfx-surface-xcb", nullptr}, michael@0: {"gfx-surface-glitz???", nullptr}, // should never be used michael@0: {"gfx-surface-quartz", nullptr}, michael@0: {"gfx-surface-win32", nullptr}, michael@0: {"gfx-surface-beos", nullptr}, michael@0: {"gfx-surface-directfb???", nullptr}, // should never be used michael@0: {"gfx-surface-svg", nullptr}, michael@0: {"gfx-surface-os2", nullptr}, michael@0: {"gfx-surface-win32printing", nullptr}, michael@0: {"gfx-surface-quartzimage", nullptr}, michael@0: {"gfx-surface-script", nullptr}, michael@0: {"gfx-surface-qpainter", nullptr}, michael@0: {"gfx-surface-recording", nullptr}, michael@0: {"gfx-surface-vg", nullptr}, michael@0: {"gfx-surface-gl", nullptr}, michael@0: {"gfx-surface-drm", nullptr}, michael@0: {"gfx-surface-tee", nullptr}, michael@0: {"gfx-surface-xml", nullptr}, michael@0: {"gfx-surface-skia", nullptr}, michael@0: {"gfx-surface-subsurface", nullptr}, michael@0: {"gfx-surface-d2d", nullptr}, michael@0: }; michael@0: michael@0: PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(sSurfaceMemoryReporterAttrs) == michael@0: size_t(gfxSurfaceType::Max)); michael@0: #ifdef CAIRO_HAS_D2D_SURFACE michael@0: PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_TYPE_D2D) == michael@0: uint32_t(gfxSurfaceType::D2D)); michael@0: #endif michael@0: PR_STATIC_ASSERT(uint32_t(CAIRO_SURFACE_TYPE_SKIA) == michael@0: uint32_t(gfxSurfaceType::Skia)); michael@0: michael@0: /* Surface size memory reporting */ michael@0: michael@0: static int64_t gSurfaceMemoryUsed[size_t(gfxSurfaceType::Max)] = { 0 }; michael@0: michael@0: class SurfaceMemoryReporter MOZ_FINAL : public nsIMemoryReporter michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: michael@0: NS_IMETHOD CollectReports(nsIMemoryReporterCallback *aCb, michael@0: nsISupports *aClosure) michael@0: { michael@0: const size_t len = ArrayLength(sSurfaceMemoryReporterAttrs); michael@0: for (size_t i = 0; i < len; i++) { michael@0: int64_t amount = gSurfaceMemoryUsed[i]; michael@0: michael@0: if (amount != 0) { michael@0: const char *path = sSurfaceMemoryReporterAttrs[i].path; michael@0: const char *desc = sSurfaceMemoryReporterAttrs[i].description; michael@0: if (!desc) { michael@0: desc = sDefaultSurfaceDescription; michael@0: } michael@0: michael@0: nsresult rv = aCb->Callback(EmptyCString(), nsCString(path), michael@0: KIND_OTHER, UNITS_BYTES, michael@0: gSurfaceMemoryUsed[i], michael@0: nsCString(desc), aClosure); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: NS_IMPL_ISUPPORTS(SurfaceMemoryReporter, nsIMemoryReporter) michael@0: michael@0: void michael@0: gfxASurface::RecordMemoryUsedForSurfaceType(gfxSurfaceType aType, michael@0: int32_t aBytes) michael@0: { michael@0: if (int(aType) < 0 || aType >= gfxSurfaceType::Max) { michael@0: NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!"); michael@0: return; michael@0: } michael@0: michael@0: static bool registered = false; michael@0: if (!registered) { michael@0: RegisterStrongMemoryReporter(new SurfaceMemoryReporter()); michael@0: registered = true; michael@0: } michael@0: michael@0: gSurfaceMemoryUsed[size_t(aType)] += aBytes; michael@0: } michael@0: michael@0: void michael@0: gfxASurface::RecordMemoryUsed(int32_t aBytes) michael@0: { michael@0: RecordMemoryUsedForSurfaceType(GetType(), aBytes); michael@0: mBytesRecorded += aBytes; michael@0: } michael@0: michael@0: void michael@0: gfxASurface::RecordMemoryFreed() michael@0: { michael@0: if (mBytesRecorded) { michael@0: RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded); michael@0: mBytesRecorded = 0; michael@0: } michael@0: } michael@0: michael@0: size_t michael@0: gfxASurface::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: // We don't measure mSurface because cairo doesn't allow it. michael@0: return 0; michael@0: } michael@0: michael@0: size_t michael@0: gfxASurface::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: /* static */ uint8_t michael@0: gfxASurface::BytesPerPixel(gfxImageFormat aImageFormat) michael@0: { michael@0: switch (aImageFormat) { michael@0: case gfxImageFormat::ARGB32: michael@0: return 4; michael@0: case gfxImageFormat::RGB24: michael@0: return 4; michael@0: case gfxImageFormat::RGB16_565: michael@0: return 2; michael@0: case gfxImageFormat::A8: michael@0: return 1; michael@0: case gfxImageFormat::A1: michael@0: return 1; // Close enough michael@0: case gfxImageFormat::Unknown: michael@0: default: michael@0: NS_NOTREACHED("Not really sure what you want me to say here"); michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxASurface::WriteAsPNG(const char* aFile) michael@0: { michael@0: FILE *file = fopen(aFile, "wb"); michael@0: if (file) { michael@0: WriteAsPNG_internal(file, true); michael@0: fclose(file); michael@0: } else { michael@0: NS_WARNING("Failed to create file!\n"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxASurface::DumpAsDataURL(FILE* aOutput) michael@0: { michael@0: WriteAsPNG_internal(aOutput, false); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::PrintAsDataURL() michael@0: { michael@0: WriteAsPNG_internal(stdout, false); michael@0: fprintf(stdout, "\n"); michael@0: } michael@0: michael@0: void michael@0: gfxASurface::CopyAsDataURL() michael@0: { michael@0: WriteAsPNG_internal(nullptr, false); michael@0: } michael@0: michael@0: /** michael@0: * Write to a PNG file. If aBinary is true, then it is written michael@0: * as binary, otherwise as a data URL. If no file is specified then michael@0: * data is copied to the clipboard (must not be binary!). michael@0: */ michael@0: void michael@0: gfxASurface::WriteAsPNG_internal(FILE* aFile, bool aBinary) michael@0: { michael@0: nsRefPtr imgsurf = GetAsImageSurface(); michael@0: nsIntSize size; michael@0: michael@0: // FIXME/bug 831898: hack r5g6b5 for now. michael@0: if (!imgsurf || imgsurf->Format() == gfxImageFormat::RGB16_565) { michael@0: size = GetSize(); michael@0: if (size.width == -1 && size.height == -1) { michael@0: printf("Could not determine surface size\n"); michael@0: return; michael@0: } michael@0: michael@0: imgsurf = michael@0: new gfxImageSurface(nsIntSize(size.width, size.height), michael@0: gfxImageFormat::ARGB32); michael@0: michael@0: if (!imgsurf || imgsurf->CairoStatus()) { michael@0: printf("Could not allocate image surface\n"); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr ctx = new gfxContext(imgsurf); michael@0: if (!ctx || ctx->HasError()) { michael@0: printf("Could not allocate image context\n"); michael@0: return; michael@0: } michael@0: michael@0: ctx->SetOperator(gfxContext::OPERATOR_SOURCE); michael@0: ctx->SetSource(this, gfxPoint(0, 0)); michael@0: ctx->Paint(); michael@0: } michael@0: size = imgsurf->GetSize(); michael@0: michael@0: nsCOMPtr encoder = michael@0: do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png"); michael@0: if (!encoder) { michael@0: int32_t w = std::min(size.width, 8); michael@0: int32_t h = std::min(size.height, 8); michael@0: printf("Could not create encoder. Printing %dx%d pixels.\n", w, h); michael@0: for (int32_t y = 0; y < h; ++y) { michael@0: for (int32_t x = 0; x < w; ++x) { michael@0: printf("%x ", reinterpret_cast(imgsurf->Data())[y*imgsurf->Stride()+ x]); michael@0: } michael@0: } michael@0: return; michael@0: } michael@0: michael@0: nsresult rv = encoder->InitFromData(imgsurf->Data(), michael@0: size.width * size.height * 4, michael@0: size.width, michael@0: size.height, michael@0: imgsurf->Stride(), michael@0: imgIEncoder::INPUT_FORMAT_HOSTARGB, michael@0: NS_LITERAL_STRING("")); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: nsCOMPtr imgStream; michael@0: CallQueryInterface(encoder.get(), getter_AddRefs(imgStream)); michael@0: if (!imgStream) michael@0: return; michael@0: michael@0: uint64_t bufSize64; michael@0: rv = imgStream->Available(&bufSize64); michael@0: if (NS_FAILED(rv)) michael@0: return; michael@0: michael@0: if (bufSize64 > UINT32_MAX - 16) michael@0: return; michael@0: michael@0: uint32_t bufSize = (uint32_t)bufSize64; michael@0: michael@0: // ...leave a little extra room so we can call read again and make sure we michael@0: // got everything. 16 bytes for better padding (maybe) michael@0: bufSize += 16; michael@0: uint32_t imgSize = 0; michael@0: char* imgData = (char*)moz_malloc(bufSize); michael@0: if (!imgData) michael@0: return; michael@0: uint32_t numReadThisTime = 0; michael@0: while ((rv = imgStream->Read(&imgData[imgSize], michael@0: bufSize - imgSize, michael@0: &numReadThisTime)) == NS_OK && numReadThisTime > 0) michael@0: { michael@0: imgSize += numReadThisTime; michael@0: if (imgSize == bufSize) { michael@0: // need a bigger buffer, just double michael@0: bufSize *= 2; michael@0: char* newImgData = (char*)moz_realloc(imgData, bufSize); michael@0: if (!newImgData) { michael@0: moz_free(imgData); michael@0: return; michael@0: } michael@0: imgData = newImgData; michael@0: } michael@0: } michael@0: michael@0: if (aBinary) { michael@0: if (aFile) { michael@0: fwrite(imgData, 1, imgSize, aFile); michael@0: } else { michael@0: NS_WARNING("Can't write binary image data without a file!"); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: // base 64, result will be null-terminated michael@0: nsCString encodedImg; michael@0: rv = Base64Encode(Substring(imgData, imgSize), encodedImg); michael@0: moz_free(imgData); michael@0: if (NS_FAILED(rv)) // not sure why this would fail michael@0: return; michael@0: michael@0: nsCString string("data:image/png;base64,"); michael@0: string.Append(encodedImg); michael@0: michael@0: if (aFile) { michael@0: #ifdef ANDROID michael@0: if (aFile == stdout || aFile == stderr) { michael@0: // ADB logcat cuts off long strings so we will break it down michael@0: const char* cStr = string.BeginReading(); michael@0: size_t len = strlen(cStr); michael@0: while (true) { michael@0: printf_stderr("IMG: %.140s\n", cStr); michael@0: if (len <= 140) michael@0: break; michael@0: len -= 140; michael@0: cStr += 140; michael@0: } michael@0: } michael@0: #endif michael@0: fprintf(aFile, "%s", string.BeginReading()); michael@0: } else { michael@0: nsCOMPtr clipboard(do_GetService("@mozilla.org/widget/clipboardhelper;1", &rv)); michael@0: if (clipboard) { michael@0: clipboard->CopyString(NS_ConvertASCIItoUTF16(string), nullptr); michael@0: } michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: void michael@0: gfxASurface::SetOpaqueRect(const gfxRect& aRect) michael@0: { michael@0: if (aRect.IsEmpty()) { michael@0: mOpaqueRect = nullptr; michael@0: } else if (!!mOpaqueRect) { michael@0: *mOpaqueRect = aRect; michael@0: } else { michael@0: mOpaqueRect = new gfxRect(aRect); michael@0: } michael@0: } michael@0: michael@0: /* static */const gfxRect& michael@0: gfxASurface::GetEmptyOpaqueRect() michael@0: { michael@0: static const gfxRect empty(0, 0, 0, 0); michael@0: return empty; michael@0: } michael@0: michael@0: const nsIntSize michael@0: gfxASurface::GetSize() const michael@0: { michael@0: return nsIntSize(-1, -1); michael@0: } michael@0: michael@0: already_AddRefed michael@0: gfxASurface::GetAsImageSurface() michael@0: { michael@0: return nullptr; michael@0: }