From 6edda5db659a67a119b3469ad92080e168ed2944 Mon Sep 17 00:00:00 2001 From: tobigun Date: Sun, 19 Oct 2008 11:36:41 +0000 Subject: offscreen rendering removed: - fixes zoom errors - fixes missing lyric lines if window is too small - better text quality - fixes some other errors git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1457 b956fd51-792f-4845-bead-9b4dfca2ff2c --- src/base/TextGL.pas | 150 +-------- src/base/UGraphic.pas | 12 +- src/base/ULyrics.pas | 765 ++++++++++++++++++-------------------------- src/screens/UScreenSing.pas | 19 +- 4 files changed, 334 insertions(+), 612 deletions(-) (limited to 'src') diff --git a/src/base/TextGL.pas b/src/base/TextGL.pas index 8569fadf..03351adf 100644 --- a/src/base/TextGL.pas +++ b/src/base/TextGL.pas @@ -37,7 +37,6 @@ uses gl, SDL, UTexture, -// SDL_ttf, ULog; procedure BuildFont; // build our bitmap font @@ -45,6 +44,7 @@ procedure KillFont; // delete the font function glTextWidth(text: PChar): real; // returns text width procedure glPrintLetter(letter: char); procedure glPrint(text: pchar); // custom GL "Print" routine +procedure ResetFont(); // reset font settings of active font procedure SetFontPos(X, Y: real); // sets X and Y procedure SetFontZ(Z: real); // sets Z procedure SetFontSize(Size: real); @@ -209,13 +209,19 @@ function glTextWidth(text: pchar): real; var Letter: char; i: integer; + Font: PFont; begin Result := 0; + Font := @Fonts[ActFont]; + for i := 0 to Length(text) -1 do begin Letter := Text[i]; - Result := Result + Fonts[ActFont].Width[Ord(Letter)] * Fonts[ActFont].Tex.H / 30 * Fonts[ActFont].AspectW; + Result := Result + Font.Width[Ord(Letter)] * Font.Tex.H / 30 * Font.AspectW; end; + + if ((Result > 0) and Font.Italic) then + Result := Result + 12 * Font.Tex.H / 60 * Font.AspectW; end; procedure glPrintLetter(Letter: char); @@ -332,6 +338,14 @@ begin end; end; +procedure ResetFont(); +begin + SetFontPos(0, 0); + SetFontZ(0); + SetFontItalic(False); + SetFontReflection(False, 0); +end; + procedure SetFontPos(X, Y: real); begin Fonts[ActFont].Tex.X := X; @@ -374,136 +388,4 @@ begin Fonts[ActFont].Blend := Enable; end; - - - -(* - I uncommented this, because it was some kind of after hour hack together with blindy -it's actually just a prove of concept, as it's having some flaws -- instead nice and clean ttf code should be placed here :) - -{$IFDEF FPC} - {$ASMMODE Intel} -{$ENDIF} - -function NextPowerOfTwo(Value: integer): integer; -begin - Result:= 1; -{$IF Defined(CPUX86_64)} - asm - mov rcx, -1 - bsr rcx, Value - inc rcx - shl Result, cl - end; -{$ELSEIF Defined(CPU386) or Defined(CPUI386)} - asm - mov ecx, -1 - bsr ecx, Value - inc ecx - shl Result, cl - end; -{$ELSE} - while (Result <= Value) do - Result := 2 * Result; -{$IFEND} -end; - -function LoadFont(FileName: PAnsiChar; PointSize: integer):PTTF_Font; -begin - if (FileExists(FileName)) then - begin - Result := TTF_OpenFont( FileName, PointSize ); - end - else - begin - Log.LogStatus('ERROR Could not find font in ' + FileName , ''); - ShowMessage( 'ERROR Could not find font in ' + FileName ); - Result := nil; - end; -end; - -function RenderText(font: PTTF_Font; Text:PAnsiChar; Color: Cardinal): PSDL_Surface; -var - clr : TSDL_color; -begin - clr.r := ((Color and $ff0000) shr 16 ) div 255; - clr.g := ((Color and $ff00 ) shr 8 ) div 255; - clr.b := ( Color and $ff ) div 255 ; - - result := TTF_RenderText_Blended( font, text, cLr); -end; - -procedure printrandomtext(); -var - stext,intermediary : PSDL_surface; - clrFg, clrBG : TSDL_color; - texture : Gluint; - font : PTTF_Font; - w,h : integer; -begin - - font := LoadFont('fonts\comicbd.ttf', 42); - - clrFg.r := 255; - clrFg.g := 255; - clrFg.b := 255; - clrFg.unused := 255; - - clrBg.r := 255; - clrbg.g := 0; - clrbg.b := 255; - clrbg.unused := 0; - - sText := RenderText(font, 'katzeeeeeee', $fe198e); - //sText := TTF_RenderText_Blended( font, 'huuuuuuuuuund', clrFG); - - // Convert the rendered text to a known format - w := nextpoweroftwo(sText.w); - h := nextpoweroftwo(sText.h); - - intermediary := SDL_CreateRGBSurface(0, w, h, 32, - $000000ff, $0000ff00, $00ff0000, $ff000000); - - SDL_SetAlpha(intermediary, 0, 255); - SDL_SetAlpha(sText, 0, 255); - SDL_BlitSurface(sText, nil, intermediary, nil); - - glGenTextures(1, @texture); - - glBindTexture(GL_TEXTURE_2D, texture); - - glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, intermediary.pixels); -// on big endian machines (powerpc) this may need to be changed to -// Needs to be tests. KaMiSchi Sept 2008 -// glTexImage2D(GL_TEXTURE_2D, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, intermediary.pixels); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - glEnable(GL_TEXTURE_2D); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); - glBindTexture(GL_TEXTURE_2D, texture); - glColor4f(1, 0, 1, 1); - - glbegin(gl_quads); - glTexCoord2f(0, 0); glVertex2f(200 , 300 ); - glTexCoord2f(0, sText.h/h); glVertex2f(200 , 300 + sText.h); - glTexCoord2f(sText.w/w, sText.h/h); glVertex2f(200 + sText.w, 300 + sText.h); - glTexCoord2f(sText.w/w, 0); glVertex2f(200 + sText.w, 300 ); - glEnd; - glfinish(); - glDisable(GL_BLEND); - gldisable(gl_texture_2d); - - SDL_FreeSurface(sText); - SDL_FreeSurface(intermediary); - glDeleteTextures(1, @texture); - TTF_CloseFont(font); - -end; -*) - - end. diff --git a/src/base/UGraphic.pas b/src/base/UGraphic.pas index 1134333b..a6620c0d 100644 --- a/src/base/UGraphic.pas +++ b/src/base/UGraphic.pas @@ -232,7 +232,17 @@ const Skin_OscG = 0; Skin_OscB = 0; - Skin_LyricsT = 494; // 500 / 510 / 400 + // TODO: add to theme ini file + Skin_LyricsT = 493; + Skin_LyricsUpperX = 80; + Skin_LyricsUpperW = 640; + Skin_LyricsUpperY = Skin_LyricsT; + Skin_LyricsUpperH = 41; + Skin_LyricsLowerX = 80; + Skin_LyricsLowerW = 640; + Skin_LyricsLowerY = Skin_LyricsT + Skin_LyricsUpperH + 1; + Skin_LyricsLowerH = 41; + Skin_SpectrumT = 470; Skin_SpectrumBot = 570; Skin_SpectrumH = 100; diff --git a/src/base/ULyrics.pas b/src/base/ULyrics.pas index 46354cbe..ece9f1d4 100644 --- a/src/base/ULyrics.pas +++ b/src/base/ULyrics.pas @@ -44,32 +44,31 @@ type // stores two textures for enabled/disabled states TPlayerIconTex = array [0..1] of TTexture; + TLyricsEffect = (lfxSimple, lfxZoom, lfxSlide, lfxBall, lfxShift); + PLyricWord = ^TLyricWord; TLyricWord = record - X: Real; // left corner - Width: Real; // width - Start: Cardinal; // start of the word in quarters (beats) - Length: Cardinal; // length of the word in quarters - Text: String; // text - Freestyle: Boolean; // is freestyle? + X: real; // left corner + Width: real; // width + Start: cardinal; // start of the word in quarters (beats) + Length: cardinal; // length of the word in quarters + Text: string; // text + Freestyle: boolean; // is freestyle? end; - ALyricWord = array of TLyricWord; + TLyricWordArray = array of TLyricWord; TLyricLine = class public - Text: String; // text - Tex: glUInt; // texture of the text - Width: Real; // width - Size: Byte; // fontsize - Words: ALyricWord; // words in this line - CurWord: Integer; // current active word idx (only valid if line is active) - Start: Integer; // start of this line in quarters (Note: negative start values are possible due to gap) - StartNote: Integer; // start of the first note of this line in quarters - Length: Integer; // length in quarters (from start of first to the end of the last note) - HasFreestyle: Boolean; // one or more word are freestyle? - CountFreestyle: Integer; // how often there is a change from freestyle to non freestyle in this line - Players: Byte; // players that should sing that line (bitset, Player1: 1, Player2: 2, Player3: 4) - LastLine: Boolean; // is this the last line of the song? + Text: string; // text + Width: real; // width + Height: real; // height + Words: TLyricWordArray; // words in this line + CurWord: integer; // current active word idx (only valid if line is active) + Start: integer; // start of this line in quarters (Note: negative start values are possible due to gap) + StartNote: integer; // start of the first note of this line in quarters + Length: integer; // length in quarters (from start of first to the end of the last note) + Players: byte; // players that should sing that line (bitset, Player1: 1, Player2: 2, Player3: 4) + LastLine: boolean; // is this the last line of the song? constructor Create(); destructor Destroy(); override; @@ -78,7 +77,7 @@ type TLyricEngine = class private - LastDrawBeat: Real; + LastDrawBeat: real; UpperLine: TLyricLine; // first line displayed (top) LowerLine: TLyricLine; // second lind displayed (bottom) QueueLine: TLyricLine; // third line (will be displayed when lower line is finished) @@ -86,80 +85,79 @@ type IndicatorTex: TTexture; // texture for lyric indikator BallTex: TTexture; // texture of the ball for the lyric effect - QueueFull: Boolean; // set to true if the queue is full and a line will be replaced with the next AddLine - LCounter: Word; // line counter + QueueFull: boolean; // set to true if the queue is full and a line will be replaced with the next AddLine + LCounter: integer; // line counter // duet mode - textures for player icons // FIXME: do not use a fixed player count, use MAX_PLAYERS instead PlayerIconTex: array[0..5] of TPlayerIconTex; - //Some helper Procedures for Lyric Drawing - procedure DrawLyrics (Beat: Real); - procedure DrawLyricsLine(X, W, Y: Real; Size: Byte; Line: TLyricLine; Beat: Real); - procedure DrawPlayerIcon(Player: Byte; Enabled: Boolean; X, Y, Size, Alpha: Real); - procedure DrawBall(XBall, YBall, Alpha:Real); + // Some helper procedures for lyric drawing + procedure DrawLyrics (Beat: real); + procedure UpdateLineMetrics(LyricLine: TLyricLine); + procedure DrawLyricsWords(LyricLine: TLyricLine; X, Y: real; StartWord, EndWord: integer); + procedure DrawLyricsLine(X, W, Y, H: real; Line: TLyricLine; Beat: real); + procedure DrawPlayerIcon(Player: byte; Enabled: boolean; X, Y: real; Size, Alpha: real); + procedure DrawBall(XBall, YBall, Alpha: real); public // positions, line specific settings - UpperLineX: Real; //X Start Pos of UpperLine - UpperLineW: Real; //Width of UpperLine with Icon(s) and Text - UpperLineY: Real; //Y Start Pos of UpperLine - UpperLineSize: Byte; //Max Size of Lyrics Text in UpperLine + UpperLineX: real; // X start-pos of UpperLine + UpperLineW: real; // Width of UpperLine with icon(s) and text + UpperLineY: real; // Y start-pos of UpperLine + UpperLineH: real; // Max. font-size of lyrics text in UpperLine - LowerLineX: Real; //X Start Pos of LowerLine - LowerLineW: Real; //Width of LowerLine with Icon(s) and Text - LowerLineY: Real; //Y Start Pos of LowerLine - LowerLineSize: Byte; //Max Size of Lyrics Text in LowerLine + LowerLineX: real; // X start-pos of LowerLine + LowerLineW: real; // Width of LowerLine with icon(s) and text + LowerLineY: real; // Y start-pos of LowerLine + LowerLineH: real; // Max. font-size of lyrics text in LowerLine // display propertys - LineColor_en: TRGBA; //Color of Words in an Enabled Line - LineColor_dis: TRGBA; //Color of Words in a Disabled Line - LineColor_act: TRGBA; //Color of teh active Word - FontStyle: Byte; //Font for the Lyric Text - FontReSize: Boolean; //ReSize Lyrics if they don't fit Screen + LineColor_en: TRGBA; // Color of words in an enabled line + LineColor_dis: TRGBA; // Color of words in a disabled line + LineColor_act: TRGBA; // Color of the active word + FontStyle: byte; // Font for the lyric text { // currently not used - FadeInEffect: Byte; //Effect for Line Fading in: 0: No Effect; 1: Fade Effect; 2: Move Upwards from Bottom to Pos - FadeOutEffect: Byte; //Effect for Line Fading out: 0: No Effect; 1: Fade Effect; 2: Move Upwards + FadeInEffect: byte; // Effect for line fading in: 0: No Effect; 1: Fade Effect; 2: Move Upwards from Bottom to Pos + FadeOutEffect: byte; // Effect for line fading out: 0: No Effect; 1: Fade Effect; 2: Move Upwards } - UseLinearFilter: Boolean; //Should Linear Tex Filter be used - // song specific settings - BPM: Real; - Resolution: Integer; + BPM: real; + Resolution: integer; // properties to easily read options of this class - property IsQueueFull: Boolean read QueueFull; // line in queue? - property LineCounter: Word read LCounter; // lines that were progressed so far (after last clear) + property IsQueueFull: boolean read QueueFull; // line in queue? + property LineCounter: integer read LCounter; // lines that were progressed so far (after last clear) procedure AddLine(Line: PLine); // adds a line to the queue, if there is space - procedure Draw (Beat: Real); // draw the current (active at beat) lyrics + procedure Draw (Beat: real); // draw the current (active at beat) lyrics // clears all cached song specific information - procedure Clear(cBPM: Real = 0; cResolution: Integer = 0); + procedure Clear(cBPM: real = 0; cResolution: integer = 0); function GetUpperLine(): TLyricLine; function GetLowerLine(): TLyricLine; - function GetUpperLineIndex(): Integer; + function GetUpperLineIndex(): integer; - constructor Create; overload; - constructor Create(ULX,ULY,ULW,ULS,LLX,LLY,LLW,LLS: Real); overload; + constructor Create(ULX, ULY, ULW, ULH, LLX, LLY, LLW, LLH: real); procedure LoadTextures; destructor Destroy; override; end; implementation -uses SysUtils, - USkins, - TextGL, - UGraphic, - UDisplay, - ULog, - math, - UIni; +uses + SysUtils, + USkins, + TextGL, + UGraphic, + UDisplay, + ULog, + math, + UIni; { TLyricLine } @@ -190,20 +188,17 @@ begin SetLength(Words, 0); CurWord := -1; - - HasFreestyle := False; - CountFreestyle := 0; end; { TLyricEngine } -//--------------- -// Create - Constructor, just get Memory -//--------------- -constructor TLyricEngine.Create; +{** + * Initializes the engine. + *} +constructor TLyricEngine.Create(ULX, ULY, ULW, ULH, LLX, LLY, LLW, LLH: real); begin - inherited; + inherited Create(); BPM := 0; Resolution := 0; @@ -214,31 +209,25 @@ begin LowerLine := TLyricLine.Create; QueueLine := TLyricLine.Create; - UseLinearFilter := True; LastDrawBeat := 0; -end; -constructor TLyricEngine.Create(ULX,ULY,ULW,ULS,LLX,LLY,LLW,LLS:Real); -begin - Create; - UpperLineX := ULX; UpperLineW := ULW; UpperLineY := ULY; - UpperLineSize := Trunc(ULS); + UpperLineH := ULH; LowerLineX := LLX; LowerLineW := LLW; LowerLineY := LLY; - LowerLineSize := Trunc(LLS); + LowerLineH := LLH; LoadTextures; end; -//--------------- -// Destroy - Frees Memory -//--------------- +{** + * Frees memory. + *} destructor TLyricEngine.Destroy; begin UpperLine.Free; @@ -247,10 +236,10 @@ begin inherited; end; -//--------------- -// Clear - Clears all cached Song specific Information -//--------------- -procedure TLyricEngine.Clear(cBPM: Real; cResolution: Integer); +{** + * Clears all cached Song specific Information. + *} +procedure TLyricEngine.Clear(cBPM: real; cResolution: integer); begin BPM := cBPM; Resolution := cResolution; @@ -261,31 +250,14 @@ begin end; -//--------------- -// LoadTextures - Load Player Textures and Create Lyric Textures -//--------------- +{** + * Loads textures needed for the drawing the lyrics, + * player icons, a ball for the ball effect and the lyric indicator. + *} procedure TLyricEngine.LoadTextures; var I: Integer; - - function CreateLineTex: glUint; - begin - // generate and bind Texture - glGenTextures(1, @Result); - glBindTexture(GL_TEXTURE_2D, Result); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - if UseLinearFilter then - begin - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - end; - end; - begin - // lyric indicator (bar that indicates when the line start) IndicatorTex := Texture.LoadTexture(Skin.GetTextureFileName('LyricHelpBar'), TEXTURE_TYPE_TRANSPARENT, $FF00FF); @@ -298,42 +270,21 @@ begin PlayerIconTex[I][0] := Texture.LoadTexture(Skin.GetTextureFileName('LyricIcon_P' + InttoStr(I+1)), TEXTURE_TYPE_TRANSPARENT, 0); PlayerIconTex[I][1] := Texture.LoadTexture(Skin.GetTextureFileName('LyricIconD_P' + InttoStr(I+1)), TEXTURE_TYPE_TRANSPARENT, 0); end; - - // create line textures - UpperLine.Tex := CreateLineTex; - LowerLine.Tex := CreateLineTex; - QueueLine.Tex := CreateLineTex; end; - -//--------------- -// AddLine - Adds LyricLine to queue -// The LyricEngine stores three lines in its queue: -// UpperLine: the upper line displayed in the lyrics -// LowerLine: the lower line displayed in the lyrics -// QueueLine: an offscreen line that precedes LowerLine -// If the queue is full the next call to AddLine will replace UpperLine with -// LowerLine, LowerLine with QueueLine and QueueLine with the Line parameter. -//--------------- +{** + * Adds LyricLine to queue. + * The LyricEngine stores three lines in its queue: + * UpperLine: the upper line displayed in the lyrics + * LowerLine: the lower line displayed in the lyrics + * QueueLine: an offscreen line that precedes LowerLine + * If the queue is full the next call to AddLine will replace UpperLine with + * LowerLine, LowerLine with QueueLine and QueueLine with the Line parameter. + *} procedure TLyricEngine.AddLine(Line: PLine); var LyricLine: TLyricLine; - PosX: Real; - I: Integer; - CurWord: PLyricWord; - RenderPass: Integer; - - function CalcWidth(LyricLine: TLyricLine): Real; - begin - Result := glTextWidth(PChar(LyricLine.Text)); - - Result := Result + (LyricLine.CountFreestyle * 10); - - // if the line ends with a freestyle not, then leave the place to finish to draw the text italic - if (LyricLine.Words[High(LyricLine.Words)].Freestyle) then - Result := Result + 12; - end; - + I: integer; begin // only add lines, if there is space if not IsQueueFull then @@ -352,7 +303,7 @@ begin end else begin // rotate lines (round-robin-like) - LyricLine := UpperLine; + LyricLine := UpperLine; UpperLine := LowerLine; LowerLine := QueueLine; QueueLine := LyricLine; @@ -381,160 +332,42 @@ begin LyricLine.Words[I].Text := Line.Note[I].Text; LyricLine.Words[I].Freestyle := Line.Note[I].NoteType = ntFreestyle; - LyricLine.HasFreestyle := LyricLine.HasFreestyle or LyricLine.Words[I].Freestyle; - LyricLine.Text := LyricLine.Text + LyricLine.Words[I].Text; - - if (I > 0) and - LyricLine.Words[I-1].Freestyle and - not LyricLine.Words[I].Freestyle then - begin - Inc(LyricLine.CountFreestyle); - end; - end; - - // set font params - SetFontStyle(FontStyle); - SetFontPos(0, 0); - LyricLine.Size := UpperLineSize; - SetFontSize(LyricLine.Size); - SetFontItalic(False); - SetFontReflection(False, 0); - glColor4f(1, 1, 1, 1); - - // change fontsize to fit the screen - LyricLine.Width := CalcWidth(LyricLine); - while (LyricLine.Width > UpperLineW) do - begin - Dec(LyricLine.Size); - - if (LyricLine.Size <=1) then - Break; - - SetFontSize(LyricLine.Size); - LyricLine.Width := CalcWidth(LyricLine); + LyricLine.Text := LyricLine.Text + LyricLine.Words[I].Text; end; - // Offscreen rendering of LyricTexture: - // First we will create a white transparent background to draw on. - // If the text was simply drawn to the screen with blending, the translucent - // parts of the text would be merged with the current color of the background. - // This would result in a texture that partly contains the screen we are - // drawing on. This will be visible in the fonts outline when the LyricTexture - // is drawn to the screen later. - // So we have to draw the text in TWO passes. - // At the first pass we copy the characters to the back-buffer in such a way - // that preceding characters are not repainted by following chars (otherwise - // some characters would be blended twice in the 2nd pass). - // To achieve this we disable blending and enable the depth-test. The z-buffer - // is used as a mask or replacement for the missing stencil-buffer. A z-value - // of 1 means the pixel was not assigned yet, whereas 0 stands for a pixel - // that was already drawn on. In addition we set the depth-func in such a way - // that assigned pixels (0) will not be drawn a second time. - // At the second pass we draw the text with blending and without depth-test. - // This will blend overlapping characters but not the background as it was - // repainted in the first pass. - - glPushAttrib(GL_VIEWPORT_BIT or GL_DEPTH_BUFFER_BIT); - glViewPort(0, 0, 800, 600); - glClearColor(0, 0, 0, 0); - glDepthRange(0, 1); - glClearDepth(1); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); - - SetFontZ(0); - - // assure blending is off and the correct depth-func is enabled - glDisable(GL_BLEND); - glDepthFunc(GL_LESS); - - // we need two passes to draw the font onto the screen. - for RenderPass := 0 to 1 do - begin - if (RenderPass = 0) then - begin - // first pass: simply copy each character to the screen without overlapping. - SetFontBlend(false); - glEnable(GL_DEPTH_TEST); - end - else - begin - // second pass: now we will blend overlapping characters. - SetFontBlend(true); - glDisable(GL_DEPTH_TEST); - end; - - PosX := 0; - - // set word positions and line size and draw the line to the back-buffer - for I := 0 to High(LyricLine.Words) do - begin - CurWord := @LyricLine.Words[I]; - - SetFontItalic(CurWord.Freestyle); - - CurWord.X := PosX; - - // Draw Lyrics - SetFontPos(PosX, 0); - glPrint(PChar(CurWord.Text)); - - CurWord.Width := glTextWidth(PChar(CurWord.Text)); - if CurWord.Freestyle then - begin - if (I < High(LyricLine.Words)) and not LyricLine.Words[I+1].Freestyle then - CurWord.Width := CurWord.Width + 10 - else if (I = High(LyricLine.Words)) then - CurWord.Width := CurWord.Width + 12; - end; - PosX := PosX + CurWord.Width; - end; - end; - - // copy back-buffer to texture - glBindTexture(GL_TEXTURE_2D, LyricLine.Tex); - // FIXME: this does not work this way - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 600-64, 1024, 64, 0); - if (glGetError() <> GL_NO_ERROR) then - Log.LogError('Creation of Lyrics-texture failed', 'TLyricEngine.AddLine'); - - // restore OpenGL state - glPopAttrib(); - - // clear buffer (use white to avoid flimmering if no cover/video is available) - glClearColor(1, 1, 1, 1); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); - end; // if (Line <> nil) and (Length(Line.Note) > 0) + UpdateLineMetrics(LyricLine); + end; // increase the counter Inc(LCounter); end; - -//--------------- -// Draw - Procedure Draws Lyrics; Beat is curent Beat in Quarters -// Draw just manage the Lyrics, drawing is done by a call of DrawLyrics -//--------------- -procedure TLyricEngine.Draw(Beat: Real); +{** + * Draws Lyrics. + * Draw just manages the Lyrics, drawing is done by a call of DrawLyrics. + * @param Beat: current Beat in Quarters + *} +procedure TLyricEngine.Draw(Beat: real); begin DrawLyrics(Beat); LastDrawBeat := Beat; end; -//--------------- -// DrawLyrics(private) - Helper for Draw; main Drawing procedure -//--------------- -procedure TLyricEngine.DrawLyrics(Beat: Real); +{** + * Main Drawing procedure. + *} +procedure TLyricEngine.DrawLyrics(Beat: real); begin - DrawLyricsLine(UpperLineX, UpperLineW, UpperlineY, 15, Upperline, Beat); - DrawLyricsLine(LowerLineX, LowerLineW, LowerlineY, 15, Lowerline, Beat); + DrawLyricsLine(UpperLineX, UpperLineW, UpperLineY, UpperLineH, UpperLine, Beat); + DrawLyricsLine(LowerLineX, LowerLineW, LowerLineY, LowerLineH, LowerLine, Beat); end; -//--------------- -// DrawPlayerIcon(private) - Helper for Draw; Draws a Playericon -//--------------- -procedure TLyricEngine.DrawPlayerIcon(Player: Byte; Enabled: Boolean; X, Y, Size, Alpha: Real); +{** + * Draws a Player's icon. + *} +procedure TLyricEngine.DrawPlayerIcon(Player: byte; Enabled: boolean; X, Y: real; Size, Alpha: real); var - IEnabled: Byte; + IEnabled: byte; begin if Enabled then IEnabled := 0 @@ -558,10 +391,10 @@ begin glDisable(GL_TEXTURE_2D); end; -//--------------- -// DrawBall(private) - Helper for Draw; Draws the Ball over the LyricLine if needed -//--------------- -procedure TLyricEngine.DrawBall(XBall, YBall, Alpha: Real); +{** + * Draws the Ball over the LyricLine if needed. + *} +procedure TLyricEngine.DrawBall(XBall, YBall, Alpha: real); begin glEnable(GL_TEXTURE_2D); glEnable(GL_BLEND); @@ -580,41 +413,122 @@ begin glDisable(GL_TEXTURE_2D); end; -//--------------- -// DrawLyricsLine(private) - Helper for Draw; Draws one LyricLine -//--------------- -procedure TLyricEngine.DrawLyricsLine(X, W, Y: Real; Size: Byte; Line: TLyricLine; Beat: Real); +procedure TLyricEngine.DrawLyricsWords(LyricLine: TLyricLine; + X, Y: real; StartWord, EndWord: integer); var - CurWordStart, CurWordEnd: Real; // screen coordinates of current word and the rest of the sentence - FreestyleDiff: Integer; // difference between top and bottom coordiantes for freestyle lyrics - Progress: Real; // progress of singing the current word - LyricX: Real; // left - LyricX2: Real; // right - LyricY: Real; // top - LyricsHeight: Real; // height the lyrics are displayed - Alpha: Real; // alphalevel to fade out at end - CurWord, LastWord: PLyricWord; // current word + I: integer; + PosX: real; + CurWord: PLyricWord; +begin + PosX := X; + + // set word positions and line size and draw the line + for I := StartWord to EndWord do + begin + CurWord := @LyricLine.Words[I]; + SetFontItalic(CurWord.Freestyle); + SetFontPos(PosX, Y); + glPrint(PChar(CurWord.Text)); + PosX := PosX + CurWord.Width; + end; +end; +procedure TLyricEngine.UpdateLineMetrics(LyricLine: TLyricLine); +var + I: integer; + PosX: real; + CurWord: PLyricWord; + RequestWidth, RequestHeight: real; +begin + PosX := 0; + + // setup font + SetFontStyle(FontStyle); + ResetFont(); + + // check if line is lower or upper line and set sizes accordingly + // Note: at the moment upper and lower lines have same width/height + // and this function is just called by AddLine() but this may change + // so that it is called by DrawLyricsLine(). + //if (LyricLine = LowerLine) then + //begin + // RequestWidth := LowerLineW; + // RequestHeight := LowerLineH; + //end + //else + //begin + RequestWidth := UpperLineW; + RequestHeight := UpperLineH; + //end; + + // set font size to a reasonable value + LyricLine.Height := RequestHeight * 0.9; + SetFontSize(LyricLine.Height/3); + LyricLine.Width := glTextWidth(PChar(LyricLine.Text)); + + // change font-size to fit into the lyric bar + if (LyricLine.Width > RequestWidth) then + begin + LyricLine.Height := Trunc(LyricLine.Height * (RequestWidth / LyricLine.Width)); + // the line is very loooong, set font to at least 1px + if (LyricLine.Height < 1) then + LyricLine.Height := 1; + + SetFontSize(LyricLine.Height/3); + LyricLine.Width := glTextWidth(PChar(LyricLine.Text)); + end; + + // calc word positions and widths + for I := 0 to High(LyricLine.Words) do + begin + CurWord := @LyricLine.Words[I]; + + // - if current word is italic but not the next word get the width of the + // italic font to avoid overlapping. + // - if two italic words follow each other use the normal style's + // width otherwise the spacing between the words will be too big. + // - if it is the line's last word use normal width + if CurWord.Freestyle and + (I+1 < Length(LyricLine.Words)) and + (not LyricLine.Words[I+1].Freestyle) then + begin + SetFontItalic(true); + end; + + CurWord.X := PosX; + CurWord.Width := glTextWidth(PChar(CurWord.Text)); + PosX := PosX + CurWord.Width; + SetFontItalic(false); + end; +end; + + +{** + * Draws one LyricLine + *} +procedure TLyricEngine.DrawLyricsLine(X, W, Y, H: real; Line: TLyricLine; Beat: real); +var + CurWord: PLyricWord; // current word + LastWord: PLyricWord; // last word in line + NextWord: PLyricWord; // word following current word + Progress: real; // progress of singing the current word + LyricX, LyricY: real; // left/top lyric position + WordY: real; // word y-position + LyricsEffect: TLyricsEffect; + Alpha: real; // alphalevel to fade out at end + ClipPlaneEq: array[0..3] of GLdouble; // clipping plane for slide effect {// duet mode - IconSize: Real; // size of player icons - IconAlpha: Real; // alpha level of player icons + IconSize: real; // size of player icons + IconAlpha: real; // alpha level of player icons } begin // do not draw empty lines - // Note: lines with no words in it do not have a valid texture - if (Length(Line.Words) = 0) or - (Line.Width <= 0) then - begin + if (Length(Line.Words) = 0) then Exit; - end; - - // this is actually a bit more than the real font size - // it helps adjusting the "zoom-center" - LyricsHeight := 30.5 * (Line.Size/10); { // duet mode - IconSize := (2 * Size); + IconSize := (2 * Height); IconAlpha := Frac(Beat/(Resolution*4)); DrawPlayerIcon (0, True, X, Y + (42 - IconSize) / 2 , IconSize, IconAlpha); @@ -622,13 +536,19 @@ begin DrawPlayerIcon (2, True, X + (IconSize + 1)*2, Y + (42 - IconSize) / 2, IconSize, IconAlpha); } - LyricX := X + W/2 - Line.Width/2; - LyricX2 := LyricX + Line.Width; + // set font size and style + SetFontStyle(FontStyle); + ResetFont(); + SetFontSize(Line.Height/3); + glColor4f(1, 1, 1, 1); - // maybe center smaller lines - //LyricY := Y; - LyricY := Y + ((Size / Line.Size - 1) * LyricsHeight) / 2; + // center lyrics + LyricX := X + (W - Line.Width) / 2; + LyricY := Y + (H - Line.Height) / 2; + // get lyrics effect + LyricsEffect := TLyricsEffect(Ini.LyricsEffect); + // TODO: what about alpha in freetype outline fonts? Alpha := 1; // check if this line is active (at least its first note must be active) @@ -648,18 +568,20 @@ begin Inc(Line.CurWord); end; - FreestyleDiff := 0; - // determine current and last word in this line. // If the end of the line is reached use the last word as current word. LastWord := @Line.Words[High(Line.Words)]; CurWord := @Line.Words[Line.CurWord]; + if (Line.CurWord+1 < Length(Line.Words)) then + NextWord := @Line.Words[Line.CurWord+1] + else + NextWord := nil; // calc the progress of the lyrics effect Progress := (Beat - CurWord.Start) / CurWord.Length; - if Progress >= 1 then + if (Progress >= 1) then Progress := 1; - if Progress <= 0 then + if (Progress <= 0) then Progress := 0; // last word of this line finished, but this line did not hide -> fade out @@ -671,157 +593,93 @@ begin Alpha := 0; end; - // determine the start-/end positions of the fragment of the current word - CurWordStart := CurWord.X; - CurWordEnd := CurWord.X + CurWord.Width; - - // Slide Effect - // simply paint the active texture to the current position - if Ini.LyricsEffect = 2 then - begin - CurWordStart := CurWordStart + CurWord.Width * Progress; - CurWordEnd := CurWordStart; - end; - - if CurWord.Freestyle then - begin - if (Line.CurWord < High(Line.Words)) and - (not Line.Words[Line.CurWord + 1].Freestyle) then - begin - FreestyleDiff := 2; - end - else - begin - FreestyleDiff := 12; - CurWordStart := CurWordStart - 1; - CurWordEnd := CurWordEnd - 2; - end; - end; - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, Line.Tex); - - // draw sentence up to current word - // type 0: simple lyric effect - // type 3: ball lyric effect - // type 4: shift lyric effect - if (Ini.LyricsEffect in [0, 3, 4]) then - // ball lyric effect - only highlight current word and not that ones before in this line + // draw sentence before current word + if (LyricsEffect in [lfxSimple, lfxBall, lfxShift]) then + // only highlight current word and not that ones before in this line glColorRGB(LineColor_en, Alpha) else glColorRGB(LineColor_act, Alpha); + DrawLyricsWords(Line, LyricX, LyricY, 0, Line.CurWord-1); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex2f(LyricX, LyricY); - - glTexCoord2f(0, 1-LyricsHeight/64); - glVertex2f(LyricX, LyricY + LyricsHeight); - - glTexCoord2f(CurWordStart/1024, 1-LyricsHeight/64); - glVertex2f(LyricX + CurWordStart, LyricY + LyricsHeight); - - glTexCoord2f((CurWordStart + FreestyleDiff)/1024, 1); - glVertex2f(LyricX + CurWordStart + FreestyleDiff, LyricY); - glEnd; - - // draw rest of sentence + // draw rest of sentence (without current word) glColorRGB(LineColor_en, Alpha); - glBegin(GL_QUADS); - glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1); - glVertex2f(LyricX + CurWordEnd + FreestyleDiff, LyricY); - - glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); - glVertex2f(LyricX + CurWordEnd, LyricY + LyricsHeight); - - glTexCoord2f(Line.Width/1024, 1-LyricsHeight/64); - glVertex2f(LyricX2, LyricY + LyricsHeight); - - glTexCoord2f(Line.Width/1024, 1); - glVertex2f(LyricX2, LyricY); - glEnd; - - // draw active word: - // type 0: simple lyric effect - // type 3: ball lyric effect - // type 4: shift lyric effect - // only change the color of the current word - if (Ini.LyricsEffect in [0, 3, 4]) then + if (NextWord <> nil) then begin - if (Ini.LyricsEffect = 4) then - LyricY := LyricY - 8 * (1-Progress); - - glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha); - glBegin(GL_QUADS); - glTexCoord2f((CurWordStart + FreestyleDiff)/1024, 1); - glVertex2f(LyricX + CurWordStart + FreestyleDiff, LyricY); - - glTexCoord2f(CurWordStart/1024, 0); - glVertex2f(LyricX + CurWordStart, LyricY + 64); - - glTexCoord2f(CurWordEnd/1024, 0); - glVertex2f(LyricX + CurWordEnd, LyricY + 64); + DrawLyricsWords(Line, LyricX + NextWord.X, LyricY, + Line.CurWord+1, High(Line.Words)); + end; - glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1); - glVertex2f(LyricX + CurWordEnd + FreestyleDiff, LyricY); - glEnd; + // draw current word + if (LyricsEffect in [lfxSimple, lfxBall, lfxShift]) then + begin + if (LyricsEffect = lfxShift) then + WordY := LyricY - 8 * (1-Progress) + else + WordY := LyricY; - if (Ini.LyricsEffect = 4) then - LyricY := LyricY + 8 * (1-Progress); + // change the color of the current word + glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha); + DrawLyricsWords(Line, LyricX + CurWord.X, WordY, Line.CurWord, Line.CurWord); end - - // draw active word: - // type 1: zoom lyric effect // change color and zoom current word - else if Ini.LyricsEffect = 1 then + else if (LyricsEffect = lfxZoom) then begin glPushMatrix; - glTranslatef(LyricX + CurWordStart + (CurWordEnd-CurWordStart)/2, - LyricY + LyricsHeight/2, 0); - - // set current zoom factor + // zoom at word center + glTranslatef(LyricX + CurWord.X + CurWord.Width/2, + LyricY + Line.Height/2, 0); glScalef(1.0 + (1-Progress) * 0.5, 1.0 + (1-Progress) * 0.5, 1.0); glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha); - glBegin(GL_QUADS); - glTexCoord2f((CurWordStart + FreestyleDiff)/1024, 1); - glVertex2f(-(CurWordEnd-CurWordStart)/2 + FreestyleDiff, -LyricsHeight/2); + DrawLyricsWords(Line, -CurWord.Width/2, -Line.Height/2, Line.CurWord, Line.CurWord); + + glPopMatrix; + end + // split current word into active and non-active part + else if (LyricsEffect = lfxSlide) then + begin + // enable clipping and set clip equation coefficients to zeros + glEnable(GL_CLIP_PLANE0); + FillChar(ClipPlaneEq[0], SizeOf(ClipPlaneEq), 0); - glTexCoord2f(CurWordStart/1024, 1-LyricsHeight/64); - glVertex2f(-(CurWordEnd-CurWordStart)/2, LyricsHeight/2); + glPushMatrix; + glTranslatef(LyricX + CurWord.X, LyricY, 0); - glTexCoord2f(CurWordEnd/1024, 1-LyricsHeight/64); - glVertex2f((CurWordEnd-CurWordStart)/2, LyricsHeight/2); + // clip non-active right part of the current word + ClipPlaneEq[0] := -1; + ClipPlaneEq[3] := CurWord.Width * Progress; + glClipPlane(GL_CLIP_PLANE0, @ClipPlaneEq); + // and draw active left part + glColor4f(LineColor_act.r, LineColor_act.g, LineColor_act.b, Alpha); + DrawLyricsWords(Line, 0, 0, Line.CurWord, Line.CurWord); - glTexCoord2f((CurWordEnd + FreestyleDiff)/1024, 1); - glVertex2f((CurWordEnd-CurWordStart)/2 + FreestyleDiff, -LyricsHeight/2); - glEnd; + // clip active left part of the current word + ClipPlaneEq[0] := -ClipPlaneEq[0]; + ClipPlaneEq[3] := -ClipPlaneEq[3]; + glClipPlane(GL_CLIP_PLANE0, @ClipPlaneEq); + // and draw non-active right part + glColor4f(LineColor_en.r, LineColor_en.g, LineColor_en.b, Alpha); + DrawLyricsWords(Line, 0, 0, Line.CurWord, Line.CurWord); glPopMatrix; - end; - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + glDisable(GL_CLIP_PLANE0); + end; - // type 3: ball lyric effect - if Ini.LyricsEffect = 3 then + // draw the ball onto the current word + if (LyricsEffect = lfxBall) then begin - DrawBall(LyricX + CurWordStart + (CurWordEnd-CurWordStart) * Progress, + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + DrawBall(LyricX + CurWord.X + CurWord.Width * Progress, LyricY - 15 - 15*sin(Progress * Pi), Alpha); end; end else begin // this section is called if the whole line can be drawn at once and no - // word has to be emphasized. - - glEnable(GL_TEXTURE_2D); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, Line.Tex); + // word is highlighted. // enable the upper, disable the lower line if (Line = UpperLine) then @@ -829,45 +687,30 @@ begin else glColorRGB(LineColor_dis); - glBegin(GL_QUADS); - glTexCoord2f(0, 1); - glVertex2f(LyricX, LyricY); - - glTexCoord2f(0, 1-LyricsHeight/64); - glVertex2f(LyricX, LyricY + LyricsHeight); - - glTexCoord2f(Line.Width/1024, 1-LyricsHeight/64); - glVertex2f(LyricX2, LyricY + LyricsHeight); - - glTexCoord2f(Line.Width/1024, 1); - glVertex2f(LyricX2, LyricY); - glEnd; - - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); + DrawLyricsWords(Line, LyricX, LyricY, 0, High(Line.Words)); end; end; -//--------------- -// GetUpperLine() - Returns a reference to the upper line -//--------------- +{** + * @returns a reference to the upper line + *} function TLyricEngine.GetUpperLine(): TLyricLine; begin Result := UpperLine; end; -//--------------- -// GetLowerLine() - Returns a reference to the lower line -//--------------- +{** + * @returns a reference to the lower line + *} function TLyricEngine.GetLowerLine(): TLyricLine; begin Result := LowerLine; end; -//--------------- -// GetUpperLineIndex() - Returns the index of the upper line -//--------------- -function TLyricEngine.GetUpperLineIndex(): Integer; +{** + * @returns the index of the upper line + *} +function TLyricEngine.GetUpperLineIndex(): integer; const QUEUE_SIZE = 3; begin diff --git a/src/screens/UScreenSing.pas b/src/screens/UScreenSing.pas index d0908ff8..1267bab8 100644 --- a/src/screens/UScreenSing.pas +++ b/src/screens/UScreenSing.pas @@ -298,7 +298,9 @@ begin //Pausepopup is not visibile at the beginning Static[StaticPausePopup].Visible := False; - Lyrics := TLyricEngine.Create(80, Skin_LyricsT, 640, 12, 80, Skin_LyricsT + 36, 640, 12); + Lyrics := TLyricEngine.Create( + Skin_LyricsUpperX, Skin_LyricsUpperY, Skin_LyricsUpperW, Skin_LyricsUpperH, + Skin_LyricsLowerX, Skin_LyricsLowerY, Skin_LyricsLowerW, Skin_LyricsLowerH); LyricsSync := TLyricsSyncSource.Create(); end; @@ -550,8 +552,6 @@ begin case Ini.LyricsFont of 0: begin - Lyrics.UpperLineSize := 14; - Lyrics.LowerLineSize := 14; Lyrics.FontStyle := 0; Lyrics.LineColor_en.R := Skin_FontR; @@ -571,8 +571,6 @@ begin end; 1: begin - Lyrics.UpperLineSize := 14; - Lyrics.LowerLineSize := 14; Lyrics.FontStyle := 2; Lyrics.LineColor_en.R := 0.75; @@ -592,8 +590,6 @@ begin end; 2: begin - Lyrics.UpperLineSize := 12; - Lyrics.LowerLineSize := 12; Lyrics.FontStyle := 3; Lyrics.LineColor_en.R := 0.75; @@ -970,15 +966,6 @@ begin else Lyrics.AddLine(nil); end; - - // AddLine draws the passed line to the back-buffer of the render context - // and copies it into a texture afterwards (offscreen rendering). - // This leaves an in invalidated screen. Calling Draw() makes sure, - // that the back-buffer stores the sing-screen, when the next - // swap between the back- and front-buffer is done (eliminates flickering) - // calling AddLine() right before the regular screen update (Display.Draw) - // would be a better solution. - Draw; end; function TLyricsSyncSource.GetClock(): real; -- cgit v1.2.3