Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / gfx / thebes / gfxASurface.cpp
blobace89bf584280cf706fe6ee3fd0931929f50ed46
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
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/
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.
15 * The Original Code is Mozilla Foundation code.
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.
21 * Contributor(s):
22 * Stuart Parmenter <stuart@mozilla.com>
23 * Vladimir Vukicevic <vladimir@pobox.com>
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.
37 * ***** END LICENSE BLOCK ***** */
39 #include "nsIMemoryReporter.h"
40 #include "nsMemory.h"
42 #include "gfxASurface.h"
44 #include "gfxImageSurface.h"
46 #include "cairo.h"
48 #ifdef CAIRO_HAS_WIN32_SURFACE
49 #include "gfxWindowsSurface.h"
50 #endif
51 #ifdef CAIRO_HAS_D2D_SURFACE
52 #include "gfxD2DSurface.h"
53 #endif
55 #ifdef MOZ_X11
56 #include "gfxXlibSurface.h"
57 #endif
59 #ifdef CAIRO_HAS_QUARTZ_SURFACE
60 #include "gfxQuartzSurface.h"
61 #include "gfxQuartzImageSurface.h"
62 #endif
64 #ifdef MOZ_DFB
65 #include "gfxDirectFBSurface.h"
66 #endif
68 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
69 #include "gfxQPainterSurface.h"
70 #endif
72 #include <stdio.h>
73 #include <limits.h>
75 static cairo_user_data_key_t gfxasurface_pointer_key;
77 // Surfaces use refcounting that's tied to the cairo surface refcnt, to avoid
78 // refcount mismatch issues.
79 nsrefcnt
80 gfxASurface::AddRef(void)
82 if (mSurfaceValid) {
83 if (mFloatingRefs) {
84 // eat a floating ref
85 mFloatingRefs--;
86 } else {
87 cairo_surface_reference(mSurface);
90 return (nsrefcnt) cairo_surface_get_reference_count(mSurface);
91 } else {
92 // the surface isn't valid, but we still need to refcount
93 // the gfxASurface
94 return ++mFloatingRefs;
98 nsrefcnt
99 gfxASurface::Release(void)
101 if (mSurfaceValid) {
102 NS_ASSERTION(mFloatingRefs == 0, "gfxASurface::Release with floating refs still hanging around!");
104 // Note that there is a destructor set on user data for mSurface,
105 // which will delete this gfxASurface wrapper when the surface's refcount goes
106 // out of scope.
107 nsrefcnt refcnt = (nsrefcnt) cairo_surface_get_reference_count(mSurface);
108 cairo_surface_destroy(mSurface);
110 // |this| may not be valid any more, don't use it!
112 return --refcnt;
113 } else {
114 if (--mFloatingRefs == 0) {
115 delete this;
116 return 0;
119 return mFloatingRefs;
123 void
124 gfxASurface::SurfaceDestroyFunc(void *data) {
125 gfxASurface *surf = (gfxASurface*) data;
126 // fprintf (stderr, "Deleting wrapper for %p (wrapper: %p)\n", surf->mSurface, data);
127 delete surf;
130 gfxASurface*
131 gfxASurface::GetSurfaceWrapper(cairo_surface_t *csurf)
133 return (gfxASurface*) cairo_surface_get_user_data(csurf, &gfxasurface_pointer_key);
136 void
137 gfxASurface::SetSurfaceWrapper(cairo_surface_t *csurf, gfxASurface *asurf)
139 cairo_surface_set_user_data(csurf, &gfxasurface_pointer_key, asurf, SurfaceDestroyFunc);
142 already_AddRefed<gfxASurface>
143 gfxASurface::Wrap (cairo_surface_t *csurf)
145 gfxASurface *result;
147 /* Do we already have a wrapper for this surface? */
148 result = GetSurfaceWrapper(csurf);
149 if (result) {
150 // fprintf(stderr, "Existing wrapper for %p -> %p\n", csurf, result);
151 NS_ADDREF(result);
152 return result;
155 /* No wrapper; figure out the surface type and create it */
156 cairo_surface_type_t stype = cairo_surface_get_type(csurf);
158 if (stype == CAIRO_SURFACE_TYPE_IMAGE) {
159 result = new gfxImageSurface(csurf);
161 #ifdef CAIRO_HAS_WIN32_SURFACE
162 else if (stype == CAIRO_SURFACE_TYPE_WIN32 ||
163 stype == CAIRO_SURFACE_TYPE_WIN32_PRINTING) {
164 result = new gfxWindowsSurface(csurf);
166 #endif
167 #ifdef CAIRO_HAS_D2D_SURFACE
168 else if (stype == CAIRO_SURFACE_TYPE_D2D) {
169 result = new gfxD2DSurface(csurf);
171 #endif
172 #ifdef MOZ_X11
173 else if (stype == CAIRO_SURFACE_TYPE_XLIB) {
174 result = new gfxXlibSurface(csurf);
176 #endif
177 #ifdef CAIRO_HAS_QUARTZ_SURFACE
178 else if (stype == CAIRO_SURFACE_TYPE_QUARTZ) {
179 result = new gfxQuartzSurface(csurf);
181 else if (stype == CAIRO_SURFACE_TYPE_QUARTZ_IMAGE) {
182 result = new gfxQuartzImageSurface(csurf);
184 #endif
185 #ifdef MOZ_DFB
186 else if (stype == CAIRO_SURFACE_TYPE_DIRECTFB) {
187 result = new gfxDirectFBSurface(csurf);
189 #endif
190 #if defined(CAIRO_HAS_QT_SURFACE) && defined(MOZ_WIDGET_QT)
191 else if (stype == CAIRO_SURFACE_TYPE_QT) {
192 result = new gfxQPainterSurface(csurf);
194 #endif
195 else {
196 result = new gfxUnknownSurface(csurf);
199 // fprintf(stderr, "New wrapper for %p -> %p\n", csurf, result);
201 NS_ADDREF(result);
202 return result;
205 void
206 gfxASurface::Init(cairo_surface_t* surface, PRBool existingSurface)
208 if (cairo_surface_status(surface)) {
209 // the surface has an error on it
210 mSurfaceValid = PR_FALSE;
211 cairo_surface_destroy(surface);
212 return;
215 SetSurfaceWrapper(surface, this);
217 mSurface = surface;
218 mSurfaceValid = PR_TRUE;
220 if (existingSurface) {
221 mFloatingRefs = 0;
222 } else {
223 mFloatingRefs = 1;
224 if (cairo_surface_get_content(surface) != CAIRO_CONTENT_COLOR) {
225 cairo_surface_set_subpixel_antialiasing(surface, CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
230 gfxASurface::gfxSurfaceType
231 gfxASurface::GetType() const
233 if (!mSurfaceValid)
234 return (gfxSurfaceType)-1;
236 return (gfxSurfaceType)cairo_surface_get_type(mSurface);
239 gfxASurface::gfxContentType
240 gfxASurface::GetContentType() const
242 if (!mSurfaceValid)
243 return (gfxContentType)-1;
245 return (gfxContentType)cairo_surface_get_content(mSurface);
248 void
249 gfxASurface::SetDeviceOffset(const gfxPoint& offset)
251 cairo_surface_set_device_offset(mSurface,
252 offset.x, offset.y);
255 gfxPoint
256 gfxASurface::GetDeviceOffset() const
258 gfxPoint pt;
259 cairo_surface_get_device_offset(mSurface, &pt.x, &pt.y);
260 return pt;
263 void
264 gfxASurface::Flush() const
266 cairo_surface_flush(mSurface);
269 void
270 gfxASurface::MarkDirty()
272 cairo_surface_mark_dirty(mSurface);
275 void
276 gfxASurface::MarkDirty(const gfxRect& r)
278 cairo_surface_mark_dirty_rectangle(mSurface,
279 (int) r.pos.x, (int) r.pos.y,
280 (int) r.size.width, (int) r.size.height);
283 void
284 gfxASurface::SetData(const cairo_user_data_key_t *key,
285 void *user_data,
286 thebes_destroy_func_t destroy)
288 cairo_surface_set_user_data(mSurface, key, user_data, destroy);
291 void *
292 gfxASurface::GetData(const cairo_user_data_key_t *key)
294 return cairo_surface_get_user_data(mSurface, key);
297 void
298 gfxASurface::Finish()
300 cairo_surface_finish(mSurface);
303 already_AddRefed<gfxASurface>
304 gfxASurface::CreateSimilarSurface(gfxContentType aContent,
305 const gfxIntSize& aSize)
307 if (!mSurface || !mSurfaceValid) {
308 return nsnull;
311 cairo_surface_t *surface =
312 cairo_surface_create_similar(mSurface, cairo_content_t(aContent),
313 aSize.width, aSize.height);
314 if (cairo_surface_status(surface)) {
315 cairo_surface_destroy(surface);
316 return nsnull;
319 nsRefPtr<gfxASurface> result = Wrap(surface);
320 cairo_surface_destroy(surface);
321 return result.forget();
325 gfxASurface::CairoStatus()
327 if (!mSurfaceValid)
328 return -1;
330 return cairo_surface_status(mSurface);
333 /* static */
334 PRBool
335 gfxASurface::CheckSurfaceSize(const gfxIntSize& sz, PRInt32 limit)
337 if (sz.width < 0 || sz.height < 0) {
338 NS_WARNING("Surface width or height < 0!");
339 return PR_FALSE;
342 #if defined(XP_MACOSX)
343 // CoreGraphics is limited to images < 32K in *height*, so clamp all surfaces on the Mac to that height
344 if (sz.height > SHRT_MAX) {
345 NS_WARNING("Surface size too large (would overflow)!");
346 return PR_FALSE;
348 #endif
350 // check to make sure we don't overflow a 32-bit
351 PRInt32 tmp = sz.width * sz.height;
352 if (tmp && tmp / sz.height != sz.width) {
353 NS_WARNING("Surface size too large (would overflow)!");
354 return PR_FALSE;
357 // always assume 4-byte stride
358 tmp = tmp * 4;
359 if (tmp && tmp / 4 != sz.width * sz.height) {
360 NS_WARNING("Surface size too large (would overflow)!");
361 return PR_FALSE;
364 // reject images with sides bigger than limit
365 if (limit &&
366 (sz.width > limit || sz.height > limit))
367 return PR_FALSE;
369 return PR_TRUE;
372 nsresult
373 gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
375 return NS_OK;
378 nsresult
379 gfxASurface::EndPrinting()
381 return NS_OK;
384 nsresult
385 gfxASurface::AbortPrinting()
387 return NS_OK;
390 nsresult
391 gfxASurface::BeginPage()
393 return NS_OK;
396 nsresult
397 gfxASurface::EndPage()
399 return NS_OK;
402 gfxASurface::gfxContentType
403 gfxASurface::ContentFromFormat(gfxImageFormat format)
405 switch (format) {
406 case ImageFormatARGB32:
407 return CONTENT_COLOR_ALPHA;
408 case ImageFormatRGB24:
409 case ImageFormatRGB16_565:
410 return CONTENT_COLOR;
411 case ImageFormatA8:
412 case ImageFormatA1:
413 return CONTENT_ALPHA;
415 case ImageFormatUnknown:
416 default:
417 return CONTENT_COLOR;
421 gfxASurface::gfxImageFormat
422 gfxASurface::FormatFromContent(gfxASurface::gfxContentType type)
424 switch (type) {
425 case CONTENT_COLOR_ALPHA:
426 return ImageFormatARGB32;
427 case CONTENT_ALPHA:
428 return ImageFormatA8;
429 case CONTENT_COLOR:
430 default:
431 return ImageFormatRGB24;
435 void
436 gfxASurface::SetSubpixelAntialiasingEnabled(PRBool aEnabled)
438 if (!mSurfaceValid)
439 return;
440 cairo_surface_set_subpixel_antialiasing(mSurface,
441 aEnabled ? CAIRO_SUBPIXEL_ANTIALIASING_ENABLED : CAIRO_SUBPIXEL_ANTIALIASING_DISABLED);
444 PRBool
445 gfxASurface::GetSubpixelAntialiasingEnabled()
447 if (!mSurfaceValid)
448 return PR_FALSE;
449 return cairo_surface_get_subpixel_antialiasing(mSurface) == CAIRO_SUBPIXEL_ANTIALIASING_ENABLED;
452 PRInt32
453 gfxASurface::BytePerPixelFromFormat(gfxImageFormat format)
455 switch (format) {
456 case ImageFormatARGB32:
457 case ImageFormatRGB24:
458 return 4;
459 case ImageFormatRGB16_565:
460 return 2;
461 case ImageFormatA8:
462 return 1;
463 default:
464 NS_WARNING("Unknown byte per pixel value for Image format");
466 return 0;
469 /** Memory reporting **/
471 static const char *sSurfaceNamesForSurfaceType[] = {
472 "gfx/surface/image",
473 "gfx/surface/pdf",
474 "gfx/surface/ps",
475 "gfx/surface/xlib",
476 "gfx/surface/xcb",
477 "gfx/surface/glitz",
478 "gfx/surface/quartz",
479 "gfx/surface/win32",
480 "gfx/surface/beos",
481 "gfx/surface/directfb",
482 "gfx/surface/svg",
483 "gfx/surface/os2",
484 "gfx/surface/win32printing",
485 "gfx/surface/quartzimage",
486 "gfx/surface/script",
487 "gfx/surface/qpainter",
488 "gfx/surface/recording",
489 "gfx/surface/vg",
490 "gfx/surface/gl",
491 "gfx/surface/drm",
492 "gfx/surface/tee",
493 "gfx/surface/xml",
494 "gfx/surface/skia",
495 "gfx/surface/d2d"
498 PR_STATIC_ASSERT(NS_ARRAY_LENGTH(sSurfaceNamesForSurfaceType) == gfxASurface::SurfaceTypeMax);
499 #ifdef CAIRO_HAS_D2D_SURFACE
500 PR_STATIC_ASSERT(PRUint32(CAIRO_SURFACE_TYPE_D2D) == PRUint32(gfxASurface::SurfaceTypeD2D));
501 #endif
502 PR_STATIC_ASSERT(PRUint32(CAIRO_SURFACE_TYPE_SKIA) == PRUint32(gfxASurface::SurfaceTypeSkia));
504 static const char *
505 SurfaceMemoryReporterPathForType(gfxASurface::gfxSurfaceType aType)
507 if (aType < 0 ||
508 aType >= gfxASurface::SurfaceTypeMax)
509 return "gfx/surface/unknown";
511 return sSurfaceNamesForSurfaceType[aType];
514 /* Surface size memory reporting */
515 static nsIMemoryReporter *gSurfaceMemoryReporters[gfxASurface::SurfaceTypeMax] = { 0 };
516 static PRInt64 gSurfaceMemoryUsed[gfxASurface::SurfaceTypeMax] = { 0 };
518 class SurfaceMemoryReporter :
519 public nsIMemoryReporter
521 public:
522 SurfaceMemoryReporter(gfxASurface::gfxSurfaceType aType)
523 : mType(aType)
526 NS_DECL_ISUPPORTS
528 NS_IMETHOD GetPath(char **memoryPath) {
529 *memoryPath = strdup(SurfaceMemoryReporterPathForType(mType));
530 return NS_OK;
533 NS_IMETHOD GetDescription(char **desc) {
534 *desc = strdup("Memory used by gfx surface of given type.");
535 return NS_OK;
538 NS_IMETHOD GetMemoryUsed(PRInt64 *memoryUsed) {
539 *memoryUsed = gSurfaceMemoryUsed[mType];
540 return NS_OK;
543 gfxASurface::gfxSurfaceType mType;
546 NS_IMPL_ISUPPORTS1(SurfaceMemoryReporter, nsIMemoryReporter)
548 void
549 gfxASurface::RecordMemoryUsedForSurfaceType(gfxASurface::gfxSurfaceType aType,
550 PRInt32 aBytes)
552 if (aType < 0 || aType >= SurfaceTypeMax) {
553 NS_WARNING("Invalid type to RecordMemoryUsedForSurfaceType!");
554 return;
557 if (gSurfaceMemoryReporters[aType] == 0) {
558 gSurfaceMemoryReporters[aType] = new SurfaceMemoryReporter(aType);
559 NS_RegisterMemoryReporter(gSurfaceMemoryReporters[aType]);
562 gSurfaceMemoryUsed[aType] += aBytes;
565 void
566 gfxASurface::RecordMemoryUsed(PRInt32 aBytes)
568 RecordMemoryUsedForSurfaceType(GetType(), aBytes);
569 mBytesRecorded += aBytes;
572 void
573 gfxASurface::RecordMemoryFreed()
575 if (mBytesRecorded) {
576 RecordMemoryUsedForSurfaceType(GetType(), -mBytesRecorded);
577 mBytesRecorded = 0;