Add image decoding mode to DM.

This is meant to supplant skimage.

BUG=skia:3235

Review URL: https://codereview.chromium.org/802793002
This commit is contained in:
mtklein 2014-12-12 16:41:12 -08:00 committed by Commit bot
parent 9880607151
commit f6139f7c38
5 changed files with 170 additions and 13 deletions

View File

@ -18,6 +18,7 @@
#include "DMCpuGMTask.h"
#include "DMGpuGMTask.h"
#include "DMGpuSupport.h"
#include "DMImageTask.h"
#include "DMJsonWriter.h"
#include "DMPDFTask.h"
#include "DMReporter.h"
@ -50,6 +51,7 @@ DEFINE_bool(gms, true, "Run GMs?");
DEFINE_bool(tests, true, "Run tests?");
DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pasted into"
" create_test_font.cpp.");
DEFINE_string(images, "resources", "Path to directory containing images to decode.");
__SK_FORCE_IMAGE_DECODER_LINKING;
@ -119,16 +121,21 @@ static void kick_off_tests(const SkTDArray<TestRegistry::Factory>& tests,
}
}
static void find_skps(SkTArray<SkString>* skps) {
if (FLAGS_skps.isEmpty()) {
static void find_files(const char* dir,
const char* suffixes[],
size_t num_suffixes,
SkTArray<SkString>* files) {
if (0 == strcmp(dir, "")) {
return;
}
SkOSFile::Iter it(FLAGS_skps[0], ".skp");
SkString filename;
while (it.next(&filename)) {
if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str())) {
skps->push_back(SkOSPath::Join(FLAGS_skps[0], filename.c_str()));
for (size_t i = 0; i < num_suffixes; i++) {
SkOSFile::Iter it(dir, suffixes[i]);
while (it.next(&filename)) {
if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str())) {
files->push_back(SkOSPath::Join(dir, filename.c_str()));
}
}
}
}
@ -155,6 +162,22 @@ static void kick_off_skps(const SkTArray<SkString>& skps,
}
}
static void kick_off_images(const SkTArray<SkString>& images,
DM::Reporter* reporter,
DM::TaskRunner* tasks) {
for (int i = 0; i < images.count(); i++) {
SkAutoTUnref<SkData> image(SkData::NewFromFileName(images[i].c_str()));
if (!image) {
SkDebugf("Could not read %s.\n", images[i].c_str());
exit(1);
}
SkString filename = SkOSPath::Basename(images[i].c_str());
tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename)));
tasks->add(SkNEW_ARGS(DM::ImageTask, (reporter, tasks, image, filename, 5/*subsets*/)));
}
}
static void report_failures(const SkTArray<SkString>& failures) {
if (failures.count() == 0) {
return;
@ -217,17 +240,28 @@ int dm_main() {
append_matching_factories<Test>(TestRegistry::Head(), &tests);
}
SkTArray<SkString> skps;
find_skps(&skps);
SkDebugf("%d GMs x %d configs, %d tests, %d pictures\n",
gms.count(), configs.count(), tests.count(), skps.count());
SkTArray<SkString> skps;
if (!FLAGS_skps.isEmpty()) {
const char* suffixes[] = { "skp" };
find_files(FLAGS_skps[0], suffixes, SK_ARRAY_COUNT(suffixes), &skps);
}
SkTArray<SkString> images;
if (!FLAGS_images.isEmpty()) {
const char* suffixes[] = { "bmp", "gif", "jpg", "png", "webp", "ktx", "astc" };
find_files(FLAGS_images[0], suffixes, SK_ARRAY_COUNT(suffixes), &images);
}
SkDebugf("%d GMs x %d configs, %d tests, %d pictures, %d images\n",
gms.count(), configs.count(), tests.count(), skps.count(), images.count());
DM::Reporter reporter;
DM::TaskRunner tasks;
kick_off_tests(tests, &reporter, &tasks);
kick_off_gms(gms, configs, gpuAPI, &reporter, &tasks);
kick_off_skps(skps, &reporter, &tasks);
kick_off_images(images, &reporter, &tasks);
tasks.wait();
DM::JsonWriter::DumpJson();

78
dm/DMImageTask.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "DMImageTask.h"
#include "DMUtil.h"
#include "DMWriteTask.h"
#include "SkImageDecoder.h"
#include "SkRandom.h"
#include <string.h>
namespace DM {
// This converts file names like "mandrill_128.r11.ktx" into
// "mandrill-128-r11_ktx" or "mandrill-128-r11-5-subsets_ktx".
static SkString task_name(SkString filename, int subsets) {
const char* ext = strrchr(filename.c_str(), '.');
SkString name(filename.c_str(), ext - filename.c_str());
if (subsets > 0) {
name.appendf("_%d_subsets", subsets);
}
name = FileToTaskName(name); // Promote any stray '.' in the filename to '_'.
name.append(ext); // Tack on the extension, including the '.'.
return FileToTaskName(name); // Promote that last '.' to '_', other '_' to '-'.
}
ImageTask::ImageTask(Reporter* r, TaskRunner* t, const SkData* encoded, SkString name, int subsets)
: CpuTask(r, t)
, fEncoded(SkRef(encoded))
, fName(task_name(name, subsets))
, fSubsets(subsets) {}
void ImageTask::draw() {
if (fSubsets == 0) {
// Decoding the whole image is considerably simpler than decoding subsets!
SkBitmap bitmap;
if (!SkImageDecoder::DecodeMemory(fEncoded->data(), fEncoded->size(), &bitmap)) {
return this->fail("Can't DecodeMemory");
}
this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "image", bitmap)));
return;
}
SkMemoryStream stream(fEncoded->data(), fEncoded->size());
SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
if (!decoder) {
return this->fail("Can't find good decoder.");
}
int w,h;
if (!decoder->buildTileIndex(&stream, &w, &h) || w*h == 1) {
return; // Subset decoding is not always supported.
}
SkBitmap composite;
composite.allocN32Pixels(w,h); // We're lazy here and just always use native 8888.
composite.eraseColor(SK_ColorTRANSPARENT);
SkCanvas canvas(composite);
SkRandom rand;
for (int i = 0; i < fSubsets; i++) {
SkIRect rect;
do {
rect.fLeft = rand.nextULessThan(w);
rect.fTop = rand.nextULessThan(h);
rect.fRight = rand.nextULessThan(w);
rect.fBottom = rand.nextULessThan(h);
rect.sort();
} while (rect.isEmpty());
SkBitmap subset;
if (!decoder->decodeSubset(&subset, rect, kN32_SkColorType)) {
return this->fail("Could not decode subset.");
}
canvas.drawBitmap(subset, SkIntToScalar(rect.fLeft), SkIntToScalar(rect.fTop));
}
canvas.flush();
this->spawnChild(SkNEW_ARGS(WriteTask, (*this, "image", composite)));
}
} // namespace DM

30
dm/DMImageTask.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef DMImageTask_DEFINED
#define DMImageTask_DEFINED
#include "DMReporter.h"
#include "DMTask.h"
#include "DMTaskRunner.h"
#include "SkData.h"
#include "SkString.h"
// Decode an image into its natural bitmap, perhaps decoding random subsets.
namespace DM {
class ImageTask : public CpuTask {
public:
ImageTask(Reporter*, TaskRunner*, const SkData*, SkString name, int subsets = 0);
void draw() SK_OVERRIDE;
bool shouldSkip() const SK_OVERRIDE { return false; }
SkString name() const SK_OVERRIDE { return fName; }
private:
SkAutoTUnref<const SkData> fEncoded;
const SkString fName;
int fSubsets;
};
} // namespace DM
#endif // DMImageTask_DEFINED

View File

@ -60,8 +60,9 @@ WriteTask::WriteTask(const Task& parent,
}
void WriteTask::makeDirOrFail(SkString dir) {
if (!sk_mkdir(dir.c_str())) {
this->fail();
// This can be a little racy, so if it fails check to see if someone else succeeded.
if (!sk_mkdir(dir.c_str()) && !sk_isdir(dir.c_str())) {
this->fail("Can't make directory.");
}
}
@ -92,6 +93,19 @@ static SkString get_md5(SkStreamAsset* stream) {
return get_md5_string(&hasher);
}
static bool encode_png(const SkBitmap& src, SkFILEWStream* file) {
SkBitmap bm;
// We can't encode A8 bitmaps as PNGs. Convert them to 8888 first.
if (src.info().colorType() == kAlpha_8_SkColorType) {
if (!src.copyTo(&bm, kN32_SkColorType)) {
return false;
}
} else {
bm = src;
}
return SkImageEncoder::EncodeStream(file, bm, SkImageEncoder::kPNG_Type, 100);
}
void WriteTask::draw() {
SkString md5;
{
@ -153,7 +167,7 @@ void WriteTask::draw() {
}
bool ok = fData ? write_asset(fData, &file)
: SkImageEncoder::EncodeStream(&file, fBitmap, SkImageEncoder::kPNG_Type, 100);
: encode_png(fBitmap, &file);
if (!ok) {
return this->fail("Can't write to file.");
}

View File

@ -32,6 +32,7 @@
'../dm/DMCpuGMTask.cpp',
'../dm/DMGpuGMTask.cpp',
'../dm/DMPDFRasterizeTask.cpp',
'../dm/DMImageTask.cpp',
'../dm/DMJsonWriter.cpp',
'../dm/DMPDFTask.cpp',
'../dm/DMPipeTask.cpp',