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/cxfa_graphics.h"
      8 
      9 #include <memory>
     10 
     11 #include "core/fxge/cfx_defaultrenderdevice.h"
     12 #include "core/fxge/cfx_renderdevice.h"
     13 #include "core/fxge/cfx_unicodeencoding.h"
     14 #include "core/fxge/dib/cfx_dibitmap.h"
     15 #include "third_party/base/ptr_util.h"
     16 #include "xfa/fxgraphics/cxfa_gecolor.h"
     17 #include "xfa/fxgraphics/cxfa_gepath.h"
     18 #include "xfa/fxgraphics/cxfa_gepattern.h"
     19 #include "xfa/fxgraphics/cxfa_geshading.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 CXFA_Graphics::CXFA_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 CXFA_Graphics::~CXFA_Graphics() {}
    109 
    110 void CXFA_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 CXFA_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 CXFA_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 CXFA_Graphics::SetLineDash(float dashPhase,
    138                                 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     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 CXFA_Graphics::SetSolidLineDash() {
    158   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    159     m_info.graphState.SetDashCount(0);
    160 }
    161 
    162 void CXFA_Graphics::SetLineWidth(float lineWidth) {
    163   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    164     m_info.graphState.m_LineWidth = lineWidth;
    165 }
    166 
    167 void CXFA_Graphics::EnableActOnDash() {
    168   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    169     m_info.isActOnDash = true;
    170 }
    171 
    172 void CXFA_Graphics::SetStrokeColor(const CXFA_GEColor& color) {
    173   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    174     m_info.strokeColor = color;
    175 }
    176 
    177 void CXFA_Graphics::SetFillColor(const CXFA_GEColor& color) {
    178   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    179     m_info.fillColor = color;
    180 }
    181 
    182 void CXFA_Graphics::StrokePath(CXFA_GEPath* path, const CFX_Matrix* matrix) {
    183   if (!path)
    184     return;
    185   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    186     RenderDeviceStrokePath(path, matrix);
    187 }
    188 
    189 void CXFA_Graphics::FillPath(CXFA_GEPath* path,
    190                              FX_FillMode fillMode,
    191                              const CFX_Matrix* matrix) {
    192   if (!path)
    193     return;
    194   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    195     RenderDeviceFillPath(path, fillMode, matrix);
    196 }
    197 
    198 void CXFA_Graphics::ConcatMatrix(const CFX_Matrix* matrix) {
    199   if (!matrix)
    200     return;
    201   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    202     m_info.CTM.Concat(*matrix);
    203   }
    204 }
    205 
    206 const CFX_Matrix* CXFA_Graphics::GetMatrix() const {
    207   if (m_type == FX_CONTEXT_Device && m_renderDevice)
    208     return &m_info.CTM;
    209   return nullptr;
    210 }
    211 
    212 CFX_RectF CXFA_Graphics::GetClipRect() const {
    213   if (m_type != FX_CONTEXT_Device || !m_renderDevice)
    214     return CFX_RectF();
    215 
    216   FX_RECT r = m_renderDevice->GetClipBox();
    217   return CFX_Rect(r.left, r.top, r.Width(), r.Height()).As<float>();
    218 }
    219 
    220 void CXFA_Graphics::SetClipRect(const CFX_RectF& rect) {
    221   if (m_type == FX_CONTEXT_Device && m_renderDevice) {
    222     m_renderDevice->SetClip_Rect(
    223         FX_RECT(FXSYS_round(rect.left), FXSYS_round(rect.top),
    224                 FXSYS_round(rect.right()), FXSYS_round(rect.bottom())));
    225   }
    226 }
    227 
    228 CFX_RenderDevice* CXFA_Graphics::GetRenderDevice() {
    229   return m_renderDevice;
    230 }
    231 
    232 void CXFA_Graphics::RenderDeviceStrokePath(const CXFA_GEPath* path,
    233                                            const CFX_Matrix* matrix) {
    234   if (m_info.strokeColor.GetType() != CXFA_GEColor::Solid)
    235     return;
    236 
    237   CFX_Matrix m = m_info.CTM;
    238   if (matrix)
    239     m.Concat(*matrix);
    240 
    241   m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, 0x0,
    242                            m_info.strokeColor.GetArgb(), 0);
    243 }
    244 
    245 void CXFA_Graphics::RenderDeviceFillPath(const CXFA_GEPath* path,
    246                                          FX_FillMode fillMode,
    247                                          const CFX_Matrix* matrix) {
    248   CFX_Matrix m = m_info.CTM;
    249   if (matrix)
    250     m.Concat(*matrix);
    251 
    252   switch (m_info.fillColor.GetType()) {
    253     case CXFA_GEColor::Solid:
    254       m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState,
    255                                m_info.fillColor.GetArgb(), 0x0, fillMode);
    256       return;
    257     case CXFA_GEColor::Pattern:
    258       FillPathWithPattern(path, fillMode, m);
    259       return;
    260     case CXFA_GEColor::Shading:
    261       FillPathWithShading(path, fillMode, m);
    262       return;
    263     default:
    264       return;
    265   }
    266 }
    267 
    268 void CXFA_Graphics::FillPathWithPattern(const CXFA_GEPath* path,
    269                                         FX_FillMode fillMode,
    270                                         const CFX_Matrix& matrix) {
    271   CXFA_GEPattern* pattern = m_info.fillColor.GetPattern();
    272   RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
    273   int32_t width = bitmap->GetWidth();
    274   int32_t height = bitmap->GetHeight();
    275   auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
    276   bmp->Create(width, height, FXDIB_Argb);
    277   m_renderDevice->GetDIBits(bmp, 0, 0);
    278 
    279   FX_HatchStyle hatchStyle = m_info.fillColor.GetPattern()->m_hatchStyle;
    280   const FX_HATCHDATA& data = hatchBitmapData[static_cast<int>(hatchStyle)];
    281 
    282   auto mask = pdfium::MakeRetain<CFX_DIBitmap>();
    283   mask->Create(data.width, data.height, FXDIB_1bppMask);
    284   memcpy(mask->GetBuffer(), data.maskBits, mask->GetPitch() * data.height);
    285   CFX_FloatRect rectf =
    286       matrix.TransformRect(path->GetPathData()->GetBoundingBox());
    287 
    288   FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top),
    289                FXSYS_round(rectf.right), FXSYS_round(rectf.bottom));
    290   CFX_DefaultRenderDevice device;
    291   device.Attach(bmp, false, nullptr, false);
    292   device.FillRect(&rect, m_info.fillColor.GetPattern()->m_backArgb);
    293   for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) {
    294     for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth())
    295       device.SetBitMask(mask, i, j, m_info.fillColor.GetPattern()->m_foreArgb);
    296   }
    297   CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
    298   m_renderDevice->SetClip_PathFill(path->GetPathData(), &matrix, fillMode);
    299   SetDIBitsWithMatrix(bmp, pattern->m_matrix);
    300 }
    301 
    302 void CXFA_Graphics::FillPathWithShading(const CXFA_GEPath* path,
    303                                         FX_FillMode fillMode,
    304                                         const CFX_Matrix& matrix) {
    305   RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap();
    306   int32_t width = bitmap->GetWidth();
    307   int32_t height = bitmap->GetHeight();
    308   float start_x = m_info.fillColor.GetShading()->m_beginPoint.x;
    309   float start_y = m_info.fillColor.GetShading()->m_beginPoint.y;
    310   float end_x = m_info.fillColor.GetShading()->m_endPoint.x;
    311   float end_y = m_info.fillColor.GetShading()->m_endPoint.y;
    312   auto bmp = pdfium::MakeRetain<CFX_DIBitmap>();
    313   bmp->Create(width, height, FXDIB_Argb);
    314   m_renderDevice->GetDIBits(bmp, 0, 0);
    315   int32_t pitch = bmp->GetPitch();
    316   bool result = false;
    317   switch (m_info.fillColor.GetShading()->m_type) {
    318     case FX_SHADING_Axial: {
    319       float x_span = end_x - start_x;
    320       float y_span = end_y - start_y;
    321       float axis_len_square = (x_span * x_span) + (y_span * y_span);
    322       for (int32_t row = 0; row < height; row++) {
    323         uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch);
    324         for (int32_t column = 0; column < width; column++) {
    325           float x = (float)(column);
    326           float y = (float)(row);
    327           float scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) /
    328                         axis_len_square;
    329           if (scale < 0) {
    330             if (!m_info.fillColor.GetShading()->m_isExtendedBegin) {
    331               continue;
    332             }
    333             scale = 0;
    334           } else if (scale > 1.0f) {
    335             if (!m_info.fillColor.GetShading()->m_isExtendedEnd) {
    336               continue;
    337             }
    338             scale = 1.0f;
    339           }
    340           int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1));
    341           dib_buf[column] = m_info.fillColor.GetShading()->m_argbArray[index];
    342         }
    343       }
    344       result = true;
    345       break;
    346     }
    347     case FX_SHADING_Radial: {
    348       float start_r = m_info.fillColor.GetShading()->m_beginRadius;
    349       float end_r = m_info.fillColor.GetShading()->m_endRadius;
    350       float a = ((start_x - end_x) * (start_x - end_x)) +
    351                 ((start_y - end_y) * (start_y - end_y)) -
    352                 ((start_r - end_r) * (start_r - end_r));
    353       for (int32_t row = 0; row < height; row++) {
    354         uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch);
    355         for (int32_t column = 0; column < width; column++) {
    356           float x = (float)(column);
    357           float y = (float)(row);
    358           float b = -2 * (((x - start_x) * (end_x - start_x)) +
    359                           ((y - start_y) * (end_y - start_y)) +
    360                           (start_r * (end_r - start_r)));
    361           float c = ((x - start_x) * (x - start_x)) +
    362                     ((y - start_y) * (y - start_y)) - (start_r * start_r);
    363           float s;
    364           if (a == 0) {
    365             s = -c / b;
    366           } else {
    367             float b2_4ac = (b * b) - 4 * (a * c);
    368             if (b2_4ac < 0) {
    369               continue;
    370             }
    371             float root = (sqrt(b2_4ac));
    372             float s1, s2;
    373             if (a > 0) {
    374               s1 = (-b - root) / (2 * a);
    375               s2 = (-b + root) / (2 * a);
    376             } else {
    377               s2 = (-b - root) / (2 * a);
    378               s1 = (-b + root) / (2 * a);
    379             }
    380             if (s2 <= 1.0f || m_info.fillColor.GetShading()->m_isExtendedEnd) {
    381               s = (s2);
    382             } else {
    383               s = (s1);
    384             }
    385             if ((start_r) + s * (end_r - start_r) < 0) {
    386               continue;
    387             }
    388           }
    389           if (s < 0) {
    390             if (!m_info.fillColor.GetShading()->m_isExtendedBegin) {
    391               continue;
    392             }
    393             s = 0;
    394           }
    395           if (s > 1.0f) {
    396             if (!m_info.fillColor.GetShading()->m_isExtendedEnd) {
    397               continue;
    398             }
    399             s = 1.0f;
    400           }
    401           int index = (int32_t)(s * (FX_SHADING_Steps - 1));
    402           dib_buf[column] = m_info.fillColor.GetShading()->m_argbArray[index];
    403         }
    404       }
    405       result = true;
    406       break;
    407     }
    408     default: {
    409       result = false;
    410       break;
    411     }
    412   }
    413   if (result) {
    414     CFX_RenderDevice::StateRestorer restorer(m_renderDevice);
    415     m_renderDevice->SetClip_PathFill(path->GetPathData(), &matrix, fillMode);
    416     SetDIBitsWithMatrix(bmp, matrix);
    417   }
    418 }
    419 
    420 void CXFA_Graphics::SetDIBitsWithMatrix(const RetainPtr<CFX_DIBSource>& source,
    421                                         const CFX_Matrix& matrix) {
    422   if (matrix.IsIdentity()) {
    423     m_renderDevice->SetDIBits(source, 0, 0);
    424   } else {
    425     CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0,
    426                  0);
    427     m.Concat(matrix);
    428     int32_t left;
    429     int32_t top;
    430     RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true);
    431     RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(&m, &left, &top);
    432     m_renderDevice->SetDIBits(bmp2, left, top);
    433   }
    434 }
    435 
    436 CXFA_Graphics::TInfo::TInfo()
    437     : isActOnDash(false), strokeColor(nullptr), fillColor(nullptr) {}
    438 
    439 CXFA_Graphics::TInfo::TInfo(const TInfo& info)
    440     : graphState(info.graphState),
    441       CTM(info.CTM),
    442       isActOnDash(info.isActOnDash),
    443       strokeColor(info.strokeColor),
    444       fillColor(info.fillColor) {}
    445 
    446 CXFA_Graphics::TInfo& CXFA_Graphics::TInfo::operator=(const TInfo& other) {
    447   graphState.Copy(other.graphState);
    448   CTM = other.CTM;
    449   isActOnDash = other.isActOnDash;
    450   strokeColor = other.strokeColor;
    451   fillColor = other.fillColor;
    452   return *this;
    453 }
    454