1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2011 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFDevice.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBlendMode.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkClipOp.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorFilter.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkFont.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImage.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkM44.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMaskFilter.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPath.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathEffect.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathTypes.h"
29*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPathUtils.h"
30*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPixmap.h"
31*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
32*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
33*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
34*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
35*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSpan.h"
36*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
37*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStrokeRec.h"
38*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
39*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurfaceProps.h"
40*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypeface.h"
41*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
42*c8dee2aaSAndroid Build Coastguard Worker #include "include/docs/SkPDFDocument.h"
43*c8dee2aaSAndroid Build Coastguard Worker #include "include/encode/SkJpegEncoder.h"
44*c8dee2aaSAndroid Build Coastguard Worker #include "include/pathops/SkPathOps.h"
45*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
46*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
47*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
48*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkScopeExit.h"
49*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTLazy.h"
50*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUTF.h"
51*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkAdvancedTypefaceMetrics.h"
52*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkAnnotationKeys.h"
53*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBitmapDevice.h"
54*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkBlendModePriv.h"
55*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkColorSpacePriv.h"
56*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkDevice.h"
57*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkDraw.h"
58*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkGlyph.h"
59*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMask.h"
60*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMaskFilterBase.h"
61*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPaintPriv.h"
62*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRasterClip.h"
63*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkSpecialImage.h"
64*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkStrikeSpec.h"
65*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkBitmapKey.h"
66*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkClusterator.h"
67*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFBitmap.h"
68*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFDocumentPriv.h"
69*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFFont.h"
70*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFFormXObject.h"
71*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFGraphicState.h"
72*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFResourceDict.h"
73*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFShader.h"
74*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFTag.h"
75*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFTypes.h"
76*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFUnion.h"
77*c8dee2aaSAndroid Build Coastguard Worker #include "src/pdf/SkPDFUtils.h"
78*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/SkColorShader.h"
79*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/SkShaderBase.h"
80*c8dee2aaSAndroid Build Coastguard Worker #include "src/text/GlyphRun.h"
81*c8dee2aaSAndroid Build Coastguard Worker #include "src/utils/SkClipStackUtils.h"
82*c8dee2aaSAndroid Build Coastguard Worker
83*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
84*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
85*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
86*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
87*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard Worker class SkBlender;
90*c8dee2aaSAndroid Build Coastguard Worker class SkMesh;
91*c8dee2aaSAndroid Build Coastguard Worker class SkVertices;
92*c8dee2aaSAndroid Build Coastguard Worker
93*c8dee2aaSAndroid Build Coastguard Worker using namespace skia_private;
94*c8dee2aaSAndroid Build Coastguard Worker
MarkedContentManager(SkPDFDocument * document,SkDynamicMemoryWStream * out)95*c8dee2aaSAndroid Build Coastguard Worker SkPDFDevice::MarkedContentManager::MarkedContentManager(SkPDFDocument* document,
96*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream* out)
97*c8dee2aaSAndroid Build Coastguard Worker : fDoc(document)
98*c8dee2aaSAndroid Build Coastguard Worker , fOut(out)
99*c8dee2aaSAndroid Build Coastguard Worker , fCurrentlyActiveMark()
100*c8dee2aaSAndroid Build Coastguard Worker , fNextMarksElemId(0)
101*c8dee2aaSAndroid Build Coastguard Worker , fMadeMarks(false)
102*c8dee2aaSAndroid Build Coastguard Worker {}
103*c8dee2aaSAndroid Build Coastguard Worker
~MarkedContentManager()104*c8dee2aaSAndroid Build Coastguard Worker SkPDFDevice::MarkedContentManager::~MarkedContentManager() {
105*c8dee2aaSAndroid Build Coastguard Worker // This does not close the last open mark, that is done in SkPDFDevice::content.
106*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fNextMarksElemId == 0);
107*c8dee2aaSAndroid Build Coastguard Worker };
108*c8dee2aaSAndroid Build Coastguard Worker
setNextMarksElemId(int nextMarksElemId)109*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::MarkedContentManager::setNextMarksElemId(int nextMarksElemId) {
110*c8dee2aaSAndroid Build Coastguard Worker fNextMarksElemId = nextMarksElemId;
111*c8dee2aaSAndroid Build Coastguard Worker }
elemId() const112*c8dee2aaSAndroid Build Coastguard Worker int SkPDFDevice::MarkedContentManager::elemId() const { return fNextMarksElemId; }
113*c8dee2aaSAndroid Build Coastguard Worker
beginMark()114*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::MarkedContentManager::beginMark() {
115*c8dee2aaSAndroid Build Coastguard Worker if (fNextMarksElemId == fCurrentlyActiveMark.elemId()) {
116*c8dee2aaSAndroid Build Coastguard Worker return;
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker if (fCurrentlyActiveMark) {
119*c8dee2aaSAndroid Build Coastguard Worker // End this mark
120*c8dee2aaSAndroid Build Coastguard Worker fOut->writeText("EMC\n");
121*c8dee2aaSAndroid Build Coastguard Worker fCurrentlyActiveMark = SkPDFStructTree::Mark();
122*c8dee2aaSAndroid Build Coastguard Worker }
123*c8dee2aaSAndroid Build Coastguard Worker if (fNextMarksElemId) {
124*c8dee2aaSAndroid Build Coastguard Worker fCurrentlyActiveMark = fDoc->createMarkForElemId(fNextMarksElemId);
125*c8dee2aaSAndroid Build Coastguard Worker if (fCurrentlyActiveMark) {
126*c8dee2aaSAndroid Build Coastguard Worker // Begin this mark
127*c8dee2aaSAndroid Build Coastguard Worker SkPDFUnion::Name(fCurrentlyActiveMark.structType()).emitObject(fOut);
128*c8dee2aaSAndroid Build Coastguard Worker fOut->writeText(" <</MCID ");
129*c8dee2aaSAndroid Build Coastguard Worker fOut->writeDecAsText(fCurrentlyActiveMark.mcid());
130*c8dee2aaSAndroid Build Coastguard Worker fOut->writeText(" >>BDC\n");
131*c8dee2aaSAndroid Build Coastguard Worker fMadeMarks = true;
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker }
134*c8dee2aaSAndroid Build Coastguard Worker }
135*c8dee2aaSAndroid Build Coastguard Worker
hasActiveMark() const136*c8dee2aaSAndroid Build Coastguard Worker bool SkPDFDevice::MarkedContentManager::hasActiveMark() const { return bool(fCurrentlyActiveMark); }
137*c8dee2aaSAndroid Build Coastguard Worker
accumulate(const SkPoint & p)138*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::MarkedContentManager::accumulate(const SkPoint& p) {
139*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fCurrentlyActiveMark);
140*c8dee2aaSAndroid Build Coastguard Worker fCurrentlyActiveMark.accumulate(p);
141*c8dee2aaSAndroid Build Coastguard Worker }
142*c8dee2aaSAndroid Build Coastguard Worker
143*c8dee2aaSAndroid Build Coastguard Worker #ifndef SK_PDF_MASK_QUALITY
144*c8dee2aaSAndroid Build Coastguard Worker // If MASK_QUALITY is in [0,100], will be used for JpegEncoder.
145*c8dee2aaSAndroid Build Coastguard Worker // Otherwise, just encode masks losslessly.
146*c8dee2aaSAndroid Build Coastguard Worker #define SK_PDF_MASK_QUALITY 50
147*c8dee2aaSAndroid Build Coastguard Worker // Since these masks are used for blurry shadows, we shouldn't need
148*c8dee2aaSAndroid Build Coastguard Worker // high quality. Raise this value if your shadows have visible JPEG
149*c8dee2aaSAndroid Build Coastguard Worker // artifacts.
150*c8dee2aaSAndroid Build Coastguard Worker // If SkJpegEncoder::Encode fails, we will fall back to the lossless
151*c8dee2aaSAndroid Build Coastguard Worker // encoding.
152*c8dee2aaSAndroid Build Coastguard Worker #endif
153*c8dee2aaSAndroid Build Coastguard Worker
154*c8dee2aaSAndroid Build Coastguard Worker // This function destroys the mask and either frees or takes the pixels.
mask_to_greyscale_image(SkMaskBuilder * mask)155*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> mask_to_greyscale_image(SkMaskBuilder* mask) {
156*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> img;
157*c8dee2aaSAndroid Build Coastguard Worker SkPixmap pm(SkImageInfo::Make(mask->fBounds.width(), mask->fBounds.height(),
158*c8dee2aaSAndroid Build Coastguard Worker kGray_8_SkColorType, kOpaque_SkAlphaType),
159*c8dee2aaSAndroid Build Coastguard Worker mask->fImage, mask->fRowBytes);
160*c8dee2aaSAndroid Build Coastguard Worker const int imgQuality = SK_PDF_MASK_QUALITY;
161*c8dee2aaSAndroid Build Coastguard Worker if (imgQuality <= 100 && imgQuality >= 0) {
162*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream buffer;
163*c8dee2aaSAndroid Build Coastguard Worker SkJpegEncoder::Options jpegOptions;
164*c8dee2aaSAndroid Build Coastguard Worker jpegOptions.fQuality = imgQuality;
165*c8dee2aaSAndroid Build Coastguard Worker if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) {
166*c8dee2aaSAndroid Build Coastguard Worker img = SkImages::DeferredFromEncodedData(buffer.detachAsData());
167*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(img);
168*c8dee2aaSAndroid Build Coastguard Worker if (img) {
169*c8dee2aaSAndroid Build Coastguard Worker SkMaskBuilder::FreeImage(mask->image());
170*c8dee2aaSAndroid Build Coastguard Worker }
171*c8dee2aaSAndroid Build Coastguard Worker }
172*c8dee2aaSAndroid Build Coastguard Worker }
173*c8dee2aaSAndroid Build Coastguard Worker if (!img) {
174*c8dee2aaSAndroid Build Coastguard Worker img = SkImages::RasterFromPixmap(
175*c8dee2aaSAndroid Build Coastguard Worker pm, [](const void* p, void*) { SkMaskBuilder::FreeImage(const_cast<void*>(p)); }, nullptr);
176*c8dee2aaSAndroid Build Coastguard Worker }
177*c8dee2aaSAndroid Build Coastguard Worker *mask = SkMaskBuilder(); // destructive;
178*c8dee2aaSAndroid Build Coastguard Worker return img;
179*c8dee2aaSAndroid Build Coastguard Worker }
180*c8dee2aaSAndroid Build Coastguard Worker
alpha_image_to_greyscale_image(const SkImage * mask)181*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> alpha_image_to_greyscale_image(const SkImage* mask) {
182*c8dee2aaSAndroid Build Coastguard Worker int w = mask->width(), h = mask->height();
183*c8dee2aaSAndroid Build Coastguard Worker SkBitmap greyBitmap;
184*c8dee2aaSAndroid Build Coastguard Worker greyBitmap.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
185*c8dee2aaSAndroid Build Coastguard Worker // TODO: support gpu images in pdf
186*c8dee2aaSAndroid Build Coastguard Worker if (!mask->readPixels(nullptr, SkImageInfo::MakeA8(w, h),
187*c8dee2aaSAndroid Build Coastguard Worker greyBitmap.getPixels(), greyBitmap.rowBytes(), 0, 0)) {
188*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
189*c8dee2aaSAndroid Build Coastguard Worker }
190*c8dee2aaSAndroid Build Coastguard Worker greyBitmap.setImmutable();
191*c8dee2aaSAndroid Build Coastguard Worker return greyBitmap.asImage();
192*c8dee2aaSAndroid Build Coastguard Worker }
193*c8dee2aaSAndroid Build Coastguard Worker
add_resource(THashSet<SkPDFIndirectReference> & resources,SkPDFIndirectReference ref)194*c8dee2aaSAndroid Build Coastguard Worker static int add_resource(THashSet<SkPDFIndirectReference>& resources, SkPDFIndirectReference ref) {
195*c8dee2aaSAndroid Build Coastguard Worker resources.add(ref);
196*c8dee2aaSAndroid Build Coastguard Worker return ref.fValue;
197*c8dee2aaSAndroid Build Coastguard Worker }
198*c8dee2aaSAndroid Build Coastguard Worker
draw_points(SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & paint,const SkIRect & bounds,SkDevice * device)199*c8dee2aaSAndroid Build Coastguard Worker static void draw_points(SkCanvas::PointMode mode,
200*c8dee2aaSAndroid Build Coastguard Worker size_t count,
201*c8dee2aaSAndroid Build Coastguard Worker const SkPoint* points,
202*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint,
203*c8dee2aaSAndroid Build Coastguard Worker const SkIRect& bounds,
204*c8dee2aaSAndroid Build Coastguard Worker SkDevice* device) {
205*c8dee2aaSAndroid Build Coastguard Worker SkRasterClip rc(bounds);
206*c8dee2aaSAndroid Build Coastguard Worker SkDraw draw;
207*c8dee2aaSAndroid Build Coastguard Worker draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(bounds.right(), bounds.bottom()), nullptr, 0);
208*c8dee2aaSAndroid Build Coastguard Worker draw.fCTM = &device->localToDevice();
209*c8dee2aaSAndroid Build Coastguard Worker draw.fRC = &rc;
210*c8dee2aaSAndroid Build Coastguard Worker draw.drawPoints(mode, count, points, paint, device);
211*c8dee2aaSAndroid Build Coastguard Worker }
212*c8dee2aaSAndroid Build Coastguard Worker
transform_shader(SkPaint * paint,const SkMatrix & ctm)213*c8dee2aaSAndroid Build Coastguard Worker static void transform_shader(SkPaint* paint, const SkMatrix& ctm) {
214*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!ctm.isIdentity());
215*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_ANDROID_FRAMEWORK)
216*c8dee2aaSAndroid Build Coastguard Worker // A shader's matrix is: CTM x LocalMatrix x WrappingLocalMatrix. We want to
217*c8dee2aaSAndroid Build Coastguard Worker // switch to device space, where CTM = I, while keeping the original behavior.
218*c8dee2aaSAndroid Build Coastguard Worker //
219*c8dee2aaSAndroid Build Coastguard Worker // I * LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
220*c8dee2aaSAndroid Build Coastguard Worker // LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
221*c8dee2aaSAndroid Build Coastguard Worker // InvLocalMatrix * LocalMatrix * NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
222*c8dee2aaSAndroid Build Coastguard Worker // NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
223*c8dee2aaSAndroid Build Coastguard Worker //
224*c8dee2aaSAndroid Build Coastguard Worker SkMatrix lm = SkPDFUtils::GetShaderLocalMatrix(paint->getShader());
225*c8dee2aaSAndroid Build Coastguard Worker SkMatrix lmInv;
226*c8dee2aaSAndroid Build Coastguard Worker if (lm.invert(&lmInv)) {
227*c8dee2aaSAndroid Build Coastguard Worker SkMatrix m = SkMatrix::Concat(SkMatrix::Concat(lmInv, ctm), lm);
228*c8dee2aaSAndroid Build Coastguard Worker paint->setShader(paint->getShader()->makeWithLocalMatrix(m));
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker return;
231*c8dee2aaSAndroid Build Coastguard Worker #endif
232*c8dee2aaSAndroid Build Coastguard Worker paint->setShader(paint->getShader()->makeWithLocalMatrix(ctm));
233*c8dee2aaSAndroid Build Coastguard Worker }
234*c8dee2aaSAndroid Build Coastguard Worker
235*c8dee2aaSAndroid Build Coastguard Worker
clean_paint(const SkPaint & srcPaint)236*c8dee2aaSAndroid Build Coastguard Worker static SkTCopyOnFirstWrite<SkPaint> clean_paint(const SkPaint& srcPaint) {
237*c8dee2aaSAndroid Build Coastguard Worker SkTCopyOnFirstWrite<SkPaint> paint(srcPaint);
238*c8dee2aaSAndroid Build Coastguard Worker // If the paint will definitely draw opaquely, replace kSrc with
239*c8dee2aaSAndroid Build Coastguard Worker // kSrcOver. http://crbug.com/473572
240*c8dee2aaSAndroid Build Coastguard Worker if (!paint->isSrcOver() &&
241*c8dee2aaSAndroid Build Coastguard Worker SkBlendFastPath::kSrcOver == CheckFastPath(*paint, false))
242*c8dee2aaSAndroid Build Coastguard Worker {
243*c8dee2aaSAndroid Build Coastguard Worker paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
244*c8dee2aaSAndroid Build Coastguard Worker }
245*c8dee2aaSAndroid Build Coastguard Worker if (paint->getColorFilter()) {
246*c8dee2aaSAndroid Build Coastguard Worker // We assume here that PDFs all draw in sRGB.
247*c8dee2aaSAndroid Build Coastguard Worker SkPaintPriv::RemoveColorFilter(paint.writable(), sk_srgb_singleton());
248*c8dee2aaSAndroid Build Coastguard Worker }
249*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!paint->getColorFilter());
250*c8dee2aaSAndroid Build Coastguard Worker return paint;
251*c8dee2aaSAndroid Build Coastguard Worker }
252*c8dee2aaSAndroid Build Coastguard Worker
set_style(SkTCopyOnFirstWrite<SkPaint> * paint,SkPaint::Style style)253*c8dee2aaSAndroid Build Coastguard Worker static void set_style(SkTCopyOnFirstWrite<SkPaint>* paint, SkPaint::Style style) {
254*c8dee2aaSAndroid Build Coastguard Worker if (paint->get()->getStyle() != style) {
255*c8dee2aaSAndroid Build Coastguard Worker paint->writable()->setStyle(style);
256*c8dee2aaSAndroid Build Coastguard Worker }
257*c8dee2aaSAndroid Build Coastguard Worker }
258*c8dee2aaSAndroid Build Coastguard Worker
259*c8dee2aaSAndroid Build Coastguard Worker /* Calculate an inverted path's equivalent non-inverted path, given the
260*c8dee2aaSAndroid Build Coastguard Worker * canvas bounds.
261*c8dee2aaSAndroid Build Coastguard Worker * outPath may alias with invPath (since this is supported by PathOps).
262*c8dee2aaSAndroid Build Coastguard Worker */
calculate_inverse_path(const SkRect & bounds,const SkPath & invPath,SkPath * outPath)263*c8dee2aaSAndroid Build Coastguard Worker static bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
264*c8dee2aaSAndroid Build Coastguard Worker SkPath* outPath) {
265*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(invPath.isInverseFillType());
266*c8dee2aaSAndroid Build Coastguard Worker return Op(SkPath::Rect(bounds), invPath, kIntersect_SkPathOp, outPath);
267*c8dee2aaSAndroid Build Coastguard Worker }
268*c8dee2aaSAndroid Build Coastguard Worker
createDevice(const CreateInfo & cinfo,const SkPaint * layerPaint)269*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkDevice> SkPDFDevice::createDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
270*c8dee2aaSAndroid Build Coastguard Worker // PDF does not support image filters, so render them on CPU.
271*c8dee2aaSAndroid Build Coastguard Worker // Note that this rendering is done at "screen" resolution (100dpi), not
272*c8dee2aaSAndroid Build Coastguard Worker // printer resolution.
273*c8dee2aaSAndroid Build Coastguard Worker
274*c8dee2aaSAndroid Build Coastguard Worker // TODO: It may be possible to express some filters natively using PDF
275*c8dee2aaSAndroid Build Coastguard Worker // to improve quality and file size (https://bug.skia.org/3043)
276*c8dee2aaSAndroid Build Coastguard Worker if ((layerPaint && (layerPaint->getImageFilter() || layerPaint->getColorFilter()))
277*c8dee2aaSAndroid Build Coastguard Worker || (cinfo.fInfo.colorSpace() && !cinfo.fInfo.colorSpace()->isSRGB())) {
278*c8dee2aaSAndroid Build Coastguard Worker // need to return a raster device, which we will detect in drawDevice()
279*c8dee2aaSAndroid Build Coastguard Worker return SkBitmapDevice::Create(cinfo.fInfo,
280*c8dee2aaSAndroid Build Coastguard Worker SkSurfaceProps());
281*c8dee2aaSAndroid Build Coastguard Worker }
282*c8dee2aaSAndroid Build Coastguard Worker return sk_make_sp<SkPDFDevice>(cinfo.fInfo.dimensions(), fDocument);
283*c8dee2aaSAndroid Build Coastguard Worker }
284*c8dee2aaSAndroid Build Coastguard Worker
285*c8dee2aaSAndroid Build Coastguard Worker // A helper class to automatically finish a ContentEntry at the end of a
286*c8dee2aaSAndroid Build Coastguard Worker // drawing method and maintain the state needed between set up and finish.
287*c8dee2aaSAndroid Build Coastguard Worker class ScopedContentEntry {
288*c8dee2aaSAndroid Build Coastguard Worker public:
ScopedContentEntry(SkPDFDevice * device,const SkClipStack * clipStack,const SkMatrix & matrix,const SkPaint & paint,SkScalar textScale=0)289*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry(SkPDFDevice* device,
290*c8dee2aaSAndroid Build Coastguard Worker const SkClipStack* clipStack,
291*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
292*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint,
293*c8dee2aaSAndroid Build Coastguard Worker SkScalar textScale = 0)
294*c8dee2aaSAndroid Build Coastguard Worker : fDevice(device)
295*c8dee2aaSAndroid Build Coastguard Worker , fBlendMode(SkBlendMode::kSrcOver)
296*c8dee2aaSAndroid Build Coastguard Worker , fClipStack(clipStack)
297*c8dee2aaSAndroid Build Coastguard Worker {
298*c8dee2aaSAndroid Build Coastguard Worker if (matrix.hasPerspective()) {
299*c8dee2aaSAndroid Build Coastguard Worker NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
300*c8dee2aaSAndroid Build Coastguard Worker return;
301*c8dee2aaSAndroid Build Coastguard Worker }
302*c8dee2aaSAndroid Build Coastguard Worker fBlendMode = paint.getBlendMode_or(SkBlendMode::kSrcOver);
303*c8dee2aaSAndroid Build Coastguard Worker fContentStream =
304*c8dee2aaSAndroid Build Coastguard Worker fDevice->setUpContentEntry(clipStack, matrix, paint, textScale, &fDstFormXObject);
305*c8dee2aaSAndroid Build Coastguard Worker }
ScopedContentEntry(SkPDFDevice * dev,const SkPaint & paint,SkScalar textScale=0)306*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, SkScalar textScale = 0)
307*c8dee2aaSAndroid Build Coastguard Worker : ScopedContentEntry(dev, &dev->cs(), dev->localToDevice(), paint, textScale) {}
308*c8dee2aaSAndroid Build Coastguard Worker
~ScopedContentEntry()309*c8dee2aaSAndroid Build Coastguard Worker ~ScopedContentEntry() {
310*c8dee2aaSAndroid Build Coastguard Worker if (fContentStream) {
311*c8dee2aaSAndroid Build Coastguard Worker SkPath* shape = &fShape;
312*c8dee2aaSAndroid Build Coastguard Worker if (shape->isEmpty()) {
313*c8dee2aaSAndroid Build Coastguard Worker shape = nullptr;
314*c8dee2aaSAndroid Build Coastguard Worker }
315*c8dee2aaSAndroid Build Coastguard Worker fDevice->finishContentEntry(fClipStack, fBlendMode, fDstFormXObject, shape);
316*c8dee2aaSAndroid Build Coastguard Worker }
317*c8dee2aaSAndroid Build Coastguard Worker }
318*c8dee2aaSAndroid Build Coastguard Worker
operator bool() const319*c8dee2aaSAndroid Build Coastguard Worker explicit operator bool() const { return fContentStream != nullptr; }
stream()320*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream* stream() { return fContentStream; }
321*c8dee2aaSAndroid Build Coastguard Worker
322*c8dee2aaSAndroid Build Coastguard Worker /* Returns true when we explicitly need the shape of the drawing. */
needShape()323*c8dee2aaSAndroid Build Coastguard Worker bool needShape() {
324*c8dee2aaSAndroid Build Coastguard Worker switch (fBlendMode) {
325*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kClear:
326*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kSrc:
327*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kSrcIn:
328*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kSrcOut:
329*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kDstIn:
330*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kDstOut:
331*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kSrcATop:
332*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kDstATop:
333*c8dee2aaSAndroid Build Coastguard Worker case SkBlendMode::kModulate:
334*c8dee2aaSAndroid Build Coastguard Worker return true;
335*c8dee2aaSAndroid Build Coastguard Worker default:
336*c8dee2aaSAndroid Build Coastguard Worker return false;
337*c8dee2aaSAndroid Build Coastguard Worker }
338*c8dee2aaSAndroid Build Coastguard Worker }
339*c8dee2aaSAndroid Build Coastguard Worker
340*c8dee2aaSAndroid Build Coastguard Worker /* Returns true unless we only need the shape of the drawing. */
needSource()341*c8dee2aaSAndroid Build Coastguard Worker bool needSource() {
342*c8dee2aaSAndroid Build Coastguard Worker if (fBlendMode == SkBlendMode::kClear) {
343*c8dee2aaSAndroid Build Coastguard Worker return false;
344*c8dee2aaSAndroid Build Coastguard Worker }
345*c8dee2aaSAndroid Build Coastguard Worker return true;
346*c8dee2aaSAndroid Build Coastguard Worker }
347*c8dee2aaSAndroid Build Coastguard Worker
348*c8dee2aaSAndroid Build Coastguard Worker /* If the shape is different than the alpha component of the content, then
349*c8dee2aaSAndroid Build Coastguard Worker * setShape should be called with the shape. In particular, images and
350*c8dee2aaSAndroid Build Coastguard Worker * devices have rectangular shape.
351*c8dee2aaSAndroid Build Coastguard Worker */
setShape(const SkPath & shape)352*c8dee2aaSAndroid Build Coastguard Worker void setShape(const SkPath& shape) {
353*c8dee2aaSAndroid Build Coastguard Worker fShape = shape;
354*c8dee2aaSAndroid Build Coastguard Worker }
355*c8dee2aaSAndroid Build Coastguard Worker
356*c8dee2aaSAndroid Build Coastguard Worker private:
357*c8dee2aaSAndroid Build Coastguard Worker SkPDFDevice* fDevice = nullptr;
358*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream* fContentStream = nullptr;
359*c8dee2aaSAndroid Build Coastguard Worker SkBlendMode fBlendMode;
360*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference fDstFormXObject;
361*c8dee2aaSAndroid Build Coastguard Worker SkPath fShape;
362*c8dee2aaSAndroid Build Coastguard Worker const SkClipStack* fClipStack;
363*c8dee2aaSAndroid Build Coastguard Worker };
364*c8dee2aaSAndroid Build Coastguard Worker
365*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
366*c8dee2aaSAndroid Build Coastguard Worker
SkPDFDevice(SkISize pageSize,SkPDFDocument * doc,const SkMatrix & transform)367*c8dee2aaSAndroid Build Coastguard Worker SkPDFDevice::SkPDFDevice(SkISize pageSize, SkPDFDocument* doc, const SkMatrix& transform)
368*c8dee2aaSAndroid Build Coastguard Worker : SkClipStackDevice(SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height()),
369*c8dee2aaSAndroid Build Coastguard Worker SkSurfaceProps())
370*c8dee2aaSAndroid Build Coastguard Worker , fInitialTransform(transform)
371*c8dee2aaSAndroid Build Coastguard Worker , fMarkManager(doc, &fContent)
372*c8dee2aaSAndroid Build Coastguard Worker , fDocument(doc) {
373*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!pageSize.isEmpty());
374*c8dee2aaSAndroid Build Coastguard Worker }
375*c8dee2aaSAndroid Build Coastguard Worker
376*c8dee2aaSAndroid Build Coastguard Worker SkPDFDevice::~SkPDFDevice() = default;
377*c8dee2aaSAndroid Build Coastguard Worker
reset()378*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::reset() {
379*c8dee2aaSAndroid Build Coastguard Worker fGraphicStateResources.reset();
380*c8dee2aaSAndroid Build Coastguard Worker fXObjectResources.reset();
381*c8dee2aaSAndroid Build Coastguard Worker fShaderResources.reset();
382*c8dee2aaSAndroid Build Coastguard Worker fFontResources.reset();
383*c8dee2aaSAndroid Build Coastguard Worker fContent.reset();
384*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState = SkPDFGraphicStackState();
385*c8dee2aaSAndroid Build Coastguard Worker }
386*c8dee2aaSAndroid Build Coastguard Worker
drawAnnotation(const SkRect & rect,const char key[],SkData * value)387*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
388*c8dee2aaSAndroid Build Coastguard Worker if (!value || !fDocument->hasCurrentPage()) {
389*c8dee2aaSAndroid Build Coastguard Worker return;
390*c8dee2aaSAndroid Build Coastguard Worker }
391*c8dee2aaSAndroid Build Coastguard Worker // Annotations are specified in absolute coordinates, so the page xform maps from device space
392*c8dee2aaSAndroid Build Coastguard Worker // to the global space, and applies the document transform.
393*c8dee2aaSAndroid Build Coastguard Worker SkMatrix pageXform = this->deviceToGlobal().asM33();
394*c8dee2aaSAndroid Build Coastguard Worker pageXform.postConcat(fDocument->currentPageTransform());
395*c8dee2aaSAndroid Build Coastguard Worker if (rect.isEmpty()) {
396*c8dee2aaSAndroid Build Coastguard Worker if (!strcmp(key, SkPDFGetElemIdKey())) {
397*c8dee2aaSAndroid Build Coastguard Worker int elemId;
398*c8dee2aaSAndroid Build Coastguard Worker if (value->size() != sizeof(elemId)) { return; }
399*c8dee2aaSAndroid Build Coastguard Worker memcpy(&elemId, value->data(), sizeof(elemId));
400*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.setNextMarksElemId(elemId);
401*c8dee2aaSAndroid Build Coastguard Worker return;
402*c8dee2aaSAndroid Build Coastguard Worker }
403*c8dee2aaSAndroid Build Coastguard Worker if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) {
404*c8dee2aaSAndroid Build Coastguard Worker SkPoint p = this->localToDevice().mapXY(rect.x(), rect.y());
405*c8dee2aaSAndroid Build Coastguard Worker pageXform.mapPoints(&p, 1);
406*c8dee2aaSAndroid Build Coastguard Worker auto pg = fDocument->currentPage();
407*c8dee2aaSAndroid Build Coastguard Worker fDocument->fNamedDestinations.push_back(SkPDFNamedDestination{sk_ref_sp(value), p, pg});
408*c8dee2aaSAndroid Build Coastguard Worker }
409*c8dee2aaSAndroid Build Coastguard Worker return;
410*c8dee2aaSAndroid Build Coastguard Worker }
411*c8dee2aaSAndroid Build Coastguard Worker // Convert to path to handle non-90-degree rotations.
412*c8dee2aaSAndroid Build Coastguard Worker SkPath path = SkPath::Rect(rect).makeTransform(this->localToDevice());
413*c8dee2aaSAndroid Build Coastguard Worker SkPath clip;
414*c8dee2aaSAndroid Build Coastguard Worker SkClipStack_AsPath(this->cs(), &clip);
415*c8dee2aaSAndroid Build Coastguard Worker Op(clip, path, kIntersect_SkPathOp, &path);
416*c8dee2aaSAndroid Build Coastguard Worker // PDF wants a rectangle only.
417*c8dee2aaSAndroid Build Coastguard Worker SkRect transformedRect = pageXform.mapRect(path.getBounds());
418*c8dee2aaSAndroid Build Coastguard Worker if (transformedRect.isEmpty()) {
419*c8dee2aaSAndroid Build Coastguard Worker return;
420*c8dee2aaSAndroid Build Coastguard Worker }
421*c8dee2aaSAndroid Build Coastguard Worker
422*c8dee2aaSAndroid Build Coastguard Worker SkPDFLink::Type linkType = SkPDFLink::Type::kNone;
423*c8dee2aaSAndroid Build Coastguard Worker if (!strcmp(SkAnnotationKeys::URL_Key(), key)) {
424*c8dee2aaSAndroid Build Coastguard Worker linkType = SkPDFLink::Type::kUrl;
425*c8dee2aaSAndroid Build Coastguard Worker } else if (!strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
426*c8dee2aaSAndroid Build Coastguard Worker linkType = SkPDFLink::Type::kNamedDestination;
427*c8dee2aaSAndroid Build Coastguard Worker }
428*c8dee2aaSAndroid Build Coastguard Worker
429*c8dee2aaSAndroid Build Coastguard Worker if (linkType != SkPDFLink::Type::kNone) {
430*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkPDFLink> link = std::make_unique<SkPDFLink>(
431*c8dee2aaSAndroid Build Coastguard Worker linkType, value, transformedRect, fMarkManager.elemId());
432*c8dee2aaSAndroid Build Coastguard Worker fDocument->fCurrentPageLinks.push_back(std::move(link));
433*c8dee2aaSAndroid Build Coastguard Worker }
434*c8dee2aaSAndroid Build Coastguard Worker }
435*c8dee2aaSAndroid Build Coastguard Worker
drawPaint(const SkPaint & srcPaint)436*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawPaint(const SkPaint& srcPaint) {
437*c8dee2aaSAndroid Build Coastguard Worker SkMatrix inverse;
438*c8dee2aaSAndroid Build Coastguard Worker if (!this->localToDevice().invert(&inverse)) {
439*c8dee2aaSAndroid Build Coastguard Worker return;
440*c8dee2aaSAndroid Build Coastguard Worker }
441*c8dee2aaSAndroid Build Coastguard Worker SkRect bbox = this->cs().bounds(this->bounds());
442*c8dee2aaSAndroid Build Coastguard Worker inverse.mapRect(&bbox);
443*c8dee2aaSAndroid Build Coastguard Worker bbox.roundOut(&bbox);
444*c8dee2aaSAndroid Build Coastguard Worker if (this->hasEmptyClip()) {
445*c8dee2aaSAndroid Build Coastguard Worker return;
446*c8dee2aaSAndroid Build Coastguard Worker }
447*c8dee2aaSAndroid Build Coastguard Worker SkPaint newPaint = srcPaint;
448*c8dee2aaSAndroid Build Coastguard Worker newPaint.setStyle(SkPaint::kFill_Style);
449*c8dee2aaSAndroid Build Coastguard Worker this->drawRect(bbox, newPaint);
450*c8dee2aaSAndroid Build Coastguard Worker }
451*c8dee2aaSAndroid Build Coastguard Worker
drawPoints(SkCanvas::PointMode mode,size_t count,const SkPoint * points,const SkPaint & srcPaint)452*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawPoints(SkCanvas::PointMode mode,
453*c8dee2aaSAndroid Build Coastguard Worker size_t count,
454*c8dee2aaSAndroid Build Coastguard Worker const SkPoint* points,
455*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& srcPaint) {
456*c8dee2aaSAndroid Build Coastguard Worker if (this->hasEmptyClip()) {
457*c8dee2aaSAndroid Build Coastguard Worker return;
458*c8dee2aaSAndroid Build Coastguard Worker }
459*c8dee2aaSAndroid Build Coastguard Worker if (count == 0) {
460*c8dee2aaSAndroid Build Coastguard Worker return;
461*c8dee2aaSAndroid Build Coastguard Worker }
462*c8dee2aaSAndroid Build Coastguard Worker SkTCopyOnFirstWrite<SkPaint> paint(clean_paint(srcPaint));
463*c8dee2aaSAndroid Build Coastguard Worker
464*c8dee2aaSAndroid Build Coastguard Worker
465*c8dee2aaSAndroid Build Coastguard Worker
466*c8dee2aaSAndroid Build Coastguard Worker if (SkCanvas::kPoints_PointMode != mode) {
467*c8dee2aaSAndroid Build Coastguard Worker set_style(&paint, SkPaint::kStroke_Style);
468*c8dee2aaSAndroid Build Coastguard Worker }
469*c8dee2aaSAndroid Build Coastguard Worker
470*c8dee2aaSAndroid Build Coastguard Worker // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
471*c8dee2aaSAndroid Build Coastguard Worker // We only use this when there's a path effect or perspective because of the overhead
472*c8dee2aaSAndroid Build Coastguard Worker // of multiple calls to setUpContentEntry it causes.
473*c8dee2aaSAndroid Build Coastguard Worker if (paint->getPathEffect() || this->localToDevice().hasPerspective()) {
474*c8dee2aaSAndroid Build Coastguard Worker draw_points(mode, count, points, *paint, this->devClipBounds(), this);
475*c8dee2aaSAndroid Build Coastguard Worker return;
476*c8dee2aaSAndroid Build Coastguard Worker }
477*c8dee2aaSAndroid Build Coastguard Worker
478*c8dee2aaSAndroid Build Coastguard Worker
479*c8dee2aaSAndroid Build Coastguard Worker if (mode == SkCanvas::kPoints_PointMode && paint->getStrokeCap() != SkPaint::kRound_Cap) {
480*c8dee2aaSAndroid Build Coastguard Worker if (paint->getStrokeWidth()) {
481*c8dee2aaSAndroid Build Coastguard Worker // PDF won't draw a single point with square/butt caps because the
482*c8dee2aaSAndroid Build Coastguard Worker // orientation is ambiguous. Draw a rectangle instead.
483*c8dee2aaSAndroid Build Coastguard Worker set_style(&paint, SkPaint::kFill_Style);
484*c8dee2aaSAndroid Build Coastguard Worker SkScalar strokeWidth = paint->getStrokeWidth();
485*c8dee2aaSAndroid Build Coastguard Worker SkScalar halfStroke = SkScalarHalf(strokeWidth);
486*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < count; i++) {
487*c8dee2aaSAndroid Build Coastguard Worker SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
488*c8dee2aaSAndroid Build Coastguard Worker r.inset(-halfStroke, -halfStroke);
489*c8dee2aaSAndroid Build Coastguard Worker this->drawRect(r, *paint);
490*c8dee2aaSAndroid Build Coastguard Worker }
491*c8dee2aaSAndroid Build Coastguard Worker return;
492*c8dee2aaSAndroid Build Coastguard Worker } else {
493*c8dee2aaSAndroid Build Coastguard Worker if (paint->getStrokeCap() != SkPaint::kRound_Cap) {
494*c8dee2aaSAndroid Build Coastguard Worker paint.writable()->setStrokeCap(SkPaint::kRound_Cap);
495*c8dee2aaSAndroid Build Coastguard Worker }
496*c8dee2aaSAndroid Build Coastguard Worker }
497*c8dee2aaSAndroid Build Coastguard Worker }
498*c8dee2aaSAndroid Build Coastguard Worker
499*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, *paint);
500*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
501*c8dee2aaSAndroid Build Coastguard Worker return;
502*c8dee2aaSAndroid Build Coastguard Worker }
503*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream* contentStream = content.stream();
504*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.beginMark();
505*c8dee2aaSAndroid Build Coastguard Worker if (fMarkManager.hasActiveMark()) {
506*c8dee2aaSAndroid Build Coastguard Worker // Destinations are in absolute coordinates.
507*c8dee2aaSAndroid Build Coastguard Worker SkMatrix pageXform = this->deviceToGlobal().asM33();
508*c8dee2aaSAndroid Build Coastguard Worker pageXform.postConcat(fDocument->currentPageTransform());
509*c8dee2aaSAndroid Build Coastguard Worker // The points do not already have localToDevice applied.
510*c8dee2aaSAndroid Build Coastguard Worker pageXform.preConcat(this->localToDevice());
511*c8dee2aaSAndroid Build Coastguard Worker
512*c8dee2aaSAndroid Build Coastguard Worker for (auto&& userPoint : SkSpan(points, count)) {
513*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.accumulate(pageXform.mapPoint(userPoint));
514*c8dee2aaSAndroid Build Coastguard Worker }
515*c8dee2aaSAndroid Build Coastguard Worker }
516*c8dee2aaSAndroid Build Coastguard Worker switch (mode) {
517*c8dee2aaSAndroid Build Coastguard Worker case SkCanvas::kPolygon_PointMode:
518*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::MoveTo(points[0].fX, points[0].fY, contentStream);
519*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 1; i < count; i++) {
520*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendLine(points[i].fX, points[i].fY, contentStream);
521*c8dee2aaSAndroid Build Coastguard Worker }
522*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::StrokePath(contentStream);
523*c8dee2aaSAndroid Build Coastguard Worker break;
524*c8dee2aaSAndroid Build Coastguard Worker case SkCanvas::kLines_PointMode:
525*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < count/2; i++) {
526*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, contentStream);
527*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY, contentStream);
528*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::StrokePath(contentStream);
529*c8dee2aaSAndroid Build Coastguard Worker }
530*c8dee2aaSAndroid Build Coastguard Worker break;
531*c8dee2aaSAndroid Build Coastguard Worker case SkCanvas::kPoints_PointMode:
532*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
533*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; i < count; i++) {
534*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::MoveTo(points[i].fX, points[i].fY, contentStream);
535*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::ClosePath(contentStream);
536*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::StrokePath(contentStream);
537*c8dee2aaSAndroid Build Coastguard Worker }
538*c8dee2aaSAndroid Build Coastguard Worker break;
539*c8dee2aaSAndroid Build Coastguard Worker default:
540*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(false);
541*c8dee2aaSAndroid Build Coastguard Worker }
542*c8dee2aaSAndroid Build Coastguard Worker }
543*c8dee2aaSAndroid Build Coastguard Worker
drawRect(const SkRect & rect,const SkPaint & paint)544*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawRect(const SkRect& rect, const SkPaint& paint) {
545*c8dee2aaSAndroid Build Coastguard Worker SkRect r = rect;
546*c8dee2aaSAndroid Build Coastguard Worker r.sort();
547*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::Rect(r), paint, true);
548*c8dee2aaSAndroid Build Coastguard Worker }
549*c8dee2aaSAndroid Build Coastguard Worker
drawRRect(const SkRRect & rrect,const SkPaint & paint)550*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
551*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::RRect(rrect), paint, true);
552*c8dee2aaSAndroid Build Coastguard Worker }
553*c8dee2aaSAndroid Build Coastguard Worker
drawOval(const SkRect & oval,const SkPaint & paint)554*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
555*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::Oval(oval), paint, true);
556*c8dee2aaSAndroid Build Coastguard Worker }
557*c8dee2aaSAndroid Build Coastguard Worker
drawPath(const SkPath & path,const SkPaint & paint,bool pathIsMutable)558*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawPath(const SkPath& path, const SkPaint& paint, bool pathIsMutable) {
559*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), path, paint, pathIsMutable);
560*c8dee2aaSAndroid Build Coastguard Worker }
561*c8dee2aaSAndroid Build Coastguard Worker
internalDrawPathWithFilter(const SkClipStack & clipStack,const SkMatrix & ctm,const SkPath & origPath,const SkPaint & origPaint)562*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
563*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& ctm,
564*c8dee2aaSAndroid Build Coastguard Worker const SkPath& origPath,
565*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& origPaint) {
566*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(origPaint.getMaskFilter());
567*c8dee2aaSAndroid Build Coastguard Worker SkPath path(origPath);
568*c8dee2aaSAndroid Build Coastguard Worker SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
569*c8dee2aaSAndroid Build Coastguard Worker
570*c8dee2aaSAndroid Build Coastguard Worker SkStrokeRec::InitStyle initStyle = skpathutils::FillPathWithPaint(path, *paint, &path)
571*c8dee2aaSAndroid Build Coastguard Worker ? SkStrokeRec::kFill_InitStyle
572*c8dee2aaSAndroid Build Coastguard Worker : SkStrokeRec::kHairline_InitStyle;
573*c8dee2aaSAndroid Build Coastguard Worker path.transform(ctm, &path);
574*c8dee2aaSAndroid Build Coastguard Worker
575*c8dee2aaSAndroid Build Coastguard Worker SkIRect bounds = clipStack.bounds(this->bounds()).roundOut();
576*c8dee2aaSAndroid Build Coastguard Worker SkMaskBuilder sourceMask;
577*c8dee2aaSAndroid Build Coastguard Worker if (!SkDraw::DrawToMask(path, bounds, paint->getMaskFilter(), &SkMatrix::I(),
578*c8dee2aaSAndroid Build Coastguard Worker &sourceMask, SkMaskBuilder::kComputeBoundsAndRenderImage_CreateMode,
579*c8dee2aaSAndroid Build Coastguard Worker initStyle)) {
580*c8dee2aaSAndroid Build Coastguard Worker return;
581*c8dee2aaSAndroid Build Coastguard Worker }
582*c8dee2aaSAndroid Build Coastguard Worker SkAutoMaskFreeImage srcAutoMaskFreeImage(sourceMask.image());
583*c8dee2aaSAndroid Build Coastguard Worker SkMaskBuilder dstMask;
584*c8dee2aaSAndroid Build Coastguard Worker SkIPoint margin;
585*c8dee2aaSAndroid Build Coastguard Worker if (!as_MFB(paint->getMaskFilter())->filterMask(&dstMask, sourceMask, ctm, &margin)) {
586*c8dee2aaSAndroid Build Coastguard Worker return;
587*c8dee2aaSAndroid Build Coastguard Worker }
588*c8dee2aaSAndroid Build Coastguard Worker SkIRect dstMaskBounds = dstMask.fBounds;
589*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> mask = mask_to_greyscale_image(&dstMask);
590*c8dee2aaSAndroid Build Coastguard Worker // PDF doesn't seem to allow masking vector graphics with an Image XObject.
591*c8dee2aaSAndroid Build Coastguard Worker // Must mask with a Form XObject.
592*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice();
593*c8dee2aaSAndroid Build Coastguard Worker {
594*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(maskDevice);
595*c8dee2aaSAndroid Build Coastguard Worker canvas.drawImage(mask, dstMaskBounds.x(), dstMaskBounds.y());
596*c8dee2aaSAndroid Build Coastguard Worker }
597*c8dee2aaSAndroid Build Coastguard Worker if (!ctm.isIdentity() && paint->getShader()) {
598*c8dee2aaSAndroid Build Coastguard Worker transform_shader(paint.writable(), ctm); // Since we are using identity matrix.
599*c8dee2aaSAndroid Build Coastguard Worker }
600*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, &clipStack, SkMatrix::I(), *paint);
601*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
602*c8dee2aaSAndroid Build Coastguard Worker return;
603*c8dee2aaSAndroid Build Coastguard Worker }
604*c8dee2aaSAndroid Build Coastguard Worker this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
605*c8dee2aaSAndroid Build Coastguard Worker maskDevice->makeFormXObjectFromDevice(dstMaskBounds, true), false,
606*c8dee2aaSAndroid Build Coastguard Worker SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), content.stream());
607*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendRectangle(SkRect::Make(dstMaskBounds), content.stream());
608*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), content.stream());
609*c8dee2aaSAndroid Build Coastguard Worker this->clearMaskOnGraphicState(content.stream());
610*c8dee2aaSAndroid Build Coastguard Worker }
611*c8dee2aaSAndroid Build Coastguard Worker
setGraphicState(SkPDFIndirectReference gs,SkDynamicMemoryWStream * content)612*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream* content) {
613*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::ApplyGraphicState(add_resource(fGraphicStateResources, gs), content);
614*c8dee2aaSAndroid Build Coastguard Worker }
615*c8dee2aaSAndroid Build Coastguard Worker
clearMaskOnGraphicState(SkDynamicMemoryWStream * contentStream)616*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) {
617*c8dee2aaSAndroid Build Coastguard Worker // The no-softmask graphic state is used to "turn off" the mask for later draw calls.
618*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference& noSMaskGS = fDocument->fNoSmaskGraphicState;
619*c8dee2aaSAndroid Build Coastguard Worker if (!noSMaskGS) {
620*c8dee2aaSAndroid Build Coastguard Worker SkPDFDict tmp("ExtGState");
621*c8dee2aaSAndroid Build Coastguard Worker tmp.insertName("SMask", "None");
622*c8dee2aaSAndroid Build Coastguard Worker noSMaskGS = fDocument->emit(tmp);
623*c8dee2aaSAndroid Build Coastguard Worker }
624*c8dee2aaSAndroid Build Coastguard Worker this->setGraphicState(noSMaskGS, contentStream);
625*c8dee2aaSAndroid Build Coastguard Worker }
626*c8dee2aaSAndroid Build Coastguard Worker
internalDrawPath(const SkClipStack & clipStack,const SkMatrix & ctm,const SkPath & origPath,const SkPaint & srcPaint,bool pathIsMutable)627*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::internalDrawPath(const SkClipStack& clipStack,
628*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& ctm,
629*c8dee2aaSAndroid Build Coastguard Worker const SkPath& origPath,
630*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& srcPaint,
631*c8dee2aaSAndroid Build Coastguard Worker bool pathIsMutable) {
632*c8dee2aaSAndroid Build Coastguard Worker if (clipStack.isEmpty(this->bounds())) {
633*c8dee2aaSAndroid Build Coastguard Worker return;
634*c8dee2aaSAndroid Build Coastguard Worker }
635*c8dee2aaSAndroid Build Coastguard Worker SkTCopyOnFirstWrite<SkPaint> paint(clean_paint(srcPaint));
636*c8dee2aaSAndroid Build Coastguard Worker SkPath modifiedPath;
637*c8dee2aaSAndroid Build Coastguard Worker SkPath* pathPtr = const_cast<SkPath*>(&origPath);
638*c8dee2aaSAndroid Build Coastguard Worker
639*c8dee2aaSAndroid Build Coastguard Worker if (paint->getMaskFilter()) {
640*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPathWithFilter(clipStack, ctm, origPath, *paint);
641*c8dee2aaSAndroid Build Coastguard Worker return;
642*c8dee2aaSAndroid Build Coastguard Worker }
643*c8dee2aaSAndroid Build Coastguard Worker
644*c8dee2aaSAndroid Build Coastguard Worker SkMatrix matrix = ctm;
645*c8dee2aaSAndroid Build Coastguard Worker
646*c8dee2aaSAndroid Build Coastguard Worker if (paint->getPathEffect()) {
647*c8dee2aaSAndroid Build Coastguard Worker if (clipStack.isEmpty(this->bounds())) {
648*c8dee2aaSAndroid Build Coastguard Worker return;
649*c8dee2aaSAndroid Build Coastguard Worker }
650*c8dee2aaSAndroid Build Coastguard Worker if (!pathIsMutable) {
651*c8dee2aaSAndroid Build Coastguard Worker modifiedPath = origPath;
652*c8dee2aaSAndroid Build Coastguard Worker pathPtr = &modifiedPath;
653*c8dee2aaSAndroid Build Coastguard Worker pathIsMutable = true;
654*c8dee2aaSAndroid Build Coastguard Worker }
655*c8dee2aaSAndroid Build Coastguard Worker if (skpathutils::FillPathWithPaint(*pathPtr, *paint, pathPtr)) {
656*c8dee2aaSAndroid Build Coastguard Worker set_style(&paint, SkPaint::kFill_Style);
657*c8dee2aaSAndroid Build Coastguard Worker } else {
658*c8dee2aaSAndroid Build Coastguard Worker set_style(&paint, SkPaint::kStroke_Style);
659*c8dee2aaSAndroid Build Coastguard Worker if (paint->getStrokeWidth() != 0) {
660*c8dee2aaSAndroid Build Coastguard Worker paint.writable()->setStrokeWidth(0);
661*c8dee2aaSAndroid Build Coastguard Worker }
662*c8dee2aaSAndroid Build Coastguard Worker }
663*c8dee2aaSAndroid Build Coastguard Worker paint.writable()->setPathEffect(nullptr);
664*c8dee2aaSAndroid Build Coastguard Worker }
665*c8dee2aaSAndroid Build Coastguard Worker
666*c8dee2aaSAndroid Build Coastguard Worker if (this->handleInversePath(*pathPtr, *paint, pathIsMutable)) {
667*c8dee2aaSAndroid Build Coastguard Worker return;
668*c8dee2aaSAndroid Build Coastguard Worker }
669*c8dee2aaSAndroid Build Coastguard Worker if (matrix.getType() & SkMatrix::kPerspective_Mask) {
670*c8dee2aaSAndroid Build Coastguard Worker if (!pathIsMutable) {
671*c8dee2aaSAndroid Build Coastguard Worker modifiedPath = origPath;
672*c8dee2aaSAndroid Build Coastguard Worker pathPtr = &modifiedPath;
673*c8dee2aaSAndroid Build Coastguard Worker pathIsMutable = true;
674*c8dee2aaSAndroid Build Coastguard Worker }
675*c8dee2aaSAndroid Build Coastguard Worker pathPtr->transform(matrix);
676*c8dee2aaSAndroid Build Coastguard Worker if (paint->getShader()) {
677*c8dee2aaSAndroid Build Coastguard Worker transform_shader(paint.writable(), matrix);
678*c8dee2aaSAndroid Build Coastguard Worker }
679*c8dee2aaSAndroid Build Coastguard Worker matrix = SkMatrix::I();
680*c8dee2aaSAndroid Build Coastguard Worker }
681*c8dee2aaSAndroid Build Coastguard Worker
682*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, &clipStack, matrix, *paint);
683*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
684*c8dee2aaSAndroid Build Coastguard Worker return;
685*c8dee2aaSAndroid Build Coastguard Worker }
686*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.beginMark();
687*c8dee2aaSAndroid Build Coastguard Worker if (fMarkManager.hasActiveMark()) {
688*c8dee2aaSAndroid Build Coastguard Worker // Destinations are in absolute coordinates.
689*c8dee2aaSAndroid Build Coastguard Worker SkMatrix pageXform = this->deviceToGlobal().asM33();
690*c8dee2aaSAndroid Build Coastguard Worker pageXform.postConcat(fDocument->currentPageTransform());
691*c8dee2aaSAndroid Build Coastguard Worker // The path does not already have localToDevice / ctm / matrix applied.
692*c8dee2aaSAndroid Build Coastguard Worker pageXform.preConcat(matrix);
693*c8dee2aaSAndroid Build Coastguard Worker
694*c8dee2aaSAndroid Build Coastguard Worker SkRect pathBounds = pathPtr->computeTightBounds();
695*c8dee2aaSAndroid Build Coastguard Worker pageXform.mapRect(&pathBounds);
696*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.accumulate({pathBounds.fLeft, pathBounds.fBottom}); // y-up
697*c8dee2aaSAndroid Build Coastguard Worker }
698*c8dee2aaSAndroid Build Coastguard Worker constexpr SkScalar kToleranceScale = 0.0625f; // smaller = better conics (circles).
699*c8dee2aaSAndroid Build Coastguard Worker SkScalar matrixScale = matrix.mapRadius(1.0f);
700*c8dee2aaSAndroid Build Coastguard Worker SkScalar tolerance = matrixScale > 0.0f ? kToleranceScale / matrixScale : kToleranceScale;
701*c8dee2aaSAndroid Build Coastguard Worker bool consumeDegeratePathSegments =
702*c8dee2aaSAndroid Build Coastguard Worker paint->getStyle() == SkPaint::kFill_Style ||
703*c8dee2aaSAndroid Build Coastguard Worker (paint->getStrokeCap() != SkPaint::kRound_Cap &&
704*c8dee2aaSAndroid Build Coastguard Worker paint->getStrokeCap() != SkPaint::kSquare_Cap);
705*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::EmitPath(*pathPtr, paint->getStyle(), consumeDegeratePathSegments, content.stream(),
706*c8dee2aaSAndroid Build Coastguard Worker tolerance);
707*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::PaintPath(paint->getStyle(), pathPtr->getFillType(), content.stream());
708*c8dee2aaSAndroid Build Coastguard Worker }
709*c8dee2aaSAndroid Build Coastguard Worker
710*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
711*c8dee2aaSAndroid Build Coastguard Worker
drawImageRect(const SkImage * image,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint)712*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawImageRect(const SkImage* image,
713*c8dee2aaSAndroid Build Coastguard Worker const SkRect* src,
714*c8dee2aaSAndroid Build Coastguard Worker const SkRect& dst,
715*c8dee2aaSAndroid Build Coastguard Worker const SkSamplingOptions& sampling,
716*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint,
717*c8dee2aaSAndroid Build Coastguard Worker SkCanvas::SrcRectConstraint) {
718*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(image);
719*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))),
720*c8dee2aaSAndroid Build Coastguard Worker src, dst, sampling, paint, this->localToDevice());
721*c8dee2aaSAndroid Build Coastguard Worker }
722*c8dee2aaSAndroid Build Coastguard Worker
drawSprite(const SkBitmap & bm,int x,int y,const SkPaint & paint)723*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawSprite(const SkBitmap& bm, int x, int y, const SkPaint& paint) {
724*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!bm.drawsNothing());
725*c8dee2aaSAndroid Build Coastguard Worker auto r = SkRect::MakeXYWH(x, y, bm.width(), bm.height());
726*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawImageRect(SkKeyedImage(bm), nullptr, r, SkSamplingOptions(), paint,
727*c8dee2aaSAndroid Build Coastguard Worker SkMatrix::I());
728*c8dee2aaSAndroid Build Coastguard Worker }
729*c8dee2aaSAndroid Build Coastguard Worker
730*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
731*c8dee2aaSAndroid Build Coastguard Worker
732*c8dee2aaSAndroid Build Coastguard Worker namespace {
733*c8dee2aaSAndroid Build Coastguard Worker class GlyphPositioner {
734*c8dee2aaSAndroid Build Coastguard Worker public:
GlyphPositioner(SkDynamicMemoryWStream * content,SkScalar textSkewX,SkPoint origin)735*c8dee2aaSAndroid Build Coastguard Worker GlyphPositioner(SkDynamicMemoryWStream* content,
736*c8dee2aaSAndroid Build Coastguard Worker SkScalar textSkewX,
737*c8dee2aaSAndroid Build Coastguard Worker SkPoint origin)
738*c8dee2aaSAndroid Build Coastguard Worker : fContent(content)
739*c8dee2aaSAndroid Build Coastguard Worker , fCurrentMatrixOrigin(origin)
740*c8dee2aaSAndroid Build Coastguard Worker , fTextSkewX(textSkewX) {
741*c8dee2aaSAndroid Build Coastguard Worker }
~GlyphPositioner()742*c8dee2aaSAndroid Build Coastguard Worker ~GlyphPositioner() { this->flush(); }
flush()743*c8dee2aaSAndroid Build Coastguard Worker void flush() {
744*c8dee2aaSAndroid Build Coastguard Worker if (fInText) {
745*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText("> Tj\n");
746*c8dee2aaSAndroid Build Coastguard Worker fInText = false;
747*c8dee2aaSAndroid Build Coastguard Worker }
748*c8dee2aaSAndroid Build Coastguard Worker }
setFont(SkPDFFont * pdfFont)749*c8dee2aaSAndroid Build Coastguard Worker void setFont(SkPDFFont* pdfFont) {
750*c8dee2aaSAndroid Build Coastguard Worker this->flush();
751*c8dee2aaSAndroid Build Coastguard Worker fPDFFont = pdfFont;
752*c8dee2aaSAndroid Build Coastguard Worker // Reader 2020.013.20064 incorrectly advances some Type3 fonts https://crbug.com/1226960
753*c8dee2aaSAndroid Build Coastguard Worker bool convertedToType3 = fPDFFont->getType() == SkAdvancedTypefaceMetrics::kOther_Font;
754*c8dee2aaSAndroid Build Coastguard Worker bool thousandEM = fPDFFont->strike().fPath.fUnitsPerEM == 1000;
755*c8dee2aaSAndroid Build Coastguard Worker fViewersAgreeOnAdvancesInFont = thousandEM || !convertedToType3;
756*c8dee2aaSAndroid Build Coastguard Worker }
writeGlyph(uint16_t glyph,SkScalar advanceWidth,SkPoint xy)757*c8dee2aaSAndroid Build Coastguard Worker void writeGlyph(uint16_t glyph, SkScalar advanceWidth, SkPoint xy) {
758*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fPDFFont);
759*c8dee2aaSAndroid Build Coastguard Worker if (!fInitialized) {
760*c8dee2aaSAndroid Build Coastguard Worker // Flip the text about the x-axis to account for origin swap and include
761*c8dee2aaSAndroid Build Coastguard Worker // the passed parameters.
762*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText("1 0 ");
763*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendScalar(-fTextSkewX, fContent);
764*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText(" -1 ");
765*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.x(), fContent);
766*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText(" ");
767*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.y(), fContent);
768*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText(" Tm\n");
769*c8dee2aaSAndroid Build Coastguard Worker fCurrentMatrixOrigin.set(0.0f, 0.0f);
770*c8dee2aaSAndroid Build Coastguard Worker fInitialized = true;
771*c8dee2aaSAndroid Build Coastguard Worker }
772*c8dee2aaSAndroid Build Coastguard Worker SkPoint position = xy - fCurrentMatrixOrigin;
773*c8dee2aaSAndroid Build Coastguard Worker if (!fViewersAgreeOnXAdvance || position != SkPoint{fXAdvance, 0}) {
774*c8dee2aaSAndroid Build Coastguard Worker this->flush();
775*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendScalar(position.x() - position.y() * fTextSkewX, fContent);
776*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText(" ");
777*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendScalar(-position.y(), fContent);
778*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText(" Td ");
779*c8dee2aaSAndroid Build Coastguard Worker fCurrentMatrixOrigin = xy;
780*c8dee2aaSAndroid Build Coastguard Worker fXAdvance = 0;
781*c8dee2aaSAndroid Build Coastguard Worker fViewersAgreeOnXAdvance = true;
782*c8dee2aaSAndroid Build Coastguard Worker }
783*c8dee2aaSAndroid Build Coastguard Worker fXAdvance += advanceWidth;
784*c8dee2aaSAndroid Build Coastguard Worker if (!fViewersAgreeOnAdvancesInFont) {
785*c8dee2aaSAndroid Build Coastguard Worker fViewersAgreeOnXAdvance = false;
786*c8dee2aaSAndroid Build Coastguard Worker }
787*c8dee2aaSAndroid Build Coastguard Worker if (!fInText) {
788*c8dee2aaSAndroid Build Coastguard Worker fContent->writeText("<");
789*c8dee2aaSAndroid Build Coastguard Worker fInText = true;
790*c8dee2aaSAndroid Build Coastguard Worker }
791*c8dee2aaSAndroid Build Coastguard Worker if (fPDFFont->multiByteGlyphs()) {
792*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::WriteUInt16BE(fContent, glyph);
793*c8dee2aaSAndroid Build Coastguard Worker } else {
794*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(0 == glyph >> 8);
795*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::WriteUInt8(fContent, static_cast<uint8_t>(glyph));
796*c8dee2aaSAndroid Build Coastguard Worker }
797*c8dee2aaSAndroid Build Coastguard Worker }
798*c8dee2aaSAndroid Build Coastguard Worker
799*c8dee2aaSAndroid Build Coastguard Worker private:
800*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream* fContent;
801*c8dee2aaSAndroid Build Coastguard Worker SkPDFFont* fPDFFont = nullptr;
802*c8dee2aaSAndroid Build Coastguard Worker SkPoint fCurrentMatrixOrigin;
803*c8dee2aaSAndroid Build Coastguard Worker SkScalar fXAdvance = 0.0f;
804*c8dee2aaSAndroid Build Coastguard Worker bool fViewersAgreeOnAdvancesInFont = true;
805*c8dee2aaSAndroid Build Coastguard Worker bool fViewersAgreeOnXAdvance = true;
806*c8dee2aaSAndroid Build Coastguard Worker SkScalar fTextSkewX;
807*c8dee2aaSAndroid Build Coastguard Worker bool fInText = false;
808*c8dee2aaSAndroid Build Coastguard Worker bool fInitialized = false;
809*c8dee2aaSAndroid Build Coastguard Worker };
810*c8dee2aaSAndroid Build Coastguard Worker } // namespace
811*c8dee2aaSAndroid Build Coastguard Worker
812*c8dee2aaSAndroid Build Coastguard Worker namespace {
813*c8dee2aaSAndroid Build Coastguard Worker struct PositionedGlyph {
814*c8dee2aaSAndroid Build Coastguard Worker SkPoint fPos;
815*c8dee2aaSAndroid Build Coastguard Worker SkGlyphID fGlyph;
816*c8dee2aaSAndroid Build Coastguard Worker };
817*c8dee2aaSAndroid Build Coastguard Worker } // namespace
818*c8dee2aaSAndroid Build Coastguard Worker
get_glyph_bounds_device_space(const SkGlyph * glyph,SkScalar xScale,SkScalar yScale,SkPoint xy,const SkMatrix & ctm)819*c8dee2aaSAndroid Build Coastguard Worker static SkRect get_glyph_bounds_device_space(const SkGlyph* glyph,
820*c8dee2aaSAndroid Build Coastguard Worker SkScalar xScale, SkScalar yScale,
821*c8dee2aaSAndroid Build Coastguard Worker SkPoint xy, const SkMatrix& ctm) {
822*c8dee2aaSAndroid Build Coastguard Worker SkRect glyphBounds = SkMatrix::Scale(xScale, yScale).mapRect(glyph->rect());
823*c8dee2aaSAndroid Build Coastguard Worker glyphBounds.offset(xy);
824*c8dee2aaSAndroid Build Coastguard Worker ctm.mapRect(&glyphBounds); // now in dev space.
825*c8dee2aaSAndroid Build Coastguard Worker return glyphBounds;
826*c8dee2aaSAndroid Build Coastguard Worker }
827*c8dee2aaSAndroid Build Coastguard Worker
contains(const SkRect & r,SkPoint p)828*c8dee2aaSAndroid Build Coastguard Worker static bool contains(const SkRect& r, SkPoint p) {
829*c8dee2aaSAndroid Build Coastguard Worker return r.left() <= p.x() && p.x() <= r.right() &&
830*c8dee2aaSAndroid Build Coastguard Worker r.top() <= p.y() && p.y() <= r.bottom();
831*c8dee2aaSAndroid Build Coastguard Worker }
832*c8dee2aaSAndroid Build Coastguard Worker
drawGlyphRunAsPath(const sktext::GlyphRun & glyphRun,SkPoint offset,const SkPaint & runPaint)833*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawGlyphRunAsPath(
834*c8dee2aaSAndroid Build Coastguard Worker const sktext::GlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint) {
835*c8dee2aaSAndroid Build Coastguard Worker const SkFont& font = glyphRun.font();
836*c8dee2aaSAndroid Build Coastguard Worker SkPath path;
837*c8dee2aaSAndroid Build Coastguard Worker
838*c8dee2aaSAndroid Build Coastguard Worker struct Rec {
839*c8dee2aaSAndroid Build Coastguard Worker SkPath* fPath;
840*c8dee2aaSAndroid Build Coastguard Worker SkPoint fOffset;
841*c8dee2aaSAndroid Build Coastguard Worker const SkPoint* fPos;
842*c8dee2aaSAndroid Build Coastguard Worker } rec = {&path, offset, glyphRun.positions().data()};
843*c8dee2aaSAndroid Build Coastguard Worker
844*c8dee2aaSAndroid Build Coastguard Worker font.getPaths(glyphRun.glyphsIDs().data(), glyphRun.glyphsIDs().size(),
845*c8dee2aaSAndroid Build Coastguard Worker [](const SkPath* path, const SkMatrix& mx, void* ctx) {
846*c8dee2aaSAndroid Build Coastguard Worker Rec* rec = reinterpret_cast<Rec*>(ctx);
847*c8dee2aaSAndroid Build Coastguard Worker if (path) {
848*c8dee2aaSAndroid Build Coastguard Worker SkMatrix total = mx;
849*c8dee2aaSAndroid Build Coastguard Worker total.postTranslate(rec->fPos->fX + rec->fOffset.fX,
850*c8dee2aaSAndroid Build Coastguard Worker rec->fPos->fY + rec->fOffset.fY);
851*c8dee2aaSAndroid Build Coastguard Worker rec->fPath->addPath(*path, total);
852*c8dee2aaSAndroid Build Coastguard Worker }
853*c8dee2aaSAndroid Build Coastguard Worker rec->fPos += 1; // move to the next glyph's position
854*c8dee2aaSAndroid Build Coastguard Worker }, &rec);
855*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), path, runPaint, true);
856*c8dee2aaSAndroid Build Coastguard Worker
857*c8dee2aaSAndroid Build Coastguard Worker SkFont transparentFont = glyphRun.font();
858*c8dee2aaSAndroid Build Coastguard Worker transparentFont.setEmbolden(false); // Stop Recursion
859*c8dee2aaSAndroid Build Coastguard Worker sktext::GlyphRun tmpGlyphRun(glyphRun, transparentFont);
860*c8dee2aaSAndroid Build Coastguard Worker
861*c8dee2aaSAndroid Build Coastguard Worker SkPaint transparent;
862*c8dee2aaSAndroid Build Coastguard Worker transparent.setColor(SK_ColorTRANSPARENT);
863*c8dee2aaSAndroid Build Coastguard Worker
864*c8dee2aaSAndroid Build Coastguard Worker if (this->localToDevice().hasPerspective()) {
865*c8dee2aaSAndroid Build Coastguard Worker SkAutoDeviceTransformRestore adr(this, SkMatrix::I());
866*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent);
867*c8dee2aaSAndroid Build Coastguard Worker } else {
868*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawGlyphRun(tmpGlyphRun, offset, transparent);
869*c8dee2aaSAndroid Build Coastguard Worker }
870*c8dee2aaSAndroid Build Coastguard Worker }
871*c8dee2aaSAndroid Build Coastguard Worker
needs_new_font(SkPDFFont * font,const SkGlyph * glyph,SkAdvancedTypefaceMetrics::FontType initialFontType)872*c8dee2aaSAndroid Build Coastguard Worker static bool needs_new_font(SkPDFFont* font, const SkGlyph* glyph,
873*c8dee2aaSAndroid Build Coastguard Worker SkAdvancedTypefaceMetrics::FontType initialFontType) {
874*c8dee2aaSAndroid Build Coastguard Worker if (!font || !font->hasGlyph(glyph->getGlyphID())) {
875*c8dee2aaSAndroid Build Coastguard Worker return true;
876*c8dee2aaSAndroid Build Coastguard Worker }
877*c8dee2aaSAndroid Build Coastguard Worker if (initialFontType == SkAdvancedTypefaceMetrics::kOther_Font) {
878*c8dee2aaSAndroid Build Coastguard Worker return false;
879*c8dee2aaSAndroid Build Coastguard Worker }
880*c8dee2aaSAndroid Build Coastguard Worker if (glyph->isEmpty()) {
881*c8dee2aaSAndroid Build Coastguard Worker return false;
882*c8dee2aaSAndroid Build Coastguard Worker }
883*c8dee2aaSAndroid Build Coastguard Worker
884*c8dee2aaSAndroid Build Coastguard Worker bool hasUnmodifiedPath = glyph->path() && !glyph->pathIsModified();
885*c8dee2aaSAndroid Build Coastguard Worker bool convertedToType3 = font->getType() == SkAdvancedTypefaceMetrics::kOther_Font;
886*c8dee2aaSAndroid Build Coastguard Worker return convertedToType3 == hasUnmodifiedPath;
887*c8dee2aaSAndroid Build Coastguard Worker }
888*c8dee2aaSAndroid Build Coastguard Worker
internalDrawGlyphRun(const sktext::GlyphRun & glyphRun,SkPoint offset,const SkPaint & runPaint)889*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::internalDrawGlyphRun(
890*c8dee2aaSAndroid Build Coastguard Worker const sktext::GlyphRun& glyphRun, SkPoint offset, const SkPaint& runPaint) {
891*c8dee2aaSAndroid Build Coastguard Worker
892*c8dee2aaSAndroid Build Coastguard Worker const SkGlyphID* glyphIDs = glyphRun.glyphsIDs().data();
893*c8dee2aaSAndroid Build Coastguard Worker uint32_t glyphCount = SkToU32(glyphRun.glyphsIDs().size());
894*c8dee2aaSAndroid Build Coastguard Worker const SkFont& glyphRunFont = glyphRun.font();
895*c8dee2aaSAndroid Build Coastguard Worker
896*c8dee2aaSAndroid Build Coastguard Worker if (!glyphCount || !glyphIDs || glyphRunFont.getSize() <= 0 || this->hasEmptyClip()) {
897*c8dee2aaSAndroid Build Coastguard Worker return;
898*c8dee2aaSAndroid Build Coastguard Worker }
899*c8dee2aaSAndroid Build Coastguard Worker
900*c8dee2aaSAndroid Build Coastguard Worker // TODO: SkPDFFont has code to handle paints with mask filters, but the viewers do not.
901*c8dee2aaSAndroid Build Coastguard Worker // See https://crbug.com/362796158 for Pdfium and b/325266484 for Preview
902*c8dee2aaSAndroid Build Coastguard Worker if (this->localToDevice().hasPerspective() || runPaint.getMaskFilter()) {
903*c8dee2aaSAndroid Build Coastguard Worker this->drawGlyphRunAsPath(glyphRun, offset, runPaint);
904*c8dee2aaSAndroid Build Coastguard Worker return;
905*c8dee2aaSAndroid Build Coastguard Worker }
906*c8dee2aaSAndroid Build Coastguard Worker
907*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPDFStrike> pdfStrike = SkPDFStrike::Make(fDocument, glyphRunFont, runPaint);
908*c8dee2aaSAndroid Build Coastguard Worker if (!pdfStrike) {
909*c8dee2aaSAndroid Build Coastguard Worker return;
910*c8dee2aaSAndroid Build Coastguard Worker }
911*c8dee2aaSAndroid Build Coastguard Worker const SkTypeface& typeface = pdfStrike->fPath.fStrikeSpec.typeface();
912*c8dee2aaSAndroid Build Coastguard Worker
913*c8dee2aaSAndroid Build Coastguard Worker const SkAdvancedTypefaceMetrics* metrics = SkPDFFont::GetMetrics(typeface, fDocument);
914*c8dee2aaSAndroid Build Coastguard Worker if (!metrics) {
915*c8dee2aaSAndroid Build Coastguard Worker return;
916*c8dee2aaSAndroid Build Coastguard Worker }
917*c8dee2aaSAndroid Build Coastguard Worker
918*c8dee2aaSAndroid Build Coastguard Worker const std::vector<SkUnichar>& glyphToUnicode = SkPDFFont::GetUnicodeMap(typeface, fDocument);
919*c8dee2aaSAndroid Build Coastguard Worker THashMap<SkGlyphID, SkString>& glyphToUnicodeEx=SkPDFFont::GetUnicodeMapEx(typeface, fDocument);
920*c8dee2aaSAndroid Build Coastguard Worker
921*c8dee2aaSAndroid Build Coastguard Worker // TODO: FontType should probably be on SkPDFStrike?
922*c8dee2aaSAndroid Build Coastguard Worker SkAdvancedTypefaceMetrics::FontType initialFontType = SkPDFFont::FontType(*pdfStrike, *metrics);
923*c8dee2aaSAndroid Build Coastguard Worker
924*c8dee2aaSAndroid Build Coastguard Worker SkClusterator clusterator(glyphRun);
925*c8dee2aaSAndroid Build Coastguard Worker
926*c8dee2aaSAndroid Build Coastguard Worker // The size, skewX, and scaleX are applied here.
927*c8dee2aaSAndroid Build Coastguard Worker SkScalar textSize = glyphRunFont.getSize();
928*c8dee2aaSAndroid Build Coastguard Worker SkScalar advanceScale = textSize * glyphRunFont.getScaleX() / pdfStrike->fPath.fUnitsPerEM;
929*c8dee2aaSAndroid Build Coastguard Worker
930*c8dee2aaSAndroid Build Coastguard Worker // textScaleX and textScaleY are used to get a conservative bounding box for glyphs.
931*c8dee2aaSAndroid Build Coastguard Worker SkScalar textScaleY = textSize / pdfStrike->fPath.fUnitsPerEM;
932*c8dee2aaSAndroid Build Coastguard Worker SkScalar textScaleX = advanceScale + glyphRunFont.getSkewX() * textScaleY;
933*c8dee2aaSAndroid Build Coastguard Worker
934*c8dee2aaSAndroid Build Coastguard Worker SkRect clipStackBounds = this->cs().bounds(this->bounds());
935*c8dee2aaSAndroid Build Coastguard Worker
936*c8dee2aaSAndroid Build Coastguard Worker // Clear everything from the runPaint that will be applied by the strike.
937*c8dee2aaSAndroid Build Coastguard Worker SkPaint fillPaint(runPaint);
938*c8dee2aaSAndroid Build Coastguard Worker if (fillPaint.getStrokeWidth() > 0) {
939*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setStroke(false);
940*c8dee2aaSAndroid Build Coastguard Worker }
941*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setPathEffect(nullptr);
942*c8dee2aaSAndroid Build Coastguard Worker fillPaint.setMaskFilter(nullptr);
943*c8dee2aaSAndroid Build Coastguard Worker SkTCopyOnFirstWrite<SkPaint> paint(clean_paint(fillPaint));
944*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, *paint, glyphRunFont.getScaleX());
945*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
946*c8dee2aaSAndroid Build Coastguard Worker return;
947*c8dee2aaSAndroid Build Coastguard Worker }
948*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream* out = content.stream();
949*c8dee2aaSAndroid Build Coastguard Worker
950*c8dee2aaSAndroid Build Coastguard Worker // Destinations are in absolute coordinates.
951*c8dee2aaSAndroid Build Coastguard Worker // The glyphs bounds go through the localToDevice separately for clipping.
952*c8dee2aaSAndroid Build Coastguard Worker SkMatrix pageXform = this->deviceToGlobal().asM33();
953*c8dee2aaSAndroid Build Coastguard Worker pageXform.postConcat(fDocument->currentPageTransform());
954*c8dee2aaSAndroid Build Coastguard Worker
955*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.beginMark();
956*c8dee2aaSAndroid Build Coastguard Worker if (!glyphRun.text().empty()) {
957*c8dee2aaSAndroid Build Coastguard Worker fDocument->addStructElemTitle(fMarkManager.elemId(), glyphRun.text());
958*c8dee2aaSAndroid Build Coastguard Worker }
959*c8dee2aaSAndroid Build Coastguard Worker
960*c8dee2aaSAndroid Build Coastguard Worker out->writeText("BT\n");
961*c8dee2aaSAndroid Build Coastguard Worker SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
962*c8dee2aaSAndroid Build Coastguard Worker
963*c8dee2aaSAndroid Build Coastguard Worker const int numGlyphs = typeface.countGlyphs();
964*c8dee2aaSAndroid Build Coastguard Worker
965*c8dee2aaSAndroid Build Coastguard Worker if (clusterator.reversedChars()) {
966*c8dee2aaSAndroid Build Coastguard Worker out->writeText("/ReversedChars BMC\n");
967*c8dee2aaSAndroid Build Coastguard Worker }
968*c8dee2aaSAndroid Build Coastguard Worker SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
969*c8dee2aaSAndroid Build Coastguard Worker GlyphPositioner glyphPositioner(out, glyphRunFont.getSkewX(), offset);
970*c8dee2aaSAndroid Build Coastguard Worker SkPDFFont* font = nullptr;
971*c8dee2aaSAndroid Build Coastguard Worker
972*c8dee2aaSAndroid Build Coastguard Worker SkBulkGlyphMetricsAndPaths paths{pdfStrike->fPath.fStrikeSpec};
973*c8dee2aaSAndroid Build Coastguard Worker auto glyphs = paths.glyphs(glyphRun.glyphsIDs());
974*c8dee2aaSAndroid Build Coastguard Worker
975*c8dee2aaSAndroid Build Coastguard Worker while (SkClusterator::Cluster c = clusterator.next()) {
976*c8dee2aaSAndroid Build Coastguard Worker int glyphIndex = c.fGlyphIndex;
977*c8dee2aaSAndroid Build Coastguard Worker int glyphLimit = glyphIndex + c.fGlyphCount;
978*c8dee2aaSAndroid Build Coastguard Worker
979*c8dee2aaSAndroid Build Coastguard Worker bool actualText = false;
980*c8dee2aaSAndroid Build Coastguard Worker SK_AT_SCOPE_EXIT(if (actualText) {
981*c8dee2aaSAndroid Build Coastguard Worker glyphPositioner.flush();
982*c8dee2aaSAndroid Build Coastguard Worker out->writeText("EMC\n");
983*c8dee2aaSAndroid Build Coastguard Worker });
984*c8dee2aaSAndroid Build Coastguard Worker if (c.fUtf8Text) {
985*c8dee2aaSAndroid Build Coastguard Worker bool toUnicode = false;
986*c8dee2aaSAndroid Build Coastguard Worker const char* textPtr = c.fUtf8Text;
987*c8dee2aaSAndroid Build Coastguard Worker const char* textEnd = c.fUtf8Text + c.fTextByteLength;
988*c8dee2aaSAndroid Build Coastguard Worker SkUnichar clusterUnichar = SkUTF::NextUTF8(&textPtr, textEnd);
989*c8dee2aaSAndroid Build Coastguard Worker // ToUnicode can only handle one glyph in a cluster.
990*c8dee2aaSAndroid Build Coastguard Worker if (clusterUnichar >= 0 && c.fGlyphCount == 1) {
991*c8dee2aaSAndroid Build Coastguard Worker SkGlyphID gid = glyphIDs[glyphIndex];
992*c8dee2aaSAndroid Build Coastguard Worker SkUnichar fontUnichar = gid < glyphToUnicode.size() ? glyphToUnicode[gid] : 0;
993*c8dee2aaSAndroid Build Coastguard Worker
994*c8dee2aaSAndroid Build Coastguard Worker // The regular cmap can handle this if there is one glyph in the cluster,
995*c8dee2aaSAndroid Build Coastguard Worker // one code point in the cluster, and the glyph maps to the code point.
996*c8dee2aaSAndroid Build Coastguard Worker toUnicode = textPtr == textEnd && clusterUnichar == fontUnichar;
997*c8dee2aaSAndroid Build Coastguard Worker
998*c8dee2aaSAndroid Build Coastguard Worker // The extended cmap can handle this if there is one glyph in the cluster,
999*c8dee2aaSAndroid Build Coastguard Worker // the font has no code point for the glyph,
1000*c8dee2aaSAndroid Build Coastguard Worker // there are less than 512 bytes in the UTF-16,
1001*c8dee2aaSAndroid Build Coastguard Worker // and the mapping matches or can be added.
1002*c8dee2aaSAndroid Build Coastguard Worker // UTF-16 uses at most 2x space of UTF-8; 64 code points seems enough.
1003*c8dee2aaSAndroid Build Coastguard Worker if (!toUnicode && fontUnichar <= 0 && c.fTextByteLength < 256) {
1004*c8dee2aaSAndroid Build Coastguard Worker SkString* unicodes = glyphToUnicodeEx.find(gid);
1005*c8dee2aaSAndroid Build Coastguard Worker if (!unicodes) {
1006*c8dee2aaSAndroid Build Coastguard Worker glyphToUnicodeEx.set(gid, SkString(c.fUtf8Text, c.fTextByteLength));
1007*c8dee2aaSAndroid Build Coastguard Worker toUnicode = true;
1008*c8dee2aaSAndroid Build Coastguard Worker } else if (unicodes->equals(c.fUtf8Text, c.fTextByteLength)) {
1009*c8dee2aaSAndroid Build Coastguard Worker toUnicode = true;
1010*c8dee2aaSAndroid Build Coastguard Worker }
1011*c8dee2aaSAndroid Build Coastguard Worker }
1012*c8dee2aaSAndroid Build Coastguard Worker }
1013*c8dee2aaSAndroid Build Coastguard Worker if (!toUnicode) {
1014*c8dee2aaSAndroid Build Coastguard Worker glyphPositioner.flush();
1015*c8dee2aaSAndroid Build Coastguard Worker // Begin marked-content sequence with associated property list.
1016*c8dee2aaSAndroid Build Coastguard Worker out->writeText("/Span<</ActualText ");
1017*c8dee2aaSAndroid Build Coastguard Worker SkPDFWriteTextString(out, c.fUtf8Text, c.fTextByteLength);
1018*c8dee2aaSAndroid Build Coastguard Worker out->writeText(" >> BDC\n");
1019*c8dee2aaSAndroid Build Coastguard Worker actualText = true;
1020*c8dee2aaSAndroid Build Coastguard Worker }
1021*c8dee2aaSAndroid Build Coastguard Worker }
1022*c8dee2aaSAndroid Build Coastguard Worker for (; glyphIndex < glyphLimit; ++glyphIndex) {
1023*c8dee2aaSAndroid Build Coastguard Worker SkGlyphID gid = glyphIDs[glyphIndex];
1024*c8dee2aaSAndroid Build Coastguard Worker if (numGlyphs <= gid) {
1025*c8dee2aaSAndroid Build Coastguard Worker continue;
1026*c8dee2aaSAndroid Build Coastguard Worker }
1027*c8dee2aaSAndroid Build Coastguard Worker SkPoint xy = glyphRun.positions()[glyphIndex];
1028*c8dee2aaSAndroid Build Coastguard Worker // Do a glyph-by-glyph bounds-reject if positions are absolute.
1029*c8dee2aaSAndroid Build Coastguard Worker SkRect glyphBounds = get_glyph_bounds_device_space(
1030*c8dee2aaSAndroid Build Coastguard Worker glyphs[glyphIndex], textScaleX, textScaleY,
1031*c8dee2aaSAndroid Build Coastguard Worker xy + offset, this->localToDevice());
1032*c8dee2aaSAndroid Build Coastguard Worker if (glyphBounds.isEmpty()) {
1033*c8dee2aaSAndroid Build Coastguard Worker if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
1034*c8dee2aaSAndroid Build Coastguard Worker continue;
1035*c8dee2aaSAndroid Build Coastguard Worker }
1036*c8dee2aaSAndroid Build Coastguard Worker } else {
1037*c8dee2aaSAndroid Build Coastguard Worker if (!clipStackBounds.intersects(glyphBounds)) {
1038*c8dee2aaSAndroid Build Coastguard Worker continue; // reject glyphs as out of bounds
1039*c8dee2aaSAndroid Build Coastguard Worker }
1040*c8dee2aaSAndroid Build Coastguard Worker }
1041*c8dee2aaSAndroid Build Coastguard Worker if (needs_new_font(font, glyphs[glyphIndex], initialFontType)) {
1042*c8dee2aaSAndroid Build Coastguard Worker // Not yet specified font or need to switch font.
1043*c8dee2aaSAndroid Build Coastguard Worker font = pdfStrike->getFontResource(glyphs[glyphIndex]);
1044*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(font); // All preconditions for SkPDFFont::GetFontResource are met.
1045*c8dee2aaSAndroid Build Coastguard Worker glyphPositioner.setFont(font);
1046*c8dee2aaSAndroid Build Coastguard Worker SkPDFWriteResourceName(out, SkPDFResourceType::kFont,
1047*c8dee2aaSAndroid Build Coastguard Worker add_resource(fFontResources, font->indirectReference()));
1048*c8dee2aaSAndroid Build Coastguard Worker out->writeText(" ");
1049*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendScalar(textSize, out);
1050*c8dee2aaSAndroid Build Coastguard Worker out->writeText(" Tf\n");
1051*c8dee2aaSAndroid Build Coastguard Worker
1052*c8dee2aaSAndroid Build Coastguard Worker }
1053*c8dee2aaSAndroid Build Coastguard Worker font->noteGlyphUsage(gid);
1054*c8dee2aaSAndroid Build Coastguard Worker SkGlyphID encodedGlyph = font->glyphToPDFFontEncoding(gid);
1055*c8dee2aaSAndroid Build Coastguard Worker SkScalar advance = advanceScale * glyphs[glyphIndex]->advanceX();
1056*c8dee2aaSAndroid Build Coastguard Worker if (fMarkManager.hasActiveMark()) {
1057*c8dee2aaSAndroid Build Coastguard Worker SkRect pageGlyphBounds = pageXform.mapRect(glyphBounds);
1058*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.accumulate({pageGlyphBounds.fLeft, pageGlyphBounds.fBottom}); // y-up
1059*c8dee2aaSAndroid Build Coastguard Worker }
1060*c8dee2aaSAndroid Build Coastguard Worker glyphPositioner.writeGlyph(encodedGlyph, advance, xy);
1061*c8dee2aaSAndroid Build Coastguard Worker }
1062*c8dee2aaSAndroid Build Coastguard Worker }
1063*c8dee2aaSAndroid Build Coastguard Worker }
1064*c8dee2aaSAndroid Build Coastguard Worker
onDrawGlyphRunList(SkCanvas *,const sktext::GlyphRunList & glyphRunList,const SkPaint & paint)1065*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::onDrawGlyphRunList(SkCanvas*,
1066*c8dee2aaSAndroid Build Coastguard Worker const sktext::GlyphRunList& glyphRunList,
1067*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint) {
1068*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!glyphRunList.hasRSXForm());
1069*c8dee2aaSAndroid Build Coastguard Worker for (const sktext::GlyphRun& glyphRun : glyphRunList) {
1070*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawGlyphRun(glyphRun, glyphRunList.origin(), paint);
1071*c8dee2aaSAndroid Build Coastguard Worker }
1072*c8dee2aaSAndroid Build Coastguard Worker }
1073*c8dee2aaSAndroid Build Coastguard Worker
drawVertices(const SkVertices *,sk_sp<SkBlender>,const SkPaint &,bool)1074*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawVertices(const SkVertices*, sk_sp<SkBlender>, const SkPaint&, bool) {
1075*c8dee2aaSAndroid Build Coastguard Worker if (this->hasEmptyClip()) {
1076*c8dee2aaSAndroid Build Coastguard Worker return;
1077*c8dee2aaSAndroid Build Coastguard Worker }
1078*c8dee2aaSAndroid Build Coastguard Worker // TODO: implement drawVertices
1079*c8dee2aaSAndroid Build Coastguard Worker }
1080*c8dee2aaSAndroid Build Coastguard Worker
drawMesh(const SkMesh &,sk_sp<SkBlender>,const SkPaint &)1081*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) {
1082*c8dee2aaSAndroid Build Coastguard Worker if (this->hasEmptyClip()) {
1083*c8dee2aaSAndroid Build Coastguard Worker return;
1084*c8dee2aaSAndroid Build Coastguard Worker }
1085*c8dee2aaSAndroid Build Coastguard Worker // TODO: implement drawMesh
1086*c8dee2aaSAndroid Build Coastguard Worker }
1087*c8dee2aaSAndroid Build Coastguard Worker
drawFormXObject(SkPDFIndirectReference xObject,SkDynamicMemoryWStream * content,SkPath * shape)1088*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawFormXObject(SkPDFIndirectReference xObject, SkDynamicMemoryWStream* content,
1089*c8dee2aaSAndroid Build Coastguard Worker SkPath* shape) {
1090*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.beginMark();
1091*c8dee2aaSAndroid Build Coastguard Worker if (fMarkManager.hasActiveMark() && shape) {
1092*c8dee2aaSAndroid Build Coastguard Worker // Destinations are in absolute coordinates.
1093*c8dee2aaSAndroid Build Coastguard Worker SkMatrix pageXform = this->deviceToGlobal().asM33();
1094*c8dee2aaSAndroid Build Coastguard Worker pageXform.postConcat(fDocument->currentPageTransform());
1095*c8dee2aaSAndroid Build Coastguard Worker // The shape already has localToDevice applied.
1096*c8dee2aaSAndroid Build Coastguard Worker
1097*c8dee2aaSAndroid Build Coastguard Worker SkRect shapeBounds = shape->computeTightBounds();
1098*c8dee2aaSAndroid Build Coastguard Worker pageXform.mapRect(&shapeBounds);
1099*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.accumulate({shapeBounds.fLeft, shapeBounds.fBottom}); // y-up
1100*c8dee2aaSAndroid Build Coastguard Worker }
1101*c8dee2aaSAndroid Build Coastguard Worker
1102*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(xObject);
1103*c8dee2aaSAndroid Build Coastguard Worker SkPDFWriteResourceName(content, SkPDFResourceType::kXObject,
1104*c8dee2aaSAndroid Build Coastguard Worker add_resource(fXObjectResources, xObject));
1105*c8dee2aaSAndroid Build Coastguard Worker content->writeText(" Do\n");
1106*c8dee2aaSAndroid Build Coastguard Worker }
1107*c8dee2aaSAndroid Build Coastguard Worker
makeSurface(const SkImageInfo & info,const SkSurfaceProps & props)1108*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1109*c8dee2aaSAndroid Build Coastguard Worker return SkSurfaces::Raster(info, &props);
1110*c8dee2aaSAndroid Build Coastguard Worker }
1111*c8dee2aaSAndroid Build Coastguard Worker
sort(const THashSet<SkPDFIndirectReference> & src)1112*c8dee2aaSAndroid Build Coastguard Worker static std::vector<SkPDFIndirectReference> sort(const THashSet<SkPDFIndirectReference>& src) {
1113*c8dee2aaSAndroid Build Coastguard Worker std::vector<SkPDFIndirectReference> dst;
1114*c8dee2aaSAndroid Build Coastguard Worker dst.reserve(src.count());
1115*c8dee2aaSAndroid Build Coastguard Worker for (SkPDFIndirectReference ref : src) {
1116*c8dee2aaSAndroid Build Coastguard Worker dst.push_back(ref);
1117*c8dee2aaSAndroid Build Coastguard Worker }
1118*c8dee2aaSAndroid Build Coastguard Worker std::sort(dst.begin(), dst.end(),
1119*c8dee2aaSAndroid Build Coastguard Worker [](SkPDFIndirectReference a, SkPDFIndirectReference b) { return a.fValue < b.fValue; });
1120*c8dee2aaSAndroid Build Coastguard Worker return dst;
1121*c8dee2aaSAndroid Build Coastguard Worker }
1122*c8dee2aaSAndroid Build Coastguard Worker
makeResourceDict()1123*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkPDFDict> SkPDFDevice::makeResourceDict() {
1124*c8dee2aaSAndroid Build Coastguard Worker return SkPDFMakeResourceDict(sort(fGraphicStateResources),
1125*c8dee2aaSAndroid Build Coastguard Worker sort(fShaderResources),
1126*c8dee2aaSAndroid Build Coastguard Worker sort(fXObjectResources),
1127*c8dee2aaSAndroid Build Coastguard Worker sort(fFontResources));
1128*c8dee2aaSAndroid Build Coastguard Worker }
1129*c8dee2aaSAndroid Build Coastguard Worker
content()1130*c8dee2aaSAndroid Build Coastguard Worker std::unique_ptr<SkStreamAsset> SkPDFDevice::content() {
1131*c8dee2aaSAndroid Build Coastguard Worker if (fActiveStackState.fContentStream) {
1132*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState.drainStack();
1133*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState = SkPDFGraphicStackState();
1134*c8dee2aaSAndroid Build Coastguard Worker }
1135*c8dee2aaSAndroid Build Coastguard Worker if (fContent.bytesWritten() == 0) {
1136*c8dee2aaSAndroid Build Coastguard Worker return std::make_unique<SkMemoryStream>();
1137*c8dee2aaSAndroid Build Coastguard Worker }
1138*c8dee2aaSAndroid Build Coastguard Worker
1139*c8dee2aaSAndroid Build Coastguard Worker // Implicitly close any still active marked-content sequence.
1140*c8dee2aaSAndroid Build Coastguard Worker // Must do this before fContent is written to buffer.
1141*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.setNextMarksElemId(0);
1142*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.beginMark();
1143*c8dee2aaSAndroid Build Coastguard Worker
1144*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream buffer;
1145*c8dee2aaSAndroid Build Coastguard Worker if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
1146*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendTransform(fInitialTransform, &buffer);
1147*c8dee2aaSAndroid Build Coastguard Worker }
1148*c8dee2aaSAndroid Build Coastguard Worker if (fNeedsExtraSave) {
1149*c8dee2aaSAndroid Build Coastguard Worker buffer.writeText("q\n");
1150*c8dee2aaSAndroid Build Coastguard Worker }
1151*c8dee2aaSAndroid Build Coastguard Worker fContent.writeToAndReset(&buffer);
1152*c8dee2aaSAndroid Build Coastguard Worker if (fNeedsExtraSave) {
1153*c8dee2aaSAndroid Build Coastguard Worker buffer.writeText("Q\n");
1154*c8dee2aaSAndroid Build Coastguard Worker }
1155*c8dee2aaSAndroid Build Coastguard Worker fNeedsExtraSave = false;
1156*c8dee2aaSAndroid Build Coastguard Worker return std::unique_ptr<SkStreamAsset>(buffer.detachAsStream());
1157*c8dee2aaSAndroid Build Coastguard Worker }
1158*c8dee2aaSAndroid Build Coastguard Worker
1159*c8dee2aaSAndroid Build Coastguard Worker /* Draws an inverse filled path by using Path Ops to compute the positive
1160*c8dee2aaSAndroid Build Coastguard Worker * inverse using the current clip as the inverse bounds.
1161*c8dee2aaSAndroid Build Coastguard Worker * Return true if this was an inverse path and was properly handled,
1162*c8dee2aaSAndroid Build Coastguard Worker * otherwise returns false and the normal drawing routine should continue,
1163*c8dee2aaSAndroid Build Coastguard Worker * either as a (incorrect) fallback or because the path was not inverse
1164*c8dee2aaSAndroid Build Coastguard Worker * in the first place.
1165*c8dee2aaSAndroid Build Coastguard Worker */
handleInversePath(const SkPath & origPath,const SkPaint & paint,bool pathIsMutable)1166*c8dee2aaSAndroid Build Coastguard Worker bool SkPDFDevice::handleInversePath(const SkPath& origPath,
1167*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint,
1168*c8dee2aaSAndroid Build Coastguard Worker bool pathIsMutable) {
1169*c8dee2aaSAndroid Build Coastguard Worker if (!origPath.isInverseFillType()) {
1170*c8dee2aaSAndroid Build Coastguard Worker return false;
1171*c8dee2aaSAndroid Build Coastguard Worker }
1172*c8dee2aaSAndroid Build Coastguard Worker
1173*c8dee2aaSAndroid Build Coastguard Worker if (this->hasEmptyClip()) {
1174*c8dee2aaSAndroid Build Coastguard Worker return false;
1175*c8dee2aaSAndroid Build Coastguard Worker }
1176*c8dee2aaSAndroid Build Coastguard Worker
1177*c8dee2aaSAndroid Build Coastguard Worker SkPath modifiedPath;
1178*c8dee2aaSAndroid Build Coastguard Worker SkPath* pathPtr = const_cast<SkPath*>(&origPath);
1179*c8dee2aaSAndroid Build Coastguard Worker SkPaint noInversePaint(paint);
1180*c8dee2aaSAndroid Build Coastguard Worker
1181*c8dee2aaSAndroid Build Coastguard Worker // Merge stroking operations into final path.
1182*c8dee2aaSAndroid Build Coastguard Worker if (SkPaint::kStroke_Style == paint.getStyle() ||
1183*c8dee2aaSAndroid Build Coastguard Worker SkPaint::kStrokeAndFill_Style == paint.getStyle()) {
1184*c8dee2aaSAndroid Build Coastguard Worker bool doFillPath = skpathutils::FillPathWithPaint(origPath, paint, &modifiedPath);
1185*c8dee2aaSAndroid Build Coastguard Worker if (doFillPath) {
1186*c8dee2aaSAndroid Build Coastguard Worker noInversePaint.setStyle(SkPaint::kFill_Style);
1187*c8dee2aaSAndroid Build Coastguard Worker noInversePaint.setStrokeWidth(0);
1188*c8dee2aaSAndroid Build Coastguard Worker pathPtr = &modifiedPath;
1189*c8dee2aaSAndroid Build Coastguard Worker } else {
1190*c8dee2aaSAndroid Build Coastguard Worker // To be consistent with the raster output, hairline strokes
1191*c8dee2aaSAndroid Build Coastguard Worker // are rendered as non-inverted.
1192*c8dee2aaSAndroid Build Coastguard Worker modifiedPath.toggleInverseFillType();
1193*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, paint, true);
1194*c8dee2aaSAndroid Build Coastguard Worker return true;
1195*c8dee2aaSAndroid Build Coastguard Worker }
1196*c8dee2aaSAndroid Build Coastguard Worker }
1197*c8dee2aaSAndroid Build Coastguard Worker
1198*c8dee2aaSAndroid Build Coastguard Worker // Get bounds of clip in current transform space
1199*c8dee2aaSAndroid Build Coastguard Worker // (clip bounds are given in device space).
1200*c8dee2aaSAndroid Build Coastguard Worker SkMatrix transformInverse;
1201*c8dee2aaSAndroid Build Coastguard Worker SkMatrix totalMatrix = this->localToDevice();
1202*c8dee2aaSAndroid Build Coastguard Worker
1203*c8dee2aaSAndroid Build Coastguard Worker if (!totalMatrix.invert(&transformInverse)) {
1204*c8dee2aaSAndroid Build Coastguard Worker return false;
1205*c8dee2aaSAndroid Build Coastguard Worker }
1206*c8dee2aaSAndroid Build Coastguard Worker SkRect bounds = this->cs().bounds(this->bounds());
1207*c8dee2aaSAndroid Build Coastguard Worker transformInverse.mapRect(&bounds);
1208*c8dee2aaSAndroid Build Coastguard Worker
1209*c8dee2aaSAndroid Build Coastguard Worker // Extend the bounds by the line width (plus some padding)
1210*c8dee2aaSAndroid Build Coastguard Worker // so the edge doesn't cause a visible stroke.
1211*c8dee2aaSAndroid Build Coastguard Worker bounds.outset(paint.getStrokeWidth() + SK_Scalar1,
1212*c8dee2aaSAndroid Build Coastguard Worker paint.getStrokeWidth() + SK_Scalar1);
1213*c8dee2aaSAndroid Build Coastguard Worker
1214*c8dee2aaSAndroid Build Coastguard Worker if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) {
1215*c8dee2aaSAndroid Build Coastguard Worker return false;
1216*c8dee2aaSAndroid Build Coastguard Worker }
1217*c8dee2aaSAndroid Build Coastguard Worker
1218*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), modifiedPath, noInversePaint, true);
1219*c8dee2aaSAndroid Build Coastguard Worker return true;
1220*c8dee2aaSAndroid Build Coastguard Worker }
1221*c8dee2aaSAndroid Build Coastguard Worker
makeFormXObjectFromDevice(SkIRect bounds,bool alpha)1222*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(SkIRect bounds, bool alpha) {
1223*c8dee2aaSAndroid Build Coastguard Worker SkMatrix inverseTransform = SkMatrix::I();
1224*c8dee2aaSAndroid Build Coastguard Worker if (!fInitialTransform.isIdentity()) {
1225*c8dee2aaSAndroid Build Coastguard Worker if (!fInitialTransform.invert(&inverseTransform)) {
1226*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGFAIL("Layer initial transform should be invertible.");
1227*c8dee2aaSAndroid Build Coastguard Worker inverseTransform.reset();
1228*c8dee2aaSAndroid Build Coastguard Worker }
1229*c8dee2aaSAndroid Build Coastguard Worker }
1230*c8dee2aaSAndroid Build Coastguard Worker const char* colorSpace = alpha ? "DeviceGray" : nullptr;
1231*c8dee2aaSAndroid Build Coastguard Worker
1232*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference xobject =
1233*c8dee2aaSAndroid Build Coastguard Worker SkPDFMakeFormXObject(fDocument, this->content(),
1234*c8dee2aaSAndroid Build Coastguard Worker SkPDFMakeArray(bounds.left(), bounds.top(),
1235*c8dee2aaSAndroid Build Coastguard Worker bounds.right(), bounds.bottom()),
1236*c8dee2aaSAndroid Build Coastguard Worker this->makeResourceDict(), inverseTransform, colorSpace);
1237*c8dee2aaSAndroid Build Coastguard Worker // We always draw the form xobjects that we create back into the device, so
1238*c8dee2aaSAndroid Build Coastguard Worker // we simply preserve the font usage instead of pulling it out and merging
1239*c8dee2aaSAndroid Build Coastguard Worker // it back in later.
1240*c8dee2aaSAndroid Build Coastguard Worker this->reset();
1241*c8dee2aaSAndroid Build Coastguard Worker return xobject;
1242*c8dee2aaSAndroid Build Coastguard Worker }
1243*c8dee2aaSAndroid Build Coastguard Worker
makeFormXObjectFromDevice(bool alpha)1244*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
1245*c8dee2aaSAndroid Build Coastguard Worker return this->makeFormXObjectFromDevice(SkIRect{0, 0, this->width(), this->height()}, alpha);
1246*c8dee2aaSAndroid Build Coastguard Worker }
1247*c8dee2aaSAndroid Build Coastguard Worker
drawFormXObjectWithMask(SkPDFIndirectReference xObject,SkPDFIndirectReference sMask,SkBlendMode mode,bool invertClip)1248*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawFormXObjectWithMask(SkPDFIndirectReference xObject,
1249*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference sMask,
1250*c8dee2aaSAndroid Build Coastguard Worker SkBlendMode mode,
1251*c8dee2aaSAndroid Build Coastguard Worker bool invertClip) {
1252*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(sMask);
1253*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
1254*c8dee2aaSAndroid Build Coastguard Worker paint.setBlendMode(mode);
1255*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, nullptr, SkMatrix::I(), paint);
1256*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
1257*c8dee2aaSAndroid Build Coastguard Worker return;
1258*c8dee2aaSAndroid Build Coastguard Worker }
1259*c8dee2aaSAndroid Build Coastguard Worker this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
1260*c8dee2aaSAndroid Build Coastguard Worker sMask, invertClip, SkPDFGraphicState::kAlpha_SMaskMode,
1261*c8dee2aaSAndroid Build Coastguard Worker fDocument), content.stream());
1262*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObject(xObject, content.stream(), nullptr);
1263*c8dee2aaSAndroid Build Coastguard Worker this->clearMaskOnGraphicState(content.stream());
1264*c8dee2aaSAndroid Build Coastguard Worker }
1265*c8dee2aaSAndroid Build Coastguard Worker
1266*c8dee2aaSAndroid Build Coastguard Worker
treat_as_regular_pdf_blend_mode(SkBlendMode blendMode)1267*c8dee2aaSAndroid Build Coastguard Worker static bool treat_as_regular_pdf_blend_mode(SkBlendMode blendMode) {
1268*c8dee2aaSAndroid Build Coastguard Worker return nullptr != SkPDFUtils::BlendModeName(blendMode);
1269*c8dee2aaSAndroid Build Coastguard Worker }
1270*c8dee2aaSAndroid Build Coastguard Worker
populate_graphic_state_entry_from_paint(SkPDFDocument * doc,const SkMatrix & matrix,const SkClipStack * clipStack,SkIRect deviceBounds,const SkPaint & paint,const SkMatrix & initialTransform,SkScalar textScale,SkPDFGraphicStackState::Entry * entry,THashSet<SkPDFIndirectReference> * shaderResources,THashSet<SkPDFIndirectReference> * graphicStateResources)1271*c8dee2aaSAndroid Build Coastguard Worker static void populate_graphic_state_entry_from_paint(
1272*c8dee2aaSAndroid Build Coastguard Worker SkPDFDocument* doc,
1273*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
1274*c8dee2aaSAndroid Build Coastguard Worker const SkClipStack* clipStack,
1275*c8dee2aaSAndroid Build Coastguard Worker SkIRect deviceBounds,
1276*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint,
1277*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& initialTransform,
1278*c8dee2aaSAndroid Build Coastguard Worker SkScalar textScale,
1279*c8dee2aaSAndroid Build Coastguard Worker SkPDFGraphicStackState::Entry* entry,
1280*c8dee2aaSAndroid Build Coastguard Worker THashSet<SkPDFIndirectReference>* shaderResources,
1281*c8dee2aaSAndroid Build Coastguard Worker THashSet<SkPDFIndirectReference>* graphicStateResources) {
1282*c8dee2aaSAndroid Build Coastguard Worker NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
1283*c8dee2aaSAndroid Build Coastguard Worker NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
1284*c8dee2aaSAndroid Build Coastguard Worker NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
1285*c8dee2aaSAndroid Build Coastguard Worker
1286*c8dee2aaSAndroid Build Coastguard Worker entry->fMatrix = matrix;
1287*c8dee2aaSAndroid Build Coastguard Worker entry->fClipStackGenID = clipStack ? clipStack->getTopmostGenID()
1288*c8dee2aaSAndroid Build Coastguard Worker : SkClipStack::kWideOpenGenID;
1289*c8dee2aaSAndroid Build Coastguard Worker SkColor4f color = paint.getColor4f();
1290*c8dee2aaSAndroid Build Coastguard Worker entry->fColor = {color.fR, color.fG, color.fB, 1};
1291*c8dee2aaSAndroid Build Coastguard Worker entry->fShaderIndex = -1;
1292*c8dee2aaSAndroid Build Coastguard Worker
1293*c8dee2aaSAndroid Build Coastguard Worker // PDF treats a shader as a color, so we only set one or the other.
1294*c8dee2aaSAndroid Build Coastguard Worker SkShader* shader = paint.getShader();
1295*c8dee2aaSAndroid Build Coastguard Worker if (shader) {
1296*c8dee2aaSAndroid Build Coastguard Worker // note: we always present the alpha as 1 for the shader, knowing that it will be
1297*c8dee2aaSAndroid Build Coastguard Worker // accounted for when we create our newGraphicsState (below)
1298*c8dee2aaSAndroid Build Coastguard Worker if (as_SB(shader)->type() == SkShaderBase::ShaderType::kColor) {
1299*c8dee2aaSAndroid Build Coastguard Worker auto colorShader = static_cast<SkColorShader*>(shader);
1300*c8dee2aaSAndroid Build Coastguard Worker // We don't have to set a shader just for a color.
1301*c8dee2aaSAndroid Build Coastguard Worker color = SkColor4f::FromColor(colorShader->color());
1302*c8dee2aaSAndroid Build Coastguard Worker entry->fColor = {color.fR, color.fG, color.fB, 1};
1303*c8dee2aaSAndroid Build Coastguard Worker } else {
1304*c8dee2aaSAndroid Build Coastguard Worker // PDF positions patterns relative to the initial transform, so
1305*c8dee2aaSAndroid Build Coastguard Worker // we need to apply the current transform to the shader parameters.
1306*c8dee2aaSAndroid Build Coastguard Worker SkMatrix transform = matrix;
1307*c8dee2aaSAndroid Build Coastguard Worker transform.postConcat(initialTransform);
1308*c8dee2aaSAndroid Build Coastguard Worker
1309*c8dee2aaSAndroid Build Coastguard Worker // PDF doesn't support kClamp_TileMode, so we simulate it by making
1310*c8dee2aaSAndroid Build Coastguard Worker // a pattern the size of the current clip.
1311*c8dee2aaSAndroid Build Coastguard Worker SkRect clipStackBounds = clipStack ? clipStack->bounds(deviceBounds)
1312*c8dee2aaSAndroid Build Coastguard Worker : SkRect::Make(deviceBounds);
1313*c8dee2aaSAndroid Build Coastguard Worker
1314*c8dee2aaSAndroid Build Coastguard Worker // We need to apply the initial transform to bounds in order to get
1315*c8dee2aaSAndroid Build Coastguard Worker // bounds in a consistent coordinate system.
1316*c8dee2aaSAndroid Build Coastguard Worker initialTransform.mapRect(&clipStackBounds);
1317*c8dee2aaSAndroid Build Coastguard Worker SkIRect bounds;
1318*c8dee2aaSAndroid Build Coastguard Worker clipStackBounds.roundOut(&bounds);
1319*c8dee2aaSAndroid Build Coastguard Worker
1320*c8dee2aaSAndroid Build Coastguard Worker auto c = paint.getColor4f();
1321*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference pdfShader = SkPDFMakeShader(doc, shader, transform, bounds,
1322*c8dee2aaSAndroid Build Coastguard Worker {c.fR, c.fG, c.fB, 1.0f});
1323*c8dee2aaSAndroid Build Coastguard Worker
1324*c8dee2aaSAndroid Build Coastguard Worker if (pdfShader) {
1325*c8dee2aaSAndroid Build Coastguard Worker // pdfShader has been canonicalized so we can directly compare pointers.
1326*c8dee2aaSAndroid Build Coastguard Worker entry->fShaderIndex = add_resource(*shaderResources, pdfShader);
1327*c8dee2aaSAndroid Build Coastguard Worker }
1328*c8dee2aaSAndroid Build Coastguard Worker }
1329*c8dee2aaSAndroid Build Coastguard Worker }
1330*c8dee2aaSAndroid Build Coastguard Worker
1331*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference newGraphicState;
1332*c8dee2aaSAndroid Build Coastguard Worker if (color == paint.getColor4f()) {
1333*c8dee2aaSAndroid Build Coastguard Worker newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(doc, paint);
1334*c8dee2aaSAndroid Build Coastguard Worker } else {
1335*c8dee2aaSAndroid Build Coastguard Worker SkPaint newPaint = paint;
1336*c8dee2aaSAndroid Build Coastguard Worker newPaint.setColor4f(color, nullptr);
1337*c8dee2aaSAndroid Build Coastguard Worker newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(doc, newPaint);
1338*c8dee2aaSAndroid Build Coastguard Worker }
1339*c8dee2aaSAndroid Build Coastguard Worker entry->fGraphicStateIndex = add_resource(*graphicStateResources, newGraphicState);
1340*c8dee2aaSAndroid Build Coastguard Worker entry->fTextScaleX = textScale;
1341*c8dee2aaSAndroid Build Coastguard Worker }
1342*c8dee2aaSAndroid Build Coastguard Worker
setUpContentEntry(const SkClipStack * clipStack,const SkMatrix & matrix,const SkPaint & paint,SkScalar textScale,SkPDFIndirectReference * dst)1343*c8dee2aaSAndroid Build Coastguard Worker SkDynamicMemoryWStream* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
1344*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& matrix,
1345*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint,
1346*c8dee2aaSAndroid Build Coastguard Worker SkScalar textScale,
1347*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference* dst) {
1348*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!*dst);
1349*c8dee2aaSAndroid Build Coastguard Worker SkBlendMode blendMode = paint.getBlendMode_or(SkBlendMode::kSrcOver);
1350*c8dee2aaSAndroid Build Coastguard Worker
1351*c8dee2aaSAndroid Build Coastguard Worker // Dst xfer mode doesn't draw source at all.
1352*c8dee2aaSAndroid Build Coastguard Worker if (blendMode == SkBlendMode::kDst) {
1353*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
1354*c8dee2aaSAndroid Build Coastguard Worker }
1355*c8dee2aaSAndroid Build Coastguard Worker
1356*c8dee2aaSAndroid Build Coastguard Worker // For the following modes, we want to handle source and destination
1357*c8dee2aaSAndroid Build Coastguard Worker // separately, so make an object of what's already there.
1358*c8dee2aaSAndroid Build Coastguard Worker if (!treat_as_regular_pdf_blend_mode(blendMode) && blendMode != SkBlendMode::kDstOver) {
1359*c8dee2aaSAndroid Build Coastguard Worker if (!isContentEmpty()) {
1360*c8dee2aaSAndroid Build Coastguard Worker *dst = this->makeFormXObjectFromDevice();
1361*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(isContentEmpty());
1362*c8dee2aaSAndroid Build Coastguard Worker } else if (blendMode != SkBlendMode::kSrc &&
1363*c8dee2aaSAndroid Build Coastguard Worker blendMode != SkBlendMode::kSrcOut) {
1364*c8dee2aaSAndroid Build Coastguard Worker // Except for Src and SrcOut, if there isn't anything already there,
1365*c8dee2aaSAndroid Build Coastguard Worker // then we're done.
1366*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
1367*c8dee2aaSAndroid Build Coastguard Worker }
1368*c8dee2aaSAndroid Build Coastguard Worker }
1369*c8dee2aaSAndroid Build Coastguard Worker // TODO(vandebo): Figure out how/if we can handle the following modes:
1370*c8dee2aaSAndroid Build Coastguard Worker // Xor, Plus. For now, we treat them as SrcOver/Normal.
1371*c8dee2aaSAndroid Build Coastguard Worker
1372*c8dee2aaSAndroid Build Coastguard Worker if (treat_as_regular_pdf_blend_mode(blendMode)) {
1373*c8dee2aaSAndroid Build Coastguard Worker if (!fActiveStackState.fContentStream) {
1374*c8dee2aaSAndroid Build Coastguard Worker if (fContent.bytesWritten() != 0) {
1375*c8dee2aaSAndroid Build Coastguard Worker fContent.writeText("Q\nq\n");
1376*c8dee2aaSAndroid Build Coastguard Worker fNeedsExtraSave = true;
1377*c8dee2aaSAndroid Build Coastguard Worker }
1378*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState = SkPDFGraphicStackState(&fContent);
1379*c8dee2aaSAndroid Build Coastguard Worker } else {
1380*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fActiveStackState.fContentStream = &fContent);
1381*c8dee2aaSAndroid Build Coastguard Worker }
1382*c8dee2aaSAndroid Build Coastguard Worker } else {
1383*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState.drainStack();
1384*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState = SkPDFGraphicStackState(&fContentBuffer);
1385*c8dee2aaSAndroid Build Coastguard Worker }
1386*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fActiveStackState.fContentStream);
1387*c8dee2aaSAndroid Build Coastguard Worker SkPDFGraphicStackState::Entry entry;
1388*c8dee2aaSAndroid Build Coastguard Worker populate_graphic_state_entry_from_paint(
1389*c8dee2aaSAndroid Build Coastguard Worker fDocument,
1390*c8dee2aaSAndroid Build Coastguard Worker matrix,
1391*c8dee2aaSAndroid Build Coastguard Worker clipStack,
1392*c8dee2aaSAndroid Build Coastguard Worker this->bounds(),
1393*c8dee2aaSAndroid Build Coastguard Worker paint,
1394*c8dee2aaSAndroid Build Coastguard Worker fInitialTransform,
1395*c8dee2aaSAndroid Build Coastguard Worker textScale,
1396*c8dee2aaSAndroid Build Coastguard Worker &entry,
1397*c8dee2aaSAndroid Build Coastguard Worker &fShaderResources,
1398*c8dee2aaSAndroid Build Coastguard Worker &fGraphicStateResources);
1399*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState.updateClip(clipStack, this->bounds());
1400*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState.updateMatrix(entry.fMatrix);
1401*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState.updateDrawingState(entry);
1402*c8dee2aaSAndroid Build Coastguard Worker
1403*c8dee2aaSAndroid Build Coastguard Worker return fActiveStackState.fContentStream;
1404*c8dee2aaSAndroid Build Coastguard Worker }
1405*c8dee2aaSAndroid Build Coastguard Worker
finishContentEntry(const SkClipStack * clipStack,SkBlendMode blendMode,SkPDFIndirectReference dst,SkPath * shape)1406*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::finishContentEntry(const SkClipStack* clipStack,
1407*c8dee2aaSAndroid Build Coastguard Worker SkBlendMode blendMode,
1408*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference dst,
1409*c8dee2aaSAndroid Build Coastguard Worker SkPath* shape) {
1410*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(blendMode != SkBlendMode::kDst);
1411*c8dee2aaSAndroid Build Coastguard Worker if (treat_as_regular_pdf_blend_mode(blendMode)) {
1412*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!dst);
1413*c8dee2aaSAndroid Build Coastguard Worker return;
1414*c8dee2aaSAndroid Build Coastguard Worker }
1415*c8dee2aaSAndroid Build Coastguard Worker
1416*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fActiveStackState.fContentStream);
1417*c8dee2aaSAndroid Build Coastguard Worker
1418*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState.drainStack();
1419*c8dee2aaSAndroid Build Coastguard Worker fActiveStackState = SkPDFGraphicStackState();
1420*c8dee2aaSAndroid Build Coastguard Worker
1421*c8dee2aaSAndroid Build Coastguard Worker if (blendMode == SkBlendMode::kDstOver) {
1422*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!dst);
1423*c8dee2aaSAndroid Build Coastguard Worker if (fContentBuffer.bytesWritten() != 0) {
1424*c8dee2aaSAndroid Build Coastguard Worker if (fContent.bytesWritten() != 0) {
1425*c8dee2aaSAndroid Build Coastguard Worker fContentBuffer.writeText("Q\nq\n");
1426*c8dee2aaSAndroid Build Coastguard Worker fNeedsExtraSave = true;
1427*c8dee2aaSAndroid Build Coastguard Worker }
1428*c8dee2aaSAndroid Build Coastguard Worker fContentBuffer.prependToAndReset(&fContent);
1429*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fContentBuffer.bytesWritten() == 0);
1430*c8dee2aaSAndroid Build Coastguard Worker }
1431*c8dee2aaSAndroid Build Coastguard Worker return;
1432*c8dee2aaSAndroid Build Coastguard Worker }
1433*c8dee2aaSAndroid Build Coastguard Worker if (fContentBuffer.bytesWritten() != 0) {
1434*c8dee2aaSAndroid Build Coastguard Worker if (fContent.bytesWritten() != 0) {
1435*c8dee2aaSAndroid Build Coastguard Worker fContent.writeText("Q\nq\n");
1436*c8dee2aaSAndroid Build Coastguard Worker fNeedsExtraSave = true;
1437*c8dee2aaSAndroid Build Coastguard Worker }
1438*c8dee2aaSAndroid Build Coastguard Worker fContentBuffer.writeToAndReset(&fContent);
1439*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fContentBuffer.bytesWritten() == 0);
1440*c8dee2aaSAndroid Build Coastguard Worker }
1441*c8dee2aaSAndroid Build Coastguard Worker
1442*c8dee2aaSAndroid Build Coastguard Worker if (!dst) {
1443*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(blendMode == SkBlendMode::kSrc ||
1444*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kSrcOut);
1445*c8dee2aaSAndroid Build Coastguard Worker return;
1446*c8dee2aaSAndroid Build Coastguard Worker }
1447*c8dee2aaSAndroid Build Coastguard Worker
1448*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(dst);
1449*c8dee2aaSAndroid Build Coastguard Worker // Changing the current content into a form-xobject will destroy the clip
1450*c8dee2aaSAndroid Build Coastguard Worker // objects which is fine since the xobject will already be clipped. However
1451*c8dee2aaSAndroid Build Coastguard Worker // if source has shape, we need to clip it too, so a copy of the clip is
1452*c8dee2aaSAndroid Build Coastguard Worker // saved.
1453*c8dee2aaSAndroid Build Coastguard Worker
1454*c8dee2aaSAndroid Build Coastguard Worker SkPaint stockPaint;
1455*c8dee2aaSAndroid Build Coastguard Worker
1456*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference srcFormXObject;
1457*c8dee2aaSAndroid Build Coastguard Worker if (this->isContentEmpty()) {
1458*c8dee2aaSAndroid Build Coastguard Worker // If nothing was drawn and there's no shape, then the draw was a
1459*c8dee2aaSAndroid Build Coastguard Worker // no-op, but dst needs to be restored for that to be true.
1460*c8dee2aaSAndroid Build Coastguard Worker // If there is shape, then an empty source with Src, SrcIn, SrcOut,
1461*c8dee2aaSAndroid Build Coastguard Worker // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
1462*c8dee2aaSAndroid Build Coastguard Worker // reduces to Dst.
1463*c8dee2aaSAndroid Build Coastguard Worker if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
1464*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kSrcATop) {
1465*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
1466*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObject(dst, content.stream(), nullptr);
1467*c8dee2aaSAndroid Build Coastguard Worker return;
1468*c8dee2aaSAndroid Build Coastguard Worker } else {
1469*c8dee2aaSAndroid Build Coastguard Worker blendMode = SkBlendMode::kClear;
1470*c8dee2aaSAndroid Build Coastguard Worker }
1471*c8dee2aaSAndroid Build Coastguard Worker } else {
1472*c8dee2aaSAndroid Build Coastguard Worker srcFormXObject = this->makeFormXObjectFromDevice();
1473*c8dee2aaSAndroid Build Coastguard Worker }
1474*c8dee2aaSAndroid Build Coastguard Worker
1475*c8dee2aaSAndroid Build Coastguard Worker // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
1476*c8dee2aaSAndroid Build Coastguard Worker // without alpha.
1477*c8dee2aaSAndroid Build Coastguard Worker if (blendMode == SkBlendMode::kSrcATop) {
1478*c8dee2aaSAndroid Build Coastguard Worker // TODO(vandebo): In order to properly support SrcATop we have to track
1479*c8dee2aaSAndroid Build Coastguard Worker // the shape of what's been drawn at all times. It's the intersection of
1480*c8dee2aaSAndroid Build Coastguard Worker // the non-transparent parts of the device and the outlines (shape) of
1481*c8dee2aaSAndroid Build Coastguard Worker // all images and devices drawn.
1482*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver, true);
1483*c8dee2aaSAndroid Build Coastguard Worker } else {
1484*c8dee2aaSAndroid Build Coastguard Worker if (shape != nullptr) {
1485*c8dee2aaSAndroid Build Coastguard Worker // Draw shape into a form-xobject.
1486*c8dee2aaSAndroid Build Coastguard Worker SkPaint filledPaint;
1487*c8dee2aaSAndroid Build Coastguard Worker filledPaint.setColor(SK_ColorBLACK);
1488*c8dee2aaSAndroid Build Coastguard Worker filledPaint.setStyle(SkPaint::kFill_Style);
1489*c8dee2aaSAndroid Build Coastguard Worker SkClipStack empty;
1490*c8dee2aaSAndroid Build Coastguard Worker SkPDFDevice shapeDev(this->size(), fDocument, fInitialTransform);
1491*c8dee2aaSAndroid Build Coastguard Worker shapeDev.internalDrawPath(clipStack ? *clipStack : empty,
1492*c8dee2aaSAndroid Build Coastguard Worker SkMatrix::I(), *shape, filledPaint, true);
1493*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObjectWithMask(dst, shapeDev.makeFormXObjectFromDevice(),
1494*c8dee2aaSAndroid Build Coastguard Worker SkBlendMode::kSrcOver, true);
1495*c8dee2aaSAndroid Build Coastguard Worker } else {
1496*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObjectWithMask(dst, srcFormXObject, SkBlendMode::kSrcOver, true);
1497*c8dee2aaSAndroid Build Coastguard Worker }
1498*c8dee2aaSAndroid Build Coastguard Worker }
1499*c8dee2aaSAndroid Build Coastguard Worker
1500*c8dee2aaSAndroid Build Coastguard Worker if (blendMode == SkBlendMode::kClear) {
1501*c8dee2aaSAndroid Build Coastguard Worker return;
1502*c8dee2aaSAndroid Build Coastguard Worker } else if (blendMode == SkBlendMode::kSrc ||
1503*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kDstATop) {
1504*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
1505*c8dee2aaSAndroid Build Coastguard Worker if (content) {
1506*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObject(srcFormXObject, content.stream(), nullptr);
1507*c8dee2aaSAndroid Build Coastguard Worker }
1508*c8dee2aaSAndroid Build Coastguard Worker if (blendMode == SkBlendMode::kSrc) {
1509*c8dee2aaSAndroid Build Coastguard Worker return;
1510*c8dee2aaSAndroid Build Coastguard Worker }
1511*c8dee2aaSAndroid Build Coastguard Worker } else if (blendMode == SkBlendMode::kSrcATop) {
1512*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, nullptr, SkMatrix::I(), stockPaint);
1513*c8dee2aaSAndroid Build Coastguard Worker if (content) {
1514*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObject(dst, content.stream(), nullptr);
1515*c8dee2aaSAndroid Build Coastguard Worker }
1516*c8dee2aaSAndroid Build Coastguard Worker }
1517*c8dee2aaSAndroid Build Coastguard Worker
1518*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(blendMode == SkBlendMode::kSrcIn ||
1519*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kDstIn ||
1520*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kSrcOut ||
1521*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kDstOut ||
1522*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kSrcATop ||
1523*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kDstATop ||
1524*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kModulate);
1525*c8dee2aaSAndroid Build Coastguard Worker
1526*c8dee2aaSAndroid Build Coastguard Worker if (blendMode == SkBlendMode::kSrcIn ||
1527*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kSrcOut ||
1528*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kSrcATop) {
1529*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver,
1530*c8dee2aaSAndroid Build Coastguard Worker blendMode == SkBlendMode::kSrcOut);
1531*c8dee2aaSAndroid Build Coastguard Worker return;
1532*c8dee2aaSAndroid Build Coastguard Worker } else {
1533*c8dee2aaSAndroid Build Coastguard Worker SkBlendMode mode = SkBlendMode::kSrcOver;
1534*c8dee2aaSAndroid Build Coastguard Worker if (blendMode == SkBlendMode::kModulate) {
1535*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObjectWithMask(srcFormXObject, dst, SkBlendMode::kSrcOver, false);
1536*c8dee2aaSAndroid Build Coastguard Worker mode = SkBlendMode::kMultiply;
1537*c8dee2aaSAndroid Build Coastguard Worker }
1538*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObjectWithMask(dst, srcFormXObject, mode, blendMode == SkBlendMode::kDstOut);
1539*c8dee2aaSAndroid Build Coastguard Worker return;
1540*c8dee2aaSAndroid Build Coastguard Worker }
1541*c8dee2aaSAndroid Build Coastguard Worker }
1542*c8dee2aaSAndroid Build Coastguard Worker
isContentEmpty()1543*c8dee2aaSAndroid Build Coastguard Worker bool SkPDFDevice::isContentEmpty() {
1544*c8dee2aaSAndroid Build Coastguard Worker return fContent.bytesWritten() == 0 && fContentBuffer.bytesWritten() == 0;
1545*c8dee2aaSAndroid Build Coastguard Worker }
1546*c8dee2aaSAndroid Build Coastguard Worker
rect_to_size(const SkRect & r)1547*c8dee2aaSAndroid Build Coastguard Worker static SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; }
1548*c8dee2aaSAndroid Build Coastguard Worker
color_filter(const SkImage * image,SkColorFilter * colorFilter)1549*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkImage> color_filter(const SkImage* image,
1550*c8dee2aaSAndroid Build Coastguard Worker SkColorFilter* colorFilter) {
1551*c8dee2aaSAndroid Build Coastguard Worker auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(image->dimensions()));
1552*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(surface);
1553*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = surface->getCanvas();
1554*c8dee2aaSAndroid Build Coastguard Worker canvas->clear(SK_ColorTRANSPARENT);
1555*c8dee2aaSAndroid Build Coastguard Worker SkPaint paint;
1556*c8dee2aaSAndroid Build Coastguard Worker paint.setColorFilter(sk_ref_sp(colorFilter));
1557*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(image, 0, 0, SkSamplingOptions(), &paint);
1558*c8dee2aaSAndroid Build Coastguard Worker return surface->makeImageSnapshot();
1559*c8dee2aaSAndroid Build Coastguard Worker }
1560*c8dee2aaSAndroid Build Coastguard Worker
1561*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
1562*c8dee2aaSAndroid Build Coastguard Worker
is_integer(SkScalar x)1563*c8dee2aaSAndroid Build Coastguard Worker static bool is_integer(SkScalar x) {
1564*c8dee2aaSAndroid Build Coastguard Worker return x == SkScalarTruncToScalar(x);
1565*c8dee2aaSAndroid Build Coastguard Worker }
1566*c8dee2aaSAndroid Build Coastguard Worker
is_integral(const SkRect & r)1567*c8dee2aaSAndroid Build Coastguard Worker static bool is_integral(const SkRect& r) {
1568*c8dee2aaSAndroid Build Coastguard Worker return is_integer(r.left()) &&
1569*c8dee2aaSAndroid Build Coastguard Worker is_integer(r.top()) &&
1570*c8dee2aaSAndroid Build Coastguard Worker is_integer(r.right()) &&
1571*c8dee2aaSAndroid Build Coastguard Worker is_integer(r.bottom());
1572*c8dee2aaSAndroid Build Coastguard Worker }
1573*c8dee2aaSAndroid Build Coastguard Worker
internalDrawImageRect(SkKeyedImage imageSubset,const SkRect * src,const SkRect & dst,const SkSamplingOptions & sampling,const SkPaint & srcPaint,const SkMatrix & ctm)1574*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
1575*c8dee2aaSAndroid Build Coastguard Worker const SkRect* src,
1576*c8dee2aaSAndroid Build Coastguard Worker const SkRect& dst,
1577*c8dee2aaSAndroid Build Coastguard Worker const SkSamplingOptions& sampling,
1578*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& srcPaint,
1579*c8dee2aaSAndroid Build Coastguard Worker const SkMatrix& ctm) {
1580*c8dee2aaSAndroid Build Coastguard Worker if (this->hasEmptyClip()) {
1581*c8dee2aaSAndroid Build Coastguard Worker return;
1582*c8dee2aaSAndroid Build Coastguard Worker }
1583*c8dee2aaSAndroid Build Coastguard Worker if (!imageSubset) {
1584*c8dee2aaSAndroid Build Coastguard Worker return;
1585*c8dee2aaSAndroid Build Coastguard Worker }
1586*c8dee2aaSAndroid Build Coastguard Worker
1587*c8dee2aaSAndroid Build Coastguard Worker // First, figure out the src->dst transform and subset the image if needed.
1588*c8dee2aaSAndroid Build Coastguard Worker SkIRect bounds = imageSubset.image()->bounds();
1589*c8dee2aaSAndroid Build Coastguard Worker SkRect srcRect = src ? *src : SkRect::Make(bounds);
1590*c8dee2aaSAndroid Build Coastguard Worker SkMatrix transform = SkMatrix::RectToRect(srcRect, dst);
1591*c8dee2aaSAndroid Build Coastguard Worker if (src && *src != SkRect::Make(bounds)) {
1592*c8dee2aaSAndroid Build Coastguard Worker if (!srcRect.intersect(SkRect::Make(bounds))) {
1593*c8dee2aaSAndroid Build Coastguard Worker return;
1594*c8dee2aaSAndroid Build Coastguard Worker }
1595*c8dee2aaSAndroid Build Coastguard Worker srcRect.roundOut(&bounds);
1596*c8dee2aaSAndroid Build Coastguard Worker transform.preTranslate(SkIntToScalar(bounds.x()),
1597*c8dee2aaSAndroid Build Coastguard Worker SkIntToScalar(bounds.y()));
1598*c8dee2aaSAndroid Build Coastguard Worker if (bounds != imageSubset.image()->bounds()) {
1599*c8dee2aaSAndroid Build Coastguard Worker imageSubset = imageSubset.subset(bounds);
1600*c8dee2aaSAndroid Build Coastguard Worker }
1601*c8dee2aaSAndroid Build Coastguard Worker if (!imageSubset) {
1602*c8dee2aaSAndroid Build Coastguard Worker return;
1603*c8dee2aaSAndroid Build Coastguard Worker }
1604*c8dee2aaSAndroid Build Coastguard Worker }
1605*c8dee2aaSAndroid Build Coastguard Worker
1606*c8dee2aaSAndroid Build Coastguard Worker // If the image is opaque and the paint's alpha is too, replace
1607*c8dee2aaSAndroid Build Coastguard Worker // kSrc blendmode with kSrcOver. http://crbug.com/473572
1608*c8dee2aaSAndroid Build Coastguard Worker SkTCopyOnFirstWrite<SkPaint> paint(srcPaint);
1609*c8dee2aaSAndroid Build Coastguard Worker if (!paint->isSrcOver() &&
1610*c8dee2aaSAndroid Build Coastguard Worker imageSubset.image()->isOpaque() &&
1611*c8dee2aaSAndroid Build Coastguard Worker SkBlendFastPath::kSrcOver == CheckFastPath(*paint, false))
1612*c8dee2aaSAndroid Build Coastguard Worker {
1613*c8dee2aaSAndroid Build Coastguard Worker paint.writable()->setBlendMode(SkBlendMode::kSrcOver);
1614*c8dee2aaSAndroid Build Coastguard Worker }
1615*c8dee2aaSAndroid Build Coastguard Worker
1616*c8dee2aaSAndroid Build Coastguard Worker // Alpha-only images need to get their color from the shader, before
1617*c8dee2aaSAndroid Build Coastguard Worker // applying the colorfilter.
1618*c8dee2aaSAndroid Build Coastguard Worker if (imageSubset.image()->isAlphaOnly() && paint->getColorFilter()) {
1619*c8dee2aaSAndroid Build Coastguard Worker // must blend alpha image and shader before applying colorfilter.
1620*c8dee2aaSAndroid Build Coastguard Worker auto surface =
1621*c8dee2aaSAndroid Build Coastguard Worker SkSurfaces::Raster(SkImageInfo::MakeN32Premul(imageSubset.image()->dimensions()));
1622*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = surface->getCanvas();
1623*c8dee2aaSAndroid Build Coastguard Worker SkPaint tmpPaint;
1624*c8dee2aaSAndroid Build Coastguard Worker // In the case of alpha images with shaders, the shader's coordinate
1625*c8dee2aaSAndroid Build Coastguard Worker // system is the image's coordiantes.
1626*c8dee2aaSAndroid Build Coastguard Worker tmpPaint.setShader(sk_ref_sp(paint->getShader()));
1627*c8dee2aaSAndroid Build Coastguard Worker tmpPaint.setColor4f(paint->getColor4f(), nullptr);
1628*c8dee2aaSAndroid Build Coastguard Worker canvas->clear(0x00000000);
1629*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(imageSubset.image().get(), 0, 0, sampling, &tmpPaint);
1630*c8dee2aaSAndroid Build Coastguard Worker if (paint->getShader() != nullptr) {
1631*c8dee2aaSAndroid Build Coastguard Worker paint.writable()->setShader(nullptr);
1632*c8dee2aaSAndroid Build Coastguard Worker }
1633*c8dee2aaSAndroid Build Coastguard Worker imageSubset = SkKeyedImage(surface->makeImageSnapshot());
1634*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!imageSubset.image()->isAlphaOnly());
1635*c8dee2aaSAndroid Build Coastguard Worker }
1636*c8dee2aaSAndroid Build Coastguard Worker
1637*c8dee2aaSAndroid Build Coastguard Worker if (imageSubset.image()->isAlphaOnly()) {
1638*c8dee2aaSAndroid Build Coastguard Worker // The ColorFilter applies to the paint color/shader, not the alpha layer.
1639*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(nullptr == paint->getColorFilter());
1640*c8dee2aaSAndroid Build Coastguard Worker
1641*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> mask = alpha_image_to_greyscale_image(imageSubset.image().get());
1642*c8dee2aaSAndroid Build Coastguard Worker if (!mask) {
1643*c8dee2aaSAndroid Build Coastguard Worker return;
1644*c8dee2aaSAndroid Build Coastguard Worker }
1645*c8dee2aaSAndroid Build Coastguard Worker // PDF doesn't seem to allow masking vector graphics with an Image XObject.
1646*c8dee2aaSAndroid Build Coastguard Worker // Must mask with a Form XObject.
1647*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice();
1648*c8dee2aaSAndroid Build Coastguard Worker {
1649*c8dee2aaSAndroid Build Coastguard Worker SkCanvas canvas(maskDevice);
1650*c8dee2aaSAndroid Build Coastguard Worker // This clip prevents the mask image shader from covering
1651*c8dee2aaSAndroid Build Coastguard Worker // entire device if unnecessary.
1652*c8dee2aaSAndroid Build Coastguard Worker canvas.clipRect(this->cs().bounds(this->bounds()));
1653*c8dee2aaSAndroid Build Coastguard Worker canvas.concat(ctm);
1654*c8dee2aaSAndroid Build Coastguard Worker if (paint->getMaskFilter()) {
1655*c8dee2aaSAndroid Build Coastguard Worker SkPaint tmpPaint;
1656*c8dee2aaSAndroid Build Coastguard Worker tmpPaint.setShader(mask->makeShader(SkSamplingOptions(), transform));
1657*c8dee2aaSAndroid Build Coastguard Worker tmpPaint.setMaskFilter(sk_ref_sp(paint->getMaskFilter()));
1658*c8dee2aaSAndroid Build Coastguard Worker canvas.drawRect(dst, tmpPaint);
1659*c8dee2aaSAndroid Build Coastguard Worker } else {
1660*c8dee2aaSAndroid Build Coastguard Worker if (src && !is_integral(*src)) {
1661*c8dee2aaSAndroid Build Coastguard Worker canvas.clipRect(dst);
1662*c8dee2aaSAndroid Build Coastguard Worker }
1663*c8dee2aaSAndroid Build Coastguard Worker canvas.concat(transform);
1664*c8dee2aaSAndroid Build Coastguard Worker canvas.drawImage(mask, 0, 0);
1665*c8dee2aaSAndroid Build Coastguard Worker }
1666*c8dee2aaSAndroid Build Coastguard Worker }
1667*c8dee2aaSAndroid Build Coastguard Worker SkIRect maskDeviceBounds = maskDevice->cs().bounds(maskDevice->bounds()).roundOut();
1668*c8dee2aaSAndroid Build Coastguard Worker if (!ctm.isIdentity() && paint->getShader()) {
1669*c8dee2aaSAndroid Build Coastguard Worker transform_shader(paint.writable(), ctm); // Since we are using identity matrix.
1670*c8dee2aaSAndroid Build Coastguard Worker }
1671*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, &this->cs(), SkMatrix::I(), *paint);
1672*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
1673*c8dee2aaSAndroid Build Coastguard Worker return;
1674*c8dee2aaSAndroid Build Coastguard Worker }
1675*c8dee2aaSAndroid Build Coastguard Worker this->setGraphicState(SkPDFGraphicState::GetSMaskGraphicState(
1676*c8dee2aaSAndroid Build Coastguard Worker maskDevice->makeFormXObjectFromDevice(maskDeviceBounds, true), false,
1677*c8dee2aaSAndroid Build Coastguard Worker SkPDFGraphicState::kLuminosity_SMaskMode, fDocument), content.stream());
1678*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::AppendRectangle(SkRect::Make(this->size()), content.stream());
1679*c8dee2aaSAndroid Build Coastguard Worker SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPathFillType::kWinding, content.stream());
1680*c8dee2aaSAndroid Build Coastguard Worker this->clearMaskOnGraphicState(content.stream());
1681*c8dee2aaSAndroid Build Coastguard Worker return;
1682*c8dee2aaSAndroid Build Coastguard Worker }
1683*c8dee2aaSAndroid Build Coastguard Worker if (paint->getMaskFilter()) {
1684*c8dee2aaSAndroid Build Coastguard Worker paint.writable()->setShader(imageSubset.image()->makeShader(SkSamplingOptions(),
1685*c8dee2aaSAndroid Build Coastguard Worker transform));
1686*c8dee2aaSAndroid Build Coastguard Worker SkPath path = SkPath::Rect(dst); // handles non-integral clipping.
1687*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawPath(this->cs(), this->localToDevice(), path, *paint, true);
1688*c8dee2aaSAndroid Build Coastguard Worker return;
1689*c8dee2aaSAndroid Build Coastguard Worker }
1690*c8dee2aaSAndroid Build Coastguard Worker transform.postConcat(ctm);
1691*c8dee2aaSAndroid Build Coastguard Worker
1692*c8dee2aaSAndroid Build Coastguard Worker bool needToRestore = false;
1693*c8dee2aaSAndroid Build Coastguard Worker if (src && !is_integral(*src)) {
1694*c8dee2aaSAndroid Build Coastguard Worker // Need sub-pixel clipping to fix https://bug.skia.org/4374
1695*c8dee2aaSAndroid Build Coastguard Worker this->cs().save();
1696*c8dee2aaSAndroid Build Coastguard Worker this->cs().clipRect(dst, ctm, SkClipOp::kIntersect, true);
1697*c8dee2aaSAndroid Build Coastguard Worker needToRestore = true;
1698*c8dee2aaSAndroid Build Coastguard Worker }
1699*c8dee2aaSAndroid Build Coastguard Worker SK_AT_SCOPE_EXIT(if (needToRestore) { this->cs().restore(); });
1700*c8dee2aaSAndroid Build Coastguard Worker
1701*c8dee2aaSAndroid Build Coastguard Worker SkMatrix matrix = transform;
1702*c8dee2aaSAndroid Build Coastguard Worker
1703*c8dee2aaSAndroid Build Coastguard Worker // Rasterize the bitmap using perspective in a new bitmap.
1704*c8dee2aaSAndroid Build Coastguard Worker if (transform.hasPerspective()) {
1705*c8dee2aaSAndroid Build Coastguard Worker // Transform the bitmap in the new space, without taking into
1706*c8dee2aaSAndroid Build Coastguard Worker // account the initial transform.
1707*c8dee2aaSAndroid Build Coastguard Worker SkRect imageBounds = SkRect::Make(imageSubset.image()->bounds());
1708*c8dee2aaSAndroid Build Coastguard Worker SkPath perspectiveOutline = SkPath::Rect(imageBounds).makeTransform(transform);
1709*c8dee2aaSAndroid Build Coastguard Worker
1710*c8dee2aaSAndroid Build Coastguard Worker // Retrieve the bounds of the new shape.
1711*c8dee2aaSAndroid Build Coastguard Worker SkRect outlineBounds = perspectiveOutline.getBounds();
1712*c8dee2aaSAndroid Build Coastguard Worker if (!outlineBounds.intersect(SkRect::Make(this->devClipBounds()))) {
1713*c8dee2aaSAndroid Build Coastguard Worker return;
1714*c8dee2aaSAndroid Build Coastguard Worker }
1715*c8dee2aaSAndroid Build Coastguard Worker
1716*c8dee2aaSAndroid Build Coastguard Worker // Transform the bitmap in the new space to the final space, to account for DPI
1717*c8dee2aaSAndroid Build Coastguard Worker SkRect physicalBounds = fInitialTransform.mapRect(outlineBounds);
1718*c8dee2aaSAndroid Build Coastguard Worker SkScalar scaleX = physicalBounds.width() / outlineBounds.width();
1719*c8dee2aaSAndroid Build Coastguard Worker SkScalar scaleY = physicalBounds.height() / outlineBounds.height();
1720*c8dee2aaSAndroid Build Coastguard Worker
1721*c8dee2aaSAndroid Build Coastguard Worker // TODO(edisonn): A better approach would be to use a bitmap shader
1722*c8dee2aaSAndroid Build Coastguard Worker // (in clamp mode) and draw a rect over the entire bounding box. Then
1723*c8dee2aaSAndroid Build Coastguard Worker // intersect perspectiveOutline to the clip. That will avoid introducing
1724*c8dee2aaSAndroid Build Coastguard Worker // alpha to the image while still giving good behavior at the edge of
1725*c8dee2aaSAndroid Build Coastguard Worker // the image. Avoiding alpha will reduce the pdf size and generation
1726*c8dee2aaSAndroid Build Coastguard Worker // CPU time some.
1727*c8dee2aaSAndroid Build Coastguard Worker
1728*c8dee2aaSAndroid Build Coastguard Worker SkISize wh = rect_to_size(physicalBounds).toCeil();
1729*c8dee2aaSAndroid Build Coastguard Worker
1730*c8dee2aaSAndroid Build Coastguard Worker auto surface = SkSurfaces::Raster(SkImageInfo::MakeN32Premul(wh));
1731*c8dee2aaSAndroid Build Coastguard Worker if (!surface) {
1732*c8dee2aaSAndroid Build Coastguard Worker return;
1733*c8dee2aaSAndroid Build Coastguard Worker }
1734*c8dee2aaSAndroid Build Coastguard Worker SkCanvas* canvas = surface->getCanvas();
1735*c8dee2aaSAndroid Build Coastguard Worker canvas->clear(SK_ColorTRANSPARENT);
1736*c8dee2aaSAndroid Build Coastguard Worker
1737*c8dee2aaSAndroid Build Coastguard Worker SkScalar deltaX = outlineBounds.left();
1738*c8dee2aaSAndroid Build Coastguard Worker SkScalar deltaY = outlineBounds.top();
1739*c8dee2aaSAndroid Build Coastguard Worker
1740*c8dee2aaSAndroid Build Coastguard Worker SkMatrix offsetMatrix = transform;
1741*c8dee2aaSAndroid Build Coastguard Worker offsetMatrix.postTranslate(-deltaX, -deltaY);
1742*c8dee2aaSAndroid Build Coastguard Worker offsetMatrix.postScale(scaleX, scaleY);
1743*c8dee2aaSAndroid Build Coastguard Worker
1744*c8dee2aaSAndroid Build Coastguard Worker // Translate the draw in the new canvas, so we perfectly fit the
1745*c8dee2aaSAndroid Build Coastguard Worker // shape in the bitmap.
1746*c8dee2aaSAndroid Build Coastguard Worker canvas->setMatrix(offsetMatrix);
1747*c8dee2aaSAndroid Build Coastguard Worker canvas->drawImage(imageSubset.image(), 0, 0);
1748*c8dee2aaSAndroid Build Coastguard Worker
1749*c8dee2aaSAndroid Build Coastguard Worker // In the new space, we use the identity matrix translated
1750*c8dee2aaSAndroid Build Coastguard Worker // and scaled to reflect DPI.
1751*c8dee2aaSAndroid Build Coastguard Worker matrix.setScale(1 / scaleX, 1 / scaleY);
1752*c8dee2aaSAndroid Build Coastguard Worker matrix.postTranslate(deltaX, deltaY);
1753*c8dee2aaSAndroid Build Coastguard Worker
1754*c8dee2aaSAndroid Build Coastguard Worker imageSubset = SkKeyedImage(surface->makeImageSnapshot());
1755*c8dee2aaSAndroid Build Coastguard Worker if (!imageSubset) {
1756*c8dee2aaSAndroid Build Coastguard Worker return;
1757*c8dee2aaSAndroid Build Coastguard Worker }
1758*c8dee2aaSAndroid Build Coastguard Worker }
1759*c8dee2aaSAndroid Build Coastguard Worker
1760*c8dee2aaSAndroid Build Coastguard Worker SkMatrix scaled;
1761*c8dee2aaSAndroid Build Coastguard Worker // Adjust for origin flip.
1762*c8dee2aaSAndroid Build Coastguard Worker scaled.setScale(SK_Scalar1, -SK_Scalar1);
1763*c8dee2aaSAndroid Build Coastguard Worker scaled.postTranslate(0, SK_Scalar1);
1764*c8dee2aaSAndroid Build Coastguard Worker // Scale the image up from 1x1 to WxH.
1765*c8dee2aaSAndroid Build Coastguard Worker SkIRect subset = imageSubset.image()->bounds();
1766*c8dee2aaSAndroid Build Coastguard Worker scaled.postScale(SkIntToScalar(subset.width()),
1767*c8dee2aaSAndroid Build Coastguard Worker SkIntToScalar(subset.height()));
1768*c8dee2aaSAndroid Build Coastguard Worker scaled.postConcat(matrix);
1769*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, &this->cs(), scaled, *paint);
1770*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
1771*c8dee2aaSAndroid Build Coastguard Worker return;
1772*c8dee2aaSAndroid Build Coastguard Worker }
1773*c8dee2aaSAndroid Build Coastguard Worker SkPath shape = SkPath::Rect(SkRect::Make(subset)).makeTransform(matrix);
1774*c8dee2aaSAndroid Build Coastguard Worker if (content.needShape()) {
1775*c8dee2aaSAndroid Build Coastguard Worker content.setShape(shape);
1776*c8dee2aaSAndroid Build Coastguard Worker }
1777*c8dee2aaSAndroid Build Coastguard Worker if (!content.needSource()) {
1778*c8dee2aaSAndroid Build Coastguard Worker return;
1779*c8dee2aaSAndroid Build Coastguard Worker }
1780*c8dee2aaSAndroid Build Coastguard Worker
1781*c8dee2aaSAndroid Build Coastguard Worker if (SkColorFilter* colorFilter = paint->getColorFilter()) {
1782*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkImage> img = color_filter(imageSubset.image().get(), colorFilter);
1783*c8dee2aaSAndroid Build Coastguard Worker imageSubset = SkKeyedImage(std::move(img));
1784*c8dee2aaSAndroid Build Coastguard Worker if (!imageSubset) {
1785*c8dee2aaSAndroid Build Coastguard Worker return;
1786*c8dee2aaSAndroid Build Coastguard Worker }
1787*c8dee2aaSAndroid Build Coastguard Worker // TODO(halcanary): de-dupe this by caching filtered images.
1788*c8dee2aaSAndroid Build Coastguard Worker // (maybe in the resource cache?)
1789*c8dee2aaSAndroid Build Coastguard Worker }
1790*c8dee2aaSAndroid Build Coastguard Worker
1791*c8dee2aaSAndroid Build Coastguard Worker SkBitmapKey key = imageSubset.key();
1792*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference* pdfimagePtr = fDocument->fPDFBitmapMap.find(key);
1793*c8dee2aaSAndroid Build Coastguard Worker SkPDFIndirectReference pdfimage = pdfimagePtr ? *pdfimagePtr : SkPDFIndirectReference();
1794*c8dee2aaSAndroid Build Coastguard Worker if (!pdfimagePtr) {
1795*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(imageSubset);
1796*c8dee2aaSAndroid Build Coastguard Worker pdfimage = SkPDFSerializeImage(imageSubset.image().get(), fDocument,
1797*c8dee2aaSAndroid Build Coastguard Worker fDocument->metadata().fEncodingQuality);
1798*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
1799*c8dee2aaSAndroid Build Coastguard Worker fDocument->fPDFBitmapMap.set(key, pdfimage);
1800*c8dee2aaSAndroid Build Coastguard Worker }
1801*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(pdfimage != SkPDFIndirectReference());
1802*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObject(pdfimage, content.stream(), &shape);
1803*c8dee2aaSAndroid Build Coastguard Worker }
1804*c8dee2aaSAndroid Build Coastguard Worker
1805*c8dee2aaSAndroid Build Coastguard Worker ///////////////////////////////////////////////////////////////////////////////////////////////////
1806*c8dee2aaSAndroid Build Coastguard Worker
1807*c8dee2aaSAndroid Build Coastguard Worker
drawDevice(SkDevice * device,const SkSamplingOptions & sampling,const SkPaint & paint)1808*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawDevice(SkDevice* device, const SkSamplingOptions& sampling,
1809*c8dee2aaSAndroid Build Coastguard Worker const SkPaint& paint) {
1810*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!paint.getImageFilter());
1811*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!paint.getMaskFilter());
1812*c8dee2aaSAndroid Build Coastguard Worker
1813*c8dee2aaSAndroid Build Coastguard Worker // Check if SkPDFDevice::createDevice returned an SkBitmapDevice.
1814*c8dee2aaSAndroid Build Coastguard Worker // SkPDFDevice::createDevice creates SkBitmapDevice for color filters.
1815*c8dee2aaSAndroid Build Coastguard Worker // Image filters generally go through makeSpecial and drawSpecial.
1816*c8dee2aaSAndroid Build Coastguard Worker SkPixmap pmap;
1817*c8dee2aaSAndroid Build Coastguard Worker if (device->peekPixels(&pmap)) {
1818*c8dee2aaSAndroid Build Coastguard Worker this->SkClipStackDevice::drawDevice(device, sampling, paint);
1819*c8dee2aaSAndroid Build Coastguard Worker return;
1820*c8dee2aaSAndroid Build Coastguard Worker }
1821*c8dee2aaSAndroid Build Coastguard Worker
1822*c8dee2aaSAndroid Build Coastguard Worker // Otherwise SkPDFDevice::createDevice() creates SkPDFDevice subclasses.
1823*c8dee2aaSAndroid Build Coastguard Worker SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
1824*c8dee2aaSAndroid Build Coastguard Worker
1825*c8dee2aaSAndroid Build Coastguard Worker if (pdfDevice->isContentEmpty()) {
1826*c8dee2aaSAndroid Build Coastguard Worker return;
1827*c8dee2aaSAndroid Build Coastguard Worker }
1828*c8dee2aaSAndroid Build Coastguard Worker
1829*c8dee2aaSAndroid Build Coastguard Worker SkMatrix matrix = device->getRelativeTransform(*this);
1830*c8dee2aaSAndroid Build Coastguard Worker ScopedContentEntry content(this, &this->cs(), matrix, paint);
1831*c8dee2aaSAndroid Build Coastguard Worker if (!content) {
1832*c8dee2aaSAndroid Build Coastguard Worker return;
1833*c8dee2aaSAndroid Build Coastguard Worker }
1834*c8dee2aaSAndroid Build Coastguard Worker SkPath shape = SkPath::Rect(SkRect::Make(device->imageInfo().dimensions()));
1835*c8dee2aaSAndroid Build Coastguard Worker shape.transform(matrix);
1836*c8dee2aaSAndroid Build Coastguard Worker if (content.needShape()) {
1837*c8dee2aaSAndroid Build Coastguard Worker content.setShape(shape);
1838*c8dee2aaSAndroid Build Coastguard Worker }
1839*c8dee2aaSAndroid Build Coastguard Worker if (!content.needSource()) {
1840*c8dee2aaSAndroid Build Coastguard Worker return;
1841*c8dee2aaSAndroid Build Coastguard Worker }
1842*c8dee2aaSAndroid Build Coastguard Worker // This XObject may contain its own marks, which are hidden if emitted inside an outer mark.
1843*c8dee2aaSAndroid Build Coastguard Worker // If it does have its own marks we need to pause the current mark and then re-set it after.
1844*c8dee2aaSAndroid Build Coastguard Worker int currentStructElemId = fMarkManager.elemId();
1845*c8dee2aaSAndroid Build Coastguard Worker if (pdfDevice->fMarkManager.madeMarks()) {
1846*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.setNextMarksElemId(0);
1847*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.beginMark();
1848*c8dee2aaSAndroid Build Coastguard Worker }
1849*c8dee2aaSAndroid Build Coastguard Worker this->drawFormXObject(pdfDevice->makeFormXObjectFromDevice(), content.stream(), &shape);
1850*c8dee2aaSAndroid Build Coastguard Worker fMarkManager.setNextMarksElemId(currentStructElemId);
1851*c8dee2aaSAndroid Build Coastguard Worker }
1852*c8dee2aaSAndroid Build Coastguard Worker
drawSpecial(SkSpecialImage * srcImg,const SkMatrix & localToDevice,const SkSamplingOptions & sampling,const SkPaint & paint,SkCanvas::SrcRectConstraint)1853*c8dee2aaSAndroid Build Coastguard Worker void SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, const SkMatrix& localToDevice,
1854*c8dee2aaSAndroid Build Coastguard Worker const SkSamplingOptions& sampling, const SkPaint& paint,
1855*c8dee2aaSAndroid Build Coastguard Worker SkCanvas::SrcRectConstraint) {
1856*c8dee2aaSAndroid Build Coastguard Worker if (this->hasEmptyClip()) {
1857*c8dee2aaSAndroid Build Coastguard Worker return;
1858*c8dee2aaSAndroid Build Coastguard Worker }
1859*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!srcImg->isGaneshBacked() && !srcImg->isGraphiteBacked());
1860*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!paint.getMaskFilter() && !paint.getImageFilter());
1861*c8dee2aaSAndroid Build Coastguard Worker
1862*c8dee2aaSAndroid Build Coastguard Worker SkBitmap resultBM;
1863*c8dee2aaSAndroid Build Coastguard Worker if (SkSpecialImages::AsBitmap(srcImg, &resultBM)) {
1864*c8dee2aaSAndroid Build Coastguard Worker auto r = SkRect::MakeWH(resultBM.width(), resultBM.height());
1865*c8dee2aaSAndroid Build Coastguard Worker this->internalDrawImageRect(SkKeyedImage(resultBM), nullptr, r, sampling, paint,
1866*c8dee2aaSAndroid Build Coastguard Worker localToDevice);
1867*c8dee2aaSAndroid Build Coastguard Worker }
1868*c8dee2aaSAndroid Build Coastguard Worker }
1869*c8dee2aaSAndroid Build Coastguard Worker
makeSpecial(const SkBitmap & bitmap)1870*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkBitmap& bitmap) {
1871*c8dee2aaSAndroid Build Coastguard Worker return SkSpecialImages::MakeFromRaster(bitmap.bounds(), bitmap, this->surfaceProps());
1872*c8dee2aaSAndroid Build Coastguard Worker }
1873*c8dee2aaSAndroid Build Coastguard Worker
makeSpecial(const SkImage * image)1874*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) {
1875*c8dee2aaSAndroid Build Coastguard Worker return SkSpecialImages::MakeFromRaster(
1876*c8dee2aaSAndroid Build Coastguard Worker image->bounds(), image->makeNonTextureImage(), this->surfaceProps());
1877*c8dee2aaSAndroid Build Coastguard Worker }
1878