1 // DirectDraw blitter
2
3 #include "burner.h"
4
5 #if !defined BUILD_X64_EXE
6 #include "vid_directx_support.h"
7 #endif
8
9 #include <InitGuid.h>
10 #define DIRECT3D_VERSION 0x0700 // Use this Direct3D version
11
12 #if defined BUILD_X64_EXE
13 #include "vid_directx_support.h"
14 #endif
15
16 #include "ddraw_core.h"
17
18 static IDirectDraw7* DtoDD = NULL; // DirectDraw interface
19 static IDirectDrawSurface7* DtoPrim = NULL; // Primary surface
20 static IDirectDrawSurface7* DtoBack = NULL; // Back buffer surface
21
22 static int nRotateGame;
23 static bool bRotateScanlines;
24 static DDBLTFX* DtoBltFx = NULL; // We use mirrored blits for flipped games
25
26 static IDirectDrawSurface7* pddsDtos = NULL; // The screen surface
27 static int nGameWidth = 0, nGameHeight = 0; // screen size
28
29 static bool bDtosScan;
30
31 static RECT Src = { 0, 0, 0, 0 };
32 static RECT Dest = { 0, 0, 0, 0 };
33
34 static int nHalfMask = 0;
35
36 static int nUseSys; // Use System or Video memory
37
DtoPrimClear()38 static int DtoPrimClear()
39 {
40 if (DtoPrim == NULL) {
41 return 1;
42 }
43
44 VidSClearSurface(DtoPrim, 0, NULL); // Clear 1st page
45
46 if (DtoBack) { // We're using a triple buffer
47 VidSClearSurface(DtoBack, 0, NULL); // Clear 2nd page
48 DtoPrim->Flip(NULL, DDFLIP_WAIT);
49 VidSClearSurface(DtoBack, 0, NULL); // Clear 3rd page
50 }
51
52 return 0;
53 }
54
DtoPrimInit(int bTriple)55 static int DtoPrimInit(int bTriple)
56 {
57 DDSURFACEDESC2 ddsd;
58
59 // Create the primary surface
60 memset(&ddsd, 0, sizeof(ddsd));
61 ddsd.dwSize = sizeof(ddsd);
62 if (bTriple) { // Make a primary surface capable of triple bufferring
63 ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
64 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX | DDSCAPS_VIDEOMEMORY;
65 ddsd.dwBackBufferCount = 2;
66 } else {
67 ddsd.dwFlags = DDSD_CAPS;
68 ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
69 }
70
71 if (FAILED(DtoDD->CreateSurface(&ddsd, &DtoPrim, NULL))) {
72 return 1;
73 }
74
75 if (bTriple) {
76 // Get the back buffer
77 memset(&ddsd.ddsCaps, 0, sizeof(ddsd.ddsCaps));
78 ddsd.ddsCaps.dwCaps = DDSCAPS_BACKBUFFER;
79
80 if (FAILED(DtoPrim->GetAttachedSurface(&ddsd.ddsCaps, &DtoBack))) { // Failed to make triple buffer
81 RELEASE(DtoPrim)
82 return 1;
83 }
84
85 DtoPrimClear(); // Clear surfaces
86 }
87
88 return 0;
89 }
90
91 // Try to autodetect the best secondary buffer type to use, based on the hardware capabilities
AutodetectUseSys()92 static int AutodetectUseSys()
93 {
94 DDCAPS ddc;
95
96 memset(&ddc, 0, sizeof(ddc));
97 ddc.dwSize = sizeof(ddc);
98 DtoDD->GetCaps(&ddc, NULL);
99
100 if (ddc.dwCaps & DDCAPS_BLTSTRETCH) { // If it can do a hardware stretch use video memory
101 return 0;
102 } else { // Otherwise use system memory:
103 return 1;
104 }
105 }
106
107 // Create a secondary DirectDraw surface for the game image
DtosMakeSurf()108 static int DtosMakeSurf()
109 {
110 int nRet;
111
112 nUseSys = 0;
113 DDSURFACEDESC2 ddsd;
114
115 if (DtoDD == NULL) {
116 return 1;
117 }
118
119 nUseSys = nVidTransferMethod;
120 if (nUseSys < 0) {
121 nUseSys = AutodetectUseSys();
122 }
123
124 // Try to allocate buffer in Video memory first, if that fails use System memory
125 do {
126 memset(&ddsd, 0, sizeof(ddsd));
127 ddsd.dwSize = sizeof(ddsd);
128
129 ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT;
130 ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
131 if (nUseSys == 0) {
132 ddsd.ddsCaps.dwCaps |= DDSCAPS_VIDEOMEMORY;
133 } else {
134 if (nUseSys == 1) {
135 ddsd.ddsCaps.dwCaps |= DDSCAPS_SYSTEMMEMORY;
136 }
137 }
138
139 ddsd.dwWidth = nGameWidth << 1; // Make the surface large enough to add scanlines
140 ddsd.dwHeight = nGameHeight << 1; //
141
142 nRet = DtoDD->CreateSurface(&ddsd, &pddsDtos, NULL);
143
144 if (SUCCEEDED(nRet)) { // Break early, so nUseSys will keep its value
145 break;
146 }
147
148 } while (++nUseSys <= 1);
149
150 if (FAILED(nRet)) {
151 return 1;
152 }
153
154 nVidScrnDepth = VidSGetSurfaceDepth(pddsDtos); // Get colourdepth of primary surface
155
156 VidSClearSurface(pddsDtos, 0, NULL);
157
158 return 0;
159 }
160
DtosExit()161 static int DtosExit()
162 {
163 RELEASE(pddsDtos);
164
165 VidSFreeVidImage();
166
167 VidSExitOSD();
168
169 return 0;
170 }
171
DtosInit()172 static int DtosInit()
173 {
174 if (DtoDD == NULL) {
175 return 1;
176 }
177
178 if (nRotateGame & 1) {
179 nVidImageWidth = nGameHeight;
180 nVidImageHeight = nGameWidth;
181 } else {
182 nVidImageWidth = nGameWidth;
183 nVidImageHeight = nGameHeight;
184 }
185
186 if (bVidScanRotate && nGameWidth < nGameHeight) {
187 bRotateScanlines = true;
188 } else {
189 bRotateScanlines = false;
190 }
191
192 nVidImageDepth = VidSGetSurfaceDepth(DtoPrim); // Get color depth of primary surface
193 nVidImageBPP = (nVidImageDepth + 7) >> 3;
194
195 // Make the mask to mask out all but the lowest intensity bit
196 if (nVidImageDepth == 15) {
197 nHalfMask = 0xFBDEFBDE;
198 } else {
199 if (nVidImageDepth == 16) {
200 nHalfMask = 0xF7DEF7DE;
201 } else {
202 nHalfMask = 0xFEFEFEFE;
203 }
204 }
205
206 // Make the normal memory buffer
207 if (VidSAllocVidImage()) {
208 DtosExit();
209 return 1;
210 }
211
212 // Make the DirectDraw secondary surface
213 if (DtosMakeSurf()) {
214 DtosExit();
215 return 1;
216 }
217
218 // Use our callback to get colors:
219 SetBurnHighCol(nVidImageDepth);
220
221 Dest.left = 0; Dest.right = -1;
222
223 RECT rect = { 0, 0, 0, 0 };
224 GetClientScreenRect(hVidWnd, &rect);
225 rect.top += nMenuHeight;
226
227 VidSScaleImage(&rect, nGameWidth, nGameHeight, bVidScanRotate);
228
229 VidSInitOSD(4);
230
231 return 0;
232 }
233
vidExit()234 static int vidExit()
235 {
236 VidSRestoreGamma();
237
238 VidSRestoreScreenMode();
239
240 DtosExit();
241
242 RELEASE(DtoPrim) // a single call releases all surfaces
243 DtoBack = NULL;
244
245 VidSExit();
246
247 if (DtoBltFx) {
248 free(DtoBltFx);
249 DtoBltFx = NULL;
250 }
251
252 RELEASE(DtoDD)
253
254 return 0;
255 }
256
257 static GUID MyGuid;
258 static int nWantDriver;
259
260 #ifdef PRINT_DEBUG_INFO
261 static int nCurrentDriver;
262 #ifdef UNICODE
MyEnumDisplayDrivers(GUID FAR * pGuid,LPWSTR pszDesc,LPWSTR,LPVOID,HMONITOR)263 static BOOL PASCAL MyEnumDisplayDrivers(GUID FAR* pGuid, LPWSTR pszDesc, LPWSTR /*pszName*/, LPVOID /*pContext*/, HMONITOR /*hMonitor*/)
264 #else
265 static BOOL PASCAL MyEnumDisplayDrivers(GUID FAR* pGuid, LPSTR pszDesc, LPSTR /*pszName*/, LPVOID /*pContext*/, HMONITOR /*hMonitor*/)
266 #endif
267 {
268 if (nCurrentDriver == nWantDriver) {
269 memcpy(&MyGuid, pGuid, sizeof(GUID));
270 }
271
272 if (nCurrentDriver == 0) {
273 dprintf(_T(" %s\n"), pszDesc);
274 } else {
275 dprintf(_T(" Display %d (on %s)\n"), nCurrentDriver, pszDesc);
276 }
277
278 nCurrentDriver++;
279
280 return DDENUMRET_OK;
281 }
282 #endif
283
vidInit()284 static int vidInit()
285 {
286 hVidWnd = hScrnWnd; // Use Screen window for video
287
288 #ifdef PRINT_DEBUG_INFO
289 dprintf(_T(" * Enumerating available drivers:\n"));
290 nWantDriver = 0;
291 nCurrentDriver = 0;
292 _DirectDrawEnumerateEx(MyEnumDisplayDrivers, NULL, DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_DETACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES);
293 #endif
294
295 // Get pointer to DirectDraw device
296 _DirectDrawCreateEx(nWantDriver ? &MyGuid : NULL, (void**)&DtoDD, IID_IDirectDraw7, NULL);
297
298 VidSInit(DtoDD);
299
300 nGameWidth = nVidImageWidth; nGameHeight = nVidImageHeight;
301
302 nRotateGame = 0;
303 if (bDrvOkay) {
304 DtoBltFx = NULL;
305
306 // Get the game screen size
307 BurnDrvGetVisibleSize(&nGameWidth, &nGameHeight);
308
309 if (BurnDrvGetFlags() & BDF_ORIENTATION_VERTICAL) {
310 if (nVidRotationAdjust & 1) {
311 int n = nGameWidth;
312 nGameWidth = nGameHeight;
313 nGameHeight = n;
314 nRotateGame |= (nVidRotationAdjust & 2);
315 } else {
316 nRotateGame |= 1;
317 }
318 }
319 if (BurnDrvGetFlags() & BDF_ORIENTATION_FLIPPED) {
320 nRotateGame ^= 2;
321 }
322
323 if (nRotateGame & 2) {
324 DDCAPS ddcaps;
325
326 // Disable flipping until we've checked the hardware supports it
327 nRotateGame &= ~2;
328
329 memset(&ddcaps, 0, sizeof(ddcaps));
330 ddcaps.dwSize = sizeof(ddcaps);
331
332 DtoDD->GetCaps(&ddcaps, NULL);
333 if (((ddcaps.dwFXCaps & DDFXCAPS_BLTMIRRORLEFTRIGHT) && (ddcaps.dwFXCaps & DDFXCAPS_BLTMIRRORUPDOWN)) || bVidForceFlip) {
334
335 DtoBltFx = (DDBLTFX*)malloc(sizeof(DDBLTFX));
336 if (DtoBltFx == NULL) {
337 vidExit();
338 return 1;
339 }
340
341 memset(DtoBltFx, 0, sizeof(DDBLTFX));
342
343 DtoBltFx->dwSize = sizeof(DDBLTFX);
344 DtoBltFx->dwDDFX = DDBLTFX_MIRRORLEFTRIGHT | DDBLTFX_MIRRORUPDOWN;
345
346 // Enable flipping now
347 nRotateGame |= 2;
348 }
349 }
350 }
351
352 DtoPrim = NULL; // No primary surface yet
353 DtoBack = NULL;
354
355 // Remember the changes to the display
356 if (nVidFullscreen) {
357 if (VidSEnterFullscreenMode(nScreenSize, 0)) {
358 vidExit();
359 return 1;
360 }
361 } else {
362 DtoDD->SetCooperativeLevel(hVidWnd, DDSCL_NORMAL);
363 }
364
365 #ifdef PRINT_DEBUG_INFO
366 {
367 // Display amount of free video memory
368 DDSCAPS2 ddsCaps2;
369 DWORD dwTotal;
370 DWORD dwFree;
371
372 memset(&ddsCaps2, 0, sizeof(ddsCaps2));
373 ddsCaps2.dwCaps = DDSCAPS_PRIMARYSURFACE;
374
375 dprintf(_T(" ** Starting DirectDraw7 blitter.\n"));
376
377 if (SUCCEEDED(DtoDD->GetAvailableVidMem(&ddsCaps2, &dwTotal, &dwFree))) {
378 dprintf(_T(" * Initialising video: Total video memory minus display surface: %.2fMB.\n"), (double)dwTotal / (1024 * 1024));
379 }
380
381 if (bDrvOkay) {
382 if (BurnDrvGetFlags() & BDF_ORIENTATION_FLIPPED) {
383 if (nRotateGame & 2) {
384 dprintf(_T(" * Using graphics hardware to rotate the image 180 degrees.\n"));
385 } else {
386 dprintf(_T(" * Warning: Graphics hardware does not support mirroring blits.\n Image orientation will be incorrect.\n"));
387 }
388 }
389 }
390 }
391 #endif
392
393 if (bVidTripleBuffer && nVidFullscreen) {
394 if (DtoPrimInit(1)) { // Try to make triple buffer
395
396 #ifdef PRINT_DEBUG_INFO
397 dprintf(_T(" * Warning: Couldn't allocate a triple-buffering surface.\n"));
398 #endif
399
400 // If we fail, fail entirely and make a normal buffer
401 RELEASE(DtoPrim)
402 }
403 }
404
405 if (DtoPrim == NULL) {
406 // No primary surface yet, so try normal
407 if (DtoPrimInit(0)) {
408 #ifdef PRINT_DEBUG_INFO
409 dprintf(_T(" * Error: Couldn't create primary surface.\n"));
410 #endif
411
412 vidExit();
413 return 1;
414 }
415 }
416
417 if (nVidFullscreen) {
418 DtoDD->Compact();
419 }
420
421 VidSClipperInit(DtoPrim);
422
423 VidSSetupGamma(DtoPrim);
424
425 // Init the buffer surfaces
426 if (DtosInit()) {
427 vidExit();
428 return 1;
429 }
430
431 #ifdef PRINT_DEBUG_INFO
432 {
433 DDSCAPS2 ddsCaps2;
434 DWORD dwTotal;
435 DWORD dwFree;
436
437 memset(&ddsCaps2, 0, sizeof(ddsCaps2));
438 ddsCaps2.dwCaps = DDSCAPS_PRIMARYSURFACE;
439
440 if (SUCCEEDED(DtoDD->GetAvailableVidMem(&ddsCaps2, &dwTotal, &dwFree))) {
441 dprintf(_T(" * Initialisation complete: %.2fMB video memory free.\n"), (double)dwFree / (1024 * 1024));
442 dprintf(_T(" Displaying and rendering in %i-bit mode, emulation running in %i-bit mode.\n"), nVidScrnDepth, nVidImageDepth);
443 if (nUseSys) {
444 dprintf(_T(" Blitting directly from system memory.\n"));
445 } else {
446 dprintf(_T(" Transferring the image to video memory before blitting.\n"));
447 }
448 if (nVidFullscreen) {
449 dprintf(_T(" Running in fullscreen mode (%i x %i)"), nVidScrnWidth, nVidScrnHeight);
450 if (DtoBack != NULL) {
451 dprintf(_T(", using a triple buffer"));
452 }
453 dprintf(_T(".\n"));
454 } else {
455 dprintf(_T(" Running in windowed mode.\n"));
456 }
457 }
458 }
459 #endif
460
461 return 0;
462 }
463
464 // Copy pVidImage to pddsDtos, rotate
vidRenderRotate(DDSURFACEDESC2 * ddsd)465 static int vidRenderRotate(DDSURFACEDESC2* ddsd)
466 {
467 unsigned char *pd, *ps, *pdd;
468 unsigned char *Surf;
469 int nPitch;
470
471 Surf = (unsigned char*)ddsd->lpSurface;
472 nPitch = ddsd->lPitch;
473
474 pd = Surf;
475
476 if (bDtosScan) {
477 if (bVidScanHalf) {
478 if (bVidScanRotate) {
479 switch (nVidImageBPP) {
480 case 4: {
481 int t;
482 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
483 ps = pVidImage + (nGameHeight - 1 - y) * 4;
484 pdd = pd;
485 for (int x = 0; x < nGameWidth; x++) {
486 t = *(int*)ps;
487 ps += nVidImagePitch;
488 *(int*)pdd = t;
489 pdd += 4;
490 *(int*)pdd = (t & 0xFEFEFE) >> 1;
491 pdd += 4;
492 }
493 }
494 break;
495 }
496 case 3: {
497 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
498 ps = pVidImage + (nGameHeight - 1 - y) * 3;
499 pdd = pd;
500 for (int x = 0; x < nGameWidth; x++) {
501 pdd[0] = ps[0];
502 pdd[1] = ps[1];
503 pdd[2] = ps[2];
504 pdd[3] = ps[0] >> 1;
505 pdd[4] = ps[1] >> 1;
506 pdd[5] = ps[2] >> 1;
507 ps += nVidImagePitch;
508 pdd +=6;
509 }
510 }
511 break;
512 }
513 case 2: {
514 short t;
515 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
516 ps = pVidImage + (nGameHeight - 1 - y) * 2;
517 pdd = pd;
518 for (int x = 0; x < nGameWidth; x++) {
519 t = *(short*)ps;
520 ps += nVidImagePitch;
521 *(short*)pdd = t;
522 pdd += 2;
523 *(short*)pdd = (t & nHalfMask) >> 1;
524 pdd += 2;
525 }
526 }
527 break;
528 }
529 }
530
531 } else {
532 unsigned char* pBuffer = (unsigned char*)malloc(nPitch);
533 unsigned char* pd2;
534
535 switch (nVidImageBPP) {
536 case 4: {
537 int t;
538 for (int y = 0; y < nGameHeight; y++, pd += nPitch * 2) {
539 ps = pVidImage + (nGameHeight - 1 - y) * 4;
540 pdd = pd;
541 pd2 = pBuffer;
542 for (int x = 0; x < nGameWidth; x++) {
543 t = *(int*)ps;
544 ps += nVidImagePitch;
545 *(int*)pdd = t;
546 *(int*)pd2 = t;
547 pdd += 4;
548 pd2 += 4;
549 }
550 pdd = pd + nPitch;
551 for (ps = pBuffer; ps < (pBuffer + nGameWidth * 4); ps += 4) {
552 *(int*)pdd = (*(int*)ps & 0xFEFEFE) >> 1;
553 pdd += 4;
554 }
555 }
556 break;
557 }
558 case 3: {
559 for (int y = 0; y < nGameHeight; y++, pd += nPitch * 2) {
560 ps = pVidImage + (nGameHeight - 1 - y) * 3;
561 pdd = pd;
562 pd2 = pBuffer;
563 for (int x = 0; x < nGameWidth; x++) {
564 pdd[0] = ps[0];
565 pd2[0] = ps[0];
566 pdd[1] = ps[1];
567 pd2[1] = ps[1];
568 pdd[2] = ps[2];
569 pd2[2] = ps[2];
570 ps += nVidImagePitch;
571 pdd += 3;
572 pd2 += 3;
573 }
574 pdd = pd + nPitch;
575 for (ps = pBuffer; ps < (pBuffer + nGameWidth * 3); ps += 3) {
576 pdd[0] = ps[0] >> 1;
577 pdd[1] = ps[1] >> 1;
578 pdd[2] = ps[2] >> 1;
579 pdd += 3;
580 }
581 }
582 break;
583 }
584 case 2: {
585 short t;
586 for (int y = 0; y < nGameHeight; y++, pd += nPitch * 2) {
587 ps = pVidImage + (nGameHeight - 1 - y) * 2;
588 pdd = pd;
589 pd2 = pBuffer;
590 for (int x = 0; x < nGameWidth; x++) {
591 t = *(short*)ps;
592 ps += nVidImagePitch;
593 *(short*)pdd = t;
594 *(short*)pd2 = t;
595 pdd += 2;
596 pd2 += 2;
597 }
598 pdd = pd + nPitch;
599 for (ps = pBuffer; ps < (pBuffer + nGameWidth * 2); ps += 4) {
600 *(unsigned int*)pdd = (*(unsigned int*)ps & nHalfMask) >> 1;
601 pdd += 4;
602 }
603 }
604 break;
605 }
606 }
607 if (pBuffer) {
608 free (pBuffer);
609 pBuffer = NULL;
610 }
611 }
612 } else {
613 int nPixelSize = nVidImageBPP;
614 if (bVidScanRotate) {
615 nPixelSize <<= 1;
616 } else {
617 nPitch <<= 1;
618 }
619
620 switch (nVidImageBPP) {
621 case 4: {
622 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
623 ps = pVidImage + (nGameHeight - 1 - y) * 4;
624 pdd = pd;
625 for (int x = 0; x < nGameWidth; x++) {
626 *(int*)pdd = *(int*)ps;
627 ps += nVidImagePitch;
628 pdd += nPixelSize;
629 }
630 }
631 break;
632 }
633 case 3: {
634 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
635 ps = pVidImage + (nGameHeight - 1 - y) * 3;
636 pdd = pd;
637 for (int x = 0; x < nGameWidth; x++) {
638 pdd[0] = ps[0];
639 pdd[1] = ps[1];
640 pdd[2] = ps[2];
641 ps += nVidImagePitch;
642 pdd += nPixelSize;
643 }
644 }
645 break;
646 }
647 case 2: {
648 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
649 ps = pVidImage + (nGameHeight - 1 - y) * 2;
650 pdd = pd;
651 for (int x = 0; x < nGameWidth; x++) {
652 *(short*)pdd = *(short*)ps;
653 ps += nVidImagePitch;
654 pdd += nPixelSize;
655 }
656 }
657 break;
658 }
659 }
660 }
661 } else {
662 switch (nVidImageBPP) {
663 case 4: {
664 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
665 ps = pVidImage + (nGameHeight - 1 - y) * 4;
666 pdd = pd;
667 for (int x = 0; x < nGameWidth; x++) {
668 *(int*)pdd = *(int*)ps;
669 ps += nVidImagePitch;
670 pdd += 4;
671 }
672 }
673 break;
674 }
675 case 3: {
676 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
677 ps = pVidImage + (nGameHeight - 1 - y) * 3;
678 pdd = pd;
679 for (int x = 0; x < nGameWidth; x++) {
680 pdd[0] = ps[0];
681 pdd[1] = ps[1];
682 pdd[2] = ps[2];
683 ps += nVidImagePitch;
684 pdd +=3;
685 }
686 }
687 break;
688 }
689 case 2: {
690 for (int y = 0; y < nGameHeight; y++, pd += nPitch) {
691 ps = pVidImage + (nGameHeight - 1 - y) * 2;
692 pdd = pd;
693 for (int x = 0; x < nGameWidth; x++) {
694 *(short*)pdd = *(short*)ps;
695 ps += nVidImagePitch;
696 pdd += 2;
697 }
698 }
699 break;
700 }
701 }
702 }
703
704 return 0;
705 }
706
707 // Copy pVidImage to pddsDtos, don't rotate, add scanlines in odd lines if needed
vidRenderNoRotateHorScanlines(DDSURFACEDESC2 * ddsd,int nField,int nHalf)708 static int vidRenderNoRotateHorScanlines(DDSURFACEDESC2* ddsd, int nField, int nHalf)
709 {
710 unsigned char *pd, *ps;
711
712 unsigned char* Surf = (unsigned char*)ddsd->lpSurface;
713 int nPitch = ddsd->lPitch;
714 if (bDtosScan) {
715 if (nField) { // copy to odd fields
716 Surf += nPitch;
717 }
718 nPitch <<= 1;
719 }
720
721 pd = Surf; ps = pVidImage;
722 for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
723 if (nHalf == 0) {
724 memcpy(pd, ps, nVidImagePitch);
725 } else {
726 unsigned char* psEnd = ps + nVidImagePitch;
727 unsigned char* pdp = pd;
728 unsigned char* psp = ps;
729
730 do {
731 unsigned int t;
732 t = *((unsigned int *)(psp));
733 t = (t & nHalfMask) >> 1;
734 *((unsigned int *)(pdp)) = t;
735
736 psp += 4;
737 pdp += 4;
738 } while (psp < psEnd);
739 }
740 }
741
742 return 0;
743 }
744
745 // Copy pVidImage to pddsDtos, don't rotate, add rotated scanlines
vidRenderNoRotateVertScanlines(DDSURFACEDESC2 * ddsd)746 static int vidRenderNoRotateVertScanlines(DDSURFACEDESC2* ddsd)
747 {
748 unsigned char *pd, *ps;
749 unsigned char* Surf = (unsigned char*)ddsd->lpSurface;
750 int nPitch = ddsd->lPitch;
751
752 if (bVidScanHalf) {
753 pd = Surf; ps = pVidImage;
754 switch (nVidImageBPP) {
755 case 4: {
756 unsigned char* pdp;
757 unsigned char* psp;
758 unsigned char* psEnd;
759 for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
760 pdp = pd;
761 psp = ps;
762 psEnd = ps + nVidImagePitch;
763 do {
764 unsigned int t = *(unsigned int*)psp;
765 *(unsigned int*)pdp = t;
766 psp += 4;
767 pdp += 4;
768 *(unsigned int*)pdp = (t & 0xFEFEFE) >> 1;
769 pdp += 4;
770 } while (psp < psEnd);
771 }
772 break;
773 }
774 case 3: {
775 unsigned char* pdp;
776 unsigned char* psp;
777 unsigned char* psEnd;
778 for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
779 pdp = pd;
780 psp = ps;
781 psEnd = ps + nVidImagePitch;
782 do {
783 pdp[0] = psp[0];
784 pdp[1] = psp[1];
785 pdp[2] = psp[2];
786 pdp[3] = psp[0] >> 1;
787 pdp[4] = psp[1] >> 1;
788 pdp[5] = psp[2] >> 1;
789 psp += 3;
790 pdp += 6;
791 } while (psp < psEnd);
792 }
793 break;
794 }
795 case 2: {
796 unsigned char* pdp;
797 unsigned char* psp;
798 unsigned char* psEnd;
799 unsigned short t;
800 for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
801 pdp = pd;
802 psp = ps;
803 psEnd = ps + nVidImagePitch;
804 do {
805 t = *(unsigned short*)psp;
806 *(unsigned short*)pdp = t;
807 psp += 2;
808 pdp += 2;
809 *(unsigned short*)pdp = (t & nHalfMask) >> 1;
810 pdp += 2;
811 } while (psp < psEnd);
812 }
813 break;
814 }
815 }
816
817 } else {
818 pd = Surf; ps = pVidImage;
819 switch (nVidImageBPP) {
820 case 4: {
821 unsigned char* pdp;
822 unsigned char* psp;
823 unsigned char* psEnd;
824 for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
825 pdp = pd;
826 psp = ps;
827 psEnd = ps + nVidImagePitch;
828 do {
829 *(unsigned int*)pdp = *(unsigned int*)psp;
830
831 psp += 4;
832 pdp += 8;
833 } while (psp < psEnd);
834 }
835 break;
836 }
837 case 3: {
838 unsigned char* pdp;
839 unsigned char* psp;
840 unsigned char* psEnd;
841 for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
842 pdp = pd;
843 psp = ps;
844 psEnd = ps + nVidImagePitch;
845 do {
846 pdp[0] = psp[0];
847 pdp[1] = psp[1];
848 pdp[2] = psp[2];
849
850 psp += 3;
851 pdp += 6;
852 } while (psp < psEnd);
853 }
854 break;
855 }
856 case 2: {
857 unsigned char* pdp;
858 unsigned char* psp;
859 unsigned char* psEnd;
860 for (int y = 0; y < nVidImageHeight; y++, pd += nPitch, ps += nVidImagePitch) {
861 pdp = pd;
862 psp = ps;
863 psEnd = ps + nVidImagePitch;
864 do {
865 *(unsigned short*)pdp = *(unsigned short*)psp;
866
867 psp += 2;
868 pdp += 4;
869 } while (psp < psEnd);
870 }
871 break;
872 }
873 }
874 }
875
876 return 0;
877 }
878
vidBurnToSurf()879 static int vidBurnToSurf()
880 {
881 DDSURFACEDESC2 ddsd;
882
883 if (pddsDtos == NULL) {
884 return 1;
885 }
886
887 if (DtoPrim->IsLost()) { // We've lost control of the screen
888 return 1;
889 }
890
891 GetClientScreenRect(hVidWnd, &Dest);
892
893 if (!nVidFullscreen) {
894 Dest.top += nMenuHeight;
895 }
896
897 if (bVidArcaderes && nVidFullscreen) {
898 Dest.left = (Dest.right + Dest.left) / 2;
899 Dest.left -= nGameWidth / 2;
900 Dest.right = Dest.left + nGameWidth;
901
902 Dest.top = (Dest.top + Dest.bottom) / 2;
903 Dest.top -= nGameHeight / 2;
904 Dest.bottom = Dest.top + nGameHeight;
905 } else {
906 VidSScaleImage(&Dest, nGameWidth, nGameHeight, bVidScanRotate);
907 }
908
909 Src.right = nGameWidth;
910 Src.bottom = nGameHeight;
911 bool bScan = false;
912
913 if (bVidScanlines) { // See if the display window is large enough to add scanlines if needed
914 if (bRotateScanlines) {
915 if (Dest.right - Dest.left >= (nGameWidth << 1)) { // We need to add vertical scanlines
916 Src.right <<= 1;
917 bScan = true;
918 }
919 } else {
920 if (Dest.bottom - Dest.top >= (nGameHeight << 1)) { // We need to add horizontal scanlines
921 Src.bottom <<= 1;
922 bScan = true;
923 }
924 }
925 }
926
927 // Lock the surface so we can write to it
928 memset(&ddsd, 0, sizeof(ddsd));
929 ddsd.dwSize = sizeof(ddsd);
930
931 if (FAILED(pddsDtos->Lock(NULL, &ddsd, DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL))) {
932 return 1;
933 }
934
935 if (bScan && !bDtosScan) { // Scanlines were just enabled. We need to clear the screen
936 for (int y = 0; y < (nVidImageHeight << 1); y++) {
937 memset(((unsigned char*)ddsd.lpSurface) + y * ddsd.lPitch, 0, ddsd.lPitch);
938 }
939 }
940
941 bDtosScan = bScan;
942
943 if (nRotateGame & 1) {
944 vidRenderRotate(&ddsd);
945 } else {
946 if (bDtosScan && bRotateScanlines) {
947 vidRenderNoRotateVertScanlines(&ddsd);
948 } else {
949 vidRenderNoRotateHorScanlines(&ddsd, 0, 0);
950 if (bDtosScan && bVidScanHalf) {
951 vidRenderNoRotateHorScanlines(&ddsd, 1, 1);
952 }
953 }
954 }
955
956 pddsDtos->Unlock(NULL);
957
958 return 0;
959 }
960
961 // Run one frame and render the screen
vidFrame(bool bRedraw)962 int vidFrame(bool bRedraw) // bRedraw = 0
963 {
964 if (pVidImage == NULL) {
965 return 1;
966 }
967
968 if (DtoPrim->IsLost()) { // We've lost control of the screen
969 if (VidSRestoreOSD()) {
970 return 1;
971 }
972
973 if (FAILED(DtoDD->RestoreAllSurfaces())) {
974 return 1;
975 }
976
977 DtoPrimClear();
978 }
979
980 if (bDrvOkay) {
981 if (bRedraw) { // Redraw current frame
982 if (BurnDrvRedraw()) {
983 BurnDrvFrame(); // No redraw function provided, advance one frame
984 }
985 } else {
986 BurnDrvFrame(); // Run one frame and draw the screen
987 }
988
989 if ((BurnDrvGetFlags() & BDF_16BIT_ONLY) && pVidTransCallback)
990 pVidTransCallback();
991 }
992
993 vidBurnToSurf();
994
995 return 0;
996 }
997
998 // Paint the Dtos surface onto the primary surface
vidPaint(int bValidate)999 static int vidPaint(int bValidate)
1000 {
1001 if (DtoPrim == NULL || pddsDtos == NULL) {
1002 return 1;
1003 }
1004
1005 if (DtoPrim->IsLost()) { // We've lost control of the screen
1006 return 1;
1007 }
1008
1009 if (!nVidFullscreen) { // Check if the window has changed since we prepared the image
1010 RECT rect = { 0, 0, 0, 0 };
1011
1012 GetClientScreenRect(hVidWnd, &rect);
1013 rect.top += nMenuHeight;
1014
1015 VidSScaleImage(&rect, nGameWidth, nGameHeight, bVidScanRotate);
1016
1017 if (Dest.left != rect.left || Dest.right != rect.right || Dest.top != rect.top || Dest.bottom != rect.bottom) {
1018 bValidate |= 2;
1019 }
1020 }
1021
1022 if (bValidate & 2) {
1023 vidBurnToSurf();
1024 }
1025
1026 DWORD dwBltFlags = 0; // See if we need to use blit effects
1027 if (DtoBltFx) {
1028 dwBltFlags |= DDBLT_DDFX;
1029 }
1030
1031 if (bVidVSync && !nVidFullscreen) { DtoDD->WaitForVerticalBlank(DDWAITVB_BLOCKEND, NULL); }
1032
1033 if (DtoBack != NULL) { // Triple bufferring
1034 if (FAILED(DtoBack->Blt(&Dest, pddsDtos, &Src, DDBLT_ASYNC | dwBltFlags, DtoBltFx))) {
1035 if (FAILED(DtoBack->Blt(&Dest, pddsDtos, &Src, DDBLT_WAIT | dwBltFlags, DtoBltFx))) {
1036 return 1;
1037 }
1038 }
1039 VidSDisplayOSD(DtoBack, &Dest, 0);
1040
1041 DtoPrim->Flip(NULL, DDFLIP_WAIT);
1042 } else { // Normal
1043 RECT rect = { 0, 0, nGameWidth, nGameHeight };
1044 int nFlags = 0;
1045
1046 if (bDtosScan) {
1047 if (bRotateScanlines) {
1048 nFlags |= 0x01;
1049 } else {
1050 nFlags |= 0x02;
1051 }
1052 }
1053
1054 // Display OSD text message
1055 VidSDisplayOSD(pddsDtos, &rect, nFlags);
1056
1057 if (FAILED(DtoPrim->Blt(&Dest, pddsDtos, &Src, DDBLT_ASYNC | dwBltFlags, DtoBltFx))) {
1058 if (FAILED(DtoPrim->Blt(&Dest, pddsDtos, &Src, DDBLT_WAIT | dwBltFlags, DtoBltFx))) {
1059 return 1;
1060 }
1061 }
1062 /*
1063 DWORD lpdwScanLine;
1064 RECT window;
1065 GetWindowRect(hVidWnd, &window);
1066
1067 while (1)
1068 {
1069 DtoDD->GetScanLine(&lpdwScanLine);
1070 if (lpdwScanLine >= (unsigned int)window.bottom) {
1071 break;
1072 }
1073 //Sleep(1);
1074 }
1075 */
1076 }
1077
1078 if (bValidate & 1) {
1079 // Validate the rectangle we just drew
1080 POINT c = {0, 0};
1081 ClientToScreen(hVidWnd, &c);
1082 Dest.left -= c.x; Dest.right -= c.x;
1083 Dest.top -= c.y; Dest.bottom -= c.y;
1084 ValidateRect(hVidWnd, &Dest);
1085 }
1086
1087 return 0;
1088 }
1089
vidScale(RECT * pRect,int nWidth,int nHeight)1090 static int vidScale(RECT* pRect, int nWidth, int nHeight)
1091 {
1092 return VidSScaleImage(pRect, nWidth, nHeight, bVidScanRotate);
1093 }
1094
vidGetSettings(InterfaceInfo * pInfo)1095 static int vidGetSettings(InterfaceInfo* pInfo)
1096 {
1097 if (nVidFullscreen && DtoBack) {
1098 IntInfoAddStringModule(pInfo, _T("Using a triple buffer"));
1099 } else {
1100 IntInfoAddStringModule(pInfo, _T("Using Blt() to transfer the image"));
1101 }
1102
1103 if (nUseSys) {
1104 IntInfoAddStringModule(pInfo, _T("Using system memory"));
1105 } else {
1106 IntInfoAddStringModule(pInfo, _T("Using video memory for the final blit"));
1107 }
1108
1109 if (nRotateGame) {
1110 TCHAR* pszEffect[8] = { _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T("") };
1111 TCHAR szString[MAX_PATH] = _T("");
1112
1113 pszEffect[0] = _T("Using ");
1114 if (nRotateGame & 1) {
1115 pszEffect[1] = _T("software rotation");
1116 }
1117 if (nRotateGame & 2) {
1118 if (nRotateGame & 1) {
1119 pszEffect[2] = _T(" and");
1120 }
1121 pszEffect[3] = _T(" hardware mirroring");
1122 }
1123 pszEffect[4] = _T(", ");
1124
1125 _sntprintf(szString, MAX_PATH, _T("%s%s%s%s%s%s%s%s"), pszEffect[0], pszEffect[1], pszEffect[2], pszEffect[3], pszEffect[4], pszEffect[5], pszEffect[6], pszEffect[7]);
1126 IntInfoAddStringModule(pInfo, szString);
1127 }
1128
1129 if (bDtosScan) {
1130 TCHAR* pszEffect[8] = { _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T(""), _T("") };
1131 TCHAR szString[MAX_PATH] = _T("");
1132
1133 pszEffect[0] = _T("Applying ");
1134 if (bVidScanHalf) {
1135 pszEffect[1] = _T("50% ");
1136 }
1137 if (bVidScanRotate) {
1138 pszEffect[2] = _T("rotated ");
1139 }
1140 pszEffect[3] = _T("scanlines");
1141
1142 _sntprintf(szString, MAX_PATH, _T("%s%s%s%s%s%s%s%s"), pszEffect[0], pszEffect[1], pszEffect[2], pszEffect[3], pszEffect[4], pszEffect[5], pszEffect[6], pszEffect[7]);
1143 IntInfoAddStringModule(pInfo, szString);
1144 }
1145
1146 return 0;
1147 }
1148
1149 // The video output plugin:
1150 struct VidOut VidOutDDraw = { vidInit, vidExit, vidFrame, vidPaint, vidScale, vidGetSettings, _T("DirectDraw7 video output") };
1151