diff --git a/gfx/src/Makefile.in b/gfx/src/Makefile.in index 5cc9c502f34..280135349d0 100644 --- a/gfx/src/Makefile.in +++ b/gfx/src/Makefile.in @@ -89,7 +89,7 @@ DIRS += xprintutil xprint endif ifdef MOZ_ENABLE_CAIRO_GFX -DIRS += cairo +DIRS += thebes else ifdef MOZ_ENABLE_GTK DIRS += gtk diff --git a/gfx/src/cairo/nsCairoFontMetrics.cpp b/gfx/src/cairo/nsCairoFontMetrics.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoFontMetrics.h b/gfx/src/cairo/nsCairoFontMetrics.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoGfxFactory.cpp b/gfx/src/cairo/nsCairoGfxFactory.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoImage.cpp b/gfx/src/cairo/nsCairoImage.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoImage.h b/gfx/src/cairo/nsCairoImage.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoRegion.cpp b/gfx/src/cairo/nsCairoRegion.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoRegion.h b/gfx/src/cairo/nsCairoRegion.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoRenderingContext.cpp b/gfx/src/cairo/nsCairoRenderingContext.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoRenderingContext.h b/gfx/src/cairo/nsCairoRenderingContext.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoScreen.cpp b/gfx/src/cairo/nsCairoScreen.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoScreen.h b/gfx/src/cairo/nsCairoScreen.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoScreenManager.cpp b/gfx/src/cairo/nsCairoScreenManager.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoScreenManager.h b/gfx/src/cairo/nsCairoScreenManager.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoSurfaceManager.cpp b/gfx/src/cairo/nsCairoSurfaceManager.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsCairoSurfaceManager.h b/gfx/src/cairo/nsCairoSurfaceManager.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsFontMetricsXft.cpp b/gfx/src/cairo/nsFontMetricsXft.cpp deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsFontMetricsXft.h b/gfx/src/cairo/nsFontMetricsXft.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/cairo/nsICairoFontMetrics.h b/gfx/src/cairo/nsICairoFontMetrics.h deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/gfx/src/thebes/Makefile.in b/gfx/src/thebes/Makefile.in new file mode 100644 index 00000000000..91006386a80 --- /dev/null +++ b/gfx/src/thebes/Makefile.in @@ -0,0 +1,154 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# http://www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is thebes gfx +# +# The Initial Developer of the Original Code is +# mozilla.org. +# Portions created by the Initial Developer are Copyright (C) 2005 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Vladimir Vukicevic +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = gfx +LIBRARY_NAME = gkgfxthebes +EXPORT_LIBRARY = 1 +IS_COMPONENT = 1 +MODULE_NAME = nsGfxModule +GRE_MODULE = 1 +LIBXUL_LIBRARY = 1 + +REQUIRES = xpcom \ + string \ + cairo \ + libpixman \ + glitz \ + thebes \ + gfx \ + widget \ + intl \ + view \ + pref \ + uconv \ + unicharutil \ + locale \ + necko \ + content \ + layout \ + dom \ + debug \ + imglib2 \ + $(ZLIB_REQUIRES) \ + $(NULL) + +CPPSRCS = \ + nsThebesDeviceContext.cpp \ + nsThebesDrawingSurface.cpp \ + nsThebesImage.cpp \ + nsThebesRegion.cpp \ + nsThebesBlender.cpp \ + nsThebesGfxFactory.cpp \ + nsThebesRenderingContext.cpp \ + nsThebesScreen.cpp \ + nsThebesScreenManager.cpp \ + $(NULL) + + +SHARED_LIBRARY_LIBS = \ + $(DIST)/lib/$(LIB_PREFIX)mozutil_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)gfxshared_s.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)mozcairo.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)mozlibpixman.$(LIB_SUFFIX) \ + $(DIST)/lib/$(LIB_PREFIX)thebes.$(LIB_SUFFIX) \ + $(NULL) + +ifdef MOZ_ENABLE_GLITZ +SHARED_LIBRARY_LIBS += $(DIST)/lib/$(LIB_PREFIX)mozglitz.$(LIB_SUFFIX) +endif + +EXTRA_DSO_LIBS = gkgfx + +ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2) +CPPSRCS += nsSystemFontsGTK2.cpp + +ifdef MOZ_ENABLE_PANGO +CPPSRCS += nsFontMetricsPango.cpp \ + mozilla-decoder.cpp \ + $(NULL) +endif + +ifdef MOZ_ENABLE_GLITZ +SHARED_LIBRARY_LIBS += $(DIST)/lib/$(LIB_PREFIX)mozglitzglx.$(LIB_SUFFIX) +REQUIRES += glitzglx +endif + +endif + +ifeq ($(MOZ_WIDGET_TOOLKIT),windows) +CPPSRCS += nsFontMetricsWin2.cpp \ + nsSystemFontsWin.cpp \ + $(NULL) +REQUIRES += glitzwgl + +_OS_LIBS = usp10 +OS_LIBS += $(call EXPAND_LIBNAME,$(_OS_LIBS)) +endif + +EXPORTS += nsIThebesRenderingContext.h + +LOCAL_INCLUDES = \ + -I$(srcdir)/. \ + -I$(srcdir)/.. \ + -I$(srcdir)/../shared \ + $(NULL) + +EXTRA_DSO_LDOPTS += \ + $(EXTRA_DSO_LIBS) \ + $(MOZ_COMPONENT_LIBS) \ + $(MOZ_UNICHARUTIL_LIBS) \ + $(MOZ_JS_LIBS) \ + $(TK_LIBS) \ + $(NULL) + + +include $(topsrcdir)/config/rules.mk + +CXXFLAGS += $(TK_CFLAGS) +CFLAGS += $(TK_CFLAGS) + +ifdef MOZ_ENABLE_GTK2 +DEFINES += -DMOZ_ENABLE_GTK2 +endif + diff --git a/gfx/src/thebes/mozilla-decoder.cpp b/gfx/src/thebes/mozilla-decoder.cpp new file mode 100644 index 00000000000..609595f1485 --- /dev/null +++ b/gfx/src/thebes/mozilla-decoder.cpp @@ -0,0 +1,375 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Christopher Blizzard + * . Portions created by the Initial Developer + * are Copyright (C) 2004 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#define PANGO_ENABLE_BACKEND +#define PANGO_ENABLE_ENGINE + +#include "mozilla-decoder.h" +#include +#include +#include +#include + +#include "nsString.h" +#include "nsIPersistentProperties2.h" +#include "nsNetUtil.h" +#include "nsReadableUtils.h" +#include "nsICharsetConverterManager.h" +#include "nsICharRepresentable.h" +#include "nsCompressedCharMap.h" + +#undef DEBUG_CUSTOM_ENCODER + +G_DEFINE_TYPE (MozillaDecoder, mozilla_decoder, PANGO_TYPE_FC_DECODER) + +MozillaDecoder *mozilla_decoder_new (void); + +static FcCharSet *mozilla_decoder_get_charset (PangoFcDecoder *decoder, + PangoFcFont *fcfont); +static PangoGlyph mozilla_decoder_get_glyph (PangoFcDecoder *decoder, + PangoFcFont *fcfont, + guint32 wc); + +static PangoFcDecoder *mozilla_find_decoder (FcPattern *pattern, + gpointer user_data); + +typedef struct _MozillaDecoderPrivate MozillaDecoderPrivate; + +#define MOZILLA_DECODER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MOZILLA_TYPE_DECODER, MozillaDecoderPrivate)) + +struct _MozillaDecoderPrivate { + char *family; + char *encoder; + char *cmap; + gboolean is_wide; + FcCharSet *charset; + nsCOMPtr uEncoder; +}; + +static nsICharsetConverterManager *gCharsetManager = NULL; + +static NS_DEFINE_CID(kCharsetConverterManagerCID, + NS_ICHARSETCONVERTERMANAGER_CID); + +// Hash tables that hold the custom encodings and custom cmaps used in +// various fonts. +GHashTable *encoder_hash = NULL; +GHashTable *cmap_hash = NULL; +GHashTable *wide_hash = NULL; + +void +mozilla_decoder_init (MozillaDecoder *decoder) +{ +} + +void +mozilla_decoder_class_init (MozillaDecoderClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + PangoFcDecoderClass *parent_class = PANGO_FC_DECODER_CLASS (klass); + + /* object_class->finalize = test_finalize; */ + + parent_class->get_charset = mozilla_decoder_get_charset; + parent_class->get_glyph = mozilla_decoder_get_glyph; + + g_type_class_add_private (object_class, sizeof (MozillaDecoderPrivate)); +} + +MozillaDecoder * +mozilla_decoder_new(void) +{ + return (MozillaDecoder *)g_object_new(MOZILLA_TYPE_DECODER, NULL); +} + +#ifdef DEBUG_CUSTOM_ENCODER +void +dump_hash(char *key, char *val, void *arg) +{ + printf("%s -> %s\n", key, val); +} +#endif + +/** + * mozilla_decoders_init: + * + * #mozilla_decoders_init: + * + * This initializes all of the application-specific custom decoders + * that Mozilla uses. This should only be called once during the + * lifetime of the application. + * + * Return value: zero on success, not zero on failure. + * + **/ + +int +mozilla_decoders_init(void) +{ + static PRBool initialized = PR_FALSE; + if (initialized) + return 0; + + encoder_hash = g_hash_table_new(g_str_hash, g_str_equal); + cmap_hash = g_hash_table_new(g_str_hash, g_str_equal); + wide_hash = g_hash_table_new(g_str_hash, g_str_equal); + + PRBool dumb = PR_FALSE; + nsCOMPtr props; + nsCOMPtr encodeEnum; + + NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(props), + NS_LITERAL_CSTRING("resource://gre/res/fonts/pangoFontEncoding.properties")); + + if (!props) + goto loser; + + // Enumerate the properties in this file and figure out all of the + // fonts for which we have custom encodings. + props->Enumerate(getter_AddRefs(encodeEnum)); + if (!encodeEnum) + goto loser; + + while (encodeEnum->HasMoreElements(&dumb), dumb) { + nsCOMPtr prop; + encodeEnum->GetNext(getter_AddRefs(prop)); + if (!prop) + goto loser; + + nsCAutoString name; + prop->GetKey(name); + nsAutoString value; + prop->GetValue(value); + + if (!StringBeginsWith(name, NS_LITERAL_CSTRING("encoding."))) { + printf("string doesn't begin with encoding?\n"); + continue; + } + + name = Substring(name, 9); + + if (StringEndsWith(name, NS_LITERAL_CSTRING(".ttf"))) { + name = Substring(name, 0, name.Length() - 4); + + // Strip off a .wide if it's there. + if (StringEndsWith(value, NS_LITERAL_STRING(".wide"))) { + g_hash_table_insert(wide_hash, g_strdup(name.get()), + g_strdup("wide")); + value = Substring(value, 0, name.Length() - 5); + } + + g_hash_table_insert(encoder_hash, + g_strdup(name.get()), + g_strdup(NS_ConvertUTF16toUTF8(value).get())); + } + else if (StringEndsWith(name, NS_LITERAL_CSTRING(".ftcmap"))) { + name = Substring(name, 0, name.Length() - 7); + g_hash_table_insert(cmap_hash, + g_strdup(name.get()), + g_strdup(NS_ConvertUTF16toUTF8(value).get())); + } + else { + printf("unknown suffix used for mapping\n"); + } + } + + pango_fc_font_map_add_decoder_find_func(PANGO_FC_FONT_MAP(pango_xft_get_font_map(GDK_DISPLAY(),gdk_x11_get_default_screen())), + mozilla_find_decoder, + NULL, + NULL); + + initialized = PR_TRUE; + +#ifdef DEBUG_CUSTOM_ENCODER + printf("*** encoders\n"); + g_hash_table_foreach(encoder_hash, (GHFunc)dump_hash, NULL); + + printf("*** cmaps\n"); + g_hash_table_foreach(cmap_hash, (GHFunc)dump_hash, NULL); +#endif + + return 0; + + loser: + return -1; +} + +FcCharSet * +mozilla_decoder_get_charset (PangoFcDecoder *decoder, + PangoFcFont *fcfont) +{ + MozillaDecoderPrivate *priv = MOZILLA_DECODER_GET_PRIVATE(decoder); + + if (priv->charset) + return priv->charset; + + // First time this has been accessed. Populate the charset. + priv->charset = FcCharSetCreate(); + + if (!gCharsetManager) { + CallGetService(kCharsetConverterManagerCID, &gCharsetManager); + } + + nsCOMPtr encoder; + nsCOMPtr represent; + + if (!gCharsetManager) + goto end; + + gCharsetManager->GetUnicodeEncoderRaw(priv->encoder, getter_AddRefs(encoder)); + if (!encoder) + goto end; + + encoder->SetOutputErrorBehavior(encoder->kOnError_Replace, nsnull, '?'); + + priv->uEncoder = encoder; + + represent = do_QueryInterface(encoder); + if (!represent) + goto end; + + PRUint32 map[UCS2_MAP_LEN]; + memset(map, 0, sizeof(map)); + + represent->FillInfo(map); + + for (int i = 0; i < NUM_UNICODE_CHARS; i++) { + if (IS_REPRESENTABLE(map, i)) + FcCharSetAddChar(priv->charset, i); + } + + end: + return priv->charset; +} + +PangoGlyph +mozilla_decoder_get_glyph (PangoFcDecoder *decoder, + PangoFcFont *fcfont, + guint32 wc) +{ + MozillaDecoderPrivate *priv = MOZILLA_DECODER_GET_PRIVATE(decoder); + + PangoGlyph retval = 0; + PRUnichar inchar = wc; + PRInt32 inlen = 1; + char outchar[2] = {0,0}; + PRInt32 outlen = 2; + + priv->uEncoder->Convert(&inchar, &inlen, outchar, &outlen); + if (outlen != 1) { + printf("Warning: mozilla_decoder_get_glyph doesn't support more than one character conversions.\n"); + return 0; + } + + FT_Face face = pango_fc_font_lock_face(fcfont); + +#ifdef DEBUG_CUSTOM_ENCODER + char *filename; + FcPatternGetString(fcfont->font_pattern, FC_FILE, 0, (FcChar8 **)&filename); + printf("filename is %s\n", filename); +#endif + + // Make sure to set the right charmap before trying to get the + // glyph + if (priv->cmap) { + if (!strcmp(priv->cmap, "mac_roman")) { + FT_Select_Charmap(face, ft_encoding_apple_roman); + } + else if (!strcmp(priv->cmap, "unicode")) { + FT_Select_Charmap(face, ft_encoding_unicode); + } + else { + printf("Warning: Invalid charmap entry for family %s\n", + priv->family); + } + } + + // Standard 8 bit to glyph translation + if (!priv->is_wide) { + FcChar32 blah = PRUint8(outchar[0]); + retval = FT_Get_Char_Index(face, blah); +#ifdef DEBUG_CUSTOM_ENCODER + printf("wc 0x%x outchar[0] 0x%x index 0x%x retval 0x%x face %p\n", + wc, outchar[0], blah, retval, (void *)face); +#endif + } + else { + printf("Warning: We don't support .wide fonts!\n"); + retval = 0; + } + + pango_fc_font_unlock_face(fcfont); + + return retval; +} + +PangoFcDecoder * +mozilla_find_decoder (FcPattern *pattern, gpointer user_data) +{ + // Compare the family name of the font that's been opened to see + // if we have a custom decoder. + const char *orig = NULL; + FcPatternGetString(pattern, FC_FAMILY, 0, (FcChar8 **)&orig); + + nsCAutoString family; + family.Assign(orig); + + family.StripWhitespace(); + ToLowerCase(family); + + char *encoder = (char *)g_hash_table_lookup(encoder_hash, family.get()); + if (!encoder) + return NULL; + + MozillaDecoder *decoder = mozilla_decoder_new(); + + MozillaDecoderPrivate *priv = MOZILLA_DECODER_GET_PRIVATE(decoder); + + priv->family = g_strdup(family.get()); + priv->encoder = g_strdup(encoder); + + char *cmap = (char *)g_hash_table_lookup(cmap_hash, family.get()); + if (cmap) + priv->cmap = g_strdup(cmap); + + char *wide = (char *)g_hash_table_lookup(wide_hash, family.get()); + if (wide) + priv->is_wide = TRUE; + + return PANGO_FC_DECODER(decoder); +} diff --git a/gfx/src/thebes/mozilla-decoder.h b/gfx/src/thebes/mozilla-decoder.h new file mode 100644 index 00000000000..3b78948b13f --- /dev/null +++ b/gfx/src/thebes/mozilla-decoder.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Christopher Blizzard + * . Portions created by the Initial Developer + * are Copyright (C) 2004 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _MOZILLA_DECODER_H +#define _MOZILLA_DECODER_H + +#include + +G_BEGIN_DECLS + +#define MOZILLA_TYPE_DECODER (mozilla_decoder_get_type()) +#define MOZILLA_DECODER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOZILLA_TYPE_DECODER, MozillaDecoder)) +#define MOZILLA_IS_DECODER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOZILLA_TYPE_DECODER)) + +typedef struct _MozillaDecoder MozillaDecoder; +typedef struct _MozillaDecoderClass MozillaDecoderClass; + +#define MOZILLA_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOZILLA_TYPE_DECODER, MozillaDecoderClass)) +#define MOZILLA_IS_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOZILLA_TYPE_DECODER)) +#define MOZILLA_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOZILLA_TYPE_DECODER, MozillaDecoderClass)) + +struct _MozillaDecoder +{ + PangoFcDecoder parent_instance; +}; + +struct _MozillaDecoderClass +{ + PangoFcDecoderClass parent_class; +}; + +GType mozilla_decoder_get_type (void); +int mozilla_decoders_init (void); + +G_END_DECLS + +#endif /*_MOZILLA_DECODER_H */ diff --git a/gfx/src/thebes/nsFontMetricsPango.cpp b/gfx/src/thebes/nsFontMetricsPango.cpp new file mode 100644 index 00000000000..bf5dbfd5904 --- /dev/null +++ b/gfx/src/thebes/nsFontMetricsPango.cpp @@ -0,0 +1,1668 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim:expandtab:shiftwidth=4:tabstop=4: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Christopher Blizzard + * . Portions created by the Initial Developer + * are Copyright (C) 2004 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsFont.h" +#include "nsIDeviceContext.h" +#include "nsICharsetConverterManager.h" +#include "nsIPref.h" +#include "nsServiceManagerUtils.h" + +#define PANGO_ENABLE_BACKEND +#define PANGO_ENABLE_ENGINE + +#include "nsFontMetricsPango.h" +#include "nsThebesRenderingContext.h" +#include "nsFontConfigUtils.h" + +#include "nsUnicharUtils.h" +#include "nsQuickSort.h" + +#include "cairo.h" +#include "cairo-ft.h" + +#include +#include + +#ifdef MOZ_ENABLE_XFT +#include +#endif + +#ifdef MOZ_ENABLE_GTK2 +#include +#include +#endif + +#include "mozilla-decoder.h" + +#define FORCE_PR_LOG +#include "prlog.h" + +// Globals + +static PRLogModuleInfo *gPangoFontLog; +static int gNumInstances; + +// Defines + +// This is the scaling factor that we keep fonts limited to against +// the display size. If a pixel size is requested that is more than +// this factor larger than the height of the display, it's clamped to +// that value instead of the requested size. +#define FONT_MAX_FONT_SCALE 2 + +static NS_DEFINE_CID(kCharsetConverterManagerCID, + NS_ICHARSETCONVERTERMANAGER_CID); + +#define AUTO_GLYPHBUF_SIZE 100 + +struct MozPangoLangGroup { + const char *mozLangGroup; + const char *PangoLang; +}; + +static const MozPangoLangGroup MozPangoLangGroups[] = { + { "x-western", "en" }, + { "x-central-euro", "pl" }, + { "x-cyrillic", "ru" }, + { "x-baltic", "lv" }, + { "x-devanagari", "hi" }, + { "x-tamil", "ta" }, + { "x-unicode", 0 }, + { "x-user-def", 0 }, +}; + +#define NUM_PANGO_LANG_GROUPS (sizeof (MozPangoLangGroups) / \ + sizeof (MozPangoLangGroups[0])) + +#ifdef DEBUG +#define DUMP_PRUNICHAR(ustr, ulen) for (PRUint32 llen=0;llen> 6) +#define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \ + MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s)))) + +// Static function decls + +static PRBool IsASCIIFontName (const nsString& aName); +static int FFRECountHyphens (nsACString &aFFREName); + +static PangoLanguage *GetPangoLanguage(nsIAtom *aLangGroup); +static const MozPangoLangGroup* FindPangoLangGroup (nsACString &aLangGroup); + +static void FreeGlobals (void); + +static PangoStyle CalculateStyle (PRUint8 aStyle); +static PangoWeight CalculateWeight (PRUint16 aWeight); + +static nsresult EnumFontsPango (nsIAtom* aLangGroup, const char* aGeneric, + PRUint32* aCount, PRUnichar*** aResult); +static int CompareFontNames (const void* aArg1, const void* aArg2, + void* aClosure); + +nsFontMetricsPango::nsFontMetricsPango() +{ + if (!gPangoFontLog) + gPangoFontLog = PR_NewLogModule("PangoFont"); + + gNumInstances++; + + mPangoFontDesc = nsnull; + mPangoContext = nsnull; + mLTRPangoContext = nsnull; + mRTLPangoContext = nsnull; + mPangoAttrList = nsnull; + mIsRTL = PR_FALSE; + + static PRBool initialized = PR_FALSE; + if (initialized) + return; + + // Initialized the custom decoders + if (!mozilla_decoders_init()) + initialized = PR_TRUE; +} + +nsFontMetricsPango::~nsFontMetricsPango() +{ + if (mDeviceContext) + mDeviceContext->FontMetricsDeleted(this); + + if (mPangoFontDesc) + pango_font_description_free(mPangoFontDesc); + + if (mLTRPangoContext) + g_object_unref(mLTRPangoContext); + + if (mRTLPangoContext) + g_object_unref(mRTLPangoContext); + + if (mPangoAttrList) + pango_attr_list_unref(mPangoAttrList); + + // XXX clean up all the pango objects + + if (--gNumInstances == 0) + FreeGlobals(); +} + + +NS_IMPL_ISUPPORTS1(nsFontMetricsPango, nsIFontMetrics) + +// nsIFontMetrics impl + +NS_IMETHODIMP +nsFontMetricsPango::Init(const nsFont& aFont, nsIAtom* aLangGroup, + nsIDeviceContext *aContext) +{ + mFont = aFont; + mLangGroup = aLangGroup; + + // Hang on to the device context + mDeviceContext = aContext; + + mPointSize = NSTwipsToFloatPoints(mFont.size); + + // Make sure to clamp the pixel size to something reasonable so we + // don't make the X server blow up. + nscoord screenPixels = gdk_screen_height(); + mPointSize = PR_MIN(screenPixels * FONT_MAX_FONT_SCALE, mPointSize); + + // enumerate over the font names passed in + mFont.EnumerateFamilies(nsFontMetricsPango::EnumFontCallback, this); + + nsCOMPtr prefService; + prefService = do_GetService(NS_PREF_CONTRACTID); + if (!prefService) + return NS_ERROR_FAILURE; + + nsXPIDLCString value; + const char* langGroup; + mLangGroup->GetUTF8String(&langGroup); + + // Set up the default font name if it's not set + if (!mGenericFont) { + nsCAutoString name("font.default."); + name.Append(langGroup); + prefService->CopyCharPref(name.get(), getter_Copies(value)); + + if (value.get()) + mDefaultFont = value.get(); + else + mDefaultFont = "serif"; + + mGenericFont = &mDefaultFont; + } + + // set up the minimum sizes for fonts + if (mLangGroup) { + nsCAutoString name("font.min-size."); + + if (mGenericFont->Equals("monospace")) + name.Append("fixed"); + else + name.Append("variable"); + + name.Append(char('.')); + name.Append(langGroup); + + PRInt32 minimumInt = 0; + float minimum; + nsresult res; + res = prefService->GetIntPref(name.get(), &minimumInt); + if (NS_FAILED(res)) + prefService->GetDefaultIntPref(name.get(), &minimumInt); + + if (minimumInt < 0) + minimumInt = 0; + + minimum = minimumInt; + + // The minimum size is specified in pixels, not in points. + // Convert the size from pixels to points. + //minimum = NSTwipsToFloatPoints(NSFloatPixelsToTwips(minimum, mDeviceContext->DevUnitsToAppUnits())); + if (mPointSize < minimum) + mPointSize = minimum; + } + + // Make sure that the pixel size is at least greater than zero + if (mPointSize < 1) { +#ifdef DEBUG + printf("*** Warning: nsFontMetricsPango created with point size %f\n", + mPointSize); +#endif + mPointSize = 1; + } + + nsresult rv = RealizeFont(); + if (NS_FAILED(rv)) + return rv; + + // Cache font metrics for the 'x' character + return CacheFontMetrics(); +} + +nsresult +nsFontMetricsPango::CacheFontMetrics(void) +{ + // Get our scale factor + float f; + float val; + f = mDeviceContext->DevUnitsToAppUnits(); + + mPangoAttrList = pango_attr_list_new(); + + GList *items = pango_itemize(mPangoContext, + "a", 0, 1, mPangoAttrList, NULL); + + if (!items) + return NS_ERROR_FAILURE; + + guint nitems = g_list_length(items); + if (nitems != 1) + return NS_ERROR_FAILURE; + + PangoItem *item = (PangoItem *)items->data; + PangoFcFont *fcfont = PANGO_FC_FONT(item->analysis.font); + if (!fcfont) + return NS_ERROR_FAILURE; + + FT_Face face; + TT_OS2 *os2; + XftFont *xftFont = pango_xft_font_get_font(PANGO_FONT(fcfont)); + if (!xftFont) + return NS_ERROR_NOT_AVAILABLE; + + face = XftLockFace(xftFont); + os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2); + + // mEmHeight (size in pixels of EM height) + int size; + if (FcPatternGetInteger(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != + FcResultMatch) { + size = 12; + } + mEmHeight = PR_MAX(1, nscoord(size * f)); + + // mMaxAscent + mMaxAscent = nscoord(xftFont->ascent * f); + + // mMaxDescent + mMaxDescent = nscoord(xftFont->descent * f); + + nscoord lineHeight = mMaxAscent + mMaxDescent; + + // mLeading (needs ascent and descent and EM height) + if (lineHeight > mEmHeight) + mLeading = lineHeight - mEmHeight; + else + mLeading = 0; + + // mMaxHeight (needs ascent and descent) + mMaxHeight = lineHeight; + + // mEmAscent (needs maxascent, EM height, ascent and descent) + mEmAscent = nscoord(mMaxAscent * mEmHeight / lineHeight); + + // mEmDescent (needs EM height and EM ascent + mEmDescent = mEmHeight - mEmAscent; + + // mMaxAdvance + mMaxAdvance = nscoord(xftFont->max_advance_width * f); + + // mSpaceWidth (width of a space) + nscoord tmpWidth; + GetWidth(" ", 1, tmpWidth); + mSpaceWidth = tmpWidth; + + // mAveCharWidth (width of an 'average' char) + // XftTextExtents16(GDK_DISPLAY(), xftFont, &xUnichar, 1, &extents); + //rawWidth = extents.width; + //mAveCharWidth = NSToCoordRound(rawWidth * f); + GetWidth("x", 1, tmpWidth); + mAveCharWidth = tmpWidth; + + // mXHeight (height of an 'x' character) + PRUnichar xUnichar('x'); + XGlyphInfo extents; + if (FcCharSetHasChar(xftFont->charset, xUnichar)) { + XftTextExtents16(GDK_DISPLAY(), xftFont, &xUnichar, 1, &extents); + mXHeight = extents.height; + } + else { + // 56% of ascent, best guess for non-true type or asian fonts + mXHeight = nscoord(((float)mMaxAscent) * 0.56); + } + mXHeight = nscoord(mXHeight * f); + + // mUnderlineOffset (offset for underlines) + val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_position, + face->size->metrics.y_scale); + if (val) { + mUnderlineOffset = NSToIntRound(val * f); + } + else { + mUnderlineOffset = + -NSToIntRound(PR_MAX(1, floor(0.1 * xftFont->height + 0.5)) * f); + } + + // mUnderlineSize (thickness of an underline) + val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_thickness, + face->size->metrics.y_scale); + if (val) { + mUnderlineSize = nscoord(PR_MAX(f, NSToIntRound(val * f))); + } + else { + mUnderlineSize = + NSToIntRound(PR_MAX(1, floor(0.05 * xftFont->height + 0.5)) * f); + } + + // mSuperscriptOffset + if (os2 && os2->ySuperscriptYOffset) { + val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySuperscriptYOffset, + face->size->metrics.y_scale); + mSuperscriptOffset = nscoord(PR_MAX(f, NSToIntRound(val * f))); + } + else { + mSuperscriptOffset = mXHeight; + } + + // mSubscriptOffset + if (os2 && os2->ySubscriptYOffset) { + val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySubscriptYOffset, + face->size->metrics.y_scale); + // some fonts have the incorrect sign. + val = (val < 0) ? -val : val; + mSubscriptOffset = nscoord(PR_MAX(f, NSToIntRound(val * f))); + } + else { + mSubscriptOffset = mXHeight; + } + + // mStrikeoutOffset + mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0); + + // mStrikeoutSize + mStrikeoutSize = mUnderlineSize; + + XftUnlockFace(xftFont); + + /* + printf("%i\n", mXHeight); + printf("%i\n", mSuperscriptOffset); + printf("%i\n", mSubscriptOffset); + printf("%i\n", mStrikeoutOffset); + printf("%i\n", mStrikeoutSize); + printf("%i\n", mUnderlineOffset); + printf("%i\n", mUnderlineSize); + printf("%i\n", mMaxHeight); + printf("%i\n", mLeading); + printf("%i\n", mEmHeight); + printf("%i\n", mEmAscent); + printf("%i\n", mEmDescent); + printf("%i\n", mMaxAscent); + printf("%i\n", mMaxDescent); + printf("%i\n", mMaxAdvance); + printf("%i\n", mSpaceWidth); + printf("%i\n", mAveCharWidth); + */ + + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsPango::Destroy() +{ + mDeviceContext = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsPango::GetLangGroup(nsIAtom** aLangGroup) +{ + *aLangGroup = mLangGroup; + NS_IF_ADDREF(*aLangGroup); + + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsPango::GetFontHandle(nsFontHandle &aHandle) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +// nsIFontMetricsPango impl + +nsresult +nsFontMetricsPango::GetWidth(const char* aString, PRUint32 aLength, + nscoord& aWidth, + nsThebesRenderingContext* aCtx) +{ + PangoLayout *layout = pango_layout_new(mPangoContext); + + pango_layout_set_text(layout, aString, aLength); + + int width, height; + + pango_layout_get_size(layout, &width, &height); + + width /= PANGO_SCALE; + + g_object_unref(layout); + + float f; + f = mDeviceContext->DevUnitsToAppUnits(); + aWidth = NSToCoordRound(width * f); + + // printf("GetWidth (char *) %d\n", aWidth); + + return NS_OK; +} + +nsresult +nsFontMetricsPango::GetWidth(const PRUnichar* aString, PRUint32 aLength, + nscoord& aWidth, PRInt32 *aFontID, + nsThebesRenderingContext* aCtx) +{ + nsresult rv = NS_OK; + PangoLayout *layout = pango_layout_new(mPangoContext); + + gchar *text = g_utf16_to_utf8(aString, aLength, + NULL, NULL, NULL); + + if (!text) { + aWidth = 0; +#ifdef DEBUG + NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow"); + DUMP_PRUNICHAR(aString, aLength) +#endif + rv = NS_ERROR_FAILURE; + goto loser; + } + + gint width, height; + + pango_layout_set_text(layout, text, strlen(text)); + pango_layout_get_size(layout, &width, &height); + + width /= PANGO_SCALE; + + float f; + f = mDeviceContext->DevUnitsToAppUnits(); + aWidth = NSToCoordRound(width * f); + + // printf("GetWidth %d\n", aWidth); + + loser: + g_free(text); + g_object_unref(layout); + + return rv; +} + + +nsresult +nsFontMetricsPango::GetTextDimensions(const PRUnichar* aString, + PRUint32 aLength, + nsTextDimensions& aDimensions, + PRInt32* aFontID) +{ + nsresult rv = NS_OK; + + PangoLayout *layout = pango_layout_new(mPangoContext); + + gchar *text = g_utf16_to_utf8(aString, aLength, + NULL, NULL, NULL); + + if (!text) { +#ifdef DEBUG + NS_WARNING("nsFontMetricsPango::GetTextDimensions invalid unicode to follow"); + DUMP_PRUNICHAR(aString, aLength) +#endif + aDimensions.width = 0; + aDimensions.ascent = 0; + aDimensions.descent = 0; + + rv = NS_ERROR_FAILURE; + goto loser; + } + + + pango_layout_set_text(layout, text, strlen(text)); + + // Get the logical extents + PangoLayoutLine *line; + if (pango_layout_get_line_count(layout) != 1) { + printf("Warning: more than one line!\n"); + } + line = pango_layout_get_line(layout, 0); + + PangoRectangle rect; + pango_layout_line_get_extents(line, NULL, &rect); + + float P2T; + P2T = mDeviceContext->DevUnitsToAppUnits(); + + aDimensions.width = NSToCoordRound(rect.width / PANGO_SCALE * P2T); + aDimensions.ascent = NSToCoordRound(PANGO_ASCENT(rect) / PANGO_SCALE * P2T); + aDimensions.descent = NSToCoordRound(PANGO_DESCENT(rect) / PANGO_SCALE * P2T); + + // printf("GetTextDimensions %d %d %d\n", aDimensions.width, + //aDimensions.ascent, aDimensions.descent); + + loser: + g_free(text); + g_object_unref(layout); + + return rv; +} + +nsresult +nsFontMetricsPango::GetTextDimensions(const char* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) +{ + + return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks, + aNumBreaks, aDimensions, aNumCharsFit, + aLastWordDimensions); + +} + +nsresult +nsFontMetricsPango::GetTextDimensions(const PRUnichar* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) +{ + nsresult rv = NS_OK; + PRInt32 curBreak = 0; + gchar *curChar; + + PRInt32 *utf8Breaks = new PRInt32[aNumBreaks]; + + gchar *text = g_utf16_to_utf8(aString, (PRInt32)aLength, + NULL, NULL, NULL); + + curChar = text; + + if (!text) { +#ifdef DEBUG + NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow"); + DUMP_PRUNICHAR(aString, (PRUint32)aLength) +#endif + rv = NS_ERROR_FAILURE; + goto loser; + } + + // Covert the utf16 break offsets to utf8 break offsets + for (PRInt32 curOffset=0; curOffset < aLength; + curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) { + if (aBreaks[curBreak] == curOffset) { + utf8Breaks[curBreak] = curChar - text; + curBreak++; + } + + if (IS_HIGH_SURROGATE(aString[curOffset])) + curOffset++; + } + + // Always catch the last break + utf8Breaks[curBreak] = curChar - text; + +#if 0 + if (strlen(text) != aLength) { + printf("Different lengths for utf16 %d and utf8 %d\n", aLength, strlen(text)); + DUMP_PRUNICHAR(aString, aLength) + DUMP_PRUNICHAR(text, strlen(text)) + for (PRInt32 i = 0; i < aNumBreaks; ++i) { + printf(" break %d utf16 %d utf8 %d\n", i, aBreaks[i], utf8Breaks[i]); + } + } +#endif + + // We'll use curBreak to indicate which of the breaks end up being + // used for the break point for this line. + curBreak = 0; + rv = GetTextDimensionsInternal(text, strlen(text), aAvailWidth, utf8Breaks, + aNumBreaks, aDimensions, aNumCharsFit, + aLastWordDimensions); + + // Figure out which of the breaks we ended up using to convert + // back to utf16 - start from the end. + for (PRInt32 i = aNumBreaks - 1; i >= 0; --i) { + if (utf8Breaks[i] == aNumCharsFit) { + // if (aNumCharsFit != aBreaks[i]) + // printf("Fixing utf8 -> utf16 %d -> %d\n", aNumCharsFit, aBreaks[i]); + aNumCharsFit = aBreaks[i]; + break; + } + } + + loser: + if (text) + g_free(text); + + delete[] utf8Breaks; + + return rv; +} + +nsresult +nsFontMetricsPango::DrawString(const char *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + const nscoord* aSpacing, + nsThebesRenderingContext *aContext) +{ + PangoLayout *layout = pango_layout_new(mPangoContext); + + pango_layout_set_text(layout, aString, aLength); + + PangoLayoutLine *line; + if (pango_layout_get_line_count(layout) != 1) { + printf("Warning: more than one line!\n"); + } + line = pango_layout_get_line(layout, 0); + + DrawStringSlowly(aString, NULL, aLength, aContext, + aX, aY, line, aSpacing); + + g_object_unref(layout); + + // printf("DrawString (char *)\n"); + + return NS_OK; +} + +nsresult +nsFontMetricsPango::DrawString(const PRUnichar* aString, PRUint32 aLength, + nscoord aX, nscoord aY, + PRInt32 aFontID, + const nscoord* aSpacing, + nsThebesRenderingContext *aContext) +{ + nsresult rv = NS_OK; + + PangoLayout *layout = pango_layout_new(mPangoContext); + + gchar *text = g_utf16_to_utf8(aString, aLength, + NULL, NULL, NULL); + + if (!text) { +#ifdef DEBUG + NS_WARNING("nsFontMetricsPango::DrawString invalid unicode to follow"); + DUMP_PRUNICHAR(aString, aLength) +#endif + rv = NS_ERROR_FAILURE; + goto loser; + } + + pango_layout_set_text(layout, text, strlen(text)); + + PangoLayoutLine *line; + if (pango_layout_get_line_count(layout) != 1) { + printf("Warning: more than one line!\n"); + } + line = pango_layout_get_line(layout, 0); + + DrawStringSlowly(text, aString, aLength, aContext, + aX, aY, line, aSpacing); + + loser: + + g_free(text); + g_object_unref(layout); + + // printf("DrawString\n"); + + return rv; +} + +#ifdef MOZ_MATHML +nsresult +nsFontMetricsPango::GetBoundingMetrics(const char *aString, PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics) +{ + printf("GetBoundingMetrics (char *)\n"); + return NS_ERROR_FAILURE; +} + +nsresult +nsFontMetricsPango::GetBoundingMetrics(const PRUnichar *aString, + PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics, + PRInt32 *aFontID) +{ + nsresult rv = NS_OK; + PangoLayout *layout = pango_layout_new(mPangoContext); + + gchar *text = g_utf16_to_utf8(aString, aLength, + NULL, NULL, NULL); + + if (!text) { +#ifdef DEBUG + NS_WARNING("nsFontMetricsPango::GetBoundingMetrics invalid unicode to follow"); + DUMP_PRUNICHAR(aString, aLength) +#endif + aBoundingMetrics.leftBearing = 0; + aBoundingMetrics.rightBearing = 0; + aBoundingMetrics.width = 0; + aBoundingMetrics.ascent = 0; + aBoundingMetrics.descent = 0; + + rv = NS_ERROR_FAILURE; + goto loser; + } + + pango_layout_set_text(layout, text, strlen(text)); + + // Get the logical extents + PangoLayoutLine *line; + if (pango_layout_get_line_count(layout) != 1) { + printf("Warning: more than one line!\n"); + } + line = pango_layout_get_line(layout, 0); + + // Get the ink extents + PangoRectangle rect; + pango_layout_line_get_extents(line, NULL, &rect); + + float P2T; + P2T = mDeviceContext->DevUnitsToAppUnits(); + + aBoundingMetrics.leftBearing = + NSToCoordRound(rect.x / PANGO_SCALE * P2T); + aBoundingMetrics.rightBearing = + NSToCoordRound(rect.width / PANGO_SCALE * P2T); + aBoundingMetrics.width = NSToCoordRound((rect.x + rect.width) / PANGO_SCALE * P2T); + aBoundingMetrics.ascent = NSToCoordRound(rect.y / PANGO_SCALE * P2T); + aBoundingMetrics.descent = NSToCoordRound(rect.height / PANGO_SCALE * P2T); + + loser: + g_free(text); + g_object_unref(layout); + + return rv; +} + +#endif /* MOZ_MATHML */ + +nsresult +nsFontMetricsPango::SetRightToLeftText(PRBool aIsRTL) +{ + if (aIsRTL) { + if (!mRTLPangoContext) { + mRTLPangoContext = pango_xft_get_context(GDK_DISPLAY(), 0); + pango_context_set_base_dir(mRTLPangoContext, PANGO_DIRECTION_RTL); + + gdk_pango_context_set_colormap(mRTLPangoContext, gdk_rgb_get_cmap()); + pango_context_set_language(mRTLPangoContext, GetPangoLanguage(mLangGroup)); + pango_context_set_font_description(mRTLPangoContext, mPangoFontDesc); + } + mPangoContext = mRTLPangoContext; + } + else { + mPangoContext = mLTRPangoContext; + } + + mIsRTL = aIsRTL; + return NS_OK; +} + +/* static */ +PRUint32 +nsFontMetricsPango::GetHints(void) +{ + return (NS_RENDERING_HINT_BIDI_REORDERING | + NS_RENDERING_HINT_ARABIC_SHAPING | + NS_RENDERING_HINT_FAST_MEASURE | + NS_RENDERING_HINT_REORDER_SPACED_TEXT); +} + +/* static */ +nsresult +nsFontMetricsPango::FamilyExists(nsIDeviceContext *aDevice, + const nsString &aName) +{ + if (!IsASCIIFontName(aName)) + return NS_ERROR_FAILURE; + + NS_ConvertUCS2toUTF8 name(aName); + + nsresult rv = NS_ERROR_FAILURE; + PangoContext *context = pango_xft_get_context(GDK_DISPLAY(), 0); + PangoFontFamily **familyList; + int n; + + pango_context_list_families(context, &familyList, &n); + + for (int i=0; i < n; i++) { + const char *tmpname = pango_font_family_get_name(familyList[i]); + if (!Compare(nsDependentCString(tmpname), name, + nsCaseInsensitiveCStringComparator())) { + rv = NS_OK; + break; + } + } + + g_free(familyList); + g_object_unref(context); + + return rv; +} + +// Private Methods + +nsresult +nsFontMetricsPango::RealizeFont(void) +{ + nsCString familyList; + // Create and fill out the font description. + mPangoFontDesc = pango_font_description_new(); + + // Add CSS names - walk the list of fonts, adding the generic as + // the last font + for (int i=0; i < mFontList.Count(); ++i) { + // if this was a generic name, break out of the loop since we + // don't want to add it to the pattern yet + if (mFontIsGeneric[i]) + break;; + + nsCString *familyName = mFontList.CStringAt(i); + familyList.Append(familyName->get()); + familyList.Append(','); + } + + // If there's a generic add a pref for the generic if there's one + // set. + if (mGenericFont && !mFont.systemFont) { + nsCString name; + name += "font.name."; + name += mGenericFont->get(); + name += "."; + + nsString langGroup; + mLangGroup->ToString(langGroup); + + name.AppendWithConversion(langGroup); + + nsCOMPtr pref; + pref = do_GetService(NS_PREF_CONTRACTID); + if (pref) { + nsresult rv; + nsXPIDLCString value; + rv = pref->GetCharPref(name.get(), getter_Copies(value)); + + // we ignore prefs that have three hypens since they are X + // style prefs. + if (FFRECountHyphens(value) < 3) { + nsCString tmpstr; + tmpstr.Append(value); + + familyList.Append(tmpstr); + familyList.Append(','); + } + } + } + + // Add the generic if there is one. + if (mGenericFont && !mFont.systemFont) { + familyList.Append(mGenericFont->get()); + familyList.Append(','); + } + + // Set the family + pango_font_description_set_family(mPangoFontDesc, + familyList.get()); + + // Set the point size + + // this will take the DPI from the server, which is almost + // never what we want +#if 0 + pango_font_description_set_size(mPangoFontDesc, + (gint)(mPointSize * PANGO_SCALE)); +#endif + // XXX query the system for the dpi value + pango_font_description_set_absolute_size(mPangoFontDesc, + (96.0/72.0) * mPointSize * PANGO_SCALE); + + // Set the style + pango_font_description_set_style(mPangoFontDesc, + CalculateStyle(mFont.style)); + + // Set the weight + pango_font_description_set_weight(mPangoFontDesc, + CalculateWeight(mFont.weight)); + + // Now that we have the font description set up, create the + // context. + mLTRPangoContext = pango_xft_get_context(GDK_DISPLAY(), 0); + mPangoContext = mLTRPangoContext; + + // Set the color map so we can draw later. + gdk_pango_context_set_colormap(mPangoContext, gdk_rgb_get_cmap()); + + // Set the pango language now that we have a context + pango_context_set_language(mPangoContext, GetPangoLanguage(mLangGroup)); + + // And attach the font description to this context + pango_context_set_font_description(mPangoContext, mPangoFontDesc); + + return NS_OK; +} + +/* static */ +PRBool +nsFontMetricsPango::EnumFontCallback(const nsString &aFamily, + PRBool aIsGeneric, void *aData) +{ + // make sure it's an ascii name, if not then return and continue + // enumerating + if (!IsASCIIFontName(aFamily)) + return PR_TRUE; + + nsCAutoString name; + name.AssignWithConversion(aFamily.get()); + ToLowerCase(name); + nsFontMetricsPango *metrics = (nsFontMetricsPango *)aData; + metrics->mFontList.AppendCString(name); + metrics->mFontIsGeneric.AppendElement((void *)aIsGeneric); + if (aIsGeneric) { + metrics->mGenericFont = + metrics->mFontList.CStringAt(metrics->mFontList.Count() - 1); + return PR_FALSE; // stop processing + } + + return PR_TRUE; // keep processing +} + +static void DrawCairoGlyphs(cairo_t* aCairo, PangoFont* aFont, nscoord aX, nscoord aY, + PangoGlyphString* aGlyphs, float aP2T) +{ + PangoFcFont* fcfont = PANGO_FC_FONT(aFont); + cairo_font_face_t* font = cairo_ft_font_face_create_for_pattern(fcfont->font_pattern); + cairo_set_font_face(aCairo, font); + + int size; + if (FcPatternGetInteger(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != + FcResultMatch) { + size = 12; + } + cairo_set_font_size(aCairo, size); + + cairo_glyph_t autoGlyphs[AUTO_GLYPHBUF_SIZE]; + cairo_glyph_t* glyphs = autoGlyphs; + if (aGlyphs->num_glyphs > AUTO_GLYPHBUF_SIZE) { + glyphs = new cairo_glyph_t[aGlyphs->num_glyphs]; + } + + PangoGlyphUnit offset = 0; + for (gint i = 0; i < aGlyphs->num_glyphs; ++i) { + PangoGlyphInfo* info = &aGlyphs->glyphs[i]; + glyphs[i].index = info->glyph; + glyphs[i].x = (aX/aP2T) + (offset + info->geometry.x_offset)/PANGO_SCALE; + glyphs[i].y = (aY/aP2T) + (info->geometry.y_offset)/PANGO_SCALE; + offset += info->geometry.width; + } + cairo_show_glyphs(aCairo, glyphs, aGlyphs->num_glyphs); + + if (aGlyphs->num_glyphs > AUTO_GLYPHBUF_SIZE) { + delete[] glyphs; + } + cairo_font_face_destroy(font); +} + +/* + * This is only used when there's per-character spacing happening. + * Well, really it can be either line or character spacing but it's + * just turtles all the way down! + */ + +void +nsFontMetricsPango::DrawStringSlowly(const gchar *aText, + const PRUnichar *aOrigString, + PRUint32 aLength, + nsThebesRenderingContext* aContext, + nscoord aX, nscoord aY, + PangoLayoutLine *aLine, + const nscoord *aSpacing) +{ + gint offset = 0; + float p2t = mDeviceContext->DevUnitsToAppUnits(); + + /* + * We walk the list of glyphs returned in each layout run, + * matching up the glyphs with the characters in the source text. + * We use the aSpacing argument to figure out where to place those + * glyphs. It's important to note that since the string we're + * working with is in UTF-8 while the spacing argument assumes + * that offset will be part of the UTF-16 string. Logical + * attributes in pango are in byte offsets in the UTF-8 string, so + * we need to store the offsets based on the UTF-8 string. + */ + nscoord *utf8spacing = nsnull; + + if (aSpacing) { + utf8spacing = new nscoord[strlen(aText)]; + + if (aOrigString) { + const gchar *curChar = aText; + bzero(utf8spacing, sizeof(nscoord) * strlen(aText)); + + // Covert the utf16 spacing offsets to utf8 spacing offsets + for (PRUint32 curOffset=0; curOffset < aLength; + curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) { + utf8spacing[curChar - aText] = (int)((float)aSpacing[curOffset] / p2t * PANGO_SCALE); + + if (IS_HIGH_SURROGATE(aOrigString[curOffset])) + curOffset++; + } + } + else { + memcpy(utf8spacing, aSpacing, (sizeof(nscoord *) * aLength)); + } + } + + gint curRun = 0; + + for (GSList *tmpList = aLine->runs; tmpList && tmpList->data; + tmpList = tmpList->next, curRun++) { + PangoLayoutRun *layoutRun = (PangoLayoutRun *)tmpList->data; + gint tmpOffset = 0; + + /* printf(" Rendering run %d: \"%s\"\n", curRun, + &aText[layoutRun->item->offset]); */ + + for (gint i=0; i < layoutRun->glyphs->num_glyphs; i++) { + /* printf("glyph %d offset %d orig width %d new width %d\n", i, + * layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset, + * layoutRun->glyphs->glyphs[i].geometry.width, + * (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset] * app2dev * PANGO_SCALE)); + */ + gint thisOffset; + if (aSpacing) { + thisOffset = (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset]); + layoutRun->glyphs->glyphs[i].geometry.width = thisOffset; + } else { + thisOffset = layoutRun->glyphs->glyphs[i].geometry.width; + } + tmpOffset += thisOffset; + } + + /* printf(" rendering at X coord %d\n", aX + offset); */ + + DrawCairoGlyphs(aContext->Thebes()->GetCairo(), layoutRun->item->analysis.font, + aX + nscoord(offset/PANGO_SCALE), aY, + layoutRun->glyphs, p2t); + + offset += tmpOffset; + } + + delete[] utf8spacing; +} + +nsresult +nsFontMetricsPango::GetTextDimensionsInternal(const gchar* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions) +{ + NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array"); + + // If we need to back up this state represents the last place + // we could break. We can use this to avoid remeasuring text + PRInt32 prevBreakState_BreakIndex = -1; // not known + // (hasn't been computed) + nscoord prevBreakState_Width = 0; // accumulated width to this point + + // Initialize OUT parameters + GetMaxAscent(aLastWordDimensions.ascent); + GetMaxDescent(aLastWordDimensions.descent); + aLastWordDimensions.width = -1; + aNumCharsFit = 0; + + // Iterate each character in the string and determine which font to use + nscoord width = 0; + PRInt32 start = 0; + nscoord aveCharWidth; + GetAveCharWidth(aveCharWidth); + + while (start < aLength) { + // Estimate how many characters will fit. Do that by + // diving the available space by the average character + // width. Make sure the estimated number of characters is + // at least 1 + PRInt32 estimatedNumChars = 0; + + if (aveCharWidth > 0) + estimatedNumChars = (aAvailWidth - width) / aveCharWidth; + + if (estimatedNumChars < 1) + estimatedNumChars = 1; + + // Find the nearest break offset + PRInt32 estimatedBreakOffset = start + estimatedNumChars; + PRInt32 breakIndex; + nscoord numChars; + + // Find the nearest place to break that is less than or equal to + // the estimated break offset + if (aLength <= estimatedBreakOffset) { + // All the characters should fit + numChars = aLength - start; + breakIndex = aNumBreaks - 1; + } + else { + breakIndex = prevBreakState_BreakIndex; + while (((breakIndex + 1) < aNumBreaks) && + (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) { + ++breakIndex; + } + + if (breakIndex == prevBreakState_BreakIndex) { + ++breakIndex; // make sure we advanced past the + // previous break index + } + + numChars = aBreaks[breakIndex] - start; + } + + // Measure the text + nscoord twWidth = 0; + if ((1 == numChars) && (aString[start] == ' ')) + GetSpaceWidth(twWidth); + else if (numChars > 0) + GetWidth(&aString[start], numChars, twWidth); + + // See if the text fits + PRBool textFits = (twWidth + width) <= aAvailWidth; + + // If the text fits then update the width and the number of + // characters that fit + if (textFits) { + aNumCharsFit += numChars; + width += twWidth; + start += numChars; + + // This is a good spot to back up to if we need to so remember + // this state + prevBreakState_BreakIndex = breakIndex; + prevBreakState_Width = width; + } + else { + // See if we can just back up to the previous saved + // state and not have to measure any text + if (prevBreakState_BreakIndex > 0) { + // If the previous break index is just before the + // current break index then we can use it + if (prevBreakState_BreakIndex == (breakIndex - 1)) { + aNumCharsFit = aBreaks[prevBreakState_BreakIndex]; + width = prevBreakState_Width; + break; + } + } + + // We can't just revert to the previous break state + if (0 == breakIndex) { + // There's no place to back up to, so even though + // the text doesn't fit return it anyway + aNumCharsFit += numChars; + width += twWidth; + break; + } + + // Repeatedly back up until we get to where the text + // fits or we're all the way back to the first word + width += twWidth; + while ((breakIndex >= 1) && (width > aAvailWidth)) { + twWidth = 0; + start = aBreaks[breakIndex - 1]; + numChars = aBreaks[breakIndex] - start; + + if ((1 == numChars) && (aString[start] == ' ')) + GetSpaceWidth(twWidth); + else if (numChars > 0) + GetWidth(&aString[start], numChars, twWidth); + width -= twWidth; + aNumCharsFit = start; + breakIndex--; + } + break; + } + } + + aDimensions.width = width; + GetMaxAscent(aDimensions.ascent); + GetMaxDescent(aDimensions.descent); + + /* printf("aDimensions %d %d %d aLastWordDimensions %d %d %d aNumCharsFit %d\n", + aDimensions.width, aDimensions.ascent, aDimensions.descent, + aLastWordDimensions.width, aLastWordDimensions.ascent, aLastWordDimensions.descent, + aNumCharsFit); */ + + return NS_OK; +} + +/* static */ +PRBool +IsASCIIFontName(const nsString& aName) +{ + PRUint32 len = aName.Length(); + const PRUnichar* str = aName.get(); + for (PRUint32 i = 0; i < len; i++) { + /* + * X font names are printable ASCII, ignore others (for now) + */ + if ((str[i] < 0x20) || (str[i] > 0x7E)) { + return PR_FALSE; + } + } + + return PR_TRUE; +} + +/* static */ +int +FFRECountHyphens (nsACString &aFFREName) +{ + int h = 0; + PRInt32 hyphen = 0; + while ((hyphen = aFFREName.FindChar('-', hyphen)) >= 0) { + ++h; + ++hyphen; + } + return h; +} + +/* static */ +PangoLanguage * +GetPangoLanguage(nsIAtom *aLangGroup) +{ + // Find the FC lang group for this lang group + nsCAutoString cname; + aLangGroup->ToUTF8String(cname); + + // see if the lang group needs to be translated from mozilla's + // internal mapping into fontconfig's + const struct MozPangoLangGroup *langGroup; + langGroup = FindPangoLangGroup(cname); + + // if there's no lang group, just use the lang group as it was + // passed to us + // + // we're casting away the const here for the strings - should be + // safe. + if (!langGroup) + return pango_language_from_string(cname.get()); + else if (langGroup->PangoLang) + return pango_language_from_string(langGroup->PangoLang); + + return pango_language_from_string("en"); +} + +/* static */ +const MozPangoLangGroup* +FindPangoLangGroup (nsACString &aLangGroup) +{ + for (unsigned int i=0; i < NUM_PANGO_LANG_GROUPS; ++i) { + if (aLangGroup.Equals(MozPangoLangGroups[i].mozLangGroup, + nsCaseInsensitiveCStringComparator())) { + return &MozPangoLangGroups[i]; + } + } + + return nsnull; +} + +/* static */ +void +FreeGlobals(void) +{ +} + +/* static */ +PangoStyle +CalculateStyle(PRUint8 aStyle) +{ + switch(aStyle) { + case NS_FONT_STYLE_ITALIC: + return PANGO_STYLE_OBLIQUE; + break; + case NS_FONT_STYLE_OBLIQUE: + return PANGO_STYLE_OBLIQUE; + break; + } + + return PANGO_STYLE_NORMAL; +} + +/* static */ +PangoWeight +CalculateWeight (PRUint16 aWeight) +{ + /* + * weights come in two parts crammed into one + * integer -- the "base" weight is weight / 100, + * the rest of the value is the "offset" from that + * weight -- the number of steps to move to adjust + * the weight in the list of supported font weights, + * this value can be negative or positive. + */ + PRInt32 baseWeight = (aWeight + 50) / 100; + PRInt32 offset = aWeight - baseWeight * 100; + + /* clip weights to range 0 to 9 */ + if (baseWeight < 0) + baseWeight = 0; + if (baseWeight > 9) + baseWeight = 9; + + /* Map from weight value to fcWeights index */ + static int fcWeightLookup[10] = { + 0, 0, 0, 0, 1, 1, 2, 3, 3, 4, + }; + + PRInt32 fcWeight = fcWeightLookup[baseWeight]; + + /* + * adjust by the offset value, make sure we stay inside the + * fcWeights table + */ + fcWeight += offset; + + if (fcWeight < 0) + fcWeight = 0; + if (fcWeight > 4) + fcWeight = 4; + + /* Map to final PANGO_WEIGHT value */ + static int fcWeights[5] = { + 349, + 499, + 649, + 749, + 999 + }; + + return (PangoWeight)fcWeights[fcWeight]; +} + +/* static */ +nsresult +EnumFontsPango(nsIAtom* aLangGroup, const char* aGeneric, + PRUint32* aCount, PRUnichar*** aResult) +{ + FcPattern *pat = NULL; + FcObjectSet *os = NULL; + FcFontSet *fs = NULL; + nsresult rv = NS_ERROR_FAILURE; + + PRUnichar **array = NULL; + PRUint32 narray = 0; + PRInt32 serif = 0, sansSerif = 0, monospace = 0, nGenerics; + + *aCount = 0; + *aResult = nsnull; + + pat = FcPatternCreate(); + if (!pat) + goto end; + + os = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, 0); + if (!os) + goto end; + + // take the pattern and add the lang group to it + if (aLangGroup) + NS_AddLangGroup(pat, aLangGroup); + + // get the font list + fs = FcFontList(0, pat, os); + + if (!fs) + goto end; + + if (!fs->nfont) { + rv = NS_OK; + goto end; + } + + // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and + // "monospace", slightly different from CSS's 5. + if (!aGeneric) + serif = sansSerif = monospace = 1; + else if (!strcmp(aGeneric, "serif")) + serif = 1; + else if (!strcmp(aGeneric, "sans-serif")) + sansSerif = 1; + else if (!strcmp(aGeneric, "monospace")) + monospace = 1; + else if (!strcmp(aGeneric, "cursive") || !strcmp(aGeneric, "fantasy")) + serif = sansSerif = 1; + else + NS_NOTREACHED("unexpected generic family"); + nGenerics = serif + sansSerif + monospace; + + array = NS_STATIC_CAST(PRUnichar **, + nsMemory::Alloc((fs->nfont + nGenerics) * sizeof(PRUnichar *))); + if (!array) + goto end; + + if (serif) { + PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("serif")); + if (!name) + goto end; + array[narray++] = name; + } + + if (sansSerif) { + PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("sans-serif")); + if (!name) + goto end; + array[narray++] = name; + } + + if (monospace) { + PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("monospace")); + if (!name) + goto end; + array[narray++] = name; + } + + for (int i=0; i < fs->nfont; ++i) { + char *family; + PRUnichar *name; + + // if there's no family, just move to the next iteration + if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0, + (FcChar8 **) &family) != FcResultMatch) { + continue; + } + + name = NS_STATIC_CAST(PRUnichar *, + nsMemory::Alloc ((strlen (family) + 1) + * sizeof (PRUnichar))); + + if (!name) + goto end; + + PRUnichar *r = name; + for (char *f = family; *f; ++f) + *r++ = *f; + *r = '\0'; + + array[narray++] = name; + } + + NS_QuickSort(array + nGenerics, narray - nGenerics, sizeof (PRUnichar*), + CompareFontNames, nsnull); + + *aCount = narray; + if (narray) + *aResult = array; + else + nsMemory::Free(array); + + rv = NS_OK; + + end: + if (NS_FAILED(rv) && array) { + while (narray) + nsMemory::Free (array[--narray]); + nsMemory::Free (array); + } + if (pat) + FcPatternDestroy(pat); + if (os) + FcObjectSetDestroy(os); + if (fs) + FcFontSetDestroy(fs); + + return rv; +} + +/* static */ +int +CompareFontNames (const void* aArg1, const void* aArg2, void* aClosure) +{ + const PRUnichar* str1 = *((const PRUnichar**) aArg1); + const PRUnichar* str2 = *((const PRUnichar**) aArg2); + + return nsCRT::strcmp(str1, str2); +} + + +// nsFontEnumeratorPango class + +nsFontEnumeratorPango::nsFontEnumeratorPango() +{ +} + +NS_IMPL_ISUPPORTS1(nsFontEnumeratorPango, nsIFontEnumerator) + +NS_IMETHODIMP +nsFontEnumeratorPango::EnumerateAllFonts(PRUint32 *aCount, + PRUnichar ***aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = nsnull; + NS_ENSURE_ARG_POINTER(aCount); + *aCount = 0; + + return EnumFontsPango(nsnull, nsnull, aCount, aResult); +} + +NS_IMETHODIMP +nsFontEnumeratorPango::EnumerateFonts(const char *aLangGroup, + const char *aGeneric, + PRUint32 *aCount, + PRUnichar ***aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = nsnull; + NS_ENSURE_ARG_POINTER(aCount); + *aCount = 0; + + // aLangGroup=null or "" means any (i.e., don't care) + // aGeneric=null or "" means any (i.e, don't care) + nsCOMPtr langGroup; + if (aLangGroup && *aLangGroup) + langGroup = do_GetAtom(aLangGroup); + const char* generic = nsnull; + if (aGeneric && *aGeneric) + generic = aGeneric; + + return EnumFontsPango(langGroup, generic, aCount, aResult); +} + +NS_IMETHODIMP +nsFontEnumeratorPango::HaveFontFor(const char *aLangGroup, + PRBool *aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = PR_FALSE; + NS_ENSURE_ARG_POINTER(aLangGroup); + + *aResult = PR_TRUE; // always return true for now. + // Finish me - ftang + return NS_OK; +} + +NS_IMETHODIMP +nsFontEnumeratorPango::GetDefaultFont(const char *aLangGroup, + const char *aGeneric, + PRUnichar **aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + *aResult = nsnull; + + // Have a look at nsFontEnumeratorXft::GetDefaultFont for some + // possible code for this function. + + return NS_OK; +} + +NS_IMETHODIMP +nsFontEnumeratorPango::UpdateFontList(PRBool *_retval) +{ + *_retval = PR_FALSE; // always return false for now + return NS_OK; +} diff --git a/gfx/src/cairo/Makefile.in b/gfx/src/thebes/nsFontMetricsPango.h similarity index 100% rename from gfx/src/cairo/Makefile.in rename to gfx/src/thebes/nsFontMetricsPango.h diff --git a/gfx/src/cairo/nsFontMetricsUtils.cpp b/gfx/src/thebes/nsFontMetricsUtils.cpp similarity index 100% rename from gfx/src/cairo/nsFontMetricsUtils.cpp rename to gfx/src/thebes/nsFontMetricsUtils.cpp diff --git a/gfx/src/cairo/nsFontMetricsUtils.h b/gfx/src/thebes/nsFontMetricsUtils.h similarity index 100% rename from gfx/src/cairo/nsFontMetricsUtils.h rename to gfx/src/thebes/nsFontMetricsUtils.h diff --git a/gfx/src/thebes/nsFontMetricsWin2.cpp b/gfx/src/thebes/nsFontMetricsWin2.cpp new file mode 100644 index 00000000000..8c4ce98b1d7 --- /dev/null +++ b/gfx/src/thebes/nsFontMetricsWin2.cpp @@ -0,0 +1,829 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsFontMetricsWin2.h" +#include "nsFont.h" + +#include "nsString.h" +#include "nsAutoBuffer.h" +#include +#include +#include + +#include "cairo-win32.h" + + +// for using the cairo apis for fonts... +#define USE_SCALED_FONTS + + +NS_IMPL_ISUPPORTS1(nsFontMetricsWin, nsIFontMetrics) + +#include + +nsFontMetricsWin::nsFontMetricsWin() +{ + _putenv("XPCOM_DEBUG_BREAK=warn"); +} + +nsFontMetricsWin::~nsFontMetricsWin() +{ + cairo_font_face_destroy(mCairoFontFace); + cairo_scaled_font_destroy(mCairoFont); +} + +static PRBool +FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData) +{ + nsFontMetricsWin* metrics = (nsFontMetricsWin*) aData; + metrics->mFonts.AppendString(aFamily); + if (aGeneric) { + metrics->mGeneric.Assign(aFamily); + //ToLowerCase(metrics->mGeneric); + return PR_FALSE; // stop + } + ++metrics->mGenericIndex; + + return PR_TRUE; // don't stop +} + +cairo_font_face_t * +nsFontMetricsWin::MakeCairoFontFace(const nsString *aFontName) +{ + cairo_font_face_t *fontFace = nsnull; + + LOGFONTW logFont; + memset(&logFont, 0, sizeof(LOGFONTW)); + FillLogFont(&logFont, aFontName); + + fontFace = cairo_win32_font_face_create_for_logfontw(&logFont); + + return fontFace; +} +cairo_scaled_font_t * +nsFontMetricsWin::MakeCairoScaledFont(cairo_t *cr, cairo_font_face_t *aFontFace) +{ + cairo_scaled_font_t *font = nsnull; + + float app2dev = mDeviceContext->AppUnitsToDevUnits(); + double size = NSToIntRound(mFont.size * app2dev); + cairo_matrix_t sizeMatrix, ctm; + if (cr) + cairo_get_matrix(cr, &ctm); + else + cairo_matrix_init_identity(&ctm); + + cairo_matrix_init_scale(&sizeMatrix, size, size); + + cairo_font_options_t *fontOptions = cairo_font_options_create(); + font = cairo_scaled_font_create(aFontFace, &sizeMatrix, &ctm, fontOptions); + cairo_font_options_destroy(fontOptions); + + return font; +} + +NS_IMETHODIMP +nsFontMetricsWin::Init(const nsFont& aFont, nsIAtom* aLangGroup, + nsIDeviceContext *aContext) +{ + mFont = aFont; + mLangGroup = aLangGroup; + mDeviceContext = (nsThebesDeviceContext*)aContext; + mDev2App = aContext->DevUnitsToAppUnits(); + + // set up the first font in the list as the default, and we'll go from there. + mFont.EnumerateFamilies(FontEnumCallback, this); + + mCairoFontFace = MakeCairoFontFace(mFonts[0]); + mCairoFont = MakeCairoScaledFont(nsnull, mCairoFontFace); + + HWND win = (HWND)mDeviceContext->GetWidget(); + HDC dc = ::GetDC(win); + + RealizeFont(dc); + + ::ReleaseDC(win, dc); + + return NS_OK; +} + + +#define CLIP_TURNOFF_FONTASSOCIATION 0x40 +void +nsFontMetricsWin::FillLogFont(LOGFONTW* logFont, const nsString *aFontName) +{ + float app2dev = mDeviceContext->AppUnitsToDevUnits(); + logFont->lfHeight = - NSToIntRound(mFont.size * app2dev); + + if (logFont->lfHeight == 0) { + logFont->lfHeight = -1; + } + + // Fill in logFont structure + logFont->lfWidth = 0; + logFont->lfEscapement = 0; + logFont->lfOrientation = 0; + logFont->lfUnderline = + (mFont.decorations & NS_FONT_DECORATION_UNDERLINE) + ? TRUE : FALSE; + logFont->lfStrikeOut = + (mFont.decorations & NS_FONT_DECORATION_LINE_THROUGH) + ? TRUE : FALSE; + logFont->lfCharSet = DEFAULT_CHARSET; +#ifndef WINCE + logFont->lfOutPrecision = OUT_TT_PRECIS; +#else + logFont->lfOutPrecision = OUT_DEFAULT_PRECIS; +#endif + logFont->lfClipPrecision = CLIP_TURNOFF_FONTASSOCIATION; + logFont->lfQuality = DEFAULT_QUALITY; + logFont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + logFont->lfWeight = mFont.weight; + logFont->lfItalic = (mFont.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) + ? TRUE : FALSE; // XXX need better oblique support + + int len = PR_MIN(aFontName->Length(), LF_FACESIZE); + memcpy(logFont->lfFaceName, aFontName->get(), len * 2); + logFont->lfFaceName[len] = '\0'; +} + + +NS_IMETHODIMP +nsFontMetricsWin::Destroy() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetXHeight(nscoord& aResult) +{ + aResult = mXHeight; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetSuperscriptOffset(nscoord& aResult) +{ + aResult = mSuperscriptOffset; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetSubscriptOffset(nscoord& aResult) +{ + aResult = mSubscriptOffset; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetStrikeout(nscoord& aOffset, nscoord& aSize) +{ + aOffset = mStrikeoutOffset; + aSize = mStrikeoutSize; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetUnderline(nscoord& aOffset, nscoord& aSize) +{ + aOffset = mUnderlineOffset; + aSize = mUnderlineSize; + + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetHeight(nscoord &aHeight) +{ + aHeight = mMaxHeight; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetInternalLeading(nscoord &aLeading) +{ + aLeading = mInternalLeading; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetExternalLeading(nscoord &aLeading) +{ + aLeading = mExternalLeading; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetEmHeight(nscoord &aHeight) +{ + aHeight = mEmHeight; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetEmAscent(nscoord &aAscent) +{ + aAscent = mEmAscent; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetEmDescent(nscoord &aDescent) +{ + aDescent = mEmDescent; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetMaxHeight(nscoord &aHeight) +{ + aHeight = mMaxHeight; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetMaxAscent(nscoord &aAscent) +{ + aAscent = mMaxAscent; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetMaxDescent(nscoord &aDescent) +{ + aDescent = mMaxDescent; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetMaxAdvance(nscoord &aAdvance) +{ + aAdvance = mMaxAdvance; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetLangGroup(nsIAtom** aLangGroup) +{ + *aLangGroup = mLangGroup; + NS_IF_ADDREF(*aLangGroup); + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetFontHandle(nsFontHandle &aHandle) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetAveCharWidth(nscoord& aAveCharWidth) +{ + aAveCharWidth = mAveCharWidth; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetSpaceWidth(nscoord& aSpaceCharWidth) +{ + aSpaceCharWidth = mSpaceWidth; + + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetLeading(nscoord& aLeading) +{ + aLeading = mInternalLeading; + return NS_OK; +} + +NS_IMETHODIMP +nsFontMetricsWin::GetNormalLineHeight(nscoord& aLineHeight) +{ + aLineHeight = mEmHeight + mInternalLeading; + return NS_OK; +} + + + + + +PRInt32 +nsFontMetricsWin::MeasureOrDrawUniscribe(nsThebesRenderingContext *aContext, + const PRUnichar *aString, PRUint32 aLength, + PRBool aDraw, PRInt32 aX, PRInt32 aY, const PRInt32 *aSpacing) +{ + HDC aDC = (HDC)aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_WINDOWS_DC); + + float app2dev = mDeviceContext->AppUnitsToDevUnits(); + + int loops = 0; + + cairo_t *cr = (cairo_t*)aContext->GetNativeGraphicData(nsIRenderingContext::NATIVE_CAIRO_CONTEXT); + + PRBool isComplex = (::ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK); + + PRInt32 length = 0; + HRESULT rv; + int numItems, maxItems = 2; + + SCRIPT_CACHE sc = NULL; + SCRIPT_CONTROL *control = nsnull; + SCRIPT_STATE *state = nsnull; + PRBool rtl = ((::GetTextAlign(aDC) & TA_RTLREADING) == TA_RTLREADING); + if (rtl) { + control = (SCRIPT_CONTROL *)malloc(sizeof(SCRIPT_CONTROL)); + state = (SCRIPT_STATE *)malloc(sizeof(SCRIPT_STATE)); + memset(control, 0, sizeof(control)); + memset(state, 0, sizeof(state)); + control->fNeutralOverride = 1; + state->uBidiLevel = 1; + } + + SCRIPT_ITEM *items = (SCRIPT_ITEM *)malloc(maxItems*sizeof(SCRIPT_ITEM)); + while ((rv = ScriptItemize(aString, aLength, maxItems, control, state, + items, &numItems)) == E_OUTOFMEMORY) { + maxItems *= 2; + items = (SCRIPT_ITEM *)realloc(items, maxItems*sizeof(SCRIPT_ITEM)); + } + + for (int h=0; h 0 && glyphs[0] == 0) { + if (fontIndex < mFonts.Count() - 1) { + fontIndex++; + cairo_win32_scaled_font_done_font(scaledFont); + RestoreDC(aDC, -1); + goto TRY_AGAIN_SAME_SCRIPT; + } + // otherwise we fail to draw the characters so give up and continue on. + } + + if (rv == 0) { + + ABC abc; + GOFFSET *offsets = (GOFFSET *)malloc(numGlyphs*sizeof(GOFFSET)); + int *advance = (int *)malloc(numGlyphs*sizeof(int)); + rv = ScriptPlace(aDC, &sc, glyphs, numGlyphs, attr, &items[i].a, + advance, offsets, &abc); + + if (rv != S_OK) + printf("error ScriptPlacing\n"); +#ifdef DEBUG_tor + fprintf(stderr, "ABC[%d]: %d %d %d\n", i, abc.abcA, abc.abcB, abc.abcC); +#endif + + if (!aDraw) { + length += NSToCoordRound((abc.abcA + abc.abcB + abc.abcC) * cairofontfactor * (mFont.size * app2dev); + } else { + PRInt32 *spacing = 0; + PRInt32 justTotal = 0; + if (aSpacing) { + /* need to correct for layout/gfx spacing mismatch */ +#ifdef DEBUG_tor + fprintf(stderr, "advn: "); + for (int j=0; j gfxTotal) { + spacing = (PRInt32 *)malloc(numGlyphs*sizeof(PRInt32)); + justTotal = layoutTotal - gfxTotal; +#if 0 + ScriptJustify(attr, advance, numGlyphs, justTotal, 1, spacing); +#else + memcpy(spacing, advance, sizeof(PRInt32)*numGlyphs); + + int justOpps = 0; + int lookForOpp = 0; + for (j = 0; j < numGlyphs-1; j++) { + if (attr[j+1].uJustification > 1) { + ++justOpps; + } + } + if (justOpps > 0) { + int eachJust = justTotal / justOpps; + + for (j=0; j 1) { + --justOpps; + if (justOpps == 0) { + spacing[j] += justTotal; + } else { + spacing[j] += eachJust; + justTotal -= eachJust; + } + } + } + } +#endif + +#ifdef DEBUG_tor + fprintf(stderr, "hand: "); + for (j=0; jDevUnitsToAppUnits(); + aWidth = NSToCoordRound(aWidth * f); + +#if 0 +} else { + ::GetTextExtentPoint32W(aDC, aString, aLength, &size); + size.cx -= mOverhangCorrection; + return size.cx; + } +#endif + return NS_OK; + +} + +// Get the text dimensions for this string +nsresult +nsFontMetricsWin::GetTextDimensions(const PRUnichar* aString, + PRUint32 aLength, + nsTextDimensions& aDimensions, + PRInt32* aFontID) +{ + return NS_OK; +} + +nsresult +nsFontMetricsWin::GetTextDimensions(const char* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) +{ + return NS_OK; +} +nsresult +nsFontMetricsWin::GetTextDimensions(const PRUnichar* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) +{ + return NS_OK; +} + +// Draw a string using this font handle on the surface passed in. +nsresult +nsFontMetricsWin::DrawString(const char *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + const nscoord* aSpacing, + nsThebesRenderingContext *aContext) +{ + return DrawString(PromiseFlatString(NS_ConvertUTF8toUTF16(aString, aLength)).get(), + aLength, aX, aY, 0, aSpacing, aContext); +} + +// aCachedOffset will be updated with a new offset. +nsresult +nsFontMetricsWin::DrawString(const PRUnichar* aString, PRUint32 aLength, + nscoord aX, nscoord aY, + PRInt32 aFontID, + const nscoord* aSpacing, + nsThebesRenderingContext *aContext) +{ + float app2dev = mDeviceContext->AppUnitsToDevUnits(); + MeasureOrDrawUniscribe(aContext, aString, aLength, PR_TRUE, + NSToIntRound(aX * app2dev), NSToIntRound(aY * app2dev), aSpacing); + + return NS_OK; +} + + +#ifdef MOZ_MATHML +// These two functions get the bounding metrics for this handle, +// updating the aBoundingMetrics in Points. This means that the +// caller will have to update them to twips before passing it +// back. +nsresult +nsFontMetricsWin::GetBoundingMetrics(const char *aString, PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics) +{ + return NS_OK; +} + +// aCachedOffset will be updated with a new offset. +nsresult +nsFontMetricsWin::GetBoundingMetrics(const PRUnichar *aString, + PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics, + PRInt32 *aFontID) +{ + return NS_OK; +} + +#endif /* MOZ_MATHML */ + +// Set the direction of the text rendering +nsresult +nsFontMetricsWin::SetRightToLeftText(PRBool aIsRTL) +{ + return NS_OK; +} + + + + + + + + + + + + + + + + +void +nsFontMetricsWin::RealizeFont(HDC dc) +{ + float app2dev = mDeviceContext->AppUnitsToDevUnits(); + + SaveDC(dc); + + cairo_win32_scaled_font_select_font(mCairoFont, dc); + + double cairofontfactor = cairo_win32_scaled_font_get_metrics_factor(mCairoFont); + double multiplier = mDev2App * cairofontfactor * NSToIntRound(mFont.size * app2dev); + + // Get font metrics + OUTLINETEXTMETRIC oMetrics; + TEXTMETRIC& metrics = oMetrics.otmTextMetrics; + nscoord onePixel = NSToCoordRound(1 * mDev2App); + nscoord descentPos = 0; + + if (0 < ::GetOutlineTextMetrics(dc, sizeof(oMetrics), &oMetrics)) { + // mXHeight = NSToCoordRound(oMetrics.otmsXHeight * mDev2App); XXX not really supported on windows + mXHeight = NSToCoordRound((float)metrics.tmAscent * multiplier * 0.56f); // 50% of ascent, best guess for true type + mSuperscriptOffset = NSToCoordRound(oMetrics.otmptSuperscriptOffset.y * multiplier); + mSubscriptOffset = NSToCoordRound(oMetrics.otmptSubscriptOffset.y * multiplier); + + mStrikeoutSize = PR_MAX(onePixel, NSToCoordRound(oMetrics.otmsStrikeoutSize * multiplier)); + mStrikeoutOffset = NSToCoordRound(oMetrics.otmsStrikeoutPosition * multiplier); + mUnderlineSize = PR_MAX(onePixel, NSToCoordRound(oMetrics.otmsUnderscoreSize * multiplier)); + + mUnderlineOffset = NSToCoordRound(oMetrics.otmsUnderscorePosition * multiplier); + + // Begin -- section of code to get the real x-height with GetGlyphOutline() + GLYPHMETRICS gm; + MAT2 mMat = { 1, 0, 0, 1 }; // glyph transform matrix (always identity in our context) + DWORD len = GetGlyphOutlineW(dc, PRUnichar('x'), GGO_METRICS, &gm, 0, nsnull, &mMat); + + if (GDI_ERROR != len && gm.gmptGlyphOrigin.y > 0) { + mXHeight = NSToCoordRound(gm.gmptGlyphOrigin.y * multiplier); + } + // End -- getting x-height + } + else { + // Make a best-effort guess at extended metrics + // this is based on general typographic guidelines + ::GetTextMetrics(dc, &metrics); + mXHeight = NSToCoordRound((float)metrics.tmAscent * mDev2App * 0.56f); // 56% of ascent, best guess for non-true type + mSuperscriptOffset = mXHeight; // XXX temporary code! + mSubscriptOffset = mXHeight; // XXX temporary code! + + mStrikeoutSize = onePixel; // XXX this is a guess + mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0f); // 50% of xHeight + mUnderlineSize = onePixel; // XXX this is a guess + mUnderlineOffset = -NSToCoordRound((float)metrics.tmDescent * mDev2App * 0.30f); // 30% of descent + } + + + mInternalLeading = NSToCoordRound(metrics.tmInternalLeading * multiplier); + mExternalLeading = NSToCoordRound(metrics.tmExternalLeading * multiplier); + mEmHeight = NSToCoordRound((metrics.tmHeight - metrics.tmInternalLeading) * + multiplier); + mEmAscent = NSToCoordRound((metrics.tmAscent - metrics.tmInternalLeading) * + multiplier); + mEmDescent = NSToCoordRound(metrics.tmDescent * multiplier); + mMaxHeight = NSToCoordRound(metrics.tmHeight * multiplier); + mMaxAscent = NSToCoordRound(metrics.tmAscent * multiplier); + mMaxDescent = NSToCoordRound(metrics.tmDescent * multiplier); + mMaxAdvance = NSToCoordRound(metrics.tmMaxCharWidth * multiplier); + mAveCharWidth = PR_MAX(1, NSToCoordRound(metrics.tmAveCharWidth * multiplier)); + +#if 0 + // descent position is preferred, but we need to make sure there is enough + // space available. Baseline need to be raised so that underline will stay + // within boundary. + // only do this for CJK to minimize possible risk + if (IsCJKLangGroupAtom(mLangGroup.get())) { + if (gDoingLineheightFixup && + mInternalLeading+mExternalLeading > mUnderlineSize && + descentPos < mUnderlineOffset) { + mEmAscent -= mUnderlineSize; + mEmDescent += mUnderlineSize; + mMaxAscent -= mUnderlineSize; + mMaxDescent += mUnderlineSize; + mUnderlineOffset = descentPos; + } + } +#endif + // Cache the width of a single space. + SIZE size; + ::GetTextExtentPoint32(dc, " ", 1, &size); + //size.cx -= font->mOverhangCorrection; + mSpaceWidth = NSToCoordRound(size.cx * multiplier); + + cairo_win32_scaled_font_done_font(mCairoFont); + + RestoreDC(dc, -1); +} + diff --git a/gfx/src/cairo/nsCairoBlender.cpp b/gfx/src/thebes/nsFontMetricsWin2.h similarity index 100% rename from gfx/src/cairo/nsCairoBlender.cpp rename to gfx/src/thebes/nsFontMetricsWin2.h diff --git a/gfx/src/thebes/nsIThebesFontMetrics.h b/gfx/src/thebes/nsIThebesFontMetrics.h new file mode 100644 index 00000000000..851f231d975 --- /dev/null +++ b/gfx/src/thebes/nsIThebesFontMetrics.h @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Christopher Blizzard . + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nsIThebesFontMetrics_h +#define __nsIThebesFontMetrics_h + +#include "nsIFontMetrics.h" +#include "nsIRenderingContext.h" + +class nsThebesRenderingContext; + +class nsIThebesFontMetrics : public nsIFontMetrics { +public: + // Get the width for this string. aWidth will be updated with the + // width in points, not twips. Callers must convert it if they + // want it in another format. + virtual nsresult GetWidth(const char* aString, PRUint32 aLength, + nscoord& aWidth, nsThebesRenderingContext *aContext) = 0; + // aCachedOffset will be updated with a new offset. + virtual nsresult GetWidth(const PRUnichar* aString, PRUint32 aLength, + nscoord& aWidth, PRInt32 *aFontID, + nsThebesRenderingContext *aContext) = 0; + + // Get the text dimensions for this string + virtual nsresult GetTextDimensions(const PRUnichar* aString, + PRUint32 aLength, + nsTextDimensions& aDimensions, + PRInt32* aFontID) = 0; + virtual nsresult GetTextDimensions(const char* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) = 0; + virtual nsresult GetTextDimensions(const PRUnichar* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) = 0; + + // Draw a string using this font handle on the surface passed in. + virtual nsresult DrawString(const char *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + const nscoord* aSpacing, + nsThebesRenderingContext *aContext) = 0; + // aCachedOffset will be updated with a new offset. + virtual nsresult DrawString(const PRUnichar* aString, PRUint32 aLength, + nscoord aX, nscoord aY, + PRInt32 aFontID, + const nscoord* aSpacing, + nsThebesRenderingContext *aContext) = 0; + +#ifdef MOZ_MATHML + // These two functions get the bounding metrics for this handle, + // updating the aBoundingMetrics in Points. This means that the + // caller will have to update them to twips before passing it + // back. + virtual nsresult GetBoundingMetrics(const char *aString, PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics) = 0; + // aCachedOffset will be updated with a new offset. + virtual nsresult GetBoundingMetrics(const PRUnichar *aString, + PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics, + PRInt32 *aFontID) = 0; +#endif /* MOZ_MATHML */ + + // Set the direction of the text rendering + virtual nsresult SetRightToLeftText(PRBool aIsRTL) = 0; + +}; + +#endif /* __nsIThebesFontMetrics_h */ diff --git a/gfx/src/thebes/nsIThebesRenderingContext.h b/gfx/src/thebes/nsIThebesRenderingContext.h new file mode 100644 index 00000000000..31d1964c058 --- /dev/null +++ b/gfx/src/thebes/nsIThebesRenderingContext.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Christopher Blizzard . + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef __nsIThebesRenderingContext_h +#define __nsIThebesRenderingContext_h + +#include "nsIRenderingContext.h" + +// IID for the nsIRenderingContext interface +#define NSI_THEBES_RENDERING_CONTEXT_IID \ +{ 0x8591c4c6, 0x41d4, 0x485a, \ +{ 0xb2, 0x4f, 0x9d, 0xe1, 0x9b, 0x69, 0xce, 0x02 } } + +class nsIThebesRenderingContext : public nsISupports +{ +public: + NS_DEFINE_STATIC_IID_ACCESSOR(NSI_THEBES_RENDERING_CONTEXT_IID) + + NS_IMETHOD CreateDrawingSurface(nsNativeWidget aWidget, nsIDrawingSurface* &aSurface) = 0; + +}; + +#endif /* __nsIThebesRenderingContext_h */ diff --git a/gfx/src/thebes/nsSystemFontsGTK2.cpp b/gfx/src/thebes/nsSystemFontsGTK2.cpp new file mode 100644 index 00000000000..a7b37ab6e35 --- /dev/null +++ b/gfx/src/thebes/nsSystemFontsGTK2.cpp @@ -0,0 +1,340 @@ + +// for strtod() +#include + +#include "nsIRenderingContext.h" + +#include +#include +#include + +#include +#include +#include + +#include "nsSystemFontsGTK2.h" + +static PRInt32 GetXftDPI(void); +static void AppendFontFFREName(nsString& aString, const char* aXLFDName); + +#define DEFAULT_TWIP_FONT_SIZE 240 + +nsSystemFontsGTK2::nsSystemFontsGTK2(float aPixelsToTwips) + : mDefaultFont("sans-serif", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL, + NS_FONT_WEIGHT_NORMAL, NS_FONT_DECORATION_NONE, + DEFAULT_TWIP_FONT_SIZE), + mButtonFont("sans-serif", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL, + NS_FONT_WEIGHT_NORMAL, NS_FONT_DECORATION_NONE, + DEFAULT_TWIP_FONT_SIZE), + mFieldFont("sans-serif", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL, + NS_FONT_WEIGHT_NORMAL, NS_FONT_DECORATION_NONE, + DEFAULT_TWIP_FONT_SIZE), + mMenuFont("sans-serif", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL, + NS_FONT_WEIGHT_NORMAL, NS_FONT_DECORATION_NONE, + DEFAULT_TWIP_FONT_SIZE) +{ + /* + * Much of the widget creation code here is similar to the code in + * nsLookAndFeel::InitColors(). + */ + + // mDefaultFont + GtkWidget *label = gtk_label_new("M"); + GtkWidget *parent = gtk_fixed_new(); + GtkWidget *window = gtk_window_new(GTK_WINDOW_POPUP); + + gtk_container_add(GTK_CONTAINER(parent), label); + gtk_container_add(GTK_CONTAINER(window), parent); + + gtk_widget_ensure_style(label); + + GetSystemFontInfo(label, &mDefaultFont, aPixelsToTwips); + + gtk_widget_destroy(window); // no unref, windows are different + + // mFieldFont + GtkWidget *entry = gtk_entry_new(); + parent = gtk_fixed_new(); + window = gtk_window_new(GTK_WINDOW_POPUP); + + gtk_container_add(GTK_CONTAINER(parent), entry); + gtk_container_add(GTK_CONTAINER(window), parent); + gtk_widget_ensure_style(entry); + + GetSystemFontInfo(entry, &mFieldFont, aPixelsToTwips); + + gtk_widget_destroy(window); // no unref, windows are different + + // mMenuFont + GtkWidget *accel_label = gtk_accel_label_new("M"); + GtkWidget *menuitem = gtk_menu_item_new(); + GtkWidget *menu = gtk_menu_new(); + gtk_object_ref(GTK_OBJECT(menu)); + gtk_object_sink(GTK_OBJECT(menu)); + + gtk_container_add(GTK_CONTAINER(menuitem), accel_label); + gtk_menu_append(GTK_MENU(menu), menuitem); + + gtk_widget_ensure_style(accel_label); + + GetSystemFontInfo(accel_label, &mMenuFont, aPixelsToTwips); + + gtk_widget_unref(menu); + + // mButtonFont + parent = gtk_fixed_new(); + GtkWidget *button = gtk_button_new(); + label = gtk_label_new("M"); + window = gtk_window_new(GTK_WINDOW_POPUP); + + gtk_container_add(GTK_CONTAINER(button), label); + gtk_container_add(GTK_CONTAINER(parent), button); + gtk_container_add(GTK_CONTAINER(window), parent); + + gtk_widget_ensure_style(label); + + GetSystemFontInfo(label, &mButtonFont, aPixelsToTwips); + + gtk_widget_destroy(window); // no unref, windows are different +} + +#ifdef MOZ_ENABLE_COREXFONTS +static void xlfd_from_pango_font_description(GtkWidget *aWidget, + const PangoFontDescription *aFontDesc, + nsString& aFontName); +#endif /* MOZ_ENABLE_COREXFONTS */ + +nsresult +nsSystemFontsGTK2::GetSystemFontInfo(GtkWidget *aWidget, nsFont* aFont, + float aPixelsToTwips) const +{ + GtkSettings *settings = gtk_widget_get_settings(aWidget); + + aFont->style = NS_FONT_STYLE_NORMAL; + aFont->decorations = NS_FONT_DECORATION_NONE; + + gchar *fontname; + g_object_get(settings, "gtk-font-name", &fontname, NULL); + + PangoFontDescription *desc; + desc = pango_font_description_from_string(fontname); + + aFont->systemFont = PR_TRUE; + + g_free(fontname); + + aFont->name.Truncate(); +#ifdef MOZ_ENABLE_XFT + aFont->name.Assign(PRUnichar('"')); + aFont->name.AppendWithConversion(pango_font_description_get_family(desc)); + aFont->name.Append(PRUnichar('"')); +#endif /* MOZ_ENABLE_XFT */ + +#ifdef MOZ_ENABLE_COREXFONTS + // if name already set by Xft, do nothing + if (!aFont->name.Length()) { + xlfd_from_pango_font_description(aWidget, desc, aFont->name); + } +#endif /* MOZ_ENABLE_COREXFONTS */ + aFont->weight = pango_font_description_get_weight(desc); + + float size = float(pango_font_description_get_size(desc) / PANGO_SCALE); +#ifdef MOZ_ENABLE_XFT + PRInt32 dpi = GetXftDPI(); + if (dpi != 0) { + // pixels/inch * twips/pixel * inches/twip == 1, except it isn't, since + // our idea of dpi may be different from Xft's. + size *= float(dpi) * aPixelsToTwips * (1.0f/1440.0f); + } +#endif /* MOZ_ENABLE_XFT */ + aFont->size = NSFloatPointsToTwips(size); + + pango_font_description_free(desc); + + return NS_OK; +} + +#if defined(MOZ_ENABLE_COREXFONTS) +// xlfd_from_pango_font_description copied from vte, which was +// written by nalin@redhat.com, and added some codes. +static void +xlfd_from_pango_font_description(GtkWidget *aWidget, + const PangoFontDescription *aFontDesc, + nsString& aFontName) +{ + char *spec; + PangoContext *context; + PangoFont *font; + PangoXSubfont *subfont_ids; + PangoFontMap *fontmap; + int *subfont_charsets, i, count = 0; + char *subfont; + char *encodings[] = { + "ascii-0", + "big5-0", + "dos-437", + "dos-737", + "gb18030.2000-0", + "gb18030.2000-1", + "gb2312.1980-0", + "iso8859-1", + "iso8859-2", + "iso8859-3", + "iso8859-4", + "iso8859-5", + "iso8859-7", + "iso8859-8", + "iso8859-9", + "iso8859-10", + "iso8859-15", + "iso10646-0", + "iso10646-1", + "jisx0201.1976-0", + "jisx0208.1983-0", + "jisx0208.1990-0", + "jisx0208.1997-0", + "jisx0212.1990-0", + "jisx0213.2000-1", + "jisx0213.2000-2", + "koi8-r", + "koi8-u", + "koi8-ub", + "ksc5601.1987-0", + "ksc5601.1992-3", + "tis620-0", + "iso8859-13", + "microsoft-cp1251" + "misc-fontspecific", + }; +#if XlibSpecificationRelease >= 6 + XOM xom; +#endif + if (!aFontDesc) { + return; + } + + context = gtk_widget_get_pango_context(GTK_WIDGET(aWidget)); + + pango_context_set_language (context, gtk_get_default_language ()); + fontmap = pango_x_font_map_for_display(GDK_DISPLAY()); + + if (!fontmap) { + return; + } + + font = pango_font_map_load_font(fontmap, context, aFontDesc); + if (!font) { + return; + } + +#if XlibSpecificationRelease >= 6 + xom = XOpenOM (GDK_DISPLAY(), NULL, NULL, NULL); + if (xom) { + XOMCharSetList cslist; + int n_encodings = 0; + cslist.charset_count = 0; + XGetOMValues (xom, + XNRequiredCharSet, &cslist, + NULL); + n_encodings = cslist.charset_count; + if (n_encodings) { + char **xom_encodings = (char**) g_malloc (sizeof(char*) * n_encodings); + + for (i = 0; i < n_encodings; i++) { + xom_encodings[i] = g_ascii_strdown (cslist.charset_list[i], -1); + } + count = pango_x_list_subfonts(font, xom_encodings, n_encodings, + &subfont_ids, &subfont_charsets); + + for(i = 0; i < n_encodings; i++) { + g_free (xom_encodings[i]); + } + g_free (xom_encodings); + } + XCloseOM (xom); + } +#endif + if (count == 0) { + count = pango_x_list_subfonts(font, encodings, G_N_ELEMENTS(encodings), + &subfont_ids, &subfont_charsets); + } + + for (i = 0; i < count; i++) { + subfont = pango_x_font_subfont_xlfd(font, subfont_ids[i]); + AppendFontFFREName(aFontName, subfont); + g_free(subfont); + aFontName.Append(PRUnichar(',')); + } + + spec = pango_font_description_to_string(aFontDesc); + + if (subfont_ids != NULL) { + g_free(subfont_ids); + } + if (subfont_charsets != NULL) { + g_free(subfont_charsets); + } + g_free(spec); +} +#endif /* MOZ_ENABLE_COREXFONTS */ + +#if defined(MOZ_ENABLE_COREXFONTS) || defined(MOZ_WIDGET_GTK) + +#define LOCATE_MINUS(pos, str) { \ + pos = str.FindChar('-'); \ + if (pos < 0) \ + return ; \ + } +#define NEXT_MINUS(pos, str) { \ + pos = str.FindChar('-', pos+1); \ + if (pos < 0) \ + return ; \ + } + +static void +AppendFontFFREName(nsString& aString, const char* aXLFDName) +{ + // convert fontname from XFLD to FFRE and append, ie. from + // -adobe-courier-medium-o-normal--14-140-75-75-m-90-iso8859-15 + // to + // adobe-courier-iso8859-15 + nsCAutoString nameStr(aXLFDName); + PRInt32 pos1, pos2; + // remove first '-' and everything before it. + LOCATE_MINUS(pos1, nameStr); + nameStr.Cut(0, pos1+1); + + // skip foundry and family name + LOCATE_MINUS(pos1, nameStr); + NEXT_MINUS(pos1, nameStr); + pos2 = pos1; + + // find '-' just before charset registry + for (PRInt32 i=0; i < 10; i++) { + NEXT_MINUS(pos2, nameStr); + } + + // remove everything in between + nameStr.Cut(pos1, pos2-pos1); + + aString.AppendWithConversion(nameStr.get()); +} +#endif /* MOZ_ENABLE_COREXFONTS || MOZ_WIDGET_GTK*/ + +#ifdef MOZ_ENABLE_XFT +/* static */ +PRInt32 +GetXftDPI(void) +{ + char *val = XGetDefault(GDK_DISPLAY(), "Xft", "dpi"); + if (val) { + char *e; + double d = strtod(val, &e); + + if (e != val) + return NSToCoordRound(d); + } + + return 0; +} +#endif /* MOZ_ENABLE_XFT */ diff --git a/gfx/src/thebes/nsSystemFontsGTK2.h b/gfx/src/thebes/nsSystemFontsGTK2.h new file mode 100644 index 00000000000..1d97d9fcc83 --- /dev/null +++ b/gfx/src/thebes/nsSystemFontsGTK2.h @@ -0,0 +1,43 @@ + +#ifndef _NS_SYSTEMFONTSGTK2_H_ +#define _NS_SYSTEMFONTSGTK2_H_ + +#include + +class nsSystemFontsGTK2 +{ +public: + nsSystemFontsGTK2(float aPixelsToTwips); + + const nsFont& GetDefaultFont() { return mDefaultFont; } + const nsFont& GetMenuFont() { return mMenuFont; } + const nsFont& GetFieldFont() { return mFieldFont; } + const nsFont& GetButtonFont() { return mButtonFont; } + +private: + nsresult GetSystemFontInfo(GtkWidget *aWidget, nsFont* aFont, + float aPixelsToTwips) const; + + /* + * The following system font constants exist: + * + * css2: http://www.w3.org/TR/REC-CSS2/fonts.html#x27 + * eSystemFont_Caption, eSystemFont_Icon, eSystemFont_Menu, + * eSystemFont_MessageBox, eSystemFont_SmallCaption, + * eSystemFont_StatusBar, + * // css3 + * eSystemFont_Window, eSystemFont_Document, + * eSystemFont_Workspace, eSystemFont_Desktop, + * eSystemFont_Info, eSystemFont_Dialog, + * eSystemFont_Button, eSystemFont_PullDownMenu, + * eSystemFont_List, eSystemFont_Field, + * // moz + * eSystemFont_Tooltips, eSystemFont_Widget + */ + nsFont mDefaultFont; + nsFont mButtonFont; + nsFont mFieldFont; + nsFont mMenuFont; +}; + +#endif /* _NS_SYSTEMFONTSGTK2_H_ */ diff --git a/gfx/src/thebes/nsSystemFontsWin.h b/gfx/src/thebes/nsSystemFontsWin.h new file mode 100644 index 00000000000..af9ee24ec3c --- /dev/null +++ b/gfx/src/thebes/nsSystemFontsWin.h @@ -0,0 +1,20 @@ + +#ifndef _NS_SYSTEMFONTSWIN_H_ +#define _NS_SYSTEMFONTSWIN_H_ + +#include +#include + +class nsSystemFontsWin +{ +public: + nsSystemFontsWin(float aPixelsToTwips); + + nsresult CopyLogFontToNSFont(HDC* aHDC, const LOGFONT* ptrLogFont, + nsFont* aFont, PRBool aIsWide = PR_FALSE) const; + nsresult GetSysFontInfo(HDC aHDC, nsSystemFontID anID, nsFont* aFont) const; + nsresult GetSystemFont(nsSystemFontID anID, nsFont *aFont) const; +}; + +#endif /* _NS_SYSTEMFONTSWIN_H_ */ + diff --git a/gfx/src/thebes/nsThebesBlender.cpp b/gfx/src/thebes/nsThebesBlender.cpp new file mode 100644 index 00000000000..6bdcab43629 --- /dev/null +++ b/gfx/src/thebes/nsThebesBlender.cpp @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsMemory.h" + +#include "nsThebesBlender.h" +#include "nsThebesDrawingSurface.h" + +NS_IMPL_ISUPPORTS1(nsThebesBlender, nsIBlender) + +nsThebesBlender::nsThebesBlender() + : mThebesDC(nsnull) +{ +} + +nsThebesBlender::~nsThebesBlender() +{ +} + +NS_IMETHODIMP +nsThebesBlender::Init(nsIDeviceContext *aContext) +{ + mThebesDC = NS_STATIC_CAST(nsThebesDeviceContext*, aContext); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesBlender::Blend(PRInt32 aSX, PRInt32 aSY, PRInt32 aWidth, PRInt32 aHeight, + nsIDrawingSurface* aSrc, nsIDrawingSurface* aDest, + PRInt32 aDX, PRInt32 aDY, + float aSrcOpacity, + nsIDrawingSurface* aSecondSrc, + nscolor aSrcBackColor, nscolor aSecondSrcBackColor) +{ + NS_WARNING("Should be using Push/PopFilter instead"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesBlender::Blend(PRInt32 aSX, PRInt32 aSY, PRInt32 aWidth, PRInt32 aHeight, + nsIRenderingContext *aSrc, nsIRenderingContext *aDest, + PRInt32 aDX, PRInt32 aDY, float aSrcOpacity, + nsIRenderingContext *aSecondSrc, nscolor aSrcBackColor, + nscolor aSecondSrcBackColor) +{ + NS_WARNING("Should be using Push/PopFilter instead"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsThebesBlender::GetAlphas(const nsRect& aRect, nsIDrawingSurface* aBlack, + nsIDrawingSurface* aWhite, PRUint8** aAlphas) +{ + NS_WARNING("This needs to be implemented somehow"); + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/gfx/src/cairo/nsCairoBlender.h b/gfx/src/thebes/nsThebesBlender.h similarity index 100% rename from gfx/src/cairo/nsCairoBlender.h rename to gfx/src/thebes/nsThebesBlender.h diff --git a/gfx/src/thebes/nsThebesDeviceContext.cpp b/gfx/src/thebes/nsThebesDeviceContext.cpp new file mode 100644 index 00000000000..d2642062991 --- /dev/null +++ b/gfx/src/thebes/nsThebesDeviceContext.cpp @@ -0,0 +1,537 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * Stuart Parmenter + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIServiceManager.h" + +#include "nsThebesDeviceContext.h" +#include "nsThebesRenderingContext.h" +#include "nsThebesDrawingSurface.h" + +#include "nsIView.h" + +#ifdef MOZ_ENABLE_GTK2 +// for getenv +#include + +#include +#include +#include +#include "nsFontMetricsUtils.h" +#include "nsFont.h" + +#include +#include +#include + + +#ifdef MOZ_ENABLE_GLITZ +#include "glitz-glx.h" +#endif /* GLITZ */ +#endif /* GTK2 */ + +#ifdef MOZ_ENABLE_GTK2 +#include "nsSystemFontsGTK2.h" +static nsSystemFontsGTK2 *gSystemFonts = nsnull; +#elif XP_WIN +#include +#include "nsSystemFontsWin.h" +static nsSystemFontsWin *gSystemFonts = nsnull; +#include +#else +#error Need to declare gSystemFonts! +#endif + +#ifdef MOZ_ENABLE_GTK2 +extern "C" { +static int x11_error_handler (Display *dpy, XErrorEvent *err) { + NS_ASSERTION(PR_FALSE, "X Error"); + return 0; +} +} +#endif + +#ifdef PR_LOGGING +PRLogModuleInfo* gThebesGFXLog = nsnull; +#endif + +NS_IMPL_ISUPPORTS_INHERITED0(nsThebesDeviceContext, DeviceContextImpl) + +nsThebesDeviceContext::nsThebesDeviceContext() +{ +#ifdef PR_LOGGING + if (!gThebesGFXLog) + gThebesGFXLog = PR_NewLogModule("thebesGfx"); +#endif + + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("#### Creating DeviceContext %p\n", this)); + + mDevUnitsToAppUnits = 1.0f; + mAppUnitsToDevUnits = 1.0f; + mCPixelScale = 1.0f; + mZoom = 1.0f; + mTextZoom = 1.0f; + + mWidgetSurfaceCache.Init(); + +#ifdef XP_WIN + SCRIPT_DIGITSUBSTITUTE sds; + ScriptRecordDigitSubstitution(LOCALE_USER_DEFAULT, &sds); +#endif +} + +nsThebesDeviceContext::~nsThebesDeviceContext() +{ +} + +NS_IMETHODIMP +nsThebesDeviceContext::Init(nsNativeWidget aWidget) +{ +#ifdef XP_WIN + // XXX we should really look at the widget for printing and such, but this widget is currently always null... + HDC dc = GetDC(nsnull); + mPixelsToTwips = (float)NSIntPointsToTwips(72) / (float)GetDeviceCaps(dc, LOGPIXELSY); + if (GetDeviceCaps(dc, TECHNOLOGY) == DT_RASDISPLAY) + mPixelsToTwips = (float)NSToIntRound(mPixelsToTwips); + mTwipsToPixels = 1.0f / mPixelsToTwips; + ReleaseDC(nsnull, dc); +#else + mTwipsToPixels = 96 / (float) NSIntPointsToTwips(72); + mPixelsToTwips = 1.0f / mTwipsToPixels; +#endif + + mWidget = aWidget; + + if (!mScreenManager) + mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1"); + if (!mScreenManager) + return NS_ERROR_FAILURE; + + nsCOMPtr screen; + mScreenManager->GetPrimaryScreen (getter_AddRefs(screen)); + if (screen) { + PRInt32 x, y, width, height; + screen->GetRect (&x, &y, &width, &height); + mWidthFloat = float(width); + mHeightFloat = float(height); + } + +#ifdef MOZ_ENABLE_GTK2 + if (getenv ("MOZ_X_SYNC")) { + PR_LOG (gThebesGFXLog, PR_LOG_DEBUG, ("+++ Enabling XSynchronize\n")); + XSynchronize (gdk_x11_get_default_xdisplay(), True); + XSetErrorHandler(x11_error_handler); + } +#endif + + mWidth = -1; + mHeight = -1; + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::CreateRenderingContext(nsIView *aView, + nsIRenderingContext *&aContext) +{ + NS_ENSURE_ARG_POINTER(aView); + NS_PRECONDITION(aView->HasWidget(), "View has no widget!"); + + nsCOMPtr widget; + widget = aView->GetWidget(); + + return CreateRenderingContext(widget, aContext); +} + +NS_IMETHODIMP +nsThebesDeviceContext::CreateRenderingContext(nsIDrawingSurface *aSurface, + nsIRenderingContext *&aContext) +{ + nsresult rv; + + aContext = nsnull; + nsCOMPtr pContext; + rv = CreateRenderingContextInstance(*getter_AddRefs(pContext)); + if (NS_SUCCEEDED(rv)) { + rv = pContext->Init(this, aSurface); + if (NS_SUCCEEDED(rv)) { + aContext = pContext; + NS_ADDREF(aContext); + } + } + + return rv; +} + +NS_IMETHODIMP +nsThebesDeviceContext::CreateRenderingContext(nsIWidget *aWidget, + nsIRenderingContext *&aContext) +{ + nsresult rv; + + aContext = nsnull; + nsCOMPtr pContext; + rv = CreateRenderingContextInstance(*getter_AddRefs(pContext)); + if (NS_SUCCEEDED(rv)) { + rv = pContext->Init(this, aWidget); + if (NS_SUCCEEDED(rv)) { + aContext = pContext; + NS_ADDREF(aContext); + } + } + + return rv; +} + +NS_IMETHODIMP +nsThebesDeviceContext::CreateRenderingContext(nsIRenderingContext *&aContext) +{ + NS_ERROR("CreateRenderingContext with other rendering context arg; fix this if this needs to be called"); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::CreateRenderingContextInstance(nsIRenderingContext *&aContext) +{ + nsCOMPtr renderingContext = new nsThebesRenderingContext(); + if (!renderingContext) + return NS_ERROR_OUT_OF_MEMORY; + + aContext = renderingContext; + NS_ADDREF(aContext); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::SupportsNativeWidgets(PRBool &aSupportsWidgets) +{ + aSupportsWidgets = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::GetScrollBarDimensions(float &aWidth, float &aHeight) const +{ + aWidth = 10.0f * mPixelsToTwips; + aHeight = 10.0f * mPixelsToTwips; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::GetSystemFont(nsSystemFontID aID, nsFont *aFont) const +{ + nsresult status = NS_OK; + + if (!gSystemFonts) { +#ifdef MOZ_ENABLE_GTK2 + gSystemFonts = new nsSystemFontsGTK2(mPixelsToTwips); +#elif XP_WIN + gSystemFonts = new nsSystemFontsWin(mPixelsToTwips); +#else +#error Need to know how to create gSystemFonts, fix me! +#endif + } + +#ifdef XP_WIN + gSystemFonts->GetSystemFont(aID, aFont); + return NS_OK; + +#else + switch (aID) { + case eSystemFont_Menu: // css2 + case eSystemFont_PullDownMenu: // css3 + *aFont = gSystemFonts->GetMenuFont(); + break; + + case eSystemFont_Field: // css3 + case eSystemFont_List: // css3 + *aFont = gSystemFonts->GetFieldFont(); + break; + + case eSystemFont_Button: // css3 + *aFont = gSystemFonts->GetButtonFont(); + break; + + case eSystemFont_Caption: // css2 + case eSystemFont_Icon: // css2 + case eSystemFont_MessageBox: // css2 + case eSystemFont_SmallCaption: // css2 + case eSystemFont_StatusBar: // css2 + case eSystemFont_Window: // css3 + case eSystemFont_Document: // css3 + case eSystemFont_Workspace: // css3 + case eSystemFont_Desktop: // css3 + case eSystemFont_Info: // css3 + case eSystemFont_Dialog: // css3 + case eSystemFont_Tooltips: // moz + case eSystemFont_Widget: // moz + *aFont = gSystemFonts->GetDefaultFont(); + break; + } +#endif + return status; +} + +NS_IMETHODIMP +nsThebesDeviceContext::CheckFontExistence(const nsString& aFaceName) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::GetDepth(PRUint32& aDepth) +{ + aDepth = 24; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::GetPaletteInfo(nsPaletteInfo& aPaletteInfo) +{ + aPaletteInfo.isPaletteDevice = PR_FALSE; + aPaletteInfo.sizePalette = 0; + aPaletteInfo.numReserved = 0; + aPaletteInfo.palette = nsnull; + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::ConvertPixel(nscolor aColor, PRUint32 & aPixel) +{ + aPixel = aColor; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::GetDeviceSurfaceDimensions(PRInt32 &aWidth, PRInt32 &aHeight) +{ + if (mWidth == -1) + mWidth = NSToIntRound(mWidthFloat * mDevUnitsToAppUnits); + + if (mHeight == -1) + mHeight = NSToIntRound(mHeightFloat * mDevUnitsToAppUnits); + + aWidth = mWidth; + aHeight = mHeight; + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::GetRect(nsRect &aRect) +{ + if (mWidget) { + PRInt32 x,y; + PRUint32 width, height; + +#if defined (MOZ_ENABLE_GTK2) + Window root_ignore; + PRUint32 bwidth_ignore, depth; + + XGetGeometry(GDK_WINDOW_XDISPLAY(GDK_DRAWABLE(mWidget)), + GDK_WINDOW_XWINDOW(GDK_DRAWABLE(mWidget)), + &root_ignore, &x, &y, + &width, &height, + &bwidth_ignore, &depth); +#elif defined (XP_WIN) + // XXX + x = y = 0; + width = height = 200; +#else +#error write me +#endif + + nsCOMPtr screen; + mScreenManager->ScreenForRect(x, y, width, height, getter_AddRefs(screen)); + screen->GetRect(&aRect.x, &aRect.y, &aRect.width, &aRect.height); + + aRect.x = NSToIntRound(mDevUnitsToAppUnits * aRect.x); + aRect.y = NSToIntRound(mDevUnitsToAppUnits * aRect.y); + aRect.width = NSToIntRound(mDevUnitsToAppUnits * aRect.width); + aRect.height = NSToIntRound(mDevUnitsToAppUnits * aRect.height); + } else { + aRect.x = 0; + aRect.y = 0; + + this->GetDeviceSurfaceDimensions(aRect.width, aRect.height); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDeviceContext::GetClientRect(nsRect &aRect) +{ + nsresult rv = this->GetRect(aRect); + return rv; +} + +#if defined(MOZ_ENABLE_GLITZ) +void* +nsThebesDeviceContext::GetGlitzDrawableFormat() +{ + glitz_drawable_format_t* format = nsnull; +#ifdef MOZ_ENABLE_GTK2 + glitz_drawable_format_t templ; + memset(&templ, 0, sizeof(templ)); + templ.samples = 1; // change this to change FSAA? + templ.types.window = 1; + + int defaultScreen = gdk_x11_get_default_screen(); + unsigned long mask = GLITZ_FORMAT_SAMPLES_MASK | GLITZ_FORMAT_WINDOW_MASK; + + format = glitz_glx_find_drawable_format (GDK_DISPLAY(), defaultScreen, mask, &templ, 0); +#endif + return format; +} +#endif + +#if defined(MOZ_ENABLE_GLITZ) && defined(MOZ_ENABLE_GTK2) +void* +nsThebesDeviceContext::GetDesiredVisual() +{ + Display* dpy = GDK_DISPLAY(); + int defaultScreen = gdk_x11_get_default_screen(); + glitz_drawable_format_t* format = (glitz_drawable_format_t*) GetGlitzDrawableFormat(); + if (format) { + XVisualInfo* vinfo = glitz_glx_get_visual_info_from_format(dpy, defaultScreen, format); + GdkScreen* screen = gdk_display_get_screen(gdk_x11_lookup_xdisplay(dpy), defaultScreen); + GdkVisual* vis = gdk_x11_screen_lookup_visual(screen, vinfo->visualid); + return vis; + } else { + // GL/GLX not available, force glitz off + nsThebesDrawingSurface::mGlitzMode = 0; + } + + return nsnull; +} +#endif + +NS_IMETHODIMP +nsThebesDeviceContext::PrepareNativeWidget(nsIWidget* aWidget, void** aOut) +{ +#if defined(MOZ_ENABLE_GLITZ) && defined(MOZ_ENABLE_GTK2) + *aOut = GetDesiredVisual(); +#else + *aOut = nsnull; +#endif + return NS_OK; +} + +/* + * below methods are for printing and are not implemented + */ +NS_IMETHODIMP +nsThebesDeviceContext::GetDeviceContextFor(nsIDeviceContextSpec *aDevice, + nsIDeviceContext *&aContext) +{ + /* we don't do printing */ + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::PrepareDocument(PRUnichar * aTitle, + PRUnichar* aPrintToFileName) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::BeginDocument(PRUnichar* aTitle, + PRUnichar* aPrintToFileName, + PRInt32 aStartPage, + PRInt32 aEndPage) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::EndDocument(void) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::AbortDocument(void) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::BeginPage(void) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::EndPage(void) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::SetAltDevice(nsIDeviceContext* aAltDC) +{ + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::GetAltDevice(nsIDeviceContext** aAltDC) +{ + *aAltDC = nsnull; + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesDeviceContext::SetUseAltDC(PRUint8 aValue, PRBool aOn) +{ + return NS_OK; +} + +/** End printing methods **/ diff --git a/gfx/src/thebes/nsThebesDeviceContext.h b/gfx/src/thebes/nsThebesDeviceContext.h new file mode 100644 index 00000000000..9ec105ae9a0 --- /dev/null +++ b/gfx/src/thebes/nsThebesDeviceContext.h @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * Stuart Parmenter + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _NS_THEBESDEVICECONTEXT_H_ +#define _NS_THEBESDEVICECONTEXT_H_ + +#include "nsIScreenManager.h" + +#include "nsDeviceContext.h" + +#include "nsRefPtrHashtable.h" +#include "nsHashKeys.h" + +#include "prlog.h" + +#include + +#ifdef PR_LOGGING +extern PRLogModuleInfo* gThebesGFXLog; +#endif + +class nsThebesDeviceContext : public DeviceContextImpl +{ +public: + nsThebesDeviceContext(); + virtual ~nsThebesDeviceContext(); + + NS_DECL_ISUPPORTS_INHERITED + + NS_IMETHOD Init(nsNativeWidget aWidget); + NS_IMETHOD CreateRenderingContext(nsIView *aView, nsIRenderingContext *&aContext); + + NS_IMETHOD CreateRenderingContext(nsIDrawingSurface *aSurface, nsIRenderingContext *&aContext); + NS_IMETHOD CreateRenderingContext(nsIWidget *aWidget, nsIRenderingContext *&aContext); + NS_IMETHOD CreateRenderingContext(nsIRenderingContext *&aContext); + NS_IMETHOD CreateRenderingContextInstance(nsIRenderingContext *&aContext); + + NS_IMETHOD SupportsNativeWidgets(PRBool &aSupportsWidgets); + NS_IMETHOD PrepareNativeWidget(nsIWidget* aWidget, void** aOut); + + NS_IMETHOD GetScrollBarDimensions(float &aWidth, float &aHeight) const; + + NS_IMETHOD GetSystemFont(nsSystemFontID aID, nsFont *aFont) const; + + NS_IMETHOD CheckFontExistence(const nsString& aFaceName); + + NS_IMETHOD GetDepth(PRUint32& aDepth); + + NS_IMETHOD GetPaletteInfo(nsPaletteInfo& aPaletteInfo); + + NS_IMETHOD ConvertPixel(nscolor aColor, PRUint32 & aPixel); + + NS_IMETHOD GetDeviceSurfaceDimensions(PRInt32 &aWidth, PRInt32 &aHeight); + NS_IMETHOD GetRect(nsRect &aRect); + NS_IMETHOD GetClientRect(nsRect &aRect); + +#ifdef MOZ_ENABLE_GLITZ + /* glitz_drawable_format_t */ void* GetGlitzDrawableFormat(); + +#ifdef MOZ_ENABLE_GTK2 + /* GdkVisual */ void* GetDesiredVisual(); +#endif +#endif + + /* printing goop */ + NS_IMETHOD GetDeviceContextFor(nsIDeviceContextSpec *aDevice, + nsIDeviceContext *&aContext); + + NS_IMETHOD PrepareDocument(PRUnichar * aTitle, + PRUnichar* aPrintToFileName); + + NS_IMETHOD BeginDocument(PRUnichar* aTitle, + PRUnichar* aPrintToFileName, + PRInt32 aStartPage, + PRInt32 aEndPage); + + NS_IMETHOD EndDocument(void); + NS_IMETHOD AbortDocument(void); + NS_IMETHOD BeginPage(void); + NS_IMETHOD EndPage(void); + NS_IMETHOD SetAltDevice(nsIDeviceContext* aAltDC); + NS_IMETHOD GetAltDevice(nsIDeviceContext** aAltDC); + NS_IMETHOD SetUseAltDC(PRUint8 aValue, PRBool aOn); + /* end printing goop */ + + static void DebugShowCairoSurface (const char *aName, cairo_surface_t *aSurface); + + + nsNativeWidget GetWidget() { return mWidget; } +private: + nsNativeWidget mWidget; + + nsCOMPtr mScreenManager; + + float mWidthFloat; + float mHeightFloat; + PRInt32 mWidth; + PRInt32 mHeight; + + nsRefPtrHashtable mWidgetSurfaceCache; +}; + +#endif /* _NS_CAIRODEVICECONTEXT_H_ */ + diff --git a/gfx/src/thebes/nsThebesDrawingSurface.cpp b/gfx/src/thebes/nsThebesDrawingSurface.cpp new file mode 100644 index 00000000000..24140a3d366 --- /dev/null +++ b/gfx/src/thebes/nsThebesDrawingSurface.cpp @@ -0,0 +1,393 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsThebesDrawingSurface.h" +#include "nsThebesDeviceContext.h" + +#include "nsMemory.h" + +#include "gfxImageSurface.h" + +#ifdef MOZ_ENABLE_GTK2 +#include +#include "gfxXlibSurface.h" +# ifdef MOZ_ENABLE_GLITZ +# include "gfxGlitzSurface.h" +# include "glitz-glx.h" +# endif +#elif XP_WIN +#include "gfxWindowsSurface.h" +#endif + +#include + +PRInt32 nsThebesDrawingSurface::mGlitzMode = -1; + +NS_IMPL_ISUPPORTS1(nsThebesDrawingSurface, nsIDrawingSurface) + +nsThebesDrawingSurface::nsThebesDrawingSurface() +{ +} + +nsThebesDrawingSurface::~nsThebesDrawingSurface() +{ +#ifdef WIN_XP + if (mWidget) + nsNativeWidget nativeWidget = mWidget->FreeNativeData(mNativeWidget, NS_NATIVE_GRAPHIC); +#endif +} + +nsresult +nsThebesDrawingSurface::Init(nsThebesDeviceContext *aDC, PRUint32 aWidth, PRUint32 aHeight, PRBool aFastAccess) +{ + NS_ASSERTION(mSurface == nsnull, "Surface already initialized!"); + NS_ASSERTION(aWidth > 0 && aHeight > 0, "Invalid surface dimensions!"); + + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsThebesDrawingSurface::Init aDC %p w %d h %d fast %d\n", this, aDC, aWidth, aHeight, aFastAccess)); + + mWidth = aWidth; + mHeight = aHeight; + mDC = aDC; + mNativeWidget = nsnull; + +#if defined(MOZ_ENABLE_GTK2) + if (aFastAccess) { + //fprintf (stderr, "## nsThebesDrawingSurface::Init gfxImageSurface %d %d\n", aWidth, aHeight); + mSurface = new gfxImageSurface(gfxImageSurface::ImageFormatARGB32, aWidth, aHeight); + } else { + if (!UseGlitz()) { + mSurface = new gfxXlibSurface(GDK_DISPLAY(), GDK_VISUAL_XVISUAL(gdk_rgb_get_visual()), aWidth, aHeight); + } else { +# if defined(MOZ_ENABLE_GLITZ) + glitz_drawable_format_t *gdformat = (glitz_drawable_format_t*) aDC->GetGlitzDrawableFormat(); + glitz_drawable_t *gdraw = + glitz_glx_create_pbuffer_drawable (GDK_DISPLAY(), + DefaultScreen(GDK_DISPLAY()), + gdformat, + aWidth, + aHeight); + glitz_format_t *gformat = + glitz_find_standard_format (gdraw, GLITZ_STANDARD_ARGB32); + glitz_surface_t *gsurf = + glitz_surface_create (gdraw, + gformat, + aWidth, + aHeight, + 0, + NULL); + glitz_surface_attach (gsurf, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR, 0, 0); + + //fprintf (stderr, "## nsThebesDrawingSurface::Init Glitz PBUFFER %d %d\n", aWidth, aHeight); + mSurface = new gfxGlitzSurface (gdraw, gsurf, PR_TRUE); +# endif + } + } +#elif XP_WIN + if (aFastAccess) { + mSurface = new gfxImageSurface(gfxImageSurface::ImageFormatARGB32, aWidth, aHeight); + } else { + mSurface = new gfxWindowsSurface(aWidth, aHeight); + } +#else +#error Write me! +#endif + + return NS_OK; +} + +nsresult +nsThebesDrawingSurface::Init (nsThebesDeviceContext *aDC, nsIWidget *aWidget) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsThebesDrawingSurface::Init aDC %p aWidget %p\n", this, aDC, aWidget)); + +#ifdef MOZ_ENABLE_GTK2 + nsNativeWidget nativeWidget = aWidget->GetNativeData(NS_NATIVE_WIDGET); +#elif XP_WIN + mWidget = aWidget; // hold a pointer to this.. not a reference.. because we have to free the dc... + nsNativeWidget nativeWidget = aWidget->GetNativeData(NS_NATIVE_GRAPHIC); +#else +#error Write me! +#endif + + Init(aDC, nativeWidget); + + return NS_OK; +} + +nsresult +nsThebesDrawingSurface::Init (nsThebesDeviceContext *aDC, nsNativeWidget aWidget) +{ + mDC = aDC; + mNativeWidget = aWidget; + mWidth = 0; + mHeight = 0; + +#ifdef MOZ_ENABLE_GTK2 + NS_ASSERTION (GDK_IS_WINDOW(aWidget), "unsupported native widget type!"); + + if (!UseGlitz()) { + mSurface = new gfxXlibSurface(GDK_WINDOW_XDISPLAY(GDK_DRAWABLE(aWidget)), + GDK_WINDOW_XWINDOW(GDK_DRAWABLE(aWidget)), + GDK_VISUAL_XVISUAL(gdk_drawable_get_visual(GDK_DRAWABLE(aWidget)))); + } else { +# if defined(MOZ_ENABLE_GLITZ) + glitz_surface_t *gsurf; + glitz_drawable_t *gdraw; + + glitz_drawable_format_t *gdformat = (glitz_drawable_format_t*) aDC->GetGlitzDrawableFormat(); + + Display* dpy = GDK_WINDOW_XDISPLAY(GDK_DRAWABLE(aWidget)); + Window wnd = GDK_WINDOW_XWINDOW(GDK_DRAWABLE(aWidget)); + + Window root_ignore; + int x_ignore, y_ignore; + unsigned int bwidth_ignore, width, height, depth; + + XGetGeometry(dpy, + wnd, + &root_ignore, &x_ignore, &y_ignore, + &width, &height, + &bwidth_ignore, &depth); + + gdraw = + glitz_glx_create_drawable_for_window (dpy, + DefaultScreen(dpy), + gdformat, + wnd, + width, + height); + glitz_format_t *gformat = + glitz_find_standard_format (gdraw, GLITZ_STANDARD_ARGB32); + gsurf = + glitz_surface_create (gdraw, + gformat, + width, + height, + 0, + NULL); + glitz_surface_attach (gsurf, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR, 0, 0); + + + //fprintf (stderr, "## nsThebesDrawingSurface::Init Glitz DRAWABLE %p (DC: %p)\n", aWidget, aDC); + mSurface = new gfxGlitzSurface (gdraw, gsurf, PR_TRUE); + + mWidth = width; + mHeight = height; +# endif + } +#elif XP_WIN + HDC nativeDC = (HDC)aWidget; + mSurface = new gfxWindowsSurface(nativeDC); +#else +#error write me +#endif + + return NS_OK; +} + +#ifdef MOZ_ENABLE_GTK2 +static nsresult ConvertPixmapsGTK(GdkPixmap* aPmBlack, GdkPixmap* aPmWhite, + const nsIntSize& aSize, PRUint8* aData); +#endif + +nsresult +nsThebesDrawingSurface::Init (nsThebesDeviceContext *aDC, + void* aNativePixmapBlack, + void* aNativePixmapWhite, + const nsIntSize& aSrcSize) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsThebesDrawingSurface::Init aDC %p nativeBlack %p nativeWhite %p size [%d,%d]\n", this, aDC, aNativePixmapBlack, aNativePixmapWhite, aSrcSize.width, aSrcSize.height)); + + mWidth = aSrcSize.width; + mHeight = aSrcSize.height; + +#ifdef MOZ_ENABLE_GTK2 + nsresult rv; + nsRefPtr imgSurf = new gfxImageSurface(gfxImageSurface::ImageFormatARGB32, mWidth, mHeight); + + GdkPixmap* pmBlack = NS_STATIC_CAST(GdkPixmap*, aNativePixmapBlack); + GdkPixmap* pmWhite = NS_STATIC_CAST(GdkPixmap*, aNativePixmapWhite); + + rv = ConvertPixmapsGTK(pmBlack, pmWhite, aSrcSize, imgSurf->Data()); + if (NS_FAILED(rv)) + return rv; + + mSurface = imgSurf; +#elif XP_WIN + // XXX writeme +#else +#error Write me! +#endif + + return NS_OK; +} + +nsresult +nsThebesDrawingSurface::PushFilter(const nsIntRect& aRect, PRBool aAreaIsOpaque, float aOpacity) +{ + return NS_OK; +} + +void +nsThebesDrawingSurface::PopFilter() +{ + +} + +NS_IMETHODIMP +nsThebesDrawingSurface::Lock (PRInt32 aX, PRInt32 aY, PRUint32 aWidth, PRUint32 aHeight, + void **aBits, PRInt32 *aStride, PRInt32 *aWidthBytes, + PRUint32 aFlags) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesDrawingSurface::Unlock (void) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesDrawingSurface::GetDimensions (PRUint32 *aWidth, PRUint32 *aHeight) +{ + *aWidth = mWidth; + *aHeight = mHeight; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesDrawingSurface::IsOffscreen(PRBool *aOffScreen) +{ + /* remove this method */ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesDrawingSurface::IsPixelAddressable(PRBool *aAddressable) +{ + /* remove this method */ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesDrawingSurface::GetPixelFormat(nsPixelFormat *aFormat) +{ + /* remove this method, and make canvas and DumpToPPM stop using it */ + return NS_ERROR_NOT_IMPLEMENTED; +} + +/***** Platform specific helpers ****/ + +/** + * Builds an ARGB word from channel values. r,g,b must already + * be premultipled/rendered onto black. + */ +static PRInt32 BuildARGB(PRUint8 aR, PRUint8 aG, PRUint8 aB, PRUint8 aA) +{ + return (aA << 24) | (aR << 16) | (aG << 8) | aB; +} + +#ifdef MOZ_ENABLE_GTK2 +static nsresult ConvertPixmapsGTK(GdkPixmap* aPmBlack, GdkPixmap* aPmWhite, + const nsIntSize& aSize, PRUint8* aData) +{ + GdkImage *imgBlack = gdk_image_get(aPmBlack, 0, 0, + aSize.width, + aSize.height); + GdkImage *imgWhite = gdk_image_get(aPmWhite, 0, 0, + aSize.width, + aSize.height); + + if (!imgBlack || !imgWhite) + return NS_ERROR_OUT_OF_MEMORY; + + PRUint8* blackData = (PRUint8*)GDK_IMAGE_XIMAGE(imgBlack)->data; + PRUint8* whiteData = (PRUint8*)GDK_IMAGE_XIMAGE(imgWhite)->data; + PRInt32 bpp = GDK_IMAGE_XIMAGE(imgBlack)->bits_per_pixel; + PRInt32 stride = GDK_IMAGE_XIMAGE(imgBlack)->bytes_per_line; + PRInt32* argb = (PRInt32*)aData; + + if (bpp == 24 || bpp == 32) { + PRInt32 pixSize = bpp/8; + for (PRInt32 y = 0; y < aSize.height; ++y) { + PRUint8* blackRow = blackData + y*stride; + PRUint8* whiteRow = whiteData + y*stride; + for (PRInt32 x = 0; x < aSize.width; ++x) { + PRUint8 alpha = 0; + if (blackRow[0] == whiteRow[0] && + blackRow[1] == whiteRow[1] && + blackRow[2] == whiteRow[2]) { + alpha = 0xFF; + } + *argb++ = BuildARGB(blackRow[2], blackRow[1], + blackRow[0], alpha); + blackRow += pixSize; + whiteRow += pixSize; + } + } + } else { + NS_ERROR("Unhandled bpp!"); + } + + gdk_image_unref(imgBlack); + gdk_image_unref(imgWhite); + + return NS_OK; +} +#endif + +PRBool +nsThebesDrawingSurface::UseGlitz() +{ +#ifdef MOZ_ENABLE_GLITZ + if (mGlitzMode == -1) { + if (getenv("MOZ_GLITZ")) { + glitz_glx_init (NULL); + mGlitzMode = 1; + } else { + mGlitzMode = 0; + } + } + + if (mGlitzMode) + return PR_TRUE; + +#endif + return PR_FALSE; +} diff --git a/gfx/src/thebes/nsThebesDrawingSurface.h b/gfx/src/thebes/nsThebesDrawingSurface.h new file mode 100644 index 00000000000..adc9ae6e348 --- /dev/null +++ b/gfx/src/thebes/nsThebesDrawingSurface.h @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the NPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the NPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _NSTHEBESDRAWINGSURFACE_H_ +#define _NSTHEBESDRAWINGSURFACE_H_ + +#include "nsCOMPtr.h" + +#include "nsSize.h" +#include "nsRect.h" +#include "nsIDrawingSurface.h" +#include "nsIWidget.h" + +#include "gfxASurface.h" + +class nsThebesDeviceContext; + +class nsThebesDrawingSurface : public nsIDrawingSurface +{ +public: + nsThebesDrawingSurface (); + virtual ~nsThebesDrawingSurface (); + + // create a image surface if aFastAccess == TRUE, otherwise create + // a fast server pixmap + nsresult Init (nsThebesDeviceContext *aDC, PRUint32 aWidth, PRUint32 aHeight, PRBool aFastAccess); + + // create a fast drawing surface for a native widget + nsresult Init (nsThebesDeviceContext *aDC, nsIWidget *aWidget); + + // create a fast drawing surface for a native widget + nsresult Init (nsThebesDeviceContext *aDC, nsNativeWidget aWidget); + + // create a drawing surface for a native pixmap + nsresult Init (nsThebesDeviceContext* aDC, void* aNativePixmapBlack, + void* aNativePixmapWhite, + const nsIntSize& aSize); + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsIDrawingSurface interface + + NS_IMETHOD Lock(PRInt32 aX, PRInt32 aY, PRUint32 aWidth, PRUint32 aHeight, + void **aBits, PRInt32 *aStride, PRInt32 *aWidthBytes, + PRUint32 aFlags); + NS_IMETHOD Unlock(void); + NS_IMETHOD GetDimensions(PRUint32 *aWidth, PRUint32 *aHeight); + NS_IMETHOD IsOffscreen(PRBool *aOffScreen); + NS_IMETHOD IsPixelAddressable(PRBool *aAddressable); + NS_IMETHOD GetPixelFormat(nsPixelFormat *aFormat); + + /* utility functions */ + gfxASurface *GetThebesSurface(void) { return mSurface; } + PRInt32 GetDepth() { /* XXX */ return 24; } + nsNativeWidget GetNativeWidget(void) { return mNativeWidget; } + + nsresult PushFilter(const nsIntRect& aRect, PRBool aAreaIsOpaque, float aOpacity); + void PopFilter(); + + static PRBool UseGlitz(); + static PRInt32 mGlitzMode; +private: + nsRefPtr mSurface; + nsThebesDeviceContext *mDC; + nsNativeWidget mNativeWidget; +#ifdef XP_WIN + nsIWidget *mWidget; +#endif + + PRUint32 mWidth, mHeight; +}; + +#endif /* _NSTHEBESDRAWINGSURFACE_H_ */ diff --git a/gfx/src/thebes/nsThebesFontMetrics.cpp b/gfx/src/thebes/nsThebesFontMetrics.cpp new file mode 100644 index 00000000000..8dc230e7509 --- /dev/null +++ b/gfx/src/thebes/nsThebesFontMetrics.cpp @@ -0,0 +1,383 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsThebesFontMetrics.h" +#include "nsFont.h" + +#include "nsDirectoryServiceDefs.h" +#include "nsIFile.h" +#include "nsString.h" + +#define FONT_FILE "Vera.ttf" +#define FONT_SIZE 10 +/* +#define FONT_FILE "verdana.ttf" +#define FONT_SIZE 12 +*/ + +#define FT_FLOOR(X) ((X & -64) >> 6) +#define FT_CEIL(X) (((X + 63) & -64) >> 6) + +static FT_Library ftlib = nsnull; + +NS_IMPL_ISUPPORTS1(nsThebesFontMetrics, nsIFontMetrics) + +nsThebesFontMetrics::nsThebesFontMetrics() : + mMaxAscent(0), + mMaxDescent(0), + mMaxAdvance(0), + mUnderlineOffset(0), + mUnderlineHeight(0) +{ + NS_INIT_ISUPPORTS(); + if (!ftlib) { + FT_Init_FreeType(&ftlib); + } +} + +nsThebesFontMetrics::~nsThebesFontMetrics() +{ + FT_Done_Face(mFace); +} + +static char * +GetFontPath() +{ + return strdup("/tmp/fonts/" FONT_FILE); + + nsCOMPtr aFile; + NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, getter_AddRefs(aFile)); + aFile->Append(NS_LITERAL_STRING(FONT_FILE)); + nsAutoString pathBuf; + aFile->GetPath(pathBuf); + NS_LossyConvertUCS2toASCII pathCBuf(pathBuf); + + return strdup(pathCBuf.get()); +} + +NS_IMETHODIMP +nsThebesFontMetrics::Init(const nsFont& aFont, nsIAtom* aLangGroup, + nsIDeviceContext *aContext) +{ + mFont = aFont; + mLangGroup = aLangGroup; + + mDeviceContext = aContext; + mDev2App = 1.0; + + char *fontPath = GetFontPath(); + FT_Error ferr = FT_New_Face(ftlib, fontPath, 0, &mFace); + if (ferr != 0) { + fprintf (stderr, "FT Error: %d\n", ferr); + return NS_ERROR_FAILURE; + } + free(fontPath); + + FT_Set_Char_Size(mFace, 0, FONT_SIZE << 6, 72, 72); + + mMaxAscent = mFace->bbox.yMax; + mMaxDescent = mFace->bbox.yMin; + mMaxAdvance = mFace->max_advance_width; + + mUnderlineOffset = mFace->underline_position; + mUnderlineHeight = mFace->underline_thickness; + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::Destroy() +{ + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetXHeight(nscoord& aResult) +{ + aResult = NSToCoordRound(14 * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetSuperscriptOffset(nscoord& aResult) +{ + aResult = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetSubscriptOffset(nscoord& aResult) +{ + aResult = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize) +{ + aOffset = 0; + aSize = NSToCoordRound(1 * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize) +{ + const FT_Fixed scale = mFace->size->metrics.y_scale; + + aOffset = FT_FLOOR(FT_MulFix(mUnderlineOffset, scale)); + aOffset = NSToCoordRound(aOffset * mDev2App); + + aSize = FT_CEIL(FT_MulFix(mUnderlineHeight, scale)); + if (aSize > 1) + aSize = 1; + aSize = NSToCoordRound(aSize * mDev2App); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetHeight(nscoord &aHeight) +{ + aHeight = NSToCoordRound((mFace->size->metrics.height >> 6) * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetInternalLeading(nscoord &aLeading) +{ + // aLeading = 0 * mDev2App; + aLeading = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetExternalLeading(nscoord &aLeading) +{ + // aLeading = 0 * mDev2App; + aLeading = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetEmHeight(nscoord &aHeight) +{ + /* ascent + descent */ +#if FONT_SIZE == 10 + const nscoord emHeight = 10; +#else + /* XXX this is for 12px verdana ... */ + const nscoord emHeight = 14; +#endif + + aHeight = NSToCoordRound(emHeight * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetEmAscent(nscoord &aAscent) +{ + /* units above the base line */ +#if FONT_SIZE == 10 + const nscoord emAscent = 10; +#else + /* XXX this is for 12px verdana ... */ + const nscoord emAscent = 12; +#endif + aAscent = NSToCoordRound(emAscent * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetEmDescent(nscoord &aDescent) +{ + /* units below the base line */ +#if FONT_SIZE == 10 + const nscoord emDescent = 0; +#else + /* XXX this is for 12px verdana ... */ + const nscoord emDescent = 2; +#endif + aDescent = NSToCoordRound(emDescent * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetMaxHeight(nscoord &aHeight) +{ + /* ascent + descent */ + aHeight = FT_CEIL(FT_MulFix(mMaxAscent, mFace->size->metrics.y_scale)) + - FT_CEIL(FT_MulFix(mMaxDescent, mFace->size->metrics.y_scale)) + + 1; + + aHeight = NSToCoordRound(aHeight * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetMaxAscent(nscoord &aAscent) +{ + /* units above the base line */ + aAscent = FT_CEIL(FT_MulFix(mMaxAscent, mFace->size->metrics.y_scale)); + aAscent = NSToCoordRound(aAscent * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetMaxDescent(nscoord &aDescent) +{ + /* units below the base line */ + aDescent = -FT_CEIL(FT_MulFix(mMaxDescent, mFace->size->metrics.y_scale)); + aDescent = NSToCoordRound(aDescent * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetMaxAdvance(nscoord &aAdvance) +{ + aAdvance = FT_CEIL(FT_MulFix(mMaxAdvance, mFace->size->metrics.x_scale)); + aAdvance = NSToCoordRound(aAdvance * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetLangGroup(nsIAtom** aLangGroup) +{ + *aLangGroup = mLangGroup; + NS_IF_ADDREF(*aLangGroup); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetFontHandle(nsFontHandle &aHandle) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetAveCharWidth(nscoord& aAveCharWidth) +{ + aAveCharWidth = NSToCoordRound(7 * mDev2App); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetSpaceWidth(nscoord& aSpaceCharWidth) +{ + FT_Load_Char(mFace, ' ', FT_LOAD_DEFAULT | FT_LOAD_NO_AUTOHINT); + + aSpaceCharWidth = NSToCoordRound((mFace->glyph->advance.x >> 6) * mDev2App); + + return NS_OK; +} + +nscoord +nsThebesFontMetrics::MeasureString(const char *aString, PRUint32 aLength) +{ + nscoord width = 0; + PRUint32 i; + int error; + + FT_UInt glyph_index; + FT_UInt previous = 0; + + for (i=0; i < aLength; ++i) { + glyph_index = FT_Get_Char_Index(mFace, aString[i]); +/* + if (previous && glyph_index) { + FT_Vector delta; + FT_Get_Kerning(mFace, previous, glyph_index, + FT_KERNING_DEFAULT, &delta); + width += delta.x >> 6; + } +*/ + error = FT_Load_Glyph(mFace, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_NO_AUTOHINT); + if (error) + continue; + + width += mFace->glyph->advance.x; + previous = glyph_index; + } + + return NSToCoordRound((width >> 6) * mDev2App); +} + +nscoord +nsThebesFontMetrics::MeasureString(const PRUnichar *aString, PRUint32 aLength) +{ + nscoord width = 0; + PRUint32 i; + int error; + + FT_UInt glyph_index; + FT_UInt previous = 0; + + for (i=0; i < aLength; ++i) { + glyph_index = FT_Get_Char_Index(mFace, aString[i]); +/* + if (previous && glyph_index) { + FT_Vector delta; + FT_Get_Kerning(mFace, previous, glyph_index, + FT_KERNING_DEFAULT, &delta); + width += delta.x >> 6; + } +*/ + error = FT_Load_Glyph(mFace, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_NO_AUTOHINT); + if (error) + continue; + + width += mFace->glyph->advance.x; + previous = glyph_index; + } + + return NSToCoordRound((width >> 6) * mDev2App); +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetLeading(nscoord& aLeading) +{ + aLeading = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesFontMetrics::GetNormalLineHeight(nscoord& aLineHeight) +{ + aLineHeight = 10; + return NS_OK; +} diff --git a/gfx/src/thebes/nsThebesFontMetrics.h b/gfx/src/thebes/nsThebesFontMetrics.h new file mode 100644 index 00000000000..aaac5927cde --- /dev/null +++ b/gfx/src/thebes/nsThebesFontMetrics.h @@ -0,0 +1,102 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef NSTHEBESFONTMETRICS__H__ +#define NSTHEBESFONTMETRICS__H__ + +#include "nsIFontMetrics.h" +#include "nsCOMPtr.h" +#include "nsIDeviceContext.h" +#include "nsIAtom.h" + +#include +#include FT_FREETYPE_H + +class nsThebesFontMetrics : public nsIFontMetrics +{ +public: + nsThebesFontMetrics(); + virtual ~nsThebesFontMetrics(); + + NS_DECL_ISUPPORTS + + NS_IMETHOD Init(const nsFont& aFont, nsIAtom* aLangGroup, + nsIDeviceContext *aContext); + NS_IMETHOD Destroy(); + NS_IMETHOD GetXHeight(nscoord& aResult); + NS_IMETHOD GetSuperscriptOffset(nscoord& aResult); + NS_IMETHOD GetSubscriptOffset(nscoord& aResult); + NS_IMETHOD GetStrikeout(nscoord& aOffset, nscoord& aSize); + NS_IMETHOD GetUnderline(nscoord& aOffset, nscoord& aSize); + NS_IMETHOD GetHeight(nscoord &aHeight); + NS_IMETHOD GetInternalLeading(nscoord &aLeading); + NS_IMETHOD GetExternalLeading(nscoord &aLeading); + NS_IMETHOD GetEmHeight(nscoord &aHeight); + NS_IMETHOD GetEmAscent(nscoord &aAscent); + NS_IMETHOD GetEmDescent(nscoord &aDescent); + NS_IMETHOD GetMaxHeight(nscoord &aHeight); + NS_IMETHOD GetMaxAscent(nscoord &aAscent); + NS_IMETHOD GetMaxDescent(nscoord &aDescent); + NS_IMETHOD GetMaxAdvance(nscoord &aAdvance); + NS_IMETHOD GetLangGroup(nsIAtom** aLangGroup); + NS_IMETHOD GetFontHandle(nsFontHandle &aHandle); + NS_IMETHOD GetAveCharWidth(nscoord& aAveCharWidth); + NS_IMETHOD GetSpaceWidth(nscoord& aSpaceCharWidth); + NS_IMETHOD GetLeading(nscoord& aLeading); + NS_IMETHOD GetNormalLineHeight(nscoord& aLineHeight); + +/* local methods */ + nscoord MeasureString(const char *aString, PRUint32 aLength); + nscoord MeasureString(const PRUnichar *aString, PRUint32 aLength); + +private: + FT_Face mFace; + nsCOMPtr mDeviceContext; + nsCOMPtr mLangGroup; + float mDev2App; + + long mMaxAscent; + long mMaxDescent; + short mMaxAdvance; + + long mUnderlineOffset; + long mUnderlineHeight; +}; + +#endif /* NSTHEBESFONTMETRICS__H__ */ diff --git a/gfx/src/thebes/nsThebesGfxFactory.cpp b/gfx/src/thebes/nsThebesGfxFactory.cpp new file mode 100644 index 00000000000..2a5f7d10808 --- /dev/null +++ b/gfx/src/thebes/nsThebesGfxFactory.cpp @@ -0,0 +1,166 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIGenericFactory.h" +#include "nsIModule.h" +#include "nsCOMPtr.h" +#include "nsGfxCIID.h" + +#include "nsScriptableRegion.h" +#include "gfxImageFrame.h" + +#include "nsThebesDeviceContext.h" +#include "nsThebesRenderingContext.h" +#include "nsThebesImage.h" +#include "nsThebesRegion.h" +#include "nsThebesScreen.h" +#include "nsThebesScreenManager.h" +#include "nsThebesBlender.h" + +#ifdef MOZ_ENABLE_PANGO +#include "nsFontMetricsPango.h" +NS_GENERIC_FACTORY_CONSTRUCTOR(nsFontMetricsPango) +#endif +#ifdef XP_WIN +#include "nsFontMetricsWin2.h" +NS_GENERIC_FACTORY_CONSTRUCTOR(nsFontMetricsWin) +#endif + +NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesBlender) +NS_GENERIC_FACTORY_CONSTRUCTOR(gfxImageFrame) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesDeviceContext) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesRenderingContext) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesImage) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesRegion) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsThebesScreenManager) + +static NS_IMETHODIMP nsScriptableRegionConstructor(nsISupports *aOuter, REFNSIID aIID, void **aResult) +{ + nsresult rv; + + nsIScriptableRegion *inst = nsnull; + + if ( !aResult ) + { + rv = NS_ERROR_NULL_POINTER; + return rv; + } + *aResult = nsnull; + if (aOuter) + { + rv = NS_ERROR_NO_AGGREGATION; + return rv; + } + + nsCOMPtr rgn; + NS_NEWXPCOM(rgn, nsThebesRegion); + nsCOMPtr scriptableRgn; + if (rgn != nsnull) + { + scriptableRgn = new nsScriptableRegion(rgn); + inst = scriptableRgn; + } + if (!inst) + { + rv = NS_ERROR_OUT_OF_MEMORY; + return rv; + } + NS_ADDREF(inst); + // release our variable above now that we have created our owning + // reference - we don't want this to go out of scope early! + scriptableRgn = nsnull; + rv = inst->QueryInterface(aIID, aResult); + NS_RELEASE(inst); + + return rv; +} + +static const nsModuleComponentInfo components[] = +{ + { "Thebes nsFontMetrics", + NS_FONT_METRICS_CID, + "@mozilla.org/gfx/fontmetrics;1", +#ifdef MOZ_ENABLE_PANGO + nsFontMetricsPangoConstructor +#elif XP_WIN + nsFontMetricsWinConstructor +#else +#error write me! +#endif + }, + { "Thebes Device Context", + NS_DEVICE_CONTEXT_CID, + "@mozilla.org/gfx/devicecontext;1", + nsThebesDeviceContextConstructor }, + { "Thebes Rendering Context", + NS_RENDERING_CONTEXT_CID, + "@mozilla.org/gfx/renderingcontext;1", + nsThebesRenderingContextConstructor }, + { "Thebes nsImage", + NS_IMAGE_CID, + "@mozilla.org/gfx/image;1", + nsThebesImageConstructor }, + { "Thebes Region", + NS_REGION_CID, + "@mozilla.org/gfx/region/nsThebes;1", + nsThebesRegionConstructor }, + { "Thebes Screen Manager", + NS_SCREENMANAGER_CID, + "@mozilla.org/gfx/screenmanager;1", + nsThebesScreenManagerConstructor }, + { "Scriptable Region", + NS_SCRIPTABLE_REGION_CID, + "@mozilla.org/gfx/region;1", + nsScriptableRegionConstructor }, + { "Thebes Blender", + NS_BLENDER_CID, + "@mozilla.org/gfx/blender;1", + nsThebesBlenderConstructor }, + { "image frame", + GFX_IMAGEFRAME_CID, + "@mozilla.org/gfx/image/frame;2", + gfxImageFrameConstructor, } +}; + +PR_STATIC_CALLBACK(void) +nsThebesGfxModuleDtor(nsIModule *self) +{ +} + +NS_IMPL_NSGETMODULE_WITH_DTOR(nsGfxModule, components, nsThebesGfxModuleDtor) + diff --git a/gfx/src/thebes/nsThebesImage.cpp b/gfx/src/thebes/nsThebesImage.cpp new file mode 100644 index 00000000000..12d362f2d26 --- /dev/null +++ b/gfx/src/thebes/nsThebesImage.cpp @@ -0,0 +1,643 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsMemory.h" +#include "nsColor.h" + +#include "nsThebesDeviceContext.h" +#include "nsThebesRenderingContext.h" +#include "nsThebesDrawingSurface.h" +#include "nsThebesImage.h" + +#include "gfxContext.h" +#include "gfxPattern.h" + +#ifdef MOZ_ENABLE_GTK2 +#include +#include "gfxXlibSurface.h" + +#ifdef MOZ_ENABLE_GLITZ +#include "glitz-glx.h" +#include "gfxGlitzSurface.h" +#endif +#endif + +static PRUint8 Unpremultiply(PRUint8 aVal, PRUint8 aAlpha); +static void ARGBToThreeChannel(PRUint32* aARGB, PRUint8* aData); +static PRUint8 Premultiply(PRUint8 aVal, PRUint8 aAlpha); +static PRUint32 ThreeChannelToARGB(PRUint8* aData, PRUint8 aAlpha); + +NS_IMPL_ISUPPORTS1(nsThebesImage, nsIImage) + +nsThebesImage::nsThebesImage() + : mWidth(0), + mHeight(0), + mDecoded(0,0,0,0), + mLockedData(nsnull), + mLockedAlpha(nsnull), + mAlphaDepth(0), + mLocked(PR_FALSE), + mHadAnyData(PR_FALSE), + mUpToDate(PR_FALSE) +{ +} + +nsresult +nsThebesImage::Init(PRInt32 aWidth, PRInt32 aHeight, PRInt32 aDepth, nsMaskRequirements aMaskRequirements) +{ + NS_ASSERTION(aDepth == 24, "nsThebesImage::Init called with depth != 24"); + + gfxImageSurface::gfxImageFormat format; + + mWidth = aWidth; + mHeight = aHeight; + + switch(aMaskRequirements) + { + case nsMaskRequirements_kNeeds1Bit: + mAlphaDepth = 1; + format = gfxImageSurface::ImageFormatARGB32; + break; + case nsMaskRequirements_kNeeds8Bit: + mAlphaDepth = 8; + format = gfxImageSurface::ImageFormatARGB32; + break; + default: + mAlphaDepth = 0; + format = gfxImageSurface::ImageFormatARGB32; + break; + } + + mImageSurface = new gfxImageSurface (format, mWidth, mHeight); + memset(mImageSurface->Data(), 0xFF, mHeight * mImageSurface->Stride()); + + return NS_OK; +} + +nsThebesImage::~nsThebesImage() +{ + if (mLockedData) + nsMemory::Free(mLockedData); + if (mLockedAlpha) + nsMemory::Free(mLockedAlpha); +} + +PRInt32 +nsThebesImage::GetBytesPix() +{ + // not including alpha + return 3; +} + +PRBool +nsThebesImage::GetIsRowOrderTopToBottom() +{ + return PR_TRUE; +} + +PRInt32 +nsThebesImage::GetWidth() +{ + return mWidth; +} + +PRInt32 +nsThebesImage::GetHeight() +{ + return mHeight; +} + +PRUint8 * +nsThebesImage::GetBits() +{ + //NS_ASSERTION(mLocked == PR_TRUE, "GetBits called outside of Lock!"); + return mLockedData; +} + +PRInt32 +nsThebesImage::GetLineStride() +{ + return mWidth * 3; +} + +PRBool +nsThebesImage::GetHasAlphaMask() +{ + return mAlphaDepth > 0; +} + +PRUint8 * +nsThebesImage::GetAlphaBits() +{ + //NS_ASSERTION(mLocked == PR_TRUE, "GetAlphaBits called outside of Lock!"); + return (PRUint8 *) mLockedAlpha; +} + +PRInt32 +nsThebesImage::GetAlphaLineStride() +{ + return mAlphaDepth == 1 ? (mWidth+7)/8 : mWidth; +} + +void +nsThebesImage::ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect) +{ + mDecoded.UnionRect(mDecoded, *aUpdateRect); +} + +PRBool +nsThebesImage::GetIsImageComplete() +{ + return mDecoded == nsRect(0, 0, mWidth, mHeight); +} + +nsresult +nsThebesImage::Optimize(nsIDeviceContext* aContext) +{ + UpdateFromLockedData(); + + if (!mOptSurface) { +#ifdef MOZ_ENABLE_GTK2 + if (!nsThebesDrawingSurface::UseGlitz()) { + XRenderPictFormat *fmt = nsnull; + + if (mRealAlphaDepth == 0) { + fmt = gfxXlibSurface::FindRenderFormat(GDK_DISPLAY(), gfxASurface::ImageFormatRGB24); + } else if (mRealAlphaDepth == 1) { + fmt = gfxXlibSurface::FindRenderFormat(GDK_DISPLAY(), gfxASurface::ImageFormatARGB32); + } else if (mRealAlphaDepth == 8) { + fmt = gfxXlibSurface::FindRenderFormat(GDK_DISPLAY(), gfxASurface::ImageFormatARGB32); + } + + /* XXX handle mRealAlphaDepth = 1, and create separate mask */ + + if (fmt) { + mOptSurface = new gfxXlibSurface(GDK_DISPLAY(), + fmt, + mWidth, mHeight); + } + } else { +# ifdef MOZ_ENABLE_GLITZ + // glitz + nsThebesDeviceContext *tdc = NS_STATIC_CAST(nsThebesDeviceContext*, aContext); + glitz_drawable_format_t *gdf = (glitz_drawable_format_t*) tdc->GetGlitzDrawableFormat(); + glitz_drawable_t *gdraw = glitz_glx_create_pbuffer_drawable (GDK_DISPLAY(), + DefaultScreen(GDK_DISPLAY()), + gdf, + mWidth, mHeight); + glitz_format_t *gf = + glitz_find_standard_format (gdraw, GLITZ_STANDARD_ARGB32); + glitz_surface_t *gsurf = + glitz_surface_create (gdraw, + gf, + mWidth, + mHeight, + 0, + NULL); + + glitz_surface_attach (gsurf, gdraw, GLITZ_DRAWABLE_BUFFER_FRONT_COLOR, 0, 0); + mOptSurface = new gfxGlitzSurface (gdraw, gsurf, PR_TRUE); +# endif + } +#endif + } + + if (mOptSurface) { + nsRefPtr tmpCtx(new gfxContext(mOptSurface)); + tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE); + tmpCtx->SetSource(mImageSurface); +#if 0 + PRUint32 k = (PRUint32) this; + k |= 0xff000000; + tmpCtx->SetColor(gfxRGBA(nscolor(k))); +#endif + tmpCtx->Paint(); +#if 0 + tmpCtx->NewPath(); + tmpCtx->Rectangle(gfxRect(0, 0, mWidth, mHeight)); + tmpCtx->Fill(); +#endif + } + + return NS_OK; +} + +nsColorMap * +nsThebesImage::GetColorMap() +{ + return NULL; +} + +PRInt8 +nsThebesImage::GetAlphaDepth() +{ + return mAlphaDepth; +} + +void * +nsThebesImage::GetBitInfo() +{ + return NULL; +} + +NS_IMETHODIMP +nsThebesImage::LockImagePixels(PRBool aMaskPixels) +{ + //NS_ASSERTION(!mLocked, "LockImagePixels called on already locked image!"); + PRUint32 tmpSize; + + // if we already have locked data allocated, + // and we have some data, and we're not up to date, + // then don't bother updating frmo the image surface + // to the 2 buffers -- the 2 buffers are more current, + // because UpdateFromLockedData() hasn't been called yet. + // + // UpdateFromLockedData() is only called right before we + // actually try to draw, because the image decoders + // will call Lock/Unlock pretty frequently as they decode + // rows of the image. + + if (mLockedData && mHadAnyData && !mUpToDate) + return NS_OK; + + if (mAlphaDepth > 0) { + NS_ASSERTION(mAlphaDepth == 1 || mAlphaDepth == 8, + "Bad alpha depth"); + tmpSize = mHeight*(mAlphaDepth == 1 ? ((mWidth+7)/8) : mWidth); + if (!mLockedAlpha) + mLockedAlpha = (PRUint8*)nsMemory::Alloc(tmpSize); + if (!mLockedAlpha) + return NS_ERROR_OUT_OF_MEMORY; + } + + tmpSize = mWidth * mHeight * 3; + if (!mLockedData) + mLockedData = (PRUint8*)nsMemory::Alloc(tmpSize); + if (!mLockedData) + return NS_ERROR_OUT_OF_MEMORY; + + if (mAlphaDepth > 0 && mHadAnyData) { + // fill from existing ARGB buffer + if (mAlphaDepth == 8) { + PRInt32 destCount = 0; + + for (PRInt32 row = 0; row < mHeight; ++row) { + PRUint32* srcRow = (PRUint32*) (mImageSurface->Data() + (mImageSurface->Stride()*row)); + + for (PRInt32 col = 0; col < mWidth; ++col) { + mLockedAlpha[destCount++] = (PRUint8)(srcRow[col] >> 24); + } + } + } else { + PRInt32 i = 0; + for (PRInt32 row = 0; row < mHeight; ++row) { + PRUint32* srcRow = (PRUint32*) (mImageSurface->Data() + (mImageSurface->Stride()*row)); + PRUint8 alphaBits = 0; + + for (PRInt32 col = 0; col < mWidth; ++col) { + PRUint8 mask = 1 << (7 - (col&7)); + + if (srcRow[col] & 0xFF000000) { + alphaBits |= mask; + } + if (mask == 0x01) { + // This mask byte is complete, write it back + mLockedAlpha[i] = alphaBits; + alphaBits = 0; + ++i; + } + } + if (mWidth & 7) { + // write back the incomplete alpha mask + mLockedAlpha[i] = alphaBits; + ++i; + } + } + } + } + + if (mHadAnyData) { + int destCount = 0; + + for (PRInt32 row = 0; row < mHeight; ++row) { + PRUint32* srcRow = (PRUint32*) (mImageSurface->Data() + (mImageSurface->Stride()*row)); + + for (PRInt32 col = 0; col < mWidth; ++col) { + ARGBToThreeChannel(&srcRow[col], &mLockedData[destCount*3]); + destCount++; + } + } + } + + mLocked = PR_TRUE; + + mOptSurface = nsnull; + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesImage::UnlockImagePixels(PRBool aMaskPixels) +{ + //NS_ASSERTION(mLocked, "UnlockImagePixels called on unlocked image!"); + + mLocked = PR_FALSE; + mUpToDate = PR_FALSE; + + return NS_OK; +} + +void +nsThebesImage::UpdateFromLockedData() +{ + if (!mLockedData || mUpToDate) + return; + + mUpToDate = PR_TRUE; + + NS_ASSERTION(mAlphaDepth == 0 || mAlphaDepth == 1 || mAlphaDepth == 8, + "Bad alpha depth"); + + mRealAlphaDepth = 0; + + PRInt32 alphaIndex = 0; + PRInt32 lockedBufIndex = 0; + for (PRInt32 row = 0; row < mHeight; ++row) { + PRUint32 *destPtr = (PRUint32*) (mImageSurface->Data() + (row * mImageSurface->Stride())); + + for (PRInt32 col = 0; col < mWidth; ++col) { + PRUint8 alpha = 0xFF; + + if (mAlphaDepth == 1) { + PRUint8 mask = 1 << (7 - (col&7)); + if (!(mLockedAlpha[alphaIndex] & mask)) { + alpha = 0; + } + if (mask == 0x01) { + ++alphaIndex; + } + } else if (mAlphaDepth == 8) { + alpha = mLockedAlpha[lockedBufIndex]; + } + + if (mRealAlphaDepth == 0 && alpha != 0xff) + mRealAlphaDepth = mAlphaDepth; + + *destPtr++ = + ThreeChannelToARGB(&mLockedData[lockedBufIndex*3], alpha); + + ++lockedBufIndex; + } + if (mAlphaDepth == 1) { + if (mWidth & 7) { + ++alphaIndex; + } + } + } + + if (PR_FALSE) { // Enabling this saves memory but can lead to pathologically bad + // performance. Would also cause problems with premultiply/unpremultiply too + nsMemory::Free(mLockedData); + mLockedData = nsnull; + if (mLockedAlpha) { + nsMemory::Free(mLockedAlpha); + mLockedAlpha = nsnull; + } + } + + mHadAnyData = PR_TRUE; +} + +/* NB: These are pixels, not twips. */ +NS_IMETHODIMP +nsThebesImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface, + PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) +{ + return Draw(aContext, aSurface, 0, 0, mWidth, mHeight, aX, aY, aWidth, aHeight); +} + +/* NB: These are pixels, not twips. */ +/* BUT nsRenderingContextImpl's DrawImage calls this with twips. */ +NS_IMETHODIMP +nsThebesImage::Draw(nsIRenderingContext &aContext, nsIDrawingSurface *aSurface, + PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, + PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) +{ + UpdateFromLockedData(); + + nsThebesRenderingContext *thebesRC = NS_STATIC_CAST(nsThebesRenderingContext*, &aContext); + gfxContext *ctx = thebesRC->Thebes(); + +#if 0 + fprintf (stderr, "nsThebesImage::Draw src [%d %d %d %d] dest [%d %d %d %d] tx [%f %f]\n", + aSX, aSY, aSWidth, aSHeight, aDX, aDY, aDWidth, aDHeight, + ctx->CurrentMatrix().GetTranslation().x, ctx->CurrentMatrix().GetTranslation().y); +#endif + + gfxRect sr(aSX, aSY, aSWidth, aSHeight); + gfxRect dr(aDX, aDY, aDWidth, aDHeight); + + gfxMatrix mat; + mat.Translate(gfxPoint(aSX, aSY)); + mat.Scale(double(aSWidth)/aDWidth, double(aSHeight)/aDHeight); + + nsRefPtr pat = new gfxPattern(ThebesSurface()); + pat->SetMatrix(mat); + + ctx->NewPath(); + ctx->PixelSnappedRectangleAndSetPattern(dr, pat); + ctx->Fill(); + + return NS_OK; +} + +/* DrawTile is always relative to the device; never relative to the current + * transformation matrix on the device. This is where the IdentityMatrix() bits + * come from. + */ +/* NB: Still pixels! */ +NS_IMETHODIMP +nsThebesImage::DrawTile(nsIRenderingContext &aContext, + nsIDrawingSurface *aSurface, + PRInt32 aSXOffset, PRInt32 aSYOffset, + PRInt32 aPadX, PRInt32 aPadY, + const nsRect &aTileRect) +{ + UpdateFromLockedData(); + + nsThebesRenderingContext *thebesRC = NS_STATIC_CAST(nsThebesRenderingContext*, &aContext); + gfxContext *ctx = thebesRC->Thebes(); + + if (aTileRect.width <= 0 || aTileRect.height <= 0) + return NS_OK; + + if (aPadX || aPadY) + fprintf (stderr, "Warning: nsThebesImage::DrawTile given padX(%d)/padY(%d), ignoring\n", aPadX, aPadY); + +#if 0 + fprintf (stderr, "****** nsThebesImage::DrawTile: (%d,%d [%d, %d]) -> (%d,%d,%d,%d)\n", + aSXOffset, aSYOffset, mWidth, mHeight, + aTileRect.x, aTileRect.y, + aTileRect.width, aTileRect.height); +#endif + + gfxMatrix savedMatrix = ctx->CurrentMatrix(); + PRBool reallyRepeating = PR_TRUE; + + /* Let's figure out if this really is repeating, or if we're just drawing a subrect */ + if (aTileRect.x + aTileRect.width > mWidth || + aTileRect.y + aTileRect.height > mHeight) + { + reallyRepeating = PR_TRUE; + } + + ctx->IdentityMatrix(); + + PRInt32 x0 = aTileRect.x - aSXOffset; + PRInt32 y0 = aTileRect.y - aSYOffset; + + ctx->Translate(gfxPoint(x0, y0)); + + nsRefPtr pat = new gfxPattern(ThebesSurface()); + if (reallyRepeating) + pat->SetExtend(CAIRO_EXTEND_REPEAT); + + ctx->NewPath(); + ctx->PixelSnappedRectangleAndSetPattern(gfxRect(aSXOffset, aSYOffset, + aTileRect.width, aTileRect.height), + pat); + ctx->Fill(); + + ctx->SetMatrix(savedMatrix); + + return NS_OK; +} + +/* This is only used by the GIF decoder, via gfxImageFrame::DrawTo */ +NS_IMETHODIMP +nsThebesImage::DrawToImage(nsIImage* aDstImage, PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight) +{ + UpdateFromLockedData(); + + nsThebesImage *dstThebesImage = NS_STATIC_CAST(nsThebesImage*, aDstImage); + + nsRefPtr dst = new gfxContext(dstThebesImage->ThebesSurface()); + + dst->NewPath(); + // We don't use PixelSnappedRectangleAndSetPattern because if + // these coords aren't already pixel aligned, we've lost + // before we've even begun. + dst->Translate(gfxPoint(aDX, aDY)); + dst->Rectangle(gfxRect(0, 0, aDWidth, aDHeight), PR_TRUE); + dst->Scale(double(aDWidth)/mWidth, double(aDHeight)/mHeight); + + dst->SetSource(ThebesSurface()); + dst->Fill(); + + return NS_OK; +} + +/** Image conversion utils **/ + +static PRUint8 Unpremultiply(PRUint8 aVal, PRUint8 aAlpha) { + return (aVal*255)/aAlpha; +} + +static void ARGBToThreeChannel(PRUint32* aARGB, PRUint8* aData) { + PRUint8 a = (PRUint8)(*aARGB >> 24); + PRUint8 r = (PRUint8)(*aARGB >> 16); + PRUint8 g = (PRUint8)(*aARGB >> 8); + PRUint8 b = (PRUint8)(*aARGB >> 0); + + if (a != 0xFF) { + if (a == 0) { + r = 0; + g = 0; + b = 0; + } else { + r = Unpremultiply(r, a); + g = Unpremultiply(g, a); + b = Unpremultiply(b, a); + } + } + +#if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON) + // BGR format; assume little-endian system +#ifndef IS_LITTLE_ENDIAN +#error Strange big-endian/OS combination +#endif + // BGR, blue byte first + aData[0] = b; aData[1] = g; aData[2] = r; +#else + // RGB, red byte first + aData[0] = r; aData[1] = g; aData[2] = b; +#endif +} + +static PRUint8 Premultiply(PRUint8 aVal, PRUint8 aAlpha) { + PRUint32 r; + FAST_DIVIDE_BY_255(r, aVal*aAlpha); + return (PRUint8)r; +} + +static PRUint32 ThreeChannelToARGB(PRUint8* aData, PRUint8 aAlpha) { + PRUint8 r, g, b; +#if defined(XP_WIN) || defined(XP_OS2) || defined(XP_BEOS) || defined(MOZ_WIDGET_PHOTON) + // BGR format; assume little-endian system +#ifndef IS_LITTLE_ENDIAN +#error Strange big-endian/OS combination +#endif + // BGR, blue byte first + b = aData[0]; g = aData[1]; r = aData[2]; +#else + // RGB, red byte first + r = aData[0]; g = aData[1]; b = aData[2]; +#endif + if (aAlpha != 0xFF) { + if (aAlpha == 0) { + r = 0; + g = 0; + b = 0; + } else { + r = Premultiply(r, aAlpha); + g = Premultiply(g, aAlpha); + b = Premultiply(b, aAlpha); + } + } + return (aAlpha << 24) | (r << 16) | (g << 8) | b; +} diff --git a/gfx/src/thebes/nsThebesImage.h b/gfx/src/thebes/nsThebesImage.h new file mode 100644 index 00000000000..50b7e8c3190 --- /dev/null +++ b/gfx/src/thebes/nsThebesImage.h @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _NSTHEBESIMAGE_H_ +#define _NSTHEBESIMAGE_H_ + +#include "nsIImage.h" + +#include "gfxASurface.h" +#include "gfxImageSurface.h" + +class nsThebesImage : public nsIImage +{ +public: + nsThebesImage(); + ~nsThebesImage(); + + NS_DECL_ISUPPORTS + + virtual nsresult Init(PRInt32 aWidth, PRInt32 aHeight, + PRInt32 aDepth, nsMaskRequirements aMaskRequirements); + virtual PRInt32 GetBytesPix(); + virtual PRBool GetIsRowOrderTopToBottom(); + virtual PRInt32 GetWidth(); + virtual PRInt32 GetHeight(); + virtual PRUint8 *GetBits(); + virtual PRInt32 GetLineStride(); + virtual PRBool GetHasAlphaMask(); + virtual PRUint8 *GetAlphaBits(); + virtual PRInt32 GetAlphaLineStride(); + virtual PRBool GetIsImageComplete(); + virtual void ImageUpdated(nsIDeviceContext *aContext, PRUint8 aFlags, nsRect *aUpdateRect); + virtual nsresult Optimize(nsIDeviceContext* aContext); + virtual nsColorMap *GetColorMap(); + + NS_IMETHOD Draw(nsIRenderingContext &aContext, + nsIDrawingSurface *aSurface, + PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight); + NS_IMETHOD Draw(nsIRenderingContext &aContext, + nsIDrawingSurface *aSurface, + PRInt32 aSX, PRInt32 aSY, PRInt32 aSWidth, PRInt32 aSHeight, + PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight); + NS_IMETHOD DrawTile(nsIRenderingContext &aContext, + nsIDrawingSurface *aSurface, + PRInt32 aSXOffset, PRInt32 aSYOffset, + PRInt32 aPadX, PRInt32 aPadY, + const nsRect &aTileRect); + NS_IMETHOD DrawToImage(nsIImage* aDstImage, + PRInt32 aDX, PRInt32 aDY, PRInt32 aDWidth, PRInt32 aDHeight); + + virtual PRInt8 GetAlphaDepth(); + virtual void* GetBitInfo(); + NS_IMETHOD LockImagePixels(PRBool aMaskPixels); + NS_IMETHOD UnlockImagePixels(PRBool aMaskPixels); + + void UpdateFromLockedData(); + + gfxASurface* ThebesSurface() { + if (mOptSurface) + return mOptSurface; + return mImageSurface; + } + +protected: + + PRInt32 mWidth; + PRInt32 mHeight; + nsRect mDecoded; + + nsRefPtr mImageSurface; + nsRefPtr mOptSurface; + + // temporary buffers for Lock() + PRUint8* mLockedData; + PRUint8* mLockedAlpha; + + PRUint8 mAlphaDepth; + PRUint8 mRealAlphaDepth; + PRPackedBool mLocked; + PRPackedBool mHadAnyData; + PRPackedBool mUpToDate; +}; + +#endif /* _NSTHEBESIMAGE_H_ */ diff --git a/gfx/src/thebes/nsThebesRegion.cpp b/gfx/src/thebes/nsThebesRegion.cpp new file mode 100644 index 00000000000..3df1b73d512 --- /dev/null +++ b/gfx/src/thebes/nsThebesRegion.cpp @@ -0,0 +1,212 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsThebesRegion.h" + +NS_IMPL_ISUPPORTS1(nsThebesRegion, nsIRegion) + +nsThebesRegion::nsThebesRegion() +{ + NS_INIT_ISUPPORTS(); +} + +nsresult nsThebesRegion::Init (void) +{ + mRegion.SetEmpty(); + return NS_OK; +} + +void nsThebesRegion::SetTo (const nsIRegion &aRegion) +{ + const nsThebesRegion* pRegion = NS_STATIC_CAST (const nsThebesRegion*, &aRegion); + mRegion = pRegion->mRegion; +} + +void nsThebesRegion::SetTo (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) +{ + mRegion = nsRect (aX, aY, aWidth, aHeight); +} + +void nsThebesRegion::Intersect (const nsIRegion &aRegion) +{ + const nsThebesRegion* pRegion = NS_STATIC_CAST (const nsThebesRegion*, &aRegion); + mRegion.And (mRegion, pRegion->mRegion); +} + +void nsThebesRegion::Intersect (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) +{ + mRegion.And (mRegion, nsRect (aX, aY, aWidth, aHeight)); +} + +void nsThebesRegion::Union (const nsIRegion &aRegion) +{ + const nsThebesRegion* pRegion = NS_STATIC_CAST (const nsThebesRegion*, &aRegion); + mRegion.Or (mRegion, pRegion->mRegion); +} + +void nsThebesRegion::Union (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) +{ + mRegion.Or (mRegion, nsRect (aX, aY, aWidth, aHeight)); +} + +void nsThebesRegion::Subtract (const nsIRegion &aRegion) +{ + const nsThebesRegion* pRegion = NS_STATIC_CAST (const nsThebesRegion*, &aRegion); + mRegion.Sub (mRegion, pRegion->mRegion); +} + +void nsThebesRegion::Subtract (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) +{ + mRegion.Sub (mRegion, nsRect (aX, aY, aWidth, aHeight)); +} + +PRBool nsThebesRegion::IsEmpty (void) +{ + return mRegion.IsEmpty (); +} + +PRBool nsThebesRegion::IsEqual (const nsIRegion &aRegion) +{ + const nsThebesRegion* pRegion = NS_STATIC_CAST (const nsThebesRegion*, &aRegion); + return mRegion.IsEqual (pRegion->mRegion); +} + +void nsThebesRegion::GetBoundingBox (PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight) +{ + nsRect BoundRect; + BoundRect = mRegion.GetBounds(); + *aX = BoundRect.x; + *aY = BoundRect.y; + *aWidth = BoundRect.width; + *aHeight = BoundRect.height; +} + +void nsThebesRegion::Offset (PRInt32 aXOffset, PRInt32 aYOffset) +{ + mRegion.MoveBy (aXOffset, aYOffset); +} + +PRBool nsThebesRegion::ContainsRect (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight) +{ + nsRegion TmpRegion; + TmpRegion.And (mRegion, nsRect (aX, aY, aWidth, aHeight)); + return (!TmpRegion.IsEmpty ()); +} + +NS_IMETHODIMP +nsThebesRegion::GetRects (nsRegionRectSet **aRects) +{ + if (!aRects) + return NS_ERROR_NULL_POINTER; + + nsRegionRectSet* pRegionSet = *aRects; + PRUint32 NumRects = mRegion.GetNumRects (); + + if (pRegionSet == nsnull) // Not yet allocated + { + PRUint8* pBuf = new PRUint8 [sizeof (nsRegionRectSet) + NumRects * sizeof (nsRegionRect)]; + pRegionSet = NS_REINTERPRET_CAST (nsRegionRectSet*, pBuf); + pRegionSet->mRectsLen = NumRects + 1; + } else // Already allocated in previous call + { + if (NumRects > pRegionSet->mRectsLen) // passed array is not big enough - reallocate it. + { + delete [] NS_REINTERPRET_CAST (PRUint8*, pRegionSet); + PRUint8* pBuf = new PRUint8 [sizeof (nsRegionRectSet) + NumRects * sizeof (nsRegionRect)]; + pRegionSet = NS_REINTERPRET_CAST (nsRegionRectSet*, pBuf); + pRegionSet->mRectsLen = NumRects + 1; + } + } + pRegionSet->mNumRects = NumRects; + *aRects = pRegionSet; + + + nsRegionRectIterator ri (mRegion); + nsRegionRect* pDest = &pRegionSet->mRects [0]; + const nsRect* pSrc; + + while ((pSrc = ri.Next ())) + { + pDest->x = pSrc->x; + pDest->y = pSrc->y; + pDest->width = pSrc->width; + pDest->height = pSrc->height; + + pDest++; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRegion::FreeRects (nsRegionRectSet *aRects) +{ + if (!aRects) + return NS_ERROR_NULL_POINTER; + + delete [] NS_REINTERPRET_CAST (PRUint8*, aRects); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRegion::GetNativeRegion (void *&aRegion) const +{ + aRegion = 0; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRegion::GetRegionComplexity (nsRegionComplexity &aComplexity) const +{ + switch (mRegion.GetNumRects ()) + { + case 0: aComplexity = eRegionComplexity_empty; break; + case 1: aComplexity = eRegionComplexity_rect; break; + default: aComplexity = eRegionComplexity_complex; break; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRegion::GetNumRects (PRUint32 *aRects) const +{ + *aRects = mRegion.GetNumRects (); + return NS_OK; +} diff --git a/gfx/src/thebes/nsThebesRegion.h b/gfx/src/thebes/nsThebesRegion.h new file mode 100644 index 00000000000..e2f33128eb1 --- /dev/null +++ b/gfx/src/thebes/nsThebesRegion.h @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef NSTHEBESREGION__H__ +#define NSTHEBESREGION__H__ + +#include "nsIRegion.h" +#include "nsRegion.h" + +class nsThebesRegion : public nsIRegion +{ +public: + nsThebesRegion(); + + NS_DECL_ISUPPORTS + + // nsIRegion + nsresult Init (void); + void SetTo (const nsIRegion &aRegion); + void SetTo (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight); + void Intersect (const nsIRegion &aRegion); + void Intersect (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight); + void Union (const nsIRegion &aRegion); + void Union (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight); + void Subtract (const nsIRegion &aRegion); + void Subtract (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight); + PRBool IsEmpty (void); + PRBool IsEqual (const nsIRegion &aRegion); + void GetBoundingBox (PRInt32 *aX, PRInt32 *aY, PRInt32 *aWidth, PRInt32 *aHeight); + void Offset (PRInt32 aXOffset, PRInt32 aYOffset); + PRBool ContainsRect (PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeight); + NS_IMETHOD GetRects (nsRegionRectSet **aRects); + NS_IMETHOD FreeRects (nsRegionRectSet *aRects); + NS_IMETHOD GetNativeRegion (void *&aRegion) const; + NS_IMETHOD GetRegionComplexity (nsRegionComplexity &aComplexity) const; + NS_IMETHOD GetNumRects (PRUint32 *aRects) const; + +protected: + nsRegion mRegion; +}; + +#endif diff --git a/gfx/src/thebes/nsThebesRenderingContext.cpp b/gfx/src/thebes/nsThebesRenderingContext.cpp new file mode 100644 index 00000000000..7ba61f10878 --- /dev/null +++ b/gfx/src/thebes/nsThebesRenderingContext.cpp @@ -0,0 +1,1404 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2004 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Stuart Parmenter + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsThebesRenderingContext.h" +#include "nsThebesDeviceContext.h" + +#include "nsRect.h" +#include "nsString.h" +#include "nsTransform2D.h" +#include "nsIRegion.h" +#include "nsIServiceManager.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsGfxCIID.h" + +#include "imgIContainer.h" +#include "gfxIImageFrame.h" +#include "nsIImage.h" + +#include "nsIThebesFontMetrics.h" +#include "nsThebesDrawingSurface.h" +#include "nsThebesRegion.h" +#include "nsThebesImage.h" + +#include +#include +#include + +#ifdef XP_WIN +#include "gfxWindowsSurface.h" +#endif + +static NS_DEFINE_CID(kRegionCID, NS_REGION_CID); + +////////////////////////////////////////////////////////////////////// + +#define FROM_TWIPS(_x) ((gfxFloat)((_x)/(mP2T))) +#define FROM_TWIPS_INT(_x) (NSToIntRound((gfxFloat)((_x)/(mP2T)))) +#define TO_TWIPS(_x) ((nscoord)((_x)*(mP2T))) +#define GFX_RECT_FROM_TWIPS_RECT(_r) (gfxRect(FROM_TWIPS((_r).x), FROM_TWIPS((_r).y), FROM_TWIPS((_r).width), FROM_TWIPS((_r).height))) + +////////////////////////////////////////////////////////////////////// + +NS_IMPL_ISUPPORTS2(nsThebesRenderingContext, nsIRenderingContext, nsIThebesRenderingContext) + +nsThebesRenderingContext::nsThebesRenderingContext() : + mLineStyle(nsLineStyle_kNone) +{ +} + +nsThebesRenderingContext::~nsThebesRenderingContext() +{ +} + +////////////////////////////////////////////////////////////////////// +//// nsIRenderingContext + +NS_IMETHODIMP +nsThebesRenderingContext::Init(nsIDeviceContext* aContext, nsIWidget *aWidget) +{ + + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Init ctx %p widget %p\n", this, aContext, aWidget)); + + nsThebesDeviceContext *thebesDC = NS_STATIC_CAST(nsThebesDeviceContext*, aContext); + + mDeviceContext = aContext; + mWidget = aWidget; + + mDrawingSurface = new nsThebesDrawingSurface(); + mDrawingSurface->Init(thebesDC, aWidget); + + mThebes = new gfxContext(mDrawingSurface->GetThebesSurface()); + + //mThebes->SetColor(gfxRGBA(0.9, 0.0, 0.0, 0.3)); + //mThebes->Paint(); + + //mThebes->Translate(gfxPoint(300,0)); + //mThebes->Rotate(M_PI/4); + + return (CommonInit()); +} + +NS_IMETHODIMP +nsThebesRenderingContext::Init(nsIDeviceContext* aContext, nsIDrawingSurface *aSurface) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Init ctx %p ds %p\n", this, aContext, aSurface)); + + nsThebesDrawingSurface *cds = (nsThebesDrawingSurface *) aSurface; + + mDeviceContext = aContext; + mWidget = nsnull; + + mDrawingSurface = (nsThebesDrawingSurface *) cds; + + mThebes = new gfxContext(mDrawingSurface->GetThebesSurface()); + + //mThebes->SetColor(gfxRGBA(1.0, 1.0, 1.0, 1.0)); + //mThebes->Paint(); + + return (CommonInit()); +} + +NS_IMETHODIMP +nsThebesRenderingContext::CommonInit(void) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::CommonInit\n", this)); + + mThebes->SetLineWidth(1.0); + + mT2P = mDeviceContext->AppUnitsToDevUnits(); + mP2T = mDeviceContext->DevUnitsToAppUnits(); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::Reset(void) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Reset\n", this)); + + nsRefPtr surf = mThebes->CurrentSurface(); + + mThebes = new gfxContext(surf); + + return (CommonInit()); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetDeviceContext(nsIDeviceContext *& aDeviceContext) +{ + aDeviceContext = mDeviceContext; + NS_IF_ADDREF(aDeviceContext); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::SelectOffScreenDrawingSurface(nsIDrawingSurface *aSurface) +{ + NS_WARNING("Unimplemented function called"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetDrawingSurface(nsIDrawingSurface **aSurface) +{ + *aSurface = mDrawingSurface; + NS_IF_ADDREF (*aSurface); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::PushTranslation(PushedTranslation* aState) +{ + //PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PushTranslation\n", this)); + + // XXX this is slow! + PushState(); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::PopTranslation(PushedTranslation* aState) +{ + //PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PopTranslation\n", this)); + + // XXX this is slow! + PopState(); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetHints(PRUint32& aResult) +{ + aResult = 0; + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::PushState() +{ + //PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PushState\n", this)); + + mThebes->Save(); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::PopState() +{ + //PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::PopState\n", this)); + + mThebes->Restore(); + return NS_OK; +} + +// +// clipping +// + +NS_IMETHODIMP +nsThebesRenderingContext::SetClipRect(const nsRect& aRect, + nsClipCombine aCombine) +{ + //return NS_OK; + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetClipRect [%d,%d,%d,%d] %d\n", this, aRect.x, aRect.y, aRect.width, aRect.height, aCombine)); + + if (aCombine == nsClipCombine_kReplace) + mThebes->ResetClip(); + else if (aCombine != nsClipCombine_kIntersect) + NS_WARNING("Unexpected usage of SetClipRect"); + + mThebes->NewPath(); + mThebes->Rectangle(GFX_RECT_FROM_TWIPS_RECT(aRect), PR_TRUE); + mThebes->Clip(); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::SetClipRegion(const nsIRegion& pxRegion, + nsClipCombine aCombine) +{ + //return NS_OK; + // Region is in device coords, no transformation. + // This should only be called when there is no transform in place, when we + // we just start painting a widget. The region is set by the platform paint + // routine. + NS_ASSERTION(aCombine == nsClipCombine_kReplace, + "Unexpected usage of SetClipRegion"); + + nsRegionComplexity cplx; + pxRegion.GetRegionComplexity(cplx); + + gfxMatrix mat = mThebes->CurrentMatrix(); + mThebes->IdentityMatrix(); + + mThebes->ResetClip(); + // GetBoundingBox, GetRects, FreeRects are non-const + nsIRegion *evilPxRegion = NS_CONST_CAST(nsIRegion*, &pxRegion); + if (cplx == eRegionComplexity_rect) { + PRInt32 x, y, w, h; + evilPxRegion->GetBoundingBox(&x, &y, &w, &h); + + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetClipRegion %d [%d,%d,%d,%d]", this, cplx, x, y, w, h)); + + mThebes->NewPath(); + mThebes->Rectangle(gfxRect(x, y, w, h), PR_TRUE); + mThebes->Clip(); + } else if (cplx == eRegionComplexity_complex) { + nsRegionRectSet *rects = nsnull; + nsresult rv = evilPxRegion->GetRects (&rects); + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetClipRegion %d %d rects", this, cplx, rects->mNumRects)); + if (NS_FAILED(rv) || !rects) { + mThebes->SetMatrix(mat); + return rv; + } + + mThebes->NewPath(); + for (PRUint32 i = 0; i < rects->mNumRects; i++) { + mThebes->Rectangle(gfxRect(rects->mRects[i].x, + rects->mRects[i].y, + rects->mRects[i].width, + rects->mRects[i].height), + PR_TRUE); + } + + mThebes->Clip(); + + evilPxRegion->FreeRects (rects); + } + + mThebes->SetMatrix(mat); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetClipRegion(nsIRegion **aRegion) +{ + // used in a few misc places (FontMetricsXft, generic RenderingContextImpl for DrawImage, + // and SVGGDIPlusCanvas + NS_WARNING("Unimplemented function called"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +// only gets called from the caret blinker. +NS_IMETHODIMP +nsThebesRenderingContext::FlushRect(const nsRect& aRect) +{ + return FlushRect(aRect.x, aRect.y, aRect.width, aRect.height); +} + +NS_IMETHODIMP +nsThebesRenderingContext::FlushRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) +{ + return NS_OK; +} + +// +// other junk +// + +NS_IMETHODIMP +nsThebesRenderingContext::SetLineStyle(nsLineStyle aLineStyle) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetLineStyle %d\n", this, aLineStyle)); + switch (aLineStyle) { + case nsLineStyle_kSolid: + mThebes->SetDash(gfxContext::gfxLineSolid); + break; + case nsLineStyle_kDashed: + mThebes->SetDash(gfxContext::gfxLineDashed); + break; + case nsLineStyle_kDotted: + mThebes->SetDash(gfxContext::gfxLineDotted); + break; + case nsLineStyle_kNone: + default: + // nothing uses kNone + NS_ERROR("SetLineStyle: Invalid line style"); + break; + } + + mLineStyle = aLineStyle; + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesRenderingContext::SetColor(nscolor aColor) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetColor 0x%08x\n", this, aColor)); + mThebes->SetColor(gfxRGBA(aColor)); + + mColor = aColor; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetColor(nscolor &aColor) const +{ + aColor = mColor; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::Translate(nscoord aX, nscoord aY) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Translate %d %d\n", this, aX, aY)); + mThebes->Translate (gfxPoint(FROM_TWIPS(aX), FROM_TWIPS(aY))); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::Scale(float aSx, float aSy) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::Scale %f %f\n", this, aSx, aSy)); + mThebes->Scale (FROM_TWIPS(aSx), FROM_TWIPS(aSy)); + return NS_OK; +} + +void +nsThebesRenderingContext::UpdateTempTransformMatrix() +{ + //PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::UpdateTempTransformMatrix\n", this)); + + /***** + * Thebes matrix layout: gfx matrix layout: + * | xx yx 0 | | m00 m01 0 | + * | xy yy 0 | | m10 m11 0 | + * | x0 y0 1 | | m20 m21 1 | + *****/ + + // this sort of sucks + gfxFloat xx, yx, xy, yy, x0, y0; + mThebes->CurrentMatrix().ToValues(&xx, &yx, &xy, &yy, &x0, &y0); + + NS_ASSERTION(yx == 0 && xy == 0, "Can't represent Thebes matrix to Gfx"); + mTempTransform.SetToTranslate(TO_TWIPS(x0), TO_TWIPS(y0)); + mTempTransform.AddScale(xx, yy); +} + +nsTransform2D& +nsThebesRenderingContext::CurrentTransform() +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::CurrentTransform\n", this)); + UpdateTempTransformMatrix(); + return mTempTransform; +} + +/**** + **** XXXXXX + **** + **** On other gfx implementations, the transform returned by this + **** has a built in twips to pixels ratio. That is, you pass in + **** twips to any nsTransform2D TransformCoord method, and you + **** get back pixels. This makes no sense. We don't do this. + **** This in turn breaks SVG and ; those should just be + **** fixed to not use this! + ****/ + +NS_IMETHODIMP +nsThebesRenderingContext::GetCurrentTransform(nsTransform2D *&aTransform) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::GetCurrentTransform\n", this)); + UpdateTempTransformMatrix(); + aTransform = &mTempTransform; + return NS_OK; +} + +void +nsThebesRenderingContext::TransformCoord (nscoord *aX, nscoord *aY) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::TransformCoord\n", this)); + + gfxPoint pt(FROM_TWIPS(*aX), FROM_TWIPS(*aY)); + + pt = mThebes->UserToDevice (pt); + + *aX = TO_TWIPS(pt.x); + *aY = TO_TWIPS(pt.y); +} + +NS_IMETHODIMP +nsThebesRenderingContext::CreateDrawingSurface(const nsRect &aBounds, + PRUint32 aSurfFlags, + nsIDrawingSurface* &aSurface) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::CreateDrawingSurface [%d,%d,%d,%d] 0x%08x\n", this, aBounds.x, aBounds.y, aBounds.width, aBounds.height, aSurfFlags)); + + nsThebesDrawingSurface *cds = new nsThebesDrawingSurface(); + nsIDeviceContext *dc = mDeviceContext.get(); + cds->Init (NS_STATIC_CAST(nsThebesDeviceContext *, dc), + aBounds.width, aBounds.height, + PR_FALSE); + aSurface = (nsIDrawingSurface*) cds; + NS_ADDREF(aSurface); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::CreateDrawingSurface(nsNativeWidget aWidget, nsIDrawingSurface* &aSurface) +{ + nsThebesDrawingSurface *cds = new nsThebesDrawingSurface(); + nsIDeviceContext *dc = mDeviceContext.get(); + cds->Init (NS_STATIC_CAST(nsThebesDeviceContext *, dc), aWidget); + aSurface = (nsIDrawingSurface*) cds; + NS_ADDREF(aSurface); + + return NS_OK; +} + + +NS_IMETHODIMP +nsThebesRenderingContext::DestroyDrawingSurface(nsIDrawingSurface *aDS) +{ + NS_RELEASE(aDS); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawLine(nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawLine %d %d %d %d\n", this, aX0, aY0, aX1, aY1)); + + gfxPoint p0 = gfxPoint(FROM_TWIPS(aX0), FROM_TWIPS(aY0)); + gfxPoint p1 = gfxPoint(FROM_TWIPS(aX1), FROM_TWIPS(aY1)); + + // we can't draw thick lines with gfx, so we always assume we want pixel-aligned + // lines if the rendering context is at 1.0 scale + gfxMatrix savedMatrix = mThebes->CurrentMatrix(); + if (savedMatrix.GetScaling() == gfxSize(1.0, 1.0)) { + p0 = mThebes->UserToDevice(p0); + p1 = mThebes->UserToDevice(p1); + + p0.round(); + p1.round(); + + mThebes->IdentityMatrix(); + + mThebes->NewPath(); + + // snap straight lines + if (p0.x == p1.x) { + mThebes->Line(p0 + gfxPoint(0.5, 0), + p1 + gfxPoint(0.5, 0)); + } else if (p0.y == p1.y) { + mThebes->Line(p0 + gfxPoint(0, 0.5), + p1 + gfxPoint(0, 0.5)); + } else { + mThebes->Line(p0, p1); + } + + mThebes->Stroke(); + + mThebes->SetMatrix(savedMatrix); + } else { + mThebes->NewPath(); + mThebes->Line(p0, p1); + mThebes->Stroke(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawRect(const nsRect& aRect) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawRect [%d,%d,%d,%d]\n", this, aRect.x, aRect.y, aRect.width, aRect.height)); + + mThebes->NewPath(); + mThebes->Rectangle(GFX_RECT_FROM_TWIPS_RECT(aRect), PR_TRUE); + mThebes->Stroke(); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) +{ + DrawRect(nsRect(aX, aY, aWidth, aHeight)); + return NS_OK; +} + + +/* Clamp r to (0,0) (32766,32766); + * these are to be device coordinates + */ +static void +ConditionRect(gfxRect& r) { + if (r.pos.x < 0.0) { + r.size.width += r.pos.x; + r.pos.x = 0.0; + } + + if (r.pos.x + r.size.width > 32766.0) { + r.size.width = 32766.0 - r.pos.x; + } + + if (r.pos.y < 0.0) { + r.size.height += r.pos.y; + r.pos.y = 0.0; + } + + if (r.pos.y + r.size.height > 32766.0) { + r.size.height = 32766.0 - r.pos.y; + } +} + +NS_IMETHODIMP +nsThebesRenderingContext::FillRect(const nsRect& aRect) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillRect [%d,%d,%d,%d]\n", this, aRect.x, aRect.y, aRect.width, aRect.height)); + + gfxRect r(GFX_RECT_FROM_TWIPS_RECT(aRect)); + + /* Clamp coordinates to work around a design bug in cairo */ + nscoord bigval = (nscoord)(32766*mP2T); + if (aRect.width > bigval || + aRect.height > bigval || + aRect.x < -bigval || + aRect.x > bigval || + aRect.y < -bigval || + aRect.y > bigval) + { + gfxMatrix mat = mThebes->CurrentMatrix(); + + r = mat.Transform(r); + + ConditionRect(r); + + mThebes->IdentityMatrix(); + mThebes->NewPath(); + mThebes->Rectangle(r, PR_TRUE); + mThebes->Fill(); + mThebes->SetMatrix(mat); + + return NS_OK; + } + + mThebes->NewPath(); + mThebes->Rectangle(r, PR_TRUE); + mThebes->Fill(); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::FillRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) +{ + FillRect(nsRect(aX, aY, aWidth, aHeight)); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::InvertRect(const nsRect& aRect) +{ + gfxContext::GraphicsOperator lastOp = mThebes->CurrentOperator(); + + mThebes->SetOperator(gfxContext::OPERATOR_XOR); + nsresult rv = FillRect(aRect); + mThebes->SetOperator(lastOp); + + return rv; +} + +NS_IMETHODIMP +nsThebesRenderingContext::InvertRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) +{ + return InvertRect(nsRect(aX, aY, aWidth, aHeight)); +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawEllipse(const nsRect& aRect) +{ + return DrawEllipse(aRect.x, aRect.y, aRect.width, aRect.height); +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawEllipse [%d,%d,%d,%d]\n", this, aX, aY, aWidth, aHeight)); + + mThebes->NewPath(); + mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0, + FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0), + gfxSize(FROM_TWIPS(aWidth), + FROM_TWIPS(aHeight))); + mThebes->Stroke(); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::FillEllipse(const nsRect& aRect) +{ + return FillEllipse(aRect.x, aRect.y, aRect.width, aRect.height); +} + +NS_IMETHODIMP +nsThebesRenderingContext::FillEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillEllipse [%d,%d,%d,%d]\n", this, aX, aY, aWidth, aHeight)); + + mThebes->NewPath(); + mThebes->Ellipse(gfxPoint(FROM_TWIPS(aX) + FROM_TWIPS(aWidth)/2.0, + FROM_TWIPS(aY) + FROM_TWIPS(aHeight)/2.0), + gfxSize(FROM_TWIPS(aWidth), + FROM_TWIPS(aHeight))); + mThebes->Fill(); + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::FillPolygon(const nsPoint twPoints[], PRInt32 aNumPoints) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::FillPolygon %d\n", this, aNumPoints)); + + if (aNumPoints == 0) + return NS_OK; + + if (aNumPoints == 4) { + } + + nsAutoArrayPtr pxPoints(new gfxPoint[aNumPoints]); + + for (int i = 0; i < aNumPoints; i++) { + pxPoints[i].x = FROM_TWIPS(twPoints[i].x); + pxPoints[i].y = FROM_TWIPS(twPoints[i].y); + } + + mThebes->NewPath(); + mThebes->Polygon(pxPoints, aNumPoints); + mThebes->Fill(); + + return NS_OK; +} + +void* +nsThebesRenderingContext::GetNativeGraphicData(GraphicDataType aType) +{ + //if (mWidget && aType == NATIVE_GDK_DRAWABLE) + // return mWidget->GetNativeData(NS_NATIVE_WIDGET); + if (aType == NATIVE_THEBES_CONTEXT) + return mThebes; + if (aType == NATIVE_CAIRO_CONTEXT) + return mThebes->GetCairo(); +#ifdef XP_WIN + if (aType == NATIVE_WINDOWS_DC) + return ((gfxWindowsSurface*)mThebes->CurrentSurface())->GetDC(); +#endif + + return nsnull; +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawNativeWidgetPixmap(void* aSrcSurfaceBlack, + void* aSrcSurfaceWhite, + const nsIntSize& pxSrcSize, + const nsPoint& twDestPos) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawNativeWidgetPixmap %p %p [%d,%d] [%d,%d]\n", this, aSrcSurfaceBlack, aSrcSurfaceWhite, pxSrcSize.width, pxSrcSize.height, twDestPos.x, twDestPos.y)); + +#if 0 + // debug + mThebes->Save(); + mThebes->SetColor(gfxRGBA(0.9, 0.9, 1.0, 1.0)); + mThebes->NewPath(); + mThebes->Rectangle(gfxRect(FROM_TWIPS(twDestPos.x), + FROM_TWIPS(twDestPos.y), + pxSrcSize.width, + pxSrcSize.height), + PR_TRUE); + mThebes->Fill(); + mThebes->Restore(); + + return NS_OK; +#endif + + nsThebesDrawingSurface* tmpSurf = new nsThebesDrawingSurface(); + nsIDeviceContext* dc = mDeviceContext.get(); + nsThebesDeviceContext* cdc = NS_STATIC_CAST(nsThebesDeviceContext*, dc); + + tmpSurf->Init(cdc, aSrcSurfaceBlack, aSrcSurfaceWhite, pxSrcSize); + + nsRefPtr pat = new gfxPattern(tmpSurf->GetThebesSurface()); + + mThebes->Save(); + mThebes->NewPath(); + // mm, mixed twips and pixels. are we having fun yet? + mThebes->PixelSnappedRectangleAndSetPattern(gfxRect(FROM_TWIPS(twDestPos.x), + FROM_TWIPS(twDestPos.y), + pxSrcSize.width, + pxSrcSize.height), + pat); + mThebes->Fill(); + mThebes->Restore(); + + delete tmpSurf; + + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::UseBackbuffer(PRBool* aUseBackbuffer) +{ + *aUseBackbuffer = PR_FALSE; + *aUseBackbuffer = PR_TRUE; + return NS_OK; +} + +nsresult nsThebesRenderingContext::AllocateBackbuffer(const nsRect &aRequestedSize, const nsRect &aMaxSize, nsIDrawingSurface* &aBackbuffer, PRBool aCacheBackbuffer, PRUint32 aSurfFlags) +{ +#if 0 + nsRect newBounds; + nsresult rv = NS_OK; + + if (! aCacheBackbuffer) { + newBounds = aRequestedSize; + } else { + GetDrawingSurfaceSize(aMaxSize, aRequestedSize, newBounds); + } + + if ((nsnull == gBackbuffer) + || (gBackbufferBounds.width != newBounds.width) + || (gBackbufferBounds.height != newBounds.height)) + { + if (gBackbuffer) { + //destroy existing DS + DestroyDrawingSurface(gBackbuffer); + gBackbuffer = nsnull; + } + + rv = CreateDrawingSurface(newBounds, aSurfFlags, gBackbuffer); + // printf("Allocating a new drawing surface %d %d\n", newBounds.width, newBounds.height); + if (NS_SUCCEEDED(rv)) { + gBackbufferBounds = newBounds; + SelectOffScreenDrawingSurface(gBackbuffer); + } else { + gBackbufferBounds.SetRect(0,0,0,0); + gBackbuffer = nsnull; + } + } else { + SelectOffScreenDrawingSurface(gBackbuffer); + + float p2t; + nsCOMPtr dx; + GetDeviceContext(*getter_AddRefs(dx)); + p2t = dx->DevUnitsToAppUnits(); + nsRect bounds = aRequestedSize; + bounds *= p2t; + + SetClipRect(bounds, nsClipCombine_kReplace); + } + + aBackbuffer = gBackbuffer; + return rv; +#endif + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsThebesRenderingContext::GetBackbuffer(const nsRect &aRequestedSize, + const nsRect &aMaxSize, + PRBool aForBlending, + nsIDrawingSurface* &aBackbuffer) +{ + return AllocateBackbuffer(aRequestedSize, aMaxSize, aBackbuffer, PR_FALSE, aForBlending ? NS_CREATEDRAWINGSURFACE_FOR_PIXEL_ACCESS : 0); + //NS_WARNING("Unimplemented function called"); + //return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::ReleaseBackbuffer(void) +{ + NS_WARNING("Unimplemented function called"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::DestroyCachedBackbuffer(void) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::PushFilter(const nsRect& twRect, PRBool aAreaIsOpaque, float aOpacity) +{ + gfxPoint p0(FROM_TWIPS(twRect.x), FROM_TWIPS(twRect.y)); + gfxPoint p1(FROM_TWIPS(twRect.XMost()), FROM_TWIPS(twRect.YMost())); + + p0 = mThebes->UserToDevice(p0); + p1 = mThebes->UserToDevice(p1); + + nsIntRect deviceRect(TO_TWIPS(floor(PR_MIN(p0.x, p1.x))), + TO_TWIPS(floor(PR_MIN(p0.y, p1.y))), + TO_TWIPS(ceil(PR_MAX(p0.x, p1.x))), + TO_TWIPS(ceil(PR_MAX(p0.y, p1.y)))); + + return mDrawingSurface->PushFilter(deviceRect, aAreaIsOpaque, aOpacity); +} + +NS_IMETHODIMP +nsThebesRenderingContext::PopFilter() +{ + mDrawingSurface->PopFilter(); + return NS_OK; +} + +// +// Both twSrcRect and twDestRect need to be +// transformed by the CTM. This is especially odd +// for the SrcRect! +// + +NS_IMETHODIMP +nsThebesRenderingContext::DrawImage(imgIContainer *aImage, + const nsRect &twSrcRect, + const nsRect &twDestRect) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawImage %p [%d,%d,%d,%d] [%d,%d,%d,%d]\n", + this, aImage, twSrcRect.x, twSrcRect.y, twSrcRect.width, twSrcRect.height, + twDestRect.x, twDestRect.y, twDestRect.width, twDestRect.height)); + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p translation: %f %f\n", + this, + mThebes->CurrentMatrix().GetTranslation().x, + mThebes->CurrentMatrix().GetTranslation().y)); +#if 0 + fprintf (stderr, "DrawImage: src [%f %f %f %f]\n dst [%f %f %f %f]\n x0: %f y0: %f\n", + FROM_TWIPS(twSrcRect.x), FROM_TWIPS(twSrcRect.y), FROM_TWIPS(twSrcRect.width), FROM_TWIPS(twSrcRect.height), + FROM_TWIPS(twDestRect.x), FROM_TWIPS(twDestRect.y), FROM_TWIPS(twDestRect.width), FROM_TWIPS(twDestRect.height), + mThebes->CurrentMatrix().GetTranslation().x, mThebes->CurrentMatrix().GetTranslation().y); +#endif + + nsCOMPtr imgFrame; + aImage->GetCurrentFrame(getter_AddRefs(imgFrame)); + if (!imgFrame) return NS_ERROR_FAILURE; + + nsCOMPtr img(do_GetInterface(imgFrame)); + if (!img) return NS_ERROR_FAILURE; + + // For Bug 87819 + // imgFrame may want image to start at different position, so adjust + nsIntRect pxImgFrameRect; + imgFrame->GetRect(pxImgFrameRect); + + // twSrcRect is always in appunits (twips), + // and has nothing to do with the current transform (it's a region + // of the image) + nsIntRect pxSr; + pxSr.x = NSToIntRound(FROM_TWIPS(twSrcRect.x)); + pxSr.y = NSToIntRound(FROM_TWIPS(twSrcRect.y)); + pxSr.width = NSToIntRound(FROM_TWIPS(twSrcRect.XMost())) - pxSr.x; + pxSr.height = NSToIntRound(FROM_TWIPS(twSrcRect.YMost())) - pxSr.y; + + // the dest rect is affected by the current transform; that'll be + // handled by Image::Draw(), when we actually set up the rectangle. + nsIntRect pxDr; + pxDr.x = NSToIntRound(FROM_TWIPS(twDestRect.x)); + pxDr.y = NSToIntRound(FROM_TWIPS(twDestRect.y)); + pxDr.width = NSToIntRound(FROM_TWIPS(twDestRect.XMost())) - pxDr.x; + pxDr.height = NSToIntRound(FROM_TWIPS(twDestRect.YMost())) - pxDr.y; + + if (pxImgFrameRect != pxSr) { + double xscale = double(pxDr.width)/pxSr.width; + double yscale = double(pxDr.height)/pxSr.height; + + nsIntRect pxScaledUpIRect; + pxScaledUpIRect.x = NSToCoordRound(pxImgFrameRect.x*xscale); + pxScaledUpIRect.y = NSToCoordRound(pxImgFrameRect.y*yscale); + pxScaledUpIRect.width = NSToCoordRound(pxImgFrameRect.XMost()*xscale) - pxScaledUpIRect.x; + pxScaledUpIRect.height = NSToCoordRound(pxImgFrameRect.YMost()*yscale) - pxScaledUpIRect.y; + + pxSr.IntersectRect(pxSr, pxImgFrameRect); + pxDr.IntersectRect(pxDr, pxScaledUpIRect); + } + + return img->Draw(*this, mDrawingSurface, + pxSr.x - pxImgFrameRect.x, pxSr.y - pxImgFrameRect.y, + pxSr.width, pxSr.height, + pxDr.x, pxDr.y, pxDr.width, pxDr.height); +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawTile(imgIContainer *aImage, + nscoord twXOffset, nscoord twYOffset, + const nsRect *twTargetRect) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::DrawTile %p %d %d [%d,%d,%d,%d]\n", + this, aImage, twXOffset, twYOffset, + twTargetRect->x, twTargetRect->y, twTargetRect->width, twTargetRect->height)); + +#if 0 + PRInt32 pxWidth, pxHeight; + aImage->GetWidth(&pxWidth); + aImage->GetHeight(&pxHeight); + + nsCOMPtr imgFrame; + aImage->GetCurrentFrame(getter_AddRefs(imgFrame)); + if (!imgFrame) return NS_ERROR_FAILURE; + + nsCOMPtr img(do_GetInterface(imgFrame)); + if (!img) return NS_ERROR_FAILURE; + + nsIImage *q = (nsIImage*) img.get(); + + nsThebesImage *thbimg = NS_STATIC_CAST(nsThebesImage*, q); + nsRefPtr pat = new gfxPattern(thbimg->ThebesSurface()); + + gfxMatrix m; + m.Translate(gfxPoint(FROM_TWIPS(twXOffset % TO_TWIPS(pxWidth)), FROM_TWIPS(twYOffset % TO_TWIPS(pxHeight)))); + m.Scale(FROM_TWIPS(twTargetRect->width) / pxWidth, + FROM_TWIPS(twTargetRect->height) / pxHeight); + pat->SetMatrix(m); + + mThebes->SetPattern(pat); + mThebes->NewPath(); + mThebes->Rectangle(GFX_RECT_FROM_TWIPS_RECT(*twTargetRect)); + mThebes->Fill(); + + return NS_OK; + +#else +#if 0 + fprintf (stderr, "DrawTile: %d %d [%d %d %d %d]\n", + twXOffset, twYOffset, twTargetRect->x, + twTargetRect->y, twTargetRect->width, twTargetRect->height); +#endif + + /* Almost verbatim from nsRenderingContextImpl */ + UpdateTempTransformMatrix(); + + nsRect twDr(*twTargetRect); + mTempTransform.TransformCoord(&twDr.x, &twDr.y, &twDr.width, &twDr.height); + mTempTransform.TransformCoord(&twXOffset, &twYOffset); + + // may have become empty due to transform shinking small number to 0 + if (twDr.IsEmpty()) + return NS_OK; + + PRInt32 pxWidth, pxHeight; + aImage->GetWidth(&pxWidth); + aImage->GetHeight(&pxHeight); + + if (pxWidth == 0 || pxHeight == 0) + return NS_OK; + + PRInt32 pxXOffset = NSToIntRound(FROM_TWIPS((twDr.x - twXOffset) % TO_TWIPS(pxWidth))); + PRInt32 pxYOffset = NSToIntRound(FROM_TWIPS((twDr.y - twYOffset) % TO_TWIPS(pxHeight))); + + nsCOMPtr imgFrame; + aImage->GetCurrentFrame(getter_AddRefs(imgFrame)); + if (!imgFrame) return NS_ERROR_FAILURE; + + nsCOMPtr img(do_GetInterface(imgFrame)); + if (!img) return NS_ERROR_FAILURE; + + nsIDrawingSurface *surface = nsnull; + GetDrawingSurface(&surface); + if (!surface) return NS_ERROR_FAILURE; + + /* bug 113561 - frame can be smaller than container */ + nsIntRect pxImgFrameRect; + imgFrame->GetRect(pxImgFrameRect); + PRInt32 pxPadX = pxWidth - pxImgFrameRect.width; + PRInt32 pxPadY = pxHeight - pxImgFrameRect.height; + + nsIntRect pxDr(NSToIntRound(FROM_TWIPS(twDr.x)), + NSToIntRound(FROM_TWIPS(twDr.y)), + NSToIntRound(FROM_TWIPS(twDr.width)), + NSToIntRound(FROM_TWIPS(twDr.height))); + + return img->DrawTile(*this, surface, + pxXOffset - pxImgFrameRect.x, pxYOffset - pxImgFrameRect.y, + pxPadX, pxPadY, + pxDr); +#endif +} + +// +// text junk +// + +NS_IMETHODIMP +nsThebesRenderingContext::SetFont(const nsFont& aFont, nsIAtom* aLangGroup) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetFont %p\n", this, &aFont)); + + nsCOMPtr newMetrics; + mDeviceContext->GetMetricsFor(aFont, aLangGroup, *getter_AddRefs(newMetrics)); + mFontMetrics = NS_REINTERPRET_CAST(nsIThebesFontMetrics*, newMetrics.get()); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::SetFont(nsIFontMetrics *aFontMetrics) +{ + PR_LOG(gThebesGFXLog, PR_LOG_DEBUG, ("## %p nsTRC::SetFont[Metrics] %p\n", this, aFontMetrics)); + + mFontMetrics = NS_STATIC_CAST(nsIThebesFontMetrics*, aFontMetrics); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetFontMetrics(nsIFontMetrics *&aFontMetrics) +{ + aFontMetrics = mFontMetrics; + NS_IF_ADDREF(aFontMetrics); + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetWidth(char aC, nscoord &aWidth) +{ + if (aC == ' ' && mFontMetrics) + return mFontMetrics->GetSpaceWidth(aWidth); + + return GetWidth(&aC, 1, aWidth); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetWidth(PRUnichar aC, nscoord &aWidth, PRInt32 *aFontID) +{ + return GetWidth(&aC, 1, aWidth, aFontID); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetWidth(const nsString& aString, nscoord &aWidth, PRInt32 *aFontID) +{ + return GetWidth(aString.get(), aString.Length(), aWidth, aFontID); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetWidth(const char* aString, nscoord& aWidth) +{ + return GetWidth(aString, strlen(aString), aWidth); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetWidth(const char* aString, PRUint32 aLength, nscoord& aWidth) +{ + if (aLength == 0) { + aWidth = 0; + return NS_OK; + } + + return mFontMetrics->GetWidth(aString, aLength, aWidth, this); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetWidth(const PRUnichar *aString, PRUint32 aLength, + nscoord &aWidth, PRInt32 *aFontID) +{ + if (aLength == 0) { + aWidth = 0; + return NS_OK; + } + + return mFontMetrics->GetWidth(aString, aLength, aWidth, aFontID, this); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetTextDimensions(const char* aString, PRUint32 aLength, + nsTextDimensions& aDimensions) +{ + mFontMetrics->GetMaxAscent(aDimensions.ascent); + mFontMetrics->GetMaxDescent(aDimensions.descent); + return GetWidth(aString, aLength, aDimensions.width); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetTextDimensions(const PRUnichar* aString, + PRUint32 aLength, + nsTextDimensions& aDimensions, + PRInt32* aFontID) +{ + mFontMetrics->GetMaxAscent(aDimensions.ascent); + mFontMetrics->GetMaxDescent(aDimensions.descent); + return GetWidth(aString, aLength, aDimensions.width, aFontID); +} + +#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) +NS_IMETHODIMP +nsThebesRenderingContext::GetTextDimensions(const char* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetTextDimensions(const PRUnichar* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} +#endif + +#ifdef MOZ_MATHML +NS_IMETHODIMP +nsThebesRenderingContext::GetBoundingMetrics(const char* aString, + PRUint32 aLength, + nsBoundingMetrics& aBoundingMetrics) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetBoundingMetrics(const PRUnichar* aString, + PRUint32 aLength, + nsBoundingMetrics& aBoundingMetrics, + PRInt32* aFontID) +{ + return NS_OK; +} +#endif // MOZ_MATHML + +NS_IMETHODIMP +nsThebesRenderingContext::DrawString(const char *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + const nscoord* aSpacing) +{ + return mFontMetrics->DrawString(aString, aLength, aX, aY, aSpacing, + this); +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawString(const PRUnichar *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + PRInt32 aFontID, + const nscoord* aSpacing) +{ + return mFontMetrics->DrawString(aString, aLength, aX, aY, aFontID, + aSpacing, this); +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawString(const nsString& aString, + nscoord aX, nscoord aY, + PRInt32 aFontID, + const nscoord* aSpacing) +{ + return DrawString(aString.get(), aString.Length(), + aX, aY, aFontID, aSpacing); +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetClusterInfo(const PRUnichar *aText, + PRUint32 aLength, + PRUint8 *aClusterStarts) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +PRInt32 +nsThebesRenderingContext::GetPosition(const PRUnichar *aText, + PRUint32 aLength, + nsPoint aPt) +{ + return -1; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetRangeWidth(const PRUnichar *aText, + PRUint32 aLength, + PRUint32 aStart, + PRUint32 aEnd, + PRUint32 &aWidth) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetRangeWidth(const char *aText, + PRUint32 aLength, + PRUint32 aStart, + PRUint32 aEnd, + PRUint32 &aWidth) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::RenderEPS(const nsRect& aRect, FILE *aDataFile) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +//// +//// Unused junk +//// + +NS_IMETHODIMP +nsThebesRenderingContext::IsVisibleRect(const nsRect& aRect, PRBool &aIsVisible) +{ + NS_ERROR("not used anywhere"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetClipRect(nsRect &aRect, PRBool &aHasLocalClip) +{ + NS_ERROR("not used anywhere"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::LockDrawingSurface(PRInt32 aX, PRInt32 aY, + PRUint32 aWidth, PRUint32 aHeight, + void **aBits, PRInt32 *aStride, + PRInt32 *aWidthBytes, + PRUint32 aFlags) +{ + NS_WARNING("Unimplemented function called"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::UnlockDrawingSurface(void) +{ + NS_WARNING("Unimplemented function called"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::CopyClipRegion(nsIRegion &aRegion) +{ + NS_WARNING("Unimplemented function called"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetLineStyle(nsLineStyle &aLineStyle) +{ + NS_ERROR("not used anywhere"); + aLineStyle = mLineStyle; + return NS_OK; +} + +NS_IMETHODIMP +nsThebesRenderingContext::SetPenMode(nsPenMode aPenMode) +{ + // aPenMode == nsPenMode_kNone, nsPenMode_kInverted. + NS_ERROR("not used anywhere"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::GetPenMode(nsPenMode &aPenMode) +{ + //NS_ERROR("not used anywhere, but used in a debug thing"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsThebesRenderingContext::DrawArc(const nsRect& aRect, + float aStartAngle, float aEndAngle) +{ + return DrawArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle); +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight, + float aStartAngle, float aEndAngle) +{ + NS_ERROR("not used"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::FillArc(const nsRect& aRect, + float aStartAngle, float aEndAngle) +{ + return FillArc(aRect.x, aRect.y, aRect.width, aRect.height, aStartAngle, aEndAngle); +} + +NS_IMETHODIMP +nsThebesRenderingContext::FillArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight, + float aStartAngle, float aEndAngle) +{ + NS_ERROR("not used"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawPolyline(const nsPoint aPoints[], + PRInt32 aNumPoints) +{ + NS_ERROR("DrawPolyline called!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsThebesRenderingContext::DrawPolygon(const nsPoint aPoints[], PRInt32 aNumPoints) +{ + NS_ERROR("DrawPolygon called!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + + +NS_IMETHODIMP +nsThebesRenderingContext::CopyOffScreenBits(nsIDrawingSurface *aSrcSurf, + PRInt32 aSrcX, PRInt32 aSrcY, + const nsRect &aDestBounds, + PRUint32 aCopyFlags) +{ + NS_WARNING ("not used"); + return NS_ERROR_NOT_IMPLEMENTED; +} diff --git a/gfx/src/thebes/nsThebesRenderingContext.h b/gfx/src/thebes/nsThebesRenderingContext.h new file mode 100644 index 00000000000..2bf75192ec3 --- /dev/null +++ b/gfx/src/thebes/nsThebesRenderingContext.h @@ -0,0 +1,270 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes gfx + * + * The Initial Developer of the Original Code is + * mozilla.org. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef NSTHEBESRENDERINGCONTEXT__H__ +#define NSTHEBESRENDERINGCONTEXT__H__ + +#include "nsCOMPtr.h" +#include "nsIRenderingContext.h" +#include "nsIDeviceContext.h" +#include "nsIFontMetrics.h" +#include "nsIWidget.h" +#include "nsPoint.h" +#include "nsSize.h" +#include "nsColor.h" +#include "nsRect.h" +#include "nsIRegion.h" +#include "nsTransform2D.h" +#include "nsVoidArray.h" +#include "nsIThebesFontMetrics.h" +#include "nsIThebesRenderingContext.h" +#include "gfxContext.h" + +class nsIImage; + +class nsThebesDrawingSurface; + +class nsThebesRenderingContext : public nsIRenderingContext, public nsIThebesRenderingContext +{ +public: + nsThebesRenderingContext(); + virtual ~nsThebesRenderingContext(); + + NS_DECL_ISUPPORTS + + NS_IMETHOD Init(nsIDeviceContext* aContext, nsIWidget *aWidget); + NS_IMETHOD Init(nsIDeviceContext* aContext, nsIDrawingSurface *aSurface); + NS_IMETHOD CommonInit(void); + NS_IMETHOD Reset(void); + NS_IMETHOD GetDeviceContext(nsIDeviceContext *& aDeviceContext); + NS_IMETHOD LockDrawingSurface(PRInt32 aX, PRInt32 aY, + PRUint32 aWidth, PRUint32 aHeight, + void **aBits, PRInt32 *aStride, + PRInt32 *aWidthBytes, + PRUint32 aFlags); + NS_IMETHOD UnlockDrawingSurface(void); + NS_IMETHOD SelectOffScreenDrawingSurface(nsIDrawingSurface *aSurface); + NS_IMETHOD GetDrawingSurface(nsIDrawingSurface **aSurface); + NS_IMETHOD GetHints(PRUint32& aResult); + NS_IMETHOD DrawNativeWidgetPixmap(void* aSrcSurfaceBlack, + void* aSrcSurfaceWhite, + const nsIntSize& aSrcSize, + const nsPoint& aDestPos); + NS_IMETHOD PushState(void); + NS_IMETHOD PopState(void); + NS_IMETHOD IsVisibleRect(const nsRect& aRect, PRBool &aIsVisible); + NS_IMETHOD SetClipRect(const nsRect& aRect, nsClipCombine aCombine); + NS_IMETHOD GetClipRect(nsRect &aRect, PRBool &aHasLocalClip); + NS_IMETHOD SetLineStyle(nsLineStyle aLineStyle); + NS_IMETHOD GetLineStyle(nsLineStyle &aLineStyle); + NS_IMETHOD GetPenMode(nsPenMode &aPenMode); + NS_IMETHOD SetPenMode(nsPenMode aPenMode); + NS_IMETHOD SetClipRegion(const nsIRegion& aRegion, nsClipCombine aCombine); + NS_IMETHOD CopyClipRegion(nsIRegion &aRegion); + NS_IMETHOD GetClipRegion(nsIRegion **aRegion); + NS_IMETHOD SetColor(nscolor aColor); + NS_IMETHOD GetColor(nscolor &aColor) const; + NS_IMETHOD SetFont(const nsFont& aFont, nsIAtom* aLangGroup); + NS_IMETHOD SetFont(nsIFontMetrics *aFontMetrics); + NS_IMETHOD GetFontMetrics(nsIFontMetrics *&aFontMetrics); + NS_IMETHOD Translate(nscoord aX, nscoord aY); + NS_IMETHOD Scale(float aSx, float aSy); + NS_IMETHOD GetCurrentTransform(nsTransform2D *&aTransform); + NS_IMETHOD CreateDrawingSurface(const nsRect &aBounds, PRUint32 aSurfFlags, nsIDrawingSurface* &aSurface); + NS_IMETHOD CreateDrawingSurface(nsNativeWidget aWidget, nsIDrawingSurface* &aSurface); + NS_IMETHOD DestroyDrawingSurface(nsIDrawingSurface *aDS); + + NS_IMETHOD DrawLine(nscoord aX0, nscoord aY0, nscoord aX1, nscoord aY1); + NS_IMETHOD DrawPolyline(const nsPoint aPoints[], PRInt32 aNumPoints); + NS_IMETHOD DrawRect(const nsRect& aRect); + NS_IMETHOD DrawRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); + NS_IMETHOD FillRect(const nsRect& aRect); + NS_IMETHOD FillRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); + NS_IMETHOD InvertRect(const nsRect& aRect); + NS_IMETHOD InvertRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); + NS_IMETHOD FlushRect(const nsRect& aRect); + NS_IMETHOD FlushRect(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); + NS_IMETHOD DrawPolygon(const nsPoint aPoints[], PRInt32 aNumPoints); + NS_IMETHOD FillPolygon(const nsPoint aPoints[], PRInt32 aNumPoints); + NS_IMETHOD DrawEllipse(const nsRect& aRect); + NS_IMETHOD DrawEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); + NS_IMETHOD FillEllipse(const nsRect& aRect); + NS_IMETHOD FillEllipse(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight); + NS_IMETHOD DrawArc(const nsRect& aRect, + float aStartAngle, float aEndAngle); + NS_IMETHOD DrawArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight, + float aStartAngle, float aEndAngle); + NS_IMETHOD FillArc(const nsRect& aRect, + float aStartAngle, float aEndAngle); + NS_IMETHOD FillArc(nscoord aX, nscoord aY, nscoord aWidth, nscoord aHeight, + float aStartAngle, float aEndAngle); + NS_IMETHOD GetWidth(char aC, nscoord &aWidth); + NS_IMETHOD GetWidth(PRUnichar aC, nscoord &aWidth, + PRInt32 *aFontID = nsnull); + NS_IMETHOD GetWidth(const nsString& aString, nscoord &aWidth, + PRInt32 *aFontID = nsnull); + NS_IMETHOD GetWidth(const char* aString, nscoord& aWidth); + NS_IMETHOD GetWidth(const char* aString, PRUint32 aLength, + nscoord& aWidth); + NS_IMETHOD GetWidth(const PRUnichar *aString, PRUint32 aLength, + nscoord &aWidth, PRInt32 *aFontID = nsnull); + NS_IMETHOD GetTextDimensions(const char* aString, PRUint32 aLength, + nsTextDimensions& aDimensions); + NS_IMETHOD GetTextDimensions(const PRUnichar* aString, PRUint32 aLength, + nsTextDimensions& aDimensions, PRInt32* aFontID = nsnull); + + NS_IMETHOD PushFilter(const nsRect& aRect, PRBool aAreaIsOpaque, float aOpacity); + NS_IMETHOD PopFilter(); + +#if defined(_WIN32) || defined(XP_OS2) || defined(MOZ_X11) || defined(XP_BEOS) + NS_IMETHOD GetTextDimensions(const char* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID = nsnull); + + NS_IMETHOD GetTextDimensions(const PRUnichar* aString, + PRInt32 aLength, + PRInt32 aAvailWidth, + PRInt32* aBreaks, + PRInt32 aNumBreaks, + nsTextDimensions& aDimensions, + PRInt32& aNumCharsFit, + nsTextDimensions& aLastWordDimensions, + PRInt32* aFontID = nsnull); +#endif + + NS_IMETHOD DrawString(const char *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + const nscoord* aSpacing = nsnull); + NS_IMETHOD DrawString(const PRUnichar *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + PRInt32 aFontID = -1, + const nscoord* aSpacing = nsnull); + NS_IMETHOD DrawString(const nsString& aString, nscoord aX, nscoord aY, + PRInt32 aFontID = -1, + const nscoord* aSpacing = nsnull); + NS_IMETHOD CopyOffScreenBits(nsIDrawingSurface *aSrcSurf, + PRInt32 aSrcX, PRInt32 aSrcY, + const nsRect &aDestBounds, + PRUint32 aCopyFlags); + virtual void* GetNativeGraphicData(GraphicDataType aType); + NS_IMETHOD GetBackbuffer(const nsRect &aRequestedSize, + const nsRect &aMaxSize, + PRBool aForBlending, + nsIDrawingSurface* &aBackbuffer); + NS_IMETHOD ReleaseBackbuffer(void); + NS_IMETHOD DestroyCachedBackbuffer(void); + NS_IMETHOD UseBackbuffer(PRBool* aUseBackbuffer); + + NS_IMETHOD PushTranslation(PushedTranslation* aState); + NS_IMETHOD PopTranslation(PushedTranslation* aState); + +#ifdef MOZ_MATHML + NS_IMETHOD GetBoundingMetrics(const char* aString, PRUint32 aLength, nsBoundingMetrics& aBoundingMetrics); + NS_IMETHOD GetBoundingMetrics(const PRUnichar* aString, PRUint32 aLength, + nsBoundingMetrics& aBoundingMetrics, + PRInt32* aFontID); + +#endif // MOZ_MATHML + + + NS_IMETHOD DrawImage(imgIContainer *aImage, + const nsRect &aSrcRect, + const nsRect &aDestRect); + NS_IMETHOD DrawTile(imgIContainer *aImage, nscoord aXOffset, nscoord aYOffset, + const nsRect * aTargetRect); + NS_IMETHOD SetRightToLeftText(PRBool aIsRTL) { return NS_OK; } + + NS_IMETHOD GetClusterInfo(const PRUnichar *aText, + PRUint32 aLength, + PRUint8 *aClusterStarts); + virtual PRInt32 GetPosition(const PRUnichar *aText, + PRUint32 aLength, + nsPoint aPt); + NS_IMETHOD GetRangeWidth(const PRUnichar *aText, + PRUint32 aLength, + PRUint32 aStart, + PRUint32 aEnd, + PRUint32 &aWidth); + NS_IMETHOD GetRangeWidth(const char *aText, + PRUint32 aLength, + PRUint32 aStart, + PRUint32 aEnd, + PRUint32 &aWidth); + + NS_IMETHOD RenderEPS(const nsRect& aRect, FILE *aDataFile); + + // Thebes specific stuff + + gfxContext *Thebes() { return mThebes; } + + nsTransform2D& CurrentTransform(); + + void TransformCoord (nscoord *aX, nscoord *aY); + + nsresult AllocateBackbuffer(const nsRect &aRequestedSize, const nsRect &aMaxSize, nsIDrawingSurface* &aBackbuffer, PRBool aCacheBackbuffer, PRUint32 aSurfFlags); + + +protected: + nsCOMPtr mDeviceContext; + // cached pixels2twips, twips2pixels values + double mP2T, mT2P; + + nsCOMPtr mWidget; + + // we need to manage our own clip region (since we can't get at + // the old one from cairo) + nsCOMPtr mFontMetrics; + + nsLineStyle mLineStyle; + nscolor mColor; + + nsRefPtr mThebes; + nsCOMPtr mDrawingSurface; + + // for handing out to people + void UpdateTempTransformMatrix(); + nsTransform2D mTempTransform; +}; + +#endif // NSCAIRORENDERINGCONTEXT__H__ diff --git a/gfx/src/cairo/nsCairoDeviceContext.cpp b/gfx/src/thebes/nsThebesScreen.cpp similarity index 100% rename from gfx/src/cairo/nsCairoDeviceContext.cpp rename to gfx/src/thebes/nsThebesScreen.cpp diff --git a/gfx/src/cairo/nsCairoDeviceContext.h b/gfx/src/thebes/nsThebesScreen.h similarity index 100% rename from gfx/src/cairo/nsCairoDeviceContext.h rename to gfx/src/thebes/nsThebesScreen.h diff --git a/gfx/src/cairo/nsCairoDrawingSurface.cpp b/gfx/src/thebes/nsThebesScreenManager.cpp similarity index 100% rename from gfx/src/cairo/nsCairoDrawingSurface.cpp rename to gfx/src/thebes/nsThebesScreenManager.cpp diff --git a/gfx/src/cairo/nsCairoDrawingSurface.h b/gfx/src/thebes/nsThebesScreenManager.h similarity index 100% rename from gfx/src/cairo/nsCairoDrawingSurface.h rename to gfx/src/thebes/nsThebesScreenManager.h diff --git a/gfx/thebes/public/Makefile.in b/gfx/thebes/public/Makefile.in index f70bba8f827..85ae86bffee 100644 --- a/gfx/thebes/public/Makefile.in +++ b/gfx/thebes/public/Makefile.in @@ -30,4 +30,8 @@ ifeq ($(MOZ_GFX_TOOLKIT),gtk2) EXPORTS += gfxXlibSurface.h endif +ifdef MOZ_ENABLE_GLITZ +EXPORTS += gfxGlitzSurface.h +endif + include $(topsrcdir)/config/rules.mk diff --git a/gfx/thebes/public/gfxASurface.h b/gfx/thebes/public/gfxASurface.h index feb5d2803bc..20b30d4cefe 100644 --- a/gfx/thebes/public/gfxASurface.h +++ b/gfx/thebes/public/gfxASurface.h @@ -42,10 +42,25 @@ #include "gfxTypes.h" +/** + * A surface is something you can draw on. Instantiate a subclass of this + * abstract class, and use gfxContext to draw on this surface. + */ class gfxASurface { THEBES_DECL_REFCOUNTING_ABSTRACT public: + /** + * The format for an image surface. For all formats with alpha data, 0 + * means transparent, 1 or 255 means fully opaque. + */ + typedef enum { + ImageFormatARGB32, ///< ARGB data in native endianness, using premultiplied alpha + ImageFormatRGB24, ///< xRGB data in native endianness + ImageFormatA8, ///< Only an alpha channel + ImageFormatA1 ///< Packed transparency information (one byte refers to 8 pixels) + } gfxImageFormat; + /*** this DOES NOT addref the surface */ cairo_surface_t* CairoSurface() { return mSurface; } diff --git a/gfx/thebes/public/gfxColor.h b/gfx/thebes/public/gfxColor.h index 49f70d955a2..24d9ba6d910 100644 --- a/gfx/thebes/public/gfxColor.h +++ b/gfx/thebes/public/gfxColor.h @@ -42,18 +42,39 @@ #include "gfxTypes.h" +/** + * A color value, storing red, green, blue and alpha components. + * This class does not use premultiplied alpha. + * + * XXX should this use doubles (instead of gfxFloat), for consistency with + * cairo? + */ struct gfxRGBA { gfxFloat r, g, b, a; gfxRGBA() { } gfxRGBA(const gfxRGBA& c) : r(c.r), g(c.g), b(c.b), a(c.a) {} + /** + * Intialize this color using explicit red, green, blue and alpha + * values. + */ gfxRGBA(gfxFloat _r, gfxFloat _g, gfxFloat _b, gfxFloat _a=1.0) : r(_r), g(_g), b(_b), a(_a) {} + /** + * Initialize this color from a packed 32-bit color. + * Premultiplied alpha isn't used. + * This uses ABGR byte order in the native endianness. + * + * @see gfxRGBA::Packed + */ gfxRGBA(PRUint32 c) { r = ((c >> 0) & 0xff) / 255.0; g = ((c >> 8) & 0xff) / 255.0; b = ((c >> 16) & 0xff) / 255.0; a = ((c >> 24) & 0xff) / 255.0; } + /** + * Initialize this color by parsing the given string. + */ gfxRGBA(const char* str) { a = 1.0; // if aString[0] is a #, parse it as hex @@ -61,6 +82,11 @@ struct gfxRGBA { // if aString[0] is a number, parse it loosely as hex } + /** + * Returns this color value as a packed 32-bit integer. This uses ABGR + * byte order in the native endianness, as accepted by the corresponding + * constructor of this class. + */ PRUint32 Packed() const { return (((PRUint8)(a * 255.0) << 24) | ((PRUint8)(b * 255.0) << 16) | @@ -68,6 +94,10 @@ struct gfxRGBA { ((PRUint8)(r * 255.0))); } + /** + * Convert this color to a hex value. For example, for rgb(255,0,0), + * this will return FF0000. + */ // XXX I'd really prefer to just have this return an nsACString // Does this function even make sense, since we're just ignoring the alpha value? void Hex(nsACString& result) const { diff --git a/gfx/thebes/public/gfxContext.h b/gfx/thebes/public/gfxContext.h index 6662eb4653b..2384fad51bc 100644 --- a/gfx/thebes/public/gfxContext.h +++ b/gfx/thebes/public/gfxContext.h @@ -53,10 +53,24 @@ class gfxRegion; class gfxFilter; class gfxTextRun; +/** + * This is the main class for doing actual drawing. It is initialized using + * a surface and can be drawn on. It manages various state information like + * a current transformation matrix (CTM), a current path, current color, + * etc. + * + * All drawing happens by creating a path and then stroking or filling it. + * The functions like Rectangle and Arc do not do any drawing themselves. + * When a path is drawn (stroked or filled), it is filled/stroked with a + * pattern set by SetPattern, SetColor or SetSource. + */ class gfxContext { THEBES_DECL_REFCOUNTING public: + /** + * Initialize this context from a surface. + */ gfxContext(gfxASurface *surface); ~gfxContext(); @@ -75,27 +89,81 @@ public: ** Paths & Drawing **/ - // these do not consume the current path + /** + * Stroke the current path using the current settings (such as line + * width and color). + * A path is set up using functions such as Line, Rectangle and Arc. + * + * Does not consume the current path. + */ void Stroke(); + /** + * Fill the current path according to the current settings. + * + * Does not consume the current path. + */ void Fill(); + /** + * Forgets the current path. + */ void NewPath(); + + /** + * Closes the path, i.e. connects the last drawn point to the first one. + * + * Filling a path will implicitly close it. + */ void ClosePath(); + /** + * Moves the pen to a new point without drawing a line. + */ void MoveTo(gfxPoint pt); + + /** + * Draws a line from the current point to pt. + * + * @see MoveTo + */ void LineTo(gfxPoint pt); + + /** + * Draws a quadratic Bézier curve with control points pt1, pt2 and pt3. + */ void CurveTo(gfxPoint pt1, gfxPoint pt2, gfxPoint pt3); - // clockwise arc + /** + * Draws a clockwise arc (i.e. a circle segment). + * @param center The center of the circle + * @param radius The radius of the circle + * @param angle1 Starting angle for the segment + * @param angle2 Ending angle + */ void Arc(gfxPoint center, gfxFloat radius, gfxFloat angle1, gfxFloat angle2); - // counterclockwise arc + /** + * Draws a counter-clockwise arc (i.e. a circle segment). + * @param center The center of the circle + * @param radius The radius of the circle + * @param angle1 Starting angle for the segment + * @param angle2 Ending angle + */ + void NegativeArc(gfxPoint center, gfxFloat radius, gfxFloat angle1, gfxFloat angle2); // path helpers + /** + * Draws a line from start to end. + */ void Line(gfxPoint start, gfxPoint end); // XXX snapToPixels option? + + /** + * Draws the rectangle given by rect. + * @param snapToPixels ? + */ void Rectangle(gfxRect rect, PRBool snapToPixels = PR_FALSE); void Ellipse(gfxPoint center, gfxSize dimensions); void Polygon(const gfxPoint *points, PRUint32 numPoints); @@ -104,56 +172,172 @@ public: ** Text **/ - // Add the text outline to the current path + /** + * Add the text outline to the current path. + */ void AddStringToPath(gfxTextRun& text, int pos, int len); - // Draw a substring of the text run at the current point + /** + * Draw a substring of the text run at the current point. + */ void DrawString(gfxTextRun& text, int pos, int len); /** ** Transformation Matrix manipulation **/ + /** + * Adds a translation to the current matrix. This translation takes place + * before the previously set transformations. + */ void Translate(gfxPoint pt); - void Scale(gfxFloat x, gfxFloat y); - void Rotate(gfxFloat angle); // radians - // post-multiplies 'other' onto the current CTM + /** + * Adds a scale to the current matrix. This scaling takes place before the + * previously set transformations. + */ + void Scale(gfxFloat x, gfxFloat y); + + /** + * Adds a rotation around the origin to the current matrix. This rotation + * takes place before the previously set transformations. + * + * @param angle The angle in radians. + */ + void Rotate(gfxFloat angle); + + /** + * Post-multiplies 'other' onto the current CTM, i.e. this + * matrix's transformation will take place before the previously set + * transformations. + */ void Multiply(const gfxMatrix& other); + /** + * Replaces the current transformation matrix with matrix. + */ void SetMatrix(const gfxMatrix& matrix); + + /** + * Sets the transformation matrix to the identity matrix. + */ void IdentityMatrix(); + + /** + * Returns the current transformation matrix. + */ gfxMatrix CurrentMatrix() const; + /** + * Converts a point from device to user coordinates using the inverse + * transformation matrix. + */ gfxPoint DeviceToUser(gfxPoint point) const; + + /** + * Converts a size from device to user coordinates. This does not apply + * translation components of the matrix. + */ gfxSize DeviceToUser(gfxSize size) const; + + /** + * Converts a rectangle from device to user coordinates; this has the + * same effect as using DeviceToUser on both the rectangle's point and + * size. + */ gfxRect DeviceToUser(gfxRect rect) const; + + /** + * Converts a point from user to device coordinates using the inverse + * transformation matrix. + */ gfxPoint UserToDevice(gfxPoint point) const; + + /** + * Converts a size from user to device coordinates. This does not apply + * translation components of the matrix. + */ gfxSize UserToDevice(gfxSize size) const; + + /** + * Converts a rectangle from user to device coordinates; this has the + * same effect as using DeviceToUser on both the rectangle's point and + * size. + */ gfxRect UserToDevice(gfxRect rect) const; + /** + * Takes the given rect and tries to align it to device pixels. If + * this succeeds, the method will return PR_TRUE, and the rect will + * be in device coordinates (already transformed by the CTM). If it + * fails, the method will return PR_FALSE, and the rect will not be + * changed. + */ + PRBool UserToDevicePixelSnapped(gfxRect& rect) const; + + /** + * Attempts to pixel snap the rectangle, add it to the current + * path, and to set pattern as the current painting source. This + * should be used for drawing filled pixel-snapped rectangles (like + * images), because the CTM at the time of the SetPattern call needs + * to have a snapped translation, or you get smeared images. + */ + void PixelSnappedRectangleAndSetPattern(const gfxRect& rect, gfxPattern *pattern); + /** ** Painting sources **/ + /** + * Uses a solid color for drawing. + */ void SetColor(const gfxRGBA& c); + + /** + * Uses a pattern for drawing. + */ void SetPattern(gfxPattern *pattern); - void SetSource(gfxASurface *surface) { - SetSource(surface, gfxPoint(0, 0)); - } - void SetSource(gfxASurface* surface, gfxPoint offset); + + /** + * Uses a surface for drawing. This is a shorthand for creating a + * pattern and setting it. + * + * @param offset ? + */ + void SetSource(gfxASurface* surface, gfxPoint offset = gfxPoint(0.0, 0.0)); /** ** Painting **/ + /** + * Paints the current source surface/pattern everywhere in the current + * clip region. + */ void Paint(gfxFloat alpha = 1.0); + /** + ** Painting with a Mask + **/ + /** + * Like Paint, except that it only draws the source where pattern is + * non-transparent. + */ + void Mask(gfxPattern *pattern); + + /** + * Shorthand for creating a pattern and calling the pattern-taking + * variant of Mask. + */ + void Mask(gfxASurface *surface, gfxPoint offset = gfxPoint(0.0, 0.0)); + /** ** Shortcuts **/ - // Creates a new path with a rectangle from 0,0 to size.w,size.h - // and calls cairo_fill + /** + * Creates a new path with a rectangle from 0,0 to size.w,size.h + * and calls cairo_fill. + */ void DrawSurface(gfxASurface *surface, gfxSize size); /** @@ -170,7 +354,16 @@ public: void SetDash(gfxFloat *dashes, int ndash, gfxFloat offset); //void getDash() const; + /** + * Sets the line width that's used for line drawing. + */ void SetLineWidth(gfxFloat width); + + /** + * Returns the currently set line width. + * + * @see SetLineWidth + */ gfxFloat CurrentLineWidth() const; enum GraphicsLineCap { @@ -178,6 +371,9 @@ public: LINE_CAP_ROUND, LINE_CAP_SQUARE }; + /** + * Sets the line caps, i.e. how line endings are drawn. + */ void SetLineCap(GraphicsLineCap cap); GraphicsLineCap CurrentLineCap() const; @@ -186,6 +382,10 @@ public: LINE_JOIN_ROUND, LINE_JOIN_BEVEL }; + /** + * Sets the line join, i.e. how the connection between two lines is + * drawn. + */ void SetLineJoin(GraphicsLineJoin join); GraphicsLineJoin CurrentLineJoin() const; @@ -198,21 +398,30 @@ public: // define enum for operators (clear, src, dst, etc) enum GraphicsOperator { - OPERATOR_CLEAR, - OPERATOR_SRC, - OPERATOR_DST, - OPERATOR_OVER, - OPERATOR_OVER_REVERSE, - OPERATOR_IN, - OPERATOR_IN_REVERSE, - OPERATOR_OUT, - OPERATOR_OUT_REVERSE, - OPERATOR_ATOP, - OPERATOR_ATOP_REVERSE, - OPERATOR_XOR, - OPERATOR_ADD, - OPERATOR_SATURATE + OPERATOR_CLEAR = CAIRO_OPERATOR_CLEAR, + OPERATOR_SOURCE = CAIRO_OPERATOR_SOURCE, + + OPERATOR_OVER = CAIRO_OPERATOR_OVER, + OPERATOR_IN = CAIRO_OPERATOR_IN, + OPERATOR_OUT = CAIRO_OPERATOR_OUT, + OPERATOR_ATOP = CAIRO_OPERATOR_ATOP, + + OPERATOR_DEST = CAIRO_OPERATOR_DEST, + OPERATOR_DEST_OVER = CAIRO_OPERATOR_DEST_OVER, + OPERATOR_DEST_IN = CAIRO_OPERATOR_DEST_IN, + OPERATOR_DEST_OUT = CAIRO_OPERATOR_DEST_OUT, + OPERATOR_DEST_ATOP = CAIRO_OPERATOR_DEST_ATOP, + + OPERATOR_XOR = CAIRO_OPERATOR_XOR, + OPERATOR_ADD = CAIRO_OPERATOR_ADD, + OPERATOR_SATURATE = CAIRO_OPERATOR_SATURATE }; + /** + * Sets the operator used for all further drawing. The operator affects + * how drawing something will modify the destination. For example, the + * OVER operator will do alpha blending of source and destination, while + * SOURCE will replace the destination with the source. + */ void SetOperator(GraphicsOperator op); GraphicsOperator CurrentOperator() const; @@ -232,11 +441,22 @@ public: ** Clipping **/ - void Clip(); // will clip the last path you've drawn - void ResetClip(); // will remove any clip set - // Helper functions that will create a rect path and call Clip(). - // Any current path will be destroyed by these functions! - // + /** + * Clips all further drawing to the current path. + * This does not consume the current path. + */ + void Clip(); + + /** + * Undoes any clipping. Further drawings will only be restricted by the + * surface dimensions. + */ + void ResetClip(); + + /** + * Helper functions that will create a rect path and call Clip(). + * Any current path will be destroyed by these functions! + */ void Clip(gfxRect rect); // will clip to a rect void Clip(const gfxRegion& region); // will clip to a region @@ -249,11 +469,15 @@ public: FILTER_OPAQUE_DRAW }; - // Start rendering under the filter. We guarantee not to draw outside 'maxArea'. + /** + * Start rendering under the filter. We guarantee not to draw outside 'maxArea'. + */ void PushFilter(gfxFilter& filter, FilterHints hints, gfxRect& maxArea); - // Completed rendering under the filter, composite what we rendered back to the - // underlying surface using the filter. + /** + * Completed rendering under the filter, composite what we rendered back to the + * underlying surface using the filter. + */ void PopFilter(); private: diff --git a/gfx/thebes/public/gfxFilter.h b/gfx/thebes/public/gfxFilter.h index e9c6e53f06a..d08222f5b70 100644 --- a/gfx/thebes/public/gfxFilter.h +++ b/gfx/thebes/public/gfxFilter.h @@ -40,6 +40,11 @@ #include "gfxTypes.h" +/** + * A filter. + * + * @see gfxContext::PushFilter, gfxContext::PopFilter + */ class gfxFilter { static gfxFilter* CreateOpacityFilter(gfxFloat alpha); // CreateGaussianFilter, etc diff --git a/gfx/thebes/public/gfxGlitzSurface.h b/gfx/thebes/public/gfxGlitzSurface.h new file mode 100644 index 00000000000..57c3d119f73 --- /dev/null +++ b/gfx/thebes/public/gfxGlitzSurface.h @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Oracle Corporation code. + * + * The Initial Developer of the Original Code is Oracle Corporation. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFX_GLITZSURFACE_H +#define GFX_GLITZSURFACE_H + +#include "gfxASurface.h" + +#include + +/** + * A surface that wraps a glitz surface. + */ +class gfxGlitzSurface : public gfxASurface { + THEBES_DECL_ISUPPORTS_INHERITED + +public: + gfxGlitzSurface(glitz_drawable_t *drawable, + glitz_surface_t *glitzSurface, + PRBool takeOwnership = PR_FALSE); + + virtual ~gfxGlitzSurface(); + + /** + * When double-buffering is used, swaps the back and the front buffer. + */ + void SwapBuffers(); + + unsigned long Width(); + unsigned long Height(); + + glitz_surface_t* GlitzSurface() { return mGlitzSurface; } + glitz_drawable_t* GlitzDrawable() { return mGlitzDrawable; } + +protected: + glitz_drawable_t *mGlitzDrawable; + glitz_surface_t *mGlitzSurface; + PRBool mOwnsSurface; +}; + +#endif /* GFX_GLITZSURFACE_H */ diff --git a/gfx/thebes/public/gfxImageSurface.h b/gfx/thebes/public/gfxImageSurface.h index 48ef7448c55..fc5bd45a58c 100644 --- a/gfx/thebes/public/gfxImageSurface.h +++ b/gfx/thebes/public/gfxImageSurface.h @@ -43,17 +43,25 @@ // ARGB -- raw buffer.. wont be changed.. good for storing data. +/** + * A raw image buffer. The format can be set in the constructor. Its main + * purpose is for storing read-only images and using it as a source surface, + * but it can also be drawn to. + */ class gfxImageSurface : public gfxASurface { THEBES_DECL_ISUPPORTS_INHERITED public: - typedef enum { - ImageFormatARGB32, - ImageFormatRGB24, - ImageFormatA8, - ImageFormatA1 - } gfxImageFormat; - + /** + * Construct an image surface. + * @param format Format of the data + * @param width Width of the surface in pixels + * @param height Height in pixels + * + * @see gfxImageFormat + * + * XXX why not unsigned long for the dimensions? And, why not gfxSize? + */ gfxImageSurface(gfxImageFormat format, long width, long height); virtual ~gfxImageSurface(); @@ -61,7 +69,15 @@ public: int Format() const { return mFormat; } long Width() const { return mWidth; } long Height() const { return mHeight; } + /** + * Distance in bytes between the start of a line and the start of the + * next line. + */ long Stride() const; + /** + * Returns a pointer for the image data. Users of this function can + * write to it, but must not attempt to free the buffer. + */ unsigned char* Data() { return mData; } // delete this data under us and die. private: diff --git a/gfx/thebes/public/gfxMatrix.h b/gfx/thebes/public/gfxMatrix.h index 954d4061e59..87c552deefc 100644 --- a/gfx/thebes/public/gfxMatrix.h +++ b/gfx/thebes/public/gfxMatrix.h @@ -42,20 +42,44 @@ #include "gfxPoint.h" #include "gfxTypes.h" +#include "gfxRect.h" // XX - I don't think this class should use gfxFloat at all, // but should use 'double' and be called gfxDoubleMatrix; // we can then typedef that to gfxMatrix where we typedef // double to be gfxFloat. +/** + * A matrix that represents an affine transformation. Projective + * transformations are not supported. This matrix looks like: + * + * / a b tx \ + * | c d ty | + * \ 0 0 1 / + * + * So, transforming a point (x, y) results in: + * + * / a b 0 \ / a * x + c * y + tx \ T + * (x y 1) * | c d 0 | = | b * x + d * y + ty | + * \ tx ty 1 / \ 1 / + * + */ class gfxMatrix { protected: cairo_matrix_t mat; public: + /** + * Initializes this matrix as the identity matrix. + */ gfxMatrix() { Reset(); } gfxMatrix(const gfxMatrix& m) : mat(m.mat) {} + /** + * Initializes the matrix from individual components. See the class + * description for the layout of the matrix. + */ gfxMatrix(gfxFloat a, gfxFloat b, gfxFloat c, gfxFloat d, gfxFloat tx, gfxFloat ty) { + // XXX cairo_matrix_init? mat.xx = a; mat.yx = b; mat.xy = c; mat.yy = d; mat.x0 = tx; mat.y0 = ty; } @@ -68,11 +92,17 @@ public: return *this; } + /** + * Post-multiplies m onto the matrix. + */ const gfxMatrix& operator *= (const gfxMatrix& m) { return Multiply(m); } - gfxMatrix operator * (const gfxMatrix& m) { + /** + * Multiplies *this with m and returns the result. + */ + gfxMatrix operator * (const gfxMatrix& m) const { return gfxMatrix(*this).Multiply(m); } @@ -94,27 +124,52 @@ public: } // matrix operations + /** + * Resets this matrix to the identity matrix. + */ const gfxMatrix& Reset() { cairo_matrix_init_identity(&mat); return *this; } + /** + * Inverts this matrix, if possible. Otherwise, the matrix is left + * unchanged. + * + * XXX should this do something with the return value of + * cairo_matrix_invert? + */ const gfxMatrix& Invert() { cairo_matrix_invert(&mat); return *this; } + /** + * Scales this matrix. The scale is pre-multiplied onto this matrix, + * i.e. the scaling takes place before the other transformations. + */ const gfxMatrix& Scale(gfxFloat x, gfxFloat y) { cairo_matrix_scale(&mat, x, y); return *this; } + /** + * Translates this matrix. The translation is pre-multiplied onto this matrix, + * i.e. the translation takes place before the other transformations. + */ const gfxMatrix& Translate(const gfxPoint& pt) { cairo_matrix_translate(&mat, pt.x, pt.y); return *this; } + /** + * Rotates this matrix. The rotation is pre-multiplied onto this matrix, + * i.e. the translation takes place after the other transformations. + * + * @param radians Angle in radians. + */ const gfxMatrix& Rotate(gfxFloat radians) { + // cairo_matrix_rotate? gfxFloat s = sin(radians); gfxFloat c = cos(radians); gfxMatrix t( c, s, @@ -123,27 +178,58 @@ public: return *this = t.Multiply(*this); } + /** + * Multiplies the current matrix with m. + * This is a post-multiplication, i.e. the transformations of m are + * applied _after_ the existing transformations. + * + * XXX is that difference (compared to Rotate etc) a good thing? + */ const gfxMatrix& Multiply(const gfxMatrix& m) { cairo_matrix_multiply(&mat, &mat, &m.mat); return *this; } - void TransformDistance(gfxFloat *dx, gfxFloat *dy) const { - cairo_matrix_transform_distance(&mat, dx, dy); + /** + * Transforms a point according to this matrix. + */ + gfxPoint Transform(const gfxPoint point) const { + gfxPoint ret = point; + cairo_matrix_transform_point(&mat, &ret.x, &ret.y); + return ret; } - void TransformPoint(gfxFloat *x, gfxFloat *y) const { - cairo_matrix_transform_point(&mat, x, y); + /** + * Transform a distance according to this matrix. This does not apply + * any translation components. + */ + gfxSize Transform(const gfxSize size) const { + gfxSize ret = size; + cairo_matrix_transform_distance(&mat, &ret.width, &ret.height); + return ret; } + /** + * Transforms both the point and distance according to this matrix. + */ + gfxRect Transform(const gfxRect rect) const { + gfxRect ret(Transform(rect.pos), Transform(rect.size)); + return ret; + } + + // XXX this is wrong and should go away gfxSize GetScaling() const { return gfxSize(mat.xx, mat.yy); } + /** + * Returns the translation component of this matrix. + */ gfxPoint GetTranslation() const { return gfxPoint(mat.x0, mat.y0); } + // XXX this is wrong and should go away bool HasShear() const { return ((mat.xy != 0.0) || (mat.yx != 0.0)); } diff --git a/gfx/thebes/public/gfxWindowsSurface.h b/gfx/thebes/public/gfxWindowsSurface.h index 0e110fff513..c798549ab80 100644 --- a/gfx/thebes/public/gfxWindowsSurface.h +++ b/gfx/thebes/public/gfxWindowsSurface.h @@ -47,7 +47,19 @@ class gfxWindowsSurface : public gfxASurface { public: gfxWindowsSurface(HDC dc); + gfxWindowsSurface(HDC dc, unsigned long width, unsigned long height); + gfxWindowsSurface(unsigned long width, unsigned long height); virtual ~gfxWindowsSurface(); + + + HDC GetDC() { return mDC; } +private: + PRBool mOwnsDC; + HDC mDC; + HBITMAP mOrigBitmap; + + PRInt32 mWidth; + PRInt32 mHeight; }; #endif /* GFX_WINDOWSSURFACE_H */ diff --git a/gfx/thebes/public/gfxXlibSurface.h b/gfx/thebes/public/gfxXlibSurface.h index a7ab5f7b123..990a66137c1 100644 --- a/gfx/thebes/public/gfxXlibSurface.h +++ b/gfx/thebes/public/gfxXlibSurface.h @@ -20,6 +20,7 @@ * * Contributor(s): * Stuart Parmenter + * Vladimir Vukicevic * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -41,6 +42,7 @@ #include "gfxASurface.h" #include +#include class gfxXlibSurface : public gfxASurface { THEBES_DECL_ISUPPORTS_INHERITED @@ -59,6 +61,12 @@ public: // for the default screen, and attach the given visual gfxXlibSurface(Display *dpy, Visual *visual, unsigned long width, unsigned long height); + gfxXlibSurface(Display* dpy, Drawable drawable, XRenderPictFormat *format, + unsigned long width, unsigned long height); + + gfxXlibSurface(Display* dpy, XRenderPictFormat *format, + unsigned long width, unsigned long height); + virtual ~gfxXlibSurface(); unsigned long Width() { return mWidth; } @@ -67,6 +75,8 @@ public: Display* XDisplay() { return mDisplay; } Drawable XDrawable() { return mDrawable; } + static XRenderPictFormat *FindRenderFormat(Display *dpy, gfxImageFormat format); + protected: PRBool mOwnsPixmap; diff --git a/gfx/thebes/src/Makefile.in b/gfx/thebes/src/Makefile.in index 072b6a217cd..5ca5623cb50 100644 --- a/gfx/thebes/src/Makefile.in +++ b/gfx/thebes/src/Makefile.in @@ -14,7 +14,8 @@ REQUIRES = \ cairo \ libpixman \ string \ - xpcom + xpcom \ + $(NULL) CPPSRCS = \ gfxContext.cpp \ @@ -31,6 +32,15 @@ ifeq ($(MOZ_GFX_TOOLKIT),gtk2) CPPSRCS += gfxXlibSurface.cpp endif +ifdef MOZ_ENABLE_GLITZ +REQUIRES += glitz +CPPSRCS += gfxGlitzSurface.cpp + +ifeq ($(MOZ_GFX_TOOLKIT),gtk2) +REQUIRES += glitzglx +endif +endif + FORCE_STATIC_LIB = 1 # This library is used by other shared libs in a static build FORCE_USE_PIC = 1 diff --git a/gfx/thebes/src/gfxContext.cpp b/gfx/thebes/src/gfxContext.cpp index ad996dcc001..be4d95245bc 100644 --- a/gfx/thebes/src/gfxContext.cpp +++ b/gfx/thebes/src/gfxContext.cpp @@ -20,6 +20,7 @@ * * Contributor(s): * Stuart Parmenter + * Vladimir Vukicevic * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -120,40 +121,26 @@ void gfxContext::Line(gfxPoint start, gfxPoint end) LineTo(end); } +// XXX snapToPixels is only valid when snapping for filled +// rectangles and for even-width stroked rectangles. +// For odd-width stroked rectangles, we need to offset x/y by +// 0.5... void gfxContext::Rectangle(gfxRect rect, PRBool snapToPixels) { if (snapToPixels) { - gfxPoint p1 = UserToDevice(rect.pos); - gfxPoint p2 = UserToDevice(rect.pos + rect.size); + gfxRect snappedRect(rect); - gfxPoint p3 = UserToDevice(rect.pos + gfxSize(rect.size.width, 0.0)); - gfxPoint p4 = UserToDevice(rect.pos + gfxSize(0.0, rect.size.height)); + if (UserToDevicePixelSnapped(snappedRect)) { + cairo_matrix_t mat; + cairo_get_matrix(mCairo, &mat); + cairo_identity_matrix(mCairo); + Rectangle(snappedRect); + cairo_set_matrix(mCairo, &mat); - if (p1.x != p4.x || - p2.x != p3.x || - p1.y != p3.y || - p2.y != p4.y) - // rectangle is no longer axis-aligned after transforming, so don't snap - goto dontsnap; - - cairo_matrix_t mat; - cairo_get_matrix(mCairo, &mat); - - if (mat.xx != 1.0 || - mat.yy != 1.0) - // if we're not at 1.0 scale, don't snap - goto dontsnap; - - p1.round(); - p2.round(); - - cairo_identity_matrix(mCairo); - cairo_rectangle(mCairo, p1.x, p1.y, p2.x - p1.x, p2.y - p1.y); - cairo_set_matrix(mCairo, &mat); - return; + return; + } } -dontsnap: cairo_rectangle(mCairo, rect.pos.x, rect.pos.y, rect.size.width, rect.size.height); } @@ -297,6 +284,69 @@ gfxRect gfxContext::UserToDevice(gfxRect rect) const return ret; } +PRBool gfxContext::UserToDevicePixelSnapped(gfxRect& rect) const +{ + // if we're not at 1.0 scale, don't snap + cairo_matrix_t mat; + cairo_get_matrix(mCairo, &mat); + if (mat.xx != 1.0 || mat.yy != 1.0) + return PR_FALSE; + + gfxPoint p1 = UserToDevice(rect.pos); + gfxPoint p2 = UserToDevice(rect.pos + rect.size); + + gfxPoint p3 = UserToDevice(rect.pos + gfxSize(rect.size.width, 0.0)); + gfxPoint p4 = UserToDevice(rect.pos + gfxSize(0.0, rect.size.height)); + + // rectangle is no longer axis-aligned after transforming, so we can't snap + if (p1.x != p4.x || + p2.x != p3.x || + p1.y != p3.y || + p2.y != p4.y) + return PR_FALSE; + + p1.round(); + p2.round(); + + gfxPoint pd = p2 - p1; + + rect.pos = p1; + rect.size = gfxSize(pd.x, pd.y); + + return PR_TRUE; +} + +void gfxContext::PixelSnappedRectangleAndSetPattern(const gfxRect& rect, + gfxPattern *pattern) +{ + gfxRect r(rect); + + // Bob attempts to pixel-snap the rectangle, and returns true if + // the snapping succeeds. If it does, we need to set up an + // identity matrix, because the rectangle given back is in device + // coordinates. + // + // We then have to call a translate to dr.pos afterwards, to make + // sure the image lines up in the right place with our pixel + // snapped rectangle. + // + // If snapping wasn't successful, we just translate to where the + // pattern would normally start (in app coordinates) and do the + // same thing. + + gfxMatrix mat = CurrentMatrix(); + if (UserToDevicePixelSnapped(r)) { + IdentityMatrix(); + } + + Translate(r.pos); + r.pos.x = r.pos.y = 0; + Rectangle(r); + SetPattern(pattern); + + SetMatrix(mat); +} + void gfxContext::SetAntialiasMode(AntialiasMode mode) { // XXX implement me @@ -418,6 +468,18 @@ void gfxContext::SetSource(gfxASurface *surface, gfxPoint offset) cairo_set_source_surface(mCairo, surface->CairoSurface(), offset.x, offset.y); } +// masking + +void gfxContext::Mask(gfxPattern *pattern) +{ + cairo_mask(mCairo, pattern->CairoPattern()); +} + +void gfxContext::Mask(gfxASurface *surface, gfxPoint offset) +{ + cairo_mask_surface(mCairo, surface->CairoSurface(), offset.x, offset.y); +} + // fonts? void gfxContext::DrawString(gfxTextRun& text, int pos, int len) { diff --git a/gfx/thebes/src/gfxGlitzSurface.cpp b/gfx/thebes/src/gfxGlitzSurface.cpp new file mode 100644 index 00000000000..7bb14db1605 --- /dev/null +++ b/gfx/thebes/src/gfxGlitzSurface.cpp @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is thebes + * + * The Initial Developer of the Original Code is + * mozilla.org + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "gfxGlitzSurface.h" + +THEBES_IMPL_REFCOUNTING(gfxGlitzSurface) + +gfxGlitzSurface::gfxGlitzSurface(glitz_drawable_t *drawable, glitz_surface_t *surface, PRBool takeOwnership) + : mGlitzDrawable (drawable), mGlitzSurface(surface), mOwnsSurface(takeOwnership) +{ + cairo_surface_t *surf = cairo_glitz_surface_create (mGlitzSurface); + Init(surf); +} + +gfxGlitzSurface::~gfxGlitzSurface() +{ + Destroy(); + + if (mOwnsSurface) { + if (mGlitzSurface) { + glitz_surface_flush(mGlitzSurface); + glitz_surface_destroy(mGlitzSurface); + } + + if (mGlitzDrawable) { + glitz_drawable_flush(mGlitzDrawable); + glitz_drawable_finish(mGlitzDrawable); + glitz_drawable_destroy(mGlitzDrawable); + } + } +} + +void +gfxGlitzSurface::SwapBuffers() +{ + glitz_drawable_swap_buffers (GlitzDrawable()); +} + +unsigned long +gfxGlitzSurface::Width() +{ + return glitz_drawable_get_width (GlitzDrawable()); +} + +unsigned long +gfxGlitzSurface::Height() +{ + return glitz_drawable_get_height (GlitzDrawable()); +} diff --git a/gfx/thebes/src/gfxWindowsSurface.cpp b/gfx/thebes/src/gfxWindowsSurface.cpp index 20d6cb7bb47..ce02b2d0baa 100644 --- a/gfx/thebes/src/gfxWindowsSurface.cpp +++ b/gfx/thebes/src/gfxWindowsSurface.cpp @@ -39,11 +39,44 @@ THEBES_IMPL_REFCOUNTING(gfxWindowsSurface) -gfxWindowsSurface::gfxWindowsSurface(HDC dc) +gfxWindowsSurface::gfxWindowsSurface(HDC dc) : + mOwnsDC(PR_FALSE), mDC(dc), mOrigBitmap(nsnull) { - Init(cairo_win32_surface_create(dc)); + Init(cairo_win32_surface_create(mDC)); +} + +gfxWindowsSurface::gfxWindowsSurface(HDC dc, unsigned long width, unsigned long height) : + mOwnsDC(PR_TRUE), mWidth(width), mHeight(height) +{ + mDC = CreateCompatibleDC(dc); + + HBITMAP tbits = nsnull; + + if (width > 0 && height > 0) + tbits = CreateCompatibleBitmap(dc, width, height); + else + tbits = CreateCompatibleBitmap(dc, 2, 2); + + mOrigBitmap = (HBITMAP)SelectObject(mDC, tbits); + + Init(cairo_win32_surface_create(mDC)); +} + +gfxWindowsSurface::gfxWindowsSurface(unsigned long width, unsigned long height) +{ + // Init(cairo_win32_surface_create(dc)); } gfxWindowsSurface::~gfxWindowsSurface() { + Destroy(); + + if (mDC && mOrigBitmap) { + HBITMAP tbits = (HBITMAP)SelectObject(mDC, mOrigBitmap); + if (tbits) + DeleteObject(tbits); + } + + if (mOwnsDC) + DeleteDC(mDC); } diff --git a/gfx/thebes/src/gfxXlibSurface.cpp b/gfx/thebes/src/gfxXlibSurface.cpp index 3d8bbc3257e..6cf451e7986 100644 --- a/gfx/thebes/src/gfxXlibSurface.cpp +++ b/gfx/thebes/src/gfxXlibSurface.cpp @@ -36,12 +36,13 @@ * * ***** END LICENSE BLOCK ***** */ +#include #include "gfxXlibSurface.h" THEBES_IMPL_REFCOUNTING(gfxXlibSurface) -gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual) : - mOwnsPixmap(PR_FALSE), mDisplay(dpy), mDrawable(drawable) +gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual) + : mOwnsPixmap(PR_FALSE), mDisplay(dpy), mDrawable(drawable) { // figure out width/height/depth Window root_ignore; @@ -62,15 +63,15 @@ gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual) } gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, Visual* visual, - unsigned long width, unsigned long height) : - mOwnsPixmap(PR_FALSE), mDisplay(dpy), mDrawable(drawable), mWidth(width), mHeight(height) + unsigned long width, unsigned long height) + : mOwnsPixmap(PR_FALSE), mDisplay(dpy), mDrawable(drawable), mWidth(width), mHeight(height) { cairo_surface_t *surf = cairo_xlib_surface_create(dpy, drawable, visual, width, height); Init(surf); } -gfxXlibSurface::gfxXlibSurface(Display* dpy, Visual* visual, unsigned long width, unsigned long height) : - mOwnsPixmap(PR_TRUE), mDisplay(dpy), mWidth(width), mHeight(height) +gfxXlibSurface::gfxXlibSurface(Display* dpy, Visual* visual, unsigned long width, unsigned long height) + : mOwnsPixmap(PR_TRUE), mDisplay(dpy), mWidth(width), mHeight(height) { mDrawable = (Drawable)XCreatePixmap(dpy, @@ -82,6 +83,32 @@ gfxXlibSurface::gfxXlibSurface(Display* dpy, Visual* visual, unsigned long width Init(surf); } +gfxXlibSurface::gfxXlibSurface(Display* dpy, Drawable drawable, XRenderPictFormat *format, + unsigned long width, unsigned long height) + : mOwnsPixmap(PR_FALSE), mDisplay(dpy), mDrawable(drawable), + mWidth(width), mHeight(height) +{ + cairo_surface_t *surf = cairo_xlib_surface_create_with_xrender_format (dpy, drawable, + ScreenOfDisplay(dpy,DefaultScreen(dpy)), + format, width, height); + Init(surf); +} + +gfxXlibSurface::gfxXlibSurface(Display* dpy, XRenderPictFormat *format, + unsigned long width, unsigned long height) + : mOwnsPixmap(PR_TRUE), mDisplay(dpy), mWidth(width), mHeight(height) +{ + mDrawable = (Drawable)XCreatePixmap(dpy, + RootWindow(dpy, DefaultScreen(dpy)), + width, height, + format->depth); + + cairo_surface_t *surf = cairo_xlib_surface_create_with_xrender_format (dpy, mDrawable, + ScreenOfDisplay(dpy,DefaultScreen(dpy)), + format, width, height); + Init(surf); +} + gfxXlibSurface::~gfxXlibSurface() { Destroy(); @@ -89,3 +116,24 @@ gfxXlibSurface::~gfxXlibSurface() if (mOwnsPixmap) XFreePixmap(mDisplay, mDrawable); } + +XRenderPictFormat* +gfxXlibSurface::FindRenderFormat(Display *dpy, gfxImageFormat format) +{ + switch (format) { + case ImageFormatARGB32: + return XRenderFindStandardFormat (dpy, PictStandardARGB32); + break; + case ImageFormatRGB24: + return XRenderFindStandardFormat (dpy, PictStandardRGB24); + break; + case ImageFormatA8: + return XRenderFindStandardFormat (dpy, PictStandardA8); + break; + case ImageFormatA1: + return XRenderFindStandardFormat (dpy, PictStandardA1); + break; + } + + return (XRenderPictFormat*)NULL; +}