New docio-lib to detect file format by content (fix #776)

This commit is contained in:
David Capello 2016-10-26 19:37:42 -03:00
parent 02e82289dc
commit 693a68844c
29 changed files with 326 additions and 86 deletions

View File

@ -109,6 +109,7 @@ add_subdirectory(gen)
add_subdirectory(gfx)
add_subdirectory(net)
add_subdirectory(render)
add_subdirectory(docio)
add_subdirectory(script)
add_subdirectory(she)
add_subdirectory(ui)

View File

@ -47,11 +47,11 @@ because they don't depend on any other component.
## Level 4
* [iff](iff/) (base, doc, render): Image File Formats library (load/save documents).
* [docio](docio/) (base, flic): Load/save documents.
## Level 5
* [app](app/) (allegro, base, doc, filters, fixmath, gfx, iff, pen, render, scripting, she, ui, undo, updater, webserver)
* [app](app/) (allegro, base, doc, docio, filters, fixmath, flic, gfx, pen, render, scripting, she, ui, undo, updater, webserver)
## Level 6

View File

@ -451,6 +451,7 @@ target_link_libraries(app-lib
clip
css-lib
doc-lib
docio-lib
filters-lib
fixmath-lib
flic-lib

View File

@ -145,6 +145,7 @@ private:
class AseFormat : public FileFormat {
const char* onGetName() const override { return "ase"; }
const char* onGetExtensions() const override { return "ase,aseprite"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::ASE_ANIMATION; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -45,6 +45,7 @@ class BmpFormat : public FileFormat {
const char* onGetName() const override { return "bmp"; }
const char* onGetExtensions() const override { return "bmp"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::BMP_IMAGE; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -28,6 +28,7 @@
#include "base/shared_ptr.h"
#include "base/string.h"
#include "doc/doc.h"
#include "docio/detect_format.h"
#include "render/quantization.h"
#include "render/render.h"
#include "ui/alert.h"
@ -127,10 +128,7 @@ FileOp* FileOp::createLoadDocumentOperation(Context* context, const char* filena
if (!fop)
return nullptr;
// Get the extension of the filename (in lower case)
std::string extension = base::string_to_lower(base::get_file_extension(filename));
LOG("Loading file \"%s\" (%s)\n", filename, extension.c_str());
LOG("Loading file \"%s\"\n", filename);
// Does file exist?
if (!base::is_file(filename)) {
@ -139,12 +137,12 @@ FileOp* FileOp::createLoadDocumentOperation(Context* context, const char* filena
}
// Get the format through the extension of the filename
fop->m_format = FileFormatsManager::instance()
->getFileFormatByExtension(extension.c_str());
fop->m_format = FileFormatsManager::instance()->getFileFormat(
docio::detect_format(filename));
if (!fop->m_format ||
!fop->m_format->support(FILE_SUPPORT_LOAD)) {
fop->setError("%s can't load \"%s\" files\n", PACKAGE, extension.c_str());
fop->setError("%s can't load \"%s\" file (\"%s\")\n", PACKAGE,
filename, base::get_file_extension(filename).c_str());
goto done;
}
@ -228,17 +226,15 @@ FileOp* FileOp::createSaveDocumentOperation(const Context* context,
fop->m_document = const_cast<Document*>(document);
// Get the extension of the filename (in lower case)
std::string extension = base::string_to_lower(base::get_file_extension(filename));
LOG("Saving document \"%s\" (%s)\n", filename, extension.c_str());
LOG("Saving document \"%s\"\n", filename);
// Get the format through the extension of the filename
fop->m_format = FileFormatsManager::instance()
->getFileFormatByExtension(extension.c_str());
fop->m_format = FileFormatsManager::instance()->getFileFormat(
docio::detect_format_by_file_extension(filename));
if (!fop->m_format ||
!fop->m_format->support(FILE_SUPPORT_SAVE)) {
fop->setError("%s can't save \"%s\" files\n", PACKAGE, extension.c_str());
fop->setError("%s can't save \"%s\" file (\"%s\")\n", PACKAGE,
filename, base::get_file_extension(filename).c_str());
return fop.release();
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -33,6 +33,11 @@ const char* FileFormat::extensions() const
return onGetExtensions();
}
docio::FileFormat FileFormat::docioFormat() const
{
return onGetDocioFormat();
}
bool FileFormat::load(FileOp* fop)
{
ASSERT(support(FILE_SUPPORT_LOAD));

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -9,6 +9,7 @@
#pragma once
#include "base/shared_ptr.h"
#include "docio/file_format.h"
#include <vector>
@ -44,6 +45,8 @@ namespace app {
const char* name() const; // File format name
const char* extensions() const; // Extensions (e.g. "jpeg,jpg")
docio::FileFormat docioFormat() const;
bool load(FileOp* fop);
#ifdef ENABLE_SAVE
bool save(FileOp* fop);
@ -70,6 +73,7 @@ namespace app {
protected:
virtual const char* onGetName() const = 0;
virtual const char* onGetExtensions() const = 0;
virtual docio::FileFormat onGetDocioFormat() const = 0;
virtual int onGetFlags() const = 0;
virtual bool onLoad(FileOp* fop) = 0;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -13,6 +13,7 @@
#include "app/file/file_format.h"
#include "app/file/format_options.h"
#include "base/string.h"
#include "docio/detect_format.h"
#include <algorithm>
#include <cstring>
@ -93,21 +94,12 @@ FileFormatsList::iterator FileFormatsManager::end()
return m_formats.end();
}
FileFormat* FileFormatsManager::getFileFormatByExtension(const char* extension) const
FileFormat* FileFormatsManager::getFileFormat(const docio::FileFormat docioFormat) const
{
char buf[512], *tok;
for (FileFormat* ff : m_formats) {
std::strcpy(buf, ff->extensions());
for (tok=std::strtok(buf, ","); tok;
tok=std::strtok(NULL, ",")) {
if (base::utf8_icmp(extension, tok) == 0)
return ff;
}
}
return NULL;
for (FileFormat* ff : m_formats)
if (ff->docioFormat() == docioFormat)
return ff;
return nullptr;
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -8,6 +8,8 @@
#define APP_FILE_FILE_FORMATS_MANAGER_H_INCLUDED
#pragma once
#include "docio/file_format.h"
#include <vector>
namespace app {
@ -34,7 +36,7 @@ namespace app {
FileFormatsList::iterator begin();
FileFormatsList::iterator end();
FileFormat* getFileFormatByExtension(const char* extension) const;
FileFormat* getFileFormat(const docio::FileFormat docioFormat) const;
private:
// Register one format.

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -27,6 +27,7 @@ using namespace base;
class FliFormat : public FileFormat {
const char* onGetName() const override { return "flc"; }
const char* onGetExtensions() const override{ return "flc,fli"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::FLIC_ANIMATION; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -63,6 +63,7 @@ class GifFormat : public FileFormat {
const char* onGetName() const override { return "gif"; }
const char* onGetExtensions() const override { return "gif"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::GIF_ANIMATION; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -26,6 +26,7 @@ using namespace base;
class IcoFormat : public FileFormat {
const char* onGetName() const override { return "ico"; }
const char* onGetExtensions() const override { return "ico"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::ICO_IMAGES; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -42,6 +42,7 @@ class JpegFormat : public FileFormat {
const char* onGetName() const override { return "jpeg"; }
const char* onGetExtensions() const override { return "jpeg,jpg"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::JPEG_IMAGE; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -24,6 +24,7 @@
#include "doc/layer.h"
#include "doc/palette.h"
#include "doc/sprite.h"
#include "docio/detect_format.h"
#include <cstring>
@ -45,26 +46,34 @@ std::string get_writable_palette_extensions()
return buf;
}
Palette* load_palette(const char *filename)
Palette* load_palette(const char* filename)
{
std::string ext = base::string_to_lower(base::get_file_extension(filename));
Palette* pal = NULL;
docio::FileFormat docioFormat = docio::detect_format(filename);
Palette* pal = nullptr;
switch (docioFormat) {
case docio::FileFormat::COL_PALETTE:
pal = doc::file::load_col_file(filename);
break;
case docio::FileFormat::GPL_PALETTE:
pal = doc::file::load_gpl_file(filename);
break;
case docio::FileFormat::HEX_PALETTE:
pal = doc::file::load_hex_file(filename);
break;
case docio::FileFormat::PAL_PALETTE:
pal = doc::file::load_pal_file(filename);
break;
default: {
FileFormat* ff = FileFormatsManager::instance()->getFileFormat(docioFormat);
if (!ff || !ff->support(FILE_SUPPORT_LOAD))
break;
if (ext == "col") {
pal = doc::file::load_col_file(filename);
}
else if (ext == "gpl") {
pal = doc::file::load_gpl_file(filename);
}
else if (ext == "hex") {
pal = doc::file::load_hex_file(filename);
}
else if (ext == "pal") {
pal = doc::file::load_pal_file(filename);
}
else {
FileFormat* ff = FileFormatsManager::instance()->getFileFormatByExtension(ext.c_str());
if (ff && ff->support(FILE_SUPPORT_LOAD)) {
base::UniquePtr<FileOp> fop(
FileOp::createLoadDocumentOperation(
nullptr, filename,
@ -85,6 +94,7 @@ Palette* load_palette(const char *filename)
delete fop->releaseDocument();
fop->done();
}
break;
}
}
@ -94,26 +104,34 @@ Palette* load_palette(const char *filename)
return pal;
}
bool save_palette(const char *filename, const Palette* pal, int columns)
bool save_palette(const char* filename, const Palette* pal, int columns)
{
std::string ext = base::string_to_lower(base::get_file_extension(filename));
docio::FileFormat docioFormat = docio::detect_format_by_file_extension(filename);
bool success = false;
if (ext == "col") {
success = doc::file::save_col_file(pal, filename);
}
else if (ext == "gpl") {
success = doc::file::save_gpl_file(pal, filename);
}
else if (ext == "hex") {
success = doc::file::save_hex_file(pal, filename);
}
else if (ext == "pal") {
success = doc::file::save_pal_file(pal, filename);
}
else {
FileFormat* ff = FileFormatsManager::instance()->getFileFormatByExtension(ext.c_str());
if (ff && ff->support(FILE_SUPPORT_SAVE)) {
switch (docioFormat) {
case docio::FileFormat::COL_PALETTE:
success = doc::file::save_col_file(pal, filename);
break;
case docio::FileFormat::GPL_PALETTE:
success = doc::file::save_gpl_file(pal, filename);
break;
case docio::FileFormat::HEX_PALETTE:
success = doc::file::save_hex_file(pal, filename);
break;
case docio::FileFormat::PAL_PALETTE:
success = doc::file::save_pal_file(pal, filename);
break;
default: {
FileFormat* ff = FileFormatsManager::instance()->getFileFormat(docioFormat);
if (!ff || !ff->support(FILE_SUPPORT_SAVE))
break;
int w = (columns > 0 ? MID(0, columns, pal->size()): pal->size());
int h = (pal->size() / w) + (pal->size() % w > 0 ? 1: 0);
@ -150,6 +168,7 @@ bool save_palette(const char *filename, const Palette* pal, int columns)
doc->close();
delete doc;
break;
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -24,6 +24,7 @@ using namespace base;
class PcxFormat : public FileFormat {
const char* onGetName() const override { return "pcx"; }
const char* onGetExtensions() const override { return "pcx"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::PCX_IMAGE; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -31,6 +31,7 @@ using namespace base;
class PixlyFormat : public FileFormat {
const char* onGetName() const override { return "anim"; }
const char* onGetExtensions() const override { return "anim"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::PIXLY_ANIMATION; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -29,6 +29,7 @@ using namespace base;
class PngFormat : public FileFormat {
const char* onGetName() const override { return "png"; }
const char* onGetExtensions() const override { return "png"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::PNG_IMAGE; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -25,6 +25,7 @@ using namespace base;
class TgaFormat : public FileFormat {
const char* onGetName() const override { return "tga"; }
const char* onGetExtensions() const override { return "tga"; }
docio::FileFormat onGetDocioFormat() const override { return docio::FileFormat::TARGA_IMAGE; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -1,6 +1,6 @@
// Aseprite
// Copyright (C) 2015 Gabriel Rauter
// Copyright (C) 2015 David Capello
// Copyright (C) 2015-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -41,6 +41,7 @@ class WebPFormat : public FileFormat {
const char* onGetName() const override { return "webp"; }
const char* onGetExtensions() const override { return "webp"; }
docio::FileFormat onGetDocioFormat() const override { return docio::WEBP_ANIMATION; }
int onGetFlags() const override {
return
FILE_SUPPORT_LOAD |

View File

@ -1,4 +1,4 @@
Copyright (c) 2001-2015 David Capello
Copyright (c) 2001-2016 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@ -1,4 +1,4 @@
# Aseprite Document Library
*Copyright (C) 2001-2015 David Capello*
*Copyright (C) 2001-2016 David Capello*
> Distributed under [MIT license](LICENSE.txt)

9
src/docio/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
# Aseprite Document IO Library
# Copyright (c) 2016 David Capello
add_library(docio-lib
detect_format.cpp)
target_link_libraries(docio-lib
flic-lib
base-lib)

View File

@ -1,4 +1,4 @@
Copyright (c) 2014 David Capello
Copyright (c) 2016 David Capello
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

4
src/docio/README.md Normal file
View File

@ -0,0 +1,4 @@
# Aseprite Document IO Library
*Copyright (C) 2016 David Capello*
> Distributed under [MIT license](LICENSE.txt)

140
src/docio/detect_format.cpp Normal file
View File

@ -0,0 +1,140 @@
// Aseprite Document IO Library
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#include "docio/detect_format.h"
#include "base/file_handle.h"
#include "base/path.h"
#include "base/string.h"
#include "flic/flic_details.h"
#include <cstring>
#define ASE_MAGIC_NUMBER 0xA5E0
#define BMP_MAGIC_NUMBER 0x4D42 // "BM"
#define JPG_MAGIC_NUMBER 0xD8FF
#define GIF_87_STAMP "GIF87a"
#define GIF_89_STAMP "GIF89a"
#define PNG_MAGIC_DWORD1 0x474E5089
#define PNG_MAGIC_DWORD2 0x0A1A0A0D
namespace docio {
FileFormat detect_format(const std::string& filename)
{
FileFormat ff = detect_format_by_file_content(filename);
if (ff == FileFormat::UNKNOWN)
ff = detect_format_by_file_extension(filename);
return ff;
}
FileFormat detect_format_by_file_content(const std::string& filename)
{
#define IS_MAGIC_WORD(offset, word) \
((buf[offset+0] == (word & 0xff)) && \
(buf[offset+1] == ((word & 0xff00) >> 8)))
#define IS_MAGIC_DWORD(offset, dword) \
((buf[offset+0] == (dword & 0xff)) && \
(buf[offset+1] == ((dword & 0xff00) >> 8)) && \
(buf[offset+2] == ((dword & 0xff0000) >> 16)) && \
(buf[offset+3] == ((dword & 0xff000000) >> 24)))
base::FileHandle handle(base::open_file(filename.c_str(), "rb"));
if (!handle)
return FileFormat::ERROR;
FILE* f = handle.get();
unsigned char buf[8];
int count = fread(buf, 1, 8, f);
if (count >= 2) {
if (IS_MAGIC_WORD(0, BMP_MAGIC_NUMBER))
return FileFormat::BMP_IMAGE;
if (IS_MAGIC_WORD(0, JPG_MAGIC_NUMBER))
return FileFormat::JPEG_IMAGE;
if (count >= 6) {
if (std::strncmp((const char*)buf, GIF_87_STAMP, 6) == 0 ||
std::strncmp((const char*)buf, GIF_89_STAMP, 6) == 0)
return FileFormat::GIF_ANIMATION;
if (IS_MAGIC_WORD(4, ASE_MAGIC_NUMBER))
return FileFormat::ASE_ANIMATION;
if (IS_MAGIC_WORD(4, FLI_MAGIC_NUMBER) ||
IS_MAGIC_WORD(4, FLC_MAGIC_NUMBER))
return FileFormat::FLIC_ANIMATION;
if (count >= 8) {
if (IS_MAGIC_DWORD(0, PNG_MAGIC_DWORD1) &&
IS_MAGIC_DWORD(4, PNG_MAGIC_DWORD2))
return FileFormat::PNG_IMAGE;
}
}
}
return FileFormat::UNKNOWN;
}
FileFormat detect_format_by_file_extension(const std::string& filename)
{
// By extension
const std::string ext = base::string_to_lower(base::get_file_extension(filename));
if (ext == "ase" ||
ext == "aseprite")
return FileFormat::ASE_ANIMATION;
if (ext == "bmp")
return FileFormat::BMP_IMAGE;
if (ext == "col")
return FileFormat::COL_PALETTE;
if (ext == "flc" ||
ext == "fli")
return FileFormat::FLIC_ANIMATION;
if (ext == "gif")
return FileFormat::GIF_ANIMATION;
if (ext == "gpl")
return FileFormat::GPL_PALETTE;
if (ext == "hex")
return FileFormat::HEX_PALETTE;
if (ext == "ico")
return FileFormat::ICO_IMAGES;
if (ext == "jpg" ||
ext == "jpeg")
return FileFormat::JPEG_IMAGE;
if (ext == "pal")
return FileFormat::PAL_PALETTE;
if (ext == "pcx")
return FileFormat::PCX_IMAGE;
if (ext == "anim")
return FileFormat::PIXLY_ANIMATION;
if (ext == "png")
return FileFormat::PNG_IMAGE;
if (ext == "tga")
return FileFormat::TARGA_IMAGE;
if (ext == "webp")
return FileFormat::WEBP_ANIMATION;
return FileFormat::UNKNOWN;
}
} // namespace docio

23
src/docio/detect_format.h Normal file
View File

@ -0,0 +1,23 @@
// Aseprite Document IO Library
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOCIO_DETECT_FORMAT_H_INCLUDED
#define DOCIO_DETECT_FORMAT_H_INCLUDED
#pragma once
#include "docio/file_format.h"
#include <string>
namespace docio {
FileFormat detect_format(const std::string& filename);
FileFormat detect_format_by_file_content(const std::string& filename);
FileFormat detect_format_by_file_extension(const std::string& filename);
} // namespace docio
#endif

37
src/docio/file_format.h Normal file
View File

@ -0,0 +1,37 @@
// Aseprite Document IO Library
// Copyright (c) 2016 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
#ifndef DOCIO_FILE_FORMAT_H_INCLUDED
#define DOCIO_FILE_FORMAT_H_INCLUDED
#pragma once
namespace docio {
enum class FileFormat {
ERROR = -1,
UNKNOWN = 0,
ASE_ANIMATION, // Aseprite File Format
ASE_PALETTE, // Adobe Swatch Exchange
BMP_IMAGE,
COL_PALETTE,
FLIC_ANIMATION,
GIF_ANIMATION,
GPL_PALETTE,
HEX_PALETTE,
ICO_IMAGES,
JPEG_IMAGE,
PAL_PALETTE,
PCX_IMAGE,
PIXLY_ANIMATION,
PNG_IMAGE,
TARGA_IMAGE,
WEBP_ANIMATION,
};
} // namespace docio
#endif

View File

@ -1,4 +0,0 @@
# Aseprite Image File Formats Library
*Copyright (C) 2014 David Capello*
> Distributed under [MIT license](LICENSE.txt)