Home | History | Annotate | Download | only in src
      1 // Copyright 2014 PDFium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
      6 
      7 #include <memory>
      8 
      9 #include "fx_path_generator.h"
     10 #include "pre.h"
     11 
     12 class CAGG_Graphics {
     13  public:
     14   CAGG_Graphics();
     15   FX_ERR Create(CFX_Graphics* owner,
     16                 int32_t width,
     17                 int32_t height,
     18                 FXDIB_Format format);
     19   virtual ~CAGG_Graphics();
     20 
     21  private:
     22   CFX_Graphics* _owner;
     23 };
     24 CFX_Graphics::CFX_Graphics() {
     25   _type = FX_CONTEXT_None;
     26   _info._graphState.SetDashCount(0);
     27   _info._isAntialiasing = TRUE;
     28   _info._strokeAlignment = FX_STROKEALIGNMENT_Center;
     29   _info._CTM.SetIdentity();
     30   _info._isActOnDash = FALSE;
     31   _info._strokeColor = NULL;
     32   _info._fillColor = NULL;
     33   _info._font = NULL;
     34   _info._fontSize = 40.0;
     35   _info._fontHScale = 1.0;
     36   _info._fontSpacing = 0.0;
     37   _renderDevice = NULL;
     38   _aggGraphics = NULL;
     39 }
     40 FX_ERR CFX_Graphics::Create(CFX_RenderDevice* renderDevice,
     41                             FX_BOOL isAntialiasing) {
     42   _FX_RETURN_VALUE_IF_FAIL(renderDevice, FX_ERR_Parameter_Invalid);
     43   if (_type != FX_CONTEXT_None) {
     44     return FX_ERR_Property_Invalid;
     45   }
     46   _type = FX_CONTEXT_Device;
     47   _info._isAntialiasing = isAntialiasing;
     48   _renderDevice = renderDevice;
     49   if (_renderDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP) {
     50     return FX_ERR_Succeeded;
     51   }
     52   return FX_ERR_Indefinite;
     53 }
     54 FX_ERR CFX_Graphics::Create(int32_t width,
     55                             int32_t height,
     56                             FXDIB_Format format,
     57                             FX_BOOL isNative,
     58                             FX_BOOL isAntialiasing) {
     59   if (_type != FX_CONTEXT_None) {
     60     return FX_ERR_Property_Invalid;
     61   }
     62   _type = FX_CONTEXT_Device;
     63   _info._isAntialiasing = isAntialiasing;
     64   {
     65     _aggGraphics = new CAGG_Graphics;
     66     return _aggGraphics->Create(this, width, height, format);
     67   }
     68 }
     69 CFX_Graphics::~CFX_Graphics() {
     70   if (_aggGraphics) {
     71     delete _aggGraphics;
     72     _aggGraphics = NULL;
     73   }
     74   _renderDevice = NULL;
     75   _info._graphState.SetDashCount(0);
     76   _type = FX_CONTEXT_None;
     77 }
     78 FX_ERR CFX_Graphics::GetDeviceCap(const int32_t capID, FX_DeviceCap& capVal) {
     79   switch (_type) {
     80     case FX_CONTEXT_Device: {
     81       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
     82       capVal = _renderDevice->GetDeviceCaps(capID);
     83       return FX_ERR_Succeeded;
     84     }
     85     default: { return FX_ERR_Property_Invalid; }
     86   }
     87 }
     88 FX_ERR CFX_Graphics::IsPrinterDevice(FX_BOOL& isPrinter) {
     89   switch (_type) {
     90     case FX_CONTEXT_Device: {
     91       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
     92       int32_t deviceClass = _renderDevice->GetDeviceClass();
     93       if (deviceClass == FXDC_PRINTER) {
     94         isPrinter = TRUE;
     95       } else {
     96         isPrinter = FALSE;
     97       }
     98       return FX_ERR_Succeeded;
     99     }
    100     default: { return FX_ERR_Property_Invalid; }
    101   }
    102 }
    103 FX_ERR CFX_Graphics::EnableAntialiasing(FX_BOOL isAntialiasing) {
    104   switch (_type) {
    105     case FX_CONTEXT_Device: {
    106       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    107       _info._isAntialiasing = isAntialiasing;
    108       return FX_ERR_Succeeded;
    109     }
    110     default: { return FX_ERR_Property_Invalid; }
    111   }
    112 }
    113 FX_ERR CFX_Graphics::SaveGraphState() {
    114   switch (_type) {
    115     case FX_CONTEXT_Device: {
    116       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    117       _renderDevice->SaveState();
    118       TInfo* info = new TInfo;
    119       info->_graphState.Copy(_info._graphState);
    120       info->_isAntialiasing = _info._isAntialiasing;
    121       info->_strokeAlignment = _info._strokeAlignment;
    122       info->_CTM = _info._CTM;
    123       info->_isActOnDash = _info._isActOnDash;
    124       info->_strokeColor = _info._strokeColor;
    125       info->_fillColor = _info._fillColor;
    126       info->_font = _info._font;
    127       info->_fontSize = _info._fontSize;
    128       info->_fontHScale = _info._fontHScale;
    129       info->_fontSpacing = _info._fontSpacing;
    130       _infoStack.Add(info);
    131       return FX_ERR_Succeeded;
    132     }
    133     default: { return FX_ERR_Property_Invalid; }
    134   }
    135 }
    136 FX_ERR CFX_Graphics::RestoreGraphState() {
    137   switch (_type) {
    138     case FX_CONTEXT_Device: {
    139       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    140       _renderDevice->RestoreState();
    141       int32_t size = _infoStack.GetSize();
    142       if (size <= 0) {
    143         return FX_ERR_Intermediate_Value_Invalid;
    144       }
    145       int32_t topIndex = size - 1;
    146       TInfo* info = (TInfo*)_infoStack.GetAt(topIndex);
    147       _FX_RETURN_VALUE_IF_FAIL(info, FX_ERR_Intermediate_Value_Invalid);
    148       _info._graphState.Copy(info->_graphState);
    149       _info._isAntialiasing = info->_isAntialiasing;
    150       _info._strokeAlignment = info->_strokeAlignment;
    151       _info._CTM = info->_CTM;
    152       _info._isActOnDash = info->_isActOnDash;
    153       _info._strokeColor = info->_strokeColor;
    154       _info._fillColor = info->_fillColor;
    155       _info._font = info->_font;
    156       _info._fontSize = info->_fontSize;
    157       _info._fontHScale = info->_fontHScale;
    158       _info._fontSpacing = info->_fontSpacing;
    159       delete info;
    160       info = NULL;
    161       _infoStack.RemoveAt(topIndex);
    162       return FX_ERR_Succeeded;
    163     }
    164     default: { return FX_ERR_Property_Invalid; }
    165   }
    166 }
    167 FX_ERR CFX_Graphics::GetLineCap(CFX_GraphStateData::LineCap& lineCap) {
    168   switch (_type) {
    169     case FX_CONTEXT_Device: {
    170       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    171       lineCap = _info._graphState.m_LineCap;
    172       return FX_ERR_Succeeded;
    173     }
    174     default: { return FX_ERR_Property_Invalid; }
    175   }
    176 }
    177 FX_ERR CFX_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
    178   switch (_type) {
    179     case FX_CONTEXT_Device: {
    180       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    181       _info._graphState.m_LineCap = lineCap;
    182       return FX_ERR_Succeeded;
    183     }
    184     default: { return FX_ERR_Property_Invalid; }
    185   }
    186 }
    187 FX_ERR CFX_Graphics::GetDashCount(int32_t& dashCount) {
    188   switch (_type) {
    189     case FX_CONTEXT_Device: {
    190       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    191       dashCount = _info._graphState.m_DashCount;
    192       return FX_ERR_Succeeded;
    193     }
    194     default: { return FX_ERR_Property_Invalid; }
    195   }
    196 }
    197 FX_ERR CFX_Graphics::GetLineDash(FX_FLOAT& dashPhase, FX_FLOAT* dashArray) {
    198   _FX_RETURN_VALUE_IF_FAIL(dashArray, FX_ERR_Parameter_Invalid);
    199   switch (_type) {
    200     case FX_CONTEXT_Device: {
    201       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    202       dashPhase = _info._graphState.m_DashPhase;
    203       FXSYS_memcpy(dashArray, _info._graphState.m_DashArray,
    204                    _info._graphState.m_DashCount * sizeof(FX_FLOAT));
    205       return FX_ERR_Succeeded;
    206     }
    207     default: { return FX_ERR_Property_Invalid; }
    208   }
    209 }
    210 FX_ERR CFX_Graphics::SetLineDash(FX_FLOAT dashPhase,
    211                                  FX_FLOAT* dashArray,
    212                                  int32_t dashCount) {
    213   if (dashCount > 0 && !dashArray) {
    214     return FX_ERR_Parameter_Invalid;
    215   }
    216   dashCount = dashCount < 0 ? 0 : dashCount;
    217   switch (_type) {
    218     case FX_CONTEXT_Device: {
    219       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    220       FX_FLOAT scale = 1.0;
    221       if (_info._isActOnDash) {
    222         scale = _info._graphState.m_LineWidth;
    223       }
    224       _info._graphState.m_DashPhase = dashPhase;
    225       _info._graphState.SetDashCount(dashCount);
    226       for (int32_t i = 0; i < dashCount; i++) {
    227         _info._graphState.m_DashArray[i] = dashArray[i] * scale;
    228       }
    229       return FX_ERR_Succeeded;
    230     }
    231     default: { return FX_ERR_Property_Invalid; }
    232   }
    233 }
    234 FX_ERR CFX_Graphics::SetLineDash(FX_DashStyle dashStyle) {
    235   switch (_type) {
    236     case FX_CONTEXT_Device: {
    237       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    238       return RenderDeviceSetLineDash(dashStyle);
    239     }
    240     default: { return FX_ERR_Property_Invalid; }
    241   }
    242 }
    243 FX_ERR CFX_Graphics::GetLineJoin(CFX_GraphStateData::LineJoin& lineJoin) {
    244   switch (_type) {
    245     case FX_CONTEXT_Device: {
    246       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    247       lineJoin = _info._graphState.m_LineJoin;
    248       return FX_ERR_Succeeded;
    249     }
    250     default: { return FX_ERR_Property_Invalid; }
    251   }
    252 }
    253 FX_ERR CFX_Graphics::SetLineJoin(CFX_GraphStateData::LineJoin lineJoin) {
    254   switch (_type) {
    255     case FX_CONTEXT_Device: {
    256       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    257       _info._graphState.m_LineJoin = lineJoin;
    258       return FX_ERR_Succeeded;
    259     }
    260     default: { return FX_ERR_Property_Invalid; }
    261   }
    262 }
    263 FX_ERR CFX_Graphics::GetMiterLimit(FX_FLOAT& miterLimit) {
    264   switch (_type) {
    265     case FX_CONTEXT_Device: {
    266       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    267       miterLimit = _info._graphState.m_MiterLimit;
    268       return FX_ERR_Succeeded;
    269     }
    270     default: { return FX_ERR_Property_Invalid; }
    271   }
    272 }
    273 FX_ERR CFX_Graphics::SetMiterLimit(FX_FLOAT miterLimit) {
    274   switch (_type) {
    275     case FX_CONTEXT_Device: {
    276       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    277       _info._graphState.m_MiterLimit = miterLimit;
    278       return FX_ERR_Succeeded;
    279     }
    280     default: { return FX_ERR_Property_Invalid; }
    281   }
    282 }
    283 FX_ERR CFX_Graphics::GetLineWidth(FX_FLOAT& lineWidth) {
    284   switch (_type) {
    285     case FX_CONTEXT_Device: {
    286       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    287       lineWidth = _info._graphState.m_LineWidth;
    288       return FX_ERR_Succeeded;
    289     }
    290     default: { return FX_ERR_Property_Invalid; }
    291   }
    292 }
    293 FX_ERR CFX_Graphics::SetLineWidth(FX_FLOAT lineWidth, FX_BOOL isActOnDash) {
    294   switch (_type) {
    295     case FX_CONTEXT_Device: {
    296       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    297       _info._graphState.m_LineWidth = lineWidth;
    298       _info._isActOnDash = isActOnDash;
    299       return FX_ERR_Succeeded;
    300     }
    301     default: { return FX_ERR_Property_Invalid; }
    302   }
    303 }
    304 FX_ERR CFX_Graphics::GetStrokeAlignment(FX_StrokeAlignment& strokeAlignment) {
    305   switch (_type) {
    306     case FX_CONTEXT_Device: {
    307       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    308       strokeAlignment = _info._strokeAlignment;
    309       return FX_ERR_Succeeded;
    310     }
    311     default: { return FX_ERR_Property_Invalid; }
    312   }
    313 }
    314 FX_ERR CFX_Graphics::SetStrokeAlignment(FX_StrokeAlignment strokeAlignment) {
    315   switch (_type) {
    316     case FX_CONTEXT_Device: {
    317       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    318       _info._strokeAlignment = strokeAlignment;
    319       return FX_ERR_Succeeded;
    320     }
    321     default: { return FX_ERR_Property_Invalid; }
    322   }
    323 }
    324 FX_ERR CFX_Graphics::SetStrokeColor(CFX_Color* color) {
    325   _FX_RETURN_VALUE_IF_FAIL(color, FX_ERR_Parameter_Invalid);
    326   switch (_type) {
    327     case FX_CONTEXT_Device: {
    328       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    329       _info._strokeColor = color;
    330       return FX_ERR_Succeeded;
    331     }
    332     default: { return FX_ERR_Property_Invalid; }
    333   }
    334 }
    335 FX_ERR CFX_Graphics::SetFillColor(CFX_Color* color) {
    336   _FX_RETURN_VALUE_IF_FAIL(color, FX_ERR_Parameter_Invalid);
    337   switch (_type) {
    338     case FX_CONTEXT_Device: {
    339       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    340       _info._fillColor = color;
    341       return FX_ERR_Succeeded;
    342     }
    343     default: { return FX_ERR_Property_Invalid; }
    344   }
    345 }
    346 FX_ERR CFX_Graphics::StrokePath(CFX_Path* path, CFX_Matrix* matrix) {
    347   _FX_RETURN_VALUE_IF_FAIL(path, FX_ERR_Parameter_Invalid);
    348   switch (_type) {
    349     case FX_CONTEXT_Device: {
    350       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    351       return RenderDeviceStrokePath(path, matrix);
    352     }
    353     default: { return FX_ERR_Property_Invalid; }
    354   }
    355 }
    356 FX_ERR CFX_Graphics::FillPath(CFX_Path* path,
    357                               FX_FillMode fillMode,
    358                               CFX_Matrix* matrix) {
    359   _FX_RETURN_VALUE_IF_FAIL(path, FX_ERR_Parameter_Invalid);
    360   switch (_type) {
    361     case FX_CONTEXT_Device: {
    362       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    363       return RenderDeviceFillPath(path, fillMode, matrix);
    364     }
    365     default: { return FX_ERR_Property_Invalid; }
    366   }
    367 }
    368 FX_ERR CFX_Graphics::ClipPath(CFX_Path* path,
    369                               FX_FillMode fillMode,
    370                               CFX_Matrix* matrix) {
    371   _FX_RETURN_VALUE_IF_FAIL(path, FX_ERR_Parameter_Invalid);
    372   switch (_type) {
    373     case FX_CONTEXT_Device: {
    374       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    375       FX_BOOL result = _renderDevice->SetClip_PathFill(
    376           path->GetPathData(), (CFX_Matrix*)matrix, fillMode);
    377       _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
    378       return FX_ERR_Succeeded;
    379     }
    380     default: { return FX_ERR_Property_Invalid; }
    381   }
    382 }
    383 FX_ERR CFX_Graphics::DrawImage(CFX_DIBSource* source,
    384                                const CFX_PointF& point,
    385                                CFX_Matrix* matrix) {
    386   _FX_RETURN_VALUE_IF_FAIL(source, FX_ERR_Parameter_Invalid);
    387   switch (_type) {
    388     case FX_CONTEXT_Device: {
    389       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    390       return RenderDeviceDrawImage(source, point, matrix);
    391     }
    392     default: { return FX_ERR_Property_Invalid; }
    393   }
    394 }
    395 FX_ERR CFX_Graphics::StretchImage(CFX_DIBSource* source,
    396                                   const CFX_RectF& rect,
    397                                   CFX_Matrix* matrix) {
    398   _FX_RETURN_VALUE_IF_FAIL(source, FX_ERR_Parameter_Invalid);
    399   switch (_type) {
    400     case FX_CONTEXT_Device: {
    401       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    402       return RenderDeviceStretchImage(source, rect, matrix);
    403     }
    404     default: { return FX_ERR_Property_Invalid; }
    405   }
    406 }
    407 FX_ERR CFX_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
    408   _FX_RETURN_VALUE_IF_FAIL(matrix, FX_ERR_Parameter_Invalid);
    409   switch (_type) {
    410     case FX_CONTEXT_Device: {
    411       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    412       _info._CTM.Concat(*matrix);
    413       return FX_ERR_Succeeded;
    414     }
    415     default: { return FX_ERR_Property_Invalid; }
    416   }
    417 }
    418 CFX_Matrix* CFX_Graphics::GetMatrix() {
    419   switch (_type) {
    420     case FX_CONTEXT_Device: {
    421       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, NULL);
    422       return &_info._CTM;
    423     }
    424     default: { return NULL; }
    425   }
    426 }
    427 FX_ERR CFX_Graphics::GetClipRect(CFX_RectF& rect) {
    428   switch (_type) {
    429     case FX_CONTEXT_Device: {
    430       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    431       FX_RECT r = _renderDevice->GetClipBox();
    432       rect.left = (FX_FLOAT)r.left;
    433       rect.top = (FX_FLOAT)r.top;
    434       rect.width = (FX_FLOAT)r.Width();
    435       rect.height = (FX_FLOAT)r.Height();
    436       return FX_ERR_Succeeded;
    437     }
    438     default: { return FX_ERR_Property_Invalid; }
    439   }
    440 }
    441 FX_ERR CFX_Graphics::SetClipRect(const CFX_RectF& rect) {
    442   switch (_type) {
    443     case FX_CONTEXT_Device: {
    444       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    445       FX_RECT r(FXSYS_round(rect.left), FXSYS_round(rect.top),
    446                 FXSYS_round(rect.right()), FXSYS_round(rect.bottom()));
    447       FX_BOOL result = _renderDevice->SetClip_Rect(&r);
    448       _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
    449       return FX_ERR_Succeeded;
    450     }
    451     default: { return FX_ERR_Property_Invalid; }
    452   }
    453 }
    454 FX_ERR CFX_Graphics::ClearClip() {
    455   switch (_type) {
    456     case FX_CONTEXT_Device: {
    457       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    458       FX_BOOL result = FX_ERR_Succeeded;
    459       _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
    460       return FX_ERR_Succeeded;
    461     }
    462     default: { return FX_ERR_Property_Invalid; }
    463   }
    464 }
    465 FX_ERR CFX_Graphics::SetFont(CFX_Font* font) {
    466   _FX_RETURN_VALUE_IF_FAIL(font, FX_ERR_Parameter_Invalid);
    467   switch (_type) {
    468     case FX_CONTEXT_Device: {
    469       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    470       _info._font = font;
    471       return FX_ERR_Succeeded;
    472     }
    473     default: { return FX_ERR_Property_Invalid; }
    474   }
    475 }
    476 FX_ERR CFX_Graphics::SetFontSize(const FX_FLOAT size) {
    477   FX_FLOAT fontSize = size <= 0 ? 1.0f : size;
    478   switch (_type) {
    479     case FX_CONTEXT_Device: {
    480       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    481       _info._fontSize = fontSize;
    482       return FX_ERR_Succeeded;
    483     }
    484     default: { return FX_ERR_Property_Invalid; }
    485   }
    486 }
    487 FX_ERR CFX_Graphics::SetFontHScale(const FX_FLOAT scale) {
    488   FX_FLOAT fontHScale = scale <= 0 ? 1.0f : scale;
    489   switch (_type) {
    490     case FX_CONTEXT_Device: {
    491       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    492       _info._fontHScale = fontHScale;
    493       return FX_ERR_Succeeded;
    494     }
    495     default: { return FX_ERR_Property_Invalid; }
    496   }
    497 }
    498 FX_ERR CFX_Graphics::SetCharSpacing(const FX_FLOAT spacing) {
    499   FX_FLOAT fontSpacing = spacing < 0 ? 0 : spacing;
    500   switch (_type) {
    501     case FX_CONTEXT_Device: {
    502       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    503       _info._fontSpacing = fontSpacing;
    504       return FX_ERR_Succeeded;
    505     }
    506     default: { return FX_ERR_Property_Invalid; }
    507   }
    508 }
    509 FX_ERR CFX_Graphics::SetTextDrawingMode(const int32_t mode) {
    510   switch (_type) {
    511     case FX_CONTEXT_Device: {
    512       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    513       return FX_ERR_Succeeded;
    514     }
    515     default: { return FX_ERR_Property_Invalid; }
    516   }
    517 }
    518 FX_ERR CFX_Graphics::ShowText(const CFX_PointF& point,
    519                               const CFX_WideString& text,
    520                               CFX_Matrix* matrix) {
    521   switch (_type) {
    522     case FX_CONTEXT_Device: {
    523       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    524       return RenderDeviceShowText(point, text, matrix);
    525     }
    526     default: { return FX_ERR_Property_Invalid; }
    527   }
    528 }
    529 FX_ERR CFX_Graphics::CalcTextRect(CFX_RectF& rect,
    530                                   const CFX_WideString& text,
    531                                   FX_BOOL isMultiline,
    532                                   CFX_Matrix* matrix) {
    533   switch (_type) {
    534     case FX_CONTEXT_Device: {
    535       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    536       int32_t length = text.GetLength();
    537       FX_DWORD* charCodes = FX_Alloc(FX_DWORD, length);
    538       FXTEXT_CHARPOS* charPos = FX_Alloc(FXTEXT_CHARPOS, length);
    539       CalcTextInfo(text, charCodes, charPos, rect);
    540       FX_Free(charPos);
    541       FX_Free(charCodes);
    542       return FX_ERR_Succeeded;
    543     }
    544     default: { return FX_ERR_Property_Invalid; }
    545   }
    546 }
    547 FX_ERR CFX_Graphics::Transfer(CFX_Graphics* graphics,
    548                               const CFX_Matrix* matrix) {
    549   _FX_RETURN_VALUE_IF_FAIL(graphics, FX_ERR_Parameter_Invalid);
    550   CFX_Matrix m;
    551   m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
    552         _info._CTM.f);
    553   if (matrix) {
    554     m.Concat(*matrix);
    555   }
    556   switch (_type) {
    557     case FX_CONTEXT_Device: {
    558       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    559       {
    560         _FX_RETURN_VALUE_IF_FAIL(graphics->_renderDevice,
    561                                  FX_ERR_Parameter_Invalid);
    562         CFX_DIBitmap* bitmap = graphics->_renderDevice->GetBitmap();
    563         FX_BOOL result = _renderDevice->SetDIBits(bitmap, 0, 0);
    564         _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
    565       }
    566     }
    567     default: { return FX_ERR_Property_Invalid; }
    568   }
    569 }
    570 FX_ERR CFX_Graphics::Transfer(CFX_Graphics* graphics,
    571                               FX_FLOAT srcLeft,
    572                               FX_FLOAT srcTop,
    573                               const CFX_RectF& dstRect,
    574                               const CFX_Matrix* matrix) {
    575   _FX_RETURN_VALUE_IF_FAIL(graphics, FX_ERR_Parameter_Invalid);
    576   CFX_Matrix m;
    577   m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
    578         _info._CTM.f);
    579   if (matrix) {
    580     m.Concat(*matrix);
    581   }
    582   switch (_type) {
    583     case FX_CONTEXT_Device: {
    584       _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    585       {
    586         _FX_RETURN_VALUE_IF_FAIL(graphics->_renderDevice,
    587                                  FX_ERR_Parameter_Invalid);
    588         CFX_DIBitmap* bitmap = graphics->_renderDevice->GetBitmap();
    589         FX_BOOL result = FX_ERR_Indefinite;
    590         CFX_DIBitmap bmp;
    591         result = bmp.Create((int32_t)dstRect.width, (int32_t)dstRect.height,
    592                             bitmap->GetFormat());
    593         _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Intermediate_Value_Invalid);
    594         result = graphics->_renderDevice->GetDIBits(&bmp, (int32_t)srcLeft,
    595                                                     (int32_t)srcTop);
    596         _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
    597         result = _renderDevice->SetDIBits(&bmp, (int32_t)dstRect.left,
    598                                           (int32_t)dstRect.top);
    599         _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Method_Not_Supported);
    600         return FX_ERR_Succeeded;
    601       }
    602     }
    603     default: { return FX_ERR_Property_Invalid; }
    604   }
    605 }
    606 CFX_RenderDevice* CFX_Graphics::GetRenderDevice() {
    607   return _renderDevice;
    608 }
    609 FX_ERR CFX_Graphics::InverseRect(const CFX_RectF& rect) {
    610   _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    611   CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
    612   _FX_RETURN_VALUE_IF_FAIL(bitmap, FX_ERR_Property_Invalid);
    613   CFX_RectF temp(rect);
    614   _info._CTM.TransformRect(temp);
    615   CFX_RectF r;
    616   r.Set(0, 0, (FX_FLOAT)bitmap->GetWidth(), (FX_FLOAT)bitmap->GetWidth());
    617   r.Intersect(temp);
    618   if (r.IsEmpty()) {
    619     return FX_ERR_Parameter_Invalid;
    620   }
    621   FX_ARGB* pBuf =
    622       (FX_ARGB*)(bitmap->GetBuffer() + int32_t(r.top) * bitmap->GetPitch());
    623   int32_t bottom = (int32_t)r.bottom();
    624   int32_t right = (int32_t)r.right();
    625   for (int32_t i = (int32_t)r.top; i < bottom; i++) {
    626     FX_ARGB* pLine = pBuf + (int32_t)r.left;
    627     for (int32_t j = (int32_t)r.left; j < right; j++) {
    628       FX_ARGB c = *pLine;
    629       *pLine++ = (c & 0xFF000000) | (0xFFFFFF - (c & 0x00FFFFFF));
    630     }
    631     pBuf = (FX_ARGB*)((uint8_t*)pBuf + bitmap->GetPitch());
    632   }
    633   return FX_ERR_Succeeded;
    634 }
    635 FX_ERR CFX_Graphics::XorDIBitmap(const CFX_DIBitmap* srcBitmap,
    636                                  const CFX_RectF& rect) {
    637   _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    638   CFX_DIBitmap* dst = _renderDevice->GetBitmap();
    639   _FX_RETURN_VALUE_IF_FAIL(dst, FX_ERR_Property_Invalid);
    640   CFX_RectF temp(rect);
    641   _info._CTM.TransformRect(temp);
    642   CFX_RectF r;
    643   r.Set(0, 0, (FX_FLOAT)dst->GetWidth(), (FX_FLOAT)dst->GetWidth());
    644   r.Intersect(temp);
    645   if (r.IsEmpty()) {
    646     return FX_ERR_Parameter_Invalid;
    647   }
    648   FX_ARGB* pSrcBuf = (FX_ARGB*)(srcBitmap->GetBuffer() +
    649                                 int32_t(r.top) * srcBitmap->GetPitch());
    650   FX_ARGB* pDstBuf =
    651       (FX_ARGB*)(dst->GetBuffer() + int32_t(r.top) * dst->GetPitch());
    652   int32_t bottom = (int32_t)r.bottom();
    653   int32_t right = (int32_t)r.right();
    654   for (int32_t i = (int32_t)r.top; i < bottom; i++) {
    655     FX_ARGB* pSrcLine = pSrcBuf + (int32_t)r.left;
    656     FX_ARGB* pDstLine = pDstBuf + (int32_t)r.left;
    657     for (int32_t j = (int32_t)r.left; j < right; j++) {
    658       FX_ARGB c = *pDstLine;
    659       *pDstLine++ =
    660           ArgbEncode(FXARGB_A(c), (c & 0xFFFFFF) ^ (*pSrcLine & 0xFFFFFF));
    661       pSrcLine++;
    662     }
    663     pSrcBuf = (FX_ARGB*)((uint8_t*)pSrcBuf + srcBitmap->GetPitch());
    664     pDstBuf = (FX_ARGB*)((uint8_t*)pDstBuf + dst->GetPitch());
    665   }
    666   return FX_ERR_Succeeded;
    667 }
    668 FX_ERR CFX_Graphics::EqvDIBitmap(const CFX_DIBitmap* srcBitmap,
    669                                  const CFX_RectF& rect) {
    670   _FX_RETURN_VALUE_IF_FAIL(_renderDevice, FX_ERR_Property_Invalid);
    671   CFX_DIBitmap* dst = _renderDevice->GetBitmap();
    672   _FX_RETURN_VALUE_IF_FAIL(dst, FX_ERR_Property_Invalid);
    673   CFX_RectF temp(rect);
    674   _info._CTM.TransformRect(temp);
    675   CFX_RectF r;
    676   r.Set(0, 0, (FX_FLOAT)dst->GetWidth(), (FX_FLOAT)dst->GetWidth());
    677   r.Intersect(temp);
    678   if (r.IsEmpty()) {
    679     return FX_ERR_Parameter_Invalid;
    680   }
    681   FX_ARGB* pSrcBuf = (FX_ARGB*)(srcBitmap->GetBuffer() +
    682                                 int32_t(r.top) * srcBitmap->GetPitch());
    683   FX_ARGB* pDstBuf =
    684       (FX_ARGB*)(dst->GetBuffer() + int32_t(r.top) * dst->GetPitch());
    685   int32_t bottom = (int32_t)r.bottom();
    686   int32_t right = (int32_t)r.right();
    687   for (int32_t i = (int32_t)r.top; i < bottom; i++) {
    688     FX_ARGB* pSrcLine = pSrcBuf + (int32_t)r.left;
    689     FX_ARGB* pDstLine = pDstBuf + (int32_t)r.left;
    690     for (int32_t j = (int32_t)r.left; j < right; j++) {
    691       FX_ARGB c = *pDstLine;
    692       *pDstLine++ =
    693           ArgbEncode(FXARGB_A(c), ~((c & 0xFFFFFF) ^ (*pSrcLine & 0xFFFFFF)));
    694       pSrcLine++;
    695     }
    696     pSrcBuf = (FX_ARGB*)((uint8_t*)pSrcBuf + srcBitmap->GetPitch());
    697     pDstBuf = (FX_ARGB*)((uint8_t*)pDstBuf + dst->GetPitch());
    698   }
    699   return FX_ERR_Succeeded;
    700 }
    701 FX_ERR CFX_Graphics::RenderDeviceSetLineDash(FX_DashStyle dashStyle) {
    702   switch (dashStyle) {
    703     case FX_DASHSTYLE_Solid: {
    704       _info._graphState.SetDashCount(0);
    705       return FX_ERR_Succeeded;
    706     }
    707     case FX_DASHSTYLE_Dash: {
    708       FX_FLOAT dashArray[] = {3, 1};
    709       SetLineDash(0, dashArray, 2);
    710       return FX_ERR_Succeeded;
    711     }
    712     case FX_DASHSTYLE_Dot: {
    713       FX_FLOAT dashArray[] = {1, 1};
    714       SetLineDash(0, dashArray, 2);
    715       return FX_ERR_Succeeded;
    716     }
    717     case FX_DASHSTYLE_DashDot: {
    718       FX_FLOAT dashArray[] = {3, 1, 1, 1};
    719       SetLineDash(0, dashArray, 4);
    720       return FX_ERR_Succeeded;
    721     }
    722     case FX_DASHSTYLE_DashDotDot: {
    723       FX_FLOAT dashArray[] = {4, 1, 2, 1, 2, 1};
    724       SetLineDash(0, dashArray, 6);
    725       return FX_ERR_Succeeded;
    726     }
    727     default: { return FX_ERR_Parameter_Invalid; }
    728   }
    729 }
    730 FX_ERR CFX_Graphics::RenderDeviceStrokePath(CFX_Path* path,
    731                                             CFX_Matrix* matrix) {
    732   _FX_RETURN_VALUE_IF_FAIL(_info._strokeColor, FX_ERR_Property_Invalid);
    733   CFX_Matrix m;
    734   m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
    735         _info._CTM.f);
    736   if (matrix) {
    737     m.Concat(*matrix);
    738   }
    739   switch (_info._strokeColor->_type) {
    740     case FX_COLOR_Solid: {
    741       FX_BOOL result = _renderDevice->DrawPath(
    742           path->GetPathData(), (CFX_Matrix*)&m, &_info._graphState, 0x0,
    743           _info._strokeColor->_argb, 0);
    744       _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
    745       return FX_ERR_Succeeded;
    746     }
    747     case FX_COLOR_Pattern: {
    748       return StrokePathWithPattern(path, &m);
    749     }
    750     case FX_COLOR_Shading: {
    751       return StrokePathWithShading(path, &m);
    752     }
    753     default: { return FX_ERR_Property_Invalid; }
    754   }
    755 }
    756 FX_ERR CFX_Graphics::RenderDeviceFillPath(CFX_Path* path,
    757                                           FX_FillMode fillMode,
    758                                           CFX_Matrix* matrix) {
    759   _FX_RETURN_VALUE_IF_FAIL(_info._fillColor, FX_ERR_Property_Invalid);
    760   CFX_Matrix m;
    761   m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
    762         _info._CTM.f);
    763   if (matrix) {
    764     m.Concat(*matrix);
    765   }
    766   switch (_info._fillColor->_type) {
    767     case FX_COLOR_Solid: {
    768       FX_BOOL result = _renderDevice->DrawPath(
    769           path->GetPathData(), (CFX_Matrix*)&m, &_info._graphState,
    770           _info._fillColor->_argb, 0x0, fillMode);
    771       _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
    772       return FX_ERR_Succeeded;
    773     }
    774     case FX_COLOR_Pattern: {
    775       { return FillPathWithPattern(path, fillMode, &m); }
    776     }
    777     case FX_COLOR_Shading: {
    778       { return FillPathWithShading(path, fillMode, &m); }
    779     }
    780     default: { return FX_ERR_Property_Invalid; }
    781   }
    782 }
    783 FX_ERR CFX_Graphics::RenderDeviceDrawImage(CFX_DIBSource* source,
    784                                            const CFX_PointF& point,
    785                                            CFX_Matrix* matrix) {
    786   CFX_Matrix m1;
    787   m1.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
    788          _info._CTM.f);
    789   if (matrix) {
    790     m1.Concat(*matrix);
    791   }
    792   CFX_Matrix m2;
    793   m2.Set((FX_FLOAT)source->GetWidth(), 0.0, 0.0, (FX_FLOAT)source->GetHeight(),
    794          point.x, point.y);
    795   m2.Concat(m1);
    796   int32_t left, top;
    797   CFX_DIBitmap* bmp1 = source->FlipImage(FALSE, TRUE);
    798   CFX_DIBitmap* bmp2 = bmp1->TransformTo((CFX_Matrix*)&m2, left, top);
    799   CFX_RectF r;
    800   GetClipRect(r);
    801   FX_ERR result = FX_ERR_Indefinite;
    802   {
    803     CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
    804     CFX_DIBitmap bmp;
    805     bmp.Create(bitmap->GetWidth(), bitmap->GetHeight(), FXDIB_Argb);
    806     _renderDevice->GetDIBits(&bmp, 0, 0);
    807     bmp.TransferBitmap(FXSYS_round(r.left), FXSYS_round(r.top),
    808                        FXSYS_round(r.Width()), FXSYS_round(r.Height()), bmp2,
    809                        FXSYS_round(r.left - left), FXSYS_round(r.top - top));
    810     _renderDevice->SetDIBits(&bmp, 0, 0);
    811     result = FX_ERR_Succeeded;
    812   }
    813   if (bmp2) {
    814     delete bmp2;
    815     bmp2 = NULL;
    816   }
    817   if (bmp1) {
    818     delete bmp1;
    819     bmp1 = NULL;
    820   }
    821   return result;
    822 }
    823 FX_ERR CFX_Graphics::RenderDeviceStretchImage(CFX_DIBSource* source,
    824                                               const CFX_RectF& rect,
    825                                               CFX_Matrix* matrix) {
    826   CFX_Matrix m1;
    827   m1.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
    828          _info._CTM.f);
    829   if (matrix) {
    830     m1.Concat(*matrix);
    831   }
    832   CFX_DIBitmap* bmp1 =
    833       source->StretchTo((int32_t)rect.Width(), (int32_t)rect.Height());
    834   CFX_Matrix m2;
    835   m2.Set(rect.Width(), 0.0, 0.0, rect.Height(), rect.left, rect.top);
    836   m2.Concat(m1);
    837   int32_t left, top;
    838   CFX_DIBitmap* bmp2 = bmp1->FlipImage(FALSE, TRUE);
    839   CFX_DIBitmap* bmp3 = bmp2->TransformTo((CFX_Matrix*)&m2, left, top);
    840   CFX_RectF r;
    841   GetClipRect(r);
    842   FX_ERR result = FX_ERR_Indefinite;
    843   {
    844     CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
    845     bitmap->CompositeBitmap(FXSYS_round(r.left), FXSYS_round(r.top),
    846                             FXSYS_round(r.Width()), FXSYS_round(r.Height()),
    847                             bmp3, FXSYS_round(r.left - left),
    848                             FXSYS_round(r.top - top));
    849     result = FX_ERR_Succeeded;
    850   }
    851   if (bmp3) {
    852     delete bmp3;
    853     bmp3 = NULL;
    854   }
    855   if (bmp2) {
    856     delete bmp2;
    857     bmp2 = NULL;
    858   }
    859   if (bmp1) {
    860     delete bmp1;
    861     bmp1 = NULL;
    862   }
    863   return result;
    864 }
    865 FX_ERR CFX_Graphics::RenderDeviceShowText(const CFX_PointF& point,
    866                                           const CFX_WideString& text,
    867                                           CFX_Matrix* matrix) {
    868   int32_t length = text.GetLength();
    869   FX_DWORD* charCodes = FX_Alloc(FX_DWORD, length);
    870   FXTEXT_CHARPOS* charPos = FX_Alloc(FXTEXT_CHARPOS, length);
    871   CFX_RectF rect;
    872   rect.Set(point.x, point.y, 0, 0);
    873   CalcTextInfo(text, charCodes, charPos, rect);
    874   CFX_Matrix m;
    875   m.Set(_info._CTM.a, _info._CTM.b, _info._CTM.c, _info._CTM.d, _info._CTM.e,
    876         _info._CTM.f);
    877   m.Translate(0, _info._fontSize * _info._fontHScale);
    878   if (matrix) {
    879     m.Concat(*matrix);
    880   }
    881   FX_BOOL result = _renderDevice->DrawNormalText(
    882       length, charPos, _info._font, CFX_GEModule::Get()->GetFontCache(),
    883       -_info._fontSize * _info._fontHScale, (CFX_Matrix*)&m,
    884       _info._fillColor->_argb, FXTEXT_CLEARTYPE);
    885   _FX_RETURN_VALUE_IF_FAIL(result, FX_ERR_Indefinite);
    886   FX_Free(charPos);
    887   FX_Free(charCodes);
    888   return FX_ERR_Succeeded;
    889 }
    890 FX_ERR CFX_Graphics::StrokePathWithPattern(CFX_Path* path, CFX_Matrix* matrix) {
    891   return FX_ERR_Method_Not_Supported;
    892 }
    893 FX_ERR CFX_Graphics::StrokePathWithShading(CFX_Path* path, CFX_Matrix* matrix) {
    894   return FX_ERR_Method_Not_Supported;
    895 }
    896 FX_ERR CFX_Graphics::FillPathWithPattern(CFX_Path* path,
    897                                          FX_FillMode fillMode,
    898                                          CFX_Matrix* matrix) {
    899   CFX_Pattern* pattern = _info._fillColor->_pattern;
    900   CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
    901   int32_t width = bitmap->GetWidth();
    902   int32_t height = bitmap->GetHeight();
    903   CFX_DIBitmap bmp;
    904   bmp.Create(width, height, FXDIB_Argb);
    905   _renderDevice->GetDIBits(&bmp, 0, 0);
    906   switch (pattern->_type) {
    907     case FX_PATTERN_Bitmap: {
    908       int32_t xStep = FXSYS_round(pattern->_x1Step);
    909       int32_t yStep = FXSYS_round(pattern->_y1Step);
    910       int32_t xCount = width / xStep + 1;
    911       int32_t yCount = height / yStep + 1;
    912       for (int32_t i = 0; i <= yCount; i++) {
    913         for (int32_t j = 0; j <= xCount; j++) {
    914           bmp.TransferBitmap(j * xStep, i * yStep, xStep, yStep,
    915                              pattern->_bitmap, 0, 0);
    916         }
    917       }
    918       break;
    919     }
    920     case FX_PATTERN_Hatch: {
    921       FX_HatchStyle hatchStyle = _info._fillColor->_pattern->_hatchStyle;
    922       if (hatchStyle < FX_HATCHSTYLE_Horizontal ||
    923           hatchStyle > FX_HATCHSTYLE_SolidDiamond) {
    924         return FX_ERR_Intermediate_Value_Invalid;
    925       }
    926       const FX_HATCHDATA& data = hatchBitmapData[hatchStyle];
    927       CFX_DIBitmap mask;
    928       mask.Create(data.width, data.height, FXDIB_1bppMask);
    929       FXSYS_memcpy(mask.GetBuffer(), data.maskBits,
    930                    mask.GetPitch() * data.height);
    931       CFX_FloatRect rectf = path->GetPathData()->GetBoundingBox();
    932       if (matrix) {
    933         rectf.Transform((const CFX_Matrix*)matrix);
    934       }
    935       FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
    936                    FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
    937       CFX_FxgeDevice device;
    938       device.Attach(&bmp);
    939       device.FillRect(&rect, _info._fillColor->_pattern->_backArgb);
    940       for (int32_t j = rect.bottom; j < rect.top; j += mask.GetHeight()) {
    941         for (int32_t i = rect.left; i < rect.right; i += mask.GetWidth()) {
    942           device.SetBitMask(&mask, i, j, _info._fillColor->_pattern->_foreArgb);
    943         }
    944       }
    945       break;
    946     }
    947   }
    948   _renderDevice->SaveState();
    949   _renderDevice->SetClip_PathFill(path->GetPathData(), (CFX_Matrix*)matrix,
    950                                   fillMode);
    951   SetDIBitsWithMatrix(&bmp, &pattern->_matrix);
    952   _renderDevice->RestoreState();
    953   return FX_ERR_Succeeded;
    954 }
    955 FX_ERR CFX_Graphics::FillPathWithShading(CFX_Path* path,
    956                                          FX_FillMode fillMode,
    957                                          CFX_Matrix* matrix) {
    958   CFX_DIBitmap* bitmap = _renderDevice->GetBitmap();
    959   int32_t width = bitmap->GetWidth();
    960   int32_t height = bitmap->GetHeight();
    961   FX_FLOAT start_x = _info._fillColor->_shading->_beginPoint.x;
    962   FX_FLOAT start_y = _info._fillColor->_shading->_beginPoint.y;
    963   FX_FLOAT end_x = _info._fillColor->_shading->_endPoint.x;
    964   FX_FLOAT end_y = _info._fillColor->_shading->_endPoint.y;
    965   CFX_DIBitmap bmp;
    966   bmp.Create(width, height, FXDIB_Argb);
    967   _renderDevice->GetDIBits(&bmp, 0, 0);
    968   int32_t pitch = bmp.GetPitch();
    969   FX_BOOL result = FALSE;
    970   switch (_info._fillColor->_shading->_type) {
    971     case FX_SHADING_Axial: {
    972       FX_FLOAT x_span = end_x - start_x;
    973       FX_FLOAT y_span = end_y - start_y;
    974       FX_FLOAT axis_len_square =
    975           FXSYS_Mul(x_span, x_span) + FXSYS_Mul(y_span, y_span);
    976       for (int32_t row = 0; row < height; row++) {
    977         FX_DWORD* dib_buf = (FX_DWORD*)(bmp.GetBuffer() + row * pitch);
    978         for (int32_t column = 0; column < width; column++) {
    979           FX_FLOAT x = (FX_FLOAT)(column);
    980           FX_FLOAT y = (FX_FLOAT)(row);
    981           FX_FLOAT scale = FXSYS_Div(
    982               FXSYS_Mul(x - start_x, x_span) + FXSYS_Mul(y - start_y, y_span),
    983               axis_len_square);
    984           if (scale < 0) {
    985             if (!_info._fillColor->_shading->_isExtendedBegin) {
    986               continue;
    987             }
    988             scale = 0;
    989           } else if (scale > 1.0f) {
    990             if (!_info._fillColor->_shading->_isExtendedEnd) {
    991               continue;
    992             }
    993             scale = 1.0f;
    994           }
    995           int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1));
    996           dib_buf[column] = _info._fillColor->_shading->_argbArray[index];
    997         }
    998       }
    999       result = TRUE;
   1000       break;
   1001     }
   1002     case FX_SHADING_Radial: {
   1003       FX_FLOAT start_r = _info._fillColor->_shading->_beginRadius;
   1004       FX_FLOAT end_r = _info._fillColor->_shading->_endRadius;
   1005       FX_FLOAT a = FXSYS_Mul(start_x - end_x, start_x - end_x) +
   1006                    FXSYS_Mul(start_y - end_y, start_y - end_y) -
   1007                    FXSYS_Mul(start_r - end_r, start_r - end_r);
   1008       for (int32_t row = 0; row < height; row++) {
   1009         FX_DWORD* dib_buf = (FX_DWORD*)(bmp.GetBuffer() + row * pitch);
   1010         for (int32_t column = 0; column < width; column++) {
   1011           FX_FLOAT x = (FX_FLOAT)(column);
   1012           FX_FLOAT y = (FX_FLOAT)(row);
   1013           FX_FLOAT b = -2 * (FXSYS_Mul(x - start_x, end_x - start_x) +
   1014                              FXSYS_Mul(y - start_y, end_y - start_y) +
   1015                              FXSYS_Mul(start_r, end_r - start_r));
   1016           FX_FLOAT c = FXSYS_Mul(x - start_x, x - start_x) +
   1017                        FXSYS_Mul(y - start_y, y - start_y) -
   1018                        FXSYS_Mul(start_r, start_r);
   1019           FX_FLOAT s;
   1020           if (a == 0) {
   1021             s = (FXSYS_Div(-c, b));
   1022           } else {
   1023             FX_FLOAT b2_4ac = FXSYS_Mul(b, b) - 4 * FXSYS_Mul(a, c);
   1024             if (b2_4ac < 0) {
   1025               continue;
   1026             }
   1027             FX_FLOAT root = (FXSYS_sqrt(b2_4ac));
   1028             FX_FLOAT s1, s2;
   1029             if (a > 0) {
   1030               s1 = FXSYS_Div(-b - root, 2 * a);
   1031               s2 = FXSYS_Div(-b + root, 2 * a);
   1032             } else {
   1033               s2 = FXSYS_Div(-b - root, 2 * a);
   1034               s1 = FXSYS_Div(-b + root, 2 * a);
   1035             }
   1036             if (s2 <= 1.0f || _info._fillColor->_shading->_isExtendedEnd) {
   1037               s = (s2);
   1038             } else {
   1039               s = (s1);
   1040             }
   1041             if ((start_r) + s * (end_r - start_r) < 0) {
   1042               continue;
   1043             }
   1044           }
   1045           if (s < 0) {
   1046             if (!_info._fillColor->_shading->_isExtendedBegin) {
   1047               continue;
   1048             }
   1049             s = 0;
   1050           }
   1051           if (s > 1.0f) {
   1052             if (!_info._fillColor->_shading->_isExtendedEnd) {
   1053               continue;
   1054             }
   1055             s = 1.0f;
   1056           }
   1057           int index = (int32_t)(s * (FX_SHADING_Steps - 1));
   1058           dib_buf[column] = _info._fillColor->_shading->_argbArray[index];
   1059         }
   1060       }
   1061       result = TRUE;
   1062       break;
   1063     }
   1064     default: { result = FALSE; }
   1065   }
   1066   if (result) {
   1067     _renderDevice->SaveState();
   1068     _renderDevice->SetClip_PathFill(path->GetPathData(), (CFX_Matrix*)matrix,
   1069                                     fillMode);
   1070     SetDIBitsWithMatrix(&bmp, matrix);
   1071     _renderDevice->RestoreState();
   1072   }
   1073   return result;
   1074 }
   1075 FX_ERR CFX_Graphics::SetDIBitsWithMatrix(CFX_DIBSource* source,
   1076                                          CFX_Matrix* matrix) {
   1077   if (matrix->IsIdentity()) {
   1078     _renderDevice->SetDIBits(source, 0, 0);
   1079   } else {
   1080     CFX_Matrix m;
   1081     m.Set((FX_FLOAT)source->GetWidth(), 0, 0, (FX_FLOAT)source->GetHeight(), 0,
   1082           0);
   1083     m.Concat(*matrix);
   1084     int32_t left, top;
   1085     CFX_DIBitmap* bmp1 = source->FlipImage(FALSE, TRUE);
   1086     CFX_DIBitmap* bmp2 = bmp1->TransformTo((CFX_Matrix*)&m, left, top);
   1087     _renderDevice->SetDIBits(bmp2, left, top);
   1088     if (bmp2) {
   1089       delete bmp2;
   1090       bmp2 = NULL;
   1091     }
   1092     if (bmp1) {
   1093       delete bmp1;
   1094       bmp1 = NULL;
   1095     }
   1096   }
   1097   return FX_ERR_Succeeded;
   1098 }
   1099 FX_ERR CFX_Graphics::CalcTextInfo(const CFX_WideString& text,
   1100                                   FX_DWORD* charCodes,
   1101                                   FXTEXT_CHARPOS* charPos,
   1102                                   CFX_RectF& rect) {
   1103   std::unique_ptr<CFX_UnicodeEncoding> encoding(
   1104       new CFX_UnicodeEncoding(_info._font));
   1105   int32_t length = text.GetLength();
   1106   FX_FLOAT penX = (FX_FLOAT)rect.left;
   1107   FX_FLOAT penY = (FX_FLOAT)rect.top;
   1108   FX_FLOAT left = (FX_FLOAT)(0);
   1109   FX_FLOAT top = (FX_FLOAT)(0);
   1110   charCodes[0] = text.GetAt(0);
   1111   charPos[0].m_OriginX = penX + left;
   1112   charPos[0].m_OriginY = penY + top;
   1113   charPos[0].m_GlyphIndex = encoding->GlyphFromCharCode(charCodes[0]);
   1114   charPos[0].m_FontCharWidth = FXSYS_round(
   1115       _info._font->GetGlyphWidth(charPos[0].m_GlyphIndex) * _info._fontHScale);
   1116   charPos[0].m_bGlyphAdjust = TRUE;
   1117   charPos[0].m_AdjustMatrix[0] = -1;
   1118   charPos[0].m_AdjustMatrix[1] = 0;
   1119   charPos[0].m_AdjustMatrix[2] = 0;
   1120   charPos[0].m_AdjustMatrix[3] = 1;
   1121   penX += (FX_FLOAT)(charPos[0].m_FontCharWidth) * _info._fontSize / 1000 +
   1122           _info._fontSpacing;
   1123   for (int32_t i = 1; i < length; i++) {
   1124     charCodes[i] = text.GetAt(i);
   1125     charPos[i].m_OriginX = penX + left;
   1126     charPos[i].m_OriginY = penY + top;
   1127     charPos[i].m_GlyphIndex = encoding->GlyphFromCharCode(charCodes[i]);
   1128     charPos[i].m_FontCharWidth =
   1129         FXSYS_round(_info._font->GetGlyphWidth(charPos[i].m_GlyphIndex) *
   1130                     _info._fontHScale);
   1131     charPos[i].m_bGlyphAdjust = TRUE;
   1132     charPos[i].m_AdjustMatrix[0] = -1;
   1133     charPos[i].m_AdjustMatrix[1] = 0;
   1134     charPos[i].m_AdjustMatrix[2] = 0;
   1135     charPos[i].m_AdjustMatrix[3] = 1;
   1136     penX += (FX_FLOAT)(charPos[i].m_FontCharWidth) * _info._fontSize / 1000 +
   1137             _info._fontSpacing;
   1138   }
   1139   rect.width = (FX_FLOAT)penX - rect.left;
   1140   rect.height = rect.top + _info._fontSize * _info._fontHScale - rect.top;
   1141   return FX_ERR_Succeeded;
   1142 }
   1143 CAGG_Graphics::CAGG_Graphics() {
   1144   _owner = NULL;
   1145 }
   1146 FX_ERR CAGG_Graphics::Create(CFX_Graphics* owner,
   1147                              int32_t width,
   1148                              int32_t height,
   1149                              FXDIB_Format format) {
   1150   if (owner->_renderDevice) {
   1151     return FX_ERR_Parameter_Invalid;
   1152   }
   1153   if (_owner) {
   1154     return FX_ERR_Property_Invalid;
   1155   }
   1156   CFX_FxgeDevice* device = new CFX_FxgeDevice;
   1157   device->Create(width, height, format);
   1158   _owner = owner;
   1159   _owner->_renderDevice = device;
   1160   _owner->_renderDevice->GetBitmap()->Clear(0xFFFFFFFF);
   1161   return FX_ERR_Succeeded;
   1162 }
   1163 CAGG_Graphics::~CAGG_Graphics() {
   1164   if (_owner->_renderDevice) {
   1165     delete (CFX_FxgeDevice*)_owner->_renderDevice;
   1166   }
   1167   _owner = NULL;
   1168 }
   1169 CFX_Path::CFX_Path() {
   1170   _generator = NULL;
   1171 }
   1172 FX_ERR CFX_Path::Create() {
   1173   if (_generator) {
   1174     return FX_ERR_Property_Invalid;
   1175   }
   1176   _generator = new CFX_PathGenerator;
   1177   _generator->Create();
   1178   return FX_ERR_Succeeded;
   1179 }
   1180 CFX_Path::~CFX_Path() {
   1181   if (_generator) {
   1182     delete _generator;
   1183     _generator = NULL;
   1184   }
   1185 }
   1186 FX_ERR CFX_Path::MoveTo(FX_FLOAT x, FX_FLOAT y) {
   1187   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1188   _generator->MoveTo(x, y);
   1189   return FX_ERR_Succeeded;
   1190 }
   1191 FX_ERR CFX_Path::LineTo(FX_FLOAT x, FX_FLOAT y) {
   1192   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1193   _generator->LineTo(x, y);
   1194   return FX_ERR_Succeeded;
   1195 }
   1196 FX_ERR CFX_Path::BezierTo(FX_FLOAT ctrlX1,
   1197                           FX_FLOAT ctrlY1,
   1198                           FX_FLOAT ctrlX2,
   1199                           FX_FLOAT ctrlY2,
   1200                           FX_FLOAT toX,
   1201                           FX_FLOAT toY) {
   1202   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1203   _generator->BezierTo(ctrlX1, ctrlY1, ctrlX2, ctrlY2, toX, toY);
   1204   return FX_ERR_Succeeded;
   1205 }
   1206 FX_ERR CFX_Path::ArcTo(FX_FLOAT left,
   1207                        FX_FLOAT top,
   1208                        FX_FLOAT width,
   1209                        FX_FLOAT height,
   1210                        FX_FLOAT startAngle,
   1211                        FX_FLOAT sweepAngle) {
   1212   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1213   _generator->ArcTo(left + width / 2, top + height / 2, width / 2, height / 2,
   1214                     startAngle, sweepAngle);
   1215   return FX_ERR_Succeeded;
   1216 }
   1217 FX_ERR CFX_Path::Close() {
   1218   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1219   _generator->Close();
   1220   return FX_ERR_Succeeded;
   1221 }
   1222 FX_ERR CFX_Path::AddLine(FX_FLOAT x1, FX_FLOAT y1, FX_FLOAT x2, FX_FLOAT y2) {
   1223   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1224   _generator->AddLine(x1, y1, x2, y2);
   1225   return FX_ERR_Succeeded;
   1226 }
   1227 FX_ERR CFX_Path::AddBezier(FX_FLOAT startX,
   1228                            FX_FLOAT startY,
   1229                            FX_FLOAT ctrlX1,
   1230                            FX_FLOAT ctrlY1,
   1231                            FX_FLOAT ctrlX2,
   1232                            FX_FLOAT ctrlY2,
   1233                            FX_FLOAT endX,
   1234                            FX_FLOAT endY) {
   1235   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1236   _generator->AddBezier(startX, startY, ctrlX1, ctrlY1, ctrlX2, ctrlY2, endX,
   1237                         endY);
   1238   return FX_ERR_Succeeded;
   1239 }
   1240 FX_ERR CFX_Path::AddRectangle(FX_FLOAT left,
   1241                               FX_FLOAT top,
   1242                               FX_FLOAT width,
   1243                               FX_FLOAT height) {
   1244   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1245   _generator->AddRectangle(left, top, left + width, top + height);
   1246   return FX_ERR_Succeeded;
   1247 }
   1248 FX_ERR CFX_Path::AddEllipse(FX_FLOAT left,
   1249                             FX_FLOAT top,
   1250                             FX_FLOAT width,
   1251                             FX_FLOAT height) {
   1252   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1253   _generator->AddEllipse(left + width / 2, top + height / 2, width / 2,
   1254                          height / 2);
   1255   return FX_ERR_Succeeded;
   1256 }
   1257 FX_ERR CFX_Path::AddEllipse(const CFX_RectF& rect) {
   1258   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1259   _generator->AddEllipse(rect.left + rect.Width() / 2,
   1260                          rect.top + rect.Height() / 2, rect.Width() / 2,
   1261                          rect.Height() / 2);
   1262   return FX_ERR_Succeeded;
   1263 }
   1264 FX_ERR CFX_Path::AddArc(FX_FLOAT left,
   1265                         FX_FLOAT top,
   1266                         FX_FLOAT width,
   1267                         FX_FLOAT height,
   1268                         FX_FLOAT startAngle,
   1269                         FX_FLOAT sweepAngle) {
   1270   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1271   _generator->AddArc(left + width / 2, top + height / 2, width / 2, height / 2,
   1272                      startAngle, sweepAngle);
   1273   return FX_ERR_Succeeded;
   1274 }
   1275 FX_ERR CFX_Path::AddPie(FX_FLOAT left,
   1276                         FX_FLOAT top,
   1277                         FX_FLOAT width,
   1278                         FX_FLOAT height,
   1279                         FX_FLOAT startAngle,
   1280                         FX_FLOAT sweepAngle) {
   1281   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1282   _generator->AddPie(left + width / 2, top + height / 2, width / 2, height / 2,
   1283                      startAngle, sweepAngle);
   1284   return FX_ERR_Succeeded;
   1285 }
   1286 FX_ERR CFX_Path::AddSubpath(CFX_Path* path) {
   1287   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1288   _generator->AddPathData(path->GetPathData());
   1289   return FX_ERR_Succeeded;
   1290 }
   1291 FX_ERR CFX_Path::Clear() {
   1292   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1293   _generator->GetPathData()->SetPointCount(0);
   1294   return FX_ERR_Succeeded;
   1295 }
   1296 FX_BOOL CFX_Path::IsEmpty() {
   1297   _FX_RETURN_VALUE_IF_FAIL(_generator, FX_ERR_Property_Invalid);
   1298   if (_generator->GetPathData()->GetPointCount() == 0) {
   1299     return TRUE;
   1300   }
   1301   return FALSE;
   1302 }
   1303 CFX_PathData* CFX_Path::GetPathData() {
   1304   _FX_RETURN_VALUE_IF_FAIL(_generator, NULL);
   1305   return _generator->GetPathData();
   1306 }
   1307 CFX_Color::CFX_Color() {
   1308   _type = FX_COLOR_None;
   1309 }
   1310 CFX_Color::CFX_Color(const FX_ARGB argb) {
   1311   _type = FX_COLOR_None;
   1312   Set(argb);
   1313 }
   1314 CFX_Color::CFX_Color(CFX_Pattern* pattern, const FX_ARGB argb) {
   1315   _type = FX_COLOR_None;
   1316   Set(pattern, argb);
   1317 }
   1318 CFX_Color::CFX_Color(CFX_Shading* shading) {
   1319   _type = FX_COLOR_None;
   1320   Set(shading);
   1321 }
   1322 CFX_Color::~CFX_Color() {
   1323   _type = FX_COLOR_None;
   1324 }
   1325 FX_ERR CFX_Color::Set(const FX_ARGB argb) {
   1326   _type = FX_COLOR_Solid;
   1327   _argb = argb;
   1328   _pattern = NULL;
   1329   return FX_ERR_Succeeded;
   1330 }
   1331 FX_ERR CFX_Color::Set(CFX_Pattern* pattern, const FX_ARGB argb) {
   1332   _FX_RETURN_VALUE_IF_FAIL(pattern, FX_ERR_Parameter_Invalid);
   1333   _type = FX_COLOR_Pattern;
   1334   _argb = argb;
   1335   _pattern = pattern;
   1336   return FX_ERR_Succeeded;
   1337 }
   1338 FX_ERR CFX_Color::Set(CFX_Shading* shading) {
   1339   _FX_RETURN_VALUE_IF_FAIL(shading, FX_ERR_Parameter_Invalid);
   1340   _type = FX_COLOR_Shading;
   1341   _shading = shading;
   1342   return FX_ERR_Succeeded;
   1343 }
   1344 CFX_Pattern::CFX_Pattern() {
   1345   _type = FX_PATTERN_None;
   1346   _matrix.SetIdentity();
   1347 }
   1348 FX_ERR CFX_Pattern::Create(CFX_DIBitmap* bitmap,
   1349                            const FX_FLOAT xStep,
   1350                            const FX_FLOAT yStep,
   1351                            CFX_Matrix* matrix) {
   1352   _FX_RETURN_VALUE_IF_FAIL(bitmap, FX_ERR_Parameter_Invalid);
   1353   if (_type != FX_PATTERN_None) {
   1354     return FX_ERR_Property_Invalid;
   1355   }
   1356   _type = FX_PATTERN_Bitmap;
   1357   _bitmap = bitmap;
   1358   _x1Step = xStep;
   1359   _y1Step = yStep;
   1360   if (matrix) {
   1361     _matrix.Set(matrix->a, matrix->b, matrix->c, matrix->d, matrix->e,
   1362                 matrix->f);
   1363   }
   1364   return FX_ERR_Succeeded;
   1365 }
   1366 FX_ERR CFX_Pattern::Create(FX_HatchStyle hatchStyle,
   1367                            const FX_ARGB foreArgb,
   1368                            const FX_ARGB backArgb,
   1369                            CFX_Matrix* matrix) {
   1370   if (hatchStyle < FX_HATCHSTYLE_Horizontal ||
   1371       hatchStyle > FX_HATCHSTYLE_SolidDiamond) {
   1372     return FX_ERR_Parameter_Invalid;
   1373   }
   1374   if (_type != FX_PATTERN_None) {
   1375     return FX_ERR_Property_Invalid;
   1376   }
   1377   _type = FX_PATTERN_Hatch;
   1378   _hatchStyle = hatchStyle;
   1379   _foreArgb = foreArgb;
   1380   _backArgb = backArgb;
   1381   if (matrix) {
   1382     _matrix.Set(matrix->a, matrix->b, matrix->c, matrix->d, matrix->e,
   1383                 matrix->f);
   1384   }
   1385   return FX_ERR_Succeeded;
   1386 }
   1387 CFX_Pattern::~CFX_Pattern() {
   1388   _type = FX_PATTERN_None;
   1389 }
   1390 CFX_Shading::CFX_Shading() {
   1391   _type = FX_SHADING_None;
   1392 }
   1393 FX_ERR CFX_Shading::CreateAxial(const CFX_PointF& beginPoint,
   1394                                 const CFX_PointF& endPoint,
   1395                                 FX_BOOL isExtendedBegin,
   1396                                 FX_BOOL isExtendedEnd,
   1397                                 const FX_ARGB beginArgb,
   1398                                 const FX_ARGB endArgb) {
   1399   if (_type != FX_SHADING_None) {
   1400     return FX_ERR_Property_Invalid;
   1401   }
   1402   _type = FX_SHADING_Axial;
   1403   _beginPoint = beginPoint;
   1404   _endPoint = endPoint;
   1405   _isExtendedBegin = isExtendedBegin;
   1406   _isExtendedEnd = isExtendedEnd;
   1407   _beginArgb = beginArgb;
   1408   _endArgb = endArgb;
   1409   return InitArgbArray();
   1410 }
   1411 FX_ERR CFX_Shading::CreateRadial(const CFX_PointF& beginPoint,
   1412                                  const CFX_PointF& endPoint,
   1413                                  const FX_FLOAT beginRadius,
   1414                                  const FX_FLOAT endRadius,
   1415                                  FX_BOOL isExtendedBegin,
   1416                                  FX_BOOL isExtendedEnd,
   1417                                  const FX_ARGB beginArgb,
   1418                                  const FX_ARGB endArgb) {
   1419   if (_type != FX_SHADING_None) {
   1420     return FX_ERR_Property_Invalid;
   1421   }
   1422   _type = FX_SHADING_Radial;
   1423   _beginPoint = beginPoint;
   1424   _endPoint = endPoint;
   1425   _beginRadius = beginRadius;
   1426   _endRadius = endRadius;
   1427   _isExtendedBegin = isExtendedBegin;
   1428   _isExtendedEnd = isExtendedEnd;
   1429   _beginArgb = beginArgb;
   1430   _endArgb = endArgb;
   1431   return InitArgbArray();
   1432 }
   1433 CFX_Shading::~CFX_Shading() {
   1434   _type = FX_SHADING_None;
   1435 }
   1436 FX_ERR CFX_Shading::InitArgbArray() {
   1437   int32_t a1, r1, g1, b1;
   1438   ArgbDecode(_beginArgb, a1, r1, g1, b1);
   1439   int32_t a2, r2, g2, b2;
   1440   ArgbDecode(_endArgb, a2, r2, g2, b2);
   1441   FX_FLOAT f = (FX_FLOAT)(FX_SHADING_Steps - 1);
   1442   FX_FLOAT aScale = (FX_FLOAT)(1.0 * (a2 - a1) / f);
   1443   FX_FLOAT rScale = (FX_FLOAT)(1.0 * (r2 - r1) / f);
   1444   FX_FLOAT gScale = (FX_FLOAT)(1.0 * (g2 - g1) / f);
   1445   FX_FLOAT bScale = (FX_FLOAT)(1.0 * (b2 - b1) / f);
   1446   int32_t a3, r3, g3, b3;
   1447   for (int32_t i = 0; i < FX_SHADING_Steps; i++) {
   1448     a3 = (int32_t)(i * aScale);
   1449     r3 = (int32_t)(i * rScale);
   1450     g3 = (int32_t)(i * gScale);
   1451     b3 = (int32_t)(i * bScale);
   1452     _argbArray[i] =
   1453         FXARGB_TODIB(FXARGB_MAKE((a1 + a3), (r1 + r3), (g1 + g3), (b1 + b3)));
   1454   }
   1455   return FX_ERR_Succeeded;
   1456 }
   1457 class CFX_Pause : public IFX_Pause {
   1458  public:
   1459   virtual FX_BOOL NeedToPauseNow() { return TRUE; }
   1460 };
   1461