mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-01 01:20:25 +00:00
Fix bug loading/saving .gif files with Unicode file names
This commit is contained in:
parent
2462767aac
commit
f2bdf38cc6
@ -20,20 +20,23 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "base/unique_ptr.h"
|
|
||||||
#include "app/document.h"
|
#include "app/document.h"
|
||||||
#include "app/file/file.h"
|
#include "app/file/file.h"
|
||||||
#include "app/file/file_format.h"
|
#include "app/file/file_format.h"
|
||||||
#include "app/file/format_options.h"
|
#include "app/file/format_options.h"
|
||||||
#include "app/modules/gui.h"
|
#include "app/modules/gui.h"
|
||||||
|
#include "app/util/autocrop.h"
|
||||||
|
#include "base/file_handle.h"
|
||||||
|
#include "base/unique_ptr.h"
|
||||||
#include "raster/raster.h"
|
#include "raster/raster.h"
|
||||||
#include "ui/alert.h"
|
#include "ui/alert.h"
|
||||||
#include "app/util/autocrop.h"
|
|
||||||
|
|
||||||
#include <gif_lib.h>
|
#include <gif_lib.h>
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
|
using namespace base;
|
||||||
|
|
||||||
enum DisposalMethod {
|
enum DisposalMethod {
|
||||||
DISPOSAL_METHOD_NONE,
|
DISPOSAL_METHOD_NONE,
|
||||||
DISPOSAL_METHOD_DO_NOT_DISPOSE,
|
DISPOSAL_METHOD_DO_NOT_DISPOSE,
|
||||||
@ -100,8 +103,10 @@ static int interlaced_jumps[] = { 8, 8, 4, 2 };
|
|||||||
|
|
||||||
bool GifFormat::onLoad(FileOp* fop)
|
bool GifFormat::onLoad(FileOp* fop)
|
||||||
{
|
{
|
||||||
base::UniquePtr<GifFileType, int(*)(GifFileType*)> gif_file(DGifOpenFileName(fop->filename.c_str()),
|
UniquePtr<GifFileType, int(*)(GifFileType*)> gif_file
|
||||||
DGifCloseFile);
|
(DGifOpenFileHandle(open_file_descriptor_with_exception(fop->filename, "rb")),
|
||||||
|
DGifCloseFile);
|
||||||
|
|
||||||
if (!gif_file) {
|
if (!gif_file) {
|
||||||
fop_error(fop, "Error loading GIF header.\n");
|
fop_error(fop, "Error loading GIF header.\n");
|
||||||
return false;
|
return false;
|
||||||
@ -113,8 +118,8 @@ bool GifFormat::onLoad(FileOp* fop)
|
|||||||
data->sprite_w = gif_file->SWidth;
|
data->sprite_w = gif_file->SWidth;
|
||||||
data->sprite_h = gif_file->SHeight;
|
data->sprite_h = gif_file->SHeight;
|
||||||
|
|
||||||
base::UniquePtr<Palette> current_palette(new Palette(FrameNumber(0), 256));
|
UniquePtr<Palette> current_palette(new Palette(FrameNumber(0), 256));
|
||||||
base::UniquePtr<Palette> previous_palette(new Palette(FrameNumber(0), 256));
|
UniquePtr<Palette> previous_palette(new Palette(FrameNumber(0), 256));
|
||||||
|
|
||||||
// If the GIF image has a global palette, it has a valid
|
// If the GIF image has a global palette, it has a valid
|
||||||
// background color (so the GIF is not transparent).
|
// background color (so the GIF is not transparent).
|
||||||
@ -141,13 +146,13 @@ bool GifFormat::onLoad(FileOp* fop)
|
|||||||
int frame_delay = -1;
|
int frame_delay = -1;
|
||||||
do {
|
do {
|
||||||
if (DGifGetRecordType(gif_file, &record_type) == GIF_ERROR)
|
if (DGifGetRecordType(gif_file, &record_type) == GIF_ERROR)
|
||||||
throw base::Exception("Invalid GIF record in file.\n");
|
throw Exception("Invalid GIF record in file.\n");
|
||||||
|
|
||||||
switch (record_type) {
|
switch (record_type) {
|
||||||
|
|
||||||
case IMAGE_DESC_RECORD_TYPE: {
|
case IMAGE_DESC_RECORD_TYPE: {
|
||||||
if (DGifGetImageDesc(gif_file) == GIF_ERROR)
|
if (DGifGetImageDesc(gif_file) == GIF_ERROR)
|
||||||
throw base::Exception("Invalid GIF image descriptor.\n");
|
throw Exception("Invalid GIF image descriptor.\n");
|
||||||
|
|
||||||
// These are the bounds of the image to read.
|
// These are the bounds of the image to read.
|
||||||
int frame_x = gif_file->Image.Left;
|
int frame_x = gif_file->Image.Left;
|
||||||
@ -158,7 +163,7 @@ bool GifFormat::onLoad(FileOp* fop)
|
|||||||
if (frame_x < 0 || frame_y < 0 ||
|
if (frame_x < 0 || frame_y < 0 ||
|
||||||
frame_x + frame_w > data->sprite_w ||
|
frame_x + frame_w > data->sprite_w ||
|
||||||
frame_y + frame_h > data->sprite_h)
|
frame_y + frame_h > data->sprite_h)
|
||||||
throw base::Exception("Image %d is out of sprite bounds.\n", (int)frame_num);
|
throw Exception("Image %d is out of sprite bounds.\n", (int)frame_num);
|
||||||
|
|
||||||
// Add a new frames.
|
// Add a new frames.
|
||||||
if (frame_num >= FrameNumber(data->frames.size()))
|
if (frame_num >= FrameNumber(data->frames.size()))
|
||||||
@ -191,7 +196,7 @@ bool GifFormat::onLoad(FileOp* fop)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create a temporary image to load frame pixels.
|
// Create a temporary image to load frame pixels.
|
||||||
base::UniquePtr<Image> frame_image(Image::create(IMAGE_INDEXED, frame_w, frame_h));
|
UniquePtr<Image> frame_image(Image::create(IMAGE_INDEXED, frame_w, frame_h));
|
||||||
IndexedTraits::address_t addr;
|
IndexedTraits::address_t addr;
|
||||||
|
|
||||||
if (gif_file->Image.Interlace) {
|
if (gif_file->Image.Interlace) {
|
||||||
@ -200,14 +205,14 @@ bool GifFormat::onLoad(FileOp* fop)
|
|||||||
for (int y = interlaced_offset[i]; y < frame_h; y += interlaced_jumps[i]) {
|
for (int y = interlaced_offset[i]; y < frame_h; y += interlaced_jumps[i]) {
|
||||||
addr = frame_image->getPixelAddress(0, y);
|
addr = frame_image->getPixelAddress(0, y);
|
||||||
if (DGifGetLine(gif_file, addr, frame_w) == GIF_ERROR)
|
if (DGifGetLine(gif_file, addr, frame_w) == GIF_ERROR)
|
||||||
throw base::Exception("Invalid interlaced image data.");
|
throw Exception("Invalid interlaced image data.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int y = 0; y < frame_h; ++y) {
|
for (int y = 0; y < frame_h; ++y) {
|
||||||
addr = frame_image->getPixelAddress(0, y);
|
addr = frame_image->getPixelAddress(0, y);
|
||||||
if (DGifGetLine(gif_file, addr, frame_w) == GIF_ERROR)
|
if (DGifGetLine(gif_file, addr, frame_w) == GIF_ERROR)
|
||||||
throw base::Exception("Invalid image data (%d).\n", GifLastError());
|
throw Exception("Invalid image data (%d).\n", GifLastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +236,7 @@ bool GifFormat::onLoad(FileOp* fop)
|
|||||||
int ext_code;
|
int ext_code;
|
||||||
|
|
||||||
if (DGifGetExtension(gif_file, &ext_code, &extension) == GIF_ERROR)
|
if (DGifGetExtension(gif_file, &ext_code, &extension) == GIF_ERROR)
|
||||||
throw base::Exception("Invalid GIF extension record.\n");
|
throw Exception("Invalid GIF extension record.\n");
|
||||||
|
|
||||||
if (ext_code == GRAPHICS_EXT_FUNC_CODE) {
|
if (ext_code == GRAPHICS_EXT_FUNC_CODE) {
|
||||||
if (extension[0] >= 4) {
|
if (extension[0] >= 4) {
|
||||||
@ -246,7 +251,7 @@ bool GifFormat::onLoad(FileOp* fop)
|
|||||||
|
|
||||||
while (extension != NULL) {
|
while (extension != NULL) {
|
||||||
if (DGifGetExtensionNext(gif_file, &extension) == GIF_ERROR)
|
if (DGifGetExtensionNext(gif_file, &extension) == GIF_ERROR)
|
||||||
throw base::Exception("Invalid GIF extension record.\n");
|
throw Exception("Invalid GIF extension record.\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -334,7 +339,7 @@ bool GifFormat::onPostLoad(FileOp* fop)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create the sprite with the GIF dimension
|
// Create the sprite with the GIF dimension
|
||||||
base::UniquePtr<Sprite> sprite(new Sprite(pixelFormat, data->sprite_w, data->sprite_h, 256));
|
UniquePtr<Sprite> sprite(new Sprite(pixelFormat, data->sprite_w, data->sprite_h, 256));
|
||||||
|
|
||||||
// Create the main layer
|
// Create the main layer
|
||||||
LayerImage* layer = new LayerImage(sprite);
|
LayerImage* layer = new LayerImage(sprite);
|
||||||
@ -350,8 +355,8 @@ bool GifFormat::onPostLoad(FileOp* fop)
|
|||||||
// The previous image is used to support the special disposal method
|
// The previous image is used to support the special disposal method
|
||||||
// of GIF frames DISPOSAL_METHOD_RESTORE_PREVIOUS (number 3 in
|
// of GIF frames DISPOSAL_METHOD_RESTORE_PREVIOUS (number 3 in
|
||||||
// Graphics Extension)
|
// Graphics Extension)
|
||||||
base::UniquePtr<Image> current_image(Image::create(pixelFormat, data->sprite_w, data->sprite_h));
|
UniquePtr<Image> current_image(Image::create(pixelFormat, data->sprite_w, data->sprite_h));
|
||||||
base::UniquePtr<Image> previous_image(Image::create(pixelFormat, data->sprite_w, data->sprite_h));
|
UniquePtr<Image> previous_image(Image::create(pixelFormat, data->sprite_w, data->sprite_h));
|
||||||
|
|
||||||
// Clear both images with the transparent color (alpha = 0).
|
// Clear both images with the transparent color (alpha = 0).
|
||||||
uint32_t bgcolor = (pixelFormat == IMAGE_RGB ? rgba(0, 0, 0, 0):
|
uint32_t bgcolor = (pixelFormat == IMAGE_RGB ? rgba(0, 0, 0, 0):
|
||||||
@ -484,10 +489,12 @@ void GifFormat::onDestroyData(FileOp* fop)
|
|||||||
|
|
||||||
bool GifFormat::onSave(FileOp* fop)
|
bool GifFormat::onSave(FileOp* fop)
|
||||||
{
|
{
|
||||||
base::UniquePtr<GifFileType, int(*)(GifFileType*)> gif_file(EGifOpenFileName(fop->filename.c_str(), 0),
|
UniquePtr<GifFileType, int(*)(GifFileType*)> gif_file
|
||||||
EGifCloseFile);
|
(EGifOpenFileHandle(open_file_descriptor_with_exception(fop->filename, "wb")),
|
||||||
|
EGifCloseFile);
|
||||||
|
|
||||||
if (!gif_file)
|
if (!gif_file)
|
||||||
throw base::Exception("Error creating GIF file.\n");
|
throw Exception("Error creating GIF file.\n");
|
||||||
|
|
||||||
Sprite* sprite = fop->document->getSprite();
|
Sprite* sprite = fop->document->getSprite();
|
||||||
int sprite_w = sprite->getWidth();
|
int sprite_w = sprite->getWidth();
|
||||||
@ -510,11 +517,11 @@ bool GifFormat::onSave(FileOp* fop)
|
|||||||
if (EGifPutScreenDesc(gif_file, sprite_w, sprite_h,
|
if (EGifPutScreenDesc(gif_file, sprite_w, sprite_h,
|
||||||
color_map->BitsPerPixel,
|
color_map->BitsPerPixel,
|
||||||
background_color, color_map) == GIF_ERROR)
|
background_color, color_map) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF header.\n");
|
throw Exception("Error writing GIF header.\n");
|
||||||
|
|
||||||
base::UniquePtr<Image> buffer_image;
|
UniquePtr<Image> buffer_image;
|
||||||
base::UniquePtr<Image> current_image(Image::create(IMAGE_INDEXED, sprite_w, sprite_h));
|
UniquePtr<Image> current_image(Image::create(IMAGE_INDEXED, sprite_w, sprite_h));
|
||||||
base::UniquePtr<Image> previous_image(Image::create(IMAGE_INDEXED, sprite_w, sprite_h));
|
UniquePtr<Image> previous_image(Image::create(IMAGE_INDEXED, sprite_w, sprite_h));
|
||||||
int frame_x, frame_y, frame_w, frame_h;
|
int frame_x, frame_y, frame_w, frame_h;
|
||||||
int u1, v1, u2, v2;
|
int u1, v1, u2, v2;
|
||||||
int i1, j1, i2, j2;
|
int i1, j1, i2, j2;
|
||||||
@ -597,16 +604,16 @@ bool GifFormat::onSave(FileOp* fop)
|
|||||||
|
|
||||||
memcpy(extension_bytes, "NETSCAPE2.0", 11);
|
memcpy(extension_bytes, "NETSCAPE2.0", 11);
|
||||||
if (EGifPutExtensionFirst(gif_file, APPLICATION_EXT_FUNC_CODE, 11, extension_bytes) == GIF_ERROR)
|
if (EGifPutExtensionFirst(gif_file, APPLICATION_EXT_FUNC_CODE, 11, extension_bytes) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
throw Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
||||||
|
|
||||||
extension_bytes[0] = 1;
|
extension_bytes[0] = 1;
|
||||||
extension_bytes[1] = (loop & 0xff);
|
extension_bytes[1] = (loop & 0xff);
|
||||||
extension_bytes[2] = (loop >> 8) & 0xff;
|
extension_bytes[2] = (loop >> 8) & 0xff;
|
||||||
if (EGifPutExtensionNext(gif_file, APPLICATION_EXT_FUNC_CODE, 3, extension_bytes) == GIF_ERROR)
|
if (EGifPutExtensionNext(gif_file, APPLICATION_EXT_FUNC_CODE, 3, extension_bytes) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
throw Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
||||||
|
|
||||||
if (EGifPutExtensionLast(gif_file, APPLICATION_EXT_FUNC_CODE, 0, NULL) == GIF_ERROR)
|
if (EGifPutExtensionLast(gif_file, APPLICATION_EXT_FUNC_CODE, 0, NULL) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
throw Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write graphics extension record (to save the duration of the
|
// Write graphics extension record (to save the duration of the
|
||||||
@ -624,7 +631,7 @@ bool GifFormat::onSave(FileOp* fop)
|
|||||||
extension_bytes[3] = (transparent_index >= 0 ? transparent_index: 0);
|
extension_bytes[3] = (transparent_index >= 0 ? transparent_index: 0);
|
||||||
|
|
||||||
if (EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, 4, extension_bytes) == GIF_ERROR)
|
if (EGifPutExtension(gif_file, GRAPHICS_EXT_FUNC_CODE, 4, extension_bytes) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
throw Exception("Error writing GIF graphics extension record for frame %d.\n", (int)frame_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image color map
|
// Image color map
|
||||||
@ -644,7 +651,7 @@ bool GifFormat::onSave(FileOp* fop)
|
|||||||
frame_x, frame_y,
|
frame_x, frame_y,
|
||||||
frame_w, frame_h, interlace ? 1: 0,
|
frame_w, frame_h, interlace ? 1: 0,
|
||||||
image_color_map) == GIF_ERROR)
|
image_color_map) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF frame %d.\n", (int)frame_num);
|
throw Exception("Error writing GIF frame %d.\n", (int)frame_num);
|
||||||
|
|
||||||
// Write the image data (pixels).
|
// Write the image data (pixels).
|
||||||
if (interlace) {
|
if (interlace) {
|
||||||
@ -655,7 +662,7 @@ bool GifFormat::onSave(FileOp* fop)
|
|||||||
(IndexedTraits::address_t)current_image->getPixelAddress(frame_x, frame_y + y);
|
(IndexedTraits::address_t)current_image->getPixelAddress(frame_x, frame_y + y);
|
||||||
|
|
||||||
if (EGifPutLine(gif_file, addr, frame_w) == GIF_ERROR)
|
if (EGifPutLine(gif_file, addr, frame_w) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF image scanlines for frame %d.\n", (int)frame_num);
|
throw Exception("Error writing GIF image scanlines for frame %d.\n", (int)frame_num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -665,7 +672,7 @@ bool GifFormat::onSave(FileOp* fop)
|
|||||||
(IndexedTraits::address_t)current_image->getPixelAddress(frame_x, frame_y + y);
|
(IndexedTraits::address_t)current_image->getPixelAddress(frame_x, frame_y + y);
|
||||||
|
|
||||||
if (EGifPutLine(gif_file, addr, frame_w) == GIF_ERROR)
|
if (EGifPutLine(gif_file, addr, frame_w) == GIF_ERROR)
|
||||||
throw base::Exception("Error writing GIF image scanlines for frame %d.\n", (int)frame_num);
|
throw Exception("Error writing GIF image scanlines for frame %d.\n", (int)frame_num);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,4 +45,24 @@ FileHandle open_file_with_exception(const string& filename, const string& mode)
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int open_file_descriptor_with_exception(const string& filename, const string& mode)
|
||||||
|
{
|
||||||
|
int flags = 0;
|
||||||
|
if (mode.find('r') != string::npos) flags |= _O_RDONLY;
|
||||||
|
if (mode.find('w') != string::npos) flags |= _O_WRONLY | _O_TRUNC;
|
||||||
|
if (mode.find('b') != string::npos) flags |= _O_BINARY;
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
#ifdef WIN32
|
||||||
|
fd = _wopen(from_utf8(filename).c_str(), flags);
|
||||||
|
#else
|
||||||
|
fd = open(filename.c_str(), flags);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (fd == -1)
|
||||||
|
throw runtime_error("Cannot open " + filename);
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ namespace base {
|
|||||||
FILE* open_file_raw(const string& filename, const string& mode);
|
FILE* open_file_raw(const string& filename, const string& mode);
|
||||||
FileHandle open_file(const string& filename, const string& mode);
|
FileHandle open_file(const string& filename, const string& mode);
|
||||||
FileHandle open_file_with_exception(const string& filename, const string& mode);
|
FileHandle open_file_with_exception(const string& filename, const string& mode);
|
||||||
|
int open_file_descriptor_with_exception(const string& filename, const string& mode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user