You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libksquirrel/kernel/kls_gif/fmt_codec_gif.cpp

574 lines
12 KiB

/* This file is part of the ksquirrel-libs (http://ksquirrel.sf.net)
Copyright (c) 2004,2005 Dmitry Baryshev <ksquirrel@tut.by>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License (LGPL) as published by the Free Software Foundation;
either version 2 of the License, or (at your option) any later
version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Library General Public License
as32 with this library; see the file COPYING. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "ksquirrel-libs/fmt_types.h"
#include "ksquirrel-libs/fileio.h"
#include "ksquirrel-libs/error.h"
#include "ksquirrel-libs/fmt_utils.h"
#include "fmt_codec_gif_defs.h"
extern "C" {
#include "gif_lib.h"
}
#include "fmt_codec_gif.h"
#include "../xpm/codec_gif.xpm"
/* GIFLIB_MAJOR is only defined in libgif >= 4.2.0 */
/* libgif 4.2.0 has retired PrintGifError() and added GifErrorString() */
#if defined(GIFLIB_MAJOR) && defined(GIFLIB_MINOR) && \
((GIFLIB_MAJOR == 4 && GIFLIB_MINOR >= 2) || GIFLIB_MAJOR > 4)
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
static void PrintGifError(int ErrorCode)
#else
static void PrintGifError(void)
#endif
{
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
const char *Err = GifErrorString(ErrorCode);
#else
const char *Err = GifErrorString();
#endif
if (Err != NULL) {
fprintf(stderr, "\nGIF-LIB error: %s.\n", Err);
} else {
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
fprintf(stderr, "\nGIF-LIB undefined error %d.\n", ErrorCode);
#else
fprintf(stderr, "\nGIF-LIB undefined error %d.\n", GifError());
#endif
}
}
#endif
/*
*
* Originally designed to facilitate image transfer and online
* storage for use by CompuServe and its customers,
* GIF is primarily an exchange and storage
* format, although it is based on, and is supported by, many
* applications.
*
*/
static s32
InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
fmt_codec::fmt_codec() : fmt_codec_base()
{}
fmt_codec::~fmt_codec()
{}
void fmt_codec::options(codec_options *o)
{
o->version = "1.3.1";
o->name = "Compuserve GIF";
o->filter = "*.gif ";
o->config = "";
o->mime = "\x0047\x0049\x0046\x0038[\x0039\x0037]\x0061";
o->mimetype = "image/gif";
o->pixmap = codec_gif;
o->readable = true;
o->canbemultiple = true;
o->writestatic = false;
o->writeanimated = false;
o->needtempfile = false;
}
s32 fmt_codec::read_init(const std::string &file)
{
frs.open(file.c_str(), ios::binary | ios::in);
if(!frs.good())
return SQE_R_NOFILE;
frs.close();
transIndex = -1;
Last = 0;
Lines = 0;
buf = 0;
saved = 0;
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
int ErrorCode;
gif = DGifOpenFileName(file.c_str(), &ErrorCode);
#else
gif = DGifOpenFileName(file.c_str());
#endif
// for safety...
if(!gif)
return SQE_R_BADFILE;
linesz = gif->SWidth * sizeof(GifPixelType);
if((buf = (u8*)malloc(linesz)) == NULL)
return SQE_R_NOMEMORY;
if((saved = (RGBA *)calloc(linesz, sizeof(RGBA))) == NULL)
return SQE_R_NOMEMORY;
if(gif->SColorMap)
{
back.r = gif->SColorMap->Colors[gif->SBackGroundColor].Red;
back.g = gif->SColorMap->Colors[gif->SBackGroundColor].Green;
back.b = gif->SColorMap->Colors[gif->SBackGroundColor].Blue;
back.a = 255;
}
else
memset(&back, 0, sizeof(RGBA));
layer = -1;
line = 0;
curLine = 0;
Lines_h = gif->SHeight;
Lines = (RGBA **)malloc(Lines_h * sizeof(RGBA*));
if(!Lines)
return SQE_R_NOMEMORY;
for(s32 i = 0;i < Lines_h;i++)
Lines[i] = (RGBA *)0;
map = (gif->Image.ColorMap) ? gif->Image.ColorMap : gif->SColorMap;
Last = (RGBA **)malloc(gif->SHeight * sizeof(RGBA*));
if(!Last)
return SQE_R_NOMEMORY;
for(s32 i = 0;i < gif->SHeight;i++)
Last[i] = (RGBA *)0;
for(s32 i = 0;i < gif->SHeight;i++)
{
Last[i] = (RGBA *)calloc(gif->SWidth, sizeof(RGBA));
if(!Last[i])
return SQE_R_NOMEMORY;
// for(s32 k = 0;k < gif->SWidth;k++)
// memcpy(Last[i]+k, &back, sizeof(RGBA));
}
currentImage = -1;
lastDisposal = DISPOSAL_NO;
finfo.animated = false;
return SQE_OK;
}
s32 fmt_codec::read_next_pass()
{
layer++;
currentPass++;
line = 0;
curLine = 0;
return SQE_OK;
}
s32 fmt_codec::read_next()
{
bool foundExt = false;
currentImage++;
fmt_image image;
image.interlaced = gif->Image.Interlace;
image.passes = (gif->Image.Interlace) ? 4 : 1;
while(true)
{
if (DGifGetRecordType(gif, &record) == GIF_ERROR)
{
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
PrintGifError(gif->Error);
#else
PrintGifError();
#endif
return SQE_R_BADFILE;
}
switch(record)
{
case IMAGE_DESC_RECORD_TYPE:
if(DGifGetImageDesc(gif) == GIF_ERROR)
{
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
PrintGifError(gif->Error);
#else
PrintGifError();
#endif
return SQE_R_BADFILE;
}
if(!foundExt)
{
lastDisposal = disposal;
disposal = DISPOSAL_NO;
image.delay = 100;
transIndex = -1;
image.hasalpha = true;
}
lastRow = (currentImage) ? Row : gif->Image.Top;
lastCol = (currentImage) ? Col : gif->Image.Left;
Row = gif->Image.Top;
Col = gif->Image.Left;
image.w = gif->SWidth;
image.h = gif->SHeight;
lastWidth = (currentImage) ? Width : gif->Image.Width;
lastHeight = (currentImage) ? Height : gif->Image.Height;
Width = gif->Image.Width;
Height = gif->Image.Height;
image.bpp = 8;
curLine = 0;
if(gif->Image.Left + gif->Image.Width > gif->SWidth || gif->Image.Top + gif->Image.Height > gif->SHeight)
{
return SQE_R_BADFILE;
}
break;
case EXTENSION_RECORD_TYPE:
if(DGifGetExtension(gif, &ExtCode, &Extension) == GIF_ERROR)
{
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
PrintGifError(gif->Error);
#else
PrintGifError();
#endif
return SQE_R_BADFILE;
}
if(!Extension)
break;
if(ExtCode == 249)
{
foundExt = true;
lastDisposal = disposal;
disposal = (Extension[1] >> 2) & 7;
bool b = Extension[1] & 1;
s32 u = (unsigned)*(Extension + 2);
image.delay = (!u) ? 100 : (u * 10);
if(b)
transIndex = Extension[4];
image.hasalpha = b;
}
else if(ExtCode == 254 && Extension[0])
{
fmt_metaentry mt;
s8 d[Extension[0]+1];
memcpy(d, (s8*)Extension+1, Extension[0]);
d[Extension[0]] = '\0';
for(s32 s = 0;s < Extension[0];s++)
if(d[s] == '\n')
d[s] = ' ';
mt.group = "Comment";
mt.data = d;
addmeta(mt);
}
while(Extension)
{
if(DGifGetExtensionNext(gif, &Extension) == GIF_ERROR)
{
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
PrintGifError(gif->Error);
#else
PrintGifError();
#endif
return SQE_R_BADFILE;
}
}
break;
case TERMINATE_RECORD_TYPE:
return SQE_NOTOK;
default: ;
}
if(record == IMAGE_DESC_RECORD_TYPE)
{
if(currentImage >= 1)
finfo.animated = true;
map = (gif->Image.ColorMap) ? gif->Image.ColorMap : gif->SColorMap;
back.a = (transIndex != -1) ? 0 : 255;
for(s32 k = 0;k < gif->SWidth;k++)
memcpy(saved+k, &back, sizeof(RGBA));
image.compression = "LZW";
image.colorspace = "Color indexed";
image.interlaced = gif->Image.Interlace;
image.passes = (gif->Image.Interlace) ? 4 : 1;
finfo.image.push_back(image);
layer = -1;
currentPass = -1;
return SQE_OK;
}
}
}
s32 fmt_codec::read_scanline(RGBA *scan)
{
fmt_image *im = image(currentImage);
fmt_utils::fillAlpha(scan, im->w);
if(curLine < Row || curLine >= Row + Height)
{
if(currentPass == im->passes-1)
{
memcpy(scan, Last[curLine], im->w * sizeof(RGBA));
if(lastDisposal == DISPOSAL_BACKGROUND)
if(curLine >= lastRow && curLine < lastRow+lastHeight)
{
memcpy(scan+lastCol, saved, lastWidth * sizeof(RGBA));
memcpy(Last[curLine], scan, im->w * sizeof(RGBA));
}
}
curLine++;
return SQE_OK;
}
curLine++;
s32 i;
s32 index;
if(gif->Image.Interlace)
{
memcpy(scan, Last[curLine-1], im->w * sizeof(RGBA));
if(line == 0)
j = InterlacedOffset[layer];
if(line == j)
{
if(DGifGetLine(gif, buf, Width) == GIF_ERROR)
{
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
PrintGifError(gif->Error);
#else
PrintGifError();
#endif
memset(scan, 255, im->w * sizeof(RGBA));
return SQE_R_BADFILE;
}
else
{
j += InterlacedJumps[layer];
for(i = 0;i < Width;i++)
{
index = Col + i;
if(buf[i] == transIndex && transIndex != -1)
{
RGB rgb = *((RGB *)&(map->Colors[buf[i]]));
if(back == rgb && !currentImage)
(scan+index)->a = 0;
else if(back == rgb && lastDisposal != DISPOSAL_BACKGROUND && currentImage)
{
RGBA *t = &Last[curLine-1][index];
memcpy(scan+index, t, sizeof(RGBA));
}
else if(back == rgb && lastDisposal == DISPOSAL_BACKGROUND && currentImage)
{
(scan+index)->a = 0;
}
else if(currentImage)
{
RGBA *t = &Last[curLine-1][index];
if(lastDisposal == DISPOSAL_BACKGROUND)
{
memcpy(scan+index, &back, sizeof(RGBA));//(scan+index)->a=0;
if(t->a == 0)
(scan+index)->a=0;
}
}
}
else
{
memcpy(scan+index, &(map->Colors[buf[i]]), sizeof(RGB));
(scan+index)->a = 255;
}
}
Lines[line] = (RGBA*)realloc(Lines[line], im->w * sizeof(RGBA));
if(!Lines[line])
return SQE_R_NOMEMORY;
memcpy(Lines[line], scan, im->w * sizeof(RGBA));
}
} // if(line == j)
else
{
if(Lines[line])
memcpy(scan, Lines[line], im->w * sizeof(RGBA));
else
memset(scan, 255, im->w * sizeof(RGBA));
}
if(currentPass == im->passes-1)
memcpy(Last[curLine-1], scan, im->w * sizeof(RGBA));
line++;
}
else // !s32erlaced
{
if(DGifGetLine(gif, buf, Width) == GIF_ERROR)
{
memset(scan, 255, im->w * sizeof(RGBA));
#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR > 4
PrintGifError(gif->Error);
#else
PrintGifError();
#endif
return SQE_R_BADFILE;
}
else
{
memcpy(scan, Last[curLine-1], im->w * sizeof(RGBA));
if(lastDisposal == DISPOSAL_BACKGROUND)
{
if(curLine-1 >= lastRow && curLine-1 < lastRow+lastHeight)
memcpy(scan+lastCol, saved, lastWidth * sizeof(RGBA));
}
for(i = 0;i < Width;i++)
{
index = Col + i;
if(buf[i] == transIndex && transIndex != -1)
{
RGB rgb = *((RGB *)&(map->Colors[buf[i]]));
if(back == rgb && !currentImage)
(scan+index)->a = 0;
else if(back == rgb && lastDisposal != DISPOSAL_BACKGROUND && currentImage)
{
RGBA *t = &Last[curLine-1][index];
memcpy(scan+index, t, sizeof(RGBA));// = 255;
}
else if(back == rgb && lastDisposal == DISPOSAL_BACKGROUND && currentImage)
{
(scan+index)->a = 0;
}
else if(currentImage)
{
RGBA *t = &Last[curLine-1][index];
if(lastDisposal == DISPOSAL_BACKGROUND)
{
memcpy(scan+index, &back, sizeof(RGBA));//(scan+index)->a=0;
if(t->a == 0)
(scan+index)->a=0;
}
}
}// if transIndex
else
{
memcpy(scan+index, &(map->Colors[buf[i]]), sizeof(RGB));
(scan+index)->a = 255;
}
} // for
memcpy(Last[curLine-1], scan, im->w * sizeof(RGBA));
}
}
return SQE_OK;
}
void fmt_codec::read_close()
{
if(buf) free(buf);
if(saved) free(saved);
if(Lines)
{
for(s32 i = 0;i < Lines_h;i++)
if(Lines[i])
free(Lines[i]);
free(Lines);
Lines = 0;
}
if(Last)
{
for(s32 i = 0;i < gif->SHeight;i++)
if(Last[i])
free(Last[i]);
free(Last);
Last = 0;
}
finfo.meta.clear();
finfo.image.clear();
if(gif) DGifCloseFile(gif);
}
#include "fmt_codec_cd_func.h"