Rasterize non-ASCII glyphs in the UI.

Now it is possible to give non-ASCII names to groups
as well as see non-ASCII filenames of imported files.
In the future this makes localization possible.

This works for LTR languages, such as European and CJK,
but not RTL such as Arabic. Does Arabic even exist in
monospaced form? I have no idea.
pull/4/head
whitequark 2015-11-05 22:39:27 +03:00
parent 31fd64af0a
commit 9d2a035a71
14 changed files with 323 additions and 136 deletions

View File

@ -306,6 +306,7 @@ add_executable(solvespace WIN32 MACOSX_BUNDLE
target_link_libraries(solvespace
"${OPENGL_LIBRARIES}"
"${PNG_LIBRARIES}"
"${ZLIB_LIBRARIES}"
"${platform_LIBRARIES}")
if(WIN32 AND NOT MINGW)

View File

@ -237,25 +237,25 @@ void TextWindow::ShowConfiguration(void) {
&ScreenChangeExportOffset, 0);
Printf(false, "");
Printf(false, " %Fd%f%Ll%c export shaded 2d triangles%E",
Printf(false, " %Fd%f%Ll%s export shaded 2d triangles%E",
&ScreenChangeShadedTriangles,
SS.exportShadedTriangles ? CHECK_TRUE : CHECK_FALSE);
if(fabs(SS.exportOffset) > LENGTH_EPS) {
Printf(false, " %Fd%c curves as piecewise linear%E "
Printf(false, " %Fd%s curves as piecewise linear%E "
"(since cutter radius is not zero)", CHECK_TRUE);
} else {
Printf(false, " %Fd%f%Ll%c export curves as piecewise linear%E",
Printf(false, " %Fd%f%Ll%s export curves as piecewise linear%E",
&ScreenChangePwlCurves,
SS.exportPwlCurves ? CHECK_TRUE : CHECK_FALSE);
}
Printf(false, " %Fd%f%Ll%c fix white exported lines%E",
Printf(false, " %Fd%f%Ll%s fix white exported lines%E",
&ScreenChangeFixExportColors,
SS.fixExportColors ? CHECK_TRUE : CHECK_FALSE);
Printf(false, "");
Printf(false, "%Ft export canvas size: "
"%f%Fd%Lf%c fixed%E "
"%f%Fd%Lt%c auto%E",
"%f%Fd%Lf%s fixed%E "
"%f%Fd%Lt%s auto%E",
&ScreenChangeCanvasSizeAuto,
!SS.exportCanvasSizeAuto ? RADIO_TRUE : RADIO_FALSE,
&ScreenChangeCanvasSizeAuto,
@ -295,10 +295,10 @@ void TextWindow::ShowConfiguration(void) {
SS.MmToString(SS.gCode.plungeFeed), &ScreenChangeGCodeParameter);
Printf(false, "");
Printf(false, " %Fd%f%Ll%c draw triangle back faces in red%E",
Printf(false, " %Fd%f%Ll%s draw triangle back faces in red%E",
&ScreenChangeBackFaces,
SS.drawBackFaces ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %Fd%f%Ll%c check sketch for closed contour%E",
Printf(false, " %Fd%f%Ll%s check sketch for closed contour%E",
&ScreenChangeCheckClosedContour,
SS.checkClosedContour ? CHECK_TRUE : CHECK_FALSE);

View File

@ -306,7 +306,7 @@ void TextWindow::DescribeSelection(void) {
if(c->type == Constraint::DIAMETER) {
Printf(false, "%FtDIAMETER CONSTRAINT");
Printf(true, " %Fd%f%D%Ll%c show as radius",
Printf(true, " %Fd%f%D%Ll%s show as radius",
&ScreenConstraintShowAsRadius, gs.constraint[0],
c->other ? CHECK_TRUE : CHECK_FALSE);
} else {

View File

@ -213,7 +213,7 @@ void SolveSpaceUI::GenerateAll(int first, int last, bool andFindFree) {
glColor3d(0.0, 0.0, 0.0);
ssglAxisAlignedLineLoop(left, left+width, top, top-height);
ssglCreateBitmapFont();
ssglInitializeBitmapFont();
glColor3d(0, 0, 0);
glPushMatrix();
glTranslated(left+8, top-20, 0);

View File

@ -3,6 +3,7 @@
//
// Copyright 2008-2013 Jonathan Westhues.
//-----------------------------------------------------------------------------
#include <zlib.h>
#include "solvespace.h"
namespace SolveSpace {
@ -506,58 +507,126 @@ void ssglDepthRangeLockToFront(bool yes)
}
}
void ssglCreateBitmapFont(void)
{
// Place the font in our texture in a two-dimensional grid; 1d would
// be simpler, but long skinny textures (256*16 = 4096 pixels wide)
// won't work.
static uint8_t MappedTexture[4*16*64*16];
int a, i;
for(a = 0; a < 256; a++) {
int row = a / 4, col = a % 4;
const int BitmapFontChunkSize = 64 * 64;
static bool BitmapFontChunkInitialized[0x10000 / BitmapFontChunkSize];
static int BitmapFontCurrentChunk = -1;
for(i = 0; i < 16; i++) {
memcpy(MappedTexture + row*4*16*16 + col*16 + i*4*16,
FontTexture + a*16*16 + i*16,
static void CreateBitmapFontChunk(const uint8_t *source, size_t sourceLength,
int textureIndex)
{
// Place the font in our texture in a two-dimensional grid.
// The maximum texture size that is reasonably supported is 1024x1024.
const size_t fontTextureSize = BitmapFontChunkSize*16*16;
uint8_t *fontTexture = (uint8_t *)malloc(fontTextureSize),
*mappedTexture = (uint8_t *)malloc(fontTextureSize);
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
if(inflateInit(&stream) != Z_OK)
oops();
stream.next_in = (Bytef *)source;
stream.avail_in = sourceLength;
stream.next_out = fontTexture;
stream.avail_out = fontTextureSize;
if(inflate(&stream, Z_NO_FLUSH) != Z_STREAM_END)
oops();
if(stream.avail_out != 0)
oops();
inflateEnd(&stream);
for(int a = 0; a < BitmapFontChunkSize; a++) {
int row = a / 64, col = a % 64;
for(int i = 0; i < 16; i++) {
memcpy(mappedTexture + row*64*16*16 + col*16 + i*64*16,
fontTexture + a*16*16 + i*16,
16);
}
}
glBindTexture(GL_TEXTURE_2D, TEXTURE_BITMAP_FONT);
free(fontTexture);
glBindTexture(GL_TEXTURE_2D, textureIndex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
16*4, 64*16,
16*64, 64*16,
0,
GL_ALPHA, GL_UNSIGNED_BYTE,
MappedTexture);
mappedTexture);
free(mappedTexture);
}
void ssglBitmapCharQuad(char c, double x, double y)
static void SwitchToBitmapFontChunkFor(char32_t chr)
{
uint8_t b = (uint8_t)c;
int w, h;
int plane = chr / BitmapFontChunkSize,
textureIndex = TEXTURE_BITMAP_FONT + plane;
if(b & 0x80) {
// Special character, like a checkbox or a radio button
w = h = 16;
x -= 3;
if(BitmapFontCurrentChunk != textureIndex) {
glEnd();
if(!BitmapFontChunkInitialized[plane]) {
CreateBitmapFontChunk(CompressedFontTexture[plane].data,
CompressedFontTexture[plane].length,
textureIndex);
BitmapFontChunkInitialized[plane] = true;
} else {
// Normal character from our font
w = SS.TW.CHAR_WIDTH,
h = SS.TW.CHAR_HEIGHT;
glBindTexture(GL_TEXTURE_2D, textureIndex);
}
if(b != ' ' && b != 0) {
int row = b / 4, col = b % 4;
double s0 = col/4.0,
s1 = (col+1)/4.0,
BitmapFontCurrentChunk = textureIndex;
glBegin(GL_QUADS);
}
}
void ssglInitializeBitmapFont()
{
memset(BitmapFontChunkInitialized, 0, sizeof(BitmapFontChunkInitialized));
BitmapFontCurrentChunk = -1;
}
int ssglBitmapCharWidth(char32_t chr) {
if(!CodepointProperties[chr].exists)
oops();
return CodepointProperties[chr].isWide ? 2 : 1;
}
void ssglBitmapCharQuad(char32_t chr, double x, double y)
{
int w, h;
h = 16;
if(chr >= 0xe000 && chr <= 0xefff) {
// Special character, like a checkbox or a radio button
w = 16;
x -= 3;
} else if(CodepointProperties[chr].isWide) {
// Wide (usually CJK or reserved) character
w = 16;
} else {
// Normal character
w = 8;
}
if(chr != ' ' && chr != 0) {
int n = chr % BitmapFontChunkSize;
int row = n / 64, col = n % 64;
double s0 = col/64.0,
s1 = (col+1)/64.0,
t0 = row/64.0,
t1 = t0 + (w/16.0)/64;
SwitchToBitmapFontChunkFor(chr);
glTexCoord2d(s1, t0);
glVertex2d(x, y);
@ -577,10 +646,11 @@ void ssglBitmapText(const char *str, Vector p)
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS);
while(*str) {
ssglBitmapCharQuad(*str, p.x, p.y);
char32_t chr;
str = ReadUTF8(str, &chr);
str++;
p.x += SS.TW.CHAR_WIDTH;
ssglBitmapCharQuad(chr, p.x, p.y);
p.x += 8 * ssglBitmapCharWidth(chr);
}
glEnd();
glDisable(GL_TEXTURE_2D);

View File

@ -205,10 +205,8 @@ void Group::MenuGroup(int id) {
groupName.erase(pos);
for(int i = 0; i < groupName.length(); i++) {
if(isalnum(groupName[i])) {
// do nothing, valid character
} else {
// convert invalid characters (like spaces) to dashes
if(!(isalnum(groupName[i]) || (unsigned)groupName[i] >= 0x80)) {
// convert punctuation to dashes
groupName[i] = '-';
}
}

View File

@ -331,14 +331,15 @@ void ssglColorRGBa(RgbaColor rgb, double a);
void ssglDepthRangeOffset(int units);
void ssglDepthRangeLockToFront(bool yes);
void ssglDrawPixelsWithTexture(uint8_t *data, int w, int h);
void ssglCreateBitmapFont(void);
void ssglInitializeBitmapFont();
void ssglBitmapText(const char *str, Vector p);
void ssglBitmapCharQuad(char c, double x, double y);
void ssglBitmapCharQuad(char32_t chr, double x, double y);
int ssglBitmapCharWidth(char32_t chr);
#define TEXTURE_BACKGROUND_IMG 10
#define TEXTURE_BITMAP_FONT 20
#define TEXTURE_DRAW_PIXELS 30
#define TEXTURE_COLOR_PICKER_2D 40
#define TEXTURE_COLOR_PICKER_1D 50
#define TEXTURE_DRAW_PIXELS 20
#define TEXTURE_COLOR_PICKER_2D 30
#define TEXTURE_COLOR_PICKER_1D 40
#define TEXTURE_BITMAP_FONT 50
#define arraylen(x) (sizeof((x))/sizeof((x)[0]))
@ -348,6 +349,7 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14,
double a31, double a32, double a33, double a34,
double a41, double a42, double a43, double a44);
bool MakeAcceleratorLabel(int accel, char *out);
const char *ReadUTF8(const char *str, char32_t *chr);
bool StringAllPrintable(const char *str);
bool FilenameHasExtension(const std::string &str, const char *ext);
void Message(const char *str, ...);

View File

@ -695,8 +695,8 @@ bool TextWindow::EditControlDoneForStyles(const char *str) {
break;
}
case EDIT_STYLE_NAME:
if(!StringAllPrintable(str) || !*str) {
Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -");
if(!*str) {
Error("Style name cannot be empty");
} else {
SS.UndoRemember();
s = Style::Get(edit.style);
@ -760,8 +760,8 @@ void TextWindow::ShowStyleInfo(void) {
Printf(false,"%Ba %Ftin units of %Fdpixels%E");
} else {
Printf(false,"%Ba %Ftin units of %Fd"
"%D%f%LW%c pixels%E "
"%D%f%Lw%c %s",
"%D%f%LW%s pixels%E "
"%D%f%Lw%s %s",
s->h.v, &ScreenChangeStyleYesNo,
widthpx ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
@ -780,7 +780,7 @@ void TextWindow::ShowStyleInfo(void) {
s->fillColor.redF(), s->fillColor.greenF(), s->fillColor.blueF(),
s->h.v, ScreenChangeStyleColor);
Printf(false, "%Bd %D%f%Lf%c contours are filled%E",
Printf(false, "%Bd %D%f%Lf%s contours are filled%E",
s->h.v, &ScreenChangeStyleYesNo,
s->filled ? CHECK_TRUE : CHECK_FALSE);
}
@ -807,8 +807,8 @@ void TextWindow::ShowStyleInfo(void) {
Printf(false,"%Bd %Ftin units of %Fdpixels");
} else {
Printf(false,"%Bd %Ftin units of %Fd"
"%D%f%LG%c pixels%E "
"%D%f%Lg%c %s",
"%D%f%LG%s pixels%E "
"%D%f%Lg%s %s",
s->h.v, &ScreenChangeStyleYesNo,
textHeightpx ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
@ -826,9 +826,9 @@ void TextWindow::ShowStyleInfo(void) {
bool neither;
neither = !(s->textOrigin & (Style::ORIGIN_LEFT | Style::ORIGIN_RIGHT));
Printf(false, "%Ba "
"%D%f%LL%c left%E "
"%D%f%LH%c center%E "
"%D%f%LR%c right%E ",
"%D%f%LL%s left%E "
"%D%f%LH%s center%E "
"%D%f%LR%s right%E ",
s->h.v, &ScreenChangeStyleYesNo,
(s->textOrigin & Style::ORIGIN_LEFT) ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
@ -838,9 +838,9 @@ void TextWindow::ShowStyleInfo(void) {
neither = !(s->textOrigin & (Style::ORIGIN_BOT | Style::ORIGIN_TOP));
Printf(false, "%Bd "
"%D%f%LB%c bottom%E "
"%D%f%LV%c center%E "
"%D%f%LT%c top%E ",
"%D%f%LB%s bottom%E "
"%D%f%LV%s center%E "
"%D%f%LT%s top%E ",
s->h.v, &ScreenChangeStyleYesNo,
(s->textOrigin & Style::ORIGIN_BOT) ? RADIO_TRUE : RADIO_FALSE,
s->h.v, &ScreenChangeStyleYesNo,
@ -852,11 +852,11 @@ void TextWindow::ShowStyleInfo(void) {
if(s->h.v >= Style::FIRST_CUSTOM) {
Printf(false, "");
Printf(false, " %Fd%D%f%Lv%c show these objects on screen%E",
Printf(false, " %Fd%D%f%Lv%s show these objects on screen%E",
s->h.v, &ScreenChangeStyleYesNo,
s->visible ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %Fd%D%f%Le%c export these objects%E",
Printf(false, " %Fd%D%f%Le%s export these objects%E",
s->h.v, &ScreenChangeStyleYesNo,
s->exportable ? CHECK_TRUE : CHECK_FALSE);

View File

@ -97,10 +97,10 @@ void TextWindow::ScreenGoToWebsite(int link, uint32_t v) {
OpenWebsite("http://solvespace.com/txtlink");
}
void TextWindow::ShowListOfGroups(void) {
char radioTrue[] = { ' ', (char)RADIO_TRUE, ' ', 0 },
radioFalse[] = { ' ', (char)RADIO_FALSE, ' ', 0 },
checkTrue[] = { ' ', (char)CHECK_TRUE, ' ', 0 },
checkFalse[] = { ' ', (char)CHECK_FALSE, ' ', 0 };
const char *radioTrue = " " RADIO_TRUE " ",
*radioFalse = " " RADIO_FALSE " ",
*checkTrue = " " CHECK_TRUE " ",
*checkFalse = " " CHECK_FALSE " ";
Printf(true, "%Ft active");
Printf(false, "%Ft shown ok group-name%E");
@ -304,8 +304,8 @@ void TextWindow::ShowGroupInfo(void) {
bool one = (g->subtype == Group::ONE_SIDED);
Printf(false,
"%Ba %f%Ls%Fd%c one-sided%E "
"%f%LS%Fd%c two-sided%E",
"%Ba %f%Ls%Fd%s one-sided%E "
"%f%LS%Fd%s two-sided%E",
&TextWindow::ScreenChangeGroupOption,
one ? RADIO_TRUE : RADIO_FALSE,
&TextWindow::ScreenChangeGroupOption,
@ -315,8 +315,8 @@ void TextWindow::ShowGroupInfo(void) {
if(g->subtype == Group::ONE_SIDED) {
bool skip = g->skipFirst;
Printf(false,
"%Bd %Ftstart %f%LK%Fd%c with original%E "
"%f%Lk%Fd%c with copy #1%E",
"%Bd %Ftstart %f%LK%Fd%s with original%E "
"%f%Lk%Fd%s with copy #1%E",
&ScreenChangeGroupOption,
!skip ? RADIO_TRUE : RADIO_FALSE,
&ScreenChangeGroupOption,
@ -354,9 +354,9 @@ void TextWindow::ShowGroupInfo(void) {
bool asa = (g->type == Group::IMPORTED);
Printf(false, " %Ftsolid model as");
Printf(false, "%Ba %f%D%Lc%Fd%c union%E "
"%f%D%Lc%Fd%c difference%E "
"%f%D%Lc%Fd%c%s%E ",
Printf(false, "%Ba %f%D%Lc%Fd%s union%E "
"%f%D%Lc%Fd%s difference%E "
"%f%D%Lc%Fd%s%s%E ",
&TextWindow::ScreenChangeGroupOption,
Group::COMBINE_AS_UNION,
un ? RADIO_TRUE : RADIO_FALSE,
@ -365,7 +365,7 @@ void TextWindow::ShowGroupInfo(void) {
diff ? RADIO_TRUE : RADIO_FALSE,
&TextWindow::ScreenChangeGroupOption,
Group::COMBINE_AS_ASSEMBLE,
asa ? (asy ? RADIO_TRUE : RADIO_FALSE) : 0,
asa ? (asy ? RADIO_TRUE : RADIO_FALSE) : " ",
asa ? " assemble" : "");
if(g->type == Group::EXTRUDE ||
@ -381,7 +381,7 @@ void TextWindow::ShowGroupInfo(void) {
&TextWindow::ScreenOpacity);
} else if(g->type == Group::IMPORTED) {
bool sup = g->suppress;
Printf(false, " %Fd%f%LP%c suppress this group's solid model",
Printf(false, " %Fd%f%LP%s suppress this group's solid model",
&TextWindow::ScreenChangeGroupOption,
g->suppress ? CHECK_TRUE : CHECK_FALSE);
}
@ -389,24 +389,24 @@ void TextWindow::ShowGroupInfo(void) {
Printf(false, "");
}
Printf(false, " %f%Lv%Fd%c show entities from this group",
Printf(false, " %f%Lv%Fd%s show entities from this group",
&TextWindow::ScreenChangeGroupOption,
g->visible ? CHECK_TRUE : CHECK_FALSE);
Group *pg; pg = g->PreviousGroup();
if(pg && pg->runningMesh.IsEmpty() && g->thisMesh.IsEmpty()) {
Printf(false, " %f%Lf%Fd%c force NURBS surfaces to triangle mesh",
Printf(false, " %f%Lf%Fd%s force NURBS surfaces to triangle mesh",
&TextWindow::ScreenChangeGroupOption,
g->forceToMesh ? CHECK_TRUE : CHECK_FALSE);
} else {
Printf(false, " (model already forced to triangle mesh)");
}
Printf(true, " %f%Lr%Fd%c relax constraints and dimensions",
Printf(true, " %f%Lr%Fd%s relax constraints and dimensions",
&TextWindow::ScreenChangeGroupOption,
g->relaxConstraints ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %f%Ld%Fd%c treat all dimensions as reference",
Printf(false, " %f%Ld%Fd%s treat all dimensions as reference",
&TextWindow::ScreenChangeGroupOption,
g->allDimsReference ? CHECK_TRUE : CHECK_FALSE);
@ -604,10 +604,10 @@ void TextWindow::ShowTangentArc(void) {
}
Printf(false, "");
Printf(false, " %Fd%f%La%c choose radius automatically%E",
Printf(false, " %Fd%f%La%s choose radius automatically%E",
&ScreenChangeTangentArc,
!SS.tangentArcManual ? CHECK_TRUE : CHECK_FALSE);
Printf(false, " %Fd%f%Ld%c delete original entities afterward%E",
Printf(false, " %Fd%f%Ld%s delete original entities afterward%E",
&ScreenChangeTangentArc,
SS.tangentArcDeleteOld ? CHECK_TRUE : CHECK_FALSE);
@ -665,8 +665,8 @@ void TextWindow::EditControlDone(const char *s) {
break;
}
case EDIT_GROUP_NAME: {
if(!StringAllPrintable(s) || !*s) {
Error("Invalid characters. Allowed are: A-Z a-z 0-9 _ -");
if(!*s) {
Error("Group name cannot be empty");
} else {
SS.UndoRemember();

View File

@ -250,13 +250,20 @@ void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
break;
}
} else {
buf[0] = *fmt;
buf[1] = '\0';
char32_t chr;
const char *fmtNext = ReadUTF8(fmt, &chr);
strncpy(buf, fmt, fmtNext - fmt);
buf[fmtNext - fmt] = '\0';
}
for(unsigned i = 0; i < strlen(buf); i++) {
const char *bufIter = buf;
while(*bufIter) {
char32_t chr;
bufIter = ReadUTF8(bufIter, &chr);
for(int i = 0; i < ssglBitmapCharWidth(chr); i++) {
if(c >= MAX_COLS) goto done;
text[r][c] = buf[i];
text[r][c] = (i == 0) ? chr : ' ';
meta[r][c].fg = fg;
meta[r][c].bg = bg;
meta[r][c].bgRgb = bgRgb;
@ -266,6 +273,7 @@ void TextWindow::Printf(bool halfLine, const char *fmt, ...) {
meta[r][c].h = h;
c++;
}
}
fmt++;
}
@ -449,7 +457,7 @@ void TextWindow::DrawOrHitTestIcons(int how, double mx, double my)
ox = min(ox, (double) (width - 25) - tw);
oy = max(oy, 5.0);
ssglCreateBitmapFont();
ssglInitializeBitmapFont();
glLineWidth(1);
glColor4d(1.0, 1.0, 0.6, 1.0);
ssglAxisAlignedQuad(ox, ox+tw, oy, oy+LINE_HEIGHT);
@ -833,7 +841,7 @@ void TextWindow::Paint(void) {
glBegin(GL_QUADS);
} else if(a == 1) {
glEnable(GL_TEXTURE_2D);
ssglCreateBitmapFont();
ssglInitializeBitmapFont();
glBegin(GL_QUADS);
}
@ -895,7 +903,8 @@ void TextWindow::Paint(void) {
}
// But don't underline checkboxes or radio buttons
while((text[r][cs] & 0x80 || text[r][cs] == ' ') &&
while(((text[r][cs] >= 0xe000 && text[r][cs] <= 0xefff) ||
text[r][cs] == ' ') &&
cs < cf)
{
cs++;

View File

@ -204,7 +204,7 @@ bool GraphicsWindow::ToolbarDrawOrHitTest(int mx, int my,
if(paint) {
// Do this last so that nothing can draw over it.
if(toolTip.show) {
ssglCreateBitmapFont();
ssglInitializeBitmapFont();
char str[1024];
if(strlen(toolTip.str) >= 200) oops();
strcpy(str, toolTip.str);

View File

@ -31,17 +31,17 @@ public:
CHAR_HEIGHT = 16,
LINE_HEIGHT = 20,
LEFT_MARGIN = 6,
CHECK_FALSE = 0x80,
CHECK_TRUE = 0x81,
RADIO_FALSE = 0x82,
RADIO_TRUE = 0x83
};
#define CHECK_FALSE "\xEE\x80\x80" // U+E000
#define CHECK_TRUE "\xEE\x80\x81"
#define RADIO_FALSE "\xEE\x80\x82"
#define RADIO_TRUE "\xEE\x80\x83"
int scrollPos; // The scrollbar position, in half-row units
int halfRows; // The height of our window, in half-row units
uint8_t text[MAX_ROWS][MAX_COLS];
uint32_t text[MAX_ROWS][MAX_COLS];
typedef void LinkFunction(int link, uint32_t v);
enum { NOT_A_LINK = 0 };
struct {

View File

@ -6,6 +6,30 @@
//-----------------------------------------------------------------------------
#include "solvespace.h"
// See https://github.com/GNOME/glibmm/blob/2fbd9f23/glib/glibmm/ustring.cc#L227
const char *SolveSpace::ReadUTF8(const char *str, char32_t *result)
{
*result = (unsigned char) *str;
if((*result & 0x80) != 0)
{
unsigned int mask = 0x40;
do
{
*result <<= 6;
const unsigned int c = (unsigned char) (*++str);
mask <<= 5;
*result += c - 0x80;
}
while((*result & mask) != 0);
*result &= mask - 1;
}
return str + 1;
}
bool SolveSpace::StringAllPrintable(const char *str)
{
const char *t;

View File

@ -3,10 +3,15 @@
#include <stdlib.h>
#include <zlib.h>
#include <png.h>
#include <map>
#define die(msg) do { fprintf(stderr, "%s\n", msg); abort(); } while(0)
#ifdef NDEBUG
#define COMPRESSION_LEVEL 9
#else
#define COMPRESSION_LEVEL 5
#endif
unsigned short* read_png(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp)
@ -63,8 +68,7 @@ unsigned short* read_png(const char *filename) {
b = image[y][x + 2];
if(r + g + b >= 11) {
int pos = y * width + (width - x / 3);
glyph[pos / 16] |= 1 << (pos % 16);
glyph[y] |= 1 << (width - x / 3);
}
}
}
@ -75,6 +79,8 @@ unsigned short* read_png(const char *filename) {
fclose(fp);
png_destroy_read_struct(&png, &png_info, NULL);
return glyph;
}
@ -85,20 +91,31 @@ const static unsigned short replacement[16] = {
0xAAAA, 0xAAAA, 0xAAAA, 0xAAAA,
};
struct CodepointProperties {
bool exists:1;
bool isWide:1;
};
int main(int argc, char** argv) {
if(argc < 3) {
fprintf(stderr, "Usage: %s <header/source out> <unifont.hex> <png glyph>...\n"
" where <png glyph>s are mapped into private use area\n"
" starting at U+0080.\n",
" starting at U+E000.\n",
argv[0]);
return 1;
}
unsigned short *font[256] = {};
const int codepoint_count = 0x10000;
unsigned short **font =
(unsigned short **)calloc(sizeof(unsigned short*), codepoint_count);
CodepointProperties *properties =
(CodepointProperties *)calloc(sizeof(CodepointProperties), codepoint_count);
const int private_start = 0x80, private_count = argc - 3;
const int private_start = 0xE000;
for(int i = 3; i < argc; i++) {
font[private_start + i - 3] = read_png(argv[i]);
int codepoint = private_start + i - 3;
font[codepoint] = read_png(argv[i]);
properties[codepoint].exists = true;
}
gzFile unifont = gzopen(argv[2], "rb");
@ -106,9 +123,6 @@ int main(int argc, char** argv) {
die("unifont fopen failed");
while(1) {
unsigned short codepoint;
unsigned short *glyph = (unsigned short *) calloc(32, 1);
char buf[100];
if(!gzgets(unifont, buf, sizeof(buf))){
if(gzeof(unifont)) {
@ -118,6 +132,9 @@ int main(int argc, char** argv) {
}
}
unsigned short codepoint;
unsigned short *glyph = (unsigned short *) calloc(32, 1);
bool isWide;
if( sscanf(buf, "%4hx:%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx"
"%4hx%4hx%4hx%4hx%4hx%4hx%4hx%4hx\n",
&codepoint,
@ -126,6 +143,7 @@ int main(int argc, char** argv) {
&glyph[8], &glyph[9], &glyph[10], &glyph[11],
&glyph[12], &glyph[13], &glyph[14], &glyph[15]) == 17) {
/* read 16x16 character */
isWide = true;
} else if(sscanf(buf, "%4hx:%2hx%2hx%2hx%2hx%2hx%2hx%2hx%2hx"
"%2hx%2hx%2hx%2hx%2hx%2hx%2hx%2hx\n",
&codepoint,
@ -136,15 +154,14 @@ int main(int argc, char** argv) {
/* read 8x16 character */
for(int i = 0; i < 16; i++)
glyph[i] <<= 8;
isWide = false;
} else {
die("parse unifont character");
}
if(codepoint >= 0x00 && codepoint < 0x80) {
font[codepoint] = glyph;
} else {
free(glyph);
}
properties[codepoint].exists = true;
properties[codepoint].isWide = isWide;
}
gzclose(unifont);
@ -153,26 +170,92 @@ int main(int argc, char** argv) {
if(!source)
die("source fopen failed");
fprintf(source, "/**** This is a generated file - do not edit ****/\n\n");
fprintf(source, "static const unsigned char FontTexture[256 * 16 * 16] = {\n");
const int chunk_size = 64 * 64,
chunks = codepoint_count / chunk_size;
for(int codepoint = 0; codepoint < 0x100; codepoint++) {
const int chunk_input_size = chunk_size * 16 * 16;
unsigned int chunk_output_size[chunks] = {};
unsigned char *chunk_data = (unsigned char *)calloc(1, chunk_input_size);
unsigned int chunk_data_index;
fprintf(source, "/**** This is a generated file - do not edit ****/\n\n");
for(int chunk_index = 0; chunk_index < chunks; chunk_index++) {
chunk_data_index = 0;
const int chunk_start = chunk_index * chunk_size;
for(int codepoint = chunk_start; codepoint < chunk_start + chunk_size; codepoint++) {
const unsigned short *glyph = font[codepoint] != NULL ? font[codepoint] : replacement;
for(int x = 15; x >= 0; x--) {
for(int y = 0; y < 16; y++) {
int pos = y * 16 + x;
if(glyph[pos / 16] & (1 << (pos % 16))) {
fprintf(source, "255, ");
} else {
fprintf(source, " 0, ");
chunk_data[chunk_data_index++] = (glyph[y] & (1 << x)) ? 0xff : 0;
}
}
fprintf(source, "\n");
}
fprintf(source, "\n");
}
if(font[codepoint] != NULL)
free(font[codepoint]);
}
fprintf(source, "static const uint8_t CompressedFontTextureChunk%d[] = {\n",
chunk_start / chunk_size);
z_stream stream;
stream.zalloc = Z_NULL;
stream.zfree = Z_NULL;
stream.opaque = Z_NULL;
if(deflateInit(&stream, COMPRESSION_LEVEL) != Z_OK)
die("deflateInit failed");
stream.next_in = chunk_data;
stream.avail_in = chunk_input_size;
do {
unsigned char compressed_chunk_data[16384] = {};
stream.next_out = compressed_chunk_data;
stream.avail_out = sizeof(compressed_chunk_data);
deflate(&stream, Z_FINISH);
chunk_output_size[chunk_index] += sizeof(compressed_chunk_data) - stream.avail_out;
for(int i = 0; i < sizeof(compressed_chunk_data) - stream.avail_out; i += 16) {
unsigned char *d = &compressed_chunk_data[i];
fprintf(source, " %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d, "
"%3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\n",
d[ 0], d[ 1], d[ 2], d[ 3], d[ 4], d[ 5], d[ 6], d[ 7],
d[ 8], d[ 9], d[10], d[11], d[12], d[13], d[14], d[15]);
}
} while(stream.avail_out == 0);
deflateEnd(&stream);
fprintf(source, "};\n\n");
}
free(chunk_data);
free(font);
fprintf(source, "static const struct {\n"
" size_t length;"
" const uint8_t *data;"
"} CompressedFontTexture[%d] = {\n", chunks);
for(int i = 0; i < chunks; i++) {
fprintf(source, " { %d, CompressedFontTextureChunk%d },\n",
chunk_output_size[i], i);
}
fprintf(source, "};\n\n");
fprintf(source, "struct GlyphProperties {\n"
" bool exists:1;\n"
" bool isWide:1;\n"
"} CodepointProperties[%d] = {\n", codepoint_count);
for(int i = 0; i < codepoint_count; i++) {
fprintf(source, " { %s, %s },\n",
properties[i].exists ? "true" : "false",
properties[i].isWide ? "true" : "false");
}
fprintf(source, "};\n");
free(properties);
fclose(source);
}