leocad/common/image.cpp
2011-09-07 21:06:51 +00:00

512 lines
12 KiB
C++

// Image I/O routines
//
#include "opengl.h"
#ifdef LC_WINDOWS
#include <windows.h>
#include <windowsx.h>
//#include <mmsystem.h>
#include <vfw.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "config.h"
#include "image.h"
#include "file.h"
// =============================================================================
// Image functions
Image::Image ()
{
m_nWidth = 0;
m_nHeight = 0;
m_bAlpha = false;
m_pData = NULL;
}
Image::~Image ()
{
free (m_pData);
}
void Image::FreeData ()
{
m_nWidth = 0;
m_nHeight = 0;
m_bAlpha = false;
free (m_pData);
m_pData = NULL;
}
void Image::Allocate (int width, int height, bool alpha)
{
FreeData ();
m_nWidth = width;
m_nHeight = height;
m_bAlpha = alpha;
if (m_bAlpha)
m_pData = (unsigned char*)malloc (width * height * 4);
else
m_pData = (unsigned char*)malloc (width * height * 3);
}
void Image::ResizePow2 ()
{
int i, shifted_x, shifted_y;
shifted_x = m_nWidth;
for (i = 0; ((i < 16) && (shifted_x != 0)); i++)
shifted_x = shifted_x >> 1;
shifted_x = (i != 0) ? 1 << (i-1) : 1;
shifted_y = m_nHeight;
for (i = 0; ((i < 16) && (shifted_y != 0)); i++)
shifted_y = shifted_y >> 1;
shifted_y = (i != 0) ? 1 << (i-1) : 1;
if ((shifted_x != m_nWidth) || (shifted_y != m_nHeight))
Resize (shifted_x, shifted_y);
}
void Image::Resize (int width, int height)
{
int i, j, k, components, stx, sty;
float accumx, accumy;
unsigned char* bits;
if (m_bAlpha)
components = 4;
else
components = 3;
bits = (unsigned char*)malloc (width * height * components);
for (j = 0; j < m_nHeight; j++)
{
accumy = (float)height*j/(float)m_nHeight;
sty = (int)floor(accumy);
for (i = 0; i < m_nWidth; i++)
{
accumx = (float)width*i/(float)m_nWidth;
stx = (int)floor(accumx);
for (k = 0; k < components; k++)
bits[(stx+sty*width)*components+k] = m_pData[(i+j*m_nWidth)*components+k];
}
}
free (m_pData);
m_pData = bits;
m_nWidth = width;
m_nHeight = height;
}
void Image::FromOpenGL (int width, int height)
{
unsigned char *buf;
buf = (unsigned char*)malloc (width*height*3);
FreeData ();
m_pData = (unsigned char*)malloc (width*height*3);
m_nWidth = width;
m_nHeight = height;
m_bAlpha = false;
glPixelStorei (GL_PACK_ALIGNMENT, 1);
glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buf);
for (int row = 0; row < height; row++)
memcpy (m_pData + (row*width*3), buf + ((height-row-1)*width*3), width*3);
free (buf);
}
bool Image::FileLoad (File& file)
{
unsigned char buf[8];
// Read a few bytes
if (file.Read (buf, 8) != 8)
return false;
file.Seek (-8, SEEK_CUR);
// Check for the BMP header
if ((buf[0] == 'B') && (buf[1] == 'M'))
{
if (!LoadBMP (file))
return false;
return true;
}
#ifdef LC_HAVE_JPEGLIB
if ((buf[0] == 0xFF) && (buf[1] == 0xD8))
{
if (!LoadJPG (file))
return false;
return true;
}
#endif
#ifdef LC_HAVE_PNGLIB
const unsigned char png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
// Check for the PNG header
if (memcmp (buf, png_signature, 8) == 0)
{
if (!LoadPNG (file))
return false;
return true;
}
#endif
// Check for the GIF header
if ((buf[0] == 'G') && (buf[1] == 'I') && (buf[2] == 'F') &&
(buf[3] == '8') && ((buf[4] == '7') || (buf[4] == '9')) &&
(buf[5] == 'a'))
{
if (!LoadGIF (file))
return false;
return true;
}
// MessageBox (NULL, "Unknown File Format", "Error", MB_ICONSTOP);
return false;
}
bool Image::FileLoad (const char* filename)
{
FileDisk file;
if (!file.Open (filename, "rb"))
return false;
return FileLoad (file);
}
bool Image::FileSave (File& file, LC_IMAGE_OPTS* opts) const
{
switch (opts->format)
{
#ifdef LC_HAVE_JPEGLIB
case LC_IMAGE_JPG:
return SaveJPG (file, opts->quality, opts->interlaced);
#endif
case LC_IMAGE_GIF:
return SaveGIF (file, opts->transparent, opts->interlaced, opts->background);
case LC_IMAGE_BMP:
return SaveBMP (file, opts->truecolor == false);
#ifdef LC_HAVE_PNGLIB
case LC_IMAGE_PNG:
return SavePNG (file, opts->transparent, opts->interlaced, opts->background);
#endif
default:
break;
}
// MessageBox (NULL, "Could not save file", "Error", MB_ICONSTOP);
return false;
}
bool Image::FileSave (const char* filename, LC_IMAGE_OPTS* opts) const
{
char name[LC_MAXPATH], ext[5], *p;
FileDisk file;
bool needext = false;
strcpy (name, filename);
p = name + strlen (name) - 1;
while ((p > name) && (*p != '/') && (*p != '\\') && (*p != '.'))
p--;
if (*p != '.')
needext = true;
else
{
if (strlen (p) > 5)
needext = true;
else
{
strcpy (ext, p+1);
strlwr (ext);
if (strcmp (ext, "bmp") == 0)
opts->format = LC_IMAGE_BMP;
else if (strcmp (ext, "gif") == 0)
opts->format = LC_IMAGE_GIF;
#ifdef LC_HAVE_JPEGLIB
else if (strcmp (ext, "jpg") == 0)
opts->format = LC_IMAGE_JPG;
else if (strcmp (ext, "jpeg") == 0)
opts->format = LC_IMAGE_JPG;
#endif
#ifdef LC_HAVE_PNGLIB
else if (strcmp (ext, "png") == 0)
opts->format = LC_IMAGE_PNG;
#endif
else
needext = true;
}
}
if (needext)
{
// no extension, add from the options
switch (opts->format)
{
case LC_IMAGE_BMP:
strcat (name, ".bmp");
break;
case LC_IMAGE_GIF:
strcat (name, ".gif");
break;
#ifdef LC_HAVE_JPEGLIB
case LC_IMAGE_JPG:
strcat (name, ".jpg");
break;
#endif
#ifdef LC_HAVE_PNGLIB
case LC_IMAGE_PNG:
strcat (name, ".png");
break;
#endif
default:
return false;
}
}
if (!file.Open (name, "wb"))
return false;
return FileSave (file, opts);
}
// =============================================================================
// Global functions
#ifdef LC_WINDOWS
#include "system.h"
#define AVIIF_KEYFRAME 0x00000010L // this frame is a key frame.
#define LPLPBI LPBITMAPINFOHEADER *
static HANDLE MakeDib (HBITMAP hbitmap, Image& image)
{
HANDLE hdib ;
HDC hdc ;
BITMAP bitmap ;
UINT wLineLen ;
DWORD dwSize ;
DWORD wColSize ;
LPBITMAPINFOHEADER lpbi ;
LPBYTE lpBits ;
UINT bits = 24;
int i, j;
GetObject(hbitmap,sizeof(BITMAP),&bitmap) ;
// DWORD align the width of the DIB
// Figure out the size of the colour table
// Calculate the size of the DIB
wLineLen = (bitmap.bmWidth*bits+31)/32 * 4;
wColSize = sizeof(RGBQUAD)*((bits <= 8) ? 1<<bits : 0);
dwSize = sizeof(BITMAPINFOHEADER) + wColSize +
(DWORD)(UINT)wLineLen*(DWORD)(UINT)bitmap.bmHeight;
// Allocate room for a DIB and set the LPBI fields
hdib = GlobalAlloc(GHND,dwSize);
if (!hdib)
return hdib ;
lpbi = (LPBITMAPINFOHEADER)GlobalLock(hdib) ;
lpbi->biSize = sizeof(BITMAPINFOHEADER) ;
lpbi->biWidth = bitmap.bmWidth ;
lpbi->biHeight = bitmap.bmHeight ;
lpbi->biPlanes = 1 ;
lpbi->biBitCount = (WORD) bits ;
lpbi->biCompression = BI_RGB ;
lpbi->biSizeImage = dwSize - sizeof(BITMAPINFOHEADER) - wColSize ;
lpbi->biXPelsPerMeter = 0 ;
lpbi->biYPelsPerMeter = 0 ;
lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;
lpbi->biClrImportant = 0 ;
// Get the bits from the bitmap and stuff them after the LPBI
lpBits = (LPBYTE)(lpbi+1)+wColSize ;
hdc = CreateCompatibleDC(NULL) ;
GetDIBits(hdc,hbitmap,0,bitmap.bmHeight,lpBits,(LPBITMAPINFO)lpbi, DIB_RGB_COLORS);
for (i = 0; i < lpbi->biHeight; i++)
{
unsigned char *src = image.GetData() + i * image.Width() * 3;
unsigned char *dst = lpBits + (lpbi->biHeight - i - 1) * wLineLen;
for (j = 0; j < lpbi->biWidth; j++)
{
dst[0] = src[2];
dst[1] = src[1];
dst[2] = src[0];
src += 3;
dst += 3;
}
}
// Fix this if GetDIBits messed it up....
lpbi->biClrUsed = (bits <= 8) ? 1<<bits : 0;
DeleteDC(hdc) ;
GlobalUnlock(hdib);
return hdib ;
}
// Walk through our array of LPBI's and free them
static void FreeFrames(LPLPBI alpbi, int frames)
{
int w;
if (!alpbi[0])
return;
// Don't free a frame if it's a duplicate of the previous one
for (w=0; w < frames; w++)
if (alpbi[w] && alpbi[w] != alpbi[w-1])
GlobalFreePtr(alpbi[w]);
for (w=0; w < frames; w++)
alpbi[w] = NULL;
}
// Fill an array of LPBI's with the frames for this movie
static void MakeFrames (LPLPBI alpbi, Image *images, int frames)
{
HBITMAP hbitmap, hbitmapOld;
HDC hdc, hdcMem;
int i;
RECT rc;
hdc = GetDC (NULL);
hdcMem = CreateCompatibleDC (NULL);
hbitmap = CreateCompatibleBitmap (hdc, images[0].Width (), images[0].Height ());
// Now walk through and make all the frames
for ( i=0; i < frames; i++ )
{
hbitmapOld = SelectBitmap(hdcMem, hbitmap);
// Fill the whole frame with white
SetRect(&rc,0,0,images[0].Width (), images[0].Height ());
FillRect(hdcMem,&rc,GetStockBrush(WHITE_BRUSH));
SelectBitmap(hdcMem, hbitmapOld);
// Make this into a DIB and stuff it into the array
alpbi[i] = (LPBITMAPINFOHEADER)GlobalLock(MakeDib(hbitmap, images[i]));
// For an error, just duplicate the last frame if we can
if (alpbi[i] == NULL && i )
alpbi[i] = alpbi[i-1] ;
}
// Select all the old objects back and delete resources
DeleteBitmap(hbitmap) ;
DeleteObject(hdcMem) ;
ReleaseDC(NULL,hdc) ;
}
void SaveVideo(char* filename, Image *images, int count, float fps)
{
AVISTREAMINFO strhdr;
PAVIFILE pfile = NULL;
PAVISTREAM ps = NULL, psCompressed = NULL;
AVICOMPRESSOPTIONS opts;
AVICOMPRESSOPTIONS FAR * aopts[1] = { &opts };
LPBITMAPINFOHEADER *plpbi;
_fmemset(&opts, 0, sizeof(opts));
// first let's make sure we are running on 1.1
WORD wVer = HIWORD(VideoForWindowsVersion());
if (wVer < 0x010a)
{
SystemDoMessageBox("Video for Windows 1.1 or later required", MB_OK|MB_ICONSTOP);
return;
}
AVIFileInit();
plpbi = (LPBITMAPINFOHEADER*) malloc (count*sizeof (LPBITMAPINFOHEADER));
MakeFrames (plpbi, images, count);
if (AVIFileOpen(&pfile, filename, OF_WRITE | OF_CREATE, NULL) == AVIERR_OK)
{
// Fill in the header for the video stream.
_fmemset(&strhdr, 0, sizeof(strhdr));
strhdr.fccType = streamtypeVIDEO;
strhdr.fccHandler = 0;
strhdr.dwScale = 1;
/////////////// set FPS
strhdr.dwRate = 15; // 15 fps
strhdr.dwSuggestedBufferSize = plpbi[0]->biSizeImage;
SetRect(&strhdr.rcFrame, 0, 0, (int) plpbi[0]->biWidth, (int) plpbi[0]->biHeight);
// And create the stream.
if (AVIFileCreateStream(pfile, &ps, &strhdr) == AVIERR_OK)
if (AVISaveOptions(NULL, 0, 1, &ps, (LPAVICOMPRESSOPTIONS FAR *) &aopts))
// if (AVISaveOptions(AfxGetMainWnd()->m_hWnd, 0, 1, &ps, (LPAVICOMPRESSOPTIONS FAR *) &aopts))
if (AVIMakeCompressedStream(&psCompressed, ps, &opts, NULL) == AVIERR_OK)
if (AVIStreamSetFormat(psCompressed, 0, plpbi[0], plpbi[0]->biSize + plpbi[0]->biClrUsed * sizeof(RGBQUAD)) == AVIERR_OK)
{
float fPause = (float)Sys_ProfileLoadInt("Default", "AVI Pause", 100)/100;
int time = (int)(fPause * 15);
///////////// set FPS
time = 1;
for (int i = 0; i < count; i++)
{
if (AVIStreamWrite(psCompressed, i * time, 1,
(LPBYTE) plpbi[i] + plpbi[i]->biSize + plpbi[i]->biClrUsed * sizeof(RGBQUAD),
plpbi[i]->biSizeImage, i%5 ? 0 : AVIIF_KEYFRAME, NULL, NULL) != AVIERR_OK)
break;
}
}
}
FreeFrames (plpbi, count);
// Now close the file
if (ps) AVIStreamClose(ps);
if (psCompressed) AVIStreamClose(psCompressed);
if (pfile) AVIFileClose(pfile);
AVIFileExit();
}
#else
void SaveVideo(char* filename, Image *images, int count, float fps)
{
// SystemDoMessageBox("Format not supported under this platform.", LC_MB_OK|LC_MB_ERROR);
}
#endif