File: cpw_images.c/***************************************************************************/
/* */
/* cpw_images.c */
/* */
/* Support for loading and saving a few image formats commonly used */
/* in game programming. Also supports a few image manipulation */
/* routines. Simple, no-nonsence, and built to stay that way. */
/* */
/* Currently supports: TGA, BMP */
/* */
/* Copyright 2001-2002 by */
/* Jim Mathies, */
/* */
/* This file is part of the Cpw project, and may only be used, */
/* modified, and distributed under the terms of the Cpw project */
/* license. By continuing to use, modify, or distribute this file */
/* you indicate that you have read the license and understand and */
/* accept it fully. */
/* */
/* File Platform: cross */
/* */
/***************************************************************************/
#include "cpw_images.h"
#include "cpw_error.h"
#include "cpw_libtarga.h"
#include "cpw_bitmaps.h"
#include "cpw_window.h"
#include <math.h>
#include CPW_GLU_H
/*************************************************************************/
/* */
/* image api */
/* */
/*************************************************************************/
/* load an image into an image buffer and texture fit it if requested */
bool cpwLoadImage( pCpw cpw, CpwImage * image,
CpwImageType type, pChar filename, bool texturefit )
{
if ( image == 0 ) {
cpw_error_set( cpw, cpw_error_invalidparameter );
return false;
}
memset( image, 0, sizeof( CpwImage ) );
if ( type == CPW_IMAGEFORMAT_BMP ) {
/* bmp files */
if ( cpw_bitmap_load( cpw, filename, image ) == false ) {
/* error set by load routine */
return false;
}
} else if ( type == CPW_IMAGEFORMAT_TGA ) {
/* tga files */
TargaInfo tga;
memset( &tga, 0, sizeof( TargaInfo ) );
image->data = tga_load( &tga, filename );
if ( tga.error == true ) {
cpw_error_set( cpw, cpw_error_failedtoload );
return false;
}
image->width = tga.width;
image->height = tga.height;
image->depth = tga.format;
} else {
cpw_error_set( cpw, cpw_error_invalidformat );
return false;
}
if ( texturefit )
cpwTextureFit( cpw, image );
return true;
}
/* free the resources associated with an image buffer */
bool cpwFreeImage( pCpw cpw, CpwImage * image )
{
if ( image == 0 ) {
cpw_error_set( cpw, cpw_error_invalidparameter );
return false;
}
cpwfree( image->data );
memset( image, 0, sizeof( CpwImage ) );
return true;
}
/* fit an image to a width and height suitable for gl texturing */
bool cpwTextureFit( pCpw cpw, CpwImage * image )
{
uint_32 width;
uint_32 height;
if ( image == 0 ) {
cpw_error_set( cpw, cpw_error_invalidparameter );
return false;
}
width = image->width;
height = image->height;
if ( cpw_images_bestfit( &width, &height ) )
cpwResizeImage( cpw, image, width, height );
return true;
}
/* take a screenshot of the current rendering context's frame buffer */
bool cpwScreenShot( pCpw cpw, CpwImage * image )
{
CpwWindowInfo info;
uint_32 size;
if ( image == 0 ) {
cpw_error_set( cpw, cpw_error_invalidparameter );
return false;
}
memset( image, 0, sizeof( CpwImage ) );
if ( !cpwGetWindowInfo( cpw, &info, 0 ) ) { /* 0 winid returns current foreground info */
cpw_error_set( cpw, cpw_error_novalidcontext );
return false;
}
size = info.width * info.height * 3; /* RGB pixel width = 3 */
if ( size == 0 ) return true;
glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT );
glPushAttrib( GL_PIXEL_MODE_BIT );
glPixelStorei( GL_UNPACK_LSB_FIRST, GL_FALSE );
glPixelStorei( GL_PACK_ROW_LENGTH, info.width );
glPixelStorei( GL_PACK_SKIP_ROWS, 0 );
glPixelStorei( GL_PACK_SKIP_PIXELS, 0 );
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
image->data = cpwmalloc( size );
if ( image->data == 0 ) {
cpw_error_set( cpw, cpw_error_outofmemory );
return false;
}
memset( image->data, 0, size );
glReadPixels( 0, 0, info.width, info.height, GL_RGB, GL_UNSIGNED_BYTE, image->data );
image->width = info.width;
image->height = info.height;
image->depth = 3;
glPopClientAttrib();
glPopAttrib();
return true;
}
/* save an image buffer to a file */
bool cpwSaveImage( pCpw cpw, CpwImage * image, pChar filename )
{
if ( filename == 0 || image == 0 ) {
cpw_error_set( cpw, cpw_error_invalidparameter );
return false;
}
/* save as rle tga */
if ( !tga_write_rle( filename, image->width, image->height, image->data, TGA_TRUECOLOR_24 ) )
return false;
/*
this works too but the file size is much bigger.
if ( !tga_write_raw( filename, image->width, image->height, image->data, TGA_TRUECOLOR_24 ) )
return false;
*/
return true;
}
/* simple easy to use interface to gluScaleImage */
bool cpwResizeImage( pCpw cpw, CpwImage * image, uint_32 new_width, uint_32 new_height )
{
pChar newdata;
GLenum format;
switch( image->depth ) {
case 1: format = GL_LUMINANCE; break;
case 3: format = GL_RGB; break;
case 4: format = GL_RGBA; break;
default:
cpw_error_set( cpw, cpw_error_invalidparameter );
return false;
}
newdata = cpwmalloc( new_width * new_height * image->depth ); /* op */
memset( newdata, 0, new_width * new_height * image->depth );
gluScaleImage( format, image->width, image->height, GL_UNSIGNED_BYTE, image->data,
new_width, new_height, GL_UNSIGNED_BYTE, newdata );
cpwfree( image->data );
image->width = new_width;
image->height = new_height;
image->data = newdata;
return true;
}
/* fit a texture's width and height to GL texture requirements */
bool cpw_images_bestfit( uint_32 * width, uint_32 * height )
{
float xratio = (float)*width / 64;
float yratio = (float)*height / 64;
if ( xratio == (int)xratio && yratio == (int)yratio ) return false;
if ( ( xratio - (int)floor( xratio ) ) > .5 ) {
*width = (int)( 64 * ceil( xratio ));
} else {
*width = (int)( 64 * floor( xratio ));
}
if ( ( yratio - (int)floor( yratio ) ) > .5 ) {
*height = (int)( 64 * ceil( yratio ));
} else {
*height = (int)( 64 * floor( xratio ));
}
if ( *height == 0 ) *height = 64;
if ( *width == 0 ) *width = 64;
return true;
}