bug 631479 (part 4) - implement gfxGraphiteShaper to handle text-shaping for graphite-enabled fonts. r=jdaggett

This commit is contained in:
Jonathan Kew 2011-12-09 22:32:29 +00:00
parent e24f640930
commit 164f1ffdde
16 changed files with 8830 additions and 7 deletions

View File

@ -216,6 +216,12 @@ CPPSRCS = \
gfxSharedImageSurface.cpp \
$(NULL)
ifdef MOZ_GRAPHITE
CPPSRCS += \
gfxGraphiteShaper.cpp \
$(NULL)
endif
# Are we targeting x86 or x64? If so, build gfxAlphaRecoverySSE2.cpp.
ifneq (,$(INTEL_ARCHITECTURE))
CPPSRCS += gfxAlphaRecoverySSE2.cpp

View File

@ -0,0 +1,150 @@
#!/usr/bin/env perl
# ***** 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 Foundation code.
#
# The Initial Developer of the Original Code is Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2009-2010
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Jonathan Kew <jfkthame@gmail.com>
#
# 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 *****
# This tool is used to prepare a list of valid language subtags from the
# IANA registry (http://www.iana.org/assignments/language-subtag-registry).
# Run as
#
# perl genLanguageTagList.pl language-subtag-registry > gfxLanguageTagList.cpp
#
# where language-subtag-registry is a copy of the IANA registry file.
use strict;
my $timestamp = gmtime();
print <<__END;
/* ***** 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 Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009-2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonathan Kew <jfkthame\@gmail.com>
*
* 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 ***** */
/*
* Derived from the IANA language subtag registry by genLanguageTagList.pl.
*
* Created on $timestamp.
*
* * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
*/
__END
my $isLanguage = 0;
while (<>) {
# strip leading/trailing whitespace, if any
chomp;
s/^\s+//;
s/\s+$//;
# assume File-Date precedes the actual list;
# record the date, and begin assignment to an array of valid tags
if (m/^File-Date:\s*(.+)$/) {
print "// Based on IANA registry dated $1\n\n";
print "static const PRUint32 sLanguageTagList[] = {";
next;
}
if (m/^%%/) {
$isLanguage = 0;
next;
}
# we only care about records of type 'language'
if (m/^Type:\s*(.+)$/) {
$isLanguage = ($1 eq 'language');
next;
}
# append the tag to our string, with ";" as a delimiter
if ($isLanguage && m/^Subtag:\s*([a-z]{2,3})\s*$/) {
my $tagstr = $1;
print "\n TRUETYPE_TAG(",
join(",", map { $_ eq " " ? " 0 " : "'" . $_ . "'" } split(//, substr($tagstr . " ", 0, 4))),
"), // ", $tagstr;
next;
}
if ($isLanguage && m/^Description:\s*(.+)$/) {
print " = $1";
$isLanguage = 0; # only print first Description field
next;
}
}
# at end of file, terminate our assignment to the array of tags
print <<__END;
0x0 // end of language code list
};
/*
* * * * * This file contains MACHINE-GENERATED DATA, do not edit! * * * * *
*/
__END

View File

@ -38,6 +38,9 @@
#include "gfxDWriteFonts.h"
#include "gfxDWriteShaper.h"
#include "gfxHarfBuzzShaper.h"
#ifdef MOZ_GRAPHITE
#include "gfxGraphiteShaper.h"
#endif
#include "gfxDWriteFontList.h"
#include "gfxContext.h"
#include <dwrite.h>
@ -151,6 +154,12 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
ComputeMetrics(anAAOption);
#ifdef MOZ_GRAPHITE
if (FontCanSupportGraphite()) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
#endif
if (FontCanSupportHarfBuzz()) {
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}

View File

@ -56,6 +56,9 @@
#include "gfxFT2FontList.h"
#include <locale.h>
#include "gfxHarfBuzzShaper.h"
#ifdef MOZ_GRAPHITE
#include "gfxGraphiteShaper.h"
#endif
#include "gfxUnicodeProperties.h"
#include "gfxAtoms.h"
#include "nsTArray.h"
@ -432,7 +435,20 @@ gfxFT2Font::InitTextRun(gfxContext *aContext,
{
bool ok = false;
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) {
#ifdef MOZ_GRAPHITE
if (FontCanSupportGraphite()) {
if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
if (!mGraphiteShaper) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
ok = mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,
aRunScript);
}
}
#endif
if (!ok && gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) {
if (!mHarfBuzzShaper) {
gfxFT2LockedFace face(this);
mFUnitsConvFactor = face.XScale();

View File

@ -384,6 +384,16 @@ gfxFontEntry::ShareFontTableAndGetBlob(PRUint32 aTag,
return entry->ShareTableAndGetBlob(*aBuffer, &mFontTableCache);
}
#ifdef MOZ_GRAPHITE
void
gfxFontEntry::CheckForGraphiteTables()
{
AutoFallibleTArray<PRUint8,16384> buffer;
mHasGraphiteTables =
NS_SUCCEEDED(GetFontTable(TRUETYPE_TAG('S','i','l','f'), buffer));
}
#endif
//////////////////////////////////////////////////////////////////////////////
//
// class gfxFontFamily
@ -1064,9 +1074,7 @@ gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
mStyle(*aFontStyle),
mAdjustedSize(0.0),
mFUnitsConvFactor(0.0f),
mAntialiasOption(anAAOption),
mPlatformShaper(nsnull),
mHarfBuzzShaper(nsnull)
mAntialiasOption(anAAOption)
{
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
++gFontCount;
@ -1603,7 +1611,15 @@ gfxFont::InitTextRun(gfxContext *aContext,
{
bool ok = false;
if (mHarfBuzzShaper && !aPreferPlatformShaping) {
#ifdef MOZ_GRAPHITE
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
ok = mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,
aRunScript);
}
#endif
if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) {
ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,

View File

@ -212,6 +212,9 @@ public:
mSymbolFont(false),
mIgnoreGDEF(false),
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
#ifdef MOZ_GRAPHITE
mCheckedForGraphiteTables(false),
#endif
mHasCmapTable(false),
mCmapInitialized(false),
mUVSOffset(0), mUVSData(nsnull),
@ -244,6 +247,16 @@ public:
virtual bool IsSymbolFont();
#ifdef MOZ_GRAPHITE
inline bool HasGraphiteTables() {
if (!mCheckedForGraphiteTables) {
CheckForGraphiteTables();
mCheckedForGraphiteTables = true;
}
return mHasGraphiteTables;
}
#endif
inline bool HasCmapTable() {
if (!mCmapInitialized) {
ReadCMAP();
@ -318,6 +331,10 @@ public:
PRUint16 mWeight;
PRInt16 mStretch;
#ifdef MOZ_GRAPHITE
bool mHasGraphiteTables;
bool mCheckedForGraphiteTables;
#endif
bool mHasCmapTable;
bool mCmapInitialized;
gfxSparseBitSet mCharacterMap;
@ -345,6 +362,9 @@ protected:
mSymbolFont(false),
mIgnoreGDEF(false),
mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
#ifdef MOZ_GRAPHITE
mCheckedForGraphiteTables(false),
#endif
mHasCmapTable(false),
mCmapInitialized(false),
mUVSOffset(0), mUVSData(nsnull),
@ -358,6 +378,10 @@ protected:
return nsnull;
}
#ifdef MOZ_GRAPHITE
virtual void CheckForGraphiteTables();
#endif
gfxFontFamily *mFamily;
private:
@ -1004,6 +1028,13 @@ public:
return mFontEntry->HasCmapTable();
}
#ifdef MOZ_GRAPHITE
// check whether this is an sfnt we can potentially use with Graphite
bool FontCanSupportGraphite() {
return mFontEntry->HasGraphiteTables();
}
#endif
// Access to raw font table data (needed for Harfbuzz):
// returns a pointer to data owned by the fontEntry or the OS,
// which will remain valid until released.
@ -1252,6 +1283,9 @@ protected:
// of the text run being shaped
nsAutoPtr<gfxFontShaper> mPlatformShaper;
nsAutoPtr<gfxFontShaper> mHarfBuzzShaper;
#ifdef MOZ_GRAPHITE
nsAutoPtr<gfxFontShaper> mGraphiteShaper;
#endif
// Create a default platform text shaper for this font.
// (TODO: This should become pure virtual once all font backends have

View File

@ -43,6 +43,9 @@
#include "gfxGDIShaper.h"
#include "gfxUniscribeShaper.h"
#include "gfxHarfBuzzShaper.h"
#ifdef MOZ_GRAPHITE
#include "gfxGraphiteShaper.h"
#endif
#include "gfxWindowsPlatform.h"
#include "gfxContext.h"
#include "gfxUnicodeProperties.h"
@ -79,6 +82,11 @@ gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry,
mSpaceGlyph(0),
mNeedsBold(aNeedsBold)
{
#ifdef MOZ_GRAPHITE
if (FontCanSupportGraphite()) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
#endif
if (FontCanSupportHarfBuzz()) {
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}
@ -159,7 +167,15 @@ gfxGDIFont::InitTextRun(gfxContext *aContext,
// creating a "toy" font internally (see bug 544617)
SetupCairoFont(aContext);
if (mHarfBuzzShaper) {
#ifdef MOZ_GRAPHITE
if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
ok = mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,
aRunScript);
}
#endif
if (!ok && mHarfBuzzShaper) {
if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) {
ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,

View File

@ -0,0 +1,478 @@
/* -*- 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 Graphite integration code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonathan Kew <jfkthame@gmail.com>
*
* 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 "prtypes.h"
#include "prmem.h"
#include "nsString.h"
#include "nsBidiUtils.h"
#include "nsMathUtils.h"
#include "nsHashSets.h"
#include "gfxTypes.h"
#include "gfxContext.h"
#include "gfxPlatform.h"
#include "gfxGraphiteShaper.h"
#include "gfxFontUtils.h"
#include "gfxUnicodeProperties.h"
#include "graphite2/Font.h"
#include "graphite2/Segment.h"
#include "harfbuzz/hb-blob.h"
#include "cairo.h"
#include "nsUnicodeRange.h"
#include "nsCRT.h"
#define FloatToFixed(f) (65536 * (f))
#define FixedToFloat(f) ((f) * (1.0 / 65536.0))
// Right shifts of negative (signed) integers are undefined, as are overflows
// when converting unsigned to negative signed integers.
// (If speed were an issue we could make some 2's complement assumptions.)
#define FixedToIntRound(f) ((f) > 0 ? ((32768 + (f)) >> 16) \
: -((32767 - (f)) >> 16))
using namespace mozilla; // for AutoSwap_* types
/*
* Creation and destruction; on deletion, release any font tables we're holding
*/
gfxGraphiteShaper::gfxGraphiteShaper(gfxFont *aFont)
: gfxFontShaper(aFont),
mGrFace(nsnull),
mGrFont(nsnull),
mUseFontGlyphWidths(false)
{
mTables.Init();
mCallbackData.mFont = aFont;
mCallbackData.mShaper = this;
}
PLDHashOperator
ReleaseTableFunc(const PRUint32& /* aKey */,
gfxGraphiteShaper::TableRec& aData,
void* /* aUserArg */)
{
hb_blob_unlock(aData.mBlob);
hb_blob_destroy(aData.mBlob);
return PL_DHASH_REMOVE;
}
gfxGraphiteShaper::~gfxGraphiteShaper()
{
if (mGrFont) {
gr_font_destroy(mGrFont);
}
if (mGrFace) {
gr_face_destroy(mGrFace);
}
mTables.Enumerate(ReleaseTableFunc, nsnull);
}
static const void*
GrGetTable(const void* appFaceHandle, unsigned int name, size_t *len)
{
const gfxGraphiteShaper::CallbackData *cb =
static_cast<const gfxGraphiteShaper::CallbackData*>(appFaceHandle);
return cb->mShaper->GetTable(name, len);
}
const void*
gfxGraphiteShaper::GetTable(PRUint32 aTag, size_t *aLength)
{
TableRec tableRec;
if (!mTables.Get(aTag, &tableRec)) {
hb_blob_t *blob = mFont->GetFontTable(aTag);
if (blob) {
// mFont->GetFontTable() gives us a reference to the blob.
// We will destroy (release) it in our destructor.
// Meanwhile, we hold the blob locked because Graphite expects
// the table pointer to remain valid for the life of the face.
tableRec.mBlob = blob;
tableRec.mData = hb_blob_lock(blob);
tableRec.mLength = hb_blob_get_length(blob);
mTables.Put(aTag, tableRec);
} else {
return nsnull;
}
}
*aLength = tableRec.mLength;
return tableRec.mData;
}
static float
GrGetAdvance(const void* appFontHandle, gr_uint16 glyphid)
{
const gfxGraphiteShaper::CallbackData *cb =
static_cast<const gfxGraphiteShaper::CallbackData*>(appFontHandle);
return FixedToFloat(cb->mFont->GetGlyphWidth(cb->mContext, glyphid));
}
static inline PRUint32
MakeGraphiteLangTag(PRUint32 aTag)
{
PRUint32 grLangTag = aTag;
// replace trailing space-padding with NULs for graphite
PRUint32 mask = 0x000000FF;
while ((grLangTag & mask) == ' ') {
grLangTag &= ~mask;
mask <<= 8;
}
return grLangTag;
}
bool
gfxGraphiteShaper::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript)
{
// some font back-ends require this in order to get proper hinted metrics
mFont->SetupCairoFont(aContext);
mCallbackData.mContext = aContext;
if (!mGrFont) {
mGrFace = gr_make_face(&mCallbackData, GrGetTable, gr_face_default);
if (!mGrFace) {
return false;
}
mGrFont = mUseFontGlyphWidths ?
gr_make_font_with_advance_fn(mFont->GetAdjustedSize(),
&mCallbackData, GrGetAdvance,
mGrFace) :
gr_make_font(mFont->GetAdjustedSize(), mGrFace);
if (!mGrFont) {
gr_face_destroy(mGrFace);
mGrFace = nsnull;
return false;
}
}
const gfxFontStyle *style = aTextRun->GetFontGroup()->GetStyle();
PRUint32 grLang = 0;
if (style->languageOverride) {
grLang = MakeGraphiteLangTag(style->languageOverride);
} else if (mFont->GetFontEntry()->mLanguageOverride) {
grLang = MakeGraphiteLangTag(mFont->GetFontEntry()->mLanguageOverride);
} else {
nsCAutoString langString;
style->language->ToUTF8String(langString);
grLang = GetGraphiteTagForLang(langString);
}
gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
bool disableLigatures =
(aTextRun->GetFlags() &
gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES) != 0;
if (disableLigatures) {
const gr_feature_ref* fref =
gr_face_find_fref(mGrFace, TRUETYPE_TAG('l','i','g','a'));
if (fref) {
gr_fref_set_feature_value(fref, 0, grFeatures);
}
}
const nsTArray<gfxFontFeature> *features = &style->featureSettings;
if (features->IsEmpty()) {
features = &mFont->GetFontEntry()->mFeatureSettings;
}
for (PRUint32 i = 0; i < features->Length(); ++i) {
const gr_feature_ref* fref =
gr_face_find_fref(mGrFace, (*features)[i].mTag);
if (fref) {
gr_fref_set_feature_value(fref, (*features)[i].mValue, grFeatures);
}
}
const PRUnichar *textStart = aString + aRunStart;
const PRUnichar *textEnd = textStart + aRunLength;
const void *pError;
size_t nChars = gr_count_unicode_characters(gr_utf16,
textStart, textEnd,
&pError);
if (pError != nsnull) {
return false;
}
gr_segment *seg = gr_make_seg(mGrFont, mGrFace, 0, grFeatures,
gr_utf16, textStart, nChars,
aTextRun->IsRightToLeft());
if (features) {
gr_featureval_destroy(grFeatures);
}
if (!seg) {
return false;
}
nsresult rv = SetGlyphsFromSegment(aTextRun, aRunStart, aRunLength, seg);
gr_seg_destroy(seg);
return NS_SUCCEEDED(rv);
}
#define SMALL_GLYPH_RUN 256 // avoid heap allocation of per-glyph data arrays
// for short (typical) runs up to this length
struct Cluster {
PRUint32 baseChar;
PRUint32 baseGlyph;
PRUint32 nChars;
PRUint32 nGlyphs;
Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) { }
};
nsresult
gfxGraphiteShaper::SetGlyphsFromSegment(gfxTextRun *aTextRun,
PRUint32 aRunStart,
PRUint32 aRunLength,
gr_segment *aSegment)
{
PRInt32 dev2appUnits = aTextRun->GetAppUnitsPerDevUnit();
bool rtl = aTextRun->IsRightToLeft();
PRUint32 glyphCount = gr_seg_n_slots(aSegment);
// identify clusters; graphite may have reordered/expanded/ligated glyphs.
nsAutoTArray<Cluster,SMALL_GLYPH_RUN> clusters;
nsAutoTArray<PRUint16,SMALL_GLYPH_RUN> gids;
nsAutoTArray<float,SMALL_GLYPH_RUN> xLocs;
nsAutoTArray<float,SMALL_GLYPH_RUN> yLocs;
if (!clusters.SetLength(aRunLength) ||
!gids.SetLength(glyphCount) ||
!xLocs.SetLength(glyphCount) ||
!yLocs.SetLength(glyphCount))
{
return NS_ERROR_OUT_OF_MEMORY;
}
// walk through the glyph slots and check which original character
// each is associated with
PRUint32 gIndex = 0; // glyph slot index
PRUint32 cIndex = 0; // current cluster index
for (const gr_slot *slot = gr_seg_first_slot(aSegment);
slot != nsnull;
slot = gr_slot_next_in_segment(slot), gIndex++)
{
PRUint32 before = gr_slot_before(slot);
PRUint32 after = gr_slot_after(slot);
gids[gIndex] = gr_slot_gid(slot);
xLocs[gIndex] = gr_slot_origin_X(slot);
yLocs[gIndex] = gr_slot_origin_Y(slot);
// if this glyph has a "before" character index that precedes the
// current cluster's char index, we need to merge preceding
// clusters until it gets included
while (before < clusters[cIndex].baseChar && cIndex > 0) {
clusters[cIndex-1].nChars += clusters[cIndex].nChars;
clusters[cIndex-1].nGlyphs += clusters[cIndex].nGlyphs;
--cIndex;
}
// if there's a gap between the current cluster's base character and
// this glyph's, extend the cluster to include the intervening chars
if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars &&
before >= clusters[cIndex].baseChar + clusters[cIndex].nChars)
{
NS_ASSERTION(cIndex < aRunLength - 1, "cIndex at end of run");
Cluster& c = clusters[cIndex + 1];
c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars;
c.nChars = before - c.baseChar;
c.baseGlyph = gIndex;
c.nGlyphs = 0;
++cIndex;
}
// increment cluster's glyph count to include current slot
NS_ASSERTION(cIndex < aRunLength, "cIndex beyond valid run length");
++clusters[cIndex].nGlyphs;
// extend cluster if necessary to reach the glyph's "after" index
if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) {
clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar;
}
}
// now put glyphs into the textrun, one cluster at a time
for (PRUint32 i = 0; i <= cIndex; ++i) {
const Cluster& c = clusters[i];
float adv; // total advance of the cluster
if (rtl) {
if (i == 0) {
adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
} else {
adv = xLocs[clusters[i-1].baseGlyph] - xLocs[c.baseGlyph];
}
} else {
if (i == cIndex) {
adv = gr_seg_advance_X(aSegment) - xLocs[c.baseGlyph];
} else {
adv = xLocs[clusters[i+1].baseGlyph] - xLocs[c.baseGlyph];
}
}
// Check for default-ignorable char that didn't get filtered, combined,
// etc by the shaping process, and skip it.
PRUint32 offs = gr_cinfo_base(gr_seg_cinfo(aSegment, c.baseChar));
NS_ASSERTION(offs >= c.baseChar && offs < aRunLength,
"unexpected offset");
if (c.nGlyphs == 1 && c.nChars == 1 &&
aTextRun->FilterIfIgnorable(aRunStart + offs))
{
continue;
}
PRUint32 appAdvance = adv * dev2appUnits;
if (c.nGlyphs == 1 &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(gids[c.baseGlyph]) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(appAdvance) &&
yLocs[c.baseGlyph] == 0)
{
gfxTextRun::CompressedGlyph g;
aTextRun->SetSimpleGlyph(aRunStart + offs,
g.SetSimpleGlyph(appAdvance,
gids[c.baseGlyph]));
} else {
// not a one-to-one mapping with simple metrics: use DetailedGlyph
nsAutoTArray<gfxTextRun::DetailedGlyph,8> details;
float clusterLoc;
for (PRUint32 j = c.baseGlyph; j < c.baseGlyph + c.nGlyphs; ++j) {
gfxTextRun::DetailedGlyph* d = details.AppendElement();
d->mGlyphID = gids[j];
d->mYOffset = -yLocs[j] * dev2appUnits;
if (j == c.baseGlyph) {
d->mXOffset = 0;
d->mAdvance = appAdvance;
clusterLoc = xLocs[j];
} else {
d->mXOffset = (xLocs[j] - clusterLoc - adv) * dev2appUnits;
d->mAdvance = 0;
}
}
gfxTextRun::CompressedGlyph g;
g.SetComplex(aTextRun->IsClusterStart(aRunStart + offs),
true, details.Length());
aTextRun->SetGlyphs(aRunStart + offs, g, details.Elements());
}
for (PRUint32 j = c.baseChar + 1; j < c.baseChar + c.nChars; ++j) {
offs = gr_cinfo_base(gr_seg_cinfo(aSegment, j));
NS_ASSERTION(offs >= j && offs < aRunLength,
"unexpected offset");
gfxTextRun::CompressedGlyph g;
g.SetComplex(aTextRun->IsClusterStart(aRunStart + offs),
false, 0);
aTextRun->SetGlyphs(aRunStart + offs, g, nsnull);
}
}
return NS_OK;
}
// for language tag validation - include list of tags from the IANA registry
#include "gfxLanguageTagList.cpp"
nsTHashtable<nsUint32HashKey> gfxGraphiteShaper::sLanguageTags;
/*static*/ PRUint32
gfxGraphiteShaper::GetGraphiteTagForLang(const nsCString& aLang)
{
int len = aLang.Length();
if (len < 2) {
return 0;
}
// convert primary language subtag to a left-packed, NUL-padded integer
// for the Graphite API
PRUint32 grLang = 0;
for (int i = 0; i < 4; ++i) {
grLang <<= 8;
if (i < len) {
PRUint8 ch = aLang[i];
if (ch == '-') {
// found end of primary language subtag, truncate here
len = i;
continue;
}
if (ch < 'a' || ch > 'z') {
// invalid character in tag, so ignore it completely
return 0;
}
grLang += ch;
}
}
// valid tags must have length = 2 or 3
if (len < 2 || len > 3) {
return 0;
}
if (!sLanguageTags.IsInitialized()) {
// store the registered IANA tags in a hash for convenient validation
sLanguageTags.Init(ArrayLength(sLanguageTagList));
for (const PRUint32 *tag = sLanguageTagList; *tag != 0; ++tag) {
sLanguageTags.PutEntry(*tag);
}
}
// only accept tags known in the IANA registry
if (sLanguageTags.GetEntry(grLang)) {
return grLang;
}
return 0;
}
/*static*/ void
gfxGraphiteShaper::Shutdown()
{
#ifdef NS_FREE_PERMANENT_DATA
if (sLanguageTags.IsInitialized()) {
sLanguageTags.Clear();
}
#endif
}

View File

@ -0,0 +1,100 @@
/* -*- 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 Graphite integration code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Jonathan Kew <jfkthame@gmail.com>
*
* 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_GRAPHITESHAPER_H
#define GFX_GRAPHITESHAPER_H
#include "gfxTypes.h"
#include "gfxFont.h"
#include "nsDataHashtable.h"
#include "nsHashKeys.h"
class gr_face;
class gr_font;
class gr_segment;
class gfxGraphiteShaper : public gfxFontShaper {
public:
gfxGraphiteShaper(gfxFont *aFont);
virtual ~gfxGraphiteShaper();
virtual bool InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength,
PRInt32 aRunScript);
const void* GetTable(PRUint32 aTag, size_t *aLength);
static void Shutdown();
struct CallbackData {
gfxFont *mFont;
gfxGraphiteShaper *mShaper;
gfxContext *mContext;
};
struct TableRec {
hb_blob_t *mBlob;
const void *mData;
PRUint32 mLength;
};
protected:
nsresult SetGlyphsFromSegment(gfxTextRun *aTextRun,
PRUint32 aRunStart,
PRUint32 aRunLength,
gr_segment *aSegment);
gr_face *mGrFace;
gr_font *mGrFont;
CallbackData mCallbackData;
nsDataHashtable<nsUint32HashKey,TableRec> mTables;
// Whether the font implements GetGlyphWidth, or we should read tables
// directly to get ideal widths
bool mUseFontGlyphWidths;
// Convert HTML 'lang' (BCP47) to Graphite language code
static PRUint32 GetGraphiteTagForLang(const nsCString& aLang);
static nsTHashtable<nsUint32HashKey> sLanguageTags;
};
#endif /* GFX_GRAPHITESHAPER_H */

File diff suppressed because it is too large Load Diff

View File

@ -41,6 +41,9 @@
#include "gfxMacFont.h"
#include "gfxCoreTextShaper.h"
#include "gfxHarfBuzzShaper.h"
#ifdef MOZ_GRAPHITE
#include "gfxGraphiteShaper.h"
#endif
#include "gfxPlatformMac.h"
#include "gfxContext.h"
#include "gfxUnicodeProperties.h"
@ -133,6 +136,11 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyl
#endif
}
#ifdef MOZ_GRAPHITE
if (FontCanSupportGraphite()) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
#endif
if (FontCanSupportHarfBuzz()) {
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}

View File

@ -368,7 +368,7 @@ ATSFontEntry::GetFontTable(PRUint32 aTableTag, FallibleTArray<PRUint8>& aBuffer)
return NS_ERROR_FAILURE;
}
if (!aBuffer.AppendElements(dataLength)) {
if (!aBuffer.SetLength(dataLength)) {
return NS_ERROR_OUT_OF_MEMORY;
}
PRUint8 *dataPtr = aBuffer.Elements();

View File

@ -66,6 +66,9 @@
#include "harfbuzz/hb-unicode.h"
#include "harfbuzz/hb-ot-tag.h"
#include "gfxHarfBuzzShaper.h"
#ifdef MOZ_GRAPHITE
#include "gfxGraphiteShaper.h"
#endif
#include "gfxUnicodeProperties.h"
#include "gfxFontconfigUtils.h"
#include "gfxUserFontSet.h"
@ -229,6 +232,10 @@ protected:
{
}
#ifdef MOZ_GRAPHITE
virtual void CheckForGraphiteTables();
#endif
// One pattern is the common case and some subclasses rely on successful
// addition of the first element to the array.
nsAutoTArray<nsCountedRef<FcPattern>,1> mPatterns;
@ -280,6 +287,19 @@ gfxFcFontEntry::RealFaceName()
return gfxFontEntry::RealFaceName();
}
#ifdef MOZ_GRAPHITE
void
gfxFcFontEntry::CheckForGraphiteTables()
{
FcChar8 *capability;
mHasGraphiteTables =
!mPatterns.IsEmpty() &&
FcPatternGetString(mPatterns[0],
FC_CAPABILITY, 0, &capability) == FcResultMatch &&
FcStrStr(capability, gfxFontconfigUtils::ToFcChar8("ttable:Silf"));
}
#endif
bool
gfxFcFontEntry::ShouldUseHarfBuzz(PRInt32 aRunScript) {
if (mSkipHarfBuzz ||
@ -2192,6 +2212,21 @@ gfxFcFont::InitTextRun(gfxContext *aContext,
{
gfxFcFontEntry *fontEntry = static_cast<gfxFcFontEntry*>(GetFontEntry());
#ifdef MOZ_GRAPHITE
if (FontCanSupportGraphite()) {
if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
if (!mGraphiteShaper) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
if (mGraphiteShaper->InitTextRun(aContext, aTextRun, aString,
aRunStart, aRunLength,
aRunScript)) {
return true;
}
}
}
#endif
if (fontEntry->ShouldUseHarfBuzz(aRunScript)) {
if (!mHarfBuzzShaper) {
gfxFT2LockedFace face(this);

View File

@ -66,6 +66,9 @@
#include "gfxUserFontSet.h"
#include "gfxUnicodeProperties.h"
#include "harfbuzz/hb-unicode.h"
#ifdef MOZ_GRAPHITE
#include "gfxGraphiteShaper.h"
#endif
#include "nsUnicodeRange.h"
#include "nsServiceManagerUtils.h"
@ -150,6 +153,10 @@ SRGBOverrideObserver::Observe(nsISupports *aSubject,
#define GFX_PREF_HARFBUZZ_SCRIPTS "gfx.font_rendering.harfbuzz.scripts"
#define HARFBUZZ_SCRIPTS_DEFAULT gfxUnicodeProperties::SHAPING_DEFAULT
#ifdef MOZ_GRAPHITE
#define GFX_PREF_GRAPHITE_SHAPING "gfx.font_rendering.graphite.enabled"
#endif
static const char* kObservedPrefs[] = {
"gfx.downloadable_fonts.",
"gfx.font_rendering.",
@ -225,6 +232,9 @@ gfxPlatform::gfxPlatform()
mUseHarfBuzzScripts = UNINITIALIZED_VALUE;
mAllowDownloadableFonts = UNINITIALIZED_VALUE;
mDownloadableFontsSanitize = UNINITIALIZED_VALUE;
#ifdef MOZ_GRAPHITE
mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
#endif
}
gfxPlatform*
@ -337,6 +347,9 @@ gfxPlatform::Shutdown()
gfxTextRunWordCache::Shutdown();
gfxFontCache::Shutdown();
gfxFontGroup::Shutdown();
#ifdef MOZ_GRAPHITE
gfxGraphiteShaper::Shutdown();
#endif
#if defined(XP_MACOSX) || defined(XP_WIN) // temporary, until this is implemented on others
gfxPlatformFontList::Shutdown();
#endif
@ -590,6 +603,19 @@ gfxPlatform::SanitizeDownloadedFonts()
return mDownloadableFontsSanitize;
}
#ifdef MOZ_GRAPHITE
bool
gfxPlatform::UseGraphiteShaping()
{
if (mGraphiteShapingEnabled == UNINITIALIZED_VALUE) {
mGraphiteShapingEnabled =
Preferences::GetBool(GFX_PREF_GRAPHITE_SHAPING, false);
}
return mGraphiteShapingEnabled;
}
#endif
bool
gfxPlatform::UseHarfBuzzForScript(PRInt32 aScriptCode)
{
@ -1329,6 +1355,12 @@ gfxPlatform::FontsPrefsChanged(const char *aPref)
mAllowDownloadableFonts = UNINITIALIZED_VALUE;
} else if (!strcmp(GFX_DOWNLOADABLE_FONTS_SANITIZE, aPref)) {
mDownloadableFontsSanitize = UNINITIALIZED_VALUE;
#ifdef MOZ_GRAPHITE
} else if (!strcmp(GFX_PREF_GRAPHITE_SHAPING, aPref)) {
mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
gfxTextRunWordCache::Flush();
gfxFontCache::GetCache()->AgeAllGenerations();
#endif
} else if (!strcmp(GFX_PREF_HARFBUZZ_SCRIPTS, aPref)) {
mUseHarfBuzzScripts = UNINITIALIZED_VALUE;
gfxTextRunWordCache::Flush();

View File

@ -305,6 +305,14 @@ public:
*/
bool SanitizeDownloadedFonts();
#ifdef MOZ_GRAPHITE
/**
* Whether to use the SIL Graphite rendering engine
* (for fonts that include Graphite tables)
*/
bool UseGraphiteShaping();
#endif
/**
* Whether to use the harfbuzz shaper (depending on script complexity).
*
@ -423,6 +431,9 @@ protected:
PRInt8 mAllowDownloadableFonts;
PRInt8 mDownloadableFontsSanitize;
#ifdef MOZ_GRAPHITE
PRInt8 mGraphiteShapingEnabled;
#endif
// which scripts should be shaped with harfbuzz
PRInt32 mUseHarfBuzzScripts;

View File

@ -200,6 +200,10 @@ pref("gfx.downloadable_fonts.enabled", true);
pref("gfx.downloadable_fonts.fallback_delay", 3000);
pref("gfx.downloadable_fonts.sanitize", true);
#ifdef MOZ_GRAPHITE
pref("gfx.font_rendering.graphite.enabled", false);
#endif
// see gfx/thebes/gfxUnicodeProperties.h for definitions of script bits
#ifdef XP_MACOSX
// use harfbuzz for default (0x01) + arabic (0x02) + hebrew (0x04) + thai (0x40)