gfxXlibSurface.cpp (14001B)
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- 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 "gfxXlibSurface.h" 7 8 #include "cairo.h" 9 #include "cairo-xlib.h" 10 #include <X11/Xlibint.h> /* For XESetCloseDisplay */ 11 #undef max // Xlibint.h defines this and it breaks std::max 12 #undef min // Xlibint.h defines this and it breaks std::min 13 #undef Data 14 15 #include "nsTArray.h" 16 #include "mozilla/gfx/2D.h" 17 #include "mozilla/Preferences.h" 18 #include <algorithm> 19 #include "mozilla/CheckedInt.h" 20 21 using namespace mozilla; 22 using namespace mozilla::gfx; 23 24 gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual) 25 : mPixmapTaken(false), 26 mDisplay(XlibDisplay::Borrow(dpy)), 27 mDrawable(drawable) { 28 const gfx::IntSize size = DoSizeQuery(); 29 cairo_surface_t* surf = 30 cairo_xlib_surface_create(dpy, drawable, visual, size.width, size.height); 31 Init(surf); 32 } 33 34 gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual, 35 const gfx::IntSize& size) 36 : gfxXlibSurface(XlibDisplay::Borrow(dpy), drawable, visual, size) {} 37 38 gfxXlibSurface::gfxXlibSurface(const std::shared_ptr<XlibDisplay>& dpy, 39 Drawable drawable, Visual* visual, 40 const gfx::IntSize& size) 41 : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable) { 42 NS_ASSERTION(Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT), 43 "Bad size"); 44 45 cairo_surface_t* surf = cairo_xlib_surface_create(*dpy, drawable, visual, 46 size.width, size.height); 47 Init(surf); 48 } 49 50 gfxXlibSurface::gfxXlibSurface(cairo_surface_t* csurf) : mPixmapTaken(false) { 51 MOZ_ASSERT(cairo_surface_status(csurf) == 0, 52 "Not expecting an error surface"); 53 54 mDrawable = cairo_xlib_surface_get_drawable(csurf); 55 mDisplay = XlibDisplay::Borrow(cairo_xlib_surface_get_display(csurf)); 56 57 Init(csurf, true); 58 } 59 60 gfxXlibSurface::~gfxXlibSurface() { 61 // gfxASurface's destructor calls RecordMemoryFreed(). 62 if (mPixmapTaken) { 63 XFreePixmap(*mDisplay, mDrawable); 64 } 65 } 66 67 static Drawable CreatePixmap(Screen* screen, const gfx::IntSize& size, 68 unsigned int depth, Drawable relatedDrawable) { 69 if (!Factory::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT)) 70 return X11None; 71 72 if (relatedDrawable == X11None) { 73 relatedDrawable = RootWindowOfScreen(screen); 74 } 75 Display* dpy = DisplayOfScreen(screen); 76 // X gives us a fatal error if we try to create a pixmap of width 77 // or height 0 78 return XCreatePixmap(dpy, relatedDrawable, std::max(1, size.width), 79 std::max(1, size.height), depth); 80 } 81 82 void gfxXlibSurface::TakePixmap() { 83 NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!"); 84 mPixmapTaken = true; 85 86 // The bit depth returned from Cairo is technically int, but this is 87 // the last place we'd be worried about that scenario. 88 unsigned int bitDepth = cairo_xlib_surface_get_depth(CairoSurface()); 89 MOZ_ASSERT((bitDepth % 8) == 0, "Memory used not recorded correctly"); 90 91 // Divide by 8 because surface_get_depth gives us the number of *bits* per 92 // pixel. 93 gfx::IntSize size = GetSize(); 94 CheckedInt32 totalBytes = 95 CheckedInt32(size.width) * CheckedInt32(size.height) * (bitDepth / 8); 96 97 // Don't do anything in the "else" case. We could add INT32_MAX, but that 98 // would overflow the memory used counter. It would also mean we tried for 99 // a 2G image. For now, we'll just assert, 100 MOZ_ASSERT(totalBytes.isValid(), "Did not expect to exceed 2Gb image"); 101 if (totalBytes.isValid()) { 102 RecordMemoryUsed(totalBytes.value()); 103 } 104 } 105 106 Drawable gfxXlibSurface::ReleasePixmap() { 107 NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!"); 108 mPixmapTaken = false; 109 RecordMemoryFreed(); 110 return mDrawable; 111 } 112 113 static cairo_user_data_key_t gDestroyPixmapKey; 114 115 struct DestroyPixmapClosure { 116 DestroyPixmapClosure(Drawable d, Screen* s) : mPixmap(d), mScreen(s) {} 117 Drawable mPixmap; 118 Screen* mScreen; 119 }; 120 121 static void DestroyPixmap(void* data) { 122 DestroyPixmapClosure* closure = static_cast<DestroyPixmapClosure*>(data); 123 XFreePixmap(DisplayOfScreen(closure->mScreen), closure->mPixmap); 124 delete closure; 125 } 126 127 /* static */ 128 cairo_surface_t* gfxXlibSurface::CreateCairoSurface(Screen* screen, 129 Visual* visual, 130 const gfx::IntSize& size, 131 Drawable relatedDrawable) { 132 Drawable drawable = CreatePixmap(screen, size, DepthOfVisual(screen, visual), 133 relatedDrawable); 134 if (!drawable) return nullptr; 135 136 cairo_surface_t* surface = cairo_xlib_surface_create( 137 DisplayOfScreen(screen), drawable, visual, size.width, size.height); 138 if (cairo_surface_status(surface)) { 139 cairo_surface_destroy(surface); 140 XFreePixmap(DisplayOfScreen(screen), drawable); 141 return nullptr; 142 } 143 144 DestroyPixmapClosure* closure = new DestroyPixmapClosure(drawable, screen); 145 cairo_surface_set_user_data(surface, &gDestroyPixmapKey, closure, 146 DestroyPixmap); 147 return surface; 148 } 149 150 /* static */ 151 already_AddRefed<gfxXlibSurface> gfxXlibSurface::Create( 152 Screen* screen, Visual* visual, const gfx::IntSize& size, 153 Drawable relatedDrawable) { 154 return Create(XlibDisplay::Borrow(DisplayOfScreen(screen)), screen, visual, 155 size, relatedDrawable); 156 }; 157 158 /* static */ 159 already_AddRefed<gfxXlibSurface> gfxXlibSurface::Create( 160 const std::shared_ptr<XlibDisplay>& display, Screen* screen, Visual* visual, 161 const gfx::IntSize& size, Drawable relatedDrawable) { 162 MOZ_ASSERT(*display == DisplayOfScreen(screen)); 163 164 Drawable drawable = CreatePixmap(screen, size, DepthOfVisual(screen, visual), 165 relatedDrawable); 166 if (!drawable) return nullptr; 167 168 RefPtr<gfxXlibSurface> result = 169 new gfxXlibSurface(display, drawable, visual, size); 170 result->TakePixmap(); 171 172 if (result->CairoStatus() != 0) return nullptr; 173 174 return result.forget(); 175 } 176 177 void gfxXlibSurface::Finish() { gfxASurface::Finish(); } 178 179 const gfx::IntSize gfxXlibSurface::GetSize() const { 180 if (!mSurfaceValid) return gfx::IntSize(0, 0); 181 182 return gfx::IntSize(cairo_xlib_surface_get_width(mSurface), 183 cairo_xlib_surface_get_height(mSurface)); 184 } 185 186 const gfx::IntSize gfxXlibSurface::DoSizeQuery() { 187 // figure out width/height/depth 188 Window root_ignore; 189 int x_ignore, y_ignore; 190 unsigned int bwidth_ignore, width, height, depth; 191 192 XGetGeometry(*mDisplay, mDrawable, &root_ignore, &x_ignore, &y_ignore, &width, 193 &height, &bwidth_ignore, &depth); 194 195 return gfx::IntSize(width, height); 196 } 197 198 class DisplayTable { 199 public: 200 static bool GetColormapAndVisual(Screen* screen, Visual* visual, 201 Colormap* colormap, 202 Visual** visualForColormap); 203 204 private: 205 struct ColormapEntry { 206 // The Screen is needed here because colormaps (and their visuals) may 207 // only be used on one Screen 208 Screen* mScreen; 209 Visual* mVisual; 210 Colormap mColormap; 211 }; 212 213 class DisplayInfo { 214 public: 215 explicit DisplayInfo(Display* display) : mDisplay(display) {} 216 Display* mDisplay; 217 nsTArray<ColormapEntry> mColormapEntries; 218 }; 219 220 // Comparator for finding the DisplayInfo 221 class FindDisplay { 222 public: 223 bool Equals(const DisplayInfo& info, const Display* display) const { 224 return info.mDisplay == display; 225 } 226 }; 227 228 static int DisplayClosing(Display* display, XExtCodes* codes); 229 230 nsTArray<DisplayInfo> mDisplays; 231 static DisplayTable* sDisplayTable; 232 }; 233 234 DisplayTable* DisplayTable::sDisplayTable; 235 236 // Pixmaps don't have a particular associated visual but the pixel values are 237 // interpreted according to a visual/colormap pairs. 238 // 239 // cairo is designed for surfaces with either TrueColor visuals or the 240 // default visual (which may not be true color). TrueColor visuals don't 241 // really need a colormap because the visual indicates the pixel format, 242 // and cairo uses the default visual with the default colormap, so cairo 243 // surfaces don't need an explicit colormap. 244 // 245 // However, some toolkits (e.g. GDK) need a colormap even with TrueColor 246 // visuals. We can create a colormap for these visuals, but it will use about 247 // 20kB of memory in the server, so we use the default colormap when 248 // suitable and share colormaps between surfaces. Another reason for 249 // minimizing colormap turnover is that the plugin process must leak resources 250 // for each new colormap id when using older GDK libraries (bug 569775). 251 // 252 // Only the format of the pixels is important for rendering to Pixmaps, so if 253 // the format of a visual matches that of the surface, then that visual can be 254 // used for rendering to the surface. Multiple visuals can match the same 255 // format (but have different GLX properties), so the visual returned may 256 // differ from the visual passed in. Colormaps are tied to a visual, so 257 // should only be used with their visual. 258 259 /* static */ 260 bool DisplayTable::GetColormapAndVisual(Screen* aScreen, Visual* aVisual, 261 Colormap* aColormap, 262 Visual** aVisualForColormap) 263 264 { 265 Display* display = DisplayOfScreen(aScreen); 266 267 // Use the default colormap if the default visual matches. 268 Visual* defaultVisual = DefaultVisualOfScreen(aScreen); 269 if (aVisual == defaultVisual) { 270 *aColormap = DefaultColormapOfScreen(aScreen); 271 *aVisualForColormap = defaultVisual; 272 return true; 273 } 274 275 // Only supporting TrueColor non-default visuals 276 if (!aVisual || aVisual->c_class != TrueColor) return false; 277 278 if (!sDisplayTable) { 279 sDisplayTable = new DisplayTable(); 280 } 281 282 nsTArray<DisplayInfo>* displays = &sDisplayTable->mDisplays; 283 size_t d = displays->IndexOf(display, 0, FindDisplay()); 284 285 if (d == displays->NoIndex) { 286 d = displays->Length(); 287 // Register for notification of display closing, when this info 288 // becomes invalid. 289 XExtCodes* codes = XAddExtension(display); 290 if (!codes) return false; 291 292 XESetCloseDisplay(display, codes->extension, DisplayClosing); 293 // Add a new DisplayInfo. 294 displays->AppendElement(display); 295 } 296 297 nsTArray<ColormapEntry>* entries = &displays->ElementAt(d).mColormapEntries; 298 299 // Only a small number of formats are expected to be used, so just do a 300 // simple linear search. 301 for (uint32_t i = 0; i < entries->Length(); ++i) { 302 const ColormapEntry& entry = entries->ElementAt(i); 303 if (aVisual == entry.mVisual) { 304 *aColormap = entry.mColormap; 305 *aVisualForColormap = entry.mVisual; 306 return true; 307 } 308 } 309 310 // No existing entry. Create a colormap and add an entry. 311 Colormap colormap = 312 XCreateColormap(display, RootWindowOfScreen(aScreen), aVisual, AllocNone); 313 ColormapEntry* newEntry = entries->AppendElement(); 314 newEntry->mScreen = aScreen; 315 newEntry->mVisual = aVisual; 316 newEntry->mColormap = colormap; 317 318 *aColormap = colormap; 319 *aVisualForColormap = aVisual; 320 return true; 321 } 322 323 /* static */ 324 int DisplayTable::DisplayClosing(Display* display, XExtCodes* codes) { 325 // No need to free the colormaps explicitly as they will be released when 326 // the connection is closed. 327 sDisplayTable->mDisplays.RemoveElement(display, FindDisplay()); 328 if (sDisplayTable->mDisplays.Length() == 0) { 329 delete sDisplayTable; 330 sDisplayTable = nullptr; 331 } 332 return 0; 333 } 334 335 /* static */ 336 bool gfxXlibSurface::GetColormapAndVisual(cairo_surface_t* aXlibSurface, 337 Colormap* aColormap, 338 Visual** aVisual) { 339 Screen* screen = cairo_xlib_surface_get_screen(aXlibSurface); 340 Visual* visual = cairo_xlib_surface_get_visual(aXlibSurface); 341 342 return DisplayTable::GetColormapAndVisual(screen, visual, aColormap, aVisual); 343 } 344 345 bool gfxXlibSurface::GetColormapAndVisual(Colormap* aColormap, 346 Visual** aVisual) { 347 if (!mSurfaceValid) return false; 348 349 return GetColormapAndVisual(CairoSurface(), aColormap, aVisual); 350 } 351 352 /* static */ 353 int gfxXlibSurface::DepthOfVisual(const Screen* screen, const Visual* visual) { 354 for (int d = 0; d < screen->ndepths; d++) { 355 const Depth& d_info = screen->depths[d]; 356 if (visual >= &d_info.visuals[0] && 357 visual < &d_info.visuals[d_info.nvisuals]) 358 return d_info.depth; 359 } 360 361 NS_ERROR("Visual not on Screen."); 362 return 0; 363 } 364 365 /* static */ 366 Visual* gfxXlibSurface::FindVisual(Screen* screen, gfxImageFormat format) { 367 int depth; 368 unsigned long red_mask, green_mask, blue_mask; 369 switch (format) { 370 case gfx::SurfaceFormat::A8R8G8B8_UINT32: 371 depth = 32; 372 red_mask = 0xff0000; 373 green_mask = 0xff00; 374 blue_mask = 0xff; 375 break; 376 case gfx::SurfaceFormat::X8R8G8B8_UINT32: 377 depth = 24; 378 red_mask = 0xff0000; 379 green_mask = 0xff00; 380 blue_mask = 0xff; 381 break; 382 case gfx::SurfaceFormat::R5G6B5_UINT16: 383 depth = 16; 384 red_mask = 0xf800; 385 green_mask = 0x7e0; 386 blue_mask = 0x1f; 387 break; 388 case gfx::SurfaceFormat::A8: 389 default: 390 return nullptr; 391 } 392 393 for (int d = 0; d < screen->ndepths; d++) { 394 const Depth& d_info = screen->depths[d]; 395 if (d_info.depth != depth) continue; 396 397 for (int v = 0; v < d_info.nvisuals; v++) { 398 Visual* visual = &d_info.visuals[v]; 399 400 if (visual->c_class == TrueColor && visual->red_mask == red_mask && 401 visual->green_mask == green_mask && visual->blue_mask == blue_mask) 402 return visual; 403 } 404 } 405 406 return nullptr; 407 } 408 409 Screen* gfxXlibSurface::XScreen() { 410 return cairo_xlib_surface_get_screen(CairoSurface()); 411 }