diff -r 000000000000 -r 6474c204b198 gfx/thebes/gfxPlatformGtk.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/thebes/gfxPlatformGtk.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,452 @@ +/* -*- 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 "gfx2DGlue.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; + +#if (MOZ_WIDGET_GTK == 2) +static cairo_user_data_key_t cairo_gdk_drawable_key; +#endif + +#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(); +} + +already_AddRefed +gfxPlatformGtk::CreateOffscreenSurface(const IntSize& 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, + ThebesIntSize(size)); + } + } else { + // We're not going to use XRender, so we don't need to + // search for a render format + newSurface = new gfxImageSurface(ThebesIntSize(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(ThebesIntSize(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); +}