OLD | NEW |
1 #include "DMWriteTask.h" | 1 #include "DMWriteTask.h" |
2 | 2 |
3 #include "DMUtil.h" | 3 #include "DMUtil.h" |
4 #include "SkColorPriv.h" | 4 #include "SkColorPriv.h" |
5 #include "SkCommandLineFlags.h" | 5 #include "SkCommandLineFlags.h" |
6 #include "SkImageDecoder.h" | |
7 #include "SkImageEncoder.h" | 6 #include "SkImageEncoder.h" |
8 #include "SkString.h" | 7 #include "SkString.h" |
9 #include "SkUnPreMultiply.h" | 8 #include "SkStream.h" |
10 | 9 |
11 DEFINE_string2(writePath, w, "", "If set, write GMs here as .pngs."); | 10 DEFINE_string2(writePath, w, "", "If set, write GMs here as .pngs."); |
12 | 11 |
13 namespace DM { | 12 namespace DM { |
14 | 13 |
15 // Splits off the last N suffixes of name (splitting on _) and appends them to o
ut. | 14 // Splits off the last N suffixes of name (splitting on _) and appends them to o
ut. |
16 // Returns the total number of characters consumed. | 15 // Returns the total number of characters consumed. |
17 static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) { | 16 static int split_suffixes(int N, const char* name, SkTArray<SkString>* out) { |
18 SkTArray<SkString> split; | 17 SkTArray<SkString> split; |
19 SkStrSplit(name, "_", &split); | 18 SkStrSplit(name, "_", &split); |
(...skipping 12 matching lines...) Expand all Loading... |
32 const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), &fSuffi
xes); | 31 const int totalSuffixLength = split_suffixes(suffixes, name.c_str(), &fSuffi
xes); |
33 fGmName.set(name.c_str(), name.size()-totalSuffixLength); | 32 fGmName.set(name.c_str(), name.size()-totalSuffixLength); |
34 } | 33 } |
35 | 34 |
36 void WriteTask::makeDirOrFail(SkString dir) { | 35 void WriteTask::makeDirOrFail(SkString dir) { |
37 if (!sk_mkdir(dir.c_str())) { | 36 if (!sk_mkdir(dir.c_str())) { |
38 this->fail(); | 37 this->fail(); |
39 } | 38 } |
40 } | 39 } |
41 | 40 |
| 41 // One file that first contains a .png of an SkBitmap, then its raw pixels. |
| 42 // We use this custom format to avoid premultiplied/unpremultiplied pixel conver
sions. |
| 43 struct PngAndRaw { |
| 44 static bool Encode(SkBitmap bitmap, const char* path) { |
| 45 SkFILEWStream stream(path); |
| 46 if (!stream.isValid()) { |
| 47 SkDebugf("Can't write %s.\n", path); |
| 48 return false; |
| 49 } |
| 50 |
| 51 // Write a PNG first for humans and other tools to look at. |
| 52 if (!SkImageEncoder::EncodeStream(&stream, bitmap, SkImageEncoder::kPNG_
Type, 100)) { |
| 53 SkDebugf("Can't encode a PNG.\n"); |
| 54 return false; |
| 55 } |
| 56 |
| 57 // Then write our secret raw pixels that only DM reads. |
| 58 SkAutoLockPixels lock(bitmap); |
| 59 return stream.write(bitmap.getPixels(), bitmap.getSize()); |
| 60 } |
| 61 |
| 62 // This assumes bitmap already has allocated pixels of the correct size. |
| 63 static bool Decode(const char* path, SkBitmap* bitmap) { |
| 64 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(path)); |
| 65 if (NULL == stream.get()) { |
| 66 SkDebugf("Can't read %s.\n", path); |
| 67 return false; |
| 68 } |
| 69 |
| 70 // The raw pixels are at the end of the stream. Seek ahead to skip beyo
nd the encoded PNG. |
| 71 const size_t bitmapBytes = bitmap->getSize(); |
| 72 if (stream->getLength() < bitmapBytes) { |
| 73 SkDebugf("%s is too small to contain the bitmap we're looking for.\n
", path); |
| 74 return false; |
| 75 } |
| 76 SkAssertResult(stream->seek(stream->getLength() - bitmapBytes)); |
| 77 |
| 78 // Read the raw pixels. |
| 79 // TODO(mtklein): can we install a pixelref that just hangs onto the str
eam instead of |
| 80 // copying into a malloc'd buffer? |
| 81 SkAutoLockPixels lock(*bitmap); |
| 82 if (bitmapBytes != stream->read(bitmap->getPixels(), bitmapBytes)) { |
| 83 SkDebugf("Couldn't read raw bitmap from %s at %lu of %lu.\n", |
| 84 path, stream->getPosition(), stream->getLength()); |
| 85 return false; |
| 86 } |
| 87 |
| 88 SkASSERT(stream->isAtEnd()); |
| 89 return true; |
| 90 } |
| 91 }; |
| 92 |
42 void WriteTask::draw() { | 93 void WriteTask::draw() { |
43 SkString dir(FLAGS_writePath[0]); | 94 SkString dir(FLAGS_writePath[0]); |
44 this->makeDirOrFail(dir); | 95 this->makeDirOrFail(dir); |
45 for (int i = 0; i < fSuffixes.count(); i++) { | 96 for (int i = 0; i < fSuffixes.count(); i++) { |
46 dir = SkOSPath::SkPathJoin(dir.c_str(), fSuffixes[i].c_str()); | 97 dir = SkOSPath::SkPathJoin(dir.c_str(), fSuffixes[i].c_str()); |
47 this->makeDirOrFail(dir); | 98 this->makeDirOrFail(dir); |
48 } | 99 } |
49 SkString path = SkOSPath::SkPathJoin(dir.c_str(), fGmName.c_str()); | 100 SkString path = SkOSPath::SkPathJoin(dir.c_str(), fGmName.c_str()); |
50 path.append(".png"); | 101 path.append(".png"); |
51 if (!SkImageEncoder::EncodeFile(path.c_str(), | 102 if (!PngAndRaw::Encode(fBitmap, path.c_str())) { |
52 fBitmap, | |
53 SkImageEncoder::kPNG_Type, | |
54 100/*quality*/)) { | |
55 this->fail(); | 103 this->fail(); |
56 } | 104 } |
57 } | 105 } |
58 | 106 |
59 SkString WriteTask::name() const { | 107 SkString WriteTask::name() const { |
60 SkString name("writing "); | 108 SkString name("writing "); |
61 for (int i = 0; i < fSuffixes.count(); i++) { | 109 for (int i = 0; i < fSuffixes.count(); i++) { |
62 name.appendf("%s/", fSuffixes[i].c_str()); | 110 name.appendf("%s/", fSuffixes[i].c_str()); |
63 } | 111 } |
64 name.append(fGmName.c_str()); | 112 name.append(fGmName.c_str()); |
(...skipping 13 matching lines...) Expand all Loading... |
78 const int suffixLength = split_suffixes(1, filename.c_str(), &suffixes); | 126 const int suffixLength = split_suffixes(1, filename.c_str(), &suffixes); |
79 SkASSERT(1 == suffixes.count()); | 127 SkASSERT(1 == suffixes.count()); |
80 | 128 |
81 // We'll look in root/suffix for images. | 129 // We'll look in root/suffix for images. |
82 const SkString dir = SkOSPath::SkPathJoin(root, suffixes[0].c_str()); | 130 const SkString dir = SkOSPath::SkPathJoin(root, suffixes[0].c_str()); |
83 | 131 |
84 // Remove the suffix and tack on a .png. | 132 // Remove the suffix and tack on a .png. |
85 filename.remove(filename.size() - suffixLength, suffixLength); | 133 filename.remove(filename.size() - suffixLength, suffixLength); |
86 filename.append(".png"); | 134 filename.append(".png"); |
87 | 135 |
88 //SkDebugf("dir %s, filename %s\n", dir.c_str(), filename.c_str()); | |
89 | |
90 return SkOSPath::SkPathJoin(dir.c_str(), filename.c_str()); | 136 return SkOSPath::SkPathJoin(dir.c_str(), filename.c_str()); |
91 } | 137 } |
92 | 138 |
93 bool WriteTask::Expectations::check(const Task& task, SkBitmap bitmap) const { | 139 bool WriteTask::Expectations::check(const Task& task, SkBitmap bitmap) const { |
94 if (!FLAGS_writePath.isEmpty() && 0 == strcmp(FLAGS_writePath[0], fRoot)) { | 140 if (!FLAGS_writePath.isEmpty() && 0 == strcmp(FLAGS_writePath[0], fRoot)) { |
95 SkDebugf("We seem to be reading and writing %s concurrently. This won't
work.\n", fRoot); | 141 SkDebugf("We seem to be reading and writing %s concurrently. This won't
work.\n", fRoot); |
96 return false; | 142 return false; |
97 } | 143 } |
98 | 144 |
99 // PNG is stored unpremultiplied, and going from premul to unpremul to premu
l is lossy. To | |
100 // skirt this problem, we decode the PNG into an unpremul bitmap, convert ou
r bitmap to unpremul | |
101 // if needed, and compare those. Each image goes once from premul to unprem
ul, never back. | |
102 const SkString path = path_to_expected_image(fRoot, task); | 145 const SkString path = path_to_expected_image(fRoot, task); |
103 | 146 SkBitmap expected; |
104 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(path.c_str()))
; | 147 expected.allocPixels(bitmap.info()); |
105 if (NULL == stream.get()) { | 148 if (!PngAndRaw::Decode(path.c_str(), &expected)) { |
106 SkDebugf("Could not read %s.\n", path.c_str()); | |
107 return false; | 149 return false; |
108 } | 150 } |
109 | 151 |
110 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream)); | |
111 if (NULL == decoder.get()) { | |
112 SkDebugf("Could not find a decoder for %s.\n", path.c_str()); | |
113 return false; | |
114 } | |
115 | |
116 const SkImageInfo info = bitmap.info(); | |
117 | |
118 SkBitmap expected; | |
119 expected.allocPixels(info); | |
120 | |
121 // expected will be unpremultiplied. | |
122 decoder->setRequireUnpremultipliedColors(true); | |
123 if (!decoder->decode(stream, &expected, SkImageDecoder::kDecodePixels_Mode))
{ | |
124 SkDebugf("Could not decode %s.\n", path.c_str()); | |
125 return false; | |
126 } | |
127 | |
128 // We always seem to decode to 8888. This puts 565 back in 565. | |
129 if (expected.colorType() != bitmap.colorType()) { | |
130 SkBitmap converted; | |
131 SkAssertResult(expected.copyTo(&converted, bitmap.colorType())); | |
132 expected.swap(converted); | |
133 } | |
134 SkASSERT(expected.config() == bitmap.config()); | |
135 | |
136 // Manually unpremultiply 8888 bitmaps to match expected. | |
137 // Their pixels are shared, concurrently even, so we must copy them. | |
138 if (info.colorType() == kPMColor_SkColorType) { | |
139 SkBitmap unpremul; | |
140 unpremul.allocPixels(info); | |
141 | |
142 SkAutoLockPixels lockSrc(bitmap), lockDst(unpremul); | |
143 const SkPMColor* src = (SkPMColor*)bitmap.getPixels(); | |
144 uint32_t* dst = (uint32_t*)unpremul.getPixels(); | |
145 for (size_t i = 0; i < bitmap.getSize()/4; i++) { | |
146 dst[i] = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(src[i]); | |
147 } | |
148 bitmap.swap(unpremul); | |
149 } | |
150 | |
151 return BitmapsEqual(expected, bitmap); | 152 return BitmapsEqual(expected, bitmap); |
152 } | 153 } |
153 | 154 |
154 } // namespace DM | 155 } // namespace DM |
OLD | NEW |