OLD | NEW |
| (Empty) |
1 #include "DMWriteTask.h" | |
2 | |
3 #include "DMJsonWriter.h" | |
4 #include "DMUtil.h" | |
5 #include "SkColorPriv.h" | |
6 #include "SkCommonFlags.h" | |
7 #include "SkData.h" | |
8 #include "SkImageEncoder.h" | |
9 #include "SkMD5.h" | |
10 #include "SkMallocPixelRef.h" | |
11 #include "SkOSFile.h" | |
12 #include "SkStream.h" | |
13 #include "SkString.h" | |
14 | |
15 DEFINE_bool(nameByHash, false, "If true, write .../hash.png instead of .../mode/
config/name.png"); | |
16 | |
17 namespace DM { | |
18 | |
19 // Splits off the last N suffixes of name (splitting on _) and appends them to o
ut. | |
20 // Returns the total number of characters consumed. | |
21 static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) { | |
22 SkTArray<SkString> split; | |
23 SkStrSplit(name, "_", &split); | |
24 int consumed = 0; | |
25 for (int i = 0; i < N; i++) { | |
26 // We're splitting off suffixes from the back to front. | |
27 out->push_back(split[split.count()-i-1]); | |
28 consumed += SkToInt(out->back().size() + 1); // Add one for the _. | |
29 } | |
30 return consumed; | |
31 } | |
32 | |
33 inline static SkString find_base_name(const Task& parent, SkTArray<SkString>* su
ffixList) { | |
34 const int suffixes = parent.depth() + 1; | |
35 const SkString& name = parent.name(); | |
36 const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixL
ist); | |
37 return SkString(name.c_str(), name.size() - totalSuffixLength); | |
38 } | |
39 | |
40 WriteTask::WriteTask(const Task& parent, const char* sourceType, SkBitmap bitmap
) | |
41 : CpuTask(parent) | |
42 , fBaseName(find_base_name(parent, &fSuffixes)) | |
43 , fSourceType(sourceType) | |
44 , fBitmap(bitmap) | |
45 , fData(NULL) | |
46 , fExtension(".png") { | |
47 } | |
48 | |
49 WriteTask::WriteTask(const Task& parent, | |
50 const char* sourceType, | |
51 SkStreamAsset *data, | |
52 const char* ext) | |
53 : CpuTask(parent) | |
54 , fBaseName(find_base_name(parent, &fSuffixes)) | |
55 , fSourceType(sourceType) | |
56 , fData(data) | |
57 , fExtension(ext) { | |
58 SkASSERT(fData.get()); | |
59 SkASSERT(fData->unique()); | |
60 } | |
61 | |
62 void WriteTask::makeDirOrFail(SkString dir) { | |
63 // This can be a little racy, so if it fails check to see if someone else su
cceeded. | |
64 if (!sk_mkdir(dir.c_str()) && !sk_isdir(dir.c_str())) { | |
65 this->fail("Can't make directory."); | |
66 } | |
67 } | |
68 | |
69 static SkString get_md5_string(SkMD5* hasher) { | |
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 static 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 | |
86 static bool write_asset(SkStreamAsset* input, SkWStream* output) { | |
87 return input->rewind() && output->writeStream(input, input->getLength()); | |
88 } | |
89 | |
90 static SkString get_md5(SkStreamAsset* stream) { | |
91 SkMD5 hasher; | |
92 write_asset(stream, &hasher); | |
93 return get_md5_string(&hasher); | |
94 } | |
95 | |
96 static 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 | |
109 void WriteTask::draw() { | |
110 SkString md5; | |
111 { | |
112 SkAutoLockPixels lock(fBitmap); | |
113 md5 = fData ? get_md5(fData) | |
114 : get_md5(fBitmap.getPixels(), fBitmap.getSize()); | |
115 } | |
116 | |
117 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 | |
124 { | |
125 const JsonWriter::BitmapResult entry = { fBaseName, | |
126 config, | |
127 mode, | |
128 fSourceType, | |
129 md5 }; | |
130 JsonWriter::AddBitmapResult(entry); | |
131 } | |
132 | |
133 SkString dir(FLAGS_writePath[0]); | |
134 #if defined(SK_BUILD_FOR_IOS) | |
135 if (dir.equals("@")) { | |
136 dir.set(FLAGS_resourcePath[0]); | |
137 } | |
138 #endif | |
139 this->makeDirOrFail(dir); | |
140 | |
141 SkString path; | |
142 if (FLAGS_nameByHash) { | |
143 // Flat directory of hash-named files. | |
144 path = SkOSPath::Join(dir.c_str(), md5.c_str()); | |
145 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 } | |
152 } else { | |
153 // 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 chan
ged. | |
162 } | |
163 | |
164 SkFILEWStream file(path.c_str()); | |
165 if (!file.isValid()) { | |
166 return this->fail("Can't open file."); | |
167 } | |
168 | |
169 bool ok = fData ? write_asset(fData, &file) | |
170 : encode_png(fBitmap, &file); | |
171 if (!ok) { | |
172 return this->fail("Can't write to file."); | |
173 } | |
174 } | |
175 | |
176 SkString WriteTask::name() const { | |
177 SkString name("writing "); | |
178 for (int i = 0; i < fSuffixes.count(); i++) { | |
179 name.appendf("%s/", fSuffixes[i].c_str()); | |
180 } | |
181 name.append(fBaseName.c_str()); | |
182 return name; | |
183 } | |
184 | |
185 bool WriteTask::shouldSkip() const { | |
186 return FLAGS_writePath.isEmpty(); | |
187 } | |
188 | |
189 } // namespace DM | |
OLD | NEW |