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 |