Home | History | Annotate | Download | only in fxgraphics
      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 "xfa/fxgraphics/cfx_graphics.h"
      8 
      9 #include <memory>
     10 
     11 #include "core/fxge/cfx_fxgedevice.h"
     12 #include "core/fxge/cfx_gemodule.h"
     13 #include "core/fxge/cfx_renderdevice.h"
     14 #include "core/fxge/cfx_unicodeencoding.h"
     15 #include "third_party/base/ptr_util.h"
     16 #include "xfa/fxgraphics/cfx_color.h"
     17 #include "xfa/fxgraphics/cfx_path.h"
     18 #include "xfa/fxgraphics/cfx_pattern.h"
     19 #include "xfa/fxgraphics/cfx_shading.h"
     20 
     21 namespace {
     22 
     23 enum {
     24   FX_CONTEXT_None = 0,
     25   FX_CONTEXT_Device,
     26 };
     27 
     28 #define FX_HATCHSTYLE_Total 53
     29 
     30 struct FX_HATCHDATA {
     31   int32_t width;
     32   int32_t height;
     33   uint8_t maskBits[64];
     34 };
     35 
     36 const FX_HATCHDATA hatchBitmapData[FX_HATCHSTYLE_Total] = {
     37     {16,  // Horizontal
     38      16,
     39      {
     40          0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     41          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     42          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
     43          0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     44          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     45          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     46      }},
     47     {16,  // Vertical
     48      16,
     49      {
     50          0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
     51          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
     52          0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80,
     53          0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
     54          0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
     55          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
     56      }},
     57     {16,  // ForwardDiagonal
     58      16,
     59      {
     60          0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00,
     61          0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04,
     62          0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80,
     63          0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00,
     64          0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00,
     65          0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00,
     66      }},
     67     {16,  // BackwardDiagonal
     68      16,
     69      {
     70          0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00,
     71          0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20,
     72          0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01,
     73          0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,
     74          0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00,
     75          0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
     76      }},
     77     {16,  // Cross
     78      16,
     79      {
     80          0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
     81          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80,
     82          0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff,
     83          0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
     84          0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00,
     85          0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00,
     86      }},
     87     {16,  // DiagonalCross
     88      16,
     89      {
     90          0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00,
     91          0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24,
     92          0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81,
     93          0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00,
     94          0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00,
     95          0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00,
     96      }},
     97 };
     98 
     99 }  // namespace
    100 
    101 CFX_Graphics::CFX_Graphics(CFX_RenderDevice* renderDevice)
    102     : m_type(FX_CONTEXT_None), m_renderDevice(renderDevice) {
    103   if (!renderDevice)
    104     return;
    105   m_type = FX_CONTEXT_Device;
    106 }
    107 
    108 CFX_Graphics::~CFX_Graphics() {}
    109 
    110 void CFX_Graphics::SaveGraphState() {
    111   if (m_type != FX_CONTEXT_Device || !m_renderDevice)
    112     return;
    113 
    114   m_renderDevice->SaveState();
    115   m_infoStack.push_back(pdfium::MakeUnique<TInfo>(m_info));
    116 }
    117 
    118 void CFX_Graphics::RestoreGraphState() {
    119   if (m_type != FX_CONTEXT_Device || !m_renderDevice)
    120     return;
    121 
    122   m_renderDevice->RestoreState(false);
    123   if (m_infoStack.empty() || !m_infoStack.back())
    124     return;
    125 
    126   m_info = *m_infoStack.back();
    127   m_infoStack.pop_back();
    128   return;
    129 }
    130 
    131 void CFX_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) {
    132   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    133     m_info.graphState.m_LineCap = lineCap;
    134   }
    135 }
    136 
    137 void CFX_Graphics::SetLineDash(FX_FLOAT dashPhase,
    138                                FX_FLOAT* dashArray,
    139                                int32_t dashCount) {
    140   if (dashCount > 0 && !dashArray)
    141     return;
    142 
    143   dashCount = dashCount < 0 ? 0 : dashCount;
    144   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    145     FX_FLOAT scale = 1.0;
    146     if (m_info.isActOnDash) {
    147       scale = m_info.graphState.m_LineWidth;
    148     }
    149     m_info.graphState.m_DashPhase = dashPhase;
    150     m_info.graphState.SetDashCount(dashCount);
    151     for (int32_t i = 0; i < dashCount; i++) {
    152       m_info.graphState.m_DashArray[i] = dashArray[i] * scale;
    153     }
    154   }
    155 }
    156 
    157 void CFX_Graphics::SetLineDash(FX_DashStyle dashStyle) {
    158   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    159     RenderDeviceSetLineDash(dashStyle);
    160 }
    161 
    162 void CFX_Graphics::SetLineWidth(FX_FLOAT lineWidth, bool isActOnDash) {
    163   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    164     m_info.graphState.m_LineWidth = lineWidth;
    165     m_info.isActOnDash = isActOnDash;
    166   }
    167 }
    168 
    169 void CFX_Graphics::SetStrokeColor(CFX_Color* color) {
    170   if (!color)
    171     return;
    172   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    173     m_info.strokeColor = color;
    174   }
    175 }
    176 
    177 void CFX_Graphics::SetFillColor(CFX_Color* color) {
    178   if (!color)
    179     return;
    180   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    181     m_info.fillColor = color;
    182   }
    183 }
    184 
    185 void CFX_Graphics::StrokePath(CFX_Path* path, CFX_Matrix* matrix) {
    186   if (!path)
    187     return;
    188   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    189     RenderDeviceStrokePath(path, matrix);
    190 }
    191 
    192 void CFX_Graphics::FillPath(CFX_Path* path,
    193                             FX_FillMode fillMode,
    194                             CFX_Matrix* matrix) {
    195   if (!path)
    196     return;
    197   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    198     RenderDeviceFillPath(path, fillMode, matrix);
    199 }
    200 
    201 void CFX_Graphics::StretchImage(CFX_DIBSource* source,
    202                                 const CFX_RectF& rect,
    203                                 CFX_Matrix* matrix) {
    204   if (!source)
    205     return;
    206   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    207     RenderDeviceStretchImage(source, rect, matrix);
    208 }
    209 
    210 void CFX_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
    211   if (!matrix)
    212     return;
    213   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    214     m_info.CTM.Concat(*matrix);
    215   }
    216 }
    217 
    218 CFX_Matrix* CFX_Graphics::GetMatrix() {
    219   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    220     return &m_info.CTM;
    221   return nullptr;
    222 }
    223 
    224 CFX_RectF CFX_Graphics::GetClipRect() const {
    225   if (m_type != FX_CONTEXT_Device || !m_renderDevice)
    226     return CFX_RectF();
    227 
    228   FX_RECT r = m_renderDevice->GetClipBox();
    229   return CFX_Rect(r.left, r.top, r.Width(), r.Height()).As<FX_FLOAT>();
    230 }
    231 
    232 void CFX_Graphics::SetClipRect(const CFX_RectF& rect) {
    233   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    234     m_renderDevice->SetClip_Rect(
    235         FX_RECT(FXSYS_round(rect.left), FXSYS_round(rect.top),
    236                 FXSYS_round(rect.right()), FXSYS_round(rect.bottom())));
    237   }
    238 }
    239 
    240 CFX_RenderDevice* CFX_Graphics::GetRenderDevice() {
    241   return m_renderDevice;
    242 }
    243 
    244 void CFX_Graphics::RenderDeviceSetLineDash(FX_DashStyle dashStyle) {
    245   switch (dashStyle) {
    246     case FX_DASHSTYLE_Solid: {
    247       m_info.graphState.SetDashCount(0);
    248       return;
    249     }
    250     case FX_DASHSTYLE_Dash: {
    251       FX_FLOAT dashArray[] = {3, 1};
    252       SetLineDash(0, dashArray, 2);
    253       return;
    254     }
    255     case FX_DASHSTYLE_Dot: {
    256       FX_FLOAT dashArray[] = {1, 1};
    257       SetLineDash(0, dashArray, 2);
    258       return;
    259     }
    260     case FX_DASHSTYLE_DashDot: {
    261       FX_FLOAT dashArray[] = {3, 1, 1, 1};
    262       SetLineDash(0, dashArray, 4);
    263       return;
    264     }
    265     case FX_DASHSTYLE_DashDotDot: {
    266       FX_FLOAT dashArray[] = {4, 1, 2, 1, 2, 1};
    267       SetLineDash(0, dashArray, 6);
    268       return;
    269     }
    270     default:
    271       return;
    272   }
    273 }
    274 
    275 void CFX_Graphics::RenderDeviceStrokePath(CFX_Path* path, CFX_Matrix* matrix) {
    276   if (!m_info.strokeColor)
    277     return;
    278   CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d,
    279                m_info.CTM.e, m_info.CTM.f);
    280   if (matrix) {
    281     m.Concat(*matrix);
    282   }
    283   switch (m_info.strokeColor->m_type) {
    284     case FX_COLOR_Solid: {
    285       m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, 0x0,
    286                                m_info.strokeColor->m_info.argb, 0);
    287       return;
    288     }
    289     default:
    290       return;
    291   }
    292 }
    293 
    294 void CFX_Graphics::RenderDeviceFillPath(CFX_Path* path,
    295                                         FX_FillMode fillMode,
    296                                         CFX_Matrix* matrix) {
    297   if (!m_info.fillColor)
    298     return;
    299   CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d,
    300                m_info.CTM.e, m_info.CTM.f);
    301   if (matrix) {
    302     m.Concat(*matrix);
    303   }
    304   switch (m_info.fillColor->m_type) {
    305     case FX_COLOR_Solid: {
    306       m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState,
    307                                m_info.fillColor->m_info.argb, 0x0, fillMode);
    308       return;
    309     }
    310     case FX_COLOR_Pattern:
    311       FillPathWithPattern(path, fillMode, &m);
    312       return;
    313     case FX_COLOR_Shading:
    314       FillPathWithShading(path, fillMode, &m);
    315       return;
    316     default:
    317       return;
    318   }
    319 }
    320 
    321 void CFX_Graphics::RenderDeviceStretchImage(CFX_DIBSource* source,
    322                                             const CFX_RectF& rect,
    323                                             CFX_Matrix* matrix) {
    324   CFX_Matrix m1(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d,
    325                 m_info.CTM.e, m_info.CTM.f);
    326   if (matrix) {
    327     m1.Concat(*matrix);
    328   }
    329   std::unique_ptr<CFX_DIBitmap> bmp1 =
    330       source->StretchTo((int32_t)rect.Width(), (int32_t)rect.Height());
    331   CFX_Matrix m2(rect.Width(), 0.0, 0.0, rect.Height(), rect.left, rect.top);
    332   m2.Concat(m1);
    333 
    334   int32_t left;
    335   int32_t top;
    336   std::unique_ptr<CFX_DIBitmap> bmp2 = bmp1->FlipImage(false, true);
    337   std::unique_ptr<CFX_DIBitmap> bmp3 = bmp2->TransformTo(&m2, left, top);
    338   CFX_RectF r = GetClipRect();
    339   CFX_DIBitmap* bitmap = m_renderDevice->GetBitmap();
    340   bitmap->CompositeBitmap(FXSYS_round(r.left), FXSYS_round(r.top),
    341                           FXSYS_round(r.Width()), FXSYS_round(r.Height()),
    342                           bmp3.get(), FXSYS_round(r.left - left),
    343                           FXSYS_round(r.top - top));
    344 }
    345 
    346 void CFX_Graphics::FillPathWithPattern(CFX_Path* path,
    347                                        FX_FillMode fillMode,
    348                                        CFX_Matrix* matrix) {
    349   CFX_Pattern* pattern = m_info.fillColor->m_info.pattern;
    350   CFX_DIBitmap* bitmap = m_renderDevice->GetBitmap();
    351   int32_t width = bitmap->GetWidth();
    352   int32_t height = bitmap->GetHeight();
    353   CFX_DIBitmap bmp;
    354   bmp.Create(width, height, FXDIB_Argb);
    355   m_renderDevice->GetDIBits(&bmp, 0, 0);
    356 
    357   FX_HatchStyle hatchStyle = m_info.fillColor->m_info.pattern->m_hatchStyle;
    358   const FX_HATCHDATA& data = hatchBitmapData[static_cast<int>(hatchStyle)];
    359 
    360   CFX_DIBitmap mask;
    361   mask.Create(data.width, data.height, FXDIB_1bppMask);
    362   FXSYS_memcpy(mask.GetBuffer(), data.maskBits, mask.GetPitch() * data.height);
    363   CFX_FloatRect rectf = path->GetPathData()->GetBoundingBox();
    364   if (matrix)
    365     matrix->TransformRect(rectf);
    366 
    367   FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
    368                FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
    369   CFX_FxgeDevice device;
    370   device.Attach(&bmp, false, nullptr, false);
    371   device.FillRect(&rect, m_info.fillColor->m_info.pattern->m_backArgb);
    372   for (int32_t j = rect.bottom; j < rect.top; j += mask.GetHeight()) {
    373     for (int32_t i = rect.left; i < rect.right; i += mask.GetWidth()) {
    374       device.SetBitMask(&mask, i, j,
    375                         m_info.fillColor->m_info.pattern->m_foreArgb);
    376     }
    377   }
    378 
    379   m_renderDevice->SaveState();
    380   m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode);
    381   SetDIBitsWithMatrix(&bmp, &pattern->m_matrix);
    382   m_renderDevice->RestoreState(false);
    383 }
    384 
    385 void CFX_Graphics::FillPathWithShading(CFX_Path* path,
    386                                        FX_FillMode fillMode,
    387                                        CFX_Matrix* matrix) {
    388   CFX_DIBitmap* bitmap = m_renderDevice->GetBitmap();
    389   int32_t width = bitmap->GetWidth();
    390   int32_t height = bitmap->GetHeight();
    391   FX_FLOAT start_x = m_info.fillColor->m_shading->m_beginPoint.x;
    392   FX_FLOAT start_y = m_info.fillColor->m_shading->m_beginPoint.y;
    393   FX_FLOAT end_x = m_info.fillColor->m_shading->m_endPoint.x;
    394   FX_FLOAT end_y = m_info.fillColor->m_shading->m_endPoint.y;
    395   CFX_DIBitmap bmp;
    396   bmp.Create(width, height, FXDIB_Argb);
    397   m_renderDevice->GetDIBits(&bmp, 0, 0);
    398   int32_t pitch = bmp.GetPitch();
    399   bool result = false;
    400   switch (m_info.fillColor->m_shading->m_type) {
    401     case FX_SHADING_Axial: {
    402       FX_FLOAT x_span = end_x - start_x;
    403       FX_FLOAT y_span = end_y - start_y;
    404       FX_FLOAT axis_len_square = (x_span * x_span) + (y_span * y_span);
    405       for (int32_t row = 0; row < height; row++) {
    406         uint32_t* dib_buf = (uint32_t*)(bmp.GetBuffer() + row * pitch);
    407         for (int32_t column = 0; column < width; column++) {
    408           FX_FLOAT x = (FX_FLOAT)(column);
    409           FX_FLOAT y = (FX_FLOAT)(row);
    410           FX_FLOAT scale =
    411               (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
    412               axis_len_square;
    413           if (scale < 0) {
    414             if (!m_info.fillColor->m_shading->m_isExtendedBegin) {
    415               continue;
    416             }
    417             scale = 0;
    418           } else if (scale > 1.0f) {
    419             if (!m_info.fillColor->m_shading->m_isExtendedEnd) {
    420               continue;
    421             }
    422             scale = 1.0f;
    423           }
    424           int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1));
    425           dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index];
    426         }
    427       }
    428       result = true;
    429       break;
    430     }
    431     case FX_SHADING_Radial: {
    432       FX_FLOAT start_r = m_info.fillColor->m_shading->m_beginRadius;
    433       FX_FLOAT end_r = m_info.fillColor->m_shading->m_endRadius;
    434       FX_FLOAT a = ((start_x - end_x) * (start_x - end_x)) +
    435                    ((start_y - end_y) * (start_y - end_y)) -
    436                    ((start_r - end_r) * (start_r - end_r));
    437       for (int32_t row = 0; row < height; row++) {
    438         uint32_t* dib_buf = (uint32_t*)(bmp.GetBuffer() + row * pitch);
    439         for (int32_t column = 0; column < width; column++) {
    440           FX_FLOAT x = (FX_FLOAT)(column);
    441           FX_FLOAT y = (FX_FLOAT)(row);
    442           FX_FLOAT b = -2 * (((x - start_x) * (end_x - start_x)) +
    443                              ((y - start_y) * (end_y - start_y)) +
    444                              (start_r * (end_r - start_r)));
    445           FX_FLOAT c = ((x - start_x) * (x - start_x)) +
    446                        ((y - start_y) * (y - start_y)) - (start_r * start_r);
    447           FX_FLOAT s;
    448           if (a == 0) {
    449             s = -c / b;
    450           } else {
    451             FX_FLOAT b2_4ac = (b * b) - 4 * (a * c);
    452             if (b2_4ac < 0) {
    453               continue;
    454             }
    455             FX_FLOAT root = (FXSYS_sqrt(b2_4ac));
    456             FX_FLOAT s1, s2;
    457             if (a > 0) {
    458               s1 = (-b - root) / (2 * a);
    459               s2 = (-b + root) / (2 * a);
    460             } else {
    461               s2 = (-b - root) / (2 * a);
    462               s1 = (-b + root) / (2 * a);
    463             }
    464             if (s2 <= 1.0f || m_info.fillColor->m_shading->m_isExtendedEnd) {
    465               s = (s2);
    466             } else {
    467               s = (s1);
    468             }
    469             if ((start_r) + s * (end_r - start_r) < 0) {
    470               continue;
    471             }
    472           }
    473           if (s < 0) {
    474             if (!m_info.fillColor->m_shading->m_isExtendedBegin) {
    475               continue;
    476             }
    477             s = 0;
    478           }
    479           if (s > 1.0f) {
    480             if (!m_info.fillColor->m_shading->m_isExtendedEnd) {
    481               continue;
    482             }
    483             s = 1.0f;
    484           }
    485           int index = (int32_t)(s * (FX_SHADING_Steps - 1));
    486           dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index];
    487         }
    488       }
    489       result = true;
    490       break;
    491     }
    492     default: {
    493       result = false;
    494       break;
    495     }
    496   }
    497   if (result) {
    498     m_renderDevice->SaveState();
    499     m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode);
    500     SetDIBitsWithMatrix(&bmp, matrix);
    501     m_renderDevice->RestoreState(false);
    502   }
    503 }
    504 
    505 void CFX_Graphics::SetDIBitsWithMatrix(CFX_DIBSource* source,
    506                                        CFX_Matrix* matrix) {
    507   if (matrix->IsIdentity()) {
    508     m_renderDevice->SetDIBits(source, 0, 0);
    509   } else {
    510     CFX_Matrix m((FX_FLOAT)source->GetWidth(), 0, 0,
    511                  (FX_FLOAT)source->GetHeight(), 0, 0);
    512     m.Concat(*matrix);
    513     int32_t left;
    514     int32_t top;
    515     std::unique_ptr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
    516     std::unique_ptr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(&m, left, top);
    517     m_renderDevice->SetDIBits(bmp2.get(), left, top);
    518   }
    519 }
    520 
    521 CFX_Graphics::TInfo::TInfo()
    522     : isActOnDash(false), strokeColor(nullptr), fillColor(nullptr) {}
    523 
    524 CFX_Graphics::TInfo::TInfo(const TInfo& info)
    525     : graphState(info.graphState),
    526       CTM(info.CTM),
    527       isActOnDash(info.isActOnDash),
    528       strokeColor(info.strokeColor),
    529       fillColor(info.fillColor) {}
    530 
    531 CFX_Graphics::TInfo& CFX_Graphics::TInfo::operator=(const TInfo& other) {
    532   graphState.Copy(other.graphState);
    533   CTM = other.CTM;
    534   isActOnDash = other.isActOnDash;
    535   strokeColor = other.strokeColor;
    536   fillColor = other.fillColor;
    537   return *this;
    538 }
    539