xref: /aosp_15_r20/external/skia/src/pdf/SkPDFDevice.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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