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.

752 lines
21 KiB

  1. /*************************************************************************\
  2. * Module Name: EngStroke.c
  3. *
  4. * EngStrokePath for bitmap simulations, plus its kith.
  5. *
  6. * Created: 5-Apr-91
  7. * Author: Paul Butzi
  8. *
  9. * Copyright (c) 1990-1999 Microsoft Corporation
  10. *
  11. \**************************************************************************/
  12. #include "precomp.hxx"
  13. #include "solline.hxx"
  14. #include "engline.hxx"
  15. // #define DEBUG_ENGSTROK
  16. // Style array for alternate style (alternates one pixel on, one pixel off):
  17. STYLEPOS gaspAlternateStyle[] = { 1 };
  18. // Array to compute ROP masks:
  19. LONG aiLineMix[] = {
  20. AND_ZERO | XOR_ONE,
  21. AND_ZERO | XOR_ZERO,
  22. AND_NOTPEN | XOR_NOTPEN,
  23. AND_NOTPEN | XOR_ZERO,
  24. AND_ZERO | XOR_NOTPEN,
  25. AND_PEN | XOR_PEN,
  26. AND_ONE | XOR_ONE,
  27. AND_ONE | XOR_PEN,
  28. AND_PEN | XOR_ONE,
  29. AND_PEN | XOR_ZERO,
  30. AND_ONE | XOR_NOTPEN,
  31. AND_ONE | XOR_ZERO,
  32. AND_PEN | XOR_NOTPEN,
  33. AND_ZERO | XOR_PEN,
  34. AND_NOTPEN | XOR_ONE,
  35. AND_NOTPEN | XOR_PEN
  36. };
  37. static CHUNK mask1[] = {
  38. 0xffffffff, 0xffffff7f, 0xffffff3f, 0xffffff1f,
  39. 0xffffff0f, 0xffffff07, 0xffffff03, 0xffffff01,
  40. 0xffffff00, 0xffff7f00, 0xffff3f00, 0xffff1f00,
  41. 0xffff0f00, 0xffff0700, 0xffff0300, 0xffff0100,
  42. 0xffff0000, 0xff7f0000, 0xff3f0000, 0xff1f0000,
  43. 0xff0f0000, 0xff070000, 0xff030000, 0xff010000,
  44. 0xff000000, 0x7f000000, 0x3f000000, 0x1f000000,
  45. 0x0f000000, 0x07000000, 0x03000000, 0x01000000,
  46. 0x00000000,
  47. };
  48. static CHUNK mask4[] = {
  49. 0xffffffff,
  50. 0xffffff0f,
  51. 0xffffff00,
  52. 0xffff0f00,
  53. 0xffff0000,
  54. 0xff0f0000,
  55. 0xff000000,
  56. 0x0f000000,
  57. 0x00000000,
  58. };
  59. static CHUNK mask8[] = {
  60. 0xffffffff,
  61. 0xffffff00,
  62. 0xffff0000,
  63. 0xff000000,
  64. 0x00000000,
  65. };
  66. static CHUNK mask16[] = {
  67. 0xffffffff,
  68. 0xffff0000,
  69. 0x00000000,
  70. };
  71. static CHUNK mask24[] = {
  72. 0xffffff00,
  73. 0x00ffffff,
  74. 0x0000ffff,
  75. 0x000000ff,
  76. 0x00000000,
  77. };
  78. static CHUNK mask32[] = {
  79. 0xffffffff,
  80. 0x00000000,
  81. };
  82. static CHUNK maskpel1[] = {
  83. 0x00000080, 0x00000040, 0x00000020, 0x00000010,
  84. 0x00000008, 0x00000004, 0x00000002, 0x00000001,
  85. 0x00008000, 0x00004000, 0x00002000, 0x00001000,
  86. 0x00000800, 0x00000400, 0x00000200, 0x00000100,
  87. 0x00800000, 0x00400000, 0x00200000, 0x00100000,
  88. 0x00080000, 0x00040000, 0x00020000, 0x00010000,
  89. 0x80000000, 0x40000000, 0x20000000, 0x10000000,
  90. 0x08000000, 0x04000000, 0x02000000, 0x01000000,
  91. 0x00000000,
  92. };
  93. static CHUNK maskpel4[] = {
  94. 0x000000f0,
  95. 0x0000000f,
  96. 0x0000f000,
  97. 0x00000f00,
  98. 0x00f00000,
  99. 0x000f0000,
  100. 0xf0000000,
  101. 0x0f000000,
  102. 0x00000000,
  103. };
  104. static CHUNK maskpel8[] = {
  105. 0x000000ff,
  106. 0x0000ff00,
  107. 0x00ff0000,
  108. 0xff000000,
  109. 0x00000000,
  110. };
  111. static CHUNK maskpel16[] = {
  112. 0x0000ffff,
  113. 0xffff0000,
  114. 0x00000000,
  115. };
  116. static CHUNK maskpel24[] = {
  117. 0x00000000,
  118. 0x00000000,
  119. 0xff000000,
  120. 0xffff0000,
  121. };
  122. static CHUNK maskpel32[] = {
  123. 0xffffffff,
  124. 0x00000000,
  125. };
  126. // { Pointer to array of start masks,
  127. // Pointer to array of pixel masks,
  128. // # of pels per chunk (power of 2)
  129. // # of bits per pel
  130. // log2(pels per chunk)
  131. // pels per chunk - 1 }
  132. BMINFO gabminfo[] = {
  133. { NULL, NULL, 0, 0, 0, 0 }, // BMF_DEVICE
  134. { mask1, maskpel1, 32, 1, 5, 31 }, // BMF_1BPP
  135. { mask4, maskpel4, 8, 4, 3, 7 }, // BMF_4BPP
  136. { mask8, maskpel8, 4, 8, 2, 3 }, // BMF_8BPP
  137. { mask16, maskpel16, 2, 16, 1, 1 }, // BMF_16BPP
  138. { mask24, maskpel24, 0, 0, -1, 0 }, // BMF_24BPP
  139. { mask32, maskpel32, 1, 32, 0, 0 }, // BMF_32BPP
  140. };
  141. #if (BMF_1BPP != 1L)
  142. error Invalid value for BMF_1BPP
  143. #endif
  144. #if (BMF_4BPP != 2L)
  145. error Invalid value for BMF_4BPP
  146. #endif
  147. #if (BMF_8BPP != 3L)
  148. error Invalid value for BMF_8BPP
  149. #endif
  150. #if (BMF_16BPP != 4L)
  151. error Invalid value for BMF_16BPP
  152. #endif
  153. #if (BMF_24BPP != 5L)
  154. error Invalid value for BMF_24BPP
  155. #endif
  156. #if (BMF_32BPP != 6L)
  157. error Invalid value for BMF_32BPP
  158. #endif
  159. /******************************Public*Routine******************************\
  160. * BOOL bStrokeCosmetic(pso, ppo, eco, pbo, pla, mix)
  161. *
  162. * Strokes the path.
  163. *
  164. * History:
  165. * 20-Mar-1991 -by- Paul Butzi
  166. * Wrote it.
  167. \**************************************************************************/
  168. BOOL bStrokeCosmetic(
  169. SURFACE* pSurf,
  170. PATHOBJ* ppo,
  171. CLIPOBJ* pco,
  172. BRUSHOBJ* pbo,
  173. LINEATTRS* pla,
  174. MIX mix)
  175. {
  176. STYLEPOS aspLeftToRight[STYLE_MAX_COUNT];
  177. STYLEPOS aspRightToLeft[STYLE_MAX_COUNT];
  178. LINESTATE ls;
  179. // Verify that things are as they should be:
  180. ASSERTGDI(pbo->iSolidColor != 0xFFFFFFFF, "Expect solid cosmetic pen");
  181. ASSERTGDI(!(pla->fl & LA_GEOMETRIC) && !(ppo->fl & PO_BEZIERS),
  182. "Unprocessable path");
  183. FLONG fl = 0;
  184. // Look after styling initialization:
  185. if (pla->fl & LA_ALTERNATE)
  186. {
  187. ls.bStartGap = 0; // First pel is a dash
  188. ls.cStyle = 1; // Size of style array
  189. ls.xStep = 1; // x-styled step size
  190. ls.yStep = 1; // y-styled step size
  191. ls.spTotal = 1; // Sum of style array
  192. ls.spTotal2 = 2; // Twice the sum
  193. ls.aspRightToLeft = &gaspAlternateStyle[0]; // Right-to-left array
  194. ls.aspLeftToRight = &gaspAlternateStyle[0]; // Left-to-right array
  195. ls.spNext = HIWORD(pla->elStyleState.l) & 1;
  196. // Light first pixel if
  197. // a multiple of 2
  198. ls.xyDensity = 1; // Each 'dot' is one pixel
  199. // long
  200. fl |= FL_STYLED;
  201. }
  202. else if (pla->pstyle != (FLOAT_LONG*) NULL)
  203. {
  204. ASSERTGDI(pla->cstyle <= STYLE_MAX_COUNT, "Style array too large");
  205. {
  206. HDEV hdev = ((SURFACE *)pSurf)->hdev();
  207. if (hdev == 0)
  208. {
  209. // It is a pretty common mistake made by driver writers
  210. // (I've made it several times myself) to call EngStrokePath
  211. // on a temporary bitmap that it forgot to EngAssociateSurface.
  212. WARNING("Can't get at the physical device information!\n");
  213. WARNING("I'll bet the display/printer driver forgot to call");
  214. WARNING("EngAssociateSurface for a bitmap it created, and");
  215. WARNING("is now asking us to draw styled lines on it (we");
  216. WARNING("need the device's style information).\n");
  217. RIP("Please call EngAssociateSurface on the bitmap.");
  218. // Assume some defaults:
  219. ls.xStep = 1;
  220. ls.yStep = 1;
  221. ls.xyDensity = 3;
  222. }
  223. else
  224. {
  225. // We need the PDEV style information so that we can draw
  226. // the styles with the correct length dashes:
  227. PDEVOBJ po(hdev);
  228. ls.xStep = po.xStyleStep();
  229. ls.yStep = po.yStyleStep();
  230. ls.xyDensity = po.denStyleStep();
  231. ASSERTGDI(ls.xyDensity != 0,
  232. "Invalid denStyleStep supplied by the device!");
  233. }
  234. }
  235. fl |= FL_STYLED;
  236. ls.cStyle = pla->cstyle;
  237. ls.bStartGap = (pla->fl & LA_STARTGAP) > 0;
  238. ls.aspRightToLeft = aspRightToLeft;
  239. ls.aspLeftToRight = aspLeftToRight;
  240. FLOAT_LONG* pstyle = pla->pstyle;
  241. STYLEPOS* pspDown = &ls.aspRightToLeft[ls.cStyle - 1];
  242. STYLEPOS* pspUp = &ls.aspLeftToRight[0];
  243. ls.spTotal = 0;
  244. while (pspDown >= &ls.aspRightToLeft[0])
  245. {
  246. ASSERTGDI(pstyle->l > 0 && pstyle->l <= STYLE_MAX_VALUE,
  247. "Illegal style array value");
  248. *pspDown = pstyle->l * ls.xyDensity;
  249. *pspUp = *pspDown;
  250. ls.spTotal += *pspDown;
  251. pspUp++;
  252. pspDown--;
  253. pstyle++;
  254. }
  255. ls.spTotal2 = 2 * ls.spTotal;
  256. // Compute starting style position (this is guaranteed not to overflow):
  257. ls.spNext = HIWORD(pla->elStyleState.l) * ls.xyDensity +
  258. LOWORD(pla->elStyleState.l);
  259. // Do some normalizing:
  260. if (ls.spNext < 0)
  261. {
  262. RIP("Someone left style state negative");
  263. ls.spNext = 0;
  264. }
  265. if (ls.spNext >= ls.spTotal2)
  266. ls.spNext %= ls.spTotal2;
  267. }
  268. // Get device all warmed up and ready to go
  269. LONG lOldStyleState = pla->elStyleState.l;
  270. ULONG ulFormat = pSurf->iFormat();
  271. LONG lDelta = pSurf->lDelta() / (LONG)sizeof(CHUNK);
  272. CHUNK* pchBits = (CHUNK*)pSurf->pvScan0();
  273. BMINFO* pbmi = &gabminfo[ulFormat];
  274. CHUNK chOriginalColor = pbo->iSolidColor;
  275. switch (ulFormat)
  276. {
  277. case BMF_1BPP:
  278. chOriginalColor |= (chOriginalColor << 1);
  279. chOriginalColor |= (chOriginalColor << 2);
  280. // fall thru
  281. case BMF_4BPP:
  282. chOriginalColor |= (chOriginalColor << 4);
  283. // fall thru
  284. case BMF_8BPP:
  285. chOriginalColor |= (chOriginalColor << 8);
  286. // fall thru
  287. case BMF_16BPP:
  288. chOriginalColor |= (chOriginalColor << 16);
  289. // fall thru
  290. case BMF_24BPP:
  291. case BMF_32BPP:
  292. break;
  293. default:
  294. RIP("Invalid bitmap format");
  295. }
  296. {
  297. // All ROPs are handled in a single pass.
  298. CHUNK achColor[4];
  299. achColor[AND_ZERO] = 0;
  300. achColor[AND_PEN] = chOriginalColor;
  301. achColor[AND_NOTPEN] = ~chOriginalColor;
  302. achColor[AND_ONE] = 0xffffffff;
  303. LONG iIndex = aiLineMix[mix & 0xf];
  304. ls.chAnd = achColor[(iIndex & 0xff)];
  305. ls.chXor = achColor[iIndex >> MIX_XOR_OFFSET];
  306. }
  307. // Figure out which set of strippers to use:
  308. LONG iStrip = (ulFormat == BMF_24BPP) ? 8 : 0;
  309. iStrip |= (fl & FL_STYLED) ? 4 : 0;
  310. PFNSTRIP* apfn = &gapfnStrip[iStrip];
  311. if ((pco != NULL) && (pco->iDComplexity != DC_TRIVIAL))
  312. {
  313. // Handle complex clipping!
  314. BOOL bMore;
  315. union {
  316. BYTE aj[sizeof(CLIPLINE) + (RUN_MAX - 1) * sizeof(RUN)];
  317. CLIPLINE cl;
  318. } cl;
  319. fl |= FL_COMPLEX_CLIP;
  320. ((ECLIPOBJ*) pco)->vEnumPathStart(ppo, pSurf, pla);
  321. do {
  322. bMore = ((ECLIPOBJ*) ((EPATHOBJ*)ppo)->pco)->bEnumPath(ppo,
  323. sizeof(cl), &cl.cl);
  324. if (cl.cl.c != 0)
  325. {
  326. if (fl & FL_STYLED)
  327. {
  328. ls.spComplex = HIWORD(cl.cl.lStyleState) * ls.xyDensity
  329. + LOWORD(cl.cl.lStyleState);
  330. }
  331. if (!bLines(pbmi,
  332. &cl.cl.ptfxA,
  333. &cl.cl.ptfxB,
  334. &cl.cl.arun[0],
  335. cl.cl.c,
  336. &ls,
  337. (RECTL*) NULL,
  338. apfn,
  339. fl,
  340. pchBits,
  341. lDelta))
  342. return(FALSE);
  343. }
  344. } while (bMore);
  345. }
  346. else
  347. {
  348. // Handle simple or trivial clipping!
  349. PATHDATA pd;
  350. BOOL bMore;
  351. ULONG cptfx;
  352. POINTFIX ptfxStartFigure;
  353. POINTFIX ptfxLast;
  354. POINTFIX* pptfxFirst;
  355. POINTFIX* pptfxBuf;
  356. pd.flags = 0;
  357. ((EPATHOBJ*) ppo)->vEnumStart();
  358. do {
  359. bMore = ((EPATHOBJ*) ppo)->bEnum(&pd);
  360. cptfx = pd.count;
  361. if (cptfx == 0)
  362. {
  363. ASSERTGDI(!bMore, "Empty path record in non-empty path");
  364. break;
  365. }
  366. if (pd.flags & PD_BEGINSUBPATH)
  367. {
  368. ptfxStartFigure = *pd.pptfx;
  369. pptfxFirst = pd.pptfx;
  370. pptfxBuf = pd.pptfx + 1;
  371. cptfx--;
  372. }
  373. else
  374. {
  375. pptfxFirst = &ptfxLast;
  376. pptfxBuf = pd.pptfx;
  377. }
  378. if (pd.flags & PD_RESETSTYLE)
  379. ls.spNext = 0;
  380. // We have to check for cptfx == 0 because the only point in the
  381. // subpath may have been the StartFigure point:
  382. if (cptfx > 0)
  383. {
  384. if (!bLines(pbmi,
  385. pptfxFirst,
  386. pptfxBuf,
  387. (RUN*) NULL,
  388. cptfx,
  389. &ls,
  390. NULL,
  391. apfn,
  392. fl,
  393. pchBits,
  394. lDelta))
  395. return(FALSE);
  396. }
  397. ptfxLast = pd.pptfx[pd.count - 1];
  398. if (pd.flags & PD_CLOSEFIGURE)
  399. {
  400. if (!bLines(pbmi,
  401. &ptfxLast,
  402. &ptfxStartFigure,
  403. (RUN*) NULL,
  404. 1,
  405. &ls,
  406. NULL,
  407. apfn,
  408. fl,
  409. pchBits,
  410. lDelta))
  411. return(FALSE);
  412. }
  413. } while (bMore);
  414. if (fl & FL_STYLED)
  415. {
  416. // Save the style state:
  417. ULONG ulHigh = ls.spNext / ls.xyDensity;
  418. ULONG ulLow = ls.spNext % ls.xyDensity;
  419. pla->elStyleState.l = MAKELONG(ulLow, ulHigh);
  420. }
  421. }
  422. return(TRUE);
  423. }
  424. /******************************Public*Routine******************************\
  425. * BOOL EngStrokePath(pso, ppo, pco, pxo, pbo, pptlBrushOrg, pla, mix)
  426. *
  427. * Strokes the path.
  428. *
  429. * History:
  430. * 5-Apr-1992 -by- J. Andrew Goossen
  431. * Wrote it.
  432. \**************************************************************************/
  433. BOOL EngStrokePath(
  434. SURFOBJ *pso,
  435. PATHOBJ *ppo,
  436. CLIPOBJ *pco,
  437. XFORMOBJ *pxo,
  438. BRUSHOBJ *pbo,
  439. PPOINTL pptlBrushOrg,
  440. PLINEATTRS pla,
  441. MIX mix)
  442. {
  443. ASSERTGDI(pso != (SURFOBJ *) NULL, "EngStrokePath: surface\n");
  444. ASSERTGDI(ppo != (PATHOBJ *) NULL, "EngStrokePath: path\n");
  445. ASSERTGDI(pbo != (BRUSHOBJ *) NULL, "EngStrokePath: brushobj\n");
  446. PSURFACE pSurf = SURFOBJ_TO_SURFACE(pso);
  447. if (pla->fl & LA_GEOMETRIC)
  448. {
  449. // Handle wide lines, remembering that the widened bounds have
  450. // already been computed:
  451. if (!((EPATHOBJ*) ppo)->bWiden(pxo, pla))
  452. return(FALSE);
  453. return(EngFillPath(pSurf->pSurfobj(),
  454. ppo,
  455. pco,
  456. pbo,
  457. pptlBrushOrg,
  458. mix,
  459. WINDING));
  460. }
  461. if (ppo->fl & PO_BEZIERS)
  462. if (!((EPATHOBJ*) ppo)->bFlatten())
  463. return(FALSE);
  464. if (pSurf->iType() != STYPE_BITMAP)
  465. {
  466. // It's a cosmetic line to a device-managed surface. The DDI
  467. // requires that the driver support DrvStrokePath for cosmetic
  468. // lines if it has any device-managed surfaces:
  469. PDEVOBJ po(pSurf->hdev());
  470. ASSERTGDI(PPFNVALID(po, StrokePath),
  471. "Driver must hook DrvStrokePath if it supports device-managed surfaces");
  472. return((*PPFNDRV(po, StrokePath))(pSurf->pSurfobj(),
  473. ppo,
  474. pco,
  475. pxo,
  476. pbo,
  477. pptlBrushOrg,
  478. pla,
  479. mix));
  480. }
  481. // Before we touch any bits, make sure the device is happy about it.
  482. {
  483. PDEVOBJ po(pSurf->hdev());
  484. po.vSync(pso,NULL, 0);
  485. }
  486. // If this is a single pixel wide solid color line
  487. // with trivial or simple clipping then call solid line routine:
  488. if (((mix & 0xFF) == R2_COPYPEN) &&
  489. ((pco == NULL) || (pco->iDComplexity != DC_COMPLEX)) &&
  490. (pla->pstyle == NULL) &&
  491. !(pla->fl & LA_ALTERNATE))
  492. {
  493. vSolidLine(pSurf,
  494. ppo,
  495. NULL,
  496. pco,
  497. pbo->iSolidColor);
  498. return(TRUE);
  499. }
  500. return(bStrokeCosmetic(pSurf, ppo, pco, pbo, pla, mix));
  501. }
  502. /******************************Public*Routine******************************\
  503. * BOOL EngLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix)
  504. *
  505. * Draws a single solid integer-only cosmetic line.
  506. *
  507. * History:
  508. *
  509. * 12-Sept-1996 -by- Tom Zakrajsek
  510. * Made it work for device managed surfaces.
  511. *
  512. * 4-June-1995 -by- J. Andrew Goossen
  513. * Wrote it.
  514. \**************************************************************************/
  515. BOOL EngLineTo(
  516. SURFOBJ *pso,
  517. CLIPOBJ *pco,
  518. BRUSHOBJ *pbo,
  519. LONG x1,
  520. LONG y1,
  521. LONG x2,
  522. LONG y2,
  523. RECTL *prclBounds,
  524. MIX mix)
  525. {
  526. GDIFunctionID(EngLineTo);
  527. ASSERTGDI(pso != (SURFOBJ *) NULL, "pso is NULL\n");
  528. BOOL bReturn;
  529. PSURFACE pSurf = SURFOBJ_TO_SURFACE(pso);
  530. LINEATTRS la;
  531. PATHOBJ* ppo;
  532. POINTFIX aptfx[2];
  533. bReturn = FALSE;
  534. aptfx[0].x = x1 << 4;
  535. aptfx[0].y = y1 << 4;
  536. aptfx[1].x = x2 << 4;
  537. aptfx[1].y = y2 << 4;
  538. if (pSurf->iType() != STYPE_BITMAP)
  539. {
  540. // Device managed surface
  541. {
  542. // Solid cosmetic lines have everything zeroed in the LINEATTRS:
  543. memset(&la, 0, sizeof(LINEATTRS));
  544. la.elWidth.l = 1;
  545. // Create a path that describes the line:
  546. ppo = EngCreatePath();
  547. if (ppo != NULL)
  548. {
  549. if (PATHOBJ_bMoveTo(ppo, aptfx[0]) &&
  550. PATHOBJ_bPolyLineTo(ppo, &aptfx[1], 1))
  551. {
  552. PDEVOBJ pdo(pSurf->hdev());
  553. ECLIPOBJ eco;
  554. RGNMEMOBJTMP rmoTmp;
  555. // StrokePath is guaranteed not to get a NULL clip
  556. // object, so construct a temporary one:
  557. if (pco == NULL)
  558. {
  559. if (rmoTmp.bValid())
  560. {
  561. rmoTmp.vSet(prclBounds);
  562. eco.vSetup(rmoTmp.prgnGet(),
  563. *(ERECTL *) prclBounds);
  564. pco = &eco;
  565. }
  566. }
  567. // Clip may still be NULL if RGNMEMOBJTMP constructor
  568. // failed, so need to check:
  569. if (pco)
  570. {
  571. bReturn = (*PPFNGET(pdo,StrokePath,pSurf->flags())) (
  572. pso,
  573. ppo,
  574. pco,
  575. NULL, // pxo
  576. pbo,
  577. NULL, // pptlBrush
  578. &la,
  579. mix);
  580. }
  581. }
  582. EngDeletePath(ppo);
  583. }
  584. }
  585. }
  586. else
  587. {
  588. // Engine managed surface
  589. // Before we touch any bits, make sure the device is happy about it.
  590. {
  591. PDEVOBJ po(pSurf->hdev());
  592. po.vSync(pso,NULL,0);
  593. }
  594. if (((pco == NULL) || (pco->iDComplexity != DC_COMPLEX)) &&
  595. (mix == 0x0d0d))
  596. {
  597. // If this is a single pixel wide solid color line
  598. // with trivial or simple clipping then call solid line routine:
  599. vSolidLine(pSurf,
  600. NULL,
  601. aptfx,
  602. pco,
  603. pbo->iSolidColor);
  604. bReturn = TRUE;
  605. }
  606. else
  607. {
  608. // Solid cosmetic lines have everything zeroed in the LINEATTRS:
  609. memset(&la, 0, sizeof(LINEATTRS));
  610. // Create a path that describes the line:
  611. ppo = EngCreatePath();
  612. if (ppo != NULL)
  613. {
  614. if (PATHOBJ_bMoveTo(ppo, aptfx[0]) &&
  615. PATHOBJ_bPolyLineTo(ppo, &aptfx[1], 1))
  616. {
  617. bReturn = bStrokeCosmetic(pSurf, ppo, pco, pbo, &la, mix);
  618. }
  619. EngDeletePath(ppo);
  620. }
  621. }
  622. }
  623. return(bReturn);
  624. }