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