aboutsummaryrefslogtreecommitdiffhomepage
path: root/tools/skpinfo.cpp
blob: 53ea8f326dccbd2a898500f33c96c52f39874ff6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
/*
 * Copyright 2014 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkCommandLineFlags.h"
#include "SkFontDescriptor.h"
#include "SkPicture.h"
#include "SkPictureCommon.h"
#include "SkPictureData.h"
#include "SkStream.h"
#include "SkTo.h"

DEFINE_string2(input, i, "", "skp on which to report");
DEFINE_bool2(version, v, true, "version");
DEFINE_bool2(cullRect, c, true, "cullRect");
DEFINE_bool2(flags, f, true, "flags");
DEFINE_bool2(tags, t, true, "tags");
DEFINE_bool2(quiet, q, false, "quiet");

// This tool can print simple information about an SKP but its main use
// is just to check if an SKP has been truncated during the recording
// process.
// return codes:
static const int kSuccess = 0;
static const int kTruncatedFile = 1;
static const int kNotAnSKP = 2;
static const int kInvalidTag = 3;
static const int kMissingInput = 4;
static const int kIOError = 5;

int main(int argc, char** argv) {
    SkCommandLineFlags::SetUsage("Prints information about an skp file");
    SkCommandLineFlags::Parse(argc, argv);

    if (FLAGS_input.count() != 1) {
        if (!FLAGS_quiet) {
            SkDebugf("Missing input file\n");
        }
        return kMissingInput;
    }

    SkFILEStream stream(FLAGS_input[0]);
    if (!stream.isValid()) {
        if (!FLAGS_quiet) {
            SkDebugf("Couldn't open file\n");
        }
        return kIOError;
    }

    size_t totStreamSize = stream.getLength();

    SkPictInfo info;
    if (!SkPicture_StreamIsSKP(&stream, &info)) {
        return kNotAnSKP;
    }

    if (FLAGS_version && !FLAGS_quiet) {
        SkDebugf("Version: %d\n", info.getVersion());
    }
    if (FLAGS_cullRect && !FLAGS_quiet) {
        SkDebugf("Cull Rect: %f,%f,%f,%f\n",
                 info.fCullRect.fLeft, info.fCullRect.fTop,
                 info.fCullRect.fRight, info.fCullRect.fBottom);
    }

    bool hasData;
    if (!stream.readBool(&hasData)) { return kTruncatedFile; }
    if (!hasData) {
        // If we read true there's a picture playback object flattened
        // in the file; if false, there isn't a playback, so we're done
        // reading the file.
        return kSuccess;
    }

    for (;;) {
        uint32_t tag;
        if (!stream.readU32(&tag)) { return kTruncatedFile; }
        if (SK_PICT_EOF_TAG == tag) {
            break;
        }

        uint32_t chunkSize;
        if (!stream.readU32(&chunkSize)) { return kTruncatedFile; }
        size_t curPos = stream.getPosition();

        // "move" doesn't error out when seeking beyond the end of file
        // so we need a preemptive check here.
        if (curPos+chunkSize > totStreamSize) {
            if (!FLAGS_quiet) {
                SkDebugf("truncated file\n");
            }
            return kTruncatedFile;
        }

        // Not all the tags store the chunk size (in bytes). Three
        // of them store tag-specific size information (e.g., number of
        // fonts) instead. This forces us to early exit when those
        // chunks are encountered.
        switch (tag) {
        case SK_PICT_READER_TAG:
            if (FLAGS_tags && !FLAGS_quiet) {
                SkDebugf("SK_PICT_READER_TAG %d\n", chunkSize);
            }
            break;
        case SK_PICT_FACTORY_TAG:
            if (FLAGS_tags && !FLAGS_quiet) {
                SkDebugf("SK_PICT_FACTORY_TAG %d\n", chunkSize);
            }
            break;
        case SK_PICT_TYPEFACE_TAG: {
            if (FLAGS_tags && !FLAGS_quiet) {
                SkDebugf("SK_PICT_TYPEFACE_TAG %d\n", chunkSize);
            }

            const int count = SkToInt(chunkSize);
            for (int i = 0; i < count; i++) {
                SkFontDescriptor desc;
                if (!SkFontDescriptor::Deserialize(&stream, &desc)) {
                    if (!FLAGS_quiet) {
                        SkDebugf("File corruption in SkFontDescriptor\n");
                    }
                    return kInvalidTag;
                }
            }

            // clear this since we've consumed all the typefaces
            chunkSize = 0;
            break;
        }
        case SK_PICT_PICTURE_TAG:
            if (FLAGS_tags && !FLAGS_quiet) {
                SkDebugf("SK_PICT_PICTURE_TAG %d\n", chunkSize);
                SkDebugf("Exiting early due to format limitations\n");
            }
            return kSuccess;       // TODO: need to store size in bytes
            break;
        case SK_PICT_BUFFER_SIZE_TAG:
            if (FLAGS_tags && !FLAGS_quiet) {
                SkDebugf("SK_PICT_BUFFER_SIZE_TAG %d\n", chunkSize);
            }
            break;
        default:
            if (!FLAGS_quiet) {
                SkDebugf("Unknown tag %d\n", chunkSize);
            }
            return kInvalidTag;
        }

        if (!stream.move(chunkSize)) {
            if (!FLAGS_quiet) {
                SkDebugf("seek error\n");
            }
            return kTruncatedFile;
        }
    }

    return kSuccess;
}