SkXPSDevice.cpp revision b58a639b2fbe919489654bb506efdb024a308a8e
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#ifndef UNICODE
9#define UNICODE
10#endif
11#ifndef _UNICODE
12#define _UNICODE
13#endif
14#include "SkTypes.h"
15#include <ObjBase.h>
16#include <XpsObjectModel.h>
17#include <T2EmbApi.h>
18#include <FontSub.h>
19
20#include "SkColor.h"
21#include "SkConstexprMath.h"
22#include "SkData.h"
23#include "SkDraw.h"
24#include "SkDrawProcs.h"
25#include "SkFontHost.h"
26#include "SkGlyphCache.h"
27#include "SkHRESULT.h"
28#include "SkImageEncoder.h"
29#include "SkIStream.h"
30#include "SkMaskFilter.h"
31#include "SkPaint.h"
32#include "SkPoint.h"
33#include "SkRasterizer.h"
34#include "SkShader.h"
35#include "SkSize.h"
36#include "SkStream.h"
37#include "SkTDArray.h"
38#include "SkTLazy.h"
39#include "SkTScopedComPtr.h"
40#include "SkUtils.h"
41#include "SkXPSDevice.h"
42
43//Windows defines a FLOAT type,
44//make it clear when converting a scalar that this is what is wanted.
45#define SkScalarToFLOAT(n) SkScalarToFloat(n)
46
47//Dummy representation of a GUID from create_id.
48#define L_GUID_ID L"XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX"
49//Length of GUID representation from create_id, including NULL terminator.
50#define GUID_ID_LEN SK_ARRAY_COUNT(L_GUID_ID)
51
52/**
53   Formats a GUID and places it into buffer.
54   buffer should have space for at least GUID_ID_LEN wide characters.
55   The string will always be wchar null terminated.
56   XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0
57   @return -1 if there was an error, > 0 if success.
58 */
59static int format_guid(const GUID& guid,
60                       wchar_t* buffer, size_t bufferSize,
61                       wchar_t sep = '-') {
62    SkASSERT(bufferSize >= GUID_ID_LEN);
63    return swprintf_s(buffer,
64                      bufferSize,
65                      L"%08lX%c%04X%c%04X%c%02X%02X%c%02X%02X%02X%02X%02X%02X",
66                      guid.Data1,
67                      sep,
68                      guid.Data2,
69                      sep,
70                      guid.Data3,
71                      sep,
72                      guid.Data4[0],
73                      guid.Data4[1],
74                      sep,
75                      guid.Data4[2],
76                      guid.Data4[3],
77                      guid.Data4[4],
78                      guid.Data4[5],
79                      guid.Data4[6],
80                      guid.Data4[7]);
81}
82/**
83   Creates a GUID based id and places it into buffer.
84   buffer should have space for at least GUID_ID_LEN wide characters.
85   The string will always be wchar null terminated.
86   XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0
87   The string may begin with a digit,
88   and so may not be suitable as a bare resource key.
89 */
90static HRESULT create_id(wchar_t* buffer, size_t bufferSize,
91                         wchar_t sep = '-') {
92    GUID guid = {};
93    HRM(CoCreateGuid(&guid), "Could not create GUID for id.");
94
95    if (format_guid(guid, buffer, bufferSize, sep) == -1) {
96        HRM(E_UNEXPECTED, "Could not format GUID into id.");
97    }
98
99    return S_OK;
100}
101
102static SkBitmap make_fake_bitmap(int width, int height) {
103    SkBitmap bitmap;
104    bitmap.setConfig(SkBitmap::kNo_Config, width, height);
105    return bitmap;
106}
107
108SkXPSDevice::SkXPSDevice()
109    : SkDevice(make_fake_bitmap(10000, 10000))
110    , fCurrentPage(0) {
111}
112
113SkXPSDevice::~SkXPSDevice() {
114}
115
116SkXPSDevice::TypefaceUse::TypefaceUse()
117    : typefaceId(0xffffffff)
118    , fontData(NULL)
119    , xpsFont(NULL)
120    , glyphsUsed(NULL) {
121}
122
123SkXPSDevice::TypefaceUse::~TypefaceUse() {
124    //xpsFont owns fontData ref
125    this->xpsFont->Release();
126    delete this->glyphsUsed;
127}
128
129bool SkXPSDevice::beginPortfolio(SkWStream* outputStream) {
130    if (!this->fAutoCo.succeeded()) return false;
131
132    //Create XPS Factory.
133    HRBM(CoCreateInstance(
134             CLSID_XpsOMObjectFactory,
135             NULL,
136             CLSCTX_INPROC_SERVER,
137             IID_PPV_ARGS(&this->fXpsFactory)),
138         "Could not create XPS factory.");
139
140    HRBM(SkWIStream::CreateFromSkWStream(outputStream, &this->fOutputStream),
141         "Could not convert SkStream to IStream.");
142
143    return true;
144}
145
146bool SkXPSDevice::beginSheet(
147        const SkVector& unitsPerMeter,
148        const SkVector& pixelsPerMeter,
149        const SkSize& trimSize,
150        const SkRect* mediaBox,
151        const SkRect* bleedBox,
152        const SkRect* artBox,
153        const SkRect* cropBox) {
154    ++this->fCurrentPage;
155
156    //For simplicity, just write everything out in geometry units,
157    //then have a base canvas do the scale to physical units.
158    this->fCurrentCanvasSize = trimSize;
159    this->fCurrentUnitsPerMeter = unitsPerMeter;
160    this->fCurrentPixelsPerMeter = pixelsPerMeter;
161
162    this->fCurrentXpsCanvas.reset();
163    HRBM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas),
164         "Could not create base canvas.");
165
166    return true;
167}
168
169HRESULT SkXPSDevice::createXpsThumbnail(IXpsOMPage* page,
170                                        const unsigned int pageNum,
171                                        IXpsOMImageResource** image) {
172    SkTScopedComPtr<IXpsOMThumbnailGenerator> thumbnailGenerator;
173    HRM(CoCreateInstance(
174            CLSID_XpsOMThumbnailGenerator,
175            NULL,
176            CLSCTX_INPROC_SERVER,
177            IID_PPV_ARGS(&thumbnailGenerator)),
178        "Could not create thumbnail generator.");
179
180    SkTScopedComPtr<IOpcPartUri> partUri;
181    static const size_t size = SK_MAX(
182        SK_ARRAY_COUNT(L"/Documents/1/Metadata/.png") + SK_DIGITS_IN(pageNum),
183        SK_ARRAY_COUNT(L"/Metadata/" L_GUID_ID L".png")
184    );
185    wchar_t buffer[size];
186    if (pageNum > 0) {
187        swprintf_s(buffer, size, L"/Documents/1/Metadata/%u.png", pageNum);
188    } else {
189        wchar_t id[GUID_ID_LEN];
190        HR(create_id(id, GUID_ID_LEN));
191        swprintf_s(buffer, size, L"/Metadata/%s.png", id);
192    }
193    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
194        "Could not create thumbnail part uri.");
195
196    HRM(thumbnailGenerator->GenerateThumbnail(page,
197                                              XPS_IMAGE_TYPE_PNG,
198                                              XPS_THUMBNAIL_SIZE_LARGE,
199                                              partUri.get(),
200                                              image),
201        "Could not generate thumbnail.");
202
203    return S_OK;
204}
205
206HRESULT SkXPSDevice::createXpsPage(const XPS_SIZE& pageSize,
207                                   IXpsOMPage** page) {
208    static const size_t size = SK_ARRAY_COUNT(L"/Documents/1/Pages/.fpage")
209                             + SK_DIGITS_IN(fCurrentPage);
210    wchar_t buffer[size];
211    swprintf_s(buffer, size, L"/Documents/1/Pages/%u.fpage",
212                             this->fCurrentPage);
213    SkTScopedComPtr<IOpcPartUri> partUri;
214    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
215        "Could not create page part uri.");
216
217    //If the language is unknown, use "und" (XPS Spec 2.3.5.1).
218    HRM(this->fXpsFactory->CreatePage(&pageSize,
219                                      L"und",
220                                      partUri.get(),
221                                      page),
222        "Could not create page.");
223
224    return S_OK;
225}
226
227HRESULT SkXPSDevice::initXpsDocumentWriter(IXpsOMImageResource* image) {
228    //Create package writer.
229    {
230        SkTScopedComPtr<IOpcPartUri> partUri;
231        HRM(this->fXpsFactory->CreatePartUri(L"/FixedDocumentSequence.fdseq",
232                                             &partUri),
233            "Could not create document sequence part uri.");
234        HRM(this->fXpsFactory->CreatePackageWriterOnStream(
235                this->fOutputStream.get(),
236                TRUE,
237                XPS_INTERLEAVING_OFF, //XPS_INTERLEAVING_ON,
238                partUri.get(),
239                NULL,
240                image,
241                NULL,
242                NULL,
243                &this->fPackageWriter),
244            "Could not create package writer.");
245    }
246
247    //Begin the lone document.
248    {
249        SkTScopedComPtr<IOpcPartUri> partUri;
250        HRM(this->fXpsFactory->CreatePartUri(
251                L"/Documents/1/FixedDocument.fdoc",
252                &partUri),
253            "Could not create fixed document part uri.");
254        HRM(this->fPackageWriter->StartNewDocument(partUri.get(),
255                                                   NULL,
256                                                   NULL,
257                                                   NULL,
258                                                   NULL),
259            "Could not start document.");
260    }
261
262    return S_OK;
263}
264
265bool SkXPSDevice::endSheet() {
266    //XPS is fixed at 96dpi (XPS Spec 11.1).
267    static const float xpsDPI = 96.0f;
268    static const float inchesPerMeter = 10000.0f / 254.0f;
269    static const float targetUnitsPerMeter = xpsDPI * inchesPerMeter;
270    const float scaleX = targetUnitsPerMeter
271                       / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fX);
272    const float scaleY = targetUnitsPerMeter
273                       / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fY);
274
275    //Create the scale canvas.
276    SkTScopedComPtr<IXpsOMCanvas> scaleCanvas;
277    HRBM(this->fXpsFactory->CreateCanvas(&scaleCanvas),
278         "Could not create scale canvas.");
279    SkTScopedComPtr<IXpsOMVisualCollection> scaleCanvasVisuals;
280    HRBM(scaleCanvas->GetVisuals(&scaleCanvasVisuals),
281         "Could not get scale canvas visuals.");
282
283    SkTScopedComPtr<IXpsOMMatrixTransform> geomToPhys;
284    XPS_MATRIX rawGeomToPhys = { scaleX, 0, 0, scaleY, 0, 0, };
285    HRBM(this->fXpsFactory->CreateMatrixTransform(&rawGeomToPhys, &geomToPhys),
286         "Could not create geometry to physical transform.");
287    HRBM(scaleCanvas->SetTransformLocal(geomToPhys.get()),
288         "Could not set transform on scale canvas.");
289
290    //Add the content canvas to the scale canvas.
291    HRBM(scaleCanvasVisuals->Append(this->fCurrentXpsCanvas.get()),
292         "Could not add base canvas to scale canvas.");
293
294    //Create the page.
295    XPS_SIZE pageSize = {
296        SkScalarToFLOAT(this->fCurrentCanvasSize.width()) * scaleX,
297        SkScalarToFLOAT(this->fCurrentCanvasSize.height()) * scaleY,
298    };
299    SkTScopedComPtr<IXpsOMPage> page;
300    HRB(this->createXpsPage(pageSize, &page));
301
302    SkTScopedComPtr<IXpsOMVisualCollection> pageVisuals;
303    HRBM(page->GetVisuals(&pageVisuals), "Could not get page visuals.");
304
305    //Add the scale canvas to the page.
306    HRBM(pageVisuals->Append(scaleCanvas.get()),
307         "Could not add scale canvas to page.");
308
309    //Create the package writer if it hasn't been created yet.
310    if (NULL == this->fPackageWriter.get()) {
311        SkTScopedComPtr<IXpsOMImageResource> image;
312        //Ignore return, thumbnail is completely optional.
313        this->createXpsThumbnail(page.get(), 0, &image);
314
315        HRB(this->initXpsDocumentWriter(image.get()));
316    }
317
318    HRBM(this->fPackageWriter->AddPage(page.get(),
319                                       &pageSize,
320                                       NULL,
321                                       NULL,
322                                       NULL,
323                                       NULL),
324         "Could not write the page.");
325    this->fCurrentXpsCanvas.reset();
326
327    return true;
328}
329
330static HRESULT subset_typeface(SkXPSDevice::TypefaceUse* current) {
331    //CreateFontPackage wants unsigned short.
332    //Microsoft, Y U NO stdint.h?
333    SkTDArray<unsigned short> keepList;
334    current->glyphsUsed->exportTo(&keepList);
335
336    //The following are declared with the types required by CreateFontPackage.
337    unsigned char *puchFontPackageBuffer;
338    unsigned long pulFontPackageBufferSize;
339    unsigned long pulBytesWritten;
340    unsigned long result = CreateFontPackage(
341        (unsigned char *) current->fontData->getMemoryBase(),
342        (unsigned long) current->fontData->getLength(),
343        &puchFontPackageBuffer,
344        &pulFontPackageBufferSize,
345        &pulBytesWritten,
346        TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST,// | TTFCFP_FLAGS_TTC,
347        0,//TTC index
348        TTFCFP_SUBSET,
349        0,
350        0,
351        0,
352        keepList.begin(),
353        keepList.count(),
354        sk_malloc_throw,
355        sk_realloc_throw,
356        sk_free,
357        NULL);
358    if (result != NO_ERROR) {
359        SkDEBUGF(("CreateFontPackage Error %lu", result));
360        return E_UNEXPECTED;
361    }
362
363    SkMemoryStream* newStream = new SkMemoryStream;
364    newStream->setMemoryOwned(puchFontPackageBuffer, pulBytesWritten);
365    SkTScopedComPtr<IStream> newIStream;
366    SkIStream::CreateFromSkStream(newStream, true, &newIStream);
367
368    XPS_FONT_EMBEDDING embedding;
369    HRM(current->xpsFont->GetEmbeddingOption(&embedding),
370        "Could not get embedding option from font.");
371
372    SkTScopedComPtr<IOpcPartUri> partUri;
373    HRM(current->xpsFont->GetPartName(&partUri),
374        "Could not get part uri from font.");
375
376    HRM(current->xpsFont->SetContent(
377            newIStream.get(),
378            embedding,
379            partUri.get()),
380        "Could not set new stream for subsetted font.");
381
382    return S_OK;
383}
384
385bool SkXPSDevice::endPortfolio() {
386    //Subset fonts
387    if (!this->fTypefaces.empty()) {
388        SkXPSDevice::TypefaceUse* current = &this->fTypefaces.front();
389        const TypefaceUse* last = &this->fTypefaces.back();
390        for (; current <= last; ++current) {
391            //Ignore return for now, if it didn't subset, let it be.
392            subset_typeface(current);
393        }
394    }
395
396    HRBM(this->fPackageWriter->Close(), "Could not close writer.");
397
398    return true;
399}
400
401static XPS_COLOR xps_color(const SkColor skColor) {
402    //XPS uses non-pre-multiplied alpha (XPS Spec 11.4).
403    XPS_COLOR xpsColor;
404    xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
405    xpsColor.value.sRGB.alpha = SkColorGetA(skColor);
406    xpsColor.value.sRGB.red = SkColorGetR(skColor);
407    xpsColor.value.sRGB.green = SkColorGetG(skColor);
408    xpsColor.value.sRGB.blue = SkColorGetB(skColor);
409
410    return xpsColor;
411}
412
413static XPS_POINT xps_point(const SkPoint& point) {
414    XPS_POINT xpsPoint = {
415        SkScalarToFLOAT(point.fX),
416        SkScalarToFLOAT(point.fY),
417    };
418    return xpsPoint;
419}
420
421static XPS_POINT xps_point(const SkPoint& point, const SkMatrix& matrix) {
422    SkPoint skTransformedPoint;
423    matrix.mapXY(point.fX, point.fY, &skTransformedPoint);
424    return xps_point(skTransformedPoint);
425}
426
427static XPS_SPREAD_METHOD xps_spread_method(SkShader::TileMode tileMode) {
428    switch (tileMode) {
429    case SkShader::kClamp_TileMode:
430        return XPS_SPREAD_METHOD_PAD;
431    case SkShader::kRepeat_TileMode:
432        return XPS_SPREAD_METHOD_REPEAT;
433    case SkShader::kMirror_TileMode:
434        return XPS_SPREAD_METHOD_REFLECT;
435    default:
436        SkASSERT(!"Unknown tile mode.");
437    }
438    return XPS_SPREAD_METHOD_PAD;
439}
440
441static void transform_offsets(SkScalar* stopOffsets, const int numOffsets,
442                              const SkPoint& start, const SkPoint& end,
443                              const SkMatrix& transform) {
444    SkPoint startTransformed;
445    transform.mapXY(start.fX, start.fY, &startTransformed);
446    SkPoint endTransformed;
447    transform.mapXY(end.fX, end.fY, &endTransformed);
448
449    //Manhattan distance between transformed start and end.
450    SkScalar startToEnd = (endTransformed.fX - startTransformed.fX)
451                        + (endTransformed.fY - startTransformed.fY);
452    if (SkScalarNearlyZero(startToEnd)) {
453        for (int i = 0; i < numOffsets; ++i) {
454            stopOffsets[i] = 0;
455        }
456        return;
457    }
458
459    for (int i = 0; i < numOffsets; ++i) {
460        SkPoint stop;
461        stop.fX = SkScalarMul(end.fX - start.fX, stopOffsets[i]);
462        stop.fY = SkScalarMul(end.fY - start.fY, stopOffsets[i]);
463
464        SkPoint stopTransformed;
465        transform.mapXY(stop.fX, stop.fY, &stopTransformed);
466
467        //Manhattan distance between transformed start and stop.
468        SkScalar startToStop = (stopTransformed.fX - startTransformed.fX)
469                             + (stopTransformed.fY - startTransformed.fY);
470        //Percentage along transformed line.
471        stopOffsets[i] = SkScalarDiv(startToStop, startToEnd);
472    }
473}
474
475HRESULT SkXPSDevice::createXpsTransform(const SkMatrix& matrix,
476                                        IXpsOMMatrixTransform** xpsTransform) {
477    SkScalar affine[6];
478    if (!matrix.asAffine(affine)) {
479        *xpsTransform = NULL;
480        return S_FALSE;
481    }
482    XPS_MATRIX rawXpsMatrix = {
483        SkScalarToFLOAT(affine[SkMatrix::kAScaleX]),
484        SkScalarToFLOAT(affine[SkMatrix::kASkewY]),
485        SkScalarToFLOAT(affine[SkMatrix::kASkewX]),
486        SkScalarToFLOAT(affine[SkMatrix::kAScaleY]),
487        SkScalarToFLOAT(affine[SkMatrix::kATransX]),
488        SkScalarToFLOAT(affine[SkMatrix::kATransY]),
489    };
490    HRM(this->fXpsFactory->CreateMatrixTransform(&rawXpsMatrix, xpsTransform),
491        "Could not create transform.");
492
493    return S_OK;
494}
495
496HRESULT SkXPSDevice::createPath(IXpsOMGeometryFigure* figure,
497                                IXpsOMVisualCollection* visuals,
498                                IXpsOMPath** path) {
499    SkTScopedComPtr<IXpsOMGeometry> geometry;
500    HRM(this->fXpsFactory->CreateGeometry(&geometry),
501        "Could not create geometry.");
502
503    SkTScopedComPtr<IXpsOMGeometryFigureCollection> figureCollection;
504    HRM(geometry->GetFigures(&figureCollection), "Could not get figures.");
505    HRM(figureCollection->Append(figure), "Could not add figure.");
506
507    HRM(this->fXpsFactory->CreatePath(path), "Could not create path.");
508    HRM((*path)->SetGeometryLocal(geometry.get()), "Could not set geometry");
509
510    HRM(visuals->Append(*path), "Could not add path to visuals.");
511    return S_OK;
512}
513
514HRESULT SkXPSDevice::createXpsSolidColorBrush(const SkColor skColor,
515                                              const SkAlpha alpha,
516                                              IXpsOMBrush** xpsBrush) {
517    XPS_COLOR xpsColor = xps_color(skColor);
518    SkTScopedComPtr<IXpsOMSolidColorBrush> solidBrush;
519    HRM(this->fXpsFactory->CreateSolidColorBrush(&xpsColor, NULL, &solidBrush),
520        "Could not create solid color brush.");
521    HRM(solidBrush->SetOpacity(alpha / 255.0f), "Could not set opacity.");
522    HRM(solidBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI Fail.");
523    return S_OK;
524}
525
526HRESULT SkXPSDevice::sideOfClamp(const SkRect& areaToFill,
527                                 const XPS_RECT& imageViewBox,
528                                 IXpsOMImageResource* image,
529                                 IXpsOMVisualCollection* visuals) {
530    SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
531    HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
532
533    SkTScopedComPtr<IXpsOMPath> areaToFillPath;
534    HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
535
536    SkTScopedComPtr<IXpsOMImageBrush> areaToFillBrush;
537    HRM(this->fXpsFactory->CreateImageBrush(image,
538                                            &imageViewBox,
539                                            &imageViewBox,
540                                            &areaToFillBrush),
541        "Could not create brush for side of clamp.");
542    HRM(areaToFillBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
543        "Could not set tile mode for side of clamp.");
544    HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
545        "Could not set brush for side of clamp");
546
547    return S_OK;
548}
549
550HRESULT SkXPSDevice::cornerOfClamp(const SkRect& areaToFill,
551                                   const SkColor color,
552                                   IXpsOMVisualCollection* visuals) {
553    SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
554    HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
555
556    SkTScopedComPtr<IXpsOMPath> areaToFillPath;
557    HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
558
559    SkTScopedComPtr<IXpsOMBrush> areaToFillBrush;
560    HR(this->createXpsSolidColorBrush(color, 0xFF, &areaToFillBrush));
561    HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
562        "Could not set brush for corner of clamp.");
563
564    return S_OK;
565}
566
567static const XPS_TILE_MODE XTM_N  = XPS_TILE_MODE_NONE;
568static const XPS_TILE_MODE XTM_T  = XPS_TILE_MODE_TILE;
569static const XPS_TILE_MODE XTM_X  = XPS_TILE_MODE_FLIPX;
570static const XPS_TILE_MODE XTM_Y  = XPS_TILE_MODE_FLIPY;
571static const XPS_TILE_MODE XTM_XY = XPS_TILE_MODE_FLIPXY;
572
573//TODO(bungeman): In the future, should skia add None,
574//handle None+Mirror and None+Repeat correctly.
575//None is currently an internal hack so masks don't repeat (None+None only).
576static XPS_TILE_MODE SkToXpsTileMode[SkShader::kTileModeCount+1]
577                                    [SkShader::kTileModeCount+1] = {
578           //Clamp //Repeat //Mirror //None
579/*Clamp */ XTM_N,  XTM_T,   XTM_Y,   XTM_N,
580/*Repeat*/ XTM_T,  XTM_T,   XTM_Y,   XTM_N,
581/*Mirror*/ XTM_X,  XTM_X,   XTM_XY,  XTM_X,
582/*None  */ XTM_N,  XTM_N,   XTM_Y,   XTM_N,
583};
584
585HRESULT SkXPSDevice::createXpsImageBrush(
586        const SkBitmap& bitmap,
587        const SkMatrix& localMatrix,
588        const SkShader::TileMode (&xy)[2],
589        const SkAlpha alpha,
590        IXpsOMTileBrush** xpsBrush) {
591    SkDynamicMemoryWStream write;
592    if (!SkImageEncoder::EncodeStream(&write, bitmap,
593                                      SkImageEncoder::kPNG_Type, 100)) {
594        HRM(E_FAIL, "Unable to encode bitmap as png.");
595    }
596    SkMemoryStream* read = new SkMemoryStream;
597    read->setData(write.copyToData())->unref();
598    SkTScopedComPtr<IStream> readWrapper;
599    HRM(SkIStream::CreateFromSkStream(read, true, &readWrapper),
600        "Could not create stream from png data.");
601
602    const size_t size =
603        SK_ARRAY_COUNT(L"/Documents/1/Resources/Images/" L_GUID_ID L".png");
604    wchar_t buffer[size];
605    wchar_t id[GUID_ID_LEN];
606    HR(create_id(id, GUID_ID_LEN));
607    swprintf_s(buffer, size, L"/Documents/1/Resources/Images/%s.png", id);
608
609    SkTScopedComPtr<IOpcPartUri> imagePartUri;
610    HRM(this->fXpsFactory->CreatePartUri(buffer, &imagePartUri),
611        "Could not create image part uri.");
612
613    SkTScopedComPtr<IXpsOMImageResource> imageResource;
614    HRM(this->fXpsFactory->CreateImageResource(
615            readWrapper.get(),
616            XPS_IMAGE_TYPE_PNG,
617            imagePartUri.get(),
618            &imageResource),
619        "Could not create image resource.");
620
621    XPS_RECT bitmapRect = {
622        0.0, 0.0,
623        static_cast<FLOAT>(bitmap.width()), static_cast<FLOAT>(bitmap.height())
624    };
625    SkTScopedComPtr<IXpsOMImageBrush> xpsImageBrush;
626    HRM(this->fXpsFactory->CreateImageBrush(imageResource.get(),
627                                            &bitmapRect, &bitmapRect,
628                                            &xpsImageBrush),
629        "Could not create image brush.");
630
631    if (SkShader::kClamp_TileMode != xy[0] &&
632        SkShader::kClamp_TileMode != xy[1]) {
633
634        HRM(xpsImageBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]),
635            "Could not set image tile mode");
636        HRM(xpsImageBrush->SetOpacity(alpha / 255.0f),
637            "Could not set image opacity.");
638        HRM(xpsImageBrush->QueryInterface(xpsBrush), "QI failed.");
639    } else {
640        //TODO(bungeman): compute how big this really needs to be.
641        const SkScalar BIG = SkIntToScalar(1000); //SK_ScalarMax;
642        const FLOAT BIG_F = SkScalarToFLOAT(BIG);
643        const SkScalar bWidth = SkIntToScalar(bitmap.width());
644        const SkScalar bHeight = SkIntToScalar(bitmap.height());
645
646        //create brush canvas
647        SkTScopedComPtr<IXpsOMCanvas> brushCanvas;
648        HRM(this->fXpsFactory->CreateCanvas(&brushCanvas),
649            "Could not create image brush canvas.");
650        SkTScopedComPtr<IXpsOMVisualCollection> brushVisuals;
651        HRM(brushCanvas->GetVisuals(&brushVisuals),
652            "Could not get image brush canvas visuals collection.");
653
654        //create central figure
655        const SkRect bitmapPoints = SkRect::MakeLTRB(0, 0, bWidth, bHeight);
656        SkTScopedComPtr<IXpsOMGeometryFigure> centralFigure;
657        HR(this->createXpsRect(bitmapPoints, FALSE, TRUE, &centralFigure));
658
659        SkTScopedComPtr<IXpsOMPath> centralPath;
660        HR(this->createPath(centralFigure.get(),
661                            brushVisuals.get(),
662                            &centralPath));
663        HRM(xpsImageBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
664            "Could not set tile mode for image brush central path.");
665        HRM(centralPath->SetFillBrushLocal(xpsImageBrush.get()),
666            "Could not set fill brush for image brush central path.");
667
668        //add left/right
669        if (SkShader::kClamp_TileMode == xy[0]) {
670            SkRect leftArea = SkRect::MakeLTRB(-BIG, 0, 0, bHeight);
671            XPS_RECT leftImageViewBox = {
672                0.0, 0.0,
673                1.0, static_cast<FLOAT>(bitmap.height()),
674            };
675            HR(this->sideOfClamp(leftArea, leftImageViewBox,
676                                 imageResource.get(),
677                                 brushVisuals.get()));
678
679            SkRect rightArea = SkRect::MakeLTRB(bWidth, 0, BIG, bHeight);
680            XPS_RECT rightImageViewBox = {
681                bitmap.width() - 1.0f, 0.0f,
682                1.0f, static_cast<FLOAT>(bitmap.height()),
683            };
684            HR(this->sideOfClamp(rightArea, rightImageViewBox,
685                                 imageResource.get(),
686                                 brushVisuals.get()));
687        }
688
689        //add top/bottom
690        if (SkShader::kClamp_TileMode == xy[1]) {
691            SkRect topArea = SkRect::MakeLTRB(0, -BIG, bWidth, 0);
692            XPS_RECT topImageViewBox = {
693                0.0, 0.0,
694                static_cast<FLOAT>(bitmap.width()), 1.0,
695            };
696            HR(this->sideOfClamp(topArea, topImageViewBox,
697                                 imageResource.get(),
698                                 brushVisuals.get()));
699
700            SkRect bottomArea = SkRect::MakeLTRB(0, bHeight, bWidth, BIG);
701            XPS_RECT bottomImageViewBox = {
702                0.0f, bitmap.height() - 1.0f,
703                static_cast<FLOAT>(bitmap.width()), 1.0f,
704            };
705            HR(this->sideOfClamp(bottomArea, bottomImageViewBox,
706                                 imageResource.get(),
707                                 brushVisuals.get()));
708        }
709
710        //add tl, tr, bl, br
711        if (SkShader::kClamp_TileMode == xy[0] &&
712            SkShader::kClamp_TileMode == xy[1]) {
713
714            SkAutoLockPixels alp(bitmap);
715
716            const SkColor tlColor = bitmap.getColor(0,0);
717            const SkRect tlArea = SkRect::MakeLTRB(-BIG, -BIG, 0, 0);
718            HR(this->cornerOfClamp(tlArea, tlColor, brushVisuals.get()));
719
720            const SkColor trColor = bitmap.getColor(bitmap.width()-1,0);
721            const SkRect trArea = SkRect::MakeLTRB(bWidth, -BIG, BIG, 0);
722            HR(this->cornerOfClamp(trArea, trColor, brushVisuals.get()));
723
724            const SkColor brColor = bitmap.getColor(bitmap.width()-1,
725                                                    bitmap.height()-1);
726            const SkRect brArea = SkRect::MakeLTRB(bWidth, bHeight, BIG, BIG);
727            HR(this->cornerOfClamp(brArea, brColor, brushVisuals.get()));
728
729            const SkColor blColor = bitmap.getColor(0,bitmap.height()-1);
730            const SkRect blArea = SkRect::MakeLTRB(-BIG, bHeight, 0, BIG);
731            HR(this->cornerOfClamp(blArea, blColor, brushVisuals.get()));
732        }
733
734        //create visual brush from canvas
735        XPS_RECT bound = {};
736        if (SkShader::kClamp_TileMode == xy[0] &&
737            SkShader::kClamp_TileMode == xy[1]) {
738
739            bound.x = BIG_F / -2;
740            bound.y = BIG_F / -2;
741            bound.width = BIG_F;
742            bound.height = BIG_F;
743        } else if (SkShader::kClamp_TileMode == xy[0]) {
744            bound.x = BIG_F / -2;
745            bound.y = 0.0f;
746            bound.width = BIG_F;
747            bound.height = static_cast<FLOAT>(bitmap.height());
748        } else if (SkShader::kClamp_TileMode == xy[1]) {
749            bound.x = 0;
750            bound.y = BIG_F / -2;
751            bound.width = static_cast<FLOAT>(bitmap.width());
752            bound.height = BIG_F;
753        }
754        SkTScopedComPtr<IXpsOMVisualBrush> clampBrush;
755        HRM(this->fXpsFactory->CreateVisualBrush(&bound, &bound, &clampBrush),
756            "Could not create visual brush for image brush.");
757        HRM(clampBrush->SetVisualLocal(brushCanvas.get()),
758            "Could not set canvas on visual brush for image brush.");
759        HRM(clampBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]),
760            "Could not set tile mode on visual brush for image brush.");
761        HRM(clampBrush->SetOpacity(alpha / 255.0f),
762            "Could not set opacity on visual brush for image brush.");
763
764        HRM(clampBrush->QueryInterface(xpsBrush), "QI failed.");
765    }
766
767    SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
768    HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
769    if (NULL != xpsMatrixToUse.get()) {
770        HRM((*xpsBrush)->SetTransformLocal(xpsMatrixToUse.get()),
771            "Could not set transform for image brush.");
772    } else {
773        //TODO(bungeman): perspective bitmaps in general.
774    }
775
776    return S_OK;
777}
778
779HRESULT SkXPSDevice::createXpsGradientStop(const SkColor skColor,
780                                           const SkScalar offset,
781                                           IXpsOMGradientStop** xpsGradStop) {
782    XPS_COLOR gradStopXpsColor = xps_color(skColor);
783    HRM(this->fXpsFactory->CreateGradientStop(&gradStopXpsColor,
784                                              NULL,
785                                              SkScalarToFLOAT(offset),
786                                              xpsGradStop),
787        "Could not create gradient stop.");
788    return S_OK;
789}
790
791HRESULT SkXPSDevice::createXpsLinearGradient(SkShader::GradientInfo info,
792                                             const SkAlpha alpha,
793                                             const SkMatrix& localMatrix,
794                                             IXpsOMMatrixTransform* xpsMatrix,
795                                             IXpsOMBrush** xpsBrush) {
796    XPS_POINT startPoint;
797    XPS_POINT endPoint;
798    if (NULL != xpsMatrix) {
799        startPoint = xps_point(info.fPoint[0]);
800        endPoint = xps_point(info.fPoint[1]);
801    } else {
802        transform_offsets(info.fColorOffsets, info.fColorCount,
803                          info.fPoint[0], info.fPoint[1],
804                          localMatrix);
805        startPoint = xps_point(info.fPoint[0], localMatrix);
806        endPoint = xps_point(info.fPoint[1], localMatrix);
807    }
808
809    SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
810    HR(createXpsGradientStop(info.fColors[0],
811                             info.fColorOffsets[0],
812                             &gradStop0));
813
814    SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
815    HR(createXpsGradientStop(info.fColors[1],
816                             info.fColorOffsets[1],
817                             &gradStop1));
818
819    SkTScopedComPtr<IXpsOMLinearGradientBrush> gradientBrush;
820    HRM(this->fXpsFactory->CreateLinearGradientBrush(gradStop0.get(),
821                                                     gradStop1.get(),
822                                                     &startPoint,
823                                                     &endPoint,
824                                                     &gradientBrush),
825        "Could not create linear gradient brush.");
826    if (NULL != xpsMatrix) {
827        HRM(gradientBrush->SetTransformLocal(xpsMatrix),
828            "Could not set transform on linear gradient brush.");
829    }
830
831    SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
832    HRM(gradientBrush->GetGradientStops(&gradStopCollection),
833        "Could not get linear gradient stop collection.");
834    for (int i = 2; i < info.fColorCount; ++i) {
835        SkTScopedComPtr<IXpsOMGradientStop> gradStop;
836        HR(createXpsGradientStop(info.fColors[i],
837                                 info.fColorOffsets[i],
838                                 &gradStop));
839        HRM(gradStopCollection->Append(gradStop.get()),
840            "Could not add linear gradient stop.");
841    }
842
843    HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)),
844        "Could not set spread method of linear gradient.");
845
846    HRM(gradientBrush->SetOpacity(alpha / 255.0f),
847        "Could not set opacity of linear gradient brush.");
848    HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed");
849
850    return S_OK;
851}
852
853HRESULT SkXPSDevice::createXpsRadialGradient(SkShader::GradientInfo info,
854                                             const SkAlpha alpha,
855                                             const SkMatrix& localMatrix,
856                                             IXpsOMMatrixTransform* xpsMatrix,
857                                             IXpsOMBrush** xpsBrush) {
858    SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
859    HR(createXpsGradientStop(info.fColors[0],
860                             info.fColorOffsets[0],
861                             &gradStop0));
862
863    SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
864    HR(createXpsGradientStop(info.fColors[1],
865                             info.fColorOffsets[1],
866                             &gradStop1));
867
868    //TODO: figure out how to fake better if not affine
869    XPS_POINT centerPoint;
870    XPS_POINT gradientOrigin;
871    XPS_SIZE radiiSizes;
872    if (NULL != xpsMatrix) {
873        centerPoint = xps_point(info.fPoint[0]);
874        gradientOrigin = xps_point(info.fPoint[0]);
875        radiiSizes.width = SkScalarToFLOAT(info.fRadius[0]);
876        radiiSizes.height = SkScalarToFLOAT(info.fRadius[0]);
877    } else {
878        centerPoint = xps_point(info.fPoint[0], localMatrix);
879        gradientOrigin = xps_point(info.fPoint[0], localMatrix);
880
881        SkScalar radius = info.fRadius[0];
882        SkVector vec[2];
883
884        vec[0].set(radius, 0);
885        vec[1].set(0, radius);
886        localMatrix.mapVectors(vec, 2);
887
888        SkScalar d0 = vec[0].length();
889        SkScalar d1 = vec[1].length();
890
891        radiiSizes.width = SkScalarToFLOAT(d0);
892        radiiSizes.height = SkScalarToFLOAT(d1);
893    }
894
895    SkTScopedComPtr<IXpsOMRadialGradientBrush> gradientBrush;
896    HRM(this->fXpsFactory->CreateRadialGradientBrush(gradStop0.get(),
897                                                     gradStop1.get(),
898                                                     &centerPoint,
899                                                     &gradientOrigin,
900                                                     &radiiSizes,
901                                                     &gradientBrush),
902        "Could not create radial gradient brush.");
903    if (NULL != xpsMatrix) {
904        HRM(gradientBrush->SetTransformLocal(xpsMatrix),
905            "Could not set transform on radial gradient brush.");
906    }
907
908    SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
909    HRM(gradientBrush->GetGradientStops(&gradStopCollection),
910        "Could not get radial gradient stop collection.");
911    for (int i = 2; i < info.fColorCount; ++i) {
912        SkTScopedComPtr<IXpsOMGradientStop> gradStop;
913        HR(createXpsGradientStop(info.fColors[i],
914                                 info.fColorOffsets[i],
915                                 &gradStop));
916        HRM(gradStopCollection->Append(gradStop.get()),
917            "Could not add radial gradient stop.");
918    }
919
920    HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)),
921        "Could not set spread method of radial gradient.");
922
923    HRM(gradientBrush->SetOpacity(alpha / 255.0f),
924        "Could not set opacity of radial gradient brush.");
925    HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed.");
926
927    return S_OK;
928}
929
930HRESULT SkXPSDevice::createXpsBrush(const SkPaint& skPaint,
931                                    IXpsOMBrush** brush,
932                                    const SkMatrix* parentTransform) {
933    const SkShader *shader = skPaint.getShader();
934    if (NULL == shader) {
935        HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
936        return S_OK;
937    }
938
939    //Gradient shaders.
940    SkShader::GradientInfo info;
941    info.fColorCount = 0;
942    info.fColors = NULL;
943    info.fColorOffsets = NULL;
944    SkShader::GradientType gradientType = shader->asAGradient(&info);
945
946    if (SkShader::kNone_GradientType == gradientType) {
947        //Nothing to see, move along.
948
949    } else if (SkShader::kColor_GradientType == gradientType) {
950        SkASSERT(1 == info.fColorCount);
951        SkColor color;
952        info.fColors = &color;
953        shader->asAGradient(&info);
954        SkAlpha alpha = skPaint.getAlpha();
955        HR(this->createXpsSolidColorBrush(color, alpha, brush));
956        return S_OK;
957
958    } else {
959        if (info.fColorCount == 0) {
960            const SkColor color = skPaint.getColor();
961            HR(this->createXpsSolidColorBrush(color, 0xFF, brush));
962            return S_OK;
963        }
964
965        SkAutoTArray<SkColor> colors(info.fColorCount);
966        SkAutoTArray<SkScalar> colorOffsets(info.fColorCount);
967        info.fColors = colors.get();
968        info.fColorOffsets = colorOffsets.get();
969        shader->asAGradient(&info);
970
971        if (1 == info.fColorCount) {
972            SkColor color = info.fColors[0];
973            SkAlpha alpha = skPaint.getAlpha();
974            HR(this->createXpsSolidColorBrush(color, alpha, brush));
975            return S_OK;
976        }
977
978        SkMatrix localMatrix = shader->getLocalMatrix();
979        if (NULL != parentTransform) {
980            localMatrix.preConcat(*parentTransform);
981        }
982        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
983        HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
984
985        if (SkShader::kLinear_GradientType == gradientType) {
986            HR(this->createXpsLinearGradient(info,
987                                             skPaint.getAlpha(),
988                                             localMatrix,
989                                             xpsMatrixToUse.get(),
990                                             brush));
991            return S_OK;
992        }
993
994        if (SkShader::kRadial_GradientType == gradientType) {
995            HR(this->createXpsRadialGradient(info,
996                                             skPaint.getAlpha(),
997                                             localMatrix,
998                                             xpsMatrixToUse.get(),
999                                             brush));
1000            return S_OK;
1001        }
1002
1003        if (SkShader::kRadial2_GradientType == gradientType ||
1004            SkShader::kConical_GradientType == gradientType) {
1005            //simple if affine and one is 0, otherwise will have to fake
1006        }
1007
1008        if (SkShader::kSweep_GradientType == gradientType) {
1009            //have to fake
1010        }
1011    }
1012
1013    SkBitmap outTexture;
1014    SkMatrix outMatrix;
1015    SkShader::TileMode xy[2];
1016    SkShader::BitmapType bitmapType = shader->asABitmap(&outTexture,
1017                                                        &outMatrix,
1018                                                        xy);
1019    switch (bitmapType) {
1020        case SkShader::kNone_BitmapType:
1021            break;
1022        case SkShader::kDefault_BitmapType: {
1023            //TODO: outMatrix??
1024            SkMatrix localMatrix = shader->getLocalMatrix();
1025            if (NULL != parentTransform) {
1026                localMatrix.preConcat(*parentTransform);
1027            }
1028
1029            SkTScopedComPtr<IXpsOMTileBrush> tileBrush;
1030            HR(this->createXpsImageBrush(outTexture,
1031                                         localMatrix,
1032                                         xy,
1033                                         skPaint.getAlpha(),
1034                                         &tileBrush));
1035
1036            HRM(tileBrush->QueryInterface<IXpsOMBrush>(brush), "QI failed.");
1037
1038            return S_OK;
1039        }
1040        case SkShader::kRadial_BitmapType:
1041        case SkShader::kSweep_BitmapType:
1042        case SkShader::kTwoPointRadial_BitmapType:
1043        default:
1044            break;
1045    }
1046
1047    HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
1048    return S_OK;
1049}
1050
1051static bool rect_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
1052    const bool zeroWidth = (0 == paint.getStrokeWidth());
1053    const bool stroke = (SkPaint::kFill_Style != paint.getStyle());
1054
1055    return paint.getPathEffect() ||
1056           paint.getMaskFilter() ||
1057           paint.getRasterizer() ||
1058           (stroke && (
1059               (matrix.hasPerspective() && !zeroWidth) ||
1060               SkPaint::kMiter_Join != paint.getStrokeJoin() ||
1061               (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
1062                paint.getStrokeMiter() < SK_ScalarSqrt2)
1063           ))
1064    ;
1065}
1066
1067HRESULT SkXPSDevice::createXpsRect(const SkRect& rect, BOOL stroke, BOOL fill,
1068                                   IXpsOMGeometryFigure** xpsRect) {
1069    const SkPoint points[4] = {
1070        { rect.fLeft, rect.fTop },
1071        { rect.fRight, rect.fTop },
1072        { rect.fRight, rect.fBottom },
1073        { rect.fLeft, rect.fBottom },
1074    };
1075    return this->createXpsQuad(points, stroke, fill, xpsRect);
1076}
1077HRESULT SkXPSDevice::createXpsQuad(const SkPoint (&points)[4],
1078                                   BOOL stroke, BOOL fill,
1079                                   IXpsOMGeometryFigure** xpsQuad) {
1080    // Define the start point.
1081    XPS_POINT startPoint = xps_point(points[0]);
1082
1083    // Create the figure.
1084    HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint, xpsQuad),
1085        "Could not create quad geometry figure.");
1086
1087    // Define the type of each segment.
1088    XPS_SEGMENT_TYPE segmentTypes[3] = {
1089        XPS_SEGMENT_TYPE_LINE,
1090        XPS_SEGMENT_TYPE_LINE,
1091        XPS_SEGMENT_TYPE_LINE,
1092    };
1093
1094    // Define the x and y coordinates of each corner of the figure.
1095    FLOAT segmentData[6] = {
1096        SkScalarToFLOAT(points[1].fX), SkScalarToFLOAT(points[1].fY),
1097        SkScalarToFLOAT(points[2].fX), SkScalarToFLOAT(points[2].fY),
1098        SkScalarToFLOAT(points[3].fX), SkScalarToFLOAT(points[3].fY),
1099    };
1100
1101    // Describe if the segments are stroked.
1102    BOOL segmentStrokes[3] = {
1103        stroke, stroke, stroke,
1104    };
1105
1106    // Add the segment data to the figure.
1107    HRM((*xpsQuad)->SetSegments(
1108            3, 6,
1109            segmentTypes , segmentData, segmentStrokes),
1110        "Could not add segment data to quad.");
1111
1112    // Set the closed and filled properties of the figure.
1113    HRM((*xpsQuad)->SetIsClosed(stroke), "Could not set quad close.");
1114    HRM((*xpsQuad)->SetIsFilled(fill), "Could not set quad fill.");
1115
1116    return S_OK;
1117}
1118
1119uint32_t SkXPSDevice::getDeviceCapabilities() {
1120    return kVector_Capability;
1121}
1122
1123void SkXPSDevice::clear(SkColor color) {
1124    //TODO: override this for XPS
1125    SkDEBUGF(("XPS clear not yet implemented."));
1126}
1127
1128void SkXPSDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
1129                             size_t count, const SkPoint points[],
1130                             const SkPaint& paint) {
1131    //This will call back into the device to do the drawing.
1132    d.drawPoints(mode, count, points, paint, true);
1133}
1134
1135void SkXPSDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
1136                               int vertexCount, const SkPoint verts[],
1137                               const SkPoint texs[], const SkColor colors[],
1138                               SkXfermode* xmode, const uint16_t indices[],
1139                               int indexCount, const SkPaint& paint) {
1140    //TODO: override this for XPS
1141    SkDEBUGF(("XPS drawVertices not yet implemented."));
1142}
1143
1144void SkXPSDevice::drawPaint(const SkDraw& d, const SkPaint& origPaint) {
1145    const SkRect r = SkRect::MakeSize(this->fCurrentCanvasSize);
1146
1147    //If trying to paint with a stroke, ignore that and fill.
1148    SkPaint* fillPaint = const_cast<SkPaint*>(&origPaint);
1149    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1150    if (paint->getStyle() != SkPaint::kFill_Style) {
1151        paint.writable()->setStyle(SkPaint::kFill_Style);
1152    }
1153
1154    this->internalDrawRect(d, r, false, *fillPaint);
1155}
1156
1157void SkXPSDevice::drawRect(const SkDraw& d,
1158                           const SkRect& r,
1159                           const SkPaint& paint) {
1160    this->internalDrawRect(d, r, true, paint);
1161}
1162
1163void SkXPSDevice::internalDrawRect(const SkDraw& d,
1164                                   const SkRect& r,
1165                                   bool transformRect,
1166                                   const SkPaint& paint) {
1167    //Exit early if there is nothing to draw.
1168    if (d.fClip->isEmpty() ||
1169        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1170        return;
1171    }
1172
1173    //Path the rect if we can't optimize it.
1174    if (rect_must_be_pathed(paint, *d.fMatrix)) {
1175        SkPath tmp;
1176        tmp.addRect(r);
1177        tmp.setFillType(SkPath::kWinding_FillType);
1178        this->drawPath(d, tmp, paint, NULL, true);
1179        return;
1180    }
1181
1182    //Create the shaded path.
1183    SkTScopedComPtr<IXpsOMPath> shadedPath;
1184    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1185         "Could not create shaded path for rect.");
1186
1187    //Create the shaded geometry.
1188    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1189    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1190         "Could not create shaded geometry for rect.");
1191
1192    //Add the geometry to the shaded path.
1193    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1194         "Could not set shaded geometry for rect.");
1195
1196    //Set the brushes.
1197    BOOL fill = FALSE;
1198    BOOL stroke = FALSE;
1199    HRV(this->shadePath(shadedPath.get(), paint, *d.fMatrix, &fill, &stroke));
1200
1201    bool xpsTransformsPath = true;
1202    //Transform the geometry.
1203    if (transformRect && xpsTransformsPath) {
1204        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1205        HRV(this->createXpsTransform(*d.fMatrix, &xpsTransform));
1206        if (xpsTransform.get()) {
1207            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1208                 "Could not set transform for rect.");
1209        } else {
1210            xpsTransformsPath = false;
1211        }
1212    }
1213
1214    //Create the figure.
1215    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
1216    {
1217        SkPoint points[4] = {
1218            { r.fLeft, r.fTop },
1219            { r.fLeft, r.fBottom },
1220            { r.fRight, r.fBottom },
1221            { r.fRight, r.fTop },
1222        };
1223        if (!xpsTransformsPath && transformRect) {
1224            d.fMatrix->mapPoints(points, SK_ARRAY_COUNT(points));
1225        }
1226        HRV(this->createXpsQuad(points, stroke, fill, &rectFigure));
1227    }
1228
1229    //Get the figures of the shaded geometry.
1230    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1231    HRVM(shadedGeometry->GetFigures(&shadedFigures),
1232         "Could not get shaded figures for rect.");
1233
1234    //Add the figure to the shaded geometry figures.
1235    HRVM(shadedFigures->Append(rectFigure.get()),
1236         "Could not add shaded figure for rect.");
1237
1238    HRV(this->clip(shadedPath.get(), d));
1239
1240    //Add the shaded path to the current visuals.
1241    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1242    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1243         "Could not get current visuals for rect.");
1244    HRVM(currentVisuals->Append(shadedPath.get()),
1245         "Could not add rect to current visuals.");
1246}
1247
1248static HRESULT close_figure(const SkTDArray<XPS_SEGMENT_TYPE>& segmentTypes,
1249                            const SkTDArray<BOOL>& segmentStrokes,
1250                            const SkTDArray<FLOAT>& segmentData,
1251                            BOOL stroke, BOOL fill,
1252                            IXpsOMGeometryFigure* figure,
1253                            IXpsOMGeometryFigureCollection* figures) {
1254    // Add the segment data to the figure.
1255    HRM(figure->SetSegments(segmentTypes.count(), segmentData.count(),
1256                            segmentTypes.begin() , segmentData.begin(),
1257                            segmentStrokes.begin()),
1258        "Could not set path segments.");
1259
1260    // Set the closed and filled properties of the figure.
1261    HRM(figure->SetIsClosed(stroke), "Could not set path closed.");
1262    HRM(figure->SetIsFilled(fill), "Could not set path fill.");
1263
1264    // Add the figure created above to this geometry.
1265    HRM(figures->Append(figure), "Could not add path to geometry.");
1266    return S_OK;
1267}
1268
1269HRESULT SkXPSDevice::addXpsPathGeometry(
1270        IXpsOMGeometryFigureCollection* xpsFigures,
1271        BOOL stroke, BOOL fill, const SkPath& path) {
1272    SkTDArray<XPS_SEGMENT_TYPE> segmentTypes;
1273    SkTDArray<BOOL> segmentStrokes;
1274    SkTDArray<FLOAT> segmentData;
1275
1276    SkTScopedComPtr<IXpsOMGeometryFigure> xpsFigure;
1277    SkPath::Iter iter(path, true);
1278    SkPoint points[4];
1279    SkPath::Verb verb;
1280    while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
1281        switch (verb) {
1282            case SkPath::kMove_Verb: {
1283                if (NULL != xpsFigure.get()) {
1284                    HR(close_figure(segmentTypes, segmentStrokes, segmentData,
1285                                    stroke, fill,
1286                                    xpsFigure.get() , xpsFigures));
1287                    xpsFigure.reset();
1288                    segmentTypes.rewind();
1289                    segmentStrokes.rewind();
1290                    segmentData.rewind();
1291                }
1292                // Define the start point.
1293                XPS_POINT startPoint = xps_point(points[0]);
1294                // Create the figure.
1295                HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint,
1296                                                            &xpsFigure),
1297                    "Could not create path geometry figure.");
1298                break;
1299            }
1300            case SkPath::kLine_Verb:
1301                if (iter.isCloseLine()) break; //ignore the line, auto-closed
1302                segmentTypes.push(XPS_SEGMENT_TYPE_LINE);
1303                segmentStrokes.push(stroke);
1304                segmentData.push(SkScalarToFLOAT(points[1].fX));
1305                segmentData.push(SkScalarToFLOAT(points[1].fY));
1306                break;
1307            case SkPath::kQuad_Verb:
1308                segmentTypes.push(XPS_SEGMENT_TYPE_QUADRATIC_BEZIER);
1309                segmentStrokes.push(stroke);
1310                segmentData.push(SkScalarToFLOAT(points[1].fX));
1311                segmentData.push(SkScalarToFLOAT(points[1].fY));
1312                segmentData.push(SkScalarToFLOAT(points[2].fX));
1313                segmentData.push(SkScalarToFLOAT(points[2].fY));
1314                break;
1315            case SkPath::kCubic_Verb:
1316                segmentTypes.push(XPS_SEGMENT_TYPE_BEZIER);
1317                segmentStrokes.push(stroke);
1318                segmentData.push(SkScalarToFLOAT(points[1].fX));
1319                segmentData.push(SkScalarToFLOAT(points[1].fY));
1320                segmentData.push(SkScalarToFLOAT(points[2].fX));
1321                segmentData.push(SkScalarToFLOAT(points[2].fY));
1322                segmentData.push(SkScalarToFLOAT(points[3].fX));
1323                segmentData.push(SkScalarToFLOAT(points[3].fY));
1324                break;
1325            case SkPath::kClose_Verb:
1326                // we ignore these, and just get the whole segment from
1327                // the corresponding line/quad/cubic verbs
1328                break;
1329            default:
1330                SkASSERT(!"unexpected verb");
1331                break;
1332        }
1333    }
1334    if (NULL != xpsFigure.get()) {
1335        HR(close_figure(segmentTypes, segmentStrokes, segmentData,
1336                        stroke, fill,
1337                        xpsFigure.get(), xpsFigures));
1338    }
1339    return S_OK;
1340}
1341
1342HRESULT SkXPSDevice::drawInverseWindingPath(const SkDraw& d,
1343                                            const SkPath& devicePath,
1344                                            IXpsOMPath* shadedPath) {
1345    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
1346        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
1347
1348    const XPS_RECT universeRectXps = {
1349        0.0f, 0.0f,
1350        SkScalarToFLOAT(this->fCurrentCanvasSize.fWidth),
1351        SkScalarToFLOAT(this->fCurrentCanvasSize.fHeight),
1352    };
1353
1354    //Get the geometry.
1355    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1356    HRM(shadedPath->GetGeometry(&shadedGeometry),
1357        "Could not get shaded geometry for inverse path.");
1358
1359    //Get the figures from the geometry.
1360    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1361    HRM(shadedGeometry->GetFigures(&shadedFigures),
1362        "Could not get shaded figures for inverse path.");
1363
1364    HRM(shadedGeometry->SetFillRule(XPS_FILL_RULE_NONZERO),
1365        "Could not set shaded fill rule for inverse path.");
1366
1367    //Take everything drawn so far, and make a shared resource out of it.
1368    //Replace everything drawn so far with
1369    //inverse canvas
1370    //  old canvas of everything so far
1371    //  world shaded figure, clipped to current clip
1372    //  top canvas of everything so far, clipped to path
1373    //Note: this is not quite right when there is nothing solid in the
1374    //canvas of everything so far, as the bit on top will allow
1375    //the world paint to show through.
1376
1377    //Create new canvas.
1378    SkTScopedComPtr<IXpsOMCanvas> newCanvas;
1379    HRM(this->fXpsFactory->CreateCanvas(&newCanvas),
1380        "Could not create inverse canvas.");
1381
1382    //Save the old canvas to a dictionary on the new canvas.
1383    SkTScopedComPtr<IXpsOMDictionary> newDictionary;
1384    HRM(this->fXpsFactory->CreateDictionary(&newDictionary),
1385        "Could not create inverse dictionary.");
1386    HRM(newCanvas->SetDictionaryLocal(newDictionary.get()),
1387        "Could not set inverse dictionary.");
1388
1389    const size_t size = SK_ARRAY_COUNT(L"ID" L_GUID_ID);
1390    wchar_t buffer[size];
1391    wchar_t id[GUID_ID_LEN];
1392    HR(create_id(id, GUID_ID_LEN, '_'));
1393    swprintf_s(buffer, size, L"ID%s", id);
1394    HRM(newDictionary->Append(buffer, this->fCurrentXpsCanvas.get()),
1395        "Could not add canvas to inverse dictionary.");
1396
1397    //Start drawing
1398    SkTScopedComPtr<IXpsOMVisualCollection> newVisuals;
1399    HRM(newCanvas->GetVisuals(&newVisuals),
1400        "Could not get inverse canvas visuals.");
1401
1402    //Draw old canvas from dictionary onto new canvas.
1403    SkTScopedComPtr<IXpsOMGeometry> oldGeometry;
1404    HRM(this->fXpsFactory->CreateGeometry(&oldGeometry),
1405        "Could not create old inverse geometry.");
1406
1407    SkTScopedComPtr<IXpsOMGeometryFigureCollection> oldFigures;
1408    HRM(oldGeometry->GetFigures(&oldFigures),
1409        "Could not get old inverse figures.");
1410
1411    SkTScopedComPtr<IXpsOMGeometryFigure> oldFigure;
1412    HR(this->createXpsRect(universeRect, FALSE, TRUE, &oldFigure));
1413    HRM(oldFigures->Append(oldFigure.get()),
1414        "Could not add old inverse figure.");
1415
1416    SkTScopedComPtr<IXpsOMVisualBrush> oldBrush;
1417    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
1418                                             &universeRectXps,
1419                                             &oldBrush),
1420        "Could not create old inverse brush.");
1421
1422    SkTScopedComPtr<IXpsOMPath> oldPath;
1423    HRM(this->fXpsFactory->CreatePath(&oldPath),
1424        "Could not create old inverse path.");
1425    HRM(oldPath->SetGeometryLocal(oldGeometry.get()),
1426        "Could not set old inverse geometry.");
1427    HRM(oldPath->SetFillBrushLocal(oldBrush.get()),
1428        "Could not set old inverse fill brush.");
1429    //the brush must be parented before setting the lookup.
1430    HRM(newVisuals->Append(oldPath.get()),
1431        "Could not add old inverse path to new canvas visuals.");
1432    HRM(oldBrush->SetVisualLookup(buffer),
1433        "Could not set old inverse brush visual lookup.");
1434
1435    //Draw the clip filling shader.
1436    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
1437    HR(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure));
1438    HRM(shadedFigures->Append(shadedFigure.get()),
1439        "Could not add inverse shaded figure.");
1440    //the geometry is already set
1441    HR(this->clip(shadedPath, d));
1442    HRM(newVisuals->Append(shadedPath),
1443        "Could not add inverse shaded path to canvas visuals.");
1444
1445    //Draw the old canvas on top, clipped to the original path.
1446    SkTScopedComPtr<IXpsOMCanvas> topCanvas;
1447    HRM(this->fXpsFactory->CreateCanvas(&topCanvas),
1448        "Could not create top inverse canvas.");
1449    //Clip the canvas to prevent alpha spill.
1450    //This is the entire reason this canvas exists.
1451    HR(this->clip(topCanvas.get(), d));
1452
1453    SkTScopedComPtr<IXpsOMGeometry> topGeometry;
1454    HRM(this->fXpsFactory->CreateGeometry(&topGeometry),
1455        "Could not create top inverse geometry.");
1456
1457    SkTScopedComPtr<IXpsOMGeometryFigureCollection> topFigures;
1458    HRM(topGeometry->GetFigures(&topFigures),
1459        "Could not get top inverse figures.");
1460
1461    SkTScopedComPtr<IXpsOMGeometryFigure> topFigure;
1462    HR(this->createXpsRect(universeRect, FALSE, TRUE, &topFigure));
1463    HRM(topFigures->Append(topFigure.get()),
1464        "Could not add old inverse figure.");
1465
1466    SkTScopedComPtr<IXpsOMVisualBrush> topBrush;
1467    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
1468                                             &universeRectXps,
1469                                             &topBrush),
1470        "Could not create top inverse brush.");
1471
1472    SkTScopedComPtr<IXpsOMPath> topPath;
1473    HRM(this->fXpsFactory->CreatePath(&topPath),
1474        "Could not create top inverse path.");
1475    HRM(topPath->SetGeometryLocal(topGeometry.get()),
1476        "Could not set top inverse geometry.");
1477    HRM(topPath->SetFillBrushLocal(topBrush.get()),
1478        "Could not set top inverse fill brush.");
1479    //the brush must be parented before setting the lookup.
1480    HRM(newVisuals->Append(topCanvas.get()),
1481        "Could not add top canvas to inverse canvas visuals.");
1482    SkTScopedComPtr<IXpsOMVisualCollection> topVisuals;
1483    HRM(topCanvas->GetVisuals(&topVisuals),
1484        "Could not get top inverse canvas visuals.");
1485    HRM(topVisuals->Append(topPath.get()),
1486        "Could not add top inverse path to top canvas visuals.");
1487    HRM(topBrush->SetVisualLookup(buffer),
1488        "Could not set top inverse brush visual lookup.");
1489
1490    HR(this->clipToPath(topPath.get(), devicePath, XPS_FILL_RULE_NONZERO));
1491
1492    //swap current canvas to new canvas
1493    this->fCurrentXpsCanvas.swap(newCanvas);
1494
1495    return S_OK;
1496}
1497
1498void SkXPSDevice::convertToPpm(const SkMaskFilter* filter,
1499                               SkMatrix* matrix,
1500                               SkVector* ppuScale,
1501                               const SkIRect& clip, SkIRect* clipIRect) {
1502    //TODO: currently ignoring the ppm if blur ignoring transform.
1503    if (filter) {
1504        SkMaskFilter::BlurInfo blurInfo;
1505        SkMaskFilter::BlurType blurType = filter->asABlur(&blurInfo);
1506
1507        if (SkMaskFilter::kNone_BlurType != blurType
1508            && blurInfo.fIgnoreTransform) {
1509
1510            ppuScale->fX = SK_Scalar1;
1511            ppuScale->fY = SK_Scalar1;
1512            *clipIRect = clip;
1513            return;
1514        }
1515    }
1516
1517    //This action is in unit space, but the ppm is specified in physical space.
1518    ppuScale->fX = SkScalarDiv(this->fCurrentPixelsPerMeter.fX,
1519                               this->fCurrentUnitsPerMeter.fX);
1520    ppuScale->fY = SkScalarDiv(this->fCurrentPixelsPerMeter.fY,
1521                               this->fCurrentUnitsPerMeter.fY);
1522
1523    matrix->postScale(ppuScale->fX, ppuScale->fY);
1524
1525    const SkIRect& irect = clip;
1526    SkRect clipRect = SkRect::MakeLTRB(
1527        SkScalarMul(SkIntToScalar(irect.fLeft), ppuScale->fX),
1528        SkScalarMul(SkIntToScalar(irect.fTop), ppuScale->fY),
1529        SkScalarMul(SkIntToScalar(irect.fRight), ppuScale->fX),
1530        SkScalarMul(SkIntToScalar(irect.fBottom), ppuScale->fY));
1531    clipRect.roundOut(clipIRect);
1532}
1533
1534HRESULT SkXPSDevice::applyMask(const SkDraw& d,
1535                               const SkMask& mask,
1536                               const SkVector& ppuScale,
1537                               IXpsOMPath* shadedPath) {
1538    //Get the geometry object.
1539    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1540    HRM(shadedPath->GetGeometry(&shadedGeometry),
1541        "Could not get mask shaded geometry.");
1542
1543    //Get the figures from the geometry.
1544    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1545    HRM(shadedGeometry->GetFigures(&shadedFigures),
1546        "Could not get mask shaded figures.");
1547
1548    SkMatrix m;
1549    m.reset();
1550    m.setTranslate(SkIntToScalar(mask.fBounds.fLeft),
1551                   SkIntToScalar(mask.fBounds.fTop));
1552    m.postScale(SkScalarInvert(ppuScale.fX), SkScalarInvert(ppuScale.fY));
1553
1554    SkShader::TileMode xy[2];
1555    xy[0] = (SkShader::TileMode)3;
1556    xy[1] = (SkShader::TileMode)3;
1557
1558    SkBitmap bm;
1559    bm.setConfig(SkBitmap::kA8_Config,
1560                 mask.fBounds.width(),
1561                 mask.fBounds.height(),
1562                 mask.fRowBytes);
1563    bm.setPixels(mask.fImage);
1564
1565    SkTScopedComPtr<IXpsOMTileBrush> maskBrush;
1566    HR(this->createXpsImageBrush(bm, m, xy, 0xFF, &maskBrush));
1567    HRM(shadedPath->SetOpacityMaskBrushLocal(maskBrush.get()),
1568        "Could not set mask.");
1569
1570    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
1571        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
1572    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
1573    HRM(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure),
1574        "Could not create mask shaded figure.");
1575    HRM(shadedFigures->Append(shadedFigure.get()),
1576        "Could not add mask shaded figure.");
1577
1578    HR(this->clip(shadedPath, d));
1579
1580    //Add the path to the active visual collection.
1581    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1582    HRM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1583        "Could not get mask current visuals.");
1584    HRM(currentVisuals->Append(shadedPath),
1585        "Could not add masked shaded path to current visuals.");
1586
1587    return S_OK;
1588}
1589
1590HRESULT SkXPSDevice::shadePath(IXpsOMPath* shadedPath,
1591                               const SkPaint& shaderPaint,
1592                               const SkMatrix& matrix,
1593                               BOOL* fill, BOOL* stroke) {
1594    *fill = FALSE;
1595    *stroke = FALSE;
1596
1597    const SkPaint::Style style = shaderPaint.getStyle();
1598    const bool hasFill = SkPaint::kFill_Style == style
1599                      || SkPaint::kStrokeAndFill_Style == style;
1600    const bool hasStroke = SkPaint::kStroke_Style == style
1601                        || SkPaint::kStrokeAndFill_Style == style;
1602
1603    //TODO(bungeman): use dictionaries and lookups.
1604    if (hasFill) {
1605        *fill = TRUE;
1606        SkTScopedComPtr<IXpsOMBrush> fillBrush;
1607        HR(this->createXpsBrush(shaderPaint, &fillBrush, &matrix));
1608        HRM(shadedPath->SetFillBrushLocal(fillBrush.get()),
1609            "Could not set fill for shaded path.");
1610    }
1611
1612    if (hasStroke) {
1613        *stroke = TRUE;
1614        SkTScopedComPtr<IXpsOMBrush> strokeBrush;
1615        HR(this->createXpsBrush(shaderPaint, &strokeBrush, &matrix));
1616        HRM(shadedPath->SetStrokeBrushLocal(strokeBrush.get()),
1617            "Could not set stroke brush for shaded path.");
1618        HRM(shadedPath->SetStrokeThickness(
1619                SkScalarToFLOAT(shaderPaint.getStrokeWidth())),
1620            "Could not set shaded path stroke thickness.");
1621
1622        if (0 == shaderPaint.getStrokeWidth()) {
1623            //XPS hair width is a hack. (XPS Spec 11.6.12).
1624            SkTScopedComPtr<IXpsOMDashCollection> dashes;
1625            HRM(shadedPath->GetStrokeDashes(&dashes),
1626                "Could not set dashes for shaded path.");
1627            XPS_DASH dash;
1628            dash.length = 1.0;
1629            dash.gap = 0.0;
1630            HRM(dashes->Append(&dash), "Could not add dashes to shaded path.");
1631            HRM(shadedPath->SetStrokeDashOffset(-2.0),
1632                "Could not set dash offset for shaded path.");
1633        }
1634    }
1635    return S_OK;
1636}
1637
1638void SkXPSDevice::drawPath(const SkDraw& d,
1639                           const SkPath& platonicPath,
1640                           const SkPaint& origPaint,
1641                           const SkMatrix* prePathMatrix,
1642                           bool pathIsMutable) {
1643    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
1644
1645    // nothing to draw
1646    if (d.fClip->isEmpty() ||
1647        (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
1648        return;
1649    }
1650
1651    SkPath modifiedPath;
1652    const bool paintHasPathEffect = paint->getPathEffect()
1653                                 || paint->getStyle() != SkPaint::kFill_Style;
1654
1655    //Apply pre-path matrix [Platonic-path -> Skeletal-path].
1656    SkMatrix matrix = *d.fMatrix;
1657    SkPath* skeletalPath = const_cast<SkPath*>(&platonicPath);
1658    if (prePathMatrix) {
1659        if (paintHasPathEffect || paint->getRasterizer()) {
1660            if (!pathIsMutable) {
1661                skeletalPath = &modifiedPath;
1662                pathIsMutable = true;
1663            }
1664            platonicPath.transform(*prePathMatrix, skeletalPath);
1665        } else {
1666            if (!matrix.preConcat(*prePathMatrix)) {
1667                return;
1668            }
1669        }
1670    }
1671
1672    //Apply path effect [Skeletal-path -> Fillable-path].
1673    SkPath* fillablePath = skeletalPath;
1674    if (paintHasPathEffect) {
1675        if (!pathIsMutable) {
1676            fillablePath = &modifiedPath;
1677            pathIsMutable = true;
1678        }
1679        bool fill = paint->getFillPath(*skeletalPath, fillablePath);
1680
1681        SkPaint* writablePaint = paint.writable();
1682        writablePaint->setPathEffect(NULL);
1683        if (fill) {
1684            writablePaint->setStyle(SkPaint::kFill_Style);
1685        } else {
1686            writablePaint->setStyle(SkPaint::kStroke_Style);
1687            writablePaint->setStrokeWidth(0);
1688        }
1689    }
1690
1691    //Create the shaded path. This will be the path which is painted.
1692    SkTScopedComPtr<IXpsOMPath> shadedPath;
1693    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1694         "Could not create shaded path for path.");
1695
1696    //Create the geometry for the shaded path.
1697    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1698    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1699         "Could not create shaded geometry for path.");
1700
1701    //Add the geometry to the shaded path.
1702    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1703         "Could not add the shaded geometry to shaded path.");
1704
1705    SkRasterizer* rasterizer = paint->getRasterizer();
1706    SkMaskFilter* filter = paint->getMaskFilter();
1707
1708    //Determine if we will draw or shade and mask.
1709    if (rasterizer || filter) {
1710        if (paint->getStyle() != SkPaint::kFill_Style) {
1711            paint.writable()->setStyle(SkPaint::kFill_Style);
1712        }
1713    }
1714
1715    //Set the brushes.
1716    BOOL fill;
1717    BOOL stroke;
1718    HRV(this->shadePath(shadedPath.get(),
1719                        *paint,
1720                        *d.fMatrix,
1721                        &fill,
1722                        &stroke));
1723
1724    //Rasterizer
1725    if (rasterizer) {
1726        SkIRect clipIRect;
1727        SkVector ppuScale;
1728        this->convertToPpm(filter,
1729                           &matrix,
1730                           &ppuScale,
1731                           d.fClip->getBounds(),
1732                           &clipIRect);
1733
1734        SkMask* mask = NULL;
1735
1736        //[Fillable-path -> Mask]
1737        SkMask rasteredMask;
1738        if (rasterizer->rasterize(
1739                *fillablePath,
1740                matrix,
1741                &clipIRect,
1742                filter,  //just to compute how much to draw.
1743                &rasteredMask,
1744                SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
1745
1746            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
1747            mask = &rasteredMask;
1748
1749            //[Mask -> Mask]
1750            SkMask filteredMask;
1751            if (filter &&
1752                filter->filterMask(&filteredMask, *mask, *d.fMatrix, NULL)) {
1753
1754                mask = &filteredMask;
1755            } else {
1756                filteredMask.fImage = NULL;
1757            }
1758            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
1759
1760            //Draw mask.
1761            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
1762        }
1763        return;
1764    }
1765
1766    //Mask filter
1767    if (filter) {
1768        SkIRect clipIRect;
1769        SkVector ppuScale;
1770        this->convertToPpm(filter,
1771                           &matrix,
1772                           &ppuScale,
1773                           d.fClip->getBounds(),
1774                           &clipIRect);
1775
1776        //[Fillable-path -> Pixel-path]
1777        SkPath* pixelPath = pathIsMutable ? fillablePath : &modifiedPath;
1778        fillablePath->transform(matrix, pixelPath);
1779
1780        SkMask* mask = NULL;
1781
1782        //[Pixel-path -> Mask]
1783        SkMask rasteredMask;
1784        if (SkDraw::DrawToMask(
1785                        *pixelPath,
1786                        &clipIRect,
1787                        filter,  //just to compute how much to draw.
1788                        &matrix,
1789                        &rasteredMask,
1790                        SkMask::kComputeBoundsAndRenderImage_CreateMode,
1791                        paint->getStyle())) {
1792
1793            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
1794            mask = &rasteredMask;
1795
1796            //[Mask -> Mask]
1797            SkMask filteredMask;
1798            if (filter->filterMask(&filteredMask,
1799                                   rasteredMask,
1800                                   matrix,
1801                                   NULL)) {
1802                mask = &filteredMask;
1803            } else {
1804                filteredMask.fImage = NULL;
1805            }
1806            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
1807
1808            //Draw mask.
1809            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
1810        }
1811        return;
1812    }
1813
1814    //Get the figures from the shaded geometry.
1815    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1816    HRVM(shadedGeometry->GetFigures(&shadedFigures),
1817         "Could not get shaded figures for shaded path.");
1818
1819    bool xpsTransformsPath = true;
1820
1821    //Set the fill rule.
1822    XPS_FILL_RULE xpsFillRule;
1823    switch (platonicPath.getFillType()) {
1824        case SkPath::kWinding_FillType:
1825            xpsFillRule = XPS_FILL_RULE_NONZERO;
1826            break;
1827        case SkPath::kEvenOdd_FillType:
1828            xpsFillRule = XPS_FILL_RULE_EVENODD;
1829            break;
1830        case SkPath::kInverseWinding_FillType: {
1831            //[Fillable-path -> Device-path]
1832            SkPath* devicePath = pathIsMutable ? fillablePath : &modifiedPath;
1833            fillablePath->transform(matrix, devicePath);
1834
1835            HRV(this->drawInverseWindingPath(d,
1836                                             *devicePath,
1837                                             shadedPath.get()));
1838            return;
1839        }
1840        case SkPath::kInverseEvenOdd_FillType: {
1841            const SkRect universe = SkRect::MakeLTRB(
1842                0, 0,
1843                this->fCurrentCanvasSize.fWidth,
1844                this->fCurrentCanvasSize.fHeight);
1845            SkTScopedComPtr<IXpsOMGeometryFigure> addOneFigure;
1846            HRV(this->createXpsRect(universe, FALSE, TRUE, &addOneFigure));
1847            HRVM(shadedFigures->Append(addOneFigure.get()),
1848                 "Could not add even-odd flip figure to shaded path.");
1849            xpsTransformsPath = false;
1850            xpsFillRule = XPS_FILL_RULE_EVENODD;
1851            break;
1852        }
1853        default:
1854            SkASSERT(!"Unknown SkPath::FillType.");
1855    }
1856    HRVM(shadedGeometry->SetFillRule(xpsFillRule),
1857         "Could not set fill rule for shaded path.");
1858
1859    //Create the XPS transform, if possible.
1860    if (xpsTransformsPath) {
1861        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1862        HRV(this->createXpsTransform(matrix, &xpsTransform));
1863
1864        if (xpsTransform.get()) {
1865            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1866                 "Could not set transform on shaded path.");
1867        } else {
1868            xpsTransformsPath = false;
1869        }
1870    }
1871
1872    SkPath* devicePath = fillablePath;
1873    if (!xpsTransformsPath) {
1874        //[Fillable-path -> Device-path]
1875        devicePath = pathIsMutable ? fillablePath : &modifiedPath;
1876        fillablePath->transform(matrix, devicePath);
1877    }
1878    HRV(this->addXpsPathGeometry(shadedFigures.get(),
1879                                 stroke, fill, *devicePath));
1880
1881    HRV(this->clip(shadedPath.get(), d));
1882
1883    //Add the path to the active visual collection.
1884    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1885    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1886         "Could not get current visuals for shaded path.");
1887    HRVM(currentVisuals->Append(shadedPath.get()),
1888         "Could not add shaded path to current visuals.");
1889}
1890
1891HRESULT SkXPSDevice::clip(IXpsOMVisual* xpsVisual, const SkDraw& d) {
1892    SkPath clipPath;
1893    SkAssertResult(d.fClip->getBoundaryPath(&clipPath));
1894
1895    return this->clipToPath(xpsVisual, clipPath, XPS_FILL_RULE_EVENODD);
1896}
1897HRESULT SkXPSDevice::clipToPath(IXpsOMVisual* xpsVisual,
1898                                const SkPath& clipPath,
1899                                XPS_FILL_RULE fillRule) {
1900    //Create the geometry.
1901    SkTScopedComPtr<IXpsOMGeometry> clipGeometry;
1902    HRM(this->fXpsFactory->CreateGeometry(&clipGeometry),
1903        "Could not create clip geometry.");
1904
1905    //Get the figure collection of the geometry.
1906    SkTScopedComPtr<IXpsOMGeometryFigureCollection> clipFigures;
1907    HRM(clipGeometry->GetFigures(&clipFigures),
1908        "Could not get the clip figures.");
1909
1910    //Create the figures into the geometry.
1911    HR(this->addXpsPathGeometry(
1912        clipFigures.get(),
1913        FALSE, TRUE, clipPath));
1914
1915    HRM(clipGeometry->SetFillRule(fillRule),
1916        "Could not set fill rule.");
1917    HRM(xpsVisual->SetClipGeometryLocal(clipGeometry.get()),
1918        "Could not set clip geometry.");
1919
1920    return S_OK;
1921}
1922
1923void SkXPSDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
1924                             const SkIRect* srcRectOrNull,
1925                             const SkMatrix& matrix, const SkPaint& paint) {
1926    if (d.fClip->isEmpty()) {
1927        return;
1928    }
1929
1930    SkIRect srcRect;
1931    SkBitmap tmp;
1932    const SkBitmap* bitmapPtr = &bitmap;
1933    if (NULL == srcRectOrNull) {
1934        srcRect.set(0, 0, bitmap.width(), bitmap.height());
1935        bitmapPtr = &bitmap;
1936    } else {
1937        srcRect = *srcRectOrNull;
1938        if (!bitmap.extractSubset(&tmp, srcRect)) {
1939            return; // extraction failed
1940        }
1941        bitmapPtr = &tmp;
1942    }
1943
1944    //Create the new shaded path.
1945    SkTScopedComPtr<IXpsOMPath> shadedPath;
1946    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1947         "Could not create path for bitmap.");
1948
1949    //Create the shaded geometry.
1950    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1951    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1952         "Could not create geometry for bitmap.");
1953
1954    //Add the shaded geometry to the shaded path.
1955    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1956         "Could not set the geometry for bitmap.");
1957
1958    //Get the shaded figures from the shaded geometry.
1959    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1960    HRVM(shadedGeometry->GetFigures(&shadedFigures),
1961         "Could not get the figures for bitmap.");
1962
1963    SkMatrix transform = matrix;
1964    transform.postConcat(*d.fMatrix);
1965
1966    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1967    HRV(this->createXpsTransform(transform, &xpsTransform));
1968    if (xpsTransform.get()) {
1969        HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1970             "Could not set transform for bitmap.");
1971    } else {
1972        //TODO: perspective that bitmap!
1973    }
1974
1975    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
1976    if (NULL != xpsTransform.get()) {
1977        const SkShader::TileMode xy[2] = {
1978            SkShader::kClamp_TileMode,
1979            SkShader::kClamp_TileMode,
1980        };
1981        SkTScopedComPtr<IXpsOMTileBrush> xpsImageBrush;
1982        HRV(this->createXpsImageBrush(*bitmapPtr,
1983                                      transform,
1984                                      xy,
1985                                      paint.getAlpha(),
1986                                      &xpsImageBrush));
1987        HRVM(shadedPath->SetFillBrushLocal(xpsImageBrush.get()),
1988             "Could not set bitmap brush.");
1989
1990        const SkRect bitmapRect = SkRect::MakeLTRB(0, 0,
1991            SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
1992        HRV(this->createXpsRect(bitmapRect, FALSE, TRUE, &rectFigure));
1993    }
1994    HRVM(shadedFigures->Append(rectFigure.get()),
1995         "Could not add bitmap figure.");
1996
1997    //Get the current visual collection and add the shaded path to it.
1998    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1999    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
2000         "Could not get current visuals for bitmap");
2001    HRVM(currentVisuals->Append(shadedPath.get()),
2002         "Could not add bitmap to current visuals.");
2003
2004    HRV(this->clip(shadedPath.get(), d));
2005}
2006
2007void SkXPSDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
2008                             int x, int y,
2009                             const SkPaint& paint) {
2010    //TODO: override this for XPS
2011    SkDEBUGF(("XPS drawSprite not yet implemented."));
2012}
2013
2014HRESULT SkXPSDevice::CreateTypefaceUse(const SkPaint& paint,
2015                                       TypefaceUse** typefaceUse) {
2016    const SkTypeface* typeface = paint.getTypeface();
2017
2018    //Check cache.
2019    const SkFontID typefaceID = SkTypeface::UniqueID(typeface);
2020    if (!this->fTypefaces.empty()) {
2021        TypefaceUse* current = &this->fTypefaces.front();
2022        const TypefaceUse* last = &this->fTypefaces.back();
2023        for (; current <= last; ++current) {
2024            if (current->typefaceId == typefaceID) {
2025                *typefaceUse = current;
2026                return S_OK;
2027            }
2028        }
2029    }
2030
2031    //TODO: create glyph only fonts
2032    //and let the host deal with what kind of font we're looking at.
2033    XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_RESTRICTED;
2034
2035    SkTScopedComPtr<IStream> fontStream;
2036    SkStream* fontData = SkFontHost::OpenStream(typefaceID);
2037    HRM(SkIStream::CreateFromSkStream(fontData, true, &fontStream),
2038        "Could not create font stream.");
2039
2040    const size_t size =
2041        SK_ARRAY_COUNT(L"/Resources/Fonts/" L_GUID_ID L".odttf");
2042    wchar_t buffer[size];
2043    wchar_t id[GUID_ID_LEN];
2044    HR(create_id(id, GUID_ID_LEN));
2045    swprintf_s(buffer, size, L"/Resources/Fonts/%s.odttf", id);
2046
2047    SkTScopedComPtr<IOpcPartUri> partUri;
2048    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
2049        "Could not create font resource part uri.");
2050
2051    SkTScopedComPtr<IXpsOMFontResource> xpsFontResource;
2052    HRM(this->fXpsFactory->CreateFontResource(fontStream.get(),
2053                                              embedding,
2054                                              partUri.get(),
2055                                              FALSE,
2056                                              &xpsFontResource),
2057        "Could not create font resource.");
2058
2059    TypefaceUse& newTypefaceUse = this->fTypefaces.push_back();
2060    newTypefaceUse.typefaceId = typefaceID;
2061    newTypefaceUse.fontData = fontData;
2062    newTypefaceUse.xpsFont = xpsFontResource.release();
2063
2064    SkAutoGlyphCache agc = SkAutoGlyphCache(paint, NULL, &SkMatrix::I());
2065    SkGlyphCache* glyphCache = agc.getCache();
2066    unsigned int glyphCount = glyphCache->getGlyphCount();
2067    newTypefaceUse.glyphsUsed = new SkBitSet(glyphCount);
2068
2069    *typefaceUse = &newTypefaceUse;
2070    return S_OK;
2071}
2072
2073HRESULT SkXPSDevice::AddGlyphs(const SkDraw& d,
2074                               IXpsOMObjectFactory* xpsFactory,
2075                               IXpsOMCanvas* canvas,
2076                               IXpsOMFontResource* font,
2077                               LPCWSTR text,
2078                               XPS_GLYPH_INDEX* xpsGlyphs,
2079                               UINT32 xpsGlyphsLen,
2080                               XPS_POINT *origin,
2081                               FLOAT fontSize,
2082                               XPS_STYLE_SIMULATION sims,
2083                               const SkMatrix& transform,
2084                               const SkPaint& paint) {
2085    SkTScopedComPtr<IXpsOMGlyphs> glyphs;
2086    HRM(xpsFactory->CreateGlyphs(font, &glyphs), "Could not create glyphs.");
2087
2088    //XPS uses affine transformations for everything...
2089    //...except positioning text.
2090    bool useCanvasForClip;
2091    if ((transform.getType() & ~SkMatrix::kTranslate_Mask) == 0) {
2092        origin->x += SkScalarToFLOAT(transform.getTranslateX());
2093        origin->y += SkScalarToFLOAT(transform.getTranslateY());
2094        useCanvasForClip = false;
2095    } else {
2096        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
2097        HR(this->createXpsTransform(transform, &xpsMatrixToUse));
2098        if (xpsMatrixToUse.get()) {
2099            HRM(glyphs->SetTransformLocal(xpsMatrixToUse.get()),
2100                "Could not set transform matrix.");
2101            useCanvasForClip = true;
2102        } else {
2103            SkASSERT(!"Attempt to add glyphs in perspective.");
2104            useCanvasForClip = false;
2105        }
2106    }
2107
2108    SkTScopedComPtr<IXpsOMGlyphsEditor> glyphsEditor;
2109    HRM(glyphs->GetGlyphsEditor(&glyphsEditor), "Could not get glyph editor.");
2110
2111    if (NULL != text) {
2112        HRM(glyphsEditor->SetUnicodeString(text),
2113            "Could not set unicode string.");
2114    }
2115
2116    if (NULL != xpsGlyphs) {
2117        HRM(glyphsEditor->SetGlyphIndices(xpsGlyphsLen, xpsGlyphs),
2118            "Could not set glyphs.");
2119    }
2120
2121    HRM(glyphsEditor->ApplyEdits(), "Could not apply glyph edits.");
2122
2123    SkTScopedComPtr<IXpsOMBrush> xpsFillBrush;
2124    HR(this->createXpsBrush(
2125            paint,
2126            &xpsFillBrush,
2127            useCanvasForClip ? NULL : &transform));
2128
2129    HRM(glyphs->SetFillBrushLocal(xpsFillBrush.get()),
2130        "Could not set fill brush.");
2131
2132    HRM(glyphs->SetOrigin(origin), "Could not set glyph origin.");
2133
2134    HRM(glyphs->SetFontRenderingEmSize(fontSize),
2135        "Could not set font size.");
2136
2137    HRM(glyphs->SetStyleSimulations(sims),
2138        "Could not set style simulations.");
2139
2140    SkTScopedComPtr<IXpsOMVisualCollection> visuals;
2141    HRM(canvas->GetVisuals(&visuals), "Could not get glyph canvas visuals.");
2142
2143    if (!useCanvasForClip) {
2144        HR(this->clip(glyphs.get(), d));
2145        HRM(visuals->Append(glyphs.get()), "Could not add glyphs to canvas.");
2146    } else {
2147        SkTScopedComPtr<IXpsOMCanvas> glyphCanvas;
2148        HRM(this->fXpsFactory->CreateCanvas(&glyphCanvas),
2149            "Could not create glyph canvas.");
2150
2151        SkTScopedComPtr<IXpsOMVisualCollection> glyphCanvasVisuals;
2152        HRM(glyphCanvas->GetVisuals(&glyphCanvasVisuals),
2153            "Could not get glyph visuals collection.");
2154
2155        HRM(glyphCanvasVisuals->Append(glyphs.get()),
2156            "Could not add glyphs to page.");
2157        HR(this->clip(glyphCanvas.get(), d));
2158
2159        HRM(visuals->Append(glyphCanvas.get()),
2160            "Could not add glyph canvas to page.");
2161    }
2162
2163    return S_OK;
2164}
2165
2166struct SkXPSDrawProcs : public SkDrawProcs {
2167public:
2168    /** [in] Advance width and offsets for glyphs measured in
2169    hundredths of the font em size (XPS Spec 5.1.3). */
2170    FLOAT centemPerUnit;
2171    /** [in,out] The accumulated glyphs used in the current typeface. */
2172    SkBitSet* glyphUse;
2173    /** [out] The glyphs to draw. */
2174    SkTDArray<XPS_GLYPH_INDEX> xpsGlyphs;
2175};
2176
2177static void xps_draw_1_glyph(const SkDraw1Glyph& state,
2178                             SkFixed x, SkFixed y,
2179                             const SkGlyph& skGlyph) {
2180    SkASSERT(skGlyph.fWidth > 0 && skGlyph.fHeight > 0);
2181
2182    SkXPSDrawProcs* procs = static_cast<SkXPSDrawProcs*>(state.fDraw->fProcs);
2183
2184    //Draw pre-adds half the sampling frequency for floor rounding.
2185    x -= state.fHalfSampleX;
2186    y -= state.fHalfSampleY;
2187
2188    XPS_GLYPH_INDEX* xpsGlyph = procs->xpsGlyphs.append();
2189    uint16_t glyphID = skGlyph.getGlyphID();
2190    procs->glyphUse->setBit(glyphID, true);
2191    xpsGlyph->index = glyphID;
2192    if (1 == procs->xpsGlyphs.count()) {
2193        xpsGlyph->advanceWidth = 0.0f;
2194        xpsGlyph->horizontalOffset = SkFixedToFloat(x) * procs->centemPerUnit;
2195        xpsGlyph->verticalOffset = SkFixedToFloat(y) * -procs->centemPerUnit;
2196    } else {
2197        const XPS_GLYPH_INDEX& first = procs->xpsGlyphs[0];
2198        xpsGlyph->advanceWidth = 0.0f;
2199        xpsGlyph->horizontalOffset = (SkFixedToFloat(x) * procs->centemPerUnit)
2200                                     - first.horizontalOffset;
2201        xpsGlyph->verticalOffset = (SkFixedToFloat(y) * -procs->centemPerUnit)
2202                                   - first.verticalOffset;
2203    }
2204}
2205
2206static void text_draw_init(const SkPaint& paint,
2207                           const void* text, size_t byteLength,
2208                           SkBitSet& glyphsUsed,
2209                           SkDraw& myDraw, SkXPSDrawProcs& procs) {
2210    procs.fD1GProc = xps_draw_1_glyph;
2211    int numGlyphGuess;
2212    switch (paint.getTextEncoding()) {
2213        case SkPaint::kUTF8_TextEncoding:
2214            numGlyphGuess = SkUTF8_CountUnichars(
2215                static_cast<const char *>(text),
2216                byteLength);
2217            break;
2218        case SkPaint::kUTF16_TextEncoding:
2219            numGlyphGuess = SkUTF16_CountUnichars(
2220                static_cast<const uint16_t *>(text),
2221                byteLength);
2222            break;
2223        case SkPaint::kGlyphID_TextEncoding:
2224            numGlyphGuess = byteLength / 2;
2225            break;
2226        default:
2227            SK_DEBUGBREAK(true);
2228    }
2229    procs.xpsGlyphs.setReserve(numGlyphGuess);
2230    procs.glyphUse = &glyphsUsed;
2231    procs.centemPerUnit = 100.0f / SkScalarToFLOAT(paint.getTextSize());
2232
2233    myDraw.fProcs = &procs;
2234}
2235
2236static bool text_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
2237    const SkPaint::Style style = paint.getStyle();
2238    return matrix.hasPerspective()
2239        || SkPaint::kStroke_Style == style
2240        || SkPaint::kStrokeAndFill_Style == style
2241        || paint.getMaskFilter()
2242        || paint.getRasterizer()
2243    ;
2244}
2245
2246void SkXPSDevice::drawText(const SkDraw& d,
2247                           const void* text, size_t byteLen,
2248                           SkScalar x, SkScalar y,
2249                           const SkPaint& paint) {
2250    if (byteLen < 1) return;
2251
2252    if (text_must_be_pathed(paint, *d.fMatrix)) {
2253        SkPath path;
2254        paint.getTextPath(text, byteLen, x, y, &path);
2255        this->drawPath(d, path, paint, NULL, true);
2256        //TODO: add automation "text"
2257        return;
2258    }
2259
2260    TypefaceUse* typeface;
2261    HRV(CreateTypefaceUse(paint, &typeface));
2262
2263    SkDraw myDraw(d);
2264    SkXPSDrawProcs procs;
2265    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
2266
2267    myDraw.drawText(static_cast<const char*>(text), byteLen, x, y, paint);
2268
2269    // SkDraw may have clipped out the glyphs, so we need to check
2270    if (procs.xpsGlyphs.count() == 0) {
2271        return;
2272    }
2273
2274    XPS_POINT origin = {
2275        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
2276        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
2277    };
2278    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
2279    procs.xpsGlyphs[0].verticalOffset = 0.0f;
2280
2281    HRV(AddGlyphs(d,
2282                  this->fXpsFactory.get(),
2283                  this->fCurrentXpsCanvas.get(),
2284                  typeface->xpsFont,
2285                  NULL,
2286                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
2287                  &origin,
2288                  SkScalarToFLOAT(paint.getTextSize()),
2289                  XPS_STYLE_SIMULATION_NONE,
2290                  *d.fMatrix,
2291                  paint));
2292}
2293
2294void SkXPSDevice::drawPosText(const SkDraw& d,
2295                              const void* text, size_t byteLen,
2296                              const SkScalar pos[],
2297                              SkScalar constY, int scalarsPerPos,
2298                              const SkPaint& paint) {
2299    if (byteLen < 1) return;
2300
2301    if (text_must_be_pathed(paint, *d.fMatrix)) {
2302        SkPath path;
2303        //TODO: make this work, Draw currently does not handle as well.
2304        //paint.getTextPath(text, byteLength, x, y, &path);
2305        //this->drawPath(d, path, paint, NULL, true);
2306        //TODO: add automation "text"
2307        return;
2308    }
2309
2310    TypefaceUse* typeface;
2311    HRV(CreateTypefaceUse(paint, &typeface));
2312
2313    SkDraw myDraw(d);
2314    SkXPSDrawProcs procs;
2315    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
2316
2317    myDraw.drawPosText(static_cast<const char*>(text), byteLen,
2318                       pos, constY, scalarsPerPos,
2319                       paint);
2320
2321    // SkDraw may have clipped out the glyphs, so we need to check
2322    if (procs.xpsGlyphs.count() == 0) {
2323        return;
2324    }
2325
2326    XPS_POINT origin = {
2327        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
2328        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
2329    };
2330    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
2331    procs.xpsGlyphs[0].verticalOffset = 0.0f;
2332
2333    HRV(AddGlyphs(d,
2334                  this->fXpsFactory.get(),
2335                  this->fCurrentXpsCanvas.get(),
2336                  typeface->xpsFont,
2337                  NULL,
2338                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
2339                  &origin,
2340                  SkScalarToFLOAT(paint.getTextSize()),
2341                  XPS_STYLE_SIMULATION_NONE,
2342                  *d.fMatrix,
2343                  paint));
2344}
2345
2346void SkXPSDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
2347                                 const SkPath& path, const SkMatrix* matrix,
2348                                 const SkPaint& paint) {
2349    //This will call back into the device to do the drawing.
2350     d.drawTextOnPath((const char*)text, len, path, matrix, paint);
2351}
2352
2353void SkXPSDevice::drawDevice(const SkDraw& d, SkDevice* dev,
2354                             int x, int y,
2355                             const SkPaint&) {
2356    SkXPSDevice* that = static_cast<SkXPSDevice*>(dev);
2357
2358    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
2359    XPS_MATRIX rawTransform = {
2360        1.0f,
2361        0.0f,
2362        0.0f,
2363        1.0f,
2364        static_cast<FLOAT>(x),
2365        static_cast<FLOAT>(y),
2366    };
2367    HRVM(this->fXpsFactory->CreateMatrixTransform(&rawTransform, &xpsTransform),
2368         "Could not create layer transform.");
2369    HRVM(that->fCurrentXpsCanvas->SetTransformLocal(xpsTransform.get()),
2370         "Could not set layer transform.");
2371
2372    //Get the current visual collection and add the layer to it.
2373    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
2374    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
2375         "Could not get current visuals for layer.");
2376    HRVM(currentVisuals->Append(that->fCurrentXpsCanvas.get()),
2377         "Could not add layer to current visuals.");
2378}
2379
2380bool SkXPSDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
2381                               SkCanvas::Config8888) {
2382    return false;
2383}
2384
2385SkDevice* SkXPSDevice::onCreateCompatibleDevice(SkBitmap::Config config,
2386                                                int width, int height,
2387                                                bool isOpaque,
2388                                                Usage usage) {
2389    if (SkDevice::kGeneral_Usage == usage) {
2390        return NULL;
2391        SK_CRASH();
2392        //To what stream do we write?
2393        //SkXPSDevice* dev = new SkXPSDevice(this);
2394        //SkSize s = SkSize::Make(width, height);
2395        //dev->BeginCanvas(s, s, SkMatrix::I());
2396        //return dev;
2397    }
2398
2399    return new SkXPSDevice(this->fXpsFactory.get());
2400}
2401
2402SkXPSDevice::SkXPSDevice(IXpsOMObjectFactory* xpsFactory)
2403    : SkDevice(make_fake_bitmap(10000, 10000))
2404    , fCurrentPage(0) {
2405
2406    HRVM(CoCreateInstance(
2407             CLSID_XpsOMObjectFactory,
2408             NULL,
2409             CLSCTX_INPROC_SERVER,
2410             IID_PPV_ARGS(&this->fXpsFactory)),
2411         "Could not create factory for layer.");
2412
2413    HRVM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas),
2414         "Could not create canvas for layer.");
2415}
2416
2417bool SkXPSDevice::allowImageFilter(SkImageFilter*) {
2418    return false;
2419}
2420