More work on the BFont special functions.

* Reworked functions like GetEscapements(), GetBoundingBoxesAsString() and GetGlyphShapes() completely
* Made the ServerFont functions uniform in their prototypes and cleaned out unnecessary arguments
* Added new UTF8 handling functions to moreUTF8.h that are now used by ServerFont
* Put the common transformations of the FT_Face into an own GetTransformedFace() to lessen code duplication

In other words, ServerFont is now cleaned and handles UTF8 pretty efficiently. Some ToDo's are still left though.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16241 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz
2006-02-05 23:36:59 +00:00
parent 2bdbc03a1d
commit db5734a452
5 changed files with 384 additions and 347 deletions
+108
View File
@@ -98,4 +98,112 @@ UTF8CountChars(const char *text, int32 numBytes)
} }
/* UTF8ToCharCode converts the input that includes potential multibyte chars
to UTF-32 char codes that can be used by FreeType. The string pointer is
then advanced to the next character in the string. In case the terminating
0 is reached, the string pointer is not advanced anymore and spaces are
returned. This makes it safe to overruns and enables streamed processing
of UTF8 strings. */
static inline uint32
UTF8ToCharCode(const char **bytes)
{
register uint32 result = 0;
if ((*bytes)[0] & 0x80) {
if ((*bytes)[0] & 0x40) {
if ((*bytes)[0] & 0x20) {
if ((*bytes)[0] & 0x10) {
if ((*bytes)[0] & 0x08) {
/* A five byte char?!
Something's wrong, substitue. */
result += 0x20;
(*bytes)++;
return result;
}
/* A four byte char */
result += (*bytes)[0] & 0x07;
result <<= 6;
result += (*bytes)[1] & 0x3f;
result <<= 6;
result += (*bytes)[2] & 0x3f;
result <<= 6;
result += (*bytes)[3] & 0x3f;
(*bytes) += 3;
return result;
}
/* A three byte char */
result += (*bytes)[0] & 0x0f;
result <<= 6;
result += (*bytes)[1] & 0x3f;
result <<= 6;
result += (*bytes)[2] & 0x3f;
(*bytes) += 3;
return result;
}
/* A two byte char */
result += (*bytes)[0] & 0x1f;
result <<= 6;
result += (*bytes)[1] & 0x3f;
(*bytes) += 2;
return result;
}
/* This (10) is not a startbyte.
Substitute with a space. */
result += 0x20;
(*bytes)++;
return result;
}
if ((*bytes)[0] == 0) {
/* We do not advance beyond the terminating 0. Just pad any further
request with spaces. */
result += 0x20;
return result;
}
result += (*bytes)[0];
(*bytes)++;
return result;
}
/* UTF8ToLength works like strlen() but takes UTF8 encoded multibyte chars
into account. It's a quicker version of UTF8CountChars above. */
static inline int32
UTF8ToLength(const char *bytes)
{
int32 length = 0;
while (*bytes) {
length++;
if (bytes[0] & 0x80) {
if (bytes[0] & 0x40) {
if (bytes[0] & 0x20) {
if (bytes[0] & 0x10) {
bytes += 4;
continue;
}
bytes += 3;
continue;
}
bytes += 2;
continue;
}
/* Not a startbyte - skip */
}
bytes += 1;
}
return length;
}
#endif #endif
+25 -19
View File
@@ -107,33 +107,39 @@ class ServerFont {
uint16 CharMapCount() const uint16 CharMapCount() const
{ return fStyle->CharMapCount(); } { return fStyle->CharMapCount(); }
BShape** GetGlyphShapes(const char charArray[],
int32 numChars) const;
void GetHasGlyphs(const char charArray[], FT_Face GetTransformedFace(bool rotate, bool shear) const;
void PutTransformedFace(FT_Face face) const;
status_t GetGlyphShapes(const char charArray[],
int32 numChars, BShape *shapeArray[]) const;
status_t GetHasGlyphs(const char charArray[],
int32 numChars, bool hasArray[]) const; int32 numChars, bool hasArray[]) const;
void GetEdges(const char charArray[], status_t GetEdges(const char charArray[],
int32 numChars, edge_info edgeArray[]) const; int32 numChars, edge_info edgeArray[]) const;
BPoint* GetEscapements(const char charArray[], status_t GetEscapements(const char charArray[],
int32 numChars, int32 numChars, escapement_delta delta,
BPoint offsetArray[]) const; BPoint escapementArray[],
bool GetEscapements(const char charArray[], BPoint offsetArray[]) const;
int32 numChars,
int32 numBytes,
float widthArray[],
escapement_delta delta) const;
bool GetBoundingBoxesAsString(const char charArray[], status_t GetEscapements(const char charArray[],
int32 numChars, BRect rectArray[], bool string_escapement, int32 numChars, escapement_delta delta,
font_metric_mode mode, escapement_delta delta); float widthArray[]) const;
bool GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], status_t GetBoundingBoxesAsString(const char charArray[],
int32 numStrings, BRect rectArray[], int32 numChars, BRect rectArray[],
font_metric_mode mode, escapement_delta deltaArray[]); bool stringEscapement, font_metric_mode mode,
escapement_delta delta);
float StringWidth(const char* string, int32 numBytes) const; status_t GetBoundingBoxesForStrings(char *charArray[],
int32 lengthArray[], int32 numStrings,
BRect rectArray[], font_metric_mode mode,
escapement_delta deltaArray[]);
float StringWidth(const char *string, int32 numChars) const;
bool Lock() const { return fStyle->Lock(); } bool Lock() const { return fStyle->Lock(); }
void Unlock() const { fStyle->Unlock(); } void Unlock() const { fStyle->Unlock(); }
+10 -16
View File
@@ -1048,10 +1048,9 @@ BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *
link.Attach<float>(delta ? delta->nonspace : 0.0); link.Attach<float>(delta ? delta->nonspace : 0.0);
link.Attach<float>(delta ? delta->space : 0.0); link.Attach<float>(delta ? delta->space : 0.0);
// TODO: Should we not worry about the port capacity here?!?
link.Attach<int32>(numChars); link.Attach<int32>(numChars);
// TODO: Should we not worry about the port capacity here?!?
uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
link.Attach<int32>(bytesInBuffer); link.Attach<int32>(bytesInBuffer);
link.Attach(charArray, bytesInBuffer); link.Attach(charArray, bytesInBuffer);
@@ -1088,22 +1087,15 @@ BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *
link.Attach<float>(fRotation); link.Attach<float>(fRotation);
link.Attach<uint32>(fFlags); link.Attach<uint32>(fFlags);
link.Attach<float>(delta ? delta->nonspace : 0.0);
link.Attach<float>(delta ? delta->space : 0.0);
link.Attach<bool>(offsetArray != NULL);
link.Attach<int32>(numChars); link.Attach<int32>(numChars);
// TODO: Support UTF8 characters // TODO: Should we not worry about the port capacity here?!?
if (offsetArray) { uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars);
for (int32 i = 0; i < numChars; i++) { link.Attach<int32>(bytesInBuffer);
link.Attach<char>(charArray[i]); link.Attach(charArray, bytesInBuffer);
link.Attach<BPoint>(offsetArray[i]);
}
} else {
BPoint dummypt(0, 0);
for (int32 i = 0; i < numChars; i++) {
link.Attach<char>(charArray[i]);
link.Attach<BPoint>(dummypt);
}
}
int32 code; int32 code;
if (link.FlushWithReply(code) != B_OK if (link.FlushWithReply(code) != B_OK
@@ -1111,6 +1103,8 @@ BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta *
return; return;
link.Read(escapementArray, sizeof(BPoint) * numChars); link.Read(escapementArray, sizeof(BPoint) * numChars);
if (offsetArray)
link.Read(offsetArray, sizeof(BPoint) * numChars);
} }
+61 -31
View File
@@ -1558,8 +1558,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
link.Read<int32>(&numChars); link.Read<int32>(&numChars);
link.Read<int32>(&numBytes); link.Read<int32>(&numBytes);
char charArray[numBytes]; char *charArray = new char[numBytes];
link.Read(&charArray, numBytes); link.Read(charArray, numBytes);
ServerFont font; ServerFont font;
status_t status = font.SetFamilyAndStyle(familyID, styleID); status_t status = font.SetFamilyAndStyle(familyID, styleID);
@@ -1569,20 +1569,22 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
font.SetRotation(rotation); font.SetRotation(rotation);
font.SetFlags(flags); font.SetFlags(flags);
BShape **shapes = font.GetGlyphShapes(charArray, numChars); BShape **shapes = new BShape *[numChars];
if (shapes) { status = font.GetGlyphShapes(charArray, numChars, shapes);
if (status == B_OK) {
fLink.StartMessage(B_OK); fLink.StartMessage(B_OK);
for (int32 i = 0; i < numChars; i++) { for (int32 i = 0; i < numChars; i++) {
fLink.AttachShape(*shapes[i]); fLink.AttachShape(*shapes[i]);
delete shapes[i]; delete shapes[i];
} }
delete shapes; delete[] shapes;
} else } else
fLink.StartMessage(B_ERROR); fLink.StartMessage(status);
} else } else
fLink.StartMessage(status); fLink.StartMessage(status);
delete[] charArray;
fLink.Flush(); fLink.Flush();
break; break;
} }
@@ -1610,14 +1612,16 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
status_t status = font.SetFamilyAndStyle(familyID, styleID); status_t status = font.SetFamilyAndStyle(familyID, styleID);
if (status == B_OK) { if (status == B_OK) {
bool hasArray[numChars]; bool hasArray[numChars];
font.GetHasGlyphs(charArray, numChars, hasArray); status = font.GetHasGlyphs(charArray, numChars, hasArray);
fLink.StartMessage(B_OK); if (status == B_OK) {
fLink.Attach(hasArray, sizeof(hasArray)); fLink.StartMessage(B_OK);
fLink.Attach(hasArray, sizeof(hasArray));
} else
fLink.StartMessage(status);
} else } else
fLink.StartMessage(status); fLink.StartMessage(status);
delete[] charArray; delete[] charArray;
fLink.Flush(); fLink.Flush();
break; break;
} }
@@ -1647,14 +1651,16 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
status_t status = font.SetFamilyAndStyle(familyID, styleID); status_t status = font.SetFamilyAndStyle(familyID, styleID);
if (status == B_OK) { if (status == B_OK) {
edge_info edgeArray[numChars]; edge_info edgeArray[numChars];
font.GetEdges(charArray, numChars, edgeArray); status = font.GetEdges(charArray, numChars, edgeArray);
fLink.StartMessage(B_OK); if (status == B_OK) {
fLink.Attach(edgeArray, sizeof(edgeArray)); fLink.StartMessage(B_OK);
fLink.Attach(edgeArray, sizeof(edgeArray));
} else
fLink.StartMessage(status);
} else } else
fLink.StartMessage(status); fLink.StartMessage(status);
delete[] charArray; delete[] charArray;
fLink.Flush(); fLink.Flush();
break; break;
} }
@@ -1685,15 +1691,20 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
link.Read<float>(&rotation); link.Read<float>(&rotation);
link.Read<uint32>(&flags); link.Read<uint32>(&flags);
escapement_delta delta;
link.Read<float>(&delta.nonspace);
link.Read<float>(&delta.space);
bool wantsOffsets;
link.Read<bool>(&wantsOffsets);
int32 numChars; int32 numChars;
link.Read<int32>(&numChars); link.Read<int32>(&numChars);
char charArray[numChars]; uint32 numBytes;
BPoint offsetArray[numChars]; link.Read<uint32>(&numBytes);
for (int32 i = 0; i < numChars; i++) { char *charArray = new char[numBytes];
link.Read<char>(&charArray[i]); link.Read(charArray, numBytes);
link.Read<BPoint>(&offsetArray[i]);
}
ServerFont font; ServerFont font;
status_t status = font.SetFamilyAndStyle(familyID, styleID); status_t status = font.SetFamilyAndStyle(familyID, styleID);
@@ -1702,19 +1713,33 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
font.SetRotation(rotation); font.SetRotation(rotation);
font.SetFlags(flags); font.SetFlags(flags);
BPoint *escapements = font.GetEscapements(charArray, numChars, offsetArray); BPoint *escapements = new BPoint[numChars];
if (escapements) { BPoint *offsets = NULL;
if (wantsOffsets)
offsets = new BPoint[numChars];
status = font.GetEscapements(charArray, numChars, delta,
escapements, offsets);
if (status == B_OK) {
fLink.StartMessage(B_OK); fLink.StartMessage(B_OK);
for (int32 i = 0; i < numChars; i++) { for (int32 i = 0; i < numChars; i++)
fLink.Attach<BPoint>(escapements[i]); fLink.Attach<BPoint>(escapements[i]);
}
delete[] escapements; delete[] escapements;
if (wantsOffsets) {
for (int32 i = 0; i < numChars; i++)
fLink.Attach<BPoint>(offsets[i]);
delete[] offsets;
}
} else } else
fLink.StartMessage(B_ERROR); fLink.StartMessage(status);
} else } else
fLink.StartMessage(status); fLink.StartMessage(status);
delete[] charArray;
fLink.Flush(); fLink.Flush();
break; break;
} }
@@ -1771,11 +1796,13 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
font.SetRotation(rotation); font.SetRotation(rotation);
font.SetFlags(flags); font.SetFlags(flags);
if (font.GetEscapements(charArray, numChars, numBytes, escapements, delta)) { status = font.GetEscapements(charArray, numChars, delta,
escapements);
if (status == B_OK) {
fLink.StartMessage(B_OK); fLink.StartMessage(B_OK);
fLink.Attach(escapements, numChars * sizeof(float)); fLink.Attach(escapements, numChars * sizeof(float));
} else }
status = B_ERROR;
} }
delete[] charArray; delete[] charArray;
@@ -1837,7 +1864,7 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
uint32 numBytes; uint32 numBytes;
link.Read<uint32>(&numBytes); link.Read<uint32>(&numBytes);
char charArray[numBytes]; char *charArray = new char[numBytes];
link.Read(charArray, numBytes); link.Read(charArray, numBytes);
BRect rectArray[numChars]; BRect rectArray[numChars];
@@ -1853,7 +1880,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
font.SetFlags(flags); font.SetFlags(flags);
// TODO implement for real // TODO implement for real
if (font.GetBoundingBoxesAsString(charArray, numChars, rectArray, string_escapement, mode, delta)) { if (font.GetBoundingBoxesAsString(charArray, numChars,
rectArray, string_escapement, mode, delta) == B_OK) {
fLink.StartMessage(B_OK); fLink.StartMessage(B_OK);
fLink.Attach(rectArray, sizeof(rectArray)); fLink.Attach(rectArray, sizeof(rectArray));
success = true; success = true;
@@ -1863,6 +1891,7 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
if (!success) if (!success)
fLink.StartMessage(B_ERROR); fLink.StartMessage(B_ERROR);
delete[] charArray;
fLink.Flush(); fLink.Flush();
break; break;
} }
@@ -1927,7 +1956,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link)
font.SetFlags(flags); font.SetFlags(flags);
// TODO implement for real // TODO implement for real
if (font.GetBoundingBoxesForStrings(stringArray, lengthArray, numStrings, rectArray, mode, deltaArray)) { if (font.GetBoundingBoxesForStrings(stringArray, lengthArray,
numStrings, rectArray, mode, deltaArray) == B_OK) {
fLink.StartMessage(B_OK); fLink.StartMessage(B_OK);
fLink.Attach(rectArray, sizeof(rectArray)); fLink.Attach(rectArray, sizeof(rectArray));
success = true; success = true;
+180 -281
View File
@@ -1,10 +1,11 @@
/* /*
* Copyright 2001-2005, Haiku. * Copyright 2001-2006, Haiku.
* Distributed under the terms of the MIT License. * Distributed under the terms of the MIT License.
* *
* Authors: * Authors:
* DarkWyrm <bpmagic@columbus.rr.com> * DarkWyrm <bpmagic@columbus.rr.com>
* Jérôme Duval, jerome.duval@free.fr * Jérôme Duval, jerome.duval@free.fr
* Michael Lotz <mmlr@mlotz.ch>
*/ */
@@ -312,18 +313,65 @@ ServerFont::SetFace(uint32 face)
\return the combination of family and style ID numbers \return the combination of family and style ID numbers
*/ */
uint32 uint32
ServerFont::GetFamilyAndStyle(void) const ServerFont::GetFamilyAndStyle() const
{ {
return (FamilyID() << 16) | StyleID(); return (FamilyID() << 16) | StyleID();
} }
BShape ** FT_Face
ServerFont::GetGlyphShapes(const char charArray[], int32 numChars) const ServerFont::GetTransformedFace(bool rotate, bool shear) const
{ {
if (!charArray || numChars <= 0) FaceGetter getter(fStyle);
FT_Face face = getter.Face();
if (!face)
return NULL; return NULL;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
if ((rotate && fRotation != 0) || (shear && fShear != 90)) {
FT_Matrix rmatrix, smatrix;
Angle rotationAngle(fRotation);
rmatrix.xx = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
rmatrix.xy = (FT_Fixed)(-rotationAngle.Sine() * 0x10000);
rmatrix.yx = (FT_Fixed)( rotationAngle.Sine() * 0x10000);
rmatrix.yy = (FT_Fixed)( rotationAngle.Cosine() * 0x10000);
Angle shearAngle(fShear);
smatrix.xx = (FT_Fixed)(0x10000);
smatrix.xy = (FT_Fixed)(-shearAngle.Cosine() * 0x10000);
smatrix.yx = (FT_Fixed)(0);
smatrix.yy = (FT_Fixed)(0x10000);
// Multiply togheter and apply transform
FT_Matrix_Multiply(&rmatrix, &smatrix);
FT_Set_Transform(face, &smatrix, NULL);
}
return face;
}
void
ServerFont::PutTransformedFace(FT_Face face) const
{
// Reset transformation
FT_Set_Transform(face, NULL, NULL);
}
status_t
ServerFont::GetGlyphShapes(const char charArray[], int32 numChars,
BShape *shapeArray[]) const
{
if (!charArray || numChars <= 0 || !shapeArray)
return B_BAD_DATA;
FT_Face face = GetTransformedFace(true, true);
if (!face)
return B_ERROR;
FT_Outline_Funcs funcs; FT_Outline_Funcs funcs;
funcs.move_to = MoveToFunc; funcs.move_to = MoveToFunc;
funcs.line_to = LineToFunc; funcs.line_to = LineToFunc;
@@ -332,357 +380,208 @@ ServerFont::GetGlyphShapes(const char charArray[], int32 numChars) const
funcs.shift = 0; funcs.shift = 0;
funcs.delta = 0; funcs.delta = 0;
FaceGetter getter(fStyle); const char *string = charArray;
FT_Face face = getter.Face();
if (!face)
return NULL;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
Angle rotation(fRotation);
Angle shear(fShear);
// First, rotate
FT_Matrix rmatrix;
rmatrix.xx = (FT_Fixed)( rotation.Cosine()*0x10000);
rmatrix.xy = (FT_Fixed)(-rotation.Sine()*0x10000);
rmatrix.yx = (FT_Fixed)( rotation.Sine()*0x10000);
rmatrix.yy = (FT_Fixed)( rotation.Cosine()*0x10000);
// Next, shear
FT_Matrix smatrix;
smatrix.xx = (FT_Fixed)(0x10000);
smatrix.xy = (FT_Fixed)(-shear.Cosine()*0x10000);
smatrix.yx = (FT_Fixed)(0);
smatrix.yy = (FT_Fixed)(0x10000);
// Multiply togheter
FT_Matrix_Multiply(&rmatrix, &smatrix);
FT_Set_Transform(face, &smatrix, NULL);
BShape **shapes = new BShape *[numChars];
for (int i = 0; i < numChars; i++) { for (int i = 0; i < numChars; i++) {
shapes[i] = new BShape(); shapeArray[i] = new BShape();
shapes[i]->Clear(); FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
// TODO : this is wrong (the nth char isn't charArray[i])
FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP);
FT_Outline outline = face->glyph->outline; FT_Outline outline = face->glyph->outline;
FT_Outline_Decompose(&outline, &funcs, shapes[i]); FT_Outline_Decompose(&outline, &funcs, shapeArray[i]);
shapes[i]->Close(); shapeArray[i]->Close();
} }
// Reset transformation PutTransformedFace(face);
FT_Set_Transform(face, NULL, NULL); return B_OK;
return shapes;
} }
void status_t
ServerFont::GetHasGlyphs(const char charArray[], int32 numChars, bool hasArray[]) const ServerFont::GetHasGlyphs(const char charArray[], int32 numChars,
bool hasArray[]) const
{ {
if (!charArray || numChars <= 0 || !hasArray) if (!charArray || numChars <= 0 || !hasArray)
return; return B_BAD_DATA;
FaceGetter getter(fStyle); FT_Face face = GetTransformedFace(false, false);
FT_Face face = getter.Face();
if (!face) if (!face)
return; return B_ERROR;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); const char *string = charArray;
for (int i = 0; i < numChars; i++)
hasArray[i] = FT_Get_Char_Index(face, UTF8ToCharCode(&string)) > 0;
// UTF8 handling...this can probably be smarter PutTransformedFace(face);
// Here is what I do in the AGGTextRenderer to handle UTF8... return B_OK;
// It is probably highly inefficient, so it should be reviewed.
int32 numBytes = UTF8CountBytes(charArray, numChars);
int32 convertedLength = numBytes * 2;
char* convertedBuffer = new char[convertedLength];
int32 state = 0;
status_t ret;
if ((ret = convert_from_utf8(B_UNICODE_CONVERSION,
charArray, &numBytes,
convertedBuffer, &convertedLength,
&state, B_SUBSTITUTE)) >= B_OK
&& (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength,
B_SWAP_BENDIAN_TO_HOST)) >= B_OK) {
uint16* glyphIndex = (uint16*)convertedBuffer;
// just to be sure
numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16));
for (int i = 0; i < numChars; i++) {
hasArray[i] = FT_Get_Char_Index(face, glyphIndex[i]) > 0;
}
}
delete[] convertedBuffer;
} }
void status_t
ServerFont::GetEdges(const char charArray[], int32 numChars, edge_info edgeArray[]) const ServerFont::GetEdges(const char charArray[], int32 numChars,
edge_info edgeArray[]) const
{ {
if (!charArray || numChars <= 0 || !edgeArray) if (!charArray || numChars <= 0 || !edgeArray)
return; return B_BAD_DATA;
FaceGetter getter(fStyle); FT_Face face = GetTransformedFace(false, false);
FT_Face face = getter.Face();
if (!face) if (!face)
return; return B_ERROR;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); const char *string = charArray;
// UTF8 handling...this can probably be smarter
// Here is what I do in the AGGTextRenderer to handle UTF8...
// It is probably highly inefficient, so it should be reviewed.
int32 numBytes = UTF8CountBytes(charArray, numChars);
int32 convertedLength = numBytes * 2;
char* convertedBuffer = new char[convertedLength];
int32 state = 0;
status_t ret;
if ((ret = convert_from_utf8(B_UNICODE_CONVERSION,
charArray, &numBytes,
convertedBuffer, &convertedLength,
&state, B_SUBSTITUTE)) >= B_OK
&& (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength,
B_SWAP_BENDIAN_TO_HOST)) >= B_OK) {
uint16* glyphIndex = (uint16*)convertedBuffer;
// just to be sure
numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16));
for (int i = 0; i < numChars; i++) {
FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP);
if (face->glyph) {
edgeArray[i].left = float(face->glyph->metrics.horiBearingX /64) / fSize;
edgeArray[i].right = float((face->glyph->metrics.horiBearingX
+ face->glyph->metrics.width - face->glyph->metrics.horiAdvance)/64) /fSize;
}
}
}
delete[] convertedBuffer;
}
BPoint*
ServerFont::GetEscapements(const char charArray[], int32 numChars,
BPoint offsetArray[]) const
{
if (!charArray || numChars <= 0 || !offsetArray)
return NULL;
FaceGetter getter(fStyle);
FT_Face face = getter.Face();
if (!face)
return NULL;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
Angle rotation(fRotation);
Angle shear(fShear);
// First, rotate
FT_Matrix rmatrix;
rmatrix.xx = (FT_Fixed)( rotation.Cosine()*0x10000);
rmatrix.xy = (FT_Fixed)(-rotation.Sine()*0x10000);
rmatrix.yx = (FT_Fixed)( rotation.Sine()*0x10000);
rmatrix.yy = (FT_Fixed)( rotation.Cosine()*0x10000);
// Next, shear
FT_Matrix smatrix;
smatrix.xx = (FT_Fixed)(0x10000);
smatrix.xy = (FT_Fixed)(-shear.Cosine()*0x10000);
smatrix.yx = (FT_Fixed)(0);
smatrix.yy = (FT_Fixed)(0x10000);
// Multiply togheter
FT_Matrix_Multiply(&rmatrix, &smatrix);
FT_Set_Transform(face, &smatrix, NULL);
// TODO: handle UTF8... see below!!
BPoint *escapements = new BPoint[numChars];
for (int i = 0; i < numChars; i++) { for (int i = 0; i < numChars; i++) {
// TODO : this is wrong (the nth char isn't charArray[i]) FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP); edgeArray[i].left = float(face->glyph->metrics.horiBearingX)
escapements[i].x = float(face->glyph->advance.x) / 64 / fSize; / 64 / fSize;
escapements[i].y = -float(face->glyph->advance.y) / 64 / fSize; edgeArray[i].right = float(face->glyph->metrics.horiBearingX
escapements[i] += offsetArray[i]; + face->glyph->metrics.width - face->glyph->metrics.horiAdvance)
/ 64 / fSize;
} }
// Reset transformation PutTransformedFace(face);
FT_Set_Transform(face, NULL, NULL); return B_OK;
return escapements;
} }
bool status_t
ServerFont::GetEscapements(const char charArray[], int32 numChars, int32 numBytes, ServerFont::GetEscapements(const char charArray[], int32 numChars,
float widthArray[], escapement_delta delta) const escapement_delta delta, BPoint escapementArray[], BPoint offsetArray[]) const
{ {
if (!charArray || numChars <= 0) if (!charArray || numChars <= 0 || !escapementArray)
return false; return B_BAD_DATA;
FaceGetter getter(fStyle); FT_Face face = GetTransformedFace(true, false);
FT_Face face = getter.Face();
if (!face) if (!face)
return false; return B_ERROR;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); const char *string = charArray;
for (int i = 0; i < numChars; i++) {
// Do this first as UTF8ToCharCode advances string.
escapementArray[i].x = is_white_space(*string) ? delta.space : delta.nonspace;
// UTF8 handling...this can probably be smarter FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
// Here is what I do in the AGGTextRenderer to handle UTF8... escapementArray[i].x += float(face->glyph->advance.x) / 64;
// It is probably highly inefficient, so it should be reviewed. escapementArray[i].y = -float(face->glyph->advance.y) / 64;
int32 convertedLength = numBytes * 2; escapementArray[i].x /= fSize;
char* convertedBuffer = new char[convertedLength]; escapementArray[i].y /= fSize;
int32 state = 0; if (offsetArray) {
status_t ret; // ToDo: According to the BeBook: "The offsetArray is applied by
if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, // the dynamic spacing in order to improve the relative position
charArray, &numBytes, // of the character's width with relation to another character,
convertedBuffer, &convertedLength, // without altering the width." So this will probably depend on
&state, B_SUBSTITUTE)) >= B_OK // the spacing mode.
&& (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength, offsetArray[i].x = 0;
B_SWAP_BENDIAN_TO_HOST)) >= B_OK) { offsetArray[i].y = 0;
uint16* glyphIndex = (uint16*)convertedBuffer;
// just to be sure
numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16));
for (int i = 0; i < numChars; i++) {
FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP);
if (face->glyph) {
widthArray[i] = ((float)face->glyph->metrics.horiAdvance / 64.0) / fSize;
widthArray[i] += is_white_space(glyphIndex[i]) ? delta.space : delta.nonspace;
}
} }
} }
delete[] convertedBuffer;
return ret >= B_OK; PutTransformedFace(face);
return B_OK;
} }
bool status_t
ServerFont::GetEscapements(const char charArray[], int32 numChars,
escapement_delta delta, float widthArray[]) const
{
if (!charArray || numChars <= 0 || !widthArray)
return B_BAD_DATA;
FT_Face face = GetTransformedFace(false, false);
if (!face)
return B_ERROR;
const char *string = charArray;
for (int i = 0; i < numChars; i++) {
// Do this first as UTF8ToCharCode advances string.
widthArray[i] = is_white_space(*string) ? delta.space : delta.nonspace;
FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
widthArray[i] += float(face->glyph->metrics.horiAdvance) / 64.0;
widthArray[i] /= fSize;
}
PutTransformedFace(face);
return B_OK;
}
status_t
ServerFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars, ServerFont::GetBoundingBoxesAsString(const char charArray[], int32 numChars,
BRect rectArray[], bool string_escapement, font_metric_mode mode, BRect rectArray[], bool stringEscapement, font_metric_mode mode,
escapement_delta delta) escapement_delta delta)
{ {
if (!charArray || numChars <= 0 || !rectArray) if (!charArray || numChars <= 0 || !rectArray)
return false; return B_BAD_DATA;
FaceGetter getter(fStyle); FT_Face face = GetTransformedFace(true, true);
FT_Face face = getter.Face();
if (!face) if (!face)
return false; return B_ERROR;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); const char *string = charArray;
for (int i = 0; i < numChars; i++) {
if (stringEscapement) {
if (i > 0)
rectArray[i].OffsetBy(is_white_space(*string) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0);
// UTF8 handling...this can probably be smarter rectArray[i].OffsetBy(is_white_space(*string) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.0);
// Here is what I do in the AGGTextRenderer to handle UTF8...
// It is probably highly inefficient, so it should be reviewed.
int32 numBytes = UTF8CountBytes(charArray, numChars);
int32 convertedLength = numBytes * 2;
char* convertedBuffer = new char[convertedLength];
int32 state = 0;
status_t ret;
if ((ret = convert_from_utf8(B_UNICODE_CONVERSION,
charArray, &numBytes,
convertedBuffer, &convertedLength,
&state, B_SUBSTITUTE)) >= B_OK
&& (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength,
B_SWAP_BENDIAN_TO_HOST)) >= B_OK) {
uint16* glyphIndex = (uint16*)convertedBuffer;
// just to be sure
numChars = min_c((uint32)numChars, convertedLength / sizeof(uint16));
for (int i = 0; i < numChars; i++) {
if (string_escapement) {
if (i>0)
rectArray[i].OffsetBy(is_white_space(glyphIndex[i-1]) ? delta.space/2.0 : delta.nonspace/2.0, 0.0);
rectArray[i].OffsetBy(is_white_space(glyphIndex[i]) ? delta.space/2.0 : delta.nonspace/2.0, 0.0);
}
FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP);
if (face->glyph) {
if (i<numChars-1)
rectArray[i+1].left = rectArray[i+1].right = rectArray[i].left +
face->glyph->metrics.horiAdvance / 64.0;
rectArray[i].left += float(face->glyph->metrics.horiBearingX) /64.0;
rectArray[i].right += float(face->glyph->metrics.horiBearingX
+ face->glyph->metrics.width)/64.0;
rectArray[i].top = -float(face->glyph->metrics.horiBearingY) /64.0;
rectArray[i].bottom = float(face->glyph->metrics.height
- face->glyph->metrics.horiBearingY) /64.0;
}
} }
}
delete[] convertedBuffer;
return true; FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
if (i < numChars - 1) {
rectArray[i + 1].left = rectArray[i + 1].right = rectArray[i].left
+ face->glyph->metrics.horiAdvance / 64.0;
}
rectArray[i].left += float(face->glyph->metrics.horiBearingX) /64.0;
rectArray[i].right += float(face->glyph->metrics.horiBearingX
+ face->glyph->metrics.width) / 64.0;
rectArray[i].top = -float(face->glyph->metrics.horiBearingY) / 64.0;
rectArray[i].bottom = float(face->glyph->metrics.height
- face->glyph->metrics.horiBearingY) /64.0;
}
PutTransformedFace(face);
return B_OK;
} }
bool status_t
ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], ServerFont::GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[],
int32 numStrings, BRect rectArray[], font_metric_mode mode, escapement_delta deltaArray[]) int32 numStrings, BRect rectArray[], font_metric_mode mode, escapement_delta deltaArray[])
{ {
if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray)
return false; return B_BAD_DATA;
FaceGetter getter(fStyle); FT_Face face = GetTransformedFace(true, true);
FT_Face face = getter.Face();
if (!face) if (!face)
return false; return B_ERROR;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
for (int32 i = 0; i < numStrings; i++) { for (int32 i = 0; i < numStrings; i++) {
// TODO: ... // TODO: ...
} }
return true; PutTransformedFace(face);
return B_OK;
} }
float float
ServerFont::StringWidth(const char* string, int32 numBytes) const ServerFont::StringWidth(const char *_string, int32 numChars) const
{ {
if (!string || numBytes <= 0) if (!_string || numChars <= 0)
return 0.0; return 0.0;
FaceGetter getter(fStyle); FT_Face face = GetTransformedFace(false, false);
FT_Face face = getter.Face();
if (!face) if (!face)
return 0.0; return 0.0;
FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72);
int32 convertedLength = numBytes * 2;
char* convertedBuffer = new char[convertedLength];
float width = 0.0; float width = 0.0;
const char *string = _string;
int32 state = 0; for (int i = 0; i < numChars; i++) {
status_t ret; FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP);
if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, width += face->glyph->advance.x / 64.0;
string, &numBytes,
convertedBuffer, &convertedLength,
&state, B_SUBSTITUTE)) >= B_OK
&& (ret = swap_data(B_INT16_TYPE, convertedBuffer, convertedLength,
B_SWAP_BENDIAN_TO_HOST)) >= B_OK) {
uint16* glyphIndex = (uint16*)convertedBuffer;
// just to be sure
int numChars = convertedLength / sizeof(uint16);
for (int i = 0; i < numChars; i++) {
FT_Load_Char(face, glyphIndex[i], FT_LOAD_NO_BITMAP);
width += face->glyph->advance.x / 64.0;
}
} }
delete[] convertedBuffer;
PutTransformedFace(face);
return width; return width;
} }
@@ -725,7 +624,7 @@ ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
// get the escapement of each glyph in font units // get the escapement of each glyph in font units
float* escapementArray = new float[numChars]; float* escapementArray = new float[numChars];
static escapement_delta delta = (escapement_delta){ 0.0, 0.0 }; static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
GetEscapements(string, numChars, length, escapementArray, delta); GetEscapements(string, numChars, delta, escapementArray);
truncate_string(string, mode, width, result, truncate_string(string, mode, width, result,
escapementArray, fSize, ellipsisWidth, length, numChars); escapementArray, fSize, ellipsisWidth, length, numChars);