[PDF] Improve complex xfer mode support.
Xfer mode applies only to the shape of the source drawing, not everything in the clip as in currently implemented. It's just that the current gm examples draw a shape that fills the visible layer. R=edisonn@google.com, reed@google.com Review URL: https://codereview.appspot.com/4631078 git-svn-id: http://skia.googlecode.com/svn/trunk@12034 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
c1362424b8
commit
3b416216d1
@ -43,3 +43,12 @@ verylargebitmap
|
||||
# Add by robertphillips in https://codereview.chromium.org/50813002/
|
||||
morphology
|
||||
|
||||
# Added by vandebo in https://codereview.chromium.org/42473002/ because the gm
|
||||
# will need a rebaseline.
|
||||
xfermodes
|
||||
xfermodes2
|
||||
xfermodes3
|
||||
aarectmodes
|
||||
colormatrix
|
||||
lumafilter
|
||||
mixed_xfermodes
|
||||
|
164
gm/xfermodes.cpp
164
gm/xfermodes.cpp
@ -46,17 +46,84 @@ static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
|
||||
static uint16_t gData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
|
||||
|
||||
class XfermodesGM : public GM {
|
||||
enum SrcType {
|
||||
//! A WxH image with a rectangle in the lower right.
|
||||
kRectangleImage_SrcType = 0x01,
|
||||
//! kRectangleImage_SrcType with an alpha of 34.5%.
|
||||
kRectangleImageWithAlpha_SrcType = 0x02,
|
||||
//! kRectnagleImageWithAlpha_SrcType scaled down by half.
|
||||
kSmallRectangleImageWithAlpha_SrcType = 0x04,
|
||||
//! kRectangleImage_SrcType drawn directly instead in an image.
|
||||
kRectangle_SrcType = 0x08,
|
||||
//! Two rectangles, first on the right half, second on the bottom half.
|
||||
kQuarterClear_SrcType = 0x10,
|
||||
//! kQuarterClear_SrcType in a layer.
|
||||
kQuarterClearInLayer_SrcType = 0x20,
|
||||
|
||||
kAll_SrcType = 0x3F, //!< All the source types.
|
||||
kBasic_SrcType = 0x03, //!< Just basic source types.
|
||||
};
|
||||
|
||||
SkBitmap fBG;
|
||||
SkBitmap fSrcB, fDstB;
|
||||
|
||||
void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
|
||||
/* The srcType argument indicates what to draw for the source part. Skia
|
||||
* uses the implied shape of the drawing command and these modes
|
||||
* demonstrate that.
|
||||
*/
|
||||
void draw_mode(SkCanvas* canvas, SkXfermode* mode, SrcType srcType,
|
||||
SkScalar x, SkScalar y) {
|
||||
SkPaint p;
|
||||
SkMatrix m;
|
||||
bool restoreNeeded = false;
|
||||
m.setTranslate(x, y);
|
||||
|
||||
canvas->drawBitmap(fSrcB, x, y, &p);
|
||||
p.setAlpha(alpha);
|
||||
canvas->drawBitmapMatrix(fSrcB, m, &p);
|
||||
p.setXfermode(mode);
|
||||
canvas->drawBitmap(fDstB, x, y, &p);
|
||||
switch (srcType) {
|
||||
case kQuarterClearInLayer_SrcType: {
|
||||
SkRect bounds = SkRect::MakeXYWH(x, y, W, H);
|
||||
canvas->saveLayer(&bounds, &p);
|
||||
restoreNeeded = true;
|
||||
p.setXfermodeMode(SkXfermode::kSrcOver_Mode);
|
||||
// Fall through.
|
||||
}
|
||||
case kQuarterClear_SrcType: {
|
||||
SkScalar halfW = SkIntToScalar(W) / 2;
|
||||
SkScalar halfH = SkIntToScalar(H) / 2;
|
||||
p.setColor(0xFF66AAFF);
|
||||
SkRect r = SkRect::MakeXYWH(x + halfW, y, halfW, H);
|
||||
canvas->drawRect(r, p);
|
||||
p.setColor(0xFFAA66FF);
|
||||
r = SkRect::MakeXYWH(x, y + halfH, W, halfH);
|
||||
canvas->drawRect(r, p);
|
||||
break;
|
||||
}
|
||||
case kRectangle_SrcType: {
|
||||
SkScalar w = SkIntToScalar(W);
|
||||
SkScalar h = SkIntToScalar(H);
|
||||
SkRect r = SkRect::MakeXYWH(x + w / 3, y + h / 3,
|
||||
w * 37 / 60, h * 37 / 60);
|
||||
p.setColor(0xFF66AAFF);
|
||||
canvas->drawRect(r, p);
|
||||
break;
|
||||
}
|
||||
case kSmallRectangleImageWithAlpha_SrcType:
|
||||
m.postScale(SK_ScalarHalf, SK_ScalarHalf, x, y);
|
||||
// Fall through.
|
||||
case kRectangleImageWithAlpha_SrcType:
|
||||
p.setAlpha(0x88);
|
||||
// Fall through.
|
||||
case kRectangleImage_SrcType:
|
||||
canvas->drawBitmapMatrix(fDstB, m, &p);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (restoreNeeded) {
|
||||
canvas->restore();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void onOnceBeforeDraw() SK_OVERRIDE {
|
||||
@ -77,7 +144,7 @@ protected:
|
||||
}
|
||||
|
||||
virtual SkISize onISize() {
|
||||
return make_isize(790, 640);
|
||||
return make_isize(1590, 640);
|
||||
}
|
||||
|
||||
virtual void onDraw(SkCanvas* canvas) {
|
||||
@ -85,38 +152,44 @@ protected:
|
||||
|
||||
const struct {
|
||||
SkXfermode::Mode fMode;
|
||||
const char* fLabel;
|
||||
const char* fLabel;
|
||||
int fSourceTypeMask; // The source types to use this
|
||||
// mode with. See draw_mode for
|
||||
// an explanation of each type.
|
||||
// PDF has to play some tricks
|
||||
// to support the base modes,
|
||||
// test those more extensively.
|
||||
} gModes[] = {
|
||||
{ SkXfermode::kClear_Mode, "Clear" },
|
||||
{ SkXfermode::kSrc_Mode, "Src" },
|
||||
{ SkXfermode::kDst_Mode, "Dst" },
|
||||
{ SkXfermode::kSrcOver_Mode, "SrcOver" },
|
||||
{ SkXfermode::kDstOver_Mode, "DstOver" },
|
||||
{ SkXfermode::kSrcIn_Mode, "SrcIn" },
|
||||
{ SkXfermode::kDstIn_Mode, "DstIn" },
|
||||
{ SkXfermode::kSrcOut_Mode, "SrcOut" },
|
||||
{ SkXfermode::kDstOut_Mode, "DstOut" },
|
||||
{ SkXfermode::kSrcATop_Mode, "SrcATop" },
|
||||
{ SkXfermode::kDstATop_Mode, "DstATop" },
|
||||
{ SkXfermode::kXor_Mode, "Xor" },
|
||||
{ SkXfermode::kClear_Mode, "Clear", kAll_SrcType },
|
||||
{ SkXfermode::kSrc_Mode, "Src", kAll_SrcType },
|
||||
{ SkXfermode::kDst_Mode, "Dst", kAll_SrcType },
|
||||
{ SkXfermode::kSrcOver_Mode, "SrcOver", kAll_SrcType },
|
||||
{ SkXfermode::kDstOver_Mode, "DstOver", kAll_SrcType },
|
||||
{ SkXfermode::kSrcIn_Mode, "SrcIn", kAll_SrcType },
|
||||
{ SkXfermode::kDstIn_Mode, "DstIn", kAll_SrcType },
|
||||
{ SkXfermode::kSrcOut_Mode, "SrcOut", kAll_SrcType },
|
||||
{ SkXfermode::kDstOut_Mode, "DstOut", kAll_SrcType },
|
||||
{ SkXfermode::kSrcATop_Mode, "SrcATop", kAll_SrcType },
|
||||
{ SkXfermode::kDstATop_Mode, "DstATop", kAll_SrcType },
|
||||
|
||||
{ SkXfermode::kPlus_Mode, "Plus" },
|
||||
{ SkXfermode::kModulate_Mode, "Modulate" },
|
||||
{ SkXfermode::kScreen_Mode, "Screen" },
|
||||
{ SkXfermode::kOverlay_Mode, "Overlay" },
|
||||
{ SkXfermode::kDarken_Mode, "Darken" },
|
||||
{ SkXfermode::kLighten_Mode, "Lighten" },
|
||||
{ SkXfermode::kColorDodge_Mode, "ColorDodge" },
|
||||
{ SkXfermode::kColorBurn_Mode, "ColorBurn" },
|
||||
{ SkXfermode::kHardLight_Mode, "HardLight" },
|
||||
{ SkXfermode::kSoftLight_Mode, "SoftLight" },
|
||||
{ SkXfermode::kDifference_Mode, "Difference" },
|
||||
{ SkXfermode::kExclusion_Mode, "Exclusion" },
|
||||
{ SkXfermode::kMultiply_Mode, "Multiply" },
|
||||
{ SkXfermode::kHue_Mode, "Hue" },
|
||||
{ SkXfermode::kSaturation_Mode, "Saturation" },
|
||||
{ SkXfermode::kColor_Mode, "Color" },
|
||||
{ SkXfermode::kLuminosity_Mode, "Luminosity" },
|
||||
{ SkXfermode::kXor_Mode, "Xor", kBasic_SrcType },
|
||||
{ SkXfermode::kPlus_Mode, "Plus", kBasic_SrcType },
|
||||
{ SkXfermode::kModulate_Mode, "Modulate", kAll_SrcType },
|
||||
{ SkXfermode::kScreen_Mode, "Screen", kBasic_SrcType },
|
||||
{ SkXfermode::kOverlay_Mode, "Overlay", kBasic_SrcType },
|
||||
{ SkXfermode::kDarken_Mode, "Darken", kBasic_SrcType },
|
||||
{ SkXfermode::kLighten_Mode, "Lighten", kBasic_SrcType },
|
||||
{ SkXfermode::kColorDodge_Mode, "ColorDodge", kBasic_SrcType },
|
||||
{ SkXfermode::kColorBurn_Mode, "ColorBurn", kBasic_SrcType },
|
||||
{ SkXfermode::kHardLight_Mode, "HardLight", kBasic_SrcType },
|
||||
{ SkXfermode::kSoftLight_Mode, "SoftLight", kBasic_SrcType },
|
||||
{ SkXfermode::kDifference_Mode, "Difference", kBasic_SrcType },
|
||||
{ SkXfermode::kExclusion_Mode, "Exclusion", kBasic_SrcType },
|
||||
{ SkXfermode::kMultiply_Mode, "Multiply", kAll_SrcType },
|
||||
{ SkXfermode::kHue_Mode, "Hue", kBasic_SrcType },
|
||||
{ SkXfermode::kSaturation_Mode, "Saturation", kBasic_SrcType },
|
||||
{ SkXfermode::kColor_Mode, "Color", kBasic_SrcType },
|
||||
{ SkXfermode::kLuminosity_Mode, "Luminosity", kBasic_SrcType },
|
||||
};
|
||||
|
||||
const SkScalar w = SkIntToScalar(W);
|
||||
@ -135,9 +208,13 @@ protected:
|
||||
const int W = 5;
|
||||
|
||||
SkScalar x0 = 0;
|
||||
for (int twice = 0; twice < 2; twice++) {
|
||||
SkScalar x = x0, y = 0;
|
||||
SkScalar y0 = 0;
|
||||
for (int sourceType = 1; sourceType & kAll_SrcType; sourceType <<= 1) {
|
||||
SkScalar x = x0, y = y0;
|
||||
for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
|
||||
if ((gModes[i].fSourceTypeMask & sourceType) == 0) {
|
||||
continue;
|
||||
}
|
||||
SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
|
||||
SkAutoUnref aur(mode);
|
||||
SkRect r;
|
||||
@ -149,7 +226,8 @@ protected:
|
||||
canvas->drawRect(r, p);
|
||||
|
||||
canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
|
||||
draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
|
||||
draw_mode(canvas, mode, static_cast<SrcType>(sourceType),
|
||||
r.fLeft, r.fTop);
|
||||
canvas->restore();
|
||||
|
||||
r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
|
||||
@ -167,7 +245,15 @@ protected:
|
||||
y += h + SkIntToScalar(30);
|
||||
}
|
||||
}
|
||||
x0 += SkIntToScalar(400);
|
||||
if (y < 320) {
|
||||
if (x > x0) {
|
||||
y += h + SkIntToScalar(30);
|
||||
}
|
||||
y0 = y;
|
||||
} else {
|
||||
x0 += SkIntToScalar(400);
|
||||
y0 = 0;
|
||||
}
|
||||
}
|
||||
s->unref();
|
||||
}
|
||||
|
@ -262,12 +262,11 @@ private:
|
||||
void cleanUp(bool clearFontUsage);
|
||||
SkPDFFormXObject* createFormXObjectFromDevice();
|
||||
|
||||
// Clear the passed clip from all existing content entries.
|
||||
void clearClipFromContent(const SkClipStack* clipStack,
|
||||
const SkRegion& clipRegion);
|
||||
void drawFormXObjectWithClip(SkPDFFormXObject* form,
|
||||
void drawFormXObjectWithMask(int xObjectIndex,
|
||||
SkPDFFormXObject* mask,
|
||||
const SkClipStack* clipStack,
|
||||
const SkRegion& clipRegion,
|
||||
SkXfermode::Mode mode,
|
||||
bool invertClip);
|
||||
|
||||
// If the paint or clip is such that we shouldn't draw anything, this
|
||||
@ -281,7 +280,8 @@ private:
|
||||
bool hasText,
|
||||
SkPDFFormXObject** dst);
|
||||
void finishContentEntry(SkXfermode::Mode xfermode,
|
||||
SkPDFFormXObject* dst);
|
||||
SkPDFFormXObject* dst,
|
||||
SkPath* shape);
|
||||
bool isContentEmpty();
|
||||
|
||||
void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
|
||||
@ -291,6 +291,7 @@ private:
|
||||
bool hasText,
|
||||
GraphicStateEntry* entry);
|
||||
int addGraphicStateResource(SkPDFGraphicState* gs);
|
||||
int addXObjectResource(SkPDFObject* xObject);
|
||||
|
||||
void updateFont(const SkPaint& paint, uint16_t glyphID,
|
||||
ContentEntry* contentEntry);
|
||||
|
@ -480,7 +480,8 @@ void GraphicStackState::updateClip(const SkClipStack& clipStack,
|
||||
bool needRegion = false;
|
||||
const SkClipStack::Element* clipEntry;
|
||||
for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
|
||||
if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInverseFilled()) {
|
||||
if (clipEntry->getOp() != SkRegion::kIntersect_Op ||
|
||||
clipEntry->isInverseFilled()) {
|
||||
needRegion = true;
|
||||
break;
|
||||
}
|
||||
@ -619,7 +620,8 @@ public:
|
||||
const SkPaint& paint, bool hasText = false)
|
||||
: fDevice(device),
|
||||
fContentEntry(NULL),
|
||||
fXfermode(SkXfermode::kSrcOver_Mode) {
|
||||
fXfermode(SkXfermode::kSrcOver_Mode),
|
||||
fDstFormXObject(NULL) {
|
||||
init(draw.fClipStack, *draw.fClip, *draw.fMatrix, paint, hasText);
|
||||
}
|
||||
ScopedContentEntry(SkPDFDevice* device, const SkClipStack* clipStack,
|
||||
@ -627,27 +629,67 @@ public:
|
||||
const SkPaint& paint, bool hasText = false)
|
||||
: fDevice(device),
|
||||
fContentEntry(NULL),
|
||||
fXfermode(SkXfermode::kSrcOver_Mode) {
|
||||
fXfermode(SkXfermode::kSrcOver_Mode),
|
||||
fDstFormXObject(NULL) {
|
||||
init(clipStack, clipRegion, matrix, paint, hasText);
|
||||
}
|
||||
|
||||
~ScopedContentEntry() {
|
||||
if (fContentEntry) {
|
||||
fDevice->finishContentEntry(fXfermode, fDstFormXObject);
|
||||
SkPath* shape = &fShape;
|
||||
if (shape->isEmpty()) {
|
||||
shape = NULL;
|
||||
}
|
||||
fDevice->finishContentEntry(fXfermode, fDstFormXObject, shape);
|
||||
}
|
||||
SkSafeUnref(fDstFormXObject);
|
||||
}
|
||||
|
||||
ContentEntry* entry() { return fContentEntry; }
|
||||
|
||||
/* Returns true when we explicitly need the shape of the drawing. */
|
||||
bool needShape() {
|
||||
switch (fXfermode) {
|
||||
case SkXfermode::kClear_Mode:
|
||||
case SkXfermode::kSrc_Mode:
|
||||
case SkXfermode::kSrcIn_Mode:
|
||||
case SkXfermode::kSrcOut_Mode:
|
||||
case SkXfermode::kDstIn_Mode:
|
||||
case SkXfermode::kDstOut_Mode:
|
||||
case SkXfermode::kSrcATop_Mode:
|
||||
case SkXfermode::kDstATop_Mode:
|
||||
case SkXfermode::kModulate_Mode:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Returns true unless we only need the shape of the drawing. */
|
||||
bool needSource() {
|
||||
if (fXfermode == SkXfermode::kClear_Mode) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If the shape is different than the alpha component of the content, then
|
||||
* setShape should be called with the shape. In particular, images and
|
||||
* devices have rectangular shape.
|
||||
*/
|
||||
void setShape(const SkPath& shape) {
|
||||
fShape = shape;
|
||||
}
|
||||
|
||||
private:
|
||||
SkPDFDevice* fDevice;
|
||||
ContentEntry* fContentEntry;
|
||||
SkXfermode::Mode fXfermode;
|
||||
SkPDFFormXObject* fDstFormXObject;
|
||||
SkPath fShape;
|
||||
|
||||
void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
|
||||
const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
|
||||
fDstFormXObject = NULL;
|
||||
// Shape has to be flatten before we get here.
|
||||
if (matrix.hasPerspective()) {
|
||||
NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
|
||||
@ -1054,7 +1096,8 @@ void SkPDFDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
|
||||
|
||||
SkMatrix transform = matrix;
|
||||
transform.postConcat(*d.fMatrix);
|
||||
this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, NULL, paint);
|
||||
this->internalDrawBitmap(transform, d.fClipStack, *d.fClip, bitmap, NULL,
|
||||
paint);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
|
||||
@ -1065,7 +1108,8 @@ void SkPDFDevice::drawSprite(const SkDraw& d, const SkBitmap& bitmap,
|
||||
|
||||
SkMatrix matrix;
|
||||
matrix.setTranslate(SkIntToScalar(x), SkIntToScalar(y));
|
||||
this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL, paint);
|
||||
this->internalDrawBitmap(matrix, d.fClipStack, *d.fClip, bitmap, NULL,
|
||||
paint);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawText(const SkDraw& d, const void* text, size_t len,
|
||||
@ -1170,7 +1214,8 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
|
||||
// find all the typefaces needed to resolve this run of text
|
||||
bool usesOriginalTypeface = false;
|
||||
for (uint16_t x = 0; x < numGlyphs; ++x) {
|
||||
// optimization that checks to see if original typeface can resolve the glyph
|
||||
// optimization that checks to see if original typeface can resolve
|
||||
// the glyph
|
||||
if (glyphIDs[x] < origGlyphCount) {
|
||||
usesOriginalTypeface = true;
|
||||
continue;
|
||||
@ -1178,9 +1223,11 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
|
||||
|
||||
// find the fallback typeface that supports this glyph
|
||||
TypefaceFallbackData data;
|
||||
data.typeface = SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(),
|
||||
paint.getPaintOptionsAndroid(),
|
||||
&data.lowerBounds, &data.upperBounds);
|
||||
data.typeface =
|
||||
SkGetTypefaceForGlyphID(glyphIDs[x], origFace.get(),
|
||||
paint.getPaintOptionsAndroid(),
|
||||
&data.lowerBounds,
|
||||
&data.upperBounds);
|
||||
// add the typeface and its data if we don't have it
|
||||
if (data.typeface && !visitedTypefaces.contains(data)) {
|
||||
visitedTypefaces.push(data);
|
||||
@ -1208,7 +1255,8 @@ void SkPDFDevice::drawPosText(const SkDraw& d, const void* text, size_t len,
|
||||
|
||||
int tmpGlyphCount = 0;
|
||||
for (uint16_t y = 0; y < numGlyphs; ++y) {
|
||||
if (glyphIDs[y] >= data.lowerBounds && glyphIDs[y] < data.upperBounds) {
|
||||
if (glyphIDs[y] >= data.lowerBounds &&
|
||||
glyphIDs[y] < data.upperBounds) {
|
||||
tmpGlyphIDs[tmpGlyphCount] = glyphIDs[y] - data.lowerBounds;
|
||||
memcpy(&(tmpPos[tmpGlyphCount * scalarsPerPos]),
|
||||
&(pos[y * scalarsPerPos]),
|
||||
@ -1286,8 +1334,8 @@ void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
|
||||
NOT_IMPLEMENTED("drawVerticies", true);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device, int x, int y,
|
||||
const SkPaint& paint) {
|
||||
void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device,
|
||||
int x, int y, const SkPaint& paint) {
|
||||
if ((device->getDeviceCapabilities() & kVector_Capability) == 0) {
|
||||
// If we somehow get a raster device, do what our parent would do.
|
||||
INHERITED::drawDevice(d, device, x, y, paint);
|
||||
@ -1306,10 +1354,18 @@ void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device, int x, int y
|
||||
if (!content.entry()) {
|
||||
return;
|
||||
}
|
||||
if (content.needShape()) {
|
||||
SkPath shape;
|
||||
shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
|
||||
device->width(), device->height()));
|
||||
content.setShape(shape);
|
||||
}
|
||||
if (!content.needSource()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkPDFFormXObject* xobject = new SkPDFFormXObject(pdfDevice);
|
||||
fXObjectResources.push(xobject); // Transfer reference.
|
||||
SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
|
||||
SkAutoTUnref<SkPDFFormXObject> xObject(new SkPDFFormXObject(pdfDevice));
|
||||
SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()),
|
||||
&content.entry()->fContent);
|
||||
|
||||
// Merge glyph sets from the drawn device.
|
||||
@ -1552,7 +1608,8 @@ bool SkPDFDevice::handleRectAnnotation(const SkRect& r, const SkMatrix& matrix,
|
||||
handleLinkToURL(urlData, r, matrix);
|
||||
return p.isNoDrawAnnotation();
|
||||
}
|
||||
SkData* linkToName = annotationInfo->find(SkAnnotationKeys::Link_Named_Dest_Key());
|
||||
SkData* linkToName = annotationInfo->find(
|
||||
SkAnnotationKeys::Link_Named_Dest_Key());
|
||||
if (linkToName) {
|
||||
handleLinkToNamedDest(linkToName, r, matrix);
|
||||
return p.isNoDrawAnnotation();
|
||||
@ -1567,7 +1624,8 @@ bool SkPDFDevice::handlePointAnnotation(const SkPoint* points, size_t count,
|
||||
if (!annotationInfo) {
|
||||
return false;
|
||||
}
|
||||
SkData* nameData = annotationInfo->find(SkAnnotationKeys::Define_Named_Dest_Key());
|
||||
SkData* nameData = annotationInfo->find(
|
||||
SkAnnotationKeys::Define_Named_Dest_Key());
|
||||
if (nameData) {
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
defineNamedDestination(nameData, points[i], matrix);
|
||||
@ -1577,7 +1635,8 @@ bool SkPDFDevice::handlePointAnnotation(const SkPoint* points, size_t count,
|
||||
return false;
|
||||
}
|
||||
|
||||
SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r, const SkMatrix& matrix) {
|
||||
SkPDFDict* SkPDFDevice::createLinkAnnotation(const SkRect& r,
|
||||
const SkMatrix& matrix) {
|
||||
SkMatrix transform = matrix;
|
||||
transform.postConcat(fInitialTransform);
|
||||
SkRect translatedRect;
|
||||
@ -1663,7 +1722,8 @@ void SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) {
|
||||
pdfDest->appendScalar(dest->point.x());
|
||||
pdfDest->appendScalar(dest->point.y());
|
||||
pdfDest->appendInt(0); // Leave zoom unchanged
|
||||
dict->insert(static_cast<const char *>(dest->nameData->data()), pdfDest);
|
||||
dict->insert(static_cast<const char *>(dest->nameData->data()),
|
||||
pdfDest);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1677,51 +1737,31 @@ SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {
|
||||
return xobject;
|
||||
}
|
||||
|
||||
void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack,
|
||||
const SkRegion& clipRegion) {
|
||||
if (clipRegion.isEmpty() || isContentEmpty()) {
|
||||
return;
|
||||
}
|
||||
SkAutoTUnref<SkPDFFormXObject> curContent(createFormXObjectFromDevice());
|
||||
|
||||
// Redraw what we already had, but with the clip as a mask.
|
||||
drawFormXObjectWithClip(curContent, clipStack, clipRegion, true);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject,
|
||||
void SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
|
||||
SkPDFFormXObject* mask,
|
||||
const SkClipStack* clipStack,
|
||||
const SkRegion& clipRegion,
|
||||
SkXfermode::Mode mode,
|
||||
bool invertClip) {
|
||||
if (clipRegion.isEmpty() && !invertClip) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the mask.
|
||||
SkAutoTUnref<SkPDFGraphicState> sMaskGS(
|
||||
SkPDFGraphicState::GetSMaskGraphicState(
|
||||
mask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode));
|
||||
|
||||
SkMatrix identity;
|
||||
identity.reset();
|
||||
SkDraw draw;
|
||||
draw.fMatrix = &identity;
|
||||
draw.fClip = &clipRegion;
|
||||
draw.fClipStack = clipStack;
|
||||
SkPaint stockPaint;
|
||||
this->drawPaint(draw, stockPaint);
|
||||
SkAutoTUnref<SkPDFFormXObject> maskFormXObject(createFormXObjectFromDevice());
|
||||
SkAutoTUnref<SkPDFGraphicState> sMaskGS(
|
||||
SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject, invertClip,
|
||||
SkPDFGraphicState::kAlpha_SMaskMode));
|
||||
|
||||
// Draw the xobject with the clip as a mask.
|
||||
ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
|
||||
identity, stockPaint);
|
||||
SkPaint paint;
|
||||
paint.setXfermodeMode(mode);
|
||||
ScopedContentEntry content(this, clipStack, clipRegion, identity, paint);
|
||||
if (!content.entry()) {
|
||||
return;
|
||||
}
|
||||
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
|
||||
&content.entry()->fContent);
|
||||
SkPDFUtils::DrawFormXObject(fXObjectResources.count(),
|
||||
&content.entry()->fContent);
|
||||
fXObjectResources.push(xobject);
|
||||
xobject->ref();
|
||||
SkPDFUtils::DrawFormXObject(xObjectIndex, &content.entry()->fContent);
|
||||
|
||||
sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
|
||||
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
|
||||
@ -1762,28 +1802,32 @@ ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
|
||||
paint.getXfermode()->asMode(&xfermode);
|
||||
}
|
||||
|
||||
if (xfermode == SkXfermode::kClear_Mode ||
|
||||
xfermode == SkXfermode::kSrc_Mode) {
|
||||
this->clearClipFromContent(clipStack, clipRegion);
|
||||
} else if (xfermode == SkXfermode::kSrcIn_Mode ||
|
||||
xfermode == SkXfermode::kDstIn_Mode ||
|
||||
xfermode == SkXfermode::kSrcOut_Mode ||
|
||||
xfermode == SkXfermode::kDstOut_Mode) {
|
||||
// For the following modes, we use both source and destination, but
|
||||
// we use one as a smask for the other, so we have to make form xobjects
|
||||
// out of both of them: SrcIn, DstIn, SrcOut, DstOut.
|
||||
if (isContentEmpty()) {
|
||||
return NULL;
|
||||
} else {
|
||||
// For the following modes, we want to handle source and destination
|
||||
// separately, so make an object of what's already there.
|
||||
if (xfermode == SkXfermode::kClear_Mode ||
|
||||
xfermode == SkXfermode::kSrc_Mode ||
|
||||
xfermode == SkXfermode::kSrcIn_Mode ||
|
||||
xfermode == SkXfermode::kDstIn_Mode ||
|
||||
xfermode == SkXfermode::kSrcOut_Mode ||
|
||||
xfermode == SkXfermode::kDstOut_Mode ||
|
||||
xfermode == SkXfermode::kSrcATop_Mode ||
|
||||
xfermode == SkXfermode::kDstATop_Mode ||
|
||||
xfermode == SkXfermode::kModulate_Mode) {
|
||||
if (!isContentEmpty()) {
|
||||
*dst = createFormXObjectFromDevice();
|
||||
SkASSERT(isContentEmpty());
|
||||
} else if (xfermode != SkXfermode::kSrc_Mode &&
|
||||
xfermode != SkXfermode::kSrcOut_Mode) {
|
||||
// Except for Src and SrcOut, if there isn't anything already there,
|
||||
// then we're done.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
// TODO(vandebo): Figure out how/if we can handle the following modes:
|
||||
// SrcAtop, DestAtop, Xor, Plus.
|
||||
// Xor, Plus.
|
||||
|
||||
// These xfer modes don't draw source at all.
|
||||
if (xfermode == SkXfermode::kClear_Mode ||
|
||||
xfermode == SkXfermode::kDst_Mode) {
|
||||
// Dst xfer mode doesn't draw source at all.
|
||||
if (xfermode == SkXfermode::kDst_Mode) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1821,70 +1865,135 @@ ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
|
||||
}
|
||||
|
||||
void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
|
||||
SkPDFFormXObject* dst) {
|
||||
if (xfermode != SkXfermode::kSrcIn_Mode &&
|
||||
xfermode != SkXfermode::kDstIn_Mode &&
|
||||
xfermode != SkXfermode::kSrcOut_Mode &&
|
||||
xfermode != SkXfermode::kDstOut_Mode) {
|
||||
SkPDFFormXObject* dst,
|
||||
SkPath* shape) {
|
||||
if (xfermode != SkXfermode::kClear_Mode &&
|
||||
xfermode != SkXfermode::kSrc_Mode &&
|
||||
xfermode != SkXfermode::kSrcIn_Mode &&
|
||||
xfermode != SkXfermode::kDstIn_Mode &&
|
||||
xfermode != SkXfermode::kSrcOut_Mode &&
|
||||
xfermode != SkXfermode::kDstOut_Mode &&
|
||||
xfermode != SkXfermode::kSrcATop_Mode &&
|
||||
xfermode != SkXfermode::kDstATop_Mode &&
|
||||
xfermode != SkXfermode::kModulate_Mode) {
|
||||
SkASSERT(!dst);
|
||||
return;
|
||||
}
|
||||
if (!dst) {
|
||||
SkASSERT(xfermode == SkXfermode::kSrc_Mode ||
|
||||
xfermode == SkXfermode::kSrcOut_Mode);
|
||||
return;
|
||||
}
|
||||
|
||||
ContentEntry* contentEntries = getContentEntries()->get();
|
||||
SkASSERT(dst);
|
||||
SkASSERT(!contentEntries->fNext.get());
|
||||
// We have to make a copy of these here because changing the current
|
||||
// content into a form xobject will destroy them.
|
||||
// content into a form-xobject will destroy them.
|
||||
SkClipStack clipStack = contentEntries->fState.fClipStack;
|
||||
SkRegion clipRegion = contentEntries->fState.fClipRegion;
|
||||
|
||||
SkAutoTUnref<SkPDFFormXObject> srcFormXObject;
|
||||
if (!isContentEmpty()) {
|
||||
if (isContentEmpty()) {
|
||||
SkASSERT(xfermode == SkXfermode::kClear_Mode);
|
||||
} else {
|
||||
SkASSERT(!fContentEntries->fNext.get());
|
||||
srcFormXObject.reset(createFormXObjectFromDevice());
|
||||
}
|
||||
|
||||
drawFormXObjectWithClip(dst, &clipStack, clipRegion, true);
|
||||
|
||||
// We've redrawn dst minus the clip area, if there's no src, we're done.
|
||||
if (!srcFormXObject.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkMatrix identity;
|
||||
identity.reset();
|
||||
|
||||
// TODO(vandebo) srcFormXObject may contain alpha, but here we want it
|
||||
// without alpha.
|
||||
if (xfermode == SkXfermode::kSrcATop_Mode) {
|
||||
// TODO(vandebo): In order to properly support SrcATop we have to track
|
||||
// the shape of what's been drawn at all times. It's the intersection of
|
||||
// the non-transparent parts of the device and the outlines (shape) of
|
||||
// all images and devices drawn.
|
||||
drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
|
||||
&clipStack, clipRegion,
|
||||
SkXfermode::kSrcOver_Mode, true);
|
||||
} else {
|
||||
SkAutoTUnref<SkPDFFormXObject> dstMaskStorage;
|
||||
SkPDFFormXObject* dstMask = srcFormXObject.get();
|
||||
if (shape != NULL) {
|
||||
// Draw shape into a form-xobject.
|
||||
SkDraw d;
|
||||
d.fMatrix = &identity;
|
||||
d.fClip = &clipRegion;
|
||||
d.fClipStack = &clipStack;
|
||||
SkPaint filledPaint;
|
||||
filledPaint.setColor(SK_ColorBLACK);
|
||||
filledPaint.setStyle(SkPaint::kFill_Style);
|
||||
this->drawPath(d, *shape, filledPaint, NULL, true);
|
||||
|
||||
dstMaskStorage.reset(createFormXObjectFromDevice());
|
||||
dstMask = dstMaskStorage.get();
|
||||
}
|
||||
drawFormXObjectWithMask(addXObjectResource(dst), dstMask, &clipStack,
|
||||
clipRegion, SkXfermode::kSrcOver_Mode, true);
|
||||
}
|
||||
|
||||
SkPaint stockPaint;
|
||||
ScopedContentEntry inClipContentEntry(this, &fExistingClipStack,
|
||||
fExistingClipRegion, identity,
|
||||
stockPaint);
|
||||
if (!inClipContentEntry.entry()) {
|
||||
|
||||
if (xfermode == SkXfermode::kClear_Mode) {
|
||||
return;
|
||||
} else if (xfermode == SkXfermode::kSrc_Mode ||
|
||||
xfermode == SkXfermode::kDstATop_Mode) {
|
||||
ScopedContentEntry content(this, &clipStack, clipRegion, identity,
|
||||
stockPaint);
|
||||
if (content.entry()) {
|
||||
SkPDFUtils::DrawFormXObject(
|
||||
this->addXObjectResource(srcFormXObject.get()),
|
||||
&content.entry()->fContent);
|
||||
}
|
||||
if (xfermode == SkXfermode::kSrc_Mode) {
|
||||
return;
|
||||
}
|
||||
} else if (xfermode == SkXfermode::kSrcATop_Mode) {
|
||||
ScopedContentEntry content(this, &clipStack, clipRegion, identity,
|
||||
stockPaint);
|
||||
if (content.entry()) {
|
||||
SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
|
||||
&content.entry()->fContent);
|
||||
}
|
||||
}
|
||||
|
||||
SkASSERT(xfermode == SkXfermode::kSrcIn_Mode ||
|
||||
xfermode == SkXfermode::kDstIn_Mode ||
|
||||
xfermode == SkXfermode::kSrcOut_Mode ||
|
||||
xfermode == SkXfermode::kDstOut_Mode ||
|
||||
xfermode == SkXfermode::kSrcATop_Mode ||
|
||||
xfermode == SkXfermode::kDstATop_Mode ||
|
||||
xfermode == SkXfermode::kModulate_Mode);
|
||||
|
||||
ScopedContentEntry inShapeContentEntry(this, &fExistingClipStack,
|
||||
fExistingClipRegion, identity,
|
||||
stockPaint);
|
||||
if (!inShapeContentEntry.entry()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkAutoTUnref<SkPDFGraphicState> sMaskGS;
|
||||
if (xfermode == SkXfermode::kSrcIn_Mode ||
|
||||
xfermode == SkXfermode::kSrcOut_Mode) {
|
||||
sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
|
||||
dst,
|
||||
xfermode == SkXfermode::kSrcOut_Mode,
|
||||
SkPDFGraphicState::kAlpha_SMaskMode));
|
||||
fXObjectResources.push(srcFormXObject.get());
|
||||
srcFormXObject.get()->ref();
|
||||
xfermode == SkXfermode::kSrcOut_Mode ||
|
||||
xfermode == SkXfermode::kSrcATop_Mode) {
|
||||
drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
|
||||
&clipStack, clipRegion,
|
||||
SkXfermode::kSrcOver_Mode,
|
||||
xfermode == SkXfermode::kSrcOut_Mode);
|
||||
} else {
|
||||
sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
|
||||
srcFormXObject.get(),
|
||||
xfermode == SkXfermode::kDstOut_Mode,
|
||||
SkPDFGraphicState::kAlpha_SMaskMode));
|
||||
// dst already added to fXObjectResources in drawFormXObjectWithClip.
|
||||
SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
|
||||
if (xfermode == SkXfermode::kModulate_Mode) {
|
||||
drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
|
||||
dst, &clipStack, clipRegion,
|
||||
SkXfermode::kSrcOver_Mode, false);
|
||||
mode = SkXfermode::kMultiply_Mode;
|
||||
}
|
||||
drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(),
|
||||
&clipStack, clipRegion, mode,
|
||||
xfermode == SkXfermode::kDstOut_Mode);
|
||||
}
|
||||
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
|
||||
&inClipContentEntry.entry()->fContent);
|
||||
|
||||
SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
|
||||
&inClipContentEntry.entry()->fContent);
|
||||
|
||||
sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
|
||||
SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
|
||||
&inClipContentEntry.entry()->fContent);
|
||||
}
|
||||
|
||||
bool SkPDFDevice::isContentEmpty() {
|
||||
@ -1996,6 +2105,18 @@ int SkPDFDevice::addGraphicStateResource(SkPDFGraphicState* gs) {
|
||||
return result;
|
||||
}
|
||||
|
||||
int SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
|
||||
// Assumes that xobject has been canonicalized (so we can directly compare
|
||||
// pointers).
|
||||
int result = fXObjectResources.find(xObject);
|
||||
if (result < 0) {
|
||||
result = fXObjectResources.count();
|
||||
fXObjectResources.push(xObject);
|
||||
xObject->ref();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
|
||||
ContentEntry* contentEntry) {
|
||||
SkTypeface* typeface = paint.getTypeface();
|
||||
@ -2015,7 +2136,8 @@ void SkPDFDevice::updateFont(const SkPaint& paint, uint16_t glyphID,
|
||||
}
|
||||
|
||||
int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
|
||||
SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface, glyphID));
|
||||
SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface,
|
||||
glyphID));
|
||||
int resourceIndex = fFontResources.find(newFont.get());
|
||||
if (resourceIndex < 0) {
|
||||
resourceIndex = fFontResources.count();
|
||||
@ -2119,21 +2241,26 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
|
||||
SkIntToScalar(subset.height()));
|
||||
scaled.postConcat(matrix);
|
||||
ScopedContentEntry content(this, clipStack, *clipRegion, scaled, paint);
|
||||
if (!content.entry()) {
|
||||
if (!content.entry() || (srcRect && !subset.intersect(*srcRect))) {
|
||||
return;
|
||||
}
|
||||
if (content.needShape()) {
|
||||
SkPath shape;
|
||||
shape.addRect(SkRect::MakeWH(subset.width(), subset.height()));
|
||||
shape.transform(matrix);
|
||||
content.setShape(shape);
|
||||
}
|
||||
if (!content.needSource()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (srcRect && !subset.intersect(*srcRect)) {
|
||||
return;
|
||||
}
|
||||
|
||||
SkPDFImage* image = SkPDFImage::CreateImage(*bitmap, subset, fEncoder);
|
||||
SkAutoTUnref<SkPDFImage> image(
|
||||
SkPDFImage::CreateImage(*bitmap, subset, fEncoder));
|
||||
if (!image) {
|
||||
return;
|
||||
}
|
||||
|
||||
fXObjectResources.push(image); // Transfer reference.
|
||||
SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
|
||||
SkPDFUtils::DrawFormXObject(this->addXObjectResource(image.get()),
|
||||
&content.entry()->fContent);
|
||||
}
|
||||
|
||||
|
@ -14,8 +14,6 @@
|
||||
static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
|
||||
switch (mode) {
|
||||
case SkXfermode::kSrcOver_Mode: return "Normal";
|
||||
// kModulate is not really like multipy but similar most of the time.
|
||||
case SkXfermode::kModulate_Mode:
|
||||
case SkXfermode::kMultiply_Mode: return "Multiply";
|
||||
case SkXfermode::kScreen_Mode: return "Screen";
|
||||
case SkXfermode::kOverlay_Mode: return "Overlay";
|
||||
@ -41,11 +39,12 @@ static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
|
||||
case SkXfermode::kDstIn_Mode:
|
||||
case SkXfermode::kSrcOut_Mode:
|
||||
case SkXfermode::kDstOut_Mode:
|
||||
case SkXfermode::kSrcATop_Mode:
|
||||
case SkXfermode::kDstATop_Mode:
|
||||
case SkXfermode::kModulate_Mode:
|
||||
return "Normal";
|
||||
|
||||
// TODO(vandebo): Figure out if we can support more of these modes.
|
||||
case SkXfermode::kSrcATop_Mode:
|
||||
case SkXfermode::kDstATop_Mode:
|
||||
case SkXfermode::kXor_Mode:
|
||||
case SkXfermode::kPlus_Mode:
|
||||
return NULL;
|
||||
|
Loading…
Reference in New Issue
Block a user