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