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/. */
6 #include "gfxXlibSurface.h"
9 #include "cairo-xlib.h"
10 #include "cairo-xlib-xrender.h"
11 #include <X11/Xlibint.h> /* For XESetCloseDisplay */
12 #undef max // Xlibint.h defines this and it breaks std::max
13 #undef min // Xlibint.h defines this and it breaks std::min
15 #include "nsAutoPtr.h"
17 #include "nsAlgorithm.h"
18 #include "mozilla/Preferences.h"
20 #include "mozilla/CheckedInt.h"
22 using namespace mozilla
;
24 // Although the dimension parameters in the xCreatePixmapReq wire protocol are
25 // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
26 // either dimension cannot be represented by a 16-bit *signed* integer.
27 #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
29 gfxXlibSurface::gfxXlibSurface(Display
*dpy
, Drawable drawable
, Visual
*visual
)
30 : mPixmapTaken(false), mDisplay(dpy
), mDrawable(drawable
)
31 #if defined(GL_PROVIDER_GLX)
35 const gfxIntSize size
= DoSizeQuery();
36 cairo_surface_t
*surf
= cairo_xlib_surface_create(dpy
, drawable
, visual
, size
.width
, size
.height
);
40 gfxXlibSurface::gfxXlibSurface(Display
*dpy
, Drawable drawable
, Visual
*visual
, const gfxIntSize
& size
)
41 : mPixmapTaken(false), mDisplay(dpy
), mDrawable(drawable
)
42 #if defined(GL_PROVIDER_GLX)
46 NS_ASSERTION(CheckSurfaceSize(size
, XLIB_IMAGE_SIDE_SIZE_LIMIT
),
49 cairo_surface_t
*surf
= cairo_xlib_surface_create(dpy
, drawable
, visual
, size
.width
, size
.height
);
53 gfxXlibSurface::gfxXlibSurface(Screen
*screen
, Drawable drawable
, XRenderPictFormat
*format
,
54 const gfxIntSize
& size
)
55 : mPixmapTaken(false), mDisplay(DisplayOfScreen(screen
)),
57 #if defined(GL_PROVIDER_GLX)
61 NS_ASSERTION(CheckSurfaceSize(size
, XLIB_IMAGE_SIDE_SIZE_LIMIT
),
64 cairo_surface_t
*surf
=
65 cairo_xlib_surface_create_with_xrender_format(mDisplay
, drawable
,
67 size
.width
, size
.height
);
71 gfxXlibSurface::gfxXlibSurface(cairo_surface_t
*csurf
)
73 #if defined(GL_PROVIDER_GLX)
77 NS_PRECONDITION(cairo_surface_status(csurf
) == 0,
78 "Not expecting an error surface");
80 mDrawable
= cairo_xlib_surface_get_drawable(csurf
);
81 mDisplay
= cairo_xlib_surface_get_display(csurf
);
86 gfxXlibSurface::~gfxXlibSurface()
88 #if defined(GL_PROVIDER_GLX)
90 gl::sGLXLibrary
.DestroyPixmap(mDisplay
, mGLXPixmap
);
93 // gfxASurface's destructor calls RecordMemoryFreed().
95 XFreePixmap (mDisplay
, mDrawable
);
100 CreatePixmap(Screen
*screen
, const gfxIntSize
& size
, unsigned int depth
,
101 Drawable relatedDrawable
)
103 if (!gfxASurface::CheckSurfaceSize(size
, XLIB_IMAGE_SIDE_SIZE_LIMIT
))
106 if (relatedDrawable
== None
) {
107 relatedDrawable
= RootWindowOfScreen(screen
);
109 Display
*dpy
= DisplayOfScreen(screen
);
110 // X gives us a fatal error if we try to create a pixmap of width
112 return XCreatePixmap(dpy
, relatedDrawable
,
113 std::max(1, size
.width
), std::max(1, size
.height
),
118 gfxXlibSurface::TakePixmap()
120 NS_ASSERTION(!mPixmapTaken
, "I already own the Pixmap!");
123 // The bit depth returned from Cairo is technically int, but this is
124 // the last place we'd be worried about that scenario.
125 unsigned int bitDepth
= cairo_xlib_surface_get_depth(CairoSurface());
126 MOZ_ASSERT((bitDepth
% 8) == 0, "Memory used not recorded correctly");
128 // Divide by 8 because surface_get_depth gives us the number of *bits* per
130 gfxIntSize size
= GetSize();
131 CheckedInt32 totalBytes
= CheckedInt32(size
.width
) * CheckedInt32(size
.height
) * (bitDepth
/8);
133 // Don't do anything in the "else" case. We could add INT32_MAX, but that
134 // would overflow the memory used counter. It would also mean we tried for
135 // a 2G image. For now, we'll just assert,
136 MOZ_ASSERT(totalBytes
.isValid(),"Did not expect to exceed 2Gb image");
137 if (totalBytes
.isValid()) {
138 RecordMemoryUsed(totalBytes
.value());
143 gfxXlibSurface::ReleasePixmap() {
144 NS_ASSERTION(mPixmapTaken
, "I don't own the Pixmap!");
145 mPixmapTaken
= false;
150 static cairo_user_data_key_t gDestroyPixmapKey
;
152 struct DestroyPixmapClosure
{
153 DestroyPixmapClosure(Drawable d
, Screen
*s
) : mPixmap(d
), mScreen(s
) {}
159 DestroyPixmap(void *data
)
161 DestroyPixmapClosure
*closure
= static_cast<DestroyPixmapClosure
*>(data
);
162 XFreePixmap(DisplayOfScreen(closure
->mScreen
), closure
->mPixmap
);
168 gfxXlibSurface::CreateCairoSurface(Screen
*screen
, Visual
*visual
,
169 const gfxIntSize
& size
, Drawable relatedDrawable
)
172 CreatePixmap(screen
, size
, DepthOfVisual(screen
, visual
),
177 cairo_surface_t
* surface
=
178 cairo_xlib_surface_create(DisplayOfScreen(screen
), drawable
, visual
,
179 size
.width
, size
.height
);
180 if (cairo_surface_status(surface
)) {
181 cairo_surface_destroy(surface
);
182 XFreePixmap(DisplayOfScreen(screen
), drawable
);
186 DestroyPixmapClosure
*closure
= new DestroyPixmapClosure(drawable
, screen
);
187 cairo_surface_set_user_data(surface
, &gDestroyPixmapKey
,
188 closure
, DestroyPixmap
);
193 already_AddRefed
<gfxXlibSurface
>
194 gfxXlibSurface::Create(Screen
*screen
, Visual
*visual
,
195 const gfxIntSize
& size
, Drawable relatedDrawable
)
198 CreatePixmap(screen
, size
, DepthOfVisual(screen
, visual
),
203 nsRefPtr
<gfxXlibSurface
> result
=
204 new gfxXlibSurface(DisplayOfScreen(screen
), drawable
, visual
, size
);
205 result
->TakePixmap();
207 if (result
->CairoStatus() != 0)
210 return result
.forget();
214 already_AddRefed
<gfxXlibSurface
>
215 gfxXlibSurface::Create(Screen
*screen
, XRenderPictFormat
*format
,
216 const gfxIntSize
& size
, Drawable relatedDrawable
)
219 CreatePixmap(screen
, size
, format
->depth
, relatedDrawable
);
223 nsRefPtr
<gfxXlibSurface
> result
=
224 new gfxXlibSurface(screen
, drawable
, format
, size
);
225 result
->TakePixmap();
227 if (result
->CairoStatus() != 0)
230 return result
.forget();
233 static bool GetForce24bppPref()
235 return Preferences::GetBool("mozilla.widget.force-24bpp", false);
238 already_AddRefed
<gfxASurface
>
239 gfxXlibSurface::CreateSimilarSurface(gfxContentType aContent
,
240 const gfxIntSize
& aSize
)
242 if (!mSurface
|| !mSurfaceValid
) {
246 if (aContent
== gfxContentType::COLOR
) {
247 // cairo_surface_create_similar will use a matching visual if it can.
248 // However, systems with 16-bit or indexed default visuals may benefit
249 // from rendering with 24-bit formats.
250 static bool force24bpp
= GetForce24bppPref();
252 && cairo_xlib_surface_get_depth(CairoSurface()) != 24) {
253 XRenderPictFormat
* format
=
254 XRenderFindStandardFormat(mDisplay
, PictStandardRGB24
);
256 // Cairo only performs simple self-copies as desired if it
257 // knows that this is a Pixmap surface. It only knows that
258 // surfaces are pixmap surfaces if it creates the Pixmap
259 // itself, so we use cairo_surface_create_similar with a
260 // temporary reference surface to indicate the format.
261 Screen
* screen
= cairo_xlib_surface_get_screen(CairoSurface());
262 nsRefPtr
<gfxXlibSurface
> depth24reference
=
263 gfxXlibSurface::Create(screen
, format
,
264 gfxIntSize(1, 1), mDrawable
);
265 if (depth24reference
)
266 return depth24reference
->
267 gfxASurface::CreateSimilarSurface(aContent
, aSize
);
272 return gfxASurface::CreateSimilarSurface(aContent
, aSize
);
276 gfxXlibSurface::Finish()
278 #if defined(GL_PROVIDER_GLX)
280 gl::sGLXLibrary
.DestroyPixmap(mDisplay
, mGLXPixmap
);
284 gfxASurface::Finish();
288 gfxXlibSurface::GetSize() const
291 return gfxIntSize(0,0);
293 return gfxIntSize(cairo_xlib_surface_get_width(mSurface
),
294 cairo_xlib_surface_get_height(mSurface
));
298 gfxXlibSurface::DoSizeQuery()
300 // figure out width/height/depth
302 int x_ignore
, y_ignore
;
303 unsigned int bwidth_ignore
, width
, height
, depth
;
305 XGetGeometry(mDisplay
,
307 &root_ignore
, &x_ignore
, &y_ignore
,
309 &bwidth_ignore
, &depth
);
311 return gfxIntSize(width
, height
);
316 static bool GetColormapAndVisual(Screen
* screen
,
317 XRenderPictFormat
* format
,
318 Visual
* visual
, Colormap
* colormap
,
319 Visual
** visualForColormap
);
322 struct ColormapEntry
{
323 XRenderPictFormat
* mFormat
;
324 // The Screen is needed here because colormaps (and their visuals) may
325 // only be used on one Screen, but XRenderPictFormats are not unique
326 // to any one Screen.
334 DisplayInfo(Display
* display
) : mDisplay(display
) { }
336 nsTArray
<ColormapEntry
> mColormapEntries
;
339 // Comparator for finding the DisplayInfo
342 bool Equals(const DisplayInfo
& info
, const Display
*display
) const
344 return info
.mDisplay
== display
;
348 static int DisplayClosing(Display
*display
, XExtCodes
* codes
);
350 nsTArray
<DisplayInfo
> mDisplays
;
351 static DisplayTable
* sDisplayTable
;
354 DisplayTable
* DisplayTable::sDisplayTable
;
356 // Pixmaps don't have a particular associated visual but the pixel values are
357 // interpreted according to a visual/colormap pairs.
359 // cairo is designed for surfaces with either TrueColor visuals or the
360 // default visual (which may not be true color). TrueColor visuals don't
361 // really need a colormap because the visual indicates the pixel format,
362 // and cairo uses the default visual with the default colormap, so cairo
363 // surfaces don't need an explicit colormap.
365 // However, some toolkits (e.g. GDK) need a colormap even with TrueColor
366 // visuals. We can create a colormap for these visuals, but it will use about
367 // 20kB of memory in the server, so we use the default colormap when
368 // suitable and share colormaps between surfaces. Another reason for
369 // minimizing colormap turnover is that the plugin process must leak resources
370 // for each new colormap id when using older GDK libraries (bug 569775).
372 // Only the format of the pixels is important for rendering to Pixmaps, so if
373 // the format of a visual matches that of the surface, then that visual can be
374 // used for rendering to the surface. Multiple visuals can match the same
375 // format (but have different GLX properties), so the visual returned may
376 // differ from the visual passed in. Colormaps are tied to a visual, so
377 // should only be used with their visual.
380 DisplayTable::GetColormapAndVisual(Screen
* aScreen
, XRenderPictFormat
* aFormat
,
381 Visual
* aVisual
, Colormap
* aColormap
,
382 Visual
** aVisualForColormap
)
385 Display
* display
= DisplayOfScreen(aScreen
);
387 // Use the default colormap if the default visual matches.
388 Visual
*defaultVisual
= DefaultVisualOfScreen(aScreen
);
389 if (aVisual
== defaultVisual
391 && aFormat
== XRenderFindVisualFormat(display
, defaultVisual
)))
393 *aColormap
= DefaultColormapOfScreen(aScreen
);
394 *aVisualForColormap
= defaultVisual
;
398 // Only supporting TrueColor non-default visuals
399 if (!aVisual
|| aVisual
->c_class
!= TrueColor
)
402 if (!sDisplayTable
) {
403 sDisplayTable
= new DisplayTable();
406 nsTArray
<DisplayInfo
>* displays
= &sDisplayTable
->mDisplays
;
407 size_t d
= displays
->IndexOf(display
, 0, FindDisplay());
409 if (d
== displays
->NoIndex
) {
410 d
= displays
->Length();
411 // Register for notification of display closing, when this info
413 XExtCodes
*codes
= XAddExtension(display
);
417 XESetCloseDisplay(display
, codes
->extension
, DisplayClosing
);
418 // Add a new DisplayInfo.
419 displays
->AppendElement(display
);
422 nsTArray
<ColormapEntry
>* entries
=
423 &displays
->ElementAt(d
).mColormapEntries
;
425 // Only a small number of formats are expected to be used, so just do a
426 // simple linear search.
427 for (uint32_t i
= 0; i
< entries
->Length(); ++i
) {
428 const ColormapEntry
& entry
= entries
->ElementAt(i
);
429 // Only the format and screen need to match. (The visual may differ.)
430 // If there is no format (e.g. no RENDER extension) then just compare
432 if ((aFormat
&& entry
.mFormat
== aFormat
&& entry
.mScreen
== aScreen
)
433 || aVisual
== entry
.mVisual
) {
434 *aColormap
= entry
.mColormap
;
435 *aVisualForColormap
= entry
.mVisual
;
440 // No existing entry. Create a colormap and add an entry.
441 Colormap colormap
= XCreateColormap(display
, RootWindowOfScreen(aScreen
),
443 ColormapEntry
* newEntry
= entries
->AppendElement();
444 newEntry
->mFormat
= aFormat
;
445 newEntry
->mScreen
= aScreen
;
446 newEntry
->mVisual
= aVisual
;
447 newEntry
->mColormap
= colormap
;
449 *aColormap
= colormap
;
450 *aVisualForColormap
= aVisual
;
455 DisplayTable::DisplayClosing(Display
*display
, XExtCodes
* codes
)
457 // No need to free the colormaps explicitly as they will be released when
458 // the connection is closed.
459 sDisplayTable
->mDisplays
.RemoveElement(display
, FindDisplay());
460 if (sDisplayTable
->mDisplays
.Length() == 0) {
461 delete sDisplayTable
;
462 sDisplayTable
= nullptr;
469 gfxXlibSurface::GetColormapAndVisual(cairo_surface_t
* aXlibSurface
,
470 Colormap
* aColormap
, Visual
** aVisual
)
472 XRenderPictFormat
* format
=
473 cairo_xlib_surface_get_xrender_format(aXlibSurface
);
474 Screen
* screen
= cairo_xlib_surface_get_screen(aXlibSurface
);
475 Visual
* visual
= cairo_xlib_surface_get_visual(aXlibSurface
);
477 return DisplayTable::GetColormapAndVisual(screen
, format
, visual
,
482 gfxXlibSurface::GetColormapAndVisual(Colormap
* aColormap
, Visual
** aVisual
)
487 return GetColormapAndVisual(CairoSurface(), aColormap
, aVisual
);
492 gfxXlibSurface::DepthOfVisual(const Screen
* screen
, const Visual
* visual
)
494 for (int d
= 0; d
< screen
->ndepths
; d
++) {
495 const Depth
& d_info
= screen
->depths
[d
];
496 if (visual
>= &d_info
.visuals
[0]
497 && visual
< &d_info
.visuals
[d_info
.nvisuals
])
501 NS_ERROR("Visual not on Screen.");
507 gfxXlibSurface::FindVisual(Screen
*screen
, gfxImageFormat format
)
510 unsigned long red_mask
, green_mask
, blue_mask
;
512 case gfxImageFormat::ARGB32
:
518 case gfxImageFormat::RGB24
:
524 case gfxImageFormat::RGB16_565
:
530 case gfxImageFormat::A8
:
531 case gfxImageFormat::A1
:
536 for (int d
= 0; d
< screen
->ndepths
; d
++) {
537 const Depth
& d_info
= screen
->depths
[d
];
538 if (d_info
.depth
!= depth
)
541 for (int v
= 0; v
< d_info
.nvisuals
; v
++) {
542 Visual
* visual
= &d_info
.visuals
[v
];
544 if (visual
->c_class
== TrueColor
&&
545 visual
->red_mask
== red_mask
&&
546 visual
->green_mask
== green_mask
&&
547 visual
->blue_mask
== blue_mask
)
557 gfxXlibSurface::FindRenderFormat(Display
*dpy
, gfxImageFormat format
)
560 case gfxImageFormat::ARGB32
:
561 return XRenderFindStandardFormat (dpy
, PictStandardARGB32
);
562 case gfxImageFormat::RGB24
:
563 return XRenderFindStandardFormat (dpy
, PictStandardRGB24
);
564 case gfxImageFormat::RGB16_565
: {
565 // PictStandardRGB16_565 is not standard Xrender format
566 // we should try to find related visual
567 // and find xrender format by visual
568 Visual
*visual
= FindVisual(DefaultScreenOfDisplay(dpy
), format
);
571 return XRenderFindVisualFormat(dpy
, visual
);
573 case gfxImageFormat::A8
:
574 return XRenderFindStandardFormat (dpy
, PictStandardA8
);
575 case gfxImageFormat::A1
:
576 return XRenderFindStandardFormat (dpy
, PictStandardA1
);
585 gfxXlibSurface::XScreen()
587 return cairo_xlib_surface_get_screen(CairoSurface());
591 gfxXlibSurface::XRenderFormat()
593 return cairo_xlib_surface_get_xrender_format(CairoSurface());
596 #if defined(GL_PROVIDER_GLX)
598 gfxXlibSurface::GetGLXPixmap()
602 // cairo_surface_has_show_text_glyphs is used solely for the
603 // side-effect of setting the error on surface if
604 // cairo_surface_finish() has been called.
605 cairo_surface_has_show_text_glyphs(CairoSurface());
606 NS_ASSERTION(CairoStatus() != CAIRO_STATUS_SURFACE_FINISHED
,
607 "GetGLXPixmap called after surface finished");
609 mGLXPixmap
= gl::sGLXLibrary
.CreatePixmap(this);
616 gfxXlibSurface::GetMemoryLocation() const
618 return gfxMemoryLocation::OUT_OF_PROCESS
;