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
parent
31fd64af0a
commit
9d2a035a71
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
128
src/glhelper.cpp
128
src/glhelper.cpp
|
@ -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)
|
||||
{
|
||||
int plane = chr / BitmapFontChunkSize,
|
||||
textureIndex = TEXTURE_BITMAP_FONT + plane;
|
||||
|
||||
if(BitmapFontCurrentChunk != textureIndex) {
|
||||
glEnd();
|
||||
|
||||
if(!BitmapFontChunkInitialized[plane]) {
|
||||
CreateBitmapFontChunk(CompressedFontTexture[plane].data,
|
||||
CompressedFontTexture[plane].length,
|
||||
textureIndex);
|
||||
BitmapFontChunkInitialized[plane] = true;
|
||||
} else {
|
||||
glBindTexture(GL_TEXTURE_2D, textureIndex);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
uint8_t b = (uint8_t)c;
|
||||
int w, h;
|
||||
|
||||
if(b & 0x80) {
|
||||
h = 16;
|
||||
if(chr >= 0xe000 && chr <= 0xefff) {
|
||||
// Special character, like a checkbox or a radio button
|
||||
w = h = 16;
|
||||
w = 16;
|
||||
x -= 3;
|
||||
} else if(CodepointProperties[chr].isWide) {
|
||||
// Wide (usually CJK or reserved) character
|
||||
w = 16;
|
||||
} else {
|
||||
// Normal character from our font
|
||||
w = SS.TW.CHAR_WIDTH,
|
||||
h = SS.TW.CHAR_HEIGHT;
|
||||
// Normal character
|
||||
w = 8;
|
||||
}
|
||||
|
||||
if(b != ' ' && b != 0) {
|
||||
int row = b / 4, col = b % 4;
|
||||
double s0 = col/4.0,
|
||||
s1 = (col+1)/4.0,
|
||||
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);
|
||||
|
|
|
@ -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] = '-';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, ...);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -250,21 +250,29 @@ 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++) {
|
||||
if(c >= MAX_COLS) goto done;
|
||||
text[r][c] = buf[i];
|
||||
meta[r][c].fg = fg;
|
||||
meta[r][c].bg = bg;
|
||||
meta[r][c].bgRgb = bgRgb;
|
||||
meta[r][c].link = link;
|
||||
meta[r][c].data = data;
|
||||
meta[r][c].f = f;
|
||||
meta[r][c].h = h;
|
||||
c++;
|
||||
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] = (i == 0) ? chr : ' ';
|
||||
meta[r][c].fg = fg;
|
||||
meta[r][c].bg = bg;
|
||||
meta[r][c].bgRgb = bgRgb;
|
||||
meta[r][c].link = link;
|
||||
meta[r][c].data = data;
|
||||
meta[r][c].f = f;
|
||||
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,8 +903,9 @@ void TextWindow::Paint(void) {
|
|||
}
|
||||
|
||||
// But don't underline checkboxes or radio buttons
|
||||
while((text[r][cs] & 0x80 || text[r][cs] == ' ') &&
|
||||
cs < cf)
|
||||
while(((text[r][cs] >= 0xe000 && text[r][cs] <= 0xefff) ||
|
||||
text[r][cs] == ' ') &&
|
||||
cs < cf)
|
||||
{
|
||||
cs++;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
12
src/ui.h
12
src/ui.h
|
@ -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 {
|
||||
|
|
24
src/util.cpp
24
src/util.cpp
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
font[codepoint] = 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 unsigned short *glyph = font[codepoint] != NULL ? font[codepoint] : replacement;
|
||||
for(int x = 15; x >= 0; x--) {
|
||||
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");
|
||||
|
||||
if(font[codepoint] != NULL)
|
||||
free(font[codepoint]);
|
||||
}
|
||||
fprintf(source, "\n");
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue