blob: 2a129a57b16351380ac4e77644c44c0ae011119e [file] [log] [blame]
mtklein@google.coma7a9f372013-10-18 20:52:44 +00001#include "DMWriteTask.h"
2
3#include "DMUtil.h"
commit-bot@chromium.org389fb7f2014-01-15 21:28:25 +00004#include "SkColorPriv.h"
caryclark17f0b6d2014-07-22 10:15:34 -07005#include "SkCommonFlags.h"
mtklein@google.coma7a9f372013-10-18 20:52:44 +00006#include "SkImageEncoder.h"
mtklein1d0f1642014-09-08 08:05:18 -07007#include "SkMD5.h"
commit-bot@chromium.orgeef834f2014-03-05 15:37:11 +00008#include "SkMallocPixelRef.h"
mtklein1d0f1642014-09-08 08:05:18 -07009#include "SkOSFile.h"
commit-bot@chromium.org1426c1e2014-03-03 15:43:56 +000010#include "SkStream.h"
commit-bot@chromium.orgeef834f2014-03-05 15:37:11 +000011#include "SkString.h"
mtklein@google.coma7a9f372013-10-18 20:52:44 +000012
mtklein@google.coma7a9f372013-10-18 20:52:44 +000013namespace DM {
14
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000015// Splits off the last N suffixes of name (splitting on _) and appends them to out.
16// Returns the total number of characters consumed.
17static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) {
rmistry@google.comd6bab022013-12-02 13:50:38 +000018 SkTArray<SkString> split;
19 SkStrSplit(name, "_", &split);
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000020 int consumed = 0;
21 for (int i = 0; i < N; i++) {
rmistry@google.comd6bab022013-12-02 13:50:38 +000022 // We're splitting off suffixes from the back to front.
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000023 out->push_back(split[split.count()-i-1]);
24 consumed += out->back().size() + 1; // Add one for the _.
rmistry@google.comd6bab022013-12-02 13:50:38 +000025 }
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000026 return consumed;
27}
28
mtklein1d0f1642014-09-08 08:05:18 -070029inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* suffixList) {
commit-bot@chromium.orgd6dcacd2014-05-14 20:26:00 +000030 const int suffixes = parent.depth() + 1;
31 const SkString& name = parent.name();
mtklein30bf3e22014-06-03 13:57:14 -070032 const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList);
33 return SkString(name.c_str(), name.size() - totalSuffixLength);
rmistry@google.comd6bab022013-12-02 13:50:38 +000034}
mtklein@google.coma7a9f372013-10-18 20:52:44 +000035
mtklein1d0f1642014-09-08 08:05:18 -070036struct JsonData {
37 SkString name;
38 SkMD5::Digest md5;
39};
40SkTArray<JsonData> gJsonData;
41SK_DECLARE_STATIC_MUTEX(gJsonDataLock);
42
mtklein30bf3e22014-06-03 13:57:14 -070043WriteTask::WriteTask(const Task& parent, SkBitmap bitmap)
44 : CpuTask(parent)
mtklein1d0f1642014-09-08 08:05:18 -070045 , fFullName(parent.name())
46 , fBaseName(find_base_name(parent, &fSuffixes))
mtklein30bf3e22014-06-03 13:57:14 -070047 , fBitmap(bitmap)
48 , fData(NULL)
mtklein1d0f1642014-09-08 08:05:18 -070049 , fExtension(".png") {
50}
mtklein30bf3e22014-06-03 13:57:14 -070051
halcanarya4c60942014-08-26 10:38:07 -070052WriteTask::WriteTask(const Task& parent, SkStreamAsset *data, const char* ext)
mtklein30bf3e22014-06-03 13:57:14 -070053 : CpuTask(parent)
mtklein1d0f1642014-09-08 08:05:18 -070054 , fFullName(parent.name())
55 , fBaseName(find_base_name(parent, &fSuffixes))
halcanarya4c60942014-08-26 10:38:07 -070056 , fData(data)
57 , fExtension(ext) {
58 SkASSERT(fData.get());
59 SkASSERT(fData->unique());
60}
mtklein30bf3e22014-06-03 13:57:14 -070061
rmistry@google.comd6bab022013-12-02 13:50:38 +000062void WriteTask::makeDirOrFail(SkString dir) {
63 if (!sk_mkdir(dir.c_str())) {
64 this->fail();
65 }
mtklein@google.coma7a9f372013-10-18 20:52:44 +000066}
67
mtklein1d0f1642014-09-08 08:05:18 -070068static bool save_bitmap_to_file(SkBitmap bitmap, const char* path) {
mtklein30bf3e22014-06-03 13:57:14 -070069 SkFILEWStream stream(path);
mtklein1d0f1642014-09-08 08:05:18 -070070 if (!stream.isValid() ||
71 !SkImageEncoder::EncodeStream(&stream, bitmap, SkImageEncoder::kPNG_Type, 100)) {
72 SkDebugf("Can't write a PNG to %s.\n", path);
mtklein30bf3e22014-06-03 13:57:14 -070073 return false;
74 }
75 return true;
76}
77
mtklein1d0f1642014-09-08 08:05:18 -070078// Does not take ownership of data.
79static bool save_data_to_file(SkStreamAsset* data, const char* path) {
80 data->rewind();
81 SkFILEWStream stream(path);
82 if (!stream.isValid() || !stream.writeStream(data, data->getLength())) {
83 SkDebugf("Can't write data to %s.\n", path);
84 return false;
85 }
86 return true;
87}
commit-bot@chromium.orgeef834f2014-03-05 15:37:11 +000088
mtklein@google.coma7a9f372013-10-18 20:52:44 +000089void WriteTask::draw() {
rmistry@google.comd6bab022013-12-02 13:50:38 +000090 SkString dir(FLAGS_writePath[0]);
caryclark17f0b6d2014-07-22 10:15:34 -070091#if SK_BUILD_FOR_IOS
92 if (dir.equals("@")) {
93 dir.set(FLAGS_resourcePath[0]);
94 }
95#endif
rmistry@google.comd6bab022013-12-02 13:50:38 +000096 this->makeDirOrFail(dir);
97 for (int i = 0; i < fSuffixes.count(); i++) {
tfarinaa8e2e152014-07-28 19:26:58 -070098 dir = SkOSPath::Join(dir.c_str(), fSuffixes[i].c_str());
rmistry@google.comd6bab022013-12-02 13:50:38 +000099 this->makeDirOrFail(dir);
100 }
mtklein30bf3e22014-06-03 13:57:14 -0700101
mtklein1d0f1642014-09-08 08:05:18 -0700102 // FIXME: MD5 is really slow. Let's use a different hash.
103 SkMD5 hasher;
104 if (fData.get()) {
105 hasher.write(fData->getMemoryBase(), fData->getLength());
106 } else {
107 SkAutoLockPixels lock(fBitmap);
108 hasher.write(fBitmap.getPixels(), fBitmap.getSize());
109 }
110
111 JsonData entry;
112 entry.name = fFullName;
113 hasher.finish(entry.md5);
114
115 {
116 SkAutoMutexAcquire lock(&gJsonDataLock);
117 gJsonData.push_back(entry);
118 }
119
120 SkString path = SkOSPath::Join(dir.c_str(), fBaseName.c_str());
mtklein30bf3e22014-06-03 13:57:14 -0700121 path.append(fExtension);
122
halcanarya4c60942014-08-26 10:38:07 -0700123 const bool ok = fData.get() ? save_data_to_file(fData.get(), path.c_str())
mtklein1d0f1642014-09-08 08:05:18 -0700124 : save_bitmap_to_file(fBitmap, path.c_str());
mtklein30bf3e22014-06-03 13:57:14 -0700125 if (!ok) {
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000126 this->fail();
127 }
128}
129
130SkString WriteTask::name() const {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000131 SkString name("writing ");
132 for (int i = 0; i < fSuffixes.count(); i++) {
133 name.appendf("%s/", fSuffixes[i].c_str());
134 }
mtklein1d0f1642014-09-08 08:05:18 -0700135 name.append(fBaseName.c_str());
rmistry@google.comd6bab022013-12-02 13:50:38 +0000136 return name;
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000137}
138
139bool WriteTask::shouldSkip() const {
140 return FLAGS_writePath.isEmpty();
141}
142
mtklein1d0f1642014-09-08 08:05:18 -0700143WriteTask::Expectations* WriteTask::Expectations::Create(const char* path) {
144 if (!FLAGS_writePath.isEmpty() && 0 == strcmp(FLAGS_writePath[0], path)) {
145 SkDebugf("We seem to be reading and writing %s concurrently. This won't work.\n", path);
146 return NULL;
147 }
commit-bot@chromium.org99589af2013-12-10 14:53:16 +0000148
mtklein1d0f1642014-09-08 08:05:18 -0700149 SkString jsonPath;
150 if (sk_isdir(path)) {
151 jsonPath = SkOSPath::Join(path, "dm.json");
152 } else {
153 jsonPath.set(path);
154 }
commit-bot@chromium.org99589af2013-12-10 14:53:16 +0000155
mtklein1d0f1642014-09-08 08:05:18 -0700156 SkAutoDataUnref json(SkData::NewFromFileName(jsonPath.c_str()));
157 if (NULL == json.get()) {
158 SkDebugf("Can't read %s!\n", jsonPath.c_str());
159 return NULL;
160 }
commit-bot@chromium.org99589af2013-12-10 14:53:16 +0000161
mtklein1d0f1642014-09-08 08:05:18 -0700162 SkAutoTDelete<Expectations> expectations(SkNEW(Expectations));
163 Json::Reader reader;
164 const char* begin = (const char*)json->bytes();
165 const char* end = begin + json->size();
166 if (!reader.parse(begin, end, expectations->fJson)) {
167 SkDebugf("Can't read %s as JSON!\n", jsonPath.c_str());
168 return NULL;
169 }
170 return expectations.detach();
commit-bot@chromium.org99589af2013-12-10 14:53:16 +0000171}
172
173bool WriteTask::Expectations::check(const Task& task, SkBitmap bitmap) const {
mtklein1d0f1642014-09-08 08:05:18 -0700174 const SkString name = task.name();
175 if (fJson[name.c_str()].isNull()) {
176 return true; // No expectations.
commit-bot@chromium.org0888b752014-02-10 16:39:40 +0000177 }
178
mtklein1d0f1642014-09-08 08:05:18 -0700179 const char* md5Ascii = fJson[name.c_str()].asCString();
180 uint8_t md5[16];
181
182 for (int j = 0; j < 16; j++) {
183 sscanf(md5Ascii + (j*2), "%02hhx", md5 + j);
commit-bot@chromium.org69a0d7a2014-01-06 20:24:21 +0000184 }
185
mtklein1d0f1642014-09-08 08:05:18 -0700186 SkMD5 hasher;
187 {
188 SkAutoLockPixels lock(bitmap);
189 hasher.write(bitmap.getPixels(), bitmap.getSize());
190 }
191 SkMD5::Digest digest;
192 hasher.finish(digest);
193
194 return 0 == memcmp(md5, digest.data, 16);
195}
196
197void WriteTask::DumpJson() {
198 if (FLAGS_writePath.isEmpty()) {
199 return;
200 }
201
202 // FIXME: This JSON format is a complete MVP strawman.
203 Json::Value root;
204 {
205 SkAutoMutexAcquire lock(&gJsonDataLock);
206 for (int i = 0; i < gJsonData.count(); i++) {
207 char md5Ascii[32];
208 for (int j = 0; j < 16; j++) {
209 sprintf(md5Ascii + (j*2), "%02x", gJsonData[i].md5.data[j]);
210 }
211 root[gJsonData[i].name.c_str()] = md5Ascii;
212 }
213 }
214
215 SkString path = SkOSPath::Join(FLAGS_writePath[0], "dm.json");
216 SkFILEWStream stream(path.c_str());
217 stream.writeText(Json::StyledWriter().write(root).c_str());
218 stream.flush();
commit-bot@chromium.org99589af2013-12-10 14:53:16 +0000219}
220
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000221} // namespace DM