1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is Oracle Corporation code.
16 : *
17 : * The Initial Developer of the Original Code is Oracle Corporation.
18 : * Portions created by the Initial Developer are Copyright (C) 2005
19 : * the Initial Developer. All Rights Reserved.
20 : *
21 : * Contributor(s):
22 : * Stuart Parmenter <pavlov@pavlov.net>
23 : * Vladimir Vukicevic <vladimir@pobox.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "gfxXlibSurface.h"
40 :
41 : #include "cairo.h"
42 : #include "cairo-xlib.h"
43 : #include "cairo-xlib-xrender.h"
44 : #include <X11/Xlibint.h> /* For XESetCloseDisplay */
45 :
46 : #include "nsTArray.h"
47 : #include "nsAlgorithm.h"
48 : #include "mozilla/Preferences.h"
49 :
50 : using namespace mozilla;
51 :
52 : // Although the dimension parameters in the xCreatePixmapReq wire protocol are
53 : // 16-bit unsigned integers, the server's CreatePixmap returns BadAlloc if
54 : // either dimension cannot be represented by a 16-bit *signed* integer.
55 : #define XLIB_IMAGE_SIDE_SIZE_LIMIT 0x7fff
56 :
57 0 : gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual)
58 : : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable)
59 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
60 0 : , mGLXPixmap(None)
61 : #endif
62 : {
63 0 : DoSizeQuery();
64 0 : cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, mSize.width, mSize.height);
65 0 : Init(surf);
66 0 : }
67 :
68 0 : gfxXlibSurface::gfxXlibSurface(Display *dpy, Drawable drawable, Visual *visual, const gfxIntSize& size)
69 : : mPixmapTaken(false), mDisplay(dpy), mDrawable(drawable), mSize(size)
70 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
71 0 : , mGLXPixmap(None)
72 : #endif
73 : {
74 0 : NS_ASSERTION(CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
75 : "Bad size");
76 :
77 0 : cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, mSize.width, mSize.height);
78 0 : Init(surf);
79 0 : }
80 :
81 0 : gfxXlibSurface::gfxXlibSurface(Screen *screen, Drawable drawable, XRenderPictFormat *format,
82 : const gfxIntSize& size)
83 : : mPixmapTaken(false), mDisplay(DisplayOfScreen(screen)),
84 : mDrawable(drawable), mSize(size)
85 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
86 0 : , mGLXPixmap(None)
87 : #endif
88 : {
89 0 : NS_ASSERTION(CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT),
90 : "Bad Size");
91 :
92 : cairo_surface_t *surf =
93 : cairo_xlib_surface_create_with_xrender_format(mDisplay, drawable,
94 : screen, format,
95 0 : mSize.width, mSize.height);
96 0 : Init(surf);
97 0 : }
98 :
99 0 : gfxXlibSurface::gfxXlibSurface(cairo_surface_t *csurf)
100 : : mPixmapTaken(false),
101 : mSize(cairo_xlib_surface_get_width(csurf),
102 : cairo_xlib_surface_get_height(csurf))
103 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
104 0 : , mGLXPixmap(None)
105 : #endif
106 : {
107 0 : NS_PRECONDITION(cairo_surface_status(csurf) == 0,
108 : "Not expecting an error surface");
109 :
110 0 : mDrawable = cairo_xlib_surface_get_drawable(csurf);
111 0 : mDisplay = cairo_xlib_surface_get_display(csurf);
112 :
113 0 : Init(csurf, true);
114 0 : }
115 :
116 0 : gfxXlibSurface::~gfxXlibSurface()
117 : {
118 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
119 0 : if (mGLXPixmap) {
120 0 : gl::sGLXLibrary.DestroyPixmap(mGLXPixmap);
121 : }
122 : #endif
123 : // gfxASurface's destructor calls RecordMemoryFreed().
124 0 : if (mPixmapTaken) {
125 0 : XFreePixmap (mDisplay, mDrawable);
126 : }
127 0 : }
128 :
129 : static Drawable
130 0 : CreatePixmap(Screen *screen, const gfxIntSize& size, unsigned int depth,
131 : Drawable relatedDrawable)
132 : {
133 0 : if (!gfxASurface::CheckSurfaceSize(size, XLIB_IMAGE_SIDE_SIZE_LIMIT))
134 0 : return None;
135 :
136 0 : if (relatedDrawable == None) {
137 0 : relatedDrawable = RootWindowOfScreen(screen);
138 : }
139 0 : Display *dpy = DisplayOfScreen(screen);
140 : // X gives us a fatal error if we try to create a pixmap of width
141 : // or height 0
142 : return XCreatePixmap(dpy, relatedDrawable,
143 0 : NS_MAX(1, size.width), NS_MAX(1, size.height),
144 0 : depth);
145 : }
146 :
147 : void
148 0 : gfxXlibSurface::TakePixmap()
149 : {
150 0 : NS_ASSERTION(!mPixmapTaken, "I already own the Pixmap!");
151 0 : mPixmapTaken = true;
152 :
153 : // Divide by 8 because surface_get_depth gives us the number of *bits* per
154 : // pixel.
155 : RecordMemoryUsed(mSize.width * mSize.height *
156 0 : cairo_xlib_surface_get_depth(CairoSurface()) / 8);
157 0 : }
158 :
159 : Drawable
160 0 : gfxXlibSurface::ReleasePixmap() {
161 0 : NS_ASSERTION(mPixmapTaken, "I don't own the Pixmap!");
162 0 : mPixmapTaken = false;
163 0 : RecordMemoryFreed();
164 0 : return mDrawable;
165 : }
166 :
167 : /* static */
168 : already_AddRefed<gfxXlibSurface>
169 0 : gfxXlibSurface::Create(Screen *screen, Visual *visual,
170 : const gfxIntSize& size, Drawable relatedDrawable)
171 : {
172 : Drawable drawable =
173 0 : CreatePixmap(screen, size, DepthOfVisual(screen, visual),
174 0 : relatedDrawable);
175 0 : if (!drawable)
176 0 : return nsnull;
177 :
178 : nsRefPtr<gfxXlibSurface> result =
179 0 : new gfxXlibSurface(DisplayOfScreen(screen), drawable, visual, size);
180 0 : result->TakePixmap();
181 :
182 0 : if (result->CairoStatus() != 0)
183 0 : return nsnull;
184 :
185 0 : return result.forget();
186 : }
187 :
188 : /* static */
189 : already_AddRefed<gfxXlibSurface>
190 0 : gfxXlibSurface::Create(Screen *screen, XRenderPictFormat *format,
191 : const gfxIntSize& size, Drawable relatedDrawable)
192 : {
193 : Drawable drawable =
194 0 : CreatePixmap(screen, size, format->depth, relatedDrawable);
195 0 : if (!drawable)
196 0 : return nsnull;
197 :
198 : nsRefPtr<gfxXlibSurface> result =
199 0 : new gfxXlibSurface(screen, drawable, format, size);
200 0 : result->TakePixmap();
201 :
202 0 : if (result->CairoStatus() != 0)
203 0 : return nsnull;
204 :
205 0 : return result.forget();
206 : }
207 :
208 0 : static bool GetForce24bppPref()
209 : {
210 0 : return Preferences::GetBool("mozilla.widget.force-24bpp", false);
211 : }
212 :
213 : already_AddRefed<gfxASurface>
214 0 : gfxXlibSurface::CreateSimilarSurface(gfxContentType aContent,
215 : const gfxIntSize& aSize)
216 : {
217 0 : if (!mSurface || !mSurfaceValid) {
218 0 : return nsnull;
219 : }
220 :
221 0 : if (aContent == CONTENT_COLOR) {
222 : // cairo_surface_create_similar will use a matching visual if it can.
223 : // However, systems with 16-bit or indexed default visuals may benefit
224 : // from rendering with 24-bit formats.
225 0 : static bool force24bpp = GetForce24bppPref();
226 0 : if (force24bpp
227 0 : && cairo_xlib_surface_get_depth(CairoSurface()) != 24) {
228 : XRenderPictFormat* format =
229 0 : XRenderFindStandardFormat(mDisplay, PictStandardRGB24);
230 0 : if (format) {
231 : // Cairo only performs simple self-copies as desired if it
232 : // knows that this is a Pixmap surface. It only knows that
233 : // surfaces are pixmap surfaces if it creates the Pixmap
234 : // itself, so we use cairo_surface_create_similar with a
235 : // temporary reference surface to indicate the format.
236 0 : Screen* screen = cairo_xlib_surface_get_screen(CairoSurface());
237 : nsRefPtr<gfxXlibSurface> depth24reference =
238 : gfxXlibSurface::Create(screen, format,
239 0 : gfxIntSize(1, 1), mDrawable);
240 0 : if (depth24reference)
241 0 : return depth24reference->
242 0 : gfxASurface::CreateSimilarSurface(aContent, aSize);
243 : }
244 : }
245 : }
246 :
247 0 : return gfxASurface::CreateSimilarSurface(aContent, aSize);
248 : }
249 :
250 : void
251 0 : gfxXlibSurface::DoSizeQuery()
252 : {
253 : // figure out width/height/depth
254 : Window root_ignore;
255 : int x_ignore, y_ignore;
256 : unsigned int bwidth_ignore, width, height, depth;
257 :
258 : XGetGeometry(mDisplay,
259 : mDrawable,
260 : &root_ignore, &x_ignore, &y_ignore,
261 : &width, &height,
262 0 : &bwidth_ignore, &depth);
263 :
264 0 : mSize.width = width;
265 0 : mSize.height = height;
266 0 : }
267 :
268 0 : class DisplayTable {
269 : public:
270 : static bool GetColormapAndVisual(Screen* screen,
271 : XRenderPictFormat* format,
272 : Visual* visual, Colormap* colormap,
273 : Visual** visualForColormap);
274 :
275 : private:
276 0 : struct ColormapEntry {
277 : XRenderPictFormat* mFormat;
278 : // The Screen is needed here because colormaps (and their visuals) may
279 : // only be used on one Screen, but XRenderPictFormats are not unique
280 : // to any one Screen.
281 : Screen* mScreen;
282 : Visual* mVisual;
283 : Colormap mColormap;
284 : };
285 :
286 0 : class DisplayInfo {
287 : public:
288 0 : DisplayInfo(Display* display) : mDisplay(display) { }
289 : Display* mDisplay;
290 : nsTArray<ColormapEntry> mColormapEntries;
291 : };
292 :
293 : // Comparator for finding the DisplayInfo
294 : class FindDisplay {
295 : public:
296 0 : bool Equals(const DisplayInfo& info, const Display *display) const
297 : {
298 0 : return info.mDisplay == display;
299 : }
300 : };
301 :
302 : static int DisplayClosing(Display *display, XExtCodes* codes);
303 :
304 : nsTArray<DisplayInfo> mDisplays;
305 : static DisplayTable* sDisplayTable;
306 : };
307 :
308 : DisplayTable* DisplayTable::sDisplayTable;
309 :
310 : // Pixmaps don't have a particular associated visual but the pixel values are
311 : // interpreted according to a visual/colormap pairs.
312 : //
313 : // cairo is designed for surfaces with either TrueColor visuals or the
314 : // default visual (which may not be true color). TrueColor visuals don't
315 : // really need a colormap because the visual indicates the pixel format,
316 : // and cairo uses the default visual with the default colormap, so cairo
317 : // surfaces don't need an explicit colormap.
318 : //
319 : // However, some toolkits (e.g. GDK) need a colormap even with TrueColor
320 : // visuals. We can create a colormap for these visuals, but it will use about
321 : // 20kB of memory in the server, so we use the default colormap when
322 : // suitable and share colormaps between surfaces. Another reason for
323 : // minimizing colormap turnover is that the plugin process must leak resources
324 : // for each new colormap id when using older GDK libraries (bug 569775).
325 : //
326 : // Only the format of the pixels is important for rendering to Pixmaps, so if
327 : // the format of a visual matches that of the surface, then that visual can be
328 : // used for rendering to the surface. Multiple visuals can match the same
329 : // format (but have different GLX properties), so the visual returned may
330 : // differ from the visual passed in. Colormaps are tied to a visual, so
331 : // should only be used with their visual.
332 :
333 : /* static */ bool
334 0 : DisplayTable::GetColormapAndVisual(Screen* aScreen, XRenderPictFormat* aFormat,
335 : Visual* aVisual, Colormap* aColormap,
336 : Visual** aVisualForColormap)
337 :
338 : {
339 0 : Display* display = DisplayOfScreen(aScreen);
340 :
341 : // Use the default colormap if the default visual matches.
342 0 : Visual *defaultVisual = DefaultVisualOfScreen(aScreen);
343 0 : if (aVisual == defaultVisual
344 : || (aFormat
345 0 : && aFormat == XRenderFindVisualFormat(display, defaultVisual)))
346 : {
347 0 : *aColormap = DefaultColormapOfScreen(aScreen);
348 0 : *aVisualForColormap = defaultVisual;
349 0 : return true;
350 : }
351 :
352 : // Only supporting TrueColor non-default visuals
353 0 : if (!aVisual || aVisual->c_class != TrueColor)
354 0 : return false;
355 :
356 0 : if (!sDisplayTable) {
357 0 : sDisplayTable = new DisplayTable();
358 : }
359 :
360 0 : nsTArray<DisplayInfo>* displays = &sDisplayTable->mDisplays;
361 0 : PRUint32 d = displays->IndexOf(display, 0, FindDisplay());
362 :
363 0 : if (d == displays->NoIndex) {
364 0 : d = displays->Length();
365 : // Register for notification of display closing, when this info
366 : // becomes invalid.
367 0 : XExtCodes *codes = XAddExtension(display);
368 0 : if (!codes)
369 0 : return false;
370 :
371 0 : XESetCloseDisplay(display, codes->extension, DisplayClosing);
372 : // Add a new DisplayInfo.
373 0 : displays->AppendElement(display);
374 : }
375 :
376 : nsTArray<ColormapEntry>* entries =
377 0 : &displays->ElementAt(d).mColormapEntries;
378 :
379 : // Only a small number of formats are expected to be used, so just do a
380 : // simple linear search.
381 0 : for (PRUint32 i = 0; i < entries->Length(); ++i) {
382 0 : const ColormapEntry& entry = entries->ElementAt(i);
383 : // Only the format and screen need to match. (The visual may differ.)
384 : // If there is no format (e.g. no RENDER extension) then just compare
385 : // the visual.
386 0 : if ((aFormat && entry.mFormat == aFormat && entry.mScreen == aScreen)
387 : || aVisual == entry.mVisual) {
388 0 : *aColormap = entry.mColormap;
389 0 : *aVisualForColormap = entry.mVisual;
390 0 : return true;
391 : }
392 : }
393 :
394 : // No existing entry. Create a colormap and add an entry.
395 : Colormap colormap = XCreateColormap(display, RootWindowOfScreen(aScreen),
396 0 : aVisual, AllocNone);
397 0 : ColormapEntry* newEntry = entries->AppendElement();
398 0 : newEntry->mFormat = aFormat;
399 0 : newEntry->mScreen = aScreen;
400 0 : newEntry->mVisual = aVisual;
401 0 : newEntry->mColormap = colormap;
402 :
403 0 : *aColormap = colormap;
404 0 : *aVisualForColormap = aVisual;
405 0 : return true;
406 : }
407 :
408 : /* static */ int
409 0 : DisplayTable::DisplayClosing(Display *display, XExtCodes* codes)
410 : {
411 : // No need to free the colormaps explicitly as they will be released when
412 : // the connection is closed.
413 0 : sDisplayTable->mDisplays.RemoveElement(display, FindDisplay());
414 0 : if (sDisplayTable->mDisplays.Length() == 0) {
415 0 : delete sDisplayTable;
416 0 : sDisplayTable = nsnull;
417 : }
418 0 : return 0;
419 : }
420 :
421 : bool
422 0 : gfxXlibSurface::GetColormapAndVisual(Colormap* aColormap, Visual** aVisual)
423 : {
424 0 : if (!mSurfaceValid)
425 0 : return false;
426 :
427 : XRenderPictFormat* format =
428 0 : cairo_xlib_surface_get_xrender_format(CairoSurface());
429 0 : Screen* screen = cairo_xlib_surface_get_screen(CairoSurface());
430 0 : Visual* visual = cairo_xlib_surface_get_visual(CairoSurface());
431 :
432 : return DisplayTable::GetColormapAndVisual(screen, format, visual,
433 0 : aColormap, aVisual);
434 : }
435 :
436 : /* static */
437 : int
438 0 : gfxXlibSurface::DepthOfVisual(const Screen* screen, const Visual* visual)
439 : {
440 0 : for (int d = 0; d < screen->ndepths; d++) {
441 0 : const Depth& d_info = screen->depths[d];
442 0 : if (visual >= &d_info.visuals[0]
443 : && visual < &d_info.visuals[d_info.nvisuals])
444 0 : return d_info.depth;
445 : }
446 :
447 0 : NS_ERROR("Visual not on Screen.");
448 0 : return 0;
449 : }
450 :
451 : /* static */
452 : Visual*
453 0 : gfxXlibSurface::FindVisual(Screen *screen, gfxImageFormat format)
454 : {
455 : int depth;
456 : unsigned long red_mask, green_mask, blue_mask;
457 0 : switch (format) {
458 : case ImageFormatARGB32:
459 0 : depth = 32;
460 0 : red_mask = 0xff0000;
461 0 : green_mask = 0xff00;
462 0 : blue_mask = 0xff;
463 0 : break;
464 : case ImageFormatRGB24:
465 0 : depth = 24;
466 0 : red_mask = 0xff0000;
467 0 : green_mask = 0xff00;
468 0 : blue_mask = 0xff;
469 0 : break;
470 : case ImageFormatRGB16_565:
471 0 : depth = 16;
472 0 : red_mask = 0xf800;
473 0 : green_mask = 0x7e0;
474 0 : blue_mask = 0x1f;
475 0 : break;
476 : case ImageFormatA8:
477 : case ImageFormatA1:
478 : default:
479 0 : return NULL;
480 : }
481 :
482 0 : for (int d = 0; d < screen->ndepths; d++) {
483 0 : const Depth& d_info = screen->depths[d];
484 0 : if (d_info.depth != depth)
485 0 : continue;
486 :
487 0 : for (int v = 0; v < d_info.nvisuals; v++) {
488 0 : Visual* visual = &d_info.visuals[v];
489 :
490 0 : if (visual->c_class == TrueColor &&
491 : visual->red_mask == red_mask &&
492 : visual->green_mask == green_mask &&
493 : visual->blue_mask == blue_mask)
494 0 : return visual;
495 : }
496 : }
497 :
498 0 : return NULL;
499 : }
500 :
501 : /* static */
502 : XRenderPictFormat*
503 0 : gfxXlibSurface::FindRenderFormat(Display *dpy, gfxImageFormat format)
504 : {
505 0 : switch (format) {
506 : case ImageFormatARGB32:
507 0 : return XRenderFindStandardFormat (dpy, PictStandardARGB32);
508 : case ImageFormatRGB24:
509 0 : return XRenderFindStandardFormat (dpy, PictStandardRGB24);
510 : case ImageFormatRGB16_565: {
511 : // PictStandardRGB16_565 is not standard Xrender format
512 : // we should try to find related visual
513 : // and find xrender format by visual
514 0 : Visual *visual = FindVisual(DefaultScreenOfDisplay(dpy), format);
515 0 : if (!visual)
516 0 : return NULL;
517 0 : return XRenderFindVisualFormat(dpy, visual);
518 : }
519 : case ImageFormatA8:
520 0 : return XRenderFindStandardFormat (dpy, PictStandardA8);
521 : case ImageFormatA1:
522 0 : return XRenderFindStandardFormat (dpy, PictStandardA1);
523 : default:
524 : break;
525 : }
526 :
527 0 : return (XRenderPictFormat*)NULL;
528 : }
529 :
530 : Screen*
531 0 : gfxXlibSurface::XScreen()
532 : {
533 0 : return cairo_xlib_surface_get_screen(CairoSurface());
534 : }
535 :
536 : XRenderPictFormat*
537 0 : gfxXlibSurface::XRenderFormat()
538 : {
539 0 : return cairo_xlib_surface_get_xrender_format(CairoSurface());
540 : }
541 :
542 : #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
543 : GLXPixmap
544 0 : gfxXlibSurface::GetGLXPixmap()
545 : {
546 0 : if (!mGLXPixmap) {
547 0 : mGLXPixmap = gl::sGLXLibrary.CreatePixmap(this);
548 : }
549 0 : return mGLXPixmap;
550 : }
551 : #endif
552 :
553 : gfxASurface::MemoryLocation
554 0 : gfxXlibSurface::GetMemoryLocation() const
555 : {
556 0 : return MEMORY_OUT_OF_PROCESS;
557 : }
|