• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "DMWriteTask.h"
2 
3 #include "DMUtil.h"
4 #include "SkColorPriv.h"
5 #include "SkCommonFlags.h"
6 #include "SkData.h"
7 #include "SkImageEncoder.h"
8 #include "SkMD5.h"
9 #include "SkMallocPixelRef.h"
10 #include "SkOSFile.h"
11 #include "SkStream.h"
12 #include "SkString.h"
13 
14 DEFINE_bool(nameByHash, false, "If true, write .../hash.png instead of .../mode/config/name.png");
15 
16 namespace DM {
17 
18 // Splits off the last N suffixes of name (splitting on _) and appends them to out.
19 // Returns the total number of characters consumed.
split_suffixes(int N,const char * name,SkTArray<SkString> * out)20 static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) {
21     SkTArray<SkString> split;
22     SkStrSplit(name, "_", &split);
23     int consumed = 0;
24     for (int i = 0; i < N; i++) {
25         // We're splitting off suffixes from the back to front.
26         out->push_back(split[split.count()-i-1]);
27         consumed += out->back().size() + 1;  // Add one for the _.
28     }
29     return consumed;
30 }
31 
find_base_name(const Task & parent,SkTArray<SkString> * suffixList)32 inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* suffixList) {
33     const int suffixes = parent.depth() + 1;
34     const SkString& name = parent.name();
35     const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList);
36     return SkString(name.c_str(), name.size() - totalSuffixLength);
37 }
38 
WriteTask(const Task & parent,const char * sourceType,SkBitmap bitmap)39 WriteTask::WriteTask(const Task& parent, const char* sourceType, SkBitmap bitmap)
40     : CpuTask(parent)
41     , fBaseName(find_base_name(parent, &fSuffixes))
42     , fSourceType(sourceType)
43     , fBitmap(bitmap)
44     , fData(NULL)
45     , fExtension(".png") {
46 }
47 
WriteTask(const Task & parent,const char * sourceType,SkStreamAsset * data,const char * ext)48 WriteTask::WriteTask(const Task& parent,
49                      const char* sourceType,
50                      SkStreamAsset *data,
51                      const char* ext)
52     : CpuTask(parent)
53     , fBaseName(find_base_name(parent, &fSuffixes))
54     , fSourceType(sourceType)
55     , fData(data)
56     , fExtension(ext) {
57     SkASSERT(fData.get());
58     SkASSERT(fData->unique());
59 }
60 
makeDirOrFail(SkString dir)61 void WriteTask::makeDirOrFail(SkString dir) {
62     if (!sk_mkdir(dir.c_str())) {
63         this->fail();
64     }
65 }
66 
get_md5(const void * ptr,size_t len)67 static SkString get_md5(const void* ptr, size_t len) {
68     SkMD5 hasher;
69     hasher.write(ptr, len);
70     SkMD5::Digest digest;
71     hasher.finish(digest);
72 
73     SkString md5;
74     for (int i = 0; i < 16; i++) {
75         md5.appendf("%02x", digest.data[i]);
76     }
77     return md5;
78 }
79 
80 struct JsonData {
81     SkString name;            // E.g. "ninepatch-stretch", "desk-gws_skp"
82     SkString config;          //      "gpu", "8888"
83     SkString mode;            //      "direct", "default-tilegrid", "pipe"
84     SkString sourceType;      //      "GM", "SKP"
85     SkString md5;             // In ASCII, so 32 bytes long.
86 };
87 SkTArray<JsonData> gJsonData;
88 SK_DECLARE_STATIC_MUTEX(gJsonDataLock);
89 
draw()90 void WriteTask::draw() {
91     SkString md5;
92     {
93         SkAutoLockPixels lock(fBitmap);
94         md5 = fData ? get_md5(fData->getMemoryBase(), fData->getLength())
95                     : get_md5(fBitmap.getPixels(), fBitmap.getSize());
96     }
97 
98     SkASSERT(fSuffixes.count() > 0);
99     SkString config = fSuffixes.back();
100     SkString mode("direct");
101     if (fSuffixes.count() > 1) {
102         mode = fSuffixes.fromBack(1);
103     }
104 
105     JsonData entry = { fBaseName, config, mode, fSourceType, md5 };
106     {
107         SkAutoMutexAcquire lock(&gJsonDataLock);
108         gJsonData.push_back(entry);
109     }
110 
111     SkString dir(FLAGS_writePath[0]);
112 #if SK_BUILD_FOR_IOS
113     if (dir.equals("@")) {
114         dir.set(FLAGS_resourcePath[0]);
115     }
116 #endif
117     this->makeDirOrFail(dir);
118 
119     SkString path;
120     if (FLAGS_nameByHash) {
121         // Flat directory of hash-named files.
122         path = SkOSPath::Join(dir.c_str(), md5.c_str());
123         path.append(fExtension);
124         // We're content-addressed, so it's possible two threads race to write
125         // this file.  We let the first one win.  This also means we won't
126         // overwrite identical files from previous runs.
127         if (sk_exists(path.c_str())) {
128             return;
129         }
130     } else {
131         // Nested by mode, config, etc.
132         for (int i = 0; i < fSuffixes.count(); i++) {
133             dir = SkOSPath::Join(dir.c_str(), fSuffixes[i].c_str());
134             this->makeDirOrFail(dir);
135         }
136         path = SkOSPath::Join(dir.c_str(), fBaseName.c_str());
137         path.append(fExtension);
138         // The path is unique, so two threads can't both write to the same file.
139         // If already present we overwrite here, since the content may have changed.
140     }
141 
142     SkFILEWStream file(path.c_str());
143     if (!file.isValid()) {
144         return this->fail("Can't open file.");
145     }
146 
147     bool ok = fData ? file.writeStream(fData, fData->getLength())
148                     : SkImageEncoder::EncodeStream(&file, fBitmap, SkImageEncoder::kPNG_Type, 100);
149     if (!ok) {
150         return this->fail("Can't write to file.");
151     }
152 }
153 
name() const154 SkString WriteTask::name() const {
155     SkString name("writing ");
156     for (int i = 0; i < fSuffixes.count(); i++) {
157         name.appendf("%s/", fSuffixes[i].c_str());
158     }
159     name.append(fBaseName.c_str());
160     return name;
161 }
162 
shouldSkip() const163 bool WriteTask::shouldSkip() const {
164     return FLAGS_writePath.isEmpty();
165 }
166 
DumpJson()167 void WriteTask::DumpJson() {
168     if (FLAGS_writePath.isEmpty()) {
169         return;
170     }
171 
172     Json::Value root;
173 
174     for (int i = 1; i < FLAGS_properties.count(); i += 2) {
175         root[FLAGS_properties[i-1]] = FLAGS_properties[i];
176     }
177     for (int i = 1; i < FLAGS_key.count(); i += 2) {
178         root["key"][FLAGS_key[i-1]] = FLAGS_key[i];
179     }
180 
181     {
182         SkAutoMutexAcquire lock(&gJsonDataLock);
183         for (int i = 0; i < gJsonData.count(); i++) {
184             Json::Value result;
185             result["key"]["name"]            = gJsonData[i].name.c_str();
186             result["key"]["config"]          = gJsonData[i].config.c_str();
187             result["key"]["mode"]            = gJsonData[i].mode.c_str();
188             result["options"]["source_type"] = gJsonData[i].sourceType.c_str();
189             result["md5"]                    = gJsonData[i].md5.c_str();
190 
191             root["results"].append(result);
192         }
193     }
194 
195     SkString path = SkOSPath::Join(FLAGS_writePath[0], "dm.json");
196     SkFILEWStream stream(path.c_str());
197     stream.writeText(Json::StyledWriter().write(root).c_str());
198     stream.flush();
199 }
200 
201 }  // namespace DM
202