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