gfx/skia/trunk/src/device/xps/SkXPSDevice.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/gfx/skia/trunk/src/device/xps/SkXPSDevice.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2451 @@
     1.4 +/*
     1.5 + * Copyright 2011 Google Inc.
     1.6 + *
     1.7 + * Use of this source code is governed by a BSD-style license that can be
     1.8 + * found in the LICENSE file.
     1.9 + */
    1.10 +
    1.11 +#ifndef UNICODE
    1.12 +#define UNICODE
    1.13 +#endif
    1.14 +#ifndef _UNICODE
    1.15 +#define _UNICODE
    1.16 +#endif
    1.17 +#include "SkTypes.h"
    1.18 +#include <ObjBase.h>
    1.19 +#include <XpsObjectModel.h>
    1.20 +#include <T2EmbApi.h>
    1.21 +#include <FontSub.h>
    1.22 +
    1.23 +#include "SkColor.h"
    1.24 +#include "SkConstexprMath.h"
    1.25 +#include "SkData.h"
    1.26 +#include "SkDraw.h"
    1.27 +#include "SkDrawProcs.h"
    1.28 +#include "SkEndian.h"
    1.29 +#include "SkFontHost.h"
    1.30 +#include "SkGlyphCache.h"
    1.31 +#include "SkHRESULT.h"
    1.32 +#include "SkImageEncoder.h"
    1.33 +#include "SkIStream.h"
    1.34 +#include "SkMaskFilter.h"
    1.35 +#include "SkPaint.h"
    1.36 +#include "SkPoint.h"
    1.37 +#include "SkRasterizer.h"
    1.38 +#include "SkSFNTHeader.h"
    1.39 +#include "SkShader.h"
    1.40 +#include "SkSize.h"
    1.41 +#include "SkStream.h"
    1.42 +#include "SkTDArray.h"
    1.43 +#include "SkTLazy.h"
    1.44 +#include "SkTScopedComPtr.h"
    1.45 +#include "SkTTCFHeader.h"
    1.46 +#include "SkTypefacePriv.h"
    1.47 +#include "SkUtils.h"
    1.48 +#include "SkXPSDevice.h"
    1.49 +
    1.50 +//Windows defines a FLOAT type,
    1.51 +//make it clear when converting a scalar that this is what is wanted.
    1.52 +#define SkScalarToFLOAT(n) SkScalarToFloat(n)
    1.53 +
    1.54 +//Dummy representation of a GUID from create_id.
    1.55 +#define L_GUID_ID L"XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX"
    1.56 +//Length of GUID representation from create_id, including NULL terminator.
    1.57 +#define GUID_ID_LEN SK_ARRAY_COUNT(L_GUID_ID)
    1.58 +
    1.59 +/**
    1.60 +   Formats a GUID and places it into buffer.
    1.61 +   buffer should have space for at least GUID_ID_LEN wide characters.
    1.62 +   The string will always be wchar null terminated.
    1.63 +   XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0
    1.64 +   @return -1 if there was an error, > 0 if success.
    1.65 + */
    1.66 +static int format_guid(const GUID& guid,
    1.67 +                       wchar_t* buffer, size_t bufferSize,
    1.68 +                       wchar_t sep = '-') {
    1.69 +    SkASSERT(bufferSize >= GUID_ID_LEN);
    1.70 +    return swprintf_s(buffer,
    1.71 +                      bufferSize,
    1.72 +                      L"%08lX%c%04X%c%04X%c%02X%02X%c%02X%02X%02X%02X%02X%02X",
    1.73 +                      guid.Data1,
    1.74 +                      sep,
    1.75 +                      guid.Data2,
    1.76 +                      sep,
    1.77 +                      guid.Data3,
    1.78 +                      sep,
    1.79 +                      guid.Data4[0],
    1.80 +                      guid.Data4[1],
    1.81 +                      sep,
    1.82 +                      guid.Data4[2],
    1.83 +                      guid.Data4[3],
    1.84 +                      guid.Data4[4],
    1.85 +                      guid.Data4[5],
    1.86 +                      guid.Data4[6],
    1.87 +                      guid.Data4[7]);
    1.88 +}
    1.89 +
    1.90 +/**
    1.91 +   Creates a GUID based id and places it into buffer.
    1.92 +   buffer should have space for at least GUID_ID_LEN wide characters.
    1.93 +   The string will always be wchar null terminated.
    1.94 +   XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0
    1.95 +   The string may begin with a digit,
    1.96 +   and so may not be suitable as a bare resource key.
    1.97 + */
    1.98 +static HRESULT create_id(wchar_t* buffer, size_t bufferSize,
    1.99 +                         wchar_t sep = '-') {
   1.100 +    GUID guid = {};
   1.101 +    HRM(CoCreateGuid(&guid), "Could not create GUID for id.");
   1.102 +
   1.103 +    if (format_guid(guid, buffer, bufferSize, sep) == -1) {
   1.104 +        HRM(E_UNEXPECTED, "Could not format GUID into id.");
   1.105 +    }
   1.106 +
   1.107 +    return S_OK;
   1.108 +}
   1.109 +
   1.110 +static SkBitmap make_fake_bitmap(int width, int height) {
   1.111 +    SkBitmap bitmap;
   1.112 +    bitmap.setConfig(SkImageInfo::MakeUnknown(width, height));
   1.113 +    return bitmap;
   1.114 +}
   1.115 +
   1.116 +// TODO: should inherit from SkBaseDevice instead of SkBitmapDevice...
   1.117 +SkXPSDevice::SkXPSDevice()
   1.118 +    : SkBitmapDevice(make_fake_bitmap(10000, 10000))
   1.119 +    , fCurrentPage(0) {
   1.120 +}
   1.121 +
   1.122 +SkXPSDevice::~SkXPSDevice() {
   1.123 +}
   1.124 +
   1.125 +SkXPSDevice::TypefaceUse::TypefaceUse()
   1.126 +    : typefaceId(0xffffffff)
   1.127 +    , fontData(NULL)
   1.128 +    , xpsFont(NULL)
   1.129 +    , glyphsUsed(NULL) {
   1.130 +}
   1.131 +
   1.132 +SkXPSDevice::TypefaceUse::~TypefaceUse() {
   1.133 +    //xpsFont owns fontData ref
   1.134 +    this->xpsFont->Release();
   1.135 +    delete this->glyphsUsed;
   1.136 +}
   1.137 +
   1.138 +bool SkXPSDevice::beginPortfolio(SkWStream* outputStream) {
   1.139 +    if (!this->fAutoCo.succeeded()) return false;
   1.140 +
   1.141 +    //Create XPS Factory.
   1.142 +    HRBM(CoCreateInstance(
   1.143 +             CLSID_XpsOMObjectFactory,
   1.144 +             NULL,
   1.145 +             CLSCTX_INPROC_SERVER,
   1.146 +             IID_PPV_ARGS(&this->fXpsFactory)),
   1.147 +         "Could not create XPS factory.");
   1.148 +
   1.149 +    HRBM(SkWIStream::CreateFromSkWStream(outputStream, &this->fOutputStream),
   1.150 +         "Could not convert SkStream to IStream.");
   1.151 +
   1.152 +    return true;
   1.153 +}
   1.154 +
   1.155 +bool SkXPSDevice::beginSheet(
   1.156 +        const SkVector& unitsPerMeter,
   1.157 +        const SkVector& pixelsPerMeter,
   1.158 +        const SkSize& trimSize,
   1.159 +        const SkRect* mediaBox,
   1.160 +        const SkRect* bleedBox,
   1.161 +        const SkRect* artBox,
   1.162 +        const SkRect* cropBox) {
   1.163 +    ++this->fCurrentPage;
   1.164 +
   1.165 +    //For simplicity, just write everything out in geometry units,
   1.166 +    //then have a base canvas do the scale to physical units.
   1.167 +    this->fCurrentCanvasSize = trimSize;
   1.168 +    this->fCurrentUnitsPerMeter = unitsPerMeter;
   1.169 +    this->fCurrentPixelsPerMeter = pixelsPerMeter;
   1.170 +
   1.171 +    this->fCurrentXpsCanvas.reset();
   1.172 +    HRBM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas),
   1.173 +         "Could not create base canvas.");
   1.174 +
   1.175 +    return true;
   1.176 +}
   1.177 +
   1.178 +HRESULT SkXPSDevice::createXpsThumbnail(IXpsOMPage* page,
   1.179 +                                        const unsigned int pageNum,
   1.180 +                                        IXpsOMImageResource** image) {
   1.181 +    SkTScopedComPtr<IXpsOMThumbnailGenerator> thumbnailGenerator;
   1.182 +    HRM(CoCreateInstance(
   1.183 +            CLSID_XpsOMThumbnailGenerator,
   1.184 +            NULL,
   1.185 +            CLSCTX_INPROC_SERVER,
   1.186 +            IID_PPV_ARGS(&thumbnailGenerator)),
   1.187 +        "Could not create thumbnail generator.");
   1.188 +
   1.189 +    SkTScopedComPtr<IOpcPartUri> partUri;
   1.190 +    static const size_t size = SkTUMax<
   1.191 +        SK_ARRAY_COUNT(L"/Documents/1/Metadata/.png") + SK_DIGITS_IN(pageNum),
   1.192 +        SK_ARRAY_COUNT(L"/Metadata/" L_GUID_ID L".png")
   1.193 +    >::value;
   1.194 +    wchar_t buffer[size];
   1.195 +    if (pageNum > 0) {
   1.196 +        swprintf_s(buffer, size, L"/Documents/1/Metadata/%u.png", pageNum);
   1.197 +    } else {
   1.198 +        wchar_t id[GUID_ID_LEN];
   1.199 +        HR(create_id(id, GUID_ID_LEN));
   1.200 +        swprintf_s(buffer, size, L"/Metadata/%s.png", id);
   1.201 +    }
   1.202 +    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
   1.203 +        "Could not create thumbnail part uri.");
   1.204 +
   1.205 +    HRM(thumbnailGenerator->GenerateThumbnail(page,
   1.206 +                                              XPS_IMAGE_TYPE_PNG,
   1.207 +                                              XPS_THUMBNAIL_SIZE_LARGE,
   1.208 +                                              partUri.get(),
   1.209 +                                              image),
   1.210 +        "Could not generate thumbnail.");
   1.211 +
   1.212 +    return S_OK;
   1.213 +}
   1.214 +
   1.215 +HRESULT SkXPSDevice::createXpsPage(const XPS_SIZE& pageSize,
   1.216 +                                   IXpsOMPage** page) {
   1.217 +    static const size_t size = SK_ARRAY_COUNT(L"/Documents/1/Pages/.fpage")
   1.218 +                             + SK_DIGITS_IN(fCurrentPage);
   1.219 +    wchar_t buffer[size];
   1.220 +    swprintf_s(buffer, size, L"/Documents/1/Pages/%u.fpage",
   1.221 +                             this->fCurrentPage);
   1.222 +    SkTScopedComPtr<IOpcPartUri> partUri;
   1.223 +    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
   1.224 +        "Could not create page part uri.");
   1.225 +
   1.226 +    //If the language is unknown, use "und" (XPS Spec 2.3.5.1).
   1.227 +    HRM(this->fXpsFactory->CreatePage(&pageSize,
   1.228 +                                      L"und",
   1.229 +                                      partUri.get(),
   1.230 +                                      page),
   1.231 +        "Could not create page.");
   1.232 +
   1.233 +    return S_OK;
   1.234 +}
   1.235 +
   1.236 +HRESULT SkXPSDevice::initXpsDocumentWriter(IXpsOMImageResource* image) {
   1.237 +    //Create package writer.
   1.238 +    {
   1.239 +        SkTScopedComPtr<IOpcPartUri> partUri;
   1.240 +        HRM(this->fXpsFactory->CreatePartUri(L"/FixedDocumentSequence.fdseq",
   1.241 +                                             &partUri),
   1.242 +            "Could not create document sequence part uri.");
   1.243 +        HRM(this->fXpsFactory->CreatePackageWriterOnStream(
   1.244 +                this->fOutputStream.get(),
   1.245 +                TRUE,
   1.246 +                XPS_INTERLEAVING_OFF, //XPS_INTERLEAVING_ON,
   1.247 +                partUri.get(),
   1.248 +                NULL,
   1.249 +                image,
   1.250 +                NULL,
   1.251 +                NULL,
   1.252 +                &this->fPackageWriter),
   1.253 +            "Could not create package writer.");
   1.254 +    }
   1.255 +
   1.256 +    //Begin the lone document.
   1.257 +    {
   1.258 +        SkTScopedComPtr<IOpcPartUri> partUri;
   1.259 +        HRM(this->fXpsFactory->CreatePartUri(
   1.260 +                L"/Documents/1/FixedDocument.fdoc",
   1.261 +                &partUri),
   1.262 +            "Could not create fixed document part uri.");
   1.263 +        HRM(this->fPackageWriter->StartNewDocument(partUri.get(),
   1.264 +                                                   NULL,
   1.265 +                                                   NULL,
   1.266 +                                                   NULL,
   1.267 +                                                   NULL),
   1.268 +            "Could not start document.");
   1.269 +    }
   1.270 +
   1.271 +    return S_OK;
   1.272 +}
   1.273 +
   1.274 +bool SkXPSDevice::endSheet() {
   1.275 +    //XPS is fixed at 96dpi (XPS Spec 11.1).
   1.276 +    static const float xpsDPI = 96.0f;
   1.277 +    static const float inchesPerMeter = 10000.0f / 254.0f;
   1.278 +    static const float targetUnitsPerMeter = xpsDPI * inchesPerMeter;
   1.279 +    const float scaleX = targetUnitsPerMeter
   1.280 +                       / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fX);
   1.281 +    const float scaleY = targetUnitsPerMeter
   1.282 +                       / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fY);
   1.283 +
   1.284 +    //Create the scale canvas.
   1.285 +    SkTScopedComPtr<IXpsOMCanvas> scaleCanvas;
   1.286 +    HRBM(this->fXpsFactory->CreateCanvas(&scaleCanvas),
   1.287 +         "Could not create scale canvas.");
   1.288 +    SkTScopedComPtr<IXpsOMVisualCollection> scaleCanvasVisuals;
   1.289 +    HRBM(scaleCanvas->GetVisuals(&scaleCanvasVisuals),
   1.290 +         "Could not get scale canvas visuals.");
   1.291 +
   1.292 +    SkTScopedComPtr<IXpsOMMatrixTransform> geomToPhys;
   1.293 +    XPS_MATRIX rawGeomToPhys = { scaleX, 0, 0, scaleY, 0, 0, };
   1.294 +    HRBM(this->fXpsFactory->CreateMatrixTransform(&rawGeomToPhys, &geomToPhys),
   1.295 +         "Could not create geometry to physical transform.");
   1.296 +    HRBM(scaleCanvas->SetTransformLocal(geomToPhys.get()),
   1.297 +         "Could not set transform on scale canvas.");
   1.298 +
   1.299 +    //Add the content canvas to the scale canvas.
   1.300 +    HRBM(scaleCanvasVisuals->Append(this->fCurrentXpsCanvas.get()),
   1.301 +         "Could not add base canvas to scale canvas.");
   1.302 +
   1.303 +    //Create the page.
   1.304 +    XPS_SIZE pageSize = {
   1.305 +        SkScalarToFLOAT(this->fCurrentCanvasSize.width()) * scaleX,
   1.306 +        SkScalarToFLOAT(this->fCurrentCanvasSize.height()) * scaleY,
   1.307 +    };
   1.308 +    SkTScopedComPtr<IXpsOMPage> page;
   1.309 +    HRB(this->createXpsPage(pageSize, &page));
   1.310 +
   1.311 +    SkTScopedComPtr<IXpsOMVisualCollection> pageVisuals;
   1.312 +    HRBM(page->GetVisuals(&pageVisuals), "Could not get page visuals.");
   1.313 +
   1.314 +    //Add the scale canvas to the page.
   1.315 +    HRBM(pageVisuals->Append(scaleCanvas.get()),
   1.316 +         "Could not add scale canvas to page.");
   1.317 +
   1.318 +    //Create the package writer if it hasn't been created yet.
   1.319 +    if (NULL == this->fPackageWriter.get()) {
   1.320 +        SkTScopedComPtr<IXpsOMImageResource> image;
   1.321 +        //Ignore return, thumbnail is completely optional.
   1.322 +        this->createXpsThumbnail(page.get(), 0, &image);
   1.323 +
   1.324 +        HRB(this->initXpsDocumentWriter(image.get()));
   1.325 +    }
   1.326 +
   1.327 +    HRBM(this->fPackageWriter->AddPage(page.get(),
   1.328 +                                       &pageSize,
   1.329 +                                       NULL,
   1.330 +                                       NULL,
   1.331 +                                       NULL,
   1.332 +                                       NULL),
   1.333 +         "Could not write the page.");
   1.334 +    this->fCurrentXpsCanvas.reset();
   1.335 +
   1.336 +    return true;
   1.337 +}
   1.338 +
   1.339 +static HRESULT subset_typeface(SkXPSDevice::TypefaceUse* current) {
   1.340 +    //CreateFontPackage wants unsigned short.
   1.341 +    //Microsoft, Y U NO stdint.h?
   1.342 +    SkTDArray<unsigned short> keepList;
   1.343 +    current->glyphsUsed->exportTo(&keepList);
   1.344 +
   1.345 +    int ttcCount = (current->ttcIndex + 1);
   1.346 +
   1.347 +    //The following are declared with the types required by CreateFontPackage.
   1.348 +    unsigned char *fontPackageBufferRaw = NULL;
   1.349 +    unsigned long fontPackageBufferSize;
   1.350 +    unsigned long bytesWritten;
   1.351 +    unsigned long result = CreateFontPackage(
   1.352 +        (unsigned char *) current->fontData->getMemoryBase(),
   1.353 +        (unsigned long) current->fontData->getLength(),
   1.354 +        &fontPackageBufferRaw,
   1.355 +        &fontPackageBufferSize,
   1.356 +        &bytesWritten,
   1.357 +        TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST | (ttcCount > 0 ? TTFCFP_FLAGS_TTC : 0),
   1.358 +        current->ttcIndex,
   1.359 +        TTFCFP_SUBSET,
   1.360 +        0,
   1.361 +        0,
   1.362 +        0,
   1.363 +        keepList.begin(),
   1.364 +        keepList.count(),
   1.365 +        sk_malloc_throw,
   1.366 +        sk_realloc_throw,
   1.367 +        sk_free,
   1.368 +        NULL);
   1.369 +    SkAutoTMalloc<unsigned char> fontPackageBuffer(fontPackageBufferRaw);
   1.370 +    if (result != NO_ERROR) {
   1.371 +        SkDEBUGF(("CreateFontPackage Error %lu", result));
   1.372 +        return E_UNEXPECTED;
   1.373 +    }
   1.374 +
   1.375 +    // If it was originally a ttc, keep it a ttc.
   1.376 +    // CreateFontPackage over-allocates, realloc usually decreases the size substantially.
   1.377 +    size_t extra;
   1.378 +    if (ttcCount > 0) {
   1.379 +        // Create space for a ttc header.
   1.380 +        extra = sizeof(SkTTCFHeader) + (ttcCount * sizeof(SK_OT_ULONG));
   1.381 +        fontPackageBuffer.realloc(bytesWritten + extra);
   1.382 +        //overlap is certain, use memmove
   1.383 +        memmove(fontPackageBuffer.get() + extra, fontPackageBuffer.get(), bytesWritten);
   1.384 +
   1.385 +        // Write the ttc header.
   1.386 +        SkTTCFHeader* ttcfHeader = reinterpret_cast<SkTTCFHeader*>(fontPackageBuffer.get());
   1.387 +        ttcfHeader->ttcTag = SkTTCFHeader::TAG;
   1.388 +        ttcfHeader->version = SkTTCFHeader::version_1;
   1.389 +        ttcfHeader->numOffsets = SkEndian_SwapBE32(ttcCount);
   1.390 +        SK_OT_ULONG* offsetPtr = SkTAfter<SK_OT_ULONG>(ttcfHeader);
   1.391 +        for (int i = 0; i < ttcCount; ++i, ++offsetPtr) {
   1.392 +            *offsetPtr = SkEndian_SwapBE32(extra);
   1.393 +        }
   1.394 +
   1.395 +        // Fix up offsets in sfnt table entries.
   1.396 +        SkSFNTHeader* sfntHeader = SkTAddOffset<SkSFNTHeader>(fontPackageBuffer.get(), extra);
   1.397 +        int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
   1.398 +        SkSFNTHeader::TableDirectoryEntry* tableDirectory =
   1.399 +            SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
   1.400 +        for (int i = 0; i < numTables; ++i, ++tableDirectory) {
   1.401 +            tableDirectory->offset = SkEndian_SwapBE32(
   1.402 +                SkEndian_SwapBE32(tableDirectory->offset) + extra);
   1.403 +        }
   1.404 +    } else {
   1.405 +        extra = 0;
   1.406 +        fontPackageBuffer.realloc(bytesWritten);
   1.407 +    }
   1.408 +
   1.409 +    SkAutoTUnref<SkMemoryStream> newStream(new SkMemoryStream());
   1.410 +    newStream->setMemoryOwned(fontPackageBuffer.detach(), bytesWritten + extra);
   1.411 +
   1.412 +    SkTScopedComPtr<IStream> newIStream;
   1.413 +    SkIStream::CreateFromSkStream(newStream.detach(), true, &newIStream);
   1.414 +
   1.415 +    XPS_FONT_EMBEDDING embedding;
   1.416 +    HRM(current->xpsFont->GetEmbeddingOption(&embedding),
   1.417 +        "Could not get embedding option from font.");
   1.418 +
   1.419 +    SkTScopedComPtr<IOpcPartUri> partUri;
   1.420 +    HRM(current->xpsFont->GetPartName(&partUri),
   1.421 +        "Could not get part uri from font.");
   1.422 +
   1.423 +    HRM(current->xpsFont->SetContent(
   1.424 +            newIStream.get(),
   1.425 +            embedding,
   1.426 +            partUri.get()),
   1.427 +        "Could not set new stream for subsetted font.");
   1.428 +
   1.429 +    return S_OK;
   1.430 +}
   1.431 +
   1.432 +bool SkXPSDevice::endPortfolio() {
   1.433 +    //Subset fonts
   1.434 +    if (!this->fTypefaces.empty()) {
   1.435 +        SkXPSDevice::TypefaceUse* current = &this->fTypefaces.front();
   1.436 +        const TypefaceUse* last = &this->fTypefaces.back();
   1.437 +        for (; current <= last; ++current) {
   1.438 +            //Ignore return for now, if it didn't subset, let it be.
   1.439 +            subset_typeface(current);
   1.440 +        }
   1.441 +    }
   1.442 +
   1.443 +    HRBM(this->fPackageWriter->Close(), "Could not close writer.");
   1.444 +
   1.445 +    return true;
   1.446 +}
   1.447 +
   1.448 +static XPS_COLOR xps_color(const SkColor skColor) {
   1.449 +    //XPS uses non-pre-multiplied alpha (XPS Spec 11.4).
   1.450 +    XPS_COLOR xpsColor;
   1.451 +    xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
   1.452 +    xpsColor.value.sRGB.alpha = SkColorGetA(skColor);
   1.453 +    xpsColor.value.sRGB.red = SkColorGetR(skColor);
   1.454 +    xpsColor.value.sRGB.green = SkColorGetG(skColor);
   1.455 +    xpsColor.value.sRGB.blue = SkColorGetB(skColor);
   1.456 +
   1.457 +    return xpsColor;
   1.458 +}
   1.459 +
   1.460 +static XPS_POINT xps_point(const SkPoint& point) {
   1.461 +    XPS_POINT xpsPoint = {
   1.462 +        SkScalarToFLOAT(point.fX),
   1.463 +        SkScalarToFLOAT(point.fY),
   1.464 +    };
   1.465 +    return xpsPoint;
   1.466 +}
   1.467 +
   1.468 +static XPS_POINT xps_point(const SkPoint& point, const SkMatrix& matrix) {
   1.469 +    SkPoint skTransformedPoint;
   1.470 +    matrix.mapXY(point.fX, point.fY, &skTransformedPoint);
   1.471 +    return xps_point(skTransformedPoint);
   1.472 +}
   1.473 +
   1.474 +static XPS_SPREAD_METHOD xps_spread_method(SkShader::TileMode tileMode) {
   1.475 +    switch (tileMode) {
   1.476 +    case SkShader::kClamp_TileMode:
   1.477 +        return XPS_SPREAD_METHOD_PAD;
   1.478 +    case SkShader::kRepeat_TileMode:
   1.479 +        return XPS_SPREAD_METHOD_REPEAT;
   1.480 +    case SkShader::kMirror_TileMode:
   1.481 +        return XPS_SPREAD_METHOD_REFLECT;
   1.482 +    default:
   1.483 +        SkDEBUGFAIL("Unknown tile mode.");
   1.484 +    }
   1.485 +    return XPS_SPREAD_METHOD_PAD;
   1.486 +}
   1.487 +
   1.488 +static void transform_offsets(SkScalar* stopOffsets, const int numOffsets,
   1.489 +                              const SkPoint& start, const SkPoint& end,
   1.490 +                              const SkMatrix& transform) {
   1.491 +    SkPoint startTransformed;
   1.492 +    transform.mapXY(start.fX, start.fY, &startTransformed);
   1.493 +    SkPoint endTransformed;
   1.494 +    transform.mapXY(end.fX, end.fY, &endTransformed);
   1.495 +
   1.496 +    //Manhattan distance between transformed start and end.
   1.497 +    SkScalar startToEnd = (endTransformed.fX - startTransformed.fX)
   1.498 +                        + (endTransformed.fY - startTransformed.fY);
   1.499 +    if (SkScalarNearlyZero(startToEnd)) {
   1.500 +        for (int i = 0; i < numOffsets; ++i) {
   1.501 +            stopOffsets[i] = 0;
   1.502 +        }
   1.503 +        return;
   1.504 +    }
   1.505 +
   1.506 +    for (int i = 0; i < numOffsets; ++i) {
   1.507 +        SkPoint stop;
   1.508 +        stop.fX = SkScalarMul(end.fX - start.fX, stopOffsets[i]);
   1.509 +        stop.fY = SkScalarMul(end.fY - start.fY, stopOffsets[i]);
   1.510 +
   1.511 +        SkPoint stopTransformed;
   1.512 +        transform.mapXY(stop.fX, stop.fY, &stopTransformed);
   1.513 +
   1.514 +        //Manhattan distance between transformed start and stop.
   1.515 +        SkScalar startToStop = (stopTransformed.fX - startTransformed.fX)
   1.516 +                             + (stopTransformed.fY - startTransformed.fY);
   1.517 +        //Percentage along transformed line.
   1.518 +        stopOffsets[i] = SkScalarDiv(startToStop, startToEnd);
   1.519 +    }
   1.520 +}
   1.521 +
   1.522 +HRESULT SkXPSDevice::createXpsTransform(const SkMatrix& matrix,
   1.523 +                                        IXpsOMMatrixTransform** xpsTransform) {
   1.524 +    SkScalar affine[6];
   1.525 +    if (!matrix.asAffine(affine)) {
   1.526 +        *xpsTransform = NULL;
   1.527 +        return S_FALSE;
   1.528 +    }
   1.529 +    XPS_MATRIX rawXpsMatrix = {
   1.530 +        SkScalarToFLOAT(affine[SkMatrix::kAScaleX]),
   1.531 +        SkScalarToFLOAT(affine[SkMatrix::kASkewY]),
   1.532 +        SkScalarToFLOAT(affine[SkMatrix::kASkewX]),
   1.533 +        SkScalarToFLOAT(affine[SkMatrix::kAScaleY]),
   1.534 +        SkScalarToFLOAT(affine[SkMatrix::kATransX]),
   1.535 +        SkScalarToFLOAT(affine[SkMatrix::kATransY]),
   1.536 +    };
   1.537 +    HRM(this->fXpsFactory->CreateMatrixTransform(&rawXpsMatrix, xpsTransform),
   1.538 +        "Could not create transform.");
   1.539 +
   1.540 +    return S_OK;
   1.541 +}
   1.542 +
   1.543 +HRESULT SkXPSDevice::createPath(IXpsOMGeometryFigure* figure,
   1.544 +                                IXpsOMVisualCollection* visuals,
   1.545 +                                IXpsOMPath** path) {
   1.546 +    SkTScopedComPtr<IXpsOMGeometry> geometry;
   1.547 +    HRM(this->fXpsFactory->CreateGeometry(&geometry),
   1.548 +        "Could not create geometry.");
   1.549 +
   1.550 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> figureCollection;
   1.551 +    HRM(geometry->GetFigures(&figureCollection), "Could not get figures.");
   1.552 +    HRM(figureCollection->Append(figure), "Could not add figure.");
   1.553 +
   1.554 +    HRM(this->fXpsFactory->CreatePath(path), "Could not create path.");
   1.555 +    HRM((*path)->SetGeometryLocal(geometry.get()), "Could not set geometry");
   1.556 +
   1.557 +    HRM(visuals->Append(*path), "Could not add path to visuals.");
   1.558 +    return S_OK;
   1.559 +}
   1.560 +
   1.561 +HRESULT SkXPSDevice::createXpsSolidColorBrush(const SkColor skColor,
   1.562 +                                              const SkAlpha alpha,
   1.563 +                                              IXpsOMBrush** xpsBrush) {
   1.564 +    XPS_COLOR xpsColor = xps_color(skColor);
   1.565 +    SkTScopedComPtr<IXpsOMSolidColorBrush> solidBrush;
   1.566 +    HRM(this->fXpsFactory->CreateSolidColorBrush(&xpsColor, NULL, &solidBrush),
   1.567 +        "Could not create solid color brush.");
   1.568 +    HRM(solidBrush->SetOpacity(alpha / 255.0f), "Could not set opacity.");
   1.569 +    HRM(solidBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI Fail.");
   1.570 +    return S_OK;
   1.571 +}
   1.572 +
   1.573 +HRESULT SkXPSDevice::sideOfClamp(const SkRect& areaToFill,
   1.574 +                                 const XPS_RECT& imageViewBox,
   1.575 +                                 IXpsOMImageResource* image,
   1.576 +                                 IXpsOMVisualCollection* visuals) {
   1.577 +    SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
   1.578 +    HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
   1.579 +
   1.580 +    SkTScopedComPtr<IXpsOMPath> areaToFillPath;
   1.581 +    HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
   1.582 +
   1.583 +    SkTScopedComPtr<IXpsOMImageBrush> areaToFillBrush;
   1.584 +    HRM(this->fXpsFactory->CreateImageBrush(image,
   1.585 +                                            &imageViewBox,
   1.586 +                                            &imageViewBox,
   1.587 +                                            &areaToFillBrush),
   1.588 +        "Could not create brush for side of clamp.");
   1.589 +    HRM(areaToFillBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
   1.590 +        "Could not set tile mode for side of clamp.");
   1.591 +    HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
   1.592 +        "Could not set brush for side of clamp");
   1.593 +
   1.594 +    return S_OK;
   1.595 +}
   1.596 +
   1.597 +HRESULT SkXPSDevice::cornerOfClamp(const SkRect& areaToFill,
   1.598 +                                   const SkColor color,
   1.599 +                                   IXpsOMVisualCollection* visuals) {
   1.600 +    SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
   1.601 +    HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
   1.602 +
   1.603 +    SkTScopedComPtr<IXpsOMPath> areaToFillPath;
   1.604 +    HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
   1.605 +
   1.606 +    SkTScopedComPtr<IXpsOMBrush> areaToFillBrush;
   1.607 +    HR(this->createXpsSolidColorBrush(color, 0xFF, &areaToFillBrush));
   1.608 +    HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
   1.609 +        "Could not set brush for corner of clamp.");
   1.610 +
   1.611 +    return S_OK;
   1.612 +}
   1.613 +
   1.614 +static const XPS_TILE_MODE XTM_N  = XPS_TILE_MODE_NONE;
   1.615 +static const XPS_TILE_MODE XTM_T  = XPS_TILE_MODE_TILE;
   1.616 +static const XPS_TILE_MODE XTM_X  = XPS_TILE_MODE_FLIPX;
   1.617 +static const XPS_TILE_MODE XTM_Y  = XPS_TILE_MODE_FLIPY;
   1.618 +static const XPS_TILE_MODE XTM_XY = XPS_TILE_MODE_FLIPXY;
   1.619 +
   1.620 +//TODO(bungeman): In the future, should skia add None,
   1.621 +//handle None+Mirror and None+Repeat correctly.
   1.622 +//None is currently an internal hack so masks don't repeat (None+None only).
   1.623 +static XPS_TILE_MODE SkToXpsTileMode[SkShader::kTileModeCount+1]
   1.624 +                                    [SkShader::kTileModeCount+1] = {
   1.625 +           //Clamp //Repeat //Mirror //None
   1.626 +/*Clamp */ XTM_N,  XTM_T,   XTM_Y,   XTM_N,
   1.627 +/*Repeat*/ XTM_T,  XTM_T,   XTM_Y,   XTM_N,
   1.628 +/*Mirror*/ XTM_X,  XTM_X,   XTM_XY,  XTM_X,
   1.629 +/*None  */ XTM_N,  XTM_N,   XTM_Y,   XTM_N,
   1.630 +};
   1.631 +
   1.632 +HRESULT SkXPSDevice::createXpsImageBrush(
   1.633 +        const SkBitmap& bitmap,
   1.634 +        const SkMatrix& localMatrix,
   1.635 +        const SkShader::TileMode (&xy)[2],
   1.636 +        const SkAlpha alpha,
   1.637 +        IXpsOMTileBrush** xpsBrush) {
   1.638 +    SkDynamicMemoryWStream write;
   1.639 +    if (!SkImageEncoder::EncodeStream(&write, bitmap,
   1.640 +                                      SkImageEncoder::kPNG_Type, 100)) {
   1.641 +        HRM(E_FAIL, "Unable to encode bitmap as png.");
   1.642 +    }
   1.643 +    SkMemoryStream* read = new SkMemoryStream;
   1.644 +    read->setData(write.copyToData())->unref();
   1.645 +    SkTScopedComPtr<IStream> readWrapper;
   1.646 +    HRM(SkIStream::CreateFromSkStream(read, true, &readWrapper),
   1.647 +        "Could not create stream from png data.");
   1.648 +
   1.649 +    const size_t size =
   1.650 +        SK_ARRAY_COUNT(L"/Documents/1/Resources/Images/" L_GUID_ID L".png");
   1.651 +    wchar_t buffer[size];
   1.652 +    wchar_t id[GUID_ID_LEN];
   1.653 +    HR(create_id(id, GUID_ID_LEN));
   1.654 +    swprintf_s(buffer, size, L"/Documents/1/Resources/Images/%s.png", id);
   1.655 +
   1.656 +    SkTScopedComPtr<IOpcPartUri> imagePartUri;
   1.657 +    HRM(this->fXpsFactory->CreatePartUri(buffer, &imagePartUri),
   1.658 +        "Could not create image part uri.");
   1.659 +
   1.660 +    SkTScopedComPtr<IXpsOMImageResource> imageResource;
   1.661 +    HRM(this->fXpsFactory->CreateImageResource(
   1.662 +            readWrapper.get(),
   1.663 +            XPS_IMAGE_TYPE_PNG,
   1.664 +            imagePartUri.get(),
   1.665 +            &imageResource),
   1.666 +        "Could not create image resource.");
   1.667 +
   1.668 +    XPS_RECT bitmapRect = {
   1.669 +        0.0, 0.0,
   1.670 +        static_cast<FLOAT>(bitmap.width()), static_cast<FLOAT>(bitmap.height())
   1.671 +    };
   1.672 +    SkTScopedComPtr<IXpsOMImageBrush> xpsImageBrush;
   1.673 +    HRM(this->fXpsFactory->CreateImageBrush(imageResource.get(),
   1.674 +                                            &bitmapRect, &bitmapRect,
   1.675 +                                            &xpsImageBrush),
   1.676 +        "Could not create image brush.");
   1.677 +
   1.678 +    if (SkShader::kClamp_TileMode != xy[0] &&
   1.679 +        SkShader::kClamp_TileMode != xy[1]) {
   1.680 +
   1.681 +        HRM(xpsImageBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]),
   1.682 +            "Could not set image tile mode");
   1.683 +        HRM(xpsImageBrush->SetOpacity(alpha / 255.0f),
   1.684 +            "Could not set image opacity.");
   1.685 +        HRM(xpsImageBrush->QueryInterface(xpsBrush), "QI failed.");
   1.686 +    } else {
   1.687 +        //TODO(bungeman): compute how big this really needs to be.
   1.688 +        const SkScalar BIG = SkIntToScalar(1000); //SK_ScalarMax;
   1.689 +        const FLOAT BIG_F = SkScalarToFLOAT(BIG);
   1.690 +        const SkScalar bWidth = SkIntToScalar(bitmap.width());
   1.691 +        const SkScalar bHeight = SkIntToScalar(bitmap.height());
   1.692 +
   1.693 +        //create brush canvas
   1.694 +        SkTScopedComPtr<IXpsOMCanvas> brushCanvas;
   1.695 +        HRM(this->fXpsFactory->CreateCanvas(&brushCanvas),
   1.696 +            "Could not create image brush canvas.");
   1.697 +        SkTScopedComPtr<IXpsOMVisualCollection> brushVisuals;
   1.698 +        HRM(brushCanvas->GetVisuals(&brushVisuals),
   1.699 +            "Could not get image brush canvas visuals collection.");
   1.700 +
   1.701 +        //create central figure
   1.702 +        const SkRect bitmapPoints = SkRect::MakeLTRB(0, 0, bWidth, bHeight);
   1.703 +        SkTScopedComPtr<IXpsOMGeometryFigure> centralFigure;
   1.704 +        HR(this->createXpsRect(bitmapPoints, FALSE, TRUE, &centralFigure));
   1.705 +
   1.706 +        SkTScopedComPtr<IXpsOMPath> centralPath;
   1.707 +        HR(this->createPath(centralFigure.get(),
   1.708 +                            brushVisuals.get(),
   1.709 +                            &centralPath));
   1.710 +        HRM(xpsImageBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
   1.711 +            "Could not set tile mode for image brush central path.");
   1.712 +        HRM(centralPath->SetFillBrushLocal(xpsImageBrush.get()),
   1.713 +            "Could not set fill brush for image brush central path.");
   1.714 +
   1.715 +        //add left/right
   1.716 +        if (SkShader::kClamp_TileMode == xy[0]) {
   1.717 +            SkRect leftArea = SkRect::MakeLTRB(-BIG, 0, 0, bHeight);
   1.718 +            XPS_RECT leftImageViewBox = {
   1.719 +                0.0, 0.0,
   1.720 +                1.0, static_cast<FLOAT>(bitmap.height()),
   1.721 +            };
   1.722 +            HR(this->sideOfClamp(leftArea, leftImageViewBox,
   1.723 +                                 imageResource.get(),
   1.724 +                                 brushVisuals.get()));
   1.725 +
   1.726 +            SkRect rightArea = SkRect::MakeLTRB(bWidth, 0, BIG, bHeight);
   1.727 +            XPS_RECT rightImageViewBox = {
   1.728 +                bitmap.width() - 1.0f, 0.0f,
   1.729 +                1.0f, static_cast<FLOAT>(bitmap.height()),
   1.730 +            };
   1.731 +            HR(this->sideOfClamp(rightArea, rightImageViewBox,
   1.732 +                                 imageResource.get(),
   1.733 +                                 brushVisuals.get()));
   1.734 +        }
   1.735 +
   1.736 +        //add top/bottom
   1.737 +        if (SkShader::kClamp_TileMode == xy[1]) {
   1.738 +            SkRect topArea = SkRect::MakeLTRB(0, -BIG, bWidth, 0);
   1.739 +            XPS_RECT topImageViewBox = {
   1.740 +                0.0, 0.0,
   1.741 +                static_cast<FLOAT>(bitmap.width()), 1.0,
   1.742 +            };
   1.743 +            HR(this->sideOfClamp(topArea, topImageViewBox,
   1.744 +                                 imageResource.get(),
   1.745 +                                 brushVisuals.get()));
   1.746 +
   1.747 +            SkRect bottomArea = SkRect::MakeLTRB(0, bHeight, bWidth, BIG);
   1.748 +            XPS_RECT bottomImageViewBox = {
   1.749 +                0.0f, bitmap.height() - 1.0f,
   1.750 +                static_cast<FLOAT>(bitmap.width()), 1.0f,
   1.751 +            };
   1.752 +            HR(this->sideOfClamp(bottomArea, bottomImageViewBox,
   1.753 +                                 imageResource.get(),
   1.754 +                                 brushVisuals.get()));
   1.755 +        }
   1.756 +
   1.757 +        //add tl, tr, bl, br
   1.758 +        if (SkShader::kClamp_TileMode == xy[0] &&
   1.759 +            SkShader::kClamp_TileMode == xy[1]) {
   1.760 +
   1.761 +            SkAutoLockPixels alp(bitmap);
   1.762 +
   1.763 +            const SkColor tlColor = bitmap.getColor(0,0);
   1.764 +            const SkRect tlArea = SkRect::MakeLTRB(-BIG, -BIG, 0, 0);
   1.765 +            HR(this->cornerOfClamp(tlArea, tlColor, brushVisuals.get()));
   1.766 +
   1.767 +            const SkColor trColor = bitmap.getColor(bitmap.width()-1,0);
   1.768 +            const SkRect trArea = SkRect::MakeLTRB(bWidth, -BIG, BIG, 0);
   1.769 +            HR(this->cornerOfClamp(trArea, trColor, brushVisuals.get()));
   1.770 +
   1.771 +            const SkColor brColor = bitmap.getColor(bitmap.width()-1,
   1.772 +                                                    bitmap.height()-1);
   1.773 +            const SkRect brArea = SkRect::MakeLTRB(bWidth, bHeight, BIG, BIG);
   1.774 +            HR(this->cornerOfClamp(brArea, brColor, brushVisuals.get()));
   1.775 +
   1.776 +            const SkColor blColor = bitmap.getColor(0,bitmap.height()-1);
   1.777 +            const SkRect blArea = SkRect::MakeLTRB(-BIG, bHeight, 0, BIG);
   1.778 +            HR(this->cornerOfClamp(blArea, blColor, brushVisuals.get()));
   1.779 +        }
   1.780 +
   1.781 +        //create visual brush from canvas
   1.782 +        XPS_RECT bound = {};
   1.783 +        if (SkShader::kClamp_TileMode == xy[0] &&
   1.784 +            SkShader::kClamp_TileMode == xy[1]) {
   1.785 +
   1.786 +            bound.x = BIG_F / -2;
   1.787 +            bound.y = BIG_F / -2;
   1.788 +            bound.width = BIG_F;
   1.789 +            bound.height = BIG_F;
   1.790 +        } else if (SkShader::kClamp_TileMode == xy[0]) {
   1.791 +            bound.x = BIG_F / -2;
   1.792 +            bound.y = 0.0f;
   1.793 +            bound.width = BIG_F;
   1.794 +            bound.height = static_cast<FLOAT>(bitmap.height());
   1.795 +        } else if (SkShader::kClamp_TileMode == xy[1]) {
   1.796 +            bound.x = 0;
   1.797 +            bound.y = BIG_F / -2;
   1.798 +            bound.width = static_cast<FLOAT>(bitmap.width());
   1.799 +            bound.height = BIG_F;
   1.800 +        }
   1.801 +        SkTScopedComPtr<IXpsOMVisualBrush> clampBrush;
   1.802 +        HRM(this->fXpsFactory->CreateVisualBrush(&bound, &bound, &clampBrush),
   1.803 +            "Could not create visual brush for image brush.");
   1.804 +        HRM(clampBrush->SetVisualLocal(brushCanvas.get()),
   1.805 +            "Could not set canvas on visual brush for image brush.");
   1.806 +        HRM(clampBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]),
   1.807 +            "Could not set tile mode on visual brush for image brush.");
   1.808 +        HRM(clampBrush->SetOpacity(alpha / 255.0f),
   1.809 +            "Could not set opacity on visual brush for image brush.");
   1.810 +
   1.811 +        HRM(clampBrush->QueryInterface(xpsBrush), "QI failed.");
   1.812 +    }
   1.813 +
   1.814 +    SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
   1.815 +    HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
   1.816 +    if (NULL != xpsMatrixToUse.get()) {
   1.817 +        HRM((*xpsBrush)->SetTransformLocal(xpsMatrixToUse.get()),
   1.818 +            "Could not set transform for image brush.");
   1.819 +    } else {
   1.820 +        //TODO(bungeman): perspective bitmaps in general.
   1.821 +    }
   1.822 +
   1.823 +    return S_OK;
   1.824 +}
   1.825 +
   1.826 +HRESULT SkXPSDevice::createXpsGradientStop(const SkColor skColor,
   1.827 +                                           const SkScalar offset,
   1.828 +                                           IXpsOMGradientStop** xpsGradStop) {
   1.829 +    XPS_COLOR gradStopXpsColor = xps_color(skColor);
   1.830 +    HRM(this->fXpsFactory->CreateGradientStop(&gradStopXpsColor,
   1.831 +                                              NULL,
   1.832 +                                              SkScalarToFLOAT(offset),
   1.833 +                                              xpsGradStop),
   1.834 +        "Could not create gradient stop.");
   1.835 +    return S_OK;
   1.836 +}
   1.837 +
   1.838 +HRESULT SkXPSDevice::createXpsLinearGradient(SkShader::GradientInfo info,
   1.839 +                                             const SkAlpha alpha,
   1.840 +                                             const SkMatrix& localMatrix,
   1.841 +                                             IXpsOMMatrixTransform* xpsMatrix,
   1.842 +                                             IXpsOMBrush** xpsBrush) {
   1.843 +    XPS_POINT startPoint;
   1.844 +    XPS_POINT endPoint;
   1.845 +    if (NULL != xpsMatrix) {
   1.846 +        startPoint = xps_point(info.fPoint[0]);
   1.847 +        endPoint = xps_point(info.fPoint[1]);
   1.848 +    } else {
   1.849 +        transform_offsets(info.fColorOffsets, info.fColorCount,
   1.850 +                          info.fPoint[0], info.fPoint[1],
   1.851 +                          localMatrix);
   1.852 +        startPoint = xps_point(info.fPoint[0], localMatrix);
   1.853 +        endPoint = xps_point(info.fPoint[1], localMatrix);
   1.854 +    }
   1.855 +
   1.856 +    SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
   1.857 +    HR(createXpsGradientStop(info.fColors[0],
   1.858 +                             info.fColorOffsets[0],
   1.859 +                             &gradStop0));
   1.860 +
   1.861 +    SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
   1.862 +    HR(createXpsGradientStop(info.fColors[1],
   1.863 +                             info.fColorOffsets[1],
   1.864 +                             &gradStop1));
   1.865 +
   1.866 +    SkTScopedComPtr<IXpsOMLinearGradientBrush> gradientBrush;
   1.867 +    HRM(this->fXpsFactory->CreateLinearGradientBrush(gradStop0.get(),
   1.868 +                                                     gradStop1.get(),
   1.869 +                                                     &startPoint,
   1.870 +                                                     &endPoint,
   1.871 +                                                     &gradientBrush),
   1.872 +        "Could not create linear gradient brush.");
   1.873 +    if (NULL != xpsMatrix) {
   1.874 +        HRM(gradientBrush->SetTransformLocal(xpsMatrix),
   1.875 +            "Could not set transform on linear gradient brush.");
   1.876 +    }
   1.877 +
   1.878 +    SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
   1.879 +    HRM(gradientBrush->GetGradientStops(&gradStopCollection),
   1.880 +        "Could not get linear gradient stop collection.");
   1.881 +    for (int i = 2; i < info.fColorCount; ++i) {
   1.882 +        SkTScopedComPtr<IXpsOMGradientStop> gradStop;
   1.883 +        HR(createXpsGradientStop(info.fColors[i],
   1.884 +                                 info.fColorOffsets[i],
   1.885 +                                 &gradStop));
   1.886 +        HRM(gradStopCollection->Append(gradStop.get()),
   1.887 +            "Could not add linear gradient stop.");
   1.888 +    }
   1.889 +
   1.890 +    HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)),
   1.891 +        "Could not set spread method of linear gradient.");
   1.892 +
   1.893 +    HRM(gradientBrush->SetOpacity(alpha / 255.0f),
   1.894 +        "Could not set opacity of linear gradient brush.");
   1.895 +    HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed");
   1.896 +
   1.897 +    return S_OK;
   1.898 +}
   1.899 +
   1.900 +HRESULT SkXPSDevice::createXpsRadialGradient(SkShader::GradientInfo info,
   1.901 +                                             const SkAlpha alpha,
   1.902 +                                             const SkMatrix& localMatrix,
   1.903 +                                             IXpsOMMatrixTransform* xpsMatrix,
   1.904 +                                             IXpsOMBrush** xpsBrush) {
   1.905 +    SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
   1.906 +    HR(createXpsGradientStop(info.fColors[0],
   1.907 +                             info.fColorOffsets[0],
   1.908 +                             &gradStop0));
   1.909 +
   1.910 +    SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
   1.911 +    HR(createXpsGradientStop(info.fColors[1],
   1.912 +                             info.fColorOffsets[1],
   1.913 +                             &gradStop1));
   1.914 +
   1.915 +    //TODO: figure out how to fake better if not affine
   1.916 +    XPS_POINT centerPoint;
   1.917 +    XPS_POINT gradientOrigin;
   1.918 +    XPS_SIZE radiiSizes;
   1.919 +    if (NULL != xpsMatrix) {
   1.920 +        centerPoint = xps_point(info.fPoint[0]);
   1.921 +        gradientOrigin = xps_point(info.fPoint[0]);
   1.922 +        radiiSizes.width = SkScalarToFLOAT(info.fRadius[0]);
   1.923 +        radiiSizes.height = SkScalarToFLOAT(info.fRadius[0]);
   1.924 +    } else {
   1.925 +        centerPoint = xps_point(info.fPoint[0], localMatrix);
   1.926 +        gradientOrigin = xps_point(info.fPoint[0], localMatrix);
   1.927 +
   1.928 +        SkScalar radius = info.fRadius[0];
   1.929 +        SkVector vec[2];
   1.930 +
   1.931 +        vec[0].set(radius, 0);
   1.932 +        vec[1].set(0, radius);
   1.933 +        localMatrix.mapVectors(vec, 2);
   1.934 +
   1.935 +        SkScalar d0 = vec[0].length();
   1.936 +        SkScalar d1 = vec[1].length();
   1.937 +
   1.938 +        radiiSizes.width = SkScalarToFLOAT(d0);
   1.939 +        radiiSizes.height = SkScalarToFLOAT(d1);
   1.940 +    }
   1.941 +
   1.942 +    SkTScopedComPtr<IXpsOMRadialGradientBrush> gradientBrush;
   1.943 +    HRM(this->fXpsFactory->CreateRadialGradientBrush(gradStop0.get(),
   1.944 +                                                     gradStop1.get(),
   1.945 +                                                     &centerPoint,
   1.946 +                                                     &gradientOrigin,
   1.947 +                                                     &radiiSizes,
   1.948 +                                                     &gradientBrush),
   1.949 +        "Could not create radial gradient brush.");
   1.950 +    if (NULL != xpsMatrix) {
   1.951 +        HRM(gradientBrush->SetTransformLocal(xpsMatrix),
   1.952 +            "Could not set transform on radial gradient brush.");
   1.953 +    }
   1.954 +
   1.955 +    SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
   1.956 +    HRM(gradientBrush->GetGradientStops(&gradStopCollection),
   1.957 +        "Could not get radial gradient stop collection.");
   1.958 +    for (int i = 2; i < info.fColorCount; ++i) {
   1.959 +        SkTScopedComPtr<IXpsOMGradientStop> gradStop;
   1.960 +        HR(createXpsGradientStop(info.fColors[i],
   1.961 +                                 info.fColorOffsets[i],
   1.962 +                                 &gradStop));
   1.963 +        HRM(gradStopCollection->Append(gradStop.get()),
   1.964 +            "Could not add radial gradient stop.");
   1.965 +    }
   1.966 +
   1.967 +    HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)),
   1.968 +        "Could not set spread method of radial gradient.");
   1.969 +
   1.970 +    HRM(gradientBrush->SetOpacity(alpha / 255.0f),
   1.971 +        "Could not set opacity of radial gradient brush.");
   1.972 +    HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed.");
   1.973 +
   1.974 +    return S_OK;
   1.975 +}
   1.976 +
   1.977 +HRESULT SkXPSDevice::createXpsBrush(const SkPaint& skPaint,
   1.978 +                                    IXpsOMBrush** brush,
   1.979 +                                    const SkMatrix* parentTransform) {
   1.980 +    const SkShader *shader = skPaint.getShader();
   1.981 +    if (NULL == shader) {
   1.982 +        HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
   1.983 +        return S_OK;
   1.984 +    }
   1.985 +
   1.986 +    //Gradient shaders.
   1.987 +    SkShader::GradientInfo info;
   1.988 +    info.fColorCount = 0;
   1.989 +    info.fColors = NULL;
   1.990 +    info.fColorOffsets = NULL;
   1.991 +    SkShader::GradientType gradientType = shader->asAGradient(&info);
   1.992 +
   1.993 +    if (SkShader::kNone_GradientType == gradientType) {
   1.994 +        //Nothing to see, move along.
   1.995 +
   1.996 +    } else if (SkShader::kColor_GradientType == gradientType) {
   1.997 +        SkASSERT(1 == info.fColorCount);
   1.998 +        SkColor color;
   1.999 +        info.fColors = &color;
  1.1000 +        shader->asAGradient(&info);
  1.1001 +        SkAlpha alpha = skPaint.getAlpha();
  1.1002 +        HR(this->createXpsSolidColorBrush(color, alpha, brush));
  1.1003 +        return S_OK;
  1.1004 +
  1.1005 +    } else {
  1.1006 +        if (info.fColorCount == 0) {
  1.1007 +            const SkColor color = skPaint.getColor();
  1.1008 +            HR(this->createXpsSolidColorBrush(color, 0xFF, brush));
  1.1009 +            return S_OK;
  1.1010 +        }
  1.1011 +
  1.1012 +        SkAutoTArray<SkColor> colors(info.fColorCount);
  1.1013 +        SkAutoTArray<SkScalar> colorOffsets(info.fColorCount);
  1.1014 +        info.fColors = colors.get();
  1.1015 +        info.fColorOffsets = colorOffsets.get();
  1.1016 +        shader->asAGradient(&info);
  1.1017 +
  1.1018 +        if (1 == info.fColorCount) {
  1.1019 +            SkColor color = info.fColors[0];
  1.1020 +            SkAlpha alpha = skPaint.getAlpha();
  1.1021 +            HR(this->createXpsSolidColorBrush(color, alpha, brush));
  1.1022 +            return S_OK;
  1.1023 +        }
  1.1024 +
  1.1025 +        SkMatrix localMatrix = shader->getLocalMatrix();
  1.1026 +        if (NULL != parentTransform) {
  1.1027 +            localMatrix.preConcat(*parentTransform);
  1.1028 +        }
  1.1029 +        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
  1.1030 +        HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
  1.1031 +
  1.1032 +        if (SkShader::kLinear_GradientType == gradientType) {
  1.1033 +            HR(this->createXpsLinearGradient(info,
  1.1034 +                                             skPaint.getAlpha(),
  1.1035 +                                             localMatrix,
  1.1036 +                                             xpsMatrixToUse.get(),
  1.1037 +                                             brush));
  1.1038 +            return S_OK;
  1.1039 +        }
  1.1040 +
  1.1041 +        if (SkShader::kRadial_GradientType == gradientType) {
  1.1042 +            HR(this->createXpsRadialGradient(info,
  1.1043 +                                             skPaint.getAlpha(),
  1.1044 +                                             localMatrix,
  1.1045 +                                             xpsMatrixToUse.get(),
  1.1046 +                                             brush));
  1.1047 +            return S_OK;
  1.1048 +        }
  1.1049 +
  1.1050 +        if (SkShader::kRadial2_GradientType == gradientType ||
  1.1051 +            SkShader::kConical_GradientType == gradientType) {
  1.1052 +            //simple if affine and one is 0, otherwise will have to fake
  1.1053 +        }
  1.1054 +
  1.1055 +        if (SkShader::kSweep_GradientType == gradientType) {
  1.1056 +            //have to fake
  1.1057 +        }
  1.1058 +    }
  1.1059 +
  1.1060 +    SkBitmap outTexture;
  1.1061 +    SkMatrix outMatrix;
  1.1062 +    SkShader::TileMode xy[2];
  1.1063 +    SkShader::BitmapType bitmapType = shader->asABitmap(&outTexture,
  1.1064 +                                                        &outMatrix,
  1.1065 +                                                        xy);
  1.1066 +    switch (bitmapType) {
  1.1067 +        case SkShader::kNone_BitmapType:
  1.1068 +            break;
  1.1069 +        case SkShader::kDefault_BitmapType: {
  1.1070 +            //TODO: outMatrix??
  1.1071 +            SkMatrix localMatrix = shader->getLocalMatrix();
  1.1072 +            if (NULL != parentTransform) {
  1.1073 +                localMatrix.preConcat(*parentTransform);
  1.1074 +            }
  1.1075 +
  1.1076 +            SkTScopedComPtr<IXpsOMTileBrush> tileBrush;
  1.1077 +            HR(this->createXpsImageBrush(outTexture,
  1.1078 +                                         localMatrix,
  1.1079 +                                         xy,
  1.1080 +                                         skPaint.getAlpha(),
  1.1081 +                                         &tileBrush));
  1.1082 +
  1.1083 +            HRM(tileBrush->QueryInterface<IXpsOMBrush>(brush), "QI failed.");
  1.1084 +
  1.1085 +            return S_OK;
  1.1086 +        }
  1.1087 +        case SkShader::kRadial_BitmapType:
  1.1088 +        case SkShader::kSweep_BitmapType:
  1.1089 +        case SkShader::kTwoPointRadial_BitmapType:
  1.1090 +        default:
  1.1091 +            break;
  1.1092 +    }
  1.1093 +
  1.1094 +    HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
  1.1095 +    return S_OK;
  1.1096 +}
  1.1097 +
  1.1098 +static bool rect_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
  1.1099 +    const bool zeroWidth = (0 == paint.getStrokeWidth());
  1.1100 +    const bool stroke = (SkPaint::kFill_Style != paint.getStyle());
  1.1101 +
  1.1102 +    return paint.getPathEffect() ||
  1.1103 +           paint.getMaskFilter() ||
  1.1104 +           paint.getRasterizer() ||
  1.1105 +           (stroke && (
  1.1106 +               (matrix.hasPerspective() && !zeroWidth) ||
  1.1107 +               SkPaint::kMiter_Join != paint.getStrokeJoin() ||
  1.1108 +               (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
  1.1109 +                paint.getStrokeMiter() < SK_ScalarSqrt2)
  1.1110 +           ))
  1.1111 +    ;
  1.1112 +}
  1.1113 +
  1.1114 +HRESULT SkXPSDevice::createXpsRect(const SkRect& rect, BOOL stroke, BOOL fill,
  1.1115 +                                   IXpsOMGeometryFigure** xpsRect) {
  1.1116 +    const SkPoint points[4] = {
  1.1117 +        { rect.fLeft, rect.fTop },
  1.1118 +        { rect.fRight, rect.fTop },
  1.1119 +        { rect.fRight, rect.fBottom },
  1.1120 +        { rect.fLeft, rect.fBottom },
  1.1121 +    };
  1.1122 +    return this->createXpsQuad(points, stroke, fill, xpsRect);
  1.1123 +}
  1.1124 +HRESULT SkXPSDevice::createXpsQuad(const SkPoint (&points)[4],
  1.1125 +                                   BOOL stroke, BOOL fill,
  1.1126 +                                   IXpsOMGeometryFigure** xpsQuad) {
  1.1127 +    // Define the start point.
  1.1128 +    XPS_POINT startPoint = xps_point(points[0]);
  1.1129 +
  1.1130 +    // Create the figure.
  1.1131 +    HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint, xpsQuad),
  1.1132 +        "Could not create quad geometry figure.");
  1.1133 +
  1.1134 +    // Define the type of each segment.
  1.1135 +    XPS_SEGMENT_TYPE segmentTypes[3] = {
  1.1136 +        XPS_SEGMENT_TYPE_LINE,
  1.1137 +        XPS_SEGMENT_TYPE_LINE,
  1.1138 +        XPS_SEGMENT_TYPE_LINE,
  1.1139 +    };
  1.1140 +
  1.1141 +    // Define the x and y coordinates of each corner of the figure.
  1.1142 +    FLOAT segmentData[6] = {
  1.1143 +        SkScalarToFLOAT(points[1].fX), SkScalarToFLOAT(points[1].fY),
  1.1144 +        SkScalarToFLOAT(points[2].fX), SkScalarToFLOAT(points[2].fY),
  1.1145 +        SkScalarToFLOAT(points[3].fX), SkScalarToFLOAT(points[3].fY),
  1.1146 +    };
  1.1147 +
  1.1148 +    // Describe if the segments are stroked.
  1.1149 +    BOOL segmentStrokes[3] = {
  1.1150 +        stroke, stroke, stroke,
  1.1151 +    };
  1.1152 +
  1.1153 +    // Add the segment data to the figure.
  1.1154 +    HRM((*xpsQuad)->SetSegments(
  1.1155 +            3, 6,
  1.1156 +            segmentTypes , segmentData, segmentStrokes),
  1.1157 +        "Could not add segment data to quad.");
  1.1158 +
  1.1159 +    // Set the closed and filled properties of the figure.
  1.1160 +    HRM((*xpsQuad)->SetIsClosed(stroke), "Could not set quad close.");
  1.1161 +    HRM((*xpsQuad)->SetIsFilled(fill), "Could not set quad fill.");
  1.1162 +
  1.1163 +    return S_OK;
  1.1164 +}
  1.1165 +
  1.1166 +void SkXPSDevice::clear(SkColor color) {
  1.1167 +    //TODO: override this for XPS
  1.1168 +    SkDEBUGF(("XPS clear not yet implemented."));
  1.1169 +}
  1.1170 +
  1.1171 +void SkXPSDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
  1.1172 +                             size_t count, const SkPoint points[],
  1.1173 +                             const SkPaint& paint) {
  1.1174 +    //This will call back into the device to do the drawing.
  1.1175 +    d.drawPoints(mode, count, points, paint, true);
  1.1176 +}
  1.1177 +
  1.1178 +void SkXPSDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode,
  1.1179 +                               int vertexCount, const SkPoint verts[],
  1.1180 +                               const SkPoint texs[], const SkColor colors[],
  1.1181 +                               SkXfermode* xmode, const uint16_t indices[],
  1.1182 +                               int indexCount, const SkPaint& paint) {
  1.1183 +    //TODO: override this for XPS
  1.1184 +    SkDEBUGF(("XPS drawVertices not yet implemented."));
  1.1185 +}
  1.1186 +
  1.1187 +void SkXPSDevice::drawPaint(const SkDraw& d, const SkPaint& origPaint) {
  1.1188 +    const SkRect r = SkRect::MakeSize(this->fCurrentCanvasSize);
  1.1189 +
  1.1190 +    //If trying to paint with a stroke, ignore that and fill.
  1.1191 +    SkPaint* fillPaint = const_cast<SkPaint*>(&origPaint);
  1.1192 +    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
  1.1193 +    if (paint->getStyle() != SkPaint::kFill_Style) {
  1.1194 +        paint.writable()->setStyle(SkPaint::kFill_Style);
  1.1195 +    }
  1.1196 +
  1.1197 +    this->internalDrawRect(d, r, false, *fillPaint);
  1.1198 +}
  1.1199 +
  1.1200 +void SkXPSDevice::drawRect(const SkDraw& d,
  1.1201 +                           const SkRect& r,
  1.1202 +                           const SkPaint& paint) {
  1.1203 +    this->internalDrawRect(d, r, true, paint);
  1.1204 +}
  1.1205 +
  1.1206 +void SkXPSDevice::drawRRect(const SkDraw& d,
  1.1207 +                            const SkRRect& rr,
  1.1208 +                            const SkPaint& paint) {
  1.1209 +    SkPath path;
  1.1210 +    path.addRRect(rr);
  1.1211 +    this->drawPath(d, path, paint, NULL, true);
  1.1212 +}
  1.1213 +
  1.1214 +void SkXPSDevice::internalDrawRect(const SkDraw& d,
  1.1215 +                                   const SkRect& r,
  1.1216 +                                   bool transformRect,
  1.1217 +                                   const SkPaint& paint) {
  1.1218 +    //Exit early if there is nothing to draw.
  1.1219 +    if (d.fClip->isEmpty() ||
  1.1220 +        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
  1.1221 +        return;
  1.1222 +    }
  1.1223 +
  1.1224 +    //Path the rect if we can't optimize it.
  1.1225 +    if (rect_must_be_pathed(paint, *d.fMatrix)) {
  1.1226 +        SkPath tmp;
  1.1227 +        tmp.addRect(r);
  1.1228 +        tmp.setFillType(SkPath::kWinding_FillType);
  1.1229 +        this->drawPath(d, tmp, paint, NULL, true);
  1.1230 +        return;
  1.1231 +    }
  1.1232 +
  1.1233 +    //Create the shaded path.
  1.1234 +    SkTScopedComPtr<IXpsOMPath> shadedPath;
  1.1235 +    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
  1.1236 +         "Could not create shaded path for rect.");
  1.1237 +
  1.1238 +    //Create the shaded geometry.
  1.1239 +    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
  1.1240 +    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
  1.1241 +         "Could not create shaded geometry for rect.");
  1.1242 +
  1.1243 +    //Add the geometry to the shaded path.
  1.1244 +    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
  1.1245 +         "Could not set shaded geometry for rect.");
  1.1246 +
  1.1247 +    //Set the brushes.
  1.1248 +    BOOL fill = FALSE;
  1.1249 +    BOOL stroke = FALSE;
  1.1250 +    HRV(this->shadePath(shadedPath.get(), paint, *d.fMatrix, &fill, &stroke));
  1.1251 +
  1.1252 +    bool xpsTransformsPath = true;
  1.1253 +    //Transform the geometry.
  1.1254 +    if (transformRect && xpsTransformsPath) {
  1.1255 +        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
  1.1256 +        HRV(this->createXpsTransform(*d.fMatrix, &xpsTransform));
  1.1257 +        if (xpsTransform.get()) {
  1.1258 +            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
  1.1259 +                 "Could not set transform for rect.");
  1.1260 +        } else {
  1.1261 +            xpsTransformsPath = false;
  1.1262 +        }
  1.1263 +    }
  1.1264 +
  1.1265 +    //Create the figure.
  1.1266 +    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
  1.1267 +    {
  1.1268 +        SkPoint points[4] = {
  1.1269 +            { r.fLeft, r.fTop },
  1.1270 +            { r.fLeft, r.fBottom },
  1.1271 +            { r.fRight, r.fBottom },
  1.1272 +            { r.fRight, r.fTop },
  1.1273 +        };
  1.1274 +        if (!xpsTransformsPath && transformRect) {
  1.1275 +            d.fMatrix->mapPoints(points, SK_ARRAY_COUNT(points));
  1.1276 +        }
  1.1277 +        HRV(this->createXpsQuad(points, stroke, fill, &rectFigure));
  1.1278 +    }
  1.1279 +
  1.1280 +    //Get the figures of the shaded geometry.
  1.1281 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
  1.1282 +    HRVM(shadedGeometry->GetFigures(&shadedFigures),
  1.1283 +         "Could not get shaded figures for rect.");
  1.1284 +
  1.1285 +    //Add the figure to the shaded geometry figures.
  1.1286 +    HRVM(shadedFigures->Append(rectFigure.get()),
  1.1287 +         "Could not add shaded figure for rect.");
  1.1288 +
  1.1289 +    HRV(this->clip(shadedPath.get(), d));
  1.1290 +
  1.1291 +    //Add the shaded path to the current visuals.
  1.1292 +    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
  1.1293 +    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
  1.1294 +         "Could not get current visuals for rect.");
  1.1295 +    HRVM(currentVisuals->Append(shadedPath.get()),
  1.1296 +         "Could not add rect to current visuals.");
  1.1297 +}
  1.1298 +
  1.1299 +static HRESULT close_figure(const SkTDArray<XPS_SEGMENT_TYPE>& segmentTypes,
  1.1300 +                            const SkTDArray<BOOL>& segmentStrokes,
  1.1301 +                            const SkTDArray<FLOAT>& segmentData,
  1.1302 +                            BOOL stroke, BOOL fill,
  1.1303 +                            IXpsOMGeometryFigure* figure,
  1.1304 +                            IXpsOMGeometryFigureCollection* figures) {
  1.1305 +    // Add the segment data to the figure.
  1.1306 +    HRM(figure->SetSegments(segmentTypes.count(), segmentData.count(),
  1.1307 +                            segmentTypes.begin() , segmentData.begin(),
  1.1308 +                            segmentStrokes.begin()),
  1.1309 +        "Could not set path segments.");
  1.1310 +
  1.1311 +    // Set the closed and filled properties of the figure.
  1.1312 +    HRM(figure->SetIsClosed(stroke), "Could not set path closed.");
  1.1313 +    HRM(figure->SetIsFilled(fill), "Could not set path fill.");
  1.1314 +
  1.1315 +    // Add the figure created above to this geometry.
  1.1316 +    HRM(figures->Append(figure), "Could not add path to geometry.");
  1.1317 +    return S_OK;
  1.1318 +}
  1.1319 +
  1.1320 +HRESULT SkXPSDevice::addXpsPathGeometry(
  1.1321 +        IXpsOMGeometryFigureCollection* xpsFigures,
  1.1322 +        BOOL stroke, BOOL fill, const SkPath& path) {
  1.1323 +    SkTDArray<XPS_SEGMENT_TYPE> segmentTypes;
  1.1324 +    SkTDArray<BOOL> segmentStrokes;
  1.1325 +    SkTDArray<FLOAT> segmentData;
  1.1326 +
  1.1327 +    SkTScopedComPtr<IXpsOMGeometryFigure> xpsFigure;
  1.1328 +    SkPath::Iter iter(path, true);
  1.1329 +    SkPoint points[4];
  1.1330 +    SkPath::Verb verb;
  1.1331 +    while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
  1.1332 +        switch (verb) {
  1.1333 +            case SkPath::kMove_Verb: {
  1.1334 +                if (NULL != xpsFigure.get()) {
  1.1335 +                    HR(close_figure(segmentTypes, segmentStrokes, segmentData,
  1.1336 +                                    stroke, fill,
  1.1337 +                                    xpsFigure.get() , xpsFigures));
  1.1338 +                    xpsFigure.reset();
  1.1339 +                    segmentTypes.rewind();
  1.1340 +                    segmentStrokes.rewind();
  1.1341 +                    segmentData.rewind();
  1.1342 +                }
  1.1343 +                // Define the start point.
  1.1344 +                XPS_POINT startPoint = xps_point(points[0]);
  1.1345 +                // Create the figure.
  1.1346 +                HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint,
  1.1347 +                                                            &xpsFigure),
  1.1348 +                    "Could not create path geometry figure.");
  1.1349 +                break;
  1.1350 +            }
  1.1351 +            case SkPath::kLine_Verb:
  1.1352 +                if (iter.isCloseLine()) break; //ignore the line, auto-closed
  1.1353 +                segmentTypes.push(XPS_SEGMENT_TYPE_LINE);
  1.1354 +                segmentStrokes.push(stroke);
  1.1355 +                segmentData.push(SkScalarToFLOAT(points[1].fX));
  1.1356 +                segmentData.push(SkScalarToFLOAT(points[1].fY));
  1.1357 +                break;
  1.1358 +            case SkPath::kQuad_Verb:
  1.1359 +                segmentTypes.push(XPS_SEGMENT_TYPE_QUADRATIC_BEZIER);
  1.1360 +                segmentStrokes.push(stroke);
  1.1361 +                segmentData.push(SkScalarToFLOAT(points[1].fX));
  1.1362 +                segmentData.push(SkScalarToFLOAT(points[1].fY));
  1.1363 +                segmentData.push(SkScalarToFLOAT(points[2].fX));
  1.1364 +                segmentData.push(SkScalarToFLOAT(points[2].fY));
  1.1365 +                break;
  1.1366 +            case SkPath::kCubic_Verb:
  1.1367 +                segmentTypes.push(XPS_SEGMENT_TYPE_BEZIER);
  1.1368 +                segmentStrokes.push(stroke);
  1.1369 +                segmentData.push(SkScalarToFLOAT(points[1].fX));
  1.1370 +                segmentData.push(SkScalarToFLOAT(points[1].fY));
  1.1371 +                segmentData.push(SkScalarToFLOAT(points[2].fX));
  1.1372 +                segmentData.push(SkScalarToFLOAT(points[2].fY));
  1.1373 +                segmentData.push(SkScalarToFLOAT(points[3].fX));
  1.1374 +                segmentData.push(SkScalarToFLOAT(points[3].fY));
  1.1375 +                break;
  1.1376 +            case SkPath::kClose_Verb:
  1.1377 +                // we ignore these, and just get the whole segment from
  1.1378 +                // the corresponding line/quad/cubic verbs
  1.1379 +                break;
  1.1380 +            default:
  1.1381 +                SkDEBUGFAIL("unexpected verb");
  1.1382 +                break;
  1.1383 +        }
  1.1384 +    }
  1.1385 +    if (NULL != xpsFigure.get()) {
  1.1386 +        HR(close_figure(segmentTypes, segmentStrokes, segmentData,
  1.1387 +                        stroke, fill,
  1.1388 +                        xpsFigure.get(), xpsFigures));
  1.1389 +    }
  1.1390 +    return S_OK;
  1.1391 +}
  1.1392 +
  1.1393 +HRESULT SkXPSDevice::drawInverseWindingPath(const SkDraw& d,
  1.1394 +                                            const SkPath& devicePath,
  1.1395 +                                            IXpsOMPath* shadedPath) {
  1.1396 +    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
  1.1397 +        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
  1.1398 +
  1.1399 +    const XPS_RECT universeRectXps = {
  1.1400 +        0.0f, 0.0f,
  1.1401 +        SkScalarToFLOAT(this->fCurrentCanvasSize.fWidth),
  1.1402 +        SkScalarToFLOAT(this->fCurrentCanvasSize.fHeight),
  1.1403 +    };
  1.1404 +
  1.1405 +    //Get the geometry.
  1.1406 +    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
  1.1407 +    HRM(shadedPath->GetGeometry(&shadedGeometry),
  1.1408 +        "Could not get shaded geometry for inverse path.");
  1.1409 +
  1.1410 +    //Get the figures from the geometry.
  1.1411 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
  1.1412 +    HRM(shadedGeometry->GetFigures(&shadedFigures),
  1.1413 +        "Could not get shaded figures for inverse path.");
  1.1414 +
  1.1415 +    HRM(shadedGeometry->SetFillRule(XPS_FILL_RULE_NONZERO),
  1.1416 +        "Could not set shaded fill rule for inverse path.");
  1.1417 +
  1.1418 +    //Take everything drawn so far, and make a shared resource out of it.
  1.1419 +    //Replace everything drawn so far with
  1.1420 +    //inverse canvas
  1.1421 +    //  old canvas of everything so far
  1.1422 +    //  world shaded figure, clipped to current clip
  1.1423 +    //  top canvas of everything so far, clipped to path
  1.1424 +    //Note: this is not quite right when there is nothing solid in the
  1.1425 +    //canvas of everything so far, as the bit on top will allow
  1.1426 +    //the world paint to show through.
  1.1427 +
  1.1428 +    //Create new canvas.
  1.1429 +    SkTScopedComPtr<IXpsOMCanvas> newCanvas;
  1.1430 +    HRM(this->fXpsFactory->CreateCanvas(&newCanvas),
  1.1431 +        "Could not create inverse canvas.");
  1.1432 +
  1.1433 +    //Save the old canvas to a dictionary on the new canvas.
  1.1434 +    SkTScopedComPtr<IXpsOMDictionary> newDictionary;
  1.1435 +    HRM(this->fXpsFactory->CreateDictionary(&newDictionary),
  1.1436 +        "Could not create inverse dictionary.");
  1.1437 +    HRM(newCanvas->SetDictionaryLocal(newDictionary.get()),
  1.1438 +        "Could not set inverse dictionary.");
  1.1439 +
  1.1440 +    const size_t size = SK_ARRAY_COUNT(L"ID" L_GUID_ID);
  1.1441 +    wchar_t buffer[size];
  1.1442 +    wchar_t id[GUID_ID_LEN];
  1.1443 +    HR(create_id(id, GUID_ID_LEN, '_'));
  1.1444 +    swprintf_s(buffer, size, L"ID%s", id);
  1.1445 +    HRM(newDictionary->Append(buffer, this->fCurrentXpsCanvas.get()),
  1.1446 +        "Could not add canvas to inverse dictionary.");
  1.1447 +
  1.1448 +    //Start drawing
  1.1449 +    SkTScopedComPtr<IXpsOMVisualCollection> newVisuals;
  1.1450 +    HRM(newCanvas->GetVisuals(&newVisuals),
  1.1451 +        "Could not get inverse canvas visuals.");
  1.1452 +
  1.1453 +    //Draw old canvas from dictionary onto new canvas.
  1.1454 +    SkTScopedComPtr<IXpsOMGeometry> oldGeometry;
  1.1455 +    HRM(this->fXpsFactory->CreateGeometry(&oldGeometry),
  1.1456 +        "Could not create old inverse geometry.");
  1.1457 +
  1.1458 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> oldFigures;
  1.1459 +    HRM(oldGeometry->GetFigures(&oldFigures),
  1.1460 +        "Could not get old inverse figures.");
  1.1461 +
  1.1462 +    SkTScopedComPtr<IXpsOMGeometryFigure> oldFigure;
  1.1463 +    HR(this->createXpsRect(universeRect, FALSE, TRUE, &oldFigure));
  1.1464 +    HRM(oldFigures->Append(oldFigure.get()),
  1.1465 +        "Could not add old inverse figure.");
  1.1466 +
  1.1467 +    SkTScopedComPtr<IXpsOMVisualBrush> oldBrush;
  1.1468 +    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
  1.1469 +                                             &universeRectXps,
  1.1470 +                                             &oldBrush),
  1.1471 +        "Could not create old inverse brush.");
  1.1472 +
  1.1473 +    SkTScopedComPtr<IXpsOMPath> oldPath;
  1.1474 +    HRM(this->fXpsFactory->CreatePath(&oldPath),
  1.1475 +        "Could not create old inverse path.");
  1.1476 +    HRM(oldPath->SetGeometryLocal(oldGeometry.get()),
  1.1477 +        "Could not set old inverse geometry.");
  1.1478 +    HRM(oldPath->SetFillBrushLocal(oldBrush.get()),
  1.1479 +        "Could not set old inverse fill brush.");
  1.1480 +    //the brush must be parented before setting the lookup.
  1.1481 +    HRM(newVisuals->Append(oldPath.get()),
  1.1482 +        "Could not add old inverse path to new canvas visuals.");
  1.1483 +    HRM(oldBrush->SetVisualLookup(buffer),
  1.1484 +        "Could not set old inverse brush visual lookup.");
  1.1485 +
  1.1486 +    //Draw the clip filling shader.
  1.1487 +    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
  1.1488 +    HR(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure));
  1.1489 +    HRM(shadedFigures->Append(shadedFigure.get()),
  1.1490 +        "Could not add inverse shaded figure.");
  1.1491 +    //the geometry is already set
  1.1492 +    HR(this->clip(shadedPath, d));
  1.1493 +    HRM(newVisuals->Append(shadedPath),
  1.1494 +        "Could not add inverse shaded path to canvas visuals.");
  1.1495 +
  1.1496 +    //Draw the old canvas on top, clipped to the original path.
  1.1497 +    SkTScopedComPtr<IXpsOMCanvas> topCanvas;
  1.1498 +    HRM(this->fXpsFactory->CreateCanvas(&topCanvas),
  1.1499 +        "Could not create top inverse canvas.");
  1.1500 +    //Clip the canvas to prevent alpha spill.
  1.1501 +    //This is the entire reason this canvas exists.
  1.1502 +    HR(this->clip(topCanvas.get(), d));
  1.1503 +
  1.1504 +    SkTScopedComPtr<IXpsOMGeometry> topGeometry;
  1.1505 +    HRM(this->fXpsFactory->CreateGeometry(&topGeometry),
  1.1506 +        "Could not create top inverse geometry.");
  1.1507 +
  1.1508 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> topFigures;
  1.1509 +    HRM(topGeometry->GetFigures(&topFigures),
  1.1510 +        "Could not get top inverse figures.");
  1.1511 +
  1.1512 +    SkTScopedComPtr<IXpsOMGeometryFigure> topFigure;
  1.1513 +    HR(this->createXpsRect(universeRect, FALSE, TRUE, &topFigure));
  1.1514 +    HRM(topFigures->Append(topFigure.get()),
  1.1515 +        "Could not add old inverse figure.");
  1.1516 +
  1.1517 +    SkTScopedComPtr<IXpsOMVisualBrush> topBrush;
  1.1518 +    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
  1.1519 +                                             &universeRectXps,
  1.1520 +                                             &topBrush),
  1.1521 +        "Could not create top inverse brush.");
  1.1522 +
  1.1523 +    SkTScopedComPtr<IXpsOMPath> topPath;
  1.1524 +    HRM(this->fXpsFactory->CreatePath(&topPath),
  1.1525 +        "Could not create top inverse path.");
  1.1526 +    HRM(topPath->SetGeometryLocal(topGeometry.get()),
  1.1527 +        "Could not set top inverse geometry.");
  1.1528 +    HRM(topPath->SetFillBrushLocal(topBrush.get()),
  1.1529 +        "Could not set top inverse fill brush.");
  1.1530 +    //the brush must be parented before setting the lookup.
  1.1531 +    HRM(newVisuals->Append(topCanvas.get()),
  1.1532 +        "Could not add top canvas to inverse canvas visuals.");
  1.1533 +    SkTScopedComPtr<IXpsOMVisualCollection> topVisuals;
  1.1534 +    HRM(topCanvas->GetVisuals(&topVisuals),
  1.1535 +        "Could not get top inverse canvas visuals.");
  1.1536 +    HRM(topVisuals->Append(topPath.get()),
  1.1537 +        "Could not add top inverse path to top canvas visuals.");
  1.1538 +    HRM(topBrush->SetVisualLookup(buffer),
  1.1539 +        "Could not set top inverse brush visual lookup.");
  1.1540 +
  1.1541 +    HR(this->clipToPath(topPath.get(), devicePath, XPS_FILL_RULE_NONZERO));
  1.1542 +
  1.1543 +    //swap current canvas to new canvas
  1.1544 +    this->fCurrentXpsCanvas.swap(newCanvas);
  1.1545 +
  1.1546 +    return S_OK;
  1.1547 +}
  1.1548 +
  1.1549 +void SkXPSDevice::convertToPpm(const SkMaskFilter* filter,
  1.1550 +                               SkMatrix* matrix,
  1.1551 +                               SkVector* ppuScale,
  1.1552 +                               const SkIRect& clip, SkIRect* clipIRect) {
  1.1553 +    //This action is in unit space, but the ppm is specified in physical space.
  1.1554 +    ppuScale->fX = SkScalarDiv(this->fCurrentPixelsPerMeter.fX,
  1.1555 +                               this->fCurrentUnitsPerMeter.fX);
  1.1556 +    ppuScale->fY = SkScalarDiv(this->fCurrentPixelsPerMeter.fY,
  1.1557 +                               this->fCurrentUnitsPerMeter.fY);
  1.1558 +
  1.1559 +    matrix->postScale(ppuScale->fX, ppuScale->fY);
  1.1560 +
  1.1561 +    const SkIRect& irect = clip;
  1.1562 +    SkRect clipRect = SkRect::MakeLTRB(
  1.1563 +        SkScalarMul(SkIntToScalar(irect.fLeft), ppuScale->fX),
  1.1564 +        SkScalarMul(SkIntToScalar(irect.fTop), ppuScale->fY),
  1.1565 +        SkScalarMul(SkIntToScalar(irect.fRight), ppuScale->fX),
  1.1566 +        SkScalarMul(SkIntToScalar(irect.fBottom), ppuScale->fY));
  1.1567 +    clipRect.roundOut(clipIRect);
  1.1568 +}
  1.1569 +
  1.1570 +HRESULT SkXPSDevice::applyMask(const SkDraw& d,
  1.1571 +                               const SkMask& mask,
  1.1572 +                               const SkVector& ppuScale,
  1.1573 +                               IXpsOMPath* shadedPath) {
  1.1574 +    //Get the geometry object.
  1.1575 +    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
  1.1576 +    HRM(shadedPath->GetGeometry(&shadedGeometry),
  1.1577 +        "Could not get mask shaded geometry.");
  1.1578 +
  1.1579 +    //Get the figures from the geometry.
  1.1580 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
  1.1581 +    HRM(shadedGeometry->GetFigures(&shadedFigures),
  1.1582 +        "Could not get mask shaded figures.");
  1.1583 +
  1.1584 +    SkMatrix m;
  1.1585 +    m.reset();
  1.1586 +    m.setTranslate(SkIntToScalar(mask.fBounds.fLeft),
  1.1587 +                   SkIntToScalar(mask.fBounds.fTop));
  1.1588 +    m.postScale(SkScalarInvert(ppuScale.fX), SkScalarInvert(ppuScale.fY));
  1.1589 +
  1.1590 +    SkShader::TileMode xy[2];
  1.1591 +    xy[0] = (SkShader::TileMode)3;
  1.1592 +    xy[1] = (SkShader::TileMode)3;
  1.1593 +
  1.1594 +    SkBitmap bm;
  1.1595 +    bm.setConfig(SkBitmap::kA8_Config,
  1.1596 +                 mask.fBounds.width(),
  1.1597 +                 mask.fBounds.height(),
  1.1598 +                 mask.fRowBytes);
  1.1599 +    bm.setPixels(mask.fImage);
  1.1600 +
  1.1601 +    SkTScopedComPtr<IXpsOMTileBrush> maskBrush;
  1.1602 +    HR(this->createXpsImageBrush(bm, m, xy, 0xFF, &maskBrush));
  1.1603 +    HRM(shadedPath->SetOpacityMaskBrushLocal(maskBrush.get()),
  1.1604 +        "Could not set mask.");
  1.1605 +
  1.1606 +    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
  1.1607 +        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
  1.1608 +    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
  1.1609 +    HRM(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure),
  1.1610 +        "Could not create mask shaded figure.");
  1.1611 +    HRM(shadedFigures->Append(shadedFigure.get()),
  1.1612 +        "Could not add mask shaded figure.");
  1.1613 +
  1.1614 +    HR(this->clip(shadedPath, d));
  1.1615 +
  1.1616 +    //Add the path to the active visual collection.
  1.1617 +    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
  1.1618 +    HRM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
  1.1619 +        "Could not get mask current visuals.");
  1.1620 +    HRM(currentVisuals->Append(shadedPath),
  1.1621 +        "Could not add masked shaded path to current visuals.");
  1.1622 +
  1.1623 +    return S_OK;
  1.1624 +}
  1.1625 +
  1.1626 +HRESULT SkXPSDevice::shadePath(IXpsOMPath* shadedPath,
  1.1627 +                               const SkPaint& shaderPaint,
  1.1628 +                               const SkMatrix& matrix,
  1.1629 +                               BOOL* fill, BOOL* stroke) {
  1.1630 +    *fill = FALSE;
  1.1631 +    *stroke = FALSE;
  1.1632 +
  1.1633 +    const SkPaint::Style style = shaderPaint.getStyle();
  1.1634 +    const bool hasFill = SkPaint::kFill_Style == style
  1.1635 +                      || SkPaint::kStrokeAndFill_Style == style;
  1.1636 +    const bool hasStroke = SkPaint::kStroke_Style == style
  1.1637 +                        || SkPaint::kStrokeAndFill_Style == style;
  1.1638 +
  1.1639 +    //TODO(bungeman): use dictionaries and lookups.
  1.1640 +    if (hasFill) {
  1.1641 +        *fill = TRUE;
  1.1642 +        SkTScopedComPtr<IXpsOMBrush> fillBrush;
  1.1643 +        HR(this->createXpsBrush(shaderPaint, &fillBrush, &matrix));
  1.1644 +        HRM(shadedPath->SetFillBrushLocal(fillBrush.get()),
  1.1645 +            "Could not set fill for shaded path.");
  1.1646 +    }
  1.1647 +
  1.1648 +    if (hasStroke) {
  1.1649 +        *stroke = TRUE;
  1.1650 +        SkTScopedComPtr<IXpsOMBrush> strokeBrush;
  1.1651 +        HR(this->createXpsBrush(shaderPaint, &strokeBrush, &matrix));
  1.1652 +        HRM(shadedPath->SetStrokeBrushLocal(strokeBrush.get()),
  1.1653 +            "Could not set stroke brush for shaded path.");
  1.1654 +        HRM(shadedPath->SetStrokeThickness(
  1.1655 +                SkScalarToFLOAT(shaderPaint.getStrokeWidth())),
  1.1656 +            "Could not set shaded path stroke thickness.");
  1.1657 +
  1.1658 +        if (0 == shaderPaint.getStrokeWidth()) {
  1.1659 +            //XPS hair width is a hack. (XPS Spec 11.6.12).
  1.1660 +            SkTScopedComPtr<IXpsOMDashCollection> dashes;
  1.1661 +            HRM(shadedPath->GetStrokeDashes(&dashes),
  1.1662 +                "Could not set dashes for shaded path.");
  1.1663 +            XPS_DASH dash;
  1.1664 +            dash.length = 1.0;
  1.1665 +            dash.gap = 0.0;
  1.1666 +            HRM(dashes->Append(&dash), "Could not add dashes to shaded path.");
  1.1667 +            HRM(shadedPath->SetStrokeDashOffset(-2.0),
  1.1668 +                "Could not set dash offset for shaded path.");
  1.1669 +        }
  1.1670 +    }
  1.1671 +    return S_OK;
  1.1672 +}
  1.1673 +
  1.1674 +void SkXPSDevice::drawPath(const SkDraw& d,
  1.1675 +                           const SkPath& platonicPath,
  1.1676 +                           const SkPaint& origPaint,
  1.1677 +                           const SkMatrix* prePathMatrix,
  1.1678 +                           bool pathIsMutable) {
  1.1679 +    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
  1.1680 +
  1.1681 +    // nothing to draw
  1.1682 +    if (d.fClip->isEmpty() ||
  1.1683 +        (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
  1.1684 +        return;
  1.1685 +    }
  1.1686 +
  1.1687 +    SkPath modifiedPath;
  1.1688 +    const bool paintHasPathEffect = paint->getPathEffect()
  1.1689 +                                 || paint->getStyle() != SkPaint::kFill_Style;
  1.1690 +
  1.1691 +    //Apply pre-path matrix [Platonic-path -> Skeletal-path].
  1.1692 +    SkMatrix matrix = *d.fMatrix;
  1.1693 +    SkPath* skeletalPath = const_cast<SkPath*>(&platonicPath);
  1.1694 +    if (prePathMatrix) {
  1.1695 +        if (paintHasPathEffect || paint->getRasterizer()) {
  1.1696 +            if (!pathIsMutable) {
  1.1697 +                skeletalPath = &modifiedPath;
  1.1698 +                pathIsMutable = true;
  1.1699 +            }
  1.1700 +            platonicPath.transform(*prePathMatrix, skeletalPath);
  1.1701 +        } else {
  1.1702 +            if (!matrix.preConcat(*prePathMatrix)) {
  1.1703 +                return;
  1.1704 +            }
  1.1705 +        }
  1.1706 +    }
  1.1707 +
  1.1708 +    //Apply path effect [Skeletal-path -> Fillable-path].
  1.1709 +    SkPath* fillablePath = skeletalPath;
  1.1710 +    if (paintHasPathEffect) {
  1.1711 +        if (!pathIsMutable) {
  1.1712 +            fillablePath = &modifiedPath;
  1.1713 +            pathIsMutable = true;
  1.1714 +        }
  1.1715 +        bool fill = paint->getFillPath(*skeletalPath, fillablePath);
  1.1716 +
  1.1717 +        SkPaint* writablePaint = paint.writable();
  1.1718 +        writablePaint->setPathEffect(NULL);
  1.1719 +        if (fill) {
  1.1720 +            writablePaint->setStyle(SkPaint::kFill_Style);
  1.1721 +        } else {
  1.1722 +            writablePaint->setStyle(SkPaint::kStroke_Style);
  1.1723 +            writablePaint->setStrokeWidth(0);
  1.1724 +        }
  1.1725 +    }
  1.1726 +
  1.1727 +    //Create the shaded path. This will be the path which is painted.
  1.1728 +    SkTScopedComPtr<IXpsOMPath> shadedPath;
  1.1729 +    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
  1.1730 +         "Could not create shaded path for path.");
  1.1731 +
  1.1732 +    //Create the geometry for the shaded path.
  1.1733 +    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
  1.1734 +    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
  1.1735 +         "Could not create shaded geometry for path.");
  1.1736 +
  1.1737 +    //Add the geometry to the shaded path.
  1.1738 +    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
  1.1739 +         "Could not add the shaded geometry to shaded path.");
  1.1740 +
  1.1741 +    SkRasterizer* rasterizer = paint->getRasterizer();
  1.1742 +    SkMaskFilter* filter = paint->getMaskFilter();
  1.1743 +
  1.1744 +    //Determine if we will draw or shade and mask.
  1.1745 +    if (rasterizer || filter) {
  1.1746 +        if (paint->getStyle() != SkPaint::kFill_Style) {
  1.1747 +            paint.writable()->setStyle(SkPaint::kFill_Style);
  1.1748 +        }
  1.1749 +    }
  1.1750 +
  1.1751 +    //Set the brushes.
  1.1752 +    BOOL fill;
  1.1753 +    BOOL stroke;
  1.1754 +    HRV(this->shadePath(shadedPath.get(),
  1.1755 +                        *paint,
  1.1756 +                        *d.fMatrix,
  1.1757 +                        &fill,
  1.1758 +                        &stroke));
  1.1759 +
  1.1760 +    //Rasterizer
  1.1761 +    if (rasterizer) {
  1.1762 +        SkIRect clipIRect;
  1.1763 +        SkVector ppuScale;
  1.1764 +        this->convertToPpm(filter,
  1.1765 +                           &matrix,
  1.1766 +                           &ppuScale,
  1.1767 +                           d.fClip->getBounds(),
  1.1768 +                           &clipIRect);
  1.1769 +
  1.1770 +        SkMask* mask = NULL;
  1.1771 +
  1.1772 +        //[Fillable-path -> Mask]
  1.1773 +        SkMask rasteredMask;
  1.1774 +        if (rasterizer->rasterize(
  1.1775 +                *fillablePath,
  1.1776 +                matrix,
  1.1777 +                &clipIRect,
  1.1778 +                filter,  //just to compute how much to draw.
  1.1779 +                &rasteredMask,
  1.1780 +                SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
  1.1781 +
  1.1782 +            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
  1.1783 +            mask = &rasteredMask;
  1.1784 +
  1.1785 +            //[Mask -> Mask]
  1.1786 +            SkMask filteredMask;
  1.1787 +            if (filter &&
  1.1788 +                filter->filterMask(&filteredMask, *mask, *d.fMatrix, NULL)) {
  1.1789 +
  1.1790 +                mask = &filteredMask;
  1.1791 +            } else {
  1.1792 +                filteredMask.fImage = NULL;
  1.1793 +            }
  1.1794 +            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
  1.1795 +
  1.1796 +            //Draw mask.
  1.1797 +            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
  1.1798 +        }
  1.1799 +        return;
  1.1800 +    }
  1.1801 +
  1.1802 +    //Mask filter
  1.1803 +    if (filter) {
  1.1804 +        SkIRect clipIRect;
  1.1805 +        SkVector ppuScale;
  1.1806 +        this->convertToPpm(filter,
  1.1807 +                           &matrix,
  1.1808 +                           &ppuScale,
  1.1809 +                           d.fClip->getBounds(),
  1.1810 +                           &clipIRect);
  1.1811 +
  1.1812 +        //[Fillable-path -> Pixel-path]
  1.1813 +        SkPath* pixelPath = pathIsMutable ? fillablePath : &modifiedPath;
  1.1814 +        fillablePath->transform(matrix, pixelPath);
  1.1815 +
  1.1816 +        SkMask* mask = NULL;
  1.1817 +
  1.1818 +        //[Pixel-path -> Mask]
  1.1819 +        SkMask rasteredMask;
  1.1820 +        if (SkDraw::DrawToMask(
  1.1821 +                        *pixelPath,
  1.1822 +                        &clipIRect,
  1.1823 +                        filter,  //just to compute how much to draw.
  1.1824 +                        &matrix,
  1.1825 +                        &rasteredMask,
  1.1826 +                        SkMask::kComputeBoundsAndRenderImage_CreateMode,
  1.1827 +                        paint->getStyle())) {
  1.1828 +
  1.1829 +            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
  1.1830 +            mask = &rasteredMask;
  1.1831 +
  1.1832 +            //[Mask -> Mask]
  1.1833 +            SkMask filteredMask;
  1.1834 +            if (filter->filterMask(&filteredMask,
  1.1835 +                                   rasteredMask,
  1.1836 +                                   matrix,
  1.1837 +                                   NULL)) {
  1.1838 +                mask = &filteredMask;
  1.1839 +            } else {
  1.1840 +                filteredMask.fImage = NULL;
  1.1841 +            }
  1.1842 +            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
  1.1843 +
  1.1844 +            //Draw mask.
  1.1845 +            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
  1.1846 +        }
  1.1847 +        return;
  1.1848 +    }
  1.1849 +
  1.1850 +    //Get the figures from the shaded geometry.
  1.1851 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
  1.1852 +    HRVM(shadedGeometry->GetFigures(&shadedFigures),
  1.1853 +         "Could not get shaded figures for shaded path.");
  1.1854 +
  1.1855 +    bool xpsTransformsPath = true;
  1.1856 +
  1.1857 +    //Set the fill rule.
  1.1858 +    XPS_FILL_RULE xpsFillRule;
  1.1859 +    switch (platonicPath.getFillType()) {
  1.1860 +        case SkPath::kWinding_FillType:
  1.1861 +            xpsFillRule = XPS_FILL_RULE_NONZERO;
  1.1862 +            break;
  1.1863 +        case SkPath::kEvenOdd_FillType:
  1.1864 +            xpsFillRule = XPS_FILL_RULE_EVENODD;
  1.1865 +            break;
  1.1866 +        case SkPath::kInverseWinding_FillType: {
  1.1867 +            //[Fillable-path -> Device-path]
  1.1868 +            SkPath* devicePath = pathIsMutable ? fillablePath : &modifiedPath;
  1.1869 +            fillablePath->transform(matrix, devicePath);
  1.1870 +
  1.1871 +            HRV(this->drawInverseWindingPath(d,
  1.1872 +                                             *devicePath,
  1.1873 +                                             shadedPath.get()));
  1.1874 +            return;
  1.1875 +        }
  1.1876 +        case SkPath::kInverseEvenOdd_FillType: {
  1.1877 +            const SkRect universe = SkRect::MakeLTRB(
  1.1878 +                0, 0,
  1.1879 +                this->fCurrentCanvasSize.fWidth,
  1.1880 +                this->fCurrentCanvasSize.fHeight);
  1.1881 +            SkTScopedComPtr<IXpsOMGeometryFigure> addOneFigure;
  1.1882 +            HRV(this->createXpsRect(universe, FALSE, TRUE, &addOneFigure));
  1.1883 +            HRVM(shadedFigures->Append(addOneFigure.get()),
  1.1884 +                 "Could not add even-odd flip figure to shaded path.");
  1.1885 +            xpsTransformsPath = false;
  1.1886 +            xpsFillRule = XPS_FILL_RULE_EVENODD;
  1.1887 +            break;
  1.1888 +        }
  1.1889 +        default:
  1.1890 +            SkDEBUGFAIL("Unknown SkPath::FillType.");
  1.1891 +    }
  1.1892 +    HRVM(shadedGeometry->SetFillRule(xpsFillRule),
  1.1893 +         "Could not set fill rule for shaded path.");
  1.1894 +
  1.1895 +    //Create the XPS transform, if possible.
  1.1896 +    if (xpsTransformsPath) {
  1.1897 +        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
  1.1898 +        HRV(this->createXpsTransform(matrix, &xpsTransform));
  1.1899 +
  1.1900 +        if (xpsTransform.get()) {
  1.1901 +            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
  1.1902 +                 "Could not set transform on shaded path.");
  1.1903 +        } else {
  1.1904 +            xpsTransformsPath = false;
  1.1905 +        }
  1.1906 +    }
  1.1907 +
  1.1908 +    SkPath* devicePath = fillablePath;
  1.1909 +    if (!xpsTransformsPath) {
  1.1910 +        //[Fillable-path -> Device-path]
  1.1911 +        devicePath = pathIsMutable ? fillablePath : &modifiedPath;
  1.1912 +        fillablePath->transform(matrix, devicePath);
  1.1913 +    }
  1.1914 +    HRV(this->addXpsPathGeometry(shadedFigures.get(),
  1.1915 +                                 stroke, fill, *devicePath));
  1.1916 +
  1.1917 +    HRV(this->clip(shadedPath.get(), d));
  1.1918 +
  1.1919 +    //Add the path to the active visual collection.
  1.1920 +    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
  1.1921 +    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
  1.1922 +         "Could not get current visuals for shaded path.");
  1.1923 +    HRVM(currentVisuals->Append(shadedPath.get()),
  1.1924 +         "Could not add shaded path to current visuals.");
  1.1925 +}
  1.1926 +
  1.1927 +HRESULT SkXPSDevice::clip(IXpsOMVisual* xpsVisual, const SkDraw& d) {
  1.1928 +    SkPath clipPath;
  1.1929 +    SkAssertResult(d.fClip->getBoundaryPath(&clipPath));
  1.1930 +
  1.1931 +    return this->clipToPath(xpsVisual, clipPath, XPS_FILL_RULE_EVENODD);
  1.1932 +}
  1.1933 +HRESULT SkXPSDevice::clipToPath(IXpsOMVisual* xpsVisual,
  1.1934 +                                const SkPath& clipPath,
  1.1935 +                                XPS_FILL_RULE fillRule) {
  1.1936 +    //Create the geometry.
  1.1937 +    SkTScopedComPtr<IXpsOMGeometry> clipGeometry;
  1.1938 +    HRM(this->fXpsFactory->CreateGeometry(&clipGeometry),
  1.1939 +        "Could not create clip geometry.");
  1.1940 +
  1.1941 +    //Get the figure collection of the geometry.
  1.1942 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> clipFigures;
  1.1943 +    HRM(clipGeometry->GetFigures(&clipFigures),
  1.1944 +        "Could not get the clip figures.");
  1.1945 +
  1.1946 +    //Create the figures into the geometry.
  1.1947 +    HR(this->addXpsPathGeometry(
  1.1948 +        clipFigures.get(),
  1.1949 +        FALSE, TRUE, clipPath));
  1.1950 +
  1.1951 +    HRM(clipGeometry->SetFillRule(fillRule),
  1.1952 +        "Could not set fill rule.");
  1.1953 +    HRM(xpsVisual->SetClipGeometryLocal(clipGeometry.get()),
  1.1954 +        "Could not set clip geometry.");
  1.1955 +
  1.1956 +    return S_OK;
  1.1957 +}
  1.1958 +
  1.1959 +void SkXPSDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
  1.1960 +                             const SkMatrix& matrix, const SkPaint& paint) {
  1.1961 +    if (d.fClip->isEmpty()) {
  1.1962 +        return;
  1.1963 +    }
  1.1964 +
  1.1965 +    SkIRect srcRect;
  1.1966 +    srcRect.set(0, 0, bitmap.width(), bitmap.height());
  1.1967 +
  1.1968 +    //Create the new shaded path.
  1.1969 +    SkTScopedComPtr<IXpsOMPath> shadedPath;
  1.1970 +    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
  1.1971 +         "Could not create path for bitmap.");
  1.1972 +
  1.1973 +    //Create the shaded geometry.
  1.1974 +    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
  1.1975 +    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
  1.1976 +         "Could not create geometry for bitmap.");
  1.1977 +
  1.1978 +    //Add the shaded geometry to the shaded path.
  1.1979 +    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
  1.1980 +         "Could not set the geometry for bitmap.");
  1.1981 +
  1.1982 +    //Get the shaded figures from the shaded geometry.
  1.1983 +    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
  1.1984 +    HRVM(shadedGeometry->GetFigures(&shadedFigures),
  1.1985 +         "Could not get the figures for bitmap.");
  1.1986 +
  1.1987 +    SkMatrix transform = matrix;
  1.1988 +    transform.postConcat(*d.fMatrix);
  1.1989 +
  1.1990 +    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
  1.1991 +    HRV(this->createXpsTransform(transform, &xpsTransform));
  1.1992 +    if (xpsTransform.get()) {
  1.1993 +        HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
  1.1994 +             "Could not set transform for bitmap.");
  1.1995 +    } else {
  1.1996 +        //TODO: perspective that bitmap!
  1.1997 +    }
  1.1998 +
  1.1999 +    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
  1.2000 +    if (NULL != xpsTransform.get()) {
  1.2001 +        const SkShader::TileMode xy[2] = {
  1.2002 +            SkShader::kClamp_TileMode,
  1.2003 +            SkShader::kClamp_TileMode,
  1.2004 +        };
  1.2005 +        SkTScopedComPtr<IXpsOMTileBrush> xpsImageBrush;
  1.2006 +        HRV(this->createXpsImageBrush(bitmap,
  1.2007 +                                      transform,
  1.2008 +                                      xy,
  1.2009 +                                      paint.getAlpha(),
  1.2010 +                                      &xpsImageBrush));
  1.2011 +        HRVM(shadedPath->SetFillBrushLocal(xpsImageBrush.get()),
  1.2012 +             "Could not set bitmap brush.");
  1.2013 +
  1.2014 +        const SkRect bitmapRect = SkRect::MakeLTRB(0, 0,
  1.2015 +            SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
  1.2016 +        HRV(this->createXpsRect(bitmapRect, FALSE, TRUE, &rectFigure));
  1.2017 +    }
  1.2018 +    HRVM(shadedFigures->Append(rectFigure.get()),
  1.2019 +         "Could not add bitmap figure.");
  1.2020 +
  1.2021 +    //Get the current visual collection and add the shaded path to it.
  1.2022 +    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
  1.2023 +    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
  1.2024 +         "Could not get current visuals for bitmap");
  1.2025 +    HRVM(currentVisuals->Append(shadedPath.get()),
  1.2026 +         "Could not add bitmap to current visuals.");
  1.2027 +
  1.2028 +    HRV(this->clip(shadedPath.get(), d));
  1.2029 +}
  1.2030 +
  1.2031 +void SkXPSDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
  1.2032 +                             int x, int y,
  1.2033 +                             const SkPaint& paint) {
  1.2034 +    //TODO: override this for XPS
  1.2035 +    SkDEBUGF(("XPS drawSprite not yet implemented."));
  1.2036 +}
  1.2037 +
  1.2038 +HRESULT SkXPSDevice::CreateTypefaceUse(const SkPaint& paint,
  1.2039 +                                       TypefaceUse** typefaceUse) {
  1.2040 +    SkAutoResolveDefaultTypeface typeface(paint.getTypeface());
  1.2041 +
  1.2042 +    //Check cache.
  1.2043 +    const SkFontID typefaceID = typeface->uniqueID();
  1.2044 +    if (!this->fTypefaces.empty()) {
  1.2045 +        TypefaceUse* current = &this->fTypefaces.front();
  1.2046 +        const TypefaceUse* last = &this->fTypefaces.back();
  1.2047 +        for (; current <= last; ++current) {
  1.2048 +            if (current->typefaceId == typefaceID) {
  1.2049 +                *typefaceUse = current;
  1.2050 +                return S_OK;
  1.2051 +            }
  1.2052 +        }
  1.2053 +    }
  1.2054 +
  1.2055 +    //TODO: create glyph only fonts
  1.2056 +    //and let the host deal with what kind of font we're looking at.
  1.2057 +    XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_RESTRICTED;
  1.2058 +
  1.2059 +    SkTScopedComPtr<IStream> fontStream;
  1.2060 +    int ttcIndex;
  1.2061 +    SkStream* fontData = typeface->openStream(&ttcIndex);
  1.2062 +    //TODO: cannot handle FON fonts.
  1.2063 +    HRM(SkIStream::CreateFromSkStream(fontData, true, &fontStream),
  1.2064 +        "Could not create font stream.");
  1.2065 +
  1.2066 +    const size_t size =
  1.2067 +        SK_ARRAY_COUNT(L"/Resources/Fonts/" L_GUID_ID L".odttf");
  1.2068 +    wchar_t buffer[size];
  1.2069 +    wchar_t id[GUID_ID_LEN];
  1.2070 +    HR(create_id(id, GUID_ID_LEN));
  1.2071 +    swprintf_s(buffer, size, L"/Resources/Fonts/%s.odttf", id);
  1.2072 +
  1.2073 +    SkTScopedComPtr<IOpcPartUri> partUri;
  1.2074 +    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
  1.2075 +        "Could not create font resource part uri.");
  1.2076 +
  1.2077 +    SkTScopedComPtr<IXpsOMFontResource> xpsFontResource;
  1.2078 +    HRM(this->fXpsFactory->CreateFontResource(fontStream.get(),
  1.2079 +                                              embedding,
  1.2080 +                                              partUri.get(),
  1.2081 +                                              FALSE,
  1.2082 +                                              &xpsFontResource),
  1.2083 +        "Could not create font resource.");
  1.2084 +
  1.2085 +    //TODO: change openStream to return -1 for non-ttc, get rid of this.
  1.2086 +    uint8_t* data = (uint8_t*)fontData->getMemoryBase();
  1.2087 +    bool isTTC = (data &&
  1.2088 +                  fontData->getLength() >= sizeof(SkTTCFHeader) &&
  1.2089 +                  ((SkTTCFHeader*)data)->ttcTag == SkTTCFHeader::TAG);
  1.2090 +
  1.2091 +    TypefaceUse& newTypefaceUse = this->fTypefaces.push_back();
  1.2092 +    newTypefaceUse.typefaceId = typefaceID;
  1.2093 +    newTypefaceUse.ttcIndex = isTTC ? ttcIndex : -1;
  1.2094 +    newTypefaceUse.fontData = fontData;
  1.2095 +    newTypefaceUse.xpsFont = xpsFontResource.release();
  1.2096 +
  1.2097 +    SkAutoGlyphCache agc(paint, NULL, &SkMatrix::I());
  1.2098 +    SkGlyphCache* glyphCache = agc.getCache();
  1.2099 +    unsigned int glyphCount = glyphCache->getGlyphCount();
  1.2100 +    newTypefaceUse.glyphsUsed = new SkBitSet(glyphCount);
  1.2101 +
  1.2102 +    *typefaceUse = &newTypefaceUse;
  1.2103 +    return S_OK;
  1.2104 +}
  1.2105 +
  1.2106 +HRESULT SkXPSDevice::AddGlyphs(const SkDraw& d,
  1.2107 +                               IXpsOMObjectFactory* xpsFactory,
  1.2108 +                               IXpsOMCanvas* canvas,
  1.2109 +                               TypefaceUse* font,
  1.2110 +                               LPCWSTR text,
  1.2111 +                               XPS_GLYPH_INDEX* xpsGlyphs,
  1.2112 +                               UINT32 xpsGlyphsLen,
  1.2113 +                               XPS_POINT *origin,
  1.2114 +                               FLOAT fontSize,
  1.2115 +                               XPS_STYLE_SIMULATION sims,
  1.2116 +                               const SkMatrix& transform,
  1.2117 +                               const SkPaint& paint) {
  1.2118 +    SkTScopedComPtr<IXpsOMGlyphs> glyphs;
  1.2119 +    HRM(xpsFactory->CreateGlyphs(font->xpsFont, &glyphs), "Could not create glyphs.");
  1.2120 +    HRM(glyphs->SetFontFaceIndex(font->ttcIndex), "Could not set glyph font face index.");
  1.2121 +
  1.2122 +    //XPS uses affine transformations for everything...
  1.2123 +    //...except positioning text.
  1.2124 +    bool useCanvasForClip;
  1.2125 +    if ((transform.getType() & ~SkMatrix::kTranslate_Mask) == 0) {
  1.2126 +        origin->x += SkScalarToFLOAT(transform.getTranslateX());
  1.2127 +        origin->y += SkScalarToFLOAT(transform.getTranslateY());
  1.2128 +        useCanvasForClip = false;
  1.2129 +    } else {
  1.2130 +        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
  1.2131 +        HR(this->createXpsTransform(transform, &xpsMatrixToUse));
  1.2132 +        if (xpsMatrixToUse.get()) {
  1.2133 +            HRM(glyphs->SetTransformLocal(xpsMatrixToUse.get()),
  1.2134 +                "Could not set transform matrix.");
  1.2135 +            useCanvasForClip = true;
  1.2136 +        } else {
  1.2137 +            SkDEBUGFAIL("Attempt to add glyphs in perspective.");
  1.2138 +            useCanvasForClip = false;
  1.2139 +        }
  1.2140 +    }
  1.2141 +
  1.2142 +    SkTScopedComPtr<IXpsOMGlyphsEditor> glyphsEditor;
  1.2143 +    HRM(glyphs->GetGlyphsEditor(&glyphsEditor), "Could not get glyph editor.");
  1.2144 +
  1.2145 +    if (NULL != text) {
  1.2146 +        HRM(glyphsEditor->SetUnicodeString(text),
  1.2147 +            "Could not set unicode string.");
  1.2148 +    }
  1.2149 +
  1.2150 +    if (NULL != xpsGlyphs) {
  1.2151 +        HRM(glyphsEditor->SetGlyphIndices(xpsGlyphsLen, xpsGlyphs),
  1.2152 +            "Could not set glyphs.");
  1.2153 +    }
  1.2154 +
  1.2155 +    HRM(glyphsEditor->ApplyEdits(), "Could not apply glyph edits.");
  1.2156 +
  1.2157 +    SkTScopedComPtr<IXpsOMBrush> xpsFillBrush;
  1.2158 +    HR(this->createXpsBrush(
  1.2159 +            paint,
  1.2160 +            &xpsFillBrush,
  1.2161 +            useCanvasForClip ? NULL : &transform));
  1.2162 +
  1.2163 +    HRM(glyphs->SetFillBrushLocal(xpsFillBrush.get()),
  1.2164 +        "Could not set fill brush.");
  1.2165 +
  1.2166 +    HRM(glyphs->SetOrigin(origin), "Could not set glyph origin.");
  1.2167 +
  1.2168 +    HRM(glyphs->SetFontRenderingEmSize(fontSize),
  1.2169 +        "Could not set font size.");
  1.2170 +
  1.2171 +    HRM(glyphs->SetStyleSimulations(sims),
  1.2172 +        "Could not set style simulations.");
  1.2173 +
  1.2174 +    SkTScopedComPtr<IXpsOMVisualCollection> visuals;
  1.2175 +    HRM(canvas->GetVisuals(&visuals), "Could not get glyph canvas visuals.");
  1.2176 +
  1.2177 +    if (!useCanvasForClip) {
  1.2178 +        HR(this->clip(glyphs.get(), d));
  1.2179 +        HRM(visuals->Append(glyphs.get()), "Could not add glyphs to canvas.");
  1.2180 +    } else {
  1.2181 +        SkTScopedComPtr<IXpsOMCanvas> glyphCanvas;
  1.2182 +        HRM(this->fXpsFactory->CreateCanvas(&glyphCanvas),
  1.2183 +            "Could not create glyph canvas.");
  1.2184 +
  1.2185 +        SkTScopedComPtr<IXpsOMVisualCollection> glyphCanvasVisuals;
  1.2186 +        HRM(glyphCanvas->GetVisuals(&glyphCanvasVisuals),
  1.2187 +            "Could not get glyph visuals collection.");
  1.2188 +
  1.2189 +        HRM(glyphCanvasVisuals->Append(glyphs.get()),
  1.2190 +            "Could not add glyphs to page.");
  1.2191 +        HR(this->clip(glyphCanvas.get(), d));
  1.2192 +
  1.2193 +        HRM(visuals->Append(glyphCanvas.get()),
  1.2194 +            "Could not add glyph canvas to page.");
  1.2195 +    }
  1.2196 +
  1.2197 +    return S_OK;
  1.2198 +}
  1.2199 +
  1.2200 +struct SkXPSDrawProcs : public SkDrawProcs {
  1.2201 +public:
  1.2202 +    /** [in] Advance width and offsets for glyphs measured in
  1.2203 +    hundredths of the font em size (XPS Spec 5.1.3). */
  1.2204 +    FLOAT centemPerUnit;
  1.2205 +    /** [in,out] The accumulated glyphs used in the current typeface. */
  1.2206 +    SkBitSet* glyphUse;
  1.2207 +    /** [out] The glyphs to draw. */
  1.2208 +    SkTDArray<XPS_GLYPH_INDEX> xpsGlyphs;
  1.2209 +};
  1.2210 +
  1.2211 +static void xps_draw_1_glyph(const SkDraw1Glyph& state,
  1.2212 +                             SkFixed x, SkFixed y,
  1.2213 +                             const SkGlyph& skGlyph) {
  1.2214 +    SkASSERT(skGlyph.fWidth > 0 && skGlyph.fHeight > 0);
  1.2215 +
  1.2216 +    SkXPSDrawProcs* procs = static_cast<SkXPSDrawProcs*>(state.fDraw->fProcs);
  1.2217 +
  1.2218 +    //Draw pre-adds half the sampling frequency for floor rounding.
  1.2219 +    x -= state.fHalfSampleX;
  1.2220 +    y -= state.fHalfSampleY;
  1.2221 +
  1.2222 +    XPS_GLYPH_INDEX* xpsGlyph = procs->xpsGlyphs.append();
  1.2223 +    uint16_t glyphID = skGlyph.getGlyphID();
  1.2224 +    procs->glyphUse->setBit(glyphID, true);
  1.2225 +    xpsGlyph->index = glyphID;
  1.2226 +    if (1 == procs->xpsGlyphs.count()) {
  1.2227 +        xpsGlyph->advanceWidth = 0.0f;
  1.2228 +        xpsGlyph->horizontalOffset = SkFixedToFloat(x) * procs->centemPerUnit;
  1.2229 +        xpsGlyph->verticalOffset = SkFixedToFloat(y) * -procs->centemPerUnit;
  1.2230 +    } else {
  1.2231 +        const XPS_GLYPH_INDEX& first = procs->xpsGlyphs[0];
  1.2232 +        xpsGlyph->advanceWidth = 0.0f;
  1.2233 +        xpsGlyph->horizontalOffset = (SkFixedToFloat(x) * procs->centemPerUnit)
  1.2234 +                                     - first.horizontalOffset;
  1.2235 +        xpsGlyph->verticalOffset = (SkFixedToFloat(y) * -procs->centemPerUnit)
  1.2236 +                                   - first.verticalOffset;
  1.2237 +    }
  1.2238 +}
  1.2239 +
  1.2240 +static void text_draw_init(const SkPaint& paint,
  1.2241 +                           const void* text, size_t byteLength,
  1.2242 +                           SkBitSet& glyphsUsed,
  1.2243 +                           SkDraw& myDraw, SkXPSDrawProcs& procs) {
  1.2244 +    procs.fD1GProc = xps_draw_1_glyph;
  1.2245 +    size_t numGlyphGuess;
  1.2246 +    switch (paint.getTextEncoding()) {
  1.2247 +        case SkPaint::kUTF8_TextEncoding:
  1.2248 +            numGlyphGuess = SkUTF8_CountUnichars(
  1.2249 +                static_cast<const char *>(text),
  1.2250 +                byteLength);
  1.2251 +            break;
  1.2252 +        case SkPaint::kUTF16_TextEncoding:
  1.2253 +            numGlyphGuess = SkUTF16_CountUnichars(
  1.2254 +                static_cast<const uint16_t *>(text),
  1.2255 +                byteLength);
  1.2256 +            break;
  1.2257 +        case SkPaint::kGlyphID_TextEncoding:
  1.2258 +            numGlyphGuess = byteLength / 2;
  1.2259 +            break;
  1.2260 +        default:
  1.2261 +            SK_DEBUGBREAK(true);
  1.2262 +    }
  1.2263 +    procs.xpsGlyphs.setReserve(numGlyphGuess);
  1.2264 +    procs.glyphUse = &glyphsUsed;
  1.2265 +    procs.centemPerUnit = 100.0f / SkScalarToFLOAT(paint.getTextSize());
  1.2266 +
  1.2267 +    myDraw.fProcs = &procs;
  1.2268 +}
  1.2269 +
  1.2270 +static bool text_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
  1.2271 +    const SkPaint::Style style = paint.getStyle();
  1.2272 +    return matrix.hasPerspective()
  1.2273 +        || SkPaint::kStroke_Style == style
  1.2274 +        || SkPaint::kStrokeAndFill_Style == style
  1.2275 +        || paint.getMaskFilter()
  1.2276 +        || paint.getRasterizer()
  1.2277 +    ;
  1.2278 +}
  1.2279 +
  1.2280 +void SkXPSDevice::drawText(const SkDraw& d,
  1.2281 +                           const void* text, size_t byteLen,
  1.2282 +                           SkScalar x, SkScalar y,
  1.2283 +                           const SkPaint& paint) {
  1.2284 +    if (byteLen < 1) return;
  1.2285 +
  1.2286 +    if (text_must_be_pathed(paint, *d.fMatrix)) {
  1.2287 +        SkPath path;
  1.2288 +        paint.getTextPath(text, byteLen, x, y, &path);
  1.2289 +        this->drawPath(d, path, paint, NULL, true);
  1.2290 +        //TODO: add automation "text"
  1.2291 +        return;
  1.2292 +    }
  1.2293 +
  1.2294 +    TypefaceUse* typeface;
  1.2295 +    HRV(CreateTypefaceUse(paint, &typeface));
  1.2296 +
  1.2297 +    SkDraw myDraw(d);
  1.2298 +    myDraw.fMatrix = &SkMatrix::I();
  1.2299 +    SkXPSDrawProcs procs;
  1.2300 +    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
  1.2301 +
  1.2302 +    myDraw.drawText(static_cast<const char*>(text), byteLen, x, y, paint);
  1.2303 +
  1.2304 +    // SkDraw may have clipped out the glyphs, so we need to check
  1.2305 +    if (procs.xpsGlyphs.count() == 0) {
  1.2306 +        return;
  1.2307 +    }
  1.2308 +
  1.2309 +    XPS_POINT origin = {
  1.2310 +        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
  1.2311 +        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
  1.2312 +    };
  1.2313 +    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
  1.2314 +    procs.xpsGlyphs[0].verticalOffset = 0.0f;
  1.2315 +
  1.2316 +    HRV(AddGlyphs(d,
  1.2317 +                  this->fXpsFactory.get(),
  1.2318 +                  this->fCurrentXpsCanvas.get(),
  1.2319 +                  typeface,
  1.2320 +                  NULL,
  1.2321 +                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
  1.2322 +                  &origin,
  1.2323 +                  SkScalarToFLOAT(paint.getTextSize()),
  1.2324 +                  XPS_STYLE_SIMULATION_NONE,
  1.2325 +                  *d.fMatrix,
  1.2326 +                  paint));
  1.2327 +}
  1.2328 +
  1.2329 +void SkXPSDevice::drawPosText(const SkDraw& d,
  1.2330 +                              const void* text, size_t byteLen,
  1.2331 +                              const SkScalar pos[],
  1.2332 +                              SkScalar constY, int scalarsPerPos,
  1.2333 +                              const SkPaint& paint) {
  1.2334 +    if (byteLen < 1) return;
  1.2335 +
  1.2336 +    if (text_must_be_pathed(paint, *d.fMatrix)) {
  1.2337 +        SkPath path;
  1.2338 +        //TODO: make this work, Draw currently does not handle as well.
  1.2339 +        //paint.getTextPath(text, byteLength, x, y, &path);
  1.2340 +        //this->drawPath(d, path, paint, NULL, true);
  1.2341 +        //TODO: add automation "text"
  1.2342 +        return;
  1.2343 +    }
  1.2344 +
  1.2345 +    TypefaceUse* typeface;
  1.2346 +    HRV(CreateTypefaceUse(paint, &typeface));
  1.2347 +
  1.2348 +    SkDraw myDraw(d);
  1.2349 +    myDraw.fMatrix = &SkMatrix::I();
  1.2350 +    SkXPSDrawProcs procs;
  1.2351 +    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
  1.2352 +
  1.2353 +    myDraw.drawPosText(static_cast<const char*>(text), byteLen,
  1.2354 +                       pos, constY, scalarsPerPos,
  1.2355 +                       paint);
  1.2356 +
  1.2357 +    // SkDraw may have clipped out the glyphs, so we need to check
  1.2358 +    if (procs.xpsGlyphs.count() == 0) {
  1.2359 +        return;
  1.2360 +    }
  1.2361 +
  1.2362 +    XPS_POINT origin = {
  1.2363 +        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
  1.2364 +        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
  1.2365 +    };
  1.2366 +    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
  1.2367 +    procs.xpsGlyphs[0].verticalOffset = 0.0f;
  1.2368 +
  1.2369 +    HRV(AddGlyphs(d,
  1.2370 +                  this->fXpsFactory.get(),
  1.2371 +                  this->fCurrentXpsCanvas.get(),
  1.2372 +                  typeface,
  1.2373 +                  NULL,
  1.2374 +                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
  1.2375 +                  &origin,
  1.2376 +                  SkScalarToFLOAT(paint.getTextSize()),
  1.2377 +                  XPS_STYLE_SIMULATION_NONE,
  1.2378 +                  *d.fMatrix,
  1.2379 +                  paint));
  1.2380 +}
  1.2381 +
  1.2382 +void SkXPSDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
  1.2383 +                                 const SkPath& path, const SkMatrix* matrix,
  1.2384 +                                 const SkPaint& paint) {
  1.2385 +    //This will call back into the device to do the drawing.
  1.2386 +     d.drawTextOnPath((const char*)text, len, path, matrix, paint);
  1.2387 +}
  1.2388 +
  1.2389 +void SkXPSDevice::drawDevice(const SkDraw& d, SkBaseDevice* dev,
  1.2390 +                             int x, int y,
  1.2391 +                             const SkPaint&) {
  1.2392 +    SkXPSDevice* that = static_cast<SkXPSDevice*>(dev);
  1.2393 +
  1.2394 +    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
  1.2395 +    XPS_MATRIX rawTransform = {
  1.2396 +        1.0f,
  1.2397 +        0.0f,
  1.2398 +        0.0f,
  1.2399 +        1.0f,
  1.2400 +        static_cast<FLOAT>(x),
  1.2401 +        static_cast<FLOAT>(y),
  1.2402 +    };
  1.2403 +    HRVM(this->fXpsFactory->CreateMatrixTransform(&rawTransform, &xpsTransform),
  1.2404 +         "Could not create layer transform.");
  1.2405 +    HRVM(that->fCurrentXpsCanvas->SetTransformLocal(xpsTransform.get()),
  1.2406 +         "Could not set layer transform.");
  1.2407 +
  1.2408 +    //Get the current visual collection and add the layer to it.
  1.2409 +    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
  1.2410 +    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
  1.2411 +         "Could not get current visuals for layer.");
  1.2412 +    HRVM(currentVisuals->Append(that->fCurrentXpsCanvas.get()),
  1.2413 +         "Could not add layer to current visuals.");
  1.2414 +}
  1.2415 +
  1.2416 +bool SkXPSDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
  1.2417 +                               SkCanvas::Config8888) {
  1.2418 +    return false;
  1.2419 +}
  1.2420 +
  1.2421 +SkBaseDevice* SkXPSDevice::onCreateDevice(const SkImageInfo&, Usage) {
  1.2422 +//Conditional for bug compatibility with PDF device.
  1.2423 +#if 0
  1.2424 +    if (SkBaseDevice::kGeneral_Usage == usage) {
  1.2425 +        return NULL;
  1.2426 +        SK_CRASH();
  1.2427 +        //To what stream do we write?
  1.2428 +        //SkXPSDevice* dev = new SkXPSDevice(this);
  1.2429 +        //SkSize s = SkSize::Make(width, height);
  1.2430 +        //dev->BeginCanvas(s, s, SkMatrix::I());
  1.2431 +        //return dev;
  1.2432 +    }
  1.2433 +#endif
  1.2434 +    return new SkXPSDevice(this->fXpsFactory.get());
  1.2435 +}
  1.2436 +
  1.2437 +SkXPSDevice::SkXPSDevice(IXpsOMObjectFactory* xpsFactory)
  1.2438 +    : SkBitmapDevice(make_fake_bitmap(10000, 10000))
  1.2439 +    , fCurrentPage(0) {
  1.2440 +
  1.2441 +    HRVM(CoCreateInstance(
  1.2442 +             CLSID_XpsOMObjectFactory,
  1.2443 +             NULL,
  1.2444 +             CLSCTX_INPROC_SERVER,
  1.2445 +             IID_PPV_ARGS(&this->fXpsFactory)),
  1.2446 +         "Could not create factory for layer.");
  1.2447 +
  1.2448 +    HRVM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas),
  1.2449 +         "Could not create canvas for layer.");
  1.2450 +}
  1.2451 +
  1.2452 +bool SkXPSDevice::allowImageFilter(const SkImageFilter*) {
  1.2453 +    return false;
  1.2454 +}

mercurial