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