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