|
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/. */ |
|
5 |
|
6 #define PANGO_ENABLE_BACKEND |
|
7 #define PANGO_ENABLE_ENGINE |
|
8 |
|
9 #include "gfxPlatformGtk.h" |
|
10 #include "prenv.h" |
|
11 |
|
12 #include "nsUnicharUtils.h" |
|
13 #include "nsUnicodeProperties.h" |
|
14 #include "gfx2DGlue.h" |
|
15 #include "gfxFontconfigUtils.h" |
|
16 #include "gfxPangoFonts.h" |
|
17 #include "gfxContext.h" |
|
18 #include "gfxUserFontSet.h" |
|
19 #include "gfxFT2FontBase.h" |
|
20 |
|
21 #include "mozilla/gfx/2D.h" |
|
22 |
|
23 #include "cairo.h" |
|
24 #include <gtk/gtk.h> |
|
25 |
|
26 #include "gfxImageSurface.h" |
|
27 #ifdef MOZ_X11 |
|
28 #include <gdk/gdkx.h> |
|
29 #include "gfxXlibSurface.h" |
|
30 #include "cairo-xlib.h" |
|
31 #include "mozilla/Preferences.h" |
|
32 |
|
33 /* Undefine the Status from Xlib since it will conflict with system headers on OSX */ |
|
34 #if defined(__APPLE__) && defined(Status) |
|
35 #undef Status |
|
36 #endif |
|
37 |
|
38 #endif /* MOZ_X11 */ |
|
39 |
|
40 #include <fontconfig/fontconfig.h> |
|
41 |
|
42 #include "nsMathUtils.h" |
|
43 |
|
44 #define GDK_PIXMAP_SIZE_MAX 32767 |
|
45 |
|
46 using namespace mozilla; |
|
47 using namespace mozilla::gfx; |
|
48 using namespace mozilla::unicode; |
|
49 |
|
50 gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nullptr; |
|
51 |
|
52 #if (MOZ_WIDGET_GTK == 2) |
|
53 static cairo_user_data_key_t cairo_gdk_drawable_key; |
|
54 #endif |
|
55 |
|
56 #ifdef MOZ_X11 |
|
57 bool gfxPlatformGtk::sUseXRender = true; |
|
58 #endif |
|
59 |
|
60 gfxPlatformGtk::gfxPlatformGtk() |
|
61 { |
|
62 if (!sFontconfigUtils) |
|
63 sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils(); |
|
64 #ifdef MOZ_X11 |
|
65 sUseXRender = mozilla::Preferences::GetBool("gfx.xrender.enabled"); |
|
66 #endif |
|
67 |
|
68 uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA); |
|
69 uint32_t contentMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA); |
|
70 InitBackendPrefs(canvasMask, BackendType::CAIRO, |
|
71 contentMask, BackendType::CAIRO); |
|
72 } |
|
73 |
|
74 gfxPlatformGtk::~gfxPlatformGtk() |
|
75 { |
|
76 gfxFontconfigUtils::Shutdown(); |
|
77 sFontconfigUtils = nullptr; |
|
78 |
|
79 gfxPangoFontGroup::Shutdown(); |
|
80 } |
|
81 |
|
82 already_AddRefed<gfxASurface> |
|
83 gfxPlatformGtk::CreateOffscreenSurface(const IntSize& size, |
|
84 gfxContentType contentType) |
|
85 { |
|
86 nsRefPtr<gfxASurface> newSurface; |
|
87 bool needsClear = true; |
|
88 gfxImageFormat imageFormat = OptimalFormatForContent(contentType); |
|
89 #ifdef MOZ_X11 |
|
90 // XXX we really need a different interface here, something that passes |
|
91 // in more context, including the display and/or target surface type that |
|
92 // we should try to match |
|
93 GdkScreen *gdkScreen = gdk_screen_get_default(); |
|
94 if (gdkScreen) { |
|
95 if (UseXRender()) { |
|
96 Screen *screen = gdk_x11_screen_get_xscreen(gdkScreen); |
|
97 XRenderPictFormat* xrenderFormat = |
|
98 gfxXlibSurface::FindRenderFormat(DisplayOfScreen(screen), |
|
99 imageFormat); |
|
100 |
|
101 if (xrenderFormat) { |
|
102 newSurface = gfxXlibSurface::Create(screen, xrenderFormat, |
|
103 ThebesIntSize(size)); |
|
104 } |
|
105 } else { |
|
106 // We're not going to use XRender, so we don't need to |
|
107 // search for a render format |
|
108 newSurface = new gfxImageSurface(ThebesIntSize(size), imageFormat); |
|
109 // The gfxImageSurface ctor zeroes this for us, no need to |
|
110 // waste time clearing again |
|
111 needsClear = false; |
|
112 } |
|
113 } |
|
114 #endif |
|
115 |
|
116 if (!newSurface) { |
|
117 // We couldn't create a native surface for whatever reason; |
|
118 // e.g., no display, no RENDER, bad size, etc. |
|
119 // Fall back to image surface for the data. |
|
120 newSurface = new gfxImageSurface(ThebesIntSize(size), imageFormat); |
|
121 } |
|
122 |
|
123 if (newSurface->CairoStatus()) { |
|
124 newSurface = nullptr; // surface isn't valid for some reason |
|
125 } |
|
126 |
|
127 if (newSurface && needsClear) { |
|
128 gfxContext tmpCtx(newSurface); |
|
129 tmpCtx.SetOperator(gfxContext::OPERATOR_CLEAR); |
|
130 tmpCtx.Paint(); |
|
131 } |
|
132 |
|
133 return newSurface.forget(); |
|
134 } |
|
135 |
|
136 nsresult |
|
137 gfxPlatformGtk::GetFontList(nsIAtom *aLangGroup, |
|
138 const nsACString& aGenericFamily, |
|
139 nsTArray<nsString>& aListOfFonts) |
|
140 { |
|
141 return sFontconfigUtils->GetFontList(aLangGroup, aGenericFamily, |
|
142 aListOfFonts); |
|
143 } |
|
144 |
|
145 nsresult |
|
146 gfxPlatformGtk::UpdateFontList() |
|
147 { |
|
148 return sFontconfigUtils->UpdateFontList(); |
|
149 } |
|
150 |
|
151 nsresult |
|
152 gfxPlatformGtk::ResolveFontName(const nsAString& aFontName, |
|
153 FontResolverCallback aCallback, |
|
154 void *aClosure, |
|
155 bool& aAborted) |
|
156 { |
|
157 return sFontconfigUtils->ResolveFontName(aFontName, aCallback, |
|
158 aClosure, aAborted); |
|
159 } |
|
160 |
|
161 nsresult |
|
162 gfxPlatformGtk::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) |
|
163 { |
|
164 return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName); |
|
165 } |
|
166 |
|
167 gfxFontGroup * |
|
168 gfxPlatformGtk::CreateFontGroup(const nsAString &aFamilies, |
|
169 const gfxFontStyle *aStyle, |
|
170 gfxUserFontSet *aUserFontSet) |
|
171 { |
|
172 return new gfxPangoFontGroup(aFamilies, aStyle, aUserFontSet); |
|
173 } |
|
174 |
|
175 gfxFontEntry* |
|
176 gfxPlatformGtk::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, |
|
177 const nsAString& aFontName) |
|
178 { |
|
179 return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontName); |
|
180 } |
|
181 |
|
182 gfxFontEntry* |
|
183 gfxPlatformGtk::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, |
|
184 const uint8_t *aFontData, uint32_t aLength) |
|
185 { |
|
186 // passing ownership of the font data to the new font entry |
|
187 return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, |
|
188 aFontData, aLength); |
|
189 } |
|
190 |
|
191 bool |
|
192 gfxPlatformGtk::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) |
|
193 { |
|
194 // check for strange format flags |
|
195 NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED), |
|
196 "strange font format hint set"); |
|
197 |
|
198 // accept supported formats |
|
199 // Pango doesn't apply features from AAT TrueType extensions. |
|
200 // Assume that if this is the only SFNT format specified, |
|
201 // then AAT extensions are required for complex script support. |
|
202 if (aFormatFlags & (gfxUserFontSet::FLAG_FORMAT_WOFF | |
|
203 gfxUserFontSet::FLAG_FORMAT_OPENTYPE | |
|
204 gfxUserFontSet::FLAG_FORMAT_TRUETYPE)) { |
|
205 return true; |
|
206 } |
|
207 |
|
208 // reject all other formats, known and unknown |
|
209 if (aFormatFlags != 0) { |
|
210 return false; |
|
211 } |
|
212 |
|
213 // no format hint set, need to look at data |
|
214 return true; |
|
215 } |
|
216 |
|
217 static int32_t sDPI = 0; |
|
218 |
|
219 int32_t |
|
220 gfxPlatformGtk::GetDPI() |
|
221 { |
|
222 if (!sDPI) { |
|
223 // Make sure init is run so we have a resolution |
|
224 GdkScreen *screen = gdk_screen_get_default(); |
|
225 gtk_settings_get_for_screen(screen); |
|
226 sDPI = int32_t(round(gdk_screen_get_resolution(screen))); |
|
227 if (sDPI <= 0) { |
|
228 // Fall back to something sane |
|
229 sDPI = 96; |
|
230 } |
|
231 } |
|
232 return sDPI; |
|
233 } |
|
234 |
|
235 gfxImageFormat |
|
236 gfxPlatformGtk::GetOffscreenFormat() |
|
237 { |
|
238 // Make sure there is a screen |
|
239 GdkScreen *screen = gdk_screen_get_default(); |
|
240 if (screen && gdk_visual_get_depth(gdk_visual_get_system()) == 16) { |
|
241 return gfxImageFormat::RGB16_565; |
|
242 } |
|
243 |
|
244 return gfxImageFormat::RGB24; |
|
245 } |
|
246 |
|
247 static int sDepth = 0; |
|
248 |
|
249 int |
|
250 gfxPlatformGtk::GetScreenDepth() const |
|
251 { |
|
252 if (!sDepth) { |
|
253 GdkScreen *screen = gdk_screen_get_default(); |
|
254 if (screen) { |
|
255 sDepth = gdk_visual_get_depth(gdk_visual_get_system()); |
|
256 } else { |
|
257 sDepth = 24; |
|
258 } |
|
259 |
|
260 } |
|
261 |
|
262 return sDepth; |
|
263 } |
|
264 |
|
265 bool |
|
266 gfxPlatformGtk::SupportsOffMainThreadCompositing() |
|
267 { |
|
268 // Nightly builds have OMTC support by default for Electrolysis testing. |
|
269 #if defined(MOZ_X11) && !defined(NIGHTLY_BUILD) |
|
270 return (PR_GetEnv("MOZ_USE_OMTC") != nullptr) || |
|
271 (PR_GetEnv("MOZ_OMTC_ENABLED") != nullptr); |
|
272 #else |
|
273 return true; |
|
274 #endif |
|
275 } |
|
276 |
|
277 void |
|
278 gfxPlatformGtk::GetPlatformCMSOutputProfile(void *&mem, size_t &size) |
|
279 { |
|
280 mem = nullptr; |
|
281 size = 0; |
|
282 |
|
283 #ifdef MOZ_X11 |
|
284 const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA"; |
|
285 const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE"; |
|
286 |
|
287 Atom edidAtom, iccAtom; |
|
288 Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); |
|
289 // In xpcshell tests, we never initialize X and hence don't have a Display. |
|
290 // In this case, there's no output colour management to be done, so we just |
|
291 // return with nullptr. |
|
292 if (!dpy) |
|
293 return; |
|
294 |
|
295 Window root = gdk_x11_get_default_root_xwindow(); |
|
296 |
|
297 Atom retAtom; |
|
298 int retFormat; |
|
299 unsigned long retLength, retAfter; |
|
300 unsigned char *retProperty ; |
|
301 |
|
302 iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE); |
|
303 if (iccAtom) { |
|
304 // read once to get size, once for the data |
|
305 if (Success == XGetWindowProperty(dpy, root, iccAtom, |
|
306 0, INT_MAX /* length */, |
|
307 False, AnyPropertyType, |
|
308 &retAtom, &retFormat, &retLength, |
|
309 &retAfter, &retProperty)) { |
|
310 |
|
311 if (retLength > 0) { |
|
312 void *buffer = malloc(retLength); |
|
313 if (buffer) { |
|
314 memcpy(buffer, retProperty, retLength); |
|
315 mem = buffer; |
|
316 size = retLength; |
|
317 } |
|
318 } |
|
319 |
|
320 XFree(retProperty); |
|
321 if (size > 0) { |
|
322 #ifdef DEBUG_tor |
|
323 fprintf(stderr, |
|
324 "ICM profile read from %s successfully\n", |
|
325 ICC_PROFILE_ATOM_NAME); |
|
326 #endif |
|
327 return; |
|
328 } |
|
329 } |
|
330 } |
|
331 |
|
332 edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE); |
|
333 if (edidAtom) { |
|
334 if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32, |
|
335 False, AnyPropertyType, |
|
336 &retAtom, &retFormat, &retLength, |
|
337 &retAfter, &retProperty)) { |
|
338 double gamma; |
|
339 qcms_CIE_xyY whitePoint; |
|
340 qcms_CIE_xyYTRIPLE primaries; |
|
341 |
|
342 if (retLength != 128) { |
|
343 #ifdef DEBUG_tor |
|
344 fprintf(stderr, "Short EDID data\n"); |
|
345 #endif |
|
346 return; |
|
347 } |
|
348 |
|
349 // Format documented in "VESA E-EDID Implementation Guide" |
|
350 |
|
351 gamma = (100 + retProperty[0x17]) / 100.0; |
|
352 whitePoint.x = ((retProperty[0x21] << 2) | |
|
353 (retProperty[0x1a] >> 2 & 3)) / 1024.0; |
|
354 whitePoint.y = ((retProperty[0x22] << 2) | |
|
355 (retProperty[0x1a] >> 0 & 3)) / 1024.0; |
|
356 whitePoint.Y = 1.0; |
|
357 |
|
358 primaries.red.x = ((retProperty[0x1b] << 2) | |
|
359 (retProperty[0x19] >> 6 & 3)) / 1024.0; |
|
360 primaries.red.y = ((retProperty[0x1c] << 2) | |
|
361 (retProperty[0x19] >> 4 & 3)) / 1024.0; |
|
362 primaries.red.Y = 1.0; |
|
363 |
|
364 primaries.green.x = ((retProperty[0x1d] << 2) | |
|
365 (retProperty[0x19] >> 2 & 3)) / 1024.0; |
|
366 primaries.green.y = ((retProperty[0x1e] << 2) | |
|
367 (retProperty[0x19] >> 0 & 3)) / 1024.0; |
|
368 primaries.green.Y = 1.0; |
|
369 |
|
370 primaries.blue.x = ((retProperty[0x1f] << 2) | |
|
371 (retProperty[0x1a] >> 6 & 3)) / 1024.0; |
|
372 primaries.blue.y = ((retProperty[0x20] << 2) | |
|
373 (retProperty[0x1a] >> 4 & 3)) / 1024.0; |
|
374 primaries.blue.Y = 1.0; |
|
375 |
|
376 XFree(retProperty); |
|
377 |
|
378 #ifdef DEBUG_tor |
|
379 fprintf(stderr, "EDID gamma: %f\n", gamma); |
|
380 fprintf(stderr, "EDID whitepoint: %f %f %f\n", |
|
381 whitePoint.x, whitePoint.y, whitePoint.Y); |
|
382 fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n", |
|
383 primaries.Red.x, primaries.Red.y, primaries.Red.Y, |
|
384 primaries.Green.x, primaries.Green.y, primaries.Green.Y, |
|
385 primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y); |
|
386 #endif |
|
387 |
|
388 qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size); |
|
389 |
|
390 #ifdef DEBUG_tor |
|
391 if (size > 0) { |
|
392 fprintf(stderr, |
|
393 "ICM profile read from %s successfully\n", |
|
394 EDID1_ATOM_NAME); |
|
395 } |
|
396 #endif |
|
397 } |
|
398 } |
|
399 #endif |
|
400 } |
|
401 |
|
402 |
|
403 #if (MOZ_WIDGET_GTK == 2) |
|
404 void |
|
405 gfxPlatformGtk::SetGdkDrawable(cairo_surface_t *target, |
|
406 GdkDrawable *drawable) |
|
407 { |
|
408 if (cairo_surface_status(target)) |
|
409 return; |
|
410 |
|
411 g_object_ref(drawable); |
|
412 |
|
413 cairo_surface_set_user_data (target, |
|
414 &cairo_gdk_drawable_key, |
|
415 drawable, |
|
416 g_object_unref); |
|
417 } |
|
418 |
|
419 GdkDrawable * |
|
420 gfxPlatformGtk::GetGdkDrawable(cairo_surface_t *target) |
|
421 { |
|
422 if (cairo_surface_status(target)) |
|
423 return nullptr; |
|
424 |
|
425 GdkDrawable *result; |
|
426 |
|
427 result = (GdkDrawable*) cairo_surface_get_user_data (target, |
|
428 &cairo_gdk_drawable_key); |
|
429 if (result) |
|
430 return result; |
|
431 |
|
432 #ifdef MOZ_X11 |
|
433 if (cairo_surface_get_type(target) != CAIRO_SURFACE_TYPE_XLIB) |
|
434 return nullptr; |
|
435 |
|
436 // try looking it up in gdk's table |
|
437 result = (GdkDrawable*) gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(target)); |
|
438 if (result) { |
|
439 SetGdkDrawable(target, result); |
|
440 return result; |
|
441 } |
|
442 #endif |
|
443 |
|
444 return nullptr; |
|
445 } |
|
446 #endif |
|
447 |
|
448 TemporaryRef<ScaledFont> |
|
449 gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont) |
|
450 { |
|
451 return GetScaledFontForFontWithCairoSkia(aTarget, aFont); |
|
452 } |