1 #include "DMWriteTask.h" 2 3 #include "DMUtil.h" 4 #include "SkColorPriv.h" 5 #include "SkCommandLineFlags.h" 6 #include "SkImageEncoder.h" 7 #include "SkMallocPixelRef.h" 8 #include "SkStream.h" 9 #include "SkString.h" 10 11 DEFINE_string2(writePath, w, "", "If set, write GMs here as .pngs."); 12 DEFINE_bool(writePngOnly, false, "If true, don't encode raw bitmap after .png data. " 13 "This means -r won't work, but skdiff will still work fine."); 14 15 namespace DM { 16 17 // Splits off the last N suffixes of name (splitting on _) and appends them to out. 18 // Returns the total number of characters consumed. 19 static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) { 20 SkTArray<SkString> split; 21 SkStrSplit(name, "_", &split); 22 int consumed = 0; 23 for (int i = 0; i < N; i++) { 24 // We're splitting off suffixes from the back to front. 25 out->push_back(split[split.count()-i-1]); 26 consumed += out->back().size() + 1; // Add one for the _. 27 } 28 return consumed; 29 } 30 31 inline static SkString find_gm_name(const Task& parent, SkTArray<SkString>* suffixList) { 32 const int suffixes = parent.depth() + 1; 33 const SkString& name = parent.name(); 34 const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), suffixList); 35 return SkString(name.c_str(), name.size() - totalSuffixLength); 36 } 37 38 WriteTask::WriteTask(const Task& parent, SkBitmap bitmap) 39 : CpuTask(parent) 40 , fGmName(find_gm_name(parent, &fSuffixes)) 41 , fBitmap(bitmap) 42 , fData(NULL) 43 , fExtension(".png") {} 44 45 WriteTask::WriteTask(const Task& parent, SkData *data, const char* ext) 46 : CpuTask(parent) 47 , fGmName(find_gm_name(parent, &fSuffixes)) 48 , fData(SkRef(data)) 49 , fExtension(ext) {} 50 51 void WriteTask::makeDirOrFail(SkString dir) { 52 if (!sk_mkdir(dir.c_str())) { 53 this->fail(); 54 } 55 } 56 57 namespace { 58 59 // One file that first contains a .png of an SkBitmap, then its raw pixels. 60 // We use this custom format to avoid premultiplied/unpremultiplied pixel conversions. 61 struct PngAndRaw { 62 static bool Encode(SkBitmap bitmap, const char* path) { 63 SkFILEWStream stream(path); 64 if (!stream.isValid()) { 65 SkDebugf("Can't write %s.\n", path); 66 return false; 67 } 68 69 // Write a PNG first for humans and other tools to look at. 70 if (!SkImageEncoder::EncodeStream(&stream, bitmap, SkImageEncoder::kPNG_Type, 100)) { 71 SkDebugf("Can't encode a PNG.\n"); 72 return false; 73 } 74 if (FLAGS_writePngOnly) { 75 return true; 76 } 77 78 // Pad out so the raw pixels start 4-byte aligned. 79 const uint32_t maxPadding = 0; 80 const size_t pos = stream.bytesWritten(); 81 stream.write(&maxPadding, SkAlign4(pos) - pos); 82 83 // Then write our secret raw pixels that only DM reads. 84 SkAutoLockPixels lock(bitmap); 85 return stream.write(bitmap.getPixels(), bitmap.getSize()); 86 } 87 88 // This assumes bitmap already has allocated pixels of the correct size. 89 static bool Decode(const char* path, SkImageInfo info, SkBitmap* bitmap) { 90 SkAutoTUnref<SkData> data(SkData::NewFromFileName(path)); 91 if (!data) { 92 SkDebugf("Can't read %s.\n", path); 93 return false; 94 } 95 96 // The raw pixels are at the end of the file. We'll skip the encoded PNG at the front. 97 const size_t rowBytes = info.minRowBytes(); // Assume densely packed. 98 const size_t bitmapBytes = info.getSafeSize(rowBytes); 99 if (data->size() < bitmapBytes) { 100 SkDebugf("%s is too small to contain the bitmap we're looking for.\n", path); 101 return false; 102 } 103 104 const size_t offset = data->size() - bitmapBytes; 105 SkAutoTUnref<SkData> subset( 106 SkData::NewSubset(data, offset, bitmapBytes)); 107 SkAutoTUnref<SkPixelRef> pixels( 108 SkMallocPixelRef::NewWithData( 109 info, rowBytes, NULL/*ctable*/, subset)); 110 SkASSERT(pixels); 111 112 bitmap->setInfo(info, rowBytes); 113 bitmap->setPixelRef(pixels); 114 return true; 115 } 116 }; 117 118 // Does not take ownership of data. 119 bool save_data_to_file(const SkData* data, const char* path) { 120 SkFILEWStream stream(path); 121 if (!stream.isValid() || !stream.write(data->data(), data->size())) { 122 SkDebugf("Can't write %s.\n", path); 123 return false; 124 } 125 return true; 126 } 127 128 } // namespace 129 130 void WriteTask::draw() { 131 SkString dir(FLAGS_writePath[0]); 132 this->makeDirOrFail(dir); 133 for (int i = 0; i < fSuffixes.count(); i++) { 134 dir = SkOSPath::SkPathJoin(dir.c_str(), fSuffixes[i].c_str()); 135 this->makeDirOrFail(dir); 136 } 137 138 SkString path = SkOSPath::SkPathJoin(dir.c_str(), fGmName.c_str()); 139 path.append(fExtension); 140 141 const bool ok = fData.get() ? save_data_to_file(fData, path.c_str()) 142 : PngAndRaw::Encode(fBitmap, path.c_str()); 143 if (!ok) { 144 this->fail(); 145 } 146 } 147 148 SkString WriteTask::name() const { 149 SkString name("writing "); 150 for (int i = 0; i < fSuffixes.count(); i++) { 151 name.appendf("%s/", fSuffixes[i].c_str()); 152 } 153 name.append(fGmName.c_str()); 154 return name; 155 } 156 157 bool WriteTask::shouldSkip() const { 158 return FLAGS_writePath.isEmpty(); 159 } 160 161 static SkString path_to_expected_image(const char* root, const Task& task) { 162 SkString filename = task.name(); 163 164 // We know that all names passed in here belong to top-level Tasks, which have a single suffix 165 // (8888, 565, gpu, etc.) indicating what subdirectory to look in. 166 SkTArray<SkString> suffixes; 167 const int suffixLength = split_suffixes(1, filename.c_str(), &suffixes); 168 SkASSERT(1 == suffixes.count()); 169 170 // We'll look in root/suffix for images. 171 const SkString dir = SkOSPath::SkPathJoin(root, suffixes[0].c_str()); 172 173 // Remove the suffix and tack on a .png. 174 filename.remove(filename.size() - suffixLength, suffixLength); 175 filename.append(".png"); 176 177 return SkOSPath::SkPathJoin(dir.c_str(), filename.c_str()); 178 } 179 180 bool WriteTask::Expectations::check(const Task& task, SkBitmap bitmap) const { 181 if (!FLAGS_writePath.isEmpty() && 0 == strcmp(FLAGS_writePath[0], fRoot)) { 182 SkDebugf("We seem to be reading and writing %s concurrently. This won't work.\n", fRoot); 183 return false; 184 } 185 186 const SkString path = path_to_expected_image(fRoot, task); 187 SkBitmap expected; 188 if (!PngAndRaw::Decode(path.c_str(), bitmap.info(), &expected)) { 189 return false; 190 } 191 192 return BitmapsEqual(expected, bitmap); 193 } 194 195 } // namespace DM 196