In case where board can't fully fill screen, track the rects on either

side and erase them when they're invalidated.  When seeking best-fit
font, pull glyphs to measure from dictionary rather than assuming
A-Z. Speed font measuring code by passing over all glyphs only once,
noting tallest and lowest-extending then measuring only those two as
smaller sizes are tried.  This *may* make the process fast enough that
I don't need to cache the information across boots: need to try on
real hardware.
This commit is contained in:
ehouse 2008-09-09 12:31:02 +00:00
parent 69f667fba3
commit 34c4bdab9a
3 changed files with 337 additions and 99 deletions

View file

@ -38,6 +38,8 @@
#define DRAW_FUNC_NAME(nam) ce_draw_ ## nam
#endif
//#define MEASURE_OLD_WAY
#define CE_MINI_V_PADDING 6
#define CE_MINIW_PADDING 0
#define CE_SCORE_PADDING -3
@ -53,6 +55,28 @@
# define TREAT_AS_CURSOR(d,f) (((f) & CELL_ISCURSOR) != 0)
#endif
typedef enum {
RFONTS_TRAY
,RFONTS_TRAYVAL
,RFONTS_CELL
,RFONTS_PTS
,N_RESIZE_FONTS
} RFIndex;
typedef struct _PenColorPair {
COLORREF ref;
HGDIOBJ pen;
} PenColorPair;
typedef struct _FontCacheEntry {
HFONT setFont;
XP_U16 setFontHt;
XP_U16 offset;
XP_U16 actualHt;
} FontCacheEntry;
struct CEDrawCtx {
DrawCtxVTable* vtable;
@ -86,6 +110,7 @@ struct CEDrawCtx {
static void ceClearToBkground( CEDrawCtx* dctx, const XP_Rect* rect );
static void ceDrawBitmapInRect( HDC hdc, const RECT* r, HBITMAP bitmap );
static void ceClipToRect( HDC hdc, const RECT* rt );
static void ceClearFontCache( CEDrawCtx* dctx );
static void
XPRtoRECT( RECT* rt, const XP_Rect* xprect )
@ -243,6 +268,147 @@ ERROR
#endif
} /* ceClipToRect */
static void
makeTestBuf( CEDrawCtx* dctx, XP_UCHAR* buf, XP_UCHAR bufLen, RFIndex index )
{
switch( index ) {
case RFONTS_TRAY:
case RFONTS_CELL: {
Tile tile;
Tile blank = (Tile)-1;
const DictionaryCtxt* dict = dctx->dict;
XP_U16 nFaces = dict_numTileFaces( dict );
Tile tiles[nFaces];
XP_U16 nOut = 0;
XP_ASSERT( !!dict && nFaces < bufLen );
if ( dict_hasBlankTile(dict) ) {
blank = dict_getBlankTile( dict );
}
for ( tile = 0; tile < nFaces; ++tile ) {
if ( tile != blank ) {
tiles[nOut++] = tile;
}
}
(void)dict_tilesToString( dict, tiles, nOut, buf, bufLen );
}
break;
case RFONTS_TRAYVAL:
strcpy( buf, "0" ); /* all numbers the same :-) */
break;
case RFONTS_PTS:
strcpy( buf, "Pts:0?" );
break;
case N_RESIZE_FONTS:
XP_ASSERT(0);
}
XP_LOGF( "%s=>%s", __func__, buf );
} /* makeTestBuf */
#ifndef MEASURE_OLD_WAY
static void
ceMeasureGlyph( HDC hdc, HBRUSH white, wchar_t glyph,
XP_U16 minTopSeen, XP_U16 maxBottomSeen,
XP_U16* top, XP_U16* bottom )
{
SIZE size;
XP_U16 xx, yy;
XP_Bool done;
GetTextExtentPoint32( hdc, &glyph, 1, &size );
RECT rect = { 0, 0, size.cx, size.cy };
FillRect( hdc, &rect, white );
DrawText( hdc, &glyph, 1, &rect, DT_TOP | DT_LEFT );
/* char tbuf[size.cx+1]; */
/* for ( yy = 0; yy < size.cy; ++yy ) { */
/* XP_MEMSET( tbuf, 0, size.cx+1 ); */
/* for ( xx = 0; xx < size.cx; ++xx ) { */
/* COLORREF ref = GetPixel( hdc, xx, yy ); */
/* XP_ASSERT( ref != CLR_INVALID ); */
/* strcat( tbuf, ref==0? " " : "x" ); */
/* } */
/* XP_LOGF( "line[%.2d] = %s", yy, tbuf ); */
/* } */
/* Find out if this guy's taller than what we have */
for ( done = XP_FALSE, yy = 0; yy < minTopSeen && !done; ++yy ) {
for ( xx = 0; xx < size.cx; ++xx ) {
COLORREF ref = GetPixel( hdc, xx, yy );
if ( ref == CLR_INVALID ) {
break; /* done this line */
} else if ( ref == 0 ) { /* a pixel set! */
*top = yy;
done = XP_TRUE;
break;
}
}
}
/* Extends lower than seen */
for ( done = XP_FALSE, yy = size.cy - 1; yy > maxBottomSeen && !done; --yy ) {
for ( xx = 0; xx < size.cx; ++xx ) {
COLORREF ref = GetPixel( hdc, xx, yy );
if ( ref == CLR_INVALID ) {
break;
} else if ( ref == 0 ) { /* a pixel set! */
*bottom = yy;
done = XP_TRUE;
break;
}
}
}
/* XP_LOGF( "%s: top: %d; bottom: %d", __func__, *top, *bottom ); */
} /* ceMeasureGlyph */
static void
ceMeasureGlyphs( CEDrawCtx* dctx, HDC hdc, /* HFONT font, */wchar_t* str,
XP_U16* hasMinTop, XP_U16* hasMaxBottom )
{
HBRUSH white = dctx->brushes[CE_WHITE_COLOR];
XP_U16 ii;
XP_U16 len = wcslen(str);
XP_U16 minTopSeen, maxBottomSeen;
XP_U16 maxBottomIndex = 0;
XP_U16 minTopIndex = 0;
minTopSeen = 1000; /* really large... */
maxBottomSeen = 0;
for ( ii = 0; ii < len; ++ii ) {
XP_U16 thisTop, thisBottom;
ceMeasureGlyph( hdc, white, str[ii],
minTopSeen, maxBottomSeen,
&thisTop, &thisBottom );
if ( thisBottom > maxBottomSeen ) {
maxBottomSeen = thisBottom;
maxBottomIndex = ii;
}
if ( thisTop < minTopSeen ) {
minTopSeen = thisTop;
minTopIndex = ii;
}
}
/* XP_LOGF( "offset: %d; height: %d", minTopSeen, maxBottomSeen - minTopSeen + 1 ); */
/* XP_LOGF( "offset came from %d; height from %d", minTopIndex, maxBottomIndex ); */
*hasMinTop = minTopIndex;
*hasMaxBottom = maxBottomIndex;
} /* ceMeasureGlyphs */
#endif
static void
ceClearFontCache( CEDrawCtx* dctx )
{
XP_U16 ii;
for ( ii = 0; ii < N_RESIZE_FONTS; ++ii ) {
if ( !!dctx->fcEntry[ii].setFont ) {
DeleteObject( dctx->fcEntry[ii].setFont );
}
}
XP_MEMSET( &dctx->fcEntry, 0, sizeof(dctx->fcEntry) );
}
#ifdef MEASURE_OLD_WAY
static void
ceMeasureTextHt( HFONT font, const char* str, XP_U16* ht, XP_U16* offset )
{
@ -325,27 +491,121 @@ ceMeasureTextHt( HFONT font, const char* str, XP_U16* ht, XP_U16* offset )
*ht = lastLine - firstLine + 1;
/* XP_LOGF( "%s(%s)=>ht: %d; offset: %d", __func__, str, *ht, *offset ); */
} /* ceMeasureTextHt */
#endif
static void
makeTestBuf( CEDrawCtx* XP_UNUSED(dctx), XP_UCHAR* buf,
XP_UCHAR XP_UNUSED(bufLen), RFIndex index )
ceBestFitFont( CEDrawCtx* dctx, XP_U16 soughtHeight, RFIndex index,
FontCacheEntry* fce )
{
switch( index ) {
case RFONTS_TRAY:
case RFONTS_CELL:
/* build this from dictioanary */
strcpy( buf, "ABCDEFGHIJKLMNOPQRSTUVWXYZ" );
break;
case RFONTS_TRAYVAL:
strcpy( buf, "0" ); /* all numbers the same :-) */
break;
case RFONTS_PTS:
strcpy( buf, "Pts:0?" );
break;
case N_RESIZE_FONTS:
XP_ASSERT(0);
#ifdef MEASURE_OLD_WAY
HFONT font;
LOGFONT fontInfo;
char buf[65];
XP_U16 offset;
XP_U16 actualHt;
XP_U16 trialHt;
/* XP_LOGF( "%s: calculating for index: %d; height: %d", */
/* __func__, index, height ); */
makeTestBuf( dctx, buf, VSIZE(buf), index );
for ( trialHt = soughtHeight; ; /*++trialHt*/ ) {
XP_MEMSET( &fontInfo, 0, sizeof(fontInfo) );
fontInfo.lfHeight = trialHt;
HFONT testFont = CreateFontIndirect( &fontInfo );
XP_U16 testOffset, testHt;
/* XP_LOGF( "%s: looking for ht %d with testht %d", __func__, height, trialHt ); */
ceMeasureTextHt( testFont, buf, &testHt, &testOffset );
if ( testHt > soughtHeight ) {
/* we've gone too far, so choose last that fit!!! */
XP_ASSERT( !!font );
DeleteObject( testFont );
break;
/* } else if ( trialHt == height /\* first time through *\/ */
/* && testOffset > 0 ) { /\* for safety *\/ */
/* trialHt += testOffset; */
} else {
++trialHt;
}
DeleteObject( font );
font = testFont;
offset = testOffset;
actualHt = testHt;
}
}
if ( !!fce->setFont ) {
DeleteObject( fce->setFont );
}
fce->setFont = font;
fce->setFontHt = soughtHeight;
fce->offset = offset;
fce->actualHt = actualHt;
#else
wchar_t widebuf[65];
XP_U16 len;
XP_U16 hasMinTop, hasMaxBottom;
XP_Bool firstPass;
HBRUSH white = dctx->brushes[CE_WHITE_COLOR];
HDC memDC = CreateCompatibleDC( NULL );
HBITMAP memBM;
XP_U16 testSize;
XP_LOGF( "%s(index=%d)", __func__, index );
char sample[65];
makeTestBuf( dctx, sample, VSIZE(sample), index );
len = strlen(sample);
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, sample, len,
widebuf, len );
memBM = CreateCompatibleBitmap( memDC, 64, 64 );
SelectObject( memDC, memBM );
for ( firstPass = XP_TRUE, testSize = soughtHeight*2; ; --testSize ) {
LOGFONT fontInfo;
XP_MEMSET( &fontInfo, 0, sizeof(fontInfo) );
fontInfo.lfHeight = testSize;
HFONT testFont = CreateFontIndirect( &fontInfo );
if ( !!testFont ) {
XP_U16 thisHeight, top, bottom;
SelectObject( memDC, testFont );
/* first time, measure all of them to determine which chars have
high and low points */
if ( firstPass ) {
ceMeasureGlyphs( dctx, memDC, widebuf, &hasMinTop,
&hasMaxBottom );
firstPass = XP_FALSE;
}
/* Thereafter, just measure the two we know about */
ceMeasureGlyph( memDC, white, sample[hasMinTop], 1000, 0,
&top, &bottom );
ceMeasureGlyph( memDC, white, sample[hasMaxBottom],
top, bottom, &top, &bottom );
thisHeight = bottom - top + 1;
if ( thisHeight <= soughtHeight ) { /* got it!!! */
fce->setFont = testFont;
fce->setFontHt = soughtHeight;
fce->offset = top;
fce->actualHt = thisHeight;
XP_LOGF( "Looking for %d; PICKED %d",
soughtHeight, thisHeight );
break;
}
DeleteObject( testFont );
XP_LOGF( "Looking for %d; rejected %d", soughtHeight, thisHeight );
}
}
DeleteObject( memBM );
DeleteDC( memDC );
#endif
} /* ceBestFitFont */
static HFONT
ceGetSizedFont( CEDrawCtx* dctx, XP_U16 height, RFIndex index,
@ -354,51 +614,7 @@ ceGetSizedFont( CEDrawCtx* dctx, XP_U16 height, RFIndex index,
FontCacheEntry* fce = &dctx->fcEntry[index];
if ( (0 != height) /* 0 means use what we have */
&& fce->setFontHt != height ) {
HFONT font;
LOGFONT fontInfo;
char buf[65];
XP_U16 offset;
XP_U16 actualHt;
XP_U16 trialHt;
XP_LOGF( "%s: calculating for index: %d; height: %d",
__func__, index, height );
makeTestBuf( dctx, buf, VSIZE(buf), index );
for ( trialHt = height; ; /*++trialHt*/ ) {
XP_MEMSET( &fontInfo, 0, sizeof(fontInfo) );
fontInfo.lfHeight = trialHt;
HFONT testFont = CreateFontIndirect( &fontInfo );
XP_U16 testOffset, testHt;
/* XP_LOGF( "%s: looking for ht %d with testht %d", __func__, height, trialHt ); */
ceMeasureTextHt( testFont, buf, &testHt, &testOffset );
if ( testHt > height ) {
/* we've gone too far, so choose last that fit!!! */
XP_ASSERT( !!font );
DeleteObject( testFont );
break;
/* } else if ( trialHt == height /\* first time through *\/ */
/* && testOffset > 0 ) { /\* for safety *\/ */
/* trialHt += testOffset; */
} else {
++trialHt;
}
DeleteObject( font );
font = testFont;
offset = testOffset;
actualHt = testHt;
}
if ( !!fce->setFont ) {
DeleteObject( fce->setFont );
}
fce->setFont = font;
fce->setFontHt = height;
fce->offset = offset;
fce->actualHt = actualHt;
ceBestFitFont( dctx, height, index, fce );
}
if ( !!offsetP ) {
@ -407,9 +623,9 @@ ceGetSizedFont( CEDrawCtx* dctx, XP_U16 height, RFIndex index,
if ( !!actualHtP ) {
*actualHtP = fce->actualHt;
}
XP_ASSERT( !!fce->setFont );
XP_ASSERT( !!fce->setFont ); /* failing... */
return fce->setFont;
}
} /* ceGetSizedFont */
#if 0
/* I'm trying to measure individual chars, but GetGlyphOutline and
@ -516,7 +732,7 @@ DRAW_FUNC_NAME(boardBegin)( DrawCtx* p_dctx,
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_Bool canDraw = !!hdc;
XP_Bool canDraw = !!hdc && !!dctx->dict;
if ( canDraw ) {
dctx->prevBkColor = GetBkColor( hdc );
dctx->topFocus = dfs == DFS_TOP;
@ -722,7 +938,7 @@ DRAW_FUNC_NAME(trayBegin)( DrawCtx* p_dctx, const XP_Rect* XP_UNUSED(rect),
CEDrawCtx* dctx = (CEDrawCtx*)p_dctx;
CEAppGlobals* globals = dctx->globals;
HDC hdc = globals->hdc;
XP_Bool canDraw = !!hdc;
XP_Bool canDraw = !!hdc && !!dctx->dict;
if ( canDraw ) {
dctx->trayOwner = owner;
dctx->topFocus = dfs == DFS_TOP;
@ -1352,11 +1568,7 @@ DRAW_FUNC_NAME(destroyCtxt)( DrawCtx* p_dctx )
DeleteObject( dctx->playerFont );
DeleteObject( dctx->selPlayerFont );
for ( i = 0; i < N_RESIZE_FONTS; ++i ) {
if ( !!dctx->fcEntry[i].setFont ) {
DeleteObject( dctx->fcEntry[i].setFont );
}
}
ceClearFontCache( dctx );
DeleteObject( dctx->rightArrow );
DeleteObject( dctx->downArrow );

View file

@ -418,6 +418,8 @@ hideScroller( CEAppGlobals* globals )
#endif
typedef struct CEBoardParms {
XP_U16 scrnWidth;
XP_U16 boardHScale;
XP_U16 boardVScale;
XP_U16 boardTop;
@ -468,7 +470,7 @@ figureBoardParms( CEAppGlobals* globals, XP_U16 nRows, CEBoardParms* bparms )
{
RECT rc;
XP_U16 scrnWidth, scrnHeight;
XP_U16 trayVScale, boardLeft, scoreWidth, scoreHeight;
XP_U16 trayVScale, boardLeft, trayLeft, scoreWidth, scoreHeight;
XP_U16 boardHt, boardWidth, hScale, vScale, nVisibleRows;
XP_U16 trayTop, boardTop;
XP_Bool horiz;
@ -511,6 +513,7 @@ figureBoardParms( CEAppGlobals* globals, XP_U16 nRows, CEBoardParms* bparms )
}
boardWidth = scrnWidth - scrollWidth;
trayWidth = scrnWidth;
if ( horiz ) {
scoreWidth = scrnWidth;
hScale = boardWidth / nRows;
@ -518,14 +521,16 @@ figureBoardParms( CEAppGlobals* globals, XP_U16 nRows, CEBoardParms* bparms )
/* center the board */
boardWidth += scrollWidth;
boardLeft = (scrnWidth - boardWidth) / 2; /* center it all */
trayLeft = 0;
} else {
/* move extra pixels into scoreboard */
hScale = (boardWidth - CE_MIN_SCORE_WIDTH) / nRows;
boardWidth = hScale * nRows;
scoreWidth = scrnWidth - boardWidth - scrollWidth;
boardLeft = scoreWidth;
trayLeft = scoreWidth;
trayWidth -= scoreWidth;
}
trayWidth = boardWidth + scrollWidth;
trayTop = boardHt + scoreHeight + 1;
trayVScale = scrnHeight - trayTop;
@ -551,6 +556,7 @@ figureBoardParms( CEAppGlobals* globals, XP_U16 nRows, CEBoardParms* bparms )
}
}
bparms->scrnWidth = scrnWidth;
bparms->boardHScale = hScale;
bparms->boardVScale = vScale;
bparms->boardTop = boardTop;
@ -558,7 +564,7 @@ figureBoardParms( CEAppGlobals* globals, XP_U16 nRows, CEBoardParms* bparms )
bparms->trayHeight = trayVScale;
bparms->trayWidth = trayWidth;
bparms->boardLeft = boardLeft;
bparms->trayLeft = boardLeft;
bparms->trayLeft = trayLeft;
bparms->scoreWidth = scoreWidth;
bparms->scoreHeight = scoreHeight;
bparms->horiz = horiz;
@ -578,6 +584,30 @@ figureBoardParms( CEAppGlobals* globals, XP_U16 nRows, CEBoardParms* bparms )
#endif
} /* figureBoardParms */
static void
setOwnedRects( CEAppGlobals* globals, XP_U16 nRows,
const CEBoardParms* bparms )
{
if ( bparms->horiz ) {
RECT tmp;
tmp.top = bparms->scoreHeight; /* Same for both */
tmp.bottom = bparms->trayTop; /* Same for both */
tmp.left = 0;
tmp.right = bparms->boardLeft;
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_LEFT], &tmp,
sizeof(globals->ownedRects[OWNED_RECT_LEFT]) );
tmp.left = tmp.right + (bparms->boardHScale * nRows);
tmp.right = bparms->scrnWidth;
XP_MEMCPY( &globals->ownedRects[OWNED_RECT_RIGHT], &tmp,
sizeof(globals->ownedRects[OWNED_RECT_RIGHT]) );
} else {
XP_MEMSET( &globals->ownedRects, 0, sizeof(globals->ownedRects) );
}
} /* setOwnedRects */
static XP_Bool
cePositionBoard( CEAppGlobals* globals )
{
@ -590,6 +620,7 @@ cePositionBoard( CEAppGlobals* globals )
XP_ASSERT( nCols <= CE_MAX_ROWS );
figureBoardParms( globals, nCols, &bparms );
setOwnedRects( globals, nCols, &bparms );
if ( globals->gameInfo.timerEnabled ) {
board_setTimerLoc( globals->game.board, bparms.timerLeft,
@ -668,6 +699,7 @@ ceInitAndStartBoard( CEAppGlobals* globals, XP_Bool newGame,
const CommsAddrRec* XP_UNUSED_STANDALONE(addr) )
{
DictionaryCtxt* dict;
DictionaryCtxt* toBeDestroyed = NULL;
XP_UCHAR* newDictName = globals->gameInfo.dictName;
/* This needs to happen even when !newGame because it's how the dict
@ -680,7 +712,7 @@ ceInitAndStartBoard( CEAppGlobals* globals, XP_Bool newGame,
const XP_UCHAR* curDictName = dict_getName( dict );
if ( !!newDictName &&
( !curDictName || 0 != strcmp( curDictName, newDictName ) ) ) {
dict_destroy( dict );
toBeDestroyed = dict;
dict = NULL;
} else {
replaceStringIfDifferent( globals->mpool,
@ -737,6 +769,10 @@ ceInitAndStartBoard( CEAppGlobals* globals, XP_Bool newGame,
server_do( globals->game.server );
globals->isNewGame = FALSE;
if ( !!toBeDestroyed ) {
dict_destroy( toBeDestroyed );
}
} /* ceInitAndStartBoard */
#ifdef DEBUG
@ -1385,7 +1421,14 @@ drawInsidePaint( CEAppGlobals* globals, const RECT* invalR )
globals->hdc = hdc;
if ( !!invalR ) {
ce_draw_erase( globals->draw, invalR );
XP_U16 ii;
for ( ii = 0; ii < N_OWNED_RECTS; ++ii ) {
RECT interR;
if ( IntersectRect( &interR, invalR,
&globals->ownedRects[ii] ) ) {
ce_draw_erase( globals->draw, &interR );
}
}
}
board_draw( globals->game.board );

View file

@ -85,7 +85,10 @@ typedef struct CEAppPrefs {
XP_Bool fullScreen;
} CEAppPrefs;
#define NUM_BUTTONS 4
enum { OWNED_RECT_LEFT
,OWNED_RECT_RIGHT
,N_OWNED_RECTS
};
typedef struct CEAppGlobals {
HINSTANCE hInst;
@ -95,8 +98,6 @@ typedef struct CEAppGlobals {
HWND hwndCB;
#endif
HWND buttons[NUM_BUTTONS];
#ifdef _WIN32_WCE
SHACTIVATEINFO sai;
XW_WinceVersion winceVersion;
@ -121,6 +122,8 @@ typedef struct CEAppGlobals {
void* timerClosures[NUM_TIMERS_PLUS_ONE];
XP_U32 timerWhens[NUM_TIMERS_PLUS_ONE];
RECT ownedRects[N_OWNED_RECTS];
XP_U16 flags; /* bits defined below */
#ifdef CEFEATURE_CANSCROLL
@ -160,26 +163,6 @@ enum {
#define CE_NUM_EDITABLE_COLORS CE_BLACK_COLOR
typedef enum {
RFONTS_TRAY
,RFONTS_TRAYVAL
,RFONTS_CELL
,RFONTS_PTS
,N_RESIZE_FONTS
} RFIndex;
typedef struct _PenColorPair {
COLORREF ref;
HGDIOBJ pen;
} PenColorPair;
typedef struct _FontCacheEntry {
HFONT setFont;
XP_U16 setFontHt;
XP_U16 offset;
XP_U16 actualHt;
} FontCacheEntry;
int messageBoxChar( CEAppGlobals* globals, XP_UCHAR* str, wchar_t* title,
XP_U16 buttons );