aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-03-03 15:43:56 +0000
committerGravatar commit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>2014-03-03 15:43:56 +0000
commit1426c1e9b6fdd803ebc88321ac6e08bb4a31fe0a (patch)
tree98001e7be6865156df27c3c20fea3522a5dcd252
parent47d98c8e85924effc651b09df72027e6801c92a1 (diff)
DM: fix -w/-r problems stemming from PM/UPM conversions.
DM will now read and write a custom image format, which begins first with a normal PNG, but also includes the bitmap's raw pixels. Most tools (file browser, Preview, Chrome, Skia image decoder) will read the file as PNG. DM skips the encoded PNG and reads the raw pixels instead. This scheme allows perfect pixel comparisons that never go through PM -> UPM or UPM -> PM conversions, as long as you compare on a machine with the same native pixel format as the machine which generated the images. Since we've only ever used this to do same-machine comparisons, that's more than good enough for now. We could convert as needed to a standardized PM pixel format if we find we want these files to be portable. DM -w output now does increase to ~1.3G. If that turns out to be annoyingly big, I'm sure I can come up with a simple pixelwise RLE instead of writing uncompressed pixels. BUG=skia: R=reed@google.com, bsalomon@google.com, mtklein@google.com Author: mtklein@chromium.org Review URL: https://codereview.chromium.org/185343002 git-svn-id: http://skia.googlecode.com/svn/trunk@13638 2bbb7eff-a529-9590-31e7-b0007b416f81
-rw-r--r--dm/DMWriteTask.cpp111
1 files changed, 56 insertions, 55 deletions
diff --git a/dm/DMWriteTask.cpp b/dm/DMWriteTask.cpp
index e30cbdbf85..04479e94fb 100644
--- a/dm/DMWriteTask.cpp
+++ b/dm/DMWriteTask.cpp
@@ -3,10 +3,9 @@
#include "DMUtil.h"
#include "SkColorPriv.h"
#include "SkCommandLineFlags.h"
-#include "SkImageDecoder.h"
#include "SkImageEncoder.h"
#include "SkString.h"
-#include "SkUnPreMultiply.h"
+#include "SkStream.h"
DEFINE_string2(writePath, w, "", "If set, write GMs here as .pngs.");
@@ -39,6 +38,58 @@ void WriteTask::makeDirOrFail(SkString dir) {
}
}
+// One file that first contains a .png of an SkBitmap, then its raw pixels.
+// We use this custom format to avoid premultiplied/unpremultiplied pixel conversions.
+struct PngAndRaw {
+ static bool Encode(SkBitmap bitmap, const char* path) {
+ SkFILEWStream stream(path);
+ if (!stream.isValid()) {
+ SkDebugf("Can't write %s.\n", path);
+ return false;
+ }
+
+ // Write a PNG first for humans and other tools to look at.
+ if (!SkImageEncoder::EncodeStream(&stream, bitmap, SkImageEncoder::kPNG_Type, 100)) {
+ SkDebugf("Can't encode a PNG.\n");
+ return false;
+ }
+
+ // Then write our secret raw pixels that only DM reads.
+ SkAutoLockPixels lock(bitmap);
+ return stream.write(bitmap.getPixels(), bitmap.getSize());
+ }
+
+ // This assumes bitmap already has allocated pixels of the correct size.
+ static bool Decode(const char* path, SkBitmap* bitmap) {
+ SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(path));
+ if (NULL == stream.get()) {
+ SkDebugf("Can't read %s.\n", path);
+ return false;
+ }
+
+ // The raw pixels are at the end of the stream. Seek ahead to skip beyond the encoded PNG.
+ const size_t bitmapBytes = bitmap->getSize();
+ if (stream->getLength() < bitmapBytes) {
+ SkDebugf("%s is too small to contain the bitmap we're looking for.\n", path);
+ return false;
+ }
+ SkAssertResult(stream->seek(stream->getLength() - bitmapBytes));
+
+ // Read the raw pixels.
+ // TODO(mtklein): can we install a pixelref that just hangs onto the stream instead of
+ // copying into a malloc'd buffer?
+ SkAutoLockPixels lock(*bitmap);
+ if (bitmapBytes != stream->read(bitmap->getPixels(), bitmapBytes)) {
+ SkDebugf("Couldn't read raw bitmap from %s at %lu of %lu.\n",
+ path, stream->getPosition(), stream->getLength());
+ return false;
+ }
+
+ SkASSERT(stream->isAtEnd());
+ return true;
+ }
+};
+
void WriteTask::draw() {
SkString dir(FLAGS_writePath[0]);
this->makeDirOrFail(dir);
@@ -48,10 +99,7 @@ void WriteTask::draw() {
}
SkString path = SkOSPath::SkPathJoin(dir.c_str(), fGmName.c_str());
path.append(".png");
- if (!SkImageEncoder::EncodeFile(path.c_str(),
- fBitmap,
- SkImageEncoder::kPNG_Type,
- 100/*quality*/)) {
+ if (!PngAndRaw::Encode(fBitmap, path.c_str())) {
this->fail();
}
}
@@ -85,8 +133,6 @@ static SkString path_to_expected_image(const char* root, const Task& task) {
filename.remove(filename.size() - suffixLength, suffixLength);
filename.append(".png");
- //SkDebugf("dir %s, filename %s\n", dir.c_str(), filename.c_str());
-
return SkOSPath::SkPathJoin(dir.c_str(), filename.c_str());
}
@@ -96,58 +142,13 @@ bool WriteTask::Expectations::check(const Task& task, SkBitmap bitmap) const {
return false;
}
- // PNG is stored unpremultiplied, and going from premul to unpremul to premul is lossy. To
- // skirt this problem, we decode the PNG into an unpremul bitmap, convert our bitmap to unpremul
- // if needed, and compare those. Each image goes once from premul to unpremul, never back.
const SkString path = path_to_expected_image(fRoot, task);
-
- SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(path.c_str()));
- if (NULL == stream.get()) {
- SkDebugf("Could not read %s.\n", path.c_str());
- return false;
- }
-
- SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(stream));
- if (NULL == decoder.get()) {
- SkDebugf("Could not find a decoder for %s.\n", path.c_str());
- return false;
- }
-
- const SkImageInfo info = bitmap.info();
-
SkBitmap expected;
- expected.allocPixels(info);
-
- // expected will be unpremultiplied.
- decoder->setRequireUnpremultipliedColors(true);
- if (!decoder->decode(stream, &expected, SkImageDecoder::kDecodePixels_Mode)) {
- SkDebugf("Could not decode %s.\n", path.c_str());
+ expected.allocPixels(bitmap.info());
+ if (!PngAndRaw::Decode(path.c_str(), &expected)) {
return false;
}
- // We always seem to decode to 8888. This puts 565 back in 565.
- if (expected.colorType() != bitmap.colorType()) {
- SkBitmap converted;
- SkAssertResult(expected.copyTo(&converted, bitmap.colorType()));
- expected.swap(converted);
- }
- SkASSERT(expected.config() == bitmap.config());
-
- // Manually unpremultiply 8888 bitmaps to match expected.
- // Their pixels are shared, concurrently even, so we must copy them.
- if (info.colorType() == kPMColor_SkColorType) {
- SkBitmap unpremul;
- unpremul.allocPixels(info);
-
- SkAutoLockPixels lockSrc(bitmap), lockDst(unpremul);
- const SkPMColor* src = (SkPMColor*)bitmap.getPixels();
- uint32_t* dst = (uint32_t*)unpremul.getPixels();
- for (size_t i = 0; i < bitmap.getSize()/4; i++) {
- dst[i] = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(src[i]);
- }
- bitmap.swap(unpremul);
- }
-
return BitmapsEqual(expected, bitmap);
}