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