diff options
Diffstat (limited to 'src/pdf/SkPDFShader.cpp')
-rw-r--r-- | src/pdf/SkPDFShader.cpp | 99 |
1 files changed, 63 insertions, 36 deletions
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp index 9394f1b959..fa0587fc50 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(); } } |