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