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