tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }