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.

2856 lines
76 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1998 Microsoft Corporation
  4. *
  5. * Abstract:
  6. *
  7. * Graphics vector fill APIs.
  8. *
  9. * Revision History:
  10. *
  11. * 12/02/1998 andrewgo
  12. * Created it.
  13. *
  14. \**************************************************************************/
  15. #include "precomp.hpp"
  16. #include "QuadTransforms.hpp"
  17. /**************************************************************************\
  18. *
  19. * Function Description:
  20. *
  21. * API to clear the surface to a specified color
  22. *
  23. * Return Value:
  24. *
  25. * A GpStatus value indicating success or failure.
  26. *
  27. * History:
  28. *
  29. * 03/13/2000 agodfrey
  30. * Created it.
  31. *
  32. \**************************************************************************/
  33. GpStatus
  34. GpGraphics::Clear(
  35. const GpColor &color
  36. )
  37. {
  38. INT i;
  39. GpStatus status = Ok;
  40. ASSERT(this->IsValid());
  41. RectF drawRect(
  42. static_cast<float>(SurfaceBounds.X),
  43. static_cast<float>(SurfaceBounds.Y),
  44. static_cast<float>(SurfaceBounds.Width),
  45. static_cast<float>(SurfaceBounds.Height));
  46. if (IsRecording())
  47. {
  48. status = Metafile->RecordClear(&drawRect, color);
  49. if (status != Ok)
  50. {
  51. SetValid(FALSE); // Prevent any more recording
  52. return status;
  53. }
  54. if (!DownLevel)
  55. {
  56. return Ok;
  57. }
  58. // else we need to record down-level GDI EMF records as well
  59. }
  60. GpSolidFill brush(color);
  61. if (!IsTotallyClipped(&SurfaceBounds))
  62. {
  63. // Remember the compositing mode, antialiasing mode, and world
  64. // transform, and then set them up for this call.
  65. GpMatrix oldWorldToDevice = Context->WorldToDevice;
  66. INT oldAntiAliasMode = Context->AntiAliasMode;
  67. GpCompositingMode oldCompositingMode = Context->CompositingMode;
  68. Context->WorldToDevice.Reset();
  69. Context->AntiAliasMode = 0;
  70. Context->CompositingMode = CompositingModeSourceCopy;
  71. Devlock devlock(Device);
  72. status = DrvFillRects(
  73. &SurfaceBounds,
  74. 1,
  75. &drawRect,
  76. brush.GetDeviceBrush());
  77. // Restore the context state we changed
  78. Context->WorldToDevice = oldWorldToDevice;
  79. Context->AntiAliasMode = oldAntiAliasMode;
  80. Context->CompositingMode = oldCompositingMode;
  81. }
  82. return status;
  83. }
  84. /**************************************************************************\
  85. *
  86. * Function Description:
  87. *
  88. * API to fill rectangles using the specified brush
  89. *
  90. * Return Value:
  91. *
  92. * A GpStatus value indicating success or failure.
  93. *
  94. * History:
  95. *
  96. * 12/06/1998 andrewgo
  97. * Created it.
  98. *
  99. \**************************************************************************/
  100. GpStatus
  101. GpGraphics::FillRects(
  102. GpBrush* brush,
  103. const GpRectF* rects,
  104. INT count
  105. )
  106. {
  107. INT i;
  108. GpStatus status = Ok;
  109. // Objects returned from the API must always be in a valid state:
  110. ASSERT((brush != NULL) && (rects != NULL));
  111. ASSERT(this->IsValid() && brush->IsValid());
  112. // See RAID bug:
  113. // 301407 GDI+ Globals::DesktopDC has thread affinity
  114. ASSERT(GetObjectType(Globals::DesktopIc) == OBJ_DC);
  115. if (count < 0)
  116. {
  117. return InvalidParameter;
  118. }
  119. if (count == 0)
  120. {
  121. return Ok;
  122. }
  123. // Zoom through the list and accumulate the bounds. What a pain, but
  124. // we have to do this.
  125. REAL left = rects[0].X;
  126. REAL top = rects[0].Y;
  127. REAL right = rects[0].GetRight();
  128. REAL bottom = rects[0].GetBottom();
  129. // !!![andrewgo] We have a bug here, in that we don't properly handle
  130. // rectangles with negative dimensions (which after the
  131. // transform might be positive dimensions):
  132. for (i = 1; i < count; i++)
  133. {
  134. if (rects[i].X < left)
  135. {
  136. left = rects[i].X;
  137. }
  138. if (rects[i].GetRight() > right)
  139. {
  140. right = rects[i].GetRight();
  141. }
  142. if (rects[i].Y < top)
  143. {
  144. top = rects[i].Y;
  145. }
  146. if (rects[i].GetBottom() > bottom)
  147. {
  148. bottom = rects[i].GetBottom();
  149. }
  150. }
  151. // Convert the bounds to device space:
  152. GpRectF bounds;
  153. TransformBounds(&(Context->WorldToDevice), left, top, right, bottom, &bounds);
  154. if (IsRecording())
  155. {
  156. status = Metafile->RecordFillRects(&bounds, brush, rects, count);
  157. if (status != Ok)
  158. {
  159. SetValid(FALSE); // Prevent any more recording
  160. return status;
  161. }
  162. if (!DownLevel)
  163. {
  164. return Ok;
  165. }
  166. // else we need to record down-level GDI EMF records as well
  167. }
  168. if (UseDriverRects())
  169. {
  170. status = RenderFillRects(&bounds, count, rects, brush);
  171. }
  172. else
  173. {
  174. for (i = 0; i < count; i++)
  175. {
  176. if ((rects[i].Width > REAL_EPSILON) &&
  177. (rects[i].Height > REAL_EPSILON) )
  178. {
  179. GpPointF points[4];
  180. left = rects[i].X;
  181. top = rects[i].Y;
  182. right = rects[i].X + rects[i].Width;
  183. bottom = rects[i].Y + rects[i].Height;
  184. points[0].X = left;
  185. points[0].Y = top;
  186. points[1].X = right;
  187. points[1].Y = top;
  188. points[2].X = right;
  189. points[2].Y = bottom;
  190. points[3].X = left;
  191. points[3].Y = bottom;
  192. const INT stackCount = 10;
  193. GpPointF stackPoints[stackCount];
  194. BYTE stackTypes[stackCount];
  195. GpPath path(
  196. points,
  197. 4,
  198. &stackPoints[0],
  199. &stackTypes[0],
  200. stackCount,
  201. FillModeAlternate,
  202. DpPath::ConvexRectangle
  203. );
  204. path.CloseFigure();
  205. if (path.IsValid())
  206. {
  207. // Call internal FillPath so that path doesn't get recorded in
  208. // the metafile again.
  209. status = RenderFillPath(&bounds, &path, brush);
  210. // Terminate if we failed to render.
  211. if(status != Ok)
  212. {
  213. break;
  214. }
  215. }
  216. }
  217. }
  218. }
  219. return status;
  220. }
  221. /**************************************************************************\
  222. *
  223. * Function Description:
  224. *
  225. * API to fill polygons using the specified brush
  226. *
  227. * Return Value:
  228. *
  229. * A GpStatus value indicating success or failure.
  230. *
  231. * History:
  232. *
  233. * 12/06/1998 andrewgo
  234. *
  235. \**************************************************************************/
  236. GpStatus
  237. GpGraphics::FillPolygon(
  238. GpBrush* brush,
  239. const GpPointF* points,
  240. INT count,
  241. GpFillMode fillMode
  242. )
  243. {
  244. GpStatus status = Ok;
  245. ASSERT((brush != NULL) && (points != NULL));
  246. if ((count < 0) ||
  247. ((fillMode != FillModeWinding) && (fillMode != FillModeAlternate)))
  248. {
  249. return InvalidParameter;
  250. }
  251. // Two vertices or less constitutes an empty fill:
  252. if (count <= 2)
  253. {
  254. return Ok;
  255. }
  256. // Objects returned from the API must always be in a valid state:
  257. ASSERT(this->IsValid() && brush->IsValid());
  258. const stackCount = 30;
  259. GpPointF stackPoints[stackCount];
  260. BYTE stackTypes[stackCount];
  261. GpPath path(points, count, &stackPoints[0], &stackTypes[0], stackCount, fillMode);
  262. if (path.IsValid())
  263. {
  264. GpRectF bounds;
  265. // If the path is a rectangle, we can draw it much faster and
  266. // save space in spool files and metafiles if we fill it as a
  267. // rect instead of as a path.
  268. if (this->UseDriverRects() && path.IsRectangle(&(Context->WorldToDevice)))
  269. {
  270. path.GetBounds(&bounds, NULL);
  271. return this->FillRects(brush, &bounds, 1);
  272. }
  273. path.GetBounds(&bounds, &(Context->WorldToDevice));
  274. if (IsRecording())
  275. {
  276. status = Metafile->RecordFillPolygon(&bounds, brush, points,
  277. count, fillMode);
  278. if (status != Ok)
  279. {
  280. SetValid(FALSE); // Prevent any more recording
  281. return status;
  282. }
  283. if (!DownLevel)
  284. {
  285. return Ok;
  286. }
  287. // else we need to record down-level GDI EMF records as well
  288. }
  289. // Call internal FillPath so that path doesn't get recorded in
  290. // the metafile again.
  291. status = RenderFillPath(&bounds, &path, brush);
  292. }
  293. return status;
  294. }
  295. /**************************************************************************\
  296. *
  297. * Function Description:
  298. *
  299. * API to fill an ellipse using the specified brush
  300. *
  301. * Return Value:
  302. *
  303. * A GpStatus value indicating success or failure.
  304. *
  305. * History:
  306. *
  307. * 02/18/1999 ikkof
  308. * Created it.
  309. *
  310. \**************************************************************************/
  311. GpStatus
  312. GpGraphics::FillEllipse(
  313. GpBrush* brush,
  314. const GpRectF& rect
  315. )
  316. {
  317. ASSERT(brush != NULL);
  318. // Objects returned from the API must always be in a valid state:
  319. ASSERT(this->IsValid() && brush->IsValid());
  320. GpPath path;
  321. GpStatus status = path.AddEllipse(rect);
  322. if ((status == Ok) && path.IsValid())
  323. {
  324. GpRectF bounds;
  325. path.GetBounds(&bounds, &(Context->WorldToDevice));
  326. if (IsRecording())
  327. {
  328. status = Metafile->RecordFillEllipse(&bounds, brush, rect);
  329. if (status != Ok)
  330. {
  331. SetValid(FALSE); // Prevent any more recording
  332. return status;
  333. }
  334. if (!DownLevel)
  335. {
  336. return Ok;
  337. }
  338. // else we need to record down-level GDI EMF records as well
  339. }
  340. // Call internal FillPath so that path doesn't get recorded in
  341. // the metafile again.
  342. status = RenderFillPath(&bounds, &path, brush);
  343. }
  344. return status;
  345. }
  346. /**************************************************************************\
  347. *
  348. * Function Description:
  349. *
  350. * API to fill a pie shape using the specified brush
  351. *
  352. * Return Value:
  353. *
  354. * A GpStatus value indicating success or failure.
  355. *
  356. * History:
  357. *
  358. * 02/18/1999 ikkof
  359. * Created it.
  360. *
  361. \**************************************************************************/
  362. GpStatus
  363. GpGraphics::FillPie(
  364. GpBrush* brush,
  365. const GpRectF& rect,
  366. REAL startAngle,
  367. REAL sweepAngle
  368. )
  369. {
  370. ASSERT(brush != NULL);
  371. // Objects returned from the API must always be in a valid state:
  372. ASSERT(this->IsValid() && brush->IsValid());
  373. GpPath path;
  374. GpStatus status = path.AddPie(rect, startAngle, sweepAngle);
  375. if ((status == Ok) && path.IsValid())
  376. {
  377. GpRectF bounds;
  378. path.GetBounds(&bounds, &(Context->WorldToDevice));
  379. if (IsRecording())
  380. {
  381. status = Metafile->RecordFillPie(&bounds, brush, rect,
  382. startAngle, sweepAngle);
  383. if (status != Ok)
  384. {
  385. SetValid(FALSE); // Prevent any more recording
  386. return status;
  387. }
  388. if (!DownLevel)
  389. {
  390. return Ok;
  391. }
  392. // else we need to record down-level GDI EMF records as well
  393. }
  394. // Call internal FillPath so that path doesn't get recorded in
  395. // the metafile again.
  396. status = RenderFillPath(&bounds, &path, brush);
  397. }
  398. return status;
  399. }
  400. /**************************************************************************\
  401. *
  402. * Function Description:
  403. *
  404. * API to fill region using the specified brush
  405. *
  406. * Return Value:
  407. *
  408. * A GpStatus value indicating success or failure.
  409. *
  410. * History:
  411. *
  412. * 12/18/1998 ikkof
  413. * Created it.
  414. *
  415. \**************************************************************************/
  416. GpStatus
  417. GpGraphics::FillRegion(
  418. GpBrush* brush,
  419. GpRegion* region
  420. )
  421. {
  422. GpStatus status;
  423. ASSERT((brush != NULL) && (region != NULL));
  424. // Objects returned from the API must always be in a valid state:
  425. ASSERT(this->IsValid() && brush->IsValid() && region->IsValid());
  426. BOOL regionIsEmpty;
  427. if ((status = region->IsEmpty(&Context->WorldToDevice, &regionIsEmpty)) != Ok)
  428. {
  429. return status;
  430. }
  431. if (regionIsEmpty)
  432. {
  433. return Ok;
  434. }
  435. GpRectF bounds;
  436. if ((status = region->GetBounds(this, &bounds, TRUE)) == Ok)
  437. {
  438. // The region may have very large bounds if it is infinite or if
  439. // an infinite region was combined with another region. We don't
  440. // want to draw a huge region into a metafile, because it will
  441. // mess up the bounds of the metafile. So intersect the region
  442. // with the appropriate bounding rect.
  443. GpRect metafileBounds; // in device units
  444. BOOL isMetafileGraphics = (this->Type == GraphicsMetafile);
  445. if (isMetafileGraphics)
  446. {
  447. if (this->Metafile != NULL)
  448. {
  449. this->Metafile->GetMetafileBounds(metafileBounds);
  450. metafileBounds.Width++; // make exclusive
  451. metafileBounds.Height++;
  452. }
  453. else // use size of HDC
  454. {
  455. HDC hdc = Context->GetHdc(Surface);
  456. metafileBounds.X = 0;
  457. metafileBounds.Y = 0;
  458. metafileBounds.Width = ::GetDeviceCaps(hdc, HORZRES);
  459. metafileBounds.Height = ::GetDeviceCaps(hdc, VERTRES);
  460. Context->ReleaseHdc(hdc);
  461. }
  462. GpRectF metafileBoundsF;
  463. metafileBoundsF.X = (REAL)metafileBounds.X;
  464. metafileBoundsF.Y = (REAL)metafileBounds.Y;
  465. metafileBoundsF.Width = (REAL)metafileBounds.Width;
  466. metafileBoundsF.Height = (REAL)metafileBounds.Height;
  467. bounds.Intersect(metafileBoundsF);
  468. }
  469. if (IsRecording())
  470. {
  471. status = Metafile->RecordFillRegion(&bounds, brush, region);
  472. if (status != Ok)
  473. {
  474. SetValid(FALSE); // Prevent any more recording
  475. return status;
  476. }
  477. if (!DownLevel)
  478. {
  479. return Ok;
  480. }
  481. // else we need to record down-level GDI EMF records as well
  482. }
  483. if (isMetafileGraphics)
  484. {
  485. status = RenderFillRegion(&bounds, region, brush, &metafileBounds);
  486. }
  487. else // not an infinite region
  488. {
  489. // call internal FillRegion that doesn't do recording
  490. status = RenderFillRegion(&bounds, region, brush, NULL);
  491. }
  492. }
  493. return status;
  494. }
  495. /**************************************************************************\
  496. *
  497. * Function Description:
  498. *
  499. * API to fill path using the specified brush
  500. *
  501. * Return Value:
  502. *
  503. * A GpStatus value indicating success or failure.
  504. *
  505. * History:
  506. *
  507. * 12/18/1998 ikkof
  508. * Created it.
  509. *
  510. \**************************************************************************/
  511. GpStatus
  512. GpGraphics::FillPath(
  513. const GpBrush* brush,
  514. GpPath* path
  515. )
  516. {
  517. GpStatus status = Ok;
  518. ASSERT((brush != NULL) && (path != NULL));
  519. // Objects returned from the API must always be in a valid state:
  520. ASSERT(this->IsValid() && brush->IsValid() && path->IsValid());
  521. // Don't do anything with less then 2 points.
  522. if (path->GetPointCount() < 3)
  523. {
  524. return status;
  525. }
  526. GpRectF bounds;
  527. // If the path is a rectangle, we can draw it much faster and
  528. // save space in spool files and metafiles if we fill it as a
  529. // rect instead of as a path.
  530. if (this->UseDriverRects() && path->IsRectangle(&(Context->WorldToDevice)))
  531. {
  532. path->GetBounds(&bounds, NULL);
  533. return this->FillRects(const_cast<GpBrush *>(brush), &bounds, 1);
  534. }
  535. path->GetBounds(&bounds, &(Context->WorldToDevice));
  536. if (IsRecording())
  537. {
  538. status = Metafile->RecordFillPath(&bounds, brush, path);
  539. if (status != Ok)
  540. {
  541. SetValid(FALSE); // Prevent any more recording
  542. return status;
  543. }
  544. if (!DownLevel)
  545. {
  546. return Ok;
  547. }
  548. // else we need to record down-level GDI EMF records as well
  549. }
  550. // call internal FillPath that doesn't do recording
  551. status = RenderFillPath(&bounds, path, brush);
  552. return status;
  553. }
  554. /**************************************************************************\
  555. *
  556. * Function Description:
  557. *
  558. * API to fill a closed curve using the specified brush
  559. *
  560. * Return Value:
  561. *
  562. * A GpStatus value indicating success or failure.
  563. *
  564. * History:
  565. *
  566. * 02/18/1999 ikkof
  567. * Created it.
  568. *
  569. \**************************************************************************/
  570. GpStatus
  571. GpGraphics::FillClosedCurve(
  572. GpBrush* brush,
  573. const GpPointF* points,
  574. INT count,
  575. REAL tension,
  576. GpFillMode fillMode
  577. )
  578. {
  579. ASSERT((brush != NULL) && (points != NULL));
  580. // Objects returned from the API must always be in a valid state:
  581. ASSERT(this->IsValid() && brush->IsValid());
  582. if ((count < 0) ||
  583. ((fillMode != FillModeWinding) && (fillMode != FillModeAlternate)))
  584. {
  585. return InvalidParameter;
  586. }
  587. // Less than three vertices constitutes an empty fill:
  588. if (count < 3)
  589. {
  590. return Ok;
  591. }
  592. GpPath path(fillMode);
  593. GpStatus status = path.AddClosedCurve(points, count, tension);
  594. if ((status == Ok) && path.IsValid())
  595. {
  596. GpRectF bounds;
  597. path.GetBounds(&bounds, &(Context->WorldToDevice));
  598. if (IsRecording())
  599. {
  600. status = Metafile->RecordFillClosedCurve(&bounds, brush,
  601. points, count, tension,
  602. fillMode);
  603. if (status != Ok)
  604. {
  605. SetValid(FALSE); // Prevent any more recording
  606. return status;
  607. }
  608. if (!DownLevel)
  609. {
  610. return Ok;
  611. }
  612. // else we need to record down-level GDI EMF records as well
  613. }
  614. // Call internal FillPath so that path doesn't get recorded in
  615. // the metafile again.
  616. status = RenderFillPath(&bounds, &path, brush);
  617. }
  618. return status;
  619. }
  620. /**************************************************************************\
  621. *
  622. * Function Description:
  623. *
  624. * API to draw polygons using the specified pen
  625. *
  626. * Arguments:
  627. *
  628. * [IN] pen - the pen for stroking.
  629. * [IN] points - the point data.
  630. * [IN] count - the number of points given in points array.
  631. *
  632. * Return Value:
  633. *
  634. * A GpStatus value indicating success or failure.
  635. *
  636. * History:
  637. *
  638. * 01/06/1999 ikkof
  639. * Created it.
  640. *
  641. \**************************************************************************/
  642. GpStatus
  643. GpGraphics::DrawLines(
  644. GpPen* pen,
  645. const GpPointF* points,
  646. INT count,
  647. BOOL closed
  648. )
  649. {
  650. GpStatus status = Ok;
  651. ASSERT((pen != NULL) && (points != NULL));
  652. if (count < 2)
  653. {
  654. return InvalidParameter;
  655. }
  656. // Objects returned from the API must always be in a valid state:
  657. ASSERT(this->IsValid() && pen->IsValid());
  658. const stackCount = 30;
  659. GpPointF stackPoints[stackCount];
  660. BYTE stackTypes[stackCount];
  661. GpPath path(points, count, stackPoints, stackTypes, stackCount, FillModeWinding);
  662. if(closed)
  663. path.CloseFigure();
  664. if (path.IsValid())
  665. {
  666. GpRectF bounds;
  667. REAL dpiX = GetDpiX();
  668. REAL dpiY = GetDpiY();
  669. path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  670. if (IsRecording())
  671. {
  672. status = Metafile->RecordDrawLines(&bounds, pen, points,
  673. count, closed);
  674. if (status != Ok)
  675. {
  676. SetValid(FALSE); // Prevent any more recording
  677. return status;
  678. }
  679. if (!DownLevel)
  680. {
  681. return Ok;
  682. }
  683. // else we need to record down-level GDI EMF records as well
  684. }
  685. // Call internal DrawPath so that path doesn't get recorded in
  686. // the metafile again.
  687. status = RenderDrawPath(&bounds, &path, pen);
  688. }
  689. return status;
  690. }
  691. /**************************************************************************\
  692. *
  693. * Function Description:
  694. *
  695. * API to draw an arc using the specified pen
  696. *
  697. * Arguments:
  698. *
  699. * [IN] pen - the pen for stroking.
  700. * [IN] rect - the boundary rect.
  701. * [IN] startAndle - the start angle in degrees
  702. * [IN] sweepAngle - the sweep angle in degrees in clockwise
  703. *
  704. * Return Value:
  705. *
  706. * A GpStatus value indicating success or failure.
  707. *
  708. * History:
  709. *
  710. * 02/18/1999 ikkof
  711. * Created it.
  712. *
  713. \**************************************************************************/
  714. GpStatus
  715. GpGraphics::DrawArc(
  716. GpPen* pen,
  717. const GpRectF& rect,
  718. REAL startAngle,
  719. REAL sweepAngle
  720. )
  721. {
  722. ASSERT(pen != NULL);
  723. // Objects returned from the API must always be in a valid state:
  724. ASSERT(this->IsValid() && pen->IsValid());
  725. GpPath path;
  726. GpStatus status = path.AddArc(rect, startAngle, sweepAngle);
  727. if ((status == Ok) && path.IsValid())
  728. {
  729. GpRectF bounds;
  730. REAL dpiX = GetDpiX();
  731. REAL dpiY = GetDpiY();
  732. path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  733. if (IsRecording())
  734. {
  735. status = Metafile->RecordDrawArc(&bounds, pen, rect,
  736. startAngle, sweepAngle);
  737. if (status != Ok)
  738. {
  739. SetValid(FALSE); // Prevent any more recording
  740. return status;
  741. }
  742. if (!DownLevel)
  743. {
  744. return Ok;
  745. }
  746. // else we need to record down-level GDI EMF records as well
  747. }
  748. // Call internal DrawPath so that path doesn't get recorded in
  749. // the metafile again.
  750. status = RenderDrawPath(&bounds, &path, pen);
  751. }
  752. return status;
  753. }
  754. /**************************************************************************\
  755. *
  756. * Function Description:
  757. *
  758. * API to draw Cubic Bezier curves using the specified pen
  759. *
  760. * Arguments:
  761. *
  762. * [IN] pen - the pen for stroking.
  763. * [IN] points - the control points.
  764. * [IN] count - the number of control points (must be 3n + 1).
  765. *
  766. * Return Value:
  767. *
  768. * A GpStatus value indicating success or failure.
  769. *
  770. * History:
  771. *
  772. * 02/18/1999 ikkof
  773. * Created it.
  774. *
  775. \**************************************************************************/
  776. GpStatus
  777. GpGraphics::DrawBeziers(
  778. GpPen* pen,
  779. const GpPointF* points,
  780. INT count
  781. )
  782. {
  783. ASSERT((pen != NULL) && (points != NULL));
  784. // Objects returned from the API must always be in a valid state:
  785. ASSERT(this->IsValid() && pen->IsValid());
  786. // Nothing to draw
  787. if (count <= 3)
  788. {
  789. return Ok;
  790. }
  791. GpPath path;
  792. GpStatus status = path.AddBeziers(points, count);
  793. if ((status == Ok) && path.IsValid())
  794. {
  795. GpRectF bounds;
  796. REAL dpiX = GetDpiX();
  797. REAL dpiY = GetDpiY();
  798. path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  799. if (IsRecording())
  800. {
  801. status = Metafile->RecordDrawBeziers(&bounds, pen,
  802. points, count);
  803. if (status != Ok)
  804. {
  805. SetValid(FALSE); // Prevent any more recording
  806. return status;
  807. }
  808. if (!DownLevel)
  809. {
  810. return Ok;
  811. }
  812. // else we need to record down-level GDI EMF records as well
  813. }
  814. // Call internal DrawPath so that path doesn't get recorded in
  815. // the metafile again.
  816. status = RenderDrawPath(&bounds, &path, pen);
  817. }
  818. return status;
  819. }
  820. /**************************************************************************\
  821. *
  822. * Function Description:
  823. *
  824. * API to draw rectangles using the specified brush
  825. *
  826. * Arguments:
  827. *
  828. * [IN] pen - the pen for stroking.
  829. * [IN] rects - the rectangle array.
  830. * [IN] count - the number of rectangles given in rects array.
  831. *
  832. * Return Value:
  833. *
  834. * A GpStatus value indicating success or failure.
  835. *
  836. * History:
  837. *
  838. * 01/15/1998 ikkof
  839. * Created it.
  840. *
  841. \**************************************************************************/
  842. GpStatus
  843. GpGraphics::DrawRects(
  844. GpPen* pen,
  845. const GpRectF* rects,
  846. INT count
  847. )
  848. {
  849. INT i;
  850. GpStatus status = Ok;
  851. // !!! Change Eng function to do clipping
  852. // !!! Create a stack path
  853. // !!! Fix multiple inheritence thing
  854. // !!! Check tail merging
  855. // !!! Add alignment checks
  856. // !!! Change DDIs to return GpStatus?
  857. // !!! Add ICM hooks?
  858. // !!! Change path constant to include 'single figure'?
  859. // !!! Create .LIB
  860. // !!! Add convention for alpha
  861. // Objects returned from the API must always be in a valid state:
  862. ASSERT((pen != NULL) && (rects != NULL));
  863. ASSERT(this->IsValid() && pen->IsValid());
  864. if (count < 0)
  865. {
  866. return InvalidParameter;
  867. }
  868. if (count == 0)
  869. {
  870. return Ok;
  871. }
  872. // Zoom through the list and accumulate the bounds. What a pain, but
  873. // we have to do this.
  874. // !!! We're doing 'double' goop, so we should ensure correct stack
  875. // alignment
  876. REAL left = rects[0].X;
  877. REAL top = rects[0].Y;
  878. REAL right = rects[0].GetRight();
  879. REAL bottom = rects[0].GetBottom();
  880. for (i = 1; i < count; i++)
  881. {
  882. if (rects[i].X < left)
  883. {
  884. left = rects[i].X;
  885. }
  886. if (rects[i].GetRight() > right)
  887. {
  888. right = rects[i].GetRight();
  889. }
  890. if (rects[i].Y < top)
  891. {
  892. top = rects[i].Y;
  893. }
  894. if (rects[i].GetBottom() > bottom)
  895. {
  896. bottom = rects[i].GetBottom();
  897. }
  898. }
  899. GpRectF bounds;
  900. // Convert the bounds to device space and adjust for the pen width
  901. REAL dpiX = GetDpiX();
  902. REAL dpiY = GetDpiY();
  903. DpPen *dpPen = pen->GetDevicePen();
  904. REAL penWidth = 0;
  905. Unit penUnit = UnitWorld;
  906. REAL delta = 0;
  907. if(dpPen)
  908. {
  909. penWidth = dpPen->Width;
  910. penUnit = dpPen->Unit;
  911. if(penUnit == UnitWorld)
  912. {
  913. // If the pen is in World unit, strech the rectangle
  914. // by pen width before the transform.
  915. // For a case of the centered pen.
  916. // penWidth/2 is OK. But here, we
  917. // just use penWidth for all pen mode.
  918. delta = penWidth;
  919. left -= delta;
  920. top -= delta;
  921. right += delta;
  922. bottom += delta;
  923. }
  924. }
  925. TransformBounds(&(Context->WorldToDevice), left, top, right, bottom,
  926. &bounds);
  927. if(dpPen)
  928. {
  929. if(penUnit != UnitWorld)
  930. {
  931. // If the pen is not in World unit, strech the rectangle
  932. // by pen's device width after the transform.
  933. REAL dpi = max(dpiX, dpiY);
  934. penWidth = ::GetDeviceWidth(penWidth, penUnit, dpi);
  935. // For a case of the centered pen.
  936. // penWidth/2 is OK. But here, we
  937. // just use penWidth for all pen mode.
  938. delta = penWidth;
  939. bounds.X -= delta;
  940. bounds.Y -= delta;
  941. bounds.Width += 2*delta;
  942. bounds.Height += 2*delta;
  943. }
  944. }
  945. if (IsRecording())
  946. {
  947. status = Metafile->RecordDrawRects(&bounds, pen, rects, count);
  948. if (status != Ok)
  949. {
  950. SetValid(FALSE); // Prevent any more recording
  951. return status;
  952. }
  953. if (!DownLevel)
  954. {
  955. return Ok;
  956. }
  957. // else we need to record down-level GDI EMF records as well
  958. }
  959. // Increase the bounds to account for the widener's minimum pen width.
  960. // For some arcane reason, the widener doesn't use 1.0 as the minimum
  961. // pen width. Rather it uses 1.000001f. Also it has some interesting
  962. // rounding properties, so our epsilon here is much larger 0.001f
  963. bounds.Inflate(1.001f, 1.001f);
  964. for (i = 0; i < count; i++)
  965. {
  966. if ((rects[i].Width > REAL_EPSILON) &&
  967. (rects[i].Height > REAL_EPSILON) )
  968. {
  969. // !!! Should use a stack-path
  970. // !!! For StrokePath case, should check start of rectangle
  971. // for styled lines
  972. GpPointF points[4];
  973. left = rects[i].X;
  974. top = rects[i].Y;
  975. right = rects[i].X + rects[i].Width;
  976. bottom = rects[i].Y + rects[i].Height;
  977. points[0].X = left;
  978. points[0].Y = top;
  979. points[1].X = right;
  980. points[1].Y = top;
  981. points[2].X = right;
  982. points[2].Y = bottom;
  983. points[3].X = left;
  984. points[3].Y = bottom;
  985. const INT stackCount = 10;
  986. GpPointF stackPoints[stackCount];
  987. BYTE stackTypes[stackCount];
  988. GpPath path(
  989. points,
  990. 4,
  991. stackPoints,
  992. stackTypes,
  993. stackCount,
  994. FillModeAlternate,
  995. DpPath::ConvexRectangle
  996. );
  997. path.CloseFigure();
  998. if(path.IsValid())
  999. {
  1000. // Call internal DrawPath so that path doesn't get recorded in
  1001. // the metafile again.
  1002. status = RenderDrawPath(&bounds, &path, pen);
  1003. if(status != Ok)
  1004. {
  1005. break;
  1006. }
  1007. }
  1008. }
  1009. }
  1010. return status;
  1011. }
  1012. /**************************************************************************\
  1013. *
  1014. * Function Description:
  1015. *
  1016. * API to draw an ellipse using the specified pen
  1017. *
  1018. * Arguments:
  1019. *
  1020. * [IN] pen - the pen for stroking.
  1021. * [IN] rect - the boundary rectangle
  1022. *
  1023. * Return Value:
  1024. *
  1025. * A GpStatus value indicating success or failure.
  1026. *
  1027. * History:
  1028. *
  1029. * 02/18/1999 ikkof
  1030. * Created it.
  1031. *
  1032. \**************************************************************************/
  1033. GpStatus
  1034. GpGraphics::DrawEllipse(
  1035. GpPen* pen,
  1036. const GpRectF& rect
  1037. )
  1038. {
  1039. ASSERT(pen != NULL);
  1040. // Objects returned from the API must always be in a valid state:
  1041. ASSERT(this->IsValid() && pen->IsValid());
  1042. GpPath path;
  1043. GpStatus status = path.AddEllipse(rect);
  1044. if ((status == Ok) && path.IsValid())
  1045. {
  1046. GpRectF bounds;
  1047. REAL dpiX = GetDpiX();
  1048. REAL dpiY = GetDpiY();
  1049. path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  1050. if (IsRecording())
  1051. {
  1052. status = Metafile->RecordDrawEllipse(&bounds, pen, rect);
  1053. if (status != Ok)
  1054. {
  1055. SetValid(FALSE); // Prevent any more recording
  1056. return status;
  1057. }
  1058. if (!DownLevel)
  1059. {
  1060. return Ok;
  1061. }
  1062. // else we need to record down-level GDI EMF records as well
  1063. }
  1064. // Call internal DrawPath so that path doesn't get recorded in
  1065. // the metafile again.
  1066. status = RenderDrawPath(&bounds, &path, pen);
  1067. }
  1068. return status;
  1069. }
  1070. /**************************************************************************\
  1071. *
  1072. * Function Description:
  1073. *
  1074. * API to draw a pie using the specified pen
  1075. *
  1076. * Arguments:
  1077. *
  1078. * [IN] pen - the pen for stroking.
  1079. * [IN] rect - the boundary rectangle
  1080. * [IN] startAngle - the start angle in degrees.
  1081. * [IN] sweepAngle - the sweep angle in degrees in clockwise.
  1082. *
  1083. * Return Value:
  1084. *
  1085. * A GpStatus value indicating success or failure.
  1086. *
  1087. * History:
  1088. *
  1089. * 02/18/1999 ikkof
  1090. * Created it.
  1091. *
  1092. \**************************************************************************/
  1093. GpStatus
  1094. GpGraphics::DrawPie(
  1095. GpPen* pen,
  1096. const GpRectF& rect,
  1097. REAL startAngle,
  1098. REAL sweepAngle
  1099. )
  1100. {
  1101. ASSERT(pen != NULL);
  1102. // Objects returned from the API must always be in a valid state:
  1103. ASSERT(this->IsValid() && pen->IsValid());
  1104. GpPath path;
  1105. GpStatus status = path.AddPie(rect, startAngle, sweepAngle);
  1106. if ((status == Ok) && path.IsValid())
  1107. {
  1108. GpRectF bounds;
  1109. REAL dpiX = GetDpiX();
  1110. REAL dpiY = GetDpiY();
  1111. path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  1112. if (IsRecording())
  1113. {
  1114. status = Metafile->RecordDrawPie(&bounds, pen, rect,
  1115. startAngle, sweepAngle);
  1116. if (status != Ok)
  1117. {
  1118. SetValid(FALSE); // Prevent any more recording
  1119. return status;
  1120. }
  1121. if (!DownLevel)
  1122. {
  1123. return Ok;
  1124. }
  1125. // else we need to record down-level GDI EMF records as well
  1126. }
  1127. // Call internal DrawPath so that path doesn't get recorded in
  1128. // the metafile again.
  1129. status = RenderDrawPath(&bounds, &path, pen);
  1130. }
  1131. return status;
  1132. }
  1133. /**************************************************************************\
  1134. *
  1135. * Function Description:
  1136. *
  1137. * API to draw path using the specified pen
  1138. *
  1139. * Return Value:
  1140. *
  1141. * A GpStatus value indicating success or failure.
  1142. *
  1143. * History:
  1144. *
  1145. * 01/27/1999 ikkof
  1146. * Created it.
  1147. *
  1148. \**************************************************************************/
  1149. GpStatus
  1150. GpGraphics::DrawPath(
  1151. GpPen* pen,
  1152. GpPath* path
  1153. )
  1154. {
  1155. GpStatus status = Ok;
  1156. ASSERT((pen != NULL) && (path != NULL));
  1157. // Objects returned from the API must always be in a valid state:
  1158. ASSERT(this->IsValid() && pen->IsValid() && path->IsValid());
  1159. // Don't do anything unless we have at least one point
  1160. if (path->GetPointCount() < 1)
  1161. {
  1162. return status;
  1163. }
  1164. GpRectF bounds;
  1165. REAL dpiX = GetDpiX();
  1166. REAL dpiY = GetDpiY();
  1167. path->GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  1168. if (IsRecording())
  1169. {
  1170. status = Metafile->RecordDrawPath(&bounds, pen, path);
  1171. if (status != Ok)
  1172. {
  1173. SetValid(FALSE); // Prevent any more recording
  1174. return status;
  1175. }
  1176. if (!DownLevel)
  1177. {
  1178. return Ok;
  1179. }
  1180. // else we need to record down-level GDI EMF records as well
  1181. }
  1182. // call internal DrawPath that doesn't do recording
  1183. status = RenderDrawPath(&bounds, path, pen);
  1184. return status;
  1185. }
  1186. /**************************************************************************\
  1187. *
  1188. * Function Description:
  1189. *
  1190. * API to draw a curve using the specified pen.
  1191. *
  1192. * Return Value:
  1193. *
  1194. * A GpStatus value indicating success or failure.
  1195. *
  1196. * History:
  1197. *
  1198. * 02/18/1999 ikkof
  1199. * Created it.
  1200. *
  1201. \**************************************************************************/
  1202. #define DEFAULT_TENSION 0.5
  1203. GpStatus
  1204. GpGraphics::DrawCurve(
  1205. GpPen* pen,
  1206. const GpPointF* points,
  1207. INT count
  1208. )
  1209. {
  1210. return DrawCurve(pen, points, count, DEFAULT_TENSION, 0, count - 1);
  1211. }
  1212. GpStatus
  1213. GpGraphics::DrawCurve(
  1214. GpPen* pen,
  1215. const GpPointF* points,
  1216. INT count,
  1217. REAL tension,
  1218. INT offset,
  1219. INT numberOfSegments
  1220. )
  1221. {
  1222. ASSERT((pen != NULL) && (points != NULL));
  1223. // Objects returned from the API must always be in a valid state:
  1224. ASSERT(this->IsValid() && pen->IsValid());
  1225. if (count < 2)
  1226. {
  1227. return InvalidParameter;
  1228. }
  1229. GpPath path;
  1230. GpStatus status = path.AddCurve(points,
  1231. count,
  1232. tension,
  1233. offset,
  1234. numberOfSegments);
  1235. if ((status == Ok) && path.IsValid())
  1236. {
  1237. GpRectF bounds;
  1238. REAL dpiX = GetDpiX();
  1239. REAL dpiY = GetDpiY();
  1240. path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  1241. if (IsRecording())
  1242. {
  1243. status = Metafile->RecordDrawCurve(&bounds, pen, points,
  1244. count, tension, offset,
  1245. numberOfSegments);
  1246. if (status != Ok)
  1247. {
  1248. SetValid(FALSE); // Prevent any more recording
  1249. return status;
  1250. }
  1251. if (!DownLevel)
  1252. {
  1253. return Ok;
  1254. }
  1255. // else we need to record down-level GDI EMF records as well
  1256. }
  1257. // Call internal DrawPath so that path doesn't get recorded in
  1258. // the metafile again.
  1259. status = RenderDrawPath(&bounds, &path, pen);
  1260. }
  1261. return status;
  1262. }
  1263. /**************************************************************************\
  1264. *
  1265. * Function Description:
  1266. *
  1267. * API to draw a closed curve using the specified pen.
  1268. *
  1269. * Return Value:
  1270. *
  1271. * A GpStatus value indicating success or failure.
  1272. *
  1273. * History:
  1274. *
  1275. * 02/18/1999 ikkof
  1276. * Created it.
  1277. *
  1278. \**************************************************************************/
  1279. GpStatus
  1280. GpGraphics::DrawClosedCurve(
  1281. GpPen* pen,
  1282. const GpPointF* points,
  1283. INT count
  1284. )
  1285. {
  1286. return DrawClosedCurve (pen, points, count, DEFAULT_TENSION);
  1287. }
  1288. GpStatus
  1289. GpGraphics::DrawClosedCurve(
  1290. GpPen* pen,
  1291. const GpPointF* points,
  1292. INT count,
  1293. REAL tension
  1294. )
  1295. {
  1296. ASSERT((pen != NULL) && (points != NULL));
  1297. // Objects returned from the API must always be in a valid state:
  1298. ASSERT(this->IsValid() && pen->IsValid());
  1299. if (count < 3)
  1300. {
  1301. return InvalidParameter;
  1302. }
  1303. GpPath path;
  1304. GpStatus status = path.AddClosedCurve(points, count, tension);
  1305. if ((status == Ok) && path.IsValid())
  1306. {
  1307. GpRectF bounds;
  1308. REAL dpiX = GetDpiX();
  1309. REAL dpiY = GetDpiY();
  1310. path.GetBounds(&bounds, &(Context->WorldToDevice), pen->GetDevicePen(), dpiX, dpiY);
  1311. if (IsRecording())
  1312. {
  1313. status = Metafile->RecordDrawClosedCurve(&bounds, pen, points,
  1314. count, tension);
  1315. if (status != Ok)
  1316. {
  1317. SetValid(FALSE); // Prevent any more recording
  1318. return status;
  1319. }
  1320. if (!DownLevel)
  1321. {
  1322. return Ok;
  1323. }
  1324. // else we need to record down-level GDI EMF records as well
  1325. }
  1326. // Call internal DrawPath so that path doesn't get recorded in
  1327. // the metafile again.
  1328. status = RenderDrawPath(&bounds, &path, pen);
  1329. }
  1330. return status;
  1331. }
  1332. /**************************************************************************\
  1333. *
  1334. * Function Description:
  1335. *
  1336. * Internal Drawing routine for a path. Various functions will
  1337. * call RenderFillPath.
  1338. *
  1339. * Return Value:
  1340. *
  1341. * A GpStatus value indicating success or failure.
  1342. *
  1343. * History:
  1344. *
  1345. * 09/18/2000 asecchia
  1346. * Created it.
  1347. *
  1348. \**************************************************************************/
  1349. GpStatus
  1350. GpGraphics::RenderFillPath(
  1351. GpRectF* bounds,
  1352. GpPath* path,
  1353. const GpBrush* brush
  1354. )
  1355. {
  1356. // Are they asking us to draw nothing?
  1357. if( REALABS(bounds->Width) < REAL_EPSILON ||
  1358. REALABS(bounds->Height) < REAL_EPSILON )
  1359. {
  1360. // Yes. Ok, we did it.
  1361. return Ok;
  1362. }
  1363. GpRect deviceBounds;
  1364. GpStatus status = BoundsFToRect(bounds, &deviceBounds);
  1365. if (status == Ok && !IsTotallyClipped(&deviceBounds))
  1366. {
  1367. // Now that we've done a bunch of work in accumulating the bounds,
  1368. // acquire the device lock before calling the driver:
  1369. Devlock devlock(Device);
  1370. return DrvFillPath(&deviceBounds, path, brush->GetDeviceBrush());
  1371. }
  1372. return status;
  1373. }
  1374. /**************************************************************************\
  1375. *
  1376. * Function Description:
  1377. *
  1378. * Internal Drawing routine for a path. Various functions will
  1379. * call RenderDrawPath.
  1380. *
  1381. * Return Value:
  1382. *
  1383. * A GpStatus value indicating success or failure.
  1384. *
  1385. * History:
  1386. *
  1387. * 10/28/1999 ikkof
  1388. * Created it.
  1389. *
  1390. \**************************************************************************/
  1391. GpStatus
  1392. GpGraphics::RenderDrawPath(
  1393. GpRectF * bounds,
  1394. GpPath * path,
  1395. GpPen * pen
  1396. )
  1397. {
  1398. // Are they asking us to draw nothing?
  1399. if( REALABS(bounds->Width) < REAL_EPSILON ||
  1400. REALABS(bounds->Height) < REAL_EPSILON )
  1401. {
  1402. // Yes. Ok, we did it.
  1403. return Ok;
  1404. }
  1405. GpRect deviceBounds;
  1406. GpStatus status = BoundsFToRect(bounds, &deviceBounds);
  1407. INT savedState = 0;
  1408. if (status == Ok && !IsTotallyClipped(&deviceBounds))
  1409. {
  1410. // Now that we've done a bunch of work in accumulating the bounds,
  1411. // acquire the device lock before calling the driver:
  1412. Devlock devlock(Device);
  1413. status = DrvStrokePath(&deviceBounds, path, pen->GetDevicePen());
  1414. }
  1415. return status;
  1416. }
  1417. VOID
  1418. GetEmfDpi(
  1419. HDC hdc,
  1420. REAL * dpiX,
  1421. REAL * dpiY
  1422. )
  1423. {
  1424. SIZEL szlDevice; // Size of the reference device in pels
  1425. SIZEL szlMillimeters; // Size of the reference device in millimeters
  1426. szlDevice.cx = GetDeviceCaps(hdc, HORZRES);
  1427. szlDevice.cy = GetDeviceCaps(hdc, VERTRES);
  1428. szlMillimeters.cx = GetDeviceCaps(hdc, HORZSIZE);
  1429. szlMillimeters.cy = GetDeviceCaps(hdc, VERTSIZE);
  1430. if ((szlDevice.cx > 0) && (szlDevice.cy > 0) &&
  1431. (szlMillimeters.cx > 0) && (szlMillimeters.cy > 0))
  1432. {
  1433. *dpiX = (static_cast<REAL>(szlDevice.cx) /
  1434. static_cast<REAL>(szlMillimeters.cx)) * 25.4f;
  1435. *dpiY = (static_cast<REAL>(szlDevice.cy) /
  1436. static_cast<REAL>(szlMillimeters.cy)) * 25.4f;
  1437. }
  1438. else
  1439. {
  1440. WARNING(("GetDeviceCaps failed"));
  1441. *dpiX = DEFAULT_RESOLUTION;
  1442. *dpiY = DEFAULT_RESOLUTION;
  1443. }
  1444. }
  1445. /**************************************************************************\
  1446. *
  1447. * Function Description:
  1448. *
  1449. * Get the size of the destination image in the current page units.
  1450. *
  1451. * Arguments:
  1452. *
  1453. * [IN] srcDpiX - horizontal resolution of the source image
  1454. * [IN] srcDpiY - vertical resolution of the source image
  1455. * [IN] srcWidth - width of the source image in srcUnit units
  1456. * [IN] srcHeight - height of the source image in srcUnit units
  1457. * [IN] srcUnit - units of the srcWidth and srcHeight
  1458. * [OUT] destWidth - destination width in the current page units
  1459. * [OUT] destHeight - destination height in the current page units
  1460. *
  1461. * Return Value:
  1462. *
  1463. * NONE
  1464. *
  1465. * Created:
  1466. *
  1467. * 05/10/1999 DCurtis
  1468. *
  1469. \**************************************************************************/
  1470. VOID
  1471. GpGraphics::GetImageDestPageSize(
  1472. const GpImage * image,
  1473. REAL srcWidth,
  1474. REAL srcHeight,
  1475. GpPageUnit srcUnit,
  1476. REAL & destWidth,
  1477. REAL & destHeight
  1478. )
  1479. {
  1480. // UnitDisplay is device-dependent and cannot be used for a source unit
  1481. ASSERT(srcUnit != UnitDisplay);
  1482. if (srcUnit == UnitPixel)
  1483. {
  1484. REAL srcDpiX;
  1485. REAL srcDpiY;
  1486. REAL destDpiX;
  1487. REAL destDpiY;
  1488. image->GetResolution(&srcDpiX, &srcDpiY);
  1489. // We don't want to create a bitmap just to get the dpi
  1490. // so check if we can get an Hdc easily from the context.
  1491. if ((image->GetImageType() == ImageTypeMetafile) &&
  1492. (((GpMetafile *)(image))->IsEmfOrEmfPlus()) &&
  1493. (Context->Hwnd || Context->Hdc))
  1494. {
  1495. // EMFs use a different style of dpi than other images that
  1496. // is based off the screen size instead of the font size.
  1497. if (Context->Hwnd)
  1498. {
  1499. // We don't need a clean dc to find out the dpi
  1500. HDC hdc = GetDC(Context->Hwnd);
  1501. GetEmfDpi(hdc, &destDpiX, &destDpiY);
  1502. ReleaseDC(Context->Hwnd, hdc);
  1503. }
  1504. else
  1505. {
  1506. GetEmfDpi(Context->Hdc, &destDpiX, &destDpiY);
  1507. }
  1508. }
  1509. else
  1510. {
  1511. destDpiX = GetDpiX();
  1512. destDpiY = GetDpiY();
  1513. }
  1514. // To get the dest size, convert the width and height from the image
  1515. // resolution to the resolution of this graphics and then convert
  1516. // them to page units by going through the inverse of the page to
  1517. // device transform.
  1518. destWidth = (srcWidth * destDpiX) /
  1519. (srcDpiX * Context->PageMultiplierX);
  1520. destHeight = (srcHeight * destDpiY) /
  1521. (srcDpiY * Context->PageMultiplierY);
  1522. }
  1523. else
  1524. {
  1525. // Just convert from the units of the image to the current
  1526. // page units.
  1527. REAL unitMultiplierX;
  1528. REAL unitMultiplierY;
  1529. Context->GetPageMultipliers(&unitMultiplierX, &unitMultiplierY,
  1530. srcUnit);
  1531. destWidth = (srcWidth * unitMultiplierX) / Context->PageMultiplierX;
  1532. destHeight = (srcHeight * unitMultiplierY) / Context->PageMultiplierY;
  1533. }
  1534. }
  1535. /**************************************************************************\
  1536. *
  1537. * Function Description:
  1538. *
  1539. * API to draw image
  1540. *
  1541. * Arguments:
  1542. *
  1543. * [IN] image - the image to draw.
  1544. * [IN] point - the top-left corner of the drawing boundary.
  1545. *
  1546. * Return Value:
  1547. *
  1548. * A GpStatus value indicating success or failure.
  1549. *
  1550. * History:
  1551. *
  1552. * 01/06/1999 ikkof
  1553. * Created it.
  1554. *
  1555. \**************************************************************************/
  1556. GpStatus
  1557. GpGraphics::DrawImage(
  1558. GpImage* image,
  1559. const GpPointF& point
  1560. )
  1561. {
  1562. GpStatus status;
  1563. ASSERT((image != NULL));
  1564. // Objects returned from the API must always be in a valid state:
  1565. ASSERT(this->IsValid() && image->IsValid());
  1566. GpRectF srcRect;
  1567. GpPageUnit srcUnit;
  1568. REAL destWidth;
  1569. REAL destHeight;
  1570. status = image->GetBounds(&srcRect, &srcUnit);
  1571. if(status != Ok) {return status;}
  1572. // UnitDisplay is device-dependent and cannot be used for a source unit
  1573. ASSERT(srcUnit != UnitDisplay);
  1574. if (status == Ok)
  1575. {
  1576. // Get the dest size in page units
  1577. GetImageDestPageSize(image, srcRect.Width, srcRect.Height,
  1578. srcUnit, destWidth, destHeight);
  1579. GpRectF destRect(point.X, point.Y, destWidth, destHeight);
  1580. return DrawImage(image, destRect, srcRect, srcUnit);
  1581. }
  1582. return status;
  1583. }
  1584. GpStatus
  1585. GpGraphics::DrawImage(
  1586. GpImage* image,
  1587. REAL x,
  1588. REAL y,
  1589. const GpRectF & srcRect,
  1590. GpPageUnit srcUnit
  1591. )
  1592. {
  1593. // UnitDisplay is device-dependent and cannot be used for a source unit
  1594. ASSERT(srcUnit != UnitDisplay);
  1595. REAL srcDpiX, srcDpiY;
  1596. REAL destWidth;
  1597. REAL destHeight;
  1598. // Get the dest size in page units
  1599. GetImageDestPageSize(image, srcRect.Width, srcRect.Height,
  1600. srcUnit, destWidth, destHeight);
  1601. GpRectF destRect(x, y, destWidth, destHeight);
  1602. return DrawImage(image, destRect, srcRect, srcUnit);
  1603. }
  1604. /**************************************************************************\
  1605. *
  1606. * Function Description:
  1607. *
  1608. * API to draw image.
  1609. *
  1610. * [IN] image - the image to draw.
  1611. * [IN] rect - the the drawing boundary.
  1612. *
  1613. * Return Value:
  1614. *
  1615. * A GpStatus value indicating success or failure.
  1616. *
  1617. * History:
  1618. *
  1619. * 01/12/1999 ikkof
  1620. * Created it.
  1621. *
  1622. \**************************************************************************/
  1623. GpStatus
  1624. GpGraphics::DrawImage(
  1625. GpImage* image,
  1626. const GpRectF& destRect
  1627. )
  1628. {
  1629. GpStatus status;
  1630. ASSERT((image != NULL));
  1631. // Objects returned from the API must always be in a valid state:
  1632. ASSERT(this->IsValid() && image->IsValid());
  1633. GpPageUnit srcUnit;
  1634. GpRectF srcRect;
  1635. status = image->GetBounds(&srcRect, &srcUnit);
  1636. if(status != Ok) { return status; }
  1637. // UnitDisplay is device-dependent and cannot be used for a source unit
  1638. ASSERT(srcUnit != UnitDisplay);
  1639. if (status == Ok)
  1640. {
  1641. return DrawImage(image, destRect, srcRect, srcUnit);
  1642. }
  1643. return status;
  1644. }
  1645. /**************************************************************************\
  1646. *
  1647. * Function Description:
  1648. *
  1649. * API to draw image.
  1650. *
  1651. * [IN] image - the image to draw.
  1652. * [IN] destPoints - the destination quad.
  1653. * [IN] count - the number of count in destPoints[] (3 or 4).
  1654. *
  1655. * Return Value:
  1656. *
  1657. * A GpStatus value indicating success or failure.
  1658. *
  1659. * History:
  1660. *
  1661. * 04/14/1999 ikkof
  1662. * Created it.
  1663. *
  1664. \**************************************************************************/
  1665. GpStatus
  1666. GpGraphics::DrawImage(
  1667. GpImage* image,
  1668. const GpPointF* destPoints,
  1669. INT count
  1670. )
  1671. {
  1672. GpStatus status;
  1673. // count of 4 is not implemented yet (perspective blt)
  1674. if(count == 4)
  1675. {
  1676. return NotImplemented;
  1677. }
  1678. if(count != 3)
  1679. {
  1680. return InvalidParameter;
  1681. }
  1682. ASSERT(count == 3); // Currently only supports Affine transform.
  1683. ASSERT((image != NULL));
  1684. // Objects returned from the API must always be in a valid state:
  1685. ASSERT(this->IsValid() && image->IsValid());
  1686. GpPageUnit srcUnit;
  1687. GpRectF srcRect;
  1688. status = image->GetBounds(&srcRect, &srcUnit);
  1689. if(status != Ok) { return status; }
  1690. // UnitDisplay is device-dependent and cannot be used for a source unit
  1691. ASSERT(srcUnit != UnitDisplay);
  1692. if (status == Ok)
  1693. {
  1694. return DrawImage(image, destPoints, count, srcRect, srcUnit);
  1695. }
  1696. return status;
  1697. }
  1698. /**************************************************************************\
  1699. *
  1700. * Function Description:
  1701. *
  1702. * API to draw image.
  1703. *
  1704. * [IN] image - the image to draw.
  1705. * [IN] destRect - the destination rectangle.
  1706. * [IN] srcRect - the portion of the image to copy.
  1707. *
  1708. * Return Value:
  1709. *
  1710. * A GpStatus value indicating success or failure.
  1711. *
  1712. * History:
  1713. *
  1714. * 01/12/1999 ikkof
  1715. * Created it.
  1716. *
  1717. \**************************************************************************/
  1718. GpStatus
  1719. GpGraphics::DrawImage(
  1720. GpImage* image,
  1721. const GpRectF& destRect,
  1722. const GpRectF& srcRect,
  1723. GpPageUnit srcUnit,
  1724. const GpImageAttributes* imageAttributes,
  1725. DrawImageAbort callback,
  1726. VOID* callbackData
  1727. )
  1728. {
  1729. GpStatus status = Ok;
  1730. ASSERT((image != NULL));
  1731. // Objects returned from the API must always be in a valid state:
  1732. ASSERT(this->IsValid() && image->IsValid());
  1733. // UnitDisplay is device-dependent and cannot be used for a source unit
  1734. ASSERT(srcUnit != UnitDisplay);
  1735. GpRectF offsetSrcRect = srcRect ;
  1736. GpPointF destPoints[3];
  1737. destPoints[0].X = destRect.X;
  1738. destPoints[0].Y = destRect.Y;
  1739. destPoints[1].X = destRect.X + destRect.Width;
  1740. destPoints[1].Y = destRect.Y;
  1741. destPoints[2].X = destRect.X;
  1742. destPoints[2].Y = destRect.Y + destRect.Height;
  1743. GpRectF bounds;
  1744. TransformBounds(
  1745. &(Context->WorldToDevice),
  1746. destPoints[0].X,
  1747. destPoints[0].Y,
  1748. destPoints[1].X,
  1749. destPoints[2].Y,
  1750. &bounds
  1751. );
  1752. GpImageType imageType = image->GetImageType();
  1753. DriverDrawImageFlags flags = 0;
  1754. GpRecolor * recolor = NULL;
  1755. if (imageAttributes != NULL)
  1756. {
  1757. if (imageAttributes->cachedBackground)
  1758. flags |= DriverDrawImageCachedBackground;
  1759. if (imageType == ImageTypeBitmap)
  1760. {
  1761. if (imageAttributes->HasRecoloring(ColorAdjustTypeBitmap))
  1762. {
  1763. goto HasRecoloring;
  1764. }
  1765. }
  1766. else if (imageAttributes->HasRecoloring())
  1767. {
  1768. HasRecoloring:
  1769. recolor = imageAttributes->recolor;
  1770. recolor->Flush();
  1771. }
  1772. }
  1773. GpImage * adjustedImage = NULL;
  1774. GpImageAttributes noRecoloring;
  1775. if (IsRecording())
  1776. {
  1777. if (recolor != NULL)
  1778. {
  1779. // We assume that the image is a bitmap.
  1780. // For Bitmaps, we want to recolor into an image that will have an
  1781. // alpha. CloneColorAdjusted keeps the same pixel format as the
  1782. // original image and therefore might not have an alpha channel.
  1783. // recolor will convert to ARGB. When recording to a metafile this
  1784. // will only create an ARGB image if the original image is not
  1785. // palettized, therefore only for 16bit and higher. The most
  1786. // space we can waste is twice the image.
  1787. if(image->GetImageType() == ImageTypeBitmap)
  1788. {
  1789. GpBitmap * bitmap = reinterpret_cast<GpBitmap*>(image);
  1790. GpBitmap * adjustedBitmap = NULL;
  1791. if (bitmap != NULL)
  1792. {
  1793. status = bitmap->Recolor(recolor, &adjustedBitmap, NULL, NULL);
  1794. if (status == Ok)
  1795. {
  1796. adjustedImage = adjustedBitmap;
  1797. }
  1798. }
  1799. }
  1800. else
  1801. {
  1802. adjustedImage = image->CloneColorAdjusted(recolor);
  1803. }
  1804. if (adjustedImage != NULL)
  1805. {
  1806. image = adjustedImage;
  1807. // have to set the recolor to NULL in the image attributes
  1808. // or else the down-level image will be double recolored.
  1809. GpRecolor * saveRecolor = noRecoloring.recolor;
  1810. noRecoloring = *imageAttributes;
  1811. noRecoloring.recolor = saveRecolor;
  1812. imageAttributes = &noRecoloring;
  1813. recolor = noRecoloring.recolor;
  1814. }
  1815. }
  1816. // Record recolored image.
  1817. status = Metafile->RecordDrawImage(
  1818. &bounds,
  1819. image,
  1820. destRect,
  1821. srcRect,
  1822. srcUnit,
  1823. imageAttributes
  1824. );
  1825. if (status != Ok)
  1826. {
  1827. SetValid(FALSE); // Prevent any more recording
  1828. goto Done;
  1829. }
  1830. if (!DownLevel)
  1831. {
  1832. goto Done;
  1833. }
  1834. // else we need to record down-level GDI EMF records as well
  1835. }
  1836. // Metafiles do not require PixelOffsetting, in fact it results in bad
  1837. // side effects in some cases when the source metafile dpi is low. But
  1838. // we still need to offset the DestRect to match the rendering with other
  1839. // primitives
  1840. // GillesK: If we are in HalfPixelMode, then offset the source and the
  1841. // destination rects by -0.5 pixels
  1842. if ((image->GetImageType() != ImageTypeMetafile) &&
  1843. (!Context->IsPrinter) &&
  1844. ((Context->PixelOffset == PixelOffsetModeHalf) ||
  1845. (Context->PixelOffset == PixelOffsetModeHighQuality)))
  1846. {
  1847. offsetSrcRect.Offset(-0.5f, -0.5f);
  1848. }
  1849. {
  1850. GpRect deviceBounds;
  1851. status = BoundsFToRect(&bounds, &deviceBounds);
  1852. if (status == Ok && !IsTotallyClipped(&deviceBounds))
  1853. {
  1854. if (imageType == ImageTypeBitmap)
  1855. {
  1856. INT numPoints = 3;
  1857. if (status == Ok)
  1858. {
  1859. // Now that we've done a bunch of work in accumulating the bounds,
  1860. // acquire the device lock before calling the driver:
  1861. Devlock devlock(Device);
  1862. ASSERT(srcUnit == UnitPixel); // !!! for now
  1863. // Set the fpu state.
  1864. FPUStateSaver fpuState;
  1865. status = DrvDrawImage(
  1866. &deviceBounds,
  1867. (GpBitmap*)(image),
  1868. numPoints,
  1869. &destPoints[0],
  1870. &offsetSrcRect, imageAttributes,
  1871. callback, callbackData,
  1872. flags
  1873. );
  1874. }
  1875. }
  1876. else if (imageType == ImageTypeMetafile)
  1877. {
  1878. // If we are recording to a different metafile, then we have
  1879. // already recorded this metafile as an image, and now we just
  1880. // want to record the down-level parts, so we have to set
  1881. // g->Metafile to NULL so we don't record all the GDI+ records
  1882. // in the metafile again -- only the down-level ones.
  1883. // Make sure to pass in the imageAttributes recolorer since it
  1884. // might have been changed if we already recolored the image
  1885. IMetafileRecord * recorder = this->Metafile;
  1886. this->Metafile = NULL;
  1887. status = (static_cast<const GpMetafile *>(image))->Play(
  1888. destRect, offsetSrcRect, srcUnit, this, recolor,
  1889. ColorAdjustTypeDefault, callback, callbackData);
  1890. this->Metafile = recorder; // restore the recorder (if any)
  1891. }
  1892. else
  1893. {
  1894. ASSERT(0);
  1895. status = NotImplemented;
  1896. }
  1897. }
  1898. }
  1899. Done:
  1900. if (adjustedImage != NULL)
  1901. {
  1902. adjustedImage->Dispose();
  1903. }
  1904. return status;
  1905. }
  1906. /**************************************************************************\
  1907. *
  1908. * Function Description:
  1909. *
  1910. * API to draw image.
  1911. *
  1912. * [IN] image - the image to draw.
  1913. * [IN] destPoints - the destination quad.
  1914. * [IN] count - the number of count in destPoints[] (3 or 4).
  1915. * [IN] srcRect - the portion of the image to copy.
  1916. *
  1917. * Return Value:
  1918. *
  1919. * A GpStatus value indicating success or failure.
  1920. *
  1921. * History:
  1922. *
  1923. * 04/14/1999 ikkof
  1924. * Created it.
  1925. *
  1926. \**************************************************************************/
  1927. GpStatus
  1928. GpGraphics::DrawImage(
  1929. GpImage* image,
  1930. const GpPointF* destPoints,
  1931. INT count,
  1932. const GpRectF& srcRect,
  1933. GpPageUnit srcUnit,
  1934. const GpImageAttributes* imageAttributes,
  1935. DrawImageAbort callback,
  1936. VOID* callbackData
  1937. )
  1938. {
  1939. GpStatus status = Ok;
  1940. // count of 4 is not implemented yet (perspective blt)
  1941. if(count == 4)
  1942. {
  1943. return NotImplemented;
  1944. }
  1945. if(count != 3)
  1946. {
  1947. return InvalidParameter;
  1948. }
  1949. ASSERT(count == 3); // Currently only supports Affine transform.
  1950. ASSERT((image != NULL));
  1951. // Objects returned from the API must always be in a valid state:
  1952. ASSERT(this->IsValid() && image->IsValid());
  1953. // UnitDisplay is device-dependent and cannot be used for a source unit
  1954. ASSERT(srcUnit != UnitDisplay);
  1955. GpRectF offsetSrcRect = srcRect ;
  1956. // NOTE: We could do this for all image types, including Bitmaps!!!
  1957. // It would save code to do this always.
  1958. if (image->GetImageType() != ImageTypeBitmap)
  1959. {
  1960. // Metafiles don't handle the destPoints API directly, so we
  1961. // have to convert to using the destRect API instead. To do so,
  1962. // we assume a canonical destRect and set up the transform to
  1963. // map from that destRect to the destPoints.
  1964. if (count == 3)
  1965. {
  1966. GpMatrix matrix;
  1967. GpRectF destRect(0.0f, 0.0f, 1000.0f, 1000.0f);
  1968. if (matrix.InferAffineMatrix(destPoints, destRect) == Ok)
  1969. {
  1970. INT gstate;
  1971. if ((gstate = this->Save()) != 0)
  1972. {
  1973. if ((status = this->MultiplyWorldTransform(
  1974. matrix, MatrixOrderPrepend)) == Ok)
  1975. {
  1976. status = this->DrawImage(image,
  1977. destRect,
  1978. srcRect,
  1979. srcUnit,
  1980. imageAttributes,
  1981. callback,
  1982. callbackData);
  1983. }
  1984. this->Restore(gstate);
  1985. return status;
  1986. }
  1987. }
  1988. return GenericError;
  1989. }
  1990. return NotImplemented;
  1991. }
  1992. // else it is a Bitmap
  1993. REAL xmin, xmax, ymin, ymax;
  1994. ASSERT(count == 3); // Currently only supports Affine transform.
  1995. // Set to the fourth corner point.
  1996. xmin = xmax = destPoints[1].X + destPoints[2].X - destPoints[0].X;
  1997. ymin = ymax = destPoints[1].Y + destPoints[2].Y - destPoints[0].Y;
  1998. // Compare with the other three corners.
  1999. for(INT i = 0; i < 3; i++)
  2000. {
  2001. xmin = min(xmin, destPoints[i].X);
  2002. xmax = max(xmax, destPoints[i].X);
  2003. ymin = min(ymin, destPoints[i].Y);
  2004. ymax = max(ymax, destPoints[i].Y);
  2005. }
  2006. GpRectF bounds;
  2007. TransformBounds(&(Context->WorldToDevice), xmin, ymin, xmax, ymax, &bounds);
  2008. INT numPoints = 3;
  2009. GpImageType imageType = image->GetImageType();
  2010. DriverDrawImageFlags flags = 0;
  2011. GpRecolor * recolor = NULL;
  2012. if (imageAttributes != NULL)
  2013. {
  2014. if (imageAttributes->cachedBackground)
  2015. flags |= DriverDrawImageCachedBackground;
  2016. if (imageType == ImageTypeBitmap)
  2017. {
  2018. if (imageAttributes->HasRecoloring(ColorAdjustTypeBitmap))
  2019. {
  2020. goto HasRecoloring;
  2021. }
  2022. }
  2023. else if (imageAttributes->HasRecoloring())
  2024. {
  2025. HasRecoloring:
  2026. recolor = imageAttributes->recolor;
  2027. recolor->Flush();
  2028. }
  2029. }
  2030. GpImage * adjustedImage = NULL;
  2031. GpImageAttributes noRecoloring;
  2032. if (IsRecording())
  2033. {
  2034. if (recolor != NULL)
  2035. {
  2036. // We assume that the image is a bitmap.
  2037. // For Bitmaps, we want to recolor into an image that will have an
  2038. // alpha. CloneColorAdjusted keeps the same pixel format as the
  2039. // original image and therefore might not have an alpha channel.
  2040. // recolor will convert to ARGB. When recording to a metafile this
  2041. // will only create an ARGB image if the original image is not
  2042. // palettized, therefore only for 16bit and higher. The most
  2043. // space we can waste is twice the image.
  2044. if(image->GetImageType() == ImageTypeBitmap)
  2045. {
  2046. GpBitmap * bitmap = reinterpret_cast<GpBitmap*>(image);
  2047. GpBitmap * adjustedBitmap = NULL;
  2048. if (bitmap != NULL)
  2049. {
  2050. status = bitmap->Recolor(recolor, &adjustedBitmap, NULL, NULL);
  2051. if (status == Ok)
  2052. {
  2053. adjustedImage = adjustedBitmap;
  2054. }
  2055. }
  2056. }
  2057. else
  2058. {
  2059. adjustedImage = image->CloneColorAdjusted(recolor);
  2060. }
  2061. if (adjustedImage != NULL)
  2062. {
  2063. image = adjustedImage;
  2064. // have to set the recolor to NULL in the image attributes
  2065. // or else the down-level image will be double recolored.
  2066. GpRecolor * saveRecolor = noRecoloring.recolor;
  2067. noRecoloring = *imageAttributes;
  2068. noRecoloring.recolor = saveRecolor;
  2069. imageAttributes = &noRecoloring;
  2070. }
  2071. }
  2072. // Record recolored image.
  2073. status = Metafile->RecordDrawImage(
  2074. &bounds,
  2075. image,
  2076. destPoints,
  2077. count,
  2078. srcRect,
  2079. srcUnit,
  2080. imageAttributes
  2081. );
  2082. if (status != Ok)
  2083. {
  2084. SetValid(FALSE); // Prevent any more recording
  2085. goto Done;
  2086. }
  2087. if (!DownLevel)
  2088. {
  2089. goto Done;
  2090. }
  2091. // else we need to record down-level GDI EMF records as well
  2092. }
  2093. // GillesK: If we are in HalfPixelMode, then offset the source and the
  2094. // destination rects by -0.5 pixels
  2095. if ((image->GetImageType() != ImageTypeMetafile) &&
  2096. (!Context->IsPrinter) &&
  2097. ((Context->PixelOffset == PixelOffsetModeHalf) ||
  2098. (Context->PixelOffset == PixelOffsetModeHighQuality)))
  2099. {
  2100. offsetSrcRect.Offset(-0.5f, -0.5f);
  2101. }
  2102. {
  2103. GpRect deviceBounds;
  2104. status = BoundsFToRect(&bounds, &deviceBounds);
  2105. if (status == Ok && !IsTotallyClipped(&deviceBounds))
  2106. {
  2107. // Now that we've done a bunch of work in accumulating the bounds,
  2108. // acquire the device lock before calling the driver:
  2109. Devlock devlock(Device);
  2110. ASSERT(srcUnit == UnitPixel); // !!! for now
  2111. // Set the fpu state.
  2112. FPUStateSaver fpuState;
  2113. // We assume that the image is a bitmap.
  2114. ASSERT(image->GetImageType() == ImageTypeBitmap);
  2115. status = DrvDrawImage(
  2116. &deviceBounds,
  2117. static_cast<GpBitmap*>(image),
  2118. numPoints,
  2119. &destPoints[0], &offsetSrcRect,
  2120. imageAttributes,
  2121. callback, callbackData,
  2122. flags
  2123. );
  2124. }
  2125. }
  2126. Done:
  2127. if (adjustedImage != NULL)
  2128. {
  2129. adjustedImage->Dispose();
  2130. }
  2131. return status;
  2132. }
  2133. GpStatus
  2134. GpGraphics::EnumerateMetafile(
  2135. const GpMetafile * metafile,
  2136. const PointF & destPoint,
  2137. EnumerateMetafileProc callback,
  2138. VOID * callbackData,
  2139. const GpImageAttributes * imageAttributes
  2140. )
  2141. {
  2142. GpStatus status;
  2143. ASSERT(metafile != NULL);
  2144. ASSERT(callback != NULL);
  2145. // Objects from the API must always be in a valid state:
  2146. ASSERT(this->IsValid() && metafile->IsValid());
  2147. GpPageUnit srcUnit;
  2148. GpRectF srcRect;
  2149. status = metafile->GetBounds(&srcRect, &srcUnit);
  2150. if(status != Ok) { return status; }
  2151. // UnitDisplay is device-dependent and cannot be used for a source unit
  2152. ASSERT(srcUnit != UnitDisplay);
  2153. if (status == Ok)
  2154. {
  2155. return this->EnumerateMetafile(
  2156. metafile,
  2157. destPoint,
  2158. srcRect,
  2159. srcUnit,
  2160. callback,
  2161. callbackData,
  2162. imageAttributes
  2163. );
  2164. }
  2165. return status;
  2166. }
  2167. GpStatus
  2168. GpGraphics::EnumerateMetafile(
  2169. const GpMetafile * metafile,
  2170. const RectF & destRect,
  2171. EnumerateMetafileProc callback,
  2172. VOID * callbackData,
  2173. const GpImageAttributes * imageAttributes
  2174. )
  2175. {
  2176. GpStatus status;
  2177. ASSERT(metafile != NULL);
  2178. ASSERT(callback != NULL);
  2179. // Objects from the API must always be in a valid state:
  2180. ASSERT(this->IsValid() && metafile->IsValid());
  2181. GpPageUnit srcUnit;
  2182. GpRectF srcRect;
  2183. status = metafile->GetBounds(&srcRect, &srcUnit);
  2184. if(status != Ok) { return status; }
  2185. // UnitDisplay is device-dependent and cannot be used for a source unit
  2186. ASSERT(srcUnit != UnitDisplay);
  2187. if (status == Ok)
  2188. {
  2189. return this->EnumerateMetafile(
  2190. metafile,
  2191. destRect,
  2192. srcRect,
  2193. srcUnit,
  2194. callback,
  2195. callbackData,
  2196. imageAttributes
  2197. );
  2198. }
  2199. return status;
  2200. }
  2201. GpStatus
  2202. GpGraphics::EnumerateMetafile(
  2203. const GpMetafile * metafile,
  2204. const PointF * destPoints,
  2205. INT count,
  2206. EnumerateMetafileProc callback,
  2207. VOID * callbackData,
  2208. const GpImageAttributes * imageAttributes
  2209. )
  2210. {
  2211. GpStatus status;
  2212. ASSERT(metafile != NULL);
  2213. ASSERT(callback != NULL);
  2214. // Objects from the API must always be in a valid state:
  2215. ASSERT(this->IsValid() && metafile->IsValid());
  2216. GpPageUnit srcUnit;
  2217. GpRectF srcRect;
  2218. status = metafile->GetBounds(&srcRect, &srcUnit);
  2219. if(status != Ok) { return status; }
  2220. // UnitDisplay is device-dependent and cannot be used for a source unit
  2221. ASSERT(srcUnit != UnitDisplay);
  2222. if (status == Ok)
  2223. {
  2224. return this->EnumerateMetafile(
  2225. metafile,
  2226. destPoints,
  2227. count,
  2228. srcRect,
  2229. srcUnit,
  2230. callback,
  2231. callbackData,
  2232. imageAttributes
  2233. );
  2234. }
  2235. return status;
  2236. }
  2237. GpStatus
  2238. GpGraphics::EnumerateMetafile(
  2239. const GpMetafile * metafile,
  2240. const PointF & destPoint,
  2241. const RectF & srcRect,
  2242. Unit srcUnit,
  2243. EnumerateMetafileProc callback,
  2244. VOID * callbackData,
  2245. const GpImageAttributes * imageAttributes
  2246. )
  2247. {
  2248. ASSERT(metafile != NULL);
  2249. ASSERT(callback != NULL);
  2250. // Objects from the API must always be in a valid state:
  2251. ASSERT(this->IsValid() && metafile->IsValid());
  2252. // UnitDisplay is device-dependent and cannot be used for a source unit
  2253. ASSERT(srcUnit != UnitDisplay);
  2254. REAL srcDpiX, srcDpiY;
  2255. REAL destWidth;
  2256. REAL destHeight;
  2257. // Get the dest size in page units
  2258. GetImageDestPageSize(metafile, srcRect.Width, srcRect.Height,
  2259. srcUnit, destWidth, destHeight);
  2260. GpRectF destRect(destPoint.X, destPoint.Y, destWidth, destHeight);
  2261. return this->EnumerateMetafile(
  2262. metafile,
  2263. destRect,
  2264. srcRect,
  2265. srcUnit,
  2266. callback,
  2267. callbackData,
  2268. imageAttributes
  2269. );
  2270. }
  2271. // All the EnumerateMetafile methods end up calling this one
  2272. GpStatus
  2273. GpGraphics::EnumerateMetafile(
  2274. const GpMetafile * metafile,
  2275. const RectF & destRect,
  2276. const RectF & srcRect,
  2277. Unit srcUnit,
  2278. EnumerateMetafileProc callback,
  2279. VOID * callbackData,
  2280. const GpImageAttributes * imageAttributes
  2281. )
  2282. {
  2283. ASSERT(metafile != NULL);
  2284. ASSERT(callback != NULL);
  2285. // Objects from the API must always be in a valid state:
  2286. ASSERT(this->IsValid() && metafile->IsValid());
  2287. // UnitDisplay is device-dependent and cannot be used for a source unit
  2288. ASSERT(srcUnit != UnitDisplay);
  2289. GpStatus status;
  2290. GpRecolor * recolor = NULL;
  2291. if ((imageAttributes != NULL) && imageAttributes->HasRecoloring())
  2292. {
  2293. recolor = imageAttributes->recolor;
  2294. recolor->Flush();
  2295. }
  2296. // NOTE: I don't check the bounds, because even if the entire
  2297. // metafile is out of the clip bounds, I still want to enumerate it.
  2298. status = metafile->EnumerateForPlayback(
  2299. destRect,
  2300. srcRect,
  2301. srcUnit,
  2302. this,
  2303. callback,
  2304. callbackData,
  2305. recolor
  2306. );
  2307. return status;
  2308. }
  2309. GpStatus
  2310. GpGraphics::EnumerateMetafile(
  2311. const GpMetafile * metafile,
  2312. const PointF * destPoints,
  2313. INT count,
  2314. const RectF & srcRect,
  2315. Unit srcUnit,
  2316. EnumerateMetafileProc callback,
  2317. VOID * callbackData,
  2318. const GpImageAttributes * imageAttributes
  2319. )
  2320. {
  2321. ASSERT(metafile != NULL);
  2322. ASSERT(callback != NULL);
  2323. // Objects from the API must always be in a valid state:
  2324. ASSERT(this->IsValid() && metafile->IsValid());
  2325. // UnitDisplay is device-dependent and cannot be used for a source unit
  2326. ASSERT(srcUnit != UnitDisplay);
  2327. GpStatus status = Ok;
  2328. // Metafiles don't handle the destPoints API directly, so we
  2329. // have to convert to using the destRect API instead. To do so,
  2330. // we assume a canonical destRect and set up the transform to
  2331. // map from that destRect to the destPoints.
  2332. ASSERT(count == 3); // Currently only supports Affine transform.
  2333. if (count == 3)
  2334. {
  2335. GpMatrix matrix;
  2336. GpRectF destRect(0.0f, 0.0f, 100.0f, 100.0f);
  2337. if (matrix.InferAffineMatrix(destPoints, destRect) == Ok)
  2338. {
  2339. INT gstate;
  2340. if ((gstate = this->Save()) != 0)
  2341. {
  2342. if ((status = this->MultiplyWorldTransform(
  2343. matrix, MatrixOrderPrepend)) == Ok)
  2344. {
  2345. status = this->EnumerateMetafile(
  2346. metafile,
  2347. destRect,
  2348. srcRect,
  2349. srcUnit,
  2350. callback,
  2351. callbackData,
  2352. imageAttributes
  2353. );
  2354. }
  2355. this->Restore(gstate);
  2356. return status;
  2357. }
  2358. }
  2359. return GenericError;
  2360. }
  2361. return NotImplemented;
  2362. }
  2363. /**************************************************************************\
  2364. *
  2365. * Function Description:
  2366. *
  2367. * API to get color ARGB value at pixel x,y. This is private GDI+ API.
  2368. *
  2369. * [IN] x - horizontal position
  2370. * [IN] y - vertical position
  2371. * [IN] argb - argb color value
  2372. *
  2373. * Return Value:
  2374. *
  2375. * A GpStatus value indicating success or failure.
  2376. *
  2377. * History:
  2378. *
  2379. * 05/13/1999 ericvan
  2380. * Created it.
  2381. *
  2382. \**************************************************************************/
  2383. GpStatus
  2384. GpGraphics::GetPixelColor(
  2385. REAL x,
  2386. REAL y,
  2387. ARGB* argb
  2388. ) const
  2389. {
  2390. GpPointF pt(x,y);
  2391. if (!IsVisible(pt))
  2392. return InvalidParameter;
  2393. Devlock devlock(Device);
  2394. DpScanBuffer scan(Surface->Scan,
  2395. Driver,
  2396. Context,
  2397. Surface,
  2398. CompositingModeSourceCopy);
  2399. Context->WorldToDevice.Transform(&pt, 1);
  2400. ARGB* buffer = scan.NextBuffer((INT)x, (INT)y, 1);
  2401. if (buffer)
  2402. *argb = *buffer;
  2403. else
  2404. return InvalidParameter;
  2405. return Ok;
  2406. }