1
0
mirror of git://projects.qi-hardware.com/gmenu2x.git synced 2024-10-05 21:49:28 +03:00
gmenu2x/src/asfont.cpp
Maarten ter Huurne 8b17e2ef62 PNG: Make loadPNG() responsible for creating an RGBA surface.
Before this commit loadPNG() could return any surface format and the caller
was responsible for converting it to the desired format. However, in practice
all callers want a surface with an alpha channel and SDL only supports that
with 32bpp surfaces, so RGBA or a permutation thereof. So I changed the
contract for loadPNG() so it is required to return an RGBA surface, and
removed the conversion code in the callers.

The next step is to replace IMG_Load() by a function that calls libpng
directly and loads a fixed 32bpp pixel format. That way, we can drop the
SDL_image dependency and avoid unnecessary pixel format conversions.
2011-06-02 06:19:21 +02:00

197 lines
5.0 KiB
C++

#include "asfont.h"
#include "imageio.h"
#include "surface.h"
#include "utilities.h"
#include <cassert>
#include <cstring>
#define SFONTPLUS_CHARSET "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~¡¿ÀÁÈÉÌÍÒÓÙÚÝÄËÏÖÜŸÂÊÎÔÛÅÃÕÑÆÇČĎĚĽĹŇÔŘŔŠŤŮŽàáèéìíòóùúýäëïöüÿâêîôûåãõñæçčďěľĺňôřŕšťžůðßÐÞþАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяØøąćęłńśżźĄĆĘŁŃŚŻŹ"
ASFont::ASFont(const std::string &fontImagePath)
: characters(SFONTPLUS_CHARSET)
{
surface = loadPNG(fontImagePath);
if (!surface) {
return;
}
assert(surface->format->BytesPerPixel == 4);
SDL_LockSurface(surface);
// Determine character widths.
Uint32 pink = SDL_MapRGB(surface->format, 255, 0, 255);
Uint32 *topLine = static_cast<Uint32 *>(surface->pixels);
const unsigned width = surface->w;
unsigned x = 0;
unsigned c = 0;
while (c < characters.length()) {
while (x < width && topLine[x] != pink) x++;
unsigned startx = x;
x++;
while (x < width && topLine[x] == pink) x++;
charpos.push_back(startx);
charpos.push_back(x);
if (c > 0 && utf8Code(characters[c - 1])) {
// UTF8 character
charpos.push_back(startx);
charpos.push_back(x);
c++;
}
c++;
}
// Scan height of "0" glyph.
std::string::size_type pos = characters.find("0") * 2;
SDL_Rect srcrect = {
charpos[pos], 1, charpos[pos + 2] - charpos[pos], surface->h - 1
};
const unsigned alphaMask = surface->format->Amask;
unsigned y = srcrect.h;
bool nonTransparentFound = false;
while (!nonTransparentFound && y-- > 0) {
Uint32 *line = reinterpret_cast<Uint32 *>(
reinterpret_cast<Uint8 *>(surface->pixels)
+ (srcrect.y + y) * surface->pitch
);
for (unsigned x = 0; !nonTransparentFound && x < srcrect.w; x++) {
nonTransparentFound = (line[srcrect.x + x] & alphaMask) != 0;
}
}
lineHeight = y + 1;
SDL_UnlockSurface(surface);
}
ASFont::~ASFont() {
if (surface) {
SDL_FreeSurface(surface);
}
}
bool ASFont::utf8Code(unsigned char c) {
return (c>=194 && c<=198) || c==208 || c==209;
//return c>=194;
}
void ASFont::writeLine(Surface *s, const std::string &text, int x, int y) {
if (text.empty()) return;
std::string::size_type pos;
SDL_Rect srcrect, dstrect;
// these values won't change in the loop
srcrect.y = 1;
dstrect.y = y;
srcrect.h = dstrect.h = surface->h-1;
for(unsigned i=0; i<text.length() && x<surface->w; i++) {
//Utf8 characters
if (utf8Code(text[i]) && i+1<text.length()) {
pos = characters.find(text.substr(i,2));
i++;
} else
pos = characters.find(text[i]);
if (pos == std::string::npos) {
x += charpos[2]-charpos[1];
continue;
}
pos *= 2;
srcrect.x = charpos[pos];
srcrect.w = charpos[pos+2] - charpos[pos];
dstrect.x = x - charpos[pos+1] + charpos[pos];
SDL_BlitSurface(surface, &srcrect, s->raw, &dstrect);
x += charpos[pos+2] - charpos[pos+1];
}
}
int ASFont::getTextWidth(const char *text) {
int maxWidth = 0, width = 0;
while (char ch = *text++) {
if (ch == '\n') {
// New line.
maxWidth = max(width, maxWidth);
width = 0;
} else {
std::string::size_type pos;
if (utf8Code(ch) && *text) {
// 2-byte character.
pos = characters.find(std::string(&text[-1], 2));
text++;
} else {
// 1-byte character.
pos = characters.find(ch);
}
if (pos == std::string::npos) {
pos = 0;
}
width += charpos[pos * 2 + 2] - charpos[pos * 2 + 1];
}
}
return max(width, maxWidth);
}
int ASFont::getTextWidth(const std::string& text) {
return getTextWidth(text.c_str());
}
void ASFont::writeLine(Surface* surface, const std::string& text, int x, int y, HAlign halign) {
switch (halign) {
case HAlignLeft:
break;
case HAlignCenter:
x -= getTextWidth(text) / 2;
break;
case HAlignRight:
x -= getTextWidth(text);
break;
}
writeLine(surface, text, x, y);
}
void ASFont::writeLine(Surface* surface, const std::string& text, int x, int y, HAlign halign, VAlign valign) {
switch (valign) {
case VAlignTop:
break;
case VAlignMiddle:
y -= getHeight() / 2;
break;
case VAlignBottom:
y -= getHeight();
break;
}
writeLine(surface, text, x, y, halign);
}
void ASFont::writeLine(Surface* surface, const std::vector<std::string> &text, int x, int y, HAlign halign, VAlign valign) {
switch (valign) {
case VAlignTop:
break;
case VAlignMiddle:
y -= (getHeight() / 2) * text.size();
break;
case VAlignBottom:
y -= getHeight() * text.size();
break;
}
for (std::vector<std::string>::const_iterator it = text.begin(); it != text.end(); ++it) {
write(surface, *it, x, y, halign);
y += getHeight();
}
}
void ASFont::write(Surface* surface, const std::string& text, int x, int y, HAlign halign, VAlign valign) {
if (text.find("\n", 0) != std::string::npos) {
std::vector<std::string> textArr;
split(textArr, text, "\n");
writeLine(surface, textArr, x, y, halign, valign);
} else
writeLine(surface, text, x, y, halign, valign);
}