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