1 // Copyright 2014 PDFium Authors. All rights reserved.
\r
2 // Use of this source code is governed by a BSD-style license that can be
\r
3 // found in the LICENSE file.
\r
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
\r
7 #include "../../../foxitlib.h"
\r
8 #include "fde_gedevice.h"
\r
9 #include "fde_geobject.h"
\r
10 #include "fde_devbasic.h"
\r
15 FX_BOOL FDE_GetStockHatchMask(FX_INT32 iHatchStyle, CFX_DIBitmap & hatchMask)
\r
17 FDE_LPCHATCHDATA pData = FDE_DEVGetHatchData(iHatchStyle);
\r
21 hatchMask.Create(pData->iWidth, pData->iHeight, FXDIB_1bppMask);
\r
22 FXSYS_memcpy(hatchMask.GetBuffer(), pData->MaskBits, hatchMask.GetPitch() * pData->iHeight);
\r
28 IFDE_RenderDevice* IFDE_RenderDevice::Create(CFX_DIBitmap *pBitmap, FX_BOOL bRgbByteOrder )
\r
30 if (pBitmap == NULL) {
\r
33 CFX_FxgeDevice *pDevice = FX_NEW CFX_FxgeDevice;
\r
34 if (pDevice == NULL) {
\r
37 pDevice->Attach(pBitmap, 0, bRgbByteOrder);
\r
38 return FDE_New CFDE_FxgeDevice(pDevice, TRUE);
\r
40 IFDE_RenderDevice* IFDE_RenderDevice::Create(CFX_RenderDevice *pDevice)
\r
42 if (pDevice == NULL) {
\r
45 return FDE_New CFDE_FxgeDevice(pDevice, FALSE);
\r
47 CFDE_FxgeDevice::CFDE_FxgeDevice(CFX_RenderDevice *pDevice, FX_BOOL bOwnerDevice)
\r
48 : m_pDevice(pDevice)
\r
49 , m_bOwnerDevice(bOwnerDevice)
\r
53 FXSYS_assert(pDevice != NULL);
\r
54 FX_RECT rt = m_pDevice->GetClipBox();
\r
55 m_rtClip.Set((FX_FLOAT)rt.left, (FX_FLOAT)rt.top, (FX_FLOAT)rt.Width(), (FX_FLOAT)rt.Height());
\r
57 CFDE_FxgeDevice::~CFDE_FxgeDevice()
\r
59 if (m_pCharPos != NULL) {
\r
60 FDE_Free(m_pCharPos);
\r
62 if (m_bOwnerDevice && m_pDevice) {
\r
66 FX_INT32 CFDE_FxgeDevice::GetWidth() const
\r
68 return m_pDevice->GetWidth();
\r
70 FX_INT32 CFDE_FxgeDevice::GetHeight() const
\r
72 return m_pDevice->GetHeight();
\r
74 FDE_HDEVICESTATE CFDE_FxgeDevice::SaveState()
\r
76 m_pDevice->SaveState();
\r
79 void CFDE_FxgeDevice::RestoreState(FDE_HDEVICESTATE hState)
\r
81 m_pDevice->RestoreState();
\r
82 const FX_RECT &rt = m_pDevice->GetClipBox();
\r
83 m_rtClip.Set((FX_FLOAT)rt.left, (FX_FLOAT)rt.top, (FX_FLOAT)rt.Width(), (FX_FLOAT)rt.Height());
\r
85 FX_BOOL CFDE_FxgeDevice::SetClipRect(const CFX_RectF &rtClip)
\r
88 FX_RECT rt((FX_INT32)FXSYS_floor(rtClip.left), (FX_INT32)FXSYS_floor(rtClip.top),
\r
89 (FX_INT32)FXSYS_ceil(rtClip.right()), (FX_INT32)FXSYS_ceil(rtClip.bottom()));
\r
90 return m_pDevice->SetClip_Rect(&rt);
\r
92 const CFX_RectF& CFDE_FxgeDevice::GetClipRect()
\r
96 FX_BOOL CFDE_FxgeDevice::SetClipPath(const IFDE_Path *pClip)
\r
100 IFDE_Path* CFDE_FxgeDevice::GetClipPath() const
\r
104 FX_FLOAT CFDE_FxgeDevice::GetDpiX() const
\r
108 FX_FLOAT CFDE_FxgeDevice::GetDpiY() const
\r
112 FX_BOOL CFDE_FxgeDevice::DrawImage(CFX_DIBSource *pDib, const CFX_RectF *pSrcRect, const CFX_RectF &dstRect, const CFX_Matrix *pImgMatrix, const CFX_Matrix *pDevMatrix)
\r
114 FXSYS_assert(pDib != NULL);
\r
117 srcRect = *pSrcRect;
\r
119 srcRect.Set(0, 0, (FX_FLOAT)pDib->GetWidth(), (FX_FLOAT)pDib->GetHeight());
\r
121 if (srcRect.IsEmpty()) {
\r
124 CFX_Matrix dib2fxdev;
\r
126 dib2fxdev = *pImgMatrix;
\r
130 dib2fxdev.a = dstRect.width;
\r
131 dib2fxdev.d = -dstRect.height;
\r
132 dib2fxdev.e = dstRect.left;
\r
133 dib2fxdev.f = dstRect.bottom();
\r
135 dib2fxdev.Concat(*pDevMatrix);
\r
137 FX_LPVOID handle = NULL;
\r
138 m_pDevice->StartDIBits(pDib, 255, 0, (const CFX_AffineMatrix*)&dib2fxdev, 0, handle);
\r
139 while (m_pDevice->ContinueDIBits(handle, NULL)) { }
\r
140 m_pDevice->CancelDIBits(handle);
\r
141 return handle != NULL;
\r
143 FX_BOOL CFDE_FxgeDevice::DrawString(IFDE_Brush *pBrush, IFX_Font *pFont, const FXTEXT_CHARPOS *pCharPos, FX_INT32 iCount, FX_FLOAT fFontSize, const CFX_Matrix *pMatrix)
\r
145 FXSYS_assert(pBrush != NULL && pFont != NULL && pCharPos != NULL && iCount > 0);
\r
146 CFX_FontCache *pCache = CFX_GEModule::Get()->GetFontCache();
\r
147 CFX_Font *pFxFont = (CFX_Font*)pFont->GetDevFont();
\r
148 switch (pBrush->GetType()) {
\r
149 case FDE_BRUSHTYPE_Solid: {
\r
150 FX_ARGB argb = ((IFDE_SolidBrush*)pBrush)->GetColor();
\r
151 if ((pFont->GetFontStyles() & FX_FONTSTYLE_Italic) != 0 && !pFxFont->IsItalic()) {
\r
152 FXTEXT_CHARPOS *pCP = (FXTEXT_CHARPOS*)pCharPos;
\r
154 for (FX_INT32 i = 0; i < iCount; ++i) {
\r
155 static const FX_FLOAT mc = 0.267949f;
\r
156 pAM = pCP->m_AdjustMatrix;
\r
157 pAM[2] = mc * pAM[0] + pAM[2];
\r
158 pAM[3] = mc * pAM[1] + pAM[3];
\r
162 FXTEXT_CHARPOS *pCP = (FXTEXT_CHARPOS*)pCharPos;
\r
163 IFX_Font *pCurFont = NULL;
\r
164 IFX_Font *pSTFont = NULL;
\r
165 FXTEXT_CHARPOS *pCurCP = NULL;
\r
166 FX_INT32 iCurCount = 0;
\r
167 #if _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
\r
168 FX_DWORD dwFontStyle = pFont->GetFontStyles();
\r
170 CFX_SubstFont SubstFxFont;
\r
171 FxFont.m_pSubstFont = &SubstFxFont;
\r
172 SubstFxFont.m_Weight = dwFontStyle & FX_FONTSTYLE_Bold ? 700 : 400;
\r
173 SubstFxFont.m_WeightCJK = SubstFxFont.m_Weight;
\r
174 SubstFxFont.m_ItalicAngle = dwFontStyle & FX_FONTSTYLE_Italic ? -12 : 0;
\r
175 SubstFxFont.m_bItlicCJK = dwFontStyle & FX_FONTSTYLE_Italic ? TRUE : FALSE;
\r
177 for (FX_INT32 i = 0; i < iCount; ++i) {
\r
178 pSTFont = pFont->GetSubstFont((FX_INT32)pCP->m_GlyphIndex);
\r
179 pCP->m_GlyphIndex &= 0x00FFFFFF;
\r
180 pCP->m_bFontStyle = FALSE;
\r
181 if (pCurFont != pSTFont) {
\r
182 if (pCurFont != NULL) {
\r
183 pFxFont = (CFX_Font*)pCurFont->GetDevFont();
\r
184 #if _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
\r
185 FxFont.m_Face = pFxFont->m_Face;
\r
186 m_pDevice->DrawNormalText(iCurCount, pCurCP, &FxFont, pCache, -fFontSize, (const CFX_AffineMatrix*)pMatrix, argb, FXTEXT_CLEARTYPE);
\r
188 m_pDevice->DrawNormalText(iCurCount, pCurCP, pFxFont, pCache, -fFontSize, (const CFX_AffineMatrix*)pMatrix, argb, FXTEXT_CLEARTYPE);
\r
191 pCurFont = pSTFont;
\r
199 if (pCurFont != NULL && iCurCount) {
\r
200 pFxFont = (CFX_Font*)pCurFont->GetDevFont();
\r
201 #if _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
\r
202 FxFont.m_Face = pFxFont->m_Face;
\r
203 FX_BOOL bRet = m_pDevice->DrawNormalText(iCurCount, pCurCP, &FxFont, pCache, -fFontSize, (const CFX_AffineMatrix*)pMatrix, argb, FXTEXT_CLEARTYPE);
\r
204 FxFont.m_pSubstFont = NULL;
\r
205 FxFont.m_Face = NULL;
\r
208 return m_pDevice->DrawNormalText(iCurCount, pCurCP, pFxFont, pCache, -fFontSize, (const CFX_AffineMatrix*)pMatrix, argb, FXTEXT_CLEARTYPE);
\r
211 #if _FXM_PLATFORM_ != _FXM_PLATFORM_WINDOWS_
\r
212 FxFont.m_pSubstFont = NULL;
\r
213 FxFont.m_Face = NULL;
\r
222 FX_BOOL CFDE_FxgeDevice::DrawBezier(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const CFX_PointF &pt1, const CFX_PointF &pt2, const CFX_PointF &pt3, const CFX_PointF &pt4, const CFX_Matrix *pMatrix )
\r
224 CFX_PointsF points;
\r
230 path.AddBezier(points);
\r
231 return DrawPath(pPen, fPenWidth, &path, pMatrix);
\r
233 FX_BOOL CFDE_FxgeDevice::DrawCurve(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const CFX_PointsF &points, FX_BOOL bClosed, FX_FLOAT fTension , const CFX_Matrix *pMatrix )
\r
236 path.AddCurve(points, bClosed, fTension);
\r
237 return DrawPath(pPen, fPenWidth, &path, pMatrix);
\r
239 FX_BOOL CFDE_FxgeDevice::DrawEllipse(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const CFX_RectF &rect, const CFX_Matrix *pMatrix )
\r
242 path.AddEllipse(rect);
\r
243 return DrawPath(pPen, fPenWidth, &path, pMatrix);
\r
245 FX_BOOL CFDE_FxgeDevice::DrawLines(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const CFX_PointsF &points, const CFX_Matrix *pMatrix )
\r
248 path.AddLines(points);
\r
249 return DrawPath(pPen, fPenWidth, &path, pMatrix);
\r
251 FX_BOOL CFDE_FxgeDevice::DrawLine(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const CFX_PointF &pt1, const CFX_PointF &pt2, const CFX_Matrix *pMatrix )
\r
254 path.AddLine(pt1, pt2);
\r
255 return DrawPath(pPen, fPenWidth, &path, pMatrix);
\r
257 FX_BOOL CFDE_FxgeDevice::DrawPath(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const IFDE_Path *pPath, const CFX_Matrix *pMatrix )
\r
259 CFDE_Path *pGePath = (CFDE_Path*)pPath;
\r
260 if (pGePath == NULL) {
\r
263 CFX_GraphStateData graphState;
\r
264 if (!CreatePen(pPen, fPenWidth, graphState)) {
\r
267 return m_pDevice->DrawPath(&pGePath->m_Path, (const CFX_AffineMatrix*)pMatrix, &graphState, 0, pPen->GetColor(), 0);
\r
269 FX_BOOL CFDE_FxgeDevice::DrawPolygon(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const CFX_PointsF &points, const CFX_Matrix *pMatrix )
\r
272 path.AddPolygon(points);
\r
273 return DrawPath(pPen, fPenWidth, &path, pMatrix);
\r
275 FX_BOOL CFDE_FxgeDevice::DrawRectangle(IFDE_Pen *pPen, FX_FLOAT fPenWidth, const CFX_RectF &rect, const CFX_Matrix *pMatrix )
\r
278 path.AddRectangle(rect);
\r
279 return DrawPath(pPen, fPenWidth, &path, pMatrix);
\r
281 FX_BOOL CFDE_FxgeDevice::FillClosedCurve(IFDE_Brush *pBrush, const CFX_PointsF &points, FX_FLOAT fTension , const CFX_Matrix *pMatrix )
\r
284 path.AddCurve(points, TRUE, fTension);
\r
285 return FillPath(pBrush, &path, pMatrix);
\r
287 FX_BOOL CFDE_FxgeDevice::FillEllipse(IFDE_Brush* pBrush, const CFX_RectF& rect, const CFX_Matrix *pMatrix )
\r
290 path.AddEllipse(rect);
\r
291 return FillPath(pBrush, &path, pMatrix);
\r
293 FX_BOOL CFDE_FxgeDevice::FillPolygon(IFDE_Brush *pBrush, const CFX_PointsF &points, const CFX_Matrix *pMatrix )
\r
296 path.AddPolygon(points);
\r
297 return FillPath(pBrush, &path, pMatrix);
\r
299 FX_BOOL CFDE_FxgeDevice::FillRectangle(IFDE_Brush *pBrush, const CFX_RectF &rect, const CFX_Matrix *pMatrix )
\r
302 path.AddRectangle(rect);
\r
303 return FillPath(pBrush, &path, pMatrix);
\r
305 FX_BOOL CFDE_FxgeDevice::CreatePen(IFDE_Pen *pPen, FX_FLOAT fPenWidth, CFX_GraphStateData &graphState)
\r
307 if (pPen == NULL) {
\r
310 graphState.m_LineCap = (CFX_GraphStateData::LineCap)pPen->GetLineCap();
\r
311 graphState.m_LineJoin = (CFX_GraphStateData::LineJoin)pPen->GetLineJoin();
\r
312 graphState.m_LineWidth = fPenWidth;
\r
313 graphState.m_MiterLimit = pPen->GetMiterLimit();
\r
314 graphState.m_DashPhase = pPen->GetDashPhase();
\r
315 CFX_FloatArray dashArray;
\r
316 switch (pPen->GetDashStyle()) {
\r
317 case FDE_DASHSTYLE_Dash:
\r
321 case FDE_DASHSTYLE_Dot:
\r
325 case FDE_DASHSTYLE_DashDot:
\r
331 case FDE_DASHSTYLE_DashDotDot:
\r
339 case FDE_DASHSTYLE_Customized:
\r
340 pPen->GetDashArray(dashArray);
\r
343 FX_INT32 iDashCount = dashArray.GetSize();
\r
344 if (iDashCount > 0) {
\r
345 graphState.SetDashCount(iDashCount);
\r
346 for (FX_INT32 i = 0; i < iDashCount; ++i) {
\r
347 graphState.m_DashArray[i] = dashArray[i] * fPenWidth;
\r
352 typedef FX_BOOL (CFDE_FxgeDevice::*pfFillPath)(IFDE_Brush *pBrush, const CFX_PathData *pPath, const CFX_Matrix *pMatrix);
\r
353 static const pfFillPath gs_FillPath[] = {
\r
354 &CFDE_FxgeDevice::FillSolidPath,
\r
355 &CFDE_FxgeDevice::FillHatchPath,
\r
356 &CFDE_FxgeDevice::FillTexturePath,
\r
357 &CFDE_FxgeDevice::FillLinearGradientPath,
\r
359 FX_BOOL CFDE_FxgeDevice::FillPath(IFDE_Brush *pBrush, const IFDE_Path *pPath, const CFX_Matrix *pMatrix)
\r
361 CFDE_Path *pGePath = (CFDE_Path*)pPath;
\r
362 if (pGePath == NULL) {
\r
365 if (pBrush == NULL) {
\r
368 FX_INT32 iType = pBrush->GetType();
\r
369 if (iType < 0 || iType > FDE_BRUSHTYPE_MAX) {
\r
372 return (this->*gs_FillPath[iType])(pBrush, &pGePath->m_Path, pMatrix);
\r
374 FX_BOOL CFDE_FxgeDevice::FillSolidPath(IFDE_Brush *pBrush, const CFX_PathData *pPath, const CFX_Matrix *pMatrix)
\r
376 FXSYS_assert(pPath && pBrush && pBrush->GetType() == FDE_BRUSHTYPE_Solid);
\r
377 IFDE_SolidBrush *pSolidBrush = (IFDE_SolidBrush*)pBrush;
\r
378 return m_pDevice->DrawPath(pPath, (const CFX_AffineMatrix*)pMatrix, NULL, pSolidBrush->GetColor(), 0, FXFILL_WINDING);
\r
380 FX_BOOL CFDE_FxgeDevice::FillHatchPath(IFDE_Brush *pBrush, const CFX_PathData *pPath, const CFX_Matrix *pMatrix)
\r
382 FXSYS_assert(pPath && pBrush && pBrush->GetType() == FDE_BRUSHTYPE_Hatch);
\r
383 IFDE_HatchBrush *pHatchBrush = (IFDE_HatchBrush*)pBrush;
\r
384 FX_INT32 iStyle = pHatchBrush->GetHatchStyle();
\r
385 if (iStyle < FDE_HATCHSTYLE_Min || iStyle > FDE_HATCHSTYLE_Max) {
\r
389 if (!FDE_GetStockHatchMask(iStyle, mask)) {
\r
392 FX_ARGB dwForeColor = pHatchBrush->GetColor(TRUE);
\r
393 FX_ARGB dwBackColor = pHatchBrush->GetColor(FALSE);
\r
394 CFX_FloatRect rectf = pPath->GetBoundingBox();
\r
396 rectf.Transform((const CFX_AffineMatrix*)pMatrix);
\r
398 FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
\r
399 FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
\r
400 m_pDevice->SaveState();
\r
401 m_pDevice->StartRendering();
\r
402 m_pDevice->SetClip_PathFill(pPath, (const CFX_AffineMatrix*)pMatrix, FXFILL_WINDING);
\r
403 m_pDevice->FillRect(&rect, dwBackColor);
\r
404 for (FX_INT32 j = rect.bottom; j < rect.top; j += mask.GetHeight())
\r
405 for (FX_INT32 i = rect.left; i < rect.right; i += mask.GetWidth()) {
\r
406 m_pDevice->SetBitMask(&mask, i, j, dwForeColor);
\r
408 m_pDevice->EndRendering();
\r
409 m_pDevice->RestoreState();
\r
412 FX_BOOL CFDE_FxgeDevice::FillTexturePath(IFDE_Brush *pBrush, const CFX_PathData *pPath, const CFX_Matrix *pMatrix)
\r
414 FXSYS_assert(pPath && pBrush && pBrush->GetType() == FDE_BRUSHTYPE_Texture);
\r
415 IFDE_TextureBrush *pTextureBrush = (IFDE_TextureBrush*)pBrush;
\r
416 IFDE_Image *pImage = (IFDE_Image*)pTextureBrush->GetImage();
\r
417 if (pImage == NULL) {
\r
421 size.Set(pImage->GetImageWidth(), pImage->GetImageHeight());
\r
423 bmp.Create(size.x, size.y, FXDIB_Argb);
\r
424 if (!pImage->StartLoadImage(&bmp, 0, 0, size.x, size.y, 0, 0, size.x, size.y)) {
\r
427 if (pImage->DoLoadImage() < 100) {
\r
430 pImage->StopLoadImage();
\r
431 return WrapTexture(pTextureBrush->GetWrapMode(), &bmp, pPath, pMatrix);
\r
433 FX_BOOL CFDE_FxgeDevice::WrapTexture(FX_INT32 iWrapMode, const CFX_DIBitmap *pBitmap, const CFX_PathData *pPath, const CFX_Matrix *pMatrix)
\r
435 CFX_FloatRect rectf = pPath->GetBoundingBox();
\r
437 rectf.Transform((const CFX_AffineMatrix*)pMatrix);
\r
439 FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
\r
440 FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
\r
442 if (rect.IsEmpty()) {
\r
445 m_pDevice->SaveState();
\r
446 m_pDevice->StartRendering();
\r
447 m_pDevice->SetClip_PathFill(pPath, (const CFX_AffineMatrix*)pMatrix, FXFILL_WINDING);
\r
448 switch (iWrapMode) {
\r
449 case FDE_WRAPMODE_Tile:
\r
450 case FDE_WRAPMODE_TileFlipX:
\r
451 case FDE_WRAPMODE_TileFlipY:
\r
452 case FDE_WRAPMODE_TileFlipXY: {
\r
453 FX_BOOL bFlipX = iWrapMode == FDE_WRAPMODE_TileFlipXY || iWrapMode == FDE_WRAPMODE_TileFlipX;
\r
454 FX_BOOL bFlipY = iWrapMode == FDE_WRAPMODE_TileFlipXY || iWrapMode == FDE_WRAPMODE_TileFlipY;
\r
455 const CFX_DIBitmap *pFlip[2][2];
\r
456 pFlip[0][0] = pBitmap;
\r
457 pFlip[0][1] = bFlipX ? pBitmap->FlipImage(TRUE, FALSE) : pBitmap;
\r
458 pFlip[1][0] = bFlipY ? pBitmap->FlipImage(FALSE, TRUE) : pBitmap;
\r
459 pFlip[1][1] = (bFlipX || bFlipY) ? pBitmap->FlipImage(bFlipX, bFlipY) : pBitmap;
\r
460 FX_INT32 iCounterY = 0;
\r
461 for (FX_INT32 j = rect.top; j < rect.bottom; j += pBitmap->GetHeight()) {
\r
462 FX_INT32 indexY = iCounterY++ % 2;
\r
463 FX_INT32 iCounterX = 0;
\r
464 for (FX_INT32 i = rect.left; i < rect.right; i += pBitmap->GetWidth()) {
\r
465 FX_INT32 indexX = iCounterX++ % 2;
\r
466 m_pDevice->SetDIBits(pFlip[indexY][indexX], i, j);
\r
469 if (pFlip[0][1] != pFlip[0][0]) {
\r
470 delete pFlip[0][1];
\r
472 if (pFlip[1][0] != pFlip[0][0]) {
\r
473 delete pFlip[1][0];
\r
475 if (pFlip[1][1] != pFlip[0][0]) {
\r
476 delete pFlip[1][1];
\r
480 case FDE_WRAPMODE_Clamp: {
\r
481 m_pDevice->SetDIBits(pBitmap, rect.left, rect.bottom);
\r
485 m_pDevice->EndRendering();
\r
486 m_pDevice->RestoreState();
\r
489 FX_BOOL CFDE_FxgeDevice::FillLinearGradientPath(IFDE_Brush *pBrush, const CFX_PathData *pPath, const CFX_Matrix *pMatrix)
\r
491 FXSYS_assert(pPath && pBrush && pBrush->GetType() == FDE_BRUSHTYPE_LinearGradient);
\r
492 IFDE_LinearGradientBrush *pLinearBrush = (IFDE_LinearGradientBrush*)pBrush;
\r
493 CFX_PointF pt0, pt1;
\r
494 pLinearBrush->GetLinearPoints(pt0, pt1);
\r
495 CFX_VectorF fDiagonal;
\r
496 fDiagonal.Set(pt0, pt1);
\r
497 FX_FLOAT fTheta = FXSYS_atan2(fDiagonal.y, fDiagonal.x);
\r
498 FX_FLOAT fLength = fDiagonal.Length();
\r
499 FX_FLOAT fTotalX = fLength / FXSYS_cos(fTheta);
\r
500 FX_FLOAT fTotalY = fLength / FXSYS_cos(FX_PI / 2 - fTheta);
\r
501 FX_FLOAT fSteps = FX_MAX(fTotalX, fTotalY);
\r
502 FX_FLOAT dx = fTotalX / fSteps;
\r
503 FX_FLOAT dy = fTotalY / fSteps;
\r
505 pLinearBrush->GetLinearColors(cr0, cr1);
\r
506 FX_FLOAT a0 = FXARGB_A(cr0);
\r
507 FX_FLOAT r0 = FXARGB_R(cr0);
\r
508 FX_FLOAT g0 = FXARGB_G(cr0);
\r
509 FX_FLOAT b0 = FXARGB_B(cr0);
\r
510 FX_FLOAT da = (FXARGB_A(cr1) - a0) / fSteps;
\r
511 FX_FLOAT dr = (FXARGB_R(cr1) - r0) / fSteps;
\r
512 FX_FLOAT dg = (FXARGB_G(cr1) - g0) / fSteps;
\r
513 FX_FLOAT db = (FXARGB_B(cr1) - b0) / fSteps;
\r
515 bmp.Create(FXSYS_round(FXSYS_fabs(fDiagonal.x)), FXSYS_round(FXSYS_fabs(fDiagonal.y)), FXDIB_Argb);
\r
516 CFX_FxgeDevice dev;
\r
519 FX_INT32 iSteps = FXSYS_round(FXSYS_ceil(fSteps));
\r
520 while (--iSteps >= 0) {
\r
521 cr0 = ArgbEncode(FXSYS_round(a0), FXSYS_round(r0), FXSYS_round(g0), FXSYS_round(b0));
\r
522 dev.DrawCosmeticLine(pt0.x, pt0.y, pt1.x, pt1.y, cr0);
\r
530 return WrapTexture(pLinearBrush->GetWrapMode(), &bmp, pPath, pMatrix);
\r