Esempio n. 1
0
std::string MovieTexture_Generic::Init()
{
	std::string sError = m_pDecoder->Open( GetID().filename );
	if( sError != "" )
		return sError;

	CreateTexture();
	CreateFrameRects();

	/* Decode one frame, to guarantee that the texture is drawn when this function returns. */
	int ret = m_pDecoder->DecodeFrame( -1 );
	if( ret == -1 )
		return fmt::sprintf( "%s: error getting first frame", GetID().filename.c_str() );
	if( ret == 0 )
	{
		/* There's nothing there. */
		return fmt::sprintf( "%s: EOF getting first frame", GetID().filename.c_str() );
	}

	m_ImageWaiting = FRAME_DECODED;

	LOG->Trace( "Resolution: %ix%i (%ix%i, %ix%i)",
			m_iSourceWidth, m_iSourceHeight,
			m_iImageWidth, m_iImageHeight, m_iTextureWidth, m_iTextureHeight );

	UpdateFrame();

	CHECKPOINT_M("Generic initialization completed. No errors found.");

	return std::string();
}
Esempio n. 2
0
MovieTexture_Null::MovieTexture_Null(RageTextureID ID) : RageMovieTexture(ID)
{
	LOG->Trace("MovieTexture_Null::MovieTexture_Null(ID)");
	texHandle = 0;

	RageTextureID actualID = GetID();

	actualID.iAlphaBits = 0;
	int size = 64;
	m_iSourceWidth = size;
	m_iSourceHeight = size;
	m_iImageWidth = size;
	m_iImageHeight = size;
	m_iTextureWidth = power_of_two(size);
	m_iTextureHeight = m_iTextureWidth;
	m_iFramesWide = 1;
	m_iFramesHigh = 1;

	CreateFrameRects();

	RagePixelFormat pixfmt = RagePixelFormat_RGBA4;
	if( !DISPLAY->SupportsTextureFormat(pixfmt) )
		pixfmt = RagePixelFormat_RGBA8;
	ASSERT( DISPLAY->SupportsTextureFormat(pixfmt) );

	const RageDisplay::RagePixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc( pixfmt );
	RageSurface *img = CreateSurface( size, size, pfd->bpp,
		pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
	memset( img->pixels, 0, img->pitch*img->h );

	texHandle = DISPLAY->CreateTexture( pixfmt, img, false );

	delete img;
}
Esempio n. 3
0
	RageMovieTexture_Generic_Intermediate( RageTextureID ID, int iWidth, int iHeight,
		int iImageWidth, int iImageHeight, int iTextureWidth, int iTextureHeight,
		RageSurfaceFormat SurfaceFormat, RagePixelFormat pixfmt ):
		RageTexture(ID),
		m_SurfaceFormat( SurfaceFormat )
	{
		m_PixFmt = pixfmt;
		m_iSourceWidth = iWidth;
		m_iSourceHeight = iHeight;
/*		int iMaxSize = std::min( GetID().iMaxSize, DISPLAY->GetMaxTextureSize() );
		m_iImageWidth = std::min( m_iSourceWidth, iMaxSize );
		m_iImageHeight = std::min( m_iSourceHeight, iMaxSize );
		m_iTextureWidth = power_of_two( m_iImageWidth );
		m_iTextureHeight = power_of_two( m_iImageHeight );
*/

		m_iImageWidth = iImageWidth;
		m_iImageHeight = iImageHeight;
		m_iTextureWidth = iTextureWidth;
		m_iTextureHeight = iTextureHeight;

		CreateFrameRects();

		m_uTexHandle = 0;
		CreateTexture();
	}
/* RageTextureID identifies a file and a mechanism for loading it. We don't use
 * any of that, except as a unique identifier for this texture, so it can be
 * loaded elsewhere. Render targets can't be loaded blindly like a regular
 * texture, anyway, since something has to render into it. */
void RageTextureRenderTarget::Create()
{
	/* All render targets support non-power-of-two targets,
	 * but some require that the resulting texture dimensions be powers of two.
	 * CreateRenderTarget returns the actual resolution. */
	m_iTexHandle = DISPLAY->CreateRenderTarget( m_Param, m_iTextureWidth, m_iTextureHeight );

	m_iSourceWidth = m_Param.iWidth;
	m_iSourceHeight = m_Param.iHeight;
	m_iImageWidth = m_Param.iWidth;
	m_iImageHeight = m_Param.iHeight;
	m_iFramesWide = m_iFramesHigh = 1;

	CreateFrameRects();
}
Esempio n. 5
0
	void Create()
	{
		ASSERT( img );

		/* The image is preprocessed; do as little work as possible. */

		/* The source width is the width of the original file. */
		m_iSourceWidth = width;
		m_iSourceHeight = height;

		/* The image width (within the texture) is always the entire texture. 
		 * Only resize if the max texture size requires it; since these images
		 * are already scaled down, this shouldn't happen often. */
		if( img->w > DISPLAY->GetMaxTextureSize() || 
			img->h > DISPLAY->GetMaxTextureSize() )
		{
			LOG->Warn("Converted %s at runtime", GetID().filename.c_str() );
			int width = min( img->w, DISPLAY->GetMaxTextureSize() );
			int height = min( img->h, DISPLAY->GetMaxTextureSize() );
			RageSurfaceUtils::Zoom( img, width, height );
		}

		/* We did this when we cached it. */
		ASSERT( img->w == power_of_two(img->w) );
		ASSERT( img->h == power_of_two(img->h) );

		m_iTextureWidth = m_iImageWidth = img->w;
		m_iTextureHeight = m_iImageHeight = img->h;

		/* Find a supported texture format.  If it happens to match the stored
		 * file, we won't have to do any conversion here, and that'll happen often
		 * with paletted images. */
		RageDisplay::PixelFormat pf = img->format->BitsPerPixel == 8? RageDisplay::FMT_PAL: RageDisplay::FMT_RGB5A1;
		if( !DISPLAY->SupportsTextureFormat(pf) )
			pf = RageDisplay::FMT_RGBA4;
		ASSERT( DISPLAY->SupportsTextureFormat(pf) );

		ASSERT(img);
		m_uTexHandle = DISPLAY->CreateTexture( pf, img, false );

		CreateFrameRects();
	}
CString MovieTexture_FFMpeg::Init()
{
	CString sError = CreateDecoder();
	if( sError != "" )
		return sError;

	LOG->Trace("Bitrate: %i", decoder->m_stream->codec.bit_rate );
	LOG->Trace("Codec pixel format: %s", avcodec::avcodec_get_pix_fmt_name(decoder->m_stream->codec.pix_fmt) );

	/* Decode one frame, to guarantee that the texture is drawn when this function returns. */
	int ret = decoder->GetFrame();
	if( ret == -1 )
		return ssprintf( "%s: error getting first frame", GetID().filename.c_str() );
	if( ret == 0 )
	{
		/* There's nothing there. */
		return ssprintf( "%s: EOF getting first frame", GetID().filename.c_str() );
	}

	m_ImageWaiting = FRAME_DECODED;

	CreateTexture();
	LOG->Trace( "Resolution: %ix%i (%ix%i, %ix%i)",
			m_iSourceWidth, m_iSourceHeight,
			m_iImageWidth, m_iImageHeight, m_iTextureWidth, m_iTextureHeight );
	LOG->Trace( "Texture pixel format: %i", m_AVTexfmt );

	CreateFrameRects();

	ConvertFrame();
	UpdateFrame();

	CHECKPOINT;

	StartThread();

	return "";
}
Esempio n. 7
0
/*
 * 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.
 */
void RageBitmapTexture::Create()
{
	RageTextureID actualID = GetID();

	ASSERT( actualID.filename != "" );

	/* Load the image into a RageSurface. */
	RString error;
	RageSurface *pImg= NULL;
	if(actualID.filename == TEXTUREMAN->GetScreenTextureID().filename)
	{
		pImg= TEXTUREMAN->GetScreenSurface();
	}
	else
	{
		pImg= RageSurfaceUtils::LoadFile(actualID.filename, error);
	}

	/* Tolerate corrupt/unknown images. */
	if( pImg == NULL )
	{
		RString warning = ssprintf("RageBitmapTexture: Couldn't load %s: %s",
			actualID.filename.c_str(), error.c_str());
		LOG->Warn("%s", warning.c_str());
		Dialog::OK(warning, "missing_texture");
		pImg = RageSurfaceUtils::MakeDummySurface( 64, 64 );
		ASSERT( pImg != NULL );
	}

	if( actualID.bHotPinkColorKey )
		RageSurfaceUtils::ApplyHotPinkColorKey( pImg );

	{
		/* Do this after setting the color key for paletted images; it'll also return
		 * TRAIT_NO_TRANSPARENCY if the color key is never used. */
		int iTraits = RageSurfaceUtils::FindSurfaceTraits( pImg );
		if( iTraits & RageSurfaceUtils::TRAIT_NO_TRANSPARENCY )
			actualID.iAlphaBits = 0;
		else if( iTraits & RageSurfaceUtils::TRAIT_BOOL_TRANSPARENCY )
			actualID.iAlphaBits = 1;
	}

	// look in the file name for a format hints
	RString sHintString = GetID().filename + actualID.AdditionalTextureHints;
	sHintString.MakeLower();

	if( sHintString.find("32bpp") != string::npos )			actualID.iColorDepth = 32;
	else if( sHintString.find("16bpp") != string::npos )		actualID.iColorDepth = 16;
	if( sHintString.find("dither") != string::npos )		actualID.bDither = true;
	if( sHintString.find("stretch") != string::npos )		actualID.bStretch = true;
	if( sHintString.find("mipmaps") != string::npos )		actualID.bMipMaps = true;
	if( sHintString.find("nomipmaps") != string::npos )		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. */
	if( sHintString.find("grayscale") != string::npos )		actualID.iGrayscaleBits = 8-actualID.iAlphaBits;

	/* This indicates that the only component in the texture is alpha; assume all
	 * color is white. */
	if( sHintString.find("alphamap") != string::npos )		actualID.iGrayscaleBits = 0;

	/* 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. */
	if( actualID.iGrayscaleBits != -1 && pImg->format->BitsPerPixel == 8 )
		actualID.iGrayscaleBits = -1;

	/* Cap the max texture size to the hardware max. */
	actualID.iMaxSize = min( actualID.iMaxSize, DISPLAY->GetMaxTextureSize() );

	/* Save information about the source. */
	m_iSourceWidth = pImg->w;
	m_iSourceHeight = pImg->h;

	/* in-game image dimensions are the same as the source graphic */
	m_iImageWidth = m_iSourceWidth;
	m_iImageHeight = m_iSourceHeight;

	/* if "doubleres" (high resolution) and we're not allowing high res textures, then image dimensions are half of the source */
	if( sHintString.find("doubleres") != string::npos )
	{
		if( !StepMania::GetHighResolutionTextures() )
		{
			m_iImageWidth = m_iImageWidth / 2;
			m_iImageHeight = m_iImageHeight / 2;
		}
	}

	/* image size cannot exceed max size */
	m_iImageWidth = min( m_iImageWidth, actualID.iMaxSize );
	m_iImageHeight = min( m_iImageHeight, actualID.iMaxSize );

	/* 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);

	/* If we're under 8x8, increase it, to avoid filtering problems on odd hardware. */
	if( m_iTextureWidth < 8 || m_iTextureHeight < 8 )
	{
		actualID.bStretch = true;
		m_iTextureWidth = max( 8, m_iTextureWidth );
		m_iTextureHeight = max( 8, m_iTextureHeight );
	}

	ASSERT_M( m_iTextureWidth <= actualID.iMaxSize, ssprintf("w %i, %i", m_iTextureWidth, actualID.iMaxSize) );
	ASSERT_M( m_iTextureHeight <= actualID.iMaxSize, ssprintf("h %i, %i", m_iTextureHeight, actualID.iMaxSize) );

	if( actualID.bStretch )
	{
		/* The hints asked for the image to be stretched to the texture size,
		 * probably for tiling. */
		m_iImageWidth = m_iTextureWidth;
		m_iImageHeight = m_iTextureHeight;
	}

	if( pImg->w != m_iImageWidth || pImg->h != m_iImageHeight ) 
		RageSurfaceUtils::Zoom( pImg, m_iImageWidth, m_iImageHeight );

	if( actualID.iGrayscaleBits != -1 && DISPLAY->SupportsTextureFormat(RagePixelFormat_PAL) )
	{
		RageSurface *pGrayscale = RageSurfaceUtils::PalettizeToGrayscale( pImg, actualID.iGrayscaleBits, actualID.iAlphaBits );

		delete pImg;
		pImg = pGrayscale;
	}

	// Figure out which texture format we want the renderer to use.
	RagePixelFormat pixfmt;

	// If the source is palleted, always load as paletted if supported.
	if( pImg->format->BitsPerPixel == 8 && DISPLAY->SupportsTextureFormat(RagePixelFormat_PAL) )
	{
		pixfmt = RagePixelFormat_PAL;
	}
	else
	{
		// not paletted
		switch( actualID.iColorDepth )
		{
		case 16:
			{
				// Bits of alpha in the source:
				int iSourceAlphaBits = 8 - pImg->format->Loss[3];

				// Don't use more than we were hinted to.
				iSourceAlphaBits = min( actualID.iAlphaBits, iSourceAlphaBits );

				switch( iSourceAlphaBits )
				{
				case 0:
				case 1:
					pixfmt = RagePixelFormat_RGB5A1;
					break;
				default:
					pixfmt = RagePixelFormat_RGBA4;
					break;
				}
			}
			break;
		case 32:
			pixfmt = RagePixelFormat_RGBA8;
			break;
		default: FAIL_M( ssprintf("%i", actualID.iColorDepth) );
		}
	}

	// Make we're using a supported format. Every card supports either RGBA8 or RGBA4.
	if( !DISPLAY->SupportsTextureFormat(pixfmt) )
	{
		pixfmt = RagePixelFormat_RGBA8;
		if( !DISPLAY->SupportsTextureFormat(pixfmt) )
			pixfmt = RagePixelFormat_RGBA4;
	}

	/* 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==RagePixelFormat_RGBA4 || pixfmt==RagePixelFormat_RGB5A1) )
	{
		// Dither down to the destination format.
		const RageDisplay::RagePixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt);
		RageSurface *dst = CreateSurface( pImg->w, pImg->h, pfd->bpp,
			pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );

		RageSurfaceUtils::ErrorDiffusionDither( pImg, dst );
		delete pImg;
		pImg = dst;
	}

	/* 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. */
	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] );
	
	m_uTexHandle = DISPLAY->CreateTexture( pixfmt, pImg, actualID.bMipMaps );

	CreateFrameRects();


	{
		// Enforce frames in the image have even dimensions.
		// Otherwise, pixel/texel alignment will be off.
		int iDimensionMultiple = 2;

		if( sHintString.find("doubleres") != string::npos )
		{
			iDimensionMultiple = 4;
		}

		bool bRunCheck = true;

		// Don't check if the artist intentionally blanked the image by making it very tiny.
		if( this->GetSourceWidth()<=iDimensionMultiple || this->GetSourceHeight()<=iDimensionMultiple )
			bRunCheck = false;

		// HACK: Don't check song graphics. Many of them are weird dimensions.
		if( !TEXTUREMAN->GetOddDimensionWarning() )
			bRunCheck = false;

		// Don't check if this is the screen texture, the theme can't do anything
		// about it. -Kyz
		if(actualID == TEXTUREMAN->GetScreenTextureID())
		{
			bRunCheck= false;
		}

		if( bRunCheck  )
		{
			float fFrameWidth = this->GetSourceWidth() / (float)this->GetFramesWide();
			float fFrameHeight = this->GetSourceHeight() / (float)this->GetFramesHigh();
			float fBetterFrameWidth = ceilf(fFrameWidth/iDimensionMultiple) * iDimensionMultiple;
			float fBetterFrameHeight = ceilf(fFrameHeight/iDimensionMultiple) * iDimensionMultiple;
			float fBetterSourceWidth = this->GetFramesWide() * fBetterFrameWidth;
			float fBetterSourceHeight = this->GetFramesHigh() * fBetterFrameHeight;
			if( fFrameWidth!=fBetterFrameWidth || fFrameHeight!=fBetterFrameHeight )
			{
				RString sWarning = ssprintf(
					"The graphic '%s' has frame dimensions that aren't a multiple of %d.\n"
					"The entire image is %dx%d and frame size is %.1fx%.1f.\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(), 
					iDimensionMultiple,
					this->GetSourceWidth(), this->GetSourceHeight(), 
					fFrameWidth, fFrameHeight,
					fBetterSourceWidth, fBetterSourceHeight,
					fBetterFrameWidth, fBetterFrameHeight );
				LOG->Warn( "%s", sWarning.c_str() );
				Dialog::OK( sWarning, "FRAME_DIMENSIONS_WARNING" );
			}
		}
	}


	delete pImg;

	// Check for hints that override the apparent "size".
	GetResolutionFromFileName( actualID.filename, m_iSourceWidth, m_iSourceHeight );

	/* if "doubleres" (high resolution) then we want the image to appear in-game
	 * with dimensions 1/2 of the source. So, cut down the source dimension here
	 * after everything above is finished operating with the real image
	 * source dimensions. */
	if( sHintString.find("doubleres") != string::npos )
	{
		m_iSourceWidth = m_iSourceWidth / 2;
		m_iSourceHeight = m_iSourceHeight / 2;
	}


	RString sProperties;
	sProperties += RagePixelFormatToString( 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(),
	//	sProperties.c_str(), m_iSourceWidth, m_iSourceHeight,
	//	m_iImageWidth, m_iImageHeight );
}
Esempio n. 8
0
/*
 * 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.
 */
void RageBitmapTexture::Create()
{
	RageTextureID actualID = GetID();

	ASSERT( actualID.filename != "" );

	/* Create (and return) a surface ready to be loaded to OpenGL */
	/* Load the image into a RageSurface. */
	CString error;
	RageSurface *img = RageSurfaceUtils::LoadFile( actualID.filename, error );

	/* Tolerate corrupt/unknown images. */
	if( img == NULL )
	{
		CString sWarning = ssprintf( "RageBitmapTexture: Couldn't load %s: %s", actualID.filename.c_str(), error.c_str() );
		Dialog::OK( sWarning );
		img = RageSurfaceUtils::MakeDummySurface( 64, 64 );
		ASSERT( img != NULL );
	}

	if( actualID.bHotPinkColorKey )
		RageSurfaceUtils::ApplyHotPinkColorKey( img );

	{
		/* Do this after setting the color key for paletted images; it'll also return
		 * TRAIT_NO_TRANSPARENCY if the color key is never used. */
		int traits = RageSurfaceUtils::FindSurfaceTraits(img);
		if( traits & RageSurfaceUtils::TRAIT_NO_TRANSPARENCY )
			actualID.iAlphaBits = 0;
		else if( traits & RageSurfaceUtils::TRAIT_BOOL_TRANSPARENCY )
			actualID.iAlphaBits = 1;
	}

	// look in the file name for a format hints
	CString HintString = GetID().filename + actualID.AdditionalTextureHints;
	HintString.MakeLower();

	if( HintString.find("32bpp") != CString::npos )			actualID.iColorDepth = 32;
	else if( HintString.find("16bpp") != CString::npos )		actualID.iColorDepth = 16;
	if( HintString.find("dither") != CString::npos )		actualID.bDither = true;
	if( HintString.find("stretch") != CString::npos )		actualID.bStretch = true;
	if( HintString.find("mipmaps") != CString::npos )		actualID.bMipMaps = true;
	if( HintString.find("nomipmaps") != CString::npos )		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. */
	if( HintString.find("grayscale") != CString::npos )		actualID.iGrayscaleBits = 8-actualID.iAlphaBits;

	/* This indicates that the only component in the texture is alpha; assume all
	 * color is white. */
	if( HintString.find("alphamap") != CString::npos )		actualID.iGrayscaleBits = 0;

	/* 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. */
	if( actualID.iGrayscaleBits != -1 && img->format->BitsPerPixel == 8 )
		actualID.iGrayscaleBits = -1;

	/* Cap the max texture size to the hardware max. */
	actualID.iMaxSize = min( actualID.iMaxSize, DISPLAY->GetMaxTextureSize() );

	/* Save information about the source. */
	m_iSourceWidth = img->w;
	m_iSourceHeight = img->h;

	/* image size cannot exceed max size */
	m_iImageWidth = min( m_iSourceWidth, actualID.iMaxSize );
	m_iImageHeight = min( m_iSourceHeight, actualID.iMaxSize );

	/* 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);

	/* If we're under 8x8, increase it, to avoid filtering problems on odd hardware. */
	if(m_iTextureWidth < 8 || m_iTextureHeight < 8)
	{
		actualID.bStretch = true;
		m_iTextureWidth = max(8, m_iTextureWidth);
		m_iTextureHeight = max(8, m_iTextureHeight);
	}

	ASSERT( m_iTextureWidth <= actualID.iMaxSize );
	ASSERT( m_iTextureHeight <= actualID.iMaxSize );

	if(actualID.bStretch)
	{
		/* The hints asked for the image to be stretched to the texture size,
		 * probably for tiling. */
		m_iImageWidth = m_iTextureWidth;
		m_iImageHeight = m_iTextureHeight;
	}

	if( img->w != m_iImageWidth || img->h != m_iImageHeight ) 
		RageSurfaceUtils::Zoom( img, m_iImageWidth, m_iImageHeight );

	// Format of the image that we will pass to OpenGL and that we want OpenGL to use
	RageDisplay::PixelFormat pixfmt;

	if( actualID.iGrayscaleBits != -1 && DISPLAY->SupportsTextureFormat(RageDisplay::FMT_PAL) )
	{
		RageSurface *dst = RageSurfaceUtils::PalettizeToGrayscale( img, actualID.iGrayscaleBits, actualID.iAlphaBits );

		delete img;
		img = dst;
	}

	/* Figure out which texture format to use. */
	// if the source is palleted, load palleted no matter what the prefs
	if(img->format->BitsPerPixel == 8 && DISPLAY->SupportsTextureFormat(RageDisplay::FMT_PAL))
	{
		pixfmt = RageDisplay::FMT_PAL;
	}
	else
	{
		// not paletted
		switch( actualID.iColorDepth )
		{
		case 16:
			{
				/* Bits of alpha in the source: */
				int src_alpha_bits = 8 - img->format->Loss[3];

				/* Don't use more than we were hinted to. */
				src_alpha_bits = min( actualID.iAlphaBits, src_alpha_bits );

				switch( src_alpha_bits ) {
				case 0:
				case 1:
					pixfmt = RageDisplay::FMT_RGB5A1;
					break;
				default:	
					pixfmt = RageDisplay::FMT_RGBA4;
					break;
				}
			}
			break;
		case 32:
			pixfmt = RageDisplay::FMT_RGBA8;
			break;
		default:
			RageException::Throw( "Invalid color depth: %d bits", actualID.iColorDepth );
		}
	}

	/* 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;
	}

	/* 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);
		RageSurface *dst = CreateSurface( img->w, img->h, pfd->bpp,
			pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );

		RageSurfaceUtils::ErrorDiffusionDither( img, dst );
		delete img;
		img = dst;
	}

	/* 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. */
	RageSurfaceUtils::FixHiddenAlpha(img);

	/* Convert the data to the destination format and dimensions 
	 * required by OpenGL if it's not in it already.  */
	/* We no longer need to do this; pixfmt and the format of img no longer have
	 * to match.  This means that if we have a paletted image, but the hardware
	 * doesn't support it, we can leave the data in the smaller paletted format
	 * and let OpenGL dereference it, as long as nothing else (such as dithering)
	 * ends up depalettizing it first.  We only have to scale it up to the texture
	 * size, which we won't have to do either if the image size is already a power
	 * of two. */
	// const RageDisplay::PixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt);
	RageSurfaceUtils::ConvertSurface( img, m_iTextureWidth, m_iTextureHeight,
		img->fmt.BitsPerPixel, img->fmt.Mask[0], img->fmt.Mask[1], img->fmt.Mask[2], img->fmt.Mask[3] );
	//	pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
	
	m_uTexHandle = DISPLAY->CreateTexture( pixfmt, img, actualID.bMipMaps );

	CreateFrameRects();


	//
	// Enforce frames in the image have even dimensions.  Otherwise, 
	// pixel/texel alignment will be off.
	//
	bool bRunCheck = true;
	
	 // Don't check if the artist intentionally blanked the image by making it very tiny.
	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;

	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 );
			Dialog::OK( sWarning, "FRAME_DIMENSIONS_WARNING" );
		}
	}



	delete img;

	/* See if the apparent "size" is being overridden. */
	GetResolutionFromFileName(actualID.filename, m_iSourceWidth, m_iSourceHeight);


	CString props;
	props += RageDisplay::PixelFormatToString( pixfmt ) + " ";
	if(actualID.iAlphaBits == 0) props += "opaque ";
	if(actualID.iAlphaBits == 1) props += "matte ";
	if(actualID.bStretch) props += "stretch ";
	if(actualID.bDither) props += "dither ";
	props.erase(props.size()-1);
	LOG->Trace( "RageBitmapTexture: Loaded '%s' (%ux%u); %s, source %d,%d;  image %d,%d.", 
		actualID.filename.c_str(), GetTextureWidth(), GetTextureHeight(),
		props.c_str(), m_iSourceWidth, m_iSourceHeight,
		m_iImageWidth,	m_iImageHeight);
}