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