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
This commit is contained in:
bungeman@google.com 2012-08-16 16:13:40 +00:00
parent 0557d9ea94
commit e8f0592ae8
14 changed files with 2216 additions and 7 deletions

View File

@ -50,6 +50,11 @@
],
}
],
[ 'skia_directwrite', {
'defines': [
'SK_FONTHOST_WIN_DW',
],
}],
[ 'skia_mesa', {
'defines': [
'SK_MESA',

View File

@ -70,6 +70,7 @@
# Do not turn on 'skia_angle' - it is currently experimental
'skia_angle%': 0,
'skia_arch_type%': 'x86',
'skia_directwrite%': 0,
'android_make_apk%': 1,
'skia_gpu%': 1,
'skia_static_initializers%': 1,
@ -85,6 +86,7 @@
'skia_angle%': '<(skia_angle)',
'skia_arch_type%': '<(skia_arch_type)',
'skia_arch_width%': '<(skia_arch_width)',
'skia_directwrite%': '<(skia_directwrite)',
'android_make_apk%': '<(android_make_apk)',
'skia_gpu%': '<(skia_gpu)',
'skia_static_initializers%': '<(skia_static_initializers)',
@ -99,4 +101,4 @@
# tab-width:2
# indent-tabs-mode:nil
# End:
# vim: set expandtab tabstop=2 shiftwidth=2:
# vim: set expandtab tabstop=2 shiftwidth=2:

View File

@ -24,6 +24,7 @@
'../src/ports/SkFontDescriptor.cpp',
'../src/ports/SkFontHost_sandbox_none.cpp',
'../src/ports/SkFontHost_win.cpp',
'../src/ports/SkFontHost_win_dw.cpp',
'../src/ports/SkGlobalInitialization_default.cpp',
'../src/ports/SkThread_win.cpp',
@ -70,6 +71,18 @@
[ 'skia_os == "win"', {
'include_dirs': [
'config/win',
'../src/utils/win',
],
'conditions': [
[ 'skia_directwrite', {
'sources!': [
'../src/ports/SkFontHost_win.cpp',
],
}, { # else !skia_directwrite
'sources!': [
'../src/ports/SkFontHost_win_dw.cpp',
],
}],
],
'sources!': [ # these are used everywhere but windows
'../src/ports/SkDebug_stdio.cpp',

View File

@ -2,10 +2,11 @@
'targets': [
{
'target_name': 'utils',
'dependencies': [
'core.gyp:core',
],
'type': 'static_library',
'include_dirs': [
'../include/config',
'../include/core',
'../include/effects',
'../include/pipe',
'../include/utils',
@ -13,6 +14,7 @@
'../include/utils/unix',
'../include/utils/win',
'../include/xml',
'../src/utils',
],
'sources': [
'../include/utils/SkBoundaryPatch.h',
@ -45,6 +47,7 @@
'../src/utils/SkCullPoints.cpp',
'../src/utils/SkDeferredCanvas.cpp',
'../src/utils/SkDumpCanvas.cpp',
'../src/utils/SkFloatUtils.h',
'../src/utils/SkInterpolator.cpp',
'../src/utils/SkLayer.cpp',
'../src/utils/SkMatrix44.cpp',
@ -77,6 +80,10 @@
'../include/utils/win/SkIStream.h',
'../include/utils/win/SkTScopedComPtr.h',
'../src/utils/win/SkAutoCoInitialize.cpp',
'../src/utils/win/SkDWriteFontFileStream.cpp',
'../src/utils/win/SkDWriteFontFileStream.h',
'../src/utils/win/SkDWriteGeometrySink.cpp',
'../src/utils/win/SkDWriteGeometrySink.h',
'../src/utils/win/SkHRESULT.cpp',
'../src/utils/win/SkIStream.cpp',
'../src/utils/win/SkWGL_win.cpp',
@ -149,6 +156,10 @@
'../include/utils/win/SkIStream.h',
'../include/utils/win/SkTScopedComPtr.h',
'../src/utils/win/SkAutoCoInitialize.cpp',
'../src/utils/win/SkDWriteFontFileStream.cpp',
'../src/utils/win/SkDWriteFontFileStream.h',
'../src/utils/win/SkDWriteGeometrySink.cpp',
'../src/utils/win/SkDWriteGeometrySink.h',
'../src/utils/win/SkHRESULT.cpp',
'../src/utils/win/SkIStream.cpp',
],

View File

@ -36,6 +36,7 @@ If the HRESULT FAILED then the macro will return from the current function.
In variants ending with 'M' the given message will be traced when FAILED.
The HR variants will return the HRESULT when FAILED.
The HRB variants will return false when FAILED.
The HRN variants will return NULL when FAILED.
The HRV variants will simply return when FAILED.
*/
#define HR(ex) HR_GENERAL(ex, NULL, _hr)
@ -44,6 +45,9 @@ The HRV variants will simply return when FAILED.
#define HRB(ex) HR_GENERAL(ex, NULL, false)
#define HRBM(ex, msg) HR_GENERAL(ex, msg, false)
#define HRN(ex) HR_GENERAL(ex, NULL, NULL)
#define HRNM(ex, msg) HR_GENERAL(ex, msg, NULL)
#define HRV(ex) HR_GENERAL(ex, NULL, )
#define HRVM(ex, msg) HR_GENERAL(ex, msg, )
//@}

View File

@ -29,7 +29,7 @@ public:
~SkTScopedComPtr() {
this->reset();
}
T &operator*() const { return *fPtr; }
T &operator*() const { SkASSERT(fPtr != NULL); return *fPtr; }
SkBlockComRef<T> *operator->() const {
return static_cast<SkBlockComRef<T>*>(fPtr);
}

View File

@ -30,7 +30,7 @@
extern void skia_set_text_gamma(float blackGamma, float whiteGamma);
#ifdef SK_BUILD_FOR_WIN
#if defined(SK_BUILD_FOR_WIN) && !defined(SK_FONTHOST_WIN_DW)
extern SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&);
#endif
@ -45,7 +45,7 @@ static const char gText[] =
class TextBoxView : public SampleView {
public:
TextBoxView() {
#ifdef SK_BUILD_FOR_WIN
#if defined(SK_BUILD_FOR_WIN) && !defined(SK_FONTHOST_WIN_DW)
LOGFONT lf;
sk_bzero(&lf, sizeof(lf));
lf.lfHeight = 9;

View File

@ -12,6 +12,10 @@
SK_DEFINE_INST_COUNT(SkAdvancedTypefaceMetrics)
#if defined(SK_BUILD_FOR_WIN) && defined(SK_FONTHOST_WIN_DW)
#include <DWrite.h>
#endif
#if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
#include <ft2build.h>
#include FT_FREETYPE_H
@ -253,13 +257,20 @@ SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* getAdvanceData(
// Make AdvanceMetric template functions available for linking with typename
// WidthRange and VerticalAdvanceRange.
#if defined(SK_BUILD_FOR_WIN)
#if defined(SK_BUILD_FOR_WIN) && !defined(SK_FONTHOST_WIN_DW)
template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
HDC hdc,
int num_glyphs,
const uint32_t* subsetGlyphIDs,
uint32_t subsetGlyphIDsLength,
bool (*getAdvance)(HDC hdc, int gId, int16_t* data));
#elif defined(SK_BUILD_FOR_WIN) && defined(SK_FONTHOST_WIN_DW)
template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
IDWriteFontFace* fontFace,
int num_glyphs,
const uint32_t* subsetGlyphIDs,
uint32_t subsetGlyphIDsLength,
bool (*getAdvance)(IDWriteFontFace* fontFace, int gId, int16_t* data));
#elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
FT_Face face,

File diff suppressed because it is too large Load Diff

173
src/utils/SkFloatUtils.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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