Bumping manifests a=b2g-bump
[gecko.git] / gfx / thebes / gfxXlibSurface.cpp
blob393c212307dca6c7d2932613213e9af94683ffc5
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"
8 #include "cairo.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"
16 #include "nsTArray.h"
17 #include "nsAlgorithm.h"
18 #include "mozilla/Preferences.h"
19 #include <algorithm>
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)
32 , mGLXPixmap(None)
33 #endif
35 const gfxIntSize size = DoSizeQuery();
36 cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height);
37 Init(surf);
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)
43 , mGLXPixmap(None)
44 #endif
46 NS_ASSERTION(CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
47 "Bad size");
49 cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height);
50 Init(surf);
53 gfxXlibSurface::gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFormat *format,
54 const gfxIntSize& size)
55 : mPixmapTaken(false), mDisplay(DisplayOfScreen(screen)),
56 mDrawable(drawable)
57 #if defined(GL_PROVIDER_GLX)
58 , mGLXPixmap(None)
59 #endif
61 NS_ASSERTION(CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
62 "Bad Size");
64 cairo_surface_t *surf =
65 cairo_xlib_surface_create_with_xrender_format(mDisplay, drawable,
66 screen, format,
67 size.width, size.height);
68 Init(surf);
71 gfxXlibSurface::gfxXlibSurface(cairo_surface_t *csurf)
72 : mPixmapTaken(false)
73 #if defined(GL_PROVIDER_GLX)
74 , mGLXPixmap(None)
75 #endif
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);
83 Init(csurf, true);
86 gfxXlibSurface::~gfxXlibSurface()
88 #if defined(GL_PROVIDER_GLX)
89 if (mGLXPixmap) {
90 gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap);
92 #endif
93 // gfxASurface's destructor calls RecordMemoryFreed().
94 if (mPixmapTaken) {
95 XFreePixmap (mDisplay, mDrawable);
99 static Drawable
100 CreatePixmap(Screen *screen, const gfxIntSize& size, unsigned int depth,
101 Drawable relatedDrawable)
103 if (!gfxASurface::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
104 return None;
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
111 // or height 0
112 return XCreatePixmap(dpy, relatedDrawable,
113 std::max(1, size.width), std::max(1, size.height),
114 depth);
117 void
118 gfxXlibSurface::TakePixmap()
120 NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!");
121 mPixmapTaken = true;
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
129 // pixel.
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());
142 Drawable
143 gfxXlibSurface::ReleasePixmap() {
144 NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!");
145 mPixmapTaken = false;
146 RecordMemoryFreed();
147 return mDrawable;
150 static cairo_user_data_key_t gDestroyPixmapKey;
152 struct DestroyPixmapClosure {
153 DestroyPixmapClosure(Drawable d, Screen *s) : mPixmap(d), mScreen(s) {}
154 Drawable mPixmap;
155 Screen *mScreen;
158 static void
159 DestroyPixmap(void *data)
161 DestroyPixmapClosure *closure = static_cast<DestroyPixmapClosure*>(data);
162 XFreePixmap(DisplayOfScreen(closure->mScreen), closure->mPixmap);
163 delete closure;
166 /* static */
167 cairo_surface_t *
168 gfxXlibSurface::CreateCairoSurface(Screen *screen, Visual *visual,
169 const gfxIntSize& size, Drawable relatedDrawable)
171 Drawable drawable =
172 CreatePixmap(screen, size, DepthOfVisual(screen, visual),
173 relatedDrawable);
174 if (!drawable)
175 return nullptr;
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);
183 return nullptr;
186 DestroyPixmapClosure *closure = new DestroyPixmapClosure(drawable, screen);
187 cairo_surface_set_user_data(surface, &gDestroyPixmapKey,
188 closure, DestroyPixmap);
189 return surface;
192 /* static */
193 already_AddRefed<gfxXlibSurface>
194 gfxXlibSurface::Create(Screen *screen, Visual *visual,
195 const gfxIntSize& size, Drawable relatedDrawable)
197 Drawable drawable =
198 CreatePixmap(screen, size, DepthOfVisual(screen, visual),
199 relatedDrawable);
200 if (!drawable)
201 return nullptr;
203 nsRefPtr<gfxXlibSurface> result =
204 new gfxXlibSurface(DisplayOfScreen(screen), drawable, visual, size);
205 result->TakePixmap();
207 if (result->CairoStatus() != 0)
208 return nullptr;
210 return result.forget();
213 /* static */
214 already_AddRefed<gfxXlibSurface>
215 gfxXlibSurface::Create(Screen *screen, XRenderPictFormat *format,
216 const gfxIntSize& size, Drawable relatedDrawable)
218 Drawable drawable =
219 CreatePixmap(screen, size, format->depth, relatedDrawable);
220 if (!drawable)
221 return nullptr;
223 nsRefPtr<gfxXlibSurface> result =
224 new gfxXlibSurface(screen, drawable, format, size);
225 result->TakePixmap();
227 if (result->CairoStatus() != 0)
228 return nullptr;
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) {
243 return nullptr;
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();
251 if (force24bpp
252 && cairo_xlib_surface_get_depth(CairoSurface()) != 24) {
253 XRenderPictFormat* format =
254 XRenderFindStandardFormat(mDisplay, PictStandardRGB24);
255 if (format) {
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);
275 void
276 gfxXlibSurface::Finish()
278 #if defined(GL_PROVIDER_GLX)
279 if (mGLXPixmap) {
280 gl::sGLXLibrary.DestroyPixmap(mDisplay, mGLXPixmap);
281 mGLXPixmap = None;
283 #endif
284 gfxASurface::Finish();
287 const gfxIntSize
288 gfxXlibSurface::GetSize() const
290 if (!mSurfaceValid)
291 return gfxIntSize(0,0);
293 return gfxIntSize(cairo_xlib_surface_get_width(mSurface),
294 cairo_xlib_surface_get_height(mSurface));
297 const gfxIntSize
298 gfxXlibSurface::DoSizeQuery()
300 // figure out width/height/depth
301 Window root_ignore;
302 int x_ignore, y_ignore;
303 unsigned int bwidth_ignore, width, height, depth;
305 XGetGeometry(mDisplay,
306 mDrawable,
307 &root_ignore, &x_ignore, &y_ignore,
308 &width, &height,
309 &bwidth_ignore, &depth);
311 return gfxIntSize(width, height);
314 class DisplayTable {
315 public:
316 static bool GetColormapAndVisual(Screen* screen,
317 XRenderPictFormat* format,
318 Visual* visual, Colormap* colormap,
319 Visual** visualForColormap);
321 private:
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.
327 Screen* mScreen;
328 Visual* mVisual;
329 Colormap mColormap;
332 class DisplayInfo {
333 public:
334 DisplayInfo(Display* display) : mDisplay(display) { }
335 Display* mDisplay;
336 nsTArray<ColormapEntry> mColormapEntries;
339 // Comparator for finding the DisplayInfo
340 class FindDisplay {
341 public:
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.
379 /* static */ bool
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
390 || (aFormat
391 && aFormat == XRenderFindVisualFormat(display, defaultVisual)))
393 *aColormap = DefaultColormapOfScreen(aScreen);
394 *aVisualForColormap = defaultVisual;
395 return true;
398 // Only supporting TrueColor non-default visuals
399 if (!aVisual || aVisual->c_class != TrueColor)
400 return false;
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
412 // becomes invalid.
413 XExtCodes *codes = XAddExtension(display);
414 if (!codes)
415 return false;
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
431 // the visual.
432 if ((aFormat && entry.mFormat == aFormat && entry.mScreen == aScreen)
433 || aVisual == entry.mVisual) {
434 *aColormap = entry.mColormap;
435 *aVisualForColormap = entry.mVisual;
436 return true;
440 // No existing entry. Create a colormap and add an entry.
441 Colormap colormap = XCreateColormap(display, RootWindowOfScreen(aScreen),
442 aVisual, AllocNone);
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;
451 return true;
454 /* static */ int
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;
464 return 0;
467 /* static */
468 bool
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,
478 aColormap, aVisual);
481 bool
482 gfxXlibSurface::GetColormapAndVisual(Colormap* aColormap, Visual** aVisual)
484 if (!mSurfaceValid)
485 return false;
487 return GetColormapAndVisual(CairoSurface(), aColormap, aVisual);
490 /* static */
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])
498 return d_info.depth;
501 NS_ERROR("Visual not on Screen.");
502 return 0;
505 /* static */
506 Visual*
507 gfxXlibSurface::FindVisual(Screen *screen, gfxImageFormat format)
509 int depth;
510 unsigned long red_mask, green_mask, blue_mask;
511 switch (format) {
512 case gfxImageFormat::ARGB32:
513 depth = 32;
514 red_mask = 0xff0000;
515 green_mask = 0xff00;
516 blue_mask = 0xff;
517 break;
518 case gfxImageFormat::RGB24:
519 depth = 24;
520 red_mask = 0xff0000;
521 green_mask = 0xff00;
522 blue_mask = 0xff;
523 break;
524 case gfxImageFormat::RGB16_565:
525 depth = 16;
526 red_mask = 0xf800;
527 green_mask = 0x7e0;
528 blue_mask = 0x1f;
529 break;
530 case gfxImageFormat::A8:
531 case gfxImageFormat::A1:
532 default:
533 return nullptr;
536 for (int d = 0; d < screen->ndepths; d++) {
537 const Depth& d_info = screen->depths[d];
538 if (d_info.depth != depth)
539 continue;
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)
548 return visual;
552 return nullptr;
555 /* static */
556 XRenderPictFormat*
557 gfxXlibSurface::FindRenderFormat(Display *dpy, gfxImageFormat format)
559 switch (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);
569 if (!visual)
570 return nullptr;
571 return XRenderFindVisualFormat(dpy, visual);
573 case gfxImageFormat::A8:
574 return XRenderFindStandardFormat (dpy, PictStandardA8);
575 case gfxImageFormat::A1:
576 return XRenderFindStandardFormat (dpy, PictStandardA1);
577 default:
578 break;
581 return nullptr;
584 Screen*
585 gfxXlibSurface::XScreen()
587 return cairo_xlib_surface_get_screen(CairoSurface());
590 XRenderPictFormat*
591 gfxXlibSurface::XRenderFormat()
593 return cairo_xlib_surface_get_xrender_format(CairoSurface());
596 #if defined(GL_PROVIDER_GLX)
597 GLXPixmap
598 gfxXlibSurface::GetGLXPixmap()
600 if (!mGLXPixmap) {
601 #ifdef DEBUG
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");
608 #endif
609 mGLXPixmap = gl::sGLXLibrary.CreatePixmap(this);
611 return mGLXPixmap;
613 #endif
615 gfxMemoryLocation
616 gfxXlibSurface::GetMemoryLocation() const
618 return gfxMemoryLocation::OUT_OF_PROCESS;