diff options
author | caryclark <caryclark@google.com> | 2016-03-25 14:08:26 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-25 14:08:26 -0700 |
commit | 342f6fa66f6d843fe07d9b6a133656f83c8d62f6 (patch) | |
tree | a87e4ca21ea2d4444d98cd0ce28001de9925fa7a /core/fxge | |
parent | 46c8b1e3202ab5ae3663141dc25bec3b4360322e (diff) | |
download | pdfium-342f6fa66f6d843fe07d9b6a133656f83c8d62f6.tar.xz |
support gradients and stroke+fill
To draw paths that are stroked and filled with a pair of colors,
reduce the fill by the width of the stroke. This is implemented
with path ops subtracting the resolved stroke shape from the
fill shape. This permits rendering the result without requiring
an offscreen bitmap.
The implementation for stroke+fill requires a new entry into
the graphics engine, so a bit was added to device caps for that.
Extract the gradient information out of the axial gradient
function descriptions, and when possible, use Skia to draw
the linear gradient directly. This requires making the function
descriptions non-opaque, and adding a bit to device caps for
another entry into the graphics engine.
BUG=
Review URL: https://codereview.chromium.org/1828283002
Diffstat (limited to 'core/fxge')
-rw-r--r-- | core/fxge/agg/fx_agg_driver.h | 2 | ||||
-rw-r--r-- | core/fxge/apple/apple_int.h | 2 | ||||
-rw-r--r-- | core/fxge/ge/fx_ge_device.cpp | 31 | ||||
-rw-r--r-- | core/fxge/skia/fx_skia_device.cpp | 163 | ||||
-rw-r--r-- | core/fxge/skia/fx_skia_device.h | 5 | ||||
-rw-r--r-- | core/fxge/win32/win32_int.h | 4 |
6 files changed, 183 insertions, 24 deletions
diff --git a/core/fxge/agg/fx_agg_driver.h b/core/fxge/agg/fx_agg_driver.h index ea4c36059b..2b6b6683f6 100644 --- a/core/fxge/agg/fx_agg_driver.h +++ b/core/fxge/agg/fx_agg_driver.h @@ -122,7 +122,7 @@ class CFX_AggDeviceDriver : public IFX_RenderDeviceDriver { FX_DWORD color, int alpha_flag, void* pIccTransform) override; - int GetDriverType() override { return 1; } + int GetDriverType() const override { return 1; } FX_BOOL RenderRasterizer(agg::rasterizer_scanline_aa& rasterizer, FX_DWORD color, diff --git a/core/fxge/apple/apple_int.h b/core/fxge/apple/apple_int.h index 037eca767f..c4f95d6d26 100644 --- a/core/fxge/apple/apple_int.h +++ b/core/fxge/apple/apple_int.h @@ -173,7 +173,7 @@ class CFX_QuartzDeviceDriver : public IFX_RenderDeviceDriver { FX_DWORD color, int alpha_flag = 0, void* pIccTransform = NULL) override; - void* GetPlatformSurface() override { return NULL; } + void* GetPlatformSurface() const override { return NULL; } void ClearDriver() override; protected: diff --git a/core/fxge/ge/fx_ge_device.cpp b/core/fxge/ge/fx_ge_device.cpp index b8ddfec841..eefac5c033 100644 --- a/core/fxge/ge/fx_ge_device.cpp +++ b/core/fxge/ge/fx_ge_device.cpp @@ -109,6 +109,7 @@ void CFX_RenderDevice::UpdateClipBox() { m_ClipBox.right = m_Width; m_ClipBox.bottom = m_Height; } + FX_BOOL CFX_RenderDevice::DrawPath(const CFX_PathData* pPathData, const CFX_Matrix* pObject2Device, const CFX_GraphStateData* pGraphState, @@ -224,6 +225,31 @@ FX_BOOL CFX_RenderDevice::DrawPath(const CFX_PathData* pPathData, } if ((fill_mode & 3) && fill_alpha && stroke_alpha < 0xff && (fill_mode & FX_FILL_STROKE)) { + if (m_RenderCaps & FXRC_FILLSTROKE_PATH) { + return m_pDeviceDriver->DrawPath(pPathData, pObject2Device, pGraphState, + fill_color, stroke_color, fill_mode, + alpha_flag, pIccTransform, blend_type); + } + return DrawFillStrokePath(pPathData, pObject2Device, pGraphState, + fill_color, stroke_color, fill_mode, alpha_flag, + pIccTransform, blend_type); + } + return m_pDeviceDriver->DrawPath(pPathData, pObject2Device, pGraphState, + fill_color, stroke_color, fill_mode, + alpha_flag, pIccTransform, blend_type); +} + +// This can be removed once PDFium entirely relies on Skia +FX_BOOL CFX_RenderDevice::DrawFillStrokePath( + const CFX_PathData* pPathData, + const CFX_Matrix* pObject2Device, + const CFX_GraphStateData* pGraphState, + FX_DWORD fill_color, + FX_DWORD stroke_color, + int fill_mode, + int alpha_flag, + void* pIccTransform, + int blend_type) { if (!(m_RenderCaps & FXRC_GET_BITS)) { return FALSE; } @@ -272,11 +298,8 @@ FX_BOOL CFX_RenderDevice::DrawPath(const CFX_PathData* pPathData, FXSYS_round(rect.Height() * fScaleY)); return m_pDeviceDriver->SetDIBits(&bitmap, 0, &src_rect, rect.left, rect.top, FXDIB_BLEND_NORMAL); - } - return m_pDeviceDriver->DrawPath(pPathData, pObject2Device, pGraphState, - fill_color, stroke_color, fill_mode, - alpha_flag, pIccTransform, blend_type); } + FX_BOOL CFX_RenderDevice::SetPixel(int x, int y, FX_DWORD color, diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp index f0c7d4e442..ed6ff82fd0 100644 --- a/core/fxge/skia/fx_skia_device.cpp +++ b/core/fxge/skia/fx_skia_device.cpp @@ -7,6 +7,10 @@ #if defined(_SKIA_SUPPORT_) #include "core/include/fxcodec/fx_codec.h" +#include "core/fpdfapi/fpdf_page/cpdf_shadingpattern.h" +#include "core/fpdfapi/fpdf_page/pageint.h" +#include "core/fpdfapi/fpdf_parser/include/cpdf_array.h" +#include "core/fpdfapi/fpdf_parser/include/cpdf_dictionary.h" #include "core/fxge/agg/fx_agg_driver.h" #include "core/fxge/skia/fx_skia_device.h" @@ -18,6 +22,8 @@ #include "third_party/skia/include/core/SkStream.h" #include "third_party/skia/include/core/SkTypeface.h" #include "third_party/skia/include/effects/SkDashPathEffect.h" +#include "third_party/skia/include/effects/SkGradientShader.h" +#include "third_party/skia/include/pathops/SkPathOps.h" namespace { @@ -150,6 +156,53 @@ SkXfermode::Mode GetSkiaBlendMode(int blend_type) { } } +bool AddColors(const CPDF_Function* pFunc, SkTDArray<SkColor>* skColors) { + if (pFunc->CountInputs() != 1) + return false; + ASSERT(CPDF_Function::Type::kType2ExpotentialInterpolation == + pFunc->GetType()); + const CPDF_ExpIntFunc* expIntFunc = + static_cast<const CPDF_ExpIntFunc*>(pFunc); + if (expIntFunc->m_Exponent != 1) + return false; + if (expIntFunc->m_nOrigOutputs != 3) + return false; + skColors->push(SkColorSetARGB( + 0xFF, SkUnitScalarClampToByte(expIntFunc->m_pBeginValues[0]), + SkUnitScalarClampToByte(expIntFunc->m_pBeginValues[1]), + SkUnitScalarClampToByte(expIntFunc->m_pBeginValues[2]))); + skColors->push( + SkColorSetARGB(0xFF, SkUnitScalarClampToByte(expIntFunc->m_pEndValues[0]), + SkUnitScalarClampToByte(expIntFunc->m_pEndValues[1]), + SkUnitScalarClampToByte(expIntFunc->m_pEndValues[2]))); + return true; +} + +bool AddStitching(const CPDF_Function* pFunc, + SkTDArray<SkColor>* skColors, + SkTDArray<SkScalar>* skPos) { + int inputs = pFunc->CountInputs(); + ASSERT(CPDF_Function::Type::kType3Stitching == pFunc->GetType()); + const CPDF_StitchFunc* stitchFunc = + static_cast<const CPDF_StitchFunc*>(pFunc); + FX_FLOAT boundsStart = stitchFunc->GetDomain(0); + + for (int i = 0; i < inputs; ++i) { + const CPDF_Function* pSubFunc = stitchFunc->m_pSubFunctions[i]; + if (pSubFunc->GetType() != + CPDF_Function::Type::kType2ExpotentialInterpolation) + return false; + if (!AddColors(pSubFunc, skColors)) + return false; + FX_FLOAT boundsEnd = + i < inputs - 1 ? stitchFunc->m_pBounds[i] : stitchFunc->GetDomain(1); + skPos->push(boundsStart); + skPos->push(boundsEnd); + boundsStart = boundsEnd; + } + return true; +} + } // namespace // convert a stroking path to scanlines @@ -303,7 +356,8 @@ int CFX_SkiaDeviceDriver::GetDeviceCaps(int caps_id) { return 0; case FXDC_RENDER_CAPS: return FXRC_GET_BITS | FXRC_ALPHA_PATH | FXRC_ALPHA_IMAGE | - FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_ALPHA_OUTPUT; + FXRC_BLEND_MODE | FXRC_SOFT_CLIP | FXRC_ALPHA_OUTPUT | + FXRC_FILLSTROKE_PATH | FXRC_SHADING; case FXDC_DITHER_BITS: return m_ditherBits; } @@ -385,32 +439,44 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawPath( SkIRect rect; rect.set(0, 0, GetDeviceCaps(FXDC_PIXEL_WIDTH), GetDeviceCaps(FXDC_PIXEL_HEIGHT)); + SkMatrix skMatrix = ToSkMatrix(*pObject2Device); + SkPaint skPaint; + skPaint.setAntiAlias(true); + int stroke_alpha = FXGETFLAG_COLORTYPE(alpha_flag) + ? FXGETFLAG_ALPHA_STROKE(alpha_flag) + : FXARGB_A(stroke_color); + if (pGraphState && stroke_alpha) + PaintStroke(&skPaint, pGraphState, skMatrix); SkPath skPath = BuildPath(pPathData); - SkPaint spaint; - spaint.setAntiAlias(true); - spaint.setXfermodeMode(GetSkiaBlendMode(blend_type)); m_pCanvas->save(); - SkMatrix skMatrix = ToSkMatrix(*pObject2Device); m_pCanvas->concat(skMatrix); if ((fill_mode & 3) && fill_color) { skPath.setFillType((fill_mode & 3) == FXFILL_WINDING ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType); - - spaint.setStyle(SkPaint::kFill_Style); - spaint.setColor(fill_color); - m_pCanvas->drawPath(skPath, spaint); + SkPath strokePath; + const SkPath* fillPath = &skPath; + if (pGraphState && stroke_alpha) { + SkAlpha fillA = SkColorGetA(fill_color); + SkAlpha strokeA = SkColorGetA(stroke_color); + if (fillA && fillA < 0xFF && strokeA && strokeA < 0xFF) { + skPaint.getFillPath(skPath, &strokePath); + if (Op(skPath, strokePath, SkPathOp::kDifference_SkPathOp, + &strokePath)) { + fillPath = &strokePath; + } + } + } + skPaint.setStyle(SkPaint::kFill_Style); + skPaint.setColor(fill_color); + m_pCanvas->drawPath(*fillPath, skPaint); } - int stroke_alpha = FXGETFLAG_COLORTYPE(alpha_flag) - ? FXGETFLAG_ALPHA_STROKE(alpha_flag) - : FXARGB_A(stroke_color); - if (pGraphState && stroke_alpha) { - spaint.setColor(stroke_color); - PaintStroke(&spaint, pGraphState, skMatrix); DebugShowSkiaPath(skPath); DebugShowCanvasMatrix(m_pCanvas); - m_pCanvas->drawPath(skPath, spaint); + skPaint.setStyle(SkPaint::kStroke_Style); + skPaint.setColor(stroke_color); + m_pCanvas->drawPath(skPath, skPaint); } m_pCanvas->restore(); return TRUE; @@ -432,6 +498,71 @@ FX_BOOL CFX_SkiaDeviceDriver::FillRect(const FX_RECT* pRect, return TRUE; } +FX_BOOL CFX_SkiaDeviceDriver::DrawShading(CPDF_ShadingPattern* pPattern, + CFX_Matrix* pMatrix, + int alpha, + FX_BOOL bAlphaMode) { + CPDF_Function** pFuncs = pPattern->m_pFunctions; + int nFuncs = pPattern->m_nFuncs; + if (nFuncs != 1) // TODO(caryclark) remove this restriction + return false; + CPDF_Dictionary* pDict = pPattern->m_pShadingObj->GetDict(); + CPDF_Array* pCoords = pDict->GetArrayBy("Coords"); + if (!pCoords) + return true; + FX_FLOAT start_x = pCoords->GetNumberAt(0); + FX_FLOAT start_y = pCoords->GetNumberAt(1); + FX_FLOAT end_x = pCoords->GetNumberAt(2); + FX_FLOAT end_y = pCoords->GetNumberAt(3); + FX_FLOAT t_min = 0; + FX_FLOAT t_max = 1; + CPDF_Array* pArray = pDict->GetArrayBy("Domain"); + if (pArray) { + t_min = pArray->GetNumberAt(0); + t_max = pArray->GetNumberAt(1); + } + FX_BOOL bStartExtend = FALSE, bEndExtend = FALSE; + pArray = pDict->GetArrayBy("Extend"); + if (pArray) { + bStartExtend = pArray->GetIntegerAt(0); + bEndExtend = pArray->GetIntegerAt(1); + } + SkTDArray<SkColor> skColors; + SkTDArray<SkScalar> skPos; + for (int j = 0; j < nFuncs; j++) { + const CPDF_Function* pFunc = pFuncs[j]; + if (!pFunc) + continue; + switch (pFunc->GetType()) { + case CPDF_Function::Type::kType2ExpotentialInterpolation: + if (!AddColors(pFunc, &skColors)) + return false; + skPos.push(0); + skPos.push(1); + break; + case CPDF_Function::Type::kType3Stitching: + if (!AddStitching(pFunc, &skColors, &skPos)) + return false; + break; + default: + return false; + } + } + SkMatrix skMatrix = ToSkMatrix(*pMatrix); + SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}}; + SkPaint paint; + paint.setAntiAlias(true); + paint.setShader(SkGradientShader::MakeLinear(pts, skColors.begin(), + skPos.begin(), skColors.count(), + SkShader::kClamp_TileMode)); + paint.setAlpha(alpha); + m_pCanvas->save(); + m_pCanvas->concat(skMatrix); + m_pCanvas->drawRect(SkRect::MakeWH(1, 1), paint); + m_pCanvas->restore(); + return true; +} + FX_BOOL CFX_SkiaDeviceDriver::GetClipBox(FX_RECT* pRect) { // TODO(caryclark) call m_canvas->getClipDeviceBounds() instead pRect->left = 0; diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h index 012e370717..8ce7c6c61f 100644 --- a/core/fxge/skia/fx_skia_device.h +++ b/core/fxge/skia/fx_skia_device.h @@ -128,6 +128,11 @@ class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver { int alpha_flag = 0, void* pIccTransform = NULL) override; + FX_BOOL DrawShading(CPDF_ShadingPattern* pPattern, + CFX_Matrix* pMatrix, + int alpha, + FX_BOOL bAlphaMode) override; + virtual uint8_t* GetBuffer() const { return m_pAggDriver->GetBuffer(); } void PaintStroke(SkPaint* spaint, const CFX_GraphStateData* pGraphState, diff --git a/core/fxge/win32/win32_int.h b/core/fxge/win32/win32_int.h index 923aa84f33..d903d3abf5 100644 --- a/core/fxge/win32/win32_int.h +++ b/core/fxge/win32/win32_int.h @@ -145,7 +145,7 @@ class CGdiDeviceDriver : public IFX_RenderDeviceDriver { void* pIccTransform, int blend_type) override; FX_BOOL GetClipBox(FX_RECT* pRect) override; - void* GetPlatformSurface() override { return (void*)m_hDC; } + void* GetPlatformSurface() const override { return (void*)m_hDC; } virtual void* GetClipRgn(); virtual FX_BOOL SetClipRgn(void* pRgn); @@ -352,7 +352,7 @@ class CPSPrinterDriver : public IFX_RenderDeviceDriver { FX_DWORD color, int alpha_flag, void* pIccTransform) override; - void* GetPlatformSurface() override { return (void*)m_hDC; } + void* GetPlatformSurface() const override { return (void*)m_hDC; } HDC m_hDC; FX_BOOL m_bCmykOutput; |