/*
* anim_image.cc
*
* Copyright (C) 1995-2001 Kenichi Kourai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with qvwm; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <sys/stat.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "main.h"
#include "anim_image.h"
#include "pixmap_image.h"
#include "timer.h"
#include "callback.h"
#include "qvwmrc.h"
#ifdef USE_IMLIB
#include "extra_image.h"
#endif
//
// animation file format
// 'ANIM' <# of images(4B)> ; header
// 'DATA' <image data size(4B)> <interval(4B)>
// <image name length(4B)> <image name(4B align)>
// <data(4B align)> ; image data
// or
// 'REFE' <frame number(4B)> <interval(4B)> ; reference(backward only)
//
// All value is big endian.
// <interval> is the time during which the image is displayed.
//
AnimImage::AnimImage()
{
m_imageNum = 0;
m_images = NULL;
m_import = True;
}
AnimImage::AnimImage(char* filename, Timer* timer)
{
FILE* fp;
struct stat st;
int num;
char** array;
int* len;
int* ival;
m_images = NULL;
m_import = True; // m_pix, m_mask and m_gc of AnimImage are fake
if ((fp = fopen(filename, "r")) == NULL) {
QvwmError("cannot open animation file: '%s'", filename);
m_error = -1;
return;
}
if (fstat(fileno(fp), &st) < 0) {
QvwmError("cannot access animation file: '%s'", filename);
m_error = -1;
fclose(fp);
return;
}
if (ParseHeader(fp, &num, filename) == -1) {
m_error = -1;
fclose(fp);
return;
}
// use the first image if not animating image
if (!ImageAnimation)
num = 1;
array = new char*[num];
len = new int[num];
ival = new int[num];
for (int i = 0; i < num; i++) {
if (ReadImageData(fp, &array[i], &len[i], &ival[i], st.st_size, filename)
== -1) {
m_error = -1;
fclose(fp);
for (int j = 0; j < i; j++)
delete array[j];
delete [] array;
delete [] len;
delete [] ival;
return;
}
}
CreateImages(array, len, ival, num, timer, filename);
for (int j = 0; j < num; j++)
delete array[j];
delete [] array;
delete [] len;
fclose(fp);
if (m_imageNum > 0)
SetCurrentImage();
}
int AnimImage::ParseHeader(FILE* fp, int* num, char* filename)
{
char id[4];
// read and check animation file id
if (fread(id, 4, 1, fp) < 1) {
QvwmError("cannot read animation file: '%s'", filename);
return -1;
}
if (memcmp(id, "ANIM", 4) != 0) {
QvwmError("'%s' is not animation file", filename);
return -1;
}
// read # of images
if (GetBWord(fp, num) == -1) {
QvwmError("cannot read animation image file: '%s'", filename);
return -1;
}
return 0;
}
int AnimImage::ReadImageData(FILE* fp, char** data, int* len, int* ival,
int filesize, char* filename)
{
char id[4];
char pad[3];
int namlen;
char name[256]; // XXX contained image name length is limited by 256
// read and check the start of image section
if (fread(id, 4, 1, fp) < 1) {
QvwmError("cannot read image section: '%s'", filename);
m_error = 1;
return -1;
}
if (memcmp(id, "DATA", 4) == 0) {
// read image data length
if (GetBWord(fp, len) == -1) {
QvwmError("cannot read contained image length: '%s'", filename);
return -1;
}
if (*len < 0) {
QvwmError("contained image length is negative: '%s'", filename);
return -1;
}
// read image display interval
if (GetBWord(fp, ival) == -1) {
QvwmError("cannot read interval: '%s'", filename);
return -1;
}
if (*ival < 0) {
QvwmError("interval is negative: '%s'", filename);
return -1;
}
// read contained image name length
if (GetBWord(fp, &namlen) == -1) {
QvwmError("cannot read contained image name length: '%s'", filename);
return -1;
}
if (namlen > 0) {
if (ftell(fp) + namlen > filesize) {
QvwmError("contained image name length is too large: '%s'", filename);
return -1;
}
if ((namlen & 3) != 0)
namlen = (namlen + 3) / 4 * 4;
// name is not used in qvwm now...
if (fread(name, namlen, 1, fp) < 1) {
QvwmError("cannot read contained image name: '%s'", filename);
return -1;
}
}
else if (namlen < 0) {
QvwmError("contained image name length is negative: '%s'", filename);
return -1;
}
if (ftell(fp) + *len > filesize) {
QvwmError("contained image length is too large: '%s'", filename);
return -1;
}
*data = new char[*len];
if (fread(*data, *len, 1, fp) < 1) {
QvwmError("cannot read contained image data: '%s'", filename);
delete *data;
return -1;
}
// read until 4 byte alignment
if ((*len & 3) != 0)
fread(pad, 4 - (*len & 3), 1, fp);
}
else if (memcmp(id, "REFE", 4) == 0) {
if (GetBWord(fp, len) == -1) {
QvwmError("cannot read contained image length: '%s'", filename);
return -1;
}
if (*len < 0) {
QvwmError("contained image length is negative: '%s'", filename);
return -1;
}
if (GetBWord(fp, ival) == -1) {
QvwmError("cannot read interval: '%s'", filename);
return -1;
}
if (*ival < 0) {
QvwmError("interval is negative: '%s'", filename);
return -1;
}
*data = NULL; // stands for frame reference
}
else {
QvwmError("'%s' is not animation file", filename);
return -1;
}
return 0;
}
int AnimImage::GetBWord(FILE* fp, int* word)
{
unsigned char data[4];
if (fread(data, 4, 1, fp) < 1)
return -1;
*word = (int)(data[3] + ((int)data[2] << 8) + ((int)data[1] << 16)
+ ((int)data[0] << 24));
return 0;
}
void AnimImage::CreateImages(char** array, int* len, int* ival, int num,
Timer* timer, char* filename)
{
m_imageNum = num;
m_curNum = 0;
m_speed = ival;
m_timer = timer;
Init();
for (int i = 0; i < m_imageNum; i++) {
if (array[i] == NULL && len[i] < i)
m_images[i] = m_images[len[i]]->Duplicate();
else
m_images[i] = CreateImageFromData(array[i], len[i]);
if (m_images[i] == NULL) {
QvwmError("unsupported image is contained in %s", filename);
m_error = -1;
return;
}
else if ((m_error = m_images[i]->GetError())) {
m_images[i]->OutputError(filename);
return;
}
}
}
QvImage* AnimImage::CreateImageFromData(char* data, int len)
{
QvImage* img;
if (strncmp(data, "/* XPM */", 9) == 0)
img = new PixmapImage(data, len);
else {
#ifdef USE_IMLIB
img = new ExtraImage(data, len);
#else
img = NULL;
#endif
}
return img;
}
void AnimImage::Init()
{
m_images = new QvImage*[m_imageNum];
for (int i = 0; i < m_imageNum; i++)
m_images[i] = NULL; // for correct destruction in error
m_winDisp = None;
m_ptDisp = Point(0, 0);
m_bgImg = False;
if (m_imageNum > 1 && m_timer)
m_timer->SetTimeout(m_speed[m_curNum],
new Callback<AnimImage>(this, &AnimImage::Display));
}
AnimImage::~AnimImage()
{
if (m_images == NULL)
return;
for (int i = 0; i < m_imageNum; i++)
QvImage::Destroy(m_images[i]);
delete [] m_images;
delete [] m_speed;
if (m_imageNum > 1 && m_timer)
m_timer->ResetTimeout(new Callback<AnimImage>(this, &AnimImage::Display));
}
AnimImage* AnimImage::Duplicate()
{
AnimImage* img = new AnimImage();
int i;
img->m_imageNum = m_imageNum;
img->m_curNum = m_curNum;
img->m_timer = m_timer;
img->m_speed = new int[m_imageNum];
for (i = 0; i < m_imageNum; i++)
img->m_speed[i] = m_speed[i];
img->Init();
for (i = 0; i < img->m_imageNum; i++)
img->m_images[i] = m_images[i]->Duplicate();
if (img->m_imageNum > 0)
img->SetCurrentImage();
return img;
}
void AnimImage::SetCurrentImage()
{
m_pix = m_images[m_curNum]->GetPixmap();
m_mask = m_images[m_curNum]->GetMask();
m_gc = m_images[m_curNum]->GetGC();
m_size = m_images[m_curNum]->GetSize();
}
void AnimImage::ChangeNextImage()
{
if (++m_curNum >= m_imageNum)
m_curNum = 0;
SetCurrentImage();
}
void AnimImage::Display(Window win, const Point& pt)
{
m_winDisp = win;
m_ptDisp = pt;
QvImage::Display(win, pt);
}
void AnimImage::Display()
{
ChangeNextImage();
if (m_cbPre)
m_cbPre->Execute();
if (m_cbDisp)
m_cbDisp->Execute(); // for special purpose
else if (m_winDisp != None) {
if (m_bgImg) {
QvImage::SetBackground(m_winDisp);
XClearArea(display, m_winDisp, 0, 0, 0, 0, True);
}
else {
Dim size = GetSize();
XClearArea(display, m_winDisp,
m_ptDisp.x, m_ptDisp.y, size.width, size.height, False);
QvImage::Display(m_winDisp, m_ptDisp);
XFlush(display);
}
}
if (m_cbPost)
m_cbPost->Execute();
if (m_imageNum > 0 && m_timer)
m_timer->SetTimeout(m_speed[m_curNum],
new Callback<AnimImage>(this, &AnimImage::Display));
}
void AnimImage::SetBackground(Window win)
{
m_bgImg = True;
m_winDisp = win;
QvImage::SetBackground(win);
}
QvImage* AnimImage::GetOffsetImage(const Point& pt)
{
AnimImage* img = new AnimImage();
int i;
img->m_imageNum = m_imageNum;
img->m_curNum = m_curNum;
img->m_timer = m_timer;
img->m_speed = new int[m_imageNum];
for (i = 0; i < m_imageNum; i++)
img->m_speed[i] = m_speed[i];
img->Init();
for (i = 0; i < img->m_imageNum; i++)
img->m_images[i] = m_images[i]->GetOffsetImage(pt);
if (img->m_imageNum > 0)
img->SetCurrentImage();
return img;
}
syntax highlighted by Code2HTML, v. 0.9.1