diff options
Diffstat (limited to 'xfa/fxgraphics/cxfa_graphics.cpp')
-rw-r--r-- | xfa/fxgraphics/cxfa_graphics.cpp | 536 |
1 files changed, 536 insertions, 0 deletions
diff --git a/xfa/fxgraphics/cxfa_graphics.cpp b/xfa/fxgraphics/cxfa_graphics.cpp new file mode 100644 index 0000000000..8648abb81d --- /dev/null +++ b/xfa/fxgraphics/cxfa_graphics.cpp @@ -0,0 +1,536 @@ +// Copyright 2014 PDFium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com + +#include "xfa/fxgraphics/cxfa_graphics.h" + +#include <memory> + +#include "core/fxge/cfx_defaultrenderdevice.h" +#include "core/fxge/cfx_renderdevice.h" +#include "core/fxge/cfx_unicodeencoding.h" +#include "third_party/base/ptr_util.h" +#include "xfa/fxgraphics/cxfa_color.h" +#include "xfa/fxgraphics/cxfa_path.h" +#include "xfa/fxgraphics/cxfa_pattern.h" +#include "xfa/fxgraphics/cxfa_shading.h" + +namespace { + +enum { + FX_CONTEXT_None = 0, + FX_CONTEXT_Device, +}; + +#define FX_HATCHSTYLE_Total 53 + +struct FX_HATCHDATA { + int32_t width; + int32_t height; + uint8_t maskBits[64]; +}; + +const FX_HATCHDATA hatchBitmapData[FX_HATCHSTYLE_Total] = { + {16, // Horizontal + 16, + { + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }}, + {16, // Vertical + 16, + { + 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, + 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, + 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, + 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, + 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, + }}, + {16, // ForwardDiagonal + 16, + { + 0x80, 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, + 0x00, 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x80, + 0x80, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, + 0x10, 0x10, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x04, 0x04, 0x00, + 0x00, 0x02, 0x02, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, + }}, + {16, // BackwardDiagonal + 16, + { + 0x01, 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, + 0x00, 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, + 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, + 0x08, 0x08, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x20, 0x20, 0x00, + 0x00, 0x40, 0x40, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, + }}, + {16, // Cross + 16, + { + 0xff, 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, + 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0xff, + 0xff, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, + 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, + 0x00, 0x80, 0x80, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, + }}, + {16, // DiagonalCross + 16, + { + 0x81, 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, + 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, + 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, 0x81, + 0x81, 0x00, 0x00, 0x42, 0x42, 0x00, 0x00, 0x24, 0x24, 0x00, 0x00, + 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x24, 0x24, 0x00, + 0x00, 0x42, 0x42, 0x00, 0x00, 0x81, 0x81, 0x00, 0x00, + }}, +}; + +} // namespace + +CXFA_Graphics::CXFA_Graphics(CFX_RenderDevice* renderDevice) + : m_type(FX_CONTEXT_None), m_renderDevice(renderDevice) { + if (!renderDevice) + return; + m_type = FX_CONTEXT_Device; +} + +CXFA_Graphics::~CXFA_Graphics() {} + +void CXFA_Graphics::SaveGraphState() { + if (m_type != FX_CONTEXT_Device || !m_renderDevice) + return; + + m_renderDevice->SaveState(); + m_infoStack.push_back(pdfium::MakeUnique<TInfo>(m_info)); +} + +void CXFA_Graphics::RestoreGraphState() { + if (m_type != FX_CONTEXT_Device || !m_renderDevice) + return; + + m_renderDevice->RestoreState(false); + if (m_infoStack.empty() || !m_infoStack.back()) + return; + + m_info = *m_infoStack.back(); + m_infoStack.pop_back(); + return; +} + +void CXFA_Graphics::SetLineCap(CFX_GraphStateData::LineCap lineCap) { + if (m_type == FX_CONTEXT_Device && m_renderDevice) { + m_info.graphState.m_LineCap = lineCap; + } +} + +void CXFA_Graphics::SetLineDash(float dashPhase, + float* dashArray, + int32_t dashCount) { + if (dashCount > 0 && !dashArray) + return; + + dashCount = dashCount < 0 ? 0 : dashCount; + if (m_type == FX_CONTEXT_Device && m_renderDevice) { + float scale = 1.0; + if (m_info.isActOnDash) { + scale = m_info.graphState.m_LineWidth; + } + m_info.graphState.m_DashPhase = dashPhase; + m_info.graphState.SetDashCount(dashCount); + for (int32_t i = 0; i < dashCount; i++) { + m_info.graphState.m_DashArray[i] = dashArray[i] * scale; + } + } +} + +void CXFA_Graphics::SetLineDash(FX_DashStyle dashStyle) { + if (m_type == FX_CONTEXT_Device && m_renderDevice) + RenderDeviceSetLineDash(dashStyle); +} + +void CXFA_Graphics::SetLineWidth(float lineWidth, bool isActOnDash) { + if (m_type == FX_CONTEXT_Device && m_renderDevice) { + m_info.graphState.m_LineWidth = lineWidth; + m_info.isActOnDash = isActOnDash; + } +} + +void CXFA_Graphics::SetStrokeColor(CXFA_Color* color) { + if (!color) + return; + if (m_type == FX_CONTEXT_Device && m_renderDevice) { + m_info.strokeColor = color; + } +} + +void CXFA_Graphics::SetFillColor(CXFA_Color* color) { + if (!color) + return; + if (m_type == FX_CONTEXT_Device && m_renderDevice) { + m_info.fillColor = color; + } +} + +void CXFA_Graphics::StrokePath(CXFA_Path* path, CFX_Matrix* matrix) { + if (!path) + return; + if (m_type == FX_CONTEXT_Device && m_renderDevice) + RenderDeviceStrokePath(path, matrix); +} + +void CXFA_Graphics::FillPath(CXFA_Path* path, + FX_FillMode fillMode, + CFX_Matrix* matrix) { + if (!path) + return; + if (m_type == FX_CONTEXT_Device && m_renderDevice) + RenderDeviceFillPath(path, fillMode, matrix); +} + +void CXFA_Graphics::StretchImage(const CFX_RetainPtr<CFX_DIBSource>& source, + const CFX_RectF& rect, + CFX_Matrix* matrix) { + if (!source) + return; + if (m_type == FX_CONTEXT_Device && m_renderDevice) + RenderDeviceStretchImage(source, rect, matrix); +} + +void CXFA_Graphics::ConcatMatrix(const CFX_Matrix* matrix) { + if (!matrix) + return; + if (m_type == FX_CONTEXT_Device && m_renderDevice) { + m_info.CTM.Concat(*matrix); + } +} + +CFX_Matrix* CXFA_Graphics::GetMatrix() { + if (m_type == FX_CONTEXT_Device && m_renderDevice) + return &m_info.CTM; + return nullptr; +} + +CFX_RectF CXFA_Graphics::GetClipRect() const { + if (m_type != FX_CONTEXT_Device || !m_renderDevice) + return CFX_RectF(); + + FX_RECT r = m_renderDevice->GetClipBox(); + return CFX_Rect(r.left, r.top, r.Width(), r.Height()).As<float>(); +} + +void CXFA_Graphics::SetClipRect(const CFX_RectF& rect) { + if (m_type == FX_CONTEXT_Device && m_renderDevice) { + m_renderDevice->SetClip_Rect( + FX_RECT(FXSYS_round(rect.left), FXSYS_round(rect.top), + FXSYS_round(rect.right()), FXSYS_round(rect.bottom()))); + } +} + +CFX_RenderDevice* CXFA_Graphics::GetRenderDevice() { + return m_renderDevice; +} + +void CXFA_Graphics::RenderDeviceSetLineDash(FX_DashStyle dashStyle) { + switch (dashStyle) { + case FX_DASHSTYLE_Solid: { + m_info.graphState.SetDashCount(0); + return; + } + case FX_DASHSTYLE_Dash: { + float dashArray[] = {3, 1}; + SetLineDash(0, dashArray, 2); + return; + } + case FX_DASHSTYLE_Dot: { + float dashArray[] = {1, 1}; + SetLineDash(0, dashArray, 2); + return; + } + case FX_DASHSTYLE_DashDot: { + float dashArray[] = {3, 1, 1, 1}; + SetLineDash(0, dashArray, 4); + return; + } + case FX_DASHSTYLE_DashDotDot: { + float dashArray[] = {4, 1, 2, 1, 2, 1}; + SetLineDash(0, dashArray, 6); + return; + } + default: + return; + } +} + +void CXFA_Graphics::RenderDeviceStrokePath(CXFA_Path* path, + CFX_Matrix* matrix) { + if (!m_info.strokeColor) + return; + CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d, + m_info.CTM.e, m_info.CTM.f); + if (matrix) { + m.Concat(*matrix); + } + switch (m_info.strokeColor->m_type) { + case FX_COLOR_Solid: { + m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, 0x0, + m_info.strokeColor->m_info.argb, 0); + return; + } + default: + return; + } +} + +void CXFA_Graphics::RenderDeviceFillPath(CXFA_Path* path, + FX_FillMode fillMode, + CFX_Matrix* matrix) { + if (!m_info.fillColor) + return; + CFX_Matrix m(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d, + m_info.CTM.e, m_info.CTM.f); + if (matrix) { + m.Concat(*matrix); + } + switch (m_info.fillColor->m_type) { + case FX_COLOR_Solid: { + m_renderDevice->DrawPath(path->GetPathData(), &m, &m_info.graphState, + m_info.fillColor->m_info.argb, 0x0, fillMode); + return; + } + case FX_COLOR_Pattern: + FillPathWithPattern(path, fillMode, &m); + return; + case FX_COLOR_Shading: + FillPathWithShading(path, fillMode, &m); + return; + default: + return; + } +} + +void CXFA_Graphics::RenderDeviceStretchImage( + const CFX_RetainPtr<CFX_DIBSource>& source, + const CFX_RectF& rect, + CFX_Matrix* matrix) { + CFX_Matrix m1(m_info.CTM.a, m_info.CTM.b, m_info.CTM.c, m_info.CTM.d, + m_info.CTM.e, m_info.CTM.f); + if (matrix) { + m1.Concat(*matrix); + } + CFX_RetainPtr<CFX_DIBitmap> bmp1 = + source->StretchTo(static_cast<int32_t>(rect.Width()), + static_cast<int32_t>(rect.Height()), 0, nullptr); + CFX_Matrix m2(rect.Width(), 0.0, 0.0, rect.Height(), rect.left, rect.top); + m2.Concat(m1); + + int32_t left; + int32_t top; + CFX_RetainPtr<CFX_DIBitmap> bmp2 = bmp1->FlipImage(false, true); + CFX_RetainPtr<CFX_DIBitmap> bmp3 = bmp2->TransformTo(&m2, &left, &top); + CFX_RectF r = GetClipRect(); + CFX_RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap(); + bitmap->CompositeBitmap(FXSYS_round(r.left), FXSYS_round(r.top), + FXSYS_round(r.Width()), FXSYS_round(r.Height()), bmp3, + FXSYS_round(r.left - left), FXSYS_round(r.top - top)); +} + +void CXFA_Graphics::FillPathWithPattern(CXFA_Path* path, + FX_FillMode fillMode, + CFX_Matrix* matrix) { + CXFA_Pattern* pattern = m_info.fillColor->m_info.pattern; + CFX_RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap(); + int32_t width = bitmap->GetWidth(); + int32_t height = bitmap->GetHeight(); + auto bmp = pdfium::MakeRetain<CFX_DIBitmap>(); + bmp->Create(width, height, FXDIB_Argb); + m_renderDevice->GetDIBits(bmp, 0, 0); + + FX_HatchStyle hatchStyle = m_info.fillColor->m_info.pattern->m_hatchStyle; + const FX_HATCHDATA& data = hatchBitmapData[static_cast<int>(hatchStyle)]; + + auto mask = pdfium::MakeRetain<CFX_DIBitmap>(); + mask->Create(data.width, data.height, FXDIB_1bppMask); + memcpy(mask->GetBuffer(), data.maskBits, mask->GetPitch() * data.height); + CFX_FloatRect rectf = path->GetPathData()->GetBoundingBox(); + if (matrix) + matrix->TransformRect(rectf); + + FX_RECT rect(FXSYS_round(rectf.left), FXSYS_round(rectf.top), + FXSYS_round(rectf.right), FXSYS_round(rectf.bottom)); + CFX_DefaultRenderDevice device; + device.Attach(bmp, false, nullptr, false); + device.FillRect(&rect, m_info.fillColor->m_info.pattern->m_backArgb); + for (int32_t j = rect.bottom; j < rect.top; j += mask->GetHeight()) { + for (int32_t i = rect.left; i < rect.right; i += mask->GetWidth()) { + device.SetBitMask(mask, i, j, + m_info.fillColor->m_info.pattern->m_foreArgb); + } + } + CFX_RenderDevice::StateRestorer restorer(m_renderDevice); + m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode); + SetDIBitsWithMatrix(bmp, &pattern->m_matrix); +} + +void CXFA_Graphics::FillPathWithShading(CXFA_Path* path, + FX_FillMode fillMode, + CFX_Matrix* matrix) { + CFX_RetainPtr<CFX_DIBitmap> bitmap = m_renderDevice->GetBitmap(); + int32_t width = bitmap->GetWidth(); + int32_t height = bitmap->GetHeight(); + float start_x = m_info.fillColor->m_shading->m_beginPoint.x; + float start_y = m_info.fillColor->m_shading->m_beginPoint.y; + float end_x = m_info.fillColor->m_shading->m_endPoint.x; + float end_y = m_info.fillColor->m_shading->m_endPoint.y; + auto bmp = pdfium::MakeRetain<CFX_DIBitmap>(); + bmp->Create(width, height, FXDIB_Argb); + m_renderDevice->GetDIBits(bmp, 0, 0); + int32_t pitch = bmp->GetPitch(); + bool result = false; + switch (m_info.fillColor->m_shading->m_type) { + case FX_SHADING_Axial: { + float x_span = end_x - start_x; + float y_span = end_y - start_y; + float axis_len_square = (x_span * x_span) + (y_span * y_span); + for (int32_t row = 0; row < height; row++) { + uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch); + for (int32_t column = 0; column < width; column++) { + float x = (float)(column); + float y = (float)(row); + float scale = (((x - start_x) * x_span) + ((y - start_y) * y_span)) / + axis_len_square; + if (scale < 0) { + if (!m_info.fillColor->m_shading->m_isExtendedBegin) { + continue; + } + scale = 0; + } else if (scale > 1.0f) { + if (!m_info.fillColor->m_shading->m_isExtendedEnd) { + continue; + } + scale = 1.0f; + } + int32_t index = (int32_t)(scale * (FX_SHADING_Steps - 1)); + dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index]; + } + } + result = true; + break; + } + case FX_SHADING_Radial: { + float start_r = m_info.fillColor->m_shading->m_beginRadius; + float end_r = m_info.fillColor->m_shading->m_endRadius; + float a = ((start_x - end_x) * (start_x - end_x)) + + ((start_y - end_y) * (start_y - end_y)) - + ((start_r - end_r) * (start_r - end_r)); + for (int32_t row = 0; row < height; row++) { + uint32_t* dib_buf = (uint32_t*)(bmp->GetBuffer() + row * pitch); + for (int32_t column = 0; column < width; column++) { + float x = (float)(column); + float y = (float)(row); + float b = -2 * (((x - start_x) * (end_x - start_x)) + + ((y - start_y) * (end_y - start_y)) + + (start_r * (end_r - start_r))); + float c = ((x - start_x) * (x - start_x)) + + ((y - start_y) * (y - start_y)) - (start_r * start_r); + float s; + if (a == 0) { + s = -c / b; + } else { + float b2_4ac = (b * b) - 4 * (a * c); + if (b2_4ac < 0) { + continue; + } + float root = (sqrt(b2_4ac)); + float s1, s2; + if (a > 0) { + s1 = (-b - root) / (2 * a); + s2 = (-b + root) / (2 * a); + } else { + s2 = (-b - root) / (2 * a); + s1 = (-b + root) / (2 * a); + } + if (s2 <= 1.0f || m_info.fillColor->m_shading->m_isExtendedEnd) { + s = (s2); + } else { + s = (s1); + } + if ((start_r) + s * (end_r - start_r) < 0) { + continue; + } + } + if (s < 0) { + if (!m_info.fillColor->m_shading->m_isExtendedBegin) { + continue; + } + s = 0; + } + if (s > 1.0f) { + if (!m_info.fillColor->m_shading->m_isExtendedEnd) { + continue; + } + s = 1.0f; + } + int index = (int32_t)(s * (FX_SHADING_Steps - 1)); + dib_buf[column] = m_info.fillColor->m_shading->m_argbArray[index]; + } + } + result = true; + break; + } + default: { + result = false; + break; + } + } + if (result) { + CFX_RenderDevice::StateRestorer restorer(m_renderDevice); + m_renderDevice->SetClip_PathFill(path->GetPathData(), matrix, fillMode); + SetDIBitsWithMatrix(bmp, matrix); + } +} + +void CXFA_Graphics::SetDIBitsWithMatrix( + const CFX_RetainPtr<CFX_DIBSource>& source, + CFX_Matrix* matrix) { + if (matrix->IsIdentity()) { + m_renderDevice->SetDIBits(source, 0, 0); + } else { + CFX_Matrix m((float)source->GetWidth(), 0, 0, (float)source->GetHeight(), 0, + 0); + m.Concat(*matrix); + int32_t left; + int32_t top; + CFX_RetainPtr<CFX_DIBitmap> bmp1 = source->FlipImage(false, true); + CFX_RetainPtr<CFX_DIBitmap> bmp2 = bmp1->TransformTo(&m, &left, &top); + m_renderDevice->SetDIBits(bmp2, left, top); + } +} + +CXFA_Graphics::TInfo::TInfo() + : isActOnDash(false), strokeColor(nullptr), fillColor(nullptr) {} + +CXFA_Graphics::TInfo::TInfo(const TInfo& info) + : graphState(info.graphState), + CTM(info.CTM), + isActOnDash(info.isActOnDash), + strokeColor(info.strokeColor), + fillColor(info.fillColor) {} + +CXFA_Graphics::TInfo& CXFA_Graphics::TInfo::operator=(const TInfo& other) { + graphState.Copy(other.graphState); + CTM = other.CTM; + isActOnDash = other.isActOnDash; + strokeColor = other.strokeColor; + fillColor = other.fillColor; + return *this; +} |