Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1919 lines
55 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1998-2000 Microsoft Corporation
  4. *
  5. * Abstract:
  6. *
  7. * Contains the scan-buffer routines for DCI and GDI.
  8. *
  9. * Plan:
  10. *
  11. * !!! [agodfrey]
  12. * For batching across primitives, the DpContext code
  13. * needs to flush the batch whenever the Context changes state.
  14. *
  15. * If that isn't feasible for some kinds of state, then EpScanGdiDci
  16. * could keep its own DpContext, updated when "context update" records
  17. * are inserted into the batch. (This would happen during a call to
  18. * Start()).
  19. *
  20. * Revision History:
  21. *
  22. * 12/08/1998 andrewgo
  23. * Created it.
  24. * 01/20/2000 agodfrey
  25. * Moved it from Engine\Entry.
  26. * 03/23/2000 andrewgo
  27. * Integrate DCI and GDI scan cases, for IsMoveSizeActive handling.
  28. * 02/22/2000 agodfrey
  29. * For ClearType, but also useful for other future improvements:
  30. * Expanded the batch structure to allow different types of record.
  31. *
  32. \**************************************************************************/
  33. #include "precomp.hpp"
  34. #include <limits.h>
  35. /**************************************************************************\
  36. *
  37. * Function Description:
  38. *
  39. * Downloads the clipping rectangles for the specified window. Updates
  40. * internal class clipping variables and returns the window offset to
  41. * be used.
  42. *
  43. * Return Value:
  44. *
  45. * None
  46. *
  47. * History:
  48. *
  49. * 04/04/1999 andrewgo
  50. * Created it.
  51. *
  52. \**************************************************************************/
  53. VOID
  54. EpScanGdiDci::DownloadClipping_Dci(
  55. HDC hdc,
  56. POINT *clientOffset // In/Out
  57. )
  58. {
  59. INT i;
  60. RECT *rect;
  61. HRGN regionHandle = CacheRegionHandle;
  62. RGNDATA *data = CacheRegionData;
  63. INT size = CacheDataSize;
  64. // Query the VisRgn:
  65. INT getResult = GetRandomRgn(hdc, regionHandle, SYSRGN);
  66. if (getResult == TRUE)
  67. {
  68. INT newSize = GetRegionData(regionHandle, size, data);
  69. // The spec says that GetRegionData returns '1' in the event of
  70. // success, but NT returns the actual number of bytes written if
  71. // successful, and returns '0' if the buffer wasn't large enough:
  72. if ((newSize < 1) || (newSize > size))
  73. {
  74. do {
  75. newSize = GetRegionData(regionHandle, 0, NULL);
  76. // Free the old buffer and allocate a new one:
  77. GpFree(data);
  78. data = NULL;
  79. size = 0;
  80. if (newSize < 1)
  81. break;
  82. data = static_cast<RGNDATA*>(GpMalloc(newSize));
  83. if (data == NULL)
  84. break;
  85. size = newSize;
  86. newSize = GetRegionData(CacheRegionHandle, size, data);
  87. // On NT, it's possible due to multithreading that the
  88. // regionHandle could have increased in complexity since we
  89. // asked for the size. (On Win9x, this isn't possible due
  90. // to the fact that BeginAccess acquires the Win16Lock.)
  91. // So in the rare case that this might happen, loop again:
  92. } while (newSize < size);
  93. CacheRegionData = data;
  94. CacheDataSize = size;
  95. }
  96. if (data != NULL)
  97. {
  98. INT xOffset = clientOffset->x;
  99. INT yOffset = clientOffset->y;
  100. // Set up some enumeration state:
  101. EnumerateCount = data->rdh.nCount;
  102. EnumerateRect = reinterpret_cast<RECT*>(&data->Buffer[0]);
  103. // Handle our multimon goop:
  104. INT screenOffsetX = Device->ScreenOffsetX;
  105. INT screenOffsetY = Device->ScreenOffsetY;
  106. if ((screenOffsetX != 0) || (screenOffsetY != 0))
  107. {
  108. // Adjust for screen offset for the multimon case:
  109. xOffset -= screenOffsetX;
  110. yOffset -= screenOffsetY;
  111. // Adjust and intersect every clip rectangle to account for
  112. // this monitor's location:
  113. if(Globals::IsNt)
  114. {
  115. for (rect = EnumerateRect, i = EnumerateCount;
  116. i != 0;
  117. i--, rect++)
  118. {
  119. // subtract off the screen origin so we can get
  120. // screen relative rectangles. This is only appropriate
  121. // on NT - Win9x is window relative.
  122. rect->left -= screenOffsetX;
  123. rect->right -= screenOffsetX;
  124. rect->top -= screenOffsetY;
  125. rect->bottom -= screenOffsetY;
  126. // Clamp to the screen dimension.
  127. if (rect->left < 0)
  128. {
  129. rect->left = 0;
  130. }
  131. if (rect->right > Device->ScreenWidth)
  132. {
  133. rect->right = Device->ScreenWidth;
  134. }
  135. if (rect->top < 0)
  136. {
  137. rect->top = 0;
  138. }
  139. if (rect->bottom > Device->ScreenHeight)
  140. {
  141. rect->bottom = Device->ScreenHeight;
  142. }
  143. }
  144. }
  145. }
  146. // On Win9x, GetRandomRgn returns the rectangles in window
  147. // coordinates, not screen coordinates, so we adjust them
  148. // here:
  149. if (!Globals::IsNt)
  150. {
  151. for (rect = EnumerateRect, i = EnumerateCount;
  152. i != 0;
  153. rect++, i--)
  154. {
  155. // Add the screen relative window offset to the rect.
  156. // The rect is window relative on Win9x, so this
  157. // calculation will give us the screen relative rectangle
  158. // we need.
  159. rect->left += xOffset;
  160. rect->right += xOffset;
  161. rect->top += yOffset;
  162. rect->bottom += yOffset;
  163. // clamp to the screen dimentions.
  164. if (rect->left < 0)
  165. {
  166. rect->left = 0;
  167. }
  168. if (rect->top < 0)
  169. {
  170. rect->top = 0;
  171. }
  172. if (rect->right > Device->ScreenWidth)
  173. {
  174. rect->right = Device->ScreenWidth;
  175. }
  176. if (rect->bottom > Device->ScreenHeight)
  177. {
  178. rect->bottom = Device->ScreenHeight;
  179. }
  180. }
  181. }
  182. // Return the offset:
  183. clientOffset->x = xOffset;
  184. clientOffset->y = yOffset;
  185. return;
  186. }
  187. }
  188. // Something failed (could have been a bad 'hdc' specified, or low
  189. // memory). As a result we set the clip regionHandle to 'empty':
  190. EnumerateCount = 0;
  191. }
  192. /**************************************************************************\
  193. *
  194. * Function Description:
  195. *
  196. * Sits in a tight loop to read the scan data structures, do any
  197. * necessary clipping, and render the result.
  198. *
  199. * Return Value:
  200. *
  201. * Points to the queue record it couldn't understand (typically a
  202. * header record), or end of the buffer if reached.
  203. *
  204. * History:
  205. *
  206. * 04/04/1999 andrewgo
  207. * Created it.
  208. *
  209. \**************************************************************************/
  210. EpScanRecord*
  211. FASTCALL
  212. EpScanGdiDci::DrawScanRecords_Dci(
  213. BYTE* bits,
  214. INT stride,
  215. EpScanRecord* record,
  216. EpScanRecord* endRecord,
  217. INT xOffset,
  218. INT yOffset,
  219. INT xClipLeft,
  220. INT yClipTop,
  221. INT xClipRight,
  222. INT yClipBottom
  223. )
  224. {
  225. INT blenderNum;
  226. INT x;
  227. INT y;
  228. INT width;
  229. INT xLeft;
  230. INT xRight;
  231. INT count;
  232. INT pixelSize = PixelSize;
  233. // Set up the AlphaBlender objects
  234. PixelFormatID dstFormat = Surface->PixelFormat;
  235. BOOL result = Device->GetScanBuffers(
  236. Surface->Width,
  237. NULL,
  238. NULL,
  239. NULL,
  240. Buffers);
  241. if (result && (dstFormat != PIXFMT_UNDEFINED))
  242. {
  243. // Palette and PaletteMap are set up by Start().
  244. // initialize the AlphaBlenders.
  245. BlenderConfig[0].Initialize(
  246. dstFormat,
  247. Context,
  248. Context->Palette ? Context->Palette : Device->Palette,
  249. Buffers,
  250. TRUE,
  251. TRUE,
  252. SolidColor
  253. );
  254. BlenderConfig[1].Initialize(
  255. dstFormat,
  256. Context,
  257. Context->Palette ? Context->Palette : Device->Palette,
  258. Buffers,
  259. TRUE,
  260. TRUE,
  261. SolidColor
  262. );
  263. }
  264. else
  265. {
  266. ONCE(WARNING(("DrawScanRecords_Dci: Unrecognized pixel format")));
  267. return endRecord;
  268. }
  269. INT ditherOriginX = DitherOriginX;
  270. INT ditherOriginY = DitherOriginY;
  271. do {
  272. // SrcOver_Gdi_ARGB assumes that if the format is lower than 8bpp,
  273. // then the DIBSection format is 8bpp.
  274. // Bug 310285: This assert is disabled and should be reinvestigated
  275. // for v2. It is likely the Surface pointer needs to be changed
  276. // to the real surface that's being rendered.
  277. //ASSERT( (Surface->PixelFormat == PixelFormatUndefined)
  278. // || (GetPixelFormatSize(Surface->PixelFormat) >= 8)
  279. // || (dstFormat == PixelFormat8bppIndexed));
  280. blenderNum = record->BlenderNum;
  281. ASSERT(record->GetScanType() == BlenderConfig[blenderNum].ScanType);
  282. x = record->X + xOffset + BatchOffsetX;
  283. y = record->Y + yOffset + BatchOffsetY;
  284. width = record->Width;
  285. INT recordFormatSize =
  286. GetPixelFormatSize(BlenderConfig[blenderNum].SourcePixelFormat) >> 3;
  287. if ((y >= yClipTop) && (y < yClipBottom))
  288. {
  289. xRight = x + width;
  290. if (xRight > xClipRight)
  291. xRight = xClipRight;
  292. xLeft = x;
  293. if (xLeft < xClipLeft)
  294. xLeft = xClipLeft;
  295. count = xRight - xLeft;
  296. if (count > 0)
  297. {
  298. BYTE *src = NULL;
  299. BYTE *ctBuffer = NULL;
  300. EpScanType scanType = record->GetScanType();
  301. if (scanType != EpScanTypeCTSolidFill)
  302. {
  303. src = reinterpret_cast<BYTE*>(record->GetColorBuffer());
  304. src += (xLeft - x)*recordFormatSize;
  305. }
  306. if ( (scanType == EpScanTypeCT)
  307. || (scanType == EpScanTypeCTSolidFill))
  308. {
  309. ctBuffer = record->GetCTBuffer(
  310. GetPixelFormatSize(BlenderConfig[0].SourcePixelFormat) >> 3
  311. );
  312. ctBuffer += (xLeft - x);
  313. }
  314. BYTE *dst = bits + (y * stride) + (xLeft * pixelSize);
  315. BlenderConfig[blenderNum].AlphaBlender.Blend(
  316. dst,
  317. src,
  318. count,
  319. xLeft - ditherOriginX,
  320. y - ditherOriginY,
  321. ctBuffer
  322. );
  323. }
  324. }
  325. // Advance to the next record:
  326. record = record->NextScanRecord(recordFormatSize);
  327. } while (record < endRecord);
  328. return(record);
  329. }
  330. /**************************************************************************\
  331. *
  332. * Function Description:
  333. *
  334. * Processes all the data in the queue.
  335. *
  336. * Note that it does not empty the queue; the caller is responsible.
  337. *
  338. * Return Value:
  339. *
  340. * None
  341. *
  342. * History:
  343. *
  344. * 04/04/1999 andrewgo
  345. * Created it.
  346. *
  347. \**************************************************************************/
  348. VOID
  349. EpScanGdiDci::ProcessBatch_Dci(
  350. HDC hdc, // Only used to query window offset and clipping
  351. EpScanRecord *buffer,
  352. EpScanRecord *bufferEnd
  353. )
  354. {
  355. INT i;
  356. RECT *clipRect;
  357. EpScanRecord *nextBuffer;
  358. POINT clientOffset;
  359. POINT duplicateOffset;
  360. INT result;
  361. while (TRUE)
  362. {
  363. // Peek at the window offset so that we can specify the bounds on
  364. // the DCI lock properly. Note that the window offset may change
  365. // between the time we do this peak, and the time we do the
  366. // BeginAccess.
  367. //
  368. // GetDCOrgEx may fail if the DC is bad, in which case we shouldn't
  369. // draw anything:
  370. if (!GetDCOrgEx(hdc, &clientOffset))
  371. {
  372. return;
  373. }
  374. // Adjust for the monitor's offset:
  375. INT xOffset = clientOffset.x - Device->ScreenOffsetX;
  376. INT yOffset = clientOffset.y - Device->ScreenOffsetY;
  377. // Clip to the surface bounds (multi-mon might cause the bounds
  378. // to be bigger than our surface):
  379. INT x = max(MinX + xOffset, 0);
  380. INT y = max(MinY + yOffset, 0);
  381. // MaxY is inclusive:
  382. INT width = min(MaxX + xOffset, Device->ScreenWidth) - x;
  383. INT height = min(MaxY + yOffset + 1, Device->ScreenHeight) - y;
  384. if ((width <= 0) || (height <= 0))
  385. {
  386. return;
  387. }
  388. // Acquire the DCI lock:
  389. result = Globals::DciBeginAccessFunction(
  390. DciSurface,
  391. x, y,
  392. width,
  393. height
  394. );
  395. if (result < DCI_OK)
  396. {
  397. // The DCI lock failed. There are really two possible reasons:
  398. //
  399. // 1. There was a mode change;
  400. // 2. The system at some point switched to a secure desktop
  401. // (such as by Ctrl-Alt-Del or by a screen saver) on NT.
  402. //
  403. // For the former case, we get a WM_DISPLAYCHANGED notification
  404. // message, and we have code that recreates all the GDI+ surface
  405. // representations (because a color-depth change happened, or
  406. // the multi-mon configuration might have changed, and we can't
  407. // recover from either of those this deep in the rendering
  408. // pipeline).
  409. //
  410. // For the second case, we get no notification other than the
  411. // DCI lock failure. So for this case, we try to reinitialize
  412. // our DCI state right here.
  413. if (!Reinitialize_Dci())
  414. {
  415. return;
  416. }
  417. result = Globals::DciBeginAccessFunction(
  418. DciSurface,
  419. x, y,
  420. width,
  421. height
  422. );
  423. }
  424. // If we failed to get a DCI lock, don't bother processing any
  425. // of the queue. We're outta here:
  426. if (result < DCI_OK)
  427. {
  428. return;
  429. }
  430. // Check the window offset again and verify that it's still
  431. // the same as the value we used to compute the lock rectangle:
  432. GetDCOrgEx(hdc, &duplicateOffset);
  433. if ((duplicateOffset.x == clientOffset.x) &&
  434. (duplicateOffset.y == clientOffset.y))
  435. {
  436. break;
  437. }
  438. // The window moved between the time we computed the
  439. // DCI lock area and the time we actually did the DCI
  440. // lock. Unlock and repeat:
  441. Globals::DciEndAccessFunction(DciSurface);
  442. }
  443. // Every time we acquire the DCI lock, we have to requery the
  444. // clipping.
  445. //
  446. // Now that we've acquired the DCI lock (or we failed to acquire
  447. // it but won't actually draw anything), then it's safe to
  448. // download the clipping, because it won't change until we do
  449. // the DCI unlock:
  450. DownloadClipping_Dci(hdc, &clientOffset);
  451. // Copy the data to the surface:
  452. BYTE *bits = reinterpret_cast<BYTE*>(DciSurface->dwOffSurface);
  453. INT stride = DciSurface->lStride;
  454. // We don't have to do any rendering when the clipping is empty:
  455. if (EnumerateCount != 0)
  456. {
  457. while (buffer < bufferEnd)
  458. {
  459. // Redraw each scan buffer once for every clip rectangle:
  460. i = EnumerateCount;
  461. clipRect = EnumerateRect;
  462. do {
  463. nextBuffer = DrawScanRecords_Dci(
  464. bits,
  465. stride,
  466. buffer,
  467. bufferEnd,
  468. clientOffset.x,
  469. clientOffset.y,
  470. clipRect->left,
  471. clipRect->top,
  472. clipRect->right,
  473. clipRect->bottom
  474. );
  475. } while (clipRect++, --i);
  476. buffer = nextBuffer;
  477. }
  478. }
  479. // Unlock the primary:
  480. Globals::DciEndAccessFunction(DciSurface);
  481. }
  482. /**************************************************************************\
  483. *
  484. * Function Description:
  485. *
  486. * Try to re-initialize DCI after a Lock failure (as may be caused by
  487. * a switch to a secure desktop). This will succeed only if the mode
  488. * is exactly the same resolution and color depth as it was before
  489. * (otherwise our clipping or halftone or whatever would be wrong if
  490. * we continued).
  491. *
  492. * Return Value:
  493. *
  494. * TRUE if successfully re-initialized; FALSE if not (with the side
  495. * effect that we switch to using GDI).
  496. *
  497. * History:
  498. *
  499. * 04/04/1999 andrewgo
  500. * Created it.
  501. *
  502. \**************************************************************************/
  503. BOOL
  504. EpScanGdiDci::Reinitialize_Dci()
  505. {
  506. ASSERT(Status == GdiDciStatus_UseDci);
  507. DWORD oldBitCount = DciSurface->dwBitCount;
  508. DWORD oldWidth = DciSurface->dwWidth;
  509. DWORD oldHeight = DciSurface->dwHeight;
  510. Globals::DciDestroyFunction(DciSurface);
  511. DciSurface = NULL;
  512. if (Globals::DciCreatePrimaryFunction(Device->DeviceHdc,
  513. &DciSurface) == DCI_OK)
  514. {
  515. if ((DciSurface->dwBitCount == oldBitCount) &&
  516. (DciSurface->dwWidth == oldWidth) &&
  517. (DciSurface->dwHeight == oldHeight))
  518. {
  519. return(TRUE);
  520. }
  521. }
  522. // Uh oh, we failed to recreate exactly the same surface. Switch
  523. // over to using GDI:
  524. Status = GdiDciStatus_UseGdi;
  525. return(FALSE);
  526. }
  527. /**************************************************************************\
  528. *
  529. * Function Description:
  530. *
  531. * Return the PixelFormatID for the given DCI surface.
  532. *
  533. * Added because of bug #96879 - when I fixed it, I found that
  534. * DCI would then succeed on the ATI Mach 64 GX's
  535. * non-standard 32bpp mode, and we were happily trying to draw it
  536. * (and getting our colors mixed up).
  537. *
  538. * Arguments:
  539. *
  540. * si - The DCISURFACEINFO to examine
  541. *
  542. * Return Value:
  543. *
  544. * The PixelFormatID.
  545. *
  546. * History:
  547. *
  548. * 09/10/2000 agodfrey
  549. * Created it.
  550. *
  551. \**************************************************************************/
  552. static PixelFormatID
  553. ExtractPixelFormatFromDCISurface(
  554. DCISURFACEINFO *si
  555. )
  556. {
  557. // 8bpp
  558. if (si->dwBitCount == 8)
  559. {
  560. return PixelFormat8bppIndexed;
  561. }
  562. // 24bpp RGB and 32bpp RGB
  563. if ((si->dwMask[0] == 0x00ff0000) &&
  564. (si->dwMask[1] == 0x0000ff00) &&
  565. (si->dwMask[2] == 0x000000ff))
  566. {
  567. if (si->dwBitCount == 24)
  568. {
  569. return PixelFormat24bppRGB;
  570. }
  571. else if (si->dwBitCount == 32)
  572. {
  573. return PixelFormat32bppRGB;
  574. }
  575. }
  576. // 16bpp 555
  577. if ((si->dwMask[0] == 0x00007c00) &&
  578. (si->dwMask[1] == 0x000003e0) &&
  579. (si->dwMask[2] == 0x0000001f) &&
  580. (si->dwBitCount == 16))
  581. {
  582. return PixelFormat16bppRGB555;
  583. }
  584. // 16bpp 565
  585. if ((si->dwMask[0] == 0x0000f800) &&
  586. (si->dwMask[1] == 0x000007e0) &&
  587. (si->dwMask[2] == 0x0000001f) &&
  588. (si->dwBitCount == 16))
  589. {
  590. return PixelFormat16bppRGB565;
  591. }
  592. // Unsupported format
  593. return PixelFormatUndefined;
  594. }
  595. /**************************************************************************\
  596. *
  597. * Function Description:
  598. *
  599. * Does all the initialization needed for DCI.
  600. *
  601. * Return Value:
  602. *
  603. * None
  604. *
  605. * History:
  606. *
  607. * 04/04/1999 andrewgo
  608. * Created it.
  609. *
  610. \**************************************************************************/
  611. VOID
  612. EpScanGdiDci::LazyInitialize_Dci()
  613. {
  614. // If a mirror driver is active, never use DCI:
  615. if (Globals::IsMirrorDriverActive || Globals::g_fAccessibilityPresent)
  616. {
  617. Status = GdiDciStatus_UseGdi;
  618. return;
  619. }
  620. // Use the LoadLibrary critical section to protect access
  621. // to our global variables.
  622. LoadLibraryCriticalSection llcs;
  623. // We'll lose memory if we're called more than once:
  624. ASSERT(Status == GdiDciStatus_TryDci);
  625. // DCIMAN32 exists on all versions of Win9x and NT4 and newer.
  626. if (Globals::DcimanHandle == NULL)
  627. {
  628. // Note: [minliu: 12/18/2000] The reason I added this following line
  629. // here:
  630. // 1) This is Office bug #300329
  631. // 2) The basic problem is that on Windows 98, with ATI Fury Pro/Xpert
  632. // 2000 Pro (English) card, with latest display driver,
  633. // wme_w98_r128_4_12_6292.exe. The floating point control state get
  634. // changed after we call LoadLibraryA("dciman32.dll"). Apparently the
  635. // display driver changes it. We are going to reporting this problem to
  636. // ATI Tech. In the meantime we need to check this in so that
  637. // PowerPointer can be launched on that machine
  638. FPUStateSandbox fps;
  639. HMODULE module = LoadLibraryA("dciman32.dll");
  640. if (module)
  641. {
  642. Globals::DciEndAccessFunction = (DCIENDACCESSFUNCTION)
  643. GetProcAddress(module, "DCIEndAccess");
  644. Globals::DciBeginAccessFunction = (DCIBEGINACCESSFUNCTION)
  645. GetProcAddress(module, "DCIBeginAccess");
  646. Globals::DciDestroyFunction = (DCIDESTROYFUNCTION)
  647. GetProcAddress(module, "DCIDestroy");
  648. Globals::DciCreatePrimaryFunction = (DCICREATEPRIMARYFUNCTION)
  649. GetProcAddress(module, "DCICreatePrimary");
  650. if ((Globals::DciEndAccessFunction != NULL) &&
  651. (Globals::DciBeginAccessFunction != NULL) &&
  652. (Globals::DciDestroyFunction != NULL) &&
  653. (Globals::DciCreatePrimaryFunction != NULL))
  654. {
  655. Globals::DcimanHandle = module;
  656. }
  657. else
  658. {
  659. // It failed, so free the library.
  660. FreeLibrary(module);
  661. }
  662. }
  663. }
  664. if (Globals::DcimanHandle != NULL)
  665. {
  666. if (Globals::DciCreatePrimaryFunction(Device->DeviceHdc,
  667. &DciSurface) == DCI_OK)
  668. {
  669. // Check that the format is one we can handle natively.
  670. if (EpAlphaBlender::IsSupportedPixelFormat(
  671. ExtractPixelFormatFromDCISurface(DciSurface)))
  672. {
  673. PixelSize = DciSurface->dwBitCount >> 3;
  674. CacheRegionHandle = CreateRectRgn(0, 0, 0, 0);
  675. if (CacheRegionHandle)
  676. {
  677. // Okay, initialize the whole class:
  678. // We're all set to use DCI:
  679. Status = GdiDciStatus_UseDci;
  680. return;
  681. }
  682. }
  683. Globals::DciDestroyFunction(DciSurface);
  684. DciSurface = NULL;
  685. }
  686. }
  687. // Darn, we can't use DCI.
  688. Status = GdiDciStatus_UseGdi;
  689. }
  690. /**************************************************************************\
  691. *
  692. * Function Description:
  693. *
  694. * Handles a SrcOver call via GDI routines, making sure to do the
  695. * smallest GDI calls possible
  696. *
  697. * Return Value:
  698. *
  699. * None
  700. *
  701. * History:
  702. *
  703. * 03/22/2000 andrewgo
  704. * Created it.
  705. *
  706. \**************************************************************************/
  707. static BOOL OptimizeRuns(ARGB *src, INT width)
  708. {
  709. if (width <= 8)
  710. return TRUE;
  711. BYTE * alpha = reinterpret_cast<BYTE*>(src) + 3;
  712. UINT numberOfRuns = 0;
  713. BOOL inRun = FALSE;
  714. for (INT pos = 0; pos < width; ++pos, alpha += 4)
  715. {
  716. if (static_cast<BYTE>(*alpha + 1) <= 1)
  717. {
  718. if (inRun)
  719. {
  720. ++numberOfRuns;
  721. if (numberOfRuns > 4)
  722. return TRUE;
  723. inRun = FALSE;
  724. }
  725. }
  726. else
  727. {
  728. inRun = TRUE;
  729. }
  730. }
  731. return FALSE;
  732. } // OptimizeRuns
  733. VOID
  734. EpScanGdiDci::SrcOver_Gdi_ARGB(
  735. HDC destinationHdc,
  736. HDC dibSectionHdc,
  737. VOID *dibSection,
  738. EpScanRecord *scanRecord
  739. )
  740. {
  741. BYTE* alpha;
  742. INT left;
  743. INT right;
  744. INT bltWidth;
  745. VOID *src = NULL;
  746. BYTE *ctBuffer = NULL;
  747. EpScanType scanType = scanRecord->GetScanType();
  748. if (scanType != EpScanTypeCTSolidFill)
  749. {
  750. src = scanRecord->GetColorBuffer();
  751. }
  752. if ( (scanType == EpScanTypeCT)
  753. || (scanType == EpScanTypeCTSolidFill))
  754. {
  755. ctBuffer = scanRecord->GetCTBuffer(
  756. GetPixelFormatSize(BlenderConfig[0].SourcePixelFormat) >> 3
  757. );
  758. }
  759. INT x = scanRecord->X;
  760. INT y = scanRecord->Y;
  761. INT width = scanRecord->Width;
  762. if (GetPixelFormatSize(Surface->PixelFormat) < 8)
  763. {
  764. // [agodfrey]: #98904 - we hit an assert or potential AV in
  765. // Convert_8_sRGB, because the buffer contains values that
  766. // are out-of-range of the palette. To fix this, zero the
  767. // temporary buffer whenever we're in less than 8bpp mode.
  768. GpMemset(dibSection, 0, width);
  769. }
  770. BOOL optimizeStretchBlt = FALSE;
  771. if ( (BlenderConfig[0].ScanType == EpScanTypeCT)
  772. || (BlenderConfig[0].ScanType == EpScanTypeCTSolidFill))
  773. optimizeStretchBlt = TRUE;
  774. else
  775. optimizeStretchBlt = OptimizeRuns(
  776. reinterpret_cast<ARGB *>(src),
  777. width
  778. );
  779. if (optimizeStretchBlt)
  780. {
  781. StretchBlt(dibSectionHdc, 0, 0, width, 1,
  782. destinationHdc, x, y, width, 1,
  783. SRCCOPY);
  784. }
  785. else
  786. {
  787. ASSERT(src != NULL);
  788. // We discovered on NT5 that some printer drivers will fall over
  789. // if we ask to blt from their surface. Consequently, we must
  790. // ensure we never get into this code path for printers!
  791. // [ericvan] This improperly asserts on compatible printer DC's
  792. // [agodfrey] No, see Start().
  793. // ASSERT(GetDeviceCaps(destinationHdc, TECHNOLOGY) != DT_RASPRINTER);
  794. // Only read those pixels that we have to read. In benchmarks
  795. // where we're going through GDI to the screen or to a compatible
  796. // bitmap, we're getting killed by our per-pixel read costs.
  797. // Terminal Server at least has the 'frame buffer' sitting in
  798. // system memory, but with NetMeeting we still have to read from
  799. // video memory.
  800. //
  801. // Unfortunately, the per-call overhead of StretchBlt is fairly
  802. // high (but not too bad when we're using a DIB-section - it
  803. // beats the heck out of StretchDIBits).
  804. alpha = reinterpret_cast<BYTE*>(src) + 3;
  805. right = 0;
  806. while (TRUE)
  807. {
  808. // Find the first translucent pixel:
  809. left = right;
  810. while ((left < width) && (static_cast<BYTE>(*alpha + 1) <= 1))
  811. {
  812. left++;
  813. alpha += 4;
  814. }
  815. // If there are no more runs of translucent pixels,
  816. // we're done:
  817. if (left >= width)
  818. break;
  819. // Now find the next completely transparent or opaque
  820. // pixel:
  821. right = left;
  822. while ((right < width) && (static_cast<BYTE>(*alpha + 1) > 1))
  823. {
  824. right++;
  825. alpha += 4;
  826. }
  827. bltWidth = right - left;
  828. // BitBlt doesn't work on Multimon on Win2K when the destinationHdc
  829. // is 8bpp. But StretchBlt does work.
  830. StretchBlt(dibSectionHdc, left, 0, bltWidth, 1,
  831. destinationHdc, x + left, y, bltWidth, 1,
  832. SRCCOPY);
  833. }
  834. }
  835. // Do the blend:
  836. BlenderConfig[scanRecord->BlenderNum].AlphaBlender.Blend(
  837. dibSection,
  838. src,
  839. width,
  840. x - DitherOriginX,
  841. y - DitherOriginY,
  842. ctBuffer
  843. );
  844. if (optimizeStretchBlt)
  845. {
  846. StretchBlt(destinationHdc, x, y, width, 1,
  847. dibSectionHdc, 0, 0, width, 1,
  848. SRCCOPY);
  849. }
  850. else
  851. {
  852. // Write the portions that aren't completely transparent back
  853. // to the screen:
  854. alpha = reinterpret_cast<BYTE*>(src) + 3;
  855. right = 0;
  856. while (TRUE)
  857. {
  858. // Find the first non-transparent pixel:
  859. left = right;
  860. while ((left < width) && (*alpha == 0))
  861. {
  862. left++;
  863. alpha += 4;
  864. }
  865. // If there are no more runs of non-transparent pixels,
  866. // we're done:
  867. if (left >= width)
  868. break;
  869. // Now find the next completely transparent pixel:
  870. right = left;
  871. while ((right < width) && (*alpha != 0))
  872. {
  873. right++;
  874. alpha += 4;
  875. }
  876. bltWidth = right - left;
  877. // BitBlt doesn't work on Multimon on Win2K when the
  878. // destinationHdc is 8bpp. But StretchBlt does work.
  879. StretchBlt(destinationHdc, x + left, y, bltWidth, 1,
  880. dibSectionHdc, left, 0, bltWidth, 1,
  881. SRCCOPY);
  882. }
  883. }
  884. }
  885. /**************************************************************************\
  886. *
  887. * Function Description:
  888. *
  889. * Processes all the data in the queue and resets it to be empty.
  890. *
  891. * Return Value:
  892. *
  893. * None
  894. *
  895. * History:
  896. *
  897. * 04/04/1999 andrewgo
  898. * Created it.
  899. *
  900. \**************************************************************************/
  901. VOID
  902. EpScanGdiDci::ProcessBatch_Gdi(
  903. HDC destinationHdc,
  904. EpScanRecord *buffer,
  905. EpScanRecord *bufferEnd
  906. )
  907. {
  908. ULONG type;
  909. INT x;
  910. INT y;
  911. INT width;
  912. VOID *dibSection;
  913. HDC dibSectionHdc;
  914. // Set up the AlphaBlender objects
  915. PixelFormatID dstFormat;
  916. // We must never be called with an empty batch.
  917. ASSERT(MaxX >= MinX);
  918. // We should be using Surface->Width as an upper bound on
  919. // our internal blending buffer, but because of other bugs
  920. // we're potentially using the wrong Surface here.
  921. // Instead we use the width of the bounding rectangle for
  922. // all the spans in the batch.
  923. BOOL result = Device->GetScanBuffers(
  924. //Surface->Width,
  925. MaxX - MinX,
  926. &dibSection,
  927. &dibSectionHdc,
  928. &dstFormat,
  929. Buffers
  930. );
  931. if (result && (dstFormat != PIXFMT_UNDEFINED))
  932. {
  933. // Palette and PaletteMap are set up by Start().
  934. // initialize the AlphaBlenders.
  935. BlenderConfig[0].Initialize(
  936. dstFormat,
  937. Context,
  938. Context->Palette ? Context->Palette : Device->Palette,
  939. Buffers,
  940. TRUE,
  941. TRUE,
  942. SolidColor
  943. );
  944. BlenderConfig[1].Initialize(
  945. dstFormat,
  946. Context,
  947. Context->Palette ? Context->Palette : Device->Palette,
  948. Buffers,
  949. TRUE,
  950. TRUE,
  951. SolidColor
  952. );
  953. }
  954. else
  955. {
  956. ONCE(WARNING(("EmptyBatch_Gdi: Unrecognized pixel format")));
  957. return;
  958. }
  959. INT ditherOriginX = DitherOriginX;
  960. INT ditherOriginY = DitherOriginY;
  961. do {
  962. INT blenderNum = buffer->BlenderNum;
  963. x = buffer->X + BatchOffsetX;
  964. y = buffer->Y + BatchOffsetY;
  965. width = buffer->Width;
  966. // This must never happen. If it does, we are going to write off
  967. // the end of our DIBSection and blending buffers. On win9x this will
  968. // overwrite some stuff in GDI and bring down the entire system.
  969. // On NT we'll AV and bring down the app.
  970. ASSERT(width <= Device->BufferWidth);
  971. EpScanType scanType = buffer->GetScanType();
  972. if (scanType != EpScanTypeOpaque)
  973. {
  974. SrcOver_Gdi_ARGB(
  975. destinationHdc,
  976. dibSectionHdc,
  977. dibSection,
  978. buffer
  979. );
  980. }
  981. else
  982. {
  983. ASSERT(scanType == EpScanTypeOpaque);
  984. // Do the copy:
  985. BlenderConfig[blenderNum].AlphaBlender.Blend(
  986. dibSection,
  987. buffer->GetColorBuffer(),
  988. width,
  989. x - ditherOriginX,
  990. y - ditherOriginY,
  991. NULL
  992. );
  993. // Write the result back to the screen:
  994. StretchBlt(destinationHdc, x, y, width, 1,
  995. dibSectionHdc, 0, 0, width, 1,
  996. SRCCOPY);
  997. }
  998. // Advance to the next buffer:
  999. buffer = buffer->NextScanRecord(
  1000. GetPixelFormatSize(BlenderConfig[blenderNum].SourcePixelFormat) >> 3
  1001. );
  1002. } while (buffer < bufferEnd);
  1003. }
  1004. /**************************************************************************\
  1005. *
  1006. * Function Description:
  1007. *
  1008. * Takes a batch as input and calls the internal flush
  1009. * mechanism to process it after which it restores the
  1010. * internal state.
  1011. *
  1012. * Notes
  1013. *
  1014. * This routine can handle multiple pixel formats with different
  1015. * SourceOver or SourceCopy combinations.
  1016. *
  1017. * Return Value:
  1018. *
  1019. * TRUE
  1020. *
  1021. * History:
  1022. *
  1023. * 5/4/2000 asecchia
  1024. * Created it.
  1025. *
  1026. \**************************************************************************/
  1027. BOOL EpScanGdiDci::ProcessBatch(
  1028. EpScanRecord *batchStart,
  1029. EpScanRecord *batchEnd,
  1030. INT minX,
  1031. INT minY,
  1032. INT maxX,
  1033. INT maxY
  1034. )
  1035. {
  1036. // NOTE: From the comments for class EpScan:
  1037. // NOTE: These classes are not reentrant, and therefore cannot be used
  1038. // by more than one thread at a time. In actual use, this means
  1039. // that their use must be synchronized under the device lock.
  1040. // Flush the batch
  1041. Flush();
  1042. // Save the buffers
  1043. EpScanRecord *bs = BufferStart; // Points to queue buffer start
  1044. EpScanRecord *be = BufferEnd; // Points to end of queue buffer
  1045. EpScanRecord *bc = BufferCurrent; // Points to current queue position
  1046. INT size = BufferSize; // Size of queue buffer in bytes
  1047. // Set up the buffers for the new batch.
  1048. BufferStart = batchStart;
  1049. BufferEnd = batchEnd;
  1050. BufferCurrent = batchEnd;
  1051. // note this implies that the buffer is not larger than MAXINT
  1052. // !!! [asecchia] not sure if the Flush needs this to be set.
  1053. BufferSize = (INT)((INT_PTR)batchEnd - (INT_PTR)batchStart);
  1054. // Set the bounds:
  1055. // Don't need to save the old bounds because the flush will reset them.
  1056. MinX = minX;
  1057. MinY = minY;
  1058. MaxX = maxX;
  1059. MaxY = maxY;
  1060. // Set the batch offset to the drawing offset.
  1061. BatchOffsetX = minX;
  1062. BatchOffsetY = minY;
  1063. // Flush the batch.
  1064. Flush();
  1065. // Restore the buffers.
  1066. BufferStart = bs;
  1067. BufferEnd = be;
  1068. BufferCurrent = bc;
  1069. BufferSize = size;
  1070. BatchOffsetX = 0;
  1071. BatchOffsetY = 0;
  1072. return TRUE;
  1073. }
  1074. /**************************************************************************\
  1075. *
  1076. * Function Description:
  1077. *
  1078. * Instantiates a scan instance for rendering to the screen via either
  1079. * DCI or GDI.
  1080. *
  1081. * Return Value:
  1082. *
  1083. * FALSE if all the necessary buffers couldn't be created
  1084. *
  1085. * History:
  1086. *
  1087. * 04/04/1999 andrewgo
  1088. * Created it.
  1089. *
  1090. \**************************************************************************/
  1091. BOOL
  1092. EpScanGdiDci::Start(
  1093. DpDriver *driver,
  1094. DpContext *context,
  1095. DpBitmap *surface,
  1096. NEXTBUFFERFUNCTION *nextBuffer,
  1097. EpScanType scanType,
  1098. PixelFormatID pixFmtGeneral,
  1099. PixelFormatID pixFmtOpaque,
  1100. ARGB solidColor
  1101. )
  1102. {
  1103. // Inherit initialization
  1104. // This sets up BlenderConfig[]. Mostly, these will be used at the time
  1105. // we flush the batch. But BlenderConfig[0].ScanType is also used in
  1106. // GetCurrentCTBuffer().
  1107. EpScan::Start(
  1108. driver,
  1109. context,
  1110. surface,
  1111. nextBuffer,
  1112. scanType,
  1113. pixFmtGeneral,
  1114. pixFmtOpaque,
  1115. solidColor
  1116. );
  1117. BOOL bRet = TRUE;
  1118. // Record the solid color for later
  1119. SolidColor = solidColor;
  1120. // The ScanBuffer and EpScan classes _MUST_ be protected by
  1121. // the Device Lock - if you hit this ASSERT, go and acquire
  1122. // the Devlock before entering the driver.
  1123. // EpScanGdiDci is used exclusively for drawing to the screen, therefore
  1124. // we assert on the DesktopDriver Device being locked. If you're locking
  1125. // some other device, that's also a bug - use a different EpScan class.
  1126. ASSERT(Globals::DesktopDriver->Device->DeviceLock.IsLockedByCurrentThread());
  1127. // First check to see if we have something in the queue for a different
  1128. // Graphics (which can happen with multiple threads drawing to different
  1129. // windows, since we only have one queue):
  1130. if ((context != Context) || (surface != Surface))
  1131. {
  1132. // Check to see if we have to do a lazy initialize:
  1133. if (Status == GdiDciStatus_TryDci)
  1134. {
  1135. LazyInitialize_Dci();
  1136. }
  1137. EmptyBatch();
  1138. // We stash away a pointer to the context. Note that the GpGraphics
  1139. // destructor always calls us to flush, thus ensuring that we
  1140. // won't use a stale Context pointer in the EmptyBatch call above.
  1141. Context = context;
  1142. Surface = surface;
  1143. }
  1144. GpCompositingMode compositingMode = context->CompositingMode;
  1145. // !!![andrewgo] We discovered that NT will fall over with some printer
  1146. // drivers if we ask to read from the printer surface.
  1147. // But we should never be hitting the scan interface in
  1148. // the printer case! (Scans are too much overhead, and
  1149. // alpha should be handled via screen-door anyway.)
  1150. // [ericvan] This improperly asserts on compatible printer DC's
  1151. // [agodfrey] No, we should have a separate scan class for that case
  1152. // - one which doesn't batch up the scans, and doesn't try to
  1153. // use DCI. Printer DC's also shouldn't have a pointer
  1154. // to the desktop device.
  1155. // If this is fixed, the assertion below can be reenabled.
  1156. //ASSERT(GetDeviceCaps(Context->Hdc, TECHNOLOGY) != DT_RASPRINTER);
  1157. // GDI and DCI destinations don't have an alpha channel:
  1158. ASSERT(surface->SurfaceTransparency == TransparencyNoAlpha);
  1159. // Allocate our queue buffer, if necessary:
  1160. // This makes some assumptions:
  1161. // 1) The records in blender 0 are bigger than records in blender 1.
  1162. // 2) The biggest color buffer format is 4 bytes per pixel.
  1163. EpScanRecord *maxRecordEnd = EpScanRecord::CalculateNextScanRecord(
  1164. BufferCurrent,
  1165. BlenderConfig[0].ScanType,
  1166. surface->Width,
  1167. 4);
  1168. INT_PTR requiredSize = reinterpret_cast<BYTE *>(maxRecordEnd) -
  1169. reinterpret_cast<BYTE *>(BufferCurrent);
  1170. if (requiredSize > BufferSize)
  1171. {
  1172. // If we need to resize, it follows that there should be nothing
  1173. // sitting in the queue for this surface:
  1174. //
  1175. // [agodfrey] It does? More explanation needed.
  1176. ASSERT(BufferCurrent == BufferStart);
  1177. // Free the old queue and allocate a new one:
  1178. GpFree(BufferMemory);
  1179. // Scan records are much smaller than 2GB, so 'requiredSize' will fit
  1180. // into an INT.
  1181. ASSERT(requiredSize < INT_MAX);
  1182. BufferSize = (INT)max(requiredSize, SCAN_BUFFER_SIZE);
  1183. // We may need up to 7 extra bytes in order to QWORD align BufferStart.
  1184. BufferMemory = GpMalloc(BufferSize+7);
  1185. if (BufferMemory == NULL)
  1186. {
  1187. BufferSize = 0;
  1188. bRet = FALSE;
  1189. return bRet;
  1190. }
  1191. BufferStart = MAKE_QWORD_ALIGNED(EpScanRecord *, BufferMemory);
  1192. // Make sure that we didn't overstep the 7
  1193. // padding bytes in the allocation.
  1194. ASSERT(((INT_PTR) BufferStart) - ((INT_PTR) BufferMemory) <= 7);
  1195. BufferEnd = reinterpret_cast<EpScanRecord *>
  1196. (reinterpret_cast<BYTE*>(BufferStart) + BufferSize);
  1197. BufferCurrent = BufferStart;
  1198. }
  1199. *nextBuffer = (NEXTBUFFERFUNCTION) EpScanGdiDci::NextBuffer;
  1200. // Cache the translation vector and palette for the device. We only
  1201. // need to do so in 8bpp mode.
  1202. //
  1203. // Update the color palette and palette map if necessary.
  1204. if (Device->Palette != NULL)
  1205. {
  1206. // Grab the DC just for the purposes of looking at the palette
  1207. // selected:
  1208. HDC destinationHdc = context->GetHdc(surface);
  1209. EpPaletteMap *paletteMap = context->PaletteMap;
  1210. if (paletteMap != NULL)
  1211. {
  1212. // IsValid() check isn't necessary because if we are in an
  1213. // invalid state, we may be able to get out of it in
  1214. // UpdateTranslate()
  1215. if (paletteMap->GetUniqueness() != Globals::PaletteChangeCount)
  1216. {
  1217. paletteMap->UpdateTranslate(destinationHdc);
  1218. paletteMap->SetUniqueness(Globals::PaletteChangeCount);
  1219. }
  1220. }
  1221. else
  1222. {
  1223. paletteMap = new EpPaletteMap(destinationHdc);
  1224. if (paletteMap != NULL)
  1225. {
  1226. paletteMap->SetUniqueness(Globals::PaletteChangeCount);
  1227. // This is very silly, but we must update this map to
  1228. // the entire DpContext chain...
  1229. // !!![andrewgo] Is this thread safe?
  1230. // !!![andrewgo] Is this stuff cleaned up?
  1231. // !!![andrewgo] This all looks really wrong...
  1232. DpContext* curContext = Context;
  1233. while (curContext != NULL)
  1234. {
  1235. curContext->PaletteMap = paletteMap;
  1236. curContext = curContext->Prev;
  1237. }
  1238. }
  1239. else
  1240. {
  1241. bRet = FALSE;
  1242. }
  1243. }
  1244. context->ReleaseHdc(destinationHdc);
  1245. }
  1246. return bRet;
  1247. }
  1248. /**************************************************************************\
  1249. *
  1250. * Function Description:
  1251. *
  1252. * Processes all the data in the queue and resets it to be empty.
  1253. *
  1254. * Return Value:
  1255. *
  1256. * None
  1257. *
  1258. * History:
  1259. *
  1260. * 04/04/1999 andrewgo
  1261. * Created it.
  1262. *
  1263. \**************************************************************************/
  1264. VOID
  1265. EpScanGdiDci::EmptyBatch()
  1266. {
  1267. // Watch out for an empty batch (which can happen with Flush or with
  1268. // the first allocation of the queue buffer):
  1269. if (BufferCurrent != BufferStart)
  1270. {
  1271. // If we're emptying a non-empty batch, it follows that we
  1272. // should no longer be unsure whether we'll be using DCI or GDI:
  1273. ASSERT(Status != GdiDciStatus_TryDci);
  1274. // Remember where we are in the queue:
  1275. EpScanRecord *bufferStart = BufferStart;
  1276. EpScanRecord *bufferEnd = BufferCurrent;
  1277. // Reset the queue before doing anything else, in case whatever
  1278. // we're doing causes us to force a 'surface->Flush()' call
  1279. // (which would re-enter this routine):
  1280. BufferCurrent = BufferStart;
  1281. // Use DCI to process the queue if DCI was successfully enabled.
  1282. //
  1283. // On NT we also add the weird condition that we won't invoke
  1284. // DCI if the user is actively moving a window around. The
  1285. // reason is that NT is forced to repaint the whole desktop if
  1286. // the Visrgn for any window changes while a DCI primary surface
  1287. // lock is held (this is how NT avoids having something like
  1288. // the Win16Lock, which would let a user-mode app prevent windows
  1289. // from moving, an obvious robustness issue).
  1290. //
  1291. // Note that the 'IsMoveSizeActive' thing is not a fool-proof
  1292. // solution for avoiding whole-desktop repaints (since there's
  1293. // a chance the user could still move a window while we're
  1294. // in ProcessBatch_Dci), but as a heuristic it works quite well.
  1295. //
  1296. // To summarize, drop through to GDI rendering codepath if any
  1297. // of the following conditions are TRUE:
  1298. //
  1299. // ICM required
  1300. // Thus we must go through GDI to use ICM2.0 support.
  1301. //
  1302. // DCI disabled
  1303. // We have no choice but to fallback to GDI.
  1304. //
  1305. // GDI layering
  1306. // GDI layering means that GDI is hooking rendering to the
  1307. // screen and invisibly redirecting output to a backing store.
  1308. // Thus, the actual rendering surface is inaccessible via DCI
  1309. // and we must fallback to GDI.
  1310. //
  1311. // Window move or resize processing
  1312. // To prevent excessive repainting
  1313. if ((Context->IcmMode != IcmModeOn) &&
  1314. (Context->GdiLayered == FALSE) &&
  1315. (Status == GdiDciStatus_UseDci) &&
  1316. (!Globals::IsMoveSizeActive) &&
  1317. (!Globals::g_fAccessibilityPresent))
  1318. {
  1319. // If the Graphics was derived using an Hwnd, we have to use that
  1320. // to first get an HDC which we can query for clipping.
  1321. //
  1322. // Note that we don't need a 'clean' DC in order to query the
  1323. // clipping, so we don't call GetHdc() if we already have a DC
  1324. // hanging around:
  1325. HDC hdc = Context->Hdc;
  1326. if (Context->Hwnd != NULL)
  1327. {
  1328. hdc = Context->GetHdc(Surface);
  1329. }
  1330. ProcessBatch_Dci(hdc, bufferStart, bufferEnd);
  1331. if (Context->Hwnd != NULL)
  1332. {
  1333. Context->ReleaseHdc(hdc, Surface);
  1334. }
  1335. }
  1336. else
  1337. {
  1338. // We need a clean DC if we're going to draw using GDI:
  1339. HDC hdc = Context->GetHdc(Surface);
  1340. ProcessBatch_Gdi(hdc, bufferStart, bufferEnd);
  1341. Context->ReleaseHdc(hdc);
  1342. }
  1343. // Reset our bounds.
  1344. MinX = INT_MAX;
  1345. MinY = INT_MAX;
  1346. MaxX = INT_MIN;
  1347. MaxY = INT_MIN;
  1348. }
  1349. }
  1350. /**************************************************************************\
  1351. *
  1352. * Function Description:
  1353. *
  1354. * Flushes any buffers in the DCI queue. Note that the DCI queue can
  1355. * be accumulated over numerous API calls without flushing, which forces
  1356. * us to expose a Flush mechanism to the application.
  1357. *
  1358. * Return Value:
  1359. *
  1360. * None
  1361. *
  1362. * History:
  1363. *
  1364. * 04/04/1999 andrewgo
  1365. * Created it.
  1366. *
  1367. \**************************************************************************/
  1368. VOID
  1369. EpScanGdiDci::Flush()
  1370. {
  1371. // Note that we might be called to Flush even before we've
  1372. // initialized DCI:
  1373. EmptyBatch();
  1374. }
  1375. /**************************************************************************\
  1376. *
  1377. * Function Description:
  1378. *
  1379. * Ends the previous buffer (if there was one), and returns the
  1380. * next buffer for doing a SrcOver blend.
  1381. *
  1382. * Return Value:
  1383. *
  1384. * Points to the resulting scan buffer
  1385. *
  1386. * History:
  1387. *
  1388. * 04/04/1999 andrewgo
  1389. * Created it.
  1390. *
  1391. \**************************************************************************/
  1392. VOID *EpScanGdiDci::NextBuffer(
  1393. INT x,
  1394. INT y,
  1395. INT nextWidth,
  1396. INT currentWidth,
  1397. INT blenderNum
  1398. )
  1399. {
  1400. ASSERT(nextWidth >= 0);
  1401. ASSERT(currentWidth >= 0);
  1402. // Avoid pointer aliasing by loading up a local copy:
  1403. EpScanRecord *bufferCurrent = BufferCurrent;
  1404. // The first call that a drawing routine makes to us always has
  1405. // a 'currentWidth' of 0 (since it doesn't have a 'current'
  1406. // buffer yet):
  1407. if (currentWidth != 0)
  1408. {
  1409. // Accumulate the bounds using the final, completed scan:
  1410. INT xCurrent = bufferCurrent->X;
  1411. MinX = min(MinX, xCurrent);
  1412. MaxX = max(MaxX, xCurrent + currentWidth);
  1413. INT yCurrent = bufferCurrent->Y;
  1414. MinY = min(MinY, yCurrent);
  1415. MaxY = max(MaxY, yCurrent);
  1416. // Complete the previous scan request.
  1417. // Now that we know how much the caller actually wrote into
  1418. // the buffer, update the width in the old record and advance
  1419. // to the next:
  1420. bufferCurrent->Width = currentWidth;
  1421. bufferCurrent = bufferCurrent->NextScanRecord(
  1422. GetPixelFormatSize(
  1423. BlenderConfig[bufferCurrent->BlenderNum].SourcePixelFormat
  1424. ) >> 3
  1425. );
  1426. // Don't forget to update the class version:
  1427. BufferCurrent = bufferCurrent;
  1428. }
  1429. // From here on, the code is operating on the current scan request
  1430. // I.e. bufferCurrent applies to the current scan - it has been updated
  1431. // and no longer refers to the last scan.
  1432. // See if there's room in the buffer for the next scan:
  1433. // bufferCurrent is not initialized for this scan yet, so we can't rely
  1434. // on it having a valid PixelFormat - we need to get the PixelFormat
  1435. // from the context of the call.
  1436. PixelFormatID pixFmt = BlenderConfig[blenderNum].SourcePixelFormat;
  1437. EpScanRecord* scanEnd = EpScanRecord::CalculateNextScanRecord(
  1438. bufferCurrent,
  1439. BlenderConfig[blenderNum].ScanType,
  1440. nextWidth,
  1441. GetPixelFormatSize(pixFmt) >> 3
  1442. );
  1443. if (scanEnd > BufferEnd)
  1444. {
  1445. EmptyBatch();
  1446. // Reload our local variable:
  1447. bufferCurrent = BufferCurrent;
  1448. }
  1449. // Remember the x and y for the brush offset (halftone & dither).
  1450. // Note: We do not have to remember x and y in CurrentX and CurrentY
  1451. // because we remember them in bufferCurrent (below).
  1452. // CurrentX = x;
  1453. // CurrentY = y;
  1454. // Initialize the bufferCurrent.
  1455. // We initialize everything except the width - which we don't know till
  1456. // the caller is done and we get called for the subsequent scan.
  1457. bufferCurrent->SetScanType(BlenderConfig[blenderNum].ScanType);
  1458. bufferCurrent->SetBlenderNum(blenderNum);
  1459. bufferCurrent->X = x;
  1460. bufferCurrent->Y = y;
  1461. bufferCurrent->OrgWidth = nextWidth;
  1462. // Note: we don't actually use LastBlenderNum in the EpScanGdiDci
  1463. // See EpScanEngine for a class that uses it.
  1464. // LastBlenderNum = blenderNum;
  1465. return bufferCurrent->GetColorBuffer();
  1466. }
  1467. /**************************************************************************\
  1468. *
  1469. * Function Description:
  1470. *
  1471. * Denotes the end of the use of the scan buffer for this API call.
  1472. * Note that this does not force a flush of the buffer.
  1473. *
  1474. * Arguments:
  1475. *
  1476. * NONE
  1477. *
  1478. * Return Value:
  1479. *
  1480. * NONE
  1481. *
  1482. * History:
  1483. *
  1484. * 04/04/1999 andrewgo
  1485. * Created it.
  1486. *
  1487. \**************************************************************************/
  1488. VOID
  1489. EpScanGdiDci::End(
  1490. INT updateWidth
  1491. )
  1492. {
  1493. // Get the last record into the queue:
  1494. NextBuffer(0, 0, 0, updateWidth, 0);
  1495. // Note that we do not flush the buffer here for DCI! This is VERY
  1496. // INTENDED, to allow spans to be batched across primitives. In fact,
  1497. // THAT'S THE WHOLE POINT OF THE BATCHING!
  1498. // !!![andrewgo] Actually, our scan architecture needs to be fixed
  1499. // to allow this for the GDI cases too. If I don't
  1500. // flush here for the GDI case, we die running
  1501. // Office CITs in Graphics::GetHdc when we do the
  1502. // EmptyBatch, because there's a stale Context in
  1503. // the non-empty batch buffer from previous drawing
  1504. // that didn't get flushed on ~GpGraphics because
  1505. // the driver didn't pass the Flush through to the
  1506. // scan class!
  1507. // if (Status != GdiDciStatus_UseDci)
  1508. {
  1509. EmptyBatch();
  1510. }
  1511. }
  1512. /**************************************************************************\
  1513. *
  1514. * Function Description:
  1515. *
  1516. * Constructor for all GDI/DCI drawing.
  1517. *
  1518. * Return Value:
  1519. *
  1520. * None
  1521. *
  1522. * History:
  1523. *
  1524. * 04/04/1999 andrewgo
  1525. * Created it.
  1526. *
  1527. \**************************************************************************/
  1528. EpScanGdiDci::EpScanGdiDci(GpDevice *device, BOOL tryDci)
  1529. {
  1530. Device = device;
  1531. Status = (tryDci) ? GdiDciStatus_TryDci : GdiDciStatus_UseGdi;
  1532. Context = NULL;
  1533. Surface = NULL;
  1534. BufferSize = 0;
  1535. BufferMemory = NULL;
  1536. BufferCurrent = NULL;
  1537. BufferStart = NULL;
  1538. BufferEnd = NULL;
  1539. CacheRegionHandle = NULL;
  1540. CacheRegionData = NULL;
  1541. CacheDataSize = 0;
  1542. MinX = INT_MAX;
  1543. MinY = INT_MAX;
  1544. MaxX = INT_MIN;
  1545. MaxY = INT_MIN;
  1546. BatchOffsetX = 0;
  1547. BatchOffsetY = 0;
  1548. }
  1549. /**************************************************************************\
  1550. *
  1551. * Function Description:
  1552. *
  1553. * Destructor for the GDI/DCI interface. Typically only called when
  1554. * the 'device' is destroyed.
  1555. *
  1556. * Return Value:
  1557. *
  1558. * None
  1559. *
  1560. * History:
  1561. *
  1562. * 04/04/1999 andrewgo
  1563. * Created it.
  1564. *
  1565. \**************************************************************************/
  1566. EpScanGdiDci::~EpScanGdiDci()
  1567. {
  1568. if (Status == GdiDciStatus_UseDci)
  1569. {
  1570. // DciDestroy doesn't do anything if 'DciSurface' is NULL:
  1571. Globals::DciDestroyFunction(DciSurface);
  1572. }
  1573. DeleteObject(CacheRegionHandle);
  1574. GpFree(CacheRegionData);
  1575. GpFree(BufferMemory);
  1576. }