gfx/thebes/gfxASurface.cpp

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

mercurial