SkXPSDevice.cpp revision 089130c24e29dd0ffc53f0396acdc8f0b50dabd4
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        SkShader::GradientType gradientType = 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;
979        shader->getLocalMatrix(&localMatrix);
980        if (NULL != parentTransform) {
981            localMatrix.preConcat(*parentTransform);
982        }
983        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
984        HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
985
986        if (SkShader::kLinear_GradientType == gradientType) {
987            HR(this->createXpsLinearGradient(info,
988                                             skPaint.getAlpha(),
989                                             localMatrix,
990                                             xpsMatrixToUse.get(),
991                                             brush));
992            return S_OK;
993        }
994
995        if (SkShader::kRadial_GradientType == gradientType) {
996            HR(this->createXpsRadialGradient(info,
997                                             skPaint.getAlpha(),
998                                             localMatrix,
999                                             xpsMatrixToUse.get(),
1000                                             brush));
1001            return S_OK;
1002        }
1003
1004        if (SkShader::kRadial2_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    SkScalar twoPointRadialParams[3];
1017    SkShader::BitmapType bitmapType = shader->asABitmap(&outTexture,
1018                                                        &outMatrix,
1019                                                        xy,
1020                                                        twoPointRadialParams);
1021    switch (bitmapType) {
1022        case SkShader::kNone_BitmapType:
1023            break;
1024        case SkShader::kDefault_BitmapType: {
1025            //TODO: outMatrix??
1026            SkMatrix localMatrix;
1027            shader->getLocalMatrix(&localMatrix);
1028            if (NULL != parentTransform) {
1029                localMatrix.preConcat(*parentTransform);
1030            }
1031
1032            SkTScopedComPtr<IXpsOMTileBrush> tileBrush;
1033            HR(this->createXpsImageBrush(outTexture,
1034                                         localMatrix,
1035                                         xy,
1036                                         skPaint.getAlpha(),
1037                                         &tileBrush));
1038
1039            HRM(tileBrush->QueryInterface<IXpsOMBrush>(brush), "QI failed.");
1040
1041            return S_OK;
1042        }
1043        case SkShader::kRadial_BitmapType:
1044        case SkShader::kSweep_BitmapType:
1045        case SkShader::kTwoPointRadial_BitmapType:
1046        default:
1047            break;
1048    }
1049
1050    HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
1051    return S_OK;
1052}
1053
1054static bool rect_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
1055    const bool zeroWidth = (0 == paint.getStrokeWidth());
1056    const bool stroke = (SkPaint::kFill_Style != paint.getStyle());
1057
1058    return paint.getPathEffect() ||
1059           paint.getMaskFilter() ||
1060           paint.getRasterizer() ||
1061           (stroke && (
1062               (matrix.hasPerspective() && !zeroWidth) ||
1063               SkPaint::kMiter_Join != paint.getStrokeJoin() ||
1064               (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
1065                paint.getStrokeMiter() < SK_ScalarSqrt2)
1066           ))
1067    ;
1068}
1069
1070HRESULT SkXPSDevice::createXpsRect(const SkRect& rect, BOOL stroke, BOOL fill,
1071                                   IXpsOMGeometryFigure** xpsRect) {
1072    const SkPoint points[4] = {
1073        { rect.fLeft, rect.fTop },
1074        { rect.fRight, rect.fTop },
1075        { rect.fRight, rect.fBottom },
1076        { rect.fLeft, rect.fBottom },
1077    };
1078    return this->createXpsQuad(points, stroke, fill, xpsRect);
1079}
1080HRESULT SkXPSDevice::createXpsQuad(const SkPoint (&points)[4],
1081                                   BOOL stroke, BOOL fill,
1082                                   IXpsOMGeometryFigure** xpsQuad) {
1083    // Define the start point.
1084    XPS_POINT startPoint = xps_point(points[0]);
1085
1086    // Create the figure.
1087    HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint, xpsQuad),
1088        "Could not create quad geometry figure.");
1089
1090    // Define the type of each segment.
1091    XPS_SEGMENT_TYPE segmentTypes[3] = {
1092        XPS_SEGMENT_TYPE_LINE,
1093        XPS_SEGMENT_TYPE_LINE,
1094        XPS_SEGMENT_TYPE_LINE,
1095    };
1096
1097    // Define the x and y coordinates of each corner of the figure.
1098    FLOAT segmentData[6] = {
1099        SkScalarToFLOAT(points[1].fX), SkScalarToFLOAT(points[1].fY),
1100        SkScalarToFLOAT(points[2].fX), SkScalarToFLOAT(points[2].fY),
1101        SkScalarToFLOAT(points[3].fX), SkScalarToFLOAT(points[3].fY),
1102    };
1103
1104    // Describe if the segments are stroked.
1105    BOOL segmentStrokes[3] = {
1106        stroke, stroke, stroke,
1107    };
1108
1109    // Add the segment data to the figure.
1110    HRM((*xpsQuad)->SetSegments(
1111            3, 6,
1112            segmentTypes , segmentData, segmentStrokes),
1113        "Could not add segment data to quad.");
1114
1115    // Set the closed and filled properties of the figure.
1116    HRM((*xpsQuad)->SetIsClosed(stroke), "Could not set quad close.");
1117    HRM((*xpsQuad)->SetIsFilled(fill), "Could not set quad fill.");
1118
1119    return S_OK;
1120}
1121
1122uint32_t SkXPSDevice::getDeviceCapabilities() {
1123    return kVector_Capability;
1124}
1125
1126void SkXPSDevice::clear(SkColor color) {
1127    //TODO: override this for XPS
1128    SkDEBUGF(("XPS clear not yet implemented."));
1129}
1130
1131void SkXPSDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
1132                             size_t count, const SkPoint points[],
1133                             const SkPaint& paint) {
1134    //This will call back into the device to do the drawing.
1135    d.drawPoints(mode, count, points, paint, true);
1136}
1137
1138void SkXPSDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
1139                               int vertexCount, const SkPoint verts[],
1140                               const SkPoint texs[], const SkColor colors[],
1141                               SkXfermode* xmode, const uint16_t indices[],
1142                               int indexCount, const SkPaint& paint) {
1143    //TODO: override this for XPS
1144    SkDEBUGF(("XPS drawVertices not yet implemented."));
1145}
1146
1147void SkXPSDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
1148    const SkRect r = SkRect::MakeSize(this->fCurrentCanvasSize);
1149
1150    //If trying to paint with a stroke, ignore that and fill.
1151    SkPaint* fillPaint = const_cast<SkPaint*>(&paint);
1152    SkTLazy<SkPaint> modifiedPaint;
1153    if (paint.getStyle() != SkPaint::kFill_Style) {
1154        fillPaint = modifiedPaint.set(paint);
1155        fillPaint->setStyle(SkPaint::kFill_Style);
1156    }
1157
1158    this->internalDrawRect(d, r, false, *fillPaint);
1159}
1160
1161void SkXPSDevice::drawRect(const SkDraw& d,
1162                           const SkRect& r,
1163                           const SkPaint& paint) {
1164    this->internalDrawRect(d, r, true, paint);
1165}
1166
1167void SkXPSDevice::internalDrawRect(const SkDraw& d,
1168                                   const SkRect& r,
1169                                   bool transformRect,
1170                                   const SkPaint& paint) {
1171    //Exit early if there is nothing to draw.
1172    if (d.fClip->isEmpty() ||
1173        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1174        return;
1175    }
1176
1177    //Path the rect if we can't optimize it.
1178    if (rect_must_be_pathed(paint, *d.fMatrix)) {
1179        SkPath tmp;
1180        tmp.addRect(r);
1181        tmp.setFillType(SkPath::kWinding_FillType);
1182        this->drawPath(d, tmp, paint, NULL, true);
1183        return;
1184    }
1185
1186    //Create the shaded path.
1187    SkTScopedComPtr<IXpsOMPath> shadedPath;
1188    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1189         "Could not create shaded path for rect.");
1190
1191    //Create the shaded geometry.
1192    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1193    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1194         "Could not create shaded geometry for rect.");
1195
1196    //Add the geometry to the shaded path.
1197    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1198         "Could not set shaded geometry for rect.");
1199
1200    //Set the brushes.
1201    BOOL fill = FALSE;
1202    BOOL stroke = FALSE;
1203    HRV(this->shadePath(shadedPath.get(), paint, *d.fMatrix, &fill, &stroke));
1204
1205    bool xpsTransformsPath = true;
1206    //Transform the geometry.
1207    if (transformRect && xpsTransformsPath) {
1208        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1209        HRV(this->createXpsTransform(*d.fMatrix, &xpsTransform));
1210        if (xpsTransform.get()) {
1211            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1212                 "Could not set transform for rect.");
1213        } else {
1214            xpsTransformsPath = false;
1215        }
1216    }
1217
1218    //Create the figure.
1219    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
1220    {
1221        SkPoint points[4] = {
1222            { r.fLeft, r.fTop },
1223            { r.fLeft, r.fBottom },
1224            { r.fRight, r.fBottom },
1225            { r.fRight, r.fTop },
1226        };
1227        if (!xpsTransformsPath && transformRect) {
1228            d.fMatrix->mapPoints(points, SK_ARRAY_COUNT(points));
1229        }
1230        HRV(this->createXpsQuad(points, stroke, fill, &rectFigure));
1231    }
1232
1233    //Get the figures of the shaded geometry.
1234    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1235    HRVM(shadedGeometry->GetFigures(&shadedFigures),
1236         "Could not get shaded figures for rect.");
1237
1238    //Add the figure to the shaded geometry figures.
1239    HRVM(shadedFigures->Append(rectFigure.get()),
1240         "Could not add shaded figure for rect.");
1241
1242    HRV(this->clip(shadedPath.get(), d));
1243
1244    //Add the shaded path to the current visuals.
1245    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1246    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1247         "Could not get current visuals for rect.");
1248    HRVM(currentVisuals->Append(shadedPath.get()),
1249         "Could not add rect to current visuals.");
1250}
1251
1252static HRESULT close_figure(const SkTDArray<XPS_SEGMENT_TYPE>& segmentTypes,
1253                            const SkTDArray<BOOL>& segmentStrokes,
1254                            const SkTDArray<FLOAT>& segmentData,
1255                            BOOL stroke, BOOL fill,
1256                            IXpsOMGeometryFigure* figure,
1257                            IXpsOMGeometryFigureCollection* figures) {
1258    // Add the segment data to the figure.
1259    HRM(figure->SetSegments(segmentTypes.count(), segmentData.count(),
1260                            segmentTypes.begin() , segmentData.begin(),
1261                            segmentStrokes.begin()),
1262        "Could not set path segments.");
1263
1264    // Set the closed and filled properties of the figure.
1265    HRM(figure->SetIsClosed(stroke), "Could not set path closed.");
1266    HRM(figure->SetIsFilled(fill), "Could not set path fill.");
1267
1268    // Add the figure created above to this geometry.
1269    HRM(figures->Append(figure), "Could not add path to geometry.");
1270    return S_OK;
1271}
1272
1273HRESULT SkXPSDevice::addXpsPathGeometry(
1274        IXpsOMGeometryFigureCollection* xpsFigures,
1275        BOOL stroke, BOOL fill, const SkPath& path) {
1276    SkTDArray<XPS_SEGMENT_TYPE> segmentTypes;
1277    SkTDArray<BOOL> segmentStrokes;
1278    SkTDArray<FLOAT> segmentData;
1279
1280    SkTScopedComPtr<IXpsOMGeometryFigure> xpsFigure;
1281    SkPath::Iter iter(path, true);
1282    SkPoint points[4];
1283    SkPath::Verb verb;
1284    while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
1285        switch (verb) {
1286            case SkPath::kMove_Verb: {
1287                if (NULL != xpsFigure.get()) {
1288                    HR(close_figure(segmentTypes, segmentStrokes, segmentData,
1289                                    stroke, fill,
1290                                    xpsFigure.get() , xpsFigures));
1291                    xpsFigure.reset();
1292                    segmentTypes.rewind();
1293                    segmentStrokes.rewind();
1294                    segmentData.rewind();
1295                }
1296                // Define the start point.
1297                XPS_POINT startPoint = xps_point(points[0]);
1298                // Create the figure.
1299                HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint,
1300                                                            &xpsFigure),
1301                    "Could not create path geometry figure.");
1302                break;
1303            }
1304            case SkPath::kLine_Verb:
1305                if (iter.isCloseLine()) break; //ignore the line, auto-closed
1306                segmentTypes.push(XPS_SEGMENT_TYPE_LINE);
1307                segmentStrokes.push(stroke);
1308                segmentData.push(SkScalarToFLOAT(points[1].fX));
1309                segmentData.push(SkScalarToFLOAT(points[1].fY));
1310                break;
1311            case SkPath::kQuad_Verb:
1312                segmentTypes.push(XPS_SEGMENT_TYPE_QUADRATIC_BEZIER);
1313                segmentStrokes.push(stroke);
1314                segmentData.push(SkScalarToFLOAT(points[1].fX));
1315                segmentData.push(SkScalarToFLOAT(points[1].fY));
1316                segmentData.push(SkScalarToFLOAT(points[2].fX));
1317                segmentData.push(SkScalarToFLOAT(points[2].fY));
1318                break;
1319            case SkPath::kCubic_Verb:
1320                segmentTypes.push(XPS_SEGMENT_TYPE_BEZIER);
1321                segmentStrokes.push(stroke);
1322                segmentData.push(SkScalarToFLOAT(points[1].fX));
1323                segmentData.push(SkScalarToFLOAT(points[1].fY));
1324                segmentData.push(SkScalarToFLOAT(points[2].fX));
1325                segmentData.push(SkScalarToFLOAT(points[2].fY));
1326                segmentData.push(SkScalarToFLOAT(points[3].fX));
1327                segmentData.push(SkScalarToFLOAT(points[3].fY));
1328                break;
1329            case SkPath::kClose_Verb:
1330                // we ignore these, and just get the whole segment from
1331                // the corresponding line/quad/cubic verbs
1332                break;
1333            default:
1334                SkASSERT(!"unexpected verb");
1335                break;
1336        }
1337    }
1338    if (NULL != xpsFigure.get()) {
1339        HR(close_figure(segmentTypes, segmentStrokes, segmentData,
1340                        stroke, fill,
1341                        xpsFigure.get(), xpsFigures));
1342    }
1343    return S_OK;
1344}
1345
1346HRESULT SkXPSDevice::drawInverseWindingPath(const SkDraw& d,
1347                                            const SkPath& devicePath,
1348                                            IXpsOMPath* shadedPath) {
1349    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
1350        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
1351
1352    const XPS_RECT universeRectXps = {
1353        0.0f, 0.0f,
1354        SkScalarToFLOAT(this->fCurrentCanvasSize.fWidth),
1355        SkScalarToFLOAT(this->fCurrentCanvasSize.fHeight),
1356    };
1357
1358    //Get the geometry.
1359    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1360    HRM(shadedPath->GetGeometry(&shadedGeometry),
1361        "Could not get shaded geometry for inverse path.");
1362
1363    //Get the figures from the geometry.
1364    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1365    HRM(shadedGeometry->GetFigures(&shadedFigures),
1366        "Could not get shaded figures for inverse path.");
1367
1368    HRM(shadedGeometry->SetFillRule(XPS_FILL_RULE_NONZERO),
1369        "Could not set shaded fill rule for inverse path.");
1370
1371    //Take everything drawn so far, and make a shared resource out of it.
1372    //Replace everything drawn so far with
1373    //inverse canvas
1374    //  old canvas of everything so far
1375    //  world shaded figure, clipped to current clip
1376    //  top canvas of everything so far, clipped to path
1377    //Note: this is not quite right when there is nothing solid in the
1378    //canvas of everything so far, as the bit on top will allow
1379    //the world paint to show through.
1380
1381    //Create new canvas.
1382    SkTScopedComPtr<IXpsOMCanvas> newCanvas;
1383    HRM(this->fXpsFactory->CreateCanvas(&newCanvas),
1384        "Could not create inverse canvas.");
1385
1386    //Save the old canvas to a dictionary on the new canvas.
1387    SkTScopedComPtr<IXpsOMDictionary> newDictionary;
1388    HRM(this->fXpsFactory->CreateDictionary(&newDictionary),
1389        "Could not create inverse dictionary.");
1390    HRM(newCanvas->SetDictionaryLocal(newDictionary.get()),
1391        "Could not set inverse dictionary.");
1392
1393    const size_t size = SK_ARRAY_COUNT(L"ID" L_GUID_ID);
1394    wchar_t buffer[size];
1395    wchar_t id[GUID_ID_LEN];
1396    HR(create_id(id, GUID_ID_LEN, '_'));
1397    swprintf_s(buffer, size, L"ID%s", id);
1398    HRM(newDictionary->Append(buffer, this->fCurrentXpsCanvas.get()),
1399        "Could not add canvas to inverse dictionary.");
1400
1401    //Start drawing
1402    SkTScopedComPtr<IXpsOMVisualCollection> newVisuals;
1403    HRM(newCanvas->GetVisuals(&newVisuals),
1404        "Could not get inverse canvas visuals.");
1405
1406    //Draw old canvas from dictionary onto new canvas.
1407    SkTScopedComPtr<IXpsOMGeometry> oldGeometry;
1408    HRM(this->fXpsFactory->CreateGeometry(&oldGeometry),
1409        "Could not create old inverse geometry.");
1410
1411    SkTScopedComPtr<IXpsOMGeometryFigureCollection> oldFigures;
1412    HRM(oldGeometry->GetFigures(&oldFigures),
1413        "Could not get old inverse figures.");
1414
1415    SkTScopedComPtr<IXpsOMGeometryFigure> oldFigure;
1416    HR(this->createXpsRect(universeRect, FALSE, TRUE, &oldFigure));
1417    HRM(oldFigures->Append(oldFigure.get()),
1418        "Could not add old inverse figure.");
1419
1420    SkTScopedComPtr<IXpsOMVisualBrush> oldBrush;
1421    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
1422                                             &universeRectXps,
1423                                             &oldBrush),
1424        "Could not create old inverse brush.");
1425
1426    SkTScopedComPtr<IXpsOMPath> oldPath;
1427    HRM(this->fXpsFactory->CreatePath(&oldPath),
1428        "Could not create old inverse path.");
1429    HRM(oldPath->SetGeometryLocal(oldGeometry.get()),
1430        "Could not set old inverse geometry.");
1431    HRM(oldPath->SetFillBrushLocal(oldBrush.get()),
1432        "Could not set old inverse fill brush.");
1433    //the brush must be parented before setting the lookup.
1434    HRM(newVisuals->Append(oldPath.get()),
1435        "Could not add old inverse path to new canvas visuals.");
1436    HRM(oldBrush->SetVisualLookup(buffer),
1437        "Could not set old inverse brush visual lookup.");
1438
1439    //Draw the clip filling shader.
1440    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
1441    HR(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure));
1442    HRM(shadedFigures->Append(shadedFigure.get()),
1443        "Could not add inverse shaded figure.");
1444    //the geometry is already set
1445    HR(this->clip(shadedPath, d));
1446    HRM(newVisuals->Append(shadedPath),
1447        "Could not add inverse shaded path to canvas visuals.");
1448
1449    //Draw the old canvas on top, clipped to the original path.
1450    SkTScopedComPtr<IXpsOMCanvas> topCanvas;
1451    HRM(this->fXpsFactory->CreateCanvas(&topCanvas),
1452        "Could not create top inverse canvas.");
1453    //Clip the canvas to prevent alpha spill.
1454    //This is the entire reason this canvas exists.
1455    HR(this->clip(topCanvas.get(), d));
1456
1457    SkTScopedComPtr<IXpsOMGeometry> topGeometry;
1458    HRM(this->fXpsFactory->CreateGeometry(&topGeometry),
1459        "Could not create top inverse geometry.");
1460
1461    SkTScopedComPtr<IXpsOMGeometryFigureCollection> topFigures;
1462    HRM(topGeometry->GetFigures(&topFigures),
1463        "Could not get top inverse figures.");
1464
1465    SkTScopedComPtr<IXpsOMGeometryFigure> topFigure;
1466    HR(this->createXpsRect(universeRect, FALSE, TRUE, &topFigure));
1467    HRM(topFigures->Append(topFigure.get()),
1468        "Could not add old inverse figure.");
1469
1470    SkTScopedComPtr<IXpsOMVisualBrush> topBrush;
1471    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
1472                                             &universeRectXps,
1473                                             &topBrush),
1474        "Could not create top inverse brush.");
1475
1476    SkTScopedComPtr<IXpsOMPath> topPath;
1477    HRM(this->fXpsFactory->CreatePath(&topPath),
1478        "Could not create top inverse path.");
1479    HRM(topPath->SetGeometryLocal(topGeometry.get()),
1480        "Could not set top inverse geometry.");
1481    HRM(topPath->SetFillBrushLocal(topBrush.get()),
1482        "Could not set top inverse fill brush.");
1483    //the brush must be parented before setting the lookup.
1484    HRM(newVisuals->Append(topCanvas.get()),
1485        "Could not add top canvas to inverse canvas visuals.");
1486    SkTScopedComPtr<IXpsOMVisualCollection> topVisuals;
1487    HRM(topCanvas->GetVisuals(&topVisuals),
1488        "Could not get top inverse canvas visuals.");
1489    HRM(topVisuals->Append(topPath.get()),
1490        "Could not add top inverse path to top canvas visuals.");
1491    HRM(topBrush->SetVisualLookup(buffer),
1492        "Could not set top inverse brush visual lookup.");
1493
1494    HR(this->clipToPath(topPath.get(), devicePath, XPS_FILL_RULE_NONZERO));
1495
1496    //swap current canvas to new canvas
1497    this->fCurrentXpsCanvas.swap(newCanvas);
1498
1499    return S_OK;
1500}
1501
1502void SkXPSDevice::convertToPpm(const SkMaskFilter* filter,
1503                               SkMatrix* matrix,
1504                               SkVector* ppuScale,
1505                               const SkIRect& clip, SkIRect* clipIRect) {
1506    //TODO: currently ignoring the ppm if blur ignoring transform.
1507    if (filter) {
1508        SkMaskFilter::BlurInfo blurInfo;
1509        SkMaskFilter::BlurType blurType = filter->asABlur(&blurInfo);
1510
1511        if (SkMaskFilter::kNone_BlurType != blurType
1512            && blurInfo.fIgnoreTransform) {
1513
1514            ppuScale->fX = SK_Scalar1;
1515            ppuScale->fY = SK_Scalar1;
1516            *clipIRect = clip;
1517            return;
1518        }
1519    }
1520
1521    //This action is in unit space, but the ppm is specified in physical space.
1522    ppuScale->fX = SkScalarDiv(this->fCurrentPixelsPerMeter.fX,
1523                               this->fCurrentUnitsPerMeter.fX);
1524    ppuScale->fY = SkScalarDiv(this->fCurrentPixelsPerMeter.fY,
1525                               this->fCurrentUnitsPerMeter.fY);
1526
1527    matrix->postScale(ppuScale->fX, ppuScale->fY);
1528
1529    const SkIRect& irect = clip;
1530    SkRect clipRect = SkRect::MakeLTRB(
1531        SkScalarMul(SkIntToScalar(irect.fLeft), ppuScale->fX),
1532        SkScalarMul(SkIntToScalar(irect.fTop), ppuScale->fY),
1533        SkScalarMul(SkIntToScalar(irect.fRight), ppuScale->fX),
1534        SkScalarMul(SkIntToScalar(irect.fBottom), ppuScale->fY));
1535    clipRect.roundOut(clipIRect);
1536}
1537
1538HRESULT SkXPSDevice::applyMask(const SkDraw& d,
1539                               const SkMask& mask,
1540                               const SkVector& ppuScale,
1541                               IXpsOMPath* shadedPath) {
1542    //Get the geometry object.
1543    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1544    HRM(shadedPath->GetGeometry(&shadedGeometry),
1545        "Could not get mask shaded geometry.");
1546
1547    //Get the figures from the geometry.
1548    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1549    HRM(shadedGeometry->GetFigures(&shadedFigures),
1550        "Could not get mask shaded figures.");
1551
1552    SkMatrix m;
1553    m.reset();
1554    m.setTranslate(SkIntToScalar(mask.fBounds.fLeft),
1555                   SkIntToScalar(mask.fBounds.fTop));
1556    m.postScale(SkScalarInvert(ppuScale.fX), SkScalarInvert(ppuScale.fY));
1557
1558    SkShader::TileMode xy[2];
1559    xy[0] = (SkShader::TileMode)3;
1560    xy[1] = (SkShader::TileMode)3;
1561
1562    SkBitmap bm;
1563    bm.setConfig(SkBitmap::kA8_Config,
1564                 mask.fBounds.width(),
1565                 mask.fBounds.height(),
1566                 mask.fRowBytes);
1567    bm.setPixels(mask.fImage);
1568
1569    SkTScopedComPtr<IXpsOMTileBrush> maskBrush;
1570    HR(this->createXpsImageBrush(bm, m, xy, 0xFF, &maskBrush));
1571    HRM(shadedPath->SetOpacityMaskBrushLocal(maskBrush.get()),
1572        "Could not set mask.");
1573
1574    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
1575        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
1576    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
1577    HRM(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure),
1578        "Could not create mask shaded figure.");
1579    HRM(shadedFigures->Append(shadedFigure.get()),
1580        "Could not add mask shaded figure.");
1581
1582    HR(this->clip(shadedPath, d));
1583
1584    //Add the path to the active visual collection.
1585    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1586    HRM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1587        "Could not get mask current visuals.");
1588    HRM(currentVisuals->Append(shadedPath),
1589        "Could not add masked shaded path to current visuals.");
1590
1591    return S_OK;
1592}
1593
1594HRESULT SkXPSDevice::shadePath(IXpsOMPath* shadedPath,
1595                               const SkPaint& shaderPaint,
1596                               const SkMatrix& matrix,
1597                               BOOL* fill, BOOL* stroke) {
1598    *fill = FALSE;
1599    *stroke = FALSE;
1600
1601    const SkPaint::Style style = shaderPaint.getStyle();
1602    const bool hasFill = SkPaint::kFill_Style == style
1603                      || SkPaint::kStrokeAndFill_Style == style;
1604    const bool hasStroke = SkPaint::kStroke_Style == style
1605                        || SkPaint::kStrokeAndFill_Style == style;
1606
1607    //TODO(bungeman): use dictionaries and lookups.
1608    if (hasFill) {
1609        *fill = TRUE;
1610        SkTScopedComPtr<IXpsOMBrush> fillBrush;
1611        HR(this->createXpsBrush(shaderPaint, &fillBrush, &matrix));
1612        HRM(shadedPath->SetFillBrushLocal(fillBrush.get()),
1613            "Could not set fill for shaded path.");
1614    }
1615
1616    if (hasStroke) {
1617        *stroke = TRUE;
1618        SkTScopedComPtr<IXpsOMBrush> strokeBrush;
1619        HR(this->createXpsBrush(shaderPaint, &strokeBrush, &matrix));
1620        HRM(shadedPath->SetStrokeBrushLocal(strokeBrush.get()),
1621            "Could not set stroke brush for shaded path.");
1622        HRM(shadedPath->SetStrokeThickness(
1623                SkScalarToFLOAT(shaderPaint.getStrokeWidth())),
1624            "Could not set shaded path stroke thickness.");
1625
1626        if (0 == shaderPaint.getStrokeWidth()) {
1627            //XPS hair width is a hack. (XPS Spec 11.6.12).
1628            SkTScopedComPtr<IXpsOMDashCollection> dashes;
1629            HRM(shadedPath->GetStrokeDashes(&dashes),
1630                "Could not set dashes for shaded path.");
1631            XPS_DASH dash;
1632            dash.length = 1.0;
1633            dash.gap = 0.0;
1634            HRM(dashes->Append(&dash), "Could not add dashes to shaded path.");
1635            HRM(shadedPath->SetStrokeDashOffset(-2.0),
1636                "Could not set dash offset for shaded path.");
1637        }
1638    }
1639    return S_OK;
1640}
1641
1642void SkXPSDevice::drawPath(const SkDraw& d,
1643                           const SkPath& platonicPath,
1644                           const SkPaint& paint,
1645                           const SkMatrix* prePathMatrix,
1646                           bool pathIsMutable) {
1647    // nothing to draw
1648    if (d.fClip->isEmpty() ||
1649        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
1650        return;
1651    }
1652
1653    SkPath modifiedPath;
1654    const bool paintHasPathEffect = paint.getPathEffect()
1655                                 || paint.getStyle() != SkPaint::kFill_Style;
1656
1657    //Apply pre-path matrix [Platonic-path -> Skeletal-path].
1658    SkMatrix matrix = *d.fMatrix;
1659    SkPath* skeletalPath = const_cast<SkPath*>(&platonicPath);
1660    if (prePathMatrix) {
1661        if (paintHasPathEffect || paint.getRasterizer()) {
1662            if (!pathIsMutable) {
1663                skeletalPath = &modifiedPath;
1664                pathIsMutable = true;
1665            }
1666            platonicPath.transform(*prePathMatrix, skeletalPath);
1667        } else {
1668            if (!matrix.preConcat(*prePathMatrix)) {
1669                return;
1670            }
1671        }
1672    }
1673
1674    SkTLazy<SkPaint> modifiedPaint;
1675    SkPaint* shaderPaint = const_cast<SkPaint*>(&paint);
1676
1677    //Apply path effect [Skeletal-path -> Fillable-path].
1678    SkPath* fillablePath = skeletalPath;
1679    if (paintHasPathEffect) {
1680        if (!pathIsMutable) {
1681            fillablePath = &modifiedPath;
1682            pathIsMutable = true;
1683        }
1684        bool fill = paint.getFillPath(*skeletalPath, fillablePath);
1685
1686        shaderPaint = modifiedPaint.set(paint);
1687        shaderPaint->setPathEffect(NULL);
1688        if (fill) {
1689            shaderPaint->setStyle(SkPaint::kFill_Style);
1690        } else {
1691            shaderPaint->setStyle(SkPaint::kStroke_Style);
1692            shaderPaint->setStrokeWidth(0);
1693        }
1694    }
1695
1696    //Create the shaded path. This will be the path which is painted.
1697    SkTScopedComPtr<IXpsOMPath> shadedPath;
1698    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1699         "Could not create shaded path for path.");
1700
1701    //Create the geometry for the shaded path.
1702    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1703    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1704         "Could not create shaded geometry for path.");
1705
1706    //Add the geometry to the shaded path.
1707    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1708         "Could not add the shaded geometry to shaded path.");
1709
1710    //Set the brushes.
1711    BOOL fill;
1712    BOOL stroke;
1713    HRV(this->shadePath(shadedPath.get(),
1714                        *shaderPaint,
1715                        *d.fMatrix,
1716                        &fill,
1717                        &stroke));
1718
1719    SkMaskFilter* filter = paint.getMaskFilter();
1720
1721    //Rasterizer
1722    if (paint.getRasterizer()) {
1723        SkIRect clipIRect;
1724        SkVector ppuScale;
1725        this->convertToPpm(filter,
1726                           &matrix,
1727                           &ppuScale,
1728                           d.fClip->getBounds(),
1729                           &clipIRect);
1730
1731        SkMask* mask = NULL;
1732
1733        //[Fillable-path -> Mask]
1734        SkMask rasteredMask;
1735        if (paint.getRasterizer()->rasterize(
1736                *fillablePath,
1737                matrix,
1738                &clipIRect,
1739                filter,  //just to compute how much to draw.
1740                &rasteredMask,
1741                SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
1742
1743            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
1744            mask = &rasteredMask;
1745
1746            //[Mask -> Mask]
1747            SkMask filteredMask;
1748            if (filter &&
1749                filter->filterMask(&filteredMask, *mask, *d.fMatrix, NULL)) {
1750
1751                mask = &filteredMask;
1752            } else {
1753                filteredMask.fImage = NULL;
1754            }
1755            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
1756
1757            //Draw mask.
1758            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
1759        }
1760        return;
1761    }
1762
1763    //Mask filter
1764    if (filter) {
1765        SkIRect clipIRect;
1766        SkVector ppuScale;
1767        this->convertToPpm(filter,
1768                           &matrix,
1769                           &ppuScale,
1770                           d.fClip->getBounds(),
1771                           &clipIRect);
1772
1773        //[Fillable-path -> Pixel-path]
1774        SkPath* pixelPath = pathIsMutable ? fillablePath : &modifiedPath;
1775        fillablePath->transform(matrix, pixelPath);
1776
1777        SkMask* mask = NULL;
1778
1779        //[Pixel-path -> Mask]
1780        SkMask rasteredMask;
1781        if (SkDraw::DrawToMask(
1782                        *pixelPath,
1783                        &clipIRect,
1784                        filter,  //just to compute how much to draw.
1785                        &matrix,
1786                        &rasteredMask,
1787                        SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
1788
1789            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
1790            mask = &rasteredMask;
1791
1792            //[Mask -> Mask]
1793            SkMask filteredMask;
1794            if (filter->filterMask(&filteredMask,
1795                                   rasteredMask,
1796                                   matrix,
1797                                   NULL)) {
1798                mask = &filteredMask;
1799            } else {
1800                filteredMask.fImage = NULL;
1801            }
1802            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
1803
1804            //Draw mask.
1805            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
1806        }
1807        return;
1808    }
1809
1810    //Get the figures from the shaded geometry.
1811    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1812    HRVM(shadedGeometry->GetFigures(&shadedFigures),
1813         "Could not get shaded figures for shaded path.");
1814
1815    bool xpsTransformsPath = true;
1816
1817    //Set the fill rule.
1818    XPS_FILL_RULE xpsFillRule;
1819    switch (platonicPath.getFillType()) {
1820        case SkPath::kWinding_FillType:
1821            xpsFillRule = XPS_FILL_RULE_NONZERO;
1822            break;
1823        case SkPath::kEvenOdd_FillType:
1824            xpsFillRule = XPS_FILL_RULE_EVENODD;
1825            break;
1826        case SkPath::kInverseWinding_FillType: {
1827            //[Fillable-path -> Device-path]
1828            SkPath* devicePath = pathIsMutable ? fillablePath : &modifiedPath;
1829            fillablePath->transform(matrix, devicePath);
1830
1831            HRV(this->drawInverseWindingPath(d,
1832                                             *devicePath,
1833                                             shadedPath.get()));
1834            return;
1835        }
1836        case SkPath::kInverseEvenOdd_FillType: {
1837            const SkRect universe = SkRect::MakeLTRB(
1838                0, 0,
1839                this->fCurrentCanvasSize.fWidth,
1840                this->fCurrentCanvasSize.fHeight);
1841            SkTScopedComPtr<IXpsOMGeometryFigure> addOneFigure;
1842            HRV(this->createXpsRect(universe, FALSE, TRUE, &addOneFigure));
1843            HRVM(shadedFigures->Append(addOneFigure.get()),
1844                 "Could not add even-odd flip figure to shaded path.");
1845            xpsTransformsPath = false;
1846            xpsFillRule = XPS_FILL_RULE_EVENODD;
1847            break;
1848        }
1849        default:
1850            SkASSERT(!"Unknown SkPath::FillType.");
1851    }
1852    HRVM(shadedGeometry->SetFillRule(xpsFillRule),
1853         "Could not set fill rule for shaded path.");
1854
1855    //Create the XPS transform, if possible.
1856    if (xpsTransformsPath) {
1857        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1858        HRV(this->createXpsTransform(matrix, &xpsTransform));
1859
1860        if (xpsTransform.get()) {
1861            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1862                 "Could not set transform on shaded path.");
1863        } else {
1864            xpsTransformsPath = false;
1865        }
1866    }
1867
1868    SkPath* devicePath = fillablePath;
1869    if (!xpsTransformsPath) {
1870        //[Fillable-path -> Device-path]
1871        devicePath = pathIsMutable ? fillablePath : &modifiedPath;
1872        fillablePath->transform(matrix, devicePath);
1873    }
1874    HRV(this->addXpsPathGeometry(shadedFigures.get(),
1875                                 stroke, fill, *devicePath));
1876
1877    HRV(this->clip(shadedPath.get(), d));
1878
1879    //Add the path to the active visual collection.
1880    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1881    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1882         "Could not get current visuals for shaded path.");
1883    HRVM(currentVisuals->Append(shadedPath.get()),
1884         "Could not add shaded path to current visuals.");
1885}
1886
1887HRESULT SkXPSDevice::clip(IXpsOMVisual* xpsVisual, const SkDraw& d) {
1888    SkPath clipPath;
1889    SkAssertResult(d.fClip->getBoundaryPath(&clipPath));
1890
1891    return this->clipToPath(xpsVisual, clipPath, XPS_FILL_RULE_EVENODD);
1892}
1893HRESULT SkXPSDevice::clipToPath(IXpsOMVisual* xpsVisual,
1894                                const SkPath& clipPath,
1895                                XPS_FILL_RULE fillRule) {
1896    //Create the geometry.
1897    SkTScopedComPtr<IXpsOMGeometry> clipGeometry;
1898    HRM(this->fXpsFactory->CreateGeometry(&clipGeometry),
1899        "Could not create clip geometry.");
1900
1901    //Get the figure collection of the geometry.
1902    SkTScopedComPtr<IXpsOMGeometryFigureCollection> clipFigures;
1903    HRM(clipGeometry->GetFigures(&clipFigures),
1904        "Could not get the clip figures.");
1905
1906    //Create the figures into the geometry.
1907    HR(this->addXpsPathGeometry(
1908        clipFigures.get(),
1909        FALSE, TRUE, clipPath));
1910
1911    HRM(clipGeometry->SetFillRule(fillRule),
1912        "Could not set fill rule.");
1913    HRM(xpsVisual->SetClipGeometryLocal(clipGeometry.get()),
1914        "Could not set clip geometry.");
1915
1916    return S_OK;
1917}
1918
1919void SkXPSDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
1920                             const SkIRect* srcRectOrNull,
1921                             const SkMatrix& matrix, const SkPaint& paint) {
1922    if (d.fClip->isEmpty()) {
1923        return;
1924    }
1925
1926    SkIRect srcRect;
1927    SkBitmap tmp;
1928    const SkBitmap* bitmapPtr = &bitmap;
1929    if (NULL == srcRectOrNull) {
1930        srcRect.set(0, 0, bitmap.width(), bitmap.height());
1931        bitmapPtr = &bitmap;
1932    } else {
1933        srcRect = *srcRectOrNull;
1934        if (!bitmap.extractSubset(&tmp, srcRect)) {
1935            return; // extraction failed
1936        }
1937        bitmapPtr = &tmp;
1938    }
1939
1940    //Create the new shaded path.
1941    SkTScopedComPtr<IXpsOMPath> shadedPath;
1942    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1943         "Could not create path for bitmap.");
1944
1945    //Create the shaded geometry.
1946    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1947    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1948         "Could not create geometry for bitmap.");
1949
1950    //Add the shaded geometry to the shaded path.
1951    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1952         "Could not set the geometry for bitmap.");
1953
1954    //Get the shaded figures from the shaded geometry.
1955    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1956    HRVM(shadedGeometry->GetFigures(&shadedFigures),
1957         "Could not get the figures for bitmap.");
1958
1959    SkMatrix transform = matrix;
1960    transform.postConcat(*d.fMatrix);
1961
1962    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1963    HRV(this->createXpsTransform(transform, &xpsTransform));
1964    if (xpsTransform.get()) {
1965        HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1966             "Could not set transform for bitmap.");
1967    } else {
1968        //TODO: perspective that bitmap!
1969    }
1970
1971    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
1972    if (NULL != xpsTransform.get()) {
1973        const SkShader::TileMode xy[2] = {
1974            SkShader::kClamp_TileMode,
1975            SkShader::kClamp_TileMode,
1976        };
1977        SkTScopedComPtr<IXpsOMTileBrush> xpsImageBrush;
1978        HRV(this->createXpsImageBrush(*bitmapPtr,
1979                                      transform,
1980                                      xy,
1981                                      paint.getAlpha(),
1982                                      &xpsImageBrush));
1983        HRVM(shadedPath->SetFillBrushLocal(xpsImageBrush.get()),
1984             "Could not set bitmap brush.");
1985
1986        const SkRect bitmapRect = SkRect::MakeLTRB(0, 0,
1987            SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
1988        HRV(this->createXpsRect(bitmapRect, FALSE, TRUE, &rectFigure));
1989    }
1990    HRVM(shadedFigures->Append(rectFigure.get()),
1991         "Could not add bitmap figure.");
1992
1993    //Get the current visual collection and add the shaded path to it.
1994    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1995    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1996         "Could not get current visuals for bitmap");
1997    HRVM(currentVisuals->Append(shadedPath.get()),
1998         "Could not add bitmap to current visuals.");
1999
2000    HRV(this->clip(shadedPath.get(), d));
2001}
2002
2003void SkXPSDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
2004                             int x, int y,
2005                             const SkPaint& paint) {
2006    //TODO: override this for XPS
2007    SkDEBUGF(("XPS drawSprite not yet implemented."));
2008}
2009
2010HRESULT SkXPSDevice::CreateTypefaceUse(const SkPaint& paint,
2011                                       TypefaceUse** typefaceUse) {
2012    const SkTypeface* typeface = paint.getTypeface();
2013
2014    //Check cache.
2015    const SkFontID typefaceID = SkTypeface::UniqueID(typeface);
2016    if (!this->fTypefaces.empty()) {
2017        TypefaceUse* current = &this->fTypefaces.front();
2018        const TypefaceUse* last = &this->fTypefaces.back();
2019        for (; current <= last; ++current) {
2020            if (current->typefaceId == typefaceID) {
2021                *typefaceUse = current;
2022                return S_OK;
2023            }
2024        }
2025    }
2026
2027    //TODO: create glyph only fonts
2028    //and let the host deal with what kind of font we're looking at.
2029    XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_RESTRICTED;
2030
2031    SkTScopedComPtr<IStream> fontStream;
2032    SkStream* fontData = SkFontHost::OpenStream(typefaceID);
2033    HRM(SkIStream::CreateFromSkStream(fontData,
2034                                      true,
2035                                      &fontStream),
2036        "Could not create font stream.");
2037
2038    const size_t size =
2039        SK_ARRAY_COUNT(L"/Resources/Fonts/" L_GUID_ID L".odttf");
2040    wchar_t buffer[size];
2041    wchar_t id[GUID_ID_LEN];
2042    HR(create_id(id, GUID_ID_LEN));
2043    swprintf_s(buffer, size, L"/Resources/Fonts/%s.odttf", id);
2044
2045    SkTScopedComPtr<IOpcPartUri> partUri;
2046    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
2047        "Could not create font resource part uri.");
2048
2049    SkTScopedComPtr<IXpsOMFontResource> xpsFontResource;
2050    HRM(this->fXpsFactory->CreateFontResource(fontStream.get(),
2051                                              embedding,
2052                                              partUri.get(),
2053                                              FALSE,
2054                                              &xpsFontResource),
2055        "Could not create font resource.");
2056
2057    TypefaceUse& newTypefaceUse = this->fTypefaces.push_back();
2058    newTypefaceUse.typefaceId = typefaceID;
2059    newTypefaceUse.fontData = fontData;
2060    newTypefaceUse.xpsFont = xpsFontResource.release();
2061
2062    SkAutoGlyphCache agc = SkAutoGlyphCache(paint, &SkMatrix::I());
2063    SkGlyphCache* glyphCache = agc.getCache();
2064    unsigned int glyphCount = glyphCache->getGlyphCount();
2065    newTypefaceUse.glyphsUsed = new SkBitSet(glyphCount);
2066
2067    *typefaceUse = &newTypefaceUse;
2068    return S_OK;
2069}
2070
2071HRESULT SkXPSDevice::AddGlyphs(const SkDraw& d,
2072                               IXpsOMObjectFactory* xpsFactory,
2073                               IXpsOMCanvas* canvas,
2074                               IXpsOMFontResource* font,
2075                               LPCWSTR text,
2076                               XPS_GLYPH_INDEX* xpsGlyphs,
2077                               UINT32 xpsGlyphsLen,
2078                               XPS_POINT *origin,
2079                               FLOAT fontSize,
2080                               XPS_STYLE_SIMULATION sims,
2081                               const SkMatrix& transform,
2082                               const SkPaint& paint) {
2083    SkTScopedComPtr<IXpsOMGlyphs> glyphs;
2084    HRM(xpsFactory->CreateGlyphs(font, &glyphs), "Could not create glyphs.");
2085
2086    //XPS uses affine transformations for everything...
2087    //...except positioning text.
2088    bool useCanvasForClip;
2089    if ((transform.getType() & ~SkMatrix::kTranslate_Mask) == 0) {
2090        origin->x += SkScalarToFLOAT(transform.getTranslateX());
2091        origin->y += SkScalarToFLOAT(transform.getTranslateY());
2092        useCanvasForClip = false;
2093    } else {
2094        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
2095        HR(this->createXpsTransform(transform, &xpsMatrixToUse));
2096        if (xpsMatrixToUse.get()) {
2097            HRM(glyphs->SetTransformLocal(xpsMatrixToUse.get()),
2098                "Could not set transform matrix.");
2099            useCanvasForClip = true;
2100        } else {
2101            SkASSERT(!"Attempt to add glyphs in perspective.");
2102            useCanvasForClip = false;
2103        }
2104    }
2105
2106    SkTScopedComPtr<IXpsOMGlyphsEditor> glyphsEditor;
2107    HRM(glyphs->GetGlyphsEditor(&glyphsEditor), "Could not get glyph editor.");
2108
2109    if (NULL != text) {
2110        HRM(glyphsEditor->SetUnicodeString(text),
2111            "Could not set unicode string.");
2112    }
2113
2114    if (NULL != xpsGlyphs) {
2115        HRM(glyphsEditor->SetGlyphIndices(xpsGlyphsLen, xpsGlyphs),
2116            "Could not set glyphs.");
2117    }
2118
2119    HRM(glyphsEditor->ApplyEdits(), "Could not apply glyph edits.");
2120
2121    SkTScopedComPtr<IXpsOMBrush> xpsFillBrush;
2122    HR(this->createXpsBrush(
2123            paint,
2124            &xpsFillBrush,
2125            useCanvasForClip ? NULL : &transform));
2126
2127    HRM(glyphs->SetFillBrushLocal(xpsFillBrush.get()),
2128        "Could not set fill brush.");
2129
2130    HRM(glyphs->SetOrigin(origin), "Could not set glyph origin.");
2131
2132    HRM(glyphs->SetFontRenderingEmSize(fontSize),
2133        "Could not set font size.");
2134
2135    HRM(glyphs->SetStyleSimulations(sims),
2136        "Could not set style simulations.");
2137
2138    SkTScopedComPtr<IXpsOMVisualCollection> visuals;
2139    HRM(canvas->GetVisuals(&visuals), "Could not get glyph canvas visuals.");
2140
2141    if (!useCanvasForClip) {
2142        HR(this->clip(glyphs.get(), d));
2143        HRM(visuals->Append(glyphs.get()), "Could not add glyphs to canvas.");
2144    } else {
2145        SkTScopedComPtr<IXpsOMCanvas> glyphCanvas;
2146        HRM(this->fXpsFactory->CreateCanvas(&glyphCanvas),
2147            "Could not create glyph canvas.");
2148
2149        SkTScopedComPtr<IXpsOMVisualCollection> glyphCanvasVisuals;
2150        HRM(glyphCanvas->GetVisuals(&glyphCanvasVisuals),
2151            "Could not get glyph visuals collection.");
2152
2153        HRM(glyphCanvasVisuals->Append(glyphs.get()),
2154            "Could not add glyphs to page.");
2155        HR(this->clip(glyphCanvas.get(), d));
2156
2157        HRM(visuals->Append(glyphCanvas.get()),
2158            "Could not add glyph canvas to page.");
2159    }
2160
2161    return S_OK;
2162}
2163
2164struct SkXPSDrawProcs : public SkDrawProcs {
2165public:
2166    /** [in] Advance width and offsets for glyphs measured in
2167    hundredths of the font em size (XPS Spec 5.1.3). */
2168    FLOAT centemPerUnit;
2169    /** [in,out] The accumulated glyphs used in the current typeface. */
2170    SkBitSet* glyphUse;
2171    /** [out] The glyphs to draw. */
2172    SkTDArray<XPS_GLYPH_INDEX> xpsGlyphs;
2173};
2174
2175static void xps_draw_1_glyph(const SkDraw1Glyph& state,
2176                             SkFixed x, SkFixed y,
2177                             const SkGlyph& skGlyph) {
2178    SkASSERT(skGlyph.fWidth > 0 && skGlyph.fHeight > 0);
2179
2180    SkXPSDrawProcs* procs = static_cast<SkXPSDrawProcs*>(state.fDraw->fProcs);
2181    //Draw pre-adds half for floor rounding.
2182    x -= SK_FixedHalf;
2183    y -= SK_FixedHalf;
2184
2185    XPS_GLYPH_INDEX* xpsGlyph = procs->xpsGlyphs.append();
2186    uint16_t glyphID = skGlyph.getGlyphID();
2187    procs->glyphUse->setBit(glyphID, true);
2188    xpsGlyph->index = glyphID;
2189    if (1 == procs->xpsGlyphs.count()) {
2190        xpsGlyph->advanceWidth = 0.0f;
2191        xpsGlyph->horizontalOffset = SkFixedToFloat(x) * procs->centemPerUnit;
2192        xpsGlyph->verticalOffset = SkFixedToFloat(y) * -procs->centemPerUnit;
2193    } else {
2194        const XPS_GLYPH_INDEX& first = procs->xpsGlyphs[0];
2195        xpsGlyph->advanceWidth = 0.0f;
2196        xpsGlyph->horizontalOffset = (SkFixedToFloat(x) * procs->centemPerUnit)
2197                                     - first.horizontalOffset;
2198        xpsGlyph->verticalOffset = (SkFixedToFloat(y) * -procs->centemPerUnit)
2199                                   - first.verticalOffset;
2200    }
2201}
2202
2203static void text_draw_init(const SkPaint& paint,
2204                           const void* text, size_t byteLength,
2205                           SkBitSet& glyphsUsed,
2206                           SkDraw& myDraw, SkXPSDrawProcs& procs) {
2207    procs.fD1GProc = xps_draw_1_glyph;
2208    int numGlyphGuess;
2209    switch (paint.getTextEncoding()) {
2210        case SkPaint::kUTF8_TextEncoding:
2211            numGlyphGuess = SkUTF8_CountUnichars(
2212                static_cast<const char *>(text),
2213                byteLength);
2214            break;
2215        case SkPaint::kUTF16_TextEncoding:
2216            numGlyphGuess = SkUTF16_CountUnichars(
2217                static_cast<const uint16_t *>(text),
2218                byteLength);
2219            break;
2220        case SkPaint::kGlyphID_TextEncoding:
2221            numGlyphGuess = byteLength / 2;
2222            break;
2223        default:
2224            SK_DEBUGBREAK(true);
2225    }
2226    procs.xpsGlyphs.setReserve(numGlyphGuess);
2227    procs.glyphUse = &glyphsUsed;
2228    procs.centemPerUnit = 100.0f / SkScalarToFLOAT(paint.getTextSize());
2229
2230    myDraw.fProcs = &procs;
2231    myDraw.fMVMatrix = &SkMatrix::I();
2232    myDraw.fExtMatrix = &SkMatrix::I();
2233}
2234
2235static bool text_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
2236    const SkPaint::Style style = paint.getStyle();
2237    return matrix.hasPerspective()
2238        || SkPaint::kStroke_Style == style
2239        || SkPaint::kStrokeAndFill_Style == style
2240        || paint.getMaskFilter()
2241        || paint.getRasterizer()
2242    ;
2243}
2244
2245void SkXPSDevice::drawText(const SkDraw& d,
2246                           const void* text, size_t byteLen,
2247                           SkScalar x, SkScalar y,
2248                           const SkPaint& paint) {
2249    if (byteLen < 1) return;
2250
2251    if (text_must_be_pathed(paint, *d.fMatrix)) {
2252        SkPath path;
2253        paint.getTextPath(text, byteLen, x, y, &path);
2254        this->drawPath(d, path, paint, NULL, true);
2255        //TODO: add automation "text"
2256        return;
2257    }
2258
2259    TypefaceUse* typeface;
2260    HRV(CreateTypefaceUse(paint, &typeface));
2261
2262    SkDraw myDraw(d);
2263    SkXPSDrawProcs procs;
2264    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
2265
2266    myDraw.drawText(static_cast<const char*>(text), byteLen, x, y, paint);
2267
2268    // SkDraw may have clipped out the glyphs, so we need to check
2269    if (procs.xpsGlyphs.count() == 0) {
2270        return;
2271    }
2272
2273    XPS_POINT origin = {
2274        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
2275        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
2276    };
2277    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
2278    procs.xpsGlyphs[0].verticalOffset = 0.0f;
2279
2280    HRV(AddGlyphs(d,
2281                  this->fXpsFactory.get(),
2282                  this->fCurrentXpsCanvas.get(),
2283                  typeface->xpsFont,
2284                  NULL,
2285                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
2286                  &origin,
2287                  SkScalarToFLOAT(paint.getTextSize()),
2288                  XPS_STYLE_SIMULATION_NONE,
2289                  *d.fMatrix,
2290                  paint));
2291}
2292
2293void SkXPSDevice::drawPosText(const SkDraw& d,
2294                              const void* text, size_t byteLen,
2295                              const SkScalar pos[],
2296                              SkScalar constY, int scalarsPerPos,
2297                              const SkPaint& paint) {
2298    if (byteLen < 1) return;
2299
2300    if (text_must_be_pathed(paint, *d.fMatrix)) {
2301        SkPath path;
2302        //TODO: make this work, Draw currently does not handle as well.
2303        //paint.getTextPath(text, byteLength, x, y, &path);
2304        //this->drawPath(d, path, paint, NULL, true);
2305        //TODO: add automation "text"
2306        return;
2307    }
2308
2309    TypefaceUse* typeface;
2310    HRV(CreateTypefaceUse(paint, &typeface));
2311
2312    SkDraw myDraw(d);
2313    SkXPSDrawProcs procs;
2314    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
2315
2316    myDraw.drawPosText(static_cast<const char*>(text), byteLen,
2317                       pos, constY, scalarsPerPos,
2318                       paint);
2319
2320    // SkDraw may have clipped out the glyphs, so we need to check
2321    if (procs.xpsGlyphs.count() == 0) {
2322        return;
2323    }
2324
2325    XPS_POINT origin = {
2326        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
2327        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
2328    };
2329    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
2330    procs.xpsGlyphs[0].verticalOffset = 0.0f;
2331
2332    HRV(AddGlyphs(d,
2333                  this->fXpsFactory.get(),
2334                  this->fCurrentXpsCanvas.get(),
2335                  typeface->xpsFont,
2336                  NULL,
2337                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
2338                  &origin,
2339                  SkScalarToFLOAT(paint.getTextSize()),
2340                  XPS_STYLE_SIMULATION_NONE,
2341                  *d.fMatrix,
2342                  paint));
2343}
2344
2345void SkXPSDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
2346                                 const SkPath& path, const SkMatrix* matrix,
2347                                 const SkPaint& paint) {
2348    //This will call back into the device to do the drawing.
2349     d.drawTextOnPath((const char*)text, len, path, matrix, paint);
2350}
2351
2352void SkXPSDevice::drawDevice(const SkDraw& d, SkDevice* dev,
2353                             int x, int y,
2354                             const SkPaint&) {
2355    SkXPSDevice* that = static_cast<SkXPSDevice*>(dev);
2356
2357    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
2358    XPS_MATRIX rawTransform = {
2359        1.0f,
2360        0.0f,
2361        0.0f,
2362        1.0f,
2363        static_cast<FLOAT>(x),
2364        static_cast<FLOAT>(y),
2365    };
2366    HRVM(this->fXpsFactory->CreateMatrixTransform(&rawTransform, &xpsTransform),
2367         "Could not create layer transform.");
2368    HRVM(that->fCurrentXpsCanvas->SetTransformLocal(xpsTransform.get()),
2369         "Could not set layer transform.");
2370
2371    //Get the current visual collection and add the layer to it.
2372    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
2373    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
2374         "Could not get current visuals for layer.");
2375    HRVM(currentVisuals->Append(that->fCurrentXpsCanvas.get()),
2376         "Could not add layer to current visuals.");
2377}
2378
2379bool SkXPSDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
2380                               SkCanvas::Config8888) {
2381    return false;
2382}
2383
2384SkDevice* SkXPSDevice::onCreateCompatibleDevice(SkBitmap::Config config,
2385                                                int width, int height,
2386                                                bool isOpaque,
2387                                                Usage usage) {
2388    if (SkDevice::kGeneral_Usage == usage) {
2389        SK_CRASH();
2390        //To what stream do we write?
2391        //SkXPSDevice* dev = new SkXPSDevice(this);
2392        //SkSize s = SkSize::Make(width, height);
2393        //dev->BeginCanvas(s, s, SkMatrix::I());
2394        //return dev;
2395    }
2396
2397    return new SkXPSDevice(this->fXpsFactory.get());
2398}
2399
2400SkXPSDevice::SkXPSDevice(IXpsOMObjectFactory* xpsFactory)
2401    : SkDevice(make_fake_bitmap(10000, 10000))
2402    , fCurrentPage(0) {
2403
2404    HRVM(CoCreateInstance(
2405             CLSID_XpsOMObjectFactory,
2406             NULL,
2407             CLSCTX_INPROC_SERVER,
2408             IID_PPV_ARGS(&this->fXpsFactory)),
2409         "Could not create factory for layer.");
2410
2411    HRVM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas),
2412         "Could not create canvas for layer.");
2413}
2414