summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/fpdfapi/fpdf_page/fpdf_page_func.cpp86
-rw-r--r--core/fpdfapi/fpdf_page/pageint.h28
-rw-r--r--core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp4
-rw-r--r--core/fxge/include/fx_ge.h5
-rw-r--r--core/fxge/skia/fx_skia_device.cpp301
-rw-r--r--core/fxge/skia/fx_skia_device.h5
-rw-r--r--core/fxge/skia/fx_skia_device_unittest.cpp31
-rw-r--r--pdfium.gyp11
8 files changed, 385 insertions, 86 deletions
diff --git a/core/fpdfapi/fpdf_page/fpdf_page_func.cpp b/core/fpdfapi/fpdf_page/fpdf_page_func.cpp
index b00ab73af1..31faf6a2d7 100644
--- a/core/fpdfapi/fpdf_page/fpdf_page_func.cpp
+++ b/core/fpdfapi/fpdf_page/fpdf_page_func.cpp
@@ -491,28 +491,40 @@ static uint32_t _GetBits32(const uint8_t* pData, int bitpos, int nbits) {
}
return result;
}
-typedef struct {
- FX_FLOAT encode_max, encode_min;
- int sizes;
-} SampleEncodeInfo;
-typedef struct { FX_FLOAT decode_max, decode_min; } SampleDecodeInfo;
-class CPDF_SampledFunc : public CPDF_Function {
+class CPDF_PSFunc : public CPDF_Function {
public:
- CPDF_SampledFunc();
- ~CPDF_SampledFunc() override;
-
+ CPDF_PSFunc() : CPDF_Function(Type::kType4PostScript) {}
// CPDF_Function
FX_BOOL v_Init(CPDF_Object* pObj) override;
FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const override;
- SampleEncodeInfo* m_pEncodeInfo;
- SampleDecodeInfo* m_pDecodeInfo;
- uint32_t m_nBitsPerSample;
- uint32_t m_SampleMax;
- CPDF_StreamAcc* m_pSampleStream;
+ private:
+ CPDF_PSEngine m_PS;
};
+FX_BOOL CPDF_PSFunc::v_Init(CPDF_Object* pObj) {
+ CPDF_StreamAcc acc;
+ acc.LoadAllData(pObj->AsStream(), FALSE);
+ return m_PS.Parse(reinterpret_cast<const FX_CHAR*>(acc.GetData()),
+ acc.GetSize());
+}
+
+FX_BOOL CPDF_PSFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const {
+ CPDF_PSEngine& PS = const_cast<CPDF_PSEngine&>(m_PS);
+ PS.Reset();
+ for (uint32_t i = 0; i < m_nInputs; i++)
+ PS.Push(inputs[i]);
+ PS.Execute();
+ if (PS.GetStackSize() < m_nOutputs)
+ return FALSE;
+ for (uint32_t i = 0; i < m_nOutputs; i++)
+ results[m_nOutputs - i - 1] = PS.Pop();
+ return TRUE;
+}
+
+} // namespace
+
CPDF_SampledFunc::CPDF_SampledFunc() : CPDF_Function(Type::kType0Sampled) {
m_pSampleStream = NULL;
m_pEncodeInfo = NULL;
@@ -524,6 +536,7 @@ CPDF_SampledFunc::~CPDF_SampledFunc() {
FX_Free(m_pEncodeInfo);
FX_Free(m_pDecodeInfo);
}
+
FX_BOOL CPDF_SampledFunc::v_Init(CPDF_Object* pObj) {
CPDF_Stream* pStream = pObj->AsStream();
if (!pStream)
@@ -579,13 +592,14 @@ FX_BOOL CPDF_SampledFunc::v_Init(CPDF_Object* pObj) {
}
return TRUE;
}
+
FX_BOOL CPDF_SampledFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const {
int pos = 0;
CFX_FixedBufGrow<FX_FLOAT, 16> encoded_input_buf(m_nInputs);
FX_FLOAT* encoded_input = encoded_input_buf;
- CFX_FixedBufGrow<int, 32> int_buf(m_nInputs * 2);
- int* index = int_buf;
- int* blocksize = index + m_nInputs;
+ CFX_FixedBufGrow<uint32_t, 32> int_buf(m_nInputs * 2);
+ uint32_t* index = int_buf;
+ uint32_t* blocksize = index + m_nInputs;
for (uint32_t i = 0; i < m_nInputs; i++) {
if (i == 0)
blocksize[i] = 1;
@@ -594,11 +608,8 @@ FX_BOOL CPDF_SampledFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const {
encoded_input[i] = PDF_Interpolate(
inputs[i], m_pDomains[i * 2], m_pDomains[i * 2 + 1],
m_pEncodeInfo[i].encode_min, m_pEncodeInfo[i].encode_max);
- index[i] = (int)encoded_input[i];
- if (index[i] < 0)
- index[i] = 0;
- else if (index[i] > m_pEncodeInfo[i].sizes - 1)
- index[i] = m_pEncodeInfo[i].sizes - 1;
+ index[i] = std::min((uint32_t)std::max(0.f, encoded_input[i]),
+ m_pEncodeInfo[i].sizes - 1);
pos += index[i] * blocksize[i];
}
FX_SAFE_INT32 bits_to_output = m_nOutputs;
@@ -650,37 +661,6 @@ FX_BOOL CPDF_SampledFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const {
return TRUE;
}
-class CPDF_PSFunc : public CPDF_Function {
- public:
- // CPDF_Function
- CPDF_PSFunc() : CPDF_Function(Type::kType4PostScript) {}
- FX_BOOL v_Init(CPDF_Object* pObj) override;
- FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const override;
-
- CPDF_PSEngine m_PS;
-};
-
-FX_BOOL CPDF_PSFunc::v_Init(CPDF_Object* pObj) {
- CPDF_Stream* pStream = pObj->AsStream();
- CPDF_StreamAcc acc;
- acc.LoadAllData(pStream, FALSE);
- return m_PS.Parse((const FX_CHAR*)acc.GetData(), acc.GetSize());
-}
-FX_BOOL CPDF_PSFunc::v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const {
- CPDF_PSEngine& PS = (CPDF_PSEngine&)m_PS;
- PS.Reset();
- for (uint32_t i = 0; i < m_nInputs; i++)
- PS.Push(inputs[i]);
- PS.Execute();
- if (PS.GetStackSize() < m_nOutputs)
- return FALSE;
- for (uint32_t i = 0; i < m_nOutputs; i++)
- results[m_nOutputs - i - 1] = PS.Pop();
- return TRUE;
-}
-
-} // namespace
-
CPDF_ExpIntFunc::CPDF_ExpIntFunc()
: CPDF_Function(Type::kType2ExpotentialInterpolation) {
m_pBeginValues = NULL;
diff --git a/core/fpdfapi/fpdf_page/pageint.h b/core/fpdfapi/fpdf_page/pageint.h
index e6496cfd20..98a05d5495 100644
--- a/core/fpdfapi/fpdf_page/pageint.h
+++ b/core/fpdfapi/fpdf_page/pageint.h
@@ -393,6 +393,7 @@ class CPDF_Function {
uint32_t CountInputs() const { return m_nInputs; }
uint32_t CountOutputs() const { return m_nOutputs; }
FX_FLOAT GetDomain(int i) const { return m_pDomains[i]; }
+ FX_FLOAT GetRange(int i) const { return m_pRanges[i]; }
Type GetType() const { return m_Type; }
protected:
@@ -423,6 +424,33 @@ class CPDF_ExpIntFunc : public CPDF_Function {
FX_FLOAT* m_pEndValues;
};
+class CPDF_SampledFunc : public CPDF_Function {
+ public:
+ struct SampleEncodeInfo {
+ FX_FLOAT encode_max;
+ FX_FLOAT encode_min;
+ uint32_t sizes;
+ };
+
+ struct SampleDecodeInfo {
+ FX_FLOAT decode_max;
+ FX_FLOAT decode_min;
+ };
+
+ CPDF_SampledFunc();
+ ~CPDF_SampledFunc() override;
+
+ // CPDF_Function
+ FX_BOOL v_Init(CPDF_Object* pObj) override;
+ FX_BOOL v_Call(FX_FLOAT* inputs, FX_FLOAT* results) const override;
+
+ SampleEncodeInfo* m_pEncodeInfo;
+ SampleDecodeInfo* m_pDecodeInfo;
+ uint32_t m_nBitsPerSample;
+ uint32_t m_SampleMax;
+ CPDF_StreamAcc* m_pSampleStream;
+};
+
class CPDF_StitchFunc : public CPDF_Function {
public:
CPDF_StitchFunc();
diff --git a/core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp b/core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp
index 3f98279dbf..48f9f5d98a 100644
--- a/core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp
+++ b/core/fpdfapi/fpdf_render/fpdf_render_pattern.cpp
@@ -855,8 +855,8 @@ void CPDF_RenderStatus::DrawShading(CPDF_ShadingPattern* pPattern,
clip_rect.Intersect(rect.GetOutterRect());
}
if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SHADING &&
- m_pDevice->GetDeviceDriver()->DrawShading(pPattern, pMatrix, alpha,
- bAlphaMode)) {
+ m_pDevice->GetDeviceDriver()->DrawShading(pPattern, pMatrix, clip_rect,
+ alpha, bAlphaMode)) {
return;
}
CPDF_DeviceBuffer buffer;
diff --git a/core/fxge/include/fx_ge.h b/core/fxge/include/fx_ge.h
index 4530677119..2a3a0253a3 100644
--- a/core/fxge/include/fx_ge.h
+++ b/core/fxge/include/fx_ge.h
@@ -603,8 +603,9 @@ class IFX_RenderDeviceDriver {
virtual int GetDriverType() const { return 0; }
virtual void ClearDriver() {}
- virtual FX_BOOL DrawShading(CPDF_ShadingPattern* pPattern,
- CFX_Matrix* pMatrix,
+ virtual FX_BOOL DrawShading(const CPDF_ShadingPattern* pPattern,
+ const CFX_Matrix* pMatrix,
+ const FX_RECT& clip_rect,
int alpha,
FX_BOOL bAlphaMode) {
return false;
diff --git a/core/fxge/skia/fx_skia_device.cpp b/core/fxge/skia/fx_skia_device.cpp
index ba671c2511..e617630bdc 100644
--- a/core/fxge/skia/fx_skia_device.cpp
+++ b/core/fxge/skia/fx_skia_device.cpp
@@ -11,6 +11,7 @@
#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/fpdfapi/fpdf_parser/include/cpdf_stream_acc.h"
#include "core/fxge/skia/fx_skia_device.h"
#include "third_party/skia/include/core/SkCanvas.h"
@@ -177,6 +178,90 @@ bool AddColors(const CPDF_Function* pFunc, SkTDArray<SkColor>* skColors) {
return true;
}
+uint32_t GetBits32(const uint8_t* pData, int bitpos, int nbits) {
+ ASSERT(0 < nbits && nbits <= 32);
+ const uint8_t* dataPtr = &pData[bitpos / 8];
+ int bitShift;
+ int bitMask;
+ int dstShift;
+ int bitCount = bitpos & 0x07;
+ if (nbits < 8 && nbits + bitCount <= 8) {
+ bitShift = 8 - nbits - bitCount;
+ bitMask = (1 << nbits) - 1;
+ dstShift = 0;
+ } else {
+ bitShift = 0;
+ int bitOffset = 8 - bitCount;
+ bitMask = (1 << SkTMin(bitOffset, nbits)) - 1;
+ dstShift = nbits - bitOffset;
+ }
+ uint32_t result = (uint32_t)(*dataPtr++ >> bitShift & bitMask) << dstShift;
+ while (dstShift >= 8) {
+ dstShift -= 8;
+ result |= *dataPtr++ << dstShift;
+ }
+ if (dstShift > 0) {
+ bitShift = 8 - dstShift;
+ bitMask = (1 << dstShift) - 1;
+ result |= *dataPtr++ >> bitShift & bitMask;
+ }
+ return result;
+}
+
+uint8_t FloatToByte(FX_FLOAT f) {
+ ASSERT(0 <= f && f <= 1);
+ return (uint8_t)(f * 255.99f);
+}
+
+bool AddSamples(const CPDF_Function* pFunc,
+ SkTDArray<SkColor>* skColors,
+ SkTDArray<SkScalar>* skPos) {
+ if (pFunc->CountInputs() != 1)
+ return false;
+ if (pFunc->CountOutputs() != 3) // expect rgb
+ return false;
+ ASSERT(CPDF_Function::Type::kType0Sampled == pFunc->GetType());
+ const CPDF_SampledFunc* sampledFunc =
+ static_cast<const CPDF_SampledFunc*>(pFunc);
+ if (!sampledFunc->m_pEncodeInfo)
+ return false;
+ const CPDF_SampledFunc::SampleEncodeInfo& encodeInfo =
+ sampledFunc->m_pEncodeInfo[0];
+ if (encodeInfo.encode_min != 0)
+ return false;
+ if (encodeInfo.encode_max != encodeInfo.sizes - 1)
+ return false;
+ uint32_t sampleSize = sampledFunc->m_nBitsPerSample;
+ uint32_t sampleCount = encodeInfo.sizes;
+ if (sampleCount != 1 << sampleSize)
+ return false;
+ if (sampledFunc->m_pSampleStream->GetSize() <
+ sampleCount * 3 * sampleSize / 8) {
+ return false;
+ }
+ FX_FLOAT colorsMin[3];
+ FX_FLOAT colorsMax[3];
+ for (int i = 0; i < 3; ++i) {
+ colorsMin[i] = sampledFunc->GetRange(i * 2);
+ colorsMax[i] = sampledFunc->GetRange(i * 2 + 1);
+ }
+ const uint8_t* pSampleData = sampledFunc->m_pSampleStream->GetData();
+ for (uint32_t i = 0; i < sampleCount; ++i) {
+ FX_FLOAT floatColors[3];
+ for (uint32_t j = 0; j < 3; ++j) {
+ int sample = GetBits32(pSampleData, (i * 3 + j) * sampleSize, sampleSize);
+ FX_FLOAT interp = (FX_FLOAT)sample / (sampleCount - 1);
+ floatColors[j] = colorsMin[j] + (colorsMax[j] - colorsMin[j]) * interp;
+ }
+ SkColor color =
+ SkPackARGB32(0xFF, FloatToByte(floatColors[0]),
+ FloatToByte(floatColors[1]), FloatToByte(floatColors[2]));
+ skColors->push(color);
+ skPos->push((FX_FLOAT)i / (sampleCount - 1));
+ }
+ return true;
+}
+
bool AddStitching(const CPDF_Function* pFunc,
SkTDArray<SkColor>* skColors,
SkTDArray<SkScalar>* skPos) {
@@ -304,6 +389,99 @@ void RgbByteOrderTransferBitmap(CFX_DIBitmap* pBitmap,
}
}
+// see https://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line
+SkScalar LineSide(const SkPoint line[2], const SkPoint& pt) {
+ return (line[1].fY - line[0].fY) * pt.fX - (line[1].fX - line[0].fX) * pt.fY +
+ line[1].fX * line[0].fY - line[1].fY * line[0].fX;
+}
+
+SkPoint IntersectSides(const SkPoint& parallelPt,
+ const SkVector& paraRay,
+ const SkPoint& perpendicularPt) {
+ SkVector perpRay = {paraRay.fY, -paraRay.fX};
+ SkScalar denom = perpRay.fY * paraRay.fX - paraRay.fY * perpRay.fX;
+ if (!denom) {
+ SkPoint zeroPt = {0, 0};
+ return zeroPt;
+ }
+ SkVector ab0 = parallelPt - perpendicularPt;
+ SkScalar numerA = ab0.fY * perpRay.fX - perpRay.fY * ab0.fX;
+ numerA /= denom;
+ SkPoint result = {parallelPt.fX + paraRay.fX * numerA,
+ parallelPt.fY + paraRay.fY * numerA};
+ return result;
+}
+
+void ClipAngledGradient(const SkPoint pts[2],
+ SkPoint rectPts[4],
+ bool clipStart,
+ bool clipEnd,
+ SkPath* clip) {
+ // find the corners furthest from the gradient perpendiculars
+ SkScalar minPerpDist = SK_ScalarMax;
+ SkScalar maxPerpDist = SK_ScalarMin;
+ int minPerpPtIndex = -1;
+ int maxPerpPtIndex = -1;
+ SkVector slope = pts[1] - pts[0];
+ SkPoint startPerp[2] = {pts[0], {pts[0].fX + slope.fY, pts[0].fY - slope.fX}};
+ SkPoint endPerp[2] = {pts[1], {pts[1].fX + slope.fY, pts[1].fY - slope.fX}};
+ for (int i = 0; i < 4; ++i) {
+ SkScalar sDist = LineSide(startPerp, rectPts[i]);
+ SkScalar eDist = LineSide(endPerp, rectPts[i]);
+ if (sDist * eDist <= 0) // if the signs are different,
+ continue; // the point is inside the gradient
+ if (sDist < 0) {
+ SkScalar smaller = SkTMin(sDist, eDist);
+ if (minPerpDist > smaller) {
+ minPerpDist = smaller;
+ minPerpPtIndex = i;
+ }
+ } else {
+ SkScalar larger = SkTMax(sDist, eDist);
+ if (maxPerpDist < larger) {
+ maxPerpDist = larger;
+ maxPerpPtIndex = i;
+ }
+ }
+ }
+ if (minPerpPtIndex < 0 && maxPerpPtIndex < 0) // nothing's outside
+ return;
+ // determine if negative distances are before start or after end
+ SkPoint beforeStart = {pts[0].fX * 2 - pts[1].fX, pts[0].fY * 2 - pts[1].fY};
+ bool beforeNeg = LineSide(startPerp, beforeStart) < 0;
+ const SkPoint& startEdgePt =
+ clipStart ? pts[0] : beforeNeg ? rectPts[minPerpPtIndex]
+ : rectPts[maxPerpPtIndex];
+ const SkPoint& endEdgePt = clipEnd ? pts[1] : beforeNeg
+ ? rectPts[maxPerpPtIndex]
+ : rectPts[minPerpPtIndex];
+ // find the corners that bound the gradient
+ SkScalar minDist = SK_ScalarMax;
+ SkScalar maxDist = SK_ScalarMin;
+ int minBounds = -1;
+ int maxBounds = -1;
+ for (int i = 0; i < 4; ++i) {
+ SkScalar dist = LineSide(pts, rectPts[i]);
+ if (minDist > dist) {
+ minDist = dist;
+ minBounds = i;
+ }
+ if (maxDist < dist) {
+ maxDist = dist;
+ maxBounds = i;
+ }
+ }
+ ASSERT(minBounds >= 0);
+ ASSERT(maxBounds != minBounds && maxBounds >= 0);
+ // construct a clip parallel to the gradient that goes through
+ // rectPts[minBounds] and rectPts[maxBounds] and perpendicular to the
+ // gradient that goes through startEdgePt, endEdgePt.
+ clip->moveTo(IntersectSides(rectPts[minBounds], slope, startEdgePt));
+ clip->lineTo(IntersectSides(rectPts[minBounds], slope, endEdgePt));
+ clip->lineTo(IntersectSides(rectPts[maxBounds], slope, endEdgePt));
+ clip->lineTo(IntersectSides(rectPts[maxBounds], slope, startEdgePt));
+}
+
} // namespace
// convert a stroking path to scanlines
@@ -604,11 +782,17 @@ FX_BOOL CFX_SkiaDeviceDriver::FillRect(const FX_RECT* pRect,
return TRUE;
}
-FX_BOOL CFX_SkiaDeviceDriver::DrawShading(CPDF_ShadingPattern* pPattern,
- CFX_Matrix* pMatrix,
+FX_BOOL CFX_SkiaDeviceDriver::DrawShading(const CPDF_ShadingPattern* pPattern,
+ const CFX_Matrix* pMatrix,
+ const FX_RECT& clip_rect,
int alpha,
FX_BOOL bAlphaMode) {
- CPDF_Function** pFuncs = pPattern->m_pFunctions;
+ if (kAxialShading != pPattern->m_ShadingType &&
+ kRadialShading != pPattern->m_ShadingType) {
+ // TODO(caryclark) more types
+ return false;
+ }
+ CPDF_Function* const* pFuncs = pPattern->m_pFunctions;
int nFuncs = pPattern->m_nFuncs;
if (nFuncs != 1) // TODO(caryclark) remove this restriction
return false;
@@ -616,23 +800,8 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawShading(CPDF_ShadingPattern* pPattern,
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);
- }
+ // TODO(caryclark) Respect Domain[0], Domain[1]. (Don't know what they do
+ // yet.)
SkTDArray<SkColor> skColors;
SkTDArray<SkScalar> skPos;
for (int j = 0; j < nFuncs; j++) {
@@ -640,6 +809,14 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawShading(CPDF_ShadingPattern* pPattern,
if (!pFunc)
continue;
switch (pFunc->GetType()) {
+ case CPDF_Function::Type::kType0Sampled:
+ /* TODO(caryclark)
+ Type 0 Sampled Functions in PostScript can also have an Order integer
+ in the dictionary. PDFium doesn't appear to check for this anywhere.
+ */
+ if (!AddSamples(pFunc, &skColors, &skPos))
+ return false;
+ break;
case CPDF_Function::Type::kType2ExpotentialInterpolation:
if (!AddColors(pFunc, &skColors))
return false;
@@ -654,17 +831,89 @@ FX_BOOL CFX_SkiaDeviceDriver::DrawShading(CPDF_ShadingPattern* pPattern,
return false;
}
}
- SkMatrix skMatrix = ToSkMatrix(*pMatrix);
- SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
+ CPDF_Array* pArray = pDict->GetArrayBy("Extend");
+ bool clipStart = !pArray || !pArray->GetIntegerAt(0);
+ bool clipEnd = !pArray || !pArray->GetIntegerAt(1);
SkPaint paint;
paint.setAntiAlias(true);
- paint.setShader(SkGradientShader::MakeLinear(pts, skColors.begin(),
- skPos.begin(), skColors.count(),
- SkShader::kClamp_TileMode));
paint.setAlpha(alpha);
+ SkMatrix skMatrix = ToSkMatrix(*pMatrix);
+ SkRect skRect = SkRect::MakeLTRB(clip_rect.left, clip_rect.top,
+ clip_rect.right, clip_rect.bottom);
+ SkPath skClip;
+ SkPath skPath;
+ if (kAxialShading == pPattern->m_ShadingType) {
+ 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);
+ SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
+ skMatrix.mapPoints(pts, SK_ARRAY_COUNT(pts));
+ paint.setShader(SkGradientShader::MakeLinear(
+ pts, skColors.begin(), skPos.begin(), skColors.count(),
+ SkShader::kClamp_TileMode));
+ if (clipStart || clipEnd) {
+ // if the gradient is horizontal or vertical, modify the draw rectangle
+ if (pts[0].fX == pts[1].fX) { // vertical
+ if (pts[0].fY > pts[1].fY) {
+ SkTSwap(pts[0].fY, pts[1].fY);
+ SkTSwap(clipStart, clipEnd);
+ }
+ if (clipStart)
+ skRect.fTop = SkTMax(skRect.fTop, pts[0].fY);
+ if (clipEnd)
+ skRect.fBottom = SkTMin(skRect.fBottom, pts[1].fY);
+ } else if (pts[0].fY == pts[1].fY) { // horizontal
+ if (pts[0].fX > pts[1].fX) {
+ SkTSwap(pts[0].fX, pts[1].fX);
+ SkTSwap(clipStart, clipEnd);
+ }
+ if (clipStart)
+ skRect.fLeft = SkTMax(skRect.fLeft, pts[0].fX);
+ if (clipEnd)
+ skRect.fRight = SkTMin(skRect.fRight, pts[1].fX);
+ } else { // if the gradient is angled and contained by the rect, clip
+ SkPoint rectPts[4] = {{skRect.fLeft, skRect.fTop},
+ {skRect.fRight, skRect.fTop},
+ {skRect.fRight, skRect.fBottom},
+ {skRect.fLeft, skRect.fBottom}};
+ ClipAngledGradient(pts, rectPts, clipStart, clipEnd, &skClip);
+ }
+ }
+ skPath.addRect(skRect);
+ skMatrix.setIdentity();
+ } else {
+ ASSERT(kRadialShading == pPattern->m_ShadingType);
+ FX_FLOAT start_x = pCoords->GetNumberAt(0);
+ FX_FLOAT start_y = pCoords->GetNumberAt(1);
+ FX_FLOAT start_r = pCoords->GetNumberAt(2);
+ FX_FLOAT end_x = pCoords->GetNumberAt(3);
+ FX_FLOAT end_y = pCoords->GetNumberAt(4);
+ FX_FLOAT end_r = pCoords->GetNumberAt(5);
+ SkPoint pts[] = {{start_x, start_y}, {end_x, end_y}};
+
+ paint.setShader(SkGradientShader::MakeTwoPointConical(
+ pts[0], start_r, pts[1], end_r, skColors.begin(), skPos.begin(),
+ skColors.count(), SkShader::kClamp_TileMode));
+ if (clipStart || clipEnd) {
+ if (clipStart && start_r)
+ skClip.addCircle(pts[0].fX, pts[0].fY, start_r);
+ if (clipEnd)
+ skClip.addCircle(pts[1].fX, pts[1].fY, end_r, SkPath::kCCW_Direction);
+ else
+ skClip.setFillType(SkPath::kInverseWinding_FillType);
+ skClip.transform(skMatrix);
+ }
+ SkMatrix inverse;
+ skMatrix.invert(&inverse);
+ skPath.addRect(skRect);
+ skPath.transform(inverse);
+ }
m_pCanvas->save();
+ if (!skClip.isEmpty())
+ m_pCanvas->clipPath(skClip);
m_pCanvas->concat(skMatrix);
- m_pCanvas->drawRect(SkRect::MakeWH(1, 1), paint);
+ m_pCanvas->drawPath(skPath, paint);
m_pCanvas->restore();
return true;
}
diff --git a/core/fxge/skia/fx_skia_device.h b/core/fxge/skia/fx_skia_device.h
index 05a102489e..ef66b94ae2 100644
--- a/core/fxge/skia/fx_skia_device.h
+++ b/core/fxge/skia/fx_skia_device.h
@@ -128,8 +128,9 @@ class CFX_SkiaDeviceDriver : public IFX_RenderDeviceDriver {
int alpha_flag = 0,
void* pIccTransform = NULL) override;
- FX_BOOL DrawShading(CPDF_ShadingPattern* pPattern,
- CFX_Matrix* pMatrix,
+ FX_BOOL DrawShading(const CPDF_ShadingPattern* pPattern,
+ const CFX_Matrix* pMatrix,
+ const FX_RECT& clip_rect,
int alpha,
FX_BOOL bAlphaMode) override;
diff --git a/core/fxge/skia/fx_skia_device_unittest.cpp b/core/fxge/skia/fx_skia_device_unittest.cpp
new file mode 100644
index 0000000000..57a4ec140b
--- /dev/null
+++ b/core/fxge/skia/fx_skia_device_unittest.cpp
@@ -0,0 +1,31 @@
+// Copyright 2016 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.
+
+#include "core/fxge/skia/fx_skia_device.cpp"
+#include "testing/fx_string_testhelpers.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+uint32_t _GetBits32(const uint8_t* pData, int bitpos, int nbits) {
+ int result = 0;
+ for (int i = 0; i < nbits; i++)
+ if (pData[(bitpos + i) / 8] & (1 << (7 - (bitpos + i) % 8))) {
+ result |= 1 << (nbits - i - 1);
+ }
+ return result;
+}
+
+} // namespace
+
+TEST(fxge, GetBits32) {
+ unsigned char data[] = {0xDE, 0x3F, 0xB1, 0x7C, 0x12, 0x9A, 0x04, 0x56};
+ for (int nbits = 1; nbits <= 32; ++nbits) {
+ for (int bitpos = 0; bitpos < (int)sizeof(data) * 8 - nbits; ++bitpos) {
+ uint32_t ref = _GetBits32(data, bitpos, nbits);
+ uint32_t test = GetBits32(data, bitpos, nbits);
+ EXPECT_TRUE(ref == test);
+ }
+ }
+}
diff --git a/pdfium.gyp b/pdfium.gyp
index 17384309cb..6fe4adf1f9 100644
--- a/pdfium.gyp
+++ b/pdfium.gyp
@@ -258,7 +258,7 @@
'core/fpdfdoc/include/cpvt_wordplace.h',
'core/fpdfdoc/include/cpvt_wordprops.h',
'core/fpdfdoc/include/cpvt_wordrange.h',
- 'core/fpdfdoc/ipvt_fontmap.h',
+ 'core/fpdfdoc/include/ipvt_fontmap.h',
'core/fpdfdoc/pdf_vt.h',
'core/fpdfdoc/tagged_int.h',
],
@@ -955,6 +955,15 @@
'fpdfsdk/javascript/public_methods_unittest.cpp',
],
}],
+ ['pdf_use_skia==1', {
+ 'defines': ['PDF_ENABLE_SKIA'],
+ 'dependencies': [
+ '<(DEPTH)/skia/skia.gyp:skia',
+ ],
+ 'sources': [
+ 'core/fxge/skia/fx_skia_device_unittest.cpp',
+ ],
+ }],
],
},
{