[PDF] Refactor content stream creation in SkPDFDevice to support more xfermodes.

Instead of writing all drawing and state updates into the final content stream immediately, this change creates a new ContentEntry each time the transform, clip, or paint changes.  Drawing is done into a stream in the ContentEntry.  When the consumer asks for the content, we combine all the ContentEntries with appropriate updates to the state (clip, transform, paint) in between.  This allows us to modify the clip even after a drawing has completed.  It also lets us remove ContentEntries with no drawing.  Further optimization can be done to better use the stack features of PDF, for now we follow the previous model of having a single clip followed by a single transform on the graphic state stack.

Push rectangle logic into SkPDFUtil::AppendRectangle.
Change private functions to adhere to coding standards.

Review URL: http://codereview.appspot.com/4459041

git-svn-id: http://skia.googlecode.com/svn/trunk@1269 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
vandebo@chromium.org 2011-05-09 07:55:58 +00:00
parent 7744c205f2
commit 9fbdf87518
6 changed files with 567 additions and 315 deletions

View File

@ -42,6 +42,7 @@ public:
B2FIter(const SkClipStack& stack);
struct Clip {
friend bool operator==(const Clip& a, const Clip& b);
const SkRect* fRect; // if non-null, this is a rect clip
const SkPath* fPath; // if non-null, this is a path clip
SkRegion::Op fOp;

View File

@ -17,11 +17,12 @@
#ifndef SkPDFDevice_DEFINED
#define SkPDFDevice_DEFINED
#include "SkRefCnt.h"
#include "SkDevice.h"
#include "SkStream.h"
#include "SkPaint.h"
#include "SkPath.h"
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTScopedPtr.h"
class SkPDFArray;
class SkPDFDevice;
@ -32,6 +33,10 @@ class SkPDFObject;
class SkPDFShader;
class SkPDFStream;
// Private classes.
struct ContentEntry;
struct GraphicStateEntry;
class SkPDFDeviceFactory : public SkDeviceFactory {
virtual SkDevice* newDevice(SkCanvas*, SkBitmap::Config, int width,
int height, bool isOpaque, bool isForLayer);
@ -70,13 +75,6 @@ public:
virtual void clear(SkColor color);
/** Called with the correct matrix and clip before this device is drawn
to using those settings. If your subclass overrides this, be sure to
call through to the base class as well.
*/
virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
const SkClipStack&);
virtual bool readPixels(const SkIRect& srcRect, SkBitmap* bitmap) {
return false;
}
@ -141,7 +139,10 @@ protected:
private:
SkISize fPageSize;
SkISize fContentSize;
SkMatrix fInitialTransform;
SkClipStack fExistingClipStack;
SkRegion fExistingClipRegion;
SkRefPtr<SkPDFDict> fResourceDict;
SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
@ -149,49 +150,45 @@ private:
SkTDArray<SkPDFFont*> fFontResources;
SkTDArray<SkPDFShader*> fShaderResources;
// In PDF, transforms and clips can only be undone by popping the graphic
// state to before the transform or clip was applied. Because it can be
// a lot of work to reapply a clip and because this class has to apply
// different transforms to accomplish various operations, the clip is
// always applied before a transform and always at a different graphic
// state-stack level than a transform. This strategy results in the
// following possible states for the graphic state stack:
// empty: (identity transform and clip to page)
// one entry: a transform
// one entry: a clip
// two entries: a clip and then a transform
// Pointers are owned by the respective Resources list.
struct GraphicStackEntry {
SkColor fColor;
SkScalar fTextSize;
SkScalar fTextScaleX;
SkPaint::Style fTextFill;
SkPDFFont* fFont;
SkPDFShader* fShader;
SkPDFGraphicState* fGraphicState;
SkRegion fClip;
SkMatrix fTransform;
};
struct GraphicStackEntry fGraphicStack[3];
int fGraphicStackIndex;
SkDynamicMemoryWStream fContent;
SkTScopedPtr<ContentEntry> fContentEntries;
ContentEntry* fCurrentContentEntry;
void init();
void cleanUp();
void updateGSFromPaint(const SkPaint& newPaint, bool forText);
void setExistingClip(const SkClipStack& clipStack,
const SkRegion& clipRegion);
void setUpContentEntry(const SkClipStack& clipStack,
const SkRegion& clipRegion,
const SkMatrix& matrix,
const SkPaint& paint,
bool hasText = false);
void setUpContentEntryForText(const SkClipStack& clipStack,
const SkRegion& clipRegion,
const SkMatrix& matrix,
const SkPaint& paint);
void populateGraphicStateEntryFromPaint(const SkMatrix& matrix,
const SkClipStack& clipStack,
const SkRegion& clipRegion,
const SkPaint& paint,
bool hasText,
GraphicStateEntry* entry);
void updateFont(const SkPaint& paint, uint16_t glyphID);
int getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID);
void pushGS();
void popGS();
void setTextTransform(SkScalar x, SkScalar y, SkScalar textSkewX);
void internalDrawPaint(const SkPaint& paint);
void internalDrawRect(const SkRect& r, const SkPaint& paint);
void internalDrawBitmap(const SkMatrix& matrix, const SkBitmap& bitmap,
const SkIRect* srcRect, const SkPaint& paint);
SkMatrix setTransform(const SkMatrix& matrix);
void internalDrawPaint(const SkPaint& paint);
void internalDrawBitmap(const SkMatrix& matrix,
const SkClipStack& clipStack,
const SkRegion& clipRegion,
const SkBitmap& bitmap,
const SkIRect* srcRect,
const SkPaint& paint);
// Disable the default copy and assign implementation.
SkPDFDevice(const SkPDFDevice&);
void operator=(const SkPDFDevice&);
};
#endif

View File

@ -22,6 +22,7 @@
class SkMatrix;
class SkPath;
class SkPDFArray;
class SkRect;
#if 0
#define PRINT_NOT_IMPL(str) fprintf(stderr, str)
@ -47,8 +48,7 @@ public:
static void AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
SkScalar ctl2X, SkScalar ctl2Y,
SkScalar dstX, SkScalar dstY, SkWStream* content);
static void AppendRectangle(SkScalar x, SkScalar y, SkScalar w, SkScalar h,
SkWStream* content);
static void AppendRectangle(const SkRect& rect, SkWStream* content);
static void EmitPath(const SkPath& path, SkWStream* content);
static void ClosePath(SkWStream* content);
static void PaintPath(SkPaint::Style style, SkPath::FillType fill,

View File

@ -181,6 +181,13 @@ void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op) {
SkClipStack::B2FIter::B2FIter() {
}
bool operator==(const SkClipStack::B2FIter::Clip& a,
const SkClipStack::B2FIter::Clip& b) {
return a.fOp == b.fOp &&
((a.fRect == NULL && b.fRect == NULL) || *a.fRect == *b.fRect) &&
((a.fPath == NULL && b.fPath == NULL) || *a.fPath == *b.fPath);
}
SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) {
this->reset(stack);
}

File diff suppressed because it is too large Load Diff

View File

@ -85,15 +85,17 @@ void SkPDFUtils::AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
}
// static
void SkPDFUtils::AppendRectangle(SkScalar x, SkScalar y,
SkScalar w, SkScalar h, SkWStream* content) {
SkPDFScalar::Append(x, content);
void SkPDFUtils::AppendRectangle(const SkRect& rect, SkWStream* content) {
// Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
SkScalar bottom = SkMinScalar(rect.fBottom, rect.fTop);
SkPDFScalar::Append(rect.fLeft, content);
content->writeText(" ");
SkPDFScalar::Append(y, content);
SkPDFScalar::Append(bottom, content);
content->writeText(" ");
SkPDFScalar::Append(w, content);
SkPDFScalar::Append(rect.width(), content);
content->writeText(" ");
SkPDFScalar::Append(h, content);
SkPDFScalar::Append(rect.height(), content);
content->writeText(" re\n");
}