зеркало из https://github.com/mozilla/moz-skia.git
Restyle SkPDFImageShader and support tiling bitmaps outside clip bounds
BUG=chromium:99458 R=edisonn@google.com, vandebo@chromium.org Author: richardlin@chromium.org Review URL: https://chromiumcodereview.appspot.com/22884013 git-svn-id: http://skia.googlecode.com/svn/trunk@10870 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
Родитель
42a7829543
Коммит
e324cc69be
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright 2013 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkShader.h"
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
// This GM draws a 3x3 grid (with the center element excluded) of rectangles
|
||||
// filled with a bitmap shader. The bitmap shader is transformed so that the
|
||||
// pattern cell is at the center (excluded) region.
|
||||
//
|
||||
// In Repeat and Mirror mode, this tests that the bitmap shader still draws
|
||||
// even though the pattern cell is outside the clip.
|
||||
//
|
||||
// In Clamp mode, this tests that the clamp is handled properly. For PDF,
|
||||
// (and possibly other exported formats) this also "tests" that the image itself
|
||||
// is not stored (well, you'll need to open it up with an external tool to
|
||||
// verify that).
|
||||
|
||||
static SkBitmap create_bitmap() {
|
||||
SkBitmap bmp;
|
||||
bmp.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
|
||||
bmp.allocPixels();
|
||||
bmp.lockPixels();
|
||||
uint32_t* pixels = reinterpret_cast<uint32_t*>(bmp.getPixels());
|
||||
pixels[0] = SkPreMultiplyColor(SK_ColorRED);
|
||||
pixels[1] = SkPreMultiplyColor(SK_ColorGREEN);
|
||||
pixels[2] = SkPreMultiplyColor(SK_ColorBLACK);
|
||||
pixels[3] = SkPreMultiplyColor(SK_ColorBLUE);
|
||||
bmp.unlockPixels();
|
||||
|
||||
return bmp;
|
||||
}
|
||||
|
||||
static const SkScalar RECT_SIZE = 64;
|
||||
static const SkScalar SLIDE_SIZE = 300;
|
||||
|
||||
class ClippedBitmapShadersGM : public GM {
|
||||
public:
|
||||
ClippedBitmapShadersGM(SkShader::TileMode mode)
|
||||
: fMode(mode) {
|
||||
}
|
||||
|
||||
protected:
|
||||
SkShader::TileMode fMode;
|
||||
|
||||
virtual SkString onShortName() {
|
||||
SkString descriptor;
|
||||
switch (fMode) {
|
||||
case SkShader::kRepeat_TileMode:
|
||||
descriptor = "tile";
|
||||
break;
|
||||
case SkShader::kMirror_TileMode:
|
||||
descriptor = "mirror";
|
||||
break;
|
||||
case SkShader::kClamp_TileMode:
|
||||
descriptor = "clamp";
|
||||
break;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
}
|
||||
descriptor.prepend("clipped-bitmap-shaders-");
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
virtual SkISize onISize() {
|
||||
return SkISize::Make(300, 300);
|
||||
}
|
||||
|
||||
virtual void onDraw(SkCanvas* canvas) {
|
||||
SkBitmap bmp = create_bitmap();
|
||||
SkShader* shader = SkShader::CreateBitmapShader(
|
||||
bmp, fMode, fMode);
|
||||
|
||||
SkPaint paint;
|
||||
SkMatrix s;
|
||||
s.reset();
|
||||
s.setScale(8, 8);
|
||||
s.postTranslate(SLIDE_SIZE / 2, SLIDE_SIZE / 2);
|
||||
shader->setLocalMatrix(s);
|
||||
paint.setShader(shader)->unref();
|
||||
|
||||
SkScalar margin = (SLIDE_SIZE / 3 - RECT_SIZE) / 2;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
SkScalar yOrigin = SLIDE_SIZE / 3 * i + margin;
|
||||
for (int j = 0; j < 3; j++) {
|
||||
SkScalar xOrigin = SLIDE_SIZE / 3 * j + margin;
|
||||
if (i == 1 && j == 1) {
|
||||
continue; // skip center element
|
||||
}
|
||||
SkRect rect = SkRect::MakeXYWH(xOrigin, yOrigin,
|
||||
RECT_SIZE, RECT_SIZE);
|
||||
canvas->save();
|
||||
canvas->clipRect(rect);
|
||||
canvas->drawRect(rect, paint);
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef GM INHERITED;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DEF_GM( return new ClippedBitmapShadersGM(SkShader::kRepeat_TileMode); )
|
||||
DEF_GM( return new ClippedBitmapShadersGM(SkShader::kMirror_TileMode); )
|
||||
DEF_GM( return new ClippedBitmapShadersGM(SkShader::kClamp_TileMode); )
|
||||
}
|
|
@ -23,6 +23,7 @@
|
|||
'../gm/canvasstate.cpp',
|
||||
'../gm/circles.cpp',
|
||||
'../gm/circularclips.cpp',
|
||||
'../gm/clippedbitmapshaders.cpp',
|
||||
'../gm/colorfilterimagefilter.cpp',
|
||||
'../gm/colormatrix.cpp',
|
||||
'../gm/colortype.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;
|
||||
}
|
||||
|
||||
SkMatrix unflip;
|
||||
unflip.setTranslate(0, SkScalarRoundToScalar(surfaceBBox.height()));
|
||||
unflip.preScale(SK_Scalar1, -SK_Scalar1);
|
||||
SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
|
||||
SkScalarRound(surfaceBBox.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());
|
||||
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(deviceBounds.height()));
|
||||
unflip.preScale(SK_Scalar1, -SK_Scalar1);
|
||||
SkISize size = SkISize::Make(SkScalarRound(deviceBounds.width()),
|
||||
SkScalarRound(deviceBounds.height()));
|
||||
SkPDFDevice pattern(size, size, unflip);
|
||||
SkCanvas canvas(&pattern);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче