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