blob: 35ae2a72097de79d75fbd3df060bfe00633a68b2 [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"
mtklein197ceda2014-09-09 07:36:57 -07006#include "SkData.h"
mtklein@google.coma7a9f372013-10-18 20:52:44 +00007#include "SkImageEncoder.h"
mtklein1d0f1642014-09-08 08:05:18 -07008#include "SkMD5.h"
commit-bot@chromium.orgeef834f2014-03-05 15:37:11 +00009#include "SkMallocPixelRef.h"
mtklein1d0f1642014-09-08 08:05:18 -070010#include "SkOSFile.h"
commit-bot@chromium.org1426c1e2014-03-03 15:43:56 +000011#include "SkStream.h"
commit-bot@chromium.orgeef834f2014-03-05 15:37:11 +000012#include "SkString.h"
mtklein@google.coma7a9f372013-10-18 20:52:44 +000013
mtklein858baf52014-09-08 11:33:48 -070014DEFINE_bool(nameByHash, false, "If true, write .../hash.png instead of .../mode/config/name.png");
15
mtklein@google.coma7a9f372013-10-18 20:52:44 +000016namespace DM {
17
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000018// Splits off the last N suffixes of name (splitting on _) and appends them to out.
19// Returns the total number of characters consumed.
20static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) {
rmistry@google.comd6bab022013-12-02 13:50:38 +000021 SkTArray<SkString> split;
22 SkStrSplit(name, "_", &split);
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000023 int consumed = 0;
24 for (int i = 0; i < N; i++) {
rmistry@google.comd6bab022013-12-02 13:50:38 +000025 // We're splitting off suffixes from the back to front.
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000026 out->push_back(split[split.count()-i-1]);
27 consumed += out->back().size() + 1; // Add one for the _.
rmistry@google.comd6bab022013-12-02 13:50:38 +000028 }
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000029 return consumed;
30}
31
mtklein1d0f1642014-09-08 08:05:18 -070032inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* suffixList) {
commit-bot@chromium.orgd6dcacd2014-05-14 20:26:00 +000033 const int suffixes = parent.depth() + 1;
34 const SkString& name = parent.name();
mtklein30bf3e22014-06-03 13:57:14 -070035 const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList);
36 return SkString(name.c_str(), name.size() - totalSuffixLength);
rmistry@google.comd6bab022013-12-02 13:50:38 +000037}
mtklein@google.coma7a9f372013-10-18 20:52:44 +000038
mtkleinea65bfa2014-09-09 07:59:46 -070039WriteTask::WriteTask(const Task& parent, const char* sourceType, SkBitmap bitmap)
mtklein30bf3e22014-06-03 13:57:14 -070040 : CpuTask(parent)
mtklein1d0f1642014-09-08 08:05:18 -070041 , fBaseName(find_base_name(parent, &fSuffixes))
mtkleinea65bfa2014-09-09 07:59:46 -070042 , fSourceType(sourceType)
mtklein30bf3e22014-06-03 13:57:14 -070043 , fBitmap(bitmap)
44 , fData(NULL)
mtklein1d0f1642014-09-08 08:05:18 -070045 , fExtension(".png") {
46}
mtklein30bf3e22014-06-03 13:57:14 -070047
mtkleinea65bfa2014-09-09 07:59:46 -070048WriteTask::WriteTask(const Task& parent,
49 const char* sourceType,
50 SkStreamAsset *data,
51 const char* ext)
mtklein30bf3e22014-06-03 13:57:14 -070052 : CpuTask(parent)
mtklein1d0f1642014-09-08 08:05:18 -070053 , fBaseName(find_base_name(parent, &fSuffixes))
mtkleinea65bfa2014-09-09 07:59:46 -070054 , fSourceType(sourceType)
halcanarya4c60942014-08-26 10:38:07 -070055 , fData(data)
56 , fExtension(ext) {
57 SkASSERT(fData.get());
58 SkASSERT(fData->unique());
59}
mtklein30bf3e22014-06-03 13:57:14 -070060
rmistry@google.comd6bab022013-12-02 13:50:38 +000061void WriteTask::makeDirOrFail(SkString dir) {
62 if (!sk_mkdir(dir.c_str())) {
63 this->fail();
64 }
mtklein@google.coma7a9f372013-10-18 20:52:44 +000065}
66
bungemand51ce442014-10-02 13:39:00 -070067static SkString get_md5_string(SkMD5* hasher) {
mtkleine2d4eb72014-09-08 12:42:23 -070068 SkMD5::Digest digest;
bungemand51ce442014-10-02 13:39:00 -070069 hasher->finish(digest);
mtkleine2d4eb72014-09-08 12:42:23 -070070
71 SkString md5;
72 for (int i = 0; i < 16; i++) {
73 md5.appendf("%02x", digest.data[i]);
74 }
75 return md5;
mtklein858baf52014-09-08 11:33:48 -070076}
77
bungemand51ce442014-10-02 13:39:00 -070078static SkString get_md5(const void* ptr, size_t len) {
79 SkMD5 hasher;
80 hasher.write(ptr, len);
81 return get_md5_string(&hasher);
82}
83
halcanarydaf36c12014-10-17 14:36:10 -070084static bool write_asset(SkStreamAsset* input, SkWStream* output) {
85 return input->rewind() && output->writeStream(input, input->getLength());
86}
87
bungemand51ce442014-10-02 13:39:00 -070088static SkString get_md5(SkStreamAsset* stream) {
89 SkMD5 hasher;
halcanarydaf36c12014-10-17 14:36:10 -070090 write_asset(stream, &hasher);
bungemand51ce442014-10-02 13:39:00 -070091 return get_md5_string(&hasher);
92}
93
mtkleinea65bfa2014-09-09 07:59:46 -070094struct JsonData {
mtklein87e24372014-09-19 10:35:07 -070095 SkString name; // E.g. "ninepatch-stretch", "desk-gws_skp"
96 SkString config; // "gpu", "8888"
97 SkString mode; // "direct", "default-tilegrid", "pipe"
98 SkString sourceType; // "GM", "SKP"
99 SkString md5; // In ASCII, so 32 bytes long.
mtkleinea65bfa2014-09-09 07:59:46 -0700100};
101SkTArray<JsonData> gJsonData;
102SK_DECLARE_STATIC_MUTEX(gJsonDataLock);
103
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000104void WriteTask::draw() {
mtkleinc54056c2014-09-09 08:42:04 -0700105 SkString md5;
106 {
107 SkAutoLockPixels lock(fBitmap);
bungemand51ce442014-10-02 13:39:00 -0700108 md5 = fData ? get_md5(fData)
mtkleinc54056c2014-09-09 08:42:04 -0700109 : get_md5(fBitmap.getPixels(), fBitmap.getSize());
mtkleine2d4eb72014-09-08 12:42:23 -0700110 }
mtklein858baf52014-09-08 11:33:48 -0700111
mtklein87e24372014-09-19 10:35:07 -0700112 SkASSERT(fSuffixes.count() > 0);
113 SkString config = fSuffixes.back();
114 SkString mode("direct");
115 if (fSuffixes.count() > 1) {
116 mode = fSuffixes.fromBack(1);
117 }
118
119 JsonData entry = { fBaseName, config, mode, fSourceType, md5 };
mtklein858baf52014-09-08 11:33:48 -0700120 {
121 SkAutoMutexAcquire lock(&gJsonDataLock);
122 gJsonData.push_back(entry);
123 }
124
rmistry@google.comd6bab022013-12-02 13:50:38 +0000125 SkString dir(FLAGS_writePath[0]);
tfarina6b87df22014-10-06 10:46:50 -0700126#if defined(SK_BUILD_FOR_IOS)
caryclark17f0b6d2014-07-22 10:15:34 -0700127 if (dir.equals("@")) {
128 dir.set(FLAGS_resourcePath[0]);
129 }
130#endif
rmistry@google.comd6bab022013-12-02 13:50:38 +0000131 this->makeDirOrFail(dir);
mtklein30bf3e22014-06-03 13:57:14 -0700132
mtklein858baf52014-09-08 11:33:48 -0700133 SkString path;
134 if (FLAGS_nameByHash) {
135 // Flat directory of hash-named files.
mtkleinc54056c2014-09-09 08:42:04 -0700136 path = SkOSPath::Join(dir.c_str(), md5.c_str());
mtklein858baf52014-09-08 11:33:48 -0700137 path.append(fExtension);
138 // We're content-addressed, so it's possible two threads race to write
139 // this file. We let the first one win. This also means we won't
140 // overwrite identical files from previous runs.
141 if (sk_exists(path.c_str())) {
142 return;
143 }
mtklein1d0f1642014-09-08 08:05:18 -0700144 } else {
mtklein858baf52014-09-08 11:33:48 -0700145 // Nested by mode, config, etc.
146 for (int i = 0; i < fSuffixes.count(); i++) {
147 dir = SkOSPath::Join(dir.c_str(), fSuffixes[i].c_str());
148 this->makeDirOrFail(dir);
149 }
150 path = SkOSPath::Join(dir.c_str(), fBaseName.c_str());
151 path.append(fExtension);
152 // The path is unique, so two threads can't both write to the same file.
153 // If already present we overwrite here, since the content may have changed.
mtklein1d0f1642014-09-08 08:05:18 -0700154 }
155
mtkleine2d4eb72014-09-08 12:42:23 -0700156 SkFILEWStream file(path.c_str());
157 if (!file.isValid()) {
158 return this->fail("Can't open file.");
159 }
160
halcanarydaf36c12014-10-17 14:36:10 -0700161 bool ok = fData ? write_asset(fData, &file)
mtkleinc54056c2014-09-09 08:42:04 -0700162 : SkImageEncoder::EncodeStream(&file, fBitmap, SkImageEncoder::kPNG_Type, 100);
163 if (!ok) {
mtkleine2d4eb72014-09-08 12:42:23 -0700164 return this->fail("Can't write to file.");
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000165 }
166}
167
168SkString WriteTask::name() const {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000169 SkString name("writing ");
170 for (int i = 0; i < fSuffixes.count(); i++) {
171 name.appendf("%s/", fSuffixes[i].c_str());
172 }
mtklein1d0f1642014-09-08 08:05:18 -0700173 name.append(fBaseName.c_str());
rmistry@google.comd6bab022013-12-02 13:50:38 +0000174 return name;
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000175}
176
177bool WriteTask::shouldSkip() const {
178 return FLAGS_writePath.isEmpty();
179}
180
mtklein1d0f1642014-09-08 08:05:18 -0700181void WriteTask::DumpJson() {
182 if (FLAGS_writePath.isEmpty()) {
183 return;
184 }
185
mtklein1d0f1642014-09-08 08:05:18 -0700186 Json::Value root;
mtkleinea65bfa2014-09-09 07:59:46 -0700187
188 for (int i = 1; i < FLAGS_properties.count(); i += 2) {
189 root[FLAGS_properties[i-1]] = FLAGS_properties[i];
190 }
191 for (int i = 1; i < FLAGS_key.count(); i += 2) {
192 root["key"][FLAGS_key[i-1]] = FLAGS_key[i];
193 }
194
mtklein1d0f1642014-09-08 08:05:18 -0700195 {
196 SkAutoMutexAcquire lock(&gJsonDataLock);
197 for (int i = 0; i < gJsonData.count(); i++) {
mtkleinea65bfa2014-09-09 07:59:46 -0700198 Json::Value result;
199 result["key"]["name"] = gJsonData[i].name.c_str();
200 result["key"]["config"] = gJsonData[i].config.c_str();
mtklein87e24372014-09-19 10:35:07 -0700201 result["key"]["mode"] = gJsonData[i].mode.c_str();
mtkleinea65bfa2014-09-09 07:59:46 -0700202 result["options"]["source_type"] = gJsonData[i].sourceType.c_str();
203 result["md5"] = gJsonData[i].md5.c_str();
204
205 root["results"].append(result);
mtklein1d0f1642014-09-08 08:05:18 -0700206 }
207 }
208
209 SkString path = SkOSPath::Join(FLAGS_writePath[0], "dm.json");
210 SkFILEWStream stream(path.c_str());
211 stream.writeText(Json::StyledWriter().write(root).c_str());
212 stream.flush();
commit-bot@chromium.org99589af2013-12-10 14:53:16 +0000213}
214
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000215} // namespace DM