textmgr.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
  1. /*
  2. LICENSE
  3. -------
  4. Copyright 2005-2013 Nullsoft, Inc.
  5. All rights reserved.
  6. Redistribution and use in source and binary forms, with or without modification,
  7. are permitted provided that the following conditions are met:
  8. * Redistributions of source code must retain the above copyright notice,
  9. this list of conditions and the following disclaimer.
  10. * Redistributions in binary form must reproduce the above copyright notice,
  11. this list of conditions and the following disclaimer in the documentation
  12. and/or other materials provided with the distribution.
  13. * Neither the name of Nullsoft nor the names of its contributors may be used to
  14. endorse or promote products derived from this software without specific prior written permission.
  15. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
  16. IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17. FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  18. CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  20. DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
  21. IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  22. OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23. */
  24. #include "textmgr.h"
  25. #include "support.h"
  26. #include "utility.h"
  27. #define MAX_MSG_CHARS (65536*2)
  28. #define SafeRelease(x) { if (x) {x->Release(); x=NULL;} }
  29. wchar_t g_szMsgPool[2][MAX_MSG_CHARS];
  30. /*
  31. NOTES ON CTextManager
  32. *** -desktop mode was SLOOOW when songtitles are on!, esp. since anim. songtitles...
  33. -> decided to cache output of ID3DXFont by rendering to a (vidmem) texture,
  34. ** only when things change. ** That became CTextManager.
  35. -uses GDI-based ID3DXFont to draw text to a 2nd (VIDEO MEMORY) surface,
  36. but each frame, it only draws what is necessary (what's changed
  37. since last frame). It then blits that image (additively) to
  38. the back buffer each frame. (note that dark boxes wouldn't work
  39. w/additive drawing, since they're black, so those have to be
  40. manually drawn (as black boxes) by the plugin shell, AS WELL AS
  41. entered into the CTextManager queue as dark boxes, to handle
  42. erasure, dirty rectangles, etc.)
  43. PROS/CONS:
  44. (+) Supports all GDI features: italics, kerning, international fonts, formatting, &, etc.
  45. (-) takes a lot of memory
  46. (-) if texture can't be created @ proper size, fonts will appear too big
  47. -> so don't use texture at all, in that case.
  48. -> at least this way it will work well on all newer cards [w/memory]
  49. (-) it's still going to crawl *when the text changes*,
  50. because d3dx will upload textures to vidmem & blit them *once for each change*.
  51. OTHER CONCERNS/KIV:
  52. -what if m_lpDDSText can't be created @ actual size of window?
  53. If it's bigger, that's ok; but if it's smaller, that should result
  54. in a clipped area for the text - hmm....
  55. */
  56. CTextManager::CTextManager()
  57. {
  58. }
  59. CTextManager::~CTextManager()
  60. {
  61. }
  62. void CTextManager::Init(LPDIRECT3DDEVICE9 lpDevice, IDirect3DTexture9* lpTextSurface, int bAdditive)
  63. {
  64. m_lpDevice = lpDevice;
  65. m_lpTextSurface = lpTextSurface;
  66. m_blit_additively = bAdditive;
  67. m_b = 0;
  68. m_nMsg[0] = 0;
  69. m_nMsg[1] = 0;
  70. m_next_msg_start_ptr = g_szMsgPool[m_b];
  71. }
  72. void CTextManager::Finish()
  73. {
  74. }
  75. void CTextManager::ClearAll()
  76. {
  77. m_nMsg[m_b] = 0;
  78. m_next_msg_start_ptr = g_szMsgPool[m_b];
  79. }
  80. void CTextManager::DrawBox(LPRECT pRect, DWORD boxColor)
  81. {
  82. if (!pRect)
  83. return;
  84. if ((m_nMsg[m_b] < MAX_MSGS) &&
  85. (DWORD)m_next_msg_start_ptr - (DWORD)g_szMsgPool[m_b] + 0 + 1 < MAX_MSG_CHARS)
  86. {
  87. *m_next_msg_start_ptr = 0;
  88. m_msg[m_b][m_nMsg[m_b]].msg = m_next_msg_start_ptr;
  89. m_msg[m_b][m_nMsg[m_b]].pfont = NULL;
  90. m_msg[m_b][m_nMsg[m_b]].rect = *pRect;
  91. m_msg[m_b][m_nMsg[m_b]].flags = 0;
  92. m_msg[m_b][m_nMsg[m_b]].color = 0xFFFFFFFF;
  93. m_msg[m_b][m_nMsg[m_b]].bgColor = boxColor;
  94. m_nMsg[m_b]++;
  95. m_next_msg_start_ptr += 1;
  96. }
  97. }
  98. int CTextManager::DrawText(LPD3DXFONT pFont, char* szText, RECT* pRect, DWORD flags, DWORD color, bool bBox, DWORD boxColor)
  99. {
  100. // these aren't supported by D3DX9:
  101. flags &= ~(DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX);
  102. if (!(pFont && pRect && szText))
  103. return 0;
  104. if (flags & DT_CALCRECT)
  105. return pFont->DrawText(NULL, szText, -1, pRect, flags, color);
  106. if (!m_lpDevice /*|| !m_lpTextSurface*/)
  107. return 0;
  108. int len = strlen(szText);
  109. if ((m_nMsg[m_b] < MAX_MSGS) &&
  110. (DWORD)m_next_msg_start_ptr - (DWORD)g_szMsgPool[m_b] + len + 1 < MAX_MSG_CHARS)
  111. {
  112. wcscpy(m_next_msg_start_ptr, AutoWide(szText));
  113. m_msg[m_b][m_nMsg[m_b]].msg = m_next_msg_start_ptr;
  114. m_msg[m_b][m_nMsg[m_b]].pfont = pFont;
  115. m_msg[m_b][m_nMsg[m_b]].rect = *pRect;
  116. m_msg[m_b][m_nMsg[m_b]].flags = flags;
  117. m_msg[m_b][m_nMsg[m_b]].color = color;
  118. m_msg[m_b][m_nMsg[m_b]].bgColor = boxColor;
  119. // shrink rects on new frame's text strings; important for deletions
  120. int h = pFont->DrawText(NULL, szText, len, &m_msg[m_b][m_nMsg[m_b]].rect, flags | DT_CALCRECT, color);
  121. m_nMsg[m_b]++;
  122. m_next_msg_start_ptr += len + 1;
  123. if (bBox)
  124. {
  125. // adds a message with no text, but the rect is the same as the text, so it creates a black box
  126. DrawBox(&m_msg[m_b][m_nMsg[m_b]-1].rect, boxColor);
  127. // now swap it with the text that precedes it, so it draws first, and becomes a background
  128. td_string x = m_msg[m_b][m_nMsg[m_b]-1];
  129. m_msg[m_b][m_nMsg[m_b]-1] = m_msg[m_b][m_nMsg[m_b]-2];
  130. m_msg[m_b][m_nMsg[m_b]-2] = x;
  131. }
  132. return h;
  133. }
  134. // no room for more text? ok, but still return accurate info:
  135. RECT r2 = *pRect;
  136. int h = pFont->DrawText(NULL, szText, len, &r2, flags | DT_CALCRECT, color);
  137. return h;
  138. }
  139. int CTextManager::DrawTextW(LPD3DXFONT pFont, wchar_t* szText, RECT* pRect, DWORD flags, DWORD color, bool bBox, DWORD boxColor)
  140. {
  141. // these aren't supported by D3DX9:
  142. flags &= ~(DT_WORD_ELLIPSIS | DT_END_ELLIPSIS | DT_NOPREFIX);
  143. if (!(pFont && pRect && szText))
  144. return 0;
  145. if (flags & DT_CALCRECT)
  146. return pFont->DrawTextW(NULL, szText, -1, pRect, flags, color);
  147. if (!m_lpDevice /*|| !m_lpTextSurface*/)
  148. return 0;
  149. int len = wcslen(szText);
  150. if ((m_nMsg[m_b] < MAX_MSGS) &&
  151. (DWORD)m_next_msg_start_ptr - (DWORD)g_szMsgPool[m_b] + len + 1 < MAX_MSG_CHARS)
  152. {
  153. wcscpy(m_next_msg_start_ptr, szText);
  154. m_msg[m_b][m_nMsg[m_b]].msg = m_next_msg_start_ptr;
  155. m_msg[m_b][m_nMsg[m_b]].pfont = pFont;
  156. m_msg[m_b][m_nMsg[m_b]].rect = *pRect;
  157. m_msg[m_b][m_nMsg[m_b]].flags = flags;
  158. m_msg[m_b][m_nMsg[m_b]].color = color;
  159. m_msg[m_b][m_nMsg[m_b]].bgColor = boxColor;
  160. // shrink rects on new frame's text strings; important for deletions
  161. int h = pFont->DrawTextW(NULL, szText, len, &m_msg[m_b][m_nMsg[m_b]].rect, flags | DT_CALCRECT, color);
  162. m_nMsg[m_b]++;
  163. m_next_msg_start_ptr += len + 1;
  164. if (bBox)
  165. {
  166. // adds a message with no text, but the rect is the same as the text, so it creates a black box
  167. DrawBox(&m_msg[m_b][m_nMsg[m_b]-1].rect, boxColor);
  168. // now swap it with the text that precedes it, so it draws first, and becomes a background
  169. td_string x = m_msg[m_b][m_nMsg[m_b]-1];
  170. m_msg[m_b][m_nMsg[m_b]-1] = m_msg[m_b][m_nMsg[m_b]-2];
  171. m_msg[m_b][m_nMsg[m_b]-2] = x;
  172. }
  173. return h;
  174. }
  175. // no room for more text? ok, but still return accurate info:
  176. RECT r2 = *pRect;
  177. int h = pFont->DrawTextW(NULL, szText, len, &r2, flags | DT_CALCRECT, color);
  178. return h;
  179. }
  180. #define MATCH(i,j) ( m_msg[m_b][i].pfont == m_msg[1-m_b][j].pfont && \
  181. m_msg[m_b][i].flags == m_msg[1-m_b][j].flags && \
  182. m_msg[m_b][i].color == m_msg[1-m_b][j].color && \
  183. m_msg[m_b][i].bgColor == m_msg[1-m_b][j].bgColor && \
  184. memcmp(&m_msg[m_b][i].rect, &m_msg[1-m_b][j].rect, sizeof(RECT))==0 && \
  185. wcscmp(m_msg[m_b][i].msg, m_msg[1-m_b][j].msg)==0 )
  186. void CTextManager::DrawNow()
  187. {
  188. if (!m_lpDevice)
  189. return;
  190. if (m_nMsg[m_b] > 0 || m_nMsg[1-m_b] > 0) // second condition req'd for clearing text in VJ mode
  191. {
  192. D3DXMATRIX Ortho2D;
  193. pMatrixOrthoLH(&Ortho2D, 2.0f, -2.0f, 0.0f, 1.0f);
  194. m_lpDevice->SetTransform(D3DTS_PROJECTION, &Ortho2D);
  195. #define NUM_DIRTY_RECTS 3
  196. RECT dirty_rect[NUM_DIRTY_RECTS];
  197. int dirty_rects_ready = 0;
  198. int bRTT = (m_lpTextSurface==NULL) ? 0 : 1;
  199. LPDIRECT3DSURFACE9 pBackBuffer=NULL;//, pZBuffer=NULL;
  200. D3DSURFACE_DESC desc_backbuf, desc_text_surface;
  201. // clear added/deleted flags
  202. void* last_dark_box = NULL;
  203. for (int i=0; i<m_nMsg[m_b]; i++)
  204. {
  205. m_msg[m_b][i].deleted = m_msg[m_b][i].added = 0;
  206. m_msg[m_b][i].prev_dark_box_ptr = last_dark_box;
  207. last_dark_box = (m_msg[m_b][i].pfont) ? last_dark_box : (void*)&m_msg[m_b][i];
  208. }
  209. last_dark_box = NULL;
  210. int j = 0;
  211. for (j = 0; j<m_nMsg[1-m_b]; j++)
  212. {
  213. m_msg[1-m_b][j].deleted = m_msg[1-m_b][j].added = 0;
  214. m_msg[1-m_b][j].prev_dark_box_ptr = last_dark_box;
  215. last_dark_box = (m_msg[1-m_b][j].pfont) ? last_dark_box : (void*)&m_msg[1-m_b][j];
  216. }
  217. int bRedrawText = 0;
  218. if (!bRTT || (m_nMsg[m_b]>0 && m_nMsg[1-m_b]==0))
  219. {
  220. bRedrawText = 2; // redraw ALL
  221. }
  222. else
  223. {
  224. // try to synchronize the text strings from last frame + this frame,
  225. // and label additions & deletions. algorithm will catch:
  226. // -insertion of any # of items in one spot
  227. // -deletion of any # of items from one spot
  228. // -changes to 1 item
  229. // -changes to 2 consecutive items
  230. // (provided that the 2 text strings immediately bounding the
  231. // additions/deletions/change(s) are left unchanged.)
  232. // in any other case, all the text is just re-rendered.
  233. int i = 0;
  234. int j = 0;
  235. while (i < m_nMsg[m_b] && j < m_nMsg[1-m_b])
  236. {
  237. // MATCH macro: first idx is record # for current stuff; second idx is record # for prev frame stuff.
  238. if (MATCH(i,j))
  239. {
  240. i++;
  241. j++;
  242. }
  243. else
  244. {
  245. int continue_now = 0;
  246. // scan to see if something was added:
  247. for (int i2=i+1; i2<m_nMsg[m_b]; i2++)
  248. if (MATCH(i2,j))
  249. {
  250. for (int i3=i; i3<i2; i3++)
  251. m_msg[m_b][i3].added = 1;
  252. i = i2;
  253. bRedrawText = 1;
  254. continue_now = 1;
  255. break;
  256. }
  257. if (continue_now)
  258. continue;
  259. // scan to see if something was deleted:
  260. for (int j2=j+1; j2<m_nMsg[1-m_b]; j2++)
  261. if (MATCH(i,j2))
  262. {
  263. for (int j3=j; j3<j2; j3++)
  264. m_msg[1-m_b][j3].deleted = 1;
  265. j = j2;
  266. bRedrawText = 1;
  267. continue_now = 1;
  268. break;
  269. }
  270. if (continue_now)
  271. continue;
  272. // scan to see if just a small group of 1-4 items were changed
  273. // [and are followed by two identical items again]
  274. int break_now = 0;
  275. for (int chgd=1; chgd<=4; chgd++)
  276. {
  277. if (i>=m_nMsg[m_b]-chgd || j>=m_nMsg[1-m_b]-chgd)
  278. {
  279. // only a few items left in one of the lists -> just finish it
  280. bRedrawText = 1;
  281. break_now = 1;
  282. break;
  283. }
  284. if (i<m_nMsg[m_b]-chgd && j<m_nMsg[1-m_b]-chgd && MATCH(i+chgd, j+chgd))
  285. {
  286. for (int k=0; k<chgd; k++)
  287. {
  288. m_msg[ m_b][i+k].added = 1;
  289. m_msg[1-m_b][j+k].deleted = 1;
  290. }
  291. i += chgd;
  292. j += chgd;
  293. bRedrawText = 1;
  294. continue_now = 1;
  295. break;
  296. }
  297. }
  298. if (break_now)
  299. break;
  300. if (continue_now)
  301. continue;
  302. // otherwise, nontrivial case -> just re-render whole thing
  303. bRedrawText = 2; // redraw ALL
  304. break;
  305. }
  306. }
  307. if (bRedrawText < 2)
  308. {
  309. while (i < m_nMsg[m_b])
  310. {
  311. m_msg[m_b][i].added = 1;
  312. bRedrawText = 1;
  313. i++;
  314. }
  315. while (j < m_nMsg[1-m_b])
  316. {
  317. m_msg[1-m_b][j].deleted = 1;
  318. bRedrawText = 1;
  319. j++;
  320. }
  321. }
  322. }
  323. // ------------------------------------------------------------
  324. // 0. remember old render target & get surface descriptions
  325. m_lpDevice->GetRenderTarget( 0, &pBackBuffer );
  326. pBackBuffer->GetDesc(&desc_backbuf);
  327. if (bRTT)
  328. {
  329. //if (m_lpDevice->GetDepthStencilSurface( &pZBuffer ) != D3D_OK)
  330. // pZBuffer = NULL; // ok if return val != D3D_OK - just means there is no zbuffer.
  331. if (m_lpTextSurface->GetLevelDesc(0, &desc_text_surface) != D3D_OK)
  332. bRTT = 0;
  333. m_lpDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
  334. m_lpDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  335. m_lpDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_CURRENT );
  336. m_lpDevice->SetTextureStageState(1, D3DTSS_COLOROP, D3DTOP_DISABLE );
  337. m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
  338. m_lpDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
  339. m_lpDevice->SetTextureStageState(1, D3DTSS_ALPHAOP, D3DTOP_DISABLE );
  340. m_lpDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
  341. }
  342. else
  343. {
  344. desc_text_surface = desc_backbuf;
  345. }
  346. if (bRTT && bRedrawText)
  347. do
  348. {
  349. // 1. change render target
  350. m_lpDevice->SetTexture(0, NULL);
  351. IDirect3DSurface9* pNewTarget = NULL;
  352. if (m_lpTextSurface->GetSurfaceLevel(0, &pNewTarget) != D3D_OK)
  353. {
  354. bRTT = 0;
  355. break;
  356. }
  357. if (m_lpDevice->SetRenderTarget(0, pNewTarget) != D3D_OK)
  358. {
  359. pNewTarget->Release();
  360. bRTT = 0;
  361. break;
  362. }
  363. //m_lpDevice->SetDepthStencilSurface( ??? );
  364. pNewTarget->Release();
  365. m_lpDevice->SetTexture(0, NULL);
  366. // 2. clear to black
  367. //m_lpDevice->SetTexture(0, NULL);
  368. m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
  369. m_lpDevice->SetVertexShader( NULL );
  370. m_lpDevice->SetFVF( WFVERTEX_FORMAT );
  371. m_lpDevice->SetPixelShader( NULL );
  372. WFVERTEX v3[4];
  373. if (bRedrawText==2)
  374. {
  375. DWORD clearcolor = m_msg[m_b][j].bgColor;//0xFF000000;// | ((rand()%32)<<16) | ((rand()%32)<<8) | ((rand()%32));
  376. int i = 0;
  377. for (i=0; i<4; i++)
  378. {
  379. v3[i].x = -1.0f + 2.0f*(i%2);
  380. v3[i].y = -1.0f + 2.0f*(i/2);
  381. v3[i].z = 0;
  382. v3[i].Diffuse = clearcolor;
  383. }
  384. m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
  385. }
  386. else
  387. {
  388. // 1. erase (draw black box over) any old text items deleted.
  389. // also, update the dirty rects; stuff that was ABOVE/BELOW these guys will need redrawn!
  390. // (..picture them staggered)
  391. int j = 0;
  392. for (j=0; j<m_nMsg[1-m_b]; j++)
  393. {
  394. // erase text from PREV frame if it was deleted.
  395. if (m_msg[1-m_b][j].deleted)
  396. {
  397. float x0 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.left/(float)desc_text_surface.Width;
  398. float x1 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.right/(float)desc_text_surface.Width;
  399. float y0 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.top/(float)desc_text_surface.Height;
  400. float y1 = -1.0f + 2.0f*m_msg[1-m_b][j].rect.bottom/(float)desc_text_surface.Height;
  401. int i = 0;
  402. for (i=0; i<4; i++)
  403. {
  404. v3[i].x = (i%2) ? x0 : x1;
  405. v3[i].y = (i/2) ? y0 : y1;
  406. v3[i].z = 0;
  407. v3[i].Diffuse = m_msg[m_b][j].bgColor;//0xFF000000;//0xFF300000;
  408. }
  409. m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
  410. //----------------------------------
  411. // special case:
  412. // if something is erased, but it's totally inside a dark box,
  413. // then don't add it to the dirty rectangle.
  414. td_string* pDarkBox = (td_string*)m_msg[1-m_b][j].prev_dark_box_ptr;
  415. int add_to_dirty_rect = 1;
  416. while (pDarkBox && add_to_dirty_rect)
  417. {
  418. RECT t;
  419. UnionRect(&t, &pDarkBox->rect, &m_msg[1-m_b][j].rect);
  420. if (EqualRect(&t, &pDarkBox->rect))
  421. add_to_dirty_rect = 0;
  422. pDarkBox = (td_string*)pDarkBox->prev_dark_box_ptr;
  423. }
  424. // also, update dirty rects
  425. // first, check to see if this shares area or a border w/any of the going dirty rects,
  426. // and if so, expand that dirty rect.
  427. if (add_to_dirty_rect)
  428. {
  429. int done = 0;
  430. RECT t;
  431. RECT r1 = m_msg[1-m_b][j].rect;
  432. RECT r2 = m_msg[1-m_b][j].rect;
  433. r2.top -= 1;
  434. r2.left -= 1;
  435. r2.right += 1;
  436. r2.bottom += 1;
  437. for (i=0; i<dirty_rects_ready; i++)
  438. {
  439. if (IntersectRect(&t, &r2, &dirty_rect[i]))
  440. {
  441. // expand the dirty rect to include r1
  442. UnionRect(&t, &r1, &dirty_rect[i]);
  443. dirty_rect[i] = t;
  444. done = 1;
  445. break;
  446. }
  447. }
  448. if (done==1) continue;
  449. // if it's in a new spot, and there are still unused dirty rects, use those
  450. if (dirty_rects_ready < NUM_DIRTY_RECTS)
  451. {
  452. dirty_rect[dirty_rects_ready] = r1;
  453. dirty_rects_ready++;
  454. continue;
  455. }
  456. // otherwise, find the closest dirty rect...
  457. float nearest_dist;
  458. int nearest_id;
  459. for (i=0; i<NUM_DIRTY_RECTS; i++)
  460. {
  461. int dx=0, dy=0;
  462. if (r1.left > dirty_rect[i].right)
  463. dx = r1.left - dirty_rect[i].right;
  464. else if (dirty_rect[i].left > r1.right)
  465. dx = dirty_rect[i].left - r1.right;
  466. if (r1.top > dirty_rect[i].bottom)
  467. dy = r1.top - dirty_rect[i].bottom;
  468. else if (dirty_rect[i].top > r1.bottom)
  469. dy = dirty_rect[i].top - r1.bottom;
  470. float dist = sqrtf((float)(dx*dx + dy*dy));
  471. if (i==0 || dist < nearest_dist)
  472. {
  473. nearest_dist = dist;
  474. nearest_id = i;
  475. }
  476. }
  477. //...and expand it to include this one.
  478. UnionRect(&t, &r1, &dirty_rect[nearest_id]);
  479. dirty_rect[nearest_id] = t;
  480. }
  481. }
  482. }
  483. // 2. erase AND REDRAW any of *this* frame's text that falls in dirty rects
  484. // from erasures of *prev* frame's deleted text:
  485. for (j=0; j<m_nMsg[m_b]; j++)
  486. {
  487. RECT t;
  488. // note: none of these could be 'deleted' status yet.
  489. if (!m_msg[m_b][j].added)
  490. {
  491. // check vs. dirty rects so far; if intersects any, erase + redraw this one.
  492. for (int i=0; i<dirty_rects_ready; i++)
  493. if (m_msg[m_b][j].pfont && // exclude dark boxes... //fixme?
  494. IntersectRect(&t, &dirty_rect[i], &m_msg[m_b][j].rect))
  495. {
  496. float x0 = -1.0f + 2.0f*m_msg[m_b][j].rect.left/(float)desc_text_surface.Width;
  497. float x1 = -1.0f + 2.0f*m_msg[m_b][j].rect.right/(float)desc_text_surface.Width;
  498. float y0 = -1.0f + 2.0f*m_msg[m_b][j].rect.top/(float)desc_text_surface.Height;
  499. float y1 = -1.0f + 2.0f*m_msg[m_b][j].rect.bottom/(float)desc_text_surface.Height;
  500. for (int i=0; i<4; i++)
  501. {
  502. v3[i].x = (i%2) ? x0 : x1;
  503. v3[i].y = (i/2) ? y0 : y1;
  504. v3[i].z = 0;
  505. v3[i].Diffuse = m_msg[m_b][j].bgColor;//0xFF000000;//0xFF000030;
  506. }
  507. m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
  508. m_msg[m_b][j].deleted = 1;
  509. m_msg[m_b][j].added = 1;
  510. bRedrawText = 1;
  511. }
  512. }
  513. }
  514. }
  515. }
  516. while (0);
  517. // 3. render text to TEXT surface
  518. if (bRedrawText)
  519. {
  520. m_lpDevice->SetTexture(0, NULL);
  521. m_lpDevice->SetTexture(1, NULL);
  522. m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
  523. m_lpDevice->SetVertexShader( NULL );
  524. m_lpDevice->SetPixelShader( NULL );
  525. m_lpDevice->SetFVF( WFVERTEX_FORMAT );
  526. for (int i=0; i<m_nMsg[m_b]; i++)
  527. if (bRedrawText==2 || m_msg[m_b][i].added==1)
  528. if (m_msg[m_b][i].pfont) // dark boxes have pfont==NULL
  529. // warning: in DX9, the DT_WORD_ELLIPSIS and DT_NOPREFIX flags cause no text to render!!
  530. 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);
  531. else if (m_msg[m_b][i].added || bRedrawText==2 || !bRTT)
  532. {
  533. WFVERTEX v3[4];
  534. float x0 = -1.0f + 2.0f*m_msg[m_b][i].rect.left/(float)desc_text_surface.Width;
  535. float x1 = -1.0f + 2.0f*m_msg[m_b][i].rect.right/(float)desc_text_surface.Width;
  536. float y0 = -1.0f + 2.0f*m_msg[m_b][i].rect.top/(float)desc_text_surface.Height;
  537. float y1 = -1.0f + 2.0f*m_msg[m_b][i].rect.bottom/(float)desc_text_surface.Height;
  538. for (int k=0; k<4; k++)
  539. {
  540. v3[k].x = (k%2) ? x0 : x1;
  541. v3[k].y = (k/2) ? y0 : y1;
  542. v3[k].z = 0;
  543. v3[k].Diffuse = m_msg[m_b][i].bgColor;//0xFF303000;
  544. }
  545. m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(WFVERTEX));
  546. }
  547. }
  548. if (bRTT)
  549. {
  550. // 4. restore render target
  551. if (bRedrawText)
  552. {
  553. m_lpDevice->SetTexture(0, NULL);
  554. m_lpDevice->SetRenderTarget( 0, pBackBuffer );//, pZBuffer );
  555. //m_lpDevice->SetDepthStencilSurface( pZBuffer );
  556. }
  557. // 5. blit text surface to backbuffer
  558. m_lpDevice->SetTexture(0, m_lpTextSurface);
  559. m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, m_blit_additively ? TRUE : FALSE);
  560. m_lpDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
  561. m_lpDevice->SetRenderState(D3DRS_DESTBLEND, m_blit_additively ? D3DBLEND_ONE : D3DBLEND_ZERO);
  562. m_lpDevice->SetVertexShader( NULL );
  563. m_lpDevice->SetPixelShader( NULL );
  564. m_lpDevice->SetFVF( SPRITEVERTEX_FORMAT );
  565. SPRITEVERTEX v3[4];
  566. ZeroMemory(v3, sizeof(SPRITEVERTEX)*4);
  567. float fx = desc_text_surface.Width / (float)desc_backbuf.Width ;
  568. float fy = desc_text_surface.Height / (float)desc_backbuf.Height;
  569. for (int i=0; i<4; i++)
  570. {
  571. v3[i].x = (i%2==0) ? -1 : -1 + 2*fx;
  572. v3[i].y = (i/2==0) ? -1 : -1 + 2*fy;
  573. v3[i].z = 0;
  574. 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!)
  575. 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!)
  576. v3[i].Diffuse = 0xFFFFFFFF;
  577. }
  578. DWORD oldblend[3];
  579. //m_lpDevice->GetTextureStageState(0, D3DTSS_MAGFILTER, &oldblend[0]);
  580. //m_lpDevice->GetTextureStageState(1, D3DTSS_MINFILTER, &oldblend[1]);
  581. //m_lpDevice->GetTextureStageState(2, D3DTSS_MIPFILTER, &oldblend[2]);
  582. m_lpDevice->GetSamplerState(0, D3DSAMP_MAGFILTER, &oldblend[0]);
  583. m_lpDevice->GetSamplerState(1, D3DSAMP_MINFILTER, &oldblend[1]);
  584. m_lpDevice->GetSamplerState(2, D3DSAMP_MIPFILTER, &oldblend[2]);
  585. m_lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
  586. m_lpDevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT);
  587. m_lpDevice->SetSamplerState(2, D3DSAMP_MIPFILTER, D3DTEXF_POINT);
  588. m_lpDevice->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, 2, v3, sizeof(SPRITEVERTEX));
  589. m_lpDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, oldblend[0]);
  590. m_lpDevice->SetSamplerState(1, D3DSAMP_MINFILTER, oldblend[1]);
  591. m_lpDevice->SetSamplerState(2, D3DSAMP_MIPFILTER, oldblend[2]);
  592. m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
  593. }
  594. SafeRelease(pBackBuffer);
  595. //SafeRelease(pZBuffer);
  596. m_lpDevice->SetTexture(0, NULL);
  597. m_lpDevice->SetTexture(1, NULL);
  598. m_lpDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
  599. m_lpDevice->SetVertexShader( NULL );
  600. m_lpDevice->SetPixelShader( NULL );
  601. m_lpDevice->SetFVF( SPRITEVERTEX_FORMAT );
  602. //D3DXMATRIX ident;
  603. //D3DXMatrixIdentity(&ident);
  604. //m_lpDevice->SetTransform(D3DTS_PROJECTION, &ident);
  605. }
  606. // flip:
  607. m_b = 1 - m_b;
  608. ClearAll();
  609. }