mirror of
https://github.com/aseprite/aseprite.git
synced 2024-11-20 05:14:45 +00:00
Add support for .qoi file format (fix #3121)
This commit is contained in:
parent
2e02bd307e
commit
d3aac6a1cd
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -81,3 +81,6 @@
|
||||
[submodule "src/psd"]
|
||||
path = src/psd
|
||||
url = https://github.com/aseprite/psd.git
|
||||
[submodule "third_party/qoi"]
|
||||
path = third_party/qoi
|
||||
url = https://github.com/aseprite/qoi.git
|
||||
|
@ -1009,6 +1009,30 @@ possible. They may also add themselves to the list below.
|
||||
*/
|
||||
```
|
||||
|
||||
# [qoi](https://github.com/phoboslab/qoi)
|
||||
|
||||
```
|
||||
Copyright (c) 2022 Dominic Szablewski
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
```
|
||||
|
||||
# [Sentry](https://sentry.io)
|
||||
|
||||
```
|
||||
|
@ -136,6 +136,7 @@ set(file_formats
|
||||
file/jpeg_format.cpp
|
||||
file/pcx_format.cpp
|
||||
file/png_format.cpp
|
||||
file/qoi_format.cpp
|
||||
file/svg_format.cpp
|
||||
file/tga_format.cpp)
|
||||
if(ENABLE_WEBP)
|
||||
@ -719,7 +720,8 @@ target_link_libraries(app-lib
|
||||
json11
|
||||
archive_static
|
||||
fmt
|
||||
tinyexpr)
|
||||
tinyexpr
|
||||
qoi)
|
||||
|
||||
if(ENABLE_PSD)
|
||||
target_link_libraries(app-lib psd)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2021 Igara Studio S.A.
|
||||
// Copyright (C) 2021-2023 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -30,6 +30,7 @@ extern FileFormat* CreateIcoFormat();
|
||||
extern FileFormat* CreateJpegFormat();
|
||||
extern FileFormat* CreatePcxFormat();
|
||||
extern FileFormat* CreatePngFormat();
|
||||
extern FileFormat* CreateQoiFormat();
|
||||
extern FileFormat* CreateSvgFormat();
|
||||
extern FileFormat* CreateTgaFormat();
|
||||
|
||||
@ -75,6 +76,7 @@ FileFormatsManager::FileFormatsManager()
|
||||
registerFormat(CreatePsdFormat());
|
||||
#endif
|
||||
|
||||
registerFormat(CreateQoiFormat());
|
||||
registerFormat(CreateSvgFormat());
|
||||
registerFormat(CreateTgaFormat());
|
||||
|
||||
|
183
src/app/file/qoi_format.cpp
Normal file
183
src/app/file/qoi_format.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2023 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "app/file/file.h"
|
||||
#include "app/file/file_format.h"
|
||||
#include "base/file_handle.h"
|
||||
|
||||
#define QOI_NO_STDIO
|
||||
#define QOI_IMPLEMENTATION
|
||||
#include "qoi.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
using namespace base;
|
||||
|
||||
class QoiFormat : public FileFormat {
|
||||
const char* onGetName() const override {
|
||||
return "qoi";
|
||||
}
|
||||
|
||||
void onGetExtensions(base::paths& exts) const override {
|
||||
exts.push_back("qoi");
|
||||
}
|
||||
|
||||
dio::FileFormat onGetDioFormat() const override {
|
||||
return dio::FileFormat::QOI_IMAGE;
|
||||
}
|
||||
|
||||
int onGetFlags() const override {
|
||||
return
|
||||
FILE_SUPPORT_LOAD |
|
||||
FILE_SUPPORT_SAVE |
|
||||
FILE_SUPPORT_RGB |
|
||||
FILE_SUPPORT_RGBA |
|
||||
FILE_SUPPORT_SEQUENCES |
|
||||
FILE_ENCODE_ABSTRACT_IMAGE;
|
||||
}
|
||||
|
||||
bool onLoad(FileOp* fop) override;
|
||||
#ifdef ENABLE_SAVE
|
||||
bool onSave(FileOp* fop) override;
|
||||
#endif
|
||||
};
|
||||
|
||||
FileFormat* CreateQoiFormat()
|
||||
{
|
||||
return new QoiFormat;
|
||||
}
|
||||
|
||||
bool QoiFormat::onLoad(FileOp* fop)
|
||||
{
|
||||
FileHandle handle(open_file_with_exception(fop->filename(), "rb"));
|
||||
FILE* f = handle.get();
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
auto size = ftell(f);
|
||||
if (size <= 0)
|
||||
return false;
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
auto data = QOI_MALLOC(size);
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
qoi_desc desc;
|
||||
auto bytes_read = fread(data, 1, size, f);
|
||||
auto pixels = qoi_decode(data, bytes_read, &desc, 0);
|
||||
QOI_FREE(data);
|
||||
if (!pixels)
|
||||
return false;
|
||||
|
||||
ImageRef image = fop->sequenceImage(IMAGE_RGB,
|
||||
desc.width,
|
||||
desc.height);
|
||||
if (!image)
|
||||
return false;
|
||||
|
||||
auto src = (const uint8_t*)pixels;
|
||||
for (int y=0; y<desc.height; ++y) {
|
||||
auto dst = (uint32_t*)image->getPixelAddress(0, y);
|
||||
switch (desc.channels) {
|
||||
case 4:
|
||||
for (int x=0; x<desc.width; ++x, ++dst) {
|
||||
*dst = doc::rgba(src[0], src[1], src[2], src[3]);
|
||||
src += 4;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for (int x=0; x<desc.width; ++x, ++dst) {
|
||||
*dst = doc::rgba(src[0], src[1], src[2], 255);
|
||||
src += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QOI_FREE(pixels);
|
||||
|
||||
if (desc.channels == 4)
|
||||
fop->sequenceSetHasAlpha(true);
|
||||
|
||||
if (ferror(handle.get())) {
|
||||
fop->setError("Error reading file.\n");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ENABLE_SAVE
|
||||
|
||||
bool QoiFormat::onSave(FileOp* fop)
|
||||
{
|
||||
const FileAbstractImage* img = fop->abstractImage();
|
||||
FileHandle handle(open_file_with_exception_sync_on_close(fop->filename(), "wb"));
|
||||
FILE* f = handle.get();
|
||||
doc::ImageRef image = img->getScaledImage();
|
||||
|
||||
qoi_desc desc;
|
||||
desc.width = img->width();
|
||||
desc.height = img->height();
|
||||
desc.channels = (img->needAlpha() ? 4: 3);
|
||||
desc.colorspace = QOI_LINEAR; // TODO QOI_SRGB
|
||||
|
||||
auto pixels = (uint8_t*)QOI_MALLOC(desc.width * desc.height * desc.channels);
|
||||
if (!pixels)
|
||||
return false;
|
||||
|
||||
auto dst = pixels;
|
||||
for (int y=0; y<desc.height; ++y) {
|
||||
auto src = (uint32_t*)image->getPixelAddress(0, y);
|
||||
switch (desc.channels) {
|
||||
case 4:
|
||||
for (int x=0; x<desc.width; ++x, ++src) {
|
||||
uint32_t c = *src;
|
||||
dst[0] = doc::rgba_getr(c);
|
||||
dst[1] = doc::rgba_getg(c);
|
||||
dst[2] = doc::rgba_getb(c);
|
||||
dst[3] = doc::rgba_geta(c);
|
||||
dst += 4;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
for (int x=0; x<desc.width; ++x, ++src) {
|
||||
uint32_t c = *src;
|
||||
dst[0] = doc::rgba_getr(c);
|
||||
dst[1] = doc::rgba_getg(c);
|
||||
dst[2] = doc::rgba_getb(c);
|
||||
dst += 3;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
auto encoded = qoi_encode(pixels, &desc, &size);
|
||||
QOI_FREE(pixels);
|
||||
if (!encoded)
|
||||
return false;
|
||||
|
||||
fwrite(encoded, 1, size, f);
|
||||
QOI_FREE(encoded);
|
||||
|
||||
if (ferror(handle.get())) {
|
||||
fop->setError("Error writing file.\n");
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ENABLE_SAVE
|
||||
|
||||
} // namespace app
|
@ -1,4 +1,4 @@
|
||||
Copyright (c) 2018-2022 Igara Studio S.A.
|
||||
Copyright (c) 2018-2023 Igara Studio S.A.
|
||||
Copyright (c) 2016-2018 David Capello
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document IO Library
|
||||
// Copyright (c) 2021 Igara Studio S.A.
|
||||
// Copyright (c) 2021-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2016-2018 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -24,6 +24,7 @@
|
||||
#define WEBP_STAMP_1 "RIFF" // "RIFFnnnnWEBP"
|
||||
#define WEBP_STAMP_2 "WEBP"
|
||||
#define PSD_STAMP "8BPS"
|
||||
#define QOI_STAMP "qoif"
|
||||
|
||||
namespace dio {
|
||||
|
||||
@ -69,6 +70,9 @@ FileFormat detect_format_by_file_content_bytes(const uint8_t* buf,
|
||||
if (std::strncmp((const char*)buf, PSD_STAMP, 4) == 0)
|
||||
return FileFormat::PSD_IMAGE;
|
||||
|
||||
if (std::strncmp((const char*)buf, QOI_STAMP, 4) == 0)
|
||||
return FileFormat::QOI_IMAGE;
|
||||
|
||||
if (IS_MAGIC_WORD(4, ASE_MAGIC_NUMBER))
|
||||
return FileFormat::ASE_ANIMATION;
|
||||
|
||||
@ -164,6 +168,9 @@ FileFormat detect_format_by_file_extension(const std::string& filename)
|
||||
ext == "psb")
|
||||
return FileFormat::PSD_IMAGE;
|
||||
|
||||
if (ext == "qoi")
|
||||
return FileFormat::QOI_IMAGE;
|
||||
|
||||
return FileFormat::UNKNOWN;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite Document IO Library
|
||||
// Copyright (c) 2021 Igara Studio S.A.
|
||||
// Copyright (c) 2021-2023 Igara Studio S.A.
|
||||
// Copyright (c) 2016-2017 David Capello
|
||||
//
|
||||
// This file is released under the terms of the MIT license.
|
||||
@ -34,6 +34,7 @@ enum class FileFormat {
|
||||
WEBP_ANIMATION,
|
||||
CSS_STYLE,
|
||||
PSD_IMAGE,
|
||||
QOI_IMAGE,
|
||||
};
|
||||
|
||||
} // namespace dio
|
||||
|
6
third_party/CMakeLists.txt
vendored
6
third_party/CMakeLists.txt
vendored
@ -1,5 +1,5 @@
|
||||
# Aseprite
|
||||
# Copyright (C) 2021-2022 Igara Studio S.A.
|
||||
# Copyright (C) 2021-2023 Igara Studio S.A.
|
||||
# Copyright (C) 2001-2018 David Capello
|
||||
|
||||
include_directories(.)
|
||||
@ -210,3 +210,7 @@ if(ENABLE_SCRIPTING)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
# qoi
|
||||
add_library(qoi INTERFACE)
|
||||
target_include_directories(qoi INTERFACE qoi)
|
||||
|
1
third_party/qoi
vendored
Submodule
1
third_party/qoi
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit c3dcfe780bf28f4a59bca7f0a60e2e18b0acf68f
|
Loading…
Reference in New Issue
Block a user