FifeGUI 0.2.0
A C++ GUI library designed for games.
imagefont.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later OR BSD-3-Clause
2// SPDX-FileCopyrightText: 2004 - 2008 Olof Naessén and Per Larsson
3// SPDX-FileCopyrightText: 2013 - 2026 Fifengine contributors
4
5#include "fifechan/imagefont.hpp"
6
7#include <sstream>
8#include <string>
9#include <utility>
10
11#include "fifechan/color.hpp"
12#include "fifechan/exception.hpp"
13#include "fifechan/graphics.hpp"
14#include "fifechan/image.hpp"
15#include "fifechan/rectangle.hpp"
16
17namespace fcn
18{
19 ImageFont::ImageFont(std::string const & filename, std::string const & glyphs) :
20 mFilename(filename), mImage(Image::load(filename, false))
21 {
22
23 Color const separator = mImage->getPixel(0, 0);
24
25 // Find the starting point for glyphs in the image
26 int startColumn = 0;
27 for (; startColumn < mImage->getWidth(); ++startColumn) {
28 if (separator != mImage->getPixel(startColumn, 0)) {
29 break;
30 }
31 }
32
33 // Check for corrupt image (all pixels are separator color)
34 if (startColumn >= mImage->getWidth()) {
35 throwException("Corrupt image.");
36 }
37
38 // Find the height of glyphs
39 int height = 0;
40 for (int j = 0; j < mImage->getHeight(); ++j) {
41 if (separator == mImage->getPixel(startColumn, j)) {
42 break;
43 }
44 ++height;
45 }
46
47 mHeight = height;
48
49 int x = 0;
50 int y = 0;
51
52 // Scan for all glyphs
53 for (char const glyph : glyphs) {
54 auto const k = static_cast<unsigned char>(glyph);
55 mGlyph[k] = scanForGlyph(k, x, y, separator);
56 // Update x and y with new coordinates.
57 x = mGlyph[k].x + mGlyph[k].width;
58 y = mGlyph[k].y;
59 }
60
61 mImage->convertToDisplayFormat();
62
63 mRowSpacing = 0;
64 mGlyphSpacing = 0;
65 }
66
67 ImageFont::ImageFont(Image* image, std::string const & glyphs) : mFilename("Image*")
68 {
69
70 if (image == nullptr) {
71 throwException("Font image is nullptr.");
72 }
73 mImage = image;
74
75 Color const separator = mImage->getPixel(0, 0);
76
77 int i = 0;
78 for (i = 0; i < mImage->getWidth() && separator == mImage->getPixel(i, 0); ++i) { }
79
80 if (i >= mImage->getWidth()) {
81 throwException("Corrupt image.");
82 }
83
84 int j = 0;
85 for (j = 0; j < mImage->getHeight(); ++j) {
86 if (separator == mImage->getPixel(i, j)) {
87 break;
88 }
89 }
90
91 mHeight = j;
92 int x = 0;
93 int y = 0;
94 unsigned char glyph = 0;
95
96 for (i = 0; std::cmp_less(i, glyphs.size()); ++i) {
97 glyph = glyphs.at(i);
98
99 mGlyph[glyph] = scanForGlyph(glyph, x, y, separator);
100 // Update x and y with new coordinates.
101 x = mGlyph[glyph].x + mGlyph[glyph].width;
102 y = mGlyph[glyph].y;
103 }
104
105 mImage->convertToDisplayFormat();
106
107 mRowSpacing = 0;
108 mGlyphSpacing = 0;
109 }
110
111 ImageFont::ImageFont(std::string const & filename, unsigned char glyphsFrom, unsigned char glyphsTo) :
112 mFilename(filename), mImage(Image::load(filename, false))
113 {
114
115 Color const separator = mImage->getPixel(0, 0);
116
117 int i = 0;
118 for (i = 0; separator == mImage->getPixel(i, 0) && i < mImage->getWidth(); ++i) { }
119
120 if (i >= mImage->getWidth()) {
121 throwException("Corrupt image.");
122 }
123
124 int j = 0;
125 for (j = 0; j < mImage->getHeight(); ++j) {
126 if (separator == mImage->getPixel(i, j)) {
127 break;
128 }
129 }
130
131 mHeight = j;
132 int x = 0;
133 int y = 0;
134
135 for (i = glyphsFrom; i < glyphsTo + 1; i++) {
136 mGlyph[i] = scanForGlyph(i, x, y, separator);
137 // Update x och y with new coordinates.
138 x = mGlyph[i].x + mGlyph[i].width;
139 y = mGlyph[i].y;
140 }
141
142 mImage->convertToDisplayFormat();
143
144 mRowSpacing = 0;
145 mGlyphSpacing = 0;
146 }
147
148 ImageFont::~ImageFont()
149 {
150 delete mImage;
151 }
152
153 int ImageFont::getWidth(unsigned char glyph) const
154 {
155 if (mGlyph[glyph].width == 0) {
156 return mGlyph[static_cast<int>((' '))].width + mGlyphSpacing;
157 }
158
159 return mGlyph[glyph].width + mGlyphSpacing;
160 }
161
163 {
164 return mHeight + mRowSpacing;
165 }
166
167 int ImageFont::drawGlyph(Graphics* graphics, unsigned char glyph, int x, int y)
168 {
169 // This is needed for drawing the glyph in the middle
170 // if we have spacing.
171 int const yoffset = getRowSpacing() / 2;
172
173 if (mGlyph[glyph].width == 0) {
174 graphics->drawRectangle(
175 x,
176 y + 1 + yoffset,
177 mGlyph[static_cast<int>((' '))].width - 1,
178 mGlyph[static_cast<int>((' '))].height - 2);
179
180 return mGlyph[static_cast<int>((' '))].width + mGlyphSpacing;
181 }
182
183 graphics->drawImage(
184 mImage, mGlyph[glyph].x, mGlyph[glyph].y, x, y + yoffset, mGlyph[glyph].width, mGlyph[glyph].height);
185
186 return mGlyph[glyph].width + mGlyphSpacing;
187 }
188
189 void ImageFont::drawString(Graphics* graphics, std::string const & text, int x, int y)
190 {
191 for (char const c : text) {
192 drawGlyph(graphics, c, x, y);
193 x += getWidth(c);
194 }
195 }
196
197 void ImageFont::setRowSpacing(int spacing)
198 {
199 mRowSpacing = spacing;
200 }
201
203 {
204 return mRowSpacing;
205 }
206
208 {
209 mGlyphSpacing = spacing;
210 }
211
213 {
214 return mGlyphSpacing;
215 }
216
217 Rectangle ImageFont::scanForGlyph(unsigned char glyph, int x, int y, Color const & separator)
218 {
219 Color color;
220 bool foundGlyphStart = false;
221
222 // Finding the start of the glyph
223 for (; !foundGlyphStart; ++x) {
224 if (x >= mImage->getWidth()) {
225 y += mHeight + 1;
226 x = 0;
227 if (y >= mImage->getHeight()) {
228 std::ostringstream os;
229 os << "Image " << mFilename << " with font is corrupt near character '" << glyph << "'";
230 throwException(os.str());
231 }
232 }
233 color = mImage->getPixel(x, y);
234 if (color != separator) {
235 foundGlyphStart = true;
236 break;
237 }
238 }
239
240 int width = 0;
241 bool foundGlyphEnd = false;
242
243 // Finding the width of the glyph
244 for (; !foundGlyphEnd; ++width) {
245 if (x + width >= mImage->getWidth()) {
246 std::ostringstream os;
247 os << "Image " << mFilename << " with font is corrupt near character '" << glyph << "'";
248 throwException(os.str());
249 }
250 color = mImage->getPixel(x + width, y);
251 if (color == separator) {
252 foundGlyphEnd = true;
253 }
254 }
255
256 // width now points to the separator pixel; glyph width is one less
257 return {x, y, width - 1, mHeight};
258 }
259
260 int ImageFont::getWidth(std::string const & text) const
261 {
262 unsigned int i = 0;
263 int size = 0;
264
265 for (i = 0; i < text.size(); ++i) {
266 size += getWidth(text.at(i));
267 }
268
269 return size - mGlyphSpacing;
270 }
271
272 int ImageFont::getStringIndexAt(std::string const & text, int x) const
273 {
274 unsigned int i = 0;
275 int size = 0;
276
277 for (i = 0; i < text.size(); ++i) {
278 size += getWidth(text.at(i));
279
280 if (size > x) {
281 return i;
282 }
283 }
284
285 return text.size();
286 }
287} // namespace fcn
Color.
Definition color.hpp:56
Abstract interface providing primitive drawing functions (lines, rectangles, etc.).
Definition graphics.hpp:57
virtual void drawImage(Image const *image, int srcX, int srcY, int dstX, int dstY, int width, int height)=0
Draws a part of an image.
virtual void drawRectangle(Rectangle const &rectangle)=0
Draws a simple, non-filled rectangle with a one pixel width.
virtual int getRowSpacing()
Gets the space between rows in pixels.
int mHeight
Holds the height of the image font.
virtual int drawGlyph(Graphics *graphics, unsigned char glyph, int x, int y)
Draws a glyph.
int getHeight() const override
Gets the height of the glyphs in the font.
int getStringIndexAt(std::string const &text, int x) const override
Gets a string index in a string providing an x coordinate.
Rectangle scanForGlyph(unsigned char glyph, int x, int y, Color const &separator)
Scans for a certain glyph.
Image * mImage
Holds the image with the font data.
std::array< Rectangle, 256 > mGlyph
Holds the glyphs areas in the image.
std::string mFilename
Holds the filename of the image with the font data.
virtual void setRowSpacing(int spacing)
Sets the space between rows in pixels.
ImageFont(std::string const &filename, std::string const &glyphs)
Constructor.
Definition imagefont.cpp:19
virtual int getWidth(unsigned char glyph) const
Gets a width of a glyph in pixels.
virtual int getGlyphSpacing()
Gets the spacing between letters in pixels.
virtual void setGlyphSpacing(int spacing)
Sets the spacing between glyphs in pixels.
int mGlyphSpacing
Holds the glyph spacing of the image font.
int mRowSpacing
Holds the row spacing of the image font.
void drawString(Graphics *graphics, std::string const &text, int x, int y) override
Draws a string.
Abstract holder for image data.
Definition image.hpp:32
Represents a rectangular area (X, Y, Width, Height).
Definition rectangle.hpp:20