1// Copyright 2019 Google LLC.
2// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3
4#include "src/core/SkTextBlobTrace.h"
5
6#include "include/core/SkTextBlob.h"
7#include "src/core/SkFontPriv.h"
8#include "src/core/SkPtrRecorder.h"
9#include "src/core/SkReadBuffer.h"
10#include "src/core/SkTextBlobPriv.h"
11#include "src/core/SkWriteBuffer.h"
12
13std::vector<SkTextBlobTrace::Record> SkTextBlobTrace::CreateBlobTrace(SkStream* stream) {
14    std::vector<SkTextBlobTrace::Record> trace;
15
16    uint32_t typefaceCount;
17    if (!stream->readU32(&typefaceCount)) {
18        return trace;
19    }
20
21    std::vector<sk_sp<SkTypeface>> typefaceArray;
22    for (uint32_t i = 0; i < typefaceCount; i++) {
23        typefaceArray.push_back(SkTypeface::MakeDeserialize(stream));
24    }
25
26    uint32_t restOfFile;
27    if (!stream->readU32(&restOfFile)) {
28        return trace;
29    }
30    sk_sp<SkData> data = SkData::MakeFromStream(stream, restOfFile);
31    SkReadBuffer readBuffer{data->data(), data->size()};
32    readBuffer.setTypefaceArray(typefaceArray.data(), typefaceArray.size());
33
34    while (!readBuffer.eof()) {
35        SkTextBlobTrace::Record record;
36        record.origUniqueID = readBuffer.readUInt();
37        record.paint = readBuffer.readPaint();
38        readBuffer.readPoint(&record.offset);
39        record.blob = SkTextBlobPriv::MakeFromBuffer(readBuffer);
40        trace.push_back(std::move(record));
41    }
42    return trace;
43}
44
45void SkTextBlobTrace::DumpTrace(const std::vector<SkTextBlobTrace::Record>& trace) {
46    for (const SkTextBlobTrace::Record& record : trace) {
47        const SkTextBlob* blob = record.blob.get();
48        const SkPaint& p = record.paint;
49        bool weirdPaint = p.getStyle() != SkPaint::kFill_Style
50        || p.getMaskFilter() != nullptr
51        || p.getPathEffect() != nullptr;
52
53        SkDebugf("Blob %d ( %g %g ) %d\n  ",
54                blob->uniqueID(), record.offset.x(), record.offset.y(), weirdPaint);
55        SkTextBlobRunIterator iter(blob);
56        int runNumber = 0;
57        while (!iter.done()) {
58            SkDebugf("Run %d\n    ", runNumber);
59            SkFont font = iter.font();
60            SkDebugf("Font %d %g %g %g %d %d %d\n    ",
61                    font.getTypefaceOrDefault()->uniqueID(),
62                    font.getSize(),
63                    font.getScaleX(),
64                    font.getSkewX(),
65                    SkFontPriv::Flags(font),
66                    (int)font.getEdging(),
67                    (int)font.getHinting());
68            uint32_t glyphCount = iter.glyphCount();
69            const uint16_t* glyphs = iter.glyphs();
70            for (uint32_t i = 0; i < glyphCount; i++) {
71                SkDebugf("%02X ", glyphs[i]);
72            }
73            SkDebugf("\n");
74            runNumber += 1;
75            iter.next();
76        }
77    }
78}
79
80SkTextBlobTrace::Capture::Capture() : fTypefaceSet(new SkRefCntSet) {
81    fWriteBuffer.setTypefaceRecorder(fTypefaceSet);
82}
83
84SkTextBlobTrace::Capture::~Capture() = default;
85
86void SkTextBlobTrace::Capture::capture(const SkGlyphRunList& glyphRunList, const SkPaint& paint) {
87    const SkTextBlob* blob = glyphRunList.blob();
88    if (blob != nullptr) {
89        fWriteBuffer.writeUInt(blob->uniqueID());
90        fWriteBuffer.writePaint(paint);
91        fWriteBuffer.writePoint(glyphRunList.origin());
92        SkTextBlobPriv::Flatten(*blob, fWriteBuffer);
93        fBlobCount++;
94    }
95}
96
97void SkTextBlobTrace::Capture::dump(SkWStream* dst) const {
98    SkTLazy<SkFILEWStream> fileStream;
99    if (!dst) {
100        uint32_t id = SkChecksum::Mix(reinterpret_cast<uintptr_t>(this));
101        SkString f = SkStringPrintf("diff-canvas-%08x-%04zu.trace", id, fBlobCount);
102        dst = fileStream.init(f.c_str());
103        if (!fileStream->isValid()) {
104            SkDebugf("Error opening '%s'.\n", f.c_str());
105            return;
106        }
107        SkDebugf("Saving trace to '%s'.\n", f.c_str());
108    }
109    SkASSERT(dst);
110    int count = fTypefaceSet->count();
111    dst->write32(count);
112    SkPtrSet::Iter iter(*fTypefaceSet);
113    while (void* ptr = iter.next()) {
114        ((const SkTypeface*)ptr)->serialize(dst, SkTypeface::SerializeBehavior::kDoIncludeData);
115    }
116    dst->write32(fWriteBuffer.bytesWritten());
117    fWriteBuffer.writeToStream(dst);
118}
119