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:
parent
9880607151
commit
f6139f7c38
54
dm/DM.cpp
54
dm/DM.cpp
@ -18,6 +18,7 @@
|
|||||||
#include "DMCpuGMTask.h"
|
#include "DMCpuGMTask.h"
|
||||||
#include "DMGpuGMTask.h"
|
#include "DMGpuGMTask.h"
|
||||||
#include "DMGpuSupport.h"
|
#include "DMGpuSupport.h"
|
||||||
|
#include "DMImageTask.h"
|
||||||
#include "DMJsonWriter.h"
|
#include "DMJsonWriter.h"
|
||||||
#include "DMPDFTask.h"
|
#include "DMPDFTask.h"
|
||||||
#include "DMReporter.h"
|
#include "DMReporter.h"
|
||||||
@ -50,6 +51,7 @@ DEFINE_bool(gms, true, "Run GMs?");
|
|||||||
DEFINE_bool(tests, true, "Run tests?");
|
DEFINE_bool(tests, true, "Run tests?");
|
||||||
DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pasted into"
|
DEFINE_bool(reportUsedChars, false, "Output test font construction data to be pasted into"
|
||||||
" create_test_font.cpp.");
|
" create_test_font.cpp.");
|
||||||
|
DEFINE_string(images, "resources", "Path to directory containing images to decode.");
|
||||||
|
|
||||||
__SK_FORCE_IMAGE_DECODER_LINKING;
|
__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) {
|
static void find_files(const char* dir,
|
||||||
if (FLAGS_skps.isEmpty()) {
|
const char* suffixes[],
|
||||||
|
size_t num_suffixes,
|
||||||
|
SkTArray<SkString>* files) {
|
||||||
|
if (0 == strcmp(dir, "")) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SkOSFile::Iter it(FLAGS_skps[0], ".skp");
|
|
||||||
SkString filename;
|
SkString filename;
|
||||||
while (it.next(&filename)) {
|
for (size_t i = 0; i < num_suffixes; i++) {
|
||||||
if (!SkCommandLineFlags::ShouldSkip(FLAGS_match, filename.c_str())) {
|
SkOSFile::Iter it(dir, suffixes[i]);
|
||||||
skps->push_back(SkOSPath::Join(FLAGS_skps[0], filename.c_str()));
|
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) {
|
static void report_failures(const SkTArray<SkString>& failures) {
|
||||||
if (failures.count() == 0) {
|
if (failures.count() == 0) {
|
||||||
return;
|
return;
|
||||||
@ -217,17 +240,28 @@ int dm_main() {
|
|||||||
append_matching_factories<Test>(TestRegistry::Head(), &tests);
|
append_matching_factories<Test>(TestRegistry::Head(), &tests);
|
||||||
}
|
}
|
||||||
|
|
||||||
SkTArray<SkString> skps;
|
|
||||||
find_skps(&skps);
|
|
||||||
|
|
||||||
SkDebugf("%d GMs x %d configs, %d tests, %d pictures\n",
|
SkTArray<SkString> skps;
|
||||||
gms.count(), configs.count(), tests.count(), skps.count());
|
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::Reporter reporter;
|
||||||
|
|
||||||
DM::TaskRunner tasks;
|
DM::TaskRunner tasks;
|
||||||
kick_off_tests(tests, &reporter, &tasks);
|
kick_off_tests(tests, &reporter, &tasks);
|
||||||
kick_off_gms(gms, configs, gpuAPI, &reporter, &tasks);
|
kick_off_gms(gms, configs, gpuAPI, &reporter, &tasks);
|
||||||
kick_off_skps(skps, &reporter, &tasks);
|
kick_off_skps(skps, &reporter, &tasks);
|
||||||
|
kick_off_images(images, &reporter, &tasks);
|
||||||
tasks.wait();
|
tasks.wait();
|
||||||
|
|
||||||
DM::JsonWriter::DumpJson();
|
DM::JsonWriter::DumpJson();
|
||||||
|
78
dm/DMImageTask.cpp
Normal file
78
dm/DMImageTask.cpp
Normal 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
30
dm/DMImageTask.h
Normal 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
|
@ -60,8 +60,9 @@ WriteTask::WriteTask(const Task& parent,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WriteTask::makeDirOrFail(SkString dir) {
|
void WriteTask::makeDirOrFail(SkString dir) {
|
||||||
if (!sk_mkdir(dir.c_str())) {
|
// This can be a little racy, so if it fails check to see if someone else succeeded.
|
||||||
this->fail();
|
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);
|
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() {
|
void WriteTask::draw() {
|
||||||
SkString md5;
|
SkString md5;
|
||||||
{
|
{
|
||||||
@ -153,7 +167,7 @@ void WriteTask::draw() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ok = fData ? write_asset(fData, &file)
|
bool ok = fData ? write_asset(fData, &file)
|
||||||
: SkImageEncoder::EncodeStream(&file, fBitmap, SkImageEncoder::kPNG_Type, 100);
|
: encode_png(fBitmap, &file);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return this->fail("Can't write to file.");
|
return this->fail("Can't write to file.");
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
'../dm/DMCpuGMTask.cpp',
|
'../dm/DMCpuGMTask.cpp',
|
||||||
'../dm/DMGpuGMTask.cpp',
|
'../dm/DMGpuGMTask.cpp',
|
||||||
'../dm/DMPDFRasterizeTask.cpp',
|
'../dm/DMPDFRasterizeTask.cpp',
|
||||||
|
'../dm/DMImageTask.cpp',
|
||||||
'../dm/DMJsonWriter.cpp',
|
'../dm/DMJsonWriter.cpp',
|
||||||
'../dm/DMPDFTask.cpp',
|
'../dm/DMPDFTask.cpp',
|
||||||
'../dm/DMPipeTask.cpp',
|
'../dm/DMPipeTask.cpp',
|
||||||
|
Loading…
Reference in New Issue
Block a user