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