DirectWrite font host for skia.
https://codereview.appspot.com/5417063/
git-svn-id: http://skia.googlecode.com/svn/trunk@5128 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/utils/SkFloatUtils.h b/src/utils/SkFloatUtils.h
new file mode 100644
index 0000000..101aac7
--- /dev/null
+++ b/src/utils/SkFloatUtils.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkFloatUtils_DEFINED
+#define SkFloatUtils_DEFINED
+
+#include "SkTypes.h"
+#include <limits.h>
+#include <float.h>
+
+template <size_t size>
+class SkTypeWithSize {
+public:
+ // Prevents using SkTypeWithSize<N> with non-specialized N.
+ typedef void UInt;
+};
+
+template <>
+class SkTypeWithSize<32> {
+public:
+ typedef uint32_t UInt;
+};
+
+template <>
+class SkTypeWithSize<64> {
+public:
+ typedef uint64_t UInt;
+};
+
+template <typename RawType>
+struct SkNumericLimits {
+ static const int digits = 0;
+};
+
+template <>
+struct SkNumericLimits<double> {
+ static const int digits = DBL_MANT_DIG;
+};
+
+template <>
+struct SkNumericLimits<float> {
+ static const int digits = FLT_MANT_DIG;
+};
+
+//See
+//http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison/3423299#3423299
+//http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h
+//http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
+
+template <typename RawType, unsigned int ULPs>
+class SkFloatingPoint {
+public:
+ /** Bits is a unsigned integer the same size as the floating point number. */
+ typedef typename SkTypeWithSize<sizeof(RawType) * CHAR_BIT>::UInt Bits;
+
+ /** # of bits in a number. */
+ static const size_t kBitCount = CHAR_BIT * sizeof(RawType);
+
+ /** # of fraction bits in a number. */
+ static const size_t kFractionBitCount = SkNumericLimits<RawType>::digits - 1;
+
+ /** # of exponent bits in a number. */
+ static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
+
+ /** The mask for the sign bit. */
+ static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
+
+ /** The mask for the fraction bits. */
+ static const Bits kFractionBitMask =
+ ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
+
+ /** The mask for the exponent bits. */
+ static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
+
+ /** How many ULP's (Units in the Last Place) to tolerate when comparing. */
+ static const size_t kMaxUlps = ULPs;
+
+ /**
+ * Constructs a FloatingPoint from a raw floating-point number.
+ *
+ * On an Intel CPU, passing a non-normalized NAN (Not a Number)
+ * around may change its bits, although the new value is guaranteed
+ * to be also a NAN. Therefore, don't expect this constructor to
+ * preserve the bits in x when x is a NAN.
+ */
+ explicit SkFloatingPoint(const RawType& x) { fU.value = x; }
+
+ /** Returns the exponent bits of this number. */
+ Bits exponent_bits() const { return kExponentBitMask & fU.bits; }
+
+ /** Returns the fraction bits of this number. */
+ Bits fraction_bits() const { return kFractionBitMask & fU.bits; }
+
+ /** Returns true iff this is NAN (not a number). */
+ bool is_nan() const {
+ // It's a NAN if both of the folloowing are true:
+ // * the exponent bits are all ones
+ // * the fraction bits are not all zero.
+ return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
+ }
+
+ /**
+ * Returns true iff this number is at most kMaxUlps ULP's away from ths.
+ * In particular, this function:
+ * - returns false if either number is (or both are) NAN.
+ * - treats really large numbers as almost equal to infinity.
+ * - thinks +0.0 and -0.0 are 0 DLP's apart.
+ */
+ bool AlmostEquals(const SkFloatingPoint& rhs) const {
+ // Any comparison operation involving a NAN must return false.
+ if (is_nan() || rhs.is_nan()) return false;
+
+ const Bits dist = DistanceBetweenSignAndMagnitudeNumbers(fU.bits,
+ rhs.fU.bits);
+ //SkDEBUGF(("(%f, %f, %d) ", u_.value_, rhs.u_.value_, dist));
+ return dist <= kMaxUlps;
+ }
+
+private:
+ /** The data type used to store the actual floating-point number. */
+ union FloatingPointUnion {
+ /** The raw floating-point number. */
+ RawType value;
+ /** The bits that represent the number. */
+ Bits bits;
+ };
+
+ /**
+ * Converts an integer from the sign-and-magnitude representation to
+ * the biased representation. More precisely, let N be 2 to the
+ * power of (kBitCount - 1), an integer x is represented by the
+ * unsigned number x + N.
+ *
+ * For instance,
+ *
+ * -N + 1 (the most negative number representable using
+ * sign-and-magnitude) is represented by 1;
+ * 0 is represented by N; and
+ * N - 1 (the biggest number representable using
+ * sign-and-magnitude) is represented by 2N - 1.
+ *
+ * Read http://en.wikipedia.org/wiki/Signed_number_representations
+ * for more details on signed number representations.
+ */
+ static Bits SignAndMagnitudeToBiased(const Bits &sam) {
+ if (kSignBitMask & sam) {
+ // sam represents a negative number.
+ return ~sam + 1;
+ } else {
+ // sam represents a positive number.
+ return kSignBitMask | sam;
+ }
+ }
+
+ /**
+ * Given two numbers in the sign-and-magnitude representation,
+ * returns the distance between them as an unsigned number.
+ */
+ static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
+ const Bits &sam2) {
+ const Bits biased1 = SignAndMagnitudeToBiased(sam1);
+ const Bits biased2 = SignAndMagnitudeToBiased(sam2);
+ return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
+ }
+
+ FloatingPointUnion fU;
+};
+
+#endif
diff --git a/src/utils/win/SkDWriteFontFileStream.cpp b/src/utils/win/SkDWriteFontFileStream.cpp
new file mode 100644
index 0000000..1569158
--- /dev/null
+++ b/src/utils/win/SkDWriteFontFileStream.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+#include "SkDWriteFontFileStream.h"
+#include "SkHRESULT.h"
+
+#include <dwrite.h>
+#include <limits>
+
+///////////////////////////////////////////////////////////////////////////////
+// SkIDWriteFontFileStream
+
+SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream)
+ : fFontFileStream(fontFileStream)
+ , fPos(0)
+ , fLockedMemory(NULL)
+ , fFragmentLock(NULL) {
+ fontFileStream->AddRef();
+}
+
+SkDWriteFontFileStream::~SkDWriteFontFileStream() {
+ if (fFragmentLock) {
+ fFontFileStream->ReleaseFileFragment(fFragmentLock);
+ }
+}
+
+const void* SkDWriteFontFileStream::getMemoryBase() {
+ if (fLockedMemory) {
+ return fLockedMemory;
+ }
+
+ UINT64 fileSize;
+ HRNM(fFontFileStream->GetFileSize(&fileSize), "Could not get file size");
+ HRNM(fFontFileStream->ReadFileFragment(&fLockedMemory, 0, fileSize, &fFragmentLock),
+ "Could not lock file fragment.");
+ return fLockedMemory;
+}
+
+bool SkDWriteFontFileStream::rewind() {
+ fPos = 0;
+ return true;
+}
+
+size_t SkDWriteFontFileStream::read(void* buffer, size_t size) {
+ HRESULT hr = S_OK;
+
+ if (NULL == buffer) {
+ UINT64 realFileSize = 0;
+ hr = fFontFileStream->GetFileSize(&realFileSize);
+ if (realFileSize > (std::numeric_limits<size_t>::max)()) {
+ return 0;
+ }
+ size_t fileSize = static_cast<size_t>(realFileSize);
+ if (size == 0) {
+ return fileSize;
+ } else {
+ if (fPos + size > fileSize) {
+ size_t skipped = fileSize - fPos;
+ fPos = fileSize;
+ return skipped;
+ } else {
+ fPos += size;
+ return size;
+ }
+ }
+ }
+
+ const void* start;
+ void* fragmentLock;
+ hr = fFontFileStream->ReadFileFragment(&start, fPos, size, &fragmentLock);
+ if (SUCCEEDED(hr)) {
+ memcpy(buffer, start, size);
+ fFontFileStream->ReleaseFileFragment(fragmentLock);
+ fPos += size;
+ return size;
+ }
+
+ //The read may have failed because we asked for too much data.
+ UINT64 realFileSize = 0;
+ hr = fFontFileStream->GetFileSize(&realFileSize);
+ if (realFileSize > (std::numeric_limits<size_t>::max)()) {
+ return 0;
+ }
+ size_t fileSize = static_cast<size_t>(realFileSize);
+ if (fPos + size > fileSize) {
+ size_t read = fileSize - fPos;
+ hr = fFontFileStream->ReadFileFragment(&start, fPos, read, &fragmentLock);
+ if (SUCCEEDED(hr)) {
+ memcpy(buffer, start, read);
+ fFontFileStream->ReleaseFileFragment(fragmentLock);
+ fPos = fileSize;
+ return read;
+ }
+ return 0;
+ } else {
+ //This means we were within bounds, but failed for some other reason.
+ return 0;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// SkIDWriteFontFileStreamWrapper
+
+HRESULT SkDWriteFontFileStreamWrapper::Create(SkStream* stream, SkDWriteFontFileStreamWrapper** streamFontFileStream) {
+ *streamFontFileStream = new SkDWriteFontFileStreamWrapper(stream);
+ if (NULL == streamFontFileStream) {
+ return E_OUTOFMEMORY;
+ }
+ return S_OK;
+}
+
+SkDWriteFontFileStreamWrapper::SkDWriteFontFileStreamWrapper(SkStream* stream)
+ : fRefCount(1), fStream(stream) {
+ stream->ref();
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::QueryInterface(REFIID iid, void** ppvObject) {
+ if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
+ *ppvObject = this;
+ AddRef();
+ return S_OK;
+ } else {
+ *ppvObject = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::AddRef() {
+ return InterlockedIncrement(&fRefCount);
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::Release() {
+ ULONG newCount = InterlockedDecrement(&fRefCount);
+ if (0 == newCount) {
+ delete this;
+ }
+ return newCount;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReadFileFragment(
+ void const** fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ void** fragmentContext)
+{
+ // The loader is responsible for doing a bounds check.
+ UINT64 fileSize;
+ this->GetFileSize(&fileSize);
+ if (fileOffset > fileSize || fragmentSize > fileSize - fileOffset) {
+ *fragmentStart = NULL;
+ *fragmentContext = NULL;
+ return E_FAIL;
+ }
+
+ if (fileOffset + fragmentSize > (std::numeric_limits<size_t>::max)()) {
+ return E_FAIL;
+ }
+
+ const void* data = fStream->getMemoryBase();
+ if (NULL != data) {
+ *fragmentStart = static_cast<BYTE const*>(data) + static_cast<size_t>(fileOffset);
+ *fragmentContext = NULL;
+
+ } else {
+ //May be called from multiple threads.
+ SkAutoMutexAcquire ama(fStreamMutex);
+
+ *fragmentStart = NULL;
+ *fragmentContext = NULL;
+
+ if (!fStream->rewind()) {
+ return E_FAIL;
+ }
+ if (fStream->skip(static_cast<size_t>(fileOffset)) != fileOffset) {
+ return E_FAIL;
+ }
+ SkAutoTDeleteArray<uint8_t> streamData(new uint8_t[static_cast<size_t>(fragmentSize)]);
+ if (fStream->read(streamData.get(), static_cast<size_t>(fragmentSize)) != fragmentSize) {
+ return E_FAIL;
+ }
+
+ *fragmentStart = streamData.get();
+ *fragmentContext = streamData.detach();
+ }
+ return S_OK;
+}
+
+void STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReleaseFileFragment(void* fragmentContext) {
+ if (NULL == fragmentContext) {
+ return;
+ }
+ delete [] fragmentContext;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetFileSize(UINT64* fileSize) {
+ *fileSize = fStream->getLength();
+ return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetLastWriteTime(UINT64* lastWriteTime) {
+ // The concept of last write time does not apply to this loader.
+ *lastWriteTime = 0;
+ return E_NOTIMPL;
+}
diff --git a/src/utils/win/SkDWriteFontFileStream.h b/src/utils/win/SkDWriteFontFileStream.h
new file mode 100644
index 0000000..cd5787e
--- /dev/null
+++ b/src/utils/win/SkDWriteFontFileStream.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDWriteFontFileStream_DEFINED
+#define SkDWriteFontFileStream_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkStream.h"
+#include "SkTScopedComPtr.h"
+
+#include <dwrite.h>
+
+/**
+ * An SkStream backed by an IDWriteFontFileStream.
+ * This allows Skia code to read an IDWriteFontFileStream.
+ */
+class SkDWriteFontFileStream : public SkStream {
+public:
+ explicit SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream);
+ virtual ~SkDWriteFontFileStream();
+
+ virtual bool rewind() SK_OVERRIDE;
+ virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
+ virtual const void* getMemoryBase() SK_OVERRIDE;
+
+private:
+ SkTScopedComPtr<IDWriteFontFileStream> fFontFileStream;
+ size_t fPos;
+ const void* fLockedMemory;
+ void* fFragmentLock;
+};
+
+/**
+ * An IDWriteFontFileStream backed by an SkStream.
+ * This allows DirectWrite to read an SkStream.
+ */
+class SkDWriteFontFileStreamWrapper : public IDWriteFontFileStream {
+public:
+ // IUnknown methods
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+ virtual ULONG STDMETHODCALLTYPE AddRef();
+ virtual ULONG STDMETHODCALLTYPE Release();
+
+ // IDWriteFontFileStream methods
+ virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(
+ void const** fragmentStart,
+ UINT64 fileOffset,
+ UINT64 fragmentSize,
+ void** fragmentContext);
+
+ virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
+ virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* fileSize);
+ virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* lastWriteTime);
+
+ static HRESULT Create(SkStream* stream, SkDWriteFontFileStreamWrapper** streamFontFileStream);
+
+private:
+ explicit SkDWriteFontFileStreamWrapper(SkStream* stream);
+
+ ULONG fRefCount;
+ SkAutoTUnref<SkStream> fStream;
+ SkMutex fStreamMutex;
+};
+#endif
diff --git a/src/utils/win/SkDWriteGeometrySink.cpp b/src/utils/win/SkDWriteGeometrySink.cpp
new file mode 100644
index 0000000..4aec3bb
--- /dev/null
+++ b/src/utils/win/SkDWriteGeometrySink.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#include "SkDWriteGeometrySink.h"
+#include "SkFloatUtils.h"
+#include "SkPath.h"
+
+#include <dwrite.h>
+#include <d2d1.h>
+
+SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path) : fRefCount(1), fPath(path) { }
+
+SkDWriteGeometrySink::~SkDWriteGeometrySink() { }
+
+HRESULT STDMETHODCALLTYPE SkDWriteGeometrySink::QueryInterface(REFIID iid, void **object) {
+ if (NULL == object) {
+ return E_INVALIDARG;
+ }
+ if (iid == __uuidof(IUnknown) || iid == __uuidof(IDWriteGeometrySink)) {
+ *object = static_cast<IDWriteGeometrySink*>(this);
+ this->AddRef();
+ return S_OK;
+ } else {
+ *object = NULL;
+ return E_NOINTERFACE;
+ }
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::AddRef(void) {
+ return static_cast<ULONG>(InterlockedIncrement(&fRefCount));
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::Release(void) {
+ ULONG res = static_cast<ULONG>(InterlockedDecrement(&fRefCount));
+ if (0 == res) {
+ delete this;
+ }
+ return res;
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) {
+ switch (fillMode) {
+ case D2D1_FILL_MODE_ALTERNATE:
+ fPath->setFillType(SkPath::kEvenOdd_FillType);
+ break;
+ case D2D1_FILL_MODE_WINDING:
+ fPath->setFillType(SkPath::kWinding_FillType);
+ break;
+ default:
+ SkASSERT(!"Unknown D2D1_FILL_MODE.");
+ break;
+ }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) {
+ if (vertexFlags == D2D1_PATH_SEGMENT_NONE || vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN) {
+ SkASSERT(!"Invalid D2D1_PATH_SEGMENT value.");
+ }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) {
+ fPath->moveTo(SkFloatToScalar(startPoint.x), SkFloatToScalar(startPoint.y));
+ if (figureBegin == D2D1_FIGURE_BEGIN_HOLLOW) {
+ SkASSERT(!"Invalid D2D1_FIGURE_BEGIN value.");
+ }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) {
+ for (const D2D1_POINT_2F *end = &points[pointsCount]; points < end; ++points) {
+ fPath->lineTo(SkFloatToScalar(points->x), SkFloatToScalar(points->y));
+ }
+}
+
+static bool approximately_equal(float a, float b) {
+ const SkFloatingPoint<float, 10> lhs(a), rhs(b);
+ return lhs.AlmostEquals(rhs);
+}
+
+typedef struct {
+ float x;
+ float y;
+} Cubic[4], Quadratic[3];
+
+static bool check_quadratic(const Cubic& cubic, Quadratic& reduction) {
+ float dx10 = cubic[1].x - cubic[0].x;
+ float dx23 = cubic[2].x - cubic[3].x;
+ float midX = cubic[0].x + dx10 * 3 / 2;
+ //NOTE: !approximately_equal(midX - cubic[3].x, dx23 * 3 / 2)
+ //does not work as subnormals get in between the left side and 0.
+ if (!approximately_equal(midX, (dx23 * 3 / 2) + cubic[3].x)) {
+ return false;
+ }
+ float dy10 = cubic[1].y - cubic[0].y;
+ float dy23 = cubic[2].y - cubic[3].y;
+ float midY = cubic[0].y + dy10 * 3 / 2;
+ if (!approximately_equal(midY, (dy23 * 3 / 2) + cubic[3].y)) {
+ return false;
+ }
+ reduction[0] = cubic[0];
+ reduction[1].x = midX;
+ reduction[1].y = midY;
+ reduction[2] = cubic[3];
+ return true;
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) {
+ SkPoint lastPt;
+ fPath->getLastPt(&lastPt);
+ D2D1_POINT_2F prevPt = { SkScalarToFloat(lastPt.fX), SkScalarToFloat(lastPt.fY) };
+
+ for (const D2D1_BEZIER_SEGMENT *end = &beziers[beziersCount]; beziers < end; ++beziers) {
+ Cubic cubic = { { prevPt.x, prevPt.y },
+ { beziers->point1.x, beziers->point1.y },
+ { beziers->point2.x, beziers->point2.y },
+ { beziers->point3.x, beziers->point3.y }, };
+ Quadratic quadratic;
+ if (check_quadratic(cubic, quadratic)) {
+ fPath->quadTo(SkFloatToScalar(quadratic[1].x), SkFloatToScalar(quadratic[1].y),
+ SkFloatToScalar(quadratic[2].x), SkFloatToScalar(quadratic[2].y));
+ } else {
+ fPath->cubicTo(SkFloatToScalar(beziers->point1.x), SkFloatToScalar(beziers->point1.y),
+ SkFloatToScalar(beziers->point2.x), SkFloatToScalar(beziers->point2.y),
+ SkFloatToScalar(beziers->point3.x), SkFloatToScalar(beziers->point3.y));
+ }
+ prevPt = beziers->point3;
+ }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) {
+ fPath->close();
+}
+
+HRESULT SkDWriteGeometrySink::Close() {
+ return S_OK;
+}
+
+HRESULT SkDWriteGeometrySink::Create(SkPath* path, IDWriteGeometrySink** geometryToPath) {
+ *geometryToPath = new SkDWriteGeometrySink(path);
+ return S_OK;
+}
diff --git a/src/utils/win/SkDWriteGeometrySink.h b/src/utils/win/SkDWriteGeometrySink.h
new file mode 100644
index 0000000..99a3262
--- /dev/null
+++ b/src/utils/win/SkDWriteGeometrySink.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDWriteToPath_DEFINED
+#define SkDWriteToPath_DEFINED
+
+#include "SkTypes.h"
+
+class SkPath;
+
+#include <dwrite.h>
+#include <d2d1.h>
+
+class SkDWriteGeometrySink : public IDWriteGeometrySink {
+private:
+ LONG fRefCount;
+ SkPath* fPath;
+
+ SkDWriteGeometrySink(const SkDWriteGeometrySink&);
+ SkDWriteGeometrySink& operator=(const SkDWriteGeometrySink&);
+
+protected:
+ explicit SkDWriteGeometrySink(SkPath* path);
+ virtual ~SkDWriteGeometrySink();
+
+public:
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) SK_OVERRIDE;
+ virtual ULONG STDMETHODCALLTYPE AddRef(void) SK_OVERRIDE;
+ virtual ULONG STDMETHODCALLTYPE Release(void) SK_OVERRIDE;
+
+ virtual void STDMETHODCALLTYPE SetFillMode(D2D1_FILL_MODE fillMode) SK_OVERRIDE;
+ virtual void STDMETHODCALLTYPE SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) SK_OVERRIDE;
+ virtual void STDMETHODCALLTYPE BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) SK_OVERRIDE;
+ virtual void STDMETHODCALLTYPE AddLines(const D2D1_POINT_2F *points, UINT pointsCount) SK_OVERRIDE;
+ virtual void STDMETHODCALLTYPE AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) SK_OVERRIDE;
+ virtual void STDMETHODCALLTYPE EndFigure(D2D1_FIGURE_END figureEnd) SK_OVERRIDE;
+ virtual HRESULT STDMETHODCALLTYPE Close() SK_OVERRIDE;
+
+ static HRESULT Create(SkPath* path, IDWriteGeometrySink** geometryToPath);
+};
+
+#endif