#ifndef WDL_NO_DEFINE_MINMAX #define WDL_NO_DEFINE_MINMAX #endif #include "lice_text.h" #include #include "lice_combine.h" #include "lice_extended.h" #define IGNORE_SCALING(mode) ((mode)&LICE_BLIT_IGNORE_SCALING) #if defined(_WIN32) && defined(WDL_SUPPORT_WIN9X) static char __1ifNT2if98=0; // 2 for iswin98 #endif // for very large fonts, fallback to native and avoid making a huge glyph cache, which would be terrible for performance #define USE_NATIVE_RENDERING_FOR_FONTS_HIGHER_THAN 256 // if other methods fail, at this point just flat out refuse to render glyphs (since they would use a ridiculous amount of memory) #ifndef ABSOLUTELY_NO_GLYPHS_HIGHER_THAN #define ABSOLUTELY_NO_GLYPHS_HIGHER_THAN 1024 #endif static int utf8makechar(char *ptrout, unsigned short charIn) { unsigned char *pout = (unsigned char *)ptrout; if (charIn < 128) { *pout = (unsigned char)charIn; return 1; } if (charIn < 2048) { pout[0] = 0xC0 + (charIn>>6); pout[1] = 0x80 + (charIn&0x3f); return 2; } pout[0] = 0xE0 + (charIn>>12); pout[1] = 0x80 + ((charIn>>6)&0x3f); pout[2] = 0x80 + (charIn&0x3f); return 3; } static int utf8char(const char *ptr, unsigned short *charOut) // returns char length { const unsigned char *p = (const unsigned char *)ptr; unsigned char tc = *p; if (tc < 128) { if (charOut) *charOut = (unsigned short) tc; return 1; } else if (tc < 0xC2) // invalid chars (subsequent in sequence, or overlong which we disable for) { } else if (tc < 0xE0) // 2 char seq { if (p[1] >= 0x80 && p[1] <= 0xC0) { if (charOut) *charOut = ((tc&0x1f)<<6) | (p[1]&0x3f); return 2; } } else if (tc < 0xF0) // 3 char seq { if (p[1] >= 0x80 && p[1] <= 0xC0 && p[2] >= 0x80 && p[2] <= 0xC0) { if (charOut) *charOut = ((tc&0xf)<<12) | ((p[1]&0x3f)<<6) | ((p[2]&0x3f)); return 3; } } else if (tc < 0xF5) // 4 char seq { if (p[1] >= 0x80 && p[1] <= 0xC0 && p[2] >= 0x80 && p[2] <= 0xC0 && p[3] >= 0x80 && p[3] <= 0xC0) { if (charOut) *charOut = (unsigned short)' '; // dont support 4 byte sequences yet(ever?) return 4; } } if (charOut) *charOut = (unsigned short) tc; return 1; } //not threadsafe ---- static LICE_SysBitmap *s_tempbitmap; // keep a sysbitmap around for rendering fonts static LICE_SysBitmap *s_nativerender_tempbitmap; static int s_tempbitmap_refcnt; int LICE_CachedFont::_charSortFunc(const void *a, const void *b) { charEnt *aa = (charEnt *)a; charEnt *bb = (charEnt *)b; return aa->charid - bb->charid; } LICE_CachedFont::LICE_CachedFont() : m_cachestore(65536) { s_tempbitmap_refcnt++; m_fg=0; m_effectcol=m_bg=LICE_RGBA(255,255,255,255); m_comb=0; m_alpha=1.0f; m_bgmode = TRANSPARENT; m_flags=0; m_line_height=0; m_lsadj=0; m_font=0; memset(m_lowchars,0,sizeof(m_lowchars)); } LICE_CachedFont::~LICE_CachedFont() { if ((m_flags&LICE_FONT_FLAG_OWNS_HFONT) && m_font) { DeleteObject(m_font); } if (!--s_tempbitmap_refcnt) { delete s_tempbitmap; s_tempbitmap=0; delete s_nativerender_tempbitmap; s_nativerender_tempbitmap=0; } } void LICE_CachedFont::SetFromHFont(HFONT font, int flags) { if ((m_flags&LICE_FONT_FLAG_OWNS_HFONT) && m_font && m_font != font) { DeleteObject(m_font); } m_flags=flags; m_font=font; if (font) { if (!s_tempbitmap) s_tempbitmap=new LICE_SysBitmap; if (s_tempbitmap->getWidth() < 256 || s_tempbitmap->getHeight() < 256) { s_tempbitmap->resize(256,256); ::SetTextColor(s_tempbitmap->getDC(),RGB(255,255,255)); ::SetBkMode(s_tempbitmap->getDC(),OPAQUE); ::SetBkColor(s_tempbitmap->getDC(),RGB(0,0,0)); } TEXTMETRIC tm; HGDIOBJ oldFont = 0; if (font) oldFont = SelectObject(s_tempbitmap->getDC(),font); GetTextMetrics(s_tempbitmap->getDC(),&tm); if (oldFont) SelectObject(s_tempbitmap->getDC(),oldFont); m_line_height = tm.tmHeight; } memset(m_lowchars,0,sizeof(m_lowchars)); m_extracharlist.Resize(0,false); m_cachestore.Resize(0); if (flags&LICE_FONT_FLAG_PRECALCALL) { int x; for(x=0;x<128;x++) RenderGlyph(x); } } bool LICE_CachedFont::RenderGlyph(unsigned short idx) // return TRUE if ok { if (m_line_height >= ABSOLUTELY_NO_GLYPHS_HIGHER_THAN) return false; bool needSort=false; charEnt *ent; if (idx>=128) { #if defined(_WIN32) && defined(WDL_SUPPORT_WIN9X) if (!__1ifNT2if98) __1ifNT2if98 = GetVersion()<0x80000000 ? 1 : 2; if (__1ifNT2if98==2) return false; #endif ent=findChar(idx); if (!ent) { if (m_flags & LICE_FONT_FLAG_PRECALCALL) return false; int oldsz=m_extracharlist.GetSize(); ent = m_extracharlist.Resize(oldsz+1) + oldsz; memset(ent,0,sizeof(*ent)); ent->charid = idx; needSort=true; } } else ent = m_lowchars+idx; const int bmsz=lice_max(m_line_height,1) * 2 + 8; if (!s_tempbitmap) s_tempbitmap=new LICE_SysBitmap; if (s_tempbitmap->getWidth() < bmsz || s_tempbitmap->getHeight() < bmsz) { s_tempbitmap->resize(bmsz,bmsz); ::SetTextColor(s_tempbitmap->getDC(),RGB(255,255,255)); ::SetBkMode(s_tempbitmap->getDC(),OPAQUE); ::SetBkColor(s_tempbitmap->getDC(),RGB(0,0,0)); } HGDIOBJ oldFont=0; if (m_font) oldFont = SelectObject(s_tempbitmap->getDC(),m_font); RECT r={0,0,0,0,}; int advance; // overrender sides and check to see if it was updated const int right_extra_pad = 2+wdl_max(m_line_height/8,0); const int left_extra_pad = 2+wdl_max(m_line_height/16,0); #ifdef _WIN32 #if defined(WDL_SUPPORT_WIN9X) if (__1ifNT2if98==1) #endif { WCHAR tmpstr[2]={(WCHAR)idx,0}; ::DrawTextW(s_tempbitmap->getDC(),tmpstr,1,&r,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); advance=r.right; r.right += right_extra_pad+left_extra_pad; LICE_FillRect(s_tempbitmap,0,0,r.right,r.bottom,0,1.0f,LICE_BLIT_MODE_COPY); r.left+=left_extra_pad; ::DrawTextW(s_tempbitmap->getDC(),tmpstr,1,&r,DT_SINGLELINE|DT_LEFT|DT_TOP|DT_NOPREFIX|DT_NOCLIP); } #if defined(WDL_SUPPORT_WIN9X) else #endif #endif #if !defined(_WIN32) || defined(WDL_SUPPORT_WIN9X) { char tmpstr[6]={(char)idx,0}; #ifndef _WIN32 if (idx>=128) utf8makechar(tmpstr,idx); #endif ::DrawText(s_tempbitmap->getDC(),tmpstr,-1,&r,DT_CALCRECT|DT_SINGLELINE|DT_NOPREFIX); advance=r.right; r.right += right_extra_pad+left_extra_pad; LICE_FillRect(s_tempbitmap,0,0,r.right,r.bottom,0,1.0f,LICE_BLIT_MODE_COPY); r.left += left_extra_pad; ::DrawText(s_tempbitmap->getDC(),tmpstr,-1,&r,DT_SINGLELINE|DT_LEFT|DT_TOP|DT_NOPREFIX|DT_NOCLIP); } #endif if (advance > s_tempbitmap->getWidth()) advance=s_tempbitmap->getWidth(); if (r.right > s_tempbitmap->getWidth()) r.right=s_tempbitmap->getWidth(); if (r.bottom > s_tempbitmap->getHeight()) r.bottom=s_tempbitmap->getHeight(); if (oldFont) SelectObject(s_tempbitmap->getDC(),oldFont); if (advance < 1 || r.bottom < 1) { ent->base_offset=-1; ent->left_extra=ent->advance=ent->width=ent->height=0; } else { ent->advance=advance; int flags=m_flags; if (flags&LICE_FONT_FLAG_FX_BLUR) { LICE_Blur(s_tempbitmap,s_tempbitmap,0,0,0,0,r.right,r.bottom); } LICE_pixel *srcbuf = s_tempbitmap->getBits(); int span=s_tempbitmap->getRowSpan(); ent->base_offset=m_cachestore.GetSize()+1; int newsz = ent->base_offset-1+r.right*r.bottom; unsigned char *destbuf = m_cachestore.Resize(newsz) + ent->base_offset-1; if (m_cachestore.GetSize() != newsz) { ent->base_offset=-1; ent->advance=ent->width=ent->height=0; } else { if (s_tempbitmap->isFlipped()) { srcbuf += (s_tempbitmap->getHeight()-1)*span; span=-span; } int x,y; int min_x=left_extra_pad; int max_x=advance + min_x; for(y=0;y 0) { const int neww = max_x-min_x; const unsigned char *rdptr=destbuf + min_x; // trim down destbuf for (y=0;ybase_offset-1+r.right*r.bottom; destbuf = m_cachestore.Resize(newsz,false) + ent->base_offset-1; } if (flags&LICE_FONT_FLAG_VERTICAL) { int a=r.bottom; r.bottom=r.right; r.right=a; unsigned char *tmpbuf = (unsigned char *)s_tempbitmap->getBits(); memcpy(tmpbuf,destbuf,r.right*r.bottom); int dptr=r.bottom; int dtmpbuf=1; if (!(flags&LICE_FONT_FLAG_VERTICAL_BOTTOMUP)) { tmpbuf += (r.right-1)*dptr; dptr=-dptr; } else { tmpbuf+=r.bottom-1; dtmpbuf=-1; } for(y=0;y130 ? 255:0; destbuf++; } destbuf -= r.right*r.bottom; } if (flags&(LICE_FONT_FLAG_FX_SHADOW|LICE_FONT_FLAG_FX_OUTLINE)) { for(y=0;y130 ? 255:0; destbuf++; } destbuf -= r.right*r.bottom; if (flags&LICE_FONT_FLAG_FX_SHADOW) { for(y=0;y0) { if (destbuf[-1]==255) *destbuf=128; else if (y>0 && destbuf[-r.right-1]==255) *destbuf=128; } if (y>0 && destbuf[-r.right]==255) *destbuf=128; } destbuf++; } } else { for(y=0;y0 && destbuf[-r.right]==255) *destbuf=128; else if (y0) { if (destbuf[-1]==255) *destbuf=128; // else if (y>0 && destbuf[-r.right-1]==255) *destbuf=128; // else if (y0 && destbuf[-r.right+1]==255) *destbuf=128; // else if (yleft_extra=left_extra_pad-min_x; ent->width = r.right; ent->height = r.bottom; } } if (needSort&&m_extracharlist.GetSize()>1) qsort(m_extracharlist.Get(),m_extracharlist.GetSize(),sizeof(charEnt),_charSortFunc); return true; } template class GlyphRenderer { public: static void Normal(unsigned char *gsrc, LICE_pixel *pout, int src_span, int dest_span, int width, int height, int red, int green, int blue, int pxa, int a256) { int y; if (a256==256) { for(y=0;y256)a=256; T::doPix((unsigned char *)(pout+x),red,green,blue,pxa,a); } } gsrc += src_span; pout += dest_span; } } } static void Mono(unsigned char *gsrc, LICE_pixel *pout, int src_span, int dest_span, int width, int height, int red, int green, int blue, int pxa, int alpha) { int y; for(y=0;yleft_extra; else ypos += ch->left_extra; } else { xpos -= ch->left_extra; } if (xpos >= clipR->right || ypos >= clipR->bottom || xpos+ch->width <= clipR->left || ypos+ch->height <= clipR->top) return true; // would have drawn but out of bounds unsigned char *gsrc = m_cachestore.Get() + ch->base_offset-1; int src_span = ch->width; int width = ch->width; int height = ch->height; #ifndef DISABLE_LICE_EXTENSIONS if (bm->Extended(LICE_EXT_SUPPORTS_ID, (void*) LICE_EXT_DRAWGLYPH_ACCEL)) { LICE_Ext_DrawGlyph_acceldata data(xpos, ypos, m_fg, gsrc, width, height, m_alpha, m_comb); if (bm->Extended(LICE_EXT_DRAWGLYPH_ACCEL, &data)) return true; } #endif if (xpos < clipR->left) { width += (xpos-clipR->left); gsrc += clipR->left-xpos; xpos=clipR->left; } if (ypos < clipR->top) { gsrc += src_span*(clipR->top-ypos); height += (ypos-clipR->top); ypos=clipR->top; } int dest_span = bm->getRowSpan(); LICE_pixel *pout = bm->getBits(); if (bm->isFlipped()) { int bm_h = bm->getHeight(); const int __sc = bm ? (int)bm->Extended(LICE_EXT_GET_SCALING,NULL) : 0; if (__sc>0) { __LICE_SCU(bm_h); } pout += (bm_h-1)*dest_span; dest_span=-dest_span; } pout += xpos + ypos * dest_span; if (width >= clipR->right-xpos) width = clipR->right-xpos; if (height >= clipR->bottom-ypos) height = clipR->bottom-ypos; if (width < 1 || height < 1) return false; // this could be an assert, it is guaranteed by the xpos/ypos checks at the top of this function int mode=m_comb&~LICE_BLIT_USE_ALPHA; float alpha=m_alpha; if (m_bgmode==OPAQUE) LICE_FillRect(bm,xpos,ypos,width,height,m_bg,alpha,mode|LICE_BLIT_IGNORE_SCALING); int red=LICE_GETR(m_fg); int green=LICE_GETG(m_fg); int blue=LICE_GETB(m_fg); int pxa=LICE_GETA(m_fg); if (m_flags&LICE_FONT_FLAG_FX_MONO) { if (alpha==1.0 && (mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY) // fast simple { LICE_pixel col=m_fg; int y; for(y=0;y256)avalint=256; #define __LICE__ACTION(comb) GlyphRenderer::Mono(gsrc,pout,src_span,dest_span,width,height,red,green,blue,pxa,avalint) __LICE_ACTION_NOSRCALPHA(mode,avalint, false); #undef __LICE__ACTION } } else if (m_flags&(LICE_FONT_FLAG_FX_SHADOW|LICE_FONT_FLAG_FX_OUTLINE)) { LICE_pixel col=m_fg; LICE_pixel bkcol=m_effectcol; if (alpha==1.0 && (mode&LICE_BLIT_MODE_MASK)==LICE_BLIT_MODE_COPY) { int y; for(y=0;y256)avalint=256; int r2=LICE_GETR(bkcol); int g2=LICE_GETG(bkcol); int b2=LICE_GETB(bkcol); int pxa2=LICE_GETA(bkcol); #define __LICE__ACTION(comb) GlyphRenderer::Effect(gsrc,pout,src_span,dest_span,width,height,red,green,blue,pxa,avalint,r2,g2,b2,pxa2) __LICE_ACTION_NOSRCALPHA(mode,avalint, false); #undef __LICE__ACTION } } else { int avalint = (int) (alpha*256.0); #define __LICE__ACTION(comb) GlyphRenderer::Normal(gsrc,pout,src_span,dest_span,width,height,red,green,blue,pxa,avalint) __LICE_ACTION_NOSRCALPHA(mode,avalint, false); #undef __LICE__ACTION } return true; // drew glyph at all (for updating max extents) } #ifndef LICE_TEXT_NONATIVE static int LICE_Text_IsWine() { static int isWine=-1; #ifdef _WIN32 if (isWine<0) { HKEY hk; if (RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Wine",&hk)==ERROR_SUCCESS) { isWine=1; RegCloseKey(hk); } else isWine=0; } #endif return isWine>0; } #endif #ifdef _WIN32 static BOOL LICE_Text_HasUTF8(const char *_str) { const unsigned char *str = (const unsigned char *)_str; if (!str) return FALSE; while (*str) { unsigned char c = *str; if (c >= 0xC2) // fuck overlongs { if (c <= 0xDF && str[1] >=0x80 && str[1] <= 0xBF) return TRUE; else if (c <= 0xEF && str[1] >=0x80 && str[1] <= 0xBF && str[2] >=0x80 && str[2] <= 0xBF) return TRUE; else if (c <= 0xF4 && str[1] >=0x80 && str[1] <= 0xBF && str[2] >=0x80 && str[2] <= 0xBF) return TRUE; } str++; } return FALSE; } #endif #define __LICE_SC_DRAWTEXT_RESTORE_RECT \ if (__sc > 0 && rect) { \ rect->left = (rect->left * 256) / __sc; \ rect->top = (rect->top * 256) / __sc; \ rect->right = (rect->right * 256) / __sc; \ rect->bottom = (rect->bottom * 256) / __sc; \ } static const char *adv_str(const char *str, int *strcnt, unsigned short *c) { int charlen=utf8char(str, c); if (strcnt && *strcnt > 0) *strcnt=wdl_max(*strcnt-charlen, 0); return str+charlen; } const char *LICE_CachedFont::NextWordBreak(const char *str, int strcnt, int w) { // returns the first character of the next line const char *next_break=NULL; while (*str && strcnt) { unsigned short c; str=adv_str(str, &strcnt, &c); if (c == '\n') return str; if (c != '\r') { charEnt *ent=findChar(c); if (ent && ent->base_offset > 0 && ent->base_offset < m_cachestore.GetSize()) { w -= ent->advance; if (w < 0) return next_break ? next_break : str; } } if (c == ' ' || c == '\t' || c == '\r') next_break=str; } return str; } int LICE_CachedFont::DrawTextImpl(LICE_IBitmap *bm, const char *str, int strcnt, RECT *rect, UINT dtFlags) { WDL_ASSERT((dtFlags & DT_SINGLELINE) || !(dtFlags & (DT_BOTTOM|DT_VCENTER))); // if DT_BOTTOM or DT_VCENTER used, must have DT_SINGLELINE if (!bm && !(dtFlags&DT_CALCRECT)) return 0; const int __sc = bm ? (int)bm->Extended(LICE_EXT_GET_SCALING,NULL) : 0; int bm_w = bm ? bm->getWidth() : 0; int bm_h = bm ? bm->getHeight() : 0; if (__sc>0 && rect) { if (!IGNORE_SCALING(m_comb)) { __LICE_SC(rect->left); __LICE_SC(rect->top); __LICE_SC(rect->right); __LICE_SC(rect->bottom); } __LICE_SC(bm_w); __LICE_SC(bm_h); } bool forceWantAlpha=false; if (dtFlags & LICE_DT_NEEDALPHA) { forceWantAlpha=true; dtFlags &= ~LICE_DT_NEEDALPHA; } if (dtFlags&DT_SINGLELINE) dtFlags &= ~DT_WORDBREAK; #ifndef _WIN32 const int lsadj = m_lsadj+3; #else const int lsadj = m_lsadj; #endif // if using line-spacing adjustments (m_lsadj), don't allow native rendering // todo: split rendering up into invidual lines and DrawText calls #ifndef LICE_TEXT_NONATIVE int ret=0; if (!bm || !bm->Extended('YUVx',NULL)) if (((m_flags&LICE_FONT_FLAG_FORCE_NATIVE) && m_font && !forceWantAlpha &&!LICE_Text_IsWine() && #ifndef _WIN32 // swell does not support DT_WORDBREAK at the moment !(dtFlags & DT_WORDBREAK) && #endif !(dtFlags & LICE_DT_USEFGALPHA) && !(m_flags&LICE_FONT_FLAG_PRECALCALL) && !LICE_FONT_FLAGS_HAS_FX(m_flags) && (!lsadj || (dtFlags&DT_SINGLELINE))) || (m_line_height >= USE_NATIVE_RENDERING_FOR_FONTS_HIGHER_THAN) ) { #ifdef _WIN32 WCHAR wtmpbuf[1024]; WCHAR *wtmp=NULL; #ifdef WDL_SUPPORT_WIN9X static int win9x; if (!win9x) win9x = GetVersion() < 0x80000000 ? -1 : 1; //>0 if win9x if (win9x<0 && LICE_Text_HasUTF8(str)) #else if (LICE_Text_HasUTF8(str)) #endif { int req = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,strcnt,NULL,0); if (req < 1000) { int cnt=0; if ((cnt=MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,strcnt,wtmpbuf,1024))) { wtmp=wtmpbuf; wtmp[cnt]=0; } } else { wtmp = (WCHAR *)malloc((req + 32)*sizeof(WCHAR)); int cnt=-1; if (wtmp && !(cnt=MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,str,strcnt,wtmp,req+1))) free(wtmp); else if (cnt>0) wtmp[cnt]=0; } } #endif HDC hdc = (bm ? bm->getDC() : 0); HGDIOBJ oldfont = 0; RECT dt_rect={0,}; bool isTmp=false; RECT tmp_rect={0,}; #ifdef _WIN32 HRGN rgn=NULL; #endif RECT nr_subbitmap_clip = {0,}; bool nr_subbitmap_clip_use=false; if (!hdc && bm) { LICE_IBitmap *bmt = bm; while (bmt->Extended(LICE_SubBitmap::LICE_GET_SUBBITMAP_VERSION,NULL) == (INT_PTR)LICE_SubBitmap::LICE_SUBBITMAP_VERSION) { LICE_SubBitmap *sb = (LICE_SubBitmap *)bmt; int sub_x = sb->m_x, sub_y = sb->m_y; if (__sc>0) { __LICE_SC(sub_x); __LICE_SC(sub_y); } nr_subbitmap_clip.left += sub_x; nr_subbitmap_clip.top += sub_y; bmt = sb->m_parent; if (!bmt) break; // ran out of parents hdc=bmt->getDC(); if (hdc) { int sub_w = ((LICE_SubBitmap*)bm)->m_w, sub_h = ((LICE_SubBitmap*)bm)->m_h; if (__sc>0) { __LICE_SC(sub_w); __LICE_SC(sub_h); } nr_subbitmap_clip.right = nr_subbitmap_clip.left + sub_w; nr_subbitmap_clip.bottom = nr_subbitmap_clip.top + sub_h; nr_subbitmap_clip_use=!(dtFlags & DT_CALCRECT); bm = bmt; break; } } } if (!hdc) { // use temp buffer -- we could optionally enable this if non-1 alpha was desired, though this only works for BLIT_MODE_COPY anyway (since it will end up compositing the whole rectangle, ugh) isTmp=true; if (!s_nativerender_tempbitmap) s_nativerender_tempbitmap = new LICE_SysBitmap; if (s_nativerender_tempbitmap->getWidth() < 4 || s_nativerender_tempbitmap->getHeight() < 4) s_nativerender_tempbitmap->resize(4,4); hdc = s_nativerender_tempbitmap->getDC(); if (!hdc) goto finish_up_native_render; oldfont = SelectObject(hdc, m_font); RECT text_size = {0,0}; ret = #ifdef _WIN32 wtmp ? ::DrawTextW(hdc,wtmp,-1,&text_size,(dtFlags&~(DT_CENTER|DT_VCENTER|DT_TOP|DT_BOTTOM|DT_LEFT|DT_RIGHT))|DT_CALCRECT|DT_NOPREFIX) : #endif ::DrawText(hdc,str,strcnt,&text_size,(dtFlags&~(DT_CENTER|DT_VCENTER|DT_TOP|DT_BOTTOM|DT_LEFT|DT_RIGHT))|DT_CALCRECT|DT_NOPREFIX); if (dtFlags & DT_CALCRECT) { rect->right = rect->left + text_size.right - text_size.left; rect->bottom = rect->top + text_size.bottom - text_size.top; goto finish_up_native_render; } if (!bm) goto finish_up_native_render; if (dtFlags & DT_RIGHT) tmp_rect.left = rect->right - text_size.right; else if (dtFlags & DT_CENTER) tmp_rect.left = (rect->right+rect->left- text_size.right)/2; else tmp_rect.left = rect->left; tmp_rect.right = tmp_rect.left + text_size.right; if (dtFlags & DT_BOTTOM) tmp_rect.top = rect->bottom - text_size.bottom; else if (dtFlags & DT_VCENTER) tmp_rect.top = (rect->bottom+rect->top- text_size.bottom)/2; else tmp_rect.top = rect->top; tmp_rect.bottom = tmp_rect.top + text_size.bottom; // tmp_rect is the desired rect of drawing, now clip to bitmap (adjusting dt_rect.top/left if starting offscreen) if (tmp_rect.right > bm_w) tmp_rect.right=bm_w; if (tmp_rect.bottom > bm_h) tmp_rect.bottom=bm_h; int lclip = 0, tclip = 0; // clip tmp_rect to rect if not DT_NOCLIP if (!(dtFlags&DT_NOCLIP)) { if (tmp_rect.right > rect->right) tmp_rect.right=rect->right; if (tmp_rect.bottom > rect->bottom) tmp_rect.bottom=rect->bottom; if (rect->left > 0) lclip = rect->left; if (rect->top > 0) tclip = rect->top; } if (tmp_rect.left < lclip) { dt_rect.left = tmp_rect.left - lclip; tmp_rect.left = lclip; } if (tmp_rect.top < tclip) { dt_rect.top = tmp_rect.top - tclip; tmp_rect.top = tclip; } if (tmp_rect.bottom <= tmp_rect.top || tmp_rect.right <= tmp_rect.left) goto finish_up_native_render; dtFlags &= ~(DT_BOTTOM|DT_RIGHT|DT_CENTER|DT_VCENTER); int left_overrender = 2+(m_line_height>=16 ? m_line_height/16 : 0); if (left_overrender>tmp_rect.left) left_overrender=tmp_rect.left; tmp_rect.left -= left_overrender; if (tmp_rect.right-tmp_rect.left > s_nativerender_tempbitmap->getWidth() || tmp_rect.bottom-tmp_rect.top > s_nativerender_tempbitmap->getHeight()) { SelectObject(hdc,oldfont); s_nativerender_tempbitmap->resize(tmp_rect.right-tmp_rect.left, tmp_rect.bottom-tmp_rect.top); hdc = s_nativerender_tempbitmap->getDC(); oldfont = SelectObject(hdc, m_font); } LICE_Blit(s_nativerender_tempbitmap, bm, 0, 0, &tmp_rect, 1.0f, LICE_BLIT_MODE_COPY); dt_rect.left += left_overrender; dt_rect.right=tmp_rect.right; dt_rect.bottom=tmp_rect.bottom; } else { oldfont = SelectObject(hdc, m_font); dt_rect = *rect; } ::SetTextColor(hdc,RGB(LICE_GETR(m_fg),LICE_GETG(m_fg),LICE_GETB(m_fg))); ::SetBkMode(hdc,m_bgmode); if (m_bgmode==OPAQUE) ::SetBkColor(hdc,RGB(LICE_GETR(m_bg),LICE_GETG(m_bg),LICE_GETB(m_bg))); if (nr_subbitmap_clip_use) { #ifdef _WIN32 rgn=CreateRectRgn(0,0,0,0); if (!GetClipRgn(hdc,rgn)) { DeleteObject(rgn); rgn=NULL; } IntersectClipRect(hdc,nr_subbitmap_clip.left,nr_subbitmap_clip.top,nr_subbitmap_clip.right,nr_subbitmap_clip.bottom); #else SWELL_PushClipRegion(hdc); SWELL_SetClipRegion(hdc,&nr_subbitmap_clip); #endif dt_rect.left += nr_subbitmap_clip.left; dt_rect.top += nr_subbitmap_clip.top; dt_rect.right += nr_subbitmap_clip.left; dt_rect.bottom += nr_subbitmap_clip.top; } ret = #ifdef _WIN32 wtmp ? ::DrawTextW(hdc,wtmp,-1,&dt_rect,dtFlags|DT_NOPREFIX) : #endif ::DrawText(hdc,str,strcnt,&dt_rect,dtFlags|DT_NOPREFIX); if (nr_subbitmap_clip_use) { #ifdef _WIN32 SelectClipRgn(hdc,rgn); // if rgn is NULL, clears region if (rgn) DeleteObject(rgn); #else SWELL_PopClipRegion(hdc); #endif } if (isTmp) LICE_Blit(bm, s_nativerender_tempbitmap, tmp_rect.left, tmp_rect.top, 0,0, tmp_rect.right-tmp_rect.left, tmp_rect.bottom-tmp_rect.top, m_alpha, // this is a slight improvement over the non-tempbuffer version, but might not be necessary... LICE_BLIT_MODE_COPY); else if (dtFlags & DT_CALCRECT) *rect = dt_rect; finish_up_native_render: if (hdc) SelectObject(hdc, oldfont); #ifdef _WIN32 if (wtmp!=wtmpbuf) free(wtmp); #endif __LICE_SC_DRAWTEXT_RESTORE_RECT if (__sc>0) ret = (ret * 256) / __sc; return ret; } #endif // ensure all glyphs rendered const char *tstr=str; int tcnt=strcnt; while (*tstr && tcnt) { unsigned short c; tstr=adv_str(tstr, &tcnt, &c); if (c == '\r') continue; if (c == '\n') { if (dtFlags & DT_SINGLELINE) c=' '; else continue; } charEnt *ent=findChar(c); if (!ent) { const int os=m_extracharlist.GetSize(); RenderGlyph(c); if (m_extracharlist.GetSize() != os) ent=findChar(c); } if (ent && ent->base_offset == 0) RenderGlyph(c); } if (dtFlags & DT_CALCRECT) { int xpos=0; int ypos=0; int max_xpos=0; int max_ypos=0; const char *next_break=NULL; while (*str && strcnt) { unsigned short c; str=adv_str(str, &strcnt, &c); if (c == '\r') continue; if (c == '\n') { if (dtFlags & DT_SINGLELINE) { c=' '; // different from win32 native behavior, which skips the character } else { if (m_flags&LICE_FONT_FLAG_VERTICAL) { xpos+=m_line_height+lsadj; ypos=0; } else { ypos+=m_line_height+lsadj; xpos=0; } if (dtFlags&DT_WORDBREAK) next_break=NULL; continue; } } charEnt *ent = findChar(c); if (ent && ent->base_offset > 0 && ent->base_offset < m_cachestore.GetSize()) { if (m_flags&LICE_FONT_FLAG_VERTICAL) { const int yext = ypos + ent->height - ent->left_extra; ypos += ent->advance; if (xpos+ent->width>max_xpos) max_xpos=xpos+ent->width; if (ypos>max_ypos) max_ypos=ypos; if (yext>max_ypos) max_ypos=yext; } else { const int xext = xpos + ent->width - ent->left_extra; xpos += ent->advance; if (ypos+ent->height>max_ypos) max_ypos=ypos+ent->height; if (xpos>max_xpos) max_xpos=xpos; if (xext>max_xpos) max_xpos=xext; } if (dtFlags&DT_WORDBREAK) { if (m_flags&LICE_FONT_FLAG_VERTICAL) { if (str == next_break) { xpos += m_line_height+lsadj; ypos=0; next_break=NULL; } if (!next_break) { next_break=NextWordBreak(str, strcnt, rect->bottom-rect->top-ypos); } } else { if (str == next_break) { ypos += m_line_height+lsadj; xpos=0; next_break=NULL; } if (!next_break) { next_break=NextWordBreak(str, strcnt, rect->right-rect->left-xpos); } } } } } rect->right = rect->left+max_xpos; rect->bottom = rect->top+max_ypos; int retval = (m_flags&LICE_FONT_FLAG_VERTICAL) ? max_xpos : max_ypos; __LICE_SC_DRAWTEXT_RESTORE_RECT if (__sc>0) return (retval * 256) / __sc; return retval; } float alphaSave = m_alpha; if (dtFlags & LICE_DT_USEFGALPHA) { m_alpha *= LICE_GETA(m_fg)/255.0; } if (m_alpha==0.0) { m_alpha=alphaSave; __LICE_SC_DRAWTEXT_RESTORE_RECT return 0; } RECT use_rect=*rect; int xpos=use_rect.left; int ypos=use_rect.top; bool isVertRev = false; if ((m_flags&(LICE_FONT_FLAG_VERTICAL|LICE_FONT_FLAG_VERTICAL_BOTTOMUP)) == (LICE_FONT_FLAG_VERTICAL|LICE_FONT_FLAG_VERTICAL_BOTTOMUP)) isVertRev=true; if (dtFlags & (DT_CENTER|DT_VCENTER|DT_RIGHT|DT_BOTTOM)) { RECT tr={0,}; DrawTextImpl(bm,str,strcnt,&tr,DT_CALCRECT|(dtFlags & DT_SINGLELINE)|(forceWantAlpha?LICE_DT_NEEDALPHA:0)); if (__sc > 0) { __LICE_SC(tr.right); __LICE_SC(tr.bottom); } if (dtFlags & DT_CENTER) { xpos += (use_rect.right-use_rect.left-tr.right)/2; } else if (dtFlags & DT_RIGHT) { xpos = use_rect.right - tr.right; } if (dtFlags & DT_VCENTER) { ypos += (use_rect.bottom-use_rect.top-tr.bottom)/2; } else if (dtFlags & DT_BOTTOM) { ypos = use_rect.bottom - tr.bottom; } if (isVertRev) ypos += tr.bottom; } else if (isVertRev) { RECT tr={0,}; DrawTextImpl(bm,str,strcnt,&tr,DT_CALCRECT|(dtFlags & DT_SINGLELINE)|(forceWantAlpha?LICE_DT_NEEDALPHA:0)); if (__sc > 0) __LICE_SC(tr.bottom); ypos += tr.bottom; } int start_y=ypos; int start_x=xpos; int max_ypos=ypos; int max_xpos=xpos; if (!(dtFlags & DT_NOCLIP)) { if (use_rect.left<0)use_rect.left=0; if (use_rect.top<0) use_rect.top=0; if (use_rect.right > bm_w) use_rect.right = bm_w; if (use_rect.bottom > bm_h) use_rect.bottom = bm_h; if (use_rect.right <= use_rect.left || use_rect.bottom <= use_rect.top) { m_alpha=alphaSave; __LICE_SC_DRAWTEXT_RESTORE_RECT return 0; } } else { use_rect.left=use_rect.top=0; use_rect.right = bm_w; use_rect.bottom = bm_h; } // todo: handle DT_END_ELLIPSIS etc // thought: calculate length of "...", then when pos+length+widthofnextchar >= right, switch // might need to precalc size to make sure it's needed, though const char *next_break=NULL; while (*str && strcnt) { unsigned short c; str=adv_str(str, &strcnt, &c); if (c == '\r') continue; if (c == '\n') { if (dtFlags & DT_SINGLELINE) { c=' '; // different from win32 native behavior, which skips the character } else { if (m_flags&LICE_FONT_FLAG_VERTICAL) { xpos+=m_line_height+lsadj; ypos=start_y; } else { ypos+=m_line_height+lsadj; xpos=start_x; } if (dtFlags&DT_WORDBREAK) next_break=NULL; continue; } } charEnt *ent = findChar(c); if (ent && ent->base_offset > 0 && ent->base_offset < m_cachestore.GetSize()) { if (isVertRev) ypos -= ent->height; bool drawn = DrawGlyph(bm,c,xpos,ypos,&use_rect); if (m_flags&LICE_FONT_FLAG_VERTICAL) { if (!isVertRev) { ypos += ent->advance; } else ypos += ent->height - ent->advance; if (drawn && xpos+ent->width > max_xpos) max_xpos=xpos; } else { xpos += ent->advance; if (drawn && ypos+ent->height>max_ypos) max_ypos=ypos+ent->height; } if (dtFlags&DT_WORDBREAK) { if (m_flags&LICE_FONT_FLAG_VERTICAL) { if (str == next_break) { xpos += m_line_height+lsadj; ypos=start_y; next_break=NULL; } if (!next_break) { next_break=NextWordBreak(str, strcnt, use_rect.bottom-ypos); } } else { if (str == next_break) { ypos += m_line_height+lsadj; xpos=start_x; next_break=NULL; } if (!next_break) { next_break=NextWordBreak(str, strcnt, use_rect.right-xpos); } } } } } m_alpha=alphaSave; int retv = (m_flags&LICE_FONT_FLAG_VERTICAL) ? max_xpos - start_x : max_ypos - start_y; __LICE_SC_DRAWTEXT_RESTORE_RECT if (__sc>0) return (retv*256)/__sc; return retv; }