| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program 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.
- **
- ** This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
- ***********************************************************************************************
- * *
- * Project Name : WW3D *
- * *
- * $Archive:: /Commando/Code/ww3d2/render2dsentence.cpp $*
- * *
- * $Author:: Steve_t $*
- * *
- * $Modtime:: 8/26/02 3:18p $*
- * *
- * $Revision:: 27 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "render2dsentence.h"
- #include "surfaceclass.h"
- #include "texture.h"
- #include "wwprofile.h"
- #include "wwmemlog.h"
- #include "dx8wrapper.h"
- ////////////////////////////////////////////////////////////////////////////////////
- // Local constants
- ////////////////////////////////////////////////////////////////////////////////////
- const int CHAR_TEXTURE_SIZE = 256;
- const int CHAR_BUFFER_LEN = 32768;
- // Macros.
- // NOTE 0: Word wrap logic does not apply to Han characters (Chinese, Japanese & Korean).
- // Therefore treat each of these characters as a word which can be preceeded by a line break.
- // NOTE 1: This is a simplification. Some Korean characters should not be line break characters.
- #define IS_BREAK_CHAR(ch) ((ch == L' ') || ((ch >= 0x3000) && (ch <= 0xdfff)))
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Render2DSentenceClass
- //
- ////////////////////////////////////////////////////////////////////////////////////
- Render2DSentenceClass::Render2DSentenceClass (void) :
- Font (NULL),
- Location (0.0F,0.0F),
- Cursor (0.0F,0.0F),
- TextureOffset (0, 0),
- TextureStartX (0),
- CurSurface (NULL),
- CurrTextureSize (0),
- MonoSpaced (false),
- IsClippedEnabled (false),
- ClipRect (0, 0, 0, 0),
- BaseLocation (0, 0),
- LockedPtr (NULL),
- LockedStride (0),
- TextureSizeHint (0),
- WrapWidth (0),
- TabStop (5.0),
- DrawExtents (0, 0, 0, 0),
- Renderers(sizeof(PreAllocatedRenderers)/sizeof(RendererDataStruct),PreAllocatedRenderers)
- {
- Shader = Render2DClass::Get_Default_Shader ();
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // ~Render2DSentenceClass
- //
- ////////////////////////////////////////////////////////////////////////////////////
- Render2DSentenceClass::~Render2DSentenceClass (void)
- {
- REF_PTR_RELEASE (Font);
- Reset ();
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Set_Font
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Set_Font (FontCharsClass *font)
- {
- Reset ();
- REF_PTR_SET (Font, font);
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Reset_Polys
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Reset_Polys (void)
- {
- for (int index = 0; index < Renderers.Count (); index ++) {
- Renderers[index].Renderer->Reset ();
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Reset
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Reset (void)
- {
- //
- // Make sure we unlock the current surface (if necessary)
- //
- if (LockedPtr != NULL) {
- CurSurface->Unlock ();
- LockedPtr = NULL;
- }
- //
- // Release our hold on the current surface
- //
- REF_PTR_RELEASE (CurSurface);
- //
- // Free each renderer
- //
- for (int i=0;i<Renderers.Count();++i) {
- delete Renderers[i].Renderer;
- }
- Renderers.Reset_Active();
- Cursor.Set (0, 0);
- MonoSpaced = false;
- Release_Pending_Surfaces ();
- Reset_Sentence_Data ();
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Make_Additive
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Make_Additive (void)
- {
- Shader.Set_Dst_Blend_Func (ShaderClass::DSTBLEND_ONE);
- Shader.Set_Src_Blend_Func (ShaderClass::SRCBLEND_ONE);
- Shader.Set_Primary_Gradient (ShaderClass::GRADIENT_MODULATE);
- Shader.Set_Secondary_Gradient (ShaderClass::SECONDARY_GRADIENT_DISABLE);
- Set_Shader (Shader);
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Make_Additive
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Set_Shader (ShaderClass shader)
- {
- Shader = shader;
- //
- // Change each renderer's shader
- //
- for (int i = 0; i < Renderers.Count (); i ++) {
- ShaderClass *curr_shader = Renderers[i].Renderer->Get_Shader ();
- (*curr_shader) = Shader;
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Render
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Render (void)
- {
- if (DX8Wrapper::Is_Device_Lost() || !DX8Wrapper::Is_Initted()) return;
- //
- // Build any textures that are pending
- //
- Build_Textures ();
- //
- // Ask each renderer to draw its contents
- //
- for (int i = 0; i < Renderers.Count (); i ++) {
- Renderers[i].Renderer->Render ();
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Set_Base_Location
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Set_Base_Location (const Vector2 &loc)
- {
- Vector2 dif = loc - BaseLocation;
- BaseLocation = loc;
- for (int i = 0; i < Renderers.Count (); i ++) {
- Renderers[i].Renderer->Move (dif);
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Set_Location
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Set_Location (const Vector2 &loc)
- {
- Location = loc;
- return ;
- }
- void
- Render2DSentenceClass::Set_Tabstop(float stop)
- {
- if (stop > 0.0) {
- TabStop = stop;
- } else {
- TabStop = 1.0;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Get_Text_Extents
- //
- ////////////////////////////////////////////////////////////////////////////////////
- Vector2
- Render2DSentenceClass::Get_Text_Extents (const WCHAR *text)
- {
- if (!DX8Wrapper::Is_Initted()) {
- Vector2 temp(0,0);
- return(temp);
- }
- Vector2 extent (0, Font->Get_Char_Height());
- while (*text) {
- WCHAR ch = *text++;
- if ( ch != (WCHAR)'\n' ) {
- extent.X += Font->Get_Char_Spacing( ch );
- }
- }
- return extent;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Find_Row_Start
- //
- ////////////////////////////////////////////////////////////////////////////////////
- const WCHAR *
- Render2DSentenceClass::Find_Row_Start( const WCHAR * text, int row_index )
- {
- if (row_index == 0) {
- return text;
- }
- if (!DX8Wrapper::Is_Initted()) {
- return text;
- }
- const WCHAR *retval = NULL;
- float max_x_pos = 0;
- float x_pos = 0;
- float y_pos = Font->Get_Char_Height ();
- int row_counter = 0;
- while (*text) {
- WCHAR ch = *text++;
- bool is_wrapped = false;
- //
- // Check to see if we need to wrap on this word-break
- //
- if (IS_BREAK_CHAR (ch) && WrapWidth > 0) {
- //
- // Find the width of the next word
- //
- const WCHAR *word = text;
- float word_width = Font->Get_Char_Spacing (ch);
- while ((*word != 0) && ((*word > L' ') && !IS_BREAK_CHAR (*word))) {
- word_width += Font->Get_Char_Spacing (*word++);
- }
- //
- // Did the word extend past the wrap width?
- //
- if ((x_pos + word_width) >= WrapWidth) {
- is_wrapped = true;
- }
- } else if (ch == L'\n') {
- is_wrapped = true;
- }
- //
- // Handle line wrapping
- //
- if (is_wrapped) {
- max_x_pos = max (max_x_pos, x_pos);
- x_pos = 0;
- y_pos += Font->Get_Char_Height ();
- //
- // Is this the line we're looking for?
- //
- row_counter ++;
- if (row_counter == row_index) {
- retval = (ch == L' ' || ch == L'\n') ? text : text - 1;
- break;
- }
- }
- if (ch != (WCHAR)'\n') {
- x_pos += Font->Get_Char_Spacing (ch);
- }
- }
- return retval;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Get_Formatted_Text_Extents
- //
- ////////////////////////////////////////////////////////////////////////////////////
- Vector2
- Render2DSentenceClass::Get_Formatted_Text_Extents (const WCHAR *text, int *row_count)
- {
- if (!DX8Wrapper::Is_Initted()) {
- Vector2 temp(0,0);
- return(temp);
- }
-
- float max_x_pos = 0;
- float x_pos = 0;
- float y_pos = Font->Get_Char_Height ();
- int row_counter = 0;
- while (*text) {
- WCHAR ch = *text++;
- bool is_wrapped = false;
- //
- // Check to see if we need to wrap on this word-break
- //
- if (IS_BREAK_CHAR (ch) && WrapWidth > 0) {
- //
- // Find the width of the next word
- //
- const WCHAR *word = text;
- float word_width = Font->Get_Char_Spacing (ch);
- while ((*word != 0) && ((*word > L' ') && !IS_BREAK_CHAR (*word))) {
- word_width += Font->Get_Char_Spacing (*word++);
- }
- //
- // Did the word extend past the wrap width?
- //
- if ((x_pos + word_width) >= WrapWidth) {
- is_wrapped = true;
- }
- } else if (ch == L'\n') {
- is_wrapped = true;
- }
- //
- // Handle line wrapping
- //
- if (is_wrapped) {
- max_x_pos = max (max_x_pos, x_pos);
- x_pos = 0;
- y_pos += Font->Get_Char_Height ();
- row_counter ++;
- }
- if (ch != (WCHAR)'\n') {
- x_pos += Font->Get_Char_Spacing (ch);
- }
- }
- //
- // Build a Vector2 out of our extents
- //
- Vector2 extent;
- extent.X = max (max_x_pos, x_pos);
- extent.Y = y_pos;
- //
- // Return the row count to the caller (if necessary)
- //
- if (row_count != NULL) {
- (*row_count) = row_counter + 1;
- }
- return extent;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Reset_Sentence_Data
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Reset_Sentence_Data (void)
- {
- //
- // Release our hold on each texture used in the sentence
- //
- for (int index = 0; index < SentenceData.Count (); index ++) {
- REF_PTR_RELEASE (SentenceData[index].Surface);
- }
- SentenceData.Reset_Active();
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Release_Pending_Surfaces
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Release_Pending_Surfaces (void)
- {
- //
- // Release our hold on each pending surface
- //
- for (int index = 0; index < PendingSurfaces.Count (); index ++) {
- SurfaceClass *curr_surface = PendingSurfaces[index].Surface;
- REF_PTR_RELEASE (curr_surface);
- }
- PendingSurfaces.Reset_Active();
- return;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Build_Textures
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Build_Textures (void)
- {
- WWMEMLOG(MEM_TEXTURE);
- //
- // Make sure we unlock the current surface
- //
- if (LockedPtr != NULL) {
- CurSurface->Unlock ();
- LockedPtr = NULL;
- }
- //
- // Release our hold on the current surface
- //
- REF_PTR_RELEASE (CurSurface);
- TextureOffset.Set (0, 0);
- TextureStartX = 0;
- //
- // Convert all pending surfaces to textures
- //
- for (int index = 0; index < PendingSurfaces.Count (); index ++) {
- PendingSurfaceStruct &surface_info = PendingSurfaces[index];
- SurfaceClass *curr_surface = surface_info.Surface;
- //
- // Get the dimensions of the surface
- //
- SurfaceClass::SurfaceDescription desc;
- curr_surface->Get_Description (desc);
- //
- // Create the new texture
- //
- TextureClass *new_texture = new TextureClass (desc.Width, desc.Width, WW3D_FORMAT_A4R4G4B4, TextureClass::MIP_LEVELS_1);
- SurfaceClass *texture_surface = new_texture->Get_Surface_Level ();
- //
- // Copy the contents of the texture from the surface
- //
- DX8Wrapper::_Copy_DX8_Rects (curr_surface->Peek_D3D_Surface (), NULL, 0, texture_surface->Peek_D3D_Surface (), NULL);
- REF_PTR_RELEASE (texture_surface);
- //
- // Assign this texture to any renderers that need it
- //
- for (int renderer_index = 0; renderer_index < surface_info.Renderers.Count (); renderer_index ++) {
- Render2DClass *renderer = surface_info.Renderers[renderer_index];
- renderer->Set_Texture (new_texture);
- }
- //
- // Release our hold on the objects
- //
- REF_PTR_RELEASE (new_texture);
- REF_PTR_RELEASE (curr_surface);
- }
- //
- // Reset the list
- //
- PendingSurfaces.Reset_Active();
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Draw_Sentence
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Draw_Sentence (uint32 color)
- {
- Render2DClass *curr_renderer = NULL;
- SurfaceClass *curr_surface = NULL;
- DrawExtents.Set (0, 0, 0, 0);
- //
- // Loop over all the parts of the sentence
- //
- for (int index = 0; index < SentenceData.Count (); index ++) {
- SentenceDataStruct &data = SentenceData[index];
- //
- // Has the surface changed?
- //
- if (data.Surface != curr_surface) {
- curr_surface = data.Surface;
- //
- // Try to find a renderer that uses the same "texture"
- //
- bool found = false;
- for (int renderer_index = 0; renderer_index < Renderers.Count (); renderer_index ++) {
- if (Renderers[renderer_index].Surface == curr_surface) {
- found = true;
- curr_renderer = Renderers[renderer_index].Renderer;
- break;
- }
- }
- //
- // Create a new renderer if we couldn't find an appropriate one
- //
- if (found == false) {
- //
- // Allocate a new renderer
- //
- curr_renderer = new Render2DClass;
- curr_renderer->Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution ());
- ShaderClass *curr_shader = curr_renderer->Get_Shader ();
- (*curr_shader) = Shader;
- //
- // Add it to our list
- //
- RendererDataStruct render_info;
- render_info.Renderer = curr_renderer;
- render_info.Surface = curr_surface;
- Renderers.Add (render_info);
- //
- // Now, add this renderer to the surface pending list
- //
- for (int surface_index = 0; surface_index < PendingSurfaces.Count (); surface_index ++) {
- PendingSurfaceStruct &surface_info = PendingSurfaces[surface_index];
- if (surface_info.Surface == curr_surface) {
- surface_info.Renderers.Add (curr_renderer);
- }
- }
- }
- }
- //
- // Get the dimensions of the surface
- //
- SurfaceClass::SurfaceDescription desc;
- curr_surface->Get_Description (desc);
- //
- // Add a quad that contains this sentence chunk
- //
- RectClass screen_rect = data.ScreenRect;
- screen_rect += Location;
- RectClass uv_rect = data.UVRect;
- //
- // Clip the quad (as necessary)
- //
- bool add_quad = true;
- if (IsClippedEnabled) {
- //
- // Check for completely clipped
- //
- if ( screen_rect.Right <= ClipRect.Left ||
- screen_rect.Bottom <= ClipRect.Top)
- {
- add_quad = false;
- /*} else if ( screen_rect.Top < ClipRect.Top ||
- screen_rect.Bottom > ClipRect.Bottom)
- {
- add_quad = false;*/
- } else {
- //
- // Clip the polygons to the specified area
- //
- RectClass clipped_rect;
- clipped_rect.Left = max (screen_rect.Left, ClipRect.Left);
- clipped_rect.Right = min (screen_rect.Right, ClipRect.Right);
- clipped_rect.Top = max (screen_rect.Top, ClipRect.Top);
- clipped_rect.Bottom = min (screen_rect.Bottom, ClipRect.Bottom);
- //
- // Clip the texture to the specified area
- //
- RectClass clipped_uv_rect;
- float percent = ((clipped_rect.Left - screen_rect.Left) / screen_rect.Width ());
- clipped_uv_rect.Left = uv_rect.Left + (uv_rect.Width () * percent);
- percent = ((clipped_rect.Right - screen_rect.Left) / screen_rect.Width ());
- clipped_uv_rect.Right = uv_rect.Left + (uv_rect.Width () * percent);
- percent = ((clipped_rect.Top - screen_rect.Top) / screen_rect.Height ());
- clipped_uv_rect.Top = uv_rect.Top + (uv_rect.Height () * percent);
- percent = ((clipped_rect.Bottom - screen_rect.Top) / screen_rect.Height ());
- clipped_uv_rect.Bottom = uv_rect.Top + (uv_rect.Height () * percent);
- //
- // Use the clipped rectangles to render
- //
- screen_rect = clipped_rect;
- uv_rect = clipped_uv_rect;
- }
- }
- if (add_quad) {
- uv_rect *= 1.0F / ((float)desc.Width);
- curr_renderer->Add_Quad (screen_rect, uv_rect, color);
- //
- // Add this rectangle to the total draw extents
- //
- if (DrawExtents.Width () == 0) {
- DrawExtents = screen_rect;
- } else {
- DrawExtents += screen_rect;
- }
- }
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Record_Sentence_Chunk
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Record_Sentence_Chunk (void)
- {
- //
- // Do we have anything to store?
- //
- int width = TextureOffset.I - TextureStartX;
- if (width > 0) {
- float char_height = Font->Get_Char_Height ();
- //
- // Build a structure that contains enough information
- // to hold this portion of the sentence
- //
- SentenceDataStruct sentence_data;
- sentence_data.Surface = CurSurface;
- sentence_data.Surface->Add_Ref ();
- sentence_data.ScreenRect.Left = Cursor.X;
- sentence_data.ScreenRect.Right = Cursor.X + width;
- sentence_data.ScreenRect.Top = Cursor.Y;
- sentence_data.ScreenRect.Bottom = Cursor.Y + char_height;
- sentence_data.UVRect.Left = TextureStartX;
- sentence_data.UVRect.Top = TextureOffset.J;
- sentence_data.UVRect.Right = TextureOffset.I;
- sentence_data.UVRect.Bottom = TextureOffset.J + char_height;
- //
- // Add this information to our list
- //
- SentenceData.Add (sentence_data);
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Allocate_New_Surface
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Allocate_New_Surface (const WCHAR *text)
- {
- //
- // Unlock the last surface (if necessary)
- //
- if (LockedPtr != NULL) {
- CurSurface->Unlock ();
- LockedPtr = NULL;
- }
- //
- // Calculate the width of the text
- //
- int text_width = 0;
- for (int index = 0; text[index] != 0; index ++) {
- text_width += Font->Get_Char_Spacing (text[index]);
- }
- int char_height = Font->Get_Char_Height ();
- //
- // Find the best texture size for the remaining text
- //
- CurrTextureSize = 256;
- int best_tex_mem_usage = 999999999;
- for (int pow2 = 6; pow2 <= 8; pow2 ++) {
- int size = 1 << pow2;
- int row_count = (text_width / size) + 1;
- int rows_per_texture = size / (char_height + 1);
- //
- // Can we even fit one character on this texture?
- //
- if (rows_per_texture > 0) {
- //
- // How many textures (at this size) would it take to render
- // the remaining text?
- //
- int texture_count = row_count / rows_per_texture;
- texture_count = max (texture_count, 1);
- //
- // Is this the best usage of texture memory we've found yet?
- //
- int texture_mem_usage = (texture_count * size * size);
- if (texture_mem_usage < best_tex_mem_usage) {
- CurrTextureSize = size;
- best_tex_mem_usage = texture_mem_usage;
- }
- }
- }
- //
- // Use whichever is larger, the hint or the calculated size
- //
- CurrTextureSize = max (TextureSizeHint, CurrTextureSize);
- //
- // Release our extra hold on the old surface
- //
- REF_PTR_RELEASE (CurSurface);
- //
- // Create the new surface
- //
- CurSurface = NEW_REF (SurfaceClass, (CurrTextureSize, CurrTextureSize, WW3D_FORMAT_A4R4G4B4));
- WWASSERT (CurSurface != NULL);
- CurSurface->Add_Ref ();
- //
- // Add this surface to our list
- //
- PendingSurfaceStruct surface_info;
- surface_info.Surface = CurSurface;
- PendingSurfaces.Add (surface_info);
- //
- // Reset to the upper left corner
- //
- TextureOffset.Set (0, 0);
- TextureStartX = 0;
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Build_Sentence
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- Render2DSentenceClass::Build_Sentence (const WCHAR *text)
- {
- if (text == NULL) {
- return ;
- }
- if (!DX8Wrapper::Is_Initted()) {
- return;
- }
- //
- // Start fresh
- //
- Reset_Sentence_Data ();
- Cursor.Set (0, 0);
- //
- // Ensure we have a surface to start with
- //
- if (CurSurface == NULL) {
- Allocate_New_Surface (text);
- }
- float char_height = Font->Get_Char_Height ();
- //
- // Loop over all the characters in the string
- //
- while (text != NULL) {
- WCHAR ch = *text++;
- //
- // Determine how much horizontal space this character requires
- //
- float char_spacing = Font->Get_Char_Spacing (ch);
- bool exceeded_texture_width = ((TextureOffset.I + char_spacing) >= CurrTextureSize);
- bool encountered_break_char = (IS_BREAK_CHAR (ch) || ch == L'\n' || ch == 0 || ch == L'\t');
- //
- // Do we need to record this portion of the sentence to its own chunk?
- //
- if (exceeded_texture_width || encountered_break_char) {
- Record_Sentence_Chunk ();
- //
- // Adjust the positions
- //
- Cursor.X += (TextureOffset.I - TextureStartX);
- TextureStartX = TextureOffset.I;
- //
- // Adjust the output coordinates
- //
- if (IS_BREAK_CHAR (ch)) {
- if (ch == L' ') {
- Cursor.X += char_spacing;
- }
- //
- // Check to see if we need to wrap on this word-break
- //
- if (WrapWidth > 0) {
- //
- // Find the length of the next word
- //
- const WCHAR *word = text;
- float word_width = (ch == L' ') ? 0 : char_spacing;
- while ((*word != 0) && ((*word > L' ') && !IS_BREAK_CHAR (*word))) {
- word_width += Font->Get_Char_Spacing (*word++);
- }
- //
- // Should we wrap the next word?
- //
- if ((Cursor.X + word_width) >= WrapWidth) {
- Cursor.X = 0;
- Cursor.Y += char_height;
- }
- }
- } else if (ch == L'\n') {
- Cursor.X = 0;
- Cursor.Y += char_height;
- } else if (ch == 0) {
- break;
- } else if (ch == L'\t') {
- float tab_spacing = (char_spacing * TabStop);
- float tab_pos = (floor(Cursor.X / tab_spacing) * tab_spacing);
- Cursor.X = (tab_pos + tab_spacing);
- }
- //
- // Did the text extend past the edge of the texture?
- //
- if (exceeded_texture_width) {
- TextureStartX = 0;
- TextureOffset.I = TextureStartX;
- TextureOffset.J += char_height;
- //
- // Did the text extent completely off the texture?
- //
- if ((TextureOffset.J + char_height) >= CurrTextureSize) {
- Allocate_New_Surface (text);
- }
- }
- }
- if (ch != L'\n' && ch != L' ' && ch != L'\t') {
- //
- // Ensure the surface is locked
- //
- if (LockedPtr == NULL) {
- LockedPtr = (uint16 *)CurSurface->Lock (&LockedStride);
- WWASSERT (LockedPtr != NULL);
- }
- //
- // Check to ensure the text will fit on this texture
- //
- WWASSERT (((TextureOffset.I + char_spacing) < CurrTextureSize) && ((TextureOffset.J + char_height) < CurrTextureSize));
- //
- // Blit the character to the surface
- //
- Font->Blit_Char (ch, LockedPtr, LockedStride, TextureOffset.I, TextureOffset.J);
- TextureOffset.I += char_spacing;
- }
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////
- void Render2DSentenceClass::Force_Alpha( float alpha )
- {
- for (int i = 0; i < Renderers.Count (); i ++) {
- Renderers[i].Renderer->Force_Alpha( alpha );
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // FontCharsClass
- //
- ////////////////////////////////////////////////////////////////////////////////////
- FontCharsClass::FontCharsClass (void) :
- OldGDIFont( NULL ),
- OldGDIBitmap( NULL ),
- GDIFont( NULL ),
- GDIBitmap( NULL ),
- GDIBitmapBits ( NULL ),
- MemDC( NULL ),
- CurrPixelOffset( 0 ),
- PointSize( 0 ),
- CharHeight( 0 ),
- UnicodeCharArray( NULL ),
- FirstUnicodeChar( 0xFFFF ),
- LastUnicodeChar( 0 ),
- IsBold (false),
- BufferList(sizeof(PreAllocatedBufferList)/sizeof(uint16*),PreAllocatedBufferList)
- {
- ::memset( ASCIICharArray, 0, sizeof (ASCIICharArray) );
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // ~FontCharsClass
- //
- ////////////////////////////////////////////////////////////////////////////////////
- FontCharsClass::~FontCharsClass (void)
- {
- for (int i=0;i<BufferList.Count(); ++i) {
- delete [] BufferList[i];
- }
- BufferList.Reset_Active();
- Free_GDI_Font();
- Free_Character_Arrays();
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Get_Char_Data
- //
- ////////////////////////////////////////////////////////////////////////////////////
- const FontCharsClass::CharDataStruct *
- FontCharsClass::Get_Char_Data (WCHAR ch)
- {
- const CharDataStruct *retval = NULL;
- if ( ch < 256 ) {
- retval = ASCIICharArray[ch];
- } else {
- Grow_Unicode_Array( ch );
- retval = UnicodeCharArray[ch - FirstUnicodeChar];
- }
- //
- // If the character wasn't found, then add it to our list
- //
- if ( retval == NULL ) {
- retval = Store_GDI_Char( ch );
- }
- WWASSERT( retval->Value == ch );
- return retval;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Get_Char_Width
- //
- ////////////////////////////////////////////////////////////////////////////////////
- int
- FontCharsClass::Get_Char_Width (WCHAR ch)
- {
- const CharDataStruct * data = Get_Char_Data( ch );
- if ( data != NULL ) {
- return data->Width;
- }
- return 0;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Get_Char_Spacing
- //
- ////////////////////////////////////////////////////////////////////////////////////
- int
- FontCharsClass::Get_Char_Spacing (WCHAR ch)
- {
- const CharDataStruct * data = Get_Char_Data( ch );
- if ( data != NULL ) {
- if ( data->Width != 0 ) {
- return data->Width + 1;
- }
- }
- return 0;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Blit_Char
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- FontCharsClass::Blit_Char (WCHAR ch, uint16 *dest_ptr, int dest_stride, int x, int y)
- {
- const CharDataStruct * data = Get_Char_Data( ch );
- if ( data != NULL && data->Width != 0 ) {
- //
- // Setup the src and destination pointers
- //
- int dest_inc = (dest_stride >> 1);
- uint16 *src_ptr = data->Buffer;
- dest_ptr += (dest_inc * y) + x;
- //
- // Simply copy the data from the src buffer to the destination
- //
- for ( int row = 0; row < CharHeight; row ++ ) {
- for ( int col = 0; col < data->Width; col ++ ) {
- dest_ptr[col] = *src_ptr++;
- }
- dest_ptr += dest_inc;
- }
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Store_GDI_Char
- //
- ////////////////////////////////////////////////////////////////////////////////////
- const FontCharsClass::CharDataStruct *
- FontCharsClass::Store_GDI_Char (WCHAR ch)
- {
- int width = PointSize * 2;
- int height = PointSize * 2;
- //
- // Get the size of the character we just drew
- //
- SIZE char_size = { 0 };
- ::GetTextExtentPoint32W( MemDC, &ch, 1, &char_size );
- int x_pos = 0;
- //
- // HACK HACK -- With the default font that Renegade uses the
- // W and V characters need to be moved over one pixel.
- //
- if ( (ch == 'W' || ch == 'V') && (GDIFontName.Compare_No_Case ("Arial MT") == 0) ) {
- x_pos = 1;
- char_size.cx += 1;
- }
- //
- // Draw the character into the memory DC
- //
- RECT rect = { 0, 0, width, height };
- ::ExtTextOutW( MemDC, x_pos, 0, ETO_OPAQUE, &rect, &ch, 1, NULL);
- //
- // Get a pointer to the surface that this character should use
- //
- Update_Current_Buffer( char_size.cx );
- uint16 *curr_buffer = BufferList[BufferList.Count () - 1];
- curr_buffer += CurrPixelOffset;
- //
- // Copy the BMP contents to the buffer
- //
- int stride = (((width * 3) + 3) & ~3);
- for (int row = 0; row < char_size.cy; row ++) {
- //
- // Compute the indices into the BMP and surface
- //
- int index = (row * stride);
- //
- // Loop over each column
- //
- for (int col = 0; col < char_size.cx; col ++) {
- //
- // Get the pixel color at this location
- //
- uint8 pixel_value = GDIBitmapBits[index];
- index += 3;
- uint16 pixel_color = 0;
- if (pixel_value != 0) {
- pixel_color = 0x0FFF;
- }
- //
- // Convert the pixel intensity from 8bit to 4bit and
- // store it in our buffer
- //
- uint8 alpha_value = ((pixel_value >> 4) & 0xF);
- *curr_buffer ++ = pixel_color | (alpha_value << 12);
- }
- }
- //
- // Save information about this character in our list
- //
- CharDataStruct *char_data = new CharDataStruct;
- char_data->Value = ch;
- char_data->Width = char_size.cx;
- char_data->Buffer = BufferList[BufferList.Count () - 1] + CurrPixelOffset;
- //
- // Insert this character into our array
- //
- if ( ch < 256 ) {
- ASCIICharArray[ch] = char_data;
- } else {
- UnicodeCharArray[ch - FirstUnicodeChar] = char_data;
- }
- //
- // Advance the character position
- //
- CurrPixelOffset += (char_size.cx * CharHeight);
- //
- // Return the index of the entry we just added
- //
- return char_data;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Update_Current_Buffer
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- FontCharsClass::Update_Current_Buffer (int char_width)
- {
- //
- // Check to see if we need to allocate a new buffer
- //
- bool needs_new_buffer = (BufferList.Count () == 0);
- if (needs_new_buffer == false) {
- //
- // Would we extend past this buffer?
- //
- if ( (CurrPixelOffset + (char_width * CharHeight)) > CHAR_BUFFER_LEN ) {
- needs_new_buffer = true;
- }
- }
- //
- // Do we need to create a new surface?
- //
- if (needs_new_buffer) {
- uint16 *new_buffer = new uint16[CHAR_BUFFER_LEN];
- BufferList.Add( new_buffer );
- CurrPixelOffset = 0;
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Create_GDI_Font
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- FontCharsClass::Create_GDI_Font (const char *font_name)
- {
- HDC screen_dc = ::GetDC (NULL);
- //
- // Calculate the height of the font in logical units
- //
- int font_height = -MulDiv (PointSize, ::GetDeviceCaps (screen_dc, LOGPIXELSY), 72);
- //
- // Create the Windows font
- //
- DWORD bold = IsBold ? FW_BOLD : FW_NORMAL;
- DWORD italic = 0;
- DWORD charset;
- // Map the current code page to a font character set.
- switch (GetACP()) {
- // Chinese.
- case 936:
- case 950:
- charset = CHINESEBIG5_CHARSET;
- break;
- // Japanese.
- case 932:
- charset = SHIFTJIS_CHARSET;
- break;
- // Korean.
- case 949:
- charset = HANGUL_CHARSET;
- break;
- // Anything else.
- default:
- charset = DEFAULT_CHARSET;
- break;
- }
- GDIFont = ::CreateFont (font_height, 0, 0, 0, bold, italic,
- FALSE, FALSE, charset, OUT_DEFAULT_PRECIS,
- CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
- VARIABLE_PITCH, font_name);
- //
- // Set-up the fields of the BITMAPINFOHEADER
- // Note: Top-down DIBs use negative height in Win32.
- //
- BITMAPINFOHEADER bitmap_info = { 0 };
- bitmap_info.biSize = sizeof (BITMAPINFOHEADER);
- bitmap_info.biWidth = PointSize * 2;
- bitmap_info.biHeight = -(PointSize * 2);
- bitmap_info.biPlanes = 1;
- bitmap_info.biBitCount = 24;
- bitmap_info.biCompression = BI_RGB;
- bitmap_info.biSizeImage = ((PointSize * PointSize * 4) * 3);
- bitmap_info.biXPelsPerMeter = 0;
- bitmap_info.biYPelsPerMeter = 0;
- bitmap_info.biClrUsed = 0;
- bitmap_info.biClrImportant = 0;
- //
- // Create a bitmap that we can access the bits directly of
- //
- GDIBitmap = ::CreateDIBSection ( screen_dc,
- (const BITMAPINFO *)&bitmap_info,
- DIB_RGB_COLORS,
- (void **)&GDIBitmapBits,
- NULL,
- 0L);
- //
- // Create a device context we can select the font and bitmap into
- //
- MemDC = ::CreateCompatibleDC (NULL);
- //
- // Now select the BMP and font into the DC
- //
- OldGDIBitmap = (HBITMAP)::SelectObject (MemDC, GDIBitmap);
- OldGDIFont = (HFONT)::SelectObject (MemDC, GDIFont);
- ::SetBkColor (MemDC, RGB (0, 0, 0));
- ::SetTextColor (MemDC, RGB (255, 255, 255));
- //
- // Lookup the pixel height of the font
- //
- TEXTMETRIC text_metric = { 0 };
- ::GetTextMetrics (MemDC, &text_metric);
- CharHeight = text_metric.tmHeight;
- //
- // Release our temporary screen DC
- //
- ::ReleaseDC (NULL, screen_dc);
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Free_GDI_Font
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- FontCharsClass::Free_GDI_Font (void)
- {
- //
- // Select the old font back into the DC and delete
- // our font object
- //
- if ( GDIFont != NULL ) {
- ::SelectObject( MemDC, OldGDIFont );
- ::DeleteObject( GDIFont );
- GDIFont = NULL;
- }
- //
- // Select the old bitmap back into the DC and delete
- // our bitmap object
- //
- if ( GDIBitmap != NULL ) {
- ::SelectObject( MemDC, OldGDIBitmap );
- ::DeleteObject( GDIBitmap );
- GDIBitmap = NULL;
- }
- //
- // Delete our memory DC
- //
- if ( MemDC != NULL ) {
- ::DeleteDC( MemDC );
- MemDC = NULL;
- }
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Initialize_GDI_Font
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- FontCharsClass::Initialize_GDI_Font (const char *font_name, int point_size, bool is_bold)
- {
- //
- // Build a unique name from the font name and its size
- //
- Name.Format ("%s%d", font_name, point_size);
- //
- // Remember these settings
- //
- GDIFontName = font_name;
- PointSize = point_size;
- IsBold = is_bold;
- //
- // Create the actual font object
- //
- Create_GDI_Font (font_name);
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Is_Font
- //
- ////////////////////////////////////////////////////////////////////////////////////
- bool
- FontCharsClass::Is_Font (const char *font_name, int point_size, bool is_bold)
- {
- bool retval = false;
- //
- // Check to see if both the name and height matches...
- //
- if ( (GDIFontName.Compare_No_Case (font_name) == 0) &&
- (point_size == PointSize) &&
- (is_bold == IsBold))
- {
- retval = true;
- }
- return retval;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Grow_Unicode_Array
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- FontCharsClass::Grow_Unicode_Array (WCHAR ch)
- {
- //
- // Don't do anything if character is in the ASCII range
- //
- if ( ch < 256 ) {
- return ;
- }
- //
- // Don't do anything if character is in the currently allocated range
- //
- if ( ch >= FirstUnicodeChar && ch <= LastUnicodeChar ) {
- return ;
- }
- uint16 first_index = min( FirstUnicodeChar, ch );
- uint16 last_index = max( LastUnicodeChar, ch );
- uint16 count = (last_index - first_index) + 1;
- //
- // Allocate enough memory to hold the new cells
- //
- CharDataStruct **new_array = new CharDataStruct *[count];
- ::memset (new_array, 0, sizeof (CharDataStruct *) * count);
- //
- // Copy the contents of the old array into the new array
- //
- if ( UnicodeCharArray != NULL ) {
- int start_offset = (FirstUnicodeChar - first_index);
- int old_count = (LastUnicodeChar - FirstUnicodeChar) + 1;
- ::memcpy (&new_array[start_offset], UnicodeCharArray, sizeof (CharDataStruct *) * old_count);
- //
- // Delete the old array
- //
- delete [] UnicodeCharArray;
- UnicodeCharArray = NULL;
- }
- FirstUnicodeChar = first_index;
- LastUnicodeChar = last_index;
- UnicodeCharArray = new_array;
- return ;
- }
- ////////////////////////////////////////////////////////////////////////////////////
- //
- // Free_Character_Arrays
- //
- ////////////////////////////////////////////////////////////////////////////////////
- void
- FontCharsClass::Free_Character_Arrays (void)
- {
- if ( UnicodeCharArray != NULL ) {
- int count = (LastUnicodeChar - FirstUnicodeChar) + 1;
- //
- // Delete each member of the unicode array
- //
- for (int index = 0; index < count; index ++) {
- if ( UnicodeCharArray[index] != NULL ) {
- delete UnicodeCharArray[index];
- UnicodeCharArray[index] = NULL;
- }
- }
- //
- // Delete the array itself
- //
- delete [] UnicodeCharArray;
- UnicodeCharArray = NULL;
- }
- //
- // Delete each member of the ascii character array
- //
- for (int index = 0; index < 256; index ++) {
- if ( ASCIICharArray[index] != NULL ) {
- delete ASCIICharArray[index];
- ASCIICharArray[index] = NULL;
- }
- }
- return ;
- }
|