/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #define PANGO_ENABLE_BACKEND #define PANGO_ENABLE_ENGINE #include "gfxPlatformGtk.h" #include "prenv.h" #include "nsUnicharUtils.h" #include "nsUnicodeProperties.h" #include "gfxFontconfigUtils.h" #include "gfxPangoFonts.h" #include "gfxContext.h" #include "gfxUserFontSet.h" #include "gfxFT2FontBase.h" #include "mozilla/gfx/2D.h" #include "cairo.h" #include #include "gfxImageSurface.h" #ifdef MOZ_X11 #include #include "gfxXlibSurface.h" #include "cairo-xlib.h" #include "mozilla/Preferences.h" /* Undefine the Status from Xlib since it will conflict with system headers on OSX */ #if defined(__APPLE__) && defined(Status) #undef Status #endif #endif /* MOZ_X11 */ #include #include "nsMathUtils.h" #define GDK_PIXMAP_SIZE_MAX 32767 using namespace mozilla; using namespace mozilla::gfx; using namespace mozilla::unicode; gfxFontconfigUtils *gfxPlatformGtk::sFontconfigUtils = nullptr; static cairo_user_data_key_t cairo_gdk_drawable_key; #ifdef MOZ_X11 bool gfxPlatformGtk::sUseXRender = true; #endif gfxPlatformGtk::gfxPlatformGtk() { if (!sFontconfigUtils) sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils(); #ifdef MOZ_X11 sUseXRender = mozilla::Preferences::GetBool("gfx.xrender.enabled"); #endif uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA); uint32_t contentMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA); InitBackendPrefs(canvasMask, BackendType::CAIRO, contentMask, BackendType::CAIRO); } gfxPlatformGtk::~gfxPlatformGtk() { gfxFontconfigUtils::Shutdown(); sFontconfigUtils = nullptr; gfxPangoFontGroup::Shutdown(); #if 0 // It would be nice to do this (although it might need to be after // the cairo shutdown that happens in ~gfxPlatform). It even looks // idempotent. But it has fatal assertions that fire if stuff is // leaked, and we hit them. FcFini(); #endif } already_AddRefed gfxPlatformGtk::CreateOffscreenSurface(const gfxIntSize& size, gfxContentType contentType) { nsRefPtr newSurface; bool needsClear = true; gfxImageFormat imageFormat = OptimalFormatForContent(contentType); #ifdef MOZ_X11 // XXX we really need a different interface here, something that passes // in more context, including the display and/or target surface type that // we should try to match GdkScreen *gdkScreen = gdk_screen_get_default(); if (gdkScreen) { if (UseXRender()) { Screen *screen = gdk_x11_screen_get_xscreen(gdkScreen); XRenderPictFormat* xrenderFormat = gfxXlibSurface::FindRenderFormat(DisplayOfScreen(screen), imageFormat); if (xrenderFormat) { newSurface = gfxXlibSurface::Create(screen, xrenderFormat, size); } } else { // We're not going to use XRender, so we don't need to // search for a render format newSurface = new gfxImageSurface(size, imageFormat); // The gfxImageSurface ctor zeroes this for us, no need to // waste time clearing again needsClear = false; } } #endif if (!newSurface) { // We couldn't create a native surface for whatever reason; // e.g., no display, no RENDER, bad size, etc. // Fall back to image surface for the data. newSurface = new gfxImageSurface(size, imageFormat); } if (newSurface->CairoStatus()) { newSurface = nullptr; // surface isn't valid for some reason } if (newSurface && needsClear) { gfxContext tmpCtx(newSurface); tmpCtx.SetOperator(gfxContext::OPERATOR_CLEAR); tmpCtx.Paint(); } return newSurface.forget(); } nsresult gfxPlatformGtk::GetFontList(nsIAtom *aLangGroup, const nsACString& aGenericFamily, nsTArray& aListOfFonts) { return sFontconfigUtils->GetFontList(aLangGroup, aGenericFamily, aListOfFonts); } nsresult gfxPlatformGtk::UpdateFontList() { return sFontconfigUtils->UpdateFontList(); } nsresult gfxPlatformGtk::ResolveFontName(const nsAString& aFontName, FontResolverCallback aCallback, void *aClosure, bool& aAborted) { return sFontconfigUtils->ResolveFontName(aFontName, aCallback, aClosure, aAborted); } nsresult gfxPlatformGtk::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) { return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName); } gfxFontGroup * gfxPlatformGtk::CreateFontGroup(const nsAString &aFamilies, const gfxFontStyle *aStyle, gfxUserFontSet *aUserFontSet) { return new gfxPangoFontGroup(aFamilies, aStyle, aUserFontSet); } gfxFontEntry* gfxPlatformGtk::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, const nsAString& aFontName) { return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontName); } gfxFontEntry* gfxPlatformGtk::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry, const uint8_t *aFontData, uint32_t aLength) { // passing ownership of the font data to the new font entry return gfxPangoFontGroup::NewFontEntry(*aProxyEntry, aFontData, aLength); } bool gfxPlatformGtk::IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags) { // check for strange format flags NS_ASSERTION(!(aFormatFlags & gfxUserFontSet::FLAG_FORMAT_NOT_USED), "strange font format hint set"); // accept supported formats // Pango doesn't apply features from AAT TrueType extensions. // Assume that if this is the only SFNT format specified, // then AAT extensions are required for complex script support. if (aFormatFlags & (gfxUserFontSet::FLAG_FORMAT_WOFF | gfxUserFontSet::FLAG_FORMAT_OPENTYPE | gfxUserFontSet::FLAG_FORMAT_TRUETYPE)) { return true; } // reject all other formats, known and unknown if (aFormatFlags != 0) { return false; } // no format hint set, need to look at data return true; } static int32_t sDPI = 0; int32_t gfxPlatformGtk::GetDPI() { if (!sDPI) { // Make sure init is run so we have a resolution GdkScreen *screen = gdk_screen_get_default(); gtk_settings_get_for_screen(screen); sDPI = int32_t(round(gdk_screen_get_resolution(screen))); if (sDPI <= 0) { // Fall back to something sane sDPI = 96; } } return sDPI; } gfxImageFormat gfxPlatformGtk::GetOffscreenFormat() { // Make sure there is a screen GdkScreen *screen = gdk_screen_get_default(); if (screen && gdk_visual_get_depth(gdk_visual_get_system()) == 16) { return gfxImageFormat::RGB16_565; } return gfxImageFormat::RGB24; } static int sDepth = 0; int gfxPlatformGtk::GetScreenDepth() const { if (!sDepth) { GdkScreen *screen = gdk_screen_get_default(); if (screen) { sDepth = gdk_visual_get_depth(gdk_visual_get_system()); } else { sDepth = 24; } } return sDepth; } bool gfxPlatformGtk::SupportsOffMainThreadCompositing() { // Nightly builds have OMTC support by default for Electrolysis testing. #if defined(MOZ_X11) && !defined(NIGHTLY_BUILD) return (PR_GetEnv("MOZ_USE_OMTC") != nullptr) || (PR_GetEnv("MOZ_OMTC_ENABLED") != nullptr); #else return true; #endif } void gfxPlatformGtk::GetPlatformCMSOutputProfile(void *&mem, size_t &size) { mem = nullptr; size = 0; #ifdef MOZ_X11 const char EDID1_ATOM_NAME[] = "XFree86_DDC_EDID1_RAWDATA"; const char ICC_PROFILE_ATOM_NAME[] = "_ICC_PROFILE"; Atom edidAtom, iccAtom; Display *dpy = GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); // In xpcshell tests, we never initialize X and hence don't have a Display. // In this case, there's no output colour management to be done, so we just // return with nullptr. if (!dpy) return; Window root = gdk_x11_get_default_root_xwindow(); Atom retAtom; int retFormat; unsigned long retLength, retAfter; unsigned char *retProperty ; iccAtom = XInternAtom(dpy, ICC_PROFILE_ATOM_NAME, TRUE); if (iccAtom) { // read once to get size, once for the data if (Success == XGetWindowProperty(dpy, root, iccAtom, 0, INT_MAX /* length */, False, AnyPropertyType, &retAtom, &retFormat, &retLength, &retAfter, &retProperty)) { if (retLength > 0) { void *buffer = malloc(retLength); if (buffer) { memcpy(buffer, retProperty, retLength); mem = buffer; size = retLength; } } XFree(retProperty); if (size > 0) { #ifdef DEBUG_tor fprintf(stderr, "ICM profile read from %s successfully\n", ICC_PROFILE_ATOM_NAME); #endif return; } } } edidAtom = XInternAtom(dpy, EDID1_ATOM_NAME, TRUE); if (edidAtom) { if (Success == XGetWindowProperty(dpy, root, edidAtom, 0, 32, False, AnyPropertyType, &retAtom, &retFormat, &retLength, &retAfter, &retProperty)) { double gamma; qcms_CIE_xyY whitePoint; qcms_CIE_xyYTRIPLE primaries; if (retLength != 128) { #ifdef DEBUG_tor fprintf(stderr, "Short EDID data\n"); #endif return; } // Format documented in "VESA E-EDID Implementation Guide" gamma = (100 + retProperty[0x17]) / 100.0; whitePoint.x = ((retProperty[0x21] << 2) | (retProperty[0x1a] >> 2 & 3)) / 1024.0; whitePoint.y = ((retProperty[0x22] << 2) | (retProperty[0x1a] >> 0 & 3)) / 1024.0; whitePoint.Y = 1.0; primaries.red.x = ((retProperty[0x1b] << 2) | (retProperty[0x19] >> 6 & 3)) / 1024.0; primaries.red.y = ((retProperty[0x1c] << 2) | (retProperty[0x19] >> 4 & 3)) / 1024.0; primaries.red.Y = 1.0; primaries.green.x = ((retProperty[0x1d] << 2) | (retProperty[0x19] >> 2 & 3)) / 1024.0; primaries.green.y = ((retProperty[0x1e] << 2) | (retProperty[0x19] >> 0 & 3)) / 1024.0; primaries.green.Y = 1.0; primaries.blue.x = ((retProperty[0x1f] << 2) | (retProperty[0x1a] >> 6 & 3)) / 1024.0; primaries.blue.y = ((retProperty[0x20] << 2) | (retProperty[0x1a] >> 4 & 3)) / 1024.0; primaries.blue.Y = 1.0; XFree(retProperty); #ifdef DEBUG_tor fprintf(stderr, "EDID gamma: %f\n", gamma); fprintf(stderr, "EDID whitepoint: %f %f %f\n", whitePoint.x, whitePoint.y, whitePoint.Y); fprintf(stderr, "EDID primaries: [%f %f %f] [%f %f %f] [%f %f %f]\n", primaries.Red.x, primaries.Red.y, primaries.Red.Y, primaries.Green.x, primaries.Green.y, primaries.Green.Y, primaries.Blue.x, primaries.Blue.y, primaries.Blue.Y); #endif qcms_data_create_rgb_with_gamma(whitePoint, primaries, gamma, &mem, &size); #ifdef DEBUG_tor if (size > 0) { fprintf(stderr, "ICM profile read from %s successfully\n", EDID1_ATOM_NAME); } #endif } } #endif } #if (MOZ_WIDGET_GTK == 2) void gfxPlatformGtk::SetGdkDrawable(cairo_surface_t *target, GdkDrawable *drawable) { if (cairo_surface_status(target)) return; g_object_ref(drawable); cairo_surface_set_user_data (target, &cairo_gdk_drawable_key, drawable, g_object_unref); } GdkDrawable * gfxPlatformGtk::GetGdkDrawable(cairo_surface_t *target) { if (cairo_surface_status(target)) return nullptr; GdkDrawable *result; result = (GdkDrawable*) cairo_surface_get_user_data (target, &cairo_gdk_drawable_key); if (result) return result; #ifdef MOZ_X11 if (cairo_surface_get_type(target) != CAIRO_SURFACE_TYPE_XLIB) return nullptr; // try looking it up in gdk's table result = (GdkDrawable*) gdk_xid_table_lookup(cairo_xlib_surface_get_drawable(target)); if (result) { SetGdkDrawable(target, result); return result; } #endif return nullptr; } #endif TemporaryRef gfxPlatformGtk::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont) { return GetScaledFontForFontWithCairoSkia(aTarget, aFont); }