Chromium Code Reviews| Index: src/pdf/SkPDFShader.cpp |
| diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp |
| index 9394f1b9597c67d25e77a99af74e2bf938fdd4c8..e07645fb4874c878392c8db470af639e44e514e0 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 pattern cell is in it. |
|
vandebo (ex-Chrome)
2013/08/21 21:32:43
nit: for clamping mode there isn't really a patter
ducky
2013/08/21 23:05:35
Done.
|
| + 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 = image->width(); |
|
vandebo (ex-Chrome)
2013/08/21 21:32:43
nit: you dropped SkIntToScalar
ducky
2013/08/21 23:05:35
Done.
|
| + SkScalar height = 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(); |
| } |
| } |