DUDS
Distributed Update of Data from Something
BppFont.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2019 Jeff Jackowski
9  */
13 #include <duds/general/Errors.hpp>
14 #include <codecvt>
15 
16 namespace duds { namespace ui { namespace graphics {
17 
25 
27  // if an implementing derived class renders the glyph, it can add the image
28  // to the glyphs map so that it doesn't need to be rendered again:
29  //glyphs[gc] = image;
30  // use the white square character if present
31  std::unordered_map<char32_t, ConstBppImageSptr>::const_iterator giter =
32  glyphs.find(9633);
33  if (giter != glyphs.end()) {
34  return giter->second;
35  }
36  // got nothing, so throw
38 }
39 
40 void BppFont::load(const std::string &path) {
41  std::ifstream is(path);
42  if (!is.good()) {
45  );
46  }
47  try {
48  load(is);
49  } catch (boost::exception &be) {
50  // add the file name to the error metadata
51  be << ImageArchiveFileName(path);
52  throw;
53  }
54 }
55 
56 void BppFont::load(std::istream &is) {
57  BppImageArchiveSequence bias(is);
58  bias.readHeader();
60  std::wstring_convert<
61  std::codecvt_utf8<char32_t>,
62  char32_t
63  > conv;
64  for (; iter != bias.end(); ++iter) {
65  // UTF-8 needs up to 4 bytes for a character code
66  if (iter->first.length() <= 4) {
67  // convert from UTF-8
68  std::u32string u32 = conv.from_bytes(iter->first);
69  // could have more than one character
70  if (u32.length() == 1) {
71  std::lock_guard<duds::general::Spinlock> lock(block);
72  glyphs[u32[0]] = iter->second;
73  }
74  }
75  }
76 }
77 
78 void BppFont::add(char32_t gc, const ConstBppImageSptr &img) {
79  std::lock_guard<duds::general::Spinlock> lock(block);
80  glyphs[gc] = img;
81 }
82 
83 void BppFont::add(char32_t gc, ConstBppImageSptr &&img) {
84  std::lock_guard<duds::general::Spinlock> lock(block);
85  glyphs[gc] = std::move(img);
86 }
87 
88 const ConstBppImageSptr &BppFont::get(char32_t gc) {
89  std::lock_guard<duds::general::Spinlock> lock(block);
90  std::unordered_map<char32_t, ConstBppImageSptr>::const_iterator
91  iter = glyphs.find(gc);
92  if (iter == glyphs.end()) {
94  glyphs[gc] = std::move(bis);
95  return glyphs[gc];
96  }
97  return iter->second;
98 }
99 
101  std::lock_guard<duds::general::Spinlock> lock(block);
102  std::unordered_map<char32_t, ConstBppImageSptr>::const_iterator
103  iter = glyphs.find(gc);
104  if (iter == glyphs.end()) {
105  ConstBppImageSptr bis;
106  try {
107  bis = renderGlyph(gc);
108  glyphs[gc] = bis;
109  } catch (...) { }
110  return bis;
111  }
112  return iter->second;
113 }
114 
116  ImageDimensions res(0, 0);
117  for (char32_t check : { '8', 'M', 'q', 'y' }) {
118  ConstBppImageSptr img = tryGet(check);
119  if (img) {
120  res = res.maxExtent(img->dimensions());
121  }
122  }
123  // if no dimensions have yet been found, but there are glyphs . . .
124  if ((res == ImageDimensions(0, 0)) && !glyphs.empty()) {
125  // . . . get the first glyph and use that
126  std::lock_guard<duds::general::Spinlock> lock(block);
127  std::unordered_map<char32_t, ConstBppImageSptr>::const_iterator iter =
128  glyphs.cbegin();
129  return iter->second->dimensions();
130  }
131  return res;
132 }
133 
137 typedef std::vector<const BppImage *> ImageVec;
138 
146  int chars;
154  constexpr LineDimensions() : chars(0), dim(0, 0) { }
155 };
156 
160 typedef std::vector<LineDimensions> DimVec;
161 
165 static std::wstring_convert< std::codecvt_utf8< char32_t >, char32_t > conv;
166 
167 BppImageSptr BppFont::render(const std::string &text, Flags flags) {
168  // convert UTF-8 to UTF-32, then render
169  std::u32string text32 = conv.from_bytes(text);
170  return render(text32, flags);
171 }
172 
173 BppImageSptr BppFont::render(const std::u32string &text, Flags flags)
174 try {
175  // Will store all glyphs needed for output in the same order as text. All
176  // lines are included. Line breaks are not.
177  ImageVec gv;
178  gv.reserve(text.size());
179  // stores dimensions for each line
180  DimVec lineDims(1);
181  // output image dimesions
182  ImageDimensions id(0, 0);
183  // maximum glyph dimesions
184  ImageDimensions md(0, 0);
185  // greatest number of characters on a line
186  int maxline = 0;
187  // find all the needed glyphs
188  std::u32string::const_iterator titer = text.cbegin();
189  std::unordered_map<char32_t, ConstBppImageSptr>::const_iterator giter;
190  for (; titer != text.cend(); ++titer) {
191  // handle line breaks
192  if (*titer == '\n') {
193  // update maximum characters per line
194  maxline = std::max(maxline, lineDims.back().chars);
195  // update image size
196  if (flags & FixedWidthPerLine) {
197  // work out fixed width size for the line now
198  lineDims.back().dim.w = md.w * lineDims.back().chars;
199  // clear the maximum glyph width for the next line
200  md.w = 0;
201  } else if (!(flags & FixedWidth)) {
202  // image width updated now for variable width; later for fixed
203  id.w = std::max(id.w, lineDims.back().dim.w);
204  }
205  if (flags & VariableHeight) {
206  // image height updated now for variable height; later for fixed
207  id.h += lineDims.back().dim.h;
208  }
209  // create a new line entry
210  lineDims.emplace_back(LineDimensions());
211  // process the next character
212  continue;
213  }
214  { // get the next glyph
215  std::lock_guard<duds::general::Spinlock> lock(block);
216  giter = glyphs.find(*titer);
217  // no glyph?
218  if (giter == glyphs.end()) {
219  // try to get it again; may throw
220  ConstBppImageSptr glyph = renderGlyph(*titer);
221  gv.emplace_back(glyph.get());
222  } else {
223  // have glyph
224  gv.push_back(giter->second.get());
225  }
226  }
227  // record dimension stats
228  const ImageDimensions &gd = gv.back()->dimensions();
229  lineDims.back().dim.w += gd.w;
230  md.w = std::max(md.w, gd.w);
231  lineDims.back().dim.h = std::max(lineDims.back().dim.h, gd.h);
232  md.h = std::max(md.h, gd.h);
233  ++lineDims.back().chars;
234  }
235  // update maximum characters per line
236  maxline = std::max(maxline, lineDims.back().chars);
237  // update image size
238  if (flags & FixedWidthPerLine) {
239  // enusre the FixedWidth flag is not also set
240  flags |= ~FixedWidth;
241  // work out fixed width size for the line
242  lineDims.back().dim.w = md.w * lineDims.back().chars;
243  } else if (!(flags & FixedWidth)) {
244  // update image width to fit line
245  id.w = std::max(id.w, lineDims.back().dim.w);
246  } else {
247  // fixed width for all characters
248  id.w = maxline * md.w;
249  // width is not set per line in lineDims
250  }
251  if (flags & VariableHeight) {
252  // update image height for variable height
253  id.h += lineDims.back().dim.h;
254  } else {
255  // height is the same for all lines
256  id.h = md.h * lineDims.size();
257  // height is not set per line in lineDims
258  }
259 
260  // gv now has all the needed glyphs in the order they must be rendered,
261  // id has the final image dimensions, and lineDims has details on each
262  // line.
263 
264  // make the destination image
265  BppImageSptr bis = BppImage::make(id);
266  // glyphs might not fill the whole image, so clear the image
267  bis->clearImage();
268  // iterate over the lines and the glyphs
269  DimVec::iterator diter = lineDims.begin();
270  ImageVec::iterator viter = gv.begin();
271  ImageLocation il(0, 0);
272  int tpos = 0;
273  for (; diter != lineDims.end(); ++diter) {
274  // update width & height if fixed across all lines
275  if (flags & FixedWidth) {
276  diter->dim.w = md.w * diter->chars;
277  }
278  if (!(flags & VariableHeight)) {
279  diter->dim.h = md.h;
280  }
281  // set the left location for the first glyph of this line
282  if (flags & AlignCenter) {
283  il.x = (id.w - diter->dim.w) / 2;
284  } else if (flags & AlignRight) {
285  il.x = id.w - diter->dim.w;
286  } else {
287  il.x = 0;
288  }
289  // render each glyph
290  for (int loop = diter->chars; loop > 0; ++viter, --loop) {
291  const ImageDimensions &gd = (*viter)->dimensions();
292  int adv = gd.w;
293  // glyph may be short
294  ImageLocation loc(il.x, il.y + diter->dim.h - gd.h);
295  // glyph may be narrow
296  if (flags & FixedWidth) {
297  loc.x += (md.w - gd.w) / 2;
298  adv = md.w;
299  } else if (flags & FixedWidthPerLine) {
300  adv = diter->dim.w / diter->chars;
301  loc.x += (adv - gd.w) / 2;
302  }
303  bis->write(*viter, loc, gd);
304  // advance position to the left
305  il.x += adv;
306  }
307  // set the upper location for the first glyph of the next line
308  il.y += diter->dim.h;
309  }
310 
311  return bis;
312 } catch (boost::exception &be) {
313  be << String(conv.to_bytes(text));
314  throw;
315 }
316 
317 ImageDimensions BppFont::lineDimensions(const std::string &text, Flags flags) {
318  // convert UTF-8 to UTF-32, then figure dimensions
319  std::u32string text32 = conv.from_bytes(text);
320  return lineDimensions(text32, flags);
321 }
322 
323 ImageDimensions BppFont::lineDimensions(const std::u32string &text, Flags flags)
324 try {
325  ImageDimensions dim(0, 0);
326  {
327  std::lock_guard<duds::general::Spinlock> lock(block);
328  for (auto c : text) {
329  // find next glyph
330  std::unordered_map<char32_t, ConstBppImageSptr>::const_iterator giter =
331  glyphs.find(c);
332  ConstBppImageSptr glyph;
333  // no glyph?
334  if (giter == glyphs.end()) {
335  // try to get it again; may throw
336  glyph = renderGlyph(c);
337  } else {
338  glyph = giter->second;
339  }
340  // height
341  dim.h = std::max(dim.h, glyph->height());
342  // width -- fixed?
343  if (flags & (FixedWidth | FixedWidthPerLine)) {
344  // store max width so far rather than cumulative width
345  dim.w = std::max(dim.w, glyph->width());
346  } else {
347  // update cumulative width
348  dim.w += glyph->width();
349  }
350  }
351  }
352  // fixed width?
353  if (flags & (FixedWidth | FixedWidthPerLine)) {
354  // compute total width
355  dim.w = dim.w * text.size();
356  }
357  return dim;
358 } catch (boost::exception &be) {
359  be << String(conv.to_bytes(text));
360  throw;
361 }
362 
363 } } }
iterator begin()
Parses the first image in the stream and returns an iterator to that data.
constexpr LineDimensions()
Ensure all fields start with zeros.
Definition: BppFont.cpp:154
void add(char32_t gc, const ConstBppImageSptr &img)
Adds or replaces a glyph in the font.
Definition: BppFont.cpp:78
ImageDimensions lineDimensions(const std::string &text, Flags flags=Flags::Zero())
Returns the dimensions of a single-line string without the overhead of rendering the string...
Definition: BppFont.cpp:317
Stores a location within an image.
Definition: BppImage.hpp:33
ConstBppImageSptr tryGet(char32_t gc)
Returns the glyph of the specified character code.
Definition: BppFont.cpp:100
std::int16_t y
Vertical coordinate.
Definition: BppImage.hpp:41
static constexpr Flags AlignRight
Align each line to the right.
Definition: BppFont.hpp:192
std::int16_t x
Horizontal coordinate.
Definition: BppImage.hpp:37
std::shared_ptr< const BppImage > ConstBppImageSptr
Definition: BppImage.hpp:1778
An input iterator that will parse and supply image data from the stream.
constexpr ImageDimensions maxExtent(const ImageDimensions &dim) const
Returns new dimensions that are minimally large enough to fit this dimension and the given dimension...
Definition: BppImage.hpp:222
move_impl move(unsigned int c, unsigned int r)
Display stream manipulator that moves the display cursor to the given location.
static constexpr Flags AlignMask
All alignment flags.
Definition: BppFont.hpp:196
The base class for errors resulting from the attempt to read an image archive stream or file...
std::vector< LineDimensions > DimVec
Information on each line of text to render.
Definition: BppFont.cpp:160
int chars
Number of characters in the line.
Definition: BppFont.cpp:146
static constexpr iterator end()
Returns the end iterator.
boost::error_info< struct Info_ImageArcFileName, std::string > ImageArchiveFileName
The name of the image archive file involved in an ImageArchiveStreamError.
static constexpr Flags AlignCenter
Center each line in the resulting image.
Definition: BppFont.hpp:188
Stores the dimensions of an image.
Definition: BppImage.hpp:125
static constexpr Flags FixedWidthPerLine
Compute fixed width individually for each line, so each line may have a different width per glyph...
Definition: BppFont.hpp:175
boost::error_info< struct Info_String, std::string > String
A string, like one requested for rendering in a specific font.
void readHeader()
Parses the headers used in BppImageArchive files.
static constexpr Flags FixedWidth
All glyphs rendered with the same width using the maximum width of the glyphs used in the string...
Definition: BppFont.hpp:170
std::shared_ptr< BppImage > BppImageSptr
Definition: BppImage.hpp:1777
duds::general::Spinlock block
Used for thread safety.
Definition: BppFont.hpp:39
virtual ConstBppImageSptr renderGlyph(char32_t gc)
Called to render the requested glyph when it is not present in the glyphs map.
Definition: BppFont.cpp:26
std::vector< const BppImage * > ImageVec
Used to hold all the glyphs needed to render a string.
Definition: BppFont.cpp:137
static std::wstring_convert< std::codecvt_utf8< char32_t >, char32_t > conv
String converter; UTF-8 to/from UTF-32.
Definition: BppFont.cpp:165
static constexpr Flags VariableHeight
Each line will have the height of its tallest glyph rather than the tallest glyph of the entire strin...
Definition: BppFont.hpp:180
Provides an input iterator to a sequence of named bit-per-pixel images read from an input stream...
ImageDimensions estimatedMaxCharacterSize()
Returns a somewhat decent estimate of the largest size of a character without actually inspecting all...
Definition: BppFont.cpp:115
ImageDimensions dim
Minimum dimensions for the line.
Definition: BppFont.cpp:150
duds::general::BitFlags< struct BppFontRenderingFlags > Flags
Option flags that affect how text is rendered.
Definition: BppFont.hpp:165
void load(const std::string &path)
Loads glyphs from an image archive in the specified file.
Definition: BppFont.cpp:40
static std::shared_ptr< BppImage > make(const ImageDimensions &id)
Convenience function to make a shared pointer to an image using the BppImage(const ImageDimensions &)...
Definition: BppImage.hpp:1018
General graphics related code.
Definition: HD44780.hpp:15
static constexpr Flags AlignLeft
Align each line to the left.
Definition: BppFont.hpp:184
boost::error_info< struct Info_Character, char32_t > Character
A character, like one requested for rendering in a specific font.
Information on a line.
Definition: BppFont.cpp:142
BppImageSptr render(const std::string &text, Flags flags=AlignLeft)
Renders the given text using this object&#39;s font.
Definition: BppFont.cpp:167
General errors.
std::unordered_map< char32_t, ConstBppImageSptr > glyphs
The glyph images keyed by character.
Definition: BppFont.hpp:35
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
const ConstBppImageSptr & get(char32_t gc)
Returns the glyph of the specified character code.
Definition: BppFont.cpp:88
A glyph required to render a string is not availble in the font.