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.

1573 lines
43 KiB

  1. /**************************************************************************\
  2. *
  3. * Copyright (c) 1998 Microsoft Corporation
  4. *
  5. * Module Name:
  6. *
  7. * TextureFill.cpp
  8. *
  9. * Abstract:
  10. *
  11. * texture fill routines.
  12. *
  13. * Revision History:
  14. *
  15. * 01/21/1999 ikkof
  16. * Created it.
  17. *
  18. \**************************************************************************/
  19. #include "precomp.hpp"
  20. /**************************************************************************\
  21. *
  22. * Function Description:
  23. *
  24. * Initializes the fixed point variables needed for texture mapping.
  25. *
  26. * Created:
  27. *
  28. * 03/14/2000 andrewgo
  29. *
  30. \**************************************************************************/
  31. VOID
  32. DpOutputBilinearSpan_MMX::InitializeFixedPointState()
  33. {
  34. // If the numbers are too large for 16.16 fixed, we'll do the computation
  35. // in floating-point, per call to OutputSpan:
  36. ScaleMatrixValid = GpValidFixed16(DeviceToWorld.GetM11()) &&
  37. GpValidFixed16(DeviceToWorld.GetM12()) &&
  38. GpValidFixed16(DeviceToWorld.GetM21()) &&
  39. GpValidFixed16(DeviceToWorld.GetM22());
  40. if (ScaleMatrixValid)
  41. {
  42. GpPointF vector(1.0f, 0.0f);
  43. DeviceToWorld.VectorTransform(&vector);
  44. UIncrement = GpRound(vector.X * (1L << 16));
  45. VIncrement = GpRound(vector.Y * (1L << 16));
  46. TranslateMatrixValid = GpValidFixed16(DeviceToWorld.GetDx()) &&
  47. GpValidFixed16(DeviceToWorld.GetDy());
  48. if (TranslateMatrixValid)
  49. {
  50. M11 = GpRound(DeviceToWorld.GetM11() * (1L << 16));
  51. M12 = GpRound(DeviceToWorld.GetM12() * (1L << 16));
  52. M21 = GpRound(DeviceToWorld.GetM21() * (1L << 16));
  53. M22 = GpRound(DeviceToWorld.GetM22() * (1L << 16));
  54. Dx = GpRound(DeviceToWorld.GetDx() * (1L << 16));
  55. Dy = GpRound(DeviceToWorld.GetDy() * (1L << 16));
  56. }
  57. ModulusWidth = (BmpData.Width << 16);
  58. ModulusHeight = (BmpData.Height << 16);
  59. // When the u,v coordinates have the pixel in the last row or column
  60. // of the texture space, the offset of the pixel to the right and the
  61. // pixel below (for bilinear filtering) is the following (for tile modes)
  62. // because they wrap around the texture space.
  63. // The XEdgeIncrement is the byte increment of the pixel to the right of
  64. // the pixel on the far right hand column of the texture. In tile mode,
  65. // we want the pixel on the same scanline, but in the first column of the
  66. // texture hence 4bytes - stride
  67. XEdgeIncrement = 4-BmpData.Stride;
  68. // The YEdgeIncrement is the byte increment of the pixel below the current
  69. // pixel when the current pixel is in the last scanline of the texture.
  70. // In tile mode the correct pixel is the one directly above this one in
  71. // the first scanline - hence the increment below:
  72. YEdgeIncrement = -(INT)(BmpData.Height-1)*(INT)(BmpData.Stride);
  73. if ((BilinearWrapMode == WrapModeTileFlipX) ||
  74. (BilinearWrapMode == WrapModeTileFlipXY))
  75. {
  76. ModulusWidth *= 2;
  77. // Wrap increment is zero for Flip mode
  78. XEdgeIncrement = 0;
  79. }
  80. if ((BilinearWrapMode == WrapModeTileFlipY) ||
  81. (BilinearWrapMode == WrapModeTileFlipXY))
  82. {
  83. ModulusHeight *= 2;
  84. // Wrap increment is zero for Flip mode
  85. YEdgeIncrement = 0;
  86. }
  87. }
  88. }
  89. /**************************************************************************\
  90. *
  91. * Function Description:
  92. *
  93. * Texture brush constructor.
  94. *
  95. * Arguments:
  96. *
  97. * Created:
  98. *
  99. * 04/26/1999 ikkof
  100. *
  101. \**************************************************************************/
  102. DpOutputBilinearSpan::DpOutputBilinearSpan(
  103. const GpTexture *textureBrush,
  104. DpScanBuffer *scan,
  105. GpMatrix *worldToDevice,
  106. DpContext *context
  107. )
  108. {
  109. ASSERT(textureBrush);
  110. dBitmap = NULL;
  111. Scan = scan;
  112. // Initialize the wrap state.
  113. BilinearWrapMode = textureBrush->GetWrapMode();
  114. ClampColor = 0;
  115. SrcRectClamp = FALSE;
  116. if(textureBrush->GetImageType() != ImageTypeBitmap)
  117. {
  118. // Currently, Metafile is not implemented yet.
  119. Bitmap = NULL;
  120. return;
  121. }
  122. Bitmap = textureBrush->GetBitmap();
  123. // on bad bitmap, we return with Valid = FALSE
  124. if (Bitmap == NULL ||
  125. !Bitmap->IsValid() ||
  126. Bitmap->LockBits(
  127. NULL,
  128. IMGLOCK_READ,
  129. PixelFormat32bppPARGB,
  130. &BmpData
  131. ) != Ok)
  132. {
  133. Bitmap = NULL;
  134. return;
  135. }
  136. Size size;
  137. Bitmap->GetSize(&size);
  138. SrcRect.X = SrcRect.Y = 0;
  139. SrcRect.Width = (REAL)size.Width;
  140. SrcRect.Height = (REAL)size.Height;
  141. WorldToDevice = *worldToDevice;
  142. // If we have a PixelOffset of Half then offset the srcRect by -0.5
  143. // need to change the WorldToDevice to take this into account.
  144. // We need to do this here because texture brushes don't have enough
  145. // information to handle the PixelOffset at a higher level. We construct
  146. // the SrcRect here, so we apply the PixelOffset immediately.
  147. if (context->PixelOffset == PixelOffsetModeHalf ||
  148. context->PixelOffset == PixelOffsetModeHighQuality)
  149. {
  150. SrcRect.Offset(-0.5f, -0.5f);
  151. WorldToDevice.Translate(0.5f, 0.5f, MatrixOrderPrepend);
  152. }
  153. if(WorldToDevice.IsInvertible())
  154. {
  155. // !!![andrewgo] This failure case is bad, we will fall over
  156. // later with an uninitialized DeviceToWorld.
  157. DeviceToWorld = WorldToDevice;
  158. DeviceToWorld.Invert();
  159. }
  160. }
  161. DpOutputBilinearSpan::DpOutputBilinearSpan(
  162. const DpBitmap *bitmap,
  163. DpScanBuffer *scan,
  164. GpMatrix *worldToDevice,
  165. DpContext *context,
  166. DpImageAttributes *imageAttributes
  167. )
  168. {
  169. ASSERT(bitmap);
  170. dBitmap = bitmap;
  171. Scan = scan;
  172. // Set the imageAttributes state that's relevant to the bilinear span.
  173. BilinearWrapMode = imageAttributes->wrapMode;
  174. ClampColor = imageAttributes->clampColor;
  175. SrcRectClamp = imageAttributes->srcRectClamp;
  176. Bitmap = NULL;
  177. // on bad bitmap, we return with Valid = FALSE
  178. if (dBitmap == NULL || !dBitmap->IsValid() )
  179. {
  180. dBitmap = NULL;
  181. return;
  182. }
  183. else
  184. {
  185. BmpData.Width = dBitmap->Width;
  186. BmpData.Height = dBitmap->Height;
  187. BmpData.PixelFormat = PIXFMT_32BPP_PARGB;
  188. BmpData.Stride = dBitmap->Delta;
  189. BmpData.Scan0 = dBitmap->Bits;
  190. }
  191. // NOTE: SrcRect is not used.
  192. // The HalfPixelOffset is already incorporated into the
  193. // wordToDevice matrix passed in - which is not the same matrix as
  194. // context->WorldToDevice
  195. SrcRect.X = 0;
  196. SrcRect.Y = 0;
  197. SrcRect.Width = (REAL)dBitmap->Width;
  198. SrcRect.Height = (REAL)dBitmap->Height;
  199. WorldToDevice = *worldToDevice;
  200. if(WorldToDevice.IsInvertible())
  201. {
  202. // !!![andrewgo] This failure case is bad, we will fall over
  203. // later with an uninitialized DeviceToWorld.
  204. DeviceToWorld = WorldToDevice;
  205. DeviceToWorld.Invert();
  206. }
  207. }
  208. /**************************************************************************\
  209. *
  210. * Function Description:
  211. *
  212. * Texture brush constructor.
  213. *
  214. * Arguments:
  215. *
  216. * Created:
  217. *
  218. * 04/26/1999 ikkof
  219. *
  220. \**************************************************************************/
  221. DpOutputBilinearSpan::DpOutputBilinearSpan(
  222. DpBitmap* bitmap,
  223. DpScanBuffer * scan,
  224. DpContext* context,
  225. DpImageAttributes imageAttributes,
  226. INT numPoints,
  227. const GpPointF *dstPoints,
  228. const GpRectF *srcRect
  229. )
  230. {
  231. // NOTE: This constructor is not used.
  232. // I have no idea if it even works anymore.
  233. WARNING(("DpOutputBilinearSpan: unsupported constructor"));
  234. Scan = scan;
  235. BilinearWrapMode = imageAttributes.wrapMode;
  236. ClampColor = imageAttributes.clampColor;
  237. SrcRectClamp = imageAttributes.srcRectClamp;
  238. dBitmap = bitmap;
  239. Bitmap = NULL;
  240. // on bad bitmap, we return with Valid = FALSE
  241. if (dBitmap == NULL || !dBitmap->IsValid() )
  242. {
  243. dBitmap = NULL;
  244. return;
  245. }
  246. else
  247. {
  248. BmpData.Width = dBitmap->Width;
  249. BmpData.Height = dBitmap->Height;
  250. BmpData.PixelFormat = PIXFMT_32BPP_PARGB;
  251. BmpData.Stride = dBitmap->Delta;
  252. BmpData.Scan0 = dBitmap->Bits;
  253. }
  254. WorldToDevice = context->WorldToDevice;
  255. context->GetDeviceToWorld(&DeviceToWorld);
  256. // If we have a srcRect then it's already taking the PixelOffset mode into
  257. // account.
  258. if(srcRect)
  259. SrcRect = *srcRect;
  260. else
  261. {
  262. SrcRect.X = 0;
  263. SrcRect.Y = 0;
  264. SrcRect.Width = (REAL)dBitmap->Width;
  265. SrcRect.Height = (REAL)dBitmap->Height;
  266. if (context->PixelOffset == PixelOffsetModeHalf ||
  267. context->PixelOffset == PixelOffsetModeHighQuality)
  268. {
  269. SrcRect.Offset(-0.5f, -0.5f);
  270. }
  271. }
  272. GpPointF points[4];
  273. GpMatrix xForm;
  274. BOOL existsTransform = TRUE;
  275. switch(numPoints)
  276. {
  277. case 0:
  278. points[0].X = 0;
  279. points[0].Y = 0;
  280. points[1].X = (REAL) SrcRect.Width;
  281. points[1].Y = 0;
  282. points[2].X = 0;
  283. points[2].Y = (REAL) SrcRect.Height;
  284. break;
  285. case 1:
  286. points[0] = dstPoints[0];
  287. points[1].X = (REAL) (points[0].X + SrcRect.Width);
  288. points[1].Y = points[0].Y;
  289. points[2].X = points[0].X;
  290. points[2].Y = (REAL) (points[0].Y + SrcRect.Height);
  291. break;
  292. case 3:
  293. case 4:
  294. GpMemcpy(&points[0], dstPoints, numPoints*sizeof(GpPointF));
  295. break;
  296. default:
  297. existsTransform = FALSE;
  298. }
  299. if(existsTransform)
  300. {
  301. xForm.InferAffineMatrix(points, SrcRect);
  302. }
  303. WorldToDevice = context->WorldToDevice;
  304. WorldToDevice.Prepend(xForm);
  305. if(WorldToDevice.IsInvertible())
  306. {
  307. DeviceToWorld = WorldToDevice;
  308. DeviceToWorld.Invert();
  309. }
  310. }
  311. /**************************************************************************\
  312. *
  313. * Function Description:
  314. *
  315. * From the ARGB value of the four corners, this returns
  316. * the bilinearly interpolated ARGB value.
  317. *
  318. * Arguments:
  319. *
  320. * [IN] colors - ARGB values at the four corners.
  321. * [IN] xFrac - the fractional value of the x-coordinates.
  322. * [IN] yFrac - the fractional value of the y-coordinates.
  323. * [IN] one, shift. half2, shift2 - the extra arguments used in the
  324. * calculations.
  325. *
  326. * Return Value:
  327. *
  328. * ARGB: returns the biliearly interpolated ARGB.
  329. *
  330. * Created:
  331. *
  332. * 04/26/1999 ikkof
  333. *
  334. \**************************************************************************/
  335. inline ARGB
  336. getBilinearFilteredARGB(
  337. ARGB* colors,
  338. INT xFrac,
  339. INT yFrac,
  340. INT one,
  341. INT shift,
  342. INT half2,
  343. INT shift2)
  344. {
  345. INT a[4], r[4], g[4], b[4];
  346. INT alpha, red, green, blue;
  347. for(INT k = 0; k < 4; k++)
  348. {
  349. ARGB c = colors[k];
  350. a[k] = GpColor::GetAlphaARGB(c);
  351. r[k] = GpColor::GetRedARGB(c);
  352. g[k] = GpColor::GetGreenARGB(c);
  353. b[k] = GpColor::GetBlueARGB(c);
  354. }
  355. alpha =
  356. (
  357. (one - yFrac)*((a[0] << shift)
  358. + (a[1] - a[0])*xFrac)
  359. + yFrac*((a[2] << shift)
  360. + (a[3] - a[2])*xFrac)
  361. + half2
  362. ) >> shift2;
  363. red =
  364. (
  365. (one - yFrac)*((r[0] << shift)
  366. + (r[1] - r[0])*xFrac)
  367. + yFrac*((r[2] << shift)
  368. + (r[3] - r[2])*xFrac)
  369. + half2
  370. ) >> shift2;
  371. green =
  372. (
  373. (one - yFrac)*((g[0] << shift)
  374. + (g[1] - g[0])*xFrac)
  375. + yFrac*((g[2] << shift)
  376. + (g[3] - g[2])*xFrac)
  377. + half2
  378. ) >> shift2;
  379. blue =
  380. (
  381. (one - yFrac)*((b[0] << shift)
  382. + (b[1] - b[0])*xFrac)
  383. + yFrac*((b[2] << shift)
  384. + (b[3] - b[2])*xFrac)
  385. + half2
  386. ) >> shift2;
  387. return GpColor::MakeARGB
  388. (
  389. (BYTE) alpha,
  390. (BYTE) red,
  391. (BYTE) green,
  392. (BYTE) blue
  393. );
  394. }
  395. /**************************************************************************\
  396. *
  397. * Function Description:
  398. * virtual destructor for DpOutputBilinearSpan
  399. *
  400. \**************************************************************************/
  401. DpOutputBilinearSpan::~DpOutputBilinearSpan()
  402. {
  403. if (Bitmap != NULL)
  404. {
  405. Bitmap->UnlockBits(&BmpData);
  406. }
  407. }
  408. /**************************************************************************\
  409. *
  410. * Function Description:
  411. *
  412. * Applies the correct wrap mode to a set of coordinates
  413. *
  414. * Created:
  415. *
  416. * 03/10/2000 asecchia
  417. *
  418. \**************************************************************************/
  419. void ApplyWrapMode(INT WrapMode, INT &x, INT &y, INT w, INT h)
  420. {
  421. INT xm, ym;
  422. switch(WrapMode) {
  423. case WrapModeTile:
  424. x = RemainderI(x, w);
  425. y = RemainderI(y, h);
  426. break;
  427. case WrapModeTileFlipX:
  428. xm = RemainderI(x, w);
  429. if(((x-xm)/w) & 1) {
  430. x = w-1-xm;
  431. }
  432. else
  433. {
  434. x = xm;
  435. }
  436. y = RemainderI(y, h);
  437. break;
  438. case WrapModeTileFlipY:
  439. x = RemainderI(x, w);
  440. ym = RemainderI(y, h);
  441. if(((y-ym)/h) & 1) {
  442. y = h-1-ym;
  443. }
  444. else
  445. {
  446. y = ym;
  447. }
  448. break;
  449. case WrapModeTileFlipXY:
  450. xm = RemainderI(x, w);
  451. if(((x-xm)/w) & 1) {
  452. x = w-1-xm;
  453. }
  454. else
  455. {
  456. x = xm;
  457. }
  458. ym = RemainderI(y, h);
  459. if(((y-ym)/h) & 1) {
  460. y = h-1-ym;
  461. }
  462. else
  463. {
  464. y = ym;
  465. }
  466. break;
  467. /*
  468. // WrapModeExtrapolate is no longer used.
  469. case WrapModeExtrapolate:
  470. // Clamp the coordinates to the edge pixels of the source
  471. if(x<0) x=0;
  472. if(x>w-1) x=w-1;
  473. if(y<0) y=0;
  474. if(y>h-1) y=h-1;
  475. break;
  476. */
  477. case WrapModeClamp:
  478. // Don't do anything - the filter code will substitute the clamp
  479. // color when it detects clamp.
  480. default:
  481. break;
  482. }
  483. }
  484. /**************************************************************************\
  485. *
  486. * Function Description:
  487. *
  488. * Outputs a single span within a raster with a texture.
  489. * Is called by the rasterizer.
  490. *
  491. * Arguments:
  492. *
  493. * [IN] y - the Y value of the raster being output
  494. * [IN] leftEdge - the DDA class of the left edge
  495. * [IN] rightEdge - the DDA class of the right edge
  496. *
  497. * Return Value:
  498. *
  499. * GpStatus - Ok
  500. *
  501. * Created:
  502. *
  503. * 01/21/1999 ikkof
  504. *
  505. \**************************************************************************/
  506. GpStatus
  507. DpOutputBilinearSpan::OutputSpan(
  508. INT y,
  509. INT xMin,
  510. INT xMax // xMax is exclusive
  511. )
  512. {
  513. // Nothing to do.
  514. if(xMin==xMax)
  515. {
  516. return Ok;
  517. }
  518. ASSERT(xMin < xMax);
  519. ARGB argb;
  520. INT width = xMax - xMin;
  521. ARGB * buffer = Scan->NextBuffer(xMin, y, width);
  522. GpPointF pt1, pt2;
  523. pt1.X = (REAL) xMin;
  524. pt1.Y = pt2.Y = (REAL) y;
  525. pt2.X = (REAL) xMax;
  526. DeviceToWorld.Transform(&pt1);
  527. DeviceToWorld.Transform(&pt2);
  528. ARGB *srcPtr0 = static_cast<ARGB*> (BmpData.Scan0);
  529. INT stride = BmpData.Stride/sizeof(ARGB);
  530. INT i;
  531. REAL dx, dy, x0, y0;
  532. INT ix, iy;
  533. x0 = pt1.X;
  534. y0 = pt1.Y;
  535. ASSERT(width > 0);
  536. dx = (pt2.X - pt1.X)/width;
  537. dy = (pt2.Y - pt1.Y)/width;
  538. // Filtered image stretch.
  539. ARGB *srcPtr1, *srcPtr2;
  540. INT shift = 11; // (2*shift + 8 < 32 bits --> shift < 12)
  541. INT shift2 = shift + shift;
  542. INT one = 1 << shift;
  543. INT half2 = 1 << (shift2 - 1);
  544. INT xFrac, yFrac;
  545. REAL real;
  546. ARGB colors[4];
  547. INT x1, y1, x2, y2;
  548. for(i = 0; i < width; i++)
  549. {
  550. iy = GpFloor(y0);
  551. ix = GpFloor(x0);
  552. xFrac = GpRound((x0 - ix)*one);
  553. yFrac = GpRound((y0 - iy)*one);
  554. x1=ix;
  555. x2=ix+1;
  556. y1=iy;
  557. y2=iy+1;
  558. if( ((UINT)ix >= (UINT)(BmpData.Width-1) ) ||
  559. ((UINT)iy >= (UINT)(BmpData.Height-1)) )
  560. {
  561. ApplyWrapMode(BilinearWrapMode, x1, y1, BmpData.Width, BmpData.Height);
  562. ApplyWrapMode(BilinearWrapMode, x2, y2, BmpData.Width, BmpData.Height);
  563. }
  564. if(y1 >= 0 && y1 < (INT) BmpData.Height)
  565. {
  566. srcPtr1 = srcPtr0 + stride*y1;
  567. }
  568. else
  569. {
  570. srcPtr1 = NULL;
  571. }
  572. if(y2 >= 0 && y2 < (INT) BmpData.Height)
  573. {
  574. srcPtr2 = srcPtr0 + stride*y2;
  575. }
  576. else
  577. {
  578. srcPtr2 = NULL;
  579. }
  580. if(x1 >= 0 && x1 < (INT) BmpData.Width)
  581. {
  582. if(srcPtr1)
  583. {
  584. colors[0] = *(srcPtr1 + x1);
  585. }
  586. else
  587. {
  588. colors[0] = ClampColor;
  589. }
  590. if(srcPtr2)
  591. {
  592. colors[2] = *(srcPtr2 + x1);
  593. }
  594. else
  595. {
  596. colors[2] = ClampColor;
  597. }
  598. }
  599. else
  600. {
  601. colors[0] = ClampColor;
  602. colors[2] = ClampColor;
  603. }
  604. if(x2 >= 0 && x2 < (INT) BmpData.Width)
  605. {
  606. if(srcPtr1)
  607. {
  608. colors[1] = *(srcPtr1 + x2);
  609. }
  610. else
  611. {
  612. colors[1] = ClampColor;
  613. }
  614. if(srcPtr2)
  615. {
  616. colors[3] = *(srcPtr2 + x2);
  617. }
  618. else
  619. {
  620. colors[3] = ClampColor;
  621. }
  622. }
  623. else
  624. {
  625. colors[1] = ClampColor;
  626. colors[3] = ClampColor;
  627. }
  628. if((x2 >= 0) &&
  629. (x1 < (INT) BmpData.Width) &&
  630. (y2 >= 0) &&
  631. (y1 < (INT) BmpData.Height))
  632. {
  633. *buffer++ = ::getBilinearFilteredARGB(
  634. colors,
  635. xFrac,
  636. yFrac,
  637. one,
  638. shift,
  639. half2,
  640. shift2
  641. );
  642. }
  643. else
  644. {
  645. *buffer++ = ClampColor;
  646. }
  647. x0 += dx;
  648. y0 += dy;
  649. }
  650. return Ok;
  651. }
  652. /**************************************************************************\
  653. *
  654. * Function Description:
  655. *
  656. * Handles bilinear texture drawing with arbitrary rotation using MMX.
  657. *
  658. * Arguments:
  659. *
  660. * [IN] y - the Y value of the raster being output
  661. * [IN] leftEdge - the DDA class of the left edge
  662. * [IN] rightEdge - the DDA class of the right edge
  663. *
  664. * Return Value:
  665. *
  666. * GpStatus - Ok
  667. *
  668. * Created:
  669. *
  670. * 01/06/2000 andrewgo
  671. *
  672. \**************************************************************************/
  673. GpStatus
  674. DpOutputBilinearSpan_MMX::OutputSpan(
  675. INT y,
  676. INT xMin,
  677. INT xMax // xMax is exclusive
  678. )
  679. {
  680. // Be a little paranoid in checking some state.
  681. ASSERT((((ULONG_PTR) BmpData.Scan0) & 3) == 0);
  682. ASSERT((BmpData.Stride & 3) == 0);
  683. ASSERT(xMax > xMin);
  684. #if defined(_X86_)
  685. INT count = xMax - xMin;
  686. ARGB *buffer = Scan->NextBuffer(xMin, y, count);
  687. // Transform an array of points using the matrix v' = v M:
  688. //
  689. // ( M11 M12 0 )
  690. // (vx', vy', 1) = (vx, vy, 1) ( M21 M22 0 )
  691. // ( dx dy 1 )
  692. //
  693. // All (u, v) calculations are done in 16.16 fixed point.
  694. INT u;
  695. INT v;
  696. // If the values were out of range for fixed point, compute u and v in
  697. // floating-point, then convert:
  698. if (TranslateMatrixValid)
  699. {
  700. u = M11 * xMin + M21 * y + Dx;
  701. v = M12 * xMin + M22 * y + Dy;
  702. }
  703. else
  704. {
  705. GpPointF point(TOREAL(xMin), TOREAL(y));
  706. DeviceToWorld.Transform(&point, 1);
  707. u = GpRound(point.X * (1 << 16));
  708. v = GpRound(point.Y * (1 << 16));
  709. }
  710. INT uIncrement = UIncrement;
  711. INT vIncrement = VIncrement;
  712. INT modulusWidth = ModulusWidth;
  713. INT modulusHeight = ModulusHeight;
  714. VOID *scan0 = BmpData.Scan0;
  715. INT stride = BmpData.Stride;
  716. INT width = BmpData.Width;
  717. INT height = BmpData.Height;
  718. INT xEdgeIncrement = XEdgeIncrement;
  719. INT yEdgeIncrement = YEdgeIncrement;
  720. INT widthMinus1 = width - 1;
  721. INT heightMinus1 = height - 1;
  722. UINT uMax = widthMinus1 << 16;
  723. UINT vMax = heightMinus1 << 16;
  724. BOOL clampMode = (BilinearWrapMode == WrapModeClamp);
  725. ARGB clampColor = ClampColor;
  726. static ULONGLONG Half8dot8 = 0x0080008000800080;
  727. _asm
  728. {
  729. mov eax, u
  730. mov ebx, v
  731. mov ecx, stride
  732. mov edi, buffer
  733. pxor mm0, mm0
  734. movq mm3, Half8dot8
  735. ; edx = scratch
  736. ; esi = source pixel
  737. PixelLoop:
  738. ; Most of the time, our texture coordinate will be from the interior
  739. ; of the texture. Things only really get tricky when we have to
  740. ; span the texture edges.
  741. ;
  742. ; Fortunately, the interior case will happen most of the time,
  743. ; so we make that as fast as possible. We pop out-of-line to
  744. ; handle the tricky cases.
  745. cmp eax, uMax
  746. jae HandleTiling ; Note unsigned compare
  747. cmp ebx, vMax
  748. jae HandleTiling ; Note unsigned compare
  749. mov edx, eax
  750. shr edx, 14
  751. and edx, 0xfffffffc
  752. mov esi, ebx
  753. shr esi, 16
  754. imul esi, ecx
  755. add esi, edx
  756. add esi, scan0 ; esi = upper left pixel
  757. ; Stall city. Write first, then reorder with VTune.
  758. movd mm4, [esi]
  759. movd mm5, [esi+4]
  760. movd mm6, [esi+ecx]
  761. movd mm7, [esi+ecx+4]
  762. ContinueLoop:
  763. movd mm1, eax
  764. punpcklwd mm1, mm1
  765. punpckldq mm1, mm1
  766. psrlw mm1, 8 ; mm1 = x fraction in low bytes
  767. movd mm2, ebx
  768. punpcklwd mm2, mm2
  769. punpckldq mm2, mm2
  770. psrlw mm2, 8 ; mm2 = y fraction in low bytes
  771. punpcklbw mm4, mm0
  772. punpcklbw mm5, mm0 ; unpack pixels A & B to low bytes
  773. psubw mm5, mm4
  774. pmullw mm5, mm1
  775. paddw mm5, mm3
  776. psrlw mm5, 8
  777. paddb mm5, mm4 ; mm5 = A' = A + xFrac * (B - A)
  778. punpcklbw mm6, mm0
  779. punpcklbw mm7, mm0 ; unpack pixels C & D to low bytes
  780. psubw mm7, mm6
  781. pmullw mm7, mm1
  782. paddw mm7, mm3
  783. psrlw mm7, 8
  784. paddb mm7, mm6 ; mm7 = B' = C + xFrac * (D - C)
  785. psubw mm7, mm5
  786. pmullw mm7, mm2
  787. paddw mm7, mm3
  788. psrlw mm7, 8
  789. paddb mm7, mm5 ; mm7 = A' + yFrac * (B' - A')
  790. packuswb mm7, mm7
  791. movd [edi], mm7 ; write the final pixel
  792. add eax, uIncrement
  793. add ebx, vIncrement
  794. add edi, 4
  795. dec count
  796. jnz PixelLoop
  797. jmp AllDone
  798. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  799. ;
  800. ; Handle tiling cases here
  801. ;
  802. ; All the tough edge cases are handled here, where we deal with
  803. ; texture coordinates that span the texture boundary.
  804. HandleTiling:
  805. cmp clampMode, 0
  806. jnz HandleClamping
  807. ; Get 'u' in the range 0 <= u < modulusWidth:
  808. cmp eax, modulusWidth
  809. jb WidthInRange ; note unsigned compare
  810. cdq
  811. idiv modulusWidth
  812. mov eax, edx ; u %= modulusWidth
  813. cmp eax, 0
  814. jge WidthInRange
  815. add eax, modulusWidth ; 0 <= u < modulusWidth
  816. WidthInRange:
  817. ; Get 'v' in the range 0 <= v < modulusHeight:
  818. cmp ebx, modulusHeight
  819. jb HeightInRange
  820. push eax
  821. mov eax, ebx
  822. cdq
  823. idiv modulusHeight
  824. mov ebx, edx ; v %= modulusHeight
  825. pop eax
  826. cmp ebx, 0
  827. jge HeightInRange
  828. add ebx, modulusHeight ; 0 <= v < modulusHeight
  829. HeightInRange:
  830. ; Now we're going to need to convert our 'u' and 'v' values
  831. ; to integers, so save the 16.16 versions:
  832. push eax
  833. push ebx
  834. push ecx
  835. push edi
  836. sar eax, 16
  837. sar ebx, 16 ; note arithmetic shift
  838. ; Handle 'flipping'. Note that edi hold flipping flags, where
  839. ; the bits have the following meanings:
  840. ; 1 = X flip in progress
  841. ; 2 = Y flip in progress
  842. ; 4 = X flip end boundary not yet reached
  843. ; 8 = Y flip end boundary not yet reached.
  844. xor edi, edi
  845. cmp eax, width
  846. jb XFlipHandled
  847. ; u is in the range (width <= u < 2*width).
  848. ;
  849. ; We want to flip it such that (0 <= u' < width), which we do by
  850. ; u' = 2*width - u - 1. Don't forget ~u = -u - 1.
  851. or edi, 1 ; mark the flip
  852. not eax
  853. add eax, width
  854. add eax, width
  855. jz XFlipHandled
  856. sub eax, 1
  857. or edi, 4 ; mark flip where adjacent pixels available
  858. XFlipHandled:
  859. cmp ebx, height
  860. jb YFlipHandled
  861. ; v is in the range (height <= v < 2*height).
  862. ;
  863. ; We want to flip it such that (0 <= v' < height), which we do by
  864. ; v' = 2*height - v - 1. Don't forget ~v = -v - 1.
  865. or edi, 2 ; mark the flip
  866. not ebx
  867. add ebx, height
  868. add ebx, height
  869. jz YFlipHandled
  870. sub ebx, 1
  871. or edi, 8 ; mark flip where adjacent pixels available
  872. YFlipHandled:
  873. mov esi, ebx
  874. imul esi, ecx ; esi = y * stride
  875. ; Set 'edx' to the byte offset to the pixel one to the right, accounting
  876. ; for wrapping past the edge of the bitmap. Only set the byte offset to
  877. ; point to right pixel for non edge cases.
  878. mov edx, 4
  879. test edi, 4
  880. jnz RightIncrementCalculated
  881. test edi, 1
  882. jnz SetXEdgeInc
  883. cmp eax, widthMinus1
  884. jb RightIncrementCalculated
  885. SetXEdgeInc:
  886. mov edx, xEdgeIncrement
  887. ; When we flipX and the current pixel is the last pixel in the texture
  888. ; line, wrapping past the end of the bitmap wraps back in the same side
  889. ; of the bitmap. I.e. for this one specific pixel we can set the pixel
  890. ; on-the-right to be the same as this pixel (increment of zero).
  891. ; Only valid because this is the edge condition.
  892. ; Note that this will occur for two successive pixels as the texture
  893. ; wrap occurs - first at width-1 and then at width-1 after wrapping.
  894. ;
  895. ; A | B
  896. ; --+--
  897. ; C | D
  898. ;
  899. ; At this point, pixel A has been computed correctly accounting for the
  900. ; flip/tile and wrapping beyond the edge of the texture. We work out
  901. ; the offset of B from A, but we again need to take into account the
  902. ; possible flipX mode if pixel A happens to be the last pixel in the
  903. ; texture scanline (the code immediately above takes into account
  904. ; tiling across the texture boundary, but not the flip)
  905. RightIncrementCalculated:
  906. ; Set 'ecx' to the byte offset to the pixel one down, accounting for
  907. ; wrapping past the edge of the bitmap. Only set the byte offset to
  908. ; point to one pixel down for non edge cases.
  909. test edi, 8
  910. jnz DownIncrementCalculated
  911. test edi, 2
  912. jnz SetYEdgeInc
  913. cmp ebx, heightMinus1
  914. jb DownIncrementCalculated
  915. SetYEdgeInc:
  916. mov ecx, yEdgeIncrement
  917. ; When we flipY and the current pixel is in the last scanline in the
  918. ; texture, wrapping past the end of the bitmap wraps back in the same
  919. ; side of the bitmap. I.e. for this one specific scanline we can set
  920. ; the pixel offset one down to be the same as this pixel
  921. ; (increment of zero).
  922. ; Only valid because this is the edge condition.
  923. ; (see comment above RightIncrementCalculated:)
  924. DownIncrementCalculated:
  925. ; Finish calculating the upper-left pixel address:
  926. add esi, scan0
  927. shl eax, 2
  928. add esi, eax ; esi = upper left pixel
  929. ; Load the 4 pixels:
  930. movd mm4, [esi]
  931. movd mm5, [esi+edx]
  932. add esi, ecx
  933. movd mm6, [esi]
  934. movd mm7, [esi+edx]
  935. ; Finish handling the flip:
  936. test edi, 1
  937. jz XSwapDone
  938. movq mm1, mm5
  939. movq mm5, mm4
  940. movq mm4, mm1 ; swap pixels A and B
  941. movq mm1, mm6
  942. movq mm6, mm7
  943. movq mm7, mm1 ; swap pixels C and D
  944. XSwapDone:
  945. test edi, 2
  946. jz YSwapDone
  947. movq mm1, mm4
  948. movq mm4, mm6
  949. movq mm6, mm1 ; swap pixels A and C
  950. movq mm1, mm5
  951. movq mm5, mm7
  952. movq mm7, mm1 ; swap pixels B and D
  953. YSwapDone:
  954. ; Restore everything and get out:
  955. pop edi
  956. pop ecx
  957. pop ebx
  958. pop eax
  959. jmp ContinueLoop
  960. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  961. ;
  962. ; Clamp mode.
  963. ;
  964. ; Set the pixel values to 0 for any not on the texture.
  965. HandleClamping:
  966. push eax
  967. push ebx
  968. movd mm4, clampColor
  969. movq mm5, mm4
  970. movq mm6, mm4 ; initialize invisible pixels to
  971. movq mm7, mm4 ; the clampColor.
  972. sar eax, 16
  973. sar ebx, 16 ; note these are arithmetic shifts
  974. ; We need to look at a 2x2 square of pixels in the texture, where
  975. ; (eax, ebx) represents the (x, y) texture coordinates. First we
  976. ; check for the case where none of the four pixel locations are
  977. ; actually anywhere on the texture.
  978. cmp eax, -1
  979. jl FinishClamp
  980. cmp eax, width
  981. jge FinishClamp ; early out if (x < -1) or (x >= width)
  982. cmp ebx, -1
  983. jl FinishClamp
  984. cmp ebx, height
  985. jge FinishClamp ; handle trivial rejection
  986. ; Okay, now we know that we have to pull at least one pixel from
  987. ; the texture. Find the address of the upper-left pixel:
  988. mov edx, eax
  989. shl edx, 2
  990. mov esi, ebx
  991. imul esi, ecx
  992. add esi, edx
  993. add esi, scan0 ; esi = upper left pixel
  994. ; Our pixel nomenclature for the 2x2 square is as follows:
  995. ;
  996. ; A | B
  997. ; ---+---
  998. ; C | D
  999. cmp ebx, 0 ; if (y < 0), we can't look at
  1000. jl Handle_CD ; row y
  1001. cmp eax, 0 ; if (x < 0), we can't look at
  1002. jl Done_A ; column x
  1003. movd mm4, [esi] ; read pixel (x, y)
  1004. Done_A:
  1005. cmp eax, widthMinus1 ; if (x >= width - 1), we can't
  1006. jge Handle_CD ; look at column x
  1007. movd mm5, [esi+4] ; read pixel (x+1, y)
  1008. Handle_CD:
  1009. cmp ebx, heightMinus1 ; if (y >= height - 1), we can't
  1010. jge FinishClamp ; look at row y
  1011. cmp eax, 0 ; if (x < 0), we can't look at
  1012. jl Done_C ; column x
  1013. movd mm6, [esi+ecx] ; read pixel (x, y+1)
  1014. Done_C:
  1015. cmp eax, widthMinus1 ; if (x >= width - 1), we can't
  1016. jge FinishClamp ; look at column x
  1017. movd mm7, [esi+ecx+4] ; read pixel (x+1, y+1)
  1018. FinishClamp:
  1019. pop ebx
  1020. pop eax
  1021. jmp ContinueLoop
  1022. AllDone:
  1023. emms
  1024. }
  1025. #endif
  1026. return Ok;
  1027. }
  1028. /**************************************************************************\
  1029. *
  1030. * Function Description:
  1031. *
  1032. * Output routine for handling texture brushes with indentity transforms
  1033. * and either 'Tile' or 'Clamp' wrap modes.
  1034. *
  1035. * Created:
  1036. *
  1037. * 03/14/2000 andrewgo
  1038. *
  1039. \**************************************************************************/
  1040. GpStatus
  1041. DpOutputBilinearSpan_Identity::OutputSpan(
  1042. INT y,
  1043. INT xMin,
  1044. INT xMax // xMax is exclusive
  1045. )
  1046. {
  1047. ASSERT(xMax > xMin);
  1048. INT count = xMax - xMin;
  1049. ARGB *buffer = Scan->NextBuffer(xMin, y, count);
  1050. INT u = xMin + Dx;
  1051. INT v = y + Dy;
  1052. INT width = BmpData.Width;
  1053. INT height = BmpData.Height;
  1054. INT i;
  1055. if (BilinearWrapMode == WrapModeTile)
  1056. {
  1057. if (PowerOfTwo)
  1058. {
  1059. u &= (width - 1);
  1060. v &= (height - 1);
  1061. }
  1062. else
  1063. {
  1064. // Single unsigned compare handles (u < 0) and (u >= width)
  1065. if (static_cast<unsigned>(u) >= static_cast<unsigned>(width))
  1066. {
  1067. u = RemainderI(u, width);
  1068. }
  1069. // Single unsigned compare handles (v < 0) and (v >= width)
  1070. if (static_cast<unsigned>(v) >= static_cast<unsigned>(height))
  1071. {
  1072. v = RemainderI(v, height);
  1073. }
  1074. }
  1075. ARGB *row = reinterpret_cast<ARGB*>
  1076. (static_cast<BYTE*>(BmpData.Scan0) + (v * BmpData.Stride));
  1077. ARGB *src;
  1078. src = row + u;
  1079. i = min(width - u, count);
  1080. count -= i;
  1081. // We don't call GpMemcpy here because by doing the copy explicitly,
  1082. // the compiler still converts to a 'rep movsd', but it doesn't have
  1083. // to add to the destination 'buffer' pointer when done:
  1084. do {
  1085. *buffer++ = *src++;
  1086. } while (--i != 0);
  1087. while (count > 0)
  1088. {
  1089. src = row;
  1090. i = min(width, count);
  1091. count -= i;
  1092. do {
  1093. *buffer++ = *src++;
  1094. } while (--i != 0);
  1095. }
  1096. }
  1097. else
  1098. {
  1099. ASSERT(BilinearWrapMode == WrapModeClamp);
  1100. ARGB borderColor = ClampColor;
  1101. // Check for trivial rejection. Unsigned compare handles
  1102. // (v < 0) and (v >= height).
  1103. if ((static_cast<unsigned>(v) >= static_cast<unsigned>(height)) ||
  1104. (u >= width) ||
  1105. (u + count <= 0))
  1106. {
  1107. // The whole scan should be the border color:
  1108. i = count;
  1109. do {
  1110. *buffer++ = borderColor;
  1111. } while (--i != 0);
  1112. }
  1113. else
  1114. {
  1115. ARGB *src = reinterpret_cast<ARGB*>
  1116. (static_cast<BYTE*>(BmpData.Scan0) + (v * BmpData.Stride));
  1117. if (u < 0)
  1118. {
  1119. i = -u;
  1120. count -= i;
  1121. do {
  1122. *buffer++ = borderColor;
  1123. } while (--i != 0);
  1124. }
  1125. else
  1126. {
  1127. src += u;
  1128. width -= u;
  1129. }
  1130. i = min(count, width);
  1131. ASSERT(i > 0); // Trivial rejection ensures this
  1132. count -= i;
  1133. /*
  1134. The compiler was generating particularly stupid code
  1135. for this loop.
  1136. do {
  1137. *buffer++ = *src++;
  1138. } while (--i != 0);
  1139. */
  1140. GpMemcpy(buffer, src, i*sizeof(ARGB));
  1141. buffer += i;
  1142. while (count-- > 0)
  1143. {
  1144. *buffer++ = borderColor;
  1145. }
  1146. }
  1147. }
  1148. return(Ok);
  1149. }
  1150. /**************************************************************************\
  1151. *
  1152. * Function Description:
  1153. *
  1154. * Hatch brush constructor.
  1155. *
  1156. * Arguments:
  1157. *
  1158. * Created:
  1159. *
  1160. * 04/15/1999 ikkof
  1161. *
  1162. \**************************************************************************/
  1163. DpOutputHatchSpan::DpOutputHatchSpan(
  1164. const GpHatch *hatchBrush,
  1165. DpScanBuffer * scan,
  1166. DpContext* context
  1167. )
  1168. {
  1169. Scan = scan;
  1170. ForeARGB = hatchBrush->DeviceBrush.Colors[0].GetPremultipliedValue();
  1171. BackARGB = hatchBrush->DeviceBrush.Colors[1].GetPremultipliedValue();
  1172. // Store the context rendering origin for the brush origin.
  1173. m_BrushOriginX = context->RenderingOriginX;
  1174. m_BrushOriginY = context->RenderingOriginY;
  1175. INT a[3], r[3], g[3], b[3];
  1176. a[0] = (BYTE) GpColor::GetAlphaARGB(ForeARGB);
  1177. r[0] = (BYTE) GpColor::GetRedARGB(ForeARGB);
  1178. g[0] = (BYTE) GpColor::GetGreenARGB(ForeARGB);
  1179. b[0] = (BYTE) GpColor::GetBlueARGB(ForeARGB);
  1180. a[1] = (BYTE) GpColor::GetAlphaARGB(BackARGB);
  1181. r[1] = (BYTE) GpColor::GetRedARGB(BackARGB);
  1182. g[1] = (BYTE) GpColor::GetGreenARGB(BackARGB);
  1183. b[1] = (BYTE) GpColor::GetBlueARGB(BackARGB);
  1184. a[2] = (a[0] + 3*a[1]) >> 2;
  1185. r[2] = (r[0] + 3*r[1]) >> 2;
  1186. g[2] = (g[0] + 3*g[1]) >> 2;
  1187. b[2] = (b[0] + 3*b[1]) >> 2;
  1188. AverageARGB = GpColor::MakeARGB((BYTE) a[2], (BYTE) r[2],
  1189. (BYTE) g[2], (BYTE) b[2]);
  1190. // Antialiase diagonal hatches.
  1191. if(hatchBrush->DeviceBrush.Style == HatchStyleForwardDiagonal ||
  1192. hatchBrush->DeviceBrush.Style == HatchStyleBackwardDiagonal ||
  1193. hatchBrush->DeviceBrush.Style == HatchStyleDiagonalCross)
  1194. {
  1195. REAL temp;
  1196. // occupied = (2*sqrt(2) - 1)/2
  1197. REAL occupied = TOREAL(0.914213562);
  1198. if(a[0] != 255 || a[1] != 255)
  1199. {
  1200. temp = TOREAL(occupied*(a[0] - a[1]) + a[1]);
  1201. a[0] = (BYTE) temp;
  1202. }
  1203. temp = TOREAL(occupied*(r[0] - r[1]) + r[1]);
  1204. if(temp > 255)
  1205. r[0] = 255;
  1206. else
  1207. r[0] = (BYTE) temp;
  1208. temp = TOREAL(occupied*(g[0] - g[1]) + g[1]);
  1209. if(temp > 255)
  1210. g[0] = 255;
  1211. else
  1212. g[0] = (BYTE) temp;
  1213. temp = TOREAL(occupied*(b[0] - b[1]) + b[1]);
  1214. if(temp > 255)
  1215. b[0] = 255;
  1216. else
  1217. b[0] = (BYTE) temp;
  1218. ForeARGB = GpColor::MakeARGB((BYTE) a[0], (BYTE) r[0],
  1219. (BYTE) g[0], (BYTE) b[0]);
  1220. }
  1221. for(INT i = 0; i < 8; i++)
  1222. {
  1223. for(INT j= 0; j < 8; j++)
  1224. {
  1225. Data[i][j] = hatchBrush->DeviceBrush.Data[i][j];
  1226. }
  1227. }
  1228. }
  1229. /**************************************************************************\
  1230. *
  1231. * Function Description:
  1232. *
  1233. * Outputs a single span within a raster with a hatch brush
  1234. * Is called by the rasterizer.
  1235. *
  1236. * Arguments:
  1237. *
  1238. * [IN] y - the Y value of the raster being output
  1239. * [IN] leftEdge - the DDA class of the left edge
  1240. * [IN] rightEdge - the DDA class of the right edge
  1241. *
  1242. * Return Value:
  1243. *
  1244. * GpStatus - Ok
  1245. *
  1246. * Created:
  1247. *
  1248. * 04/15/1999 ikkof
  1249. *
  1250. \**************************************************************************/
  1251. GpStatus
  1252. DpOutputHatchSpan::OutputSpan(
  1253. INT y,
  1254. INT xMin,
  1255. INT xMax // xMax is exclusive
  1256. )
  1257. {
  1258. ARGB argb;
  1259. INT width = xMax - xMin;
  1260. ARGB* buffer = Scan->NextBuffer(xMin, y, width);
  1261. INT yMod = (y - m_BrushOriginY) & 0x7;
  1262. for(INT x = xMin; x < xMax; x++)
  1263. {
  1264. INT xMod = (x - m_BrushOriginX) & 0x7;
  1265. INT value = Data[yMod][xMod];
  1266. if(value == 255)
  1267. *buffer++ = ForeARGB;
  1268. else if(value == 0)
  1269. *buffer++ = BackARGB;
  1270. else
  1271. *buffer++ = AverageARGB; // for antialising.
  1272. }
  1273. return Ok;
  1274. }
  1275. /**************************************************************************\
  1276. *
  1277. * Function Description:
  1278. *
  1279. * Outputs a single span within a raster with a hatch brush
  1280. * Is called by the rasterizer. This is a special version which stretches
  1281. * up the size of the hatch span to device resolution.
  1282. *
  1283. * Arguments:
  1284. *
  1285. * [IN] y - the Y value of the raster being output
  1286. * [IN] leftEdge - the DDA class of the left edge
  1287. * [IN] rightEdge - the DDA class of the right edge
  1288. *
  1289. * Return Value:
  1290. *
  1291. * GpStatus - Ok
  1292. *
  1293. * Created:
  1294. *
  1295. * 2/20/1 - ericvan
  1296. *
  1297. \**************************************************************************/
  1298. GpStatus
  1299. DpOutputStretchedHatchSpan::OutputSpan(
  1300. INT y,
  1301. INT xMin,
  1302. INT xMax // xMax is exclusive
  1303. )
  1304. {
  1305. ARGB argb;
  1306. INT width = xMax - xMin;
  1307. ARGB* buffer = Scan->NextBuffer(xMin, y, width);
  1308. INT yMod = (y - m_BrushOriginY) % (8*ScaleFactor);
  1309. for(INT x = xMin; x < xMax; x++)
  1310. {
  1311. INT xMod = (x - m_BrushOriginX) % (8*ScaleFactor);
  1312. INT value = Data[yMod/ScaleFactor][xMod/ScaleFactor];
  1313. if(value == 255)
  1314. *buffer++ = ForeARGB;
  1315. else if(value == 0)
  1316. *buffer++ = BackARGB;
  1317. else
  1318. *buffer++ = AverageARGB; // for antialising.
  1319. }
  1320. return Ok;
  1321. }