bug 502906 - part 4 - refactor DWrite font code to match Mac and GDI structure. r=bas

This commit is contained in:
Jonathan Kew 2010-04-06 21:19:39 +01:00
parent 76b1730341
commit b81c24c7fb
6 changed files with 330 additions and 314 deletions

View File

@ -67,9 +67,9 @@ public:
gfxFloat GetAdjustedSize() const { return mAdjustedSize; }
protected:
friend class gfxDWriteFontGroup;
IDWriteFontFace *GetFontFace() { return mFontFace.get(); }
protected:
void ComputeMetrics();
cairo_font_face_t *CairoFontFace();
@ -85,32 +85,4 @@ protected:
PRBool mNeedsOblique;
};
/**
* \brief Class representing a DWrite windows font group.
*/
class gfxDWriteFontGroup : public gfxFontGroup
{
public:
gfxDWriteFontGroup(const nsAString& aFamilies,
const gfxFontStyle *aStyle,
gfxUserFontSet *aUserFontSet);
virtual ~gfxDWriteFontGroup();
gfxFontGroup *Copy(const gfxFontStyle *aStyle);
/**
* Gets the font at a certain position in the font list, it will
* create this font if it hasn't actually been created yet.
*
* \param i Position in the font list
* \return Object representing the font
*/
virtual gfxDWriteFont *GetFontAt(PRInt32 i);
virtual gfxTextRun *MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
const Parameters *aParams, PRUint32 aFlags);
virtual gfxTextRun *MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
const Parameters *aParams, PRUint32 aFlags);
};
#endif

View File

@ -71,6 +71,7 @@ OS_LIBS += $(call EXPAND_LIBNAME,ddraw)
else
ifdef MOZ_ENABLE_DWRITE_FONT
CPPSRCS += gfxDWriteFonts.cpp \
gfxDWriteShaper.cpp \
gfxDWriteTextAnalysis.cpp \
gfxDWriteCommon.cpp \
gfxD2DSurface.cpp \

View File

@ -36,6 +36,7 @@
* ***** END LICENSE BLOCK ***** */
#include "gfxDWriteFonts.h"
#include "gfxDWriteShaper.h"
#include "gfxDWriteFontList.h"
#include "gfxContext.h"
#include <dwrite.h>
@ -80,6 +81,8 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
}
ComputeMetrics();
mShaper = new gfxDWriteShaper(this);
}
gfxDWriteFont::~gfxDWriteFont()
@ -278,278 +281,3 @@ gfxDWriteFont::CairoScaledFont()
return mCairoScaledFont;
}
////////////////////////////////////////////////////////////////////////////////
// gfxDWriteFontGroup
gfxDWriteFontGroup::gfxDWriteFontGroup(const nsAString& aFamilies,
const gfxFontStyle *aStyle,
gfxUserFontSet *aUserFontSet)
: gfxFontGroup(aFamilies, aStyle, aUserFontSet)
{
}
gfxDWriteFontGroup::~gfxDWriteFontGroup()
{
}
gfxDWriteFont *
gfxDWriteFontGroup::GetFontAt(PRInt32 i)
{
// If it turns out to be hard for all clients that cache font
// groups to call UpdateFontList at appropriate times, we could
// instead consider just calling UpdateFontList from someplace
// more central (such as here).
NS_ASSERTION(!mUserFontSet || mCurrGeneration == GetGeneration(),
"Whoever was caching this font group should have "
"called UpdateFontList on it");
NS_ASSERTION(mFonts.Length() > PRUint32(i),
"Requesting a font index that doesn't exist");
return static_cast<gfxDWriteFont*>(mFonts[i].get());
}
gfxFontGroup *
gfxDWriteFontGroup::Copy(const gfxFontStyle *aStyle)
{
return new gfxDWriteFontGroup(mFamilies, aStyle, mUserFontSet);
}
#define MAX_RANGE_LENGTH 25000
gfxTextRun *
gfxDWriteFontGroup::MakeTextRun(const PRUnichar *aString,
PRUint32 aLength,
const Parameters *aParams,
PRUint32 aFlags)
{
HRESULT hr;
// TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
gfxTextRun *textRun = gfxTextRun::Create(aParams,
aString,
aLength,
this,
aFlags);
gfxPlatform::GetPlatform()->SetupClusterBoundaries(textRun, aString);
DWRITE_READING_DIRECTION readingDirection =
DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
if (textRun->IsRightToLeft()) {
readingDirection = DWRITE_READING_DIRECTION_RIGHT_TO_LEFT;
}
nsRefPtr<gfxDWriteFont> font = GetFontAt(0);
textRun->AddGlyphRun(font, 0);
gfxTextRun::CompressedGlyph g;
nsRefPtr<IDWriteTextAnalyzer> analyzer;
hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
CreateTextAnalyzer(getter_AddRefs(analyzer));
nsTArray<gfxTextRange> ranges;
ComputeRanges(ranges, aString, 0, aLength);
/**
* There's an internal 16-bit limit on some things inside the analyzer.
* to be on the safe side here we split up any ranges over MAX_RANGE_LENGTH
* characters.
* TODO: Figure out what exactly is going on, and what is a safe number, and
* why.
* TODO: Figure out good places to split this.
*/
for (unsigned int i = 0; i < ranges.Length(); i++) {
if (ranges[i].Length() > MAX_RANGE_LENGTH) {
ranges.InsertElementAt(i + 1,
gfxTextRange(ranges[i].start
+ MAX_RANGE_LENGTH,
ranges[i].end));
ranges[i + 1].font = ranges[i].font;
ranges[i].end = ranges[i].start + MAX_RANGE_LENGTH;
}
}
UINT32 rangeOffset = 0;
for (unsigned int i = 0; i < ranges.Length(); i++) {
gfxTextRange &range = ranges[i];
TextAnalysis analysis(
aString + range.start, range.Length(),
NULL,
readingDirection);
TextAnalysis::Run *runHead;
DWRITE_LINE_BREAKPOINT *linebreaks;
hr = analysis.GenerateResults(analyzer, &runHead, &linebreaks);
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to generate results.");
continue;
}
if (range.font) {
font = static_cast<gfxDWriteFont*>(range.font.get());
} else {
// XXX missing glyph info needs to be set
font = GetFontAt(0);
}
textRun->AddGlyphRun(font, range.start);
PRUint32 appUnitsPerDevPixel = textRun->GetAppUnitsPerDevUnit();
UINT32 maxGlyphs = 0;
trymoreglyphs:
if ((PR_UINT32_MAX - 3 * range.Length() / 2 + 16) < maxGlyphs) {
// This isn't going to work, we're going to cross the UINT32 upper
// limit. Next range it is.
continue;
}
maxGlyphs += 3 * range.Length() / 2 + 16;
nsAutoTArray<UINT16, 400> clusters;
nsAutoTArray<UINT16, 400> indices;
nsAutoTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
nsAutoTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties;
if (!clusters.SetLength(range.Length()) ||
!indices.SetLength(maxGlyphs) ||
!textProperties.SetLength(maxGlyphs) ||
!glyphProperties.SetLength(maxGlyphs)) {
continue;
}
UINT32 actualGlyphs;
hr = analyzer->GetGlyphs(aString + range.start, range.Length(),
font->mFontFace, FALSE,
readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
&runHead->mScript, NULL, NULL, NULL, NULL, 0,
maxGlyphs, clusters.Elements(), textProperties.Elements(),
indices.Elements(), glyphProperties.Elements(), &actualGlyphs);
if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
// We increase the amount of glyphs and try again.
goto trymoreglyphs;
}
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to get glyphs.");
continue;
}
WORD gID = indices[0];
nsAutoTArray<FLOAT, 400> advances;
nsAutoTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
if (!advances.SetLength(actualGlyphs) ||
!glyphOffsets.SetLength(actualGlyphs)) {
continue;
}
hr = analyzer->GetGlyphPlacements(aString + range.start,
clusters.Elements(),
textProperties.Elements(),
range.Length(),
indices.Elements(),
glyphProperties.Elements(),
actualGlyphs,
font->mFontFace,
(float)font->GetAdjustedSize(),
FALSE,
FALSE,
&runHead->mScript,
NULL,
NULL,
NULL,
0,
advances.Elements(),
glyphOffsets.Elements());
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to get glyph placements.");
continue;
}
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
for (unsigned int c = 0; c < range.Length(); c++) {
PRUint32 k = clusters[c];
PRUint32 absC = range.start + c;
if (c > 0 && k == clusters[c - 1]) {
g.SetComplex(textRun->IsClusterStart(absC), PR_FALSE, 0);
textRun->SetGlyphs(absC, g, nsnull);
// This is a cluster continuation. No glyph here.
continue;
}
// Count glyphs for this character
PRUint32 glyphCount = actualGlyphs - k;
PRUint32 nextClusterOffset;
for (nextClusterOffset = c + 1;
nextClusterOffset < range.Length(); ++nextClusterOffset) {
if (clusters[nextClusterOffset] > k) {
glyphCount = clusters[nextClusterOffset] - k;
break;
}
}
PRInt32 advance =
(PRInt32)(advances[k] * aParams->mAppUnitsPerDevUnit);
if (glyphCount == 1 && advance >= 0 &&
glyphOffsets[k].advanceOffset == 0 &&
glyphOffsets[k].ascenderOffset == 0 &&
textRun->IsClusterStart(absC) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
textRun->SetSimpleGlyph(absC,
g.SetSimpleGlyph(advance,
indices[k]));
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(
glyphCount - detailedGlyphs.Length())) {
continue;
}
}
float totalAdvance = 0;
for (unsigned int z = 0; z < glyphCount; z++) {
detailedGlyphs[z].mGlyphID = indices[k + z];
detailedGlyphs[z].mAdvance =
(PRInt32)(advances[k + z]
* aParams->mAppUnitsPerDevUnit);
if (readingDirection ==
DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) {
detailedGlyphs[z].mXOffset =
(totalAdvance +
glyphOffsets[k + z].advanceOffset)
* aParams->mAppUnitsPerDevUnit;
} else {
detailedGlyphs[z].mXOffset =
glyphOffsets[k + z].advanceOffset *
aParams->mAppUnitsPerDevUnit;
}
detailedGlyphs[z].mYOffset =
-glyphOffsets[k + z].ascenderOffset *
aParams->mAppUnitsPerDevUnit;
totalAdvance += advances[k + z];
}
textRun->SetGlyphs(
absC,
g.SetComplex(textRun->IsClusterStart(absC),
PR_TRUE,
glyphCount),
detailedGlyphs.Elements());
}
}
}
return textRun;
}
gfxTextRun *
gfxDWriteFontGroup::MakeTextRun(const PRUint8 *aString,
PRUint32 aLength,
const Parameters *aParams,
PRUint32 aFlags)
{
nsCString string((const char*)aString, aLength);
return MakeTextRun(NS_ConvertASCIItoUTF16(string).get(),
aLength,
aParams,
aFlags);
}

View File

@ -0,0 +1,261 @@
/* -*- 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 Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bas Schouten <bschouten@mozilla.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 "gfxDWriteShaper.h"
#include "gfxWindowsPlatform.h"
#include <dwrite.h>
#include "gfxDWriteTextAnalysis.h"
#define MAX_RANGE_LENGTH 25000
PRBool
gfxDWriteShaper::InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength)
{
HRESULT hr;
// TODO: Handle TEST_DISABLE_OPTIONAL_LIGATURES
DWRITE_READING_DIRECTION readingDirection =
aTextRun->IsRightToLeft()
? DWRITE_READING_DIRECTION_RIGHT_TO_LEFT
: DWRITE_READING_DIRECTION_LEFT_TO_RIGHT;
gfxDWriteFont *font = static_cast<gfxDWriteFont*>(mFont);
gfxTextRun::CompressedGlyph g;
nsRefPtr<IDWriteTextAnalyzer> analyzer;
hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
CreateTextAnalyzer(getter_AddRefs(analyzer));
if (FAILED(hr)) {
return PR_FALSE;
}
/**
* There's an internal 16-bit limit on some things inside the analyzer.
* to be on the safe side here we split up any ranges over MAX_RANGE_LENGTH
* characters.
* TODO: Figure out what exactly is going on, and what is a safe number, and
* why.
* TODO: Figure out good places to split this.
*/
/*
for (unsigned int i = 0; i < ranges.Length(); i++) {
if (ranges[i].Length() > MAX_RANGE_LENGTH) {
ranges.InsertElementAt(i + 1,
gfxTextRange(ranges[i].start
+ MAX_RANGE_LENGTH,
ranges[i].end));
ranges[i + 1].font = ranges[i].font;
ranges[i].end = ranges[i].start + MAX_RANGE_LENGTH;
}
}
UINT32 rangeOffset = 0;
*/
// for (unsigned int i = 0; i < ranges.Length(); i++) {
// gfxTextRange &range = ranges[i];
PRBool result = PR_TRUE;
do {
gfxTextRange range(aRunStart, aRunStart + aRunLength);
TextAnalysis analysis(
aString + range.start, range.Length(),
NULL,
readingDirection);
TextAnalysis::Run *runHead;
DWRITE_LINE_BREAKPOINT *linebreaks;
hr = analysis.GenerateResults(analyzer, &runHead, &linebreaks);
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to generate results.");
result = PR_FALSE;
break;
}
PRUint32 appUnitsPerDevPixel = aTextRun->GetAppUnitsPerDevUnit();
UINT32 maxGlyphs = 0;
trymoreglyphs:
if ((PR_UINT32_MAX - 3 * range.Length() / 2 + 16) < maxGlyphs) {
// This isn't going to work, we're going to cross the UINT32 upper
// limit. Next range it is.
continue;
}
maxGlyphs += 3 * range.Length() / 2 + 16;
nsAutoTArray<UINT16, 400> clusters;
nsAutoTArray<UINT16, 400> indices;
nsAutoTArray<DWRITE_SHAPING_TEXT_PROPERTIES, 400> textProperties;
nsAutoTArray<DWRITE_SHAPING_GLYPH_PROPERTIES, 400> glyphProperties;
if (!clusters.SetLength(range.Length()) ||
!indices.SetLength(maxGlyphs) ||
!textProperties.SetLength(maxGlyphs) ||
!glyphProperties.SetLength(maxGlyphs)) {
continue;
}
UINT32 actualGlyphs;
hr = analyzer->GetGlyphs(aString + range.start, range.Length(),
font->GetFontFace(), FALSE,
readingDirection == DWRITE_READING_DIRECTION_RIGHT_TO_LEFT,
&runHead->mScript, NULL, NULL, NULL, NULL, 0,
maxGlyphs, clusters.Elements(), textProperties.Elements(),
indices.Elements(), glyphProperties.Elements(), &actualGlyphs);
if (hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
// We increase the amount of glyphs and try again.
goto trymoreglyphs;
}
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to get glyphs.");
result = PR_FALSE;
break;
}
WORD gID = indices[0];
nsAutoTArray<FLOAT, 400> advances;
nsAutoTArray<DWRITE_GLYPH_OFFSET, 400> glyphOffsets;
if (!advances.SetLength(actualGlyphs) ||
!glyphOffsets.SetLength(actualGlyphs)) {
continue;
}
hr = analyzer->GetGlyphPlacements(aString + range.start,
clusters.Elements(),
textProperties.Elements(),
range.Length(),
indices.Elements(),
glyphProperties.Elements(),
actualGlyphs,
font->GetFontFace(),
font->GetAdjustedSize(),
FALSE,
FALSE,
&runHead->mScript,
NULL,
NULL,
NULL,
0,
advances.Elements(),
glyphOffsets.Elements());
if (FAILED(hr)) {
NS_WARNING("Analyzer failed to get glyph placements.");
result = PR_FALSE;
break;
}
nsAutoTArray<gfxTextRun::DetailedGlyph,1> detailedGlyphs;
for (unsigned int c = 0; c < range.Length(); c++) {
PRUint32 k = clusters[c];
PRUint32 absC = range.start + c;
if (c > 0 && k == clusters[c - 1]) {
g.SetComplex(aTextRun->IsClusterStart(absC), PR_FALSE, 0);
aTextRun->SetGlyphs(absC, g, nsnull);
// This is a cluster continuation. No glyph here.
continue;
}
// Count glyphs for this character
PRUint32 glyphCount = actualGlyphs - k;
PRUint32 nextClusterOffset;
for (nextClusterOffset = c + 1;
nextClusterOffset < range.Length(); ++nextClusterOffset) {
if (clusters[nextClusterOffset] > k) {
glyphCount = clusters[nextClusterOffset] - k;
break;
}
}
PRInt32 advance = (PRInt32)(advances[k] * appUnitsPerDevPixel);
if (glyphCount == 1 && advance >= 0 &&
glyphOffsets[k].advanceOffset == 0 &&
glyphOffsets[k].ascenderOffset == 0 &&
aTextRun->IsClusterStart(absC) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(indices[k])) {
aTextRun->SetSimpleGlyph(absC,
g.SetSimpleGlyph(advance,
indices[k]));
} else {
if (detailedGlyphs.Length() < glyphCount) {
if (!detailedGlyphs.AppendElements(
glyphCount - detailedGlyphs.Length())) {
continue;
}
}
float totalAdvance = 0;
for (unsigned int z = 0; z < glyphCount; z++) {
detailedGlyphs[z].mGlyphID = indices[k + z];
detailedGlyphs[z].mAdvance =
(PRInt32)(advances[k + z]
* appUnitsPerDevPixel);
if (readingDirection ==
DWRITE_READING_DIRECTION_RIGHT_TO_LEFT) {
detailedGlyphs[z].mXOffset =
(totalAdvance +
glyphOffsets[k + z].advanceOffset)
* appUnitsPerDevPixel;
} else {
detailedGlyphs[z].mXOffset =
glyphOffsets[k + z].advanceOffset *
appUnitsPerDevPixel;
}
detailedGlyphs[z].mYOffset =
-glyphOffsets[k + z].ascenderOffset *
appUnitsPerDevPixel;
totalAdvance += advances[k + z];
}
aTextRun->SetGlyphs(
absC,
g.SetComplex(aTextRun->IsClusterStart(absC),
PR_TRUE,
glyphCount),
detailedGlyphs.Elements());
}
}
} while (0);
return result;
}

View File

@ -0,0 +1,62 @@
/* -*- 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 Foundation code.
*
* The Initial Developer of the Original Code is Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Bas Schouten <bschouten@mozilla.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_DWRITESHAPER_H
#define GFX_DWRITESHAPER_H
#include "gfxDWriteFonts.h"
/**
* \brief Class representing a DWrite font shaper.
*/
class gfxDWriteShaper : public gfxFontShaper
{
public:
gfxDWriteShaper(gfxDWriteFont *aFont)
: gfxFontShaper(aFont)
{ }
virtual ~gfxDWriteShaper() { }
virtual PRBool InitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const PRUnichar *aString,
PRUint32 aRunStart,
PRUint32 aRunLength);
};
#endif /* GFX_DWRITESHAPER_H */

View File

@ -314,15 +314,7 @@ gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies,
#ifdef MOZ_FT2_FONTS
return new gfxFT2FontGroup(aFamilies, aStyle);
#else
#ifdef CAIRO_HAS_DWRITE_FONT
if (GetDWriteFactory()) {
return new gfxDWriteFontGroup(aFamilies, aStyle, aUserFontSet);
} else {
#endif
return new gfxFontGroup(aFamilies, aStyle, aUserFontSet);
#ifdef CAIRO_HAS_DWRITE_FONT
}
#endif
return new gfxFontGroup(aFamilies, aStyle, aUserFontSet);
#endif
}