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