michael@0: /* michael@0: * Copyright 2011 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #ifndef UNICODE michael@0: #define UNICODE michael@0: #endif michael@0: #ifndef _UNICODE michael@0: #define _UNICODE michael@0: #endif michael@0: #include "SkTypes.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include "SkColor.h" michael@0: #include "SkConstexprMath.h" michael@0: #include "SkData.h" michael@0: #include "SkDraw.h" michael@0: #include "SkDrawProcs.h" michael@0: #include "SkEndian.h" michael@0: #include "SkFontHost.h" michael@0: #include "SkGlyphCache.h" michael@0: #include "SkHRESULT.h" michael@0: #include "SkImageEncoder.h" michael@0: #include "SkIStream.h" michael@0: #include "SkMaskFilter.h" michael@0: #include "SkPaint.h" michael@0: #include "SkPoint.h" michael@0: #include "SkRasterizer.h" michael@0: #include "SkSFNTHeader.h" michael@0: #include "SkShader.h" michael@0: #include "SkSize.h" michael@0: #include "SkStream.h" michael@0: #include "SkTDArray.h" michael@0: #include "SkTLazy.h" michael@0: #include "SkTScopedComPtr.h" michael@0: #include "SkTTCFHeader.h" michael@0: #include "SkTypefacePriv.h" michael@0: #include "SkUtils.h" michael@0: #include "SkXPSDevice.h" michael@0: michael@0: //Windows defines a FLOAT type, michael@0: //make it clear when converting a scalar that this is what is wanted. michael@0: #define SkScalarToFLOAT(n) SkScalarToFloat(n) michael@0: michael@0: //Dummy representation of a GUID from create_id. michael@0: #define L_GUID_ID L"XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX" michael@0: //Length of GUID representation from create_id, including NULL terminator. michael@0: #define GUID_ID_LEN SK_ARRAY_COUNT(L_GUID_ID) michael@0: michael@0: /** michael@0: Formats a GUID and places it into buffer. michael@0: buffer should have space for at least GUID_ID_LEN wide characters. michael@0: The string will always be wchar null terminated. michael@0: XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0 michael@0: @return -1 if there was an error, > 0 if success. michael@0: */ michael@0: static int format_guid(const GUID& guid, michael@0: wchar_t* buffer, size_t bufferSize, michael@0: wchar_t sep = '-') { michael@0: SkASSERT(bufferSize >= GUID_ID_LEN); michael@0: return swprintf_s(buffer, michael@0: bufferSize, michael@0: L"%08lX%c%04X%c%04X%c%02X%02X%c%02X%02X%02X%02X%02X%02X", michael@0: guid.Data1, michael@0: sep, michael@0: guid.Data2, michael@0: sep, michael@0: guid.Data3, michael@0: sep, michael@0: guid.Data4[0], michael@0: guid.Data4[1], michael@0: sep, michael@0: guid.Data4[2], michael@0: guid.Data4[3], michael@0: guid.Data4[4], michael@0: guid.Data4[5], michael@0: guid.Data4[6], michael@0: guid.Data4[7]); michael@0: } michael@0: michael@0: /** michael@0: Creates a GUID based id and places it into buffer. michael@0: buffer should have space for at least GUID_ID_LEN wide characters. michael@0: The string will always be wchar null terminated. michael@0: XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0 michael@0: The string may begin with a digit, michael@0: and so may not be suitable as a bare resource key. michael@0: */ michael@0: static HRESULT create_id(wchar_t* buffer, size_t bufferSize, michael@0: wchar_t sep = '-') { michael@0: GUID guid = {}; michael@0: HRM(CoCreateGuid(&guid), "Could not create GUID for id."); michael@0: michael@0: if (format_guid(guid, buffer, bufferSize, sep) == -1) { michael@0: HRM(E_UNEXPECTED, "Could not format GUID into id."); michael@0: } michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: static SkBitmap make_fake_bitmap(int width, int height) { michael@0: SkBitmap bitmap; michael@0: bitmap.setConfig(SkImageInfo::MakeUnknown(width, height)); michael@0: return bitmap; michael@0: } michael@0: michael@0: // TODO: should inherit from SkBaseDevice instead of SkBitmapDevice... michael@0: SkXPSDevice::SkXPSDevice() michael@0: : SkBitmapDevice(make_fake_bitmap(10000, 10000)) michael@0: , fCurrentPage(0) { michael@0: } michael@0: michael@0: SkXPSDevice::~SkXPSDevice() { michael@0: } michael@0: michael@0: SkXPSDevice::TypefaceUse::TypefaceUse() michael@0: : typefaceId(0xffffffff) michael@0: , fontData(NULL) michael@0: , xpsFont(NULL) michael@0: , glyphsUsed(NULL) { michael@0: } michael@0: michael@0: SkXPSDevice::TypefaceUse::~TypefaceUse() { michael@0: //xpsFont owns fontData ref michael@0: this->xpsFont->Release(); michael@0: delete this->glyphsUsed; michael@0: } michael@0: michael@0: bool SkXPSDevice::beginPortfolio(SkWStream* outputStream) { michael@0: if (!this->fAutoCo.succeeded()) return false; michael@0: michael@0: //Create XPS Factory. michael@0: HRBM(CoCreateInstance( michael@0: CLSID_XpsOMObjectFactory, michael@0: NULL, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_PPV_ARGS(&this->fXpsFactory)), michael@0: "Could not create XPS factory."); michael@0: michael@0: HRBM(SkWIStream::CreateFromSkWStream(outputStream, &this->fOutputStream), michael@0: "Could not convert SkStream to IStream."); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool SkXPSDevice::beginSheet( michael@0: const SkVector& unitsPerMeter, michael@0: const SkVector& pixelsPerMeter, michael@0: const SkSize& trimSize, michael@0: const SkRect* mediaBox, michael@0: const SkRect* bleedBox, michael@0: const SkRect* artBox, michael@0: const SkRect* cropBox) { michael@0: ++this->fCurrentPage; michael@0: michael@0: //For simplicity, just write everything out in geometry units, michael@0: //then have a base canvas do the scale to physical units. michael@0: this->fCurrentCanvasSize = trimSize; michael@0: this->fCurrentUnitsPerMeter = unitsPerMeter; michael@0: this->fCurrentPixelsPerMeter = pixelsPerMeter; michael@0: michael@0: this->fCurrentXpsCanvas.reset(); michael@0: HRBM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas), michael@0: "Could not create base canvas."); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsThumbnail(IXpsOMPage* page, michael@0: const unsigned int pageNum, michael@0: IXpsOMImageResource** image) { michael@0: SkTScopedComPtr thumbnailGenerator; michael@0: HRM(CoCreateInstance( michael@0: CLSID_XpsOMThumbnailGenerator, michael@0: NULL, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_PPV_ARGS(&thumbnailGenerator)), michael@0: "Could not create thumbnail generator."); michael@0: michael@0: SkTScopedComPtr partUri; michael@0: static const size_t size = SkTUMax< michael@0: SK_ARRAY_COUNT(L"/Documents/1/Metadata/.png") + SK_DIGITS_IN(pageNum), michael@0: SK_ARRAY_COUNT(L"/Metadata/" L_GUID_ID L".png") michael@0: >::value; michael@0: wchar_t buffer[size]; michael@0: if (pageNum > 0) { michael@0: swprintf_s(buffer, size, L"/Documents/1/Metadata/%u.png", pageNum); michael@0: } else { michael@0: wchar_t id[GUID_ID_LEN]; michael@0: HR(create_id(id, GUID_ID_LEN)); michael@0: swprintf_s(buffer, size, L"/Metadata/%s.png", id); michael@0: } michael@0: HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri), michael@0: "Could not create thumbnail part uri."); michael@0: michael@0: HRM(thumbnailGenerator->GenerateThumbnail(page, michael@0: XPS_IMAGE_TYPE_PNG, michael@0: XPS_THUMBNAIL_SIZE_LARGE, michael@0: partUri.get(), michael@0: image), michael@0: "Could not generate thumbnail."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsPage(const XPS_SIZE& pageSize, michael@0: IXpsOMPage** page) { michael@0: static const size_t size = SK_ARRAY_COUNT(L"/Documents/1/Pages/.fpage") michael@0: + SK_DIGITS_IN(fCurrentPage); michael@0: wchar_t buffer[size]; michael@0: swprintf_s(buffer, size, L"/Documents/1/Pages/%u.fpage", michael@0: this->fCurrentPage); michael@0: SkTScopedComPtr partUri; michael@0: HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri), michael@0: "Could not create page part uri."); michael@0: michael@0: //If the language is unknown, use "und" (XPS Spec 2.3.5.1). michael@0: HRM(this->fXpsFactory->CreatePage(&pageSize, michael@0: L"und", michael@0: partUri.get(), michael@0: page), michael@0: "Could not create page."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::initXpsDocumentWriter(IXpsOMImageResource* image) { michael@0: //Create package writer. michael@0: { michael@0: SkTScopedComPtr partUri; michael@0: HRM(this->fXpsFactory->CreatePartUri(L"/FixedDocumentSequence.fdseq", michael@0: &partUri), michael@0: "Could not create document sequence part uri."); michael@0: HRM(this->fXpsFactory->CreatePackageWriterOnStream( michael@0: this->fOutputStream.get(), michael@0: TRUE, michael@0: XPS_INTERLEAVING_OFF, //XPS_INTERLEAVING_ON, michael@0: partUri.get(), michael@0: NULL, michael@0: image, michael@0: NULL, michael@0: NULL, michael@0: &this->fPackageWriter), michael@0: "Could not create package writer."); michael@0: } michael@0: michael@0: //Begin the lone document. michael@0: { michael@0: SkTScopedComPtr partUri; michael@0: HRM(this->fXpsFactory->CreatePartUri( michael@0: L"/Documents/1/FixedDocument.fdoc", michael@0: &partUri), michael@0: "Could not create fixed document part uri."); michael@0: HRM(this->fPackageWriter->StartNewDocument(partUri.get(), michael@0: NULL, michael@0: NULL, michael@0: NULL, michael@0: NULL), michael@0: "Could not start document."); michael@0: } michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: bool SkXPSDevice::endSheet() { michael@0: //XPS is fixed at 96dpi (XPS Spec 11.1). michael@0: static const float xpsDPI = 96.0f; michael@0: static const float inchesPerMeter = 10000.0f / 254.0f; michael@0: static const float targetUnitsPerMeter = xpsDPI * inchesPerMeter; michael@0: const float scaleX = targetUnitsPerMeter michael@0: / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fX); michael@0: const float scaleY = targetUnitsPerMeter michael@0: / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fY); michael@0: michael@0: //Create the scale canvas. michael@0: SkTScopedComPtr scaleCanvas; michael@0: HRBM(this->fXpsFactory->CreateCanvas(&scaleCanvas), michael@0: "Could not create scale canvas."); michael@0: SkTScopedComPtr scaleCanvasVisuals; michael@0: HRBM(scaleCanvas->GetVisuals(&scaleCanvasVisuals), michael@0: "Could not get scale canvas visuals."); michael@0: michael@0: SkTScopedComPtr geomToPhys; michael@0: XPS_MATRIX rawGeomToPhys = { scaleX, 0, 0, scaleY, 0, 0, }; michael@0: HRBM(this->fXpsFactory->CreateMatrixTransform(&rawGeomToPhys, &geomToPhys), michael@0: "Could not create geometry to physical transform."); michael@0: HRBM(scaleCanvas->SetTransformLocal(geomToPhys.get()), michael@0: "Could not set transform on scale canvas."); michael@0: michael@0: //Add the content canvas to the scale canvas. michael@0: HRBM(scaleCanvasVisuals->Append(this->fCurrentXpsCanvas.get()), michael@0: "Could not add base canvas to scale canvas."); michael@0: michael@0: //Create the page. michael@0: XPS_SIZE pageSize = { michael@0: SkScalarToFLOAT(this->fCurrentCanvasSize.width()) * scaleX, michael@0: SkScalarToFLOAT(this->fCurrentCanvasSize.height()) * scaleY, michael@0: }; michael@0: SkTScopedComPtr page; michael@0: HRB(this->createXpsPage(pageSize, &page)); michael@0: michael@0: SkTScopedComPtr pageVisuals; michael@0: HRBM(page->GetVisuals(&pageVisuals), "Could not get page visuals."); michael@0: michael@0: //Add the scale canvas to the page. michael@0: HRBM(pageVisuals->Append(scaleCanvas.get()), michael@0: "Could not add scale canvas to page."); michael@0: michael@0: //Create the package writer if it hasn't been created yet. michael@0: if (NULL == this->fPackageWriter.get()) { michael@0: SkTScopedComPtr image; michael@0: //Ignore return, thumbnail is completely optional. michael@0: this->createXpsThumbnail(page.get(), 0, &image); michael@0: michael@0: HRB(this->initXpsDocumentWriter(image.get())); michael@0: } michael@0: michael@0: HRBM(this->fPackageWriter->AddPage(page.get(), michael@0: &pageSize, michael@0: NULL, michael@0: NULL, michael@0: NULL, michael@0: NULL), michael@0: "Could not write the page."); michael@0: this->fCurrentXpsCanvas.reset(); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static HRESULT subset_typeface(SkXPSDevice::TypefaceUse* current) { michael@0: //CreateFontPackage wants unsigned short. michael@0: //Microsoft, Y U NO stdint.h? michael@0: SkTDArray keepList; michael@0: current->glyphsUsed->exportTo(&keepList); michael@0: michael@0: int ttcCount = (current->ttcIndex + 1); michael@0: michael@0: //The following are declared with the types required by CreateFontPackage. michael@0: unsigned char *fontPackageBufferRaw = NULL; michael@0: unsigned long fontPackageBufferSize; michael@0: unsigned long bytesWritten; michael@0: unsigned long result = CreateFontPackage( michael@0: (unsigned char *) current->fontData->getMemoryBase(), michael@0: (unsigned long) current->fontData->getLength(), michael@0: &fontPackageBufferRaw, michael@0: &fontPackageBufferSize, michael@0: &bytesWritten, michael@0: TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST | (ttcCount > 0 ? TTFCFP_FLAGS_TTC : 0), michael@0: current->ttcIndex, michael@0: TTFCFP_SUBSET, michael@0: 0, michael@0: 0, michael@0: 0, michael@0: keepList.begin(), michael@0: keepList.count(), michael@0: sk_malloc_throw, michael@0: sk_realloc_throw, michael@0: sk_free, michael@0: NULL); michael@0: SkAutoTMalloc fontPackageBuffer(fontPackageBufferRaw); michael@0: if (result != NO_ERROR) { michael@0: SkDEBUGF(("CreateFontPackage Error %lu", result)); michael@0: return E_UNEXPECTED; michael@0: } michael@0: michael@0: // If it was originally a ttc, keep it a ttc. michael@0: // CreateFontPackage over-allocates, realloc usually decreases the size substantially. michael@0: size_t extra; michael@0: if (ttcCount > 0) { michael@0: // Create space for a ttc header. michael@0: extra = sizeof(SkTTCFHeader) + (ttcCount * sizeof(SK_OT_ULONG)); michael@0: fontPackageBuffer.realloc(bytesWritten + extra); michael@0: //overlap is certain, use memmove michael@0: memmove(fontPackageBuffer.get() + extra, fontPackageBuffer.get(), bytesWritten); michael@0: michael@0: // Write the ttc header. michael@0: SkTTCFHeader* ttcfHeader = reinterpret_cast(fontPackageBuffer.get()); michael@0: ttcfHeader->ttcTag = SkTTCFHeader::TAG; michael@0: ttcfHeader->version = SkTTCFHeader::version_1; michael@0: ttcfHeader->numOffsets = SkEndian_SwapBE32(ttcCount); michael@0: SK_OT_ULONG* offsetPtr = SkTAfter(ttcfHeader); michael@0: for (int i = 0; i < ttcCount; ++i, ++offsetPtr) { michael@0: *offsetPtr = SkEndian_SwapBE32(extra); michael@0: } michael@0: michael@0: // Fix up offsets in sfnt table entries. michael@0: SkSFNTHeader* sfntHeader = SkTAddOffset(fontPackageBuffer.get(), extra); michael@0: int numTables = SkEndian_SwapBE16(sfntHeader->numTables); michael@0: SkSFNTHeader::TableDirectoryEntry* tableDirectory = michael@0: SkTAfter(sfntHeader); michael@0: for (int i = 0; i < numTables; ++i, ++tableDirectory) { michael@0: tableDirectory->offset = SkEndian_SwapBE32( michael@0: SkEndian_SwapBE32(tableDirectory->offset) + extra); michael@0: } michael@0: } else { michael@0: extra = 0; michael@0: fontPackageBuffer.realloc(bytesWritten); michael@0: } michael@0: michael@0: SkAutoTUnref newStream(new SkMemoryStream()); michael@0: newStream->setMemoryOwned(fontPackageBuffer.detach(), bytesWritten + extra); michael@0: michael@0: SkTScopedComPtr newIStream; michael@0: SkIStream::CreateFromSkStream(newStream.detach(), true, &newIStream); michael@0: michael@0: XPS_FONT_EMBEDDING embedding; michael@0: HRM(current->xpsFont->GetEmbeddingOption(&embedding), michael@0: "Could not get embedding option from font."); michael@0: michael@0: SkTScopedComPtr partUri; michael@0: HRM(current->xpsFont->GetPartName(&partUri), michael@0: "Could not get part uri from font."); michael@0: michael@0: HRM(current->xpsFont->SetContent( michael@0: newIStream.get(), michael@0: embedding, michael@0: partUri.get()), michael@0: "Could not set new stream for subsetted font."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: bool SkXPSDevice::endPortfolio() { michael@0: //Subset fonts michael@0: if (!this->fTypefaces.empty()) { michael@0: SkXPSDevice::TypefaceUse* current = &this->fTypefaces.front(); michael@0: const TypefaceUse* last = &this->fTypefaces.back(); michael@0: for (; current <= last; ++current) { michael@0: //Ignore return for now, if it didn't subset, let it be. michael@0: subset_typeface(current); michael@0: } michael@0: } michael@0: michael@0: HRBM(this->fPackageWriter->Close(), "Could not close writer."); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static XPS_COLOR xps_color(const SkColor skColor) { michael@0: //XPS uses non-pre-multiplied alpha (XPS Spec 11.4). michael@0: XPS_COLOR xpsColor; michael@0: xpsColor.colorType = XPS_COLOR_TYPE_SRGB; michael@0: xpsColor.value.sRGB.alpha = SkColorGetA(skColor); michael@0: xpsColor.value.sRGB.red = SkColorGetR(skColor); michael@0: xpsColor.value.sRGB.green = SkColorGetG(skColor); michael@0: xpsColor.value.sRGB.blue = SkColorGetB(skColor); michael@0: michael@0: return xpsColor; michael@0: } michael@0: michael@0: static XPS_POINT xps_point(const SkPoint& point) { michael@0: XPS_POINT xpsPoint = { michael@0: SkScalarToFLOAT(point.fX), michael@0: SkScalarToFLOAT(point.fY), michael@0: }; michael@0: return xpsPoint; michael@0: } michael@0: michael@0: static XPS_POINT xps_point(const SkPoint& point, const SkMatrix& matrix) { michael@0: SkPoint skTransformedPoint; michael@0: matrix.mapXY(point.fX, point.fY, &skTransformedPoint); michael@0: return xps_point(skTransformedPoint); michael@0: } michael@0: michael@0: static XPS_SPREAD_METHOD xps_spread_method(SkShader::TileMode tileMode) { michael@0: switch (tileMode) { michael@0: case SkShader::kClamp_TileMode: michael@0: return XPS_SPREAD_METHOD_PAD; michael@0: case SkShader::kRepeat_TileMode: michael@0: return XPS_SPREAD_METHOD_REPEAT; michael@0: case SkShader::kMirror_TileMode: michael@0: return XPS_SPREAD_METHOD_REFLECT; michael@0: default: michael@0: SkDEBUGFAIL("Unknown tile mode."); michael@0: } michael@0: return XPS_SPREAD_METHOD_PAD; michael@0: } michael@0: michael@0: static void transform_offsets(SkScalar* stopOffsets, const int numOffsets, michael@0: const SkPoint& start, const SkPoint& end, michael@0: const SkMatrix& transform) { michael@0: SkPoint startTransformed; michael@0: transform.mapXY(start.fX, start.fY, &startTransformed); michael@0: SkPoint endTransformed; michael@0: transform.mapXY(end.fX, end.fY, &endTransformed); michael@0: michael@0: //Manhattan distance between transformed start and end. michael@0: SkScalar startToEnd = (endTransformed.fX - startTransformed.fX) michael@0: + (endTransformed.fY - startTransformed.fY); michael@0: if (SkScalarNearlyZero(startToEnd)) { michael@0: for (int i = 0; i < numOffsets; ++i) { michael@0: stopOffsets[i] = 0; michael@0: } michael@0: return; michael@0: } michael@0: michael@0: for (int i = 0; i < numOffsets; ++i) { michael@0: SkPoint stop; michael@0: stop.fX = SkScalarMul(end.fX - start.fX, stopOffsets[i]); michael@0: stop.fY = SkScalarMul(end.fY - start.fY, stopOffsets[i]); michael@0: michael@0: SkPoint stopTransformed; michael@0: transform.mapXY(stop.fX, stop.fY, &stopTransformed); michael@0: michael@0: //Manhattan distance between transformed start and stop. michael@0: SkScalar startToStop = (stopTransformed.fX - startTransformed.fX) michael@0: + (stopTransformed.fY - startTransformed.fY); michael@0: //Percentage along transformed line. michael@0: stopOffsets[i] = SkScalarDiv(startToStop, startToEnd); michael@0: } michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsTransform(const SkMatrix& matrix, michael@0: IXpsOMMatrixTransform** xpsTransform) { michael@0: SkScalar affine[6]; michael@0: if (!matrix.asAffine(affine)) { michael@0: *xpsTransform = NULL; michael@0: return S_FALSE; michael@0: } michael@0: XPS_MATRIX rawXpsMatrix = { michael@0: SkScalarToFLOAT(affine[SkMatrix::kAScaleX]), michael@0: SkScalarToFLOAT(affine[SkMatrix::kASkewY]), michael@0: SkScalarToFLOAT(affine[SkMatrix::kASkewX]), michael@0: SkScalarToFLOAT(affine[SkMatrix::kAScaleY]), michael@0: SkScalarToFLOAT(affine[SkMatrix::kATransX]), michael@0: SkScalarToFLOAT(affine[SkMatrix::kATransY]), michael@0: }; michael@0: HRM(this->fXpsFactory->CreateMatrixTransform(&rawXpsMatrix, xpsTransform), michael@0: "Could not create transform."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createPath(IXpsOMGeometryFigure* figure, michael@0: IXpsOMVisualCollection* visuals, michael@0: IXpsOMPath** path) { michael@0: SkTScopedComPtr geometry; michael@0: HRM(this->fXpsFactory->CreateGeometry(&geometry), michael@0: "Could not create geometry."); michael@0: michael@0: SkTScopedComPtr figureCollection; michael@0: HRM(geometry->GetFigures(&figureCollection), "Could not get figures."); michael@0: HRM(figureCollection->Append(figure), "Could not add figure."); michael@0: michael@0: HRM(this->fXpsFactory->CreatePath(path), "Could not create path."); michael@0: HRM((*path)->SetGeometryLocal(geometry.get()), "Could not set geometry"); michael@0: michael@0: HRM(visuals->Append(*path), "Could not add path to visuals."); michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsSolidColorBrush(const SkColor skColor, michael@0: const SkAlpha alpha, michael@0: IXpsOMBrush** xpsBrush) { michael@0: XPS_COLOR xpsColor = xps_color(skColor); michael@0: SkTScopedComPtr solidBrush; michael@0: HRM(this->fXpsFactory->CreateSolidColorBrush(&xpsColor, NULL, &solidBrush), michael@0: "Could not create solid color brush."); michael@0: HRM(solidBrush->SetOpacity(alpha / 255.0f), "Could not set opacity."); michael@0: HRM(solidBrush->QueryInterface(xpsBrush), "QI Fail."); michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::sideOfClamp(const SkRect& areaToFill, michael@0: const XPS_RECT& imageViewBox, michael@0: IXpsOMImageResource* image, michael@0: IXpsOMVisualCollection* visuals) { michael@0: SkTScopedComPtr areaToFillFigure; michael@0: HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure)); michael@0: michael@0: SkTScopedComPtr areaToFillPath; michael@0: HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath)); michael@0: michael@0: SkTScopedComPtr areaToFillBrush; michael@0: HRM(this->fXpsFactory->CreateImageBrush(image, michael@0: &imageViewBox, michael@0: &imageViewBox, michael@0: &areaToFillBrush), michael@0: "Could not create brush for side of clamp."); michael@0: HRM(areaToFillBrush->SetTileMode(XPS_TILE_MODE_FLIPXY), michael@0: "Could not set tile mode for side of clamp."); michael@0: HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()), michael@0: "Could not set brush for side of clamp"); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::cornerOfClamp(const SkRect& areaToFill, michael@0: const SkColor color, michael@0: IXpsOMVisualCollection* visuals) { michael@0: SkTScopedComPtr areaToFillFigure; michael@0: HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure)); michael@0: michael@0: SkTScopedComPtr areaToFillPath; michael@0: HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath)); michael@0: michael@0: SkTScopedComPtr areaToFillBrush; michael@0: HR(this->createXpsSolidColorBrush(color, 0xFF, &areaToFillBrush)); michael@0: HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()), michael@0: "Could not set brush for corner of clamp."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: static const XPS_TILE_MODE XTM_N = XPS_TILE_MODE_NONE; michael@0: static const XPS_TILE_MODE XTM_T = XPS_TILE_MODE_TILE; michael@0: static const XPS_TILE_MODE XTM_X = XPS_TILE_MODE_FLIPX; michael@0: static const XPS_TILE_MODE XTM_Y = XPS_TILE_MODE_FLIPY; michael@0: static const XPS_TILE_MODE XTM_XY = XPS_TILE_MODE_FLIPXY; michael@0: michael@0: //TODO(bungeman): In the future, should skia add None, michael@0: //handle None+Mirror and None+Repeat correctly. michael@0: //None is currently an internal hack so masks don't repeat (None+None only). michael@0: static XPS_TILE_MODE SkToXpsTileMode[SkShader::kTileModeCount+1] michael@0: [SkShader::kTileModeCount+1] = { michael@0: //Clamp //Repeat //Mirror //None michael@0: /*Clamp */ XTM_N, XTM_T, XTM_Y, XTM_N, michael@0: /*Repeat*/ XTM_T, XTM_T, XTM_Y, XTM_N, michael@0: /*Mirror*/ XTM_X, XTM_X, XTM_XY, XTM_X, michael@0: /*None */ XTM_N, XTM_N, XTM_Y, XTM_N, michael@0: }; michael@0: michael@0: HRESULT SkXPSDevice::createXpsImageBrush( michael@0: const SkBitmap& bitmap, michael@0: const SkMatrix& localMatrix, michael@0: const SkShader::TileMode (&xy)[2], michael@0: const SkAlpha alpha, michael@0: IXpsOMTileBrush** xpsBrush) { michael@0: SkDynamicMemoryWStream write; michael@0: if (!SkImageEncoder::EncodeStream(&write, bitmap, michael@0: SkImageEncoder::kPNG_Type, 100)) { michael@0: HRM(E_FAIL, "Unable to encode bitmap as png."); michael@0: } michael@0: SkMemoryStream* read = new SkMemoryStream; michael@0: read->setData(write.copyToData())->unref(); michael@0: SkTScopedComPtr readWrapper; michael@0: HRM(SkIStream::CreateFromSkStream(read, true, &readWrapper), michael@0: "Could not create stream from png data."); michael@0: michael@0: const size_t size = michael@0: SK_ARRAY_COUNT(L"/Documents/1/Resources/Images/" L_GUID_ID L".png"); michael@0: wchar_t buffer[size]; michael@0: wchar_t id[GUID_ID_LEN]; michael@0: HR(create_id(id, GUID_ID_LEN)); michael@0: swprintf_s(buffer, size, L"/Documents/1/Resources/Images/%s.png", id); michael@0: michael@0: SkTScopedComPtr imagePartUri; michael@0: HRM(this->fXpsFactory->CreatePartUri(buffer, &imagePartUri), michael@0: "Could not create image part uri."); michael@0: michael@0: SkTScopedComPtr imageResource; michael@0: HRM(this->fXpsFactory->CreateImageResource( michael@0: readWrapper.get(), michael@0: XPS_IMAGE_TYPE_PNG, michael@0: imagePartUri.get(), michael@0: &imageResource), michael@0: "Could not create image resource."); michael@0: michael@0: XPS_RECT bitmapRect = { michael@0: 0.0, 0.0, michael@0: static_cast(bitmap.width()), static_cast(bitmap.height()) michael@0: }; michael@0: SkTScopedComPtr xpsImageBrush; michael@0: HRM(this->fXpsFactory->CreateImageBrush(imageResource.get(), michael@0: &bitmapRect, &bitmapRect, michael@0: &xpsImageBrush), michael@0: "Could not create image brush."); michael@0: michael@0: if (SkShader::kClamp_TileMode != xy[0] && michael@0: SkShader::kClamp_TileMode != xy[1]) { michael@0: michael@0: HRM(xpsImageBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]), michael@0: "Could not set image tile mode"); michael@0: HRM(xpsImageBrush->SetOpacity(alpha / 255.0f), michael@0: "Could not set image opacity."); michael@0: HRM(xpsImageBrush->QueryInterface(xpsBrush), "QI failed."); michael@0: } else { michael@0: //TODO(bungeman): compute how big this really needs to be. michael@0: const SkScalar BIG = SkIntToScalar(1000); //SK_ScalarMax; michael@0: const FLOAT BIG_F = SkScalarToFLOAT(BIG); michael@0: const SkScalar bWidth = SkIntToScalar(bitmap.width()); michael@0: const SkScalar bHeight = SkIntToScalar(bitmap.height()); michael@0: michael@0: //create brush canvas michael@0: SkTScopedComPtr brushCanvas; michael@0: HRM(this->fXpsFactory->CreateCanvas(&brushCanvas), michael@0: "Could not create image brush canvas."); michael@0: SkTScopedComPtr brushVisuals; michael@0: HRM(brushCanvas->GetVisuals(&brushVisuals), michael@0: "Could not get image brush canvas visuals collection."); michael@0: michael@0: //create central figure michael@0: const SkRect bitmapPoints = SkRect::MakeLTRB(0, 0, bWidth, bHeight); michael@0: SkTScopedComPtr centralFigure; michael@0: HR(this->createXpsRect(bitmapPoints, FALSE, TRUE, ¢ralFigure)); michael@0: michael@0: SkTScopedComPtr centralPath; michael@0: HR(this->createPath(centralFigure.get(), michael@0: brushVisuals.get(), michael@0: ¢ralPath)); michael@0: HRM(xpsImageBrush->SetTileMode(XPS_TILE_MODE_FLIPXY), michael@0: "Could not set tile mode for image brush central path."); michael@0: HRM(centralPath->SetFillBrushLocal(xpsImageBrush.get()), michael@0: "Could not set fill brush for image brush central path."); michael@0: michael@0: //add left/right michael@0: if (SkShader::kClamp_TileMode == xy[0]) { michael@0: SkRect leftArea = SkRect::MakeLTRB(-BIG, 0, 0, bHeight); michael@0: XPS_RECT leftImageViewBox = { michael@0: 0.0, 0.0, michael@0: 1.0, static_cast(bitmap.height()), michael@0: }; michael@0: HR(this->sideOfClamp(leftArea, leftImageViewBox, michael@0: imageResource.get(), michael@0: brushVisuals.get())); michael@0: michael@0: SkRect rightArea = SkRect::MakeLTRB(bWidth, 0, BIG, bHeight); michael@0: XPS_RECT rightImageViewBox = { michael@0: bitmap.width() - 1.0f, 0.0f, michael@0: 1.0f, static_cast(bitmap.height()), michael@0: }; michael@0: HR(this->sideOfClamp(rightArea, rightImageViewBox, michael@0: imageResource.get(), michael@0: brushVisuals.get())); michael@0: } michael@0: michael@0: //add top/bottom michael@0: if (SkShader::kClamp_TileMode == xy[1]) { michael@0: SkRect topArea = SkRect::MakeLTRB(0, -BIG, bWidth, 0); michael@0: XPS_RECT topImageViewBox = { michael@0: 0.0, 0.0, michael@0: static_cast(bitmap.width()), 1.0, michael@0: }; michael@0: HR(this->sideOfClamp(topArea, topImageViewBox, michael@0: imageResource.get(), michael@0: brushVisuals.get())); michael@0: michael@0: SkRect bottomArea = SkRect::MakeLTRB(0, bHeight, bWidth, BIG); michael@0: XPS_RECT bottomImageViewBox = { michael@0: 0.0f, bitmap.height() - 1.0f, michael@0: static_cast(bitmap.width()), 1.0f, michael@0: }; michael@0: HR(this->sideOfClamp(bottomArea, bottomImageViewBox, michael@0: imageResource.get(), michael@0: brushVisuals.get())); michael@0: } michael@0: michael@0: //add tl, tr, bl, br michael@0: if (SkShader::kClamp_TileMode == xy[0] && michael@0: SkShader::kClamp_TileMode == xy[1]) { michael@0: michael@0: SkAutoLockPixels alp(bitmap); michael@0: michael@0: const SkColor tlColor = bitmap.getColor(0,0); michael@0: const SkRect tlArea = SkRect::MakeLTRB(-BIG, -BIG, 0, 0); michael@0: HR(this->cornerOfClamp(tlArea, tlColor, brushVisuals.get())); michael@0: michael@0: const SkColor trColor = bitmap.getColor(bitmap.width()-1,0); michael@0: const SkRect trArea = SkRect::MakeLTRB(bWidth, -BIG, BIG, 0); michael@0: HR(this->cornerOfClamp(trArea, trColor, brushVisuals.get())); michael@0: michael@0: const SkColor brColor = bitmap.getColor(bitmap.width()-1, michael@0: bitmap.height()-1); michael@0: const SkRect brArea = SkRect::MakeLTRB(bWidth, bHeight, BIG, BIG); michael@0: HR(this->cornerOfClamp(brArea, brColor, brushVisuals.get())); michael@0: michael@0: const SkColor blColor = bitmap.getColor(0,bitmap.height()-1); michael@0: const SkRect blArea = SkRect::MakeLTRB(-BIG, bHeight, 0, BIG); michael@0: HR(this->cornerOfClamp(blArea, blColor, brushVisuals.get())); michael@0: } michael@0: michael@0: //create visual brush from canvas michael@0: XPS_RECT bound = {}; michael@0: if (SkShader::kClamp_TileMode == xy[0] && michael@0: SkShader::kClamp_TileMode == xy[1]) { michael@0: michael@0: bound.x = BIG_F / -2; michael@0: bound.y = BIG_F / -2; michael@0: bound.width = BIG_F; michael@0: bound.height = BIG_F; michael@0: } else if (SkShader::kClamp_TileMode == xy[0]) { michael@0: bound.x = BIG_F / -2; michael@0: bound.y = 0.0f; michael@0: bound.width = BIG_F; michael@0: bound.height = static_cast(bitmap.height()); michael@0: } else if (SkShader::kClamp_TileMode == xy[1]) { michael@0: bound.x = 0; michael@0: bound.y = BIG_F / -2; michael@0: bound.width = static_cast(bitmap.width()); michael@0: bound.height = BIG_F; michael@0: } michael@0: SkTScopedComPtr clampBrush; michael@0: HRM(this->fXpsFactory->CreateVisualBrush(&bound, &bound, &clampBrush), michael@0: "Could not create visual brush for image brush."); michael@0: HRM(clampBrush->SetVisualLocal(brushCanvas.get()), michael@0: "Could not set canvas on visual brush for image brush."); michael@0: HRM(clampBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]), michael@0: "Could not set tile mode on visual brush for image brush."); michael@0: HRM(clampBrush->SetOpacity(alpha / 255.0f), michael@0: "Could not set opacity on visual brush for image brush."); michael@0: michael@0: HRM(clampBrush->QueryInterface(xpsBrush), "QI failed."); michael@0: } michael@0: michael@0: SkTScopedComPtr xpsMatrixToUse; michael@0: HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse)); michael@0: if (NULL != xpsMatrixToUse.get()) { michael@0: HRM((*xpsBrush)->SetTransformLocal(xpsMatrixToUse.get()), michael@0: "Could not set transform for image brush."); michael@0: } else { michael@0: //TODO(bungeman): perspective bitmaps in general. michael@0: } michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsGradientStop(const SkColor skColor, michael@0: const SkScalar offset, michael@0: IXpsOMGradientStop** xpsGradStop) { michael@0: XPS_COLOR gradStopXpsColor = xps_color(skColor); michael@0: HRM(this->fXpsFactory->CreateGradientStop(&gradStopXpsColor, michael@0: NULL, michael@0: SkScalarToFLOAT(offset), michael@0: xpsGradStop), michael@0: "Could not create gradient stop."); michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsLinearGradient(SkShader::GradientInfo info, michael@0: const SkAlpha alpha, michael@0: const SkMatrix& localMatrix, michael@0: IXpsOMMatrixTransform* xpsMatrix, michael@0: IXpsOMBrush** xpsBrush) { michael@0: XPS_POINT startPoint; michael@0: XPS_POINT endPoint; michael@0: if (NULL != xpsMatrix) { michael@0: startPoint = xps_point(info.fPoint[0]); michael@0: endPoint = xps_point(info.fPoint[1]); michael@0: } else { michael@0: transform_offsets(info.fColorOffsets, info.fColorCount, michael@0: info.fPoint[0], info.fPoint[1], michael@0: localMatrix); michael@0: startPoint = xps_point(info.fPoint[0], localMatrix); michael@0: endPoint = xps_point(info.fPoint[1], localMatrix); michael@0: } michael@0: michael@0: SkTScopedComPtr gradStop0; michael@0: HR(createXpsGradientStop(info.fColors[0], michael@0: info.fColorOffsets[0], michael@0: &gradStop0)); michael@0: michael@0: SkTScopedComPtr gradStop1; michael@0: HR(createXpsGradientStop(info.fColors[1], michael@0: info.fColorOffsets[1], michael@0: &gradStop1)); michael@0: michael@0: SkTScopedComPtr gradientBrush; michael@0: HRM(this->fXpsFactory->CreateLinearGradientBrush(gradStop0.get(), michael@0: gradStop1.get(), michael@0: &startPoint, michael@0: &endPoint, michael@0: &gradientBrush), michael@0: "Could not create linear gradient brush."); michael@0: if (NULL != xpsMatrix) { michael@0: HRM(gradientBrush->SetTransformLocal(xpsMatrix), michael@0: "Could not set transform on linear gradient brush."); michael@0: } michael@0: michael@0: SkTScopedComPtr gradStopCollection; michael@0: HRM(gradientBrush->GetGradientStops(&gradStopCollection), michael@0: "Could not get linear gradient stop collection."); michael@0: for (int i = 2; i < info.fColorCount; ++i) { michael@0: SkTScopedComPtr gradStop; michael@0: HR(createXpsGradientStop(info.fColors[i], michael@0: info.fColorOffsets[i], michael@0: &gradStop)); michael@0: HRM(gradStopCollection->Append(gradStop.get()), michael@0: "Could not add linear gradient stop."); michael@0: } michael@0: michael@0: HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)), michael@0: "Could not set spread method of linear gradient."); michael@0: michael@0: HRM(gradientBrush->SetOpacity(alpha / 255.0f), michael@0: "Could not set opacity of linear gradient brush."); michael@0: HRM(gradientBrush->QueryInterface(xpsBrush), "QI failed"); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsRadialGradient(SkShader::GradientInfo info, michael@0: const SkAlpha alpha, michael@0: const SkMatrix& localMatrix, michael@0: IXpsOMMatrixTransform* xpsMatrix, michael@0: IXpsOMBrush** xpsBrush) { michael@0: SkTScopedComPtr gradStop0; michael@0: HR(createXpsGradientStop(info.fColors[0], michael@0: info.fColorOffsets[0], michael@0: &gradStop0)); michael@0: michael@0: SkTScopedComPtr gradStop1; michael@0: HR(createXpsGradientStop(info.fColors[1], michael@0: info.fColorOffsets[1], michael@0: &gradStop1)); michael@0: michael@0: //TODO: figure out how to fake better if not affine michael@0: XPS_POINT centerPoint; michael@0: XPS_POINT gradientOrigin; michael@0: XPS_SIZE radiiSizes; michael@0: if (NULL != xpsMatrix) { michael@0: centerPoint = xps_point(info.fPoint[0]); michael@0: gradientOrigin = xps_point(info.fPoint[0]); michael@0: radiiSizes.width = SkScalarToFLOAT(info.fRadius[0]); michael@0: radiiSizes.height = SkScalarToFLOAT(info.fRadius[0]); michael@0: } else { michael@0: centerPoint = xps_point(info.fPoint[0], localMatrix); michael@0: gradientOrigin = xps_point(info.fPoint[0], localMatrix); michael@0: michael@0: SkScalar radius = info.fRadius[0]; michael@0: SkVector vec[2]; michael@0: michael@0: vec[0].set(radius, 0); michael@0: vec[1].set(0, radius); michael@0: localMatrix.mapVectors(vec, 2); michael@0: michael@0: SkScalar d0 = vec[0].length(); michael@0: SkScalar d1 = vec[1].length(); michael@0: michael@0: radiiSizes.width = SkScalarToFLOAT(d0); michael@0: radiiSizes.height = SkScalarToFLOAT(d1); michael@0: } michael@0: michael@0: SkTScopedComPtr gradientBrush; michael@0: HRM(this->fXpsFactory->CreateRadialGradientBrush(gradStop0.get(), michael@0: gradStop1.get(), michael@0: ¢erPoint, michael@0: &gradientOrigin, michael@0: &radiiSizes, michael@0: &gradientBrush), michael@0: "Could not create radial gradient brush."); michael@0: if (NULL != xpsMatrix) { michael@0: HRM(gradientBrush->SetTransformLocal(xpsMatrix), michael@0: "Could not set transform on radial gradient brush."); michael@0: } michael@0: michael@0: SkTScopedComPtr gradStopCollection; michael@0: HRM(gradientBrush->GetGradientStops(&gradStopCollection), michael@0: "Could not get radial gradient stop collection."); michael@0: for (int i = 2; i < info.fColorCount; ++i) { michael@0: SkTScopedComPtr gradStop; michael@0: HR(createXpsGradientStop(info.fColors[i], michael@0: info.fColorOffsets[i], michael@0: &gradStop)); michael@0: HRM(gradStopCollection->Append(gradStop.get()), michael@0: "Could not add radial gradient stop."); michael@0: } michael@0: michael@0: HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)), michael@0: "Could not set spread method of radial gradient."); michael@0: michael@0: HRM(gradientBrush->SetOpacity(alpha / 255.0f), michael@0: "Could not set opacity of radial gradient brush."); michael@0: HRM(gradientBrush->QueryInterface(xpsBrush), "QI failed."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsBrush(const SkPaint& skPaint, michael@0: IXpsOMBrush** brush, michael@0: const SkMatrix* parentTransform) { michael@0: const SkShader *shader = skPaint.getShader(); michael@0: if (NULL == shader) { michael@0: HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush)); michael@0: return S_OK; michael@0: } michael@0: michael@0: //Gradient shaders. michael@0: SkShader::GradientInfo info; michael@0: info.fColorCount = 0; michael@0: info.fColors = NULL; michael@0: info.fColorOffsets = NULL; michael@0: SkShader::GradientType gradientType = shader->asAGradient(&info); michael@0: michael@0: if (SkShader::kNone_GradientType == gradientType) { michael@0: //Nothing to see, move along. michael@0: michael@0: } else if (SkShader::kColor_GradientType == gradientType) { michael@0: SkASSERT(1 == info.fColorCount); michael@0: SkColor color; michael@0: info.fColors = &color; michael@0: shader->asAGradient(&info); michael@0: SkAlpha alpha = skPaint.getAlpha(); michael@0: HR(this->createXpsSolidColorBrush(color, alpha, brush)); michael@0: return S_OK; michael@0: michael@0: } else { michael@0: if (info.fColorCount == 0) { michael@0: const SkColor color = skPaint.getColor(); michael@0: HR(this->createXpsSolidColorBrush(color, 0xFF, brush)); michael@0: return S_OK; michael@0: } michael@0: michael@0: SkAutoTArray colors(info.fColorCount); michael@0: SkAutoTArray colorOffsets(info.fColorCount); michael@0: info.fColors = colors.get(); michael@0: info.fColorOffsets = colorOffsets.get(); michael@0: shader->asAGradient(&info); michael@0: michael@0: if (1 == info.fColorCount) { michael@0: SkColor color = info.fColors[0]; michael@0: SkAlpha alpha = skPaint.getAlpha(); michael@0: HR(this->createXpsSolidColorBrush(color, alpha, brush)); michael@0: return S_OK; michael@0: } michael@0: michael@0: SkMatrix localMatrix = shader->getLocalMatrix(); michael@0: if (NULL != parentTransform) { michael@0: localMatrix.preConcat(*parentTransform); michael@0: } michael@0: SkTScopedComPtr xpsMatrixToUse; michael@0: HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse)); michael@0: michael@0: if (SkShader::kLinear_GradientType == gradientType) { michael@0: HR(this->createXpsLinearGradient(info, michael@0: skPaint.getAlpha(), michael@0: localMatrix, michael@0: xpsMatrixToUse.get(), michael@0: brush)); michael@0: return S_OK; michael@0: } michael@0: michael@0: if (SkShader::kRadial_GradientType == gradientType) { michael@0: HR(this->createXpsRadialGradient(info, michael@0: skPaint.getAlpha(), michael@0: localMatrix, michael@0: xpsMatrixToUse.get(), michael@0: brush)); michael@0: return S_OK; michael@0: } michael@0: michael@0: if (SkShader::kRadial2_GradientType == gradientType || michael@0: SkShader::kConical_GradientType == gradientType) { michael@0: //simple if affine and one is 0, otherwise will have to fake michael@0: } michael@0: michael@0: if (SkShader::kSweep_GradientType == gradientType) { michael@0: //have to fake michael@0: } michael@0: } michael@0: michael@0: SkBitmap outTexture; michael@0: SkMatrix outMatrix; michael@0: SkShader::TileMode xy[2]; michael@0: SkShader::BitmapType bitmapType = shader->asABitmap(&outTexture, michael@0: &outMatrix, michael@0: xy); michael@0: switch (bitmapType) { michael@0: case SkShader::kNone_BitmapType: michael@0: break; michael@0: case SkShader::kDefault_BitmapType: { michael@0: //TODO: outMatrix?? michael@0: SkMatrix localMatrix = shader->getLocalMatrix(); michael@0: if (NULL != parentTransform) { michael@0: localMatrix.preConcat(*parentTransform); michael@0: } michael@0: michael@0: SkTScopedComPtr tileBrush; michael@0: HR(this->createXpsImageBrush(outTexture, michael@0: localMatrix, michael@0: xy, michael@0: skPaint.getAlpha(), michael@0: &tileBrush)); michael@0: michael@0: HRM(tileBrush->QueryInterface(brush), "QI failed."); michael@0: michael@0: return S_OK; michael@0: } michael@0: case SkShader::kRadial_BitmapType: michael@0: case SkShader::kSweep_BitmapType: michael@0: case SkShader::kTwoPointRadial_BitmapType: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush)); michael@0: return S_OK; michael@0: } michael@0: michael@0: static bool rect_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) { michael@0: const bool zeroWidth = (0 == paint.getStrokeWidth()); michael@0: const bool stroke = (SkPaint::kFill_Style != paint.getStyle()); michael@0: michael@0: return paint.getPathEffect() || michael@0: paint.getMaskFilter() || michael@0: paint.getRasterizer() || michael@0: (stroke && ( michael@0: (matrix.hasPerspective() && !zeroWidth) || michael@0: SkPaint::kMiter_Join != paint.getStrokeJoin() || michael@0: (SkPaint::kMiter_Join == paint.getStrokeJoin() && michael@0: paint.getStrokeMiter() < SK_ScalarSqrt2) michael@0: )) michael@0: ; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::createXpsRect(const SkRect& rect, BOOL stroke, BOOL fill, michael@0: IXpsOMGeometryFigure** xpsRect) { michael@0: const SkPoint points[4] = { michael@0: { rect.fLeft, rect.fTop }, michael@0: { rect.fRight, rect.fTop }, michael@0: { rect.fRight, rect.fBottom }, michael@0: { rect.fLeft, rect.fBottom }, michael@0: }; michael@0: return this->createXpsQuad(points, stroke, fill, xpsRect); michael@0: } michael@0: HRESULT SkXPSDevice::createXpsQuad(const SkPoint (&points)[4], michael@0: BOOL stroke, BOOL fill, michael@0: IXpsOMGeometryFigure** xpsQuad) { michael@0: // Define the start point. michael@0: XPS_POINT startPoint = xps_point(points[0]); michael@0: michael@0: // Create the figure. michael@0: HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint, xpsQuad), michael@0: "Could not create quad geometry figure."); michael@0: michael@0: // Define the type of each segment. michael@0: XPS_SEGMENT_TYPE segmentTypes[3] = { michael@0: XPS_SEGMENT_TYPE_LINE, michael@0: XPS_SEGMENT_TYPE_LINE, michael@0: XPS_SEGMENT_TYPE_LINE, michael@0: }; michael@0: michael@0: // Define the x and y coordinates of each corner of the figure. michael@0: FLOAT segmentData[6] = { michael@0: SkScalarToFLOAT(points[1].fX), SkScalarToFLOAT(points[1].fY), michael@0: SkScalarToFLOAT(points[2].fX), SkScalarToFLOAT(points[2].fY), michael@0: SkScalarToFLOAT(points[3].fX), SkScalarToFLOAT(points[3].fY), michael@0: }; michael@0: michael@0: // Describe if the segments are stroked. michael@0: BOOL segmentStrokes[3] = { michael@0: stroke, stroke, stroke, michael@0: }; michael@0: michael@0: // Add the segment data to the figure. michael@0: HRM((*xpsQuad)->SetSegments( michael@0: 3, 6, michael@0: segmentTypes , segmentData, segmentStrokes), michael@0: "Could not add segment data to quad."); michael@0: michael@0: // Set the closed and filled properties of the figure. michael@0: HRM((*xpsQuad)->SetIsClosed(stroke), "Could not set quad close."); michael@0: HRM((*xpsQuad)->SetIsFilled(fill), "Could not set quad fill."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: void SkXPSDevice::clear(SkColor color) { michael@0: //TODO: override this for XPS michael@0: SkDEBUGF(("XPS clear not yet implemented.")); michael@0: } michael@0: michael@0: void SkXPSDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode, michael@0: size_t count, const SkPoint points[], michael@0: const SkPaint& paint) { michael@0: //This will call back into the device to do the drawing. michael@0: d.drawPoints(mode, count, points, paint, true); michael@0: } michael@0: michael@0: void SkXPSDevice::drawVertices(const SkDraw&, SkCanvas::VertexMode, michael@0: int vertexCount, const SkPoint verts[], michael@0: const SkPoint texs[], const SkColor colors[], michael@0: SkXfermode* xmode, const uint16_t indices[], michael@0: int indexCount, const SkPaint& paint) { michael@0: //TODO: override this for XPS michael@0: SkDEBUGF(("XPS drawVertices not yet implemented.")); michael@0: } michael@0: michael@0: void SkXPSDevice::drawPaint(const SkDraw& d, const SkPaint& origPaint) { michael@0: const SkRect r = SkRect::MakeSize(this->fCurrentCanvasSize); michael@0: michael@0: //If trying to paint with a stroke, ignore that and fill. michael@0: SkPaint* fillPaint = const_cast(&origPaint); michael@0: SkTCopyOnFirstWrite paint(origPaint); michael@0: if (paint->getStyle() != SkPaint::kFill_Style) { michael@0: paint.writable()->setStyle(SkPaint::kFill_Style); michael@0: } michael@0: michael@0: this->internalDrawRect(d, r, false, *fillPaint); michael@0: } michael@0: michael@0: void SkXPSDevice::drawRect(const SkDraw& d, michael@0: const SkRect& r, michael@0: const SkPaint& paint) { michael@0: this->internalDrawRect(d, r, true, paint); michael@0: } michael@0: michael@0: void SkXPSDevice::drawRRect(const SkDraw& d, michael@0: const SkRRect& rr, michael@0: const SkPaint& paint) { michael@0: SkPath path; michael@0: path.addRRect(rr); michael@0: this->drawPath(d, path, paint, NULL, true); michael@0: } michael@0: michael@0: void SkXPSDevice::internalDrawRect(const SkDraw& d, michael@0: const SkRect& r, michael@0: bool transformRect, michael@0: const SkPaint& paint) { michael@0: //Exit early if there is nothing to draw. michael@0: if (d.fClip->isEmpty() || michael@0: (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) { michael@0: return; michael@0: } michael@0: michael@0: //Path the rect if we can't optimize it. michael@0: if (rect_must_be_pathed(paint, *d.fMatrix)) { michael@0: SkPath tmp; michael@0: tmp.addRect(r); michael@0: tmp.setFillType(SkPath::kWinding_FillType); michael@0: this->drawPath(d, tmp, paint, NULL, true); michael@0: return; michael@0: } michael@0: michael@0: //Create the shaded path. michael@0: SkTScopedComPtr shadedPath; michael@0: HRVM(this->fXpsFactory->CreatePath(&shadedPath), michael@0: "Could not create shaded path for rect."); michael@0: michael@0: //Create the shaded geometry. michael@0: SkTScopedComPtr shadedGeometry; michael@0: HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry), michael@0: "Could not create shaded geometry for rect."); michael@0: michael@0: //Add the geometry to the shaded path. michael@0: HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()), michael@0: "Could not set shaded geometry for rect."); michael@0: michael@0: //Set the brushes. michael@0: BOOL fill = FALSE; michael@0: BOOL stroke = FALSE; michael@0: HRV(this->shadePath(shadedPath.get(), paint, *d.fMatrix, &fill, &stroke)); michael@0: michael@0: bool xpsTransformsPath = true; michael@0: //Transform the geometry. michael@0: if (transformRect && xpsTransformsPath) { michael@0: SkTScopedComPtr xpsTransform; michael@0: HRV(this->createXpsTransform(*d.fMatrix, &xpsTransform)); michael@0: if (xpsTransform.get()) { michael@0: HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()), michael@0: "Could not set transform for rect."); michael@0: } else { michael@0: xpsTransformsPath = false; michael@0: } michael@0: } michael@0: michael@0: //Create the figure. michael@0: SkTScopedComPtr rectFigure; michael@0: { michael@0: SkPoint points[4] = { michael@0: { r.fLeft, r.fTop }, michael@0: { r.fLeft, r.fBottom }, michael@0: { r.fRight, r.fBottom }, michael@0: { r.fRight, r.fTop }, michael@0: }; michael@0: if (!xpsTransformsPath && transformRect) { michael@0: d.fMatrix->mapPoints(points, SK_ARRAY_COUNT(points)); michael@0: } michael@0: HRV(this->createXpsQuad(points, stroke, fill, &rectFigure)); michael@0: } michael@0: michael@0: //Get the figures of the shaded geometry. michael@0: SkTScopedComPtr shadedFigures; michael@0: HRVM(shadedGeometry->GetFigures(&shadedFigures), michael@0: "Could not get shaded figures for rect."); michael@0: michael@0: //Add the figure to the shaded geometry figures. michael@0: HRVM(shadedFigures->Append(rectFigure.get()), michael@0: "Could not add shaded figure for rect."); michael@0: michael@0: HRV(this->clip(shadedPath.get(), d)); michael@0: michael@0: //Add the shaded path to the current visuals. michael@0: SkTScopedComPtr currentVisuals; michael@0: HRVM(this->fCurrentXpsCanvas->GetVisuals(¤tVisuals), michael@0: "Could not get current visuals for rect."); michael@0: HRVM(currentVisuals->Append(shadedPath.get()), michael@0: "Could not add rect to current visuals."); michael@0: } michael@0: michael@0: static HRESULT close_figure(const SkTDArray& segmentTypes, michael@0: const SkTDArray& segmentStrokes, michael@0: const SkTDArray& segmentData, michael@0: BOOL stroke, BOOL fill, michael@0: IXpsOMGeometryFigure* figure, michael@0: IXpsOMGeometryFigureCollection* figures) { michael@0: // Add the segment data to the figure. michael@0: HRM(figure->SetSegments(segmentTypes.count(), segmentData.count(), michael@0: segmentTypes.begin() , segmentData.begin(), michael@0: segmentStrokes.begin()), michael@0: "Could not set path segments."); michael@0: michael@0: // Set the closed and filled properties of the figure. michael@0: HRM(figure->SetIsClosed(stroke), "Could not set path closed."); michael@0: HRM(figure->SetIsFilled(fill), "Could not set path fill."); michael@0: michael@0: // Add the figure created above to this geometry. michael@0: HRM(figures->Append(figure), "Could not add path to geometry."); michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::addXpsPathGeometry( michael@0: IXpsOMGeometryFigureCollection* xpsFigures, michael@0: BOOL stroke, BOOL fill, const SkPath& path) { michael@0: SkTDArray segmentTypes; michael@0: SkTDArray segmentStrokes; michael@0: SkTDArray segmentData; michael@0: michael@0: SkTScopedComPtr xpsFigure; michael@0: SkPath::Iter iter(path, true); michael@0: SkPoint points[4]; michael@0: SkPath::Verb verb; michael@0: while ((verb = iter.next(points)) != SkPath::kDone_Verb) { michael@0: switch (verb) { michael@0: case SkPath::kMove_Verb: { michael@0: if (NULL != xpsFigure.get()) { michael@0: HR(close_figure(segmentTypes, segmentStrokes, segmentData, michael@0: stroke, fill, michael@0: xpsFigure.get() , xpsFigures)); michael@0: xpsFigure.reset(); michael@0: segmentTypes.rewind(); michael@0: segmentStrokes.rewind(); michael@0: segmentData.rewind(); michael@0: } michael@0: // Define the start point. michael@0: XPS_POINT startPoint = xps_point(points[0]); michael@0: // Create the figure. michael@0: HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint, michael@0: &xpsFigure), michael@0: "Could not create path geometry figure."); michael@0: break; michael@0: } michael@0: case SkPath::kLine_Verb: michael@0: if (iter.isCloseLine()) break; //ignore the line, auto-closed michael@0: segmentTypes.push(XPS_SEGMENT_TYPE_LINE); michael@0: segmentStrokes.push(stroke); michael@0: segmentData.push(SkScalarToFLOAT(points[1].fX)); michael@0: segmentData.push(SkScalarToFLOAT(points[1].fY)); michael@0: break; michael@0: case SkPath::kQuad_Verb: michael@0: segmentTypes.push(XPS_SEGMENT_TYPE_QUADRATIC_BEZIER); michael@0: segmentStrokes.push(stroke); michael@0: segmentData.push(SkScalarToFLOAT(points[1].fX)); michael@0: segmentData.push(SkScalarToFLOAT(points[1].fY)); michael@0: segmentData.push(SkScalarToFLOAT(points[2].fX)); michael@0: segmentData.push(SkScalarToFLOAT(points[2].fY)); michael@0: break; michael@0: case SkPath::kCubic_Verb: michael@0: segmentTypes.push(XPS_SEGMENT_TYPE_BEZIER); michael@0: segmentStrokes.push(stroke); michael@0: segmentData.push(SkScalarToFLOAT(points[1].fX)); michael@0: segmentData.push(SkScalarToFLOAT(points[1].fY)); michael@0: segmentData.push(SkScalarToFLOAT(points[2].fX)); michael@0: segmentData.push(SkScalarToFLOAT(points[2].fY)); michael@0: segmentData.push(SkScalarToFLOAT(points[3].fX)); michael@0: segmentData.push(SkScalarToFLOAT(points[3].fY)); michael@0: break; michael@0: case SkPath::kClose_Verb: michael@0: // we ignore these, and just get the whole segment from michael@0: // the corresponding line/quad/cubic verbs michael@0: break; michael@0: default: michael@0: SkDEBUGFAIL("unexpected verb"); michael@0: break; michael@0: } michael@0: } michael@0: if (NULL != xpsFigure.get()) { michael@0: HR(close_figure(segmentTypes, segmentStrokes, segmentData, michael@0: stroke, fill, michael@0: xpsFigure.get(), xpsFigures)); michael@0: } michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::drawInverseWindingPath(const SkDraw& d, michael@0: const SkPath& devicePath, michael@0: IXpsOMPath* shadedPath) { michael@0: const SkRect universeRect = SkRect::MakeLTRB(0, 0, michael@0: this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight); michael@0: michael@0: const XPS_RECT universeRectXps = { michael@0: 0.0f, 0.0f, michael@0: SkScalarToFLOAT(this->fCurrentCanvasSize.fWidth), michael@0: SkScalarToFLOAT(this->fCurrentCanvasSize.fHeight), michael@0: }; michael@0: michael@0: //Get the geometry. michael@0: SkTScopedComPtr shadedGeometry; michael@0: HRM(shadedPath->GetGeometry(&shadedGeometry), michael@0: "Could not get shaded geometry for inverse path."); michael@0: michael@0: //Get the figures from the geometry. michael@0: SkTScopedComPtr shadedFigures; michael@0: HRM(shadedGeometry->GetFigures(&shadedFigures), michael@0: "Could not get shaded figures for inverse path."); michael@0: michael@0: HRM(shadedGeometry->SetFillRule(XPS_FILL_RULE_NONZERO), michael@0: "Could not set shaded fill rule for inverse path."); michael@0: michael@0: //Take everything drawn so far, and make a shared resource out of it. michael@0: //Replace everything drawn so far with michael@0: //inverse canvas michael@0: // old canvas of everything so far michael@0: // world shaded figure, clipped to current clip michael@0: // top canvas of everything so far, clipped to path michael@0: //Note: this is not quite right when there is nothing solid in the michael@0: //canvas of everything so far, as the bit on top will allow michael@0: //the world paint to show through. michael@0: michael@0: //Create new canvas. michael@0: SkTScopedComPtr newCanvas; michael@0: HRM(this->fXpsFactory->CreateCanvas(&newCanvas), michael@0: "Could not create inverse canvas."); michael@0: michael@0: //Save the old canvas to a dictionary on the new canvas. michael@0: SkTScopedComPtr newDictionary; michael@0: HRM(this->fXpsFactory->CreateDictionary(&newDictionary), michael@0: "Could not create inverse dictionary."); michael@0: HRM(newCanvas->SetDictionaryLocal(newDictionary.get()), michael@0: "Could not set inverse dictionary."); michael@0: michael@0: const size_t size = SK_ARRAY_COUNT(L"ID" L_GUID_ID); michael@0: wchar_t buffer[size]; michael@0: wchar_t id[GUID_ID_LEN]; michael@0: HR(create_id(id, GUID_ID_LEN, '_')); michael@0: swprintf_s(buffer, size, L"ID%s", id); michael@0: HRM(newDictionary->Append(buffer, this->fCurrentXpsCanvas.get()), michael@0: "Could not add canvas to inverse dictionary."); michael@0: michael@0: //Start drawing michael@0: SkTScopedComPtr newVisuals; michael@0: HRM(newCanvas->GetVisuals(&newVisuals), michael@0: "Could not get inverse canvas visuals."); michael@0: michael@0: //Draw old canvas from dictionary onto new canvas. michael@0: SkTScopedComPtr oldGeometry; michael@0: HRM(this->fXpsFactory->CreateGeometry(&oldGeometry), michael@0: "Could not create old inverse geometry."); michael@0: michael@0: SkTScopedComPtr oldFigures; michael@0: HRM(oldGeometry->GetFigures(&oldFigures), michael@0: "Could not get old inverse figures."); michael@0: michael@0: SkTScopedComPtr oldFigure; michael@0: HR(this->createXpsRect(universeRect, FALSE, TRUE, &oldFigure)); michael@0: HRM(oldFigures->Append(oldFigure.get()), michael@0: "Could not add old inverse figure."); michael@0: michael@0: SkTScopedComPtr oldBrush; michael@0: HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps, michael@0: &universeRectXps, michael@0: &oldBrush), michael@0: "Could not create old inverse brush."); michael@0: michael@0: SkTScopedComPtr oldPath; michael@0: HRM(this->fXpsFactory->CreatePath(&oldPath), michael@0: "Could not create old inverse path."); michael@0: HRM(oldPath->SetGeometryLocal(oldGeometry.get()), michael@0: "Could not set old inverse geometry."); michael@0: HRM(oldPath->SetFillBrushLocal(oldBrush.get()), michael@0: "Could not set old inverse fill brush."); michael@0: //the brush must be parented before setting the lookup. michael@0: HRM(newVisuals->Append(oldPath.get()), michael@0: "Could not add old inverse path to new canvas visuals."); michael@0: HRM(oldBrush->SetVisualLookup(buffer), michael@0: "Could not set old inverse brush visual lookup."); michael@0: michael@0: //Draw the clip filling shader. michael@0: SkTScopedComPtr shadedFigure; michael@0: HR(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure)); michael@0: HRM(shadedFigures->Append(shadedFigure.get()), michael@0: "Could not add inverse shaded figure."); michael@0: //the geometry is already set michael@0: HR(this->clip(shadedPath, d)); michael@0: HRM(newVisuals->Append(shadedPath), michael@0: "Could not add inverse shaded path to canvas visuals."); michael@0: michael@0: //Draw the old canvas on top, clipped to the original path. michael@0: SkTScopedComPtr topCanvas; michael@0: HRM(this->fXpsFactory->CreateCanvas(&topCanvas), michael@0: "Could not create top inverse canvas."); michael@0: //Clip the canvas to prevent alpha spill. michael@0: //This is the entire reason this canvas exists. michael@0: HR(this->clip(topCanvas.get(), d)); michael@0: michael@0: SkTScopedComPtr topGeometry; michael@0: HRM(this->fXpsFactory->CreateGeometry(&topGeometry), michael@0: "Could not create top inverse geometry."); michael@0: michael@0: SkTScopedComPtr topFigures; michael@0: HRM(topGeometry->GetFigures(&topFigures), michael@0: "Could not get top inverse figures."); michael@0: michael@0: SkTScopedComPtr topFigure; michael@0: HR(this->createXpsRect(universeRect, FALSE, TRUE, &topFigure)); michael@0: HRM(topFigures->Append(topFigure.get()), michael@0: "Could not add old inverse figure."); michael@0: michael@0: SkTScopedComPtr topBrush; michael@0: HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps, michael@0: &universeRectXps, michael@0: &topBrush), michael@0: "Could not create top inverse brush."); michael@0: michael@0: SkTScopedComPtr topPath; michael@0: HRM(this->fXpsFactory->CreatePath(&topPath), michael@0: "Could not create top inverse path."); michael@0: HRM(topPath->SetGeometryLocal(topGeometry.get()), michael@0: "Could not set top inverse geometry."); michael@0: HRM(topPath->SetFillBrushLocal(topBrush.get()), michael@0: "Could not set top inverse fill brush."); michael@0: //the brush must be parented before setting the lookup. michael@0: HRM(newVisuals->Append(topCanvas.get()), michael@0: "Could not add top canvas to inverse canvas visuals."); michael@0: SkTScopedComPtr topVisuals; michael@0: HRM(topCanvas->GetVisuals(&topVisuals), michael@0: "Could not get top inverse canvas visuals."); michael@0: HRM(topVisuals->Append(topPath.get()), michael@0: "Could not add top inverse path to top canvas visuals."); michael@0: HRM(topBrush->SetVisualLookup(buffer), michael@0: "Could not set top inverse brush visual lookup."); michael@0: michael@0: HR(this->clipToPath(topPath.get(), devicePath, XPS_FILL_RULE_NONZERO)); michael@0: michael@0: //swap current canvas to new canvas michael@0: this->fCurrentXpsCanvas.swap(newCanvas); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: void SkXPSDevice::convertToPpm(const SkMaskFilter* filter, michael@0: SkMatrix* matrix, michael@0: SkVector* ppuScale, michael@0: const SkIRect& clip, SkIRect* clipIRect) { michael@0: //This action is in unit space, but the ppm is specified in physical space. michael@0: ppuScale->fX = SkScalarDiv(this->fCurrentPixelsPerMeter.fX, michael@0: this->fCurrentUnitsPerMeter.fX); michael@0: ppuScale->fY = SkScalarDiv(this->fCurrentPixelsPerMeter.fY, michael@0: this->fCurrentUnitsPerMeter.fY); michael@0: michael@0: matrix->postScale(ppuScale->fX, ppuScale->fY); michael@0: michael@0: const SkIRect& irect = clip; michael@0: SkRect clipRect = SkRect::MakeLTRB( michael@0: SkScalarMul(SkIntToScalar(irect.fLeft), ppuScale->fX), michael@0: SkScalarMul(SkIntToScalar(irect.fTop), ppuScale->fY), michael@0: SkScalarMul(SkIntToScalar(irect.fRight), ppuScale->fX), michael@0: SkScalarMul(SkIntToScalar(irect.fBottom), ppuScale->fY)); michael@0: clipRect.roundOut(clipIRect); michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::applyMask(const SkDraw& d, michael@0: const SkMask& mask, michael@0: const SkVector& ppuScale, michael@0: IXpsOMPath* shadedPath) { michael@0: //Get the geometry object. michael@0: SkTScopedComPtr shadedGeometry; michael@0: HRM(shadedPath->GetGeometry(&shadedGeometry), michael@0: "Could not get mask shaded geometry."); michael@0: michael@0: //Get the figures from the geometry. michael@0: SkTScopedComPtr shadedFigures; michael@0: HRM(shadedGeometry->GetFigures(&shadedFigures), michael@0: "Could not get mask shaded figures."); michael@0: michael@0: SkMatrix m; michael@0: m.reset(); michael@0: m.setTranslate(SkIntToScalar(mask.fBounds.fLeft), michael@0: SkIntToScalar(mask.fBounds.fTop)); michael@0: m.postScale(SkScalarInvert(ppuScale.fX), SkScalarInvert(ppuScale.fY)); michael@0: michael@0: SkShader::TileMode xy[2]; michael@0: xy[0] = (SkShader::TileMode)3; michael@0: xy[1] = (SkShader::TileMode)3; michael@0: michael@0: SkBitmap bm; michael@0: bm.setConfig(SkBitmap::kA8_Config, michael@0: mask.fBounds.width(), michael@0: mask.fBounds.height(), michael@0: mask.fRowBytes); michael@0: bm.setPixels(mask.fImage); michael@0: michael@0: SkTScopedComPtr maskBrush; michael@0: HR(this->createXpsImageBrush(bm, m, xy, 0xFF, &maskBrush)); michael@0: HRM(shadedPath->SetOpacityMaskBrushLocal(maskBrush.get()), michael@0: "Could not set mask."); michael@0: michael@0: const SkRect universeRect = SkRect::MakeLTRB(0, 0, michael@0: this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight); michael@0: SkTScopedComPtr shadedFigure; michael@0: HRM(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure), michael@0: "Could not create mask shaded figure."); michael@0: HRM(shadedFigures->Append(shadedFigure.get()), michael@0: "Could not add mask shaded figure."); michael@0: michael@0: HR(this->clip(shadedPath, d)); michael@0: michael@0: //Add the path to the active visual collection. michael@0: SkTScopedComPtr currentVisuals; michael@0: HRM(this->fCurrentXpsCanvas->GetVisuals(¤tVisuals), michael@0: "Could not get mask current visuals."); michael@0: HRM(currentVisuals->Append(shadedPath), michael@0: "Could not add masked shaded path to current visuals."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::shadePath(IXpsOMPath* shadedPath, michael@0: const SkPaint& shaderPaint, michael@0: const SkMatrix& matrix, michael@0: BOOL* fill, BOOL* stroke) { michael@0: *fill = FALSE; michael@0: *stroke = FALSE; michael@0: michael@0: const SkPaint::Style style = shaderPaint.getStyle(); michael@0: const bool hasFill = SkPaint::kFill_Style == style michael@0: || SkPaint::kStrokeAndFill_Style == style; michael@0: const bool hasStroke = SkPaint::kStroke_Style == style michael@0: || SkPaint::kStrokeAndFill_Style == style; michael@0: michael@0: //TODO(bungeman): use dictionaries and lookups. michael@0: if (hasFill) { michael@0: *fill = TRUE; michael@0: SkTScopedComPtr fillBrush; michael@0: HR(this->createXpsBrush(shaderPaint, &fillBrush, &matrix)); michael@0: HRM(shadedPath->SetFillBrushLocal(fillBrush.get()), michael@0: "Could not set fill for shaded path."); michael@0: } michael@0: michael@0: if (hasStroke) { michael@0: *stroke = TRUE; michael@0: SkTScopedComPtr strokeBrush; michael@0: HR(this->createXpsBrush(shaderPaint, &strokeBrush, &matrix)); michael@0: HRM(shadedPath->SetStrokeBrushLocal(strokeBrush.get()), michael@0: "Could not set stroke brush for shaded path."); michael@0: HRM(shadedPath->SetStrokeThickness( michael@0: SkScalarToFLOAT(shaderPaint.getStrokeWidth())), michael@0: "Could not set shaded path stroke thickness."); michael@0: michael@0: if (0 == shaderPaint.getStrokeWidth()) { michael@0: //XPS hair width is a hack. (XPS Spec 11.6.12). michael@0: SkTScopedComPtr dashes; michael@0: HRM(shadedPath->GetStrokeDashes(&dashes), michael@0: "Could not set dashes for shaded path."); michael@0: XPS_DASH dash; michael@0: dash.length = 1.0; michael@0: dash.gap = 0.0; michael@0: HRM(dashes->Append(&dash), "Could not add dashes to shaded path."); michael@0: HRM(shadedPath->SetStrokeDashOffset(-2.0), michael@0: "Could not set dash offset for shaded path."); michael@0: } michael@0: } michael@0: return S_OK; michael@0: } michael@0: michael@0: void SkXPSDevice::drawPath(const SkDraw& d, michael@0: const SkPath& platonicPath, michael@0: const SkPaint& origPaint, michael@0: const SkMatrix* prePathMatrix, michael@0: bool pathIsMutable) { michael@0: SkTCopyOnFirstWrite paint(origPaint); michael@0: michael@0: // nothing to draw michael@0: if (d.fClip->isEmpty() || michael@0: (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { michael@0: return; michael@0: } michael@0: michael@0: SkPath modifiedPath; michael@0: const bool paintHasPathEffect = paint->getPathEffect() michael@0: || paint->getStyle() != SkPaint::kFill_Style; michael@0: michael@0: //Apply pre-path matrix [Platonic-path -> Skeletal-path]. michael@0: SkMatrix matrix = *d.fMatrix; michael@0: SkPath* skeletalPath = const_cast(&platonicPath); michael@0: if (prePathMatrix) { michael@0: if (paintHasPathEffect || paint->getRasterizer()) { michael@0: if (!pathIsMutable) { michael@0: skeletalPath = &modifiedPath; michael@0: pathIsMutable = true; michael@0: } michael@0: platonicPath.transform(*prePathMatrix, skeletalPath); michael@0: } else { michael@0: if (!matrix.preConcat(*prePathMatrix)) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: michael@0: //Apply path effect [Skeletal-path -> Fillable-path]. michael@0: SkPath* fillablePath = skeletalPath; michael@0: if (paintHasPathEffect) { michael@0: if (!pathIsMutable) { michael@0: fillablePath = &modifiedPath; michael@0: pathIsMutable = true; michael@0: } michael@0: bool fill = paint->getFillPath(*skeletalPath, fillablePath); michael@0: michael@0: SkPaint* writablePaint = paint.writable(); michael@0: writablePaint->setPathEffect(NULL); michael@0: if (fill) { michael@0: writablePaint->setStyle(SkPaint::kFill_Style); michael@0: } else { michael@0: writablePaint->setStyle(SkPaint::kStroke_Style); michael@0: writablePaint->setStrokeWidth(0); michael@0: } michael@0: } michael@0: michael@0: //Create the shaded path. This will be the path which is painted. michael@0: SkTScopedComPtr shadedPath; michael@0: HRVM(this->fXpsFactory->CreatePath(&shadedPath), michael@0: "Could not create shaded path for path."); michael@0: michael@0: //Create the geometry for the shaded path. michael@0: SkTScopedComPtr shadedGeometry; michael@0: HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry), michael@0: "Could not create shaded geometry for path."); michael@0: michael@0: //Add the geometry to the shaded path. michael@0: HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()), michael@0: "Could not add the shaded geometry to shaded path."); michael@0: michael@0: SkRasterizer* rasterizer = paint->getRasterizer(); michael@0: SkMaskFilter* filter = paint->getMaskFilter(); michael@0: michael@0: //Determine if we will draw or shade and mask. michael@0: if (rasterizer || filter) { michael@0: if (paint->getStyle() != SkPaint::kFill_Style) { michael@0: paint.writable()->setStyle(SkPaint::kFill_Style); michael@0: } michael@0: } michael@0: michael@0: //Set the brushes. michael@0: BOOL fill; michael@0: BOOL stroke; michael@0: HRV(this->shadePath(shadedPath.get(), michael@0: *paint, michael@0: *d.fMatrix, michael@0: &fill, michael@0: &stroke)); michael@0: michael@0: //Rasterizer michael@0: if (rasterizer) { michael@0: SkIRect clipIRect; michael@0: SkVector ppuScale; michael@0: this->convertToPpm(filter, michael@0: &matrix, michael@0: &ppuScale, michael@0: d.fClip->getBounds(), michael@0: &clipIRect); michael@0: michael@0: SkMask* mask = NULL; michael@0: michael@0: //[Fillable-path -> Mask] michael@0: SkMask rasteredMask; michael@0: if (rasterizer->rasterize( michael@0: *fillablePath, michael@0: matrix, michael@0: &clipIRect, michael@0: filter, //just to compute how much to draw. michael@0: &rasteredMask, michael@0: SkMask::kComputeBoundsAndRenderImage_CreateMode)) { michael@0: michael@0: SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage); michael@0: mask = &rasteredMask; michael@0: michael@0: //[Mask -> Mask] michael@0: SkMask filteredMask; michael@0: if (filter && michael@0: filter->filterMask(&filteredMask, *mask, *d.fMatrix, NULL)) { michael@0: michael@0: mask = &filteredMask; michael@0: } else { michael@0: filteredMask.fImage = NULL; michael@0: } michael@0: SkAutoMaskFreeImage filteredAmi(filteredMask.fImage); michael@0: michael@0: //Draw mask. michael@0: HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get())); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: //Mask filter michael@0: if (filter) { michael@0: SkIRect clipIRect; michael@0: SkVector ppuScale; michael@0: this->convertToPpm(filter, michael@0: &matrix, michael@0: &ppuScale, michael@0: d.fClip->getBounds(), michael@0: &clipIRect); michael@0: michael@0: //[Fillable-path -> Pixel-path] michael@0: SkPath* pixelPath = pathIsMutable ? fillablePath : &modifiedPath; michael@0: fillablePath->transform(matrix, pixelPath); michael@0: michael@0: SkMask* mask = NULL; michael@0: michael@0: //[Pixel-path -> Mask] michael@0: SkMask rasteredMask; michael@0: if (SkDraw::DrawToMask( michael@0: *pixelPath, michael@0: &clipIRect, michael@0: filter, //just to compute how much to draw. michael@0: &matrix, michael@0: &rasteredMask, michael@0: SkMask::kComputeBoundsAndRenderImage_CreateMode, michael@0: paint->getStyle())) { michael@0: michael@0: SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage); michael@0: mask = &rasteredMask; michael@0: michael@0: //[Mask -> Mask] michael@0: SkMask filteredMask; michael@0: if (filter->filterMask(&filteredMask, michael@0: rasteredMask, michael@0: matrix, michael@0: NULL)) { michael@0: mask = &filteredMask; michael@0: } else { michael@0: filteredMask.fImage = NULL; michael@0: } michael@0: SkAutoMaskFreeImage filteredAmi(filteredMask.fImage); michael@0: michael@0: //Draw mask. michael@0: HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get())); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: //Get the figures from the shaded geometry. michael@0: SkTScopedComPtr shadedFigures; michael@0: HRVM(shadedGeometry->GetFigures(&shadedFigures), michael@0: "Could not get shaded figures for shaded path."); michael@0: michael@0: bool xpsTransformsPath = true; michael@0: michael@0: //Set the fill rule. michael@0: XPS_FILL_RULE xpsFillRule; michael@0: switch (platonicPath.getFillType()) { michael@0: case SkPath::kWinding_FillType: michael@0: xpsFillRule = XPS_FILL_RULE_NONZERO; michael@0: break; michael@0: case SkPath::kEvenOdd_FillType: michael@0: xpsFillRule = XPS_FILL_RULE_EVENODD; michael@0: break; michael@0: case SkPath::kInverseWinding_FillType: { michael@0: //[Fillable-path -> Device-path] michael@0: SkPath* devicePath = pathIsMutable ? fillablePath : &modifiedPath; michael@0: fillablePath->transform(matrix, devicePath); michael@0: michael@0: HRV(this->drawInverseWindingPath(d, michael@0: *devicePath, michael@0: shadedPath.get())); michael@0: return; michael@0: } michael@0: case SkPath::kInverseEvenOdd_FillType: { michael@0: const SkRect universe = SkRect::MakeLTRB( michael@0: 0, 0, michael@0: this->fCurrentCanvasSize.fWidth, michael@0: this->fCurrentCanvasSize.fHeight); michael@0: SkTScopedComPtr addOneFigure; michael@0: HRV(this->createXpsRect(universe, FALSE, TRUE, &addOneFigure)); michael@0: HRVM(shadedFigures->Append(addOneFigure.get()), michael@0: "Could not add even-odd flip figure to shaded path."); michael@0: xpsTransformsPath = false; michael@0: xpsFillRule = XPS_FILL_RULE_EVENODD; michael@0: break; michael@0: } michael@0: default: michael@0: SkDEBUGFAIL("Unknown SkPath::FillType."); michael@0: } michael@0: HRVM(shadedGeometry->SetFillRule(xpsFillRule), michael@0: "Could not set fill rule for shaded path."); michael@0: michael@0: //Create the XPS transform, if possible. michael@0: if (xpsTransformsPath) { michael@0: SkTScopedComPtr xpsTransform; michael@0: HRV(this->createXpsTransform(matrix, &xpsTransform)); michael@0: michael@0: if (xpsTransform.get()) { michael@0: HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()), michael@0: "Could not set transform on shaded path."); michael@0: } else { michael@0: xpsTransformsPath = false; michael@0: } michael@0: } michael@0: michael@0: SkPath* devicePath = fillablePath; michael@0: if (!xpsTransformsPath) { michael@0: //[Fillable-path -> Device-path] michael@0: devicePath = pathIsMutable ? fillablePath : &modifiedPath; michael@0: fillablePath->transform(matrix, devicePath); michael@0: } michael@0: HRV(this->addXpsPathGeometry(shadedFigures.get(), michael@0: stroke, fill, *devicePath)); michael@0: michael@0: HRV(this->clip(shadedPath.get(), d)); michael@0: michael@0: //Add the path to the active visual collection. michael@0: SkTScopedComPtr currentVisuals; michael@0: HRVM(this->fCurrentXpsCanvas->GetVisuals(¤tVisuals), michael@0: "Could not get current visuals for shaded path."); michael@0: HRVM(currentVisuals->Append(shadedPath.get()), michael@0: "Could not add shaded path to current visuals."); michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::clip(IXpsOMVisual* xpsVisual, const SkDraw& d) { michael@0: SkPath clipPath; michael@0: SkAssertResult(d.fClip->getBoundaryPath(&clipPath)); michael@0: michael@0: return this->clipToPath(xpsVisual, clipPath, XPS_FILL_RULE_EVENODD); michael@0: } michael@0: HRESULT SkXPSDevice::clipToPath(IXpsOMVisual* xpsVisual, michael@0: const SkPath& clipPath, michael@0: XPS_FILL_RULE fillRule) { michael@0: //Create the geometry. michael@0: SkTScopedComPtr clipGeometry; michael@0: HRM(this->fXpsFactory->CreateGeometry(&clipGeometry), michael@0: "Could not create clip geometry."); michael@0: michael@0: //Get the figure collection of the geometry. michael@0: SkTScopedComPtr clipFigures; michael@0: HRM(clipGeometry->GetFigures(&clipFigures), michael@0: "Could not get the clip figures."); michael@0: michael@0: //Create the figures into the geometry. michael@0: HR(this->addXpsPathGeometry( michael@0: clipFigures.get(), michael@0: FALSE, TRUE, clipPath)); michael@0: michael@0: HRM(clipGeometry->SetFillRule(fillRule), michael@0: "Could not set fill rule."); michael@0: HRM(xpsVisual->SetClipGeometryLocal(clipGeometry.get()), michael@0: "Could not set clip geometry."); michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: void SkXPSDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap, michael@0: const SkMatrix& matrix, const SkPaint& paint) { michael@0: if (d.fClip->isEmpty()) { michael@0: return; michael@0: } michael@0: michael@0: SkIRect srcRect; michael@0: srcRect.set(0, 0, bitmap.width(), bitmap.height()); michael@0: michael@0: //Create the new shaded path. michael@0: SkTScopedComPtr shadedPath; michael@0: HRVM(this->fXpsFactory->CreatePath(&shadedPath), michael@0: "Could not create path for bitmap."); michael@0: michael@0: //Create the shaded geometry. michael@0: SkTScopedComPtr shadedGeometry; michael@0: HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry), michael@0: "Could not create geometry for bitmap."); michael@0: michael@0: //Add the shaded geometry to the shaded path. michael@0: HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()), michael@0: "Could not set the geometry for bitmap."); michael@0: michael@0: //Get the shaded figures from the shaded geometry. michael@0: SkTScopedComPtr shadedFigures; michael@0: HRVM(shadedGeometry->GetFigures(&shadedFigures), michael@0: "Could not get the figures for bitmap."); michael@0: michael@0: SkMatrix transform = matrix; michael@0: transform.postConcat(*d.fMatrix); michael@0: michael@0: SkTScopedComPtr xpsTransform; michael@0: HRV(this->createXpsTransform(transform, &xpsTransform)); michael@0: if (xpsTransform.get()) { michael@0: HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()), michael@0: "Could not set transform for bitmap."); michael@0: } else { michael@0: //TODO: perspective that bitmap! michael@0: } michael@0: michael@0: SkTScopedComPtr rectFigure; michael@0: if (NULL != xpsTransform.get()) { michael@0: const SkShader::TileMode xy[2] = { michael@0: SkShader::kClamp_TileMode, michael@0: SkShader::kClamp_TileMode, michael@0: }; michael@0: SkTScopedComPtr xpsImageBrush; michael@0: HRV(this->createXpsImageBrush(bitmap, michael@0: transform, michael@0: xy, michael@0: paint.getAlpha(), michael@0: &xpsImageBrush)); michael@0: HRVM(shadedPath->SetFillBrushLocal(xpsImageBrush.get()), michael@0: "Could not set bitmap brush."); michael@0: michael@0: const SkRect bitmapRect = SkRect::MakeLTRB(0, 0, michael@0: SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height())); michael@0: HRV(this->createXpsRect(bitmapRect, FALSE, TRUE, &rectFigure)); michael@0: } michael@0: HRVM(shadedFigures->Append(rectFigure.get()), michael@0: "Could not add bitmap figure."); michael@0: michael@0: //Get the current visual collection and add the shaded path to it. michael@0: SkTScopedComPtr currentVisuals; michael@0: HRVM(this->fCurrentXpsCanvas->GetVisuals(¤tVisuals), michael@0: "Could not get current visuals for bitmap"); michael@0: HRVM(currentVisuals->Append(shadedPath.get()), michael@0: "Could not add bitmap to current visuals."); michael@0: michael@0: HRV(this->clip(shadedPath.get(), d)); michael@0: } michael@0: michael@0: void SkXPSDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap, michael@0: int x, int y, michael@0: const SkPaint& paint) { michael@0: //TODO: override this for XPS michael@0: SkDEBUGF(("XPS drawSprite not yet implemented.")); michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::CreateTypefaceUse(const SkPaint& paint, michael@0: TypefaceUse** typefaceUse) { michael@0: SkAutoResolveDefaultTypeface typeface(paint.getTypeface()); michael@0: michael@0: //Check cache. michael@0: const SkFontID typefaceID = typeface->uniqueID(); michael@0: if (!this->fTypefaces.empty()) { michael@0: TypefaceUse* current = &this->fTypefaces.front(); michael@0: const TypefaceUse* last = &this->fTypefaces.back(); michael@0: for (; current <= last; ++current) { michael@0: if (current->typefaceId == typefaceID) { michael@0: *typefaceUse = current; michael@0: return S_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: //TODO: create glyph only fonts michael@0: //and let the host deal with what kind of font we're looking at. michael@0: XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_RESTRICTED; michael@0: michael@0: SkTScopedComPtr fontStream; michael@0: int ttcIndex; michael@0: SkStream* fontData = typeface->openStream(&ttcIndex); michael@0: //TODO: cannot handle FON fonts. michael@0: HRM(SkIStream::CreateFromSkStream(fontData, true, &fontStream), michael@0: "Could not create font stream."); michael@0: michael@0: const size_t size = michael@0: SK_ARRAY_COUNT(L"/Resources/Fonts/" L_GUID_ID L".odttf"); michael@0: wchar_t buffer[size]; michael@0: wchar_t id[GUID_ID_LEN]; michael@0: HR(create_id(id, GUID_ID_LEN)); michael@0: swprintf_s(buffer, size, L"/Resources/Fonts/%s.odttf", id); michael@0: michael@0: SkTScopedComPtr partUri; michael@0: HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri), michael@0: "Could not create font resource part uri."); michael@0: michael@0: SkTScopedComPtr xpsFontResource; michael@0: HRM(this->fXpsFactory->CreateFontResource(fontStream.get(), michael@0: embedding, michael@0: partUri.get(), michael@0: FALSE, michael@0: &xpsFontResource), michael@0: "Could not create font resource."); michael@0: michael@0: //TODO: change openStream to return -1 for non-ttc, get rid of this. michael@0: uint8_t* data = (uint8_t*)fontData->getMemoryBase(); michael@0: bool isTTC = (data && michael@0: fontData->getLength() >= sizeof(SkTTCFHeader) && michael@0: ((SkTTCFHeader*)data)->ttcTag == SkTTCFHeader::TAG); michael@0: michael@0: TypefaceUse& newTypefaceUse = this->fTypefaces.push_back(); michael@0: newTypefaceUse.typefaceId = typefaceID; michael@0: newTypefaceUse.ttcIndex = isTTC ? ttcIndex : -1; michael@0: newTypefaceUse.fontData = fontData; michael@0: newTypefaceUse.xpsFont = xpsFontResource.release(); michael@0: michael@0: SkAutoGlyphCache agc(paint, NULL, &SkMatrix::I()); michael@0: SkGlyphCache* glyphCache = agc.getCache(); michael@0: unsigned int glyphCount = glyphCache->getGlyphCount(); michael@0: newTypefaceUse.glyphsUsed = new SkBitSet(glyphCount); michael@0: michael@0: *typefaceUse = &newTypefaceUse; michael@0: return S_OK; michael@0: } michael@0: michael@0: HRESULT SkXPSDevice::AddGlyphs(const SkDraw& d, michael@0: IXpsOMObjectFactory* xpsFactory, michael@0: IXpsOMCanvas* canvas, michael@0: TypefaceUse* font, michael@0: LPCWSTR text, michael@0: XPS_GLYPH_INDEX* xpsGlyphs, michael@0: UINT32 xpsGlyphsLen, michael@0: XPS_POINT *origin, michael@0: FLOAT fontSize, michael@0: XPS_STYLE_SIMULATION sims, michael@0: const SkMatrix& transform, michael@0: const SkPaint& paint) { michael@0: SkTScopedComPtr glyphs; michael@0: HRM(xpsFactory->CreateGlyphs(font->xpsFont, &glyphs), "Could not create glyphs."); michael@0: HRM(glyphs->SetFontFaceIndex(font->ttcIndex), "Could not set glyph font face index."); michael@0: michael@0: //XPS uses affine transformations for everything... michael@0: //...except positioning text. michael@0: bool useCanvasForClip; michael@0: if ((transform.getType() & ~SkMatrix::kTranslate_Mask) == 0) { michael@0: origin->x += SkScalarToFLOAT(transform.getTranslateX()); michael@0: origin->y += SkScalarToFLOAT(transform.getTranslateY()); michael@0: useCanvasForClip = false; michael@0: } else { michael@0: SkTScopedComPtr xpsMatrixToUse; michael@0: HR(this->createXpsTransform(transform, &xpsMatrixToUse)); michael@0: if (xpsMatrixToUse.get()) { michael@0: HRM(glyphs->SetTransformLocal(xpsMatrixToUse.get()), michael@0: "Could not set transform matrix."); michael@0: useCanvasForClip = true; michael@0: } else { michael@0: SkDEBUGFAIL("Attempt to add glyphs in perspective."); michael@0: useCanvasForClip = false; michael@0: } michael@0: } michael@0: michael@0: SkTScopedComPtr glyphsEditor; michael@0: HRM(glyphs->GetGlyphsEditor(&glyphsEditor), "Could not get glyph editor."); michael@0: michael@0: if (NULL != text) { michael@0: HRM(glyphsEditor->SetUnicodeString(text), michael@0: "Could not set unicode string."); michael@0: } michael@0: michael@0: if (NULL != xpsGlyphs) { michael@0: HRM(glyphsEditor->SetGlyphIndices(xpsGlyphsLen, xpsGlyphs), michael@0: "Could not set glyphs."); michael@0: } michael@0: michael@0: HRM(glyphsEditor->ApplyEdits(), "Could not apply glyph edits."); michael@0: michael@0: SkTScopedComPtr xpsFillBrush; michael@0: HR(this->createXpsBrush( michael@0: paint, michael@0: &xpsFillBrush, michael@0: useCanvasForClip ? NULL : &transform)); michael@0: michael@0: HRM(glyphs->SetFillBrushLocal(xpsFillBrush.get()), michael@0: "Could not set fill brush."); michael@0: michael@0: HRM(glyphs->SetOrigin(origin), "Could not set glyph origin."); michael@0: michael@0: HRM(glyphs->SetFontRenderingEmSize(fontSize), michael@0: "Could not set font size."); michael@0: michael@0: HRM(glyphs->SetStyleSimulations(sims), michael@0: "Could not set style simulations."); michael@0: michael@0: SkTScopedComPtr visuals; michael@0: HRM(canvas->GetVisuals(&visuals), "Could not get glyph canvas visuals."); michael@0: michael@0: if (!useCanvasForClip) { michael@0: HR(this->clip(glyphs.get(), d)); michael@0: HRM(visuals->Append(glyphs.get()), "Could not add glyphs to canvas."); michael@0: } else { michael@0: SkTScopedComPtr glyphCanvas; michael@0: HRM(this->fXpsFactory->CreateCanvas(&glyphCanvas), michael@0: "Could not create glyph canvas."); michael@0: michael@0: SkTScopedComPtr glyphCanvasVisuals; michael@0: HRM(glyphCanvas->GetVisuals(&glyphCanvasVisuals), michael@0: "Could not get glyph visuals collection."); michael@0: michael@0: HRM(glyphCanvasVisuals->Append(glyphs.get()), michael@0: "Could not add glyphs to page."); michael@0: HR(this->clip(glyphCanvas.get(), d)); michael@0: michael@0: HRM(visuals->Append(glyphCanvas.get()), michael@0: "Could not add glyph canvas to page."); michael@0: } michael@0: michael@0: return S_OK; michael@0: } michael@0: michael@0: struct SkXPSDrawProcs : public SkDrawProcs { michael@0: public: michael@0: /** [in] Advance width and offsets for glyphs measured in michael@0: hundredths of the font em size (XPS Spec 5.1.3). */ michael@0: FLOAT centemPerUnit; michael@0: /** [in,out] The accumulated glyphs used in the current typeface. */ michael@0: SkBitSet* glyphUse; michael@0: /** [out] The glyphs to draw. */ michael@0: SkTDArray xpsGlyphs; michael@0: }; michael@0: michael@0: static void xps_draw_1_glyph(const SkDraw1Glyph& state, michael@0: SkFixed x, SkFixed y, michael@0: const SkGlyph& skGlyph) { michael@0: SkASSERT(skGlyph.fWidth > 0 && skGlyph.fHeight > 0); michael@0: michael@0: SkXPSDrawProcs* procs = static_cast(state.fDraw->fProcs); michael@0: michael@0: //Draw pre-adds half the sampling frequency for floor rounding. michael@0: x -= state.fHalfSampleX; michael@0: y -= state.fHalfSampleY; michael@0: michael@0: XPS_GLYPH_INDEX* xpsGlyph = procs->xpsGlyphs.append(); michael@0: uint16_t glyphID = skGlyph.getGlyphID(); michael@0: procs->glyphUse->setBit(glyphID, true); michael@0: xpsGlyph->index = glyphID; michael@0: if (1 == procs->xpsGlyphs.count()) { michael@0: xpsGlyph->advanceWidth = 0.0f; michael@0: xpsGlyph->horizontalOffset = SkFixedToFloat(x) * procs->centemPerUnit; michael@0: xpsGlyph->verticalOffset = SkFixedToFloat(y) * -procs->centemPerUnit; michael@0: } else { michael@0: const XPS_GLYPH_INDEX& first = procs->xpsGlyphs[0]; michael@0: xpsGlyph->advanceWidth = 0.0f; michael@0: xpsGlyph->horizontalOffset = (SkFixedToFloat(x) * procs->centemPerUnit) michael@0: - first.horizontalOffset; michael@0: xpsGlyph->verticalOffset = (SkFixedToFloat(y) * -procs->centemPerUnit) michael@0: - first.verticalOffset; michael@0: } michael@0: } michael@0: michael@0: static void text_draw_init(const SkPaint& paint, michael@0: const void* text, size_t byteLength, michael@0: SkBitSet& glyphsUsed, michael@0: SkDraw& myDraw, SkXPSDrawProcs& procs) { michael@0: procs.fD1GProc = xps_draw_1_glyph; michael@0: size_t numGlyphGuess; michael@0: switch (paint.getTextEncoding()) { michael@0: case SkPaint::kUTF8_TextEncoding: michael@0: numGlyphGuess = SkUTF8_CountUnichars( michael@0: static_cast(text), michael@0: byteLength); michael@0: break; michael@0: case SkPaint::kUTF16_TextEncoding: michael@0: numGlyphGuess = SkUTF16_CountUnichars( michael@0: static_cast(text), michael@0: byteLength); michael@0: break; michael@0: case SkPaint::kGlyphID_TextEncoding: michael@0: numGlyphGuess = byteLength / 2; michael@0: break; michael@0: default: michael@0: SK_DEBUGBREAK(true); michael@0: } michael@0: procs.xpsGlyphs.setReserve(numGlyphGuess); michael@0: procs.glyphUse = &glyphsUsed; michael@0: procs.centemPerUnit = 100.0f / SkScalarToFLOAT(paint.getTextSize()); michael@0: michael@0: myDraw.fProcs = &procs; michael@0: } michael@0: michael@0: static bool text_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) { michael@0: const SkPaint::Style style = paint.getStyle(); michael@0: return matrix.hasPerspective() michael@0: || SkPaint::kStroke_Style == style michael@0: || SkPaint::kStrokeAndFill_Style == style michael@0: || paint.getMaskFilter() michael@0: || paint.getRasterizer() michael@0: ; michael@0: } michael@0: michael@0: void SkXPSDevice::drawText(const SkDraw& d, michael@0: const void* text, size_t byteLen, michael@0: SkScalar x, SkScalar y, michael@0: const SkPaint& paint) { michael@0: if (byteLen < 1) return; michael@0: michael@0: if (text_must_be_pathed(paint, *d.fMatrix)) { michael@0: SkPath path; michael@0: paint.getTextPath(text, byteLen, x, y, &path); michael@0: this->drawPath(d, path, paint, NULL, true); michael@0: //TODO: add automation "text" michael@0: return; michael@0: } michael@0: michael@0: TypefaceUse* typeface; michael@0: HRV(CreateTypefaceUse(paint, &typeface)); michael@0: michael@0: SkDraw myDraw(d); michael@0: myDraw.fMatrix = &SkMatrix::I(); michael@0: SkXPSDrawProcs procs; michael@0: text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs); michael@0: michael@0: myDraw.drawText(static_cast(text), byteLen, x, y, paint); michael@0: michael@0: // SkDraw may have clipped out the glyphs, so we need to check michael@0: if (procs.xpsGlyphs.count() == 0) { michael@0: return; michael@0: } michael@0: michael@0: XPS_POINT origin = { michael@0: procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit, michael@0: procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit, michael@0: }; michael@0: procs.xpsGlyphs[0].horizontalOffset = 0.0f; michael@0: procs.xpsGlyphs[0].verticalOffset = 0.0f; michael@0: michael@0: HRV(AddGlyphs(d, michael@0: this->fXpsFactory.get(), michael@0: this->fCurrentXpsCanvas.get(), michael@0: typeface, michael@0: NULL, michael@0: procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(), michael@0: &origin, michael@0: SkScalarToFLOAT(paint.getTextSize()), michael@0: XPS_STYLE_SIMULATION_NONE, michael@0: *d.fMatrix, michael@0: paint)); michael@0: } michael@0: michael@0: void SkXPSDevice::drawPosText(const SkDraw& d, michael@0: const void* text, size_t byteLen, michael@0: const SkScalar pos[], michael@0: SkScalar constY, int scalarsPerPos, michael@0: const SkPaint& paint) { michael@0: if (byteLen < 1) return; michael@0: michael@0: if (text_must_be_pathed(paint, *d.fMatrix)) { michael@0: SkPath path; michael@0: //TODO: make this work, Draw currently does not handle as well. michael@0: //paint.getTextPath(text, byteLength, x, y, &path); michael@0: //this->drawPath(d, path, paint, NULL, true); michael@0: //TODO: add automation "text" michael@0: return; michael@0: } michael@0: michael@0: TypefaceUse* typeface; michael@0: HRV(CreateTypefaceUse(paint, &typeface)); michael@0: michael@0: SkDraw myDraw(d); michael@0: myDraw.fMatrix = &SkMatrix::I(); michael@0: SkXPSDrawProcs procs; michael@0: text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs); michael@0: michael@0: myDraw.drawPosText(static_cast(text), byteLen, michael@0: pos, constY, scalarsPerPos, michael@0: paint); michael@0: michael@0: // SkDraw may have clipped out the glyphs, so we need to check michael@0: if (procs.xpsGlyphs.count() == 0) { michael@0: return; michael@0: } michael@0: michael@0: XPS_POINT origin = { michael@0: procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit, michael@0: procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit, michael@0: }; michael@0: procs.xpsGlyphs[0].horizontalOffset = 0.0f; michael@0: procs.xpsGlyphs[0].verticalOffset = 0.0f; michael@0: michael@0: HRV(AddGlyphs(d, michael@0: this->fXpsFactory.get(), michael@0: this->fCurrentXpsCanvas.get(), michael@0: typeface, michael@0: NULL, michael@0: procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(), michael@0: &origin, michael@0: SkScalarToFLOAT(paint.getTextSize()), michael@0: XPS_STYLE_SIMULATION_NONE, michael@0: *d.fMatrix, michael@0: paint)); michael@0: } michael@0: michael@0: void SkXPSDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len, michael@0: const SkPath& path, const SkMatrix* matrix, michael@0: const SkPaint& paint) { michael@0: //This will call back into the device to do the drawing. michael@0: d.drawTextOnPath((const char*)text, len, path, matrix, paint); michael@0: } michael@0: michael@0: void SkXPSDevice::drawDevice(const SkDraw& d, SkBaseDevice* dev, michael@0: int x, int y, michael@0: const SkPaint&) { michael@0: SkXPSDevice* that = static_cast(dev); michael@0: michael@0: SkTScopedComPtr xpsTransform; michael@0: XPS_MATRIX rawTransform = { michael@0: 1.0f, michael@0: 0.0f, michael@0: 0.0f, michael@0: 1.0f, michael@0: static_cast(x), michael@0: static_cast(y), michael@0: }; michael@0: HRVM(this->fXpsFactory->CreateMatrixTransform(&rawTransform, &xpsTransform), michael@0: "Could not create layer transform."); michael@0: HRVM(that->fCurrentXpsCanvas->SetTransformLocal(xpsTransform.get()), michael@0: "Could not set layer transform."); michael@0: michael@0: //Get the current visual collection and add the layer to it. michael@0: SkTScopedComPtr currentVisuals; michael@0: HRVM(this->fCurrentXpsCanvas->GetVisuals(¤tVisuals), michael@0: "Could not get current visuals for layer."); michael@0: HRVM(currentVisuals->Append(that->fCurrentXpsCanvas.get()), michael@0: "Could not add layer to current visuals."); michael@0: } michael@0: michael@0: bool SkXPSDevice::onReadPixels(const SkBitmap& bitmap, int x, int y, michael@0: SkCanvas::Config8888) { michael@0: return false; michael@0: } michael@0: michael@0: SkBaseDevice* SkXPSDevice::onCreateDevice(const SkImageInfo&, Usage) { michael@0: //Conditional for bug compatibility with PDF device. michael@0: #if 0 michael@0: if (SkBaseDevice::kGeneral_Usage == usage) { michael@0: return NULL; michael@0: SK_CRASH(); michael@0: //To what stream do we write? michael@0: //SkXPSDevice* dev = new SkXPSDevice(this); michael@0: //SkSize s = SkSize::Make(width, height); michael@0: //dev->BeginCanvas(s, s, SkMatrix::I()); michael@0: //return dev; michael@0: } michael@0: #endif michael@0: return new SkXPSDevice(this->fXpsFactory.get()); michael@0: } michael@0: michael@0: SkXPSDevice::SkXPSDevice(IXpsOMObjectFactory* xpsFactory) michael@0: : SkBitmapDevice(make_fake_bitmap(10000, 10000)) michael@0: , fCurrentPage(0) { michael@0: michael@0: HRVM(CoCreateInstance( michael@0: CLSID_XpsOMObjectFactory, michael@0: NULL, michael@0: CLSCTX_INPROC_SERVER, michael@0: IID_PPV_ARGS(&this->fXpsFactory)), michael@0: "Could not create factory for layer."); michael@0: michael@0: HRVM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas), michael@0: "Could not create canvas for layer."); michael@0: } michael@0: michael@0: bool SkXPSDevice::allowImageFilter(const SkImageFilter*) { michael@0: return false; michael@0: }