2012-09-19 11:15:01 -04:00
//-----------------------------------------------------------------------------
// Copyright (c) 2012 GarageGames, LLC
//
// 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, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// 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. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
# include "platform/platform.h"
# include "gui/controls/guiBitmapCtrl.h"
# include "console/console.h"
# include "console/consoleTypes.h"
# include "console/engineAPI.h"
# include "gfx/gfxDevice.h"
# include "gfx/gfxDrawUtil.h"
2026-02-17 00:13:10 +00:00
# include "gfx/gfxAPI.h"
# include "gui/buttons/guiButtonBaseCtrl.h"
2012-09-19 11:15:01 -04:00
2016-05-11 17:47:05 +02:00
# include "materials/matTextureTarget.h"
2012-09-19 11:15:01 -04:00
IMPLEMENT_CONOBJECT ( GuiBitmapCtrl ) ;
2024-12-21 23:02:23 +00:00
ConsoleDocClass ( GuiBitmapCtrl ,
2012-09-19 11:15:01 -04:00
" @brief A gui control that is used to display an image. \n \n "
2024-12-21 23:02:23 +00:00
2012-09-19 11:15:01 -04:00
" The image is stretched to the constraints of the control by default. However, the control can also \n "
" tile the image as well. \n \n "
" The image itself is stored inside the GuiBitmapCtrl::bitmap field. The boolean value that decides \n "
" whether the image is stretched or tiled is stored inside the GuiBitmapCtrl::wrap field. \n "
2024-12-21 23:02:23 +00:00
2012-09-19 11:15:01 -04:00
" @tsexample \n "
" // Create a tiling GuiBitmapCtrl that displays \" myImage.png \" \n "
" %bitmapCtrl = new GuiBitmapCtrl() \n "
" { \n "
" bitmap = \" myImage.png \" ; \n "
" wrap = \" true \" ; \n "
" }; \n "
" @endtsexample \n \n "
2024-12-21 23:02:23 +00:00
2012-09-19 11:15:01 -04:00
" @ingroup GuiControls "
) ;
2026-02-17 00:13:10 +00:00
ImplementEnumType ( BitmapDrawMode ,
" Draw Mode of the bitmap control. \n \n "
" @ingroup GuiControls " )
{
GuiBitmapCtrl : : BitmapMode_Stretch , " Stretch " , " Stretches the bitmap to fill the control extents. Aspect ratio is not preserved. "
} ,
{ GuiBitmapCtrl : : BitmapMode_Tile , " Tile " , " Repeats the bitmap to fill the control extents without scaling. " } ,
{ GuiBitmapCtrl : : BitmapMode_Fit , " Fit " , " Scales the bitmap to fit entirely within the control while preserving aspect ratio. May result in letterboxing. " } ,
{ GuiBitmapCtrl : : BitmapMode_Fill , " Fill " , " Scales the bitmap to completely fill the control while preserving aspect ratio. Portions of the bitmap may be cropped. " } ,
{ GuiBitmapCtrl : : BitmapMode_Center , " Center " , " Draws the bitmap at its original size centered within the control. No scaling is applied. " } ,
EndImplementEnumType ;
2012-09-19 11:15:01 -04:00
GuiBitmapCtrl : : GuiBitmapCtrl ( void )
2024-12-21 23:02:23 +00:00
: mStartPoint ( 0 , 0 ) ,
2016-10-14 18:16:55 -05:00
mColor ( ColorI : : WHITE ) ,
2021-11-08 16:07:44 -06:00
mAngle ( 0 ) ,
2024-12-21 23:02:23 +00:00
mWrap ( false ) ,
2026-02-17 00:13:10 +00:00
mDrawMode ( BitmapMode_Stretch ) ,
mFilterType ( GFXTextureFilterLinear ) ,
2024-12-21 23:02:23 +00:00
mBitmap ( NULL ) ,
mBitmapName ( StringTable - > EmptyString ( ) )
2012-09-19 11:15:01 -04:00
{
}
void GuiBitmapCtrl : : initPersistFields ( )
{
2023-01-27 01:13:15 -06:00
docsURL ;
2024-12-21 23:02:23 +00:00
addGroup ( " Bitmap " ) ;
2021-07-19 01:07:08 -05:00
2025-03-30 11:22:42 +01:00
INITPERSISTFIELD_IMAGEASSET ( Bitmap , GuiBitmapCtrl , " The bitmap to render in this BitmapCtrl. " )
2024-01-07 23:09:41 -06:00
2026-02-17 00:23:53 +00:00
addField ( " color " , TypeColorI , Offset ( mColor , GuiBitmapCtrl ) , " color mul " ) ;
2025-03-24 20:07:06 +00:00
addField ( " wrap " , TypeBool , Offset ( mWrap , GuiBitmapCtrl ) , " If true, the bitmap is tiled inside the control rather than stretched to fit. " ) ;
2026-02-17 00:13:10 +00:00
addField ( " drawMode " , TYPEID < BitmapMode > ( ) , Offset ( mDrawMode , GuiBitmapCtrl ) , " Sets the mode to draw this bitmap in this control (wrap forces Tile mode). " ) ;
addField ( " filterType " , TYPEID < GFXTextureFilterType > ( ) , Offset ( mFilterType , GuiBitmapCtrl ) , " Sets the bitmap texture filter mode. " ) ;
2025-03-24 20:07:06 +00:00
addFieldV ( " angle " , TypeRangedF32 , Offset ( mAngle , GuiBitmapCtrl ) , & CommonValidators : : DegreeRange , " rotation " ) ;
2026-02-17 00:13:10 +00:00
endGroup ( " Bitmap " ) ;
2012-09-19 11:15:01 -04:00
Parent : : initPersistFields ( ) ;
}
bool GuiBitmapCtrl : : onWake ( )
{
2024-12-21 23:02:23 +00:00
if ( ! Parent : : onWake ( ) )
2012-09-19 11:15:01 -04:00
return false ;
setActive ( true ) ;
2021-07-19 01:07:08 -05:00
2012-09-19 11:15:01 -04:00
return true ;
}
void GuiBitmapCtrl : : onSleep ( )
{
2026-02-17 00:13:10 +00:00
mBitmap = NULL ;
2012-09-19 11:15:01 -04:00
Parent : : onSleep ( ) ;
}
//-------------------------------------
void GuiBitmapCtrl : : inspectPostApply ( )
{
// if the extent is set to (0,0) in the gui editor and appy hit, this control will
// set it's extent to be exactly the size of the bitmap (if present)
Parent : : inspectPostApply ( ) ;
2025-05-27 17:07:08 -05:00
if ( ! mWrap & & ( getExtent ( ) . x = = 0 ) & & ( getExtent ( ) . y = = 0 ) & & getBitmap ( ) )
2012-09-19 11:15:01 -04:00
{
2024-12-21 23:02:23 +00:00
setExtent ( mBitmap - > getWidth ( ) , mBitmap - > getHeight ( ) ) ;
2012-09-19 11:15:01 -04:00
}
}
2024-12-21 23:02:23 +00:00
void GuiBitmapCtrl : : setBitmap ( const char * name , bool resize )
2012-09-19 11:15:01 -04:00
{
2024-12-21 23:02:23 +00:00
// coming in here we are probably getting a filename.
if ( AssetDatabase . isDeclaredAsset ( name ) )
{
_setBitmap ( StringTable - > insert ( name ) ) ;
}
else
{
StringTableEntry assetId = ImageAsset : : getAssetIdByFilename ( StringTable - > insert ( name ) ) ;
if ( assetId ! = StringTable - > EmptyString ( ) )
_setBitmap ( assetId ) ;
else
2025-06-19 16:58:44 +01:00
_setBitmap ( StringTable - > EmptyString ( ) ) ;
2024-12-21 23:02:23 +00:00
}
2012-09-19 11:15:01 -04:00
2025-06-19 16:58:44 +01:00
if ( mBitmapAsset . notNull ( ) )
2021-07-19 01:07:08 -05:00
{
2025-06-19 16:58:44 +01:00
mBitmap = mBitmapAsset - > getTexture ( & GFXDefaultGUIProfile ) ;
if ( getBitmap ( ) & & resize )
{
setExtent ( mBitmap - > getWidth ( ) , mBitmap - > getHeight ( ) ) ;
updateSizing ( ) ;
}
2012-09-19 11:15:01 -04:00
}
setUpdate ( ) ;
}
void GuiBitmapCtrl : : setBitmapHandle ( GFXTexHandle handle , bool resize )
{
2021-07-19 01:07:08 -05:00
mBitmap = handle ;
2012-09-19 11:15:01 -04:00
2022-01-21 19:47:41 -05:00
mBitmapName = StringTable - > insert ( " texhandle " ) ;
2012-09-19 11:15:01 -04:00
// Resize the control to fit the bitmap
2024-12-21 23:02:23 +00:00
if ( resize )
2012-09-19 11:15:01 -04:00
{
2021-07-19 01:07:08 -05:00
setExtent ( mBitmap - > getWidth ( ) , mBitmap - > getHeight ( ) ) ;
2012-09-19 11:15:01 -04:00
updateSizing ( ) ;
}
}
2024-12-21 23:02:23 +00:00
void GuiBitmapCtrl : : updateSizing ( )
2012-09-19 11:15:01 -04:00
{
2024-12-21 23:02:23 +00:00
if ( ! getParent ( ) )
return ;
// updates our bounds according to our horizSizing and verSizing rules
RectI fakeBounds ( getPosition ( ) , getParent ( ) - > getExtent ( ) ) ;
parentResized ( fakeBounds , fakeBounds ) ;
}
void GuiBitmapCtrl : : onRender ( Point2I offset , const RectI & updateRect )
{
if ( mBitmap . isNull ( ) & & mBitmapAsset . notNull ( ) )
mBitmap = getBitmap ( ) ;
2021-07-19 01:07:08 -05:00
if ( mBitmap )
2012-09-19 11:15:01 -04:00
{
GFX - > getDrawUtil ( ) - > clearBitmapModulation ( ) ;
2016-07-06 02:15:46 -05:00
GFX - > getDrawUtil ( ) - > setBitmapModulation ( mColor ) ;
2026-02-17 00:13:10 +00:00
if ( dynamic_cast < GuiButtonBaseCtrl * > ( getParent ( ) ) )
{
GuiButtonBaseCtrl * parent = static_cast < GuiButtonBaseCtrl * > ( getParent ( ) ) ;
GFX - > getDrawUtil ( ) - > setBitmapModulation ( mProfile - > mFillColor ) ;
if ( parent - > isHighlighted ( ) )
GFX - > getDrawUtil ( ) - > setBitmapModulation ( mProfile - > mFillColorHL ) ;
if ( parent - > isDepressed ( ) | | parent - > getStateOn ( ) )
GFX - > getDrawUtil ( ) - > setBitmapModulation ( mProfile - > mFillColorSEL ) ;
if ( ! parent - > isActive ( ) )
GFX - > getDrawUtil ( ) - > setBitmapModulation ( mProfile - > mFillColorNA ) ;
}
if ( mWrap & & mDrawMode ! = BitmapMode_Tile )
mDrawMode = BitmapMode_Tile ;
RectI ctrlRect ( offset , getExtent ( ) ) ;
GFXTextureObject * texture = mBitmap ;
F32 texW = ( F32 ) texture - > getWidth ( ) ;
F32 texH = ( F32 ) texture - > getHeight ( ) ;
F32 ctrlW = ( F32 ) getExtent ( ) . x ;
F32 ctrlH = ( F32 ) getExtent ( ) . y ;
switch ( mDrawMode )
{
case BitmapMode_Tile :
2024-12-21 23:02:23 +00:00
{
2026-02-17 00:13:10 +00:00
// How many repetitions needed (+1 ensures full coverage)
const S32 xCount = ( ctrlW / texW ) + 1 ;
const S32 yCount = ( ctrlH / texH ) + 1 ;
// Scroll offset (wrap-safe)
const S32 xShift = mStartPoint . x % ( S32 ) texW ;
const S32 yShift = mStartPoint . y % ( S32 ) texH ;
RectI srcRegion ( 0 , 0 , ( S32 ) texW , ( S32 ) texH ) ;
2024-12-21 23:02:23 +00:00
RectI dstRegion ;
2026-02-17 00:13:10 +00:00
for ( S32 y = 0 ; y < yCount ; + + y )
{
for ( S32 x = 0 ; x < xCount ; + + x )
2024-12-21 23:02:23 +00:00
{
2026-02-17 00:13:10 +00:00
dstRegion . set (
offset . x + ( x * texW ) - xShift ,
offset . y + ( y * texH ) - yShift ,
texW ,
texH
) ;
GFX - > getDrawUtil ( ) - > drawBitmapStretchSR ( texture , dstRegion , srcRegion , GFXBitmapFlip_None , mFilterType , true , mAngle ) ;
2024-12-21 23:02:23 +00:00
}
2026-02-17 00:13:10 +00:00
}
2012-09-19 11:15:01 -04:00
2026-02-17 00:13:10 +00:00
break ;
2024-12-21 23:02:23 +00:00
}
2026-02-17 00:13:10 +00:00
case GuiBitmapCtrl : : BitmapMode_Fit :
{
F32 scale = getMin ( ctrlW / texW , ctrlH / texH ) ;
S32 drawW = ( S32 ) ( texW * scale ) ;
S32 drawH = ( S32 ) ( texH * scale ) ;
S32 x = offset . x + ( ctrlW - drawW ) * 0.5f ;
S32 y = offset . y + ( ctrlH - drawH ) * 0.5f ;
RectI dst ( x , y , drawW , drawH ) ;
GFX - > getDrawUtil ( ) - > drawBitmapStretch ( texture , dst , GFXBitmapFlip_None , mFilterType , false , mAngle ) ;
break ;
}
case GuiBitmapCtrl : : BitmapMode_Fill :
{
F32 scale = getMax ( ctrlW / texW , ctrlH / texH ) ;
S32 drawW = ( S32 ) ( texW * scale ) ;
S32 drawH = ( S32 ) ( texH * scale ) ;
S32 x = offset . x + ( ctrlW - drawW ) * 0.5f ;
S32 y = offset . y + ( ctrlH - drawH ) * 0.5f ;
RectI dst ( x , y , drawW , drawH ) ;
GFX - > getDrawUtil ( ) - > drawBitmapStretch ( texture , dst , GFXBitmapFlip_None , mFilterType , false , mAngle ) ;
break ;
}
case GuiBitmapCtrl : : BitmapMode_Center :
{
S32 x = offset . x + ( ctrlW - texW ) * 0.5f ;
S32 y = offset . y + ( ctrlH - texH ) * 0.5f ;
RectI dst ( x , y , texW , texH ) ;
GFX - > getDrawUtil ( ) - > drawBitmapStretch ( texture , dst , GFXBitmapFlip_None , mFilterType , false , mAngle ) ;
break ;
}
default :
2012-09-19 11:15:01 -04:00
{
2026-02-17 00:13:10 +00:00
GFX - > getDrawUtil ( ) - > drawBitmapStretch ( texture , ctrlRect , GFXBitmapFlip_None , mFilterType , false , mAngle ) ;
break ;
}
2012-09-19 11:15:01 -04:00
}
}
2021-07-19 01:07:08 -05:00
if ( mProfile - > mBorder | | ! mBitmap )
2012-09-19 11:15:01 -04:00
{
RectI rect ( offset . x , offset . y , getExtent ( ) . x , getExtent ( ) . y ) ;
GFX - > getDrawUtil ( ) - > drawRect ( rect , mProfile - > mBorderColor ) ;
}
renderChildControls ( offset , updateRect ) ;
}
void GuiBitmapCtrl : : setValue ( S32 x , S32 y )
{
2025-05-27 17:07:08 -05:00
if ( getBitmap ( ) )
2012-09-19 11:15:01 -04:00
{
2024-12-21 23:02:23 +00:00
x + = mBitmapAsset - > getTextureBitmapWidth ( ) / 2 ;
y + = mBitmapAsset - > getTextureBitmapHeight ( ) / 2 ;
}
while ( x < 0 )
x + = 256 ;
mStartPoint . x = x % 256 ;
while ( y < 0 )
y + = 256 ;
mStartPoint . y = y % 256 ;
2012-09-19 11:15:01 -04:00
}
2024-12-21 23:02:23 +00:00
DefineEngineMethod ( GuiBitmapCtrl , setValue , void , ( S32 x , S32 y ) , ,
2012-09-19 11:15:01 -04:00
" Set the offset of the bitmap within the control. \n "
" @param x The x-axis offset of the image. \n "
" @param y The y-axis offset of the image. \n " )
{
object - > setValue ( x , y ) ;
}
//
static ConsoleDocFragment _sGuiBitmapCtrlSetBitmap1 (
" @brief Assign an image to the control. \n \n "
" Child controls with resize according to their layout settings. \n "
" @param filename The filename of the image. \n "
" @param resize Optional parameter. If true, the GUI will resize to fit the image. " ,
" GuiBitmapCtrl " , // The class to place the method in; use NULL for functions.
2024-12-21 23:02:23 +00:00
" void setBitmap( String filename, bool resize ); " ) ; // The definition string.
2012-09-19 11:15:01 -04:00
static ConsoleDocFragment _sGuiBitmapCtrlSetBitmap2 (
" @brief Assign an image to the control. \n \n "
" Child controls will resize according to their layout settings. \n "
" @param filename The filename of the image. \n "
" @param resize A boolean value that decides whether the ctrl refreshes or not. " ,
" GuiBitmapCtrl " , // The class to place the method in; use NULL for functions.
2024-12-21 23:02:23 +00:00
" void setBitmap( String filename ); " ) ; // The definition string.
2012-09-19 11:15:01 -04:00
//"Set the bitmap displayed in the control. Note that it is limited in size, to 256x256."
2024-12-21 23:02:23 +00:00
DefineEngineMethod ( GuiBitmapCtrl , setBitmap , void , ( const char * fileRoot , bool resize ) , ( false ) ,
2012-09-19 11:15:01 -04:00
" ( String filename | String filename, bool resize ) Assign an image to the control. \n \n "
2024-12-21 23:02:23 +00:00
" @hide " )
2012-09-19 11:15:01 -04:00
{
char filename [ 1024 ] ;
2014-11-03 22:42:51 -05:00
Con : : expandScriptFilename ( filename , sizeof ( filename ) , fileRoot ) ;
2024-12-21 23:02:23 +00:00
object - > setBitmap ( filename , resize ) ;
2012-09-19 11:15:01 -04:00
}
2016-05-11 17:47:05 +02:00
2024-12-21 23:02:23 +00:00
DefineEngineMethod ( GuiBitmapCtrl , getBitmap , const char * , ( ) , ,
2021-07-19 01:07:08 -05:00
" Gets the current bitmap set for this control. \n \n "
" @hide " )
{
2024-12-21 23:02:23 +00:00
return object - > _getBitmap ( ) ;
2021-07-19 01:07:08 -05:00
}
2024-12-21 23:02:23 +00:00
DefineEngineMethod ( GuiBitmapCtrl , setNamedTexture , bool , ( String namedtexture ) , ,
2016-05-11 17:47:05 +02:00
" @brief Set a texture as the image. \n \n "
" @param namedtexture The name of the texture (NamedTexTarget). \n "
2024-12-21 23:02:23 +00:00
" @return true if the texture exists. " )
2016-05-11 17:47:05 +02:00
{
GFXTexHandle theTex ;
2024-12-21 23:02:23 +00:00
NamedTexTarget * namedTarget = NULL ;
2016-05-11 17:47:05 +02:00
namedTarget = NamedTexTarget : : find ( namedtexture . c_str ( ) ) ;
2024-12-21 23:02:23 +00:00
if ( namedTarget )
2016-05-11 17:47:05 +02:00
{
2024-12-21 23:02:23 +00:00
theTex = namedTarget - > getTexture ( 0 ) ;
2016-05-11 17:47:05 +02:00
}
2024-12-21 23:02:23 +00:00
if ( theTex . isValid ( ) )
2016-05-11 17:47:05 +02:00
{
2024-12-21 23:02:23 +00:00
object - > setBitmapHandle ( theTex , false ) ;
2016-05-11 17:47:05 +02:00
return true ; //a new texture was set correctly
}
return false ; //we couldn't change the texture
}