1fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/*
2fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Copyright 2011 Google Inc.
3fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot *
4fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Use of this source code is governed by a BSD-style license that can be
5fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * found in the LICENSE file.
6fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
7fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
8fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFDevice.h"
9fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
10fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkAdvancedTypefaceMetrics.h"
11fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkAnnotationKeys.h"
12fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkBitmapDevice.h"
13fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkBitmapKey.h"
14fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkCanvas.h"
15fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkClipOpPriv.h"
16fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkColor.h"
17fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkColorFilter.h"
18fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkDraw.h"
19fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkDrawFilter.h"
20fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkGlyphCache.h"
21fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkImageFilterCache.h"
22fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkJpegEncoder.h"
23fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkMakeUnique.h"
24fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkMaskFilterBase.h"
25fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFBitmap.h"
26fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFCanon.h"
27fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFDocument.h"
28fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFFont.h"
29fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFFormXObject.h"
30fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFGraphicState.h"
31fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFResourceDict.h"
32fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFShader.h"
33fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFTypes.h"
34fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPDFUtils.h"
35fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPath.h"
36fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPathEffect.h"
37fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPathOps.h"
38fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkPixelRef.h"
39fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkRRect.h"
40fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkRasterClip.h"
41fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkScopeExit.h"
42fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkString.h"
43fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSurface.h"
44fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTemplates.h"
45fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTextBlobRunIterator.h"
46fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkTextFormatParams.h"
47fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkUtils.h"
48fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkXfermodeInterpretation.h"
49fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
50fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#ifndef SK_PDF_MASK_QUALITY
51fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // If MASK_QUALITY is in [0,100], will be used for JpegEncoder.
52fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Otherwise, just encode masks losslessly.
53fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    #define SK_PDF_MASK_QUALITY 50
54fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Since these masks are used for blurry shadows, we shouldn't need
55fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // high quality.  Raise this value if your shadows have visible JPEG
56fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // artifacts.
57fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // If SkJpegEncoder::Encode fails, we will fall back to the lossless
58fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // encoding.
59fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
60fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
61fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// Utility functions
62fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
63fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// This function destroys the mask and either frees or takes the pixels.
64fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkImage> mask_to_greyscale_image(SkMask* mask) {
65fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkImage> img;
66fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPixmap pm(SkImageInfo::Make(mask->fBounds.width(), mask->fBounds.height(),
67fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                  kGray_8_SkColorType, kOpaque_SkAlphaType),
68fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                mask->fImage, mask->fRowBytes);
69fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const int imgQuality = SK_PDF_MASK_QUALITY;
70fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (imgQuality <= 100 && imgQuality >= 0) {
71fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkDynamicMemoryWStream buffer;
72fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkJpegEncoder::Options jpegOptions;
73fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        jpegOptions.fQuality = imgQuality;
74fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (SkJpegEncoder::Encode(&buffer, pm, jpegOptions)) {
75fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            img = SkImage::MakeFromEncoded(buffer.detachAsData());
76fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(img);
77fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (img) {
78fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkMask::FreeImage(mask->fImage);
79fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
80fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
81fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
82fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!img) {
83fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        img = SkImage::MakeFromRaster(pm, [](const void* p, void*) { SkMask::FreeImage((void*)p); },
84fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                      nullptr);
85fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
86fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    *mask = SkMask();  // destructive;
87fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return img;
88fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
89fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
90fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkImage> alpha_image_to_greyscale_image(const SkImage* mask) {
91fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int w = mask->width(), h = mask->height();
92fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBitmap greyBitmap;
93fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    greyBitmap.allocPixels(SkImageInfo::Make(w, h, kGray_8_SkColorType, kOpaque_SkAlphaType));
94fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!mask->readPixels(SkImageInfo::MakeA8(w, h),
95fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          greyBitmap.getPixels(), greyBitmap.rowBytes(), 0, 0)) {
96fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
97fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
98fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkImage::MakeFromBitmap(greyBitmap);
99fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void draw_points(SkCanvas::PointMode mode,
102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        size_t count,
103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        const SkPoint* points,
104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        const SkPaint& paint,
105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        const SkIRect& bounds,
106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        const SkMatrix& ctm,
107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkBaseDevice* device) {
108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRasterClip rc(bounds);
109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDraw draw;
110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    draw.fDst = SkPixmap(SkImageInfo::MakeUnknown(bounds.right(), bounds.bottom()), nullptr, 0);
111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    draw.fMatrix = &ctm;
112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    draw.fRC = &rc;
113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    draw.drawPoints(mode, count, points, paint, device);
114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// If the paint will definitely draw opaquely, replace kSrc with
117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// kSrcOver.  http://crbug.com/473572
118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void replace_srcmode_on_opaque_paint(SkPaint* paint) {
119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (kSrcOver_SkXfermodeInterpretation == SkInterpretXfermode(*paint, false)) {
120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setBlendMode(SkBlendMode::kSrcOver);
121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// A shader's matrix is:  CTMM x LocalMatrix x WrappingLocalMatrix.  We want to
125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// switch to device space, where CTM = I, while keeping the original behavior.
126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//               I * LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//                   LocalMatrix * NewWrappingMatrix = CTM * LocalMatrix
129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//  InvLocalMatrix * LocalMatrix * NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//                                 NewWrappingMatrix = InvLocalMatrix * CTM * LocalMatrix
131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot//
132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void transform_shader(SkPaint* paint, const SkMatrix& ctm) {
133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix lm = SkPDFUtils::GetShaderLocalMatrix(paint->getShader());
134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix lmInv;
135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (lm.invert(&lmInv)) {
136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkMatrix m = SkMatrix::Concat(SkMatrix::Concat(lmInv, ctm), lm);
137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setShader(paint->getShader()->makeWithLocalMatrix(m));
138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void emit_pdf_color(SkColor color, SkWStream* result) {
142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(SkColorGetA(color) == 0xFF);  // We handle alpha elsewhere.
143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendColorComponent(SkColorGetR(color), result);
144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    result->writeText(" ");
145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendColorComponent(SkColorGetG(color), result);
146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    result->writeText(" ");
147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendColorComponent(SkColorGetB(color), result);
148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    result->writeText(" ");
149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic SkPaint calculate_text_paint(const SkPaint& paint) {
152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint result = paint;
153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (result.isFakeBoldText()) {
154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar fakeBoldScale = SkScalarInterpFunc(result.getTextSize(),
155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                    kStdFakeBoldInterpKeys,
156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                    kStdFakeBoldInterpValues,
157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                    kStdFakeBoldInterpLength);
158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar width = result.getTextSize() * fakeBoldScale;
159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (result.getStyle() == SkPaint::kFill_Style) {
160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            result.setStyle(SkPaint::kStrokeAndFill_Style);
161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            width += result.getStrokeWidth();
163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        result.setStrokeWidth(width);
165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return result;
167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// If the paint has a color filter, apply the color filter to the shader or the
171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// paint color.  Remove the color filter.
172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid remove_color_filter(SkPaint* paint) {
173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkColorFilter* cf = paint->getColorFilter()) {
174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (SkShader* shader = paint->getShader()) {
175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            paint->setShader(shader->makeWithColorFilter(paint->refColorFilter()));
176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            paint->setColor(cf->filterColor(paint->getColor()));
178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint->setColorFilter(nullptr);
180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkPDFDevice::GraphicStateEntry::GraphicStateEntry()
184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : fColor(SK_ColorBLACK)
185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fTextScaleX(SK_Scalar1)
186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fTextFill(SkPaint::kFill_Style)
187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fShaderIndex(-1)
188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fGraphicStateIndex(-1) {
189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fMatrix.reset();
190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkPDFDevice::GraphicStateEntry::compareInitialState(
193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const GraphicStateEntry& cur) {
194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return fColor == cur.fColor &&
195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           fShaderIndex == cur.fShaderIndex &&
196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           fGraphicStateIndex == cur.fGraphicStateIndex &&
197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           fMatrix == cur.fMatrix &&
198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           fClipStack == cur.fClipStack &&
199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           (fTextScaleX == 0 ||
200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot               (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill));
201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass GraphicStackState {
204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GraphicStackState(const SkClipStack& existingClipStack,
206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                      SkWStream* contentStream)
207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            : fStackDepth(0),
208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot              fContentStream(contentStream) {
209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fEntries[0].fClipStack = existingClipStack;
210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void updateClip(const SkClipStack& clipStack,
213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    const SkPoint& translation, const SkRect& bounds);
214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void updateMatrix(const SkMatrix& matrix);
215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void updateDrawingState(const SkPDFDevice::GraphicStateEntry& state);
216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void drainStack();
218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void push();
221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void pop();
222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFDevice::GraphicStateEntry* currentEntry() { return &fEntries[fStackDepth]; }
223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Conservative limit on save depth, see impl. notes in PDF 1.4 spec.
225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static const int kMaxStackDepth = 12;
226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFDevice::GraphicStateEntry fEntries[kMaxStackDepth + 1];
227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int fStackDepth;
228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkWStream* fContentStream;
229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GraphicStackState::drainStack() {
232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (fStackDepth) {
233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pop();
234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GraphicStackState::push() {
238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(fStackDepth < kMaxStackDepth);
239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fContentStream->writeText("q\n");
240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fStackDepth++;
241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fEntries[fStackDepth] = fEntries[fStackDepth - 1];
242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GraphicStackState::pop() {
245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(fStackDepth > 0);
246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fContentStream->writeText("Q\n");
247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fStackDepth--;
248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/* Calculate an inverted path's equivalent non-inverted path, given the
251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * canvas bounds.
252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * outPath may alias with invPath (since this is supported by PathOps).
253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool calculate_inverse_path(const SkRect& bounds, const SkPath& invPath,
255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   SkPath* outPath) {
256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(invPath.isInverseFillType());
257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath clipPath;
259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    clipPath.addRect(bounds);
260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return Op(clipPath, invPath, kIntersect_SkPathOp, outPath);
262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool apply_clip(SkClipOp op, const SkPath& u, const SkPath& v, SkPath* r)  {
265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (op) {
266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkClipOp::kDifference:
267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Op(u, v, kDifference_SkPathOp, r);
268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkClipOp::kIntersect:
269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Op(u, v, kIntersect_SkPathOp, r);
270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS
271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkClipOp::kUnion_deprecated:
272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Op(u, v, kUnion_SkPathOp, r);
273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkClipOp::kXOR_deprecated:
274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Op(u, v, kXOR_SkPathOp, r);
275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkClipOp::kReverseDifference_deprecated:
276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Op(u, v, kReverseDifference_SkPathOp, r);
277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkClipOp::kReplace_deprecated:
278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *r = v;
279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return true;
280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#endif
281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        default:
282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/* Uses Path Ops to calculate a vector SkPath clip from a clip stack.
287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Returns true if successful, or false if not successful.
288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * If successful, the resulting clip is stored in outClipPath.
289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * If not successful, outClipPath is undefined, and a fallback method
290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * should be used.
291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool get_clip_stack_path(const SkMatrix& transform,
293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                const SkClipStack& clipStack,
294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                const SkRect& bounds,
295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                SkPath* outClipPath) {
296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    outClipPath->reset();
297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    outClipPath->setFillType(SkPath::kInverseWinding_FillType);
298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkClipStack::Element* clipEntry;
300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkClipStack::Iter iter;
301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    iter.reset(clipStack, SkClipStack::Iter::kBottom_IterStart);
302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath entryPath;
304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (SkClipStack::Element::DeviceSpaceType::kEmpty == clipEntry->getDeviceSpaceType()) {
305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            outClipPath->reset();
306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            outClipPath->setFillType(SkPath::kInverseWinding_FillType);
307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            continue;
308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            clipEntry->asDeviceSpacePath(&entryPath);
310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entryPath.transform(transform);
312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!apply_clip(clipEntry->getOp(), *outClipPath, entryPath, outClipPath)) {
313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (outClipPath->isInverseFillType()) {
318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The bounds are slightly outset to ensure this is correct in the
319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // face of floating-point accuracy and possible SkRegion bitmap
320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // approximations.
321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect clipBounds = bounds;
322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        clipBounds.outset(SK_Scalar1, SK_Scalar1);
323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!calculate_inverse_path(clipBounds, *outClipPath, outClipPath)) {
324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// TODO(vandebo): Take advantage of SkClipStack::getSaveCount(), the PDF
331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// graphic state stack, and the fact that we can know all the clips used
332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// on the page to optimize this.
333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GraphicStackState::updateClip(const SkClipStack& clipStack,
334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   const SkPoint& translation,
335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   const SkRect& bounds) {
336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (clipStack == currentEntry()->fClipStack) {
337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    while (fStackDepth > 0) {
341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pop();
342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (clipStack == currentEntry()->fClipStack) {
343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    push();
347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    currentEntry()->fClipStack = clipStack;
349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix transform;
351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    transform.setTranslate(translation.fX, translation.fY);
352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath clipPath;
354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (get_clip_stack_path(transform, clipStack, bounds, &clipPath)) {
355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFUtils::EmitPath(clipPath, SkPaint::kFill_Style, fContentStream);
356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath::FillType clipFill = clipPath.getFillType();
357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        NOT_IMPLEMENTED(clipFill == SkPath::kInverseEvenOdd_FillType, false);
358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        NOT_IMPLEMENTED(clipFill == SkPath::kInverseWinding_FillType, false);
359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (clipFill == SkPath::kEvenOdd_FillType) {
360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentStream->writeText("W* n\n");
361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentStream->writeText("W n\n");
363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // If Op() fails (pathological case; e.g. input values are
366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // extremely large or NaN), emit no clip at all.
367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GraphicStackState::updateMatrix(const SkMatrix& matrix) {
370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (matrix == currentEntry()->fMatrix) {
371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (currentEntry()->fMatrix.getType() != SkMatrix::kIdentity_Mask) {
375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fStackDepth > 0);
376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fEntries[fStackDepth].fClipStack ==
377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 fEntries[fStackDepth -1].fClipStack);
378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pop();
379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(currentEntry()->fMatrix.getType() == SkMatrix::kIdentity_Mask);
381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (matrix.getType() == SkMatrix::kIdentity_Mask) {
383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    push();
387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendTransform(matrix, fContentStream);
388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    currentEntry()->fMatrix = matrix;
389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid GraphicStackState::updateDrawingState(const SkPDFDevice::GraphicStateEntry& state) {
392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // PDF treats a shader as a color, so we only set one or the other.
393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (state.fShaderIndex >= 0) {
394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (state.fShaderIndex != currentEntry()->fShaderIndex) {
395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::ApplyPattern(state.fShaderIndex, fContentStream);
396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            currentEntry()->fShaderIndex = state.fShaderIndex;
397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (state.fColor != currentEntry()->fColor ||
400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                currentEntry()->fShaderIndex >= 0) {
401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            emit_pdf_color(state.fColor, fContentStream);
402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentStream->writeText("RG ");
403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            emit_pdf_color(state.fColor, fContentStream);
404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentStream->writeText("rg\n");
405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            currentEntry()->fColor = state.fColor;
406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            currentEntry()->fShaderIndex = -1;
407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (state.fGraphicStateIndex != currentEntry()->fGraphicStateIndex) {
411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFUtils::ApplyGraphicState(state.fGraphicStateIndex, fContentStream);
412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        currentEntry()->fGraphicStateIndex = state.fGraphicStateIndex;
413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (state.fTextScaleX) {
416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (state.fTextScaleX != currentEntry()->fTextScaleX) {
417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkScalar pdfScale = state.fTextScaleX * 100;
418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::AppendScalar(pdfScale, fContentStream);
419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentStream->writeText(" Tz\n");
420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            currentEntry()->fTextScaleX = state.fTextScaleX;
421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (state.fTextFill != currentEntry()->fTextFill) {
423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            static_assert(SkPaint::kFill_Style == 0, "enum_must_match_value");
424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            static_assert(SkPaint::kStroke_Style == 1, "enum_must_match_value");
425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            static_assert(SkPaint::kStrokeAndFill_Style == 2, "enum_must_match_value");
426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentStream->writeDecAsText(state.fTextFill);
427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentStream->writeText(" Tr\n");
428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            currentEntry()->fTextFill = state.fTextFill;
429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool not_supported_for_layers(const SkPaint& layerPaint) {
434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // PDF does not support image filters, so render them on CPU.
435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Note that this rendering is done at "screen" resolution (100dpi), not
436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // printer resolution.
437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO: It may be possible to express some filters natively using PDF
438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // to improve quality and file size (https://bug.skia.org/3043)
439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO: should we return true if there is a colorfilter?
441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return layerPaint.getImageFilter() != nullptr;
442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkBaseDevice* SkPDFDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (layerPaint && not_supported_for_layers(*layerPaint)) {
446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // need to return a raster device, which we will detect in drawDevice()
447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return SkBitmapDevice::Create(cinfo.fInfo, SkSurfaceProps(0, kUnknown_SkPixelGeometry));
448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return new SkPDFDevice(cinfo.fInfo.dimensions(), fDocument);
450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkPDFCanon* SkPDFDevice::getCanon() const { return fDocument->canon(); }
453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// A helper class to automatically finish a ContentEntry at the end of a
455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot// drawing method and maintain the state needed between set up and finish.
456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass ScopedContentEntry {
457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry(SkPDFDevice* device,
459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       const SkClipStack& clipStack,
460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       const SkMatrix& matrix,
461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       const SkPaint& paint,
462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       bool hasText = false)
463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : fDevice(device)
464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fContentEntry(nullptr)
465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fBlendMode(SkBlendMode::kSrcOver)
466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fDstFormXObject(nullptr)
467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (matrix.hasPerspective()) {
469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            NOT_IMPLEMENTED(!matrix.hasPerspective(), false);
470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fBlendMode = paint.getBlendMode();
473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fContentEntry =
474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fDevice->setUpContentEntry(clipStack, matrix, paint, hasText, &fDstFormXObject);
475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry(SkPDFDevice* dev, const SkPaint& paint, bool hasText = false)
477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : ScopedContentEntry(dev, dev->cs(), dev->ctm(), paint, hasText) {}
478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ~ScopedContentEntry() {
480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fContentEntry) {
481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPath* shape = &fShape;
482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (shape->isEmpty()) {
483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                shape = nullptr;
484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fDevice->finishContentEntry(fBlendMode, std::move(fDstFormXObject), shape);
486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFDevice::ContentEntry* entry() { return fContentEntry; }
490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream* stream() { return &fContentEntry->fContent; }
491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    /* Returns true when we explicitly need the shape of the drawing. */
493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool needShape() {
494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        switch (fBlendMode) {
495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kClear:
496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kSrc:
497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kSrcIn:
498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kSrcOut:
499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kDstIn:
500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kDstOut:
501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kSrcATop:
502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kDstATop:
503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            case SkBlendMode::kModulate:
504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return true;
505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            default:
506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return false;
507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    /* Returns true unless we only need the shape of the drawing. */
511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool needSource() {
512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fBlendMode == SkBlendMode::kClear) {
513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return false;
514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return true;
516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    /* If the shape is different than the alpha component of the content, then
519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     * setShape should be called with the shape.  In particular, images and
520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     * devices have rectangular shape.
521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot     */
522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void setShape(const SkPath& shape) {
523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fShape = shape;
524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFDevice* fDevice;
528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFDevice::ContentEntry* fContentEntry;
529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBlendMode fBlendMode;
530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFObject> fDstFormXObject;
531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath fShape;
532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot////////////////////////////////////////////////////////////////////////////////
535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkPDFDevice::SkPDFDevice(SkISize pageSize, SkPDFDocument* doc)
537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    : INHERITED(SkImageInfo::MakeUnknown(pageSize.width(), pageSize.height()),
538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkSurfaceProps(0, kUnknown_SkPixelGeometry))
539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fPageSize(pageSize)
540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fInitialTransform(SkMatrix::I())
541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    , fDocument(doc)
542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot{
543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!pageSize.isEmpty());
544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::setFlip() {
547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Skia generally uses the top left as the origin but PDF
548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // natively has the origin at the bottom left. This matrix
549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // corrects for that.  But that only needs to be done once, we
550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // don't do it when layering.
551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fInitialTransform.setTranslate(0, SkIntToScalar(fPageSize.fHeight));
552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fInitialTransform.preScale(SK_Scalar1, -SK_Scalar1);
553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkPDFDevice::~SkPDFDevice() {
556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->cleanUp();
557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::init() {
560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fContentEntries.reset();
561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::cleanUp() {
564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fGraphicStateResources.unrefAll();
565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fXObjectResources.unrefAll();
566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fFontResources.unrefAll();
567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fShaderResources.unrefAll();
568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawAnnotation(const SkRect& rect, const char key[], SkData* value) {
571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!value) {
572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (rect.isEmpty()) {
575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!strcmp(SkAnnotationKeys::Define_Named_Dest_Key(), key)) {
576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPoint transformedPoint;
577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->ctm().mapXY(rect.x(), rect.y(), &transformedPoint);
578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fNamedDestinations.emplace_back(NamedDestination{sk_ref_sp(value), transformedPoint});
579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
582fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Convert to path to handle non-90-degree rotations.
583fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath path;
584fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    path.addRect(rect);
585fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    path.transform(this->ctm(), &path);
586fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath clip;
587fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (void)this->cs().asPath(&clip);
588fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    Op(clip, path, kIntersect_SkPathOp, &path);
589fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // PDF wants a rectangle only.
590fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect transformedRect = path.getBounds();
591fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (transformedRect.isEmpty()) {
592fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
593fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
594fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!strcmp(SkAnnotationKeys::URL_Key(), key)) {
595fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLinkToURLs.emplace_back(RectWithData{transformedRect, sk_ref_sp(value)});
596fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (!strcmp(SkAnnotationKeys::Link_Named_Dest_Key(), key)) {
597fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLinkToDestinations.emplace_back(RectWithData{transformedRect, sk_ref_sp(value)});
598fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
599fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
600fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
601fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawPaint(const SkPaint& srcPaint) {
602fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint newPaint = srcPaint;
603fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    remove_color_filter(&newPaint);
604fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    replace_srcmode_on_opaque_paint(&newPaint);
605fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    newPaint.setStyle(SkPaint::kFill_Style);
606fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
607fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix ctm = this->ctm();
608fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (ctm.getType() & SkMatrix::kPerspective_Mask) {
609fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (newPaint.getShader()) {
610fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            transform_shader(&newPaint, ctm);
611fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
612fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ctm = SkMatrix::I();
613fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
614fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, this->cs(), ctm, newPaint);
615fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawPaint(newPaint, content.entry());
616fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
617fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
618fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::internalDrawPaint(const SkPaint& paint,
619fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                    SkPDFDevice::ContentEntry* contentEntry) {
620fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!contentEntry) {
621fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
622fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
623fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect bbox = SkRect::Make(fPageSize);
624fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix inverse;
625fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!contentEntry->fState.fMatrix.invert(&inverse)) {
626fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
627fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
628fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    inverse.mapRect(&bbox);
629fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
630fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
631fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType,
632fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          &contentEntry->fContent);
633fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
634fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
635fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawPoints(SkCanvas::PointMode mode,
636fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             size_t count,
637fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             const SkPoint* points,
638fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             const SkPaint& srcPaint) {
639fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint passedPaint = srcPaint;
640fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    remove_color_filter(&passedPaint);
641fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    replace_srcmode_on_opaque_paint(&passedPaint);
642fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkCanvas::kPoints_PointMode != mode) {
643fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        passedPaint.setStyle(SkPaint::kStroke_Style);
644fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
645fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (count == 0) {
646fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
647fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
648fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
649fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // SkDraw::drawPoints converts to multiple calls to fDevice->drawPath.
650fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // We only use this when there's a path effect because of the overhead
651fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // of multiple calls to setUpContentEntry it causes.
652fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (passedPaint.getPathEffect()) {
653fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (this->cs().isEmpty(this->bounds())) {
654fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
655fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
656fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        draw_points(mode, count, points, passedPaint,
657fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    this->devClipBounds(), this->ctm(), this);
658fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
659fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
660fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
661fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkPaint* paint = &passedPaint;
662fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint modifiedPaint;
663fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
664fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (mode == SkCanvas::kPoints_PointMode &&
665fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            paint->getStrokeCap() != SkPaint::kRound_Cap) {
666fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        modifiedPaint = *paint;
667fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint = &modifiedPaint;
668fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (paint->getStrokeWidth()) {
669fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // PDF won't draw a single point with square/butt caps because the
670fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // orientation is ambiguous.  Draw a rectangle instead.
671fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            modifiedPaint.setStyle(SkPaint::kFill_Style);
672fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkScalar strokeWidth = paint->getStrokeWidth();
673fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkScalar halfStroke = SkScalarHalf(strokeWidth);
674fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (size_t i = 0; i < count; i++) {
675fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkRect r = SkRect::MakeXYWH(points[i].fX, points[i].fY, 0, 0);
676fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                r.inset(-halfStroke, -halfStroke);
677fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->drawRect(r, modifiedPaint);
678fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
679fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
680fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
681fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            modifiedPaint.setStrokeCap(SkPaint::kRound_Cap);
682fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
683fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
684fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
685fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, *paint);
686fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.entry()) {
687fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
688fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
689fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream* contentStream = content.stream();
690fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (mode) {
691fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkCanvas::kPolygon_PointMode:
692fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::MoveTo(points[0].fX, points[0].fY, contentStream);
693fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (size_t i = 1; i < count; i++) {
694fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::AppendLine(points[i].fX, points[i].fY, contentStream);
695fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
696fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::StrokePath(contentStream);
697fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
698fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkCanvas::kLines_PointMode:
699fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (size_t i = 0; i < count/2; i++) {
700fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::MoveTo(points[i * 2].fX, points[i * 2].fY, contentStream);
701fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::AppendLine(points[i * 2 + 1].fX, points[i * 2 + 1].fY, contentStream);
702fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::StrokePath(contentStream);
703fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
704fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
705fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkCanvas::kPoints_PointMode:
706fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(paint->getStrokeCap() == SkPaint::kRound_Cap);
707fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (size_t i = 0; i < count; i++) {
708fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::MoveTo(points[i].fX, points[i].fY, contentStream);
709fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::ClosePath(contentStream);
710fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::StrokePath(contentStream);
711fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
712fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
713fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        default:
714fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(false);
715fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
716fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
717fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
718fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic sk_sp<SkPDFDict> create_link_annotation(const SkRect& translatedRect) {
719fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto annotation = sk_make_sp<SkPDFDict>("Annot");
720fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    annotation->insertName("Subtype", "Link");
721fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    annotation->insertInt("F", 4);  // required by ISO 19005
722fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
723fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto border = sk_make_sp<SkPDFArray>();
724fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    border->reserve(3);
725fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    border->appendInt(0);  // Horizontal corner radius.
726fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    border->appendInt(0);  // Vertical corner radius.
727fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    border->appendInt(0);  // Width, 0 = no border.
728fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    annotation->insertObject("Border", std::move(border));
729fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
730fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto rect = sk_make_sp<SkPDFArray>();
731fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    rect->reserve(4);
732fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    rect->appendScalar(translatedRect.fLeft);
733fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    rect->appendScalar(translatedRect.fTop);
734fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    rect->appendScalar(translatedRect.fRight);
735fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    rect->appendScalar(translatedRect.fBottom);
736fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    annotation->insertObject("Rect", std::move(rect));
737fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
738fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return annotation;
739fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
740fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
741fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic sk_sp<SkPDFDict> create_link_to_url(const SkData* urlData, const SkRect& r) {
742fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFDict> annotation = create_link_annotation(r);
743fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString url(static_cast<const char *>(urlData->data()),
744fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 urlData->size() - 1);
745fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto action = sk_make_sp<SkPDFDict>("Action");
746fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    action->insertName("S", "URI");
747fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    action->insertString("URI", url);
748fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    annotation->insertObject("A", std::move(action));
749fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return annotation;
750fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
751fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
752fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic sk_sp<SkPDFDict> create_link_named_dest(const SkData* nameData,
753fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                               const SkRect& r) {
754fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFDict> annotation = create_link_annotation(r);
755fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkString name(static_cast<const char *>(nameData->data()),
756fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                  nameData->size() - 1);
757fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    annotation->insertName("Dest", name);
758fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return annotation;
759fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
760fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
761fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawRect(const SkRect& rect,
762fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           const SkPaint& srcPaint) {
763fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint = srcPaint;
764fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    remove_color_filter(&paint);
765fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    replace_srcmode_on_opaque_paint(&paint);
766fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect r = rect;
767fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    r.sort();
768fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
769fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (paint.getPathEffect() || paint.getMaskFilter()) {
770fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (this->cs().isEmpty(this->bounds())) {
771fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
772fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
773fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath path;
774fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        path.addRect(r);
775fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->drawPath(path, paint, nullptr, true);
776fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
777fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
778fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
779fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, paint);
780fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.entry()) {
781fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
782fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
783fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendRectangle(r, content.stream());
784fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::PaintPath(paint.getStyle(), SkPath::kWinding_FillType, content.stream());
785fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
786fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
787fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawRRect(const SkRRect& rrect,
788fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            const SkPaint& srcPaint) {
789fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint = srcPaint;
790fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    remove_color_filter(&paint);
791fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    replace_srcmode_on_opaque_paint(&paint);
792fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath  path;
793fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    path.addRRect(rrect);
794fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->drawPath(path, paint, nullptr, true);
795fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
796fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
797fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawOval(const SkRect& oval,
798fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           const SkPaint& srcPaint) {
799fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint = srcPaint;
800fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    remove_color_filter(&paint);
801fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    replace_srcmode_on_opaque_paint(&paint);
802fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath  path;
803fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    path.addOval(oval);
804fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->drawPath(path, paint, nullptr, true);
805fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
806fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
807fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawPath(const SkPath& origPath,
808fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           const SkPaint& srcPaint,
809fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           const SkMatrix* prePathMatrix,
810fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           bool pathIsMutable) {
811fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawPath(
812fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->cs(), this->ctm(), origPath, srcPaint, prePathMatrix, pathIsMutable);
813fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
814fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
815fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::internalDrawPathWithFilter(const SkClipStack& clipStack,
816fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkMatrix& ctm,
817fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkPath& origPath,
818fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkPaint& origPaint,
819fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                             const SkMatrix* prePathMatrix) {
820fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(origPaint.getMaskFilter());
821fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath path(origPath);
822fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
823fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (prePathMatrix) {
824fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        path.transform(*prePathMatrix, &path);
825fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
826fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkStrokeRec::InitStyle initStyle = paint->getFillPath(path, &path)
827fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     ? SkStrokeRec::kFill_InitStyle
828fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     : SkStrokeRec::kHairline_InitStyle;
829fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    path.transform(ctm, &path);
830fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
831fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO(halcanary): respect fDocument->rasterDpi().
832fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //        SkScalar rasterScale = (float)rasterDpi / SkPDFUtils::kDpiForRasterScaleOne;
833fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Would it be easier to just change the device size (and pre-scale the canvas)?
834fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkIRect bounds = clipStack.bounds(this->bounds()).roundOut();
835fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMask sourceMask;
836fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!SkDraw::DrawToMask(path, &bounds, paint->getMaskFilter(), &SkMatrix::I(),
837fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            &sourceMask, SkMask::kComputeBoundsAndRenderImage_CreateMode,
838fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            initStyle)) {
839fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
840fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
841fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoMaskFreeImage srcAutoMaskFreeImage(sourceMask.fImage);
842fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMask dstMask;
843fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkIPoint margin;
844fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!as_MFB(paint->getMaskFilter())->filterMask(&dstMask, sourceMask, ctm, &margin)) {
845fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
846fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
847fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkIRect dstMaskBounds = dstMask.fBounds;
848fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkImage> mask = mask_to_greyscale_image(&dstMask);
849fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // PDF doesn't seem to allow masking vector graphics with an Image XObject.
850fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Must mask with a Form XObject.
851fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice();
852fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
853fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkCanvas canvas(maskDevice.get());
854fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas.drawImage(mask, dstMaskBounds.x(), dstMaskBounds.y());
855fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
856fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!ctm.isIdentity() && paint->getShader()) {
857fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        transform_shader(paint.writable(), ctm); // Since we are using identity matrix.
858fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
859fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, clipStack, SkMatrix::I(), *paint);
860fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.entry()) {
861fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
862fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
863fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->addSMaskGraphicState(std::move(maskDevice), content.stream());
864fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendRectangle(SkRect::Make(dstMaskBounds), content.stream());
865fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::PaintPath(SkPaint::kFill_Style, path.getFillType(), content.stream());
866fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->clearMaskOnGraphicState(content.stream());
867fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
868fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
869fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::addSMaskGraphicState(sk_sp<SkPDFDevice> maskDevice,
870fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                       SkDynamicMemoryWStream* contentStream) {
871fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
872fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            maskDevice->makeFormXObjectFromDevice(true), false,
873fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFGraphicState::kLuminosity_SMaskMode, this->getCanon());
874fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(sMaskGS.get()), contentStream);
875fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
876fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
877fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::clearMaskOnGraphicState(SkDynamicMemoryWStream* contentStream) {
878fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The no-softmask graphic state is used to "turn off" the mask for later draw calls.
879fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFDict>& noSMaskGS = this->getCanon()->fNoSmaskGraphicState;
880fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!noSMaskGS) {
881fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        noSMaskGS = sk_make_sp<SkPDFDict>("ExtGState");
882fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        noSMaskGS->insertName("SMask", "None");
883fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
884fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::ApplyGraphicState(this->addGraphicStateResource(noSMaskGS.get()), contentStream);
885fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
886fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
887fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::internalDrawPath(const SkClipStack& clipStack,
888fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   const SkMatrix& ctm,
889fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   const SkPath& origPath,
890fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   const SkPaint& srcPaint,
891fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   const SkMatrix* prePathMatrix,
892fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   bool pathIsMutable) {
893fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint = srcPaint;
894fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    remove_color_filter(&paint);
895fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    replace_srcmode_on_opaque_paint(&paint);
896fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath modifiedPath;
897fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath* pathPtr = const_cast<SkPath*>(&origPath);
898fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
899fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (paint.getMaskFilter()) {
900fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->internalDrawPathWithFilter(clipStack, ctm, origPath, paint, prePathMatrix);
901fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
902fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
903fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
904fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix matrix = ctm;
905fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (prePathMatrix) {
906fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (paint.getPathEffect() || paint.getStyle() != SkPaint::kFill_Style) {
907fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (!pathIsMutable) {
908fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                pathPtr = &modifiedPath;
909fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                pathIsMutable = true;
910fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
911fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            origPath.transform(*prePathMatrix, pathPtr);
912fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
913fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            matrix.preConcat(*prePathMatrix);
914fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
915fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
916fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
917fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (paint.getPathEffect()) {
918fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (clipStack.isEmpty(this->bounds())) {
919fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
920fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
921fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!pathIsMutable) {
922fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            modifiedPath = origPath;
923fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pathPtr = &modifiedPath;
924fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pathIsMutable = true;
925fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
926fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (paint.getFillPath(*pathPtr, pathPtr)) {
927fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            paint.setStyle(SkPaint::kFill_Style);
928fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
929fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            paint.setStyle(SkPaint::kStroke_Style);
930fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            paint.setStrokeWidth(0);
931fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
932fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint.setPathEffect(nullptr);
933fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
934fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
935fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (this->handleInversePath(*pathPtr, paint, pathIsMutable, prePathMatrix)) {
936fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
937fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
938fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (matrix.getType() & SkMatrix::kPerspective_Mask) {
939fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!pathIsMutable) {
940fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            modifiedPath = origPath;
941fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pathPtr = &modifiedPath;
942fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pathIsMutable = true;
943fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
944fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pathPtr->transform(matrix);
945fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (paint.getShader()) {
946fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            transform_shader(&paint, matrix);
947fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
948fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        matrix = SkMatrix::I();
949fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
950fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
951fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, clipStack, matrix, paint);
952fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.entry()) {
953fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
954fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
955fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    constexpr SkScalar kToleranceScale = 0.0625f;  // smaller = better conics (circles).
956fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar matrixScale = matrix.mapRadius(1.0f);
957fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar tolerance = matrixScale > 0.0f ? kToleranceScale / matrixScale : kToleranceScale;
958fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool consumeDegeratePathSegments =
959fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           paint.getStyle() == SkPaint::kFill_Style ||
960fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           (paint.getStrokeCap() != SkPaint::kRound_Cap &&
961fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            paint.getStrokeCap() != SkPaint::kSquare_Cap);
962fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(), consumeDegeratePathSegments, content.stream(),
963fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                         tolerance);
964fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(), content.stream());
965fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
966fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
967fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot////////////////////////////////////////////////////////////////////////////////
968fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
969fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawImageRect(const SkImage* image,
970fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                const SkRect* src,
971fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                const SkRect& dst,
972fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                const SkPaint& paint,
973fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                SkCanvas::SrcRectConstraint) {
974fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(image);
975fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))),
976fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                src, dst, paint, this->ctm());
977fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
978fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
979fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawBitmapRect(const SkBitmap& bm,
980fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 const SkRect* src,
981fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 const SkRect& dst,
982fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 const SkPaint& paint,
983fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 SkCanvas::SrcRectConstraint) {
984fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!bm.drawsNothing());
985fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawImageRect(SkKeyedImage(bm), src, dst, paint, this->ctm());
986fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
987fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
988fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawBitmap(const SkBitmap& bm, SkScalar x, SkScalar y, const SkPaint& paint) {
989fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!bm.drawsNothing());
990fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto r = SkRect::MakeXYWH(x, y, bm.width(), bm.height());
991fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawImageRect(SkKeyedImage(bm), nullptr, r, paint, this->ctm());
992fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
993fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
994fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawSprite(const SkBitmap& bm, int x, int y, const SkPaint& paint) {
995fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!bm.drawsNothing());
996fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto r = SkRect::MakeXYWH(x, y, bm.width(), bm.height());
997fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawImageRect(SkKeyedImage(bm), nullptr, r, paint, SkMatrix::I());
998fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
999fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1000fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint& paint) {
1001fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(image);
1002fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto r = SkRect::MakeXYWH(x, y, image->width(), image->height());
1003fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))),
1004fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                nullptr, r, paint, this->ctm());
1005fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1006fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1007fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot////////////////////////////////////////////////////////////////////////////////
1008fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1009fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotnamespace {
1010fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass GlyphPositioner {
1011fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
1012fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GlyphPositioner(SkDynamicMemoryWStream* content,
1013fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkScalar textSkewX,
1014fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    bool wideChars,
1015fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    bool defaultPositioning,
1016fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkPoint origin)
1017fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : fContent(content)
1018fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fCurrentMatrixOrigin(origin)
1019fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fTextSkewX(textSkewX)
1020fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fWideChars(wideChars)
1021fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fDefaultPositioning(defaultPositioning) {
1022fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1023fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ~GlyphPositioner() { this->flush(); }
1024fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void flush() {
1025fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fInText) {
1026fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContent->writeText("> Tj\n");
1027fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInText = false;
1028fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1029fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1030fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    void writeGlyph(SkPoint xy,
1031fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkScalar advanceWidth,
1032fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    uint16_t glyph) {
1033fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fInitialized) {
1034fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Flip the text about the x-axis to account for origin swap and include
1035fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // the passed parameters.
1036fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContent->writeText("1 0 ");
1037fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::AppendScalar(-fTextSkewX, fContent);
1038fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContent->writeText(" -1 ");
1039fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.x(), fContent);
1040fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContent->writeText(" ");
1041fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::AppendScalar(fCurrentMatrixOrigin.y(), fContent);
1042fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContent->writeText(" Tm\n");
1043fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fCurrentMatrixOrigin.set(0.0f, 0.0f);
1044fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInitialized = true;
1045fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1046fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fDefaultPositioning) {
1047fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPoint position = xy - fCurrentMatrixOrigin;
1048fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (position != SkPoint{fXAdvance, 0}) {
1049fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->flush();
1050fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::AppendScalar(position.x(), fContent);
1051fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fContent->writeText(" ");
1052fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPDFUtils::AppendScalar(-position.y(), fContent);
1053fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fContent->writeText(" Td ");
1054fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fCurrentMatrixOrigin = xy;
1055fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                fXAdvance = 0;
1056fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1057fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fXAdvance += advanceWidth;
1058fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1059fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fInText) {
1060fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContent->writeText("<");
1061fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInText = true;
1062fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1063fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fWideChars) {
1064fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::WriteUInt16BE(fContent, glyph);
1065fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
1066fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(0 == glyph >> 8);
1067fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::WriteUInt8(fContent, static_cast<uint8_t>(glyph));
1068fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1069fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1070fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1071fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
1072fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream* fContent;
1073fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPoint fCurrentMatrixOrigin;
1074fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar fXAdvance = 0.0f;
1075fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar fTextSkewX;
1076fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool fWideChars;
1077fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool fInText = false;
1078fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool fInitialized = false;
1079fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const bool fDefaultPositioning;
1080fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
1081fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1082fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/** Given the m-to-n glyph-to-character mapping data (as returned by
1083fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    harfbuzz), iterate over the clusters. */
1084fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotclass Clusterator {
1085fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotpublic:
1086fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    Clusterator() : fClusters(nullptr), fUtf8Text(nullptr), fGlyphCount(0), fTextByteLength(0) {}
1087fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    explicit Clusterator(uint32_t glyphCount)
1088fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : fClusters(nullptr)
1089fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fUtf8Text(nullptr)
1090fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fGlyphCount(glyphCount)
1091fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fTextByteLength(0) {}
1092fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // The clusters[] array is an array of offsets into utf8Text[],
1093fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // one offset for each glyph.  See SkTextBlobBuilder for more info.
1094fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    Clusterator(const uint32_t* clusters,
1095fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                const char* utf8Text,
1096fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                uint32_t glyphCount,
1097fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                uint32_t textByteLength)
1098fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        : fClusters(clusters)
1099fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fUtf8Text(utf8Text)
1100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fGlyphCount(glyphCount)
1101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        , fTextByteLength(textByteLength) {
1102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // This is a cheap heuristic for /ReversedChars which seems to
1103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // work for clusters produced by HarfBuzz, which either
1104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // increase from zero (LTR) or decrease to zero (RTL).
1105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // "ReversedChars" is how PDF deals with RTL text.
1106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fReversedChars =
1107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fUtf8Text && fClusters && fGlyphCount && fClusters[0] != 0;
1108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    struct Cluster {
1110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const char* fUtf8Text;
1111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t fTextByteLength;
1112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t fGlyphIndex;
1113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t fGlyphCount;
1114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        explicit operator bool() const { return fGlyphCount != 0; }
1115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
1116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // True if this looks like right-to-left text.
1117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool reversedChars() const { return fReversedChars; }
1118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    Cluster next() {
1119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if ((!fUtf8Text || !fClusters) && fGlyphCount) {
1120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // These glyphs have no text.  Treat as one "cluster".
1121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            uint32_t glyphCount = fGlyphCount;
1122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fGlyphCount = 0;
1123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Cluster{nullptr, 0, 0, glyphCount};
1124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fGlyphCount == 0 || fTextByteLength == 0) {
1126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Cluster{nullptr, 0, 0, 0};  // empty
1127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fUtf8Text);
1129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fClusters);
1130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t cluster = fClusters[0];
1131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (cluster >= fTextByteLength) {
1132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Cluster{nullptr, 0, 0, 0};  // bad input.
1133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t glyphsInCluster = 1;
1135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        while (glyphsInCluster < fGlyphCount &&
1136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot               fClusters[glyphsInCluster] == cluster) {
1137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ++glyphsInCluster;
1138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(glyphsInCluster <= fGlyphCount);
1140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t textLength = 0;
1141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (glyphsInCluster == fGlyphCount) {
1142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // consumes rest of glyphs and rest of text
1143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (kInvalidCluster == fPreviousCluster) { // LTR text or single cluster
1144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                textLength = fTextByteLength - cluster;
1145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else { // RTL text; last cluster.
1146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkASSERT(fPreviousCluster < fTextByteLength);
1147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (fPreviousCluster <= cluster) {  // bad input.
1148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    return Cluster{nullptr, 0, 0, 0};
1149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                textLength = fPreviousCluster - cluster;
1151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fGlyphCount = 0;
1153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Cluster{fUtf8Text + cluster,
1154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           textLength,
1155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           fGlyphIndex,
1156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           glyphsInCluster};
1157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(glyphsInCluster < fGlyphCount);
1159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t nextCluster = fClusters[glyphsInCluster];
1160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (nextCluster >= fTextByteLength) {
1161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return Cluster{nullptr, 0, 0, 0};  // bad input.
1162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (nextCluster > cluster) { // LTR text
1164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (kInvalidCluster != fPreviousCluster) {
1165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                return Cluster{nullptr, 0, 0, 0};  // bad input.
1166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            textLength = nextCluster - cluster;
1168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else { // RTL text
1169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(nextCluster < cluster);
1170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (kInvalidCluster == fPreviousCluster) { // first cluster
1171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                textLength = fTextByteLength - cluster;
1172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else { // later cluster
1173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (fPreviousCluster <= cluster) {
1174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    return Cluster{nullptr, 0, 0, 0}; // bad input.
1175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                textLength = fPreviousCluster - cluster;
1177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fPreviousCluster = cluster;
1179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t glyphIndex = fGlyphIndex;
1181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fGlyphCount -= glyphsInCluster;
1182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fGlyphIndex += glyphsInCluster;
1183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fClusters   += glyphsInCluster;
1184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return Cluster{fUtf8Text + cluster,
1185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       textLength,
1186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       glyphIndex,
1187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       glyphsInCluster};
1188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotprivate:
1191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    static constexpr uint32_t kInvalidCluster = 0xFFFFFFFF;
1192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const uint32_t* fClusters;
1193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* fUtf8Text;
1194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t fGlyphCount;
1195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t fTextByteLength;
1196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t fGlyphIndex = 0;
1197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t fPreviousCluster = kInvalidCluster;
1198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool fReversedChars = false;
1199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
1200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstruct TextStorage {
1202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<char> fUtf8textStorage;
1203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<uint32_t> fClusterStorage;
1204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<SkGlyphID> fGlyphStorage;
1205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot};
1206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}  // namespace
1207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/** Given some unicode text (as passed to drawText(), convert to
1209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    glyphs (via primitive shaping), while preserving
1210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    glyph-to-character mapping information. */
1211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic Clusterator make_clusterator(
1212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const void* sourceText,
1213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        size_t sourceByteCount,
1214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkPaint& paint,
1215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        TextStorage* storage,
1216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int glyphCount) {
1217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(SkPaint::kGlyphID_TextEncoding != paint.getTextEncoding());
1218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr));
1219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(glyphCount > 0);
1220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    storage->fGlyphStorage.reset(SkToSizeT(glyphCount));
1221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    (void)paint.textToGlyphs(sourceText, sourceByteCount, storage->fGlyphStorage.get());
1222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    storage->fClusterStorage.reset(SkToSizeT(glyphCount));
1223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t* clusters = storage->fClusterStorage.get();
1224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    uint32_t utf8ByteCount = 0;
1225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* utf8Text = nullptr;
1226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (paint.getTextEncoding()) {
1227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkPaint::kUTF8_TextEncoding: {
1228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const char* txtPtr = (const char*)sourceText;
1229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int i = 0; i < glyphCount; ++i) {
1230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                clusters[i] = SkToU32(txtPtr - (const char*)sourceText);
1231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                txtPtr += SkUTF8_LeadByteToCount(*(const unsigned char*)txtPtr);
1232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkASSERT(txtPtr <= (const char*)sourceText + sourceByteCount);
1233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(txtPtr == (const char*)sourceText + sourceByteCount);
1235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            utf8ByteCount = SkToU32(sourceByteCount);
1236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            utf8Text = (const char*)sourceText;
1237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkPaint::kUTF16_TextEncoding: {
1240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const uint16_t* utf16ptr = (const uint16_t*)sourceText;
1241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            int utf16count = SkToInt(sourceByteCount / sizeof(uint16_t));
1242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            utf8ByteCount = SkToU32(SkUTF16_ToUTF8(utf16ptr, utf16count));
1243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            storage->fUtf8textStorage.reset(utf8ByteCount);
1244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            char* txtPtr = storage->fUtf8textStorage.get();
1245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            utf8Text = txtPtr;
1246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            int clusterIndex = 0;
1247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            while (utf16ptr < (const uint16_t*)sourceText + utf16count) {
1248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                clusters[clusterIndex++] = SkToU32(txtPtr - utf8Text);
1249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkUnichar uni = SkUTF16_NextUnichar(&utf16ptr);
1250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                txtPtr += SkUTF8_FromUnichar(uni, txtPtr);
1251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(clusterIndex == glyphCount);
1253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(txtPtr == storage->fUtf8textStorage.get() + utf8ByteCount);
1254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(utf16ptr == (const uint16_t*)sourceText + utf16count);
1255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkPaint::kUTF32_TextEncoding: {
1258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            const SkUnichar* utf32 = (const SkUnichar*)sourceText;
1259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            int utf32count = SkToInt(sourceByteCount / sizeof(SkUnichar));
1260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(glyphCount == utf32count);
1261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int i = 0; i < utf32count; ++i) {
1262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                utf8ByteCount += SkToU32(SkUTF8_FromUnichar(utf32[i]));
1263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            storage->fUtf8textStorage.reset(SkToSizeT(utf8ByteCount));
1265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            char* txtPtr = storage->fUtf8textStorage.get();
1266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            utf8Text = txtPtr;
1267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int i = 0; i < utf32count; ++i) {
1268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                clusters[i] = SkToU32(txtPtr - utf8Text);
1269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                txtPtr += SkUTF8_FromUnichar(utf32[i], txtPtr);
1270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        default:
1274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkDEBUGFAIL("");
1275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return Clusterator(clusters, utf8Text, SkToU32(glyphCount), utf8ByteCount);
1278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic SkUnichar map_glyph(const SkTDArray<SkUnichar>& glyphToUnicode, SkGlyphID glyph) {
1281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkToInt(glyph) < glyphToUnicode.count() ? glyphToUnicode[SkToInt(glyph)] : -1;
1282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic void update_font(SkWStream* wStream, int fontIndex, SkScalar textSize) {
1285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    wStream->writeText("/");
1286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    char prefix = SkPDFResourceDict::GetResourceTypePrefix(SkPDFResourceDict::kFont_ResourceType);
1287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    wStream->write(&prefix, 1);
1288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    wStream->writeDecAsText(fontIndex);
1289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    wStream->writeText(" ");
1290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::AppendScalar(textSize, wStream);
1291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    wStream->writeText(" Tf\n");
1292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic SkPath draw_text_as_path(const void* sourceText, size_t sourceByteCount,
1295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               const SkScalar pos[], SkTextBlob::GlyphPositioning positioning,
1296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               SkPoint offset, const SkPaint& srcPaint) {
1297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath path;
1298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int glyphCount;
1299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoTMalloc<SkPoint> tmpPoints;
1300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (positioning) {
1301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkTextBlob::kDefault_Positioning:
1302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            srcPaint.getTextPath(sourceText, sourceByteCount, offset.x(), offset.y(), &path);
1303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkTextBlob::kHorizontal_Positioning:
1305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            glyphCount = srcPaint.countText(sourceText, sourceByteCount);
1306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            tmpPoints.realloc(glyphCount);
1307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int i = 0; i < glyphCount; ++i) {
1308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                tmpPoints[i] = {pos[i] + offset.x(), offset.y()};
1309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            srcPaint.getPosTextPath(sourceText, sourceByteCount, tmpPoints.get(), &path);
1311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkTextBlob::kFull_Positioning:
1313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            srcPaint.getPosTextPath(sourceText, sourceByteCount, (const SkPoint*)pos, &path);
1314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            path.offset(offset.x(), offset.y());
1315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            break;
1316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return path;
1318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool has_outline_glyph(SkGlyphID gid, SkGlyphCache* cache) {
1321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkGlyph& glyph = cache->getGlyphIDMetrics(gid);
1322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkPath* path = cache->findPath(glyph);
1323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return (path && !path->isEmpty()) || (glyph.fWidth == 0 && glyph.fHeight == 0);
1324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic SkRect get_glyph_bounds_device_space(SkGlyphID gid, SkGlyphCache* cache,
1327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkScalar xScale, SkScalar yScale,
1328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                            SkPoint xy, const SkMatrix& ctm) {
1329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkGlyph& glyph = cache->getGlyphIDMetrics(gid);
1330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect glyphBounds = {glyph.fLeft * xScale,
1331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          glyph.fTop * yScale,
1332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          (glyph.fLeft + glyph.fWidth) * xScale,
1333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                          (glyph.fTop + glyph.fHeight) * yScale};
1334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    glyphBounds.offset(xy);
1335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ctm.mapRect(&glyphBounds); // now in dev space.
1336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return glyphBounds;
1337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool contains(const SkRect& r, SkPoint p) {
1340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot   return r.left() <= p.x() && p.x() <= r.right() &&
1341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot          r.top()  <= p.y() && p.y() <= r.bottom();
1342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic sk_sp<SkImage> image_from_mask(const SkMask& mask) {
1345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!mask.fImage) {
1346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
1347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkIRect bounds = mask.fBounds;
1349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBitmap bm;
1350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    switch (mask.fFormat) {
1351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkMask::kBW_Format:
1352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            bm.allocPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()));
1353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (int y = 0; y < bm.height(); ++y) {
1354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                for (int x8 = 0; x8 < bm.width(); x8 += 8) {
1355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    uint8_t v = *mask.getAddr1(x8 + bounds.x(), y + bounds.y());
1356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    int e = SkTMin(x8 + 8, bm.width());
1357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    for (int x = x8; x < e; ++x) {
1358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        *bm.getAddr8(x, y) = (v >> (x & 0x7)) & 0x1 ? 0xFF : 0x00;
1359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            bm.setImmutable();
1363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return SkImage::MakeFromBitmap(bm);
1364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkMask::kA8_Format:
1365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            bm.installPixels(SkImageInfo::MakeA8(bounds.width(), bounds.height()),
1366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             mask.fImage, mask.fRowBytes);
1367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode);
1368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkMask::kARGB32_Format:
1369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            bm.installPixels(SkImageInfo::MakeN32Premul(bounds.width(), bounds.height()),
1370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             mask.fImage, mask.fRowBytes);
1371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return SkMakeImageFromRasterBitmap(bm, kAlways_SkCopyPixelsMode);
1372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkMask::k3D_Format:
1373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(false);
1374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
1375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        case SkMask::kLCD16_Format:
1376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(false);
1377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
1378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        default:
1379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(false);
1380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
1381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::internalDrawText(
1385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const void* sourceText, size_t sourceByteCount,
1386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkScalar pos[], SkTextBlob::GlyphPositioning positioning,
1387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint offset, const SkPaint& srcPaint, const uint32_t* clusters,
1388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        uint32_t textByteLength, const char* utf8Text) {
1389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (0 == sourceByteCount || !sourceText || srcPaint.getTextSize() <= 0) {
1390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (this->cs().isEmpty(this->bounds())) {
1393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    NOT_IMPLEMENTED(srcPaint.isVerticalText(), false);
1396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (srcPaint.isVerticalText()) {
1397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Don't pretend we support drawing vertical text.  It is not
1398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // clear to me how to switch to "vertical writing" mode in PDF.
1399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Currently neither Chromium or Android set this flag.
1400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // https://bug.skia.org/5665
1401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (srcPaint.getPathEffect()
1403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            || srcPaint.getMaskFilter()
1404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            || SkPaint::kFill_Style != srcPaint.getStyle()) {
1405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Stroked Text doesn't work well with Type3 fonts.
1406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath path = draw_text_as_path(sourceText, sourceByteCount, pos,
1407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        positioning, offset, srcPaint);
1408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->drawPath(path, srcPaint, nullptr, true);
1409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint = calculate_text_paint(srcPaint);
1412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    remove_color_filter(&paint);
1413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    replace_srcmode_on_opaque_paint(&paint);
1414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!paint.getTypeface()) {
1415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint.setTypeface(SkTypeface::MakeDefault());
1416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTypeface* typeface = paint.getTypeface();
1418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!typeface) {
1419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkDebugf("SkPDF: SkTypeface::MakeDefault() returned nullptr.\n");
1420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkAdvancedTypefaceMetrics* metrics =
1424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFFont::GetMetrics(typeface, fDocument->canon());
1425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!metrics) {
1426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int glyphCount = paint.textToGlyphs(sourceText, sourceByteCount, nullptr);
1429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (glyphCount <= 0) {
1430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // These three heap buffers are only used in the case where no glyphs
1434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // are passed to drawText() (most clients pass glyphs or a textblob).
1435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    TextStorage storage;
1436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const SkGlyphID* glyphs = nullptr;
1437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    Clusterator clusterator;
1438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (textByteLength > 0) {
1439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID)));
1440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        glyphs = (const SkGlyphID*)sourceText;
1441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        clusterator = Clusterator(clusters, utf8Text, SkToU32(glyphCount), textByteLength);
1442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(clusters);
1443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(utf8Text);
1444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(srcPaint.getTextEncoding() == SkPaint::kGlyphID_TextEncoding);
1445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr));
1446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (SkPaint::kGlyphID_TextEncoding == srcPaint.getTextEncoding()) {
1447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(glyphCount == SkToInt(sourceByteCount / sizeof(SkGlyphID)));
1448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        glyphs = (const SkGlyphID*)sourceText;
1449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        clusterator = Clusterator(SkToU32(glyphCount));
1450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(glyphCount == paint.textToGlyphs(sourceText, sourceByteCount, nullptr));
1451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(nullptr == clusters);
1452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(nullptr == utf8Text);
1453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
1454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(nullptr == clusters);
1455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(nullptr == utf8Text);
1456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        clusterator = make_clusterator(sourceText, sourceByteCount, srcPaint,
1457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                       &storage, glyphCount);
1458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        glyphs = storage.fGlyphStorage;
1459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool defaultPositioning = (positioning == SkTextBlob::kDefault_Positioning);
1461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setHinting(SkPaint::kNo_Hinting);
1462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int emSize;
1464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkAutoGlyphCache glyphCache = SkPDFFont::MakeVectorCache(typeface, &emSize);
1465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar textSize = paint.getTextSize();
1467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar advanceScale = textSize * paint.getTextScaleX() / emSize;
1468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // textScaleX and textScaleY are used to get a conservative bounding box for glyphs.
1470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar textScaleY = textSize / emSize;
1471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar textScaleX = advanceScale + paint.getTextSkewX() * textScaleY;
1472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint::Align alignment = paint.getTextAlign();
1474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    float alignmentFactor = SkPaint::kLeft_Align   == alignment ?  0.0f :
1475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            SkPaint::kCenter_Align == alignment ? -0.5f :
1476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            /* SkPaint::kRight_Align */           -1.0f;
1477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (defaultPositioning && alignment != SkPaint::kLeft_Align) {
1478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar advance = 0;
1479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (int i = 0; i < glyphCount; ++i) {
1480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            advance += advanceScale * glyphCache->getGlyphIDAdvance(glyphs[i]).fAdvanceX;
1481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        offset.offset(alignmentFactor * advance, 0);
1483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect clipStackBounds = this->cs().bounds(this->bounds());
1485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    struct PositionedGlyph {
1486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint fPos;
1487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkGlyphID fGlyph;
1488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    };
1489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTArray<PositionedGlyph> fMissingGlyphs;
1490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    {
1491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ScopedContentEntry content(this, paint, true);
1492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!content.entry()) {
1493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
1494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkDynamicMemoryWStream* out = content.stream();
1496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkTDArray<SkUnichar>& glyphToUnicode = metrics->fGlyphToUnicode;
1497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        out->writeText("BT\n");
1499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_AT_SCOPE_EXIT(out->writeText("ET\n"));
1500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkGlyphID maxGlyphID = SkToU16(typeface->countGlyphs() - 1);
1502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bool multiByteGlyphs = SkPDFFont::IsMultiByte(SkPDFFont::FontType(*metrics));
1504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (clusterator.reversedChars()) {
1505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            out->writeText("/ReversedChars BMC\n");
1506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SK_AT_SCOPE_EXIT(if (clusterator.reversedChars()) { out->writeText("EMC\n"); } );
1508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        GlyphPositioner glyphPositioner(out,
1509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        paint.getTextSkewX(),
1510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        multiByteGlyphs,
1511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        defaultPositioning,
1512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        offset);
1513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFFont* font = nullptr;
1514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        while (Clusterator::Cluster c = clusterator.next()) {
1516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            int index = c.fGlyphIndex;
1517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            int glyphLimit = index + c.fGlyphCount;
1518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            bool actualText = false;
1520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SK_AT_SCOPE_EXIT(if (actualText) {
1521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 glyphPositioner.flush();
1522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                 out->writeText("EMC\n");
1523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             });
1524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (c.fUtf8Text) {  // real cluster
1525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // Check if `/ActualText` needed.
1526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                const char* textPtr = c.fUtf8Text;
1527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                const char* textEnd = c.fUtf8Text + c.fTextByteLength;
1528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkUnichar unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
1529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (unichar < 0) {
1530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    return;
1531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (textPtr < textEnd ||                                  // more characters left
1533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    glyphLimit > index + 1 ||                             // toUnicode wouldn't work
1534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    unichar != map_glyph(glyphToUnicode, glyphs[index]))  // test single Unichar map
1535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                {
1536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    glyphPositioner.flush();
1537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    out->writeText("/Span<</ActualText <");
1538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkPDFUtils::WriteUTF16beHex(out, 0xFEFF);  // U+FEFF = BYTE ORDER MARK
1539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // the BOM marks this text as UTF-16BE, not PDFDocEncoding.
1540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkPDFUtils::WriteUTF16beHex(out, unichar);  // first char
1541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    while (textPtr < textEnd) {
1542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        unichar = SkUTF8_NextUnicharWithError(&textPtr, textEnd);
1543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        if (unichar < 0) {
1544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            break;
1545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        }
1546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        SkPDFUtils::WriteUTF16beHex(out, unichar);
1547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    out->writeText("> >> BDC\n");  // begin marked-content sequence
1549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                   // with an associated property list.
1550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    actualText = true;
1551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            for (; index < glyphLimit; ++index) {
1554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkGlyphID gid = glyphs[index];
1555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (gid > maxGlyphID) {
1556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    continue;
1557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (!font || !font->hasGlyph(gid)) {
1559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // Not yet specified font or need to switch font.
1560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    int fontIndex = this->getFontResourceIndex(typeface, gid);
1561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // All preconditions for SkPDFFont::GetFontResource are met.
1562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkASSERT(fontIndex >= 0);
1563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (fontIndex < 0) {
1564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        return;
1565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    glyphPositioner.flush();
1567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    update_font(out, fontIndex, textSize);
1568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    font = fFontResources[fontIndex];
1569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkASSERT(font);  // All preconditions for SkPDFFont::GetFontResource are met.
1570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (!font) {
1571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        return;
1572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkASSERT(font->multiByteGlyphs() == multiByteGlyphs);
1574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPoint xy = {0, 0};
1576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkScalar advance = advanceScale * glyphCache->getGlyphIDAdvance(gid).fAdvanceX;
1577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (!defaultPositioning) {
1578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    xy = SkTextBlob::kFull_Positioning == positioning
1579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       ? SkPoint{pos[2 * index], pos[2 * index + 1]}
1580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                       : SkPoint{pos[index], 0};
1581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (alignment != SkPaint::kLeft_Align) {
1582fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        xy.offset(alignmentFactor * advance, 0);
1583fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1584fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    // Do a glyph-by-glyph bounds-reject if positions are absolute.
1585fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    SkRect glyphBounds = get_glyph_bounds_device_space(
1586fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            gid, glyphCache.get(), textScaleX, textScaleY,
1587fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            xy + offset, this->ctm());
1588fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (glyphBounds.isEmpty()) {
1589fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        if (!contains(clipStackBounds, {glyphBounds.x(), glyphBounds.y()})) {
1590fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            continue;
1591fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        }
1592fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    } else {
1593fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        if (!clipStackBounds.intersects(glyphBounds)) {
1594fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            continue;  // reject glyphs as out of bounds
1595fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        }
1596fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1597fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (!has_outline_glyph(gid, glyphCache.get())) {
1598fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fMissingGlyphs.push_back({xy + offset, gid});
1599fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1600fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                } else {
1601fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    if (!has_outline_glyph(gid, glyphCache.get())) {
1602fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        fMissingGlyphs.push_back({offset, gid});
1603fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    }
1604fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    offset += SkPoint{advance, 0};
1605fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
1606fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                font->noteGlyphUsage(gid);
1607fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1608fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkGlyphID encodedGlyph = multiByteGlyphs ? gid : font->glyphToPDFFontEncoding(gid);
1609fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                glyphPositioner.writeGlyph(xy, advance, encodedGlyph);
1610fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1611fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1612fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1613fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fMissingGlyphs.count() > 0) {
1614fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Fall back on images.
1615fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint scaledGlyphCachePaint;
1616fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        scaledGlyphCachePaint.setTextSize(paint.getTextSize());
1617fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        scaledGlyphCachePaint.setTextScaleX(paint.getTextScaleX());
1618fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        scaledGlyphCachePaint.setTextSkewX(paint.getTextSkewX());
1619fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        scaledGlyphCachePaint.setTypeface(sk_ref_sp(typeface));
1620fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkAutoGlyphCache scaledGlyphCache(scaledGlyphCachePaint, nullptr, nullptr);
1621fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkTHashMap<SkPDFCanon::BitmapGlyphKey, SkPDFCanon::BitmapGlyph>* map =
1622fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            &this->getCanon()->fBitmapGlyphImages;
1623fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        for (PositionedGlyph positionedGlyph : fMissingGlyphs) {
1624fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFCanon::BitmapGlyphKey key = {typeface->uniqueID(),
1625fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                              paint.getTextSize(),
1626fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                              paint.getTextScaleX(),
1627fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                              paint.getTextSkewX(),
1628fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                              positionedGlyph.fGlyph,
1629fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                              0};
1630fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkImage* img = nullptr;
1631fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkIPoint imgOffset = {0, 0};
1632fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (SkPDFCanon::BitmapGlyph* ptr = map->find(key)) {
1633fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                img = ptr->fImage.get();
1634fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                imgOffset = ptr->fOffset;
1635fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else {
1636fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                (void)scaledGlyphCache->findImage(
1637fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                        scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph));
1638fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkMask mask;
1639fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                scaledGlyphCache->getGlyphIDMetrics(positionedGlyph.fGlyph).toMask(&mask);
1640fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                imgOffset = {mask.fBounds.x(), mask.fBounds.y()};
1641fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                img = map->set(key, {image_from_mask(mask), imgOffset})->fImage.get();
1642fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1643fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (img) {
1644fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPoint pt = positionedGlyph.fPos +
1645fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             SkPoint{(SkScalar)imgOffset.x(), (SkScalar)imgOffset.y()};
1646fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->drawImage(img, pt.x(), pt.y(), srcPaint);
1647fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
1648fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1649fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1650fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1651fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1652fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawText(const void* text, size_t len,
1653fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           SkScalar x, SkScalar y, const SkPaint& paint) {
1654fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawText(text, len, nullptr, SkTextBlob::kDefault_Positioning,
1655fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           SkPoint{x, y}, paint, nullptr, 0, nullptr);
1656fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1657fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1658fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawPosText(const void* text, size_t len,
1659fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              const SkScalar pos[], int scalarsPerPos,
1660fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              const SkPoint& offset, const SkPaint& paint) {
1661fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->internalDrawText(text, len, pos, (SkTextBlob::GlyphPositioning)scalarsPerPos,
1662fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                           offset, paint, nullptr, 0, nullptr);
1663fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1664fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1665fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
1666fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               const SkPaint &paint, SkDrawFilter* drawFilter) {
1667fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (SkTextBlobRunIterator it(blob); !it.done(); it.next()) {
1668fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint runPaint(paint);
1669fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        it.applyFontToPaint(&runPaint);
1670fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
1671fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            continue;
1672fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1673fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        runPaint.setFlags(this->filterTextFlags(runPaint));
1674fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint offset = it.offset() + SkPoint{x, y};
1675fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->internalDrawText(it.glyphs(), sizeof(SkGlyphID) * it.glyphCount(),
1676fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               it.pos(), it.positioning(), offset, runPaint,
1677fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               it.clusters(), it.textSize(), it.text());
1678fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1679fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1680fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1681fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawVertices(const SkVertices*, SkBlendMode, const SkPaint&) {
1682fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (this->cs().isEmpty(this->bounds())) {
1683fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1684fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1685fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO: implement drawVertices
1686fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1687fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1688fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) {
1689fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!paint.getImageFilter());
1690fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1691fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Check if the source device is really a bitmapdevice (because that's what we returned
1692fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // from createDevice (likely due to an imagefilter)
1693fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPixmap pmap;
1694fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (device->peekPixels(&pmap)) {
1695fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkBitmap bitmap;
1696fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bitmap.installPixels(pmap);
1697fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->drawSprite(bitmap, x, y, paint);
1698fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1699fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1700fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1701fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // our onCreateCompatibleDevice() always creates SkPDFDevice subclasses.
1702fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFDevice* pdfDevice = static_cast<SkPDFDevice*>(device);
1703fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1704fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar scalarX = SkIntToScalar(x);
1705fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkScalar scalarY = SkIntToScalar(y);
1706fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const RectWithData& l : pdfDevice->fLinkToURLs) {
1707fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect r = l.rect.makeOffset(scalarX, scalarY);
1708fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLinkToURLs.emplace_back(RectWithData{r, l.data});
1709fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1710fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const RectWithData& l : pdfDevice->fLinkToDestinations) {
1711fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect r = l.rect.makeOffset(scalarX, scalarY);
1712fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fLinkToDestinations.emplace_back(RectWithData{r, l.data});
1713fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1714fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const NamedDestination& d : pdfDevice->fNamedDestinations) {
1715fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint p = d.point + SkPoint::Make(scalarX, scalarY);
1716fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fNamedDestinations.emplace_back(NamedDestination{d.nameData, p});
1717fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1718fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1719fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (pdfDevice->isContentEmpty()) {
1720fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1721fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1722fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1723fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix matrix = SkMatrix::MakeTrans(SkIntToScalar(x), SkIntToScalar(y));
1724fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, this->cs(), matrix, paint);
1725fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.entry()) {
1726fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1727fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1728fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (content.needShape()) {
1729fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath shape;
1730fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        shape.addRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
1731fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                       SkIntToScalar(device->width()),
1732fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                       SkIntToScalar(device->height())));
1733fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        content.setShape(shape);
1734fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1735fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.needSource()) {
1736fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1737fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1738fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1739fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFObject> xObject = pdfDevice->makeFormXObjectFromDevice();
1740fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::DrawFormXObject(this->addXObjectResource(xObject.get()), content.stream());
1741fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1742fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1743fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1744fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkSurface::MakeRaster(info, &props);
1745fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1746fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1747fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1748fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkPDFDict> SkPDFDevice::makeResourceDict() const {
1749fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkTDArray<SkPDFObject*> fonts;
1750fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    fonts.setReserve(fFontResources.count());
1751fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (SkPDFFont* font : fFontResources) {
1752fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fonts.push(font);
1753fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1754fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkPDFResourceDict::Make(
1755fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            &fGraphicStateResources,
1756fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            &fShaderResources,
1757fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            &fXObjectResources,
1758fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            &fonts);
1759fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1760fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1761fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkPDFArray> SkPDFDevice::copyMediaBox() const {
1762fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto mediaBox = sk_make_sp<SkPDFArray>();
1763fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    mediaBox->reserve(4);
1764fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    mediaBox->appendInt(0);
1765fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    mediaBox->appendInt(0);
1766fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    mediaBox->appendInt(fPageSize.width());
1767fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    mediaBox->appendInt(fPageSize.height());
1768fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return mediaBox;
1769fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1770fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1771fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstd::unique_ptr<SkStreamAsset> SkPDFDevice::content() const {
1772fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkDynamicMemoryWStream buffer;
1773fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fInitialTransform.getType() != SkMatrix::kIdentity_Mask) {
1774fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFUtils::AppendTransform(fInitialTransform, &buffer);
1775fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1776fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1777fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    GraphicStackState gsState(fExistingClipStack, &buffer);
1778fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const auto& entry : fContentEntries) {
1779fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        gsState.updateClip(entry.fState.fClipStack,
1780fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                {0, 0}, SkRect::Make(this->bounds()));
1781fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        gsState.updateMatrix(entry.fState.fMatrix);
1782fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        gsState.updateDrawingState(entry.fState);
1783fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1784fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entry.fContent.writeToStream(&buffer);
1785fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1786fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    gsState.drainStack();
1787fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (buffer.bytesWritten() > 0) {
1788fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return std::unique_ptr<SkStreamAsset>(buffer.detachAsStream());
1789fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
1790fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return skstd::make_unique<SkMemoryStream>();
1791fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1792fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1793fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1794fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot/* Draws an inverse filled path by using Path Ops to compute the positive
1795fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * inverse using the current clip as the inverse bounds.
1796fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * Return true if this was an inverse path and was properly handled,
1797fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * otherwise returns false and the normal drawing routine should continue,
1798fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * either as a (incorrect) fallback or because the path was not inverse
1799fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot * in the first place.
1800fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot */
1801fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkPDFDevice::handleInversePath(const SkPath& origPath,
1802fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                    const SkPaint& paint, bool pathIsMutable,
1803fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                    const SkMatrix* prePathMatrix) {
1804fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!origPath.isInverseFillType()) {
1805fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
1806fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1807fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1808fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (this->cs().isEmpty(this->bounds())) {
1809fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
1810fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1811fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1812fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath modifiedPath;
1813fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPath* pathPtr = const_cast<SkPath*>(&origPath);
1814fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint noInversePaint(paint);
1815fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1816fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Merge stroking operations into final path.
1817fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkPaint::kStroke_Style == paint.getStyle() ||
1818fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint::kStrokeAndFill_Style == paint.getStyle()) {
1819fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bool doFillPath = paint.getFillPath(origPath, &modifiedPath);
1820fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (doFillPath) {
1821fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            noInversePaint.setStyle(SkPaint::kFill_Style);
1822fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            noInversePaint.setStrokeWidth(0);
1823fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pathPtr = &modifiedPath;
1824fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
1825fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // To be consistent with the raster output, hairline strokes
1826fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // are rendered as non-inverted.
1827fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            modifiedPath.toggleInverseFillType();
1828fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->drawPath(modifiedPath, paint, nullptr, true);
1829fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return true;
1830fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1831fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1832fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1833fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Get bounds of clip in current transform space
1834fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // (clip bounds are given in device space).
1835fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix transformInverse;
1836fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix totalMatrix = this->ctm();
1837fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (prePathMatrix) {
1838fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        totalMatrix.preConcat(*prePathMatrix);
1839fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1840fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!totalMatrix.invert(&transformInverse)) {
1841fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
1842fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1843fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect bounds = this->cs().bounds(this->bounds());
1844fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    transformInverse.mapRect(&bounds);
1845fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1846fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Extend the bounds by the line width (plus some padding)
1847fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // so the edge doesn't cause a visible stroke.
1848fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bounds.outset(paint.getStrokeWidth() + SK_Scalar1,
1849fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                  paint.getStrokeWidth() + SK_Scalar1);
1850fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1851fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!calculate_inverse_path(bounds, *pathPtr, &modifiedPath)) {
1852fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return false;
1853fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1854fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1855fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->drawPath(modifiedPath, noInversePaint, prePathMatrix, true);
1856fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return true;
1857fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1858fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1859fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::appendAnnotations(SkPDFArray* array) const {
1860fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    array->reserve(fLinkToURLs.count() + fLinkToDestinations.count());
1861fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const RectWithData& rectWithURL : fLinkToURLs) {
1862fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect r;
1863fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fInitialTransform.mapRect(&r, rectWithURL.rect);
1864fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        array->appendObject(create_link_to_url(rectWithURL.data.get(), r));
1865fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1866fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const RectWithData& linkToDestination : fLinkToDestinations) {
1867fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect r;
1868fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fInitialTransform.mapRect(&r, linkToDestination.rect);
1869fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        array->appendObject(
1870fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                create_link_named_dest(linkToDestination.data.get(), r));
1871fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1872fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1873fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1874fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::appendDestinations(SkPDFDict* dict, SkPDFObject* page) const {
1875fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    for (const NamedDestination& dest : fNamedDestinations) {
1876fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto pdfDest = sk_make_sp<SkPDFArray>();
1877fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pdfDest->reserve(5);
1878fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pdfDest->appendObjRef(sk_ref_sp(page));
1879fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pdfDest->appendName("XYZ");
1880fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPoint p = fInitialTransform.mapXY(dest.point.x(), dest.point.y());
1881fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pdfDest->appendScalar(p.x());
1882fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pdfDest->appendScalar(p.y());
1883fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pdfDest->appendInt(0);  // Leave zoom unchanged
1884fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkString name(static_cast<const char*>(dest.nameData->data()));
1885fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        dict->insertObject(name, std::move(pdfDest));
1886fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1887fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1888fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1889fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkPDFObject> SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
1890fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix inverseTransform = SkMatrix::I();
1891fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!fInitialTransform.isIdentity()) {
1892fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!fInitialTransform.invert(&inverseTransform)) {
1893fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkDEBUGFAIL("Layer initial transform should be invertible.");
1894fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            inverseTransform.reset();
1895fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1896fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1897fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    const char* colorSpace = alpha ? "DeviceGray" : nullptr;
1898fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFObject> xobject =
1899fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFMakeFormXObject(this->content(), this->copyMediaBox(),
1900fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                             this->makeResourceDict(), inverseTransform, colorSpace);
1901fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // We always draw the form xobjects that we create back into the device, so
1902fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // we simply preserve the font usage instead of pulling it out and merging
1903fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // it back in later.
1904fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->cleanUp();  // Reset this device to have no content.
1905fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->init();
1906fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return xobject;
1907fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1908fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1909fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawFormXObjectWithMask(int xObjectIndex,
1910fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          sk_sp<SkPDFObject> mask,
1911fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          const SkClipStack& clipStack,
1912fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          SkBlendMode mode,
1913fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          bool invertClip) {
1914fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!invertClip && clipStack.isEmpty(this->bounds())) {
1915fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1916fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1917fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1918fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFDict> sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
1919fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            std::move(mask), invertClip,
1920fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFGraphicState::kAlpha_SMaskMode, fDocument->canon());
1921fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1922fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint;
1923fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setBlendMode(mode);
1924fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, clipStack, SkMatrix::I(), paint);
1925fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.entry()) {
1926fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1927fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1928fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()), content.stream());
1929fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::DrawFormXObject(xObjectIndex, content.stream());
1930fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    this->clearMaskOnGraphicState(content.stream());
1931fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1932fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1933fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkPDFDevice::ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack& clipStack,
1934fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          const SkMatrix& matrix,
1935fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          const SkPaint& paint,
1936fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          bool hasText,
1937fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                                          sk_sp<SkPDFObject>* dst) {
1938fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    *dst = nullptr;
1939fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBlendMode blendMode = paint.getBlendMode();
1940fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1941fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // For the following modes, we want to handle source and destination
1942fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // separately, so make an object of what's already there.
1943fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blendMode == SkBlendMode::kClear       ||
1944fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kSrc     ||
1945fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kSrcIn   ||
1946fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kDstIn   ||
1947fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kSrcOut  ||
1948fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kDstOut  ||
1949fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kSrcATop ||
1950fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kDstATop ||
1951fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kModulate) {
1952fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!isContentEmpty()) {
1953fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            *dst = this->makeFormXObjectFromDevice();
1954fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkASSERT(isContentEmpty());
1955fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else if (blendMode != SkBlendMode::kSrc &&
1956fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                   blendMode != SkBlendMode::kSrcOut) {
1957fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Except for Src and SrcOut, if there isn't anything already there,
1958fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // then we're done.
1959fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return nullptr;
1960fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
1961fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1962fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO(vandebo): Figure out how/if we can handle the following modes:
1963fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Xor, Plus.
1964fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1965fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Dst xfer mode doesn't draw source at all.
1966fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blendMode == SkBlendMode::kDst) {
1967fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return nullptr;
1968fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1969fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1970fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFDevice::ContentEntry* entry;
1971fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (fContentEntries.back() && fContentEntries.back()->fContent.bytesWritten() == 0) {
1972fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entry = fContentEntries.back();
1973fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (blendMode != SkBlendMode::kDstOver) {
1974fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entry = fContentEntries.emplace_back();
1975fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
1976fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entry = fContentEntries.emplace_front();
1977fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1978fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    populateGraphicStateEntryFromPaint(matrix, clipStack, paint, hasText, &entry->fState);
1979fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return entry;
1980fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
1981fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
1982fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::finishContentEntry(SkBlendMode blendMode,
1983fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     sk_sp<SkPDFObject> dst,
1984fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                     SkPath* shape) {
1985fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blendMode != SkBlendMode::kClear       &&
1986fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kSrc     &&
1987fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kDstOver &&
1988fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kSrcIn   &&
1989fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kDstIn   &&
1990fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kSrcOut  &&
1991fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kDstOut  &&
1992fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kSrcATop &&
1993fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kDstATop &&
1994fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode != SkBlendMode::kModulate) {
1995fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(!dst);
1996fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
1997fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
1998fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blendMode == SkBlendMode::kDstOver) {
1999fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(!dst);
2000fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (fContentEntries.front()->fContent.bytesWritten() == 0) {
2001fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // For DstOver, an empty content entry was inserted before the rest
2002fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // of the content entries. If nothing was drawn, it needs to be
2003fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // removed.
2004fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fContentEntries.pop_front();
2005fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2006fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2007fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2008fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!dst) {
2009fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(blendMode == SkBlendMode::kSrc ||
2010fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                 blendMode == SkBlendMode::kSrcOut);
2011fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2012fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2013fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2014fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(dst);
2015fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(fContentEntries.count() == 1);
2016fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Changing the current content into a form-xobject will destroy the clip
2017fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // objects which is fine since the xobject will already be clipped. However
2018fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // if source has shape, we need to clip it too, so a copy of the clip is
2019fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // saved.
2020fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2021fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkClipStack clipStack = fContentEntries.front()->fState.fClipStack;
2022fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2023fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint stockPaint;
2024fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2025fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFObject> srcFormXObject;
2026fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (isContentEmpty()) {
2027fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // If nothing was drawn and there's no shape, then the draw was a
2028fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // no-op, but dst needs to be restored for that to be true.
2029fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // If there is shape, then an empty source with Src, SrcIn, SrcOut,
2030fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
2031fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // reduces to Dst.
2032fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (shape == nullptr || blendMode == SkBlendMode::kDstOut ||
2033fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                blendMode == SkBlendMode::kSrcATop) {
2034fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint);
2035fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // TODO: addXObjectResource take sk_sp
2036fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()), content.stream());
2037fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2038fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
2039fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode = SkBlendMode::kClear;
2040fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2041fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
2042fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fContentEntries.count() == 1);
2043fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        srcFormXObject = this->makeFormXObjectFromDevice();
2044fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2045fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2046fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
2047fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // without alpha.
2048fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blendMode == SkBlendMode::kSrcATop) {
2049fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // TODO(vandebo): In order to properly support SrcATop we have to track
2050fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the shape of what's been drawn at all times. It's the intersection of
2051fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the non-transparent parts of the device and the outlines (shape) of
2052fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // all images and devices drawn.
2053fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
2054fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                fExistingClipStack, SkBlendMode::kSrcOver, true);
2055fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
2056fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (shape != nullptr) {
2057fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // Draw shape into a form-xobject.
2058fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPaint filledPaint;
2059fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            filledPaint.setColor(SK_ColorBLACK);
2060fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            filledPaint.setStyle(SkPaint::kFill_Style);
2061fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->internalDrawPath(clipStack, SkMatrix::I(), *shape, filledPaint, nullptr, true);
2062fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->drawFormXObjectWithMask(this->addXObjectResource(dst.get()),
2063fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          this->makeFormXObjectFromDevice(),
2064fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          fExistingClipStack,
2065fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          SkBlendMode::kSrcOver, true);
2066fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
2067fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->drawFormXObjectWithMask(this->addXObjectResource(dst.get()),
2068fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          srcFormXObject,
2069fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          fExistingClipStack,
2070fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                          SkBlendMode::kSrcOver, true);
2071fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2072fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2073fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2074fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blendMode == SkBlendMode::kClear) {
2075fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2076fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (blendMode == SkBlendMode::kSrc ||
2077fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kDstATop) {
2078fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ScopedContentEntry content(this, fExistingClipStack, SkMatrix::I(), stockPaint);
2079fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (content.entry()) {
2080fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::DrawFormXObject(this->addXObjectResource(srcFormXObject.get()),
2081fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        content.stream());
2082fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2083fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (blendMode == SkBlendMode::kSrc) {
2084fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2085fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2086fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else if (blendMode == SkBlendMode::kSrcATop) {
2087fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ScopedContentEntry content(this, fExistingClipStack,
2088fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   SkMatrix::I(), stockPaint);
2089fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (content.entry()) {
2090fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst.get()), content.stream());
2091fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2092fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2093fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2094fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(blendMode == SkBlendMode::kSrcIn   ||
2095fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             blendMode == SkBlendMode::kDstIn   ||
2096fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             blendMode == SkBlendMode::kSrcOut  ||
2097fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             blendMode == SkBlendMode::kDstOut  ||
2098fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             blendMode == SkBlendMode::kSrcATop ||
2099fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             blendMode == SkBlendMode::kDstATop ||
2100fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot             blendMode == SkBlendMode::kModulate);
2101fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2102fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (blendMode == SkBlendMode::kSrcIn ||
2103fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kSrcOut ||
2104fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            blendMode == SkBlendMode::kSrcATop) {
2105fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
2106fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                std::move(dst),
2107fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                fExistingClipStack,
2108fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                SkBlendMode::kSrcOver,
2109fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                blendMode == SkBlendMode::kSrcOut);
2110fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2111fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
2112fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkBlendMode mode = SkBlendMode::kSrcOver;
2113fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        int resourceID = addXObjectResource(dst.get());
2114fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (blendMode == SkBlendMode::kModulate) {
2115fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
2116fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                    std::move(dst), fExistingClipStack,
2117fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                    SkBlendMode::kSrcOver, false);
2118fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            mode = SkBlendMode::kMultiply;
2119fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2120fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        drawFormXObjectWithMask(resourceID, std::move(srcFormXObject),
2121fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                fExistingClipStack, mode,
2122fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                blendMode == SkBlendMode::kDstOut);
2123fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2124fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2125fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2126fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2127fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotbool SkPDFDevice::isContentEmpty() {
2128fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!fContentEntries.front() || fContentEntries.front()->fContent.bytesWritten() == 0) {
2129fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fContentEntries.count() <= 1);
2130fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return true;
2131fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2132fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return false;
2133fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2134fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2135fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::populateGraphicStateEntryFromPaint(
2136fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkMatrix& matrix,
2137fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkClipStack& clipStack,
2138fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkPaint& paint,
2139fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        bool hasText,
2140fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFDevice::GraphicStateEntry* entry) {
2141fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    NOT_IMPLEMENTED(paint.getPathEffect() != nullptr, false);
2142fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    NOT_IMPLEMENTED(paint.getMaskFilter() != nullptr, false);
2143fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    NOT_IMPLEMENTED(paint.getColorFilter() != nullptr, false);
2144fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2145fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    entry->fMatrix = matrix;
2146fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    entry->fClipStack = clipStack;
2147fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
2148fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    entry->fShaderIndex = -1;
2149fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2150fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // PDF treats a shader as a color, so we only set one or the other.
2151fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFObject> pdfShader;
2152fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkShader* shader = paint.getShader();
2153fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkColor color = paint.getColor();
2154fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (shader) {
2155fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (SkShader::kColor_GradientType == shader->asAGradient(nullptr)) {
2156fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // We don't have to set a shader just for a color.
2157fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkShader::GradientInfo gradientInfo;
2158fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkColor gradientColor = SK_ColorBLACK;
2159fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            gradientInfo.fColors = &gradientColor;
2160fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            gradientInfo.fColorOffsets = nullptr;
2161fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            gradientInfo.fColorCount = 1;
2162fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkAssertResult(shader->asAGradient(&gradientInfo) == SkShader::kColor_GradientType);
2163fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            entry->fColor = SkColorSetA(gradientColor, 0xFF);
2164fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            color = gradientColor;
2165fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        } else {
2166fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // PDF positions patterns relative to the initial transform, so
2167fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // we need to apply the current transform to the shader parameters.
2168fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkMatrix transform = matrix;
2169fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            transform.postConcat(fInitialTransform);
2170fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2171fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // PDF doesn't support kClamp_TileMode, so we simulate it by making
2172fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // a pattern the size of the current clip.
2173fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkRect clipStackBounds = clipStack.bounds(this->bounds());
2174fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2175fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // We need to apply the initial transform to bounds in order to get
2176fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            // bounds in a consistent coordinate system.
2177fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            fInitialTransform.mapRect(&clipStackBounds);
2178fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkIRect bounds;
2179fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            clipStackBounds.roundOut(&bounds);
2180fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2181fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            pdfShader = SkPDFMakeShader(fDocument, shader, transform, bounds, paint.getColor());
2182fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2183fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (pdfShader.get()) {
2184fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // pdfShader has been canonicalized so we can directly compare
2185fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // pointers.
2186fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                int resourceIndex = fShaderResources.find(pdfShader.get());
2187fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (resourceIndex < 0) {
2188fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    resourceIndex = fShaderResources.count();
2189fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    fShaderResources.push(pdfShader.get());
2190fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    pdfShader.get()->ref();
2191fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
2192fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                entry->fShaderIndex = resourceIndex;
2193fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
2194fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2195fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2196fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2197fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFDict> newGraphicState;
2198fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (color == paint.getColor()) {
2199fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), paint);
2200fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
2201fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint newPaint = paint;
2202fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        newPaint.setColor(color);
2203fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(fDocument->canon(), newPaint);
2204fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2205fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int resourceIndex = addGraphicStateResource(newGraphicState.get());
2206fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    entry->fGraphicStateIndex = resourceIndex;
2207fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2208fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (hasText) {
2209fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entry->fTextScaleX = paint.getTextScaleX();
2210fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entry->fTextFill = paint.getStyle();
2211fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
2212fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        entry->fTextScaleX = 0;
2213fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2214fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2215fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2216fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotint SkPDFDevice::addGraphicStateResource(SkPDFObject* gs) {
2217fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Assumes that gs has been canonicalized (so we can directly compare
2218fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // pointers).
2219fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int result = fGraphicStateResources.find(gs);
2220fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (result < 0) {
2221fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        result = fGraphicStateResources.count();
2222fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fGraphicStateResources.push(gs);
2223fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        gs->ref();
2224fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2225fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return result;
2226fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2227fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2228fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotint SkPDFDevice::addXObjectResource(SkPDFObject* xObject) {
2229fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO(halcanary): make this take a sk_sp<SkPDFObject>
2230fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Assumes that xobject has been canonicalized (so we can directly compare
2231fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // pointers).
2232fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int result = fXObjectResources.find(xObject);
2233fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (result < 0) {
2234fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        result = fXObjectResources.count();
2235fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fXObjectResources.push(SkRef(xObject));
2236fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2237fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return result;
2238fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2239fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2240fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotint SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
2241fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFFont> newFont = SkPDFFont::GetFontResource(fDocument->canon(), typeface, glyphID);
2242fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!newFont) {
2243fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return -1;
2244fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2245fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    int resourceIndex = fFontResources.find(newFont.get());
2246fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (resourceIndex < 0) {
2247fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fDocument->registerFont(newFont.get());
2248fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        resourceIndex = fFontResources.count();
2249fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fFontResources.push(newFont.release());
2250fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2251fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return resourceIndex;
2252fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2253fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2254fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; }
2255fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2256fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic sk_sp<SkImage> color_filter(const SkImage* image,
2257fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                   SkColorFilter* colorFilter) {
2258fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    auto surface =
2259fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(image->dimensions()));
2260fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(surface);
2261fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkCanvas* canvas = surface->getCanvas();
2262fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->clear(SK_ColorTRANSPARENT);
2263fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint;
2264fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    paint.setColorFilter(sk_ref_sp(colorFilter));
2265fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    canvas->drawImage(image, 0, 0, &paint);
2266fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return surface->makeImageSnapshot();
2267fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2268fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2269fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot////////////////////////////////////////////////////////////////////////////////
2270fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2271fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool is_integer(SkScalar x) {
2272fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return x == SkScalarTruncToScalar(x);
2273fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2274fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2275fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotstatic bool is_integral(const SkRect& r) {
2276fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return is_integer(r.left()) &&
2277fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           is_integer(r.top()) &&
2278fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           is_integer(r.right()) &&
2279fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot           is_integer(r.bottom());
2280fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2281fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2282fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::internalDrawImageRect(SkKeyedImage imageSubset,
2283fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        const SkRect* src,
2284fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        const SkRect& dst,
2285fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        const SkPaint& srcPaint,
2286fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                        const SkMatrix& ctm) {
2287fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!imageSubset) {
2288fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2289fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2290fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2291fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // First, figure out the src->dst transform and subset the image if needed.
2292fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkIRect bounds = imageSubset.image()->bounds();
2293fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkRect srcRect = src ? *src : SkRect::Make(bounds);
2294fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix transform;
2295fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    transform.setRectToRect(srcRect, dst, SkMatrix::kFill_ScaleToFit);
2296fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (src && *src != SkRect::Make(bounds)) {
2297fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!srcRect.intersect(SkRect::Make(bounds))) {
2298fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2299fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2300fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        srcRect.roundOut(&bounds);
2301fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        transform.preTranslate(SkIntToScalar(bounds.x()),
2302fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                               SkIntToScalar(bounds.y()));
2303fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (bounds != imageSubset.image()->bounds()) {
2304fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            imageSubset = imageSubset.subset(bounds);
2305fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2306fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!imageSubset) {
2307fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2308fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2309fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2310fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2311fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // If the image is opaque and the paint's alpha is too, replace
2312fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // kSrc blendmode with kSrcOver.
2313fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPaint paint = srcPaint;
2314fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (imageSubset.image()->isOpaque()) {
2315fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        replace_srcmode_on_opaque_paint(&paint);
2316fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2317fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2318fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Alpha-only images need to get their color from the shader, before
2319fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // applying the colorfilter.
2320fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (imageSubset.image()->isAlphaOnly() && paint.getColorFilter()) {
2321fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // must blend alpha image and shader before applying colorfilter.
2322fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto surface =
2323fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(imageSubset.image()->dimensions()));
2324fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkCanvas* canvas = surface->getCanvas();
2325fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPaint tmpPaint;
2326fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // In the case of alpha images with shaders, the shader's coordinate
2327fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // system is the image's coordiantes.
2328fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        tmpPaint.setShader(sk_ref_sp(paint.getShader()));
2329fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        tmpPaint.setColor(paint.getColor());
2330fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->clear(0x00000000);
2331fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawImage(imageSubset.image().get(), 0, 0, &tmpPaint);
2332fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint.setShader(nullptr);
2333fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        imageSubset = SkKeyedImage(surface->makeImageSnapshot());
2334fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(!imageSubset.image()->isAlphaOnly());
2335fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2336fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2337fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (imageSubset.image()->isAlphaOnly()) {
2338fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // The ColorFilter applies to the paint color/shader, not the alpha layer.
2339fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(nullptr == paint.getColorFilter());
2340fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2341fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkImage> mask = alpha_image_to_greyscale_image(imageSubset.image().get());
2342fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!mask) {
2343fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2344fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2345fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // PDF doesn't seem to allow masking vector graphics with an Image XObject.
2346fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Must mask with a Form XObject.
2347fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkPDFDevice> maskDevice = this->makeCongruentDevice();
2348fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        {
2349fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkCanvas canvas(maskDevice.get());
2350fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (paint.getMaskFilter()) {
2351fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // This clip prevents the mask image shader from covering
2352fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                // entire device if unnecessary.
2353fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas.clipRect(this->cs().bounds(this->bounds()));
2354fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas.concat(ctm);
2355fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                SkPaint tmpPaint;
2356fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                tmpPaint.setShader(mask->makeShader(&transform));
2357fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                tmpPaint.setMaskFilter(sk_ref_sp(paint.getMaskFilter()));
2358fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas.drawRect(dst, tmpPaint);
2359fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            } else {
2360fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas.concat(ctm);
2361fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                if (src && !is_integral(*src)) {
2362fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                    canvas.clipRect(dst);
2363fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                }
2364fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas.concat(transform);
2365fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                canvas.drawImage(mask, 0, 0);
2366fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
2367fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2368fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!ctm.isIdentity() && paint.getShader()) {
2369fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            transform_shader(&paint, ctm); // Since we are using identity matrix.
2370fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2371fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        ScopedContentEntry content(this, this->cs(), SkMatrix::I(), paint);
2372fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!content.entry()) {
2373fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2374fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2375fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->addSMaskGraphicState(std::move(maskDevice), content.stream());
2376fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFUtils::AppendRectangle(SkRect::Make(fPageSize), content.stream());
2377fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPDFUtils::PaintPath(SkPaint::kFill_Style, SkPath::kWinding_FillType, content.stream());
2378fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->clearMaskOnGraphicState(content.stream());
2379fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2380fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2381fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (paint.getMaskFilter()) {
2382fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        paint.setShader(imageSubset.image()->makeShader(&transform));
2383fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath path;
2384fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        path.addRect(dst);  // handles non-integral clipping.
2385fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->internalDrawPath(this->cs(), this->ctm(), path, paint, nullptr, true);
2386fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2387fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2388fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    transform.postConcat(ctm);
2389fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2390fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    bool needToRestore = false;
2391fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (src && !is_integral(*src)) {
2392fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Need sub-pixel clipping to fix https://bug.skia.org/4374
2393fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->cs().save();
2394fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        this->cs().clipRect(dst, ctm, SkClipOp::kIntersect, true);
2395fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        needToRestore = true;
2396fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2397fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SK_AT_SCOPE_EXIT(if (needToRestore) { this->cs().restore(); });
2398fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2399fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    #ifdef SK_PDF_IMAGE_STATS
2400fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    gDrawImageCalls.fetch_add(1);
2401fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    #endif
2402fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix matrix = transform;
2403fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2404fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Rasterize the bitmap using perspective in a new bitmap.
2405fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (transform.hasPerspective()) {
2406fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(fDocument->rasterDpi() > 0);
2407fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Transform the bitmap in the new space, without taking into
2408fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // account the initial transform.
2409fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath perspectiveOutline;
2410fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect imageBounds = SkRect::Make(imageSubset.image()->bounds());
2411fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        perspectiveOutline.addRect(imageBounds);
2412fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        perspectiveOutline.transform(transform);
2413fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2414fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // TODO(edisonn): perf - use current clip too.
2415fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Retrieve the bounds of the new shape.
2416fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect bounds = perspectiveOutline.getBounds();
2417fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2418fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Transform the bitmap in the new space, taking into
2419fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // account the initial transform.
2420fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkMatrix total = transform;
2421fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        total.postConcat(fInitialTransform);
2422fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar dpiScale = SkIntToScalar(fDocument->rasterDpi()) /
2423fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                            SkIntToScalar(SkPDFUtils::kDpiForRasterScaleOne);
2424fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        total.postScale(dpiScale, dpiScale);
2425fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2426fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath physicalPerspectiveOutline;
2427fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        physicalPerspectiveOutline.addRect(imageBounds);
2428fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        physicalPerspectiveOutline.transform(total);
2429fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2430fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkRect physicalPerspectiveBounds =
2431fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                physicalPerspectiveOutline.getBounds();
2432fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar scaleX = physicalPerspectiveBounds.width() / bounds.width();
2433fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar scaleY = physicalPerspectiveBounds.height() / bounds.height();
2434fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2435fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // TODO(edisonn): A better approach would be to use a bitmap shader
2436fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // (in clamp mode) and draw a rect over the entire bounding box. Then
2437fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // intersect perspectiveOutline to the clip. That will avoid introducing
2438fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // alpha to the image while still giving good behavior at the edge of
2439fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // the image.  Avoiding alpha will reduce the pdf size and generation
2440fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // CPU time some.
2441fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2442fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkISize wh = rect_to_size(physicalPerspectiveBounds).toCeil();
2443fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2444fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        auto surface = SkSurface::MakeRaster(SkImageInfo::MakeN32Premul(wh));
2445fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!surface) {
2446fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2447fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2448fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkCanvas* canvas = surface->getCanvas();
2449fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->clear(SK_ColorTRANSPARENT);
2450fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2451fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar deltaX = bounds.left();
2452fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkScalar deltaY = bounds.top();
2453fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2454fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkMatrix offsetMatrix = transform;
2455fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        offsetMatrix.postTranslate(-deltaX, -deltaY);
2456fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        offsetMatrix.postScale(scaleX, scaleY);
2457fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2458fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Translate the draw in the new canvas, so we perfectly fit the
2459fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // shape in the bitmap.
2460fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->setMatrix(offsetMatrix);
2461fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->drawImage(imageSubset.image(), 0, 0);
2462fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // Make sure the final bits are in the bitmap.
2463fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        canvas->flush();
2464fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2465fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // In the new space, we use the identity matrix translated
2466fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // and scaled to reflect DPI.
2467fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        matrix.setScale(1 / scaleX, 1 / scaleY);
2468fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        matrix.postTranslate(deltaX, deltaY);
2469fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2470fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        imageSubset = SkKeyedImage(surface->makeImageSnapshot());
2471fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!imageSubset) {
2472fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2473fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2474fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2475fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2476fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkMatrix scaled;
2477fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Adjust for origin flip.
2478fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    scaled.setScale(SK_Scalar1, -SK_Scalar1);
2479fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    scaled.postTranslate(0, SK_Scalar1);
2480fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // Scale the image up from 1x1 to WxH.
2481fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkIRect subset = imageSubset.image()->bounds();
2482fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    scaled.postScale(SkIntToScalar(subset.width()),
2483fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                     SkIntToScalar(subset.height()));
2484fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    scaled.postConcat(matrix);
2485fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    ScopedContentEntry content(this, this->cs(), scaled, paint);
2486fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.entry()) {
2487fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2488fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2489fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (content.needShape()) {
2490fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkPath shape;
2491fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        shape.addRect(SkRect::Make(subset));
2492fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        shape.transform(matrix);
2493fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        content.setShape(shape);
2494fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2495fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!content.needSource()) {
2496fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        return;
2497fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2498fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2499fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (SkColorFilter* colorFilter = paint.getColorFilter()) {
2500fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkImage> img = color_filter(imageSubset.image().get(), colorFilter);
2501fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        imageSubset = SkKeyedImage(std::move(img));
2502fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!imageSubset) {
2503fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2504fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2505fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // TODO(halcanary): de-dupe this by caching filtered images.
2506fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // (maybe in the resource cache?)
2507fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2508fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2509fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBitmapKey key = imageSubset.key();
2510fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFObject>* pdfimagePtr = fDocument->canon()->fPDFBitmapMap.find(key);
2511fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    sk_sp<SkPDFObject> pdfimage = pdfimagePtr ? *pdfimagePtr : nullptr;
2512fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (!pdfimage) {
2513fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT(imageSubset);
2514fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        pdfimage = SkPDFCreateBitmapObject(imageSubset.release(),
2515fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                           fDocument->metadata().fEncodingQuality);
2516fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (!pdfimage) {
2517fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            return;
2518fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2519fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fDocument->serialize(pdfimage);  // serialize images early.
2520fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkASSERT((key != SkBitmapKey{{0, 0, 0, 0}, 0}));
2521fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        fDocument->canon()->fPDFBitmapMap.set(key, pdfimage);
2522fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2523fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO(halcanary): addXObjectResource() should take a sk_sp<SkPDFObject>
2524fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkPDFUtils::DrawFormXObject(this->addXObjectResource(pdfimage.get()), content.stream());
2525fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2526fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2527fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot///////////////////////////////////////////////////////////////////////////////////////////////////
2528fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2529fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkSpecialImage.h"
2530fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot#include "SkImageFilter.h"
2531fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2532fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotvoid SkPDFDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, const SkPaint& paint,
2533fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                              SkImage* clipImage, const SkMatrix& clipMatrix) {
2534fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkASSERT(!srcImg->isTextureBacked());
2535fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2536fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    //TODO: clipImage support
2537fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2538fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkBitmap resultBM;
2539fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2540fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkImageFilter* filter = paint.getImageFilter();
2541fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    if (filter) {
2542fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkIPoint offset = SkIPoint::Make(0, 0);
2543fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkMatrix matrix = this->ctm();
2544fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
2545fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        const SkIRect clipBounds =
2546fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->cs().bounds(this->bounds()).roundOut().makeOffset(-x, -y);
2547fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkImageFilterCache> cache(this->getImageFilterCache());
2548fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // TODO: Should PDF be operating in a specified color space? For now, run the filter
2549fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        // in the same color space as the source (this is different from all other backends).
2550fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkImageFilter::OutputProperties outputProperties(srcImg->getColorSpace());
2551fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties);
2552fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2553fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset));
2554fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (resultImg) {
2555fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            SkPaint tmpUnfiltered(paint);
2556fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            tmpUnfiltered.setImageFilter(nullptr);
2557fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            if (resultImg->getROPixels(&resultBM)) {
2558fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered);
2559fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            }
2560fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2561fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    } else {
2562fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        if (srcImg->getROPixels(&resultBM)) {
2563fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot            this->drawSprite(resultBM, x, y, paint);
2564fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot        }
2565fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    }
2566fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2567fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2568fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkBitmap& bitmap) {
2569fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap);
2570fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2571fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2572fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkSpecialImage> SkPDFDevice::makeSpecial(const SkImage* image) {
2573fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // TODO: See comment above in drawSpecial. The color mode we use for decode should be driven
2574fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // by the destination where we're going to draw thing thing (ie this device). But we don't have
2575fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // a color space, so we always decode in legacy mode for now.
2576fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    SkColorSpace* legacyColorSpace = nullptr;
2577fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkSpecialImage::MakeFromImage(image->bounds(),
2578fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot                                         image->makeNonTextureImage(), legacyColorSpace);
2579fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2580fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2581fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robotsk_sp<SkSpecialImage> SkPDFDevice::snapSpecial() {
2582fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return nullptr;
2583fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2584fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot
2585fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team RobotSkImageFilterCache* SkPDFDevice::getImageFilterCache() {
2586fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // We always return a transient cache, so it is freed after each
2587fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    // filter traversal.
2588fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot    return SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize);
2589fe17456d5e528078ce69b5f15cf7adf1fab963fandroid-build-team Robot}
2590