Browse Source

AnyImageImporter: detect DDS, EXR, HDR, JPEG, PNG and TGA signatures.

That's the only image test data I have at the moment.
pull/191/head
Vladimír Vondruš 8 years ago
parent
commit
df23d64c99
  1. 3
      doc/changelog.dox
  2. 70
      src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp
  3. 27
      src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h
  4. 125
      src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp
  5. 7
      src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt
  6. 1
      src/MagnumPlugins/AnyImageImporter/Test/configure.h.cmake
  7. BIN
      src/MagnumPlugins/AnyImageImporter/Test/gray.jpg
  8. BIN
      src/MagnumPlugins/AnyImageImporter/Test/image.exr
  9. 7
      src/MagnumPlugins/AnyImageImporter/Test/rgb.hdr
  10. BIN
      src/MagnumPlugins/AnyImageImporter/Test/rgb.png
  11. BIN
      src/MagnumPlugins/AnyImageImporter/Test/rgba_dxt1.dds

3
doc/changelog.dox

@ -79,6 +79,9 @@ See also:
@ref Trade::AbstractImageConverter::Feature enums and
@ref Trade::AbstractImporter::Features,
@ref Trade::AbstractImageConverter::Features enum sets
@ref Trade::AnyImageImporter "AnyImageImporter" plugin now supports
detection and loading of DDS, EXR, HDR, JPEG, PNG and TGA files from data
next to file type detection based on filename
@subsection changelog-latest-changes Changes and improvements

70
src/MagnumPlugins/AnyImageImporter/AnyImageImporter.cpp

@ -38,7 +38,7 @@ AnyImageImporter::AnyImageImporter(PluginManager::AbstractManager& manager, cons
AnyImageImporter::~AnyImageImporter() = default;
auto AnyImageImporter::doFeatures() const -> Features { return {}; }
auto AnyImageImporter::doFeatures() const -> Features { return Feature::OpenData; }
bool AnyImageImporter::doIsOpened() const { return !!_in; }
@ -118,6 +118,74 @@ void AnyImageImporter::doOpenFile(const std::string& filename) {
_in = std::move(importer);
}
void AnyImageImporter::doOpenData(Containers::ArrayView<const char> data) {
CORRADE_INTERNAL_ASSERT(manager());
std::string plugin;
/* https://docs.microsoft.com/cs-cz/windows/desktop/direct3ddds/dx-graphics-dds-pguide */
if(Utility::String::viewBeginsWith(data, "DDS "))
plugin = "DdsImporter";
/* http://www.openexr.com/openexrfilelayout.pdf */
else if(Utility::String::viewBeginsWith(data, "\x76\x2f\x31\x01"))
plugin = "OpenExrImporter";
/* https://en.wikipedia.org/wiki/Radiance_(software)#HDR_image_format */
else if(Utility::String::viewBeginsWith(data, "#?RADIANCE"))
plugin = "HdrImporter";
/* https://en.wikipedia.org/wiki/JPEG#Syntax_and_structure */
else if(Utility::String::viewBeginsWith(data, "\xff\xd8\xff"))
plugin = "JpegImporter";
/* https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header */
else if(Utility::String::viewBeginsWith(data, "\x89PNG\x0d\x0a\x1a\x0a"))
plugin = "PngImporter";
/* https://github.com/file/file/blob/d04de269e0b06ccd0a7d1bf4974fed1d75be7d9e/magic/Magdir/images#L18-L22
TGAs are a complete guesswork, so try after everything else fails. */
else if([data]() {
/* TGA header is 18 bytes */
if(data.size() < 18) return false;
/* Third byte (image type) must be one of these */
if(data[2] != 1 && data[2] != 2 && data[2] != 3 &&
data[2] != 9 && data[2] != 10 && data[2] != 11) return false;
/* If image type is 1 or 9, second byte (colormap type) must be 1 */
if((data[2] == 1 || data[2] == 9) && data[1] != 1) return false;
/* ... and 0 otherwise */
if(data[2] != 1 && data[2] != 9 && data[1] != 0) return false;
/* Colormap index (unsigned short, byte 3+4) should be 0 */
if(data[3] != 0 && data[4] != 0) return false;
/* Probably TGA, heh. Or random memory. */
return true;
}()) plugin = "TgaImporter";
else if(!data.size()) {
Error{} << "Trade::AnyImageImporter::openData(): file is empty";
return;
} else {
std::uint32_t signature = data[0] << 24;
if(data.size() > 1) signature |= data[1] << 16;
if(data.size() > 2) signature |= data[2] << 8;
if(data.size() > 3) signature |= data[3];
Error() << "Trade::AnyImageImporter::openData(): cannot determine type from signature" << reinterpret_cast<void*>(signature);
return;
}
/* Try to load the plugin */
if(!(manager()->load(plugin) & PluginManager::LoadState::Loaded)) {
Error() << "Trade::AnyImageImporter::openData(): cannot load" << plugin << "plugin";
return;
}
/* Try to open the file (error output should be printed by the plugin
itself) */
std::unique_ptr<AbstractImporter> importer = static_cast<PluginManager::Manager<AbstractImporter>*>(manager())->instantiate(plugin);
if(!importer->openData(data)) return;
/* Success, save the instance */
_in = std::move(importer);
}
UnsignedInt AnyImageImporter::doImage2DCount() const { return _in->image2DCount(); }
Containers::Optional<ImageData2D> AnyImageImporter::doImage2D(const UnsignedInt id) { return _in->image2D(id); }

27
src/MagnumPlugins/AnyImageImporter/AnyImageImporter.h

@ -68,14 +68,16 @@ of the `Magnum` package and link to the `Magnum::AnyImageImporter` target. See
Supported formats:
- Windows Bitmap (`*.bmp`), loaded with any plugin that provides `BmpImporter`
- DirectDraw Surface (`*.dds`), loaded with @ref DdsImporter or any other
plugin that provides it
- DirectDraw Surface (`*.dds` or data with corresponding signature), loaded
with @ref DdsImporter or any other plugin that provides it
- Graphics Interchange Format (`*.gif`), loaded with any plugin that provides
`GifImporter`
- OpenEXR (`*.exr`), loaded with any plugin that provides `OpenExrImporter`
- Radiance HDR (`*.hdr`), loaded with any plugin that provides `HdrImporter`
- JPEG (`*.jpg`, `*.jpe`, `*.jpeg`), loaded with @ref JpegImporter or any
other plugin that provides it
- OpenEXR (`*.exr` or data with corresponding signature), loaded with any
plugin that provides `OpenExrImporter`
- Radiance HDR (`*.hdr` or data with corresponding signature), loaded with
any plugin that provides `HdrImporter`
- JPEG (`*.jpg`, `*.jpe`, `*.jpeg` or data with corresponding signature),
loaded with @ref JpegImporter or any other plugin that provides it
- JPEG 2000 (`*.jp2`), loaded with any plugin that provides
`Jpeg2000Importer`
- Multiple-image Network Graphics (`*.mng`), loaded with any plugin that
@ -87,18 +89,20 @@ Supported formats:
- Softimage PIC (`*.pic`), loaded with any plugin that provides `PicImporter`
- Portable Anymap (`*.pnm`), loaded with any plugin that provides
`PnmImporter`
- Portable Network Graphics (`*.png`), loaded with @ref PngImporter or any
other plugin that provides it
- Portable Network Graphics (`*.png` or data with corresponding signature),
loaded with @ref PngImporter or any other plugin that provides it
- Portable Pixmap (`*.ppm`), loaded with any plugin that provides `PpmImporter`
- Adobe Photoshop (`*.psd`), loaded with any plugin that provides `PsdImporter`
- Silicon Graphics (`*.sgi`, `*.bw`, `*.rgb`, `*.rgba`), loaded with any
plugin that provides `SgiImporter`
- Tagged Image File Format (`*.tif`, `*.tiff`), loaded with any plugin that
provides `TiffImporter`
- Truevision TGA (`*.tga`, `*.vda`, `*.icb`, `*.vst`), loaded with
@ref TgaImporter or any other plugin that provides it
- Truevision TGA (`*.tga`, `*.vda`, `*.icb`, `*.vst` or data with
corresponding signature), loaded with @ref TgaImporter or any other plugin
that provides it
Only loading from files is supported.
Detecting file type through @ref openData() is supported only for a subset of
formats that are marked as such in the list above.
*/
class MAGNUM_ANYIMAGEIMPORTER_EXPORT AnyImageImporter: public AbstractImporter {
public:
@ -115,6 +119,7 @@ class MAGNUM_ANYIMAGEIMPORTER_EXPORT AnyImageImporter: public AbstractImporter {
MAGNUM_ANYIMAGEIMPORTER_LOCAL bool doIsOpened() const override;
MAGNUM_ANYIMAGEIMPORTER_LOCAL void doClose() override;
MAGNUM_ANYIMAGEIMPORTER_LOCAL void doOpenFile(const std::string& filename) override;
MAGNUM_ANYIMAGEIMPORTER_LOCAL void doOpenData(Containers::ArrayView<const char> data) override;
MAGNUM_ANYIMAGEIMPORTER_LOCAL UnsignedInt doImage2DCount() const override;
MAGNUM_ANYIMAGEIMPORTER_LOCAL Containers::Optional<ImageData2D> doImage2D(UnsignedInt id) override;

125
src/MagnumPlugins/AnyImageImporter/Test/AnyImageImporterTest.cpp

@ -26,6 +26,8 @@
#include <sstream>
#include <Corrade/PluginManager/Manager.h>
#include <Corrade/TestSuite/Tester.h>
#include <Corrade/Utility/Directory.h>
#include <Corrade/Utility/Format.h>
#include "Magnum/Trade/AbstractImporter.h"
#include "Magnum/Trade/ImageData.h"
@ -37,18 +39,70 @@ namespace Magnum { namespace Trade { namespace Test {
struct AnyImageImporterTest: TestSuite::Tester {
explicit AnyImageImporterTest();
void tga();
void load();
void unknown();
void detect();
void unknownExtension();
void unknownSignature();
void emptyData();
/* Explicitly forbid system-wide plugin dependencies */
PluginManager::Manager<AbstractImporter> _manager{"nonexistent"};
};
namespace {
Containers::ArrayView<const char> fileCallback(const std::string& filename, Trade::ImporterFileCallbackPolicy, Containers::Array<char>& storage) {
storage = Utility::Directory::read(filename);
return Containers::ArrayView<const char>{storage};
}
constexpr struct {
const char* name;
const char* filename;
Containers::ArrayView<const char>(*callback)(const std::string&, Trade::ImporterFileCallbackPolicy, Containers::Array<char>&);
} LoadData[]{
{"TGA", TGA_FILE, nullptr},
{"TGA data", TGA_FILE, fileCallback}
};
constexpr struct {
const char* name;
const char* filename;
Containers::ArrayView<const char>(*callback)(const std::string&, Trade::ImporterFileCallbackPolicy, Containers::Array<char>&);
const char* plugin;
} DetectData[]{
{"PNG", "rgb.png", nullptr, "PngImporter"},
{"PNG data", "rgb.png", fileCallback, "PngImporter"},
{"JPEG", "gray.jpg", nullptr, "JpegImporter"},
{"JPEG data", "gray.jpg", fileCallback, "JpegImporter"},
{"JPEG2000", "image.jp2", nullptr, "Jpeg2000Importer"},
{"EXR", "image.exr", nullptr, "OpenExrImporter"},
{"EXR data", "image.exr", fileCallback, "OpenExrImporter"},
{"HDR", "rgb.hdr", nullptr, "HdrImporter"},
{"HDR data", "rgb.hdr", fileCallback, "HdrImporter"},
{"DDS", "rgba_dxt1.dds", nullptr, "DdsImporter"},
{"DDS data", "rgba_dxt1.dds", fileCallback, "DdsImporter"},
{"BMP", "image.bmp", nullptr, "BmpImporter"},
{"GIF", "image.gif", nullptr, "GifImporter"},
{"PSD", "image.psd", nullptr, "PsdImporter"},
{"TIFF", "image.tiff", nullptr, "TiffImporter"}
/* Not testing everything, just the most important ones */
};
}
AnyImageImporterTest::AnyImageImporterTest() {
addTests({&AnyImageImporterTest::tga,
addInstancedTests({&AnyImageImporterTest::load},
Containers::arraySize(LoadData));
addInstancedTests({&AnyImageImporterTest::detect},
Containers::arraySize(DetectData));
&AnyImageImporterTest::unknown});
addTests({&AnyImageImporterTest::unknownExtension,
&AnyImageImporterTest::unknownSignature,
&AnyImageImporterTest::emptyData});
/* Load the plugin directly from the build tree. Otherwise it's static and
already loaded. */
@ -61,20 +115,55 @@ AnyImageImporterTest::AnyImageImporterTest() {
#endif
}
void AnyImageImporterTest::tga() {
void AnyImageImporterTest::load() {
auto&& data = LoadData[testCaseInstanceId()];
setTestCaseDescription(data.name);
if(!(_manager.loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("TgaImporter plugin not enabled, cannot test");
std::unique_ptr<AbstractImporter> importer = _manager.instantiate("AnyImageImporter");
CORRADE_VERIFY(importer->openFile(TGA_FILE));
Containers::Array<char> storage;
importer->setFileCallback(data.callback, storage);
CORRADE_VERIFY(importer->openFile(data.filename));
/* Check only size, as it is good enough proof that it is working */
Containers::Optional<ImageData2D> image = importer->image2D(0);
CORRADE_VERIFY(image);
CORRADE_COMPARE(image->size(), Vector2i(2, 3));
importer->close();
CORRADE_VERIFY(!importer->isOpened());
}
void AnyImageImporterTest::detect() {
auto&& data = DetectData[testCaseInstanceId()];
setTestCaseDescription(data.name);
std::unique_ptr<AbstractImporter> importer = _manager.instantiate("AnyImageImporter");
Containers::Array<char> storage;
importer->setFileCallback(data.callback, storage);
std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(!importer->openFile(Utility::Directory::join(TEST_FILE_DIR, data.filename)));
#ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT
CORRADE_COMPARE(out.str(), Utility::format(
R"(PluginManager::Manager::load(): plugin {0} is not static and was not found in nonexistent
Trade::AnyImageImporter::{1}(): cannot load {0} plugin
)", data.plugin, data.callback ? "openData" : "openFile"));
#else
CORRADE_COMPARE(out.str(), Utility::format(
R"(PluginManager::Manager::load(): plugin {0} was not found
Trade::AnyImageImporter::{1}(): cannot load {0} plugin
)", data.plugin, data.callback ? "openData" : "openFile"));
#endif
}
void AnyImageImporterTest::unknown() {
void AnyImageImporterTest::unknownExtension() {
std::ostringstream output;
Error redirectError{&output};
@ -84,6 +173,28 @@ void AnyImageImporterTest::unknown() {
CORRADE_COMPARE(output.str(), "Trade::AnyImageImporter::openFile(): cannot determine type of file image.xcf\n");
}
void AnyImageImporterTest::unknownSignature() {
std::ostringstream output;
Error redirectError{&output};
constexpr const char data[]{ 0x25, 0x3a };
std::unique_ptr<AbstractImporter> importer = _manager.instantiate("AnyImageImporter");
CORRADE_VERIFY(!importer->openData(data));
CORRADE_COMPARE(output.str(), "Trade::AnyImageImporter::openData(): cannot determine type from signature 0x253a0000\n");
}
void AnyImageImporterTest::emptyData() {
std::ostringstream output;
Error redirectError{&output};
std::unique_ptr<AbstractImporter> importer = _manager.instantiate("AnyImageImporter");
CORRADE_VERIFY(!importer->openData(nullptr));
CORRADE_COMPARE(output.str(), "Trade::AnyImageImporter::openData(): file is empty\n");
}
}}}
CORRADE_TEST_MAIN(Magnum::Trade::Test::AnyImageImporterTest)

7
src/MagnumPlugins/AnyImageImporter/Test/CMakeLists.txt

@ -24,8 +24,10 @@
#
if(CORRADE_TARGET_EMSCRIPTEN OR CORRADE_TARGET_ANDROID)
set(TEST_FILE_DIR .)
set(TGA_FILE file.tga)
else()
set(TEST_FILE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(TGA_FILE ${PROJECT_SOURCE_DIR}/src/MagnumPlugins/TgaImporter/Test/file.tga)
endif()
@ -52,6 +54,11 @@ endif()
corrade_add_test(AnyImageImporterTest AnyImageImporterTest.cpp
LIBRARIES MagnumTrade
FILES
gray.jpg
image.exr
rgb.hdr
rgb.png
rgba_dxt1.dds
../../TgaImporter/Test/file.tga)
if(NOT BUILD_PLUGINS_STATIC)
target_include_directories(AnyImageImporterTest PRIVATE $<TARGET_FILE_DIR:AnyImageImporterTest>)

1
src/MagnumPlugins/AnyImageImporter/Test/configure.h.cmake

@ -26,3 +26,4 @@
#cmakedefine ANYIMAGEIMPORTER_PLUGIN_FILENAME "${ANYIMAGEIMPORTER_PLUGIN_FILENAME}"
#cmakedefine TGAIMPORTER_PLUGIN_FILENAME "${TGAIMPORTER_PLUGIN_FILENAME}"
#define TGA_FILE "${TGA_FILE}"
#define TEST_FILE_DIR "${TEST_FILE_DIR}"

BIN
src/MagnumPlugins/AnyImageImporter/Test/gray.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

BIN
src/MagnumPlugins/AnyImageImporter/Test/image.exr

Binary file not shown.

7
src/MagnumPlugins/AnyImageImporter/Test/rgb.hdr

@ -0,0 +1,7 @@
#?RADIANCE
# Written by stb_image_write.h
FORMAT=32-bit_rle_rgbe
EXPOSURE= 1.0000000000000
-Y 3 +X 2
   ƒÀÀÀƒÀÀÀ‚€€€ƒ€€€<EFBFBD>€€€‚

BIN
src/MagnumPlugins/AnyImageImporter/Test/rgb.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 B

BIN
src/MagnumPlugins/AnyImageImporter/Test/rgba_dxt1.dds

Binary file not shown.
Loading…
Cancel
Save