📄 platgtk.cxx
字号:
}
void SurfaceImpl::FillRectangle(PRectangle rc, Surface &surfacePattern) {
if (static_cast<SurfaceImpl &>(surfacePattern).drawable) {
// Tile pattern over rectangle
// Currently assumes 8x8 pattern
int widthPat = 8;
int heightPat = 8;
for (int xTile = rc.left; xTile < rc.right; xTile += widthPat) {
int widthx = (xTile + widthPat > rc.right) ? rc.right - xTile : widthPat;
for (int yTile = rc.top; yTile < rc.bottom; yTile += heightPat) {
int heighty = (yTile + heightPat > rc.bottom) ? rc.bottom - yTile : heightPat;
gdk_draw_pixmap(drawable,
gc,
static_cast<SurfaceImpl &>(surfacePattern).drawable,
0, 0,
xTile, yTile,
widthx, heighty);
}
}
} else {
// Something is wrong so try to show anyway
// Shows up black because colour not allocated
FillRectangle(rc, ColourAllocated(0));
}
}
void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
if (((rc.right - rc.left) > 4) && ((rc.bottom - rc.top) > 4)) {
// Approximate a round rect with some cut off corners
Point pts[] = {
Point(rc.left + 2, rc.top),
Point(rc.right - 2, rc.top),
Point(rc.right, rc.top + 2),
Point(rc.right, rc.bottom - 2),
Point(rc.right - 2, rc.bottom),
Point(rc.left + 2, rc.bottom),
Point(rc.left, rc.bottom - 2),
Point(rc.left, rc.top + 2),
};
Polygon(pts, sizeof(pts) / sizeof(pts[0]), fore, back);
} else {
RectangleDraw(rc, fore, back);
}
}
void SurfaceImpl::Ellipse(PRectangle rc, ColourAllocated fore, ColourAllocated back) {
PenColour(back);
gdk_draw_arc(drawable, gc, 1,
rc.left + 1, rc.top + 1,
rc.right - rc.left - 2, rc.bottom - rc.top - 2,
0, 32767);
// The subtraction of 1 here is similar to the case for RectangleDraw
PenColour(fore);
gdk_draw_arc(drawable, gc, 0,
rc.left, rc.top,
rc.right - rc.left - 1, rc.bottom - rc.top - 1,
0, 32767);
}
void SurfaceImpl::Copy(PRectangle rc, Point from, Surface &surfaceSource) {
if (static_cast<SurfaceImpl &>(surfaceSource).drawable) {
gdk_draw_pixmap(drawable,
gc,
static_cast<SurfaceImpl &>(surfaceSource).drawable,
from.x, from.y,
rc.left, rc.top,
rc.right - rc.left, rc.bottom - rc.top);
}
}
static size_t UTF8Len(char ch) {
unsigned char uch = static_cast<unsigned char>(ch);
if (uch < 0x80)
return 1;
else if (uch < (0x80 + 0x40 + 0x20))
return 2;
else
return 3;
}
#ifdef USE_PANGO
static char *UTF8FromIconv(iconv_t iconvh, const char *s, int len) {
if (iconvh != ((iconv_t)(-1))) {
char *utfForm = new char[len*3+1];
char *pin = const_cast<char *>(s);
size_t inLeft = len;
char *pout = utfForm;
size_t outLeft = len*3+1;
size_t conversions = iconv(iconvh, &pin, &inLeft, &pout, &outLeft);
if (conversions != ((size_t)(-1))) {
*pout = '\0';
return utfForm;
}
delete []utfForm;
}
return 0;
}
static char *UTF8FromLatin1(const char *s, int len) {
char *utfForm = new char[len*2+1];
size_t lenU = 0;
for (int i=0;i<len;i++) {
unsigned int uch = static_cast<unsigned char>(s[i]);
if (uch < 0x80) {
utfForm[lenU++] = uch;
} else {
utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
}
}
utfForm[lenU] = '\0';
return utfForm;
}
static char *UTF8FromGdkWChar(GdkWChar *wctext, int wclen) {
char *utfForm = new char[wclen*3+1]; // Maximum of 3 UTF-8 bytes per character
size_t lenU = 0;
for (int i = 0; i < wclen && wctext[i]; i++) {
unsigned int uch = wctext[i];
if (uch < 0x80) {
utfForm[lenU++] = static_cast<char>(uch);
} else if (uch < 0x800) {
utfForm[lenU++] = static_cast<char>(0xC0 | (uch >> 6));
utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
} else {
utfForm[lenU++] = static_cast<char>(0xE0 | (uch >> 12));
utfForm[lenU++] = static_cast<char>(0x80 | ((uch >> 6) & 0x3f));
utfForm[lenU++] = static_cast<char>(0x80 | (uch & 0x3f));
}
}
utfForm[lenU] = '\0';
return utfForm;
}
static char *UTF8FromDBCS(const char *s, int len) {
GdkWChar *wctext = new GdkWChar[len + 1];
GdkWChar *wcp = wctext;
int wclen = gdk_mbstowcs(wcp, s, len);
if (wclen < 1) {
// In the annoying case when non-locale chars in the line.
// e.g. latin1 chars in Japanese locale.
delete []wctext;
return 0;
}
char *utfForm = UTF8FromGdkWChar(wctext, wclen);
delete []wctext;
return utfForm;
}
#endif
// On GTK+, wchar_t is 4 bytes
const int maxLengthTextRun = 10000;
void SurfaceImpl::DrawTextBase(PRectangle rc, Font &font_, int ybase, const char *s, int len,
ColourAllocated fore) {
PenColour(fore);
if (gc && drawable) {
int x = rc.left;
#ifdef USE_PANGO
if (PFont(font_)->pfd) {
char *utfForm = 0;
bool useGFree = false;
if (et == UTF8) {
pango_layout_set_text(layout, s, len);
} else {
if (et == dbcs) {
// Convert to utf8
utfForm = UTF8FromDBCS(s, len);
}
if (!utfForm) { // DBCS failed so treat as iconv
SetIconv(PFont(font_)->characterSet);
utfForm = UTF8FromIconv(iconvh, s, len);
}
//~ if (!utfForm) { // DBCS failed so treat as locale
//~ gsize w; // stub
//~ utfForm = g_locale_to_utf8(s, len, NULL, &w, NULL);
//~ useGFree = static_cast<bool>(utfForm);
//~ };
if (!utfForm) { // g_locale_to_utf8 failed so treat as Latin1
utfForm = UTF8FromLatin1(s, len);
}
pango_layout_set_text(layout, utfForm, strlen(utfForm));
}
pango_layout_set_font_description(layout, PFont(font_)->pfd);
PangoLayoutLine *pll = pango_layout_get_line(layout,0);
gdk_draw_layout_line(drawable, gc, x, ybase, pll);
if (useGFree) {
g_free(utfForm);
} else {
delete []utfForm;
}
return;
}
#endif
// Draw text as a series of segments to avoid limitations in X servers
const int segmentLength = 1000;
bool draw8bit = true;
if (et != singleByte) {
GdkWChar wctext[maxLengthTextRun];
int wclen;
if (et == UTF8) {
wclen = UCS2FromUTF8(s, len,
reinterpret_cast<wchar_t *>(wctext), maxLengthTextRun - 1);
} else { // dbcs, so convert using current locale
wclen = gdk_mbstowcs(
wctext, s, maxLengthTextRun - 1);
}
if (wclen > 0) {
draw8bit = false;
wctext[wclen] = L'\0';
GdkWChar *wcp = wctext;
while ((wclen > 0) && (x < maxCoordinate)) {
int lenDraw = Platform::Minimum(wclen, segmentLength);
gdk_draw_text_wc(drawable, PFont(font_)->pfont, gc,
x, ybase, wcp, lenDraw);
wclen -= lenDraw;
wcp += lenDraw;
if (wclen > 0) { // Avoid next calculation if possible as may be expensive
x += gdk_text_width_wc(PFont(font_)->pfont,
wcp, lenDraw);
}
}
}
}
if (draw8bit) {
while ((len > 0) && (x < maxCoordinate)) {
int lenDraw = Platform::Minimum(len, segmentLength);
gdk_draw_text(drawable, PFont(font_)->pfont, gc,
x, ybase, s, lenDraw);
len -= lenDraw;
s += lenDraw;
if (len > 0) { // Avoid next calculation if possible as may be expensive
x += gdk_text_width(PFont(font_)->pfont, s, lenDraw);
}
}
}
}
}
void SurfaceImpl::DrawTextNoClip(PRectangle rc, Font &font_, int ybase, const char *s, int len,
ColourAllocated fore, ColourAllocated back) {
FillRectangle(rc, back);
DrawTextBase(rc, font_, ybase, s, len, fore);
}
// On GTK+, exactly same as DrawTextNoClip
void SurfaceImpl::DrawTextClipped(PRectangle rc, Font &font_, int ybase, const char *s, int len,
ColourAllocated fore, ColourAllocated back) {
FillRectangle(rc, back);
DrawTextBase(rc, font_, ybase, s, len, fore);
}
void SurfaceImpl::DrawTextTransparent(PRectangle rc, Font &font_, int ybase, const char *s, int len,
ColourAllocated fore) {
// Avoid drawing spaces in transparent mode
for (int i=0;i<len;i++) {
if (s[i] != ' ') {
DrawTextBase(rc, font_, ybase, s, len, fore);
return;
}
}
}
void SurfaceImpl::MeasureWidths(Font &font_, const char *s, int len, int *positions) {
if (font_.GetID()) {
int totalWidth = 0;
#ifdef USE_PANGO
if (PFont(font_)->pfd) {
if (len == 1) {
int width = PFont(font_)->CharWidth(*s, et);
if (width) {
positions[0] = width;
return;
}
}
PangoRectangle pos;
pango_layout_set_font_description(layout, PFont(font_)->pfd);
if (et == UTF8) {
// Simple and direct as UTF-8 is native Pango encoding
pango_layout_set_text(layout, s, len);
int i = 0;
while (i < len) {
pango_layout_index_to_pos(layout, i+1, &pos);
positions[i++] = PANGO_PIXELS(pos.x);
}
} else {
int wclen = 0;
if (et == dbcs) {
GdkWChar *wctext = new GdkWChar[len + 1];
GdkWChar *wcp = wctext;
wclen = gdk_mbstowcs(wcp, s, len);
if (wclen >= 1 ) {
// Convert to UTF-8 so can ask Pango for widths, then
// Loop through UTF-8 and DBCS forms, taking account of different
// character byte lengths.
char *utfForm = UTF8FromGdkWChar(wctext, wclen);
pango_layout_set_text(layout, utfForm, strlen(utfForm));
int i = 0;
int iU = 0;
while (i < len) {
iU += UTF8Len(utfForm[iU]);
pango_layout_index_to_pos(layout, iU, &pos);
size_t lenChar = mblen(s+i, MB_CUR_MAX);
while (lenChar--) {
positions[i++] = PANGO_PIXELS(pos.x);
}
}
delete []utfForm;
}
delete []wctext;
}
if (wclen < 1 ) {
// Either Latin1 or DBCS conversion failed so treat as Latin1.
bool useGFree = false;
SetIconv(PFont(font_)->characterSet);
char *utfForm = UTF8FromIconv(iconvh, s, len);
//~ if (!utfForm) { // iconv failed so treat as locale
//~ gsize w; // stub
//~ utfForm = g_locale_to_utf8(s, len, NULL, &w, NULL);
//~ useGFree = static_cast<bool>(utfForm);
//~ }
if (!utfForm) {
utfForm = UTF8FromLatin1(s, len);
}
pango_layout_set_text(layout, utfForm, strlen(utfForm));
int i = 0;
int iU = 0;
while (i < len) {
iU += UTF8Len(utfForm[iU]);
pango_layout_index_to_pos(layout, iU, &pos);
positions[i++] = PANGO_PIXELS(pos.x);
}
if (useGFree) {
g_free(utfForm);
} else {
delete []utfForm;
}
}
}
if (len == 1) {
PFont(font_)->SetCharWidth(*s, positions[0], et);
}
return;
}
#endif
GdkFont *gf = PFont(font_)->pfont;
bool measure8bit = true;
if (et != singleByte) {
GdkWChar wctext[maxLengthTextRun];
int wclen;
if (et == UTF8) {
wclen = UCS2FromUTF8(s, len,
reinterpret_cast<wchar_t *>(wctext), maxLengthTextRun - 1);
} else { // dbcsMode, so convert using current locale
wclen = gdk_mbstowcs(
wctext, s, maxLengthTextRun - 1);
}
if (wclen > 0) {
measure8bit = false;
wctext[wclen] = L'\0';
// Map widths back to utf-8 or DBCS input string
int i = 0;
for (int iU = 0; iU < wclen; iU++) {
int width = gdk_char_width_wc(gf, wctext[iU]);
totalWidth += width;
size_t lenChar;
if (et == UTF8) {
lenChar = UTF8Len(s[i]);
} else {
lenChar = mblen(s+i, MB_CUR_MAX);
}
while (lenChar--) {
positions[i++] = totalWidth;
}
}
while (i < len) { // In case of problems with lengths
positions[i++] = totalWidth;
}
}
}
if (measure8bit) {
// Either Latin1 or conversion failed so treat as Latin1.
for (int i = 0; i < len; i++) {
int width = gdk_char_width(gf, s[i]);
totalWidth += width;
positions[i] = totalWidth;
}
}
} else {
// No font so return an ascending range of values
for (int i = 0; i < len; i++) {
positions[i] = i + 1;
}
}
}
int SurfaceImpl::WidthText(Font &font_, const char *s, int len) {
if (font_.GetID()) {
#ifdef USE_PANGO
if (PFont(font_)->pfd) {
char *utfForm = 0;
pango_layout_set_font_description(layout, PFont(font_)->pfd);
PangoRectangle pos;
bool useGFree = false;
if (et == UTF8) {
pango_layout_set_text(layout, s, len);
} else {
if (et == dbcs) {
// Convert to utf8
utfForm = UTF8FromDBCS(s, len);
}
if (!utfForm) { // DBCS failed so treat as iconv
SetIconv(PFont(font_)->characterSet);
utfForm = UTF8FromIconv(iconvh, s, len);
}
//~ if (!utfForm) { // iconv failed so treat as locale
//~ gsize w;
//~ utfForm = g_locale_to_utf8(s, len, NULL, &w, NULL);
//~ useGFree = static_cast<bool>(utfForm);
//~ };
if (!utfForm) { // g_locale_to_utf8 failed so treat as Latin1
utfForm = UTF8FromLatin1(s, len);
}
pango_layout_set_text(layout, utfForm, strlen(utfForm));
len = strlen(utfForm);
}
pango_layout_index_to_pos(layout, len, &pos);
int width = PANGO_PIXELS(pos.x);
if (useGFree) {
g_free(utfForm);
} else {
delete []utfForm;
}
return width;
}
#endif
if (et == UTF8) {
GdkWChar wctext[maxLengthTextRun];
size_t wclen = UCS2FromUTF8(s, len, (wchar_t *)wctext, sizeof(wctext) / sizeof(GdkWChar) - 1);
wctext[wclen] = L'\0';
return gdk_text_width_wc(PFont(font_)->pfont, wctext, wclen);
} else {
return gdk_text_width(PFont(font_)->pfont, s, len);
}
} else {
return 1;
}
}
int SurfaceImpl::WidthChar(Font &font_, char ch) {
if (font_.GetID()) {
#ifdef USE_PANGO
if (PFont(font_)->pfd) {
return WidthText(font_, &ch, 1);
}
#endif
return gdk_char_width(PFont(font_)->pfont, ch);
} else {
return 1;
}
}
// Three possible strategies for determining ascent and descent of font:
// 1) Call gdk_string_extents with string containing all letters, numbers and punctuation.
// 2) Use the ascent and descent fields of GdkFont.
// 3) Call gdk_string_extents with string as 1 but also including accented capitals.
// Smallest values given by 1 and largest by 3 with 2 in between.
// Techniques 1 and 2 sometimes chop off extreme portions of ascenders and
// descenders but are mostly OK except for accented characters like
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -