Generic PDF shader fallback

Instead of ignoring unsupported shaders (and essentially filling with
solid black), convert them to bitmap shaders using on-the-fly
rasterization.

BUG=skia:3299
R=reed@google.com,halcanary@google.com

Review URL: https://codereview.chromium.org/841763005
This commit is contained in:
fmalita 2015-01-13 08:06:11 -08:00 committed by Commit bot
parent f361b71439
commit c3796c7a74
4 changed files with 55 additions and 16 deletions

View File

@ -40,4 +40,10 @@ blurrects
# New shadow only option in SkDropShadowImageFilter
dropshadowimagefilter
# fmalita http://crrev.com/841763005
composeshader
composeshader_alpha
perlinnoise
perlinnoise_localmatrix
pictureshader
pictureshadertile

View File

@ -1950,7 +1950,8 @@ void SkPDFDevice::populateGraphicStateEntryFromPaint(
fInitialTransform.mapRect(&boundsTemp);
boundsTemp.roundOut(&bounds);
pdfShader.reset(SkPDFShader::GetPDFShader(*shader, transform, bounds));
pdfShader.reset(SkPDFShader::GetPDFShader(*shader, transform, bounds,
SkIntToScalar(fRasterDpi) / DPI_FOR_RASTER_SCALE_ONE));
if (pdfShader.get()) {
// pdfShader has been canonicalized so we can directly compare

View File

@ -489,7 +489,7 @@ public:
SkShader::TileMode fImageTileModes[2];
State(const SkShader& shader, const SkMatrix& canvasTransform,
const SkIRect& bbox);
const SkIRect& bbox, SkScalar rasterScale);
bool operator==(const State& b) const;
@ -657,10 +657,11 @@ void SkPDFShader::RemoveShader(SkPDFObject* shader) {
// static
SkPDFObject* SkPDFShader::GetPDFShader(const SkShader& shader,
const SkMatrix& matrix,
const SkIRect& surfaceBBox) {
const SkIRect& surfaceBBox,
SkScalar rasterScale) {
SkAutoMutexAcquire lock(CanonicalShadersMutex());
return GetPDFShaderByState(
SkNEW_ARGS(State, (shader, matrix, surfaceBBox)));
SkNEW_ARGS(State, (shader, matrix, surfaceBBox, rasterScale)));
}
// static
@ -1239,8 +1240,8 @@ bool SkPDFShader::State::operator==(const SkPDFShader::State& b) const {
return true;
}
SkPDFShader::State::State(const SkShader& shader,
const SkMatrix& canvasTransform, const SkIRect& bbox)
SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform,
const SkIRect& bbox, SkScalar rasterScale)
: fCanvasTransform(canvasTransform),
fBBox(bbox),
fPixelGeneration(0) {
@ -1257,10 +1258,39 @@ SkPDFShader::State::State(const SkShader& shader,
SkMatrix matrix;
bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes);
if (bitmapType != SkShader::kDefault_BitmapType) {
fImage.reset();
return;
// Generic fallback for unsupported shaders:
// * allocate a bbox-sized bitmap
// * shade the whole area
// * use the result as a bitmap shader
// Clamp the bitmap size to about 1M pixels
static const SkScalar kMaxBitmapArea = 1024 * 1024;
SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height();
if (bitmapArea > kMaxBitmapArea) {
rasterScale *= SkScalarSqrt(SkScalarDiv(kMaxBitmapArea, bitmapArea));
}
SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()),
SkScalarRoundToInt(rasterScale * bbox.height()));
SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / SkIntToScalar(bbox.width()),
SkIntToScalar(size.height()) / SkIntToScalar(bbox.height()));
fImage.allocN32Pixels(size.width(), size.height());
fImage.eraseColor(SK_ColorTRANSPARENT);
SkPaint p;
p.setShader(const_cast<SkShader*>(&shader));
SkCanvas canvas(fImage);
canvas.scale(scale.width(), scale.height());
canvas.translate(-SkIntToScalar(bbox.x()), -SkIntToScalar(bbox.y()));
canvas.drawPaint(p);
fShaderTransform.setTranslate(SkIntToScalar(bbox.x()), SkIntToScalar(bbox.y()));
fShaderTransform.preScale(1 / scale.width(), 1 / scale.height());
} else {
SkASSERT(matrix.isIdentity());
}
SkASSERT(matrix.isIdentity());
fPixelGeneration = fImage.getGenerationID();
} else {
AllocateGradientInfoStorage();

View File

@ -33,15 +33,17 @@ public:
* unreference it when done. This is needed to accommodate the weak
* reference pattern used when the returned object is new and has no
* other references.
* @param shader The SkShader to emulate.
* @param matrix The current transform. (PDF shaders are absolutely
* positioned, relative to where the page is drawn.)
* @param surfceBBox The bounding box of the drawing surface (with matrix
* already applied).
* @param shader The SkShader to emulate.
* @param matrix The current transform. (PDF shaders are absolutely
* positioned, relative to where the page is drawn.)
* @param surfceBBox The bounding box of the drawing surface (with matrix
* already applied).
* @param rasterScale Additional scale to be applied for early rasterization.
*/
static SkPDFObject* GetPDFShader(const SkShader& shader,
const SkMatrix& matrix,
const SkIRect& surfaceBBox);
const SkIRect& surfaceBBox,
SkScalar rasterScale);
protected:
class State;