gfx/thebes/gfxASurface.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.

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

mercurial