Index: src/pdf/SkPDFShader.cpp |
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp |
index 9394f1b9597c67d25e77a99af74e2bf938fdd4c8..fa0587fc5001465dda81c2e1c99dba68dd41b2ca 100644 |
--- a/src/pdf/SkPDFShader.cpp |
+++ b/src/pdf/SkPDFShader.cpp |
@@ -24,7 +24,7 @@ |
#include "SkTSet.h" |
#include "SkTypes.h" |
-static bool transformBBox(const SkMatrix& matrix, SkRect* bbox) { |
+static bool inverseTransformBBox(const SkMatrix& matrix, SkRect* bbox) { |
SkMatrix inverse; |
if (!matrix.invert(&inverse)) { |
return false; |
@@ -780,7 +780,7 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) |
SkRect bbox; |
bbox.set(fState.get()->fBBox); |
- if (!transformBBox(finalMatrix, &bbox)) { |
+ if (!inverseTransformBBox(finalMatrix, &bbox)) { |
return; |
} |
@@ -828,34 +828,60 @@ SkPDFFunctionShader::SkPDFFunctionShader(SkPDFShader::State* state) |
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
fState.get()->fImage.lockPixels(); |
+ // The image shader pattern cell will be drawn into a separate device |
+ // in pattern cell space (no scaling on the bitmap, though there may be |
+ // translations so that all content is in the device, coordinates > 0). |
+ |
+ // Map clip bounds to shader space to ensure the device is large enough |
+ // to handle fake clamping. |
SkMatrix finalMatrix = fState.get()->fCanvasTransform; |
finalMatrix.preConcat(fState.get()->fShaderTransform); |
- SkRect surfaceBBox; |
- surfaceBBox.set(fState.get()->fBBox); |
- if (!transformBBox(finalMatrix, &surfaceBBox)) { |
+ SkRect deviceBounds; |
+ deviceBounds.set(fState.get()->fBBox); |
+ if (!inverseTransformBBox(finalMatrix, &deviceBounds)) { |
return; |
} |
+ const SkBitmap* image = &fState.get()->fImage; |
+ SkRect bitmapBounds; |
+ image->getBounds(&bitmapBounds); |
+ |
+ // For tiling modes, the bounds should be extended to include the bitmap, |
+ // otherwise the bitmap gets clipped out and the shader is empty and awful. |
+ // For clamp modes, we're only interested in the clip region, whether |
+ // or not the main bitmap is in it. |
+ SkShader::TileMode tileModes[2]; |
+ tileModes[0] = fState.get()->fImageTileModes[0]; |
+ tileModes[1] = fState.get()->fImageTileModes[1]; |
+ if (tileModes[0] != SkShader::kClamp_TileMode || |
+ tileModes[1] != SkShader::kClamp_TileMode) { |
+ deviceBounds.join(bitmapBounds); |
+ } |
+ |
SkMatrix unflip; |
- unflip.setTranslate(0, SkScalarRoundToScalar(surfaceBBox.height())); |
+ unflip.setTranslate(0, SkScalarRoundToScalar(deviceBounds.height())); |
unflip.preScale(SK_Scalar1, -SK_Scalar1); |
- SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), |
- SkScalarRound(surfaceBBox.height())); |
+ SkISize size = SkISize::Make(SkScalarRound(deviceBounds.width()), |
+ SkScalarRound(deviceBounds.height())); |
SkPDFDevice pattern(size, size, unflip); |
SkCanvas canvas(&pattern); |
- canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); |
- finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); |
- const SkBitmap* image = &fState.get()->fImage; |
- SkScalar width = SkIntToScalar(image->width()); |
- SkScalar height = SkIntToScalar(image->height()); |
- SkShader::TileMode tileModes[2]; |
- tileModes[0] = fState.get()->fImageTileModes[0]; |
- tileModes[1] = fState.get()->fImageTileModes[1]; |
+ SkRect patternBBox; |
+ image->getBounds(&patternBBox); |
+ // Translate the canvas so that the bitmap origin is at (0, 0). |
+ canvas.translate(-deviceBounds.left(), -deviceBounds.top()); |
+ patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); |
+ // Undo the translation in the final matrix |
+ finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); |
+ |
+ // If the bitmap is out of bounds (i.e. clamp mode where we only see the |
+ // stretched sides), canvas will clip this out and the extraneous data |
+ // won't be saved to the PDF. |
canvas.drawBitmap(*image, 0, 0); |
- SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, |
- width, height); |
+ |
+ SkScalar width = SkIntToScalar(image->width()); |
+ SkScalar height = SkIntToScalar(image->height()); |
// Tiling is implied. First we handle mirroring. |
if (tileModes[0] == SkShader::kMirror_TileMode) { |
@@ -889,28 +915,29 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
tileModes[1] == SkShader::kClamp_TileMode) { |
SkPaint paint; |
SkRect rect; |
- rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); |
+ rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); |
if (!rect.isEmpty()) { |
paint.setColor(image->getColor(0, 0)); |
canvas.drawRect(rect, paint); |
} |
- rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); |
+ rect = SkRect::MakeLTRB(width, deviceBounds.top(), |
+ deviceBounds.right(), 0); |
if (!rect.isEmpty()) { |
paint.setColor(image->getColor(image->width() - 1, 0)); |
canvas.drawRect(rect, paint); |
} |
- rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, |
- surfaceBBox.fBottom); |
+ rect = SkRect::MakeLTRB(width, height, |
+ deviceBounds.right(), deviceBounds.bottom()); |
if (!rect.isEmpty()) { |
paint.setColor(image->getColor(image->width() - 1, |
image->height() - 1)); |
canvas.drawRect(rect, paint); |
} |
- rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, |
- surfaceBBox.fBottom); |
+ rect = SkRect::MakeLTRB(deviceBounds.left(), height, |
+ 0, deviceBounds.bottom()); |
if (!rect.isEmpty()) { |
paint.setColor(image->getColor(0, image->height() - 1)); |
canvas.drawRect(rect, paint); |
@@ -920,13 +947,13 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
// Then expand the left, right, top, then bottom. |
if (tileModes[0] == SkShader::kClamp_TileMode) { |
SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); |
- if (surfaceBBox.fLeft < 0) { |
+ if (deviceBounds.left() < 0) { |
SkBitmap left; |
SkAssertResult(image->extractSubset(&left, subset)); |
SkMatrix leftMatrix; |
- leftMatrix.setScale(-surfaceBBox.fLeft, 1); |
- leftMatrix.postTranslate(surfaceBBox.fLeft, 0); |
+ leftMatrix.setScale(-deviceBounds.left(), 1); |
+ leftMatrix.postTranslate(deviceBounds.left(), 0); |
canvas.drawBitmapMatrix(left, leftMatrix); |
if (tileModes[1] == SkShader::kMirror_TileMode) { |
@@ -937,13 +964,13 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
patternBBox.fLeft = 0; |
} |
- if (surfaceBBox.fRight > width) { |
+ if (deviceBounds.right() > width) { |
SkBitmap right; |
subset.offset(image->width() - 1, 0); |
SkAssertResult(image->extractSubset(&right, subset)); |
SkMatrix rightMatrix; |
- rightMatrix.setScale(surfaceBBox.fRight - width, 1); |
+ rightMatrix.setScale(deviceBounds.right() - width, 1); |
rightMatrix.postTranslate(width, 0); |
canvas.drawBitmapMatrix(right, rightMatrix); |
@@ -952,19 +979,19 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
rightMatrix.postTranslate(0, 2 * height); |
canvas.drawBitmapMatrix(right, rightMatrix); |
} |
- patternBBox.fRight = surfaceBBox.width(); |
+ patternBBox.fRight = deviceBounds.width(); |
} |
} |
if (tileModes[1] == SkShader::kClamp_TileMode) { |
SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); |
- if (surfaceBBox.fTop < 0) { |
+ if (deviceBounds.top() < 0) { |
SkBitmap top; |
SkAssertResult(image->extractSubset(&top, subset)); |
SkMatrix topMatrix; |
- topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop); |
- topMatrix.postTranslate(0, surfaceBBox.fTop); |
+ topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); |
+ topMatrix.postTranslate(0, deviceBounds.top()); |
canvas.drawBitmapMatrix(top, topMatrix); |
if (tileModes[0] == SkShader::kMirror_TileMode) { |
@@ -975,13 +1002,13 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
patternBBox.fTop = 0; |
} |
- if (surfaceBBox.fBottom > height) { |
+ if (deviceBounds.bottom() > height) { |
SkBitmap bottom; |
subset.offset(0, image->height() - 1); |
SkAssertResult(image->extractSubset(&bottom, subset)); |
SkMatrix bottomMatrix; |
- bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height); |
+ bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); |
bottomMatrix.postTranslate(0, height); |
canvas.drawBitmapMatrix(bottom, bottomMatrix); |
@@ -990,7 +1017,7 @@ SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { |
bottomMatrix.postTranslate(2 * width, 0); |
canvas.drawBitmapMatrix(bottom, bottomMatrix); |
} |
- patternBBox.fBottom = surfaceBBox.height(); |
+ patternBBox.fBottom = deviceBounds.height(); |
} |
} |