Files
itgmania212121/stepmania/src/RageBitmapTexture.cpp
T

332 lines
12 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2001-11-03 10:52:42 +00:00
#include "RageBitmapTexture.h"
#include "RageUtil.h"
2002-05-01 19:14:55 +00:00
#include "RageLog.h"
#include "RageTextureManager.h"
2002-07-27 19:29:51 +00:00
#include "RageException.h"
#include "RageDisplay.h"
2003-05-22 05:28:37 +00:00
#include "RageTypes.h"
2004-06-10 22:47:51 +00:00
#include "arch/Dialog/Dialog.h"
2004-06-14 00:51:00 +00:00
#include "RageSurface.h"
#include "RageSurfaceUtils.h"
#include "RageSurfaceUtils_Zoom.h"
2005-01-20 01:35:49 +00:00
#include "RageSurfaceUtils_Dither.h"
#include "RageSurface_Load.h"
2002-06-14 22:25:22 +00:00
2005-06-24 02:19:04 +00:00
static void GetResolutionFromFileName( CString sPath, int &iWidth, int &iHeight )
2003-05-05 03:14:47 +00:00
{
/* Match:
2003-05-05 06:21:01 +00:00
* Foo (res 512x128).png
* Also allow, eg:
* Foo (dither, res 512x128).png
*
2003-05-05 03:14:47 +00:00
* Be careful that this doesn't get mixed up with frame dimensions. */
2005-06-24 02:19:04 +00:00
static Regex re( "\\([^\\)]*res ([0-9]+)x([0-9]+).*\\)" );
2003-05-05 03:14:47 +00:00
2005-06-24 02:19:04 +00:00
vector<CString> asMatches;
if( !re.Compare(sPath, asMatches) )
2003-05-05 03:14:47 +00:00
return;
2005-06-24 02:19:04 +00:00
iWidth = atoi( asMatches[0].c_str() );
iHeight = atoi( asMatches[1].c_str() );
2003-05-05 03:14:47 +00:00
}
RageBitmapTexture::RageBitmapTexture( RageTextureID name ) :
RageTexture( name )
2001-11-03 10:52:42 +00:00
{
2003-05-22 05:28:37 +00:00
Create();
2001-11-03 10:52:42 +00:00
}
RageBitmapTexture::~RageBitmapTexture()
{
2003-05-22 05:28:37 +00:00
Destroy();
2002-04-28 20:42:32 +00:00
}
2001-11-03 10:52:42 +00:00
void RageBitmapTexture::Reload()
{
2003-05-22 05:28:37 +00:00
Destroy();
Create();
}
2003-05-22 05:28:37 +00:00
/*
* Each dwMaxSize, dwTextureColorDepth and iAlphaBits are maximums; we may
* use less. iAlphaBits must be 0, 1 or 4.
*
* XXX: change iAlphaBits == 4 to iAlphaBits == 8 to indicate "as much alpha
* as needed", since that's what it really is; still only use 4 in 16-bit textures.
*
* Dither forces dithering when loading 16-bit textures.
* Stretch forces the loaded image to fill the texture completely.
*/
2003-05-22 05:28:37 +00:00
void RageBitmapTexture::Create()
{
RageTextureID actualID = GetID();
2004-09-05 23:35:49 +00:00
2004-12-11 22:29:55 +00:00
ASSERT( actualID.filename != "" );
2004-06-14 05:47:17 +00:00
/* Load the image into a RageSurface. */
2004-06-14 01:12:22 +00:00
CString error;
2005-06-24 02:19:04 +00:00
RageSurface *pImg = RageSurfaceUtils::LoadFile( actualID.filename, error );
2003-06-05 19:29:27 +00:00
2004-02-20 05:11:12 +00:00
/* Tolerate corrupt/unknown images. */
2005-06-24 02:19:04 +00:00
if( pImg == NULL )
2004-02-20 05:11:12 +00:00
{
CString sWarning = ssprintf( "RageBitmapTexture: Couldn't load %s: %s", actualID.filename.c_str(), error.c_str() );
Dialog::OK( sWarning );
2005-06-24 02:19:04 +00:00
pImg = RageSurfaceUtils::MakeDummySurface( 64, 64 );
ASSERT( pImg != NULL );
2004-02-20 05:11:12 +00:00
}
2004-06-14 00:51:00 +00:00
if( actualID.bHotPinkColorKey )
2005-06-24 02:19:04 +00:00
RageSurfaceUtils::ApplyHotPinkColorKey( pImg );
2003-01-22 04:18:02 +00:00
{
/* Do this after setting the color key for paletted images; it'll also return
* TRAIT_NO_TRANSPARENCY if the color key is never used. */
2005-06-24 02:19:04 +00:00
int traits = RageSurfaceUtils::FindSurfaceTraits( pImg );
2004-06-14 00:51:00 +00:00
if( traits & RageSurfaceUtils::TRAIT_NO_TRANSPARENCY )
actualID.iAlphaBits = 0;
2004-06-14 00:51:00 +00:00
else if( traits & RageSurfaceUtils::TRAIT_BOOL_TRANSPARENCY )
actualID.iAlphaBits = 1;
}
// look in the file name for a format hints
2005-06-24 02:19:04 +00:00
CString sHintString = GetID().filename + actualID.AdditionalTextureHints;
sHintString.MakeLower();
2005-06-24 02:19:04 +00:00
if( sHintString.Find("32bpp") != -1 ) actualID.iColorDepth = 32;
else if( sHintString.Find("16bpp") != -1 ) actualID.iColorDepth = 16;
if( sHintString.Find("dither") != -1 ) actualID.bDither = true;
if( sHintString.Find("stretch") != -1 ) actualID.bStretch = true;
if( sHintString.Find("mipmaps") != -1 ) actualID.bMipMaps = true;
if( sHintString.Find("nomipmaps") != -1 ) actualID.bMipMaps = false; // check for "nomipmaps" after "mipmaps"
/* If the image is marked grayscale, then use all bits not used for alpha
* for the intensity. This way, if an image has no alpha, you get an 8-bit
* grayscale; if it only has boolean transparency, you get a 7-bit grayscale. */
2005-06-24 02:19:04 +00:00
if( sHintString.Find("grayscale") != -1 ) actualID.iGrayscaleBits = 8-actualID.iAlphaBits;
2003-10-08 23:21:14 +00:00
/* This indicates that the only component in the texture is alpha; assume all
* color is white. */
2005-06-24 02:19:04 +00:00
if( sHintString.Find("alphamap") != -1 ) actualID.iGrayscaleBits = 0;
2003-10-08 23:21:14 +00:00
2003-10-08 02:23:53 +00:00
/* No iGrayscaleBits for images that are already paletted. We don't support
* that; and that hint is intended for use on images that are already grayscale,
* it's not intended to change a color image into a grayscale image. */
2005-06-24 02:19:04 +00:00
if( actualID.iGrayscaleBits != -1 && pImg->format->BitsPerPixel == 8 )
2003-10-08 02:23:53 +00:00
actualID.iGrayscaleBits = -1;
/* Cap the max texture size to the hardware max. */
actualID.iMaxSize = min( actualID.iMaxSize, DISPLAY->GetMaxTextureSize() );
/* Save information about the source. */
2005-06-24 02:19:04 +00:00
m_iSourceWidth = pImg->w;
m_iSourceHeight = pImg->h;
/* image size cannot exceed max size */
m_iImageWidth = min( m_iSourceWidth, actualID.iMaxSize );
m_iImageHeight = min( m_iSourceHeight, actualID.iMaxSize );
2002-10-24 04:16:15 +00:00
/* Texture dimensions need to be a power of two; jump to the next. */
m_iTextureWidth = power_of_two(m_iImageWidth);
m_iTextureHeight = power_of_two(m_iImageHeight);
2003-06-22 01:02:22 +00:00
/* If we're under 8x8, increase it, to avoid filtering problems on odd hardware. */
2005-06-24 02:19:04 +00:00
if( m_iTextureWidth < 8 || m_iTextureHeight < 8 )
2003-06-22 01:02:22 +00:00
{
actualID.bStretch = true;
2005-06-24 02:19:04 +00:00
m_iTextureWidth = max( 8, m_iTextureWidth );
m_iTextureHeight = max( 8, m_iTextureHeight );
2003-06-22 01:02:22 +00:00
}
ASSERT( m_iTextureWidth <= actualID.iMaxSize );
ASSERT( m_iTextureHeight <= actualID.iMaxSize );
2005-06-24 02:19:04 +00:00
if( actualID.bStretch )
2002-11-21 22:34:37 +00:00
{
/* The hints asked for the image to be stretched to the texture size,
* probably for tiling. */
m_iImageWidth = m_iTextureWidth;
m_iImageHeight = m_iTextureHeight;
}
2005-06-24 02:19:04 +00:00
if( pImg->w != m_iImageWidth || pImg->h != m_iImageHeight )
RageSurfaceUtils::Zoom( pImg, m_iImageWidth, m_iImageHeight );
2003-01-01 09:07:34 +00:00
2003-10-08 02:23:53 +00:00
if( actualID.iGrayscaleBits != -1 && DISPLAY->SupportsTextureFormat(RageDisplay::FMT_PAL) )
{
2005-06-24 02:19:04 +00:00
RageSurface *pGrayscale = RageSurfaceUtils::PalettizeToGrayscale( pImg, actualID.iGrayscaleBits, actualID.iAlphaBits );
2003-10-08 02:23:53 +00:00
2005-06-24 02:19:04 +00:00
delete pImg;
pImg = pGrayscale;
2003-10-08 02:23:53 +00:00
}
2003-10-08 01:21:36 +00:00
2005-06-24 02:19:04 +00:00
/* Figure out which texture format we want the renderer to use. */
RageDisplay::PixelFormat pixfmt;
/* If the source is palleted, always load as paletted if supported. */
if( pImg->format->BitsPerPixel == 8 && DISPLAY->SupportsTextureFormat(RageDisplay::FMT_PAL) )
2003-05-22 05:28:37 +00:00
{
2003-10-03 21:38:38 +00:00
pixfmt = RageDisplay::FMT_PAL;
2003-05-22 05:28:37 +00:00
}
else
2003-01-26 02:35:55 +00:00
{
2003-05-22 05:28:37 +00:00
// not paletted
switch( actualID.iColorDepth )
2003-05-22 05:28:37 +00:00
{
case 16:
{
/* Bits of alpha in the source: */
2005-06-24 02:19:04 +00:00
int iSourceAlphaBits = 8 - pImg->format->Loss[3];
2003-05-22 05:28:37 +00:00
/* Don't use more than we were hinted to. */
2005-06-24 02:19:04 +00:00
iSourceAlphaBits = min( actualID.iAlphaBits, iSourceAlphaBits );
2003-05-22 05:28:37 +00:00
2005-06-24 02:19:04 +00:00
switch( iSourceAlphaBits )
{
2003-05-22 05:28:37 +00:00
case 0:
case 1:
2003-10-03 21:38:38 +00:00
pixfmt = RageDisplay::FMT_RGB5A1;
2003-05-22 05:28:37 +00:00
break;
default:
2003-10-03 21:38:38 +00:00
pixfmt = RageDisplay::FMT_RGBA4;
2003-05-22 05:28:37 +00:00
break;
}
}
break;
case 32:
2003-10-03 21:38:38 +00:00
pixfmt = RageDisplay::FMT_RGBA8;
2003-05-22 05:28:37 +00:00
break;
default:
RageException::Throw( "Invalid color depth: %d bits", actualID.iColorDepth );
2003-05-22 05:28:37 +00:00
}
2004-08-28 08:29:13 +00:00
}
2003-01-26 02:35:55 +00:00
/* Make we're using a supported format. Every card supports either RGBA8 or RGBA4. */
if( !DISPLAY->SupportsTextureFormat(pixfmt) )
{
pixfmt = RageDisplay::FMT_RGBA8;
if( !DISPLAY->SupportsTextureFormat(pixfmt) )
pixfmt = RageDisplay::FMT_RGBA4;
}
2004-08-28 08:29:13 +00:00
/* Dither if appropriate. XXX: This is a special case: don't bother dithering to
* RGBA8888. We actually want to dither only if the destination has greater color
* depth on at least one color channel than the source. For example, it doesn't
* make sense to do this when pixfmt is RGBA5551 if the image is only RGBA555. */
if( actualID.bDither &&
(pixfmt==RageDisplay::FMT_RGBA4 || pixfmt==RageDisplay::FMT_RGB5A1) )
{
/* Dither down to the destination format. */
const RageDisplay::PixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt);
2005-06-24 02:19:04 +00:00
RageSurface *dst = CreateSurface( pImg->w, pImg->h, pfd->bpp,
2004-08-28 08:29:13 +00:00
pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
2005-06-24 02:19:04 +00:00
RageSurfaceUtils::ErrorDiffusionDither( pImg, dst );
delete pImg;
pImg = dst;
2003-01-26 02:35:55 +00:00
}
/* This needs to be done *after* the final resize, since that resize
* may introduce new alpha bits that need to be set. It needs to be
* done *before* we set up the palette, since it might change it. */
2005-06-24 02:19:04 +00:00
RageSurfaceUtils::FixHiddenAlpha( pImg );
/* Scale up to the texture size, if needed. */
RageSurfaceUtils::ConvertSurface( pImg, m_iTextureWidth, m_iTextureHeight,
pImg->fmt.BitsPerPixel, pImg->fmt.Mask[0], pImg->fmt.Mask[1], pImg->fmt.Mask[2], pImg->fmt.Mask[3] );
2003-05-22 05:28:37 +00:00
2005-06-24 02:19:04 +00:00
m_uTexHandle = DISPLAY->CreateTexture( pixfmt, pImg, actualID.bMipMaps );
CreateFrameRects();
2003-05-22 05:28:37 +00:00
2003-07-05 05:34:16 +00:00
//
// Enforce frames in the image have even dimensions. Otherwise,
// pixel/texel alignment will be off.
//
bool bRunCheck = true;
2005-06-24 02:19:04 +00:00
// Don't check if the artist intentionally blanked the image by making it very tiny.
2003-07-05 05:34:16 +00:00
if( this->GetSourceWidth()<=2 || this->GetSourceHeight()<=2 )
bRunCheck = false;
// HACK: Don't check song graphics. Many of them are weird dimensions.
if( !TEXTUREMAN->GetOddDimensionWarning() )
bRunCheck = false;
2003-07-05 05:34:16 +00:00
if( bRunCheck )
{
float fFrameWidth = this->GetSourceWidth() / (float)this->GetFramesWide();
float fFrameHeight = this->GetSourceHeight() / (float)this->GetFramesHigh();
float fBetterFrameWidth = roundf((fFrameWidth+0.99f)/2)*2;
float fBetterFrameHeight = roundf((fFrameHeight+0.99f)/2)*2;
float fBetterSourceWidth = this->GetFramesWide() * fBetterFrameWidth;
float fBetterSourceHeight = this->GetFramesHigh() * fBetterFrameHeight;
if( fFrameWidth!=fBetterFrameWidth || fFrameHeight!=fBetterFrameHeight )
{
CString sWarning = ssprintf(
"The graphic '%s' has frame dimensions that aren't even numbers.\n\n"
"The entire image is %dx%d and frame size is %.1fx%.1f.\n\n"
"Image quality will be much improved if you resize the graphic to %.0fx%.0f, which is a frame size of %.0fx%.0f.",
actualID.filename.c_str(),
this->GetSourceWidth(), this->GetSourceHeight(),
fFrameWidth, fFrameHeight,
fBetterSourceWidth, fBetterSourceHeight,
fBetterFrameWidth, fBetterFrameHeight );
LOG->Warn( sWarning );
2004-06-10 22:47:51 +00:00
Dialog::OK( sWarning, "FRAME_DIMENSIONS_WARNING" );
}
}
2003-07-05 05:34:16 +00:00
2005-06-24 02:19:04 +00:00
delete pImg;
/* See if the apparent "size" is being overridden. */
2005-06-24 02:19:04 +00:00
GetResolutionFromFileName( actualID.filename, m_iSourceWidth, m_iSourceHeight );
2005-06-24 02:19:04 +00:00
CString sProperties;
sProperties += RageDisplay::PixelFormatToString( pixfmt ) + " ";
if( actualID.iAlphaBits == 0 ) sProperties += "opaque ";
if( actualID.iAlphaBits == 1 ) sProperties += "matte ";
if( actualID.bStretch ) sProperties += "stretch ";
if( actualID.bDither ) sProperties += "dither ";
sProperties.erase( sProperties.size()-1 );
LOG->Trace( "RageBitmapTexture: Loaded '%s' (%ux%u); %s, source %d,%d; image %d,%d.",
actualID.filename.c_str(), GetTextureWidth(), GetTextureHeight(),
2005-06-24 02:19:04 +00:00
sProperties.c_str(), m_iSourceWidth, m_iSourceHeight,
m_iImageWidth, m_iImageHeight );
2003-05-22 05:28:37 +00:00
}
void RageBitmapTexture::Destroy()
{
DISPLAY->DeleteTexture( m_uTexHandle );
2001-11-03 10:52:42 +00:00
}
2004-05-06 00:42:06 +00:00
/*
* Copyright (c) 2001-2004 Chris Danford, Glenn Maynard
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/