Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkXPSDevice.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9#if defined(SK_BUILD_FOR_WIN)
10
12
13#ifndef UNICODE
14#define UNICODE
15#endif
16#ifndef _UNICODE
17#define _UNICODE
18#endif
19#include <ObjBase.h>
20#include <XpsObjectModel.h>
21#include <T2EmbApi.h>
22#include <FontSub.h>
23#include <limits>
24
26#include "include/core/SkData.h"
33#include "include/core/SkSize.h"
40#include "src/base/SkEndian.h"
41#include "src/base/SkTLazy.h"
42#include "src/base/SkUtils.h"
43#include "src/core/SkDraw.h"
44#include "src/core/SkFontPriv.h"
45#include "src/core/SkGeometry.h"
55#include "src/text/GlyphRun.h"
60#include "src/xps/SkXPSDevice.h"
61
62using namespace skia_private;
63
64//Windows defines a FLOAT type,
65//make it clear when converting a scalar that this is what is wanted.
66#define SkScalarToFLOAT(n) SkScalarToFloat(n)
67
68//Placeholder representation of a GUID from createId.
69#define L_GUID_ID L"XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX"
70//Length of GUID representation from createId, including nullptr terminator.
71#define GUID_ID_LEN std::size(L_GUID_ID)
72
73/**
74 Formats a GUID and places it into buffer.
75 buffer should have space for at least GUID_ID_LEN wide characters.
76 The string will always be wchar null terminated.
77 XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0
78 @return -1 if there was an error, > 0 if success.
79 */
80static int format_guid(const GUID& guid,
81 wchar_t* buffer, size_t bufferSize,
82 wchar_t sep = '-') {
83 SkASSERT(bufferSize >= GUID_ID_LEN);
84 return swprintf_s(buffer,
85 bufferSize,
86 L"%08lX%c%04X%c%04X%c%02X%02X%c%02X%02X%02X%02X%02X%02X",
87 guid.Data1,
88 sep,
89 guid.Data2,
90 sep,
91 guid.Data3,
92 sep,
93 guid.Data4[0],
94 guid.Data4[1],
95 sep,
96 guid.Data4[2],
97 guid.Data4[3],
98 guid.Data4[4],
99 guid.Data4[5],
100 guid.Data4[6],
101 guid.Data4[7]);
102}
103
104HRESULT SkXPSDevice::createId(wchar_t* buffer, size_t bufferSize, wchar_t sep) {
105 GUID guid = {};
106#ifdef SK_XPS_USE_DETERMINISTIC_IDS
107 guid.Data1 = fNextId++;
108 // The following make this a valid Type4 UUID.
109 guid.Data3 = 0x4000;
110 guid.Data4[0] = 0x80;
111#else
112 HRM(CoCreateGuid(&guid), "Could not create GUID for id.");
113#endif
114
115 if (format_guid(guid, buffer, bufferSize, sep) == -1) {
116 HRM(E_UNEXPECTED, "Could not format GUID into id.");
117 }
118
119 return S_OK;
120}
121
122SkXPSDevice::SkXPSDevice(SkISize s)
124 , fCurrentPage(0)
125 , fTopTypefaces(&fTypefaces) {}
126
127SkXPSDevice::~SkXPSDevice() {}
128
129bool SkXPSDevice::beginPortfolio(SkWStream* outputStream, IXpsOMObjectFactory* factory) {
130 SkASSERT(factory);
131 fXpsFactory.reset(SkRefComPtr(factory));
132 HRB(SkWIStream::CreateFromSkWStream(outputStream, &this->fOutputStream));
133 return true;
134}
135
136bool SkXPSDevice::beginSheet(
137 const SkVector& unitsPerMeter,
138 const SkVector& pixelsPerMeter,
139 const SkSize& trimSize,
140 const SkRect* mediaBox,
141 const SkRect* bleedBox,
142 const SkRect* artBox,
143 const SkRect* cropBox) {
144 ++this->fCurrentPage;
145
146 //For simplicity, just write everything out in geometry units,
147 //then have a base canvas do the scale to physical units.
148 this->fCurrentCanvasSize = trimSize;
149 this->fCurrentUnitsPerMeter = unitsPerMeter;
150 this->fCurrentPixelsPerMeter = pixelsPerMeter;
151 return this->createCanvasForLayer();
152}
153
154bool SkXPSDevice::createCanvasForLayer() {
155 SkASSERT(fXpsFactory);
156 fCurrentXpsCanvas.reset();
157 HRB(fXpsFactory->CreateCanvas(&fCurrentXpsCanvas));
158 return true;
159}
160
161template <typename T> static constexpr size_t sk_digits_in() {
162 return static_cast<size_t>(std::numeric_limits<T>::digits10 + 1);
163}
164
165HRESULT SkXPSDevice::createXpsThumbnail(IXpsOMPage* page,
166 const unsigned int pageNum,
167 IXpsOMImageResource** image) {
168 SkTScopedComPtr<IXpsOMThumbnailGenerator> thumbnailGenerator;
169 HRM(CoCreateInstance(
170 CLSID_XpsOMThumbnailGenerator,
171 nullptr,
172 CLSCTX_INPROC_SERVER,
173 IID_PPV_ARGS(&thumbnailGenerator)),
174 "Could not create thumbnail generator.");
175
176 SkTScopedComPtr<IOpcPartUri> partUri;
177 constexpr size_t size = std::max(
178 std::size(L"/Documents/1/Metadata/.png") + sk_digits_in<decltype(pageNum)>(),
179 std::size(L"/Metadata/" L_GUID_ID L".png"));
180 wchar_t buffer[size];
181 if (pageNum > 0) {
182 swprintf_s(buffer, size, L"/Documents/1/Metadata/%u.png", pageNum);
183 } else {
184 wchar_t id[GUID_ID_LEN];
185 HR(this->createId(id, GUID_ID_LEN));
186 swprintf_s(buffer, size, L"/Metadata/%s.png", id);
187 }
188 HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
189 "Could not create thumbnail part uri.");
190
191 HRM(thumbnailGenerator->GenerateThumbnail(page,
192 XPS_IMAGE_TYPE_PNG,
193 XPS_THUMBNAIL_SIZE_LARGE,
194 partUri.get(),
195 image),
196 "Could not generate thumbnail.");
197
198 return S_OK;
199}
200
201HRESULT SkXPSDevice::createXpsPage(const XPS_SIZE& pageSize,
202 IXpsOMPage** page) {
203 constexpr size_t size =
204 std::size(L"/Documents/1/Pages/.fpage")
205 + sk_digits_in<decltype(fCurrentPage)>();
206 wchar_t buffer[size];
207 swprintf_s(buffer, size, L"/Documents/1/Pages/%u.fpage",
208 this->fCurrentPage);
209 SkTScopedComPtr<IOpcPartUri> partUri;
210 HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
211 "Could not create page part uri.");
212
213 //If the language is unknown, use "und" (XPS Spec 2.3.5.1).
214 HRM(this->fXpsFactory->CreatePage(&pageSize,
215 L"und",
216 partUri.get(),
217 page),
218 "Could not create page.");
219
220 return S_OK;
221}
222
223HRESULT SkXPSDevice::initXpsDocumentWriter(IXpsOMImageResource* image) {
224 //Create package writer.
225 {
226 SkTScopedComPtr<IOpcPartUri> partUri;
227 HRM(this->fXpsFactory->CreatePartUri(L"/FixedDocumentSequence.fdseq",
228 &partUri),
229 "Could not create document sequence part uri.");
230 HRM(this->fXpsFactory->CreatePackageWriterOnStream(
231 this->fOutputStream.get(),
232 TRUE,
233 XPS_INTERLEAVING_OFF, //XPS_INTERLEAVING_ON,
234 partUri.get(),
235 nullptr,
236 image,
237 nullptr,
238 nullptr,
239 &this->fPackageWriter),
240 "Could not create package writer.");
241 }
242
243 //Begin the lone document.
244 {
245 SkTScopedComPtr<IOpcPartUri> partUri;
246 HRM(this->fXpsFactory->CreatePartUri(
247 L"/Documents/1/FixedDocument.fdoc",
248 &partUri),
249 "Could not create fixed document part uri.");
250 HRM(this->fPackageWriter->StartNewDocument(partUri.get(),
251 nullptr,
252 nullptr,
253 nullptr,
254 nullptr),
255 "Could not start document.");
256 }
257
258 return S_OK;
259}
260
261bool SkXPSDevice::endSheet() {
262 //XPS is fixed at 96dpi (XPS Spec 11.1).
263 static const float xpsDPI = 96.0f;
264 static const float inchesPerMeter = 10000.0f / 254.0f;
265 static const float targetUnitsPerMeter = xpsDPI * inchesPerMeter;
266 const float scaleX = targetUnitsPerMeter
267 / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fX);
268 const float scaleY = targetUnitsPerMeter
269 / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fY);
270
271 //Create the scale canvas.
272 SkTScopedComPtr<IXpsOMCanvas> scaleCanvas;
273 HRBM(this->fXpsFactory->CreateCanvas(&scaleCanvas),
274 "Could not create scale canvas.");
275 SkTScopedComPtr<IXpsOMVisualCollection> scaleCanvasVisuals;
276 HRBM(scaleCanvas->GetVisuals(&scaleCanvasVisuals),
277 "Could not get scale canvas visuals.");
278
279 SkTScopedComPtr<IXpsOMMatrixTransform> geomToPhys;
280 XPS_MATRIX rawGeomToPhys = { scaleX, 0, 0, scaleY, 0, 0, };
281 HRBM(this->fXpsFactory->CreateMatrixTransform(&rawGeomToPhys, &geomToPhys),
282 "Could not create geometry to physical transform.");
283 HRBM(scaleCanvas->SetTransformLocal(geomToPhys.get()),
284 "Could not set transform on scale canvas.");
285
286 //Add the content canvas to the scale canvas.
287 HRBM(scaleCanvasVisuals->Append(this->fCurrentXpsCanvas.get()),
288 "Could not add base canvas to scale canvas.");
289
290 //Create the page.
291 XPS_SIZE pageSize = {
292 SkScalarToFLOAT(this->fCurrentCanvasSize.width()) * scaleX,
293 SkScalarToFLOAT(this->fCurrentCanvasSize.height()) * scaleY,
294 };
295 SkTScopedComPtr<IXpsOMPage> page;
296 HRB(this->createXpsPage(pageSize, &page));
297
298 SkTScopedComPtr<IXpsOMVisualCollection> pageVisuals;
299 HRBM(page->GetVisuals(&pageVisuals), "Could not get page visuals.");
300
301 //Add the scale canvas to the page.
302 HRBM(pageVisuals->Append(scaleCanvas.get()),
303 "Could not add scale canvas to page.");
304
305 //Create the package writer if it hasn't been created yet.
306 if (nullptr == this->fPackageWriter.get()) {
307 SkTScopedComPtr<IXpsOMImageResource> image;
308 //Ignore return, thumbnail is completely optional.
309 this->createXpsThumbnail(page.get(), 0, &image);
310
311 HRB(this->initXpsDocumentWriter(image.get()));
312 }
313
314 HRBM(this->fPackageWriter->AddPage(page.get(),
315 &pageSize,
316 nullptr,
317 nullptr,
318 nullptr,
319 nullptr),
320 "Could not write the page.");
321 this->fCurrentXpsCanvas.reset();
322
323 return true;
324}
325
326static HRESULT subset_typeface(const SkXPSDevice::TypefaceUse& current) {
327 //The CreateFontPackage API is only supported on desktop, not in UWP
328 #if defined(SK_WINUWP)
329 return E_NOTIMPL;
330 #else
331 //CreateFontPackage wants unsigned short.
332 //Microsoft, Y U NO stdint.h?
333 std::vector<unsigned short> keepList;
334 current.glyphsUsed.forEachSetIndex([&keepList](size_t v) {
335 keepList.push_back((unsigned short)v);
336 });
337
338 int ttcCount = (current.ttcIndex + 1);
339
340 //The following are declared with the types required by CreateFontPackage.
341 unsigned char *fontPackageBufferRaw = nullptr;
342 unsigned long fontPackageBufferSize;
343 unsigned long bytesWritten;
344 unsigned long result = CreateFontPackage(
345 (const unsigned char *) current.fontData->getMemoryBase(),
346 (unsigned long) current.fontData->getLength(),
347 &fontPackageBufferRaw,
348 &fontPackageBufferSize,
349 &bytesWritten,
350 TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST | (ttcCount > 0 ? TTFCFP_FLAGS_TTC : 0),
351 current.ttcIndex,
352 TTFCFP_SUBSET,
353 0,
354 0,
355 0,
356 keepList.data(),
357 SkTo<unsigned short>(keepList.size()),
360 sk_free,
361 nullptr);
362 AutoTMalloc<unsigned char> fontPackageBuffer(fontPackageBufferRaw);
363 if (result != NO_ERROR) {
364 SkDEBUGF("CreateFontPackage Error %lu", result);
365 return E_UNEXPECTED;
366 }
367
368 // If it was originally a ttc, keep it a ttc.
369 // CreateFontPackage over-allocates, realloc usually decreases the size substantially.
370 size_t extra;
371 if (ttcCount > 0) {
372 // Create space for a ttc header.
373 extra = sizeof(SkTTCFHeader) + (ttcCount * sizeof(SK_OT_ULONG));
374 fontPackageBuffer.realloc(bytesWritten + extra);
375 //overlap is certain, use memmove
376 memmove(fontPackageBuffer.get() + extra, fontPackageBuffer.get(), bytesWritten);
377
378 // Write the ttc header.
379 SkTTCFHeader* ttcfHeader = reinterpret_cast<SkTTCFHeader*>(fontPackageBuffer.get());
380 ttcfHeader->ttcTag = SkTTCFHeader::TAG;
381 ttcfHeader->version = SkTTCFHeader::version_1;
382 ttcfHeader->numOffsets = SkEndian_SwapBE32(ttcCount);
383 SK_OT_ULONG* offsetPtr = SkTAfter<SK_OT_ULONG>(ttcfHeader);
384 for (int i = 0; i < ttcCount; ++i, ++offsetPtr) {
385 *offsetPtr = SkEndian_SwapBE32(SkToU32(extra));
386 }
387
388 // Fix up offsets in sfnt table entries.
389 SkSFNTHeader* sfntHeader = SkTAddOffset<SkSFNTHeader>(fontPackageBuffer.get(), extra);
390 int numTables = SkEndian_SwapBE16(sfntHeader->numTables);
391 SkSFNTHeader::TableDirectoryEntry* tableDirectory =
392 SkTAfter<SkSFNTHeader::TableDirectoryEntry>(sfntHeader);
393 for (int i = 0; i < numTables; ++i, ++tableDirectory) {
394 tableDirectory->offset = SkEndian_SwapBE32(
395 SkToU32(SkEndian_SwapBE32(SkToU32(tableDirectory->offset)) + extra));
396 }
397 } else {
398 extra = 0;
399 fontPackageBuffer.realloc(bytesWritten);
400 }
401
402 std::unique_ptr<SkMemoryStream> newStream(new SkMemoryStream());
403 newStream->setMemoryOwned(fontPackageBuffer.release(), bytesWritten + extra);
404
405 SkTScopedComPtr<IStream> newIStream;
406 SkIStream::CreateFromSkStream(std::move(newStream), &newIStream);
407
408 XPS_FONT_EMBEDDING embedding;
409 HRM(current.xpsFont->GetEmbeddingOption(&embedding),
410 "Could not get embedding option from font.");
411
412 SkTScopedComPtr<IOpcPartUri> partUri;
413 HRM(current.xpsFont->GetPartName(&partUri),
414 "Could not get part uri from font.");
415
416 HRM(current.xpsFont->SetContent(
417 newIStream.get(),
418 embedding,
419 partUri.get()),
420 "Could not set new stream for subsetted font.");
421
422 return S_OK;
423 #endif //SK_WINUWP
424}
425
426bool SkXPSDevice::endPortfolio() {
427 //Subset fonts
428 for (const TypefaceUse& current : *this->fTopTypefaces) {
429 //Ignore return for now, if it didn't subset, let it be.
430 subset_typeface(current);
431 }
432
433 if (this->fPackageWriter) {
434 HRBM(this->fPackageWriter->Close(), "Could not close writer.");
435 }
436
437 return true;
438}
439
440static XPS_COLOR xps_color(const SkColor skColor) {
441 //XPS uses non-pre-multiplied alpha (XPS Spec 11.4).
442 XPS_COLOR xpsColor;
443 xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
444 xpsColor.value.sRGB.alpha = SkColorGetA(skColor);
445 xpsColor.value.sRGB.red = SkColorGetR(skColor);
446 xpsColor.value.sRGB.green = SkColorGetG(skColor);
447 xpsColor.value.sRGB.blue = SkColorGetB(skColor);
448
449 return xpsColor;
450}
451
452static XPS_POINT xps_point(const SkPoint& point) {
453 XPS_POINT xpsPoint = {
454 SkScalarToFLOAT(point.fX),
455 SkScalarToFLOAT(point.fY),
456 };
457 return xpsPoint;
458}
459
460static XPS_POINT xps_point(const SkPoint& point, const SkMatrix& matrix) {
461 SkPoint skTransformedPoint;
462 matrix.mapXY(point.fX, point.fY, &skTransformedPoint);
463 return xps_point(skTransformedPoint);
464}
465
466static XPS_SPREAD_METHOD xps_spread_method(SkTileMode tileMode) {
467 switch (tileMode) {
469 return XPS_SPREAD_METHOD_PAD;
471 return XPS_SPREAD_METHOD_REPEAT;
473 return XPS_SPREAD_METHOD_REFLECT;
475 // TODO: fake
476 return XPS_SPREAD_METHOD_PAD;
477 default:
478 SkDEBUGFAIL("Unknown tile mode.");
479 }
480 return XPS_SPREAD_METHOD_PAD;
481}
482
483static void transform_offsets(SkScalar* stopOffsets, const int numOffsets,
484 const SkPoint& start, const SkPoint& end,
485 const SkMatrix& transform) {
486 SkPoint startTransformed;
487 transform.mapXY(start.fX, start.fY, &startTransformed);
488 SkPoint endTransformed;
489 transform.mapXY(end.fX, end.fY, &endTransformed);
490
491 //Manhattan distance between transformed start and end.
492 SkScalar startToEnd = (endTransformed.fX - startTransformed.fX)
493 + (endTransformed.fY - startTransformed.fY);
494 if (SkScalarNearlyZero(startToEnd)) {
495 for (int i = 0; i < numOffsets; ++i) {
496 stopOffsets[i] = 0;
497 }
498 return;
499 }
500
501 for (int i = 0; i < numOffsets; ++i) {
502 SkPoint stop;
503 stop.fX = (end.fX - start.fX) * stopOffsets[i];
504 stop.fY = (end.fY - start.fY) * stopOffsets[i];
505
506 SkPoint stopTransformed;
507 transform.mapXY(stop.fX, stop.fY, &stopTransformed);
508
509 //Manhattan distance between transformed start and stop.
510 SkScalar startToStop = (stopTransformed.fX - startTransformed.fX)
511 + (stopTransformed.fY - startTransformed.fY);
512 //Percentage along transformed line.
513 stopOffsets[i] = startToStop / startToEnd;
514 }
515}
516
517HRESULT SkXPSDevice::createXpsTransform(const SkMatrix& matrix,
518 IXpsOMMatrixTransform** xpsTransform) {
519 SkScalar affine[6];
520 if (!matrix.asAffine(affine)) {
521 *xpsTransform = nullptr;
522 return S_FALSE;
523 }
524 XPS_MATRIX rawXpsMatrix = {
525 SkScalarToFLOAT(affine[SkMatrix::kAScaleX]),
526 SkScalarToFLOAT(affine[SkMatrix::kASkewY]),
527 SkScalarToFLOAT(affine[SkMatrix::kASkewX]),
528 SkScalarToFLOAT(affine[SkMatrix::kAScaleY]),
529 SkScalarToFLOAT(affine[SkMatrix::kATransX]),
530 SkScalarToFLOAT(affine[SkMatrix::kATransY]),
531 };
532 HRM(this->fXpsFactory->CreateMatrixTransform(&rawXpsMatrix, xpsTransform),
533 "Could not create transform.");
534
535 return S_OK;
536}
537
538HRESULT SkXPSDevice::createPath(IXpsOMGeometryFigure* figure,
539 IXpsOMVisualCollection* visuals,
540 IXpsOMPath** path) {
541 SkTScopedComPtr<IXpsOMGeometry> geometry;
542 HRM(this->fXpsFactory->CreateGeometry(&geometry),
543 "Could not create geometry.");
544
545 SkTScopedComPtr<IXpsOMGeometryFigureCollection> figureCollection;
546 HRM(geometry->GetFigures(&figureCollection), "Could not get figures.");
547 HRM(figureCollection->Append(figure), "Could not add figure.");
548
549 HRM(this->fXpsFactory->CreatePath(path), "Could not create path.");
550 HRM((*path)->SetGeometryLocal(geometry.get()), "Could not set geometry");
551
552 HRM(visuals->Append(*path), "Could not add path to visuals.");
553 return S_OK;
554}
555
556HRESULT SkXPSDevice::createXpsSolidColorBrush(const SkColor skColor,
557 const SkAlpha alpha,
558 IXpsOMBrush** xpsBrush) {
559 XPS_COLOR xpsColor = xps_color(skColor);
560 SkTScopedComPtr<IXpsOMSolidColorBrush> solidBrush;
561 HRM(this->fXpsFactory->CreateSolidColorBrush(&xpsColor, nullptr, &solidBrush),
562 "Could not create solid color brush.");
563 HRM(solidBrush->SetOpacity(alpha / 255.0f), "Could not set opacity.");
564 HRM(solidBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI Fail.");
565 return S_OK;
566}
567
568HRESULT SkXPSDevice::sideOfClamp(const SkRect& areaToFill,
569 const XPS_RECT& imageViewBox,
570 IXpsOMImageResource* image,
571 IXpsOMVisualCollection* visuals) {
572 SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
573 HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
574
575 SkTScopedComPtr<IXpsOMPath> areaToFillPath;
576 HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
577
578 SkTScopedComPtr<IXpsOMImageBrush> areaToFillBrush;
579 HRM(this->fXpsFactory->CreateImageBrush(image,
580 &imageViewBox,
581 &imageViewBox,
582 &areaToFillBrush),
583 "Could not create brush for side of clamp.");
584 HRM(areaToFillBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
585 "Could not set tile mode for side of clamp.");
586 HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
587 "Could not set brush for side of clamp");
588
589 return S_OK;
590}
591
592HRESULT SkXPSDevice::cornerOfClamp(const SkRect& areaToFill,
593 const SkColor color,
594 IXpsOMVisualCollection* visuals) {
595 SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
596 HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
597
598 SkTScopedComPtr<IXpsOMPath> areaToFillPath;
599 HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
600
601 SkTScopedComPtr<IXpsOMBrush> areaToFillBrush;
602 HR(this->createXpsSolidColorBrush(color, 0xFF, &areaToFillBrush));
603 HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
604 "Could not set brush for corner of clamp.");
605
606 return S_OK;
607}
608
609static const XPS_TILE_MODE XTM_N = XPS_TILE_MODE_NONE;
610static const XPS_TILE_MODE XTM_T = XPS_TILE_MODE_TILE;
611static const XPS_TILE_MODE XTM_X = XPS_TILE_MODE_FLIPX;
612static const XPS_TILE_MODE XTM_Y = XPS_TILE_MODE_FLIPY;
613static const XPS_TILE_MODE XTM_XY = XPS_TILE_MODE_FLIPXY;
614
615//TODO(bungeman): In the future, should skia add None,
616//handle None+Mirror and None+Repeat correctly.
617//None is currently an internal hack so masks don't repeat (None+None only).
618static XPS_TILE_MODE gSkToXpsTileMode[kSkTileModeCount+1]
619 [kSkTileModeCount+1] = {
620 //Clamp //Repeat //Mirror //None
621 /*Clamp */ {XTM_N, XTM_T, XTM_Y, XTM_N},
622 /*Repeat*/ {XTM_T, XTM_T, XTM_Y, XTM_N},
623 /*Mirror*/ {XTM_X, XTM_X, XTM_XY, XTM_X},
624 /*None */ {XTM_N, XTM_N, XTM_Y, XTM_N},
625};
626
627static XPS_TILE_MODE SkToXpsTileMode(SkTileMode tmx, SkTileMode tmy) {
628 return gSkToXpsTileMode[(unsigned)tmx][(unsigned)tmy];
629}
630
631HRESULT SkXPSDevice::createXpsImageBrush(
632 const SkPixmap& bitmap,
633 const SkMatrix& localMatrix,
634 const SkTileMode (&xy)[2],
635 const SkAlpha alpha,
636 IXpsOMTileBrush** xpsBrush) {
638 if (!SkPngEncoder::Encode(&write, bitmap, {})) {
639 HRM(E_FAIL, "Unable to encode bitmap as png.");
640 }
641 SkTScopedComPtr<IStream> read;
642 HRM(SkIStream::CreateFromSkStream(write.detachAsStream(), &read),
643 "Could not create stream from png data.");
644
645 const size_t size =
646 std::size(L"/Documents/1/Resources/Images/" L_GUID_ID L".png");
647 wchar_t buffer[size];
648 wchar_t id[GUID_ID_LEN];
649 HR(this->createId(id, GUID_ID_LEN));
650 swprintf_s(buffer, size, L"/Documents/1/Resources/Images/%s.png", id);
651
652 SkTScopedComPtr<IOpcPartUri> imagePartUri;
653 HRM(this->fXpsFactory->CreatePartUri(buffer, &imagePartUri),
654 "Could not create image part uri.");
655
656 SkTScopedComPtr<IXpsOMImageResource> imageResource;
657 HRM(this->fXpsFactory->CreateImageResource(
658 read.get(),
659 XPS_IMAGE_TYPE_PNG,
660 imagePartUri.get(),
661 &imageResource),
662 "Could not create image resource.");
663
664 XPS_RECT bitmapRect = {
665 0.0, 0.0,
666 static_cast<FLOAT>(bitmap.width()), static_cast<FLOAT>(bitmap.height())
667 };
668 SkTScopedComPtr<IXpsOMImageBrush> xpsImageBrush;
669 HRM(this->fXpsFactory->CreateImageBrush(imageResource.get(),
670 &bitmapRect, &bitmapRect,
671 &xpsImageBrush),
672 "Could not create image brush.");
673
674 if (SkTileMode::kClamp != xy[0] &&
675 SkTileMode::kClamp != xy[1]) {
676
677 HRM(xpsImageBrush->SetTileMode(SkToXpsTileMode(xy[0], xy[1])),
678 "Could not set image tile mode");
679 HRM(xpsImageBrush->SetOpacity(alpha / 255.0f),
680 "Could not set image opacity.");
681 HRM(xpsImageBrush->QueryInterface(xpsBrush), "QI failed.");
682 } else {
683 //TODO(bungeman): compute how big this really needs to be.
684 const SkScalar BIG = SkIntToScalar(1000); //SK_ScalarMax;
685 const FLOAT BIG_F = SkScalarToFLOAT(BIG);
686 const SkScalar bWidth = SkIntToScalar(bitmap.width());
687 const SkScalar bHeight = SkIntToScalar(bitmap.height());
688
689 //create brush canvas
690 SkTScopedComPtr<IXpsOMCanvas> brushCanvas;
691 HRM(this->fXpsFactory->CreateCanvas(&brushCanvas),
692 "Could not create image brush canvas.");
693 SkTScopedComPtr<IXpsOMVisualCollection> brushVisuals;
694 HRM(brushCanvas->GetVisuals(&brushVisuals),
695 "Could not get image brush canvas visuals collection.");
696
697 //create central figure
698 const SkRect bitmapPoints = SkRect::MakeLTRB(0, 0, bWidth, bHeight);
699 SkTScopedComPtr<IXpsOMGeometryFigure> centralFigure;
700 HR(this->createXpsRect(bitmapPoints, FALSE, TRUE, &centralFigure));
701
702 SkTScopedComPtr<IXpsOMPath> centralPath;
703 HR(this->createPath(centralFigure.get(),
704 brushVisuals.get(),
705 &centralPath));
706 HRM(xpsImageBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
707 "Could not set tile mode for image brush central path.");
708 HRM(centralPath->SetFillBrushLocal(xpsImageBrush.get()),
709 "Could not set fill brush for image brush central path.");
710
711 //add left/right
712 if (SkTileMode::kClamp == xy[0]) {
713 SkRect leftArea = SkRect::MakeLTRB(-BIG, 0, 0, bHeight);
714 XPS_RECT leftImageViewBox = {
715 0.0, 0.0,
716 1.0, static_cast<FLOAT>(bitmap.height()),
717 };
718 HR(this->sideOfClamp(leftArea, leftImageViewBox,
719 imageResource.get(),
720 brushVisuals.get()));
721
722 SkRect rightArea = SkRect::MakeLTRB(bWidth, 0, BIG, bHeight);
723 XPS_RECT rightImageViewBox = {
724 bitmap.width() - 1.0f, 0.0f,
725 1.0f, static_cast<FLOAT>(bitmap.height()),
726 };
727 HR(this->sideOfClamp(rightArea, rightImageViewBox,
728 imageResource.get(),
729 brushVisuals.get()));
730 }
731
732 //add top/bottom
733 if (SkTileMode::kClamp == xy[1]) {
734 SkRect topArea = SkRect::MakeLTRB(0, -BIG, bWidth, 0);
735 XPS_RECT topImageViewBox = {
736 0.0, 0.0,
737 static_cast<FLOAT>(bitmap.width()), 1.0,
738 };
739 HR(this->sideOfClamp(topArea, topImageViewBox,
740 imageResource.get(),
741 brushVisuals.get()));
742
743 SkRect bottomArea = SkRect::MakeLTRB(0, bHeight, bWidth, BIG);
744 XPS_RECT bottomImageViewBox = {
745 0.0f, bitmap.height() - 1.0f,
746 static_cast<FLOAT>(bitmap.width()), 1.0f,
747 };
748 HR(this->sideOfClamp(bottomArea, bottomImageViewBox,
749 imageResource.get(),
750 brushVisuals.get()));
751 }
752
753 //add tl, tr, bl, br
754 if (SkTileMode::kClamp == xy[0] &&
755 SkTileMode::kClamp == xy[1]) {
756
757 const SkColor tlColor = bitmap.getColor(0,0);
758 const SkRect tlArea = SkRect::MakeLTRB(-BIG, -BIG, 0, 0);
759 HR(this->cornerOfClamp(tlArea, tlColor, brushVisuals.get()));
760
761 const SkColor trColor = bitmap.getColor(bitmap.width()-1,0);
762 const SkRect trArea = SkRect::MakeLTRB(bWidth, -BIG, BIG, 0);
763 HR(this->cornerOfClamp(trArea, trColor, brushVisuals.get()));
764
765 const SkColor brColor = bitmap.getColor(bitmap.width()-1,
766 bitmap.height()-1);
767 const SkRect brArea = SkRect::MakeLTRB(bWidth, bHeight, BIG, BIG);
768 HR(this->cornerOfClamp(brArea, brColor, brushVisuals.get()));
769
770 const SkColor blColor = bitmap.getColor(0,bitmap.height()-1);
771 const SkRect blArea = SkRect::MakeLTRB(-BIG, bHeight, 0, BIG);
772 HR(this->cornerOfClamp(blArea, blColor, brushVisuals.get()));
773 }
774
775 //create visual brush from canvas
776 XPS_RECT bound = {};
777 if (SkTileMode::kClamp == xy[0] &&
778 SkTileMode::kClamp == xy[1]) {
779
780 bound.x = BIG_F / -2;
781 bound.y = BIG_F / -2;
782 bound.width = BIG_F;
783 bound.height = BIG_F;
784 } else if (SkTileMode::kClamp == xy[0]) {
785 bound.x = BIG_F / -2;
786 bound.y = 0.0f;
787 bound.width = BIG_F;
788 bound.height = static_cast<FLOAT>(bitmap.height());
789 } else if (SkTileMode::kClamp == xy[1]) {
790 bound.x = 0;
791 bound.y = BIG_F / -2;
792 bound.width = static_cast<FLOAT>(bitmap.width());
793 bound.height = BIG_F;
794 }
795 SkTScopedComPtr<IXpsOMVisualBrush> clampBrush;
796 HRM(this->fXpsFactory->CreateVisualBrush(&bound, &bound, &clampBrush),
797 "Could not create visual brush for image brush.");
798 HRM(clampBrush->SetVisualLocal(brushCanvas.get()),
799 "Could not set canvas on visual brush for image brush.");
800 HRM(clampBrush->SetTileMode(SkToXpsTileMode(xy[0], xy[1])),
801 "Could not set tile mode on visual brush for image brush.");
802 HRM(clampBrush->SetOpacity(alpha / 255.0f),
803 "Could not set opacity on visual brush for image brush.");
804
805 HRM(clampBrush->QueryInterface(xpsBrush), "QI failed.");
806 }
807
808 SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
809 HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
810 if (xpsMatrixToUse.get()) {
811 HRM((*xpsBrush)->SetTransformLocal(xpsMatrixToUse.get()),
812 "Could not set transform for image brush.");
813 } else {
814 //TODO(bungeman): perspective bitmaps in general.
815 }
816
817 return S_OK;
818}
819
820HRESULT SkXPSDevice::createXpsGradientStop(const SkColor skColor,
821 const SkScalar offset,
822 IXpsOMGradientStop** xpsGradStop) {
823 XPS_COLOR gradStopXpsColor = xps_color(skColor);
824 HRM(this->fXpsFactory->CreateGradientStop(&gradStopXpsColor,
825 nullptr,
826 SkScalarToFLOAT(offset),
827 xpsGradStop),
828 "Could not create gradient stop.");
829 return S_OK;
830}
831
832HRESULT SkXPSDevice::createXpsLinearGradient(SkShaderBase::GradientInfo info,
833 const SkAlpha alpha,
834 const SkMatrix& localMatrix,
835 IXpsOMMatrixTransform* xpsMatrix,
836 IXpsOMBrush** xpsBrush) {
837 XPS_POINT startPoint;
838 XPS_POINT endPoint;
839 if (xpsMatrix) {
840 startPoint = xps_point(info.fPoint[0]);
841 endPoint = xps_point(info.fPoint[1]);
842 } else {
843 transform_offsets(info.fColorOffsets, info.fColorCount,
844 info.fPoint[0], info.fPoint[1],
845 localMatrix);
846 startPoint = xps_point(info.fPoint[0], localMatrix);
847 endPoint = xps_point(info.fPoint[1], localMatrix);
848 }
849
850 SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
851 HR(createXpsGradientStop(info.fColors[0],
852 info.fColorOffsets[0],
853 &gradStop0));
854
855 SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
856 HR(createXpsGradientStop(info.fColors[1],
857 info.fColorOffsets[1],
858 &gradStop1));
859
860 SkTScopedComPtr<IXpsOMLinearGradientBrush> gradientBrush;
861 HRM(this->fXpsFactory->CreateLinearGradientBrush(gradStop0.get(),
862 gradStop1.get(),
863 &startPoint,
864 &endPoint,
865 &gradientBrush),
866 "Could not create linear gradient brush.");
867 if (xpsMatrix) {
868 HRM(gradientBrush->SetTransformLocal(xpsMatrix),
869 "Could not set transform on linear gradient brush.");
870 }
871
872 SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
873 HRM(gradientBrush->GetGradientStops(&gradStopCollection),
874 "Could not get linear gradient stop collection.");
875 for (int i = 2; i < info.fColorCount; ++i) {
876 SkTScopedComPtr<IXpsOMGradientStop> gradStop;
877 HR(createXpsGradientStop(info.fColors[i],
878 info.fColorOffsets[i],
879 &gradStop));
880 HRM(gradStopCollection->Append(gradStop.get()),
881 "Could not add linear gradient stop.");
882 }
883
884 HRM(gradientBrush->SetSpreadMethod(xps_spread_method((SkTileMode)info.fTileMode)),
885 "Could not set spread method of linear gradient.");
886
887 HRM(gradientBrush->SetOpacity(alpha / 255.0f),
888 "Could not set opacity of linear gradient brush.");
889 HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed");
890
891 return S_OK;
892}
893
894HRESULT SkXPSDevice::createXpsRadialGradient(SkShaderBase::GradientInfo info,
895 const SkAlpha alpha,
896 const SkMatrix& localMatrix,
897 IXpsOMMatrixTransform* xpsMatrix,
898 IXpsOMBrush** xpsBrush) {
899 SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
900 HR(createXpsGradientStop(info.fColors[0],
901 info.fColorOffsets[0],
902 &gradStop0));
903
904 SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
905 HR(createXpsGradientStop(info.fColors[1],
906 info.fColorOffsets[1],
907 &gradStop1));
908
909 //TODO: figure out how to fake better if not affine
910 XPS_POINT centerPoint;
911 XPS_POINT gradientOrigin;
912 XPS_SIZE radiiSizes;
913 if (xpsMatrix) {
914 centerPoint = xps_point(info.fPoint[0]);
915 gradientOrigin = xps_point(info.fPoint[0]);
916 radiiSizes.width = SkScalarToFLOAT(info.fRadius[0]);
917 radiiSizes.height = SkScalarToFLOAT(info.fRadius[0]);
918 } else {
919 centerPoint = xps_point(info.fPoint[0], localMatrix);
920 gradientOrigin = xps_point(info.fPoint[0], localMatrix);
921
922 SkScalar radius = info.fRadius[0];
923 SkVector vec[2];
924
925 vec[0].set(radius, 0);
926 vec[1].set(0, radius);
927 localMatrix.mapVectors(vec, 2);
928
929 SkScalar d0 = vec[0].length();
930 SkScalar d1 = vec[1].length();
931
932 radiiSizes.width = SkScalarToFLOAT(d0);
933 radiiSizes.height = SkScalarToFLOAT(d1);
934 }
935
936 SkTScopedComPtr<IXpsOMRadialGradientBrush> gradientBrush;
937 HRM(this->fXpsFactory->CreateRadialGradientBrush(gradStop0.get(),
938 gradStop1.get(),
939 &centerPoint,
940 &gradientOrigin,
941 &radiiSizes,
942 &gradientBrush),
943 "Could not create radial gradient brush.");
944 if (xpsMatrix) {
945 HRM(gradientBrush->SetTransformLocal(xpsMatrix),
946 "Could not set transform on radial gradient brush.");
947 }
948
949 SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
950 HRM(gradientBrush->GetGradientStops(&gradStopCollection),
951 "Could not get radial gradient stop collection.");
952 for (int i = 2; i < info.fColorCount; ++i) {
953 SkTScopedComPtr<IXpsOMGradientStop> gradStop;
954 HR(createXpsGradientStop(info.fColors[i],
955 info.fColorOffsets[i],
956 &gradStop));
957 HRM(gradStopCollection->Append(gradStop.get()),
958 "Could not add radial gradient stop.");
959 }
960
961 HRM(gradientBrush->SetSpreadMethod(xps_spread_method((SkTileMode)info.fTileMode)),
962 "Could not set spread method of radial gradient.");
963
964 HRM(gradientBrush->SetOpacity(alpha / 255.0f),
965 "Could not set opacity of radial gradient brush.");
966 HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed.");
967
968 return S_OK;
969}
970
971HRESULT SkXPSDevice::createXpsBrush(const SkPaint& skPaint,
972 IXpsOMBrush** brush,
973 const SkMatrix* parentTransform) {
974 const SkShader *shader = skPaint.getShader();
975 if (nullptr == shader) {
976 HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
977 return S_OK;
978 }
979
980 //Gradient shaders.
981 auto shaderBase = as_SB(shader);
982
983 if (shaderBase->type() == SkShaderBase::ShaderType::kColor) {
984 auto colorShader = static_cast<const SkColorShader*>(shader);
985 SkAlpha alpha = skPaint.getAlpha();
986 HR(this->createXpsSolidColorBrush(colorShader->color(), alpha, brush));
987 return S_OK;
988 } else if (shaderBase->type() == SkShaderBase::ShaderType::kGradientBase) {
990 SkShaderBase::GradientType gradientType = shaderBase->asGradient(&info);
991 if (info.fColorCount == 0) {
992 const SkColor color = skPaint.getColor();
993 HR(this->createXpsSolidColorBrush(color, 0xFF, brush));
994 return S_OK;
995 }
996
997 SkMatrix localMatrix;
998 AutoTArray<SkColor> colors(info.fColorCount);
999 AutoTArray<SkScalar> colorOffsets(info.fColorCount);
1000 info.fColors = colors.get();
1001 info.fColorOffsets = colorOffsets.get();
1002 shaderBase->asGradient(&info, &localMatrix);
1003
1004 if (1 == info.fColorCount) {
1005 SkColor color = info.fColors[0];
1006 SkAlpha alpha = skPaint.getAlpha();
1007 HR(this->createXpsSolidColorBrush(color, alpha, brush));
1008 return S_OK;
1009 }
1010
1011 if (parentTransform) {
1012 localMatrix.preConcat(*parentTransform);
1013 }
1014 SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
1015 HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
1016
1017 if (gradientType == SkShaderBase::GradientType::kLinear) {
1018 HR(this->createXpsLinearGradient(info,
1019 skPaint.getAlpha(),
1020 localMatrix,
1021 xpsMatrixToUse.get(),
1022 brush));
1023 return S_OK;
1024 }
1025
1026 if (gradientType == SkShaderBase::GradientType::kRadial) {
1027 HR(this->createXpsRadialGradient(info,
1028 skPaint.getAlpha(),
1029 localMatrix,
1030 xpsMatrixToUse.get(),
1031 brush));
1032 return S_OK;
1033 }
1034
1035 if (gradientType == SkShaderBase::GradientType::kConical) {
1036 //simple if affine and one is 0, otherwise will have to fake
1037 }
1038
1039 if (gradientType == SkShaderBase::GradientType::kSweep) {
1040 //have to fake
1041 }
1042 }
1043
1044 SkBitmap outTexture;
1045 SkMatrix outMatrix;
1046 SkTileMode xy[2];
1047 SkImage* image = shader->isAImage(&outMatrix, xy);
1048 if (image->asLegacyBitmap(&outTexture)) {
1049 if (parentTransform) {
1050 outMatrix.postConcat(*parentTransform);
1051 }
1052
1053 SkTScopedComPtr<IXpsOMTileBrush> tileBrush;
1054 HR(this->createXpsImageBrush(outTexture.pixmap(), outMatrix, xy, skPaint.getAlpha(),
1055 &tileBrush));
1056
1057 HRM(tileBrush->QueryInterface<IXpsOMBrush>(brush), "QI failed.");
1058 } else {
1059 HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
1060 }
1061 return S_OK;
1062}
1063
1064static bool rect_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
1065 const bool zeroWidth = (0 == paint.getStrokeWidth());
1066 const bool stroke = (SkPaint::kFill_Style != paint.getStyle());
1067
1068 return paint.getPathEffect() ||
1069 paint.getMaskFilter() ||
1070 (stroke && (
1071 (matrix.hasPerspective() && !zeroWidth) ||
1072 SkPaint::kMiter_Join != paint.getStrokeJoin() ||
1073 (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
1074 paint.getStrokeMiter() < SK_ScalarSqrt2)
1075 ))
1076 ;
1077}
1078
1079HRESULT SkXPSDevice::createXpsRect(const SkRect& rect, BOOL stroke, BOOL fill,
1080 IXpsOMGeometryFigure** xpsRect) {
1081 const SkPoint points[4] = {
1082 { rect.fLeft, rect.fTop },
1083 { rect.fRight, rect.fTop },
1084 { rect.fRight, rect.fBottom },
1085 { rect.fLeft, rect.fBottom },
1086 };
1087 return this->createXpsQuad(points, stroke, fill, xpsRect);
1088}
1089HRESULT SkXPSDevice::createXpsQuad(const SkPoint (&points)[4],
1090 BOOL stroke, BOOL fill,
1091 IXpsOMGeometryFigure** xpsQuad) {
1092 // Define the start point.
1093 XPS_POINT startPoint = xps_point(points[0]);
1094
1095 // Create the figure.
1096 HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint, xpsQuad),
1097 "Could not create quad geometry figure.");
1098
1099 // Define the type of each segment.
1100 XPS_SEGMENT_TYPE segmentTypes[3] = {
1101 XPS_SEGMENT_TYPE_LINE,
1102 XPS_SEGMENT_TYPE_LINE,
1103 XPS_SEGMENT_TYPE_LINE,
1104 };
1105
1106 // Define the x and y coordinates of each corner of the figure.
1107 FLOAT segmentData[6] = {
1108 SkScalarToFLOAT(points[1].fX), SkScalarToFLOAT(points[1].fY),
1109 SkScalarToFLOAT(points[2].fX), SkScalarToFLOAT(points[2].fY),
1110 SkScalarToFLOAT(points[3].fX), SkScalarToFLOAT(points[3].fY),
1111 };
1112
1113 // Describe if the segments are stroked.
1114 BOOL segmentStrokes[3] = {
1115 stroke, stroke, stroke,
1116 };
1117
1118 // Add the segment data to the figure.
1119 HRM((*xpsQuad)->SetSegments(
1120 3, 6,
1121 segmentTypes , segmentData, segmentStrokes),
1122 "Could not add segment data to quad.");
1123
1124 // Set the closed and filled properties of the figure.
1125 HRM((*xpsQuad)->SetIsClosed(stroke), "Could not set quad close.");
1126 HRM((*xpsQuad)->SetIsFilled(fill), "Could not set quad fill.");
1127
1128 return S_OK;
1129}
1130
1131void SkXPSDevice::drawPoints(SkCanvas::PointMode mode,
1132 size_t count, const SkPoint points[],
1133 const SkPaint& paint) {
1134 //TODO
1135}
1136
1137void SkXPSDevice::drawVertices(const SkVertices*, sk_sp<SkBlender>, const SkPaint&, bool) {
1138 //TODO
1139}
1140
1141void SkXPSDevice::drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) {
1142 // TODO
1143}
1144
1145void SkXPSDevice::drawPaint(const SkPaint& origPaint) {
1146 const SkRect r = SkRect::MakeSize(this->fCurrentCanvasSize);
1147
1148 //If trying to paint with a stroke, ignore that and fill.
1149 SkPaint* fillPaint = const_cast<SkPaint*>(&origPaint);
1151 if (paint->getStyle() != SkPaint::kFill_Style) {
1152 paint.writable()->setStyle(SkPaint::kFill_Style);
1153 }
1154
1155 this->internalDrawRect(r, false, *fillPaint);
1156}
1157
1158void SkXPSDevice::drawRect(const SkRect& r,
1159 const SkPaint& paint) {
1160 this->internalDrawRect(r, true, paint);
1161}
1162
1163void SkXPSDevice::drawRRect(const SkRRect& rr,
1164 const SkPaint& paint) {
1165 SkPath path;
1166 path.addRRect(rr);
1167 this->drawPath(path, paint, true);
1168}
1169
1170void SkXPSDevice::internalDrawRect(const SkRect& r,
1171 bool transformRect,
1172 const SkPaint& paint) {
1173 //Exit early if there is nothing to draw.
1174 if (this->isClipEmpty() || (paint.getAlpha() == 0 && paint.isSrcOver())) {
1175 return;
1176 }
1177
1178 //Path the rect if we can't optimize it.
1179 if (rect_must_be_pathed(paint, this->localToDevice())) {
1180 SkPath tmp;
1181 tmp.addRect(r);
1183 this->drawPath(tmp, paint, true);
1184 return;
1185 }
1186
1187 //Create the shaded path.
1188 SkTScopedComPtr<IXpsOMPath> shadedPath;
1189 HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1190 "Could not create shaded path for rect.");
1191
1192 //Create the shaded geometry.
1193 SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1194 HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1195 "Could not create shaded geometry for rect.");
1196
1197 //Add the geometry to the shaded path.
1198 HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1199 "Could not set shaded geometry for rect.");
1200
1201 //Set the brushes.
1202 BOOL fill = FALSE;
1203 BOOL stroke = FALSE;
1204 HRV(this->shadePath(shadedPath.get(), paint, this->localToDevice(), &fill, &stroke));
1205
1206 bool xpsTransformsPath = true;
1207 //Transform the geometry.
1208 if (transformRect && xpsTransformsPath) {
1209 SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1210 HRV(this->createXpsTransform(this->localToDevice(), &xpsTransform));
1211 if (xpsTransform.get()) {
1212 HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1213 "Could not set transform for rect.");
1214 } else {
1215 xpsTransformsPath = false;
1216 }
1217 }
1218
1219 //Create the figure.
1220 SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
1221 {
1222 SkPoint points[4] = {
1223 { r.fLeft, r.fTop },
1224 { r.fLeft, r.fBottom },
1225 { r.fRight, r.fBottom },
1226 { r.fRight, r.fTop },
1227 };
1228 if (!xpsTransformsPath && transformRect) {
1229 this->localToDevice().mapPoints(points, std::size(points));
1230 }
1231 HRV(this->createXpsQuad(points, stroke, fill, &rectFigure));
1232 }
1233
1234 //Get the figures of the shaded geometry.
1235 SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1236 HRVM(shadedGeometry->GetFigures(&shadedFigures),
1237 "Could not get shaded figures for rect.");
1238
1239 //Add the figure to the shaded geometry figures.
1240 HRVM(shadedFigures->Append(rectFigure.get()),
1241 "Could not add shaded figure for rect.");
1242
1243 HRV(this->clip(shadedPath.get()));
1244
1245 //Add the shaded path to the current visuals.
1246 SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1247 HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1248 "Could not get current visuals for rect.");
1249 HRVM(currentVisuals->Append(shadedPath.get()),
1250 "Could not add rect to current visuals.");
1251}
1252
1253static HRESULT close_figure(const SkTDArray<XPS_SEGMENT_TYPE>& segmentTypes,
1254 const SkTDArray<FLOAT>& segmentData,
1255 const SkTDArray<BOOL>& segmentStrokes,
1256 BOOL stroke, BOOL fill,
1257 IXpsOMGeometryFigure* figure,
1258 IXpsOMGeometryFigureCollection* figures) {
1259 // Either all are empty or none are empty.
1260 SkASSERT(( segmentTypes.empty() && segmentData.empty() && segmentStrokes.empty()) ||
1261 (!segmentTypes.empty() && !segmentData.empty() && !segmentStrokes.empty()));
1262
1263 // SkTDArray::begin() may return nullptr when the segment is empty,
1264 // but IXpsOMGeometryFigure::SetSegments returns E_POINTER if any of the pointers are nullptr
1265 // even if the counts are all 0.
1266 if (!segmentTypes.empty() && !segmentData.empty() && !segmentStrokes.empty()) {
1267 // Add the segment data to the figure.
1268 HRM(figure->SetSegments(segmentTypes.size(), segmentData.size(),
1269 segmentTypes.begin(), segmentData.begin(), segmentStrokes.begin()),
1270 "Could not set path segments.");
1271 }
1272
1273 // Set the closed and filled properties of the figure.
1274 HRM(figure->SetIsClosed(stroke), "Could not set path closed.");
1275 HRM(figure->SetIsFilled(fill), "Could not set path fill.");
1276
1277 // Add the figure created above to this geometry.
1278 HRM(figures->Append(figure), "Could not add path to geometry.");
1279 return S_OK;
1280}
1281
1282HRESULT SkXPSDevice::addXpsPathGeometry(
1283 IXpsOMGeometryFigureCollection* xpsFigures,
1284 BOOL stroke, BOOL fill, const SkPath& path) {
1285 SkTDArray<XPS_SEGMENT_TYPE> segmentTypes;
1286 SkTDArray<FLOAT> segmentData;
1287 SkTDArray<BOOL> segmentStrokes;
1288
1289 SkTScopedComPtr<IXpsOMGeometryFigure> xpsFigure;
1290 SkPath::Iter iter(path, true);
1291 SkPoint points[4];
1292 SkPath::Verb verb;
1293 while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
1294 switch (verb) {
1295 case SkPath::kMove_Verb: {
1296 if (xpsFigure.get()) {
1297 HR(close_figure(segmentTypes, segmentData, segmentStrokes,
1298 stroke, fill,
1299 xpsFigure.get() , xpsFigures));
1300 segmentTypes.clear();
1301 segmentData.clear();
1302 segmentStrokes.clear();
1303 xpsFigure.reset();
1304 }
1305 // Define the start point.
1306 XPS_POINT startPoint = xps_point(points[0]);
1307 // Create the figure.
1308 HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint,
1309 &xpsFigure),
1310 "Could not create path geometry figure.");
1311 break;
1312 }
1313 case SkPath::kLine_Verb:
1314 if (iter.isCloseLine()) break; //ignore the line, auto-closed
1315 segmentTypes.push_back(XPS_SEGMENT_TYPE_LINE);
1316 segmentData.push_back(SkScalarToFLOAT(points[1].fX));
1317 segmentData.push_back(SkScalarToFLOAT(points[1].fY));
1318 segmentStrokes.push_back(stroke);
1319 break;
1320 case SkPath::kQuad_Verb:
1321 segmentTypes.push_back(XPS_SEGMENT_TYPE_QUADRATIC_BEZIER);
1322 segmentData.push_back(SkScalarToFLOAT(points[1].fX));
1323 segmentData.push_back(SkScalarToFLOAT(points[1].fY));
1324 segmentData.push_back(SkScalarToFLOAT(points[2].fX));
1325 segmentData.push_back(SkScalarToFLOAT(points[2].fY));
1326 segmentStrokes.push_back(stroke);
1327 break;
1329 segmentTypes.push_back(XPS_SEGMENT_TYPE_BEZIER);
1330 segmentData.push_back(SkScalarToFLOAT(points[1].fX));
1331 segmentData.push_back(SkScalarToFLOAT(points[1].fY));
1332 segmentData.push_back(SkScalarToFLOAT(points[2].fX));
1333 segmentData.push_back(SkScalarToFLOAT(points[2].fY));
1334 segmentData.push_back(SkScalarToFLOAT(points[3].fX));
1335 segmentData.push_back(SkScalarToFLOAT(points[3].fY));
1336 segmentStrokes.push_back(stroke);
1337 break;
1338 case SkPath::kConic_Verb: {
1339 const SkScalar tol = SK_Scalar1 / 4;
1341 const SkPoint* quads =
1342 converter.computeQuads(points, iter.conicWeight(), tol);
1343 for (int i = 0; i < converter.countQuads(); ++i) {
1344 segmentTypes.push_back(XPS_SEGMENT_TYPE_QUADRATIC_BEZIER);
1345 segmentData.push_back(SkScalarToFLOAT(quads[2 * i + 1].fX));
1346 segmentData.push_back(SkScalarToFLOAT(quads[2 * i + 1].fY));
1347 segmentData.push_back(SkScalarToFLOAT(quads[2 * i + 2].fX));
1348 segmentData.push_back(SkScalarToFLOAT(quads[2 * i + 2].fY));
1349 segmentStrokes.push_back(stroke);
1350 }
1351 break;
1352 }
1354 // we ignore these, and just get the whole segment from
1355 // the corresponding line/quad/cubic verbs
1356 break;
1357 default:
1358 SkDEBUGFAIL("unexpected verb");
1359 break;
1360 }
1361 }
1362 if (xpsFigure.get()) {
1363 HR(close_figure(segmentTypes, segmentData, segmentStrokes,
1364 stroke, fill,
1365 xpsFigure.get(), xpsFigures));
1366 }
1367 return S_OK;
1368}
1369
1370void SkXPSDevice::convertToPpm(const SkMaskFilter* filter,
1371 SkMatrix* matrix,
1372 SkVector* ppuScale,
1373 const SkIRect& clip, SkIRect* clipIRect) {
1374 //This action is in unit space, but the ppm is specified in physical space.
1375 ppuScale->set(fCurrentPixelsPerMeter.fX / fCurrentUnitsPerMeter.fX,
1376 fCurrentPixelsPerMeter.fY / fCurrentUnitsPerMeter.fY);
1377
1378 matrix->postScale(ppuScale->fX, ppuScale->fY);
1379
1380 const SkIRect& irect = clip;
1382 SkIntToScalar(irect.fTop) * ppuScale->fY,
1383 SkIntToScalar(irect.fRight) * ppuScale->fX,
1384 SkIntToScalar(irect.fBottom) * ppuScale->fY);
1385 clipRect.roundOut(clipIRect);
1386}
1387
1388HRESULT SkXPSDevice::applyMask(const SkMask& mask,
1389 const SkVector& ppuScale,
1390 IXpsOMPath* shadedPath) {
1391 //Get the geometry object.
1392 SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1393 HRM(shadedPath->GetGeometry(&shadedGeometry),
1394 "Could not get mask shaded geometry.");
1395
1396 //Get the figures from the geometry.
1397 SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1398 HRM(shadedGeometry->GetFigures(&shadedFigures),
1399 "Could not get mask shaded figures.");
1400
1401 SkMatrix m;
1402 m.reset();
1403 m.setTranslate(SkIntToScalar(mask.fBounds.fLeft),
1404 SkIntToScalar(mask.fBounds.fTop));
1405 m.postScale(SkScalarInvert(ppuScale.fX), SkScalarInvert(ppuScale.fY));
1406
1407 SkTileMode xy[2];
1408 xy[0] = (SkTileMode)3;
1409 xy[1] = (SkTileMode)3;
1410
1413 mask.fImage, mask.fRowBytes);
1414
1415 SkTScopedComPtr<IXpsOMTileBrush> maskBrush;
1416 HR(this->createXpsImageBrush(pm, m, xy, 0xFF, &maskBrush));
1417 HRM(shadedPath->SetOpacityMaskBrushLocal(maskBrush.get()),
1418 "Could not set mask.");
1419
1420 const SkRect universeRect = SkRect::MakeLTRB(0, 0,
1421 this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
1422 SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
1423 HRM(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure),
1424 "Could not create mask shaded figure.");
1425 HRM(shadedFigures->Append(shadedFigure.get()),
1426 "Could not add mask shaded figure.");
1427
1428 HR(this->clip(shadedPath));
1429
1430 //Add the path to the active visual collection.
1431 SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1432 HRM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1433 "Could not get mask current visuals.");
1434 HRM(currentVisuals->Append(shadedPath),
1435 "Could not add masked shaded path to current visuals.");
1436
1437 return S_OK;
1438}
1439
1440HRESULT SkXPSDevice::shadePath(IXpsOMPath* shadedPath,
1441 const SkPaint& shaderPaint,
1442 const SkMatrix& matrix,
1443 BOOL* fill, BOOL* stroke) {
1444 *fill = FALSE;
1445 *stroke = FALSE;
1446
1447 const SkPaint::Style style = shaderPaint.getStyle();
1448 const bool hasFill = SkPaint::kFill_Style == style
1450 const bool hasStroke = SkPaint::kStroke_Style == style
1452
1453 //TODO(bungeman): use dictionaries and lookups.
1454 if (hasFill) {
1455 *fill = TRUE;
1456 SkTScopedComPtr<IXpsOMBrush> fillBrush;
1457 HR(this->createXpsBrush(shaderPaint, &fillBrush, &matrix));
1458 HRM(shadedPath->SetFillBrushLocal(fillBrush.get()),
1459 "Could not set fill for shaded path.");
1460 }
1461
1462 if (hasStroke) {
1463 *stroke = TRUE;
1464 SkTScopedComPtr<IXpsOMBrush> strokeBrush;
1465 HR(this->createXpsBrush(shaderPaint, &strokeBrush, &matrix));
1466 HRM(shadedPath->SetStrokeBrushLocal(strokeBrush.get()),
1467 "Could not set stroke brush for shaded path.");
1468 HRM(shadedPath->SetStrokeThickness(
1469 SkScalarToFLOAT(shaderPaint.getStrokeWidth())),
1470 "Could not set shaded path stroke thickness.");
1471
1472 if (0 == shaderPaint.getStrokeWidth()) {
1473 //XPS hair width is a hack. (XPS Spec 11.6.12).
1474 SkTScopedComPtr<IXpsOMDashCollection> dashes;
1475 HRM(shadedPath->GetStrokeDashes(&dashes),
1476 "Could not set dashes for shaded path.");
1477 XPS_DASH dash;
1478 dash.length = 1.0;
1479 dash.gap = 0.0;
1480 HRM(dashes->Append(&dash), "Could not add dashes to shaded path.");
1481 HRM(shadedPath->SetStrokeDashOffset(-2.0),
1482 "Could not set dash offset for shaded path.");
1483 }
1484 }
1485 return S_OK;
1486}
1487
1488void SkXPSDevice::drawPath(const SkPath& platonicPath,
1489 const SkPaint& origPaint,
1490 bool pathIsMutable) {
1492
1493 // nothing to draw
1494 if (this->isClipEmpty() || (paint->getAlpha() == 0 && paint->isSrcOver())) {
1495 return;
1496 }
1497
1498 SkPath modifiedPath;
1499 const bool paintHasPathEffect = paint->getPathEffect()
1500 || paint->getStyle() != SkPaint::kFill_Style;
1501
1502 //Apply pre-path matrix [Platonic-path -> Skeletal-path].
1503 SkMatrix matrix = this->localToDevice();
1504 SkPath* skeletalPath = const_cast<SkPath*>(&platonicPath);
1505
1506 //Apply path effect [Skeletal-path -> Fillable-path].
1507 SkPath* fillablePath = skeletalPath;
1508 if (paintHasPathEffect) {
1509 if (!pathIsMutable) {
1510 fillablePath = &modifiedPath;
1511 pathIsMutable = true;
1512 }
1513 bool fill = skpathutils::FillPathWithPaint(*skeletalPath, *paint, fillablePath);
1514
1515 SkPaint* writablePaint = paint.writable();
1516 writablePaint->setPathEffect(nullptr);
1517 if (fill) {
1518 writablePaint->setStyle(SkPaint::kFill_Style);
1519 } else {
1520 writablePaint->setStyle(SkPaint::kStroke_Style);
1521 writablePaint->setStrokeWidth(0);
1522 }
1523 }
1524
1525 //Create the shaded path. This will be the path which is painted.
1526 SkTScopedComPtr<IXpsOMPath> shadedPath;
1527 HRVM(this->fXpsFactory->CreatePath(&shadedPath),
1528 "Could not create shaded path for path.");
1529
1530 //Create the geometry for the shaded path.
1531 SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
1532 HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
1533 "Could not create shaded geometry for path.");
1534
1535 //Add the geometry to the shaded path.
1536 HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
1537 "Could not add the shaded geometry to shaded path.");
1538
1539 SkMaskFilter* filter = paint->getMaskFilter();
1540
1541 //Determine if we will draw or shade and mask.
1542 if (filter) {
1543 if (paint->getStyle() != SkPaint::kFill_Style) {
1544 paint.writable()->setStyle(SkPaint::kFill_Style);
1545 }
1546 }
1547
1548 //Set the brushes.
1549 BOOL fill;
1550 BOOL stroke;
1551 HRV(this->shadePath(shadedPath.get(),
1552 *paint,
1553 this->localToDevice(),
1554 &fill,
1555 &stroke));
1556
1557 //Mask filter
1558 if (filter) {
1559 SkIRect clipIRect;
1560 SkVector ppuScale;
1561 this->convertToPpm(filter,
1562 &matrix,
1563 &ppuScale,
1564 this->devClipBounds(),
1565 &clipIRect);
1566
1567 //[Fillable-path -> Pixel-path]
1568 SkPath* pixelPath = pathIsMutable ? fillablePath : &modifiedPath;
1569 fillablePath->transform(matrix, pixelPath);
1570
1571 SkMask* mask = nullptr;
1572
1573 SkASSERT(SkPaint::kFill_Style == paint->getStyle() ||
1574 (SkPaint::kStroke_Style == paint->getStyle() && 0 == paint->getStrokeWidth()));
1575 SkStrokeRec::InitStyle style = (SkPaint::kFill_Style == paint->getStyle())
1577 : SkStrokeRec::kHairline_InitStyle;
1578 //[Pixel-path -> Mask]
1579 SkMaskBuilder rasteredMask;
1581 *pixelPath,
1582 clipIRect,
1583 filter, //just to compute how much to draw.
1584 &matrix,
1585 &rasteredMask,
1587 style)) {
1588
1589 SkAutoMaskFreeImage rasteredAmi(rasteredMask.image());
1590 mask = &rasteredMask;
1591
1592 //[Mask -> Mask]
1593 SkMaskBuilder filteredMask;
1594 if (as_MFB(filter)->filterMask(&filteredMask, rasteredMask, matrix, nullptr)) {
1595 mask = &filteredMask;
1596 }
1597 SkAutoMaskFreeImage filteredAmi(filteredMask.image());
1598
1599 //Draw mask.
1600 HRV(this->applyMask(*mask, ppuScale, shadedPath.get()));
1601 }
1602 return;
1603 }
1604
1605 //Get the figures from the shaded geometry.
1606 SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
1607 HRVM(shadedGeometry->GetFigures(&shadedFigures),
1608 "Could not get shaded figures for shaded path.");
1609
1610 bool xpsTransformsPath = true;
1611
1612 //Set the fill rule.
1613 SkPath* xpsCompatiblePath = fillablePath;
1614 XPS_FILL_RULE xpsFillRule;
1615 switch (fillablePath->getFillType()) {
1617 xpsFillRule = XPS_FILL_RULE_NONZERO;
1618 break;
1620 xpsFillRule = XPS_FILL_RULE_EVENODD;
1621 break;
1623 //[Fillable-path (inverse winding) -> XPS-path (inverse even odd)]
1624 if (!pathIsMutable) {
1625 xpsCompatiblePath = &modifiedPath;
1626 pathIsMutable = true;
1627 }
1628 if (!Simplify(*fillablePath, xpsCompatiblePath)) {
1629 SkDEBUGF("Could not simplify inverse winding path.");
1630 return;
1631 }
1632 }
1633 [[fallthrough]]; // The xpsCompatiblePath is now inverse even odd, so fall through.
1635 const SkRect universe = SkRect::MakeLTRB(
1636 0, 0,
1637 this->fCurrentCanvasSize.fWidth,
1638 this->fCurrentCanvasSize.fHeight);
1639 SkTScopedComPtr<IXpsOMGeometryFigure> addOneFigure;
1640 HRV(this->createXpsRect(universe, FALSE, TRUE, &addOneFigure));
1641 HRVM(shadedFigures->Append(addOneFigure.get()),
1642 "Could not add even-odd flip figure to shaded path.");
1643 xpsTransformsPath = false;
1644 xpsFillRule = XPS_FILL_RULE_EVENODD;
1645 break;
1646 }
1647 default:
1648 SkDEBUGFAIL("Unknown SkPath::FillType.");
1649 }
1650 HRVM(shadedGeometry->SetFillRule(xpsFillRule),
1651 "Could not set fill rule for shaded path.");
1652
1653 //Create the XPS transform, if possible.
1654 if (xpsTransformsPath) {
1655 SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1656 HRV(this->createXpsTransform(matrix, &xpsTransform));
1657
1658 if (xpsTransform.get()) {
1659 HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
1660 "Could not set transform on shaded path.");
1661 } else {
1662 xpsTransformsPath = false;
1663 }
1664 }
1665
1666 SkPath* devicePath = xpsCompatiblePath;
1667 if (!xpsTransformsPath) {
1668 //[Fillable-path -> Device-path]
1669 devicePath = pathIsMutable ? xpsCompatiblePath : &modifiedPath;
1670 xpsCompatiblePath->transform(matrix, devicePath);
1671 }
1672 HRV(this->addXpsPathGeometry(shadedFigures.get(),
1673 stroke, fill, *devicePath));
1674
1675 HRV(this->clip(shadedPath.get()));
1676
1677 //Add the path to the active visual collection.
1678 SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1679 HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1680 "Could not get current visuals for shaded path.");
1681 HRVM(currentVisuals->Append(shadedPath.get()),
1682 "Could not add shaded path to current visuals.");
1683}
1684
1685HRESULT SkXPSDevice::clip(IXpsOMVisual* xpsVisual) {
1686 if (this->cs().isWideOpen()) {
1687 return S_OK;
1688 }
1690 // clipPath.addRect(this->devClipBounds()));
1691 SkClipStack_AsPath(this->cs(), &clipPath);
1692 // TODO: handle all the kinds of paths, like drawPath does
1693 return this->clipToPath(xpsVisual, clipPath, XPS_FILL_RULE_EVENODD);
1694}
1695HRESULT SkXPSDevice::clipToPath(IXpsOMVisual* xpsVisual,
1696 const SkPath& clipPath,
1697 XPS_FILL_RULE fillRule) {
1698 //Create the geometry.
1699 SkTScopedComPtr<IXpsOMGeometry> clipGeometry;
1700 HRM(this->fXpsFactory->CreateGeometry(&clipGeometry),
1701 "Could not create clip geometry.");
1702
1703 //Get the figure collection of the geometry.
1704 SkTScopedComPtr<IXpsOMGeometryFigureCollection> clipFigures;
1705 HRM(clipGeometry->GetFigures(&clipFigures),
1706 "Could not get the clip figures.");
1707
1708 //Create the figures into the geometry.
1709 HR(this->addXpsPathGeometry(
1710 clipFigures.get(),
1711 FALSE, TRUE, clipPath));
1712
1713 HRM(clipGeometry->SetFillRule(fillRule),
1714 "Could not set fill rule.");
1715 HRM(xpsVisual->SetClipGeometryLocal(clipGeometry.get()),
1716 "Could not set clip geometry.");
1717
1718 return S_OK;
1719}
1720
1721HRESULT SkXPSDevice::CreateTypefaceUse(const SkFont& font,
1722 TypefaceUse** typefaceUse) {
1723 SkTypeface* typeface = font.getTypeface();
1724
1725 //Check cache.
1726 const SkTypefaceID typefaceID = typeface->uniqueID();
1727 for (TypefaceUse& current : *this->fTopTypefaces) {
1728 if (current.typefaceId == typefaceID) {
1729 *typefaceUse = &current;
1730 return S_OK;
1731 }
1732 }
1733
1734 //TODO: create glyph only fonts
1735 //and let the host deal with what kind of font we're looking at.
1736 XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_RESTRICTED;
1737
1738 SkTScopedComPtr<IStream> fontStream;
1739 int ttcIndex;
1740 std::unique_ptr<SkStreamAsset> fontData = typeface->openStream(&ttcIndex);
1741 if (!fontData) {
1742 return E_NOTIMPL;
1743 }
1744 //TODO: cannot handle FON fonts.
1745 HRM(SkIStream::CreateFromSkStream(fontData->duplicate(), &fontStream),
1746 "Could not create font stream.");
1747
1748 const size_t size =
1749 std::size(L"/Resources/Fonts/" L_GUID_ID L".odttf");
1750 wchar_t buffer[size];
1751 wchar_t id[GUID_ID_LEN];
1752 HR(this->createId(id, GUID_ID_LEN));
1753 swprintf_s(buffer, size, L"/Resources/Fonts/%s.odttf", id);
1754
1755 SkTScopedComPtr<IOpcPartUri> partUri;
1756 HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
1757 "Could not create font resource part uri.");
1758
1759 SkTScopedComPtr<IXpsOMFontResource> xpsFontResource;
1760 HRM(this->fXpsFactory->CreateFontResource(fontStream.get(),
1761 embedding,
1762 partUri.get(),
1763 FALSE,
1764 &xpsFontResource),
1765 "Could not create font resource.");
1766
1767 //TODO: change openStream to return -1 for non-ttc, get rid of this.
1768 const uint8_t* data = (const uint8_t*)fontData->getMemoryBase();
1769 bool isTTC = (data &&
1770 fontData->getLength() >= sizeof(SkTTCFHeader) &&
1771 ((const SkTTCFHeader*)data)->ttcTag == SkTTCFHeader::TAG);
1772
1773 int glyphCount = typeface->countGlyphs();
1774
1775 TypefaceUse& newTypefaceUse = this->fTopTypefaces->emplace_back(
1776 typefaceID,
1777 isTTC ? ttcIndex : -1,
1778 std::move(fontData),
1779 std::move(xpsFontResource),
1780 glyphCount);
1781
1782 *typefaceUse = &newTypefaceUse;
1783 return S_OK;
1784}
1785
1786HRESULT SkXPSDevice::AddGlyphs(IXpsOMObjectFactory* xpsFactory,
1787 IXpsOMCanvas* canvas,
1788 const TypefaceUse* font,
1789 LPCWSTR text,
1790 XPS_GLYPH_INDEX* xpsGlyphs,
1791 UINT32 xpsGlyphsLen,
1792 XPS_POINT *origin,
1793 FLOAT fontSize,
1794 XPS_STYLE_SIMULATION sims,
1795 const SkMatrix& transform,
1796 const SkPaint& paint) {
1797 SkTScopedComPtr<IXpsOMGlyphs> glyphs;
1798 HRM(xpsFactory->CreateGlyphs(font->xpsFont.get(), &glyphs), "Could not create glyphs.");
1799 HRM(glyphs->SetFontFaceIndex(font->ttcIndex), "Could not set glyph font face index.");
1800
1801 //XPS uses affine transformations for everything...
1802 //...except positioning text.
1803 bool useCanvasForClip;
1804 if (transform.isTranslate()) {
1805 origin->x += SkScalarToFLOAT(transform.getTranslateX());
1806 origin->y += SkScalarToFLOAT(transform.getTranslateY());
1807 useCanvasForClip = false;
1808 } else {
1809 SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
1810 HR(this->createXpsTransform(transform, &xpsMatrixToUse));
1811 if (xpsMatrixToUse.get()) {
1812 HRM(glyphs->SetTransformLocal(xpsMatrixToUse.get()),
1813 "Could not set transform matrix.");
1814 useCanvasForClip = true;
1815 } else {
1816 SkDEBUGFAIL("Attempt to add glyphs in perspective.");
1817 useCanvasForClip = false;
1818 }
1819 }
1820
1821 SkTScopedComPtr<IXpsOMGlyphsEditor> glyphsEditor;
1822 HRM(glyphs->GetGlyphsEditor(&glyphsEditor), "Could not get glyph editor.");
1823
1824 if (text) {
1825 HRM(glyphsEditor->SetUnicodeString(text),
1826 "Could not set unicode string.");
1827 }
1828
1829 if (xpsGlyphs) {
1830 HRM(glyphsEditor->SetGlyphIndices(xpsGlyphsLen, xpsGlyphs),
1831 "Could not set glyphs.");
1832 }
1833
1834 HRM(glyphsEditor->ApplyEdits(), "Could not apply glyph edits.");
1835
1836 SkTScopedComPtr<IXpsOMBrush> xpsFillBrush;
1837 HR(this->createXpsBrush(
1838 paint,
1839 &xpsFillBrush,
1840 useCanvasForClip ? nullptr : &transform));
1841
1842 HRM(glyphs->SetFillBrushLocal(xpsFillBrush.get()),
1843 "Could not set fill brush.");
1844
1845 HRM(glyphs->SetOrigin(origin), "Could not set glyph origin.");
1846
1847 HRM(glyphs->SetFontRenderingEmSize(fontSize),
1848 "Could not set font size.");
1849
1850 HRM(glyphs->SetStyleSimulations(sims),
1851 "Could not set style simulations.");
1852
1853 SkTScopedComPtr<IXpsOMVisualCollection> visuals;
1854 HRM(canvas->GetVisuals(&visuals), "Could not get glyph canvas visuals.");
1855
1856 if (!useCanvasForClip) {
1857 HR(this->clip(glyphs.get()));
1858 HRM(visuals->Append(glyphs.get()), "Could not add glyphs to canvas.");
1859 } else {
1860 SkTScopedComPtr<IXpsOMCanvas> glyphCanvas;
1861 HRM(this->fXpsFactory->CreateCanvas(&glyphCanvas),
1862 "Could not create glyph canvas.");
1863
1864 SkTScopedComPtr<IXpsOMVisualCollection> glyphCanvasVisuals;
1865 HRM(glyphCanvas->GetVisuals(&glyphCanvasVisuals),
1866 "Could not get glyph visuals collection.");
1867
1868 HRM(glyphCanvasVisuals->Append(glyphs.get()),
1869 "Could not add glyphs to page.");
1870 HR(this->clip(glyphCanvas.get()));
1871
1872 HRM(visuals->Append(glyphCanvas.get()),
1873 "Could not add glyph canvas to page.");
1874 }
1875
1876 return S_OK;
1877}
1878
1879static bool text_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
1880 const SkPaint::Style style = paint.getStyle();
1881 return matrix.hasPerspective()
1882 || SkPaint::kStroke_Style == style
1884 || paint.getMaskFilter()
1885 ;
1886}
1887
1888void SkXPSDevice::onDrawGlyphRunList(SkCanvas*,
1889 const sktext::GlyphRunList& glyphRunList,
1890 const SkPaint& paint) {
1891 SkASSERT(!glyphRunList.hasRSXForm());
1892
1893 for (const auto& run : glyphRunList) {
1894 const SkGlyphID* glyphIDs = run.glyphsIDs().data();
1895 size_t glyphCount = run.glyphsIDs().size();
1896 const SkFont& font = run.font();
1897
1898 if (!glyphCount || !glyphIDs || font.getSize() <= 0) {
1899 continue;
1900 }
1901
1902 TypefaceUse* typeface;
1903 if (FAILED(CreateTypefaceUse(font, &typeface)) ||
1904 text_must_be_pathed(paint, this->localToDevice())) {
1905 SkPath path;
1906 //TODO: make this work, Draw currently does not handle as well.
1907 //paint.getTextPath(text, byteLength, x, y, &path);
1908 //this->drawPath(path, paint, nullptr, true);
1909 //TODO: add automation "text"
1910 continue;
1911 }
1912
1913 //TODO: handle font scale and skew in x (text_scale_skew)
1914
1915 // Advance width and offsets for glyphs measured in hundredths of the font em size
1916 // (XPS Spec 5.1.3).
1917 FLOAT centemPerUnit = 100.0f / SkScalarToFLOAT(font.getSize());
1918 AutoSTMalloc<32, XPS_GLYPH_INDEX> xpsGlyphs(glyphCount);
1919 size_t numGlyphs = typeface->glyphsUsed.size();
1920 size_t actualGlyphCount = 0;
1921
1922 for (size_t i = 0; i < glyphCount; ++i) {
1923 if (numGlyphs <= glyphIDs[i]) {
1924 continue;
1925 }
1926 const SkPoint& position = run.positions()[i];
1927 XPS_GLYPH_INDEX& xpsGlyph = xpsGlyphs[actualGlyphCount++];
1928 xpsGlyph.index = glyphIDs[i];
1929 xpsGlyph.advanceWidth = 0.0f;
1930 xpsGlyph.horizontalOffset = (SkScalarToFloat(position.fX) * centemPerUnit);
1931 xpsGlyph.verticalOffset = (SkScalarToFloat(position.fY) * -centemPerUnit);
1932 typeface->glyphsUsed.set(xpsGlyph.index);
1933 }
1934
1935 if (actualGlyphCount == 0) {
1936 return;
1937 }
1938
1939 XPS_POINT origin = {
1940 glyphRunList.origin().x(),
1941 glyphRunList.origin().y(),
1942 };
1943
1944 HRV(AddGlyphs(this->fXpsFactory.get(),
1945 this->fCurrentXpsCanvas.get(),
1946 typeface,
1947 nullptr,
1948 xpsGlyphs.get(), actualGlyphCount,
1949 &origin,
1950 SkScalarToFLOAT(font.getSize()),
1951 XPS_STYLE_SIMULATION_NONE,
1952 this->localToDevice(),
1953 paint));
1954 }
1955}
1956
1957void SkXPSDevice::drawDevice(SkDevice* dev, const SkSamplingOptions&, const SkPaint&) {
1958 SkXPSDevice* that = static_cast<SkXPSDevice*>(dev);
1959 SkASSERT(that->fTopTypefaces == this->fTopTypefaces);
1960
1961 SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
1962 HRVM(this->createXpsTransform(dev->getRelativeTransform(*this), &xpsTransform),
1963 "Could not create layer transform.");
1964 HRVM(that->fCurrentXpsCanvas->SetTransformLocal(xpsTransform.get()),
1965 "Could not set layer transform.");
1966
1967 //Get the current visual collection and add the layer to it.
1968 SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
1969 HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
1970 "Could not get current visuals for layer.");
1971 HRVM(currentVisuals->Append(that->fCurrentXpsCanvas.get()),
1972 "Could not add layer to current visuals.");
1973}
1974
1975sk_sp<SkDevice> SkXPSDevice::createDevice(const CreateInfo& info, const SkPaint*) {
1976 sk_sp<SkXPSDevice> dev = sk_make_sp<SkXPSDevice>(info.fInfo.dimensions());
1977 dev->fXpsFactory.reset(SkRefComPtr(fXpsFactory.get()));
1978 dev->fCurrentCanvasSize = this->fCurrentCanvasSize;
1979 dev->fCurrentUnitsPerMeter = this->fCurrentUnitsPerMeter;
1980 dev->fCurrentPixelsPerMeter = this->fCurrentPixelsPerMeter;
1981 dev->fTopTypefaces = this->fTopTypefaces;
1982 SkAssertResult(dev->createCanvasForLayer());
1983 return dev;
1984}
1985
1986void SkXPSDevice::drawOval( const SkRect& o, const SkPaint& p) {
1987 SkPath path;
1988 path.addOval(o);
1989 this->drawPath(path, p, true);
1990}
1991
1992void SkXPSDevice::drawImageRect(const SkImage* image,
1993 const SkRect* src,
1994 const SkRect& dst,
1995 const SkSamplingOptions& sampling,
1996 const SkPaint& paint,
1997 SkCanvas::SrcRectConstraint constraint) {
1998 // TODO: support gpu images
2000 if (!as_IB(image)->getROPixels(nullptr, &bitmap)) {
2001 return;
2002 }
2003
2004 SkRect bitmapBounds = SkRect::Make(bitmap.bounds());
2005 SkRect srcBounds = src ? *src : bitmapBounds;
2006 SkMatrix matrix = SkMatrix::RectToRect(srcBounds, dst);
2007 SkRect actualDst;
2008 if (!src || bitmapBounds.contains(*src)) {
2009 actualDst = dst;
2010 } else {
2011 if (!srcBounds.intersect(bitmapBounds)) {
2012 return;
2013 }
2014 matrix.mapRect(&actualDst, srcBounds);
2015 }
2016
2017 auto bitmapShader = SkMakeBitmapShaderForPaint(paint, bitmap,
2019 sampling, &matrix, kNever_SkCopyPixelsMode);
2020 SkASSERT(bitmapShader);
2021 if (!bitmapShader) { return; }
2022 SkPaint paintWithShader(paint);
2023 paintWithShader.setStyle(SkPaint::kFill_Style);
2024 paintWithShader.setShader(std::move(bitmapShader));
2025 this->drawRect(actualDst, paintWithShader);
2026}
2027#endif//defined(SK_BUILD_FOR_WIN)
#define BIG
Definition BlurBench.cpp:20
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
uint16_t glyphs[5]
int count
static const int points[]
SkColor4f color
#define SkAssertResult(cond)
Definition SkAssert.h:123
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
void SkClipStack_AsPath(const SkClipStack &cs, SkPath *path)
#define SkColorGetR(color)
Definition SkColor.h:65
#define SkColorGetG(color)
Definition SkColor.h:69
uint32_t SkColor
Definition SkColor.h:37
uint8_t SkAlpha
Definition SkColor.h:26
#define SkColorGetA(color)
Definition SkColor.h:61
#define SkColorGetB(color)
Definition SkColor.h:73
#define SkDEBUGF(...)
Definition SkDebug.h:24
#define SkEndian_SwapBE32(n)
Definition SkEndian.h:136
#define SkEndian_SwapBE16(n)
Definition SkEndian.h:135
static bool read(SkStream *stream, void *buffer, size_t amount)
@ kNever_SkCopyPixelsMode
never copy src pixels (even if they are marked mutable)
Definition SkImagePriv.h:20
sk_sp< SkShader > SkMakeBitmapShaderForPaint(const SkPaint &paint, const SkBitmap &src, SkTileMode, SkTileMode, const SkSamplingOptions &, const SkMatrix *localMatrix, SkCopyPixelsMode)
static SkImage_Base * as_IB(SkImage *image)
SK_API void sk_free(void *)
static void * sk_malloc_throw(size_t size)
Definition SkMalloc.h:67
SK_API void * sk_realloc_throw(void *buffer, size_t size)
SkMaskFilterBase * as_MFB(SkMaskFilter *mf)
std::unique_ptr< uint8_t, SkFunctionObject< SkMaskBuilder::FreeImage > > SkAutoMaskFreeImage
Definition SkMask.h:316
uint32_t SK_OT_ULONG
bool SK_API Simplify(const SkPath &path, SkPath *result)
static SkPath clip(const SkPath &path, const SkHalfPlane &plane)
Definition SkPath.cpp:3824
#define SkScalarInvert(x)
Definition SkScalar.h:73
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition SkScalar.h:101
#define SkScalarToFloat(x)
Definition SkScalar.h:61
#define SK_Scalar1
Definition SkScalar.h:18
#define SkIntToScalar(x)
Definition SkScalar.h:57
#define SK_ScalarSqrt2
Definition SkScalar.h:20
SkShaderBase * as_SB(SkShader *shader)
SkTileMode
Definition SkTileMode.h:13
static constexpr int kSkTileModeCount
Definition SkTileMode.h:39
constexpr D SkTo(S s)
Definition SkTo.h:16
constexpr uint32_t SkToU32(S x)
Definition SkTo.h:26
uint32_t SkTypefaceID
Definition SkTypeface.h:38
uint16_t SkGlyphID
Definition SkTypes.h:179
const SkPixmap & pixmap() const
Definition SkBitmap.h:133
SrcRectConstraint
Definition SkCanvas.h:1541
SkMatrix getRelativeTransform(const SkDevice &) const
Definition SkDevice.cpp:102
static bool DrawToMask(const SkPath &devPath, const SkIRect &clipBounds, const SkMaskFilter *, const SkMatrix *filterMatrix, SkMaskBuilder *dst, SkMaskBuilder::CreateMode mode, SkStrokeRec::InitStyle style)
bool asLegacyBitmap(SkBitmap *bitmap, LegacyBitmapMode legacyBitmapMode=kRO_LegacyBitmapMode) const
Definition SkImage.cpp:233
static SkMatrix RectToRect(const SkRect &src, const SkRect &dst, ScaleToFit mode=kFill_ScaleToFit)
Definition SkMatrix.h:157
static constexpr int kATransY
vertical translation
Definition SkMatrix.h:371
SkMatrix & postConcat(const SkMatrix &other)
Definition SkMatrix.cpp:683
static constexpr int kAScaleY
vertical scale factor
Definition SkMatrix.h:369
static constexpr int kASkewX
horizontal skew factor
Definition SkMatrix.h:368
void mapVectors(SkVector dst[], const SkVector src[], int count) const
static constexpr int kATransX
horizontal translation
Definition SkMatrix.h:370
static constexpr int kAScaleX
horizontal scale factor
Definition SkMatrix.h:366
static constexpr int kASkewY
vertical skew factor
Definition SkMatrix.h:367
SkMatrix & preConcat(const SkMatrix &other)
Definition SkMatrix.cpp:674
void setStyle(Style style)
Definition SkPaint.cpp:105
Style getStyle() const
Definition SkPaint.h:204
SkColor getColor() const
Definition SkPaint.h:225
@ kStroke_Style
set to stroke geometry
Definition SkPaint.h:194
@ kFill_Style
set to fill geometry
Definition SkPaint.h:193
@ kStrokeAndFill_Style
sets to stroke and fill geometry
Definition SkPaint.h:195
void setPathEffect(sk_sp< SkPathEffect > pathEffect)
SkScalar getStrokeWidth() const
Definition SkPaint.h:300
@ kMiter_Join
extends to miter limit
Definition SkPaint.h:359
uint8_t getAlpha() const
Definition SkPaint.h:264
SkShader * getShader() const
Definition SkPaint.h:397
void setStrokeWidth(SkScalar width)
Definition SkPaint.cpp:159
SkPathFillType getFillType() const
Definition SkPath.h:230
void setFillType(SkPathFillType ft)
Definition SkPath.h:235
@ kClose_Verb
Definition SkPath.h:1463
@ kMove_Verb
Definition SkPath.h:1458
@ kConic_Verb
Definition SkPath.h:1461
@ kDone_Verb
Definition SkPath.h:1464
@ kCubic_Verb
Definition SkPath.h:1462
@ kQuad_Verb
Definition SkPath.h:1460
@ kLine_Verb
Definition SkPath.h:1459
void transform(const SkMatrix &matrix, SkPath *dst, SkApplyPerspectiveClip pc=SkApplyPerspectiveClip::kYes) const
Definition SkPath.cpp:1647
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition SkPath.cpp:854
SkImage * isAImage(SkMatrix *localMatrix, SkTileMode xy[2]) const
Definition SkShader.cpp:22
int size() const
Definition SkTDArray.h:138
bool empty() const
Definition SkTDArray.h:135
void push_back(const T &v)
Definition SkTDArray.h:219
T * begin()
Definition SkTDArray.h:150
void clear()
Definition SkTDArray.h:175
int countGlyphs() const
SkTypefaceID uniqueID() const
Definition SkTypeface.h:101
std::unique_ptr< SkStreamAsset > openStream(int *ttcIndex) const
T * get() const
Definition SkRefCnt.h:303
void reset(T *ptr=nullptr)
Definition SkRefCnt.h:310
bool hasRSXForm() const
Definition GlyphRun.h:105
SkPoint origin() const
Definition GlyphRun.h:114
const Paint & paint
sk_sp< SkImage > image
Definition examples.cpp:29
float SkScalar
Definition extension.cpp:12
struct MyStruct s
glong glong end
static const uint8_t buffer[]
GAsyncResult * result
std::u16string text
static void drawPath(SkPath &path, SkCanvas *canvas, SkColor color, const SkRect &clip, SkPaint::Cap cap, SkPaint::Join join, SkPaint::Style style, SkPathFillType fill, SkScalar strokeWidth)
Definition linepaths.cpp:22
return FALSE
SK_API bool Encode(SkWStream *dst, const SkPixmap &src, const Options &options)
unsigned useCenter Optional< SkMatrix > matrix
Definition SkRecords.h:258
clipRect(r.rect, r.opAA.op(), r.opAA.aa())) template<> void Draw
clipPath(r.path, r.opAA.op(), r.opAA.aa())) DRAW(ClipRRect
sk_sp< SkBlender > blender SkRect rect
Definition SkRecords.h:350
PODArray< SkColor > colors
Definition SkRecords.h:276
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition switches.h:57
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switches.h:41
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition switches.h:259
font
Font Metadata and Metrics.
dst
Definition cp.py:12
Definition run.py:1
SK_API bool FillPathWithPaint(const SkPath &src, const SkPaint &paint, SkPath *dst, const SkRect *cullRect, SkScalar resScale=1)
Definition ref_ptr.h:256
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47
SkTileMode tmy
SkTileMode tmx
int32_t height
int32_t width
void write(SkWStream *wStream, const T &text)
Definition skqp.cpp:188
Point offset
int32_t fBottom
larger y-axis bounds
Definition SkRect.h:36
constexpr int32_t height() const
Definition SkRect.h:165
int32_t fTop
smaller y-axis bounds
Definition SkRect.h:34
constexpr int32_t width() const
Definition SkRect.h:158
int32_t fLeft
smaller x-axis bounds
Definition SkRect.h:33
int32_t fRight
larger x-axis bounds
Definition SkRect.h:35
static SkImageInfo MakeA8(int width, int height)
@ kComputeBoundsAndRenderImage_CreateMode
compute bounds, alloc image and render into it
Definition SkMask.h:301
uint8_t *& image()
Definition SkMask.h:236
const uint32_t fRowBytes
Definition SkMask.h:43
@ kA8_Format
8bits per pixel mask (e.g. antialiasing)
Definition SkMask.h:28
uint8_t const *const fImage
Definition SkMask.h:41
const SkIRect fBounds
Definition SkMask.h:42
const Format fFormat
Definition SkMask.h:44
float fX
x-axis value
void set(float x, float y)
float length() const
float fY
y-axis value
constexpr float y() const
constexpr float x() const
static SkRect Make(const SkISize &size)
Definition SkRect.h:669
SkScalar fBottom
larger y-axis bounds
Definition extension.cpp:17
bool intersect(const SkRect &r)
Definition SkRect.cpp:114
SkScalar fLeft
smaller x-axis bounds
Definition extension.cpp:14
SkScalar fRight
larger x-axis bounds
Definition extension.cpp:16
bool contains(SkScalar x, SkScalar y) const
Definition extension.cpp:19
static constexpr SkRect MakeSize(const SkSize &size)
Definition SkRect.h:633
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
SkScalar fTop
smaller y-axis bounds
Definition extension.cpp:15
SK_SFNT_USHORT numTables
SK_OT_Fixed version
SK_OT_ULONG numOffsets
static const SK_OT_Fixed version_1
SK_SFNT_ULONG ttcTag
static const SK_OT_ULONG TAG
int BOOL
#define FAILED(hr)
struct _GUID GUID