Mercurial > hg > orthanc
comparison Core/Images/Font.cpp @ 1612:96582230ddcb
Core/ImageFormats folder renamed as Core/Images
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 04 Sep 2015 16:36:18 +0200 |
parents | Core/ImageFormats/Font.cpp@5e9b2aac8b89 |
children | 644c32c07306 |
comparison
equal
deleted
inserted
replaced
1611:5e9b2aac8b89 | 1612:96582230ddcb |
---|---|
1 /** | |
2 * Orthanc - A Lightweight, RESTful DICOM Store | |
3 * Copyright (C) 2012-2015 Sebastien Jodogne, Medical Physics | |
4 * Department, University Hospital of Liege, Belgium | |
5 * | |
6 * This program is free software: you can redistribute it and/or | |
7 * modify it under the terms of the GNU General Public License as | |
8 * published by the Free Software Foundation, either version 3 of the | |
9 * License, or (at your option) any later version. | |
10 * | |
11 * In addition, as a special exception, the copyright holders of this | |
12 * program give permission to link the code of its release with the | |
13 * OpenSSL project's "OpenSSL" library (or with modified versions of it | |
14 * that use the same license as the "OpenSSL" library), and distribute | |
15 * the linked executables. You must obey the GNU General Public License | |
16 * in all respects for all of the code used other than "OpenSSL". If you | |
17 * modify file(s) with this exception, you may extend this exception to | |
18 * your version of the file(s), but you are not obligated to do so. If | |
19 * you do not wish to do so, delete this exception statement from your | |
20 * version. If you delete this exception statement from all source files | |
21 * in the program, then also delete it here. | |
22 * | |
23 * This program is distributed in the hope that it will be useful, but | |
24 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
26 * General Public License for more details. | |
27 * | |
28 * You should have received a copy of the GNU General Public License | |
29 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
30 **/ | |
31 | |
32 | |
33 #include "../PrecompiledHeaders.h" | |
34 #include "Font.h" | |
35 | |
36 #include "../Toolbox.h" | |
37 #include "../OrthancException.h" | |
38 | |
39 #include <stdio.h> | |
40 #include <memory> | |
41 #include <boost/lexical_cast.hpp> | |
42 | |
43 namespace Orthanc | |
44 { | |
45 Font::~Font() | |
46 { | |
47 for (Characters::iterator it = characters_.begin(); | |
48 it != characters_.end(); it++) | |
49 { | |
50 delete it->second; | |
51 } | |
52 } | |
53 | |
54 | |
55 void Font::LoadFromMemory(const std::string& font) | |
56 { | |
57 Json::Value v; | |
58 Json::Reader reader; | |
59 if (!reader.parse(font, v) || | |
60 v.type() != Json::objectValue || | |
61 !v.isMember("Name") || | |
62 !v.isMember("Size") || | |
63 !v.isMember("Characters") || | |
64 v["Name"].type() != Json::stringValue || | |
65 v["Size"].type() != Json::intValue || | |
66 v["Characters"].type() != Json::objectValue) | |
67 { | |
68 throw OrthancException(ErrorCode_BadFont); | |
69 } | |
70 | |
71 name_ = v["Name"].asString(); | |
72 size_ = v["Size"].asUInt(); | |
73 maxHeight_ = 0; | |
74 | |
75 Json::Value::Members characters = v["Characters"].getMemberNames(); | |
76 | |
77 for (size_t i = 0; i < characters.size(); i++) | |
78 { | |
79 const Json::Value& info = v["Characters"][characters[i]]; | |
80 if (info.type() != Json::objectValue || | |
81 !info.isMember("Advance") || | |
82 !info.isMember("Bitmap") || | |
83 !info.isMember("Height") || | |
84 !info.isMember("Top") || | |
85 !info.isMember("Width") || | |
86 info["Advance"].type() != Json::intValue || | |
87 info["Bitmap"].type() != Json::arrayValue || | |
88 info["Height"].type() != Json::intValue || | |
89 info["Top"].type() != Json::intValue || | |
90 info["Width"].type() != Json::intValue) | |
91 { | |
92 throw OrthancException(ErrorCode_BadFont); | |
93 } | |
94 | |
95 std::auto_ptr<Character> c(new Character); | |
96 | |
97 c->advance_ = info["Advance"].asUInt(); | |
98 c->height_ = info["Height"].asUInt(); | |
99 c->top_ = info["Top"].asUInt(); | |
100 c->width_ = info["Width"].asUInt(); | |
101 c->bitmap_.resize(info["Bitmap"].size()); | |
102 | |
103 if (c->height_ > maxHeight_) | |
104 { | |
105 maxHeight_ = c->height_; | |
106 } | |
107 | |
108 for (Json::Value::ArrayIndex j = 0; j < info["Bitmap"].size(); j++) | |
109 { | |
110 if (info["Bitmap"][j].type() != Json::intValue) | |
111 { | |
112 throw OrthancException(ErrorCode_BadFont); | |
113 } | |
114 | |
115 int value = info["Bitmap"][j].asInt(); | |
116 if (value < 0 || value > 255) | |
117 { | |
118 throw OrthancException(ErrorCode_BadFont); | |
119 } | |
120 | |
121 c->bitmap_[j] = value; | |
122 } | |
123 | |
124 int index = boost::lexical_cast<int>(characters[i]); | |
125 if (index < 0 || index > 255) | |
126 { | |
127 throw OrthancException(ErrorCode_BadFont); | |
128 } | |
129 | |
130 characters_[static_cast<char>(index)] = c.release(); | |
131 } | |
132 } | |
133 | |
134 | |
135 void Font::LoadFromFile(const std::string& path) | |
136 { | |
137 std::string font; | |
138 Toolbox::ReadFile(font, path); | |
139 LoadFromMemory(font); | |
140 } | |
141 | |
142 | |
143 static unsigned int MyMin(unsigned int a, | |
144 unsigned int b) | |
145 { | |
146 return a < b ? a : b; | |
147 } | |
148 | |
149 | |
150 void Font::DrawCharacter(ImageAccessor& target, | |
151 const Character& character, | |
152 int x, | |
153 int y, | |
154 const uint8_t color[4]) const | |
155 { | |
156 // Compute the bounds of the character | |
157 if (x >= static_cast<int>(target.GetWidth()) || | |
158 y >= static_cast<int>(target.GetHeight())) | |
159 { | |
160 // The character is out of the image | |
161 return; | |
162 } | |
163 | |
164 unsigned int left = x < 0 ? -x : 0; | |
165 unsigned int top = y < 0 ? -y : 0; | |
166 unsigned int width = MyMin(character.width_, target.GetWidth() - x); | |
167 unsigned int height = MyMin(character.height_, target.GetHeight() - y); | |
168 | |
169 uint8_t bpp = target.GetBytesPerPixel(); | |
170 | |
171 // Blit the font bitmap OVER the target image | |
172 // https://en.wikipedia.org/wiki/Alpha_compositing | |
173 | |
174 for (unsigned int cy = top; cy < height; cy++) | |
175 { | |
176 uint8_t* p = reinterpret_cast<uint8_t*>(target.GetRow(y + cy)) + (x + left) * bpp; | |
177 unsigned int pos = cy * character.width_ + left; | |
178 | |
179 switch (target.GetFormat()) | |
180 { | |
181 case PixelFormat_Grayscale8: | |
182 { | |
183 assert(bpp == 1); | |
184 for (unsigned int cx = left; cx < width; cx++, pos++, p++) | |
185 { | |
186 uint16_t alpha = character.bitmap_[pos]; | |
187 *p = (alpha * static_cast<uint16_t>(color[0]) + (255 - alpha) * static_cast<uint16_t>(*p)) >> 8; | |
188 } | |
189 | |
190 break; | |
191 } | |
192 | |
193 case PixelFormat_RGB24: | |
194 { | |
195 assert(bpp == 3); | |
196 for (unsigned int cx = left; cx < width; cx++, pos++, p += 3) | |
197 { | |
198 uint16_t alpha = character.bitmap_[pos]; | |
199 p[0] = (alpha * static_cast<uint16_t>(color[0]) + (255 - alpha) * static_cast<uint16_t>(p[0])) >> 8; | |
200 p[1] = (alpha * static_cast<uint16_t>(color[1]) + (255 - alpha) * static_cast<uint16_t>(p[1])) >> 8; | |
201 p[2] = (alpha * static_cast<uint16_t>(color[2]) + (255 - alpha) * static_cast<uint16_t>(p[2])) >> 8; | |
202 } | |
203 | |
204 break; | |
205 } | |
206 | |
207 case PixelFormat_RGBA32: | |
208 { | |
209 assert(bpp == 4); | |
210 | |
211 for (unsigned int cx = left; cx < width; cx++, pos++, p += 4) | |
212 { | |
213 float alpha = static_cast<float>(character.bitmap_[pos]) / 255.0f; | |
214 float beta = (1.0f - alpha) * static_cast<float>(p[3]) / 255.0f; | |
215 float denom = 1.0f / (alpha + beta); | |
216 | |
217 for (uint8_t i = 0; i < 3; i++) | |
218 { | |
219 p[i] = static_cast<uint8_t>((alpha * static_cast<float>(color[i]) + | |
220 beta * static_cast<float>(p[i])) * denom); | |
221 } | |
222 | |
223 p[3] = static_cast<uint8_t>(255.0f * (alpha + beta)); | |
224 } | |
225 | |
226 break; | |
227 } | |
228 | |
229 default: | |
230 throw OrthancException(ErrorCode_NotImplemented); | |
231 } | |
232 } | |
233 | |
234 } | |
235 | |
236 | |
237 void Font::DrawInternal(ImageAccessor& target, | |
238 const std::string& utf8, | |
239 int x, | |
240 int y, | |
241 const uint8_t color[4]) const | |
242 { | |
243 if (target.GetFormat() != PixelFormat_Grayscale8 && | |
244 target.GetFormat() != PixelFormat_RGB24 && | |
245 target.GetFormat() != PixelFormat_RGBA32) | |
246 { | |
247 throw OrthancException(ErrorCode_NotImplemented); | |
248 } | |
249 | |
250 int a = x; | |
251 | |
252 std::string s = Toolbox::ConvertFromUtf8(utf8, Encoding_Latin1); | |
253 | |
254 for (size_t i = 0; i < s.size(); i++) | |
255 { | |
256 if (s[i] == '\n') | |
257 { | |
258 // Go to the next line | |
259 a = x; | |
260 y += maxHeight_ + 1; | |
261 } | |
262 else | |
263 { | |
264 Characters::const_iterator c = characters_.find(s[i]); | |
265 if (c != characters_.end()) | |
266 { | |
267 DrawCharacter(target, *c->second, a, y + static_cast<int>(c->second->top_), color); | |
268 a += c->second->advance_; | |
269 } | |
270 } | |
271 } | |
272 } | |
273 | |
274 | |
275 void Font::Draw(ImageAccessor& target, | |
276 const std::string& utf8, | |
277 int x, | |
278 int y, | |
279 uint8_t grayscale) const | |
280 { | |
281 uint8_t color[4] = { grayscale, grayscale, grayscale, 255 }; | |
282 DrawInternal(target, utf8, x, y, color); | |
283 } | |
284 | |
285 | |
286 void Font::Draw(ImageAccessor& target, | |
287 const std::string& utf8, | |
288 int x, | |
289 int y, | |
290 uint8_t r, | |
291 uint8_t g, | |
292 uint8_t b) const | |
293 { | |
294 uint8_t color[4] = { r, g, b, 255 }; | |
295 DrawInternal(target, utf8, x, y, color); | |
296 } | |
297 | |
298 } |