(svn r14479) -Add: initial (optional) support for handling bidirectional scripts and connecting Arabic characters.

This commit is contained in:
rubidium 2008-10-17 17:14:09 +00:00
parent b6ca10b05a
commit ae0715e8c3
2 changed files with 189 additions and 14 deletions

View File

@ -69,6 +69,7 @@ set_default() {
with_libtimidity="1" with_libtimidity="1"
with_freetype="1" with_freetype="1"
with_fontconfig="1" with_fontconfig="1"
with_icu="1"
with_psp_config="1" with_psp_config="1"
with_threads="1" with_threads="1"
with_distcc="1" with_distcc="1"
@ -133,6 +134,7 @@ set_default() {
with_libtimidity with_libtimidity
with_freetype with_freetype
with_fontconfig with_fontconfig
with_icu
with_psp_config with_psp_config
with_threads with_threads
with_distcc with_distcc
@ -320,6 +322,13 @@ detect_params() {
--without-libfontconfig) with_fontconfig="0";; --without-libfontconfig) with_fontconfig="0";;
--with-libfontconfig=*) with_fontconfig="$optarg";; --with-libfontconfig=*) with_fontconfig="$optarg";;
--with-icu) with_icu="2";;
--without-icu) with_icu="0";;
--with-icu=*) with_icu="$optarg";;
--with-libicu) with_icu="2";;
--without-libicu) with_icu="0";;
--with-libicu=*) with_icu="$optarg";;
--with-psp-config) with_psp_config="2";; --with-psp-config) with_psp_config="2";;
--without-psp-config) with_psp_config="0";; --without-psp-config) with_psp_config="0";;
--with-psp-config=*) with_psp_config="$optarg";; --with-psp-config=*) with_psp_config="$optarg";;
@ -604,6 +613,7 @@ check_params() {
detect_png detect_png
detect_freetype detect_freetype
detect_fontconfig detect_fontconfig
detect_icu
detect_pspconfig detect_pspconfig
detect_libtimidity detect_libtimidity
@ -1227,6 +1237,14 @@ make_cflags_and_ldflags() {
fi fi
fi fi
if [ -n "$icu_config" ]; then
CFLAGS="$CFLAGS -DWITH_ICU"
CFLAGS="$CFLAGS `$icu_config --cppflags | tr '\n\r' ' '`"
LIBS="$LIBS `$icu_config --ldflags-libsonly | tr '\n\r' ' '`"
fi
if [ "$with_direct_music" != "0" ]; then if [ "$with_direct_music" != "0" ]; then
CFLAGS="$CFLAGS -DWIN32_ENABLE_DIRECTMUSIC_SUPPORT" CFLAGS="$CFLAGS -DWIN32_ENABLE_DIRECTMUSIC_SUPPORT"
# GCC 4.0+ doesn't like the DirectX includes (gives tons of # GCC 4.0+ doesn't like the DirectX includes (gives tons of
@ -2060,6 +2078,49 @@ detect_fontconfig() {
log 1 "checking libfontconfig... found" log 1 "checking libfontconfig... found"
} }
detect_icu() {
# 0 means no, 1 is auto-detect, 2 is force
if [ "$with_icu" = "0" ]; then
log 1 "checking libicu... disabled"
icu_config=""
return 0
fi
if [ "$with_icu" = "1" ] || [ "$with_icu" = "" ] || [ "$with_icu" = "2" ]; then
icu_config="icu-config"
else
icu_config="$with_icu"
fi
version=`$icu_config --version 2>/dev/null`
ret=$?
shortversion=`echo $version | cut -c 1,3`
log 2 "executing $icu_config --version"
log 2 " returned $version"
log 2 " exit code $ret"
if [ -z "$version" ] || [ "$ret" != "0" ] || [ "$shortversion" -lt "20" ]; then
if [ -n "$shortversion" ] && [ "$shortversion" -lt "20" ]; then
log 1 "checking libicu... needs at least version 2.0.0, icu NOT enabled"
else
log 1 "checking libicu... not found"
fi
# It was forced, so it should be found.
if [ "$with_icu" != "1" ]; then
log 1 "configure: error: icu-config couldn't be found"
log 1 "configure: error: you supplied '$with_icuconfig', but it seems invalid"
exit 1
fi
icu_config=""
return 0
fi
log 1 "checking libicu... found"
}
detect_pspconfig() { detect_pspconfig() {
# 0 means no, 1 is auto-detect, 2 is force # 0 means no, 1 is auto-detect, 2 is force
if [ "$with_psp_config" = "0" ]; then if [ "$with_psp_config" = "0" ]; then

View File

@ -51,6 +51,7 @@ DrawPixelInfo *_cur_dpi;
byte _colour_gradient[COLOUR_END][8]; byte _colour_gradient[COLOUR_END][8];
static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL); static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub = NULL);
static int ReallyDoDrawString(const char *string, int x, int y, uint16 real_colour, bool parse_string_also_when_clipped = false);
FontSize _cur_fontsize; FontSize _cur_fontsize;
static FontSize _last_fontsize; static FontSize _last_fontsize;
@ -238,6 +239,74 @@ void DrawBox(int x, int y, int dx1, int dy1, int dx2, int dy2, int dx3, int dy3)
} }
#if !defined(WITH_ICU)
static void HandleBiDiAndArabicShapes(char *text, const char *lastof) {}
#else
#include "unicode/ubidi.h"
#include "unicode/ushape.h"
/**
* Function to be able to handle right-to-left text and Arabic chars properly.
*
* First: right-to-left (RTL) is stored 'logically' in almost all applications
* and so do we. This means that their text is stored from right to the
* left in memory and any non-RTL text (like numbers or English) are
* then stored from left-to-right. When we want to actually draw the
* text we need to reverse the RTL text in memory, which is what
* happens in ubidi_writeReordered.
* Second: Arabic characters "differ" based on their context. To draw the
* correct variant we pass it through u_shapeArabic. This function can
* add or remove some characters. This is the reason for the lastof
* so we know till where we can fill the output.
*
* Sadly enough these functions work with a custom character format, UChar,
* which isn't the same size as WChar. Because of that we need to transform
* our text first to UChars and then back to something we can use.
*
* To be able to truncate strings properly you must truncate before passing to
* this function. This way the logical begin of the string remains and the end
* gets chopped of instead of the other way around.
*
* The reshaping of Arabic characters might increase or decrease the width of
* the characters/string. So it might still overflow after truncation, though
* the chance is fairly slim as most characters get shorter instead of longer.
* @param buffer the buffer to read from/to
* @param lastof the end of the buffer
*/
static void HandleBiDiAndArabicShapes(char *buffer, const char *lastof)
{
UChar input_output[DRAW_STRING_BUFFER];
UChar intermediate[DRAW_STRING_BUFFER];
char *t = buffer;
size_t length = 0;
while (*t != '\0' && length < lengthof(input_output)) {
WChar tmp;
t += Utf8Decode(&tmp, t);
input_output[length++] = tmp;
}
input_output[length] = 0;
UErrorCode err = U_ZERO_ERROR;
UBiDi *para = ubidi_openSized(length, 0, &err);
if (para == NULL) return;
ubidi_setPara(para, input_output, length, UBIDI_DEFAULT_RTL, NULL, &err);
ubidi_writeReordered(para, intermediate, length, 0, &err);
length = u_shapeArabic(intermediate, length, input_output, lengthof(input_output), U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE, &err);
ubidi_close(para);
if (U_FAILURE(err)) return;
t = buffer;
for (size_t i = 0; i < length && t < (lastof - 4); i++) {
t += Utf8Encode(t, input_output[i]);
}
*t = '\0';
}
#endif /* WITH_ICU */
/** Truncate a given string to a maximum width if neccessary. /** Truncate a given string to a maximum width if neccessary.
* If the string is truncated, add three dots ('...') to show this. * If the string is truncated, add three dots ('...') to show this.
* @param *str string that is checked and possibly truncated * @param *str string that is checked and possibly truncated
@ -322,7 +391,8 @@ int DrawString(int x, int y, StringID str, uint16 color)
char buffer[DRAW_STRING_BUFFER]; char buffer[DRAW_STRING_BUFFER];
GetString(buffer, str, lastof(buffer)); GetString(buffer, str, lastof(buffer));
return DoDrawString(buffer, x, y, color); HandleBiDiAndArabicShapes(buffer, lastof(buffer));
return ReallyDoDrawString(buffer, x, y, color);
} }
/** /**
@ -340,7 +410,8 @@ int DrawStringTruncated(int x, int y, StringID str, uint16 color, uint maxw)
{ {
char buffer[DRAW_STRING_BUFFER]; char buffer[DRAW_STRING_BUFFER];
TruncateStringID(str, buffer, maxw, lastof(buffer)); TruncateStringID(str, buffer, maxw, lastof(buffer));
return DoDrawString(buffer, x, y, color); HandleBiDiAndArabicShapes(buffer, lastof(buffer));
return ReallyDoDrawString(buffer, x, y, color);
} }
/** /**
@ -359,8 +430,10 @@ int DrawStringRightAligned(int x, int y, StringID str, uint16 color)
int w; int w;
GetString(buffer, str, lastof(buffer)); GetString(buffer, str, lastof(buffer));
HandleBiDiAndArabicShapes(buffer, lastof(buffer));
w = GetStringBoundingBox(buffer).width; w = GetStringBoundingBox(buffer).width;
DoDrawString(buffer, x - w, y, color); ReallyDoDrawString(buffer, x - w, y, color);
return w; return w;
} }
@ -379,7 +452,8 @@ void DrawStringRightAlignedTruncated(int x, int y, StringID str, uint16 color, u
char buffer[DRAW_STRING_BUFFER]; char buffer[DRAW_STRING_BUFFER];
TruncateStringID(str, buffer, maxw, lastof(buffer)); TruncateStringID(str, buffer, maxw, lastof(buffer));
DoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, color); HandleBiDiAndArabicShapes(buffer, lastof(buffer));
ReallyDoDrawString(buffer, x - GetStringBoundingBox(buffer).width, y, color);
} }
/** /**
@ -412,9 +486,10 @@ int DrawStringCentered(int x, int y, StringID str, uint16 color)
int w; int w;
GetString(buffer, str, lastof(buffer)); GetString(buffer, str, lastof(buffer));
HandleBiDiAndArabicShapes(buffer, lastof(buffer));
w = GetStringBoundingBox(buffer).width; w = GetStringBoundingBox(buffer).width;
DoDrawString(buffer, x - w / 2, y, color); ReallyDoDrawString(buffer, x - w / 2, y, color);
return w; return w;
} }
@ -433,8 +508,11 @@ int DrawStringCentered(int x, int y, StringID str, uint16 color)
int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color) int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 color)
{ {
char buffer[DRAW_STRING_BUFFER]; char buffer[DRAW_STRING_BUFFER];
int w = TruncateStringID(str, buffer, xr - xl, lastof(buffer)); TruncateStringID(str, buffer, xr - xl, lastof(buffer));
return DoDrawString(buffer, (xl + xr - w) / 2, y, color); HandleBiDiAndArabicShapes(buffer, lastof(buffer));
int w = GetStringBoundingBox(buffer).width;
return ReallyDoDrawString(buffer, (xl + xr - w) / 2, y, color);
} }
/** /**
@ -449,8 +527,12 @@ int DrawStringCenteredTruncated(int xl, int xr, int y, StringID str, uint16 colo
*/ */
int DoDrawStringCentered(int x, int y, const char *str, uint16 color) int DoDrawStringCentered(int x, int y, const char *str, uint16 color)
{ {
int w = GetStringBoundingBox(str).width; char buffer[DRAW_STRING_BUFFER];
DoDrawString(str, x - w / 2, y, color); strecpy(buffer, str, lastof(buffer));
HandleBiDiAndArabicShapes(buffer, lastof(buffer));
int w = GetStringBoundingBox(buffer).width;
ReallyDoDrawString(buffer, x - w / 2, y, color);
return w; return w;
} }
@ -613,7 +695,7 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
{ {
char buffer[DRAW_STRING_BUFFER]; char buffer[DRAW_STRING_BUFFER];
uint32 tmp; uint32 tmp;
int num, w, mt; int num, mt;
const char *src; const char *src;
WChar c; WChar c;
@ -629,8 +711,11 @@ void DrawStringMultiCenter(int x, int y, StringID str, int maxw)
src = buffer; src = buffer;
for (;;) { for (;;) {
w = GetStringBoundingBox(src).width; char buf2[DRAW_STRING_BUFFER];
DoDrawString(src, x - (w >> 1), y, 0xFE, true); strecpy(buf2, src, lastof(buf2));
HandleBiDiAndArabicShapes(buf2, lastof(buf2));
int w = GetStringBoundingBox(buf2).width;
ReallyDoDrawString(buf2, x - (w >> 1), y, 0xFE, true);
_cur_fontsize = _last_fontsize; _cur_fontsize = _last_fontsize;
for (;;) { for (;;) {
@ -680,7 +765,10 @@ uint DrawStringMultiLine(int x, int y, StringID str, int maxw, int maxh)
src = buffer; src = buffer;
for (;;) { for (;;) {
DoDrawString(src, x, y, 0xFE, true); char buf2[DRAW_STRING_BUFFER];
strecpy(buf2, src, lastof(buf2));
HandleBiDiAndArabicShapes(buf2, lastof(buf2));
ReallyDoDrawString(buf2, x, y, 0xFE, true);
_cur_fontsize = _last_fontsize; _cur_fontsize = _last_fontsize;
for (;;) { for (;;) {
@ -767,7 +855,7 @@ void DrawCharCentered(WChar c, int x, int y, uint16 real_color)
/** Draw a string at the given coordinates with the given colour. /** Draw a string at the given coordinates with the given colour.
* While drawing the string, parse it in case some formatting is specified, * While drawing the string, parse it in case some formatting is specified,
* like new colour, new size or even positionning. * like new colour, new size or even positionning.
* @param string The string to draw * @param string The string to draw. This is not yet bidi reordered.
* @param x Offset from left side of the screen * @param x Offset from left side of the screen
* @param y Offset from top side of the screen * @param y Offset from top side of the screen
* @param real_colour Colour of the string, see _string_colormap in * @param real_colour Colour of the string, see _string_colormap in
@ -782,6 +870,32 @@ void DrawCharCentered(WChar c, int x, int y, uint16 real_color)
* If nothing is drawn, the originally passed x-coordinate is returned * If nothing is drawn, the originally passed x-coordinate is returned
*/ */
int DoDrawString(const char *string, int x, int y, uint16 real_colour, bool parse_string_also_when_clipped) int DoDrawString(const char *string, int x, int y, uint16 real_colour, bool parse_string_also_when_clipped)
{
char buffer[DRAW_STRING_BUFFER];
strecpy(buffer, string, lastof(buffer));
HandleBiDiAndArabicShapes(buffer, lastof(buffer));
return ReallyDoDrawString(buffer, x, y, real_colour, parse_string_also_when_clipped);
}
/** Draw a string at the given coordinates with the given colour.
* While drawing the string, parse it in case some formatting is specified,
* like new colour, new size or even positionning.
* @param string The string to draw. This is already bidi reordered.
* @param x Offset from left side of the screen
* @param y Offset from top side of the screen
* @param real_colour Colour of the string, see _string_colormap in
* table/palettes.h or docs/ottd-colourtext-palette.png or the enum TextColour in gfx_type.h
* @param parse_string_also_when_clipped
* By default, always test the available space where to draw the string.
* When in multipline drawing, it would already be done,
* so no need to re-perform the same kind (more or less) of verifications.
* It's not only an optimisation, it's also a way to ensures the string will be parsed
* (as there are certain side effects on global variables, which are important for the next line)
* @return the x-coordinates where the drawing has finished.
* If nothing is drawn, the originally passed x-coordinate is returned
*/
static int ReallyDoDrawString(const char *string, int x, int y, uint16 real_colour, bool parse_string_also_when_clipped)
{ {
DrawPixelInfo *dpi = _cur_dpi; DrawPixelInfo *dpi = _cur_dpi;
FontSize size = _cur_fontsize; FontSize size = _cur_fontsize;