1 //
2 // Copyright (c) 2008-2017 the Urho3D project.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 //
22
23 #include "../Precompiled.h"
24
25 #include "../Core/Context.h"
26 #include "../Core/Profiler.h"
27 #include "../Graphics/Graphics.h"
28 #include "../IO/Deserializer.h"
29 #include "../IO/FileSystem.h"
30 #include "../UI/Font.h"
31 #include "../UI/FontFaceBitmap.h"
32 #include "../UI/FontFaceFreeType.h"
33 #include "../Resource/ResourceCache.h"
34 #include "../Resource/XMLElement.h"
35 #include "../Resource/XMLFile.h"
36
37 #include "../DebugNew.h"
38
39 namespace Urho3D
40 {
41
42 namespace
43 {
44 /// Convert float to 26.6 fixed-point (as used internally by FreeType)
FloatToFixed(float value)45 inline int FloatToFixed(float value)
46 {
47 return (int)(value * 64);
48 }
49 }
50
51 static const float MIN_POINT_SIZE = 1;
52 static const float MAX_POINT_SIZE = 96;
53
Font(Context * context)54 Font::Font(Context* context) :
55 Resource(context),
56 fontDataSize_(0),
57 absoluteOffset_(IntVector2::ZERO),
58 scaledOffset_(Vector2::ZERO),
59 fontType_(FONT_NONE),
60 sdfFont_(false)
61 {
62 }
63
~Font()64 Font::~Font()
65 {
66 // To ensure FreeType deallocates properly, first clear all faces, then release the raw font data
67 ReleaseFaces();
68 fontData_.Reset();
69 }
70
RegisterObject(Context * context)71 void Font::RegisterObject(Context* context)
72 {
73 context->RegisterFactory<Font>();
74 }
75
BeginLoad(Deserializer & source)76 bool Font::BeginLoad(Deserializer& source)
77 {
78 // In headless mode, do not actually load, just return success
79 Graphics* graphics = GetSubsystem<Graphics>();
80 if (!graphics)
81 return true;
82
83 fontType_ = FONT_NONE;
84 faces_.Clear();
85
86 fontDataSize_ = source.GetSize();
87 if (fontDataSize_)
88 {
89 fontData_ = new unsigned char[fontDataSize_];
90 if (source.Read(&fontData_[0], fontDataSize_) != fontDataSize_)
91 return false;
92 }
93 else
94 {
95 fontData_.Reset();
96 return false;
97 }
98
99 String ext = GetExtension(GetName());
100 if (ext == ".ttf" || ext == ".otf" || ext == ".woff")
101 {
102 fontType_ = FONT_FREETYPE;
103 LoadParameters();
104 }
105 else if (ext == ".xml" || ext == ".fnt" || ext == ".sdf")
106 fontType_ = FONT_BITMAP;
107
108 sdfFont_ = ext == ".sdf";
109
110 SetMemoryUse(fontDataSize_);
111 return true;
112 }
113
SaveXML(Serializer & dest,int pointSize,bool usedGlyphs,const String & indentation)114 bool Font::SaveXML(Serializer& dest, int pointSize, bool usedGlyphs, const String& indentation)
115 {
116 FontFace* fontFace = GetFace(pointSize);
117 if (!fontFace)
118 return false;
119
120 URHO3D_PROFILE(FontSaveXML);
121
122 SharedPtr<FontFaceBitmap> packedFontFace(new FontFaceBitmap(this));
123 if (!packedFontFace->Load(fontFace, usedGlyphs))
124 return false;
125
126 return packedFontFace->Save(dest, pointSize, indentation);
127 }
128
SetAbsoluteGlyphOffset(const IntVector2 & offset)129 void Font::SetAbsoluteGlyphOffset(const IntVector2& offset)
130 {
131 absoluteOffset_ = offset;
132 }
133
SetScaledGlyphOffset(const Vector2 & offset)134 void Font::SetScaledGlyphOffset(const Vector2& offset)
135 {
136 scaledOffset_ = offset;
137 }
138
GetFace(float pointSize)139 FontFace* Font::GetFace(float pointSize)
140 {
141 // In headless mode, always return null
142 Graphics* graphics = GetSubsystem<Graphics>();
143 if (!graphics)
144 return 0;
145
146 // For bitmap font type, always return the same font face provided by the font's bitmap file regardless of the actual requested point size
147 if (fontType_ == FONT_BITMAP)
148 pointSize = 0;
149 else
150 pointSize = Clamp(pointSize, MIN_POINT_SIZE, MAX_POINT_SIZE);
151
152 // For outline fonts, we return the nearest size in 1/64th increments, as that's what FreeType supports.
153 int key = FloatToFixed(pointSize);
154 HashMap<int, SharedPtr<FontFace> >::Iterator i = faces_.Find(key);
155 if (i != faces_.End())
156 {
157 if (!i->second_->IsDataLost())
158 return i->second_;
159 else
160 {
161 // Erase and reload face if texture data lost (OpenGL mode only)
162 faces_.Erase(i);
163 }
164 }
165
166 URHO3D_PROFILE(GetFontFace);
167
168 switch (fontType_)
169 {
170 case FONT_FREETYPE:
171 return GetFaceFreeType(pointSize);
172
173 case FONT_BITMAP:
174 return GetFaceBitmap(pointSize);
175
176 default:
177 return 0;
178 }
179 }
180
GetTotalGlyphOffset(float pointSize) const181 IntVector2 Font::GetTotalGlyphOffset(float pointSize) const
182 {
183 Vector2 multipliedOffset = pointSize * scaledOffset_;
184 return absoluteOffset_ + IntVector2((int)(multipliedOffset.x_ + 0.5f), (int)(multipliedOffset.y_ + 0.5f));
185 }
186
ReleaseFaces()187 void Font::ReleaseFaces()
188 {
189 faces_.Clear();
190 }
191
LoadParameters()192 void Font::LoadParameters()
193 {
194 ResourceCache* cache = GetSubsystem<ResourceCache>();
195 String xmlName = ReplaceExtension(GetName(), ".xml");
196 SharedPtr<XMLFile> xml = cache->GetTempResource<XMLFile>(xmlName, false);
197 if (!xml)
198 return;
199
200 XMLElement rootElem = xml->GetRoot();
201
202 XMLElement absoluteElem = rootElem.GetChild("absoluteoffset");
203 if (!absoluteElem)
204 absoluteElem = rootElem.GetChild("absolute");
205
206 if (absoluteElem)
207 {
208 absoluteOffset_.x_ = absoluteElem.GetInt("x");
209 absoluteOffset_.y_ = absoluteElem.GetInt("y");
210 }
211
212 XMLElement scaledElem = rootElem.GetChild("scaledoffset");
213 if (!scaledElem)
214 scaledElem = rootElem.GetChild("scaled");
215
216 if (scaledElem)
217 {
218 scaledOffset_.x_ = scaledElem.GetFloat("x");
219 scaledOffset_.y_ = scaledElem.GetFloat("y");
220 }
221 }
222
GetFaceFreeType(float pointSize)223 FontFace* Font::GetFaceFreeType(float pointSize)
224 {
225 SharedPtr<FontFace> newFace(new FontFaceFreeType(this));
226 if (!newFace->Load(&fontData_[0], fontDataSize_, pointSize))
227 return 0;
228
229 int key = FloatToFixed(pointSize);
230 faces_[key] = newFace;
231 return newFace;
232 }
233
GetFaceBitmap(float pointSize)234 FontFace* Font::GetFaceBitmap(float pointSize)
235 {
236 SharedPtr<FontFace> newFace(new FontFaceBitmap(this));
237 if (!newFace->Load(&fontData_[0], fontDataSize_, pointSize))
238 return 0;
239
240 int key = FloatToFixed(pointSize);
241 faces_[key] = newFace;
242 return newFace;
243 }
244
245 }
246