Bug Summary

File:root/firefox-clang/gfx/skia/skia/include/core/SkRefCnt.h
Warning:line 256, column 9
Use of memory after it is released

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name SkPDFDevice.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_SKIA -D SKIA_IMPLEMENTATION=1 -D SK_PDF_USE_HARFBUZZ_SUBSET=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/gfx/skia -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/gfx/skia -I /root/firefox-clang/gfx/skia/skia -I /root/firefox-clang/gfx/harfbuzz/src -I /root/firefox-clang/gfx/cairo/cairo/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/pango-1.0 -I /usr/include/harfbuzz -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/pixman-1 -I /usr/include/sysprof-6 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-deprecated-declarations -Wno-overloaded-virtual -Wno-sign-compare -Wno-unreachable-code -Wno-unused-function -Wno-implicit-fallthrough -Wno-inconsistent-missing-override -Wno-macro-redefined -Wno-unused-private-field -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/gfx/skia/skia/src/pdf/SkPDFDevice.cpp

/root/firefox-clang/gfx/skia/skia/src/pdf/SkPDFDevice.cpp

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
93class SkBlender;
94class SkMesh;
95class SkVertices;
96
97using namespace skia_private;
98
99SkPDFDevice::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
109SkPDFDevice::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
115void SkPDFDevice::MarkedContentManager::setNextMarksElemId(int nextMarksElemId) {
116 fNextMarksElemId = nextMarksElemId;
117}
118int SkPDFDevice::MarkedContentManager::elemId() const { return fNextMarksElemId; }
119
120void 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
172bool SkPDFDevice::MarkedContentManager::hasActiveMark() const { return bool(fCurrentlyActiveMark); }
173
174void 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.
180sk_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
211sk_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
224static int add_resource(THashSet<SkPDFIndirectReference>& resources, SkPDFIndirectReference ref) {
225 resources.add(ref);
226 return ref.fValue;
227}
228
229static 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
242static 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
265static 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
282static 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 */
292static 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
302sk_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.
320class ScopedContentEntry {
321public:
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
389private:
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
400SkPDFDevice::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
410sk_sp<SkPDFDevice> SkPDFDevice::makeCongruentDevice() {
411 return sk_make_sp<SkPDFDevice>(this->size(), fDocument);
412}
413
414SkPDFDevice::~SkPDFDevice() = default;
415
416void 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
426void 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
476void 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
491void 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
583void 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
589void SkPDFDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
590 this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::RRect(rrect), paint);
591}
592
593void SkPDFDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
594 this->internalDrawPath(this->cs(), this->localToDevice(), SkPath::Oval(oval), paint);
595}
596
597void SkPDFDevice::drawPath(const SkPath& path, const SkPaint& paint) {
598 this->internalDrawPath(this->cs(), this->localToDevice(), path, paint);
599}
600
601void 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
659void SkPDFDevice::setGraphicState(SkPDFIndirectReference gs, SkDynamicMemoryWStream* content) {
660 SkPDFUtils::ApplyGraphicState(add_resource(fGraphicStateResources, gs), content);
661}
662
663void 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
674void 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
753void 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); }() )
;
1
Assuming the condition is false
2
'?' condition is false
760 this->internalDrawImageRect(SkKeyedImage(sk_ref_sp(const_cast<SkImage*>(image))),
3
Calling 'SkPDFDevice::internalDrawImageRect'
761 src, dst, sampling, paint, this->localToDevice());
762}
763
764void 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
773namespace {
774class GlyphPositioner {
775public:
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
840private:
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
853namespace {
854struct PositionedGlyph {
855 SkPoint fPos;
856 SkGlyphID fGlyph;
857};
858} // namespace
859
860static 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
869static 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
874void 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
913static 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
930void 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
1106void 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
1115void SkPDFDevice::drawVertices(const SkVertices*, sk_sp<SkBlender>, const SkPaint&, bool) {
1116 if (this->hasEmptyClip()) {
1117 return;
1118 }
1119 // TODO: implement drawVertices
1120}
1121
1122void SkPDFDevice::drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) {
1123 if (this->hasEmptyClip()) {
1124 return;
1125 }
1126 // TODO: implement drawMesh
1127}
1128
1129void 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
1149sk_sp<SkSurface> SkPDFDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
1150 return SkSurfaces::Raster(info, &props);
1151}
1152
1153static 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
1164std::unique_ptr<SkPDFDict> SkPDFDevice::makeResourceDict() {
1165 return SkPDFMakeResourceDict(sort(fGraphicStateResources),
1166 sort(fShaderResources),
1167 sort(fXObjectResources),
1168 sort(fFontResources));
1169}
1170
1171std::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 */
1212bool 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
1260SkPDFIndirectReference 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
1282SkPDFIndirectReference SkPDFDevice::makeFormXObjectFromDevice(bool alpha) {
1283 return this->makeFormXObjectFromDevice(SkIRect{0, 0, this->width(), this->height()}, alpha);
1284}
1285
1286void 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
1305static bool treat_as_regular_pdf_blend_mode(SkBlendMode blendMode) {
1306 return nullptr != SkPDFUtils::BlendModeName(blendMode);
1307}
1308
1309static 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
1381SkDynamicMemoryWStream* 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
1444void 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
1581bool SkPDFDevice::isContentEmpty() {
1582 return fContent.bytesWritten() == 0 && fContentBuffer.bytesWritten() == 0;
1583}
1584
1585static SkSize rect_to_size(const SkRect& r) { return {r.width(), r.height()}; }
1586
1587static 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
1601static bool is_integer(SkScalar x) {
1602 return x == SkScalarTruncToScalar(x)std::trunc(x);
1603}
1604
1605static 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
1612void 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()) {
4
Assuming the condition is false
5
Taking false branch
1619 return;
1620 }
1621 if (!imageSubset) {
6
Taking false branch
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);
7
Assuming 'src' is non-null
8
'?' condition is true
1632 SkMatrix transform = SkMatrix::RectToRectOrIdentity(srcRect, dst);
1633 SkMatrix originalTransform = transform;
1634 if (src
8.1
'src' is non-null
8.1
'src' is non-null
8.1
'src' is non-null
8.1
'src' is non-null
&& *src != SkRect::Make(bounds)) {
9
Taking true branch
1635 if (!srcRect.intersect(SkRect::Make(bounds))) {
10
Assuming the condition is false
11
Taking false branch
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()) {
12
Taking true branch
1642 imageSubset = imageSubset.subset(bounds);
13
Calling defaulted move assignment operator for 'SkKeyedImage'
34
Returning; memory was released
1643 didSubset = true;
1644 }
1645 if (!imageSubset) {
35
Taking true branch
1646 return;
36
Calling implicit destructor for 'SkKeyedImage'
37
Calling '~sk_sp'
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
1870void 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
1916void 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}

/root/firefox-clang/gfx/skia/skia/src/pdf/SkKeyedImage.h

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
14class SkBitmap;
15struct 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 */
22class SkKeyedImage {
23public:
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;
14
Calling move assignment operator for 'sk_sp<SkImage>'
33
Returning; memory was released
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
39private:
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 */
48SkBitmapKey SkBitmapKeyFromImage(const SkImage*);
49#endif // SkKeyedImage_DEFINED

/root/firefox-clang/gfx/skia/skia/include/core/SkRefCnt.h

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*/
31class SK_API SkRefCntBase {
32public:
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); }() )
;
19
Assuming the condition is true
20
'?' condition is true
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)) {
21
Assuming the condition is true
22
Taking true branch
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();
23
Calling 'SkRefCntBase::internal_dispose'
29
Returning; memory was released
79 }
80 }
81
82private:
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); }() )
;
24
Assuming the condition is true
25
'?' condition is true
97 fRefCnt.store(1, std::memory_order_relaxed);
98 #endif
99 delete this;
26
Calling 'operator delete'
28
Returning from 'operator delete'
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
119class 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 */
132template <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 */
140template <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 */
149template <typename T> static inline void SkSafeUnref(T* obj) {
150 if (obj
16.1
'obj' is non-null
16.1
'obj' is non-null
16.1
'obj' is non-null
16.1
'obj' is non-null
) {
17
Taking true branch
151 obj->unref();
18
Calling 'SkRefCntBase::unref'
30
Returning; memory was released
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.
159template <typename Derived>
160class SkNVRefCnt {
161public:
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
198private:
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 */
220template <typename T> class SK_TRIVIAL_ABI sk_sp {
221public:
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);
38
Use of memory after it is released
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());
15
Calling 'sk_sp::reset'
32
Returning; memory was released
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);
16
Calling 'SkSafeUnref<SkImage>'
31
Returning; memory was released via 1st parameter
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
337private:
338 T* fPtr;
339};
340
341template <typename T> inline void swap(sk_sp<T>& a, sk_sp<T>& b) /*noexcept*/ {
342 a.swap(b);
343}
344
345template <typename T, typename U> inline bool operator==(const sk_sp<T>& a, const sk_sp<U>& b) {
346 return a.get() == b.get();
347}
348template <typename T> inline bool operator==(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ {
349 return !a;
350}
351template <typename T> inline bool operator==(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ {
352 return !b;
353}
354
355template <typename T, typename U> inline bool operator!=(const sk_sp<T>& a, const sk_sp<U>& b) {
356 return a.get() != b.get();
357}
358template <typename T> inline bool operator!=(const sk_sp<T>& a, std::nullptr_t) /*noexcept*/ {
359 return static_cast<bool>(a);
360}
361template <typename T> inline bool operator!=(std::nullptr_t, const sk_sp<T>& b) /*noexcept*/ {
362 return static_cast<bool>(b);
363}
364
365template <typename C, typename CT, typename T>
366auto operator<<(std::basic_ostream<C, CT>& os, const sk_sp<T>& sp) -> decltype(os << sp.get()) {
367 return os << sp.get();
368}
369
370template <typename T> sk_sp(T*) -> sk_sp<T>;
371
372template <typename T, typename... Args>
373sk_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 */
383template <typename T> sk_sp<T> sk_ref_sp(T* obj) {
384 return sk_sp<T>(SkSafeRef(obj));
385}
386
387template <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

/root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/mozilla/cxxalloc.h

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
32MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void* operator new(size_t size) noexcept(false) {
33 return moz_xmalloc(size);
34}
35
36MOZALLOC_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
41MOZALLOC_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.
46MOZALLOC_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
63MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr) noexcept(true) {
64 return free_impl(ptr);
27
Memory is released
65}
66
67MOZALLOC_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
72MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete[](void* ptr) noexcept(true) {
73 return free_impl(ptr);
74}
75
76MOZALLOC_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-
84MOZALLOC_EXPORT_NEW__attribute__((always_inline)) inline void operator delete(void* ptr,
85 size_t /*size*/) noexcept(true) {
86 return free_impl(ptr);
87}
88
89MOZALLOC_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 */