b5436616a3
Also applied our style guide on that class. Renamed some public globals to match our style guide. Made BitmapManager inherit from BLocker instead of duplicating its functionality (incorrectly, did not check for B_INTERRUPTED) locally. Some more cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@13259 a95241bf-73f2-0310-859d-f6bbb57e9c96
541 lines
12 KiB
C++
541 lines
12 KiB
C++
/*
|
|
* Copyright 2001-2005, Haiku.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* DarkWyrm <bpmagic@columbus.rr.com>
|
|
*/
|
|
|
|
|
|
#include <ByteOrder.h>
|
|
#include <Shape.h>
|
|
#include <String.h>
|
|
#include <UTF8.h>
|
|
|
|
#include "Angle.h"
|
|
#include "FontServer.h"
|
|
#include "moreUTF8.h"
|
|
#include "truncate_string.h"
|
|
|
|
#include FT_FREETYPE_H
|
|
#include FT_OUTLINE_H
|
|
|
|
#include "ServerFont.h"
|
|
|
|
|
|
// functions needed to convert a freetype vector graphics to a BShape
|
|
inline BPoint
|
|
VectorToPoint(FT_Vector *vector)
|
|
{
|
|
BPoint result;
|
|
result.x = float(int32(vector->x)) / 2097152;
|
|
result.y = -float(int32(vector->y)) / 2097152;
|
|
return result;
|
|
}
|
|
|
|
int
|
|
MoveToFunc(FT_Vector *to, void *user)
|
|
{
|
|
((BShape *)user)->MoveTo(VectorToPoint(to));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
LineToFunc(FT_Vector *to, void *user)
|
|
{
|
|
((BShape *)user)->LineTo(VectorToPoint(to));
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ConicToFunc(FT_Vector *control, FT_Vector *to, void *user)
|
|
{
|
|
BPoint controls[3];
|
|
|
|
controls[0] = VectorToPoint(control);
|
|
controls[1] = VectorToPoint(to);
|
|
controls[2] = controls[1];
|
|
|
|
((BShape *)user)->BezierTo(controls);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
CubicToFunc(FT_Vector *control1, FT_Vector *control2, FT_Vector *to, void *user)
|
|
{
|
|
BPoint controls[3];
|
|
|
|
controls[0] = VectorToPoint(control1);
|
|
controls[1] = VectorToPoint(control2);
|
|
controls[2] = VectorToPoint(to);
|
|
|
|
((BShape *)user)->BezierTo(controls);
|
|
return 0;
|
|
}
|
|
|
|
|
|
// is_white_space
|
|
inline bool
|
|
is_white_space(uint16 glyph)
|
|
{
|
|
// TODO: handle them all!
|
|
if (glyph == ' ' || glyph == B_TAB)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
/*!
|
|
\brief Constructor
|
|
\param style Style object to which the ServerFont belongs
|
|
\param size Character size in points
|
|
\param rotation Rotation in degrees
|
|
\param shear Shear (slant) in degrees. 45 <= shear <= 135
|
|
\param flags Style flags as defined in <Font.h>
|
|
\param spacing String spacing flag as defined in <Font.h>
|
|
*/
|
|
ServerFont::ServerFont(FontStyle *style, float size,
|
|
float rotation, float shear,
|
|
uint16 flags, uint8 spacing)
|
|
: fStyle(style),
|
|
fSize(size),
|
|
fRotation(rotation),
|
|
fShear(shear),
|
|
fBounds(0, 0, 0, 0),
|
|
fFlags(flags),
|
|
fSpacing(spacing),
|
|
fDirection(B_FONT_LEFT_TO_RIGHT),
|
|
fFace(B_REGULAR_FACE),
|
|
fEncoding(B_UNICODE_UTF8)
|
|
|
|
{
|
|
if (fStyle)
|
|
fStyle->AddDependent();
|
|
}
|
|
|
|
// TODO: fStyle should not be NULL. There should be another FontStyle
|
|
// constructor, that initializes without actually interfacing with
|
|
// freetype, so that a ServerFont can be guaranteed to be "valid".
|
|
|
|
ServerFont::ServerFont()
|
|
: fStyle(NULL),
|
|
fSize(0.0),
|
|
fRotation(0.0),
|
|
fShear(90.0),
|
|
fBounds(0, 0, 0, 0),
|
|
fFlags(0),
|
|
fSpacing(B_STRING_SPACING),
|
|
fDirection(B_FONT_LEFT_TO_RIGHT),
|
|
fFace(B_REGULAR_FACE),
|
|
fEncoding(B_UNICODE_UTF8)
|
|
{
|
|
}
|
|
|
|
/*!
|
|
\brief Copy Constructor
|
|
\param font ServerFont to copy
|
|
*/
|
|
ServerFont::ServerFont(const ServerFont &font)
|
|
: fStyle(font.fStyle),
|
|
fSize(font.fSize),
|
|
fRotation(font.fRotation),
|
|
fShear(font.fShear),
|
|
fBounds(0, 0, 0, 0),
|
|
fFlags(font.fFlags),
|
|
fSpacing(font.fSpacing),
|
|
fDirection(font.fDirection),
|
|
fFace(font.fFace),
|
|
fEncoding(font.fEncoding)
|
|
{
|
|
if (fStyle)
|
|
fStyle->AddDependent();
|
|
}
|
|
|
|
/*!
|
|
\brief Removes itself as a dependency of its owning style.
|
|
*/
|
|
ServerFont::~ServerFont()
|
|
{
|
|
if (fStyle)
|
|
fStyle->RemoveDependent();
|
|
}
|
|
|
|
/*!
|
|
\brief Returns a copy of the specified font
|
|
\param The font to copy from.
|
|
\return A copy of the specified font
|
|
*/
|
|
ServerFont&
|
|
ServerFont::operator=(const ServerFont& font)
|
|
{
|
|
fSize = font.fSize;
|
|
fRotation = font.fRotation;
|
|
fShear = font.fShear;
|
|
fFlags = font.fFlags;
|
|
fSpacing = font.fSpacing;
|
|
fDirection = font.fDirection;
|
|
fFace = font.fFace;
|
|
fEncoding = font.fEncoding;
|
|
fBounds = font.fBounds;
|
|
|
|
_SetStyle(font.fStyle);
|
|
|
|
return *this;
|
|
}
|
|
|
|
/*!
|
|
\brief Returns the number of strikes in the font
|
|
\return The number of strikes in the font
|
|
*/
|
|
int32
|
|
ServerFont::CountTuned()
|
|
{
|
|
if (fStyle)
|
|
return fStyle->TunedCount();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*!
|
|
\brief Returns the file format of the font. Currently unimplemented.
|
|
\return B_TRUETYPE_WINDOWS
|
|
*/
|
|
font_file_format
|
|
ServerFont::FileFormat()
|
|
{
|
|
// TODO: implement ServerFont::FileFormat
|
|
return B_TRUETYPE_WINDOWS;
|
|
}
|
|
|
|
const char*
|
|
ServerFont::GetStyle() const
|
|
{
|
|
if (fStyle)
|
|
return fStyle->Name();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
const char*
|
|
ServerFont::GetFamily() const
|
|
{
|
|
if (fStyle)
|
|
return fStyle->Family()->Name();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*!
|
|
\brief Sets the ServerFont instance to whatever font is specified
|
|
\param familyID ID number of the family to set
|
|
\param styleID ID number of the style to set
|
|
\return B_OK if successful, B_ERROR if not
|
|
*/
|
|
status_t
|
|
ServerFont::SetFamilyAndStyle(uint16 familyID, uint16 styleID)
|
|
{
|
|
FontStyle* style = NULL;
|
|
|
|
if (gFontServer->Lock()) {
|
|
style = gFontServer->GetStyle(familyID, styleID);
|
|
gFontServer->Unlock();
|
|
}
|
|
|
|
if (!style)
|
|
return B_ERROR;
|
|
|
|
_SetStyle(style);
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
/*!
|
|
\brief Sets the ServerFont instance to whatever font is specified
|
|
\param fontID the combination of family and style ID numbers
|
|
\return B_OK if successful, B_ERROR if not
|
|
*/
|
|
status_t
|
|
ServerFont::SetFamilyAndStyle(uint32 fontID)
|
|
{
|
|
uint16 style = fontID & 0xFFFF;
|
|
uint16 family = (fontID & 0xFFFF0000) >> 16;
|
|
|
|
return SetFamilyAndStyle(family, style);
|
|
}
|
|
|
|
/*!
|
|
\brief Gets the ID values for the ServerFont instance in one shot
|
|
\return the combination of family and style ID numbers
|
|
*/
|
|
uint32
|
|
ServerFont::GetFamilyAndStyle(void) const
|
|
{
|
|
return (FamilyID() << 16) | StyleID();
|
|
}
|
|
|
|
|
|
BShape **
|
|
ServerFont::GetGlyphShapes(const char charArray[], int32 numChars) const
|
|
{
|
|
if (!charArray || numChars <= 0)
|
|
return NULL;
|
|
|
|
FT_Outline_Funcs funcs;
|
|
funcs.move_to = MoveToFunc;
|
|
funcs.line_to = LineToFunc;
|
|
funcs.conic_to = ConicToFunc;
|
|
funcs.cubic_to = CubicToFunc;
|
|
|
|
FT_Face face = fStyle->GetFTFace();
|
|
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_Vector pen;
|
|
//FT_Set_Transform(face, &smatrix, &pen);
|
|
|
|
BShape **shapes = (BShape **)malloc(sizeof(BShape *) * numChars);
|
|
for (int i = 0; i < numChars; i++) {
|
|
shapes[i] = new BShape();
|
|
shapes[i]->Clear();
|
|
FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP);
|
|
FT_Outline outline = face->glyph->outline;
|
|
FT_Outline_Decompose(&outline, &funcs, shapes[i]);
|
|
shapes[i]->Close();
|
|
}
|
|
|
|
return shapes;
|
|
}
|
|
|
|
// GetEscapements
|
|
BPoint*
|
|
ServerFont::GetEscapements(const char charArray[], int32 numChars,
|
|
BPoint offsetArray[]) const
|
|
{
|
|
if (!fStyle || !charArray || numChars <= 0 || !offsetArray)
|
|
return NULL;
|
|
|
|
FT_Face face = fStyle->GetFTFace();
|
|
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_Vector pen;
|
|
//FT_Set_Transform(face, &smatrix, &pen);
|
|
|
|
// TODO: I'm not sure if this the correct interpretation
|
|
// of the BeBook. Have actual tests been done here?
|
|
|
|
// TODO: handle UTF8... see below!!
|
|
BPoint *escapements = (BPoint *)malloc(sizeof(BPoint) * numChars);
|
|
for (int i = 0; i < numChars; i++) {
|
|
FT_Load_Char(face, charArray[i], FT_LOAD_NO_BITMAP);
|
|
// escapements[i].x = float(face->glyph->metrics.width / 64) / fSize;
|
|
// escapements[i].y = 0;
|
|
escapements[i].x = float(face->glyph->metrics.horiAdvance / 64) / fSize;
|
|
escapements[i].y = float(face->glyph->metrics.vertAdvance / 64) / fSize;
|
|
escapements[i] += offsetArray[i];
|
|
}
|
|
|
|
return escapements;
|
|
}
|
|
|
|
|
|
// GetEscapements
|
|
bool
|
|
ServerFont::GetEscapements(const char charArray[], int32 numChars,
|
|
float widthArray[], escapement_delta delta) const
|
|
{
|
|
if (!fStyle || !charArray || numChars <= 0)
|
|
return false;
|
|
|
|
FT_Face face = fStyle->GetFTFace();
|
|
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];
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
delete[] convertedBuffer;
|
|
|
|
return ret >= B_OK;
|
|
}
|
|
|
|
// StringWidth
|
|
float
|
|
ServerFont::StringWidth(const char* string, int32 numBytes) const
|
|
{
|
|
if (!fStyle || !string || numBytes <= 0)
|
|
return 0.0;
|
|
|
|
FT_Face face = fStyle->GetFTFace();
|
|
if (!face)
|
|
return 0.0;
|
|
|
|
float width = 0.0;
|
|
|
|
int32 convertedLength = numBytes * 2;
|
|
char* convertedBuffer = new char[convertedLength];
|
|
|
|
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 += (float)face->glyph->metrics.horiAdvance / 64.0;
|
|
}
|
|
}
|
|
delete[] convertedBuffer;
|
|
|
|
return width;
|
|
}
|
|
|
|
/*!
|
|
\brief Returns a BRect which encloses the entire font
|
|
\return A BRect which encloses the entire font
|
|
*/
|
|
BRect
|
|
ServerFont::BoundingBox()
|
|
{
|
|
// TODO: fBounds is nowhere calculated!
|
|
return fBounds;
|
|
}
|
|
|
|
/*!
|
|
\brief Obtains the height values for characters in the font in its current state
|
|
\param fh pointer to a font_height object to receive the values for the font
|
|
*/
|
|
void
|
|
ServerFont::Height(font_height *fh) const
|
|
{
|
|
if (fh && fStyle)
|
|
*fh = fStyle->GetHeight(fSize);
|
|
}
|
|
|
|
// TruncateString
|
|
void
|
|
ServerFont::TruncateString(BString* inOut, uint32 mode, float width) const
|
|
{
|
|
if (inOut) {
|
|
// the width of the "…" glyph
|
|
float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, 3);
|
|
const char* string = inOut->String();
|
|
int32 length = strlen(string);
|
|
// temporary array to hold result
|
|
char* result = new char[length + 3];
|
|
// count the individual glyphs
|
|
int32 numChars = UTF8CountChars(string, length);
|
|
// 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, escapementArray, delta);
|
|
|
|
truncate_string(string, mode, width, result,
|
|
escapementArray, fSize, ellipsisWidth, length, numChars);
|
|
|
|
inOut->SetTo(result);
|
|
|
|
delete[] escapementArray;
|
|
delete[] result;
|
|
}
|
|
}
|
|
|
|
// _SetStyle
|
|
void
|
|
ServerFont::_SetStyle(FontStyle* style)
|
|
{
|
|
if (style != fStyle) {
|
|
// detach from old style
|
|
if (fStyle)
|
|
fStyle->RemoveDependent();
|
|
|
|
fStyle = style;
|
|
|
|
// attach to new style
|
|
if (fStyle)
|
|
fStyle->AddDependent();
|
|
}
|
|
}
|