blob: aee43a80b87c5fbf403abb82feae4b42b43a752b [file] [log] [blame]
mtklein@google.coma7a9f372013-10-18 20:52:44 +00001#include "DMWriteTask.h"
2
scroggo7a10fb62014-11-04 07:21:10 -08003#include "DMJsonWriter.h"
mtklein@google.coma7a9f372013-10-18 20:52:44 +00004#include "DMUtil.h"
commit-bot@chromium.org389fb7f2014-01-15 21:28:25 +00005#include "SkColorPriv.h"
caryclark17f0b6d2014-07-22 10:15:34 -07006#include "SkCommonFlags.h"
mtklein197ceda2014-09-09 07:36:57 -07007#include "SkData.h"
mtklein@google.coma7a9f372013-10-18 20:52:44 +00008#include "SkImageEncoder.h"
mtklein1d0f1642014-09-08 08:05:18 -07009#include "SkMD5.h"
commit-bot@chromium.orgeef834f2014-03-05 15:37:11 +000010#include "SkMallocPixelRef.h"
mtklein1d0f1642014-09-08 08:05:18 -070011#include "SkOSFile.h"
commit-bot@chromium.org1426c1e2014-03-03 15:43:56 +000012#include "SkStream.h"
commit-bot@chromium.orgeef834f2014-03-05 15:37:11 +000013#include "SkString.h"
mtklein@google.coma7a9f372013-10-18 20:52:44 +000014
mtklein858baf52014-09-08 11:33:48 -070015DEFINE_bool(nameByHash, false, "If true, write .../hash.png instead of .../mode/config/name.png");
16
mtklein@google.coma7a9f372013-10-18 20:52:44 +000017namespace DM {
18
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000019// Splits off the last N suffixes of name (splitting on _) and appends them to out.
20// Returns the total number of characters consumed.
21static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) {
rmistry@google.comd6bab022013-12-02 13:50:38 +000022 SkTArray<SkString> split;
23 SkStrSplit(name, "_", &split);
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000024 int consumed = 0;
25 for (int i = 0; i < N; i++) {
rmistry@google.comd6bab022013-12-02 13:50:38 +000026 // We're splitting off suffixes from the back to front.
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000027 out->push_back(split[split.count()-i-1]);
bsalomonef3fcd82014-12-12 08:51:38 -080028 consumed += SkToInt(out->back().size() + 1); // Add one for the _.
rmistry@google.comd6bab022013-12-02 13:50:38 +000029 }
commit-bot@chromium.org99589af2013-12-10 14:53:16 +000030 return consumed;
31}
32
mtklein1d0f1642014-09-08 08:05:18 -070033inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* suffixList) {
commit-bot@chromium.orgd6dcacd2014-05-14 20:26:00 +000034 const int suffixes = parent.depth() + 1;
35 const SkString& name = parent.name();
mtklein30bf3e22014-06-03 13:57:14 -070036 const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList);
37 return SkString(name.c_str(), name.size() - totalSuffixLength);
rmistry@google.comd6bab022013-12-02 13:50:38 +000038}
mtklein@google.coma7a9f372013-10-18 20:52:44 +000039
mtkleinea65bfa2014-09-09 07:59:46 -070040WriteTask::WriteTask(const Task& parent, const char* sourceType, SkBitmap bitmap)
mtklein30bf3e22014-06-03 13:57:14 -070041 : CpuTask(parent)
mtklein1d0f1642014-09-08 08:05:18 -070042 , fBaseName(find_base_name(parent, &fSuffixes))
mtkleinea65bfa2014-09-09 07:59:46 -070043 , fSourceType(sourceType)
mtklein30bf3e22014-06-03 13:57:14 -070044 , fBitmap(bitmap)
45 , fData(NULL)
mtklein1d0f1642014-09-08 08:05:18 -070046 , fExtension(".png") {
47}
mtklein30bf3e22014-06-03 13:57:14 -070048
mtkleinea65bfa2014-09-09 07:59:46 -070049WriteTask::WriteTask(const Task& parent,
50 const char* sourceType,
51 SkStreamAsset *data,
52 const char* ext)
mtklein30bf3e22014-06-03 13:57:14 -070053 : CpuTask(parent)
mtklein1d0f1642014-09-08 08:05:18 -070054 , fBaseName(find_base_name(parent, &fSuffixes))
mtkleinea65bfa2014-09-09 07:59:46 -070055 , fSourceType(sourceType)
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) {
mtkleinf6139f72014-12-12 16:41:12 -080063 // This can be a little racy, so if it fails check to see if someone else succeeded.
64 if (!sk_mkdir(dir.c_str()) && !sk_isdir(dir.c_str())) {
65 this->fail("Can't make directory.");
rmistry@google.comd6bab022013-12-02 13:50:38 +000066 }
mtklein@google.coma7a9f372013-10-18 20:52:44 +000067}
68
bungemand51ce442014-10-02 13:39:00 -070069static SkString get_md5_string(SkMD5* hasher) {
mtkleine2d4eb72014-09-08 12:42:23 -070070 SkMD5::Digest digest;
bungemand51ce442014-10-02 13:39:00 -070071 hasher->finish(digest);
mtkleine2d4eb72014-09-08 12:42:23 -070072
73 SkString md5;
74 for (int i = 0; i < 16; i++) {
75 md5.appendf("%02x", digest.data[i]);
76 }
77 return md5;
mtklein858baf52014-09-08 11:33:48 -070078}
79
bungemand51ce442014-10-02 13:39:00 -070080static SkString get_md5(const void* ptr, size_t len) {
81 SkMD5 hasher;
82 hasher.write(ptr, len);
83 return get_md5_string(&hasher);
84}
85
halcanarydaf36c12014-10-17 14:36:10 -070086static bool write_asset(SkStreamAsset* input, SkWStream* output) {
87 return input->rewind() && output->writeStream(input, input->getLength());
88}
89
bungemand51ce442014-10-02 13:39:00 -070090static SkString get_md5(SkStreamAsset* stream) {
91 SkMD5 hasher;
halcanarydaf36c12014-10-17 14:36:10 -070092 write_asset(stream, &hasher);
bungemand51ce442014-10-02 13:39:00 -070093 return get_md5_string(&hasher);
94}
95
mtkleinf6139f72014-12-12 16:41:12 -080096static bool encode_png(const SkBitmap& src, SkFILEWStream* file) {
97 SkBitmap bm;
98 // We can't encode A8 bitmaps as PNGs. Convert them to 8888 first.
99 if (src.info().colorType() == kAlpha_8_SkColorType) {
100 if (!src.copyTo(&bm, kN32_SkColorType)) {
101 return false;
102 }
103 } else {
104 bm = src;
105 }
106 return SkImageEncoder::EncodeStream(file, bm, SkImageEncoder::kPNG_Type, 100);
107}
108
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000109void WriteTask::draw() {
mtkleinc54056c2014-09-09 08:42:04 -0700110 SkString md5;
111 {
112 SkAutoLockPixels lock(fBitmap);
bungemand51ce442014-10-02 13:39:00 -0700113 md5 = fData ? get_md5(fData)
mtkleinc54056c2014-09-09 08:42:04 -0700114 : get_md5(fBitmap.getPixels(), fBitmap.getSize());
mtkleine2d4eb72014-09-08 12:42:23 -0700115 }
mtklein858baf52014-09-08 11:33:48 -0700116
mtklein87e24372014-09-19 10:35:07 -0700117 SkASSERT(fSuffixes.count() > 0);
118 SkString config = fSuffixes.back();
119 SkString mode("direct");
120 if (fSuffixes.count() > 1) {
121 mode = fSuffixes.fromBack(1);
122 }
123
mtklein858baf52014-09-08 11:33:48 -0700124 {
scroggo7a10fb62014-11-04 07:21:10 -0800125 const JsonWriter::BitmapResult entry = { fBaseName,
126 config,
127 mode,
128 fSourceType,
129 md5 };
130 JsonWriter::AddBitmapResult(entry);
mtklein858baf52014-09-08 11:33:48 -0700131 }
132
rmistry@google.comd6bab022013-12-02 13:50:38 +0000133 SkString dir(FLAGS_writePath[0]);
tfarina6b87df22014-10-06 10:46:50 -0700134#if defined(SK_BUILD_FOR_IOS)
caryclark17f0b6d2014-07-22 10:15:34 -0700135 if (dir.equals("@")) {
136 dir.set(FLAGS_resourcePath[0]);
137 }
138#endif
rmistry@google.comd6bab022013-12-02 13:50:38 +0000139 this->makeDirOrFail(dir);
mtklein30bf3e22014-06-03 13:57:14 -0700140
mtklein858baf52014-09-08 11:33:48 -0700141 SkString path;
142 if (FLAGS_nameByHash) {
143 // Flat directory of hash-named files.
mtkleinc54056c2014-09-09 08:42:04 -0700144 path = SkOSPath::Join(dir.c_str(), md5.c_str());
mtklein858baf52014-09-08 11:33:48 -0700145 path.append(fExtension);
146 // We're content-addressed, so it's possible two threads race to write
147 // this file. We let the first one win. This also means we won't
148 // overwrite identical files from previous runs.
149 if (sk_exists(path.c_str())) {
150 return;
151 }
mtklein1d0f1642014-09-08 08:05:18 -0700152 } else {
mtklein858baf52014-09-08 11:33:48 -0700153 // Nested by mode, config, etc.
154 for (int i = 0; i < fSuffixes.count(); i++) {
155 dir = SkOSPath::Join(dir.c_str(), fSuffixes[i].c_str());
156 this->makeDirOrFail(dir);
157 }
158 path = SkOSPath::Join(dir.c_str(), fBaseName.c_str());
159 path.append(fExtension);
160 // The path is unique, so two threads can't both write to the same file.
161 // If already present we overwrite here, since the content may have changed.
mtklein1d0f1642014-09-08 08:05:18 -0700162 }
163
mtkleine2d4eb72014-09-08 12:42:23 -0700164 SkFILEWStream file(path.c_str());
165 if (!file.isValid()) {
166 return this->fail("Can't open file.");
167 }
168
halcanarydaf36c12014-10-17 14:36:10 -0700169 bool ok = fData ? write_asset(fData, &file)
mtkleinf6139f72014-12-12 16:41:12 -0800170 : encode_png(fBitmap, &file);
mtkleinc54056c2014-09-09 08:42:04 -0700171 if (!ok) {
mtkleine2d4eb72014-09-08 12:42:23 -0700172 return this->fail("Can't write to file.");
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000173 }
174}
175
176SkString WriteTask::name() const {
rmistry@google.comd6bab022013-12-02 13:50:38 +0000177 SkString name("writing ");
178 for (int i = 0; i < fSuffixes.count(); i++) {
179 name.appendf("%s/", fSuffixes[i].c_str());
180 }
mtklein1d0f1642014-09-08 08:05:18 -0700181 name.append(fBaseName.c_str());
rmistry@google.comd6bab022013-12-02 13:50:38 +0000182 return name;
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000183}
184
185bool WriteTask::shouldSkip() const {
186 return FLAGS_writePath.isEmpty();
187}
188
mtklein@google.coma7a9f372013-10-18 20:52:44 +0000189} // namespace DM