First pieces of SkPDFDevice. Supports:
Matrix transforms. Rendering bitmaps. Basic paint parameters. Rendering rectangles, points, lines, polygons. Render a paint to the page. Review URL: http://codereview.appspot.com/2584041 git-svn-id: http://skia.googlecode.com/svn/trunk@614 2bbb7eff-a529-9590-31e7-b0007b416f81
This commit is contained in:
parent
17f694b038
commit
9b49dc0db8
@ -70,10 +70,10 @@ public:
|
||||
|
||||
/** Return the width of the device (in pixels).
|
||||
*/
|
||||
int width() const { return fBitmap.width(); }
|
||||
virtual int width() const { return fBitmap.width(); }
|
||||
/** Return the height of the device (in pixels).
|
||||
*/
|
||||
int height() const { return fBitmap.height(); }
|
||||
virtual int height() const { return fBitmap.height(); }
|
||||
/** Return the bitmap config of the device's pixels
|
||||
*/
|
||||
SkBitmap::Config config() const { return fBitmap.getConfig(); }
|
||||
|
155
include/pdf/SkPDFDevice.h
Normal file
155
include/pdf/SkPDFDevice.h
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SkPDFDevice_DEFINED
|
||||
#define SkPDFDevice_DEFINED
|
||||
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkDevice.h"
|
||||
#include "SkString.h"
|
||||
|
||||
class SkPDFArray;
|
||||
class SkPDFDevice;
|
||||
class SkPDFDict;
|
||||
class SkPDFGraphicState;
|
||||
class SkPDFObject;
|
||||
class SkPDFStream;
|
||||
|
||||
class SkPDFDeviceFactory : public SkDeviceFactory {
|
||||
virtual SkDevice* newDevice(SkBitmap::Config config, int width, int height,
|
||||
bool isOpaque, bool isForLayer);
|
||||
virtual uint32_t getDeviceCapabilities() { return 0; }
|
||||
};
|
||||
|
||||
/** \class SkPDFDevice
|
||||
|
||||
The drawing context for the PDF backend.
|
||||
*/
|
||||
class SkPDFDevice : public SkDevice {
|
||||
public:
|
||||
/** Create a PDF drawing context with the given width and height.
|
||||
* 72 points/in means letter paper is 612x792.
|
||||
* @param width Page width in points.
|
||||
* @param height Page height in points.
|
||||
*/
|
||||
SkPDFDevice(int width, int height);
|
||||
virtual ~SkPDFDevice();
|
||||
|
||||
virtual SkDeviceFactory* getDeviceFactory() {
|
||||
return SkNEW(SkPDFDeviceFactory);
|
||||
}
|
||||
|
||||
virtual int width() const { return fWidth; };
|
||||
|
||||
virtual int height() const { return fHeight; };
|
||||
|
||||
/** 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&);
|
||||
|
||||
/** These are called inside the per-device-layer loop for each draw call.
|
||||
When these are called, we have already applied any saveLayer operations,
|
||||
and are handling any looping from the paint, and any effects from the
|
||||
DrawFilter.
|
||||
*/
|
||||
virtual void drawPaint(const SkDraw&, const SkPaint& paint);
|
||||
virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
|
||||
size_t count, const SkPoint[],
|
||||
const SkPaint& paint);
|
||||
virtual void drawRect(const SkDraw&, const SkRect& r, const SkPaint& paint);
|
||||
virtual void drawPath(const SkDraw&, const SkPath& path,
|
||||
const SkPaint& paint);
|
||||
virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
|
||||
const SkMatrix& matrix, const SkPaint& paint);
|
||||
virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap, int x, int y,
|
||||
const SkPaint& paint);
|
||||
virtual void drawText(const SkDraw&, const void* text, size_t len,
|
||||
SkScalar x, SkScalar y, const SkPaint& paint);
|
||||
virtual void drawPosText(const SkDraw&, const void* text, size_t len,
|
||||
const SkScalar pos[], SkScalar constY,
|
||||
int scalarsPerPos, const SkPaint& paint);
|
||||
virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
|
||||
const SkPath& path, const SkMatrix* matrix,
|
||||
const SkPaint& paint);
|
||||
virtual void drawVertices(const SkDraw&, SkCanvas::VertexMode,
|
||||
int vertexCount, const SkPoint verts[],
|
||||
const SkPoint texs[], const SkColor colors[],
|
||||
SkXfermode* xmode, const uint16_t indices[],
|
||||
int indexCount, const SkPaint& paint);
|
||||
virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
|
||||
const SkPaint&);
|
||||
|
||||
// PDF specific methods.
|
||||
|
||||
/** Returns a reference to the resource dictionary for this device.
|
||||
*/
|
||||
const SkRefPtr<SkPDFDict>& getResourceDict();
|
||||
|
||||
/** Get the list of resouces (PDF objects) used on this page
|
||||
* @param resouceList A list to append the resouces to.
|
||||
*/
|
||||
void getResouces(SkTDArray<SkPDFObject*>* resouceList);
|
||||
|
||||
/** Returns the media box for this device.
|
||||
*/
|
||||
SkRefPtr<SkPDFArray> getMediaBox();
|
||||
|
||||
/** Returns a string with the page contents.
|
||||
*/
|
||||
SkString content();
|
||||
|
||||
private:
|
||||
int fWidth;
|
||||
int fHeight;
|
||||
SkRefPtr<SkPDFDict> fResourceDict;
|
||||
|
||||
SkRefPtr<SkPDFGraphicState> fCurrentGraphicState;
|
||||
SkColor fCurrentColor;
|
||||
SkScalar fCurrentTextScaleX;
|
||||
SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
|
||||
SkTDArray<SkPDFObject*> fXObjectResources;
|
||||
|
||||
SkString fContent;
|
||||
|
||||
// The last requested transforms from SkCanvas (setMatrixClip)
|
||||
SkMatrix fCurTransform;
|
||||
|
||||
// The transform currently in effect in the PDF content stream.
|
||||
SkMatrix fActiveTransform;
|
||||
|
||||
void updateGSFromPaint(const SkPaint& newPaint, SkString* textStaetUpdate);
|
||||
|
||||
void moveTo(SkScalar x, SkScalar y);
|
||||
void appendLine(SkScalar x, SkScalar y);
|
||||
void appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
|
||||
SkScalar ctl2X, SkScalar ctl2Y,
|
||||
SkScalar dstX, SkScalar dstY);
|
||||
void appendRectangle(SkScalar x, SkScalar y, SkScalar w, SkScalar h);
|
||||
void closePath();
|
||||
void strokePath();
|
||||
void internalDrawBitmap(const SkMatrix& matrix, const SkBitmap& bitmap,
|
||||
const SkPaint& paint);
|
||||
|
||||
void setTransform(const SkMatrix& matrix);
|
||||
void setNoTransform();
|
||||
void applyTempTransform(const SkMatrix& matrix);
|
||||
void removeTempTransform();
|
||||
void applyTransform(const SkMatrix& matrix);
|
||||
};
|
||||
|
||||
#endif
|
80
include/pdf/SkPDFGraphicState.h
Normal file
80
include/pdf/SkPDFGraphicState.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SkPDFGraphicState_DEFINED
|
||||
#define SkPDFGraphicState_DEFINED
|
||||
|
||||
#include "SkPaint.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkTemplates.h"
|
||||
#include "SkThread.h"
|
||||
|
||||
/** \class SkPDFGraphicState
|
||||
SkPaint objects roughly correspond to graphic state dictionaries that can
|
||||
be installed. So that a given dictionary is only output to the pdf file
|
||||
once, we want to canonicalize them. Static methods in this class manage
|
||||
a weakly referenced set of SkPDFGraphicState objects: when the last
|
||||
reference to a SkPDFGraphicState is removed, it removes itself from the
|
||||
static set of objects.
|
||||
|
||||
*/
|
||||
class SkPDFGraphicState : public SkPDFDict {
|
||||
public:
|
||||
virtual ~SkPDFGraphicState();
|
||||
|
||||
// Override emitObject and getOutputSize so that we can populate
|
||||
// the dictionary on demand.
|
||||
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
bool indirect);
|
||||
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
|
||||
|
||||
/** Get the graphic state for the passed SkPaint. The reference count of
|
||||
* the object is incremented and it is the caller's responsibility to
|
||||
* 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 paint The SkPaint to emulate.
|
||||
*/
|
||||
static SkPDFGraphicState* getGraphicStateForPaint(const SkPaint& paint);
|
||||
|
||||
private:
|
||||
const SkPaint fPaint;
|
||||
bool fPopulated;
|
||||
|
||||
class GSCanonicalEntry {
|
||||
public:
|
||||
SkPDFGraphicState* fGraphicState;
|
||||
const SkPaint* fPaint;
|
||||
|
||||
bool operator==(const GSCanonicalEntry& b) const;
|
||||
explicit GSCanonicalEntry(SkPDFGraphicState* gs)
|
||||
: fGraphicState(gs),
|
||||
fPaint(&gs->fPaint) {}
|
||||
explicit GSCanonicalEntry(const SkPaint* paint) : fPaint(paint) {}
|
||||
};
|
||||
|
||||
// This should be made a hash table if performance is a problem.
|
||||
static SkTDArray<GSCanonicalEntry>& canonicalPaints();
|
||||
static SkMutex& canonicalPaintsMutex();
|
||||
|
||||
explicit SkPDFGraphicState(const SkPaint& paint);
|
||||
|
||||
void populateDict();
|
||||
|
||||
static int find(const SkPaint& paint);
|
||||
};
|
||||
|
||||
#endif
|
67
include/pdf/SkPDFImage.h
Normal file
67
include/pdf/SkPDFImage.h
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef SkPDFImage_DEFINED
|
||||
#define SkPDFImage_DEFINED
|
||||
|
||||
#include "SkPDFStream.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkRefCnt.h"
|
||||
|
||||
class SkBitmap;
|
||||
class SkPaint;
|
||||
class SkPDFCatalog;
|
||||
|
||||
/** \class SkPDFImage
|
||||
|
||||
An image XObject.
|
||||
*/
|
||||
|
||||
// We could play the same trick here as is done in SkPDFGraphicState, storing
|
||||
// a copy of the Bitmap object (not the pixels), the pixel generation number,
|
||||
// and settings used from the paint to canonicalize image objects.
|
||||
class SkPDFImage : public SkPDFObject {
|
||||
public:
|
||||
/** Create a PDF image XObject. Entries for the image properties are
|
||||
* automatically added to the stream dictionary.
|
||||
* @param bitmap The image to use.
|
||||
* @param paint Used to calculate alpha, masks, etc.
|
||||
*/
|
||||
SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint);
|
||||
virtual ~SkPDFImage();
|
||||
|
||||
// The SkPDFObject interface.
|
||||
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
bool indirect);
|
||||
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
|
||||
|
||||
/** Add the value to the stream dictionary with the given key.
|
||||
* @param key The key for this dictionary entry.
|
||||
* @param value The value for this dictionary entry.
|
||||
*/
|
||||
void insert(SkPDFName* key, SkPDFObject* value);
|
||||
|
||||
/** Add the value to the stream dictionary with the given key.
|
||||
* @param key The text of the key for this dictionary entry.
|
||||
* @param value The value for this dictionary entry.
|
||||
*/
|
||||
void insert(const char key[], SkPDFObject* value);
|
||||
|
||||
private:
|
||||
SkRefPtr<SkPDFStream> fStream;
|
||||
};
|
||||
|
||||
#endif
|
@ -108,6 +108,27 @@ private:
|
||||
int32_t fValue;
|
||||
};
|
||||
|
||||
/** \class SkPDFBool
|
||||
|
||||
An boolean value in a PDF.
|
||||
*/
|
||||
class SkPDFBool : public SkPDFObject {
|
||||
public:
|
||||
/** Create a PDF boolean.
|
||||
* @param value true or false.
|
||||
*/
|
||||
explicit SkPDFBool(bool value);
|
||||
virtual ~SkPDFBool();
|
||||
|
||||
// The SkPDFObject interface.
|
||||
virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
bool indirect);
|
||||
virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
|
||||
|
||||
private:
|
||||
bool fValue;
|
||||
};
|
||||
|
||||
/** \class SkPDFScalar
|
||||
|
||||
A real number object in a PDF.
|
||||
|
493
src/pdf/SkPDFDevice.cpp
Normal file
493
src/pdf/SkPDFDevice.cpp
Normal file
@ -0,0 +1,493 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SkPDFDevice.h"
|
||||
|
||||
#include "SkColor.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkPDFImage.h"
|
||||
#include "SkPDFGraphicState.h"
|
||||
#include "SkPDFTypes.h"
|
||||
#include "SkPDFStream.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkString.h"
|
||||
|
||||
// Utility functions
|
||||
|
||||
namespace {
|
||||
|
||||
SkString toPDFColor(SkColor color) {
|
||||
SkASSERT(SkColorGetA(color) == 0xFF); // We handle alpha elsewhere.
|
||||
SkScalar colorMax = SkIntToScalar(0xFF);
|
||||
SkString result;
|
||||
result.appendScalar(SkIntToScalar(SkColorGetR(color))/colorMax);
|
||||
result.append(" ");
|
||||
result.appendScalar(SkIntToScalar(SkColorGetG(color))/colorMax);
|
||||
result.append(" ");
|
||||
result.appendScalar(SkIntToScalar(SkColorGetB(color))/colorMax);
|
||||
result.append(" ");
|
||||
return result;
|
||||
}
|
||||
|
||||
SkString StyleAndFillToPaintOperator(SkPaint::Style style,
|
||||
SkPath::FillType fillType) {
|
||||
SkString result;
|
||||
if (style == SkPaint::kFill_Style)
|
||||
result.append("f");
|
||||
else if (style == SkPaint::kStrokeAndFill_Style)
|
||||
result.append("B");
|
||||
else if (style == SkPaint::kStroke_Style)
|
||||
return SkString("S\n");
|
||||
|
||||
// Not supported yet.
|
||||
SkASSERT(fillType != SkPath::kInverseEvenOdd_FillType);
|
||||
SkASSERT(fillType != SkPath::kInverseWinding_FillType);
|
||||
if (fillType == SkPath::kEvenOdd_FillType)
|
||||
result.append("*");
|
||||
result.append("\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkDevice* SkPDFDeviceFactory::newDevice(SkBitmap::Config config,
|
||||
int width, int height,
|
||||
bool isOpaque, bool isForLayer) {
|
||||
return SkNEW_ARGS(SkPDFDevice, (width, height));
|
||||
}
|
||||
|
||||
SkPDFDevice::SkPDFDevice(int width, int height)
|
||||
: fWidth(width),
|
||||
fHeight(height),
|
||||
fCurrentColor(0),
|
||||
fCurrentTextScaleX(SK_Scalar1) {
|
||||
// Scale and translate to move the origin from the lower left to the upper
|
||||
// left.
|
||||
fCurTransform.setTranslate(0, height);
|
||||
fCurTransform.preScale(1, -1);
|
||||
fActiveTransform.reset();
|
||||
applyTransform(fCurTransform);
|
||||
|
||||
fContent.append("q\n");
|
||||
fCurTransform.reset();
|
||||
fActiveTransform.reset();
|
||||
}
|
||||
|
||||
SkPDFDevice::~SkPDFDevice() {
|
||||
fGraphicStateResources.unrefAll();
|
||||
fXObjectResources.unrefAll();
|
||||
}
|
||||
|
||||
void SkPDFDevice::setMatrixClip(const SkMatrix& matrix,
|
||||
const SkRegion& region) {
|
||||
// TODO(vandebo) handle clipping
|
||||
setTransform(matrix);
|
||||
fCurTransform = matrix;
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
|
||||
setNoTransform();
|
||||
|
||||
SkPaint newPaint = paint;
|
||||
newPaint.setStyle(SkPaint::kFill_Style);
|
||||
updateGSFromPaint(newPaint, NULL);
|
||||
|
||||
SkRect all = SkRect::MakeWH(width() + 1, height() + 1);
|
||||
drawRect(d, all, newPaint);
|
||||
setTransform(fCurTransform);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
|
||||
size_t count, const SkPoint* points,
|
||||
const SkPaint& paint) {
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
switch (mode) {
|
||||
case SkCanvas::kPolygon_PointMode:
|
||||
updateGSFromPaint(paint, NULL);
|
||||
moveTo(points[0].fX, points[0].fY);
|
||||
for (size_t i = 1; i < count; i++)
|
||||
appendLine(points[i].fX, points[i].fY);
|
||||
strokePath();
|
||||
break;
|
||||
case SkCanvas::kLines_PointMode:
|
||||
updateGSFromPaint(paint, NULL);
|
||||
for (size_t i = 0; i < count/2; i++) {
|
||||
moveTo(points[i * 2].fX, points[i * 2].fY);
|
||||
appendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY);
|
||||
strokePath();
|
||||
}
|
||||
break;
|
||||
case SkCanvas::kPoints_PointMode:
|
||||
if (paint.getStrokeCap() == SkPaint::kRound_Cap) {
|
||||
updateGSFromPaint(paint, NULL);
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
moveTo(points[i].fX, points[i].fY);
|
||||
strokePath();
|
||||
}
|
||||
} else {
|
||||
// PDF won't draw a single point with square/butt caps because
|
||||
// the orientation is ambiguous. Draw a rectangle instead.
|
||||
SkPaint newPaint = paint;
|
||||
newPaint.setStyle(SkPaint::kFill_Style);
|
||||
SkScalar strokeWidth = paint.getStrokeWidth();
|
||||
SkScalar halfStroke = strokeWidth * SK_ScalarHalf;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY,
|
||||
0, 0);
|
||||
r.inset(-halfStroke, -halfStroke);
|
||||
drawRect(d, r, newPaint);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SkASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& r,
|
||||
const SkPaint& paint) {
|
||||
if (paint.getPathEffect()) {
|
||||
// Draw a path instead.
|
||||
SkPath path;
|
||||
path.addRect(r);
|
||||
paint.getFillPath(path, &path);
|
||||
|
||||
SkPaint no_effect_paint(paint);
|
||||
SkSafeUnref(no_effect_paint.setPathEffect(NULL));
|
||||
drawPath(d, path, no_effect_paint);
|
||||
return;
|
||||
}
|
||||
updateGSFromPaint(paint, NULL);
|
||||
|
||||
// Skia has 0,0 at top left, pdf at bottom left. Do the right thing.
|
||||
SkScalar bottom = r.fBottom < r.fTop ? r.fBottom : r.fTop;
|
||||
appendRectangle(r.fLeft, bottom, r.width(), r.height());
|
||||
fContent.append(StyleAndFillToPaintOperator(paint.getStyle(),
|
||||
SkPath::kWinding_FillType));
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawPath(const SkDraw&, const SkPath& path,
|
||||
const SkPaint& paint) {
|
||||
SkASSERT(false);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawBitmap(const SkDraw&, const SkBitmap& bitmap,
|
||||
const SkMatrix& matrix, const SkPaint& paint) {
|
||||
SkMatrix scaled;
|
||||
// Adjust for origin flip.
|
||||
scaled.setScale(1, -1);
|
||||
scaled.postTranslate(0, 1);
|
||||
scaled.postConcat(fCurTransform);
|
||||
// Scale the image up from 1x1 to WxH.
|
||||
scaled.postScale(bitmap.width(), bitmap.height());
|
||||
scaled.postConcat(matrix);
|
||||
internalDrawBitmap(scaled, bitmap, paint);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
|
||||
int x, int y, const SkPaint& paint) {
|
||||
SkMatrix scaled;
|
||||
// Adjust for origin flip.
|
||||
scaled.setScale(1, -1);
|
||||
scaled.postTranslate(0, 1);
|
||||
// Scale the image up from 1x1 to WxH.
|
||||
scaled.postScale(bitmap.width(), -bitmap.height());
|
||||
scaled.postTranslate(x, y);
|
||||
internalDrawBitmap(scaled, bitmap, paint);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawText(const SkDraw&, const void* text, size_t len,
|
||||
SkScalar x, SkScalar y, const SkPaint& paint) {
|
||||
SkASSERT(false);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawPosText(const SkDraw&, const void* text, size_t len,
|
||||
const SkScalar pos[], SkScalar constY,
|
||||
int scalarsPerPos, const SkPaint& paint) {
|
||||
SkASSERT(false);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawTextOnPath(const SkDraw&, const void* text, size_t len,
|
||||
const SkPath& path, const SkMatrix* matrix,
|
||||
const SkPaint& paint) {
|
||||
SkASSERT(false);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
|
||||
int vertexCount, const SkPoint verts[],
|
||||
const SkPoint texs[], const SkColor colors[],
|
||||
SkXfermode* xmode, const uint16_t indices[],
|
||||
int indexCount, const SkPaint& paint) {
|
||||
SkASSERT(false);
|
||||
}
|
||||
|
||||
void SkPDFDevice::drawDevice(const SkDraw&, SkDevice*, int x, int y,
|
||||
const SkPaint&) {
|
||||
SkASSERT(false);
|
||||
}
|
||||
|
||||
const SkRefPtr<SkPDFDict>& SkPDFDevice::getResourceDict() {
|
||||
if (fResourceDict.get() == NULL) {
|
||||
fResourceDict = new SkPDFDict;
|
||||
fResourceDict->unref(); // SkRefPtr and new both took a reference.
|
||||
|
||||
if (fGraphicStateResources.count()) {
|
||||
SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
|
||||
extGState->unref(); // SkRefPtr and new both took a reference.
|
||||
for (int i = 0; i < fGraphicStateResources.count(); i++) {
|
||||
SkString nameString("G");
|
||||
nameString.appendS32(i);
|
||||
SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
|
||||
name->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFObjRef> gsRef =
|
||||
new SkPDFObjRef(fGraphicStateResources[i]);
|
||||
gsRef->unref(); // SkRefPtr and new both took a reference.
|
||||
extGState->insert(name.get(), gsRef.get());
|
||||
}
|
||||
fResourceDict->insert("ExtGState", extGState.get());
|
||||
}
|
||||
|
||||
if (fXObjectResources.count()) {
|
||||
SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
|
||||
xObjects->unref(); // SkRefPtr and new both took a reference.
|
||||
for (int i = 0; i < fXObjectResources.count(); i++) {
|
||||
SkString nameString("X");
|
||||
nameString.appendS32(i);
|
||||
SkRefPtr<SkPDFName> name = new SkPDFName(nameString);
|
||||
name->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFObjRef> xObjRef =
|
||||
new SkPDFObjRef(fXObjectResources[i]);
|
||||
xObjRef->unref(); // SkRefPtr and new both took a reference.
|
||||
xObjects->insert(name.get(), xObjRef.get());
|
||||
}
|
||||
fResourceDict->insert("XObject", xObjects.get());
|
||||
}
|
||||
}
|
||||
return fResourceDict;
|
||||
}
|
||||
|
||||
void SkPDFDevice::getResouces(SkTDArray<SkPDFObject*>* resouceList) {
|
||||
resouceList->setReserve(resouceList->count() +
|
||||
fGraphicStateResources.count() +
|
||||
fXObjectResources.count());
|
||||
for (int i = 0; i < fGraphicStateResources.count(); i++) {
|
||||
resouceList->push(fGraphicStateResources[i]);
|
||||
fGraphicStateResources[i]->ref();
|
||||
}
|
||||
for (int i = 0; i < fXObjectResources.count(); i++) {
|
||||
resouceList->push(fXObjectResources[i]);
|
||||
fXObjectResources[i]->ref();
|
||||
}
|
||||
}
|
||||
|
||||
SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() {
|
||||
SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
|
||||
zero->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFInt> width = new SkPDFInt(fWidth);
|
||||
width->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFInt> height = new SkPDFInt(fHeight);
|
||||
height->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
|
||||
mediaBox->unref(); // SkRefPtr and new both took a reference.
|
||||
mediaBox->reserve(4);
|
||||
mediaBox->append(zero.get());
|
||||
mediaBox->append(zero.get());
|
||||
mediaBox->append(width.get());
|
||||
mediaBox->append(height.get());
|
||||
return mediaBox;
|
||||
}
|
||||
|
||||
SkString SkPDFDevice::content() {
|
||||
SkString result = fContent;
|
||||
result.append("Q");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
// TODO(vandebo) handle these cases.
|
||||
#define PAINTCHECK(x,y) do { \
|
||||
if(newPaint.x() y) { \
|
||||
printf("!!" #x #y "\n"); \
|
||||
SkASSERT(false); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
void SkPDFDevice::updateGSFromPaint(const SkPaint& newPaint,
|
||||
SkString* textStateUpdate) {
|
||||
PAINTCHECK(getXfermode, != NULL);
|
||||
PAINTCHECK(getPathEffect, != NULL);
|
||||
PAINTCHECK(getMaskFilter, != NULL);
|
||||
PAINTCHECK(getShader, != NULL);
|
||||
PAINTCHECK(getColorFilter, != NULL);
|
||||
PAINTCHECK(isFakeBoldText, == true);
|
||||
PAINTCHECK(isUnderlineText, == true);
|
||||
PAINTCHECK(isStrikeThruText, == true);
|
||||
PAINTCHECK(getTextSkewX, != 0);
|
||||
|
||||
SkRefPtr<SkPDFGraphicState> newGraphicState =
|
||||
SkPDFGraphicState::getGraphicStateForPaint(newPaint);
|
||||
newGraphicState->unref(); // getGraphicState and SkRefPtr both took a ref.
|
||||
// newGraphicState has been canonicalized so we can directly compare
|
||||
// pointers.
|
||||
if (fCurrentGraphicState.get() != newGraphicState.get()) {
|
||||
int resourceIndex = fGraphicStateResources.find(newGraphicState.get());
|
||||
if (resourceIndex < 0) {
|
||||
resourceIndex = fGraphicStateResources.count();
|
||||
fGraphicStateResources.push(newGraphicState.get());
|
||||
newGraphicState->ref();
|
||||
}
|
||||
fContent.append("/G");
|
||||
fContent.appendS32(resourceIndex);
|
||||
fContent.append(" gs\n");
|
||||
fCurrentGraphicState = newGraphicState;
|
||||
}
|
||||
|
||||
SkColor newColor = newPaint.getColor();
|
||||
newColor = SkColorSetA(newColor, 0xFF);
|
||||
if (fCurrentColor != newColor) {
|
||||
SkString colorString = toPDFColor(newColor);
|
||||
fContent.append(colorString);
|
||||
fContent.append("RG ");
|
||||
fContent.append(colorString);
|
||||
fContent.append("rg\n");
|
||||
fCurrentColor = newColor;
|
||||
}
|
||||
|
||||
if (textStateUpdate != NULL &&
|
||||
fCurrentTextScaleX != newPaint.getTextScaleX()) {
|
||||
SkScalar scale = newPaint.getTextScaleX();
|
||||
SkScalar pdfScale = scale * 100;
|
||||
textStateUpdate->appendScalar(pdfScale);
|
||||
textStateUpdate->append(" Tz\n");
|
||||
fCurrentTextScaleX = scale;
|
||||
}
|
||||
}
|
||||
|
||||
void SkPDFDevice::moveTo(SkScalar x, SkScalar y) {
|
||||
fContent.appendScalar(x);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(y);
|
||||
fContent.append(" m\n");
|
||||
}
|
||||
|
||||
void SkPDFDevice::appendLine(SkScalar x, SkScalar y) {
|
||||
fContent.appendScalar(x);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(y);
|
||||
fContent.append(" l\n");
|
||||
}
|
||||
|
||||
void SkPDFDevice::appendCubic(SkScalar ctl1X, SkScalar ctl1Y,
|
||||
SkScalar ctl2X, SkScalar ctl2Y,
|
||||
SkScalar dstX, SkScalar dstY) {
|
||||
SkString cmd("y\n");
|
||||
fContent.appendScalar(ctl1X);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(ctl1Y);
|
||||
fContent.append(" ");
|
||||
if (ctl2X != dstX || ctl2Y != dstY) {
|
||||
cmd.set("c\n");
|
||||
fContent.appendScalar(ctl2X);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(ctl2Y);
|
||||
fContent.append(" ");
|
||||
}
|
||||
fContent.appendScalar(dstX);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(dstY);
|
||||
fContent.append(cmd);
|
||||
}
|
||||
|
||||
void SkPDFDevice::appendRectangle(SkScalar x, SkScalar y,
|
||||
SkScalar w, SkScalar h) {
|
||||
fContent.appendScalar(x);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(y);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(w);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(h);
|
||||
fContent.append(" re\n");
|
||||
}
|
||||
|
||||
void SkPDFDevice::closePath() {
|
||||
fContent.append("h\n");
|
||||
}
|
||||
|
||||
void SkPDFDevice::strokePath() {
|
||||
fContent.append(StyleAndFillToPaintOperator(SkPaint::kStroke_Style,
|
||||
SkPath::kWinding_FillType));
|
||||
}
|
||||
|
||||
void SkPDFDevice::internalDrawBitmap(const SkMatrix& matrix,
|
||||
const SkBitmap& bitmap,
|
||||
const SkPaint& paint) {
|
||||
setTransform(matrix);
|
||||
SkPDFImage* image = new SkPDFImage(bitmap, paint);
|
||||
fXObjectResources.push(image); // Transfer reference.
|
||||
fContent.append("/X");
|
||||
fContent.appendS32(fXObjectResources.count() - 1);
|
||||
fContent.append(" Do\n");
|
||||
setTransform(fCurTransform);
|
||||
}
|
||||
|
||||
void SkPDFDevice::setTransform(const SkMatrix& m) {
|
||||
setNoTransform();
|
||||
applyTransform(m);
|
||||
}
|
||||
|
||||
void SkPDFDevice::setNoTransform() {
|
||||
if (fActiveTransform.getType() == SkMatrix::kIdentity_Mask)
|
||||
return;
|
||||
fContent.append("Q q "); // Restore the default transform and save it.
|
||||
fCurrentGraphicState = NULL;
|
||||
fActiveTransform.reset();
|
||||
}
|
||||
|
||||
void SkPDFDevice::applyTempTransform(const SkMatrix& m) {
|
||||
fContent.append("q ");
|
||||
applyTransform(m);
|
||||
}
|
||||
|
||||
void SkPDFDevice::removeTempTransform() {
|
||||
fContent.append("Q\n");
|
||||
fActiveTransform = fCurTransform;
|
||||
}
|
||||
|
||||
void SkPDFDevice::applyTransform(const SkMatrix& m) {
|
||||
if (m == fActiveTransform)
|
||||
return;
|
||||
SkASSERT((m.getType() & SkMatrix::kPerspective_Mask) == 0);
|
||||
|
||||
fContent.appendScalar(m[SkMatrix::kMScaleX]);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(m[SkMatrix::kMSkewY]);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(m[SkMatrix::kMSkewX]);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(m[SkMatrix::kMScaleY]);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(m[SkMatrix::kMTransX]);
|
||||
fContent.append(" ");
|
||||
fContent.appendScalar(m[SkMatrix::kMTransY]);
|
||||
fContent.append(" cm\n");
|
||||
fActiveTransform = m;
|
||||
}
|
163
src/pdf/SkPDFGraphicState.cpp
Normal file
163
src/pdf/SkPDFGraphicState.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SkPDFGraphicState.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTypeface.h"
|
||||
|
||||
SkPDFGraphicState::~SkPDFGraphicState() {
|
||||
SkAutoMutexAcquire lock(canonicalPaintsMutex());
|
||||
int index = find(fPaint);
|
||||
SkASSERT(index >= 0);
|
||||
canonicalPaints().removeShuffle(index);
|
||||
}
|
||||
|
||||
void SkPDFGraphicState::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
bool indirect) {
|
||||
populateDict();
|
||||
SkPDFDict::emitObject(stream, catalog, indirect);
|
||||
}
|
||||
|
||||
size_t SkPDFGraphicState::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
|
||||
populateDict();
|
||||
return SkPDFDict::getOutputSize(catalog, indirect);
|
||||
}
|
||||
|
||||
// static
|
||||
SkTDArray<SkPDFGraphicState::GSCanonicalEntry>&
|
||||
SkPDFGraphicState::canonicalPaints() {
|
||||
// This initialization is only thread safe with gcc.
|
||||
static SkTDArray<SkPDFGraphicState::GSCanonicalEntry> gCanonicalPaints;
|
||||
return gCanonicalPaints;
|
||||
}
|
||||
|
||||
// static
|
||||
SkMutex& SkPDFGraphicState::canonicalPaintsMutex() {
|
||||
// This initialization is only thread safe with gcc.
|
||||
static SkMutex gCanonicalPaintsMutex;
|
||||
return gCanonicalPaintsMutex;
|
||||
}
|
||||
|
||||
// static
|
||||
SkPDFGraphicState* SkPDFGraphicState::getGraphicStateForPaint(
|
||||
const SkPaint& paint) {
|
||||
SkAutoMutexAcquire lock(canonicalPaintsMutex());
|
||||
int index = find(paint);
|
||||
if (index >= 0) {
|
||||
canonicalPaints()[index].fGraphicState->ref();
|
||||
return canonicalPaints()[index].fGraphicState;
|
||||
}
|
||||
GSCanonicalEntry newEntry(new SkPDFGraphicState(paint));
|
||||
canonicalPaints().push(newEntry);
|
||||
return newEntry.fGraphicState;
|
||||
}
|
||||
|
||||
// static
|
||||
int SkPDFGraphicState::find(const SkPaint& paint) {
|
||||
GSCanonicalEntry search(&paint);
|
||||
return canonicalPaints().find(search);
|
||||
}
|
||||
|
||||
SkPDFGraphicState::SkPDFGraphicState(const SkPaint& paint)
|
||||
: fPaint(paint),
|
||||
fPopulated(false) {
|
||||
}
|
||||
|
||||
// populateDict and operator== have to stay in sync with each other.
|
||||
void SkPDFGraphicState::populateDict() {
|
||||
if (!fPopulated) {
|
||||
fPopulated = true;
|
||||
SkRefPtr<SkPDFName> typeName = new SkPDFName("ExtGState");
|
||||
typeName->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("Type", typeName.get());
|
||||
|
||||
SkScalar maxAlpha = SkIntToScalar(0xFF);
|
||||
SkRefPtr<SkPDFScalar> alpha =
|
||||
new SkPDFScalar(SkColorGetA(fPaint.getColor())/maxAlpha);
|
||||
alpha->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("CA", alpha.get());
|
||||
insert("ca", alpha.get());
|
||||
|
||||
SkASSERT(SkPaint::kButt_Cap == 0);
|
||||
SkASSERT(SkPaint::kRound_Cap == 1);
|
||||
SkASSERT(SkPaint::kSquare_Cap == 2);
|
||||
SkASSERT(fPaint.getStrokeCap() >= 0 && fPaint.getStrokeCap() <= 2);
|
||||
SkRefPtr<SkPDFInt> strokeCap = new SkPDFInt(fPaint.getStrokeCap());
|
||||
strokeCap->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("LC", strokeCap.get());
|
||||
|
||||
SkASSERT(SkPaint::kMiter_Join == 0);
|
||||
SkASSERT(SkPaint::kRound_Join == 1);
|
||||
SkASSERT(SkPaint::kBevel_Join == 2);
|
||||
SkASSERT(fPaint.getStrokeJoin() >= 0 && fPaint.getStrokeJoin() <= 2);
|
||||
SkRefPtr<SkPDFInt> strokeJoin = new SkPDFInt(fPaint.getStrokeJoin());
|
||||
strokeJoin->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("LJ", strokeJoin.get());
|
||||
|
||||
/* TODO(vandebo) Font.
|
||||
if (fPaint.getTypeFace() != NULL) {
|
||||
SkRefPtr<SkPDFTypeFace> typeFace =
|
||||
SkPDFTypeFace::getFontForTypeFace(fPaint.getTypeFace);
|
||||
SkRefPtr<SkPDFObjRef> typeFaceRef = new SkPDFObjRef(typeFace.get());
|
||||
fontRef->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFScalar> fontSize =
|
||||
new SkPDFScalar(fPaint.getTetSize());
|
||||
fontSize->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFArray> font = new SkPDFArray();
|
||||
font->unref(); // SkRefPtr and new both took a reference.
|
||||
font->reserve(2);
|
||||
font->append(typeFaceRef.get());
|
||||
font->append(fontSize.get());
|
||||
insert("LJ", font.get());
|
||||
}
|
||||
*/
|
||||
|
||||
SkRefPtr<SkPDFScalar> strokeWidth =
|
||||
new SkPDFScalar(fPaint.getStrokeWidth());
|
||||
strokeWidth->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("LW", strokeWidth.get());
|
||||
|
||||
SkRefPtr<SkPDFScalar> strokeMiterLimit = new SkPDFScalar(
|
||||
fPaint.getStrokeMiter());
|
||||
strokeMiterLimit->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("ML", strokeWidth.get());
|
||||
|
||||
// Turn on automatic stroke adjustment.
|
||||
SkRefPtr<SkPDFBool> trueVal = new SkPDFBool(true);
|
||||
trueVal->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("SA", trueVal.get());
|
||||
}
|
||||
}
|
||||
|
||||
// We're only interested in some fields of the SkPaint, so we have a custom
|
||||
// operator== function.
|
||||
bool SkPDFGraphicState::GSCanonicalEntry::operator==(
|
||||
const SkPDFGraphicState::GSCanonicalEntry& gs) const {
|
||||
const SkPaint* a = fPaint;
|
||||
const SkPaint* b = gs.fPaint;
|
||||
SkASSERT(a != NULL);
|
||||
SkASSERT(b != NULL);
|
||||
SkTypeface* aFace = a->getTypeface();
|
||||
SkTypeface* bFace = b->getTypeface();
|
||||
return SkColorGetA(a->getColor()) == SkColorGetA(b->getColor()) &&
|
||||
a->getStrokeCap() == b->getStrokeCap() &&
|
||||
a->getStrokeJoin() == b->getStrokeJoin() &&
|
||||
a->getTextSize() == b->getTextSize() &&
|
||||
a->getStrokeWidth() == b->getStrokeWidth() &&
|
||||
a->getStrokeMiter() == b->getStrokeMiter() &&
|
||||
(aFace == NULL) == (bFace == NULL) &&
|
||||
(aFace == NULL || aFace->uniqueID() == bFace->uniqueID());
|
||||
}
|
248
src/pdf/SkPDFImage.cpp
Normal file
248
src/pdf/SkPDFImage.cpp
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "SkPDFImage.h"
|
||||
|
||||
#include "SkBitmap.h"
|
||||
#include "SkColor.h"
|
||||
#include "SkColorPriv.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkPackBits.h"
|
||||
#include "SkPDFCatalog.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkString.h"
|
||||
#include "SkUnPreMultiply.h"
|
||||
|
||||
namespace {
|
||||
|
||||
SkMemoryStream* extractImageData(const SkBitmap& bitmap) {
|
||||
SkMemoryStream* result;
|
||||
|
||||
switch (bitmap.getConfig()) {
|
||||
case SkBitmap::kIndex8_Config:
|
||||
result = new SkMemoryStream(bitmap.getPixels(), bitmap.getSize(),
|
||||
true);
|
||||
break;
|
||||
case SkBitmap::kRLE_Index8_Config: {
|
||||
result = new SkMemoryStream(bitmap.getSize());
|
||||
const SkBitmap::RLEPixels* rle =
|
||||
(const SkBitmap::RLEPixels*)bitmap.getPixels();
|
||||
uint8_t* dst = (uint8_t*)result->getMemoryBase();
|
||||
const int width = bitmap.width();
|
||||
for (int y = 0; y < bitmap.height(); y++) {
|
||||
SkPackBits::Unpack8(rle->packedAtY(y), width, dst);
|
||||
dst += width;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SkBitmap::kARGB_4444_Config: {
|
||||
const int width = bitmap.width();
|
||||
const int rowBytes = (width * 3 + 1) / 2;
|
||||
result = new SkMemoryStream(rowBytes * bitmap.height());
|
||||
uint8_t* dst = (uint8_t*)result->getMemoryBase();
|
||||
for (int y = 0; y < bitmap.height(); y++) {
|
||||
uint16_t* src = bitmap.getAddr16(0, y);
|
||||
for (int x = 0; x < width; x += 2) {
|
||||
dst[0] = (SkGetPackedR4444(src[0]) << 4) |
|
||||
SkGetPackedG4444(src[0]);
|
||||
dst[1] = (SkGetPackedB4444(src[0]) << 4) |
|
||||
SkGetPackedR4444(src[1]);
|
||||
dst[2] = (SkGetPackedG4444(src[1]) << 4) |
|
||||
SkGetPackedB4444(src[1]);
|
||||
src += 2;
|
||||
dst += 3;
|
||||
}
|
||||
if (width & 1) {
|
||||
dst[0] = (SkGetPackedR4444(src[0]) << 4) |
|
||||
SkGetPackedG4444(src[0]);
|
||||
dst[1] = (SkGetPackedB4444(src[0]) << 4);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SkBitmap::kRGB_565_Config: {
|
||||
const int width = bitmap.width();
|
||||
const int rowBytes = width * 3;
|
||||
result = new SkMemoryStream(rowBytes * bitmap.height());
|
||||
uint8_t* dst = (uint8_t*)result->getMemoryBase();
|
||||
for (int y = 0; y < bitmap.height(); y++) {
|
||||
uint16_t* src = bitmap.getAddr16(0, y);
|
||||
for (int x = 0; x < width; x++) {
|
||||
dst[0] = SkGetPackedR16(src[0]);
|
||||
dst[1] = SkGetPackedG16(src[0]);
|
||||
dst[2] = SkGetPackedB16(src[0]);
|
||||
src++;
|
||||
dst += 3;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SkBitmap::kARGB_8888_Config: {
|
||||
const int width = bitmap.width();
|
||||
const int rowBytes = width * 3;
|
||||
result = new SkMemoryStream(rowBytes * bitmap.height());
|
||||
uint8_t* dst = (uint8_t*)result->getMemoryBase();
|
||||
for (int y = 0; y < bitmap.height(); y++) {
|
||||
uint32_t* src = bitmap.getAddr32(0, y);
|
||||
for (int x = 0; x < width; x++) {
|
||||
dst[0] = SkGetPackedR32(src[0]);
|
||||
dst[1] = SkGetPackedG32(src[0]);
|
||||
dst[2] = SkGetPackedB32(src[0]);
|
||||
src++;
|
||||
dst += 3;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
SkASSERT(false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
|
||||
SkPDFArray* result = new SkPDFArray();
|
||||
result->reserve(4);
|
||||
SkRefPtr<SkPDFName> indexedName = new SkPDFName("Indexed");
|
||||
indexedName->unref(); // SkRefPtr and new both took a reference.
|
||||
result->append(indexedName.get());
|
||||
|
||||
SkRefPtr<SkPDFName> rgbName = new SkPDFName("DeviceRGB");
|
||||
rgbName->unref(); // SkRefPtr and new both took a reference.
|
||||
result->append(rgbName.get());
|
||||
|
||||
rgbName->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFInt> countValue = new SkPDFInt(table->count() - 1);
|
||||
result->append(countValue.get());
|
||||
|
||||
// Potentially, this could be represented in fewer bytes with a stream.
|
||||
// Max size as a string is 1.5k.
|
||||
SkString index;
|
||||
for (int i = 0; i < table->count(); i++) {
|
||||
char buf[3];
|
||||
SkColor color = SkUnPreMultiply::PMColorToColor((*table)[i]);
|
||||
buf[0] = SkGetPackedR32(color);
|
||||
buf[1] = SkGetPackedG32(color);
|
||||
buf[2] = SkGetPackedB32(color);
|
||||
index.append(buf, 3);
|
||||
}
|
||||
SkRefPtr<SkPDFString> indexValue = new SkPDFString(index);
|
||||
indexValue->unref(); // SkRefPtr and new both took a reference.
|
||||
result->append(indexValue.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
||||
SkPDFImage::SkPDFImage(const SkBitmap& bitmap, const SkPaint& paint) {
|
||||
SkBitmap::Config config = bitmap.getConfig();
|
||||
|
||||
// TODO(vandebo) Handle alpha and alpha only images correctly.
|
||||
SkASSERT(config == SkBitmap::kRGB_565_Config ||
|
||||
config == SkBitmap::kARGB_4444_Config ||
|
||||
config == SkBitmap::kARGB_8888_Config ||
|
||||
config == SkBitmap::kIndex8_Config ||
|
||||
config == SkBitmap::kRLE_Index8_Config);
|
||||
|
||||
SkMemoryStream* image_data = extractImageData(bitmap);
|
||||
SkAutoUnref image_data_unref(image_data);
|
||||
fStream = new SkPDFStream(image_data);
|
||||
fStream->unref(); // SkRefPtr and new both took a reference.
|
||||
|
||||
SkRefPtr<SkPDFName> typeValue = new SkPDFName("XObject");
|
||||
typeValue->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("Type", typeValue.get());
|
||||
|
||||
SkRefPtr<SkPDFName> subTypeValue = new SkPDFName("Image");
|
||||
subTypeValue->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("Subtype", subTypeValue.get());
|
||||
|
||||
SkRefPtr<SkPDFInt> widthValue = new SkPDFInt(bitmap.width());
|
||||
widthValue->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("Width", widthValue.get());
|
||||
|
||||
SkRefPtr<SkPDFInt> heightValue = new SkPDFInt(bitmap.height());
|
||||
heightValue->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("Height", heightValue.get());
|
||||
|
||||
// if (!image mask) {
|
||||
SkRefPtr<SkPDFObject> colorSpaceValue;
|
||||
if (config == SkBitmap::kIndex8_Config ||
|
||||
config == SkBitmap::kRLE_Index8_Config) {
|
||||
colorSpaceValue = makeIndexedColorSpace(bitmap.getColorTable());
|
||||
} else {
|
||||
colorSpaceValue = new SkPDFName("DeviceRGB");
|
||||
}
|
||||
colorSpaceValue->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("ColorSpace", colorSpaceValue.get());
|
||||
// }
|
||||
|
||||
int bitsPerComp = bitmap.bytesPerPixel() * 2;
|
||||
if (bitsPerComp == 0) {
|
||||
SkASSERT(config == SkBitmap::kA1_Config);
|
||||
bitsPerComp = 1;
|
||||
} else if (bitsPerComp == 2 ||
|
||||
(bitsPerComp == 4 && config == SkBitmap::kRGB_565_Config)) {
|
||||
bitsPerComp = 8;
|
||||
}
|
||||
SkRefPtr<SkPDFInt> bitsPerCompValue = new SkPDFInt(bitsPerComp);
|
||||
bitsPerCompValue->unref(); // SkRefPtr and new both took a reference.
|
||||
insert("BitsPerComponent", bitsPerCompValue.get());
|
||||
|
||||
if (config == SkBitmap::kRGB_565_Config) {
|
||||
SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
|
||||
zeroVal->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFScalar> scale5Val = new SkPDFScalar(8.2258); // 255/2^5-1
|
||||
scale5Val->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFScalar> scale6Val = new SkPDFScalar(4.0476); // 255/2^6-1
|
||||
scale6Val->unref(); // SkRefPtr and new both took a reference.
|
||||
SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
|
||||
decodeValue->unref(); // SkRefPtr and new both took a reference.
|
||||
decodeValue->reserve(6);
|
||||
decodeValue->append(zeroVal.get());
|
||||
decodeValue->append(scale5Val.get());
|
||||
decodeValue->append(zeroVal.get());
|
||||
decodeValue->append(scale6Val.get());
|
||||
decodeValue->append(zeroVal.get());
|
||||
decodeValue->append(scale5Val.get());
|
||||
insert("Decode", decodeValue.get());
|
||||
}
|
||||
}
|
||||
|
||||
SkPDFImage::~SkPDFImage() {}
|
||||
|
||||
void SkPDFImage::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
bool indirect) {
|
||||
if (indirect)
|
||||
return emitIndirectObject(stream, catalog);
|
||||
|
||||
fStream->emitObject(stream, catalog, indirect);
|
||||
}
|
||||
|
||||
size_t SkPDFImage::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
|
||||
if (indirect)
|
||||
return getIndirectOutputSize(catalog);
|
||||
|
||||
return fStream->getOutputSize(catalog, indirect);
|
||||
}
|
||||
|
||||
void SkPDFImage::insert(SkPDFName* key, SkPDFObject* value) {
|
||||
fStream->insert(key, value);
|
||||
}
|
||||
|
||||
void SkPDFImage::insert(const char key[], SkPDFObject* value) {
|
||||
fStream->insert(key, value);
|
||||
}
|
@ -35,7 +35,7 @@ void SkPDFStream::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
fDict.emitObject(stream, catalog, false);
|
||||
stream->writeText(" stream\n");
|
||||
stream->write(fData->getMemoryBase(), fData->read(NULL, 0));
|
||||
stream->writeText("endstream");
|
||||
stream->writeText("\nendstream");
|
||||
}
|
||||
|
||||
size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
|
||||
@ -43,7 +43,7 @@ size_t SkPDFStream::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
|
||||
return getIndirectOutputSize(catalog);
|
||||
|
||||
return fDict.getOutputSize(catalog, false) +
|
||||
strlen(" stream\nendstream") + fData->read(NULL, 0);
|
||||
strlen(" stream\n\nendstream") + fData->read(NULL, 0);
|
||||
}
|
||||
|
||||
void SkPDFStream::insert(SkPDFName* key, SkPDFObject* value) {
|
||||
|
@ -64,6 +64,26 @@ void SkPDFInt::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
stream->writeDecAsText(fValue);
|
||||
}
|
||||
|
||||
SkPDFBool::SkPDFBool(bool value) : fValue(value) {}
|
||||
SkPDFBool::~SkPDFBool() {}
|
||||
|
||||
void SkPDFBool::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
|
||||
bool indirect) {
|
||||
SkASSERT(!indirect);
|
||||
if (fValue) {
|
||||
stream->writeText("true");
|
||||
} else {
|
||||
stream->writeText("false");
|
||||
}
|
||||
}
|
||||
|
||||
size_t SkPDFBool::getOutputSize(SkPDFCatalog* catalog, bool indirect) {
|
||||
SkASSERT(!indirect);
|
||||
if (fValue)
|
||||
return strlen("true");
|
||||
return strlen("false");
|
||||
}
|
||||
|
||||
SkPDFScalar::SkPDFScalar(SkScalar value) : fValue(value) {}
|
||||
SkPDFScalar::~SkPDFScalar() {}
|
||||
|
||||
|
@ -1,6 +1,9 @@
|
||||
SOURCE := \
|
||||
SkPDFCatalog.cpp \
|
||||
SkPDFDevice.cpp \
|
||||
SkPDFDocument.cpp \
|
||||
SkPDFGraphicState.cpp \
|
||||
SkPDFImage.cpp \
|
||||
SkPDFPage.cpp \
|
||||
SkPDFStream.cpp \
|
||||
SkPDFTypes.cpp \
|
||||
|
@ -175,12 +175,12 @@ static void TestPDFPrimitives(skiatest::Reporter* reporter) {
|
||||
SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get());
|
||||
stream->unref(); // SkRefPtr and new both took a reference.
|
||||
CheckObjectOutput(reporter, stream.get(),
|
||||
"<</Length 12\n>> stream\nTest\nFoo\tBarendstream",
|
||||
"<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream",
|
||||
true);
|
||||
stream->insert(n1.get(), int42.get());
|
||||
CheckObjectOutput(reporter, stream.get(),
|
||||
"<</Length 12\n/n1 42\n>> stream\nTest\nFoo\tBar"
|
||||
"endstream",
|
||||
"\nendstream",
|
||||
true);
|
||||
|
||||
TestCatalog(reporter);
|
||||
|
Loading…
Reference in New Issue
Block a user