/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see .
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "precompiled.h"
#include "../renderer/tr_local.h"
idCVar swf_timescale( "swf_timescale", "1", CVAR_FLOAT, "timescale for swf files" );
idCVar swf_stopat( "swf_stopat", "0", CVAR_FLOAT, "stop at a specific frame" );
idCVar swf_titleSafe( "swf_titleSafe", "0.005", CVAR_FLOAT, "space between UI elements and screen edge", 0.0f, 0.075f );
idCVar swf_forceAlpha( "swf_forceAlpha", "0", CVAR_FLOAT, "force an alpha value on all elements, useful to show invisible animating elements", 0.0f, 1.0f );
extern idCVar swf_textStrokeSize;
extern idCVar swf_textStrokeSizeGlyphSpacer;
extern idCVar in_useJoystick;
#define ALPHA_EPSILON 0.001f
#define STENCIL_DECR -1
#define STENCIL_INCR -2
/*
========================
idSWF::DrawStretchPic
========================
*/
void idSWF::DrawStretchPic( float x, float y, float w, float h, float s1, float t1, float s2, float t2, const idMaterial* material )
{
renderSystem->DrawStretchPic( x * scaleToVirtual.x, y * scaleToVirtual.y, w * scaleToVirtual.x, h * scaleToVirtual.y, s1, t1, s2, t2, material );
}
/*
========================
idSWF::DrawStretchPic
========================
*/
void idSWF::DrawStretchPic( const idVec4& topLeft, const idVec4& topRight, const idVec4& bottomRight, const idVec4& bottomLeft, const idMaterial* material )
{
renderSystem->DrawStretchPic(
idVec4( topLeft.x * scaleToVirtual.x, topLeft.y * scaleToVirtual.y, topLeft.z, topLeft.w ),
idVec4( topRight.x * scaleToVirtual.x, topRight.y * scaleToVirtual.y, topRight.z, topRight.w ),
idVec4( bottomRight.x * scaleToVirtual.x, bottomRight.y * scaleToVirtual.y, bottomRight.z, bottomRight.w ),
idVec4( bottomLeft.x * scaleToVirtual.x, bottomLeft.y * scaleToVirtual.y, bottomLeft.z, bottomLeft.w ),
material );
}
/*
========================
idSWF::Render
========================
*/
void idSWF::Render( idRenderSystem* gui, int time, bool isSplitscreen )
{
if( !IsLoaded() )
{
return;
}
if( !IsActive() )
{
return;
}
if( swf_stopat.GetInteger() > 0 )
{
if( mainspriteInstance->currentFrame == swf_stopat.GetInteger() )
{
swf_timescale.SetFloat( 0.0f );
}
}
int currentTime = Sys_Milliseconds();
int framesToRun = 0;
if( paused )
{
lastRenderTime = currentTime;
}
if( swf_timescale.GetFloat() > 0.0f )
{
if( lastRenderTime == 0 )
{
lastRenderTime = currentTime;
framesToRun = 1;
}
else
{
float deltaTime = ( currentTime - lastRenderTime );
float fr = ( ( float )frameRate / 256.0f ) * swf_timescale.GetFloat();
framesToRun = idMath::Ftoi( ( fr * deltaTime ) / 1000.0f );
lastRenderTime += ( framesToRun * ( 1000.0f / fr ) );
if( framesToRun > 10 )
{
framesToRun = 10;
}
}
for( int i = 0; i < framesToRun; i++ )
{
mainspriteInstance->Run();
mainspriteInstance->RunActions();
}
}
const float pixelAspect = renderSystem->GetPixelAspect();
const float sysWidth = renderSystem->GetWidth() * ( pixelAspect > 1.0f ? pixelAspect : 1.0f );
const float sysHeight = renderSystem->GetHeight() / ( pixelAspect < 1.0f ? pixelAspect : 1.0f );
float scale = swfScale * sysHeight / ( float )frameHeight;
swfRenderState_t renderState;
renderState.stereoDepth = ( stereoDepthType_t )mainspriteInstance->GetStereoDepth();
renderState.matrix.xx = scale;
renderState.matrix.yy = scale;
renderState.matrix.tx = 0.5f * ( sysWidth - ( frameWidth * scale ) );
renderState.matrix.ty = 0.5f * ( sysHeight - ( frameHeight * scale ) );
renderBorder = renderState.matrix.tx / scale;
scaleToVirtual.Set( ( float )renderSystem->GetVirtualWidth() / sysWidth, ( float )renderSystem->GetVirtualHeight() / sysHeight );
RenderSprite( gui, mainspriteInstance, renderState, time, isSplitscreen );
if( blackbars )
{
float barWidth = renderState.matrix.tx + 0.5f;
float barHeight = renderState.matrix.ty + 0.5f;
if( barWidth > 0.0f )
{
gui->SetColor( idVec4( 0.0f, 0.0f, 0.0f, 1.0f ) );
DrawStretchPic( 0.0f, 0.0f, barWidth, sysHeight, 0, 0, 1, 1, white );
DrawStretchPic( sysWidth - barWidth, 0.0f, barWidth, sysHeight, 0, 0, 1, 1, white );
}
if( barHeight > 0.0f )
{
gui->SetColor( idVec4( 0.0f, 0.0f, 0.0f, 1.0f ) );
DrawStretchPic( 0.0f, 0.0f, sysWidth, barHeight, 0, 0, 1, 1, white );
DrawStretchPic( 0.0f, sysHeight - barHeight, sysWidth, barHeight, 0, 0, 1, 1, white );
}
}
if( isMouseInClientArea && ( mouseEnabled && useMouse ) && ( InhibitControl() || ( !InhibitControl() && !useInhibtControl ) ) )
{
gui->SetGLState( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
gui->SetColor( idVec4( 1.0f, 1.0f, 1.0f, 1.0f ) );
idVec2 mouse = renderState.matrix.Transform( idVec2( mouseX - 1, mouseY - 2 ) );
//idSWFScriptObject * hitObject = HitTest( mainspriteInstance, swfRenderState_t(), mouseX, mouseY, NULL );
if( !hasHitObject ) //hitObject == NULL ) {
{
DrawStretchPic( mouse.x, mouse.y, 32.0f, 32.0f, 0, 0, 1, 1, guiCursor_arrow );
}
else
{
DrawStretchPic( mouse.x, mouse.y, 32.0f, 32.0f, 0, 0, 1, 1, guiCursor_hand );
}
}
// restore the GL State
gui->SetGLState( 0 );
}
/*
========================
idSWF::RenderMask
========================
*/
void idSWF::RenderMask( idRenderSystem* gui, const swfDisplayEntry_t* mask, const swfRenderState_t& renderState, const int stencilMode )
{
swfRenderState_t renderState2;
renderState2.stereoDepth = renderState.stereoDepth;
renderState2.matrix = mask->matrix.Multiply( renderState.matrix );
renderState2.cxf = mask->cxf.Multiply( renderState.cxf );
renderState2.ratio = mask->ratio;
renderState2.material = guiSolid;
renderState2.activeMasks = stencilMode;
idSWFDictionaryEntry& entry = dictionary[ mask->characterID ];
if( entry.type == SWF_DICT_SHAPE )
{
RenderShape( gui, entry.shape, renderState2 );
}
else if( entry.type == SWF_DICT_MORPH )
{
RenderMorphShape( gui, entry.shape, renderState2 );
}
}
/*
========================
idSWF::RenderSprite
========================
*/
void idSWF::RenderSprite( idRenderSystem* gui, idSWFSpriteInstance* spriteInstance, const swfRenderState_t& renderState, int time, bool isSplitscreen )
{
if( spriteInstance == NULL )
{
idLib::Warning( "%s: RenderSprite: spriteInstance == NULL", filename.c_str() );
return;
}
if( !spriteInstance->isVisible )
{
return;
}
if( ( ( renderState.cxf.mul.w + renderState.cxf.add.w ) <= ALPHA_EPSILON ) && ( swf_forceAlpha.GetFloat() <= 0.0f ) )
{
return;
}
idStaticList activeMasks;
for( int i = 0; i < spriteInstance->displayList.Num(); i++ )
{
const swfDisplayEntry_t& display = spriteInstance->displayList[i];
for( int j = 0; j < activeMasks.Num(); j++ )
{
const swfDisplayEntry_t* mask = activeMasks[ j ];
if( display.depth > mask->clipDepth )
{
RenderMask( gui, mask, renderState, STENCIL_DECR );
activeMasks.RemoveIndexFast( j );
}
}
if( display.clipDepth > 0 )
{
activeMasks.Append( &display );
RenderMask( gui, &display, renderState, STENCIL_INCR );
continue;
}
idSWFDictionaryEntry* entry = FindDictionaryEntry( display.characterID );
if( entry == NULL )
{
continue;
}
swfRenderState_t renderState2;
if( spriteInstance->stereoDepth != STEREO_DEPTH_TYPE_NONE )
{
renderState2.stereoDepth = ( stereoDepthType_t )spriteInstance->stereoDepth;
}
else if( renderState.stereoDepth != STEREO_DEPTH_TYPE_NONE )
{
renderState2.stereoDepth = renderState.stereoDepth;
}
renderState2.matrix = display.matrix.Multiply( renderState.matrix );
renderState2.cxf = display.cxf.Multiply( renderState.cxf );
renderState2.ratio = display.ratio;
if( display.blendMode != 0 )
{
renderState2.blendMode = display.blendMode;
}
else
{
renderState2.blendMode = renderState.blendMode;
}
renderState2.activeMasks = renderState.activeMasks + activeMasks.Num();
if( spriteInstance->materialOverride != NULL )
{
renderState2.material = spriteInstance->materialOverride;
renderState2.materialWidth = spriteInstance->materialWidth;
renderState2.materialHeight = spriteInstance->materialHeight;
}
else
{
renderState2.material = renderState.material;
renderState2.materialWidth = renderState.materialWidth;
renderState2.materialHeight = renderState.materialHeight;
}
float xOffset = 0.0f;
float yOffset = 0.0f;
if( entry->type == SWF_DICT_SPRITE )
{
display.spriteInstance->SetAlignment( spriteInstance->xOffset, spriteInstance->yOffset );
if( display.spriteInstance->name[0] == '_' )
{
//if ( display.spriteInstance->name.Icmp( "_leftAlign" ) == 0 ) {
// float adj = (float)frameWidth * 0.10;
// renderState2.matrix.tx = ( display.matrix.tx - adj ) * renderState.matrix.xx;
//}
//if ( display.spriteInstance->name.Icmp( "_rightAlign" ) == 0 ) {
// renderState2.matrix.tx = ( (float)renderSystem->GetWidth() - ( ( (float)frameWidth - display.matrix.tx - adj ) * renderState.matrix.xx ) );
//}
float widthAdj = swf_titleSafe.GetFloat() * frameWidth;
float heightAdj = swf_titleSafe.GetFloat() * frameHeight;
const float pixelAspect = renderSystem->GetPixelAspect();
const float sysWidth = renderSystem->GetWidth() * ( pixelAspect > 1.0f ? pixelAspect : 1.0f );
const float sysHeight = renderSystem->GetHeight() / ( pixelAspect < 1.0f ? pixelAspect : 1.0f );
if( display.spriteInstance->name.Icmp( "_fullScreen" ) == 0 )
{
renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
float xScale = sysWidth / ( float )frameWidth;
float yScale = sysHeight / ( float )frameHeight;
renderState2.matrix.xx = xScale;
renderState2.matrix.yy = yScale;
}
if( display.spriteInstance->name.Icmp( "_absTop" ) == 0 )
{
renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_top" ) == 0 )
{
renderState2.matrix.ty = ( display.matrix.ty + heightAdj ) * renderState.matrix.yy;
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_topLeft" ) == 0 )
{
renderState2.matrix.tx = ( display.matrix.tx + widthAdj ) * renderState.matrix.xx;
renderState2.matrix.ty = ( display.matrix.ty + heightAdj ) * renderState.matrix.yy;
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_left" ) == 0 )
{
float prevX = renderState2.matrix.tx;
renderState2.matrix.tx = ( display.matrix.tx + widthAdj ) * renderState.matrix.xx;
xOffset = ( ( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( idStr::FindText( display.spriteInstance->name, "_absLeft", false ) >= 0 )
{
float prevX = renderState2.matrix.tx;
renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
xOffset = ( ( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_bottomLeft" ) == 0 )
{
float prevX = renderState2.matrix.tx;
renderState2.matrix.tx = ( display.matrix.tx + widthAdj ) * renderState.matrix.xx;
xOffset = ( ( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
float prevY = renderState2.matrix.ty;
renderState2.matrix.ty = ( ( float )sysHeight - ( ( ( float )frameHeight - display.matrix.ty + heightAdj ) * renderState.matrix.yy ) );
yOffset = ( ( renderState2.matrix.ty - prevY ) / renderState.matrix.yy );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_absBottom" ) == 0 )
{
renderState2.matrix.ty = ( ( float )sysHeight - ( ( ( float )frameHeight - display.matrix.ty ) * renderState.matrix.yy ) );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_bottom" ) == 0 )
{
renderState2.matrix.ty = ( ( float )sysHeight - ( ( ( float )frameHeight - display.matrix.ty + heightAdj ) * renderState.matrix.yy ) );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_topRight" ) == 0 )
{
renderState2.matrix.tx = ( ( float )sysWidth - ( ( ( float )frameWidth - display.matrix.tx + widthAdj ) * renderState.matrix.xx ) );
renderState2.matrix.ty = ( display.matrix.ty + heightAdj ) * renderState.matrix.yy;
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_right" ) == 0 )
{
float prevX = renderState2.matrix.tx;
renderState2.matrix.tx = ( ( float )sysWidth - ( ( ( float )frameWidth - display.matrix.tx + widthAdj ) * renderState.matrix.xx ) );
xOffset = ( ( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( idStr::FindText( display.spriteInstance->name, "_absRight", true ) >= 0 )
{
float prevX = renderState2.matrix.tx;
renderState2.matrix.tx = ( ( float )sysWidth - ( ( ( float )frameWidth - display.matrix.tx ) * renderState.matrix.xx ) );
xOffset = ( ( renderState2.matrix.tx - prevX ) / renderState.matrix.xx );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_bottomRight" ) == 0 )
{
renderState2.matrix.tx = ( ( float )sysWidth - ( ( ( float )frameWidth - display.matrix.tx + widthAdj ) * renderState.matrix.xx ) );
renderState2.matrix.ty = ( ( float )sysHeight - ( ( ( float )frameHeight - display.matrix.ty + heightAdj ) * renderState.matrix.yy ) );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_absTopLeft" ) == 0 ) // ABSOLUTE CORNERS OF SCREEN
{
renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_absTopRight" ) == 0 )
{
renderState2.matrix.tx = ( ( float )sysWidth - ( ( ( float )frameWidth - display.matrix.tx ) * renderState.matrix.xx ) );
renderState2.matrix.ty = display.matrix.ty * renderState.matrix.yy;
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_absBottomLeft" ) == 0 )
{
renderState2.matrix.tx = display.matrix.tx * renderState.matrix.xx;
renderState2.matrix.ty = ( ( float )sysHeight - ( ( ( float )frameHeight - display.matrix.ty ) * renderState.matrix.yy ) );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
else if( display.spriteInstance->name.Icmp( "_absBottomRight" ) == 0 )
{
renderState2.matrix.tx = ( ( float )sysWidth - ( ( ( float )frameWidth - display.matrix.tx ) * renderState.matrix.xx ) );
renderState2.matrix.ty = ( ( float )sysHeight - ( ( ( float )frameHeight - display.matrix.ty ) * renderState.matrix.yy ) );
display.spriteInstance->SetAlignment( spriteInstance->xOffset + xOffset, spriteInstance->yOffset + yOffset );
}
}
RenderSprite( gui, display.spriteInstance, renderState2, time, isSplitscreen );
}
else if( entry->type == SWF_DICT_SHAPE )
{
RenderShape( gui, entry->shape, renderState2 );
}
else if( entry->type == SWF_DICT_MORPH )
{
RenderMorphShape( gui, entry->shape, renderState2 );
}
else if( entry->type == SWF_DICT_EDITTEXT )
{
RenderEditText( gui, display.textInstance, renderState2, time, isSplitscreen );
}
else
{
//idLib::Warning( "%s: Tried to render an unrenderable character %d", filename.c_str(), entry->type );
}
}
for( int j = 0; j < activeMasks.Num(); j++ )
{
const swfDisplayEntry_t* mask = activeMasks[ j ];
RenderMask( gui, mask, renderState, STENCIL_DECR );
}
}
/*
========================
idSWF::GLStateForBlendMode
========================
*/
uint64 idSWF::GLStateForRenderState( const swfRenderState_t& renderState )
{
uint64 extraGLState = GLS_OVERRIDE | GLS_DEPTHFUNC_LESS | GLS_DEPTHMASK; // SWF GL State always overrides what's set in the material
if( renderState.activeMasks > 0 )
{
extraGLState |= GLS_STENCIL_FUNC_EQUAL | GLS_STENCIL_MAKE_REF( 128 + renderState.activeMasks ) | GLS_STENCIL_MAKE_MASK( 255 );
}
else if( renderState.activeMasks == STENCIL_INCR )
{
return GLS_COLORMASK | GLS_ALPHAMASK | GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_KEEP | GLS_STENCIL_OP_PASS_INCR;
}
else if( renderState.activeMasks == STENCIL_DECR )
{
return GLS_COLORMASK | GLS_ALPHAMASK | GLS_STENCIL_OP_FAIL_KEEP | GLS_STENCIL_OP_ZFAIL_KEEP | GLS_STENCIL_OP_PASS_DECR;
}
switch( renderState.blendMode )
{
case 7: // difference : dst = abs( dst - src )
case 9: // subtract : dst = dst - src
return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_BLENDOP_SUB );
case 8: // add : dst = dst + src
return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE );
case 6: // darken : dst = min( dst, src )
return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_BLENDOP_MIN );
case 5: // lighten : dst = max( dst, src )
return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_BLENDOP_MAX );
case 4: // screen : dst = dst + src - dst*src ( we only do dst - dst * src, we could do the extra + src with another pass if we need to)
return extraGLState | ( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_BLENDOP_SUB );
case 14: // hardlight : src < 0.5 ? multiply : screen
case 13: // overlay : dst < 0.5 ? multiply : screen
case 3: // multiply : dst = ( dst * src ) + ( dst * (1-src.a) )
return extraGLState | ( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
case 12: // erase
case 11: // alpha
case 10: // invert
case 2: // layer
case 1: // normal
case 0: // normaler
default:
return extraGLState | ( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA );
}
}
/*
========================
idSWF::RenderMorphShape
========================
*/
void idSWF::RenderMorphShape( idRenderSystem* gui, const idSWFShape* shape, const swfRenderState_t& renderState )
{
if( shape == NULL )
{
idLib::Warning( "%s: RenderMorphShape: shape == NULL", filename.c_str() );
return;
}
for( int i = 0; i < shape->fillDraws.Num(); i++ )
{
const idSWFShapeDrawFill& fill = shape->fillDraws[i];
const idMaterial* material = NULL;
swfColorXform_t color;
if( renderState.material != NULL )
{
material = renderState.material;
}
else if( fill.style.type == 0 )
{
material = guiSolid;
idVec4 startColor = fill.style.startColor.ToVec4();
idVec4 endColor = fill.style.endColor.ToVec4();
color.mul = Lerp( startColor, endColor, renderState.ratio );
}
else if( fill.style.type == 4 && fill.style.bitmapID != 65535 )
{
material = dictionary[ fill.style.bitmapID ].material;
}
else
{
material = guiSolid;
}
color = color.Multiply( renderState.cxf );
if( swf_forceAlpha.GetFloat() > 0.0f )
{
color.mul.w = swf_forceAlpha.GetFloat();
color.add.w = 0.0f;
}
if( ( color.mul.w + color.add.w ) <= ALPHA_EPSILON )
{
continue;
}
uint32 packedColorM = LittleLong( PackColor( color.mul ) );
uint32 packedColorA = LittleLong( PackColor( ( color.add * 0.5f ) + idVec4( 0.5f ) ) ); // Compress from -1..1 to 0..1
swfRect_t bounds;
bounds.tl = Lerp( shape->startBounds.tl, shape->endBounds.tl, renderState.ratio );
bounds.br = Lerp( shape->startBounds.br, shape->endBounds.br, renderState.ratio );
idVec2 size( material->GetImageWidth(), material->GetImageHeight() );
if( renderState.materialWidth > 0 )
{
size.x = renderState.materialWidth;
}
if( renderState.materialHeight > 0 )
{
size.y = renderState.materialHeight;
}
idVec2 oneOverSize( 1.0f / size.x, 1.0f / size.y );
swfMatrix_t styleMatrix;
styleMatrix.xx = Lerp( fill.style.startMatrix.xx, fill.style.endMatrix.xx, renderState.ratio );
styleMatrix.yy = Lerp( fill.style.startMatrix.yy, fill.style.endMatrix.yy, renderState.ratio );
styleMatrix.xy = Lerp( fill.style.startMatrix.xy, fill.style.endMatrix.xy, renderState.ratio );
styleMatrix.yx = Lerp( fill.style.startMatrix.yx, fill.style.endMatrix.yx, renderState.ratio );
styleMatrix.tx = Lerp( fill.style.startMatrix.tx, fill.style.endMatrix.tx, renderState.ratio );
styleMatrix.ty = Lerp( fill.style.startMatrix.ty, fill.style.endMatrix.ty, renderState.ratio );
swfMatrix_t invMatrix = styleMatrix.Inverse();
gui->SetGLState( GLStateForRenderState( renderState ) );
idDrawVert* verts = gui->AllocTris( fill.startVerts.Num(), fill.indices.Ptr(), fill.indices.Num(), material, renderState.stereoDepth );
if( verts == NULL )
{
continue;
}
for( int j = 0; j < fill.startVerts.Num(); j++ )
{
idVec2 xy = Lerp( fill.startVerts[j], fill.endVerts[j], renderState.ratio );
idVec2 st;
st.x = ( ( xy.x - bounds.tl.x ) * oneOverSize.x ) * 20.0f;
st.y = ( ( xy.y - bounds.tl.y ) * oneOverSize.y ) * 20.0f;
idVec2 adjust( 0.5f * oneOverSize.x, 0.5f * oneOverSize.y );
ALIGNTYPE16 idDrawVert tempVert;
tempVert.Clear();
tempVert.xyz.ToVec2() = renderState.matrix.Transform( xy ).Scale( scaleToVirtual );
tempVert.xyz.z = 0.0f;
tempVert.SetTexCoord( invMatrix.Transform( st ) + adjust );
tempVert.SetNativeOrderColor( packedColorM );
tempVert.SetNativeOrderColor2( packedColorA );
WriteDrawVerts16( & verts[j], & tempVert, 1 );
}
}
}
/*
========================
idSWF::RenderShape
========================
*/
void idSWF::RenderShape( idRenderSystem* gui, const idSWFShape* shape, const swfRenderState_t& renderState )
{
if( shape == NULL )
{
idLib::Warning( "%s: RenderShape: shape == NULL", filename.c_str() );
return;
}
for( int i = 0; i < shape->fillDraws.Num(); i++ )
{
const idSWFShapeDrawFill& fill = shape->fillDraws[i];
const idMaterial* material = NULL;
swfColorXform_t color;
swfMatrix_t invMatrix;
idVec2 atlasScale( 0.0f, 0.0f );
idVec2 atlasBias( 0.0f, 0.0f );
bool useAtlas = false;
idVec2 size( 1.0f, 1.0f );
if( renderState.material != NULL )
{
material = renderState.material;
invMatrix.xx = invMatrix.yy = ( 1.0f / 20.0f );
}
else if( fill.style.type == 0 )
{
material = guiSolid;
color.mul = fill.style.startColor.ToVec4();
}
else if( fill.style.type == 4 && fill.style.bitmapID != 65535 )
{
// everything in a single image atlas
idSWFDictionaryEntry* entry = &dictionary[ fill.style.bitmapID ];
material = atlasMaterial;
idVec2i atlasSize( material->GetImageWidth(), material->GetImageHeight() );
for( int i = 0 ; i < 2 ; i++ )
{
size[i] = entry->imageSize[i];
atlasScale[i] = ( float )size[i] / atlasSize[i];
atlasBias[i] = ( float )entry->imageAtlasOffset[i] / atlasSize[i];
}
// de-normalize color channels after DXT decompression
color.mul = entry->channelScale;
useAtlas = true;
const swfMatrix_t& styleMatrix = fill.style.startMatrix;
invMatrix = styleMatrix.Inverse();
}
else
{
material = guiSolid;
}
color = color.Multiply( renderState.cxf );
if( swf_forceAlpha.GetFloat() > 0.0f )
{
color.mul.w = swf_forceAlpha.GetFloat();
color.add.w = 0.0f;
}
if( ( color.mul.w + color.add.w ) <= ALPHA_EPSILON )
{
continue;
}
uint32 packedColorM = LittleLong( PackColor( color.mul ) );
uint32 packedColorA = LittleLong( PackColor( ( color.add * 0.5f ) + idVec4( 0.5f ) ) ); // Compress from -1..1 to 0..1
const swfRect_t& bounds = shape->startBounds;
if( renderState.materialWidth > 0 )
{
size.x = renderState.materialWidth;
}
if( renderState.materialHeight > 0 )
{
size.y = renderState.materialHeight;
}
idVec2 oneOverSize( 1.0f / size.x, 1.0f / size.y );
gui->SetGLState( GLStateForRenderState( renderState ) );
idDrawVert* verts = gui->AllocTris( fill.startVerts.Num(), fill.indices.Ptr(), fill.indices.Num(), material, renderState.stereoDepth );
if( verts == NULL )
{
continue;
}
ALIGNTYPE16 idDrawVert tempVerts[4];
for( int j = 0; j < fill.startVerts.Num(); j++ )
{
const idVec2& xy = fill.startVerts[j];
idDrawVert& vert = tempVerts[j & 3];
vert.Clear();
vert.xyz.ToVec2() = renderState.matrix.Transform( xy ).Scale( scaleToVirtual );
vert.xyz.z = 0.0f;
vert.SetNativeOrderColor( packedColorM );
vert.SetNativeOrderColor2( packedColorA );
// For some reason I don't understand, having texcoords
// in the range of 2000 or so causes what should be solid
// fill areas to have horizontal bands on nvidia, but not 360.
// Forcing the texcoords to zero fixes it.
if( fill.style.type != 0 )
{
idVec2 st;
// all the swf vertexes have an implicit scale of 1/20 for some reason...
st.x = ( ( xy.x - bounds.tl.x ) * oneOverSize.x ) * 20.0f;
st.y = ( ( xy.y - bounds.tl.y ) * oneOverSize.y ) * 20.0f;
st = invMatrix.Transform( st );
if( useAtlas )
{
st = st.Scale( atlasScale ) + atlasBias;
}
// inset the tc - the gui may use a vmtr and the tc might end up
// crossing page boundaries if using [0.0,1.0]
st.x = idMath::ClampFloat( 0.001f, 0.999f, st.x );
st.y = idMath::ClampFloat( 0.001f, 0.999f, st.y );
vert.SetTexCoord( st );
}
// write four verts at a time to video memory
if( ( j & 3 ) == 3 )
{
WriteDrawVerts16( & verts[j & ~3], tempVerts, 4 );
}
}
// write any remaining verts to video memory
WriteDrawVerts16( & verts[fill.startVerts.Num() & ~3], tempVerts, fill.startVerts.Num() & 3 );
}
for( int i = 0; i < shape->lineDraws.Num(); i++ )
{
const idSWFShapeDrawLine& line = shape->lineDraws[i];
swfColorXform_t color;
color.mul = line.style.startColor.ToVec4();
color = color.Multiply( renderState.cxf );
if( swf_forceAlpha.GetFloat() > 0.0f )
{
color.mul.w = swf_forceAlpha.GetFloat();
color.add.w = 0.0f;
}
if( ( color.mul.w + color.add.w ) <= ALPHA_EPSILON )
{
continue;
}
uint32 packedColorM = LittleLong( PackColor( color.mul ) );
uint32 packedColorA = LittleLong( PackColor( ( color.add * 0.5f ) + idVec4( 0.5f ) ) ); // Compress from -1..1 to 0..1
gui->SetGLState( GLStateForRenderState( renderState ) | GLS_POLYMODE_LINE );
idDrawVert* verts = gui->AllocTris( line.startVerts.Num(), line.indices.Ptr(), line.indices.Num(), white, renderState.stereoDepth );
if( verts == NULL )
{
continue;
}
for( int j = 0; j < line.startVerts.Num(); j++ )
{
const idVec2& xy = line.startVerts[j];
ALIGNTYPE16 idDrawVert tempVert;
tempVert.Clear();
tempVert.xyz.ToVec2() = renderState.matrix.Transform( xy ).Scale( scaleToVirtual );
tempVert.xyz.z = 0.0f;
tempVert.SetTexCoord( 0.0f, 0.0f );
tempVert.SetNativeOrderColor( packedColorM );
tempVert.SetNativeOrderColor2( packedColorA );
WriteDrawVerts16( & verts[j], & tempVert, 1 );
}
}
}
/*
========================
idSWF::DrawEditCursor
========================
*/
void idSWF::DrawEditCursor( idRenderSystem* gui, float x, float y, float w, float h, const swfMatrix_t& matrix )
{
idVec2 topl = matrix.Transform( idVec2( x, y ) );
idVec2 topr = matrix.Transform( idVec2( x + w, y ) );
idVec2 br = matrix.Transform( idVec2( x + w, y + h ) );
idVec2 bl = matrix.Transform( idVec2( x, y + h ) );
DrawStretchPic( idVec4( topl.x, topl.y, 0.0f, 0.0f ), idVec4( topr.x, topr.y, 1.0f, 0.0f ), idVec4( br.x, br.y, 1.0f, 1.0f ), idVec4( bl.x, bl.y, 0.0f, 1.0f ), white );
}
/*
========================
idSWF::RenderEditText
========================
*/
void idSWF::RenderEditText( idRenderSystem* gui, idSWFTextInstance* textInstance, const swfRenderState_t& renderState, int time, bool isSplitscreen )
{
if( textInstance == NULL )
{
idLib::Warning( "%s: RenderEditText: textInstance == NULL", filename.c_str() );
return;
}
if( !textInstance->visible )
{
return;
}
const idSWFEditText* shape = textInstance->editText;
idStr text;
if( textInstance->variable.IsEmpty() )
{
if( textInstance->renderMode == SWF_TEXT_RENDER_PARAGRAPH )
{
if( textInstance->NeedsGenerateRandomText() )
{
textInstance->StartParagraphText( Sys_Milliseconds() );
}
text = textInstance->GetParagraphText( Sys_Milliseconds() );
}
else if( textInstance->renderMode == SWF_TEXT_RENDER_RANDOM_APPEAR || textInstance->renderMode == SWF_TEXT_RENDER_RANDOM_APPEAR_CAPS )
{
if( textInstance->NeedsGenerateRandomText() )
{
textInstance->StartRandomText( Sys_Milliseconds() );
}
text = textInstance->GetRandomText( Sys_Milliseconds() );
}
else
{
text = idLocalization::GetString( textInstance->text );
}
}
else
{
idSWFScriptVar var = globals->Get( textInstance->variable );
if( var.IsUndefined() )
{
text = idLocalization::GetString( textInstance->text );
}
else
{
text = idLocalization::GetString( var.ToString() );
}
}
if( text.Length() == 0 )
{
textInstance->selectionEnd = -1;
textInstance->selectionStart = -1;
}
if( textInstance->NeedsSoundPlayed() )
{
PlaySound( textInstance->GetSoundClip() );
textInstance->ClearPlaySound();
}
if( textInstance->tooltip )
{
FindTooltipIcons( &text );
}
else
{
tooltipIconList.Clear();
}
int selStart = textInstance->selectionStart;
int selEnd = textInstance->selectionEnd;
int cursorPos = selEnd;
bool inputField = false;
idSWFScriptVar focusWindow = globals->Get( "focusWindow" );
if( focusWindow.IsObject() && focusWindow.GetObject() == &textInstance->scriptObject )
{
inputField = true;
}
bool drawCursor = false;
if( inputField && ( ( idLib::frameNumber >> 4 ) & 1 ) == 0 )
{
cursorPos = selEnd;
drawCursor = true;
}
if( selStart > selEnd )
{
SwapValues( selStart, selEnd );
}
idVec2 xScaleVec = renderState.matrix.Scale( idVec2( 1.0f, 0.0f ) );
idVec2 yScaleVec = renderState.matrix.Scale( idVec2( 0.0f, 1.0f ) );
float xScale = xScaleVec.Length();
float yScale = yScaleVec.Length();
if( isSplitscreen )
{
yScale *= 0.5f;
}
float invXScale = 1.0f / xScale;
float invYScale = 1.0f / yScale;
swfMatrix_t matrix = renderState.matrix;
matrix.xx *= invXScale;
matrix.xy *= invXScale;
matrix.yy *= invYScale;
matrix.yx *= invYScale;
idSWFDictionaryEntry* fontEntry = FindDictionaryEntry( shape->fontID, SWF_DICT_FONT );
if( fontEntry == NULL )
{
idLib::Warning( "idSWF::RenderEditText: NULL Font" );
return;
}
idSWFFont* swfFont = fontEntry->font;
float postTransformHeight = SWFTWIP( shape->fontHeight ) * yScale;
const idFont* fontInfo = swfFont->fontID;
float glyphScale = postTransformHeight / 48.0f;
float imageScale = postTransformHeight / 24.0f;
textInstance->glyphScale = glyphScale;
idVec4 defaultColor = textInstance->color.ToVec4();
defaultColor = defaultColor.Multiply( renderState.cxf.mul ) + renderState.cxf.add;
if( swf_forceAlpha.GetFloat() > 0.0f )
{
defaultColor.w = swf_forceAlpha.GetFloat();
}
if( defaultColor.w <= ALPHA_EPSILON )
{
return;
}
idVec4 selColor( defaultColor );
selColor.w *= 0.5f;
gui->SetColor( defaultColor );
gui->SetGLState( GLStateForRenderState( renderState ) );
swfRect_t bounds;
bounds.tl.x = xScale * ( shape->bounds.tl.x + SWFTWIP( shape->leftMargin ) );
bounds.br.x = xScale * ( shape->bounds.br.x - SWFTWIP( shape->rightMargin ) );
float linespacing = fontInfo->GetAscender( 1.15f * glyphScale );
if( shape->leading != 0 )
{
linespacing += SWFTWIP( shape->leading );
}
bounds.tl.y = yScale * ( shape->bounds.tl.y + ( 1.15f * glyphScale ) );
bounds.br.y = yScale * ( shape->bounds.br.y );
textInstance->linespacing = linespacing;
textInstance->bounds = bounds;
if( shape->flags & SWF_ET_AUTOSIZE )
{
bounds.br.x = frameWidth;
bounds.br.y = frameHeight;
}
if( drawCursor && cursorPos <= 0 )
{
float yPos = 0.0f;
scaledGlyphInfo_t glyph;
fontInfo->GetScaledGlyph( glyphScale, ' ', glyph );
yPos = glyph.height / 2.0f;
DrawEditCursor( gui, bounds.tl.x, yPos, 1.0f, linespacing, matrix );
}
if( textInstance->IsSubtitle() )
{
if( text.IsEmpty() && textInstance->subtitleText.IsEmpty() )
{
return;
}
}
else if( text.IsEmpty() )
{
return;
}
float x = bounds.tl.x;
float y = bounds.tl.y;
int maxLines = idMath::Ftoi( ( bounds.br.y - bounds.tl.y ) / linespacing );
if( maxLines == 0 )
{
maxLines = 1;
}
textInstance->maxLines = maxLines;
idList< idStr > textLines;
idStr* currentLine = &textLines.Alloc();
// tracks the last breakable character we found
int lastbreak = 0;
float lastbreakX = 0;
bool insertingImage = false;
int iconIndex = 0;
int charIndex = 0;
if( textInstance->IsSubtitle() )
{
charIndex = textInstance->GetSubStartIndex();
}
while( charIndex < text.Length() )
{
if( text[ charIndex ] == '\n' )
{
if( shape->flags & SWF_ET_MULTILINE )
{
currentLine->Append( '\n' );
x = bounds.tl.x;
y += linespacing;
currentLine = &textLines.Alloc();
lastbreak = 0;
charIndex++;
continue;
}
else
{
break;
}
}
int glyphStart = charIndex;
uint32 tc = text.UTF8Char( charIndex );
scaledGlyphInfo_t glyph;
fontInfo->GetScaledGlyph( glyphScale, tc, glyph );
float glyphSkip = glyph.xSkip;
if( textInstance->HasStroke() )
{
glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
}
tooltipIcon_t iconCheck;
if( iconIndex < tooltipIconList.Num() )
{
iconCheck = tooltipIconList[iconIndex];
}
float imageSkip = 0.0f;
if( charIndex - 1 == iconCheck.startIndex )
{
insertingImage = true;
imageSkip = iconCheck.imageWidth * imageScale;
}
else if( charIndex - 1 == iconCheck.endIndex )
{
insertingImage = false;
iconIndex++;
glyphSkip = 0.0f;
}
if( insertingImage )
{
glyphSkip = 0.0f;
}
if( !inputField ) // only break lines of text when we are not inputting data
{
if( x + glyphSkip > bounds.br.x || x + imageSkip > bounds.br.x )
{
if( shape->flags & ( SWF_ET_MULTILINE | SWF_ET_WORDWRAP ) )
{
if( lastbreak > 0 )
{
int curLineIndex = currentLine - &textLines[0];
idStr* newline = &textLines.Alloc();
currentLine = &textLines[ curLineIndex ];
if( maxLines == 1 )
{
currentLine->CapLength( currentLine->Length() - 3 );
currentLine->Append( "..." );
break;
}
else
{
*newline = currentLine->c_str() + lastbreak;
currentLine->CapLength( lastbreak );
currentLine = newline;
x -= lastbreakX;
}
}
else
{
currentLine = &textLines.Alloc();
x = bounds.tl.x;
}
lastbreak = 0;
}
else
{
break;
}
}
}
while( glyphStart < charIndex && glyphStart < text.Length() )
{
currentLine->Append( text[ glyphStart++ ] );
}
x += glyphSkip + imageSkip;
if( tc == ' ' || tc == '-' )
{
lastbreak = currentLine->Length();
lastbreakX = x;
}
}
// Subtitle functionality
if( textInstance->IsSubtitle() && textInstance->IsUpdatingSubtitle() )
{
if( textLines.Num() > 0 && textInstance->SubNeedsSwitch() )
{
int lastWordIndex = textInstance->GetApporoximateSubtitleBreak( time );
int newEndChar = textInstance->GetSubStartIndex() + textLines[0].Length();
int wordCount = 0;
bool earlyOut = false;
for( int index = 0; index < textLines[0].Length(); ++index )
{
if( textLines[0][index] == ' ' || textLines[0][index] == '-' )
{
if( index != 0 )
{
if( wordCount == lastWordIndex )
{
newEndChar = textInstance->GetSubStartIndex() + index;
earlyOut = true;
break;
}
// cover the double space at the beginning of sentences
if( index > 0 && textLines[0][index - 1 ] != ' ' )
{
wordCount++;
}
}
}
else if( index == textLines[0].Length() )
{
if( wordCount == lastWordIndex )
{
newEndChar = textInstance->GetSubStartIndex() + index;
earlyOut = true;
break;
}
wordCount++;
}
}
if( wordCount <= 0 && textLines[0].Length() > 0 )
{
wordCount = 1;
}
if( !earlyOut )
{
textInstance->LastWordChanged( wordCount, time );
}
textInstance->SetSubEndIndex( newEndChar, time );
idStr subText = textLines[0].Left( newEndChar - textInstance->GetSubStartIndex() );
idSWFParmList parms;
parms.Append( subText );
parms.Append( textInstance->GetSpeaker().c_str() );
parms.Append( textInstance->GetSubAlignment() );
Invoke( "subtitleChanged", parms );
parms.Clear();
textInstance->SetSubNextStartIndex( textInstance->GetSubEndIndex() );
textInstance->SwitchSubtitleText( time );
}
if( !textInstance->UpdateSubtitle( time ) )
{
textInstance->SubtitleComplete();
idSWFParmList parms;
parms.Append( textInstance->GetSubAlignment() );
Invoke( "subtitleComplete", parms );
parms.Clear();
textInstance->SubtitleCleanup();
}
}
//*************************************************
// CALCULATE THE NUMBER OF SCROLLS LINES LEFT
//*************************************************
textInstance->CalcMaxScroll( textLines.Num() - maxLines );
int c = 1;
int textLine = textInstance->scroll;
if( textLine + maxLines > textLines.Num() && maxLines < textLines.Num() )
{
textLine = textLines.Num() - maxLines;
textInstance->scroll = textLine;
}
else if( textLine < 0 || textLines.Num() <= maxLines )
{
textLine = 0;
textInstance->scroll = textLine;
}
else if( textInstance->renderMode == SWF_TEXT_RENDER_AUTOSCROLL )
{
textLine = textLines.Num() - maxLines;
textInstance->scroll = textInstance->maxscroll;
}
// END SCROLL CALCULATION
//*************************************************
int index = 0;
int startCharacter = 0;
int endCharacter = 0;
int inputEndChar = 0;
iconIndex = 0;
int overallIndex = 0;
int curIcon = 0;
float yPrevBottomOffset = 0.0f;
float yOffset = 0;
int strokeXOffsets[] = { -1, 1, -1, 1 };
int strokeYOffsets[] = { -1, -1, 1, 1 };
idStr inputText;
if( inputField )
{
if( textLines.Num() > 0 )
{
idStr& text = textLines[0];
float left = bounds.tl.x;
int startCheckIndex = textInstance->GetInputStartChar();
if( startCheckIndex >= text.Length() )
{
startCheckIndex = 0;
}
if( cursorPos < startCheckIndex && cursorPos >= 0 )
{
startCheckIndex = cursorPos;
}
bool endFound = false;
int c = startCheckIndex;
while( c < text.Length() )
{
uint32 tc = text.UTF8Char( c );
scaledGlyphInfo_t glyph;
fontInfo->GetScaledGlyph( glyphScale, tc, glyph );
float glyphSkip = glyph.xSkip;
if( textInstance->HasStroke() )
{
glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
}
if( left + glyphSkip > bounds.br.x )
{
if( cursorPos > c && cursorPos != endCharacter )
{
float removeSize = 0.0f;
while( removeSize < glyphSkip )
{
if( endCharacter == c )
{
break;
}
scaledGlyphInfo_t removeGlyph;
fontInfo->GetScaledGlyph( glyphScale, inputText[ endCharacter++ ], removeGlyph );
removeSize += removeGlyph.xSkip;
}
left -= removeSize;
}
else
{
inputEndChar = c;
endFound = true;
break;
}
}
inputText.AppendUTF8Char( tc );
left += glyphSkip;
}
if( !endFound )
{
inputEndChar = text.Length();
}
startCheckIndex += endCharacter;
textInstance->SetInputStartCharacter( startCheckIndex );
endCharacter = startCheckIndex;
}
}
for( int t = 0; t < textLines.Num(); t++ )
{
if( textInstance->IsSubtitle() && t > 0 )
{
break;
}
if( t < textLine )
{
idStr& text = textLines[t];
c += text.Length();
startCharacter = endCharacter;
endCharacter = startCharacter + text.Length();
overallIndex += text.Length();
// find the right icon index if we scrolled passed the previous ones
for( int iconChar = curIcon; iconChar < tooltipIconList.Num(); ++iconChar )
{
if( endCharacter > tooltipIconList[iconChar].startIndex )
{
curIcon++;
}
else
{
break;
}
}
continue;
}
if( index == maxLines )
{
break;
}
startCharacter = endCharacter;
idStr& text = textLines[textLine];
int lastChar = text.Length();
if( textInstance->IsSubtitle() )
{
lastChar = textInstance->GetSubEndIndex();
}
textLine++;
if( inputField )
{
if( inputEndChar == 0 )
{
inputEndChar += 1;
}
selStart -= startCharacter;
selEnd -= startCharacter;
cursorPos -= startCharacter;
endCharacter = inputEndChar;
lastChar = endCharacter;
text = text.Mid( startCharacter, endCharacter - startCharacter );
}
else
{
if( lastChar == 0 )
{
// blank line so add space char
endCharacter = startCharacter + 1;
}
else
{
endCharacter = startCharacter + lastChar;
}
}
float width = 0.0f;
insertingImage = false;
int i = 0;
while( i < lastChar )
{
if( curIcon < tooltipIconList.Num() && tooltipIconList[curIcon].startIndex == startCharacter + i )
{
width += tooltipIconList[curIcon].imageWidth * imageScale;
i += tooltipIconList[curIcon].endIndex - tooltipIconList[curIcon].startIndex - 1;
curIcon++;
}
else
{
if( i < text.Length() )
{
scaledGlyphInfo_t glyph;
fontInfo->GetScaledGlyph( glyphScale, text.UTF8Char( i ), glyph );
width += glyph.xSkip;
if( textInstance->HasStroke() )
{
width += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
}
}
else
{
i++;
}
}
}
y = bounds.tl.y + ( index * linespacing );
float biggestGlyphHeight = 0.0f;
/*for ( int image = 0; image < tooltipIconList.Num(); ++image ) {
if ( tooltipIconList[image].startIndex >= startCharacter && tooltipIconList[image].endIndex < endCharacter ) {
biggestGlyphHeight = tooltipIconList[image].imageHeight > biggestGlyphHeight ? tooltipIconList[image].imageHeight : biggestGlyphHeight;
}
}*/
float yBottomOffset = 0.0f;
float yTopOffset = 0.0f;
if( biggestGlyphHeight > 0.0f )
{
float topSpace = 0.0f;
float bottomSpace = 0.0f;
int idx = 0;
scaledGlyphInfo_t glyph;
fontInfo->GetScaledGlyph( glyphScale, text.UTF8Char( idx ), glyph );
topSpace = ( ( biggestGlyphHeight * imageScale ) - glyph.height ) / 2.0f;
bottomSpace = topSpace;
if( topSpace > 0.0f && t != 0 )
{
yTopOffset += topSpace;
}
if( bottomSpace > 0.0f )
{
yBottomOffset += bottomSpace;
}
}
else
{
yBottomOffset = 0.0f;
}
if( t != 0 )
{
if( yPrevBottomOffset > 0 || yTopOffset > 0 )
{
yOffset += yTopOffset > yPrevBottomOffset ? yTopOffset : yPrevBottomOffset;
}
}
y += yOffset;
yPrevBottomOffset = yBottomOffset;
float extraSpace = 0.0f;
switch( shape->align )
{
case SWF_ET_ALIGN_LEFT:
x = bounds.tl.x;
break;
case SWF_ET_ALIGN_RIGHT:
x = bounds.br.x - width;
break;
case SWF_ET_ALIGN_CENTER:
x = ( bounds.tl.x + bounds.br.x - width ) * 0.5f;
break;
case SWF_ET_ALIGN_JUSTIFY:
x = bounds.tl.x;
if( width > ( bounds.br.x - bounds.tl.x ) * 0.5f && index < textLines.Num() - 1 )
{
extraSpace = ( ( bounds.br.x - bounds.tl.x ) - width ) / ( ( float ) lastChar - 1.0f );
}
break;
}
tooltipIcon_t icon;
insertingImage = false;
// find the right icon index if we scrolled passed the previous ones
for( int iconChar = iconIndex; iconChar < tooltipIconList.Num(); ++iconChar )
{
if( overallIndex > tooltipIconList[iconChar].startIndex )
{
iconIndex++;
}
else
{
break;
}
}
float baseLine = y + ( fontInfo->GetAscender( glyphScale ) );
i = 0;
int overallLineIndex = 0;
idVec4 textColor = defaultColor;
while( i < lastChar )
{
if( i >= text.Length() )
{
break;
}
// Support colors
if( !textInstance->ignoreColor )
{
if( text[ i ] == C_COLOR_ESCAPE )
{
if( idStr::IsColor( text.c_str() + i++ ) )
{
if( text[ i ] == C_COLOR_DEFAULT )
{
i++;
textColor = defaultColor;
}
else
{
textColor = idStr::ColorForIndex( text[ i++ ] );
textColor.w = defaultColor.w;
}
continue;
}
}
}
uint32 character = text.UTF8Char( i );
if( character == '\n' )
{
c++;
overallIndex += i - overallLineIndex;
overallLineIndex = i;;
continue;
}
// Skip a single leading space
if( character == ' ' && i == 1 )
{
c++;
overallIndex += i - overallLineIndex;
overallLineIndex = i;
continue;
}
if( iconIndex < tooltipIconList.Num() )
{
icon = tooltipIconList[iconIndex];
}
if( overallIndex == icon.startIndex )
{
insertingImage = true;
scaledGlyphInfo_t glyph;
fontInfo->GetScaledGlyph( glyphScale, character, glyph );
float imageHeight = icon.imageHeight * imageScale;
float glyphHeight = glyph.height;
float imageY = 0.0f;
if( icon.baseline == 0 )
{
imageY = baseLine - glyph.top;
imageY += ( glyphHeight - imageHeight ) * 0.5f;
imageY += 2.0f;
}
else
{
imageY = ( y + glyphHeight ) - ( ( icon.imageHeight * imageScale ) - ( glyphHeight ) );
}
float imageX = x + glyph.left;
float imageW = icon.imageWidth * imageScale;
float imageH = icon.imageHeight * imageScale;
idVec2 topl = matrix.Transform( idVec2( imageX, imageY ) );
idVec2 topr = matrix.Transform( idVec2( imageX + imageW, imageY ) );
idVec2 br = matrix.Transform( idVec2( imageX + imageW, imageY + imageH ) );
idVec2 bl = matrix.Transform( idVec2( imageX, imageY + imageH ) );
float s1 = 0.0f;
float t1 = 0.0f;
float s2 = 1.0f;
float t2 = 1.0f;
//uint32 color = gui->GetColor();
idVec4 imgColor = colorWhite;
imgColor.w = defaultColor.w;
gui->SetColor( imgColor );
DrawStretchPic( idVec4( topl.x, topl.y, s1, t1 ), idVec4( topr.x, topr.y, s2, t1 ), idVec4( br.x, br.y, s2, t2 ), idVec4( bl.x, bl.y, s1, t2 ), icon.material );
gui->SetColor( defaultColor );
x += icon.imageWidth * imageScale;
x += extraSpace;
}
else if( overallIndex == icon.endIndex )
{
insertingImage = false;
iconIndex++;
}
if( insertingImage )
{
overallIndex += i - overallLineIndex;
overallLineIndex = i;
continue;
}
// the glyphs texcoords assume nearest filtering, to get proper
// bilinear support we need to go an extra half texel on each side
scaledGlyphInfo_t glyph;
fontInfo->GetScaledGlyph( glyphScale, character, glyph );
float glyphSkip = glyph.xSkip;
if( textInstance->HasStroke() )
{
glyphSkip += ( swf_textStrokeSizeGlyphSpacer.GetFloat() * textInstance->GetStrokeWeight() * glyphScale );
}
float glyphW = glyph.width + 1.0f; // +1 for bilinear half texel on each side
float glyphH = glyph.height + 1.0f;
float glyphY = baseLine - glyph.top;
float glyphX = x + glyph.left;
idVec2 topl = matrix.Transform( idVec2( glyphX, glyphY ) );
idVec2 topr = matrix.Transform( idVec2( glyphX + glyphW, glyphY ) );
idVec2 br = matrix.Transform( idVec2( glyphX + glyphW, glyphY + glyphH ) );
idVec2 bl = matrix.Transform( idVec2( glyphX, glyphY + glyphH ) );
float s1 = glyph.s1;
float t1 = glyph.t1;
float s2 = glyph.s2;
float t2 = glyph.t2;
if( c > selStart && c <= selEnd )
{
idVec2 topl = matrix.Transform( idVec2( x, y ) );
idVec2 topr = matrix.Transform( idVec2( x + glyphSkip, y ) );
idVec2 br = matrix.Transform( idVec2( x + glyphSkip, y + linespacing ) );
idVec2 bl = matrix.Transform( idVec2( x, y + linespacing ) );
gui->SetColor( selColor );
DrawStretchPic( idVec4( topl.x, topl.y, 0, 0 ), idVec4( topr.x, topr.y, 1, 0 ), idVec4( br.x, br.y, 1, 1 ), idVec4( bl.x, bl.y, 0, 1 ), white );
gui->SetColor( textColor );
}
if( textInstance->GetHasDropShadow() )
{
float dsY = glyphY + glyphScale * 2.0f;
float dsX = glyphX + glyphScale * 2.0f;
idVec2 dstopl = matrix.Transform( idVec2( dsX, dsY ) );
idVec2 dstopr = matrix.Transform( idVec2( dsX + glyphW, dsY ) );
idVec2 dsbr = matrix.Transform( idVec2( dsX + glyphW, dsY + glyphH ) );
idVec2 dsbl = matrix.Transform( idVec2( dsX, dsY + glyphH ) );
idVec4 dsColor = colorBlack;
dsColor.w = defaultColor.w;
gui->SetColor( dsColor );
DrawStretchPic( idVec4( dstopl.x, dstopl.y, s1, t1 ), idVec4( dstopr.x, dstopr.y, s2, t1 ), idVec4( dsbr.x, dsbr.y, s2, t2 ), idVec4( dsbl.x, dsbl.y, s1, t2 ), glyph.material );
gui->SetColor( textColor );
}
else if( textInstance->HasStroke() )
{
idVec4 strokeColor = colorBlack;
strokeColor.w = textInstance->GetStrokeStrength() * defaultColor.w;
gui->SetColor( strokeColor );
for( int index = 0; index < 4; ++index )
{
float xPos = glyphX + ( ( strokeXOffsets[ index ] * textInstance->GetStrokeWeight() ) * glyphScale );
float yPos = glyphY + ( ( strokeYOffsets[ index ] * textInstance->GetStrokeWeight() ) * glyphScale );
idVec2 topLeft = matrix.Transform( idVec2( xPos, yPos ) );
idVec2 topRight = matrix.Transform( idVec2( xPos + glyphW, yPos ) );
idVec2 botRight = matrix.Transform( idVec2( xPos + glyphW, yPos + glyphH ) );
idVec2 botLeft = matrix.Transform( idVec2( xPos, yPos + glyphH ) );
DrawStretchPic( idVec4( topLeft.x, topLeft.y, s1, t1 ), idVec4( topRight.x, topRight.y, s2, t1 ), idVec4( botRight.x, botRight.y, s2, t2 ), idVec4( botLeft.x, botLeft.y, s1, t2 ), glyph.material );
}
gui->SetColor( textColor );
}
DrawStretchPic( idVec4( topl.x, topl.y, s1, t1 ), idVec4( topr.x, topr.y, s2, t1 ), idVec4( br.x, br.y, s2, t2 ), idVec4( bl.x, bl.y, s1, t2 ), glyph.material );
x += glyphSkip;
x += extraSpace;
if( cursorPos == c )
{
DrawEditCursor( gui, x - 1.0f, y, 1.0f, linespacing, matrix );
}
c++;
overallIndex += i - overallLineIndex;
overallLineIndex = i;
}
index++;
}
}
/*
========================
idSWF::FindTooltipIcons
This replaces text like "_use" with platform specific text like ""
========================
*/
void idSWF::FindTooltipIcons( idStr* text )
{
tooltipIconList.Clear();
for( int i = UB_MAX_BUTTONS - 1; i >= 0; i-- )
{
//for ( userCmdString_t * ucs = userCmdStrings ; ucs->string ; ucs++ ) {
userCmdString_t ucs = userCmdStrings[i];
if( ucs.string && idStr::FindText( text->c_str(), ucs.string, false ) != idStr::INVALID_POSITION )
{
idStr replacement;
keyBindings_t bind = idKeyInput::KeyBindingsFromBinding( ucs.string, true );
idStr gamepad = "<";
gamepad.Append( bind.gamepad );
gamepad.Append( ">" );
if( !in_useJoystick.GetBool() )
{
if( !bind.mouse.IsEmpty() )
{
replacement.Format( "<%s>", bind.mouse.c_str() );
}
else if( !bind.keyboard.IsEmpty() )
{
replacement = bind.keyboard;
}
if( replacement.IsEmpty() )
{
text->Replace( ucs.string, idStrId( "#str_swf_unbound" ).GetLocalizedString() );
}
}
else
{
replacement = gamepad;
}
if( !replacement.IsEmpty() )
{
replacement.ToUpper();
text->Replace( ucs.string, replacement.c_str() );
}
}
}
for( int count = 0; count < tooltipButtonImage.Num(); ++count )
{
int index = -1;
while( ( index = idStr::FindText( text->c_str(), tooltipButtonImage[count].key, false, index + 1 ) ) != idStr::INVALID_POSITION )
{
tooltipIcon_t icon;
icon.startIndex = index;
icon.endIndex = index + idStr::Length( tooltipButtonImage[count].key );
icon.material = declManager->FindMaterial( tooltipButtonImage[count].xbImage );
if( icon.material )
{
icon.imageWidth = tooltipButtonImage[count].width;
icon.imageHeight = tooltipButtonImage[count].height;
icon.baseline = tooltipButtonImage[count].baseline;
}
else
{
icon.imageWidth = 0;
icon.imageHeight = 0;
icon.baseline = 0;
}
bool inserted = false;
if( tooltipIconList.Num() > 0 )
{
for( int i = 0; i < tooltipIconList.Num(); ++i )
{
if( tooltipIconList[i].startIndex > icon.startIndex )
{
tooltipIconList.Insert( icon, i );
inserted = true;
break;
}
}
}
if( !inserted )
{
tooltipIconList.Append( icon );
}
}
}
}