577
|
1 /**
|
|
2 * Stone of Orthanc
|
|
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
|
|
4 * Department, University Hospital of Liege, Belgium
|
|
5 * Copyright (C) 2017-2019 Osimis S.A., Belgium
|
|
6 *
|
|
7 * This program is free software: you can redistribute it and/or
|
|
8 * modify it under the terms of the GNU Affero General Public License
|
|
9 * as published by the Free Software Foundation, either version 3 of
|
|
10 * the License, or (at your option) any later version.
|
|
11 *
|
|
12 * This program is distributed in the hope that it will be useful, but
|
|
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
15 * Affero General Public License for more details.
|
|
16 *
|
|
17 * You should have received a copy of the GNU Affero General Public License
|
|
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
19 **/
|
|
20
|
|
21
|
|
22 #include "GlyphTextureAlphabet.h"
|
|
23
|
|
24 #include "TextBoundingBox.h"
|
|
25 #include "../Toolbox/DynamicBitmap.h"
|
|
26
|
|
27 #include <Core/Images/Image.h>
|
|
28 #include <Core/Images/ImageProcessing.h>
|
|
29 #include <Core/OrthancException.h>
|
|
30
|
|
31 #include <boost/math/special_functions/round.hpp>
|
|
32
|
|
33
|
|
34 namespace OrthancStone
|
|
35 {
|
|
36 class GlyphTextureAlphabet::GlyphSizeVisitor : public GlyphAlphabet::IGlyphVisitor
|
|
37 {
|
|
38 private:
|
|
39 unsigned int maxWidth_;
|
|
40 unsigned int maxHeight_;
|
|
41
|
|
42 public:
|
|
43 GlyphSizeVisitor() :
|
|
44 maxWidth_(0),
|
|
45 maxHeight_(0)
|
|
46 {
|
|
47 }
|
|
48
|
|
49 virtual void Visit(uint32_t unicode,
|
|
50 const Glyph& glyph)
|
|
51 {
|
|
52 maxWidth_ = std::max(maxWidth_, glyph.GetWidth());
|
|
53 maxHeight_ = std::max(maxHeight_, glyph.GetHeight());
|
|
54 }
|
|
55
|
|
56 unsigned int GetMaxWidth() const
|
|
57 {
|
|
58 return maxWidth_;
|
|
59 }
|
|
60
|
|
61 unsigned int GetMaxHeight() const
|
|
62 {
|
|
63 return maxHeight_;
|
|
64 }
|
|
65 };
|
|
66
|
|
67
|
|
68 class GlyphTextureAlphabet::TextureGenerator : public GlyphAlphabet::IGlyphVisitor
|
|
69 {
|
|
70 private:
|
|
71 std::auto_ptr<Orthanc::ImageAccessor> texture_;
|
|
72
|
|
73 unsigned int countColumns_;
|
|
74 unsigned int countRows_;
|
|
75 GlyphAlphabet& targetAlphabet_;
|
|
76 unsigned int glyphMaxWidth_;
|
|
77 unsigned int glyphMaxHeight_;
|
|
78 unsigned int column_;
|
|
79 unsigned int row_;
|
|
80
|
|
81 public:
|
|
82 TextureGenerator(GlyphAlphabet& targetAlphabet,
|
|
83 unsigned int countGlyphs,
|
|
84 unsigned int glyphMaxWidth,
|
|
85 unsigned int glyphMaxHeight) :
|
|
86 targetAlphabet_(targetAlphabet),
|
|
87 glyphMaxWidth_(glyphMaxWidth),
|
|
88 glyphMaxHeight_(glyphMaxHeight),
|
|
89 column_(0),
|
|
90 row_(0)
|
|
91 {
|
|
92 int c = boost::math::iround<int>(sqrt(static_cast<float>(countGlyphs)));
|
|
93
|
|
94 if (c <= 0)
|
|
95 {
|
|
96 countColumns_ = 1;
|
|
97 }
|
|
98 else
|
|
99 {
|
|
100 countColumns_ = static_cast<unsigned int>(c);
|
|
101 }
|
|
102
|
|
103 countRows_ = countGlyphs / countColumns_;
|
|
104 if (countGlyphs % countColumns_ != 0)
|
|
105 {
|
|
106 countRows_++;
|
|
107 }
|
|
108
|
|
109 texture_.reset(new Orthanc::Image(Orthanc::PixelFormat_RGBA32,
|
|
110 countColumns_ * glyphMaxWidth_,
|
|
111 countRows_ * glyphMaxHeight_,
|
|
112 true /* force minimal pitch */));
|
|
113
|
|
114 Orthanc::ImageProcessing::Set(*texture_, 0, 0, 0, 0);
|
|
115 }
|
|
116
|
|
117
|
|
118 virtual void Visit(uint32_t unicode,
|
|
119 const Glyph& glyph)
|
|
120 {
|
|
121 if (!glyph.HasPayload())
|
|
122 {
|
|
123 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
|
|
124 }
|
|
125
|
|
126 if (column_ >= countColumns_ ||
|
|
127 row_ >= countRows_)
|
|
128 {
|
|
129 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
|
|
130 }
|
|
131
|
|
132 unsigned int x = column_ * glyphMaxWidth_;
|
|
133 unsigned int y = row_ * glyphMaxHeight_;
|
|
134
|
|
135 const Orthanc::ImageAccessor& source = dynamic_cast<const DynamicBitmap&>(glyph.GetPayload()).GetBitmap();
|
|
136
|
|
137 if (source.GetFormat() != Orthanc::PixelFormat_Grayscale8)
|
|
138 {
|
|
139 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError);
|
|
140 }
|
|
141
|
|
142 targetAlphabet_.Register(unicode, glyph, new TextureLocation(x, y));
|
|
143
|
|
144 Orthanc::ImageAccessor target;
|
|
145 texture_->GetRegion(target, x, y, source.GetWidth(), source.GetHeight());
|
|
146
|
|
147 //Orthanc::ImageProcessing::Copy(target, bitmap->GetBitmap());
|
|
148
|
|
149 for (unsigned int y = 0; y < source.GetHeight(); y++)
|
|
150 {
|
|
151 const uint8_t* p = reinterpret_cast<const uint8_t*>(source.GetConstRow(y));
|
|
152 uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y));
|
|
153
|
|
154 for (unsigned int x = 0; x < source.GetWidth(); x++)
|
|
155 {
|
|
156 // Premultiplied alpha
|
|
157 q[0] = 0;
|
|
158 q[1] = 0;
|
|
159 q[2] = 0;
|
|
160 q[3] = *p;
|
|
161
|
|
162 p++;
|
|
163 q += 4;
|
|
164 }
|
|
165 }
|
|
166
|
|
167 column_++;
|
|
168 if (column_ == countColumns_)
|
|
169 {
|
|
170 column_ = 0;
|
|
171 row_++;
|
|
172 }
|
|
173 }
|
|
174
|
|
175
|
|
176 Orthanc::ImageAccessor* ReleaseTexture()
|
|
177 {
|
|
178 return texture_.release();
|
|
179 }
|
|
180 };
|
|
181
|
|
182
|
|
183 class GlyphTextureAlphabet::RenderTextVisitor : public GlyphAlphabet::ITextVisitor
|
|
184 {
|
|
185 private:
|
|
186 Orthanc::ImageAccessor& target_;
|
|
187 const Orthanc::ImageAccessor& texture_;
|
|
188 int offsetX_;
|
|
189 int offsetY_;
|
|
190
|
|
191 public:
|
|
192 RenderTextVisitor(Orthanc::ImageAccessor& target,
|
|
193 const GlyphTextureAlphabet& that,
|
|
194 int offsetX,
|
|
195 int offsetY) :
|
|
196 target_(target),
|
|
197 texture_(that.GetTexture()),
|
|
198 offsetX_(offsetX),
|
|
199 offsetY_(offsetY)
|
|
200 {
|
|
201 }
|
|
202
|
|
203 virtual void Visit(uint32_t unicode,
|
|
204 int x,
|
|
205 int y,
|
|
206 unsigned int width,
|
|
207 unsigned int height,
|
|
208 const Orthanc::IDynamicObject* payload)
|
|
209 {
|
|
210 int left = x + offsetX_;
|
|
211 int top = y + offsetY_;
|
|
212
|
|
213 assert(payload != NULL);
|
|
214 const TextureLocation& location = *dynamic_cast<const TextureLocation*>(payload);
|
|
215
|
|
216 assert(left >= 0 &&
|
|
217 top >= 0 &&
|
|
218 static_cast<unsigned int>(left) + width <= target_.GetWidth() &&
|
|
219 static_cast<unsigned int>(top) + height <= target_.GetHeight());
|
|
220
|
|
221 {
|
|
222 Orthanc::ImageAccessor to;
|
|
223 target_.GetRegion(to, left, top, width, height);
|
|
224
|
|
225 Orthanc::ImageAccessor from;
|
|
226 texture_.GetRegion(from, location.GetX(), location.GetY(), width, height);
|
|
227
|
|
228 Orthanc::ImageProcessing::Copy(to, from);
|
|
229 }
|
|
230 }
|
|
231 };
|
|
232
|
|
233
|
|
234 GlyphTextureAlphabet::GlyphTextureAlphabet(const GlyphBitmapAlphabet& sourceAlphabet) :
|
|
235 textureWidth_(0),
|
|
236 textureHeight_(0)
|
|
237 {
|
|
238 GlyphSizeVisitor size;
|
|
239 sourceAlphabet.GetAlphabet().Apply(size);
|
|
240
|
|
241 TextureGenerator generator(alphabet_,
|
|
242 sourceAlphabet.GetAlphabet().GetSize(),
|
|
243 size.GetMaxWidth(),
|
|
244 size.GetMaxHeight());
|
|
245 sourceAlphabet.GetAlphabet().Apply(generator);
|
|
246
|
|
247 texture_.reset(generator.ReleaseTexture());
|
|
248 textureWidth_ = texture_->GetWidth();
|
|
249 textureHeight_ = texture_->GetHeight();
|
|
250 }
|
|
251
|
|
252
|
|
253 const Orthanc::ImageAccessor& GlyphTextureAlphabet::GetTexture() const
|
|
254 {
|
|
255 if (texture_.get() == NULL)
|
|
256 {
|
|
257 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
|
|
258 }
|
|
259 else
|
|
260 {
|
|
261 return *texture_;
|
|
262 }
|
|
263 }
|
|
264
|
|
265
|
|
266 Orthanc::ImageAccessor* GlyphTextureAlphabet::ReleaseTexture()
|
|
267 {
|
|
268 if (texture_.get() == NULL)
|
|
269 {
|
|
270 throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls);
|
|
271 }
|
|
272 else
|
|
273 {
|
|
274 return texture_.release();
|
|
275 }
|
|
276 }
|
|
277
|
|
278
|
|
279 Orthanc::ImageAccessor* GlyphTextureAlphabet::RenderText(const std::string& utf8)
|
|
280 {
|
|
281 TextBoundingBox box(alphabet_, utf8);
|
|
282
|
|
283 std::auto_ptr<Orthanc::ImageAccessor> bitmap(
|
|
284 new Orthanc::Image(Orthanc::PixelFormat_RGBA32,
|
|
285 box.GetWidth(), box.GetHeight(),
|
|
286 true /* force minimal pitch */));
|
|
287
|
|
288 Orthanc::ImageProcessing::Set(*bitmap, 0, 0, 0, 0);
|
|
289
|
|
290 RenderTextVisitor visitor(*bitmap, *this, -box.GetLeft(), -box.GetTop());
|
|
291 alphabet_.Apply(visitor, utf8);
|
|
292
|
|
293 return bitmap.release();
|
|
294 }
|
|
295 }
|