Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | } |