From db5734a452fa53de2ed303eea64622c5d310280e Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sun, 5 Feb 2006 23:36:59 +0000 Subject: [PATCH] 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 --- headers/private/interface/moreUTF8.h | 108 ++++++ headers/private/servers/app/ServerFont.h | 44 ++- src/kits/interface/Font.cpp | 26 +- src/servers/app/ServerApp.cpp | 92 +++-- src/servers/app/ServerFont.cpp | 461 +++++++++-------------- 5 files changed, 384 insertions(+), 347 deletions(-) diff --git a/headers/private/interface/moreUTF8.h b/headers/private/interface/moreUTF8.h index 441654627b..ae23d947dd 100644 --- a/headers/private/interface/moreUTF8.h +++ b/headers/private/interface/moreUTF8.h @@ -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 diff --git a/headers/private/servers/app/ServerFont.h b/headers/private/servers/app/ServerFont.h index 09d474fcce..55bf13bb01 100644 --- a/headers/private/servers/app/ServerFont.h +++ b/headers/private/servers/app/ServerFont.h @@ -107,33 +107,39 @@ class ServerFont { uint16 CharMapCount() const { 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; - void GetEdges(const char charArray[], + status_t GetEdges(const char charArray[], int32 numChars, edge_info edgeArray[]) const; - BPoint* GetEscapements(const char charArray[], - int32 numChars, - BPoint offsetArray[]) const; - bool GetEscapements(const char charArray[], - int32 numChars, - int32 numBytes, - float widthArray[], - escapement_delta delta) const; + status_t GetEscapements(const char charArray[], + int32 numChars, escapement_delta delta, + BPoint escapementArray[], + BPoint offsetArray[]) const; - bool GetBoundingBoxesAsString(const char charArray[], - int32 numChars, BRect rectArray[], bool string_escapement, - font_metric_mode mode, escapement_delta delta); + status_t GetEscapements(const char charArray[], + int32 numChars, escapement_delta delta, + float widthArray[]) const; - bool GetBoundingBoxesForStrings(char *charArray[], int32 lengthArray[], - int32 numStrings, BRect rectArray[], - font_metric_mode mode, escapement_delta deltaArray[]); + status_t GetBoundingBoxesAsString(const char charArray[], + int32 numChars, BRect rectArray[], + 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(); } void Unlock() const { fStyle->Unlock(); } diff --git a/src/kits/interface/Font.cpp b/src/kits/interface/Font.cpp index ccfad4bcb8..95d6a1eed9 100644 --- a/src/kits/interface/Font.cpp +++ b/src/kits/interface/Font.cpp @@ -1048,10 +1048,9 @@ BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta * link.Attach(delta ? delta->nonspace : 0.0); link.Attach(delta ? delta->space : 0.0); - - // TODO: Should we not worry about the port capacity here?!? link.Attach(numChars); + // TODO: Should we not worry about the port capacity here?!? uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); link.Attach(bytesInBuffer); link.Attach(charArray, bytesInBuffer); @@ -1088,22 +1087,15 @@ BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta * link.Attach(fRotation); link.Attach(fFlags); + link.Attach(delta ? delta->nonspace : 0.0); + link.Attach(delta ? delta->space : 0.0); + link.Attach(offsetArray != NULL); link.Attach(numChars); - // TODO: Support UTF8 characters - if (offsetArray) { - for (int32 i = 0; i < numChars; i++) { - link.Attach(charArray[i]); - link.Attach(offsetArray[i]); - } - } else { - BPoint dummypt(0, 0); - - for (int32 i = 0; i < numChars; i++) { - link.Attach(charArray[i]); - link.Attach(dummypt); - } - } + // TODO: Should we not worry about the port capacity here?!? + uint32 bytesInBuffer = UTF8CountBytes(charArray, numChars); + link.Attach(bytesInBuffer); + link.Attach(charArray, bytesInBuffer); int32 code; if (link.FlushWithReply(code) != B_OK @@ -1111,6 +1103,8 @@ BFont::GetEscapements(const char charArray[], int32 numChars, escapement_delta * return; link.Read(escapementArray, sizeof(BPoint) * numChars); + if (offsetArray) + link.Read(offsetArray, sizeof(BPoint) * numChars); } diff --git a/src/servers/app/ServerApp.cpp b/src/servers/app/ServerApp.cpp index 593cd28724..53a8739d4a 100644 --- a/src/servers/app/ServerApp.cpp +++ b/src/servers/app/ServerApp.cpp @@ -1558,8 +1558,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(&numChars); link.Read(&numBytes); - char charArray[numBytes]; - link.Read(&charArray, numBytes); + char *charArray = new char[numBytes]; + link.Read(charArray, numBytes); ServerFont font; status_t status = font.SetFamilyAndStyle(familyID, styleID); @@ -1569,20 +1569,22 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) font.SetRotation(rotation); font.SetFlags(flags); - BShape **shapes = font.GetGlyphShapes(charArray, numChars); - if (shapes) { + BShape **shapes = new BShape *[numChars]; + status = font.GetGlyphShapes(charArray, numChars, shapes); + if (status == B_OK) { fLink.StartMessage(B_OK); for (int32 i = 0; i < numChars; i++) { fLink.AttachShape(*shapes[i]); delete shapes[i]; } - delete shapes; + delete[] shapes; } else - fLink.StartMessage(B_ERROR); + fLink.StartMessage(status); } else fLink.StartMessage(status); + delete[] charArray; fLink.Flush(); break; } @@ -1610,14 +1612,16 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) status_t status = font.SetFamilyAndStyle(familyID, styleID); if (status == B_OK) { bool hasArray[numChars]; - font.GetHasGlyphs(charArray, numChars, hasArray); - fLink.StartMessage(B_OK); - fLink.Attach(hasArray, sizeof(hasArray)); + status = font.GetHasGlyphs(charArray, numChars, hasArray); + if (status == B_OK) { + fLink.StartMessage(B_OK); + fLink.Attach(hasArray, sizeof(hasArray)); + } else + fLink.StartMessage(status); } else fLink.StartMessage(status); delete[] charArray; - fLink.Flush(); break; } @@ -1647,14 +1651,16 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) status_t status = font.SetFamilyAndStyle(familyID, styleID); if (status == B_OK) { edge_info edgeArray[numChars]; - font.GetEdges(charArray, numChars, edgeArray); - fLink.StartMessage(B_OK); - fLink.Attach(edgeArray, sizeof(edgeArray)); + status = font.GetEdges(charArray, numChars, edgeArray); + if (status == B_OK) { + fLink.StartMessage(B_OK); + fLink.Attach(edgeArray, sizeof(edgeArray)); + } else + fLink.StartMessage(status); } else fLink.StartMessage(status); delete[] charArray; - fLink.Flush(); break; } @@ -1685,15 +1691,20 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) link.Read(&rotation); link.Read(&flags); + escapement_delta delta; + link.Read(&delta.nonspace); + link.Read(&delta.space); + + bool wantsOffsets; + link.Read(&wantsOffsets); + int32 numChars; link.Read(&numChars); - char charArray[numChars]; - BPoint offsetArray[numChars]; - for (int32 i = 0; i < numChars; i++) { - link.Read(&charArray[i]); - link.Read(&offsetArray[i]); - } + uint32 numBytes; + link.Read(&numBytes); + char *charArray = new char[numBytes]; + link.Read(charArray, numBytes); ServerFont font; status_t status = font.SetFamilyAndStyle(familyID, styleID); @@ -1702,19 +1713,33 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) font.SetRotation(rotation); font.SetFlags(flags); - BPoint *escapements = font.GetEscapements(charArray, numChars, offsetArray); - if (escapements) { + BPoint *escapements = new BPoint[numChars]; + 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); - for (int32 i = 0; i < numChars; i++) { + for (int32 i = 0; i < numChars; i++) fLink.Attach(escapements[i]); - } delete[] escapements; + + if (wantsOffsets) { + for (int32 i = 0; i < numChars; i++) + fLink.Attach(offsets[i]); + + delete[] offsets; + } } else - fLink.StartMessage(B_ERROR); + fLink.StartMessage(status); } else fLink.StartMessage(status); + delete[] charArray; fLink.Flush(); break; } @@ -1771,11 +1796,13 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) font.SetRotation(rotation); 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.Attach(escapements, numChars * sizeof(float)); - } else - status = B_ERROR; + } } delete[] charArray; @@ -1837,7 +1864,7 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) uint32 numBytes; link.Read(&numBytes); - char charArray[numBytes]; + char *charArray = new char[numBytes]; link.Read(charArray, numBytes); BRect rectArray[numChars]; @@ -1853,7 +1880,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) font.SetFlags(flags); // 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.Attach(rectArray, sizeof(rectArray)); success = true; @@ -1863,6 +1891,7 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) if (!success) fLink.StartMessage(B_ERROR); + delete[] charArray; fLink.Flush(); break; } @@ -1927,7 +1956,8 @@ ServerApp::_DispatchMessage(int32 code, BPrivate::LinkReceiver& link) font.SetFlags(flags); // 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.Attach(rectArray, sizeof(rectArray)); success = true; diff --git a/src/servers/app/ServerFont.cpp b/src/servers/app/ServerFont.cpp index 817044bf21..739e99178e 100644 --- a/src/servers/app/ServerFont.cpp +++ b/src/servers/app/ServerFont.cpp @@ -1,10 +1,11 @@ /* - * Copyright 2001-2005, Haiku. + * Copyright 2001-2006, Haiku. * Distributed under the terms of the MIT License. * * Authors: * DarkWyrm * Jérôme Duval, jerome.duval@free.fr + * Michael Lotz */ @@ -312,18 +313,65 @@ ServerFont::SetFace(uint32 face) \return the combination of family and style ID numbers */ uint32 -ServerFont::GetFamilyAndStyle(void) const +ServerFont::GetFamilyAndStyle() const { return (FamilyID() << 16) | StyleID(); } -BShape ** -ServerFont::GetGlyphShapes(const char charArray[], int32 numChars) const +FT_Face +ServerFont::GetTransformedFace(bool rotate, bool shear) const { - if (!charArray || numChars <= 0) + FaceGetter getter(fStyle); + FT_Face face = getter.Face(); + if (!face) 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; funcs.move_to = MoveToFunc; funcs.line_to = LineToFunc; @@ -332,357 +380,208 @@ ServerFont::GetGlyphShapes(const char charArray[], int32 numChars) const funcs.shift = 0; funcs.delta = 0; - 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); - - BShape **shapes = new BShape *[numChars]; + const char *string = charArray; for (int i = 0; i < numChars; i++) { - shapes[i] = new BShape(); - shapes[i]->Clear(); - - // TODO : this is wrong (the nth char isn't charArray[i]) - FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP); + shapeArray[i] = new BShape(); + FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); FT_Outline outline = face->glyph->outline; - FT_Outline_Decompose(&outline, &funcs, shapes[i]); - shapes[i]->Close(); + FT_Outline_Decompose(&outline, &funcs, shapeArray[i]); + shapeArray[i]->Close(); } - // Reset transformation - FT_Set_Transform(face, NULL, NULL); - return shapes; + PutTransformedFace(face); + return B_OK; } -void -ServerFont::GetHasGlyphs(const char charArray[], int32 numChars, bool hasArray[]) const +status_t +ServerFont::GetHasGlyphs(const char charArray[], int32 numChars, + bool hasArray[]) const { if (!charArray || numChars <= 0 || !hasArray) - return; + return B_BAD_DATA; - FaceGetter getter(fStyle); - FT_Face face = getter.Face(); + FT_Face face = GetTransformedFace(false, false); 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 - // 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++) { - hasArray[i] = FT_Get_Char_Index(face, glyphIndex[i]) > 0; - } - } - delete[] convertedBuffer; + PutTransformedFace(face); + return B_OK; } -void -ServerFont::GetEdges(const char charArray[], int32 numChars, edge_info edgeArray[]) const +status_t +ServerFont::GetEdges(const char charArray[], int32 numChars, + edge_info edgeArray[]) const { if (!charArray || numChars <= 0 || !edgeArray) - return; + return B_BAD_DATA; - FaceGetter getter(fStyle); - FT_Face face = getter.Face(); + FT_Face face = GetTransformedFace(false, false); if (!face) - return; + return B_ERROR; - FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); - - // 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]; + const char *string = charArray; for (int i = 0; i < numChars; i++) { - // TODO : this is wrong (the nth char isn't charArray[i]) - FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP); - escapements[i].x = float(face->glyph->advance.x) / 64 / fSize; - escapements[i].y = -float(face->glyph->advance.y) / 64 / fSize; - escapements[i] += offsetArray[i]; + FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); + 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; } - // Reset transformation - FT_Set_Transform(face, NULL, NULL); - return escapements; + PutTransformedFace(face); + return B_OK; } -bool -ServerFont::GetEscapements(const char charArray[], int32 numChars, int32 numBytes, - float widthArray[], escapement_delta delta) const +status_t +ServerFont::GetEscapements(const char charArray[], int32 numChars, + escapement_delta delta, BPoint escapementArray[], BPoint offsetArray[]) const { - if (!charArray || numChars <= 0) - return false; + if (!charArray || numChars <= 0 || !escapementArray) + return B_BAD_DATA; - FaceGetter getter(fStyle); - FT_Face face = getter.Face(); + FT_Face face = GetTransformedFace(true, false); 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 - // Here is what I do in the AGGTextRenderer to handle UTF8... - // It is probably highly inefficient, so it should be reviewed. - int32 convertedLength = numBytes * 2; - char* convertedBuffer = new char[convertedLength]; + FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); + escapementArray[i].x += float(face->glyph->advance.x) / 64; + escapementArray[i].y = -float(face->glyph->advance.y) / 64; + escapementArray[i].x /= fSize; + escapementArray[i].y /= fSize; - 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) { - widthArray[i] = ((float)face->glyph->metrics.horiAdvance / 64.0) / fSize; - widthArray[i] += is_white_space(glyphIndex[i]) ? delta.space : delta.nonspace; - } + if (offsetArray) { + // ToDo: According to the BeBook: "The offsetArray is applied by + // the dynamic spacing in order to improve the relative position + // of the character's width with relation to another character, + // without altering the width." So this will probably depend on + // the spacing mode. + offsetArray[i].x = 0; + offsetArray[i].y = 0; } } - 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, - BRect rectArray[], bool string_escapement, font_metric_mode mode, + BRect rectArray[], bool stringEscapement, font_metric_mode mode, escapement_delta delta) { if (!charArray || numChars <= 0 || !rectArray) - return false; + return B_BAD_DATA; - FaceGetter getter(fStyle); - FT_Face face = getter.Face(); + FT_Face face = GetTransformedFace(true, true); if (!face) - return false; - - FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); - - // 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]; + return B_ERROR; - 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) { + 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); - 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 (iglyph->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; - } + rectArray[i].OffsetBy(is_white_space(*string) ? delta.space / 2.0 : delta.nonspace / 2.0, 0.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[], int32 numStrings, BRect rectArray[], font_metric_mode mode, escapement_delta deltaArray[]) { if (!charArray || !lengthArray|| numStrings <= 0 || !rectArray || !deltaArray) - return false; + return B_BAD_DATA; - FaceGetter getter(fStyle); - FT_Face face = getter.Face(); + FT_Face face = GetTransformedFace(true, true); if (!face) - return false; - - FT_Set_Char_Size(face, 0, int32(fSize * 64), 72, 72); + return B_ERROR; for (int32 i = 0; i < numStrings; i++) { // TODO: ... } - return true; + PutTransformedFace(face); + return B_OK; } 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; - FaceGetter getter(fStyle); - FT_Face face = getter.Face(); + FT_Face face = GetTransformedFace(false, false); if (!face) 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; - - int32 state = 0; - status_t ret; - if ((ret = convert_from_utf8(B_UNICODE_CONVERSION, - 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; - } + const char *string = _string; + for (int i = 0; i < numChars; i++) { + FT_Load_Char(face, UTF8ToCharCode(&string), FT_LOAD_NO_BITMAP); + width += face->glyph->advance.x / 64.0; } - delete[] convertedBuffer; + PutTransformedFace(face); return width; } @@ -725,7 +624,7 @@ ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const // get the escapement of each glyph in font units float* escapementArray = new float[numChars]; 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, escapementArray, fSize, ellipsisWidth, length, numChars);