aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Visualization/vis_milk2/textmgr.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Visualization/vis_milk2/textmgr.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Visualization/vis_milk2/textmgr.cpp')
-rw-r--r--Src/Plugins/Visualization/vis_milk2/textmgr.cpp700
1 files changed, 700 insertions, 0 deletions
diff --git a/Src/Plugins/Visualization/vis_milk2/textmgr.cpp b/Src/Plugins/Visualization/vis_milk2/textmgr.cpp
new file mode 100644
index 00000000..12fe540b
--- /dev/null
+++ b/Src/Plugins/Visualization/vis_milk2/textmgr.cpp
@@ -0,0 +1,700 @@
+/*
+ LICENSE
+ -------
+Copyright 2005-2013 Nullsoft, Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ * Neither the name of Nullsoft nor the names of its contributors may be used to
+ endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "textmgr.h"
+#include "support.h"
+#include "utility.h"
+
+#define MAX_MSG_CHARS (65536*2)
+#define SafeRelease(x) { if (x) {x->Release(); x=NULL;} }
+wchar_t g_szMsgPool[2][MAX_MSG_CHARS];
+
+/*
+ NOTES ON CTextManager
+
+ *** -desktop mode was SLOOOW when songtitles are on!, esp. since anim. songtitles...
+ -> decided to cache output of ID3DXFont by rendering to a (vidmem) texture,
+ ** only when things change. ** That became CTextManager.
+ -uses GDI-based ID3DXFont to draw text to a 2nd (VIDEO MEMORY) surface,
+ but each frame, it only draws what is necessary (what's changed
+ since last frame). It then blits that image (additively) to
+ the back buffer each frame. (note that dark boxes wouldn't work
+ w/additive drawing, since they're black, so those have to be
+ manually drawn (as black boxes) by the plugin shell, AS WELL AS
+ entered into the CTextManager queue as dark boxes, to handle
+ erasure, dirty rectangles, etc.)
+
+ PROS/CONS:
+ (+) Supports all GDI features: italics, kerning, international fonts, formatting, &, etc.
+ (-) takes a lot of memory
+ (-) if texture can't be created @ proper size, fonts will appear too big
+ -> so don't use texture at all, in that case.
+ -> at least this way it will work well on all newer cards [w/memory]
+ (-) it's still going to crawl *when the text changes*,
+ because d3dx will upload textures to vidmem & blit them *once for each change*.
+
+ OTHER CONCERNS/KIV:
+ -what if m_lpDDSText can't be created @ actual size of window?
+ If it's bigger, that's ok; but if it's smaller, that should result
+ in a clipped area for the text - hmm....
+*/
+
+CTextManager::CTextManager()
+{
+}
+
+CTextManager::~CTextManager()
+{
+}
+
+void CTextManager::Init(LPDIRECT3DDEVICE9 lpDevice, IDirect3DTexture9* lpTextSurface, int bAdditive)
+{
+ m_lpDevice = lpDevice;
+ m_lpTextSurface = lpTextSurface;
+ m_blit_additively = bAdditive;
+
+ m_b = 0;
+ m_nMsg[0] = 0;
+ m_nMsg[1] = 0;
+ m_next_msg_start_ptr = g_szMsgPool[m_b];
+}
+
+void CTextManager::Finish()
+{
+}
+
+void CTextManager::ClearAll()
+{
+ m_nMsg[m_b] = 0;
+ m_next_msg_start_ptr = g_szMsgPool[m_b];
+}
+
+void CTextManager::DrawBox(LPRECT pRect, DWORD boxColor)
+{
+ if (!pRect)
+ return;
+
+ if ((m_nMsg[m_b] < MAX_MSGS) &&
+ (DWORD)m_next_msg_start_ptr - (DWORD)g_szMsgPool[m_b] + 0 + 1 < MAX_MSG_CHARS)
+ {
+ *m_next_msg_start_ptr = 0;
+
+ m_msg[m_b][m_nMsg[m_b]].msg = m_next_msg_start_ptr;
+ m_msg[m_b][m_nMsg[m_b]].pfont = NULL;
+ m_msg[m_b][m_nMsg[m_b]].rect = *pRect;
+ m_msg[m_b][m_nMsg[m_b]].flags = 0;
+ m_msg[m_b][m_nMsg[m_b]].color = 0xFFFFFFFF;
+ m_msg[m_b][m_nMsg[m_b]].bgColor = boxColor;
+ m_nMsg[m_b]++;
+ m_next_msg_start_ptr += 1;
+ }
+}
+
+int CTextManager::DrawText(LPD3DXFONT pFont, char* szText, RECT* pRect, DWORD flags, DWORD color, bool bBox, DWORD boxColor)
+{
+ // these aren't supported by D3DX9:
+ flags &= ~(DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX);
+
+ if (!(pFont && pRect && szText))
+ return 0;
+
+ if (flags & DT_CALCRECT)
+ return pFont->DrawText(NULL, szText, -1, pRect, flags, color);
+
+ if (!m_lpDevice /*|| !m_lpTextSurface*/)
+ return 0;
+
+ int len = strlen(szText);
+
+ if ((m_nMsg[m_b] < MAX_MSGS) &&
+ (DWORD)m_next_msg_start_ptr - (DWORD)g_szMsgPool[m_b] + len + 1 < MAX_MSG_CHARS)
+ {
+ wcscpy(m_next_msg_start_ptr, AutoWide(szText));
+
+ m_msg[m_b][m_nMsg[m_b]].msg = m_next_msg_start_ptr;
+ m_msg[m_b][m_nMsg[m_b]].pfont = pFont;
+ m_msg[m_b][m_nMsg[m_b]].rect = *pRect;
+ m_msg[m_b][m_nMsg[m_b]].flags = flags;
+ m_msg[m_b][m_nMsg[m_b]].color = color;
+ m_msg[m_b][m_nMsg[m_b]].bgColor = boxColor;
+
+ // shrink rects on new frame's text strings; important for deletions
+ int h = pFont->DrawText(NULL, szText, len, &m_msg[m_b][m_nMsg[m_b]].rect, flags | DT_CALCRECT, color);
+
+ m_nMsg[m_b]++;
+ m_next_msg_start_ptr += len + 1;
+
+ if (bBox)
+ {
+ // adds a message with no text, but the rect is the same as the text, so it creates a black box
+ DrawBox(&m_msg[m_b][m_nMsg[m_b]-1].rect, boxColor);
+ // now swap it with the text that precedes it, so it draws first, and becomes a background
+ td_string x = m_msg[m_b][m_nMsg[m_b]-1];
+ m_msg[m_b][m_nMsg[m_b]-1] = m_msg[m_b][m_nMsg[m_b]-2];
+ m_msg[m_b][m_nMsg[m_b]-2] = x;
+ }
+ return h;
+ }
+
+ // no room for more text? ok, but still return accurate info:
+ RECT r2 = *pRect;
+ int h = pFont->DrawText(NULL, szText, len, &r2, flags | DT_CALCRECT, color);
+ return h;
+}
+
+int CTextManager::DrawTextW(LPD3DXFONT pFont, wchar_t* szText, RECT* pRect, DWORD flags, DWORD color, bool bBox, DWORD boxColor)
+{
+ // these aren't supported by D3DX9:
+ flags &= ~(DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX);
+
+ if (!(pFont && pRect && szText))
+ return 0;
+
+ if (flags & DT_CALCRECT)
+ return pFont->DrawTextW(NULL, szText, -1, pRect, flags, color);
+
+ if (!m_lpDevice /*|| !m_lpTextSurface*/)
+ return 0;
+
+ int len = wcslen(szText);
+
+ if ((m_nMsg[m_b] < MAX_MSGS) &&
+ (DWORD)m_next_msg_start_ptr - (DWORD)g_szMsgPool[m_b] + len + 1 < MAX_MSG_CHARS)
+ {
+ wcscpy(m_next_msg_start_ptr, szText);
+
+ m_msg[m_b][m_nMsg[m_b]].msg = m_next_msg_start_ptr;
+ m_msg[m_b][m_nMsg[m_b]].pfont = pFont;
+ m_msg[m_b][m_nMsg[m_b]].rect = *pRect;
+ m_msg[m_b][m_nMsg[m_b]].flags = flags;
+ m_msg[m_b][m_nMsg[m_b]].color = color;
+ m_msg[m_b][m_nMsg[m_b]].bgColor = boxColor;
+
+ // shrink rects on new frame's text strings; important for deletions
+ int h = pFont->DrawTextW(NULL, szText, len, &m_msg[m_b][m_nMsg[m_b]].rect, flags | DT_CALCRECT, color);
+
+ m_nMsg[m_b]++;
+ m_next_msg_start_ptr += len + 1;
+
+ if (bBox)
+ {
+ // adds a message with no text, but the rect is the same as the text, so it creates a black box
+ DrawBox(&m_msg[m_b][m_nMsg[m_b]-1].rect, boxColor);
+ // now swap it with the text that precedes it, so it draws first, and becomes a background
+ td_string x = m_msg[m_b][m_nMsg[m_b]-1];
+ m_msg[m_b][m_nMsg[m_b]-1] = m_msg[m_b][m_nMsg[m_b]-2];
+ m_msg[m_b][m_nMsg[m_b]-2] = x;
+ }
+ return h;
+ }
+
+ // no room for more text? ok, but still return accurate info:
+ RECT r2 = *pRect;
+ int h = pFont->DrawTextW(NULL, szText, len, &r2, flags | DT_CALCRECT, color);
+ return h;
+}
+
+#define MATCH(i,j) ( m_msg[m_b][i].pfont == m_msg[1-m_b][j].pfont && \
+ m_msg[m_b][i].flags == m_msg[1-m_b][j].flags && \
+ m_msg[m_b][i].color == m_msg[1-m_b][j].color && \
+ m_msg[m_b][i].bgColor == m_msg[1-m_b][j].bgColor && \
+ memcmp(&m_msg[m_b][i].rect, &m_msg[1-m_b][j].rect, sizeof(RECT))==0 && \
+ wcscmp(m_msg[m_b][i].msg, m_msg[1-m_b][j].msg)==0 )
+
+void CTextManager::DrawNow()
+{
+ if (!m_lpDevice)
+ return;
+
+ if (m_nMsg[m_b] > 0 || m_nMsg[1-m_b] > 0) // second condition req'd for clearing text in VJ mode
+ {
+ D3DXMATRIX Ortho2D;
+ pMatrixOrthoLH(&Ortho2D, 2.0f, -2.0f, 0.0f, 1.0f);
+ m_lpDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D);
+
+ #define NUM_DIRTY_RECTS 3
+ RECT dirty_rect[NUM_DIRTY_RECTS];
+ int dirty_rects_ready = 0;
+
+ int bRTT = (m_lpTextSurface==NULL) ? 0 : 1;
+ LPDIRECT3DSURFACE9 pBackBuffer=NULL;//, pZBuffer=NULL;
+ D3DSURFACE_DESC desc_backbuf, desc_text_surface;
+
+ // clear added/deleted flags
+ void* last_dark_box = NULL;
+ for (int i=0; i<m_nMsg[m_b]; i++)
+ {
+ m_msg[m_b][i].deleted = m_msg[m_b][i].added = 0;
+ m_msg[m_b][i].prev_dark_box_ptr = last_dark_box;
+ last_dark_box = (m_msg[m_b][i].pfont) ? last_dark_box : (void*)&m_msg[m_b][i];
+ }
+ last_dark_box = NULL;
+ int j = 0;
+ for (j = 0; j<m_nMsg[1-m_b]; j++)
+ {
+ m_msg[1-m_b][j].deleted = m_msg[1-m_b][j].added = 0;
+ m_msg[1-m_b][j].prev_dark_box_ptr = last_dark_box;
+ last_dark_box = (m_msg[1-m_b][j].pfont) ? last_dark_box : (void*)&m_msg[1-m_b][j];
+ }
+
+ int bRedrawText = 0;
+ if (!bRTT || (m_nMsg[m_b]>0 && m_nMsg[1-m_b]==0))
+ {
+ bRedrawText = 2; // redraw ALL
+ }
+ else
+ {
+ // try to synchronize the text strings from last frame + this frame,
+ // and label additions & deletions. algorithm will catch:
+ // -insertion of any # of items in one spot
+ // -deletion of any # of items from one spot
+ // -changes to 1 item
+ // -changes to 2 consecutive items
+ // (provided that the 2 text strings immediately bounding the
+ // additions/deletions/change(s) are left unchanged.)
+ // in any other case, all the text is just re-rendered.
+
+ int i = 0;
+ int j = 0;
+ while (i < m_nMsg[m_b] && j < m_nMsg[1-m_b])
+ {
+ // MATCH macro: first idx is record # for current stuff; second idx is record # for prev frame stuff.
+ if (MATCH(i,j))
+ {
+ i++;
+ j++;
+ }
+ else
+ {
+ int continue_now = 0;
+
+ // scan to see if something was added:
+ for (int i2=i+1; i2<m_nMsg[m_b]; i2++)
+ if (MATCH(i2,j))
+ {
+ for (int i3=i; i3<i2; i3++)
+ m_msg[m_b][i3].added = 1;
+ i = i2;
+ bRedrawText = 1;
+ continue_now = 1;
+ break;
+ }
+ if (continue_now)
+ continue;
+
+ // scan to see if something was deleted:
+ for (int j2=j+1; j2<m_nMsg[1-m_b]; j2++)
+ if (MATCH(i,j2))
+ {
+ for (int j3=j; j3<j2; j3++)
+ m_msg[1-m_b][j3].deleted = 1;
+ j = j2;
+ bRedrawText = 1;
+ continue_now = 1;
+ break;
+ }
+ if (continue_now)
+ continue;
+
+ // scan to see if just a small group of 1-4 items were changed
+ // [and are followed by two identical items again]
+ int break_now = 0;
+ for (int chgd=1; chgd<=4; chgd++)
+ {
+ if (i>=m_nMsg[m_b]-chgd || j>=m_nMsg[1-m_b]-chgd)
+ {
+ // only a few items left in one of the lists -> just finish it
+ bRedrawText = 1;
+ break_now = 1;
+ break;
+ }
+ if (i<m_nMsg[m_b]-chgd && j<m_nMsg[1-m_b]-chgd && MATCH(i+chgd, j+chgd))
+ {
+ for (int k=0; k<chgd; k++)
+ {
+ m_msg[ m_b][i+k].added = 1;
+ m_msg[1-m_b][j+k].deleted = 1;
+ }
+ i += chgd;
+ j += chgd;
+
+ bRedrawText = 1;
+ continue_now = 1;
+ break;
+ }
+ }
+ if (break_now)
+ break;
+ if (continue_now)
+ continue;
+
+ // otherwise, nontrivial case -> just re-render whole thing
+ bRedrawText = 2; // redraw ALL
+ break;
+ }
+ }
+
+ if (bRedrawText < 2)
+ {
+ while (i < m_nMsg[m_b])
+ {
+ m_msg[m_b][i].added = 1;
+ bRedrawText = 1;
+ i++;
+ }
+
+ while (j < m_nMsg[1-m_b])
+ {
+ m_msg[1-m_b][j].deleted = 1;
+ bRedrawText = 1;
+ j++;
+ }
+ }
+ }
+
+ // ------------------------------------------------------------
+
+ // 0. remember old render target & get surface descriptions
+ m_lpDevice->GetRenderTarget( 0, &pBackBuffer );
+ pBackBuffer->GetDesc(&desc_backbuf);
+
+ if (bRTT)
+ {
+ //if (m_lpDevice->GetDepthStencilSurface( &pZBuffer ) != D3D_OK)
+ // pZBuffer = NULL; // ok if return val != D3D_OK - just means there is no zbuffer.
+ if (m_lpTextSurface->GetLevelDesc(0, &desc_text_surface) != D3D_OK)
+ bRTT = 0;
+
+ m_lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
+ m_lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+ m_lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT );
+ m_lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE );
+
+ m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
+ m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
+ m_lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
+
+ m_lpDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
+ }
+ else
+ {
+ desc_text_surface = desc_backbuf;
+ }
+
+ if (bRTT && bRedrawText)
+ do
+ {
+ // 1. change render target
+ m_lpDevice->SetTexture(0, NULL);
+
+ IDirect3DSurface9* pNewTarget = NULL;
+ if (m_lpTextSurface->GetSurfaceLevel(0, &pNewTarget) != D3D_OK)
+ {
+ bRTT = 0;
+ break;
+ }
+ if (m_lpDevice->SetRenderTarget(0, pNewTarget) != D3D_OK)
+ {
+ pNewTarget->Release();
+ bRTT = 0;
+ break;
+ }
+ //m_lpDevice->SetDepthStencilSurface( ??? );
+ pNewTarget->Release();
+
+ m_lpDevice->SetTexture(0, NULL);
+
+ // 2. clear to black
+ //m_lpDevice->SetTexture(0, NULL);
+ m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
+ m_lpDevice->SetVertexShader( NULL );
+ m_lpDevice->SetFVF( WFVERTEX_FORMAT );
+ m_lpDevice->SetPixelShader( NULL );
+ WFVERTEX v3[4];
+ if (bRedrawText==2)
+ {
+ DWORD clearcolor = m_msg[m_b][j].bgColor;//0xFF000000;// | ((rand()%32)<<16) | ((rand()%32)<<8) | ((rand()%32));
+ int i = 0;
+ for (i=0; i<4; i++)
+ {
+ v3[i].x = -1.0f + 2.0f*(i%2);
+ v3[i].y = -1.0f + 2.0f*(i/2);
+ v3[i].z = 0;
+ v3[i].Diffuse = clearcolor;
+ }
+ m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
+ }
+ else
+ {
+ // 1. erase (draw black box over) any old text items deleted.
+ // also, update the dirty rects; stuff that was ABOVE/BELOW these guys will need redrawn!
+ // (..picture them staggered)
+ int j = 0;
+ for (j=0; j<m_nMsg[1-m_b]; j++)
+ {
+ // erase text from PREV frame if it was deleted.
+ if (m_msg[1-m_b][j].deleted)
+ {
+ float x0 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.left/(float)desc_text_surface.Width;
+ float x1 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.right/(float)desc_text_surface.Width;
+ float y0 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.top/(float)desc_text_surface.Height;
+ float y1 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.bottom/(float)desc_text_surface.Height;
+ int i = 0;
+ for (i=0; i<4; i++)
+ {
+ v3[i].x = (i%2) ? x0 : x1;
+ v3[i].y = (i/2) ? y0 : y1;
+ v3[i].z = 0;
+ v3[i].Diffuse = m_msg[m_b][j].bgColor;//0xFF000000;//0xFF300000;
+ }
+ m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
+
+ //----------------------------------
+
+ // special case:
+ // if something is erased, but it's totally inside a dark box,
+ // then don't add it to the dirty rectangle.
+ td_string* pDarkBox = (td_string*)m_msg[1-m_b][j].prev_dark_box_ptr;
+ int add_to_dirty_rect = 1;
+ while (pDarkBox && add_to_dirty_rect)
+ {
+ RECT t;
+ UnionRect(&t, &pDarkBox->rect, &m_msg[1-m_b][j].rect);
+ if (EqualRect(&t, &pDarkBox->rect))
+ add_to_dirty_rect = 0;
+ pDarkBox = (td_string*)pDarkBox->prev_dark_box_ptr;
+ }
+
+ // also, update dirty rects
+ // first, check to see if this shares area or a border w/any of the going dirty rects,
+ // and if so, expand that dirty rect.
+ if (add_to_dirty_rect)
+ {
+ int done = 0;
+ RECT t;
+ RECT r1 = m_msg[1-m_b][j].rect;
+ RECT r2 = m_msg[1-m_b][j].rect;
+ r2.top -= 1;
+ r2.left -= 1;
+ r2.right += 1;
+ r2.bottom += 1;
+ for (i=0; i<dirty_rects_ready; i++)
+ {
+ if (IntersectRect(&t, &r2, &dirty_rect[i]))
+ {
+ // expand the dirty rect to include r1
+ UnionRect(&t, &r1, &dirty_rect[i]);
+ dirty_rect[i] = t;
+ done = 1;
+ break;
+ }
+ }
+ if (done==1) continue;
+
+ // if it's in a new spot, and there are still unused dirty rects, use those
+ if (dirty_rects_ready < NUM_DIRTY_RECTS)
+ {
+ dirty_rect[dirty_rects_ready] = r1;
+ dirty_rects_ready++;
+ continue;
+ }
+
+ // otherwise, find the closest dirty rect...
+ float nearest_dist;
+ int nearest_id;
+ for (i=0; i<NUM_DIRTY_RECTS; i++)
+ {
+ int dx=0, dy=0;
+
+ if (r1.left > dirty_rect[i].right)
+ dx = r1.left - dirty_rect[i].right;
+ else if (dirty_rect[i].left > r1.right)
+ dx = dirty_rect[i].left - r1.right;
+
+ if (r1.top > dirty_rect[i].bottom)
+ dy = r1.top - dirty_rect[i].bottom;
+ else if (dirty_rect[i].top > r1.bottom)
+ dy = dirty_rect[i].top - r1.bottom;
+
+ float dist = sqrtf((float)(dx*dx + dy*dy));
+ if (i==0 || dist < nearest_dist)
+ {
+ nearest_dist = dist;
+ nearest_id = i;
+ }
+ }
+ //...and expand it to include this one.
+ UnionRect(&t, &r1, &dirty_rect[nearest_id]);
+ dirty_rect[nearest_id] = t;
+ }
+ }
+ }
+
+ // 2. erase AND REDRAW any of *this* frame's text that falls in dirty rects
+ // from erasures of *prev* frame's deleted text:
+ for (j=0; j<m_nMsg[m_b]; j++)
+ {
+ RECT t;
+ // note: none of these could be 'deleted' status yet.
+ if (!m_msg[m_b][j].added)
+ {
+ // check vs. dirty rects so far; if intersects any, erase + redraw this one.
+ for (int i=0; i<dirty_rects_ready; i++)
+ if (m_msg[m_b][j].pfont && // exclude dark boxes... //fixme?
+ IntersectRect(&t, &dirty_rect[i], &m_msg[m_b][j].rect))
+ {
+ float x0 = -1.0f + 2.0f*m_msg[m_b][j].rect.left/(float)desc_text_surface.Width;
+ float x1 = -1.0f + 2.0f*m_msg[m_b][j].rect.right/(float)desc_text_surface.Width;
+ float y0 = -1.0f + 2.0f*m_msg[m_b][j].rect.top/(float)desc_text_surface.Height;
+ float y1 = -1.0f + 2.0f*m_msg[m_b][j].rect.bottom/(float)desc_text_surface.Height;
+ for (int i=0; i<4; i++)
+ {
+ v3[i].x = (i%2) ? x0 : x1;
+ v3[i].y = (i/2) ? y0 : y1;
+ v3[i].z = 0;
+ v3[i].Diffuse = m_msg[m_b][j].bgColor;//0xFF000000;//0xFF000030;
+ }
+ m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
+
+ m_msg[m_b][j].deleted = 1;
+ m_msg[m_b][j].added = 1;
+ bRedrawText = 1;
+ }
+ }
+ }
+ }
+ }
+ while (0);
+
+ // 3. render text to TEXT surface
+ if (bRedrawText)
+ {
+ m_lpDevice->SetTexture(0, NULL);
+ m_lpDevice->SetTexture(1, NULL);
+ m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
+ m_lpDevice->SetVertexShader( NULL );
+ m_lpDevice->SetPixelShader( NULL );
+ m_lpDevice->SetFVF( WFVERTEX_FORMAT );
+
+ for (int i=0; i<m_nMsg[m_b]; i++)
+ if (bRedrawText==2 || m_msg[m_b][i].added==1)
+ if (m_msg[m_b][i].pfont) // dark boxes have pfont==NULL
+ // warning: in DX9, the DT_WORD_ELLIPSIS and DT_NOPREFIX flags cause no text to render!!
+ m_msg[m_b][i].pfont->DrawTextW(NULL, m_msg[m_b][i].msg, -1, &m_msg[m_b][i].rect, m_msg[m_b][i].flags, m_msg[m_b][i].color);
+ else if (m_msg[m_b][i].added || bRedrawText==2 || !bRTT)
+ {
+ WFVERTEX v3[4];
+ float x0 = -1.0f + 2.0f*m_msg[m_b][i].rect.left/(float)desc_text_surface.Width;
+ float x1 = -1.0f + 2.0f*m_msg[m_b][i].rect.right/(float)desc_text_surface.Width;
+ float y0 = -1.0f + 2.0f*m_msg[m_b][i].rect.top/(float)desc_text_surface.Height;
+ float y1 = -1.0f + 2.0f*m_msg[m_b][i].rect.bottom/(float)desc_text_surface.Height;
+ for (int k=0; k<4; k++)
+ {
+ v3[k].x = (k%2) ? x0 : x1;
+ v3[k].y = (k/2) ? y0 : y1;
+ v3[k].z = 0;
+ v3[k].Diffuse = m_msg[m_b][i].bgColor;//0xFF303000;
+ }
+ m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
+ }
+ }
+
+ if (bRTT)
+ {
+ // 4. restore render target
+ if (bRedrawText)
+ {
+ m_lpDevice->SetTexture(0, NULL);
+ m_lpDevice->SetRenderTarget( 0, pBackBuffer );//, pZBuffer );
+ //m_lpDevice->SetDepthStencilSurface( pZBuffer );
+ }
+
+ // 5. blit text surface to backbuffer
+ m_lpDevice->SetTexture(0, m_lpTextSurface);
+ m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, m_blit_additively ? TRUE : FALSE);
+ m_lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
+ m_lpDevice->SetRenderState(D3DRS_DESTBLEND, m_blit_additively ? D3DBLEND_ONE : D3DBLEND_ZERO);
+ m_lpDevice->SetVertexShader( NULL );
+ m_lpDevice->SetPixelShader( NULL );
+ m_lpDevice->SetFVF( SPRITEVERTEX_FORMAT );
+
+ SPRITEVERTEX v3[4];
+ ZeroMemory(v3, sizeof(SPRITEVERTEX)*4);
+ float fx = desc_text_surface.Width / (float)desc_backbuf.Width ;
+ float fy = desc_text_surface.Height / (float)desc_backbuf.Height;
+ for (int i=0; i<4; i++)
+ {
+ v3[i].x = (i%2==0) ? -1 : -1 + 2*fx;
+ v3[i].y = (i/2==0) ? -1 : -1 + 2*fy;
+ v3[i].z = 0;
+ v3[i].tu = ((i%2==0) ? 0.0f : 1.0f) + 0.5f/desc_text_surface.Width; // FIXES BLURRY TEXT even when bilinear interp. is on (which can't be turned off on all cards!)
+ v3[i].tv = ((i/2==0) ? 0.0f : 1.0f) + 0.5f/desc_text_surface.Height; // FIXES BLURRY TEXT even when bilinear interp. is on (which can't be turned off on all cards!)
+ v3[i].Diffuse = 0xFFFFFFFF;
+ }
+
+ DWORD oldblend[3];
+ //m_lpDevice->GetTextureStageState(0, D3DTSS_MAGFILTER, &oldblend[0]);
+ //m_lpDevice->GetTextureStageState(1, D3DTSS_MINFILTER, &oldblend[1]);
+ //m_lpDevice->GetTextureStageState(2, D3DTSS_MIPFILTER, &oldblend[2]);
+ m_lpDevice->GetSamplerState(0, D3DSAMP_MAGFILTER, &oldblend[0]);
+ m_lpDevice->GetSamplerState(1, D3DSAMP_MINFILTER, &oldblend[1]);
+ m_lpDevice->GetSamplerState(2, D3DSAMP_MIPFILTER, &oldblend[2]);
+ m_lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
+ m_lpDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT);
+ m_lpDevice->SetSamplerState(2, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
+
+ m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(SPRITEVERTEX));
+
+ m_lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, oldblend[0]);
+ m_lpDevice->SetSamplerState(1, D3DSAMP_MINFILTER, oldblend[1]);
+ m_lpDevice->SetSamplerState(2, D3DSAMP_MIPFILTER, oldblend[2]);
+
+ m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
+ }
+
+ SafeRelease(pBackBuffer);
+ //SafeRelease(pZBuffer);
+
+ m_lpDevice->SetTexture(0, NULL);
+ m_lpDevice->SetTexture(1, NULL);
+ m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
+ m_lpDevice->SetVertexShader( NULL );
+ m_lpDevice->SetPixelShader( NULL );
+ m_lpDevice->SetFVF( SPRITEVERTEX_FORMAT );
+
+ //D3DXMATRIX ident;
+ //D3DXMatrixIdentity(&ident);
+ //m_lpDevice->SetTransform(D3DTS_PROJECTION, &ident);
+ }
+
+ // flip:
+ m_b = 1 - m_b;
+
+ ClearAll();
+} \ No newline at end of file