|
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 } |