X-Git-Url: http://deadsoftware.ru/gitweb?p=d2df-sdl.git;a=blobdiff_plain;f=src%2Fengine%2Fe_graphics.pas;h=18f75c44ea3263119bd6bfbb429a614c05060e09;hp=25c2e8b3dad59995a08f51775aa7306e077b4016;hb=676995a77ad243efc0abee75841f81e86a7262a0;hpb=30bcb89f4decd5b5885ebde1fbb943b6563b1e3e diff --git a/src/engine/e_graphics.pas b/src/engine/e_graphics.pas index 25c2e8b..18f75c4 100644 --- a/src/engine/e_graphics.pas +++ b/src/engine/e_graphics.pas @@ -1,21 +1,38 @@ +(* Copyright (C) Doom 2D: Forever Developers + * + * 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, version 3 of the License ONLY. + * + * 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 . + *) +{$INCLUDE ../shared/a_modes.inc} unit e_graphics; interface uses - SysUtils, Math, e_log, e_textures, SDL2, GL, GLExt, MAPDEF; + {$INCLUDE ../nogl/noGLuses.inc} + {$IFDEF USE_SDL2} + SDL2, + {$ENDIF} + SysUtils, Classes, Math, e_log, e_texture, + MAPDEF, ImagingTypes, Imaging, ImagingUtility; type - TMirrorType=(M_NONE, M_HORIZONTAL, M_VERTICAL); - TBlending=(B_NONE, B_BLEND, B_FILTER, B_INVERT); + TMirrorType=(None, Horizontal, Vertical); + TBlending=(None, Blend, Filter, Invert); TPoint2i = record X, Y: Integer; end; - TPoint = MAPDEF.TPoint; // TODO: create an utiltypes.pas or something - // for other types like rect as well - TPoint2f = record X, Y: Double; end; @@ -33,7 +50,7 @@ type R, G, B: Byte; end; - PPoint = ^TPoint; + PDFPoint = ^TDFPoint; PPoint2f = ^TPoint2f; PRect = ^TRect; PRectWH = ^TRectWH; @@ -45,29 +62,40 @@ type procedure e_InitGL(); procedure e_SetViewPort(X, Y, Width, Height: Word); procedure e_ResizeWindow(Width, Height: Integer); +procedure e_ResizeFramebuffer(Width, Height: Integer); +procedure e_BlitFramebuffer(WinWidth, WinHeight: Integer); procedure e_Draw(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Mirror: TMirrorType = TMirrorType.None); procedure e_DrawAdv(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Angle: Single; RC: PPoint; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Angle: Single; RC: PDFPoint; Mirror: TMirrorType = TMirrorType.None); procedure e_DrawSize(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = TMirrorType.None); procedure e_DrawSizeMirror(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = TMirrorType.None); + procedure e_DrawFill(ID: DWORD; X, Y: Integer; XCount, YCount: Word; Alpha: Integer; - AlphaChannel: Boolean; Blending: Boolean); + AlphaChannel: Boolean; Blending: Boolean; ambientBlendMode: Boolean=false); + +procedure e_DrawFillX (id: DWORD; x, y, wdt, hgt: Integer; alpha: Integer; alphachannel: Boolean; + blending: Boolean; scale: Single; ambientBlendMode: Boolean=false); + +procedure e_AmbientQuad (x, y, w, h: Integer; r, g, b, a: Byte); + procedure e_DrawPoint(Size: Byte; X, Y: Integer; Red, Green, Blue: Byte); procedure e_DrawLine(Width: Byte; X1, Y1, X2, Y2: Integer; Red, Green, Blue: Byte; Alpha: Byte = 0); procedure e_DrawQuad(X1, Y1, X2, Y2: Integer; Red, Green, Blue: Byte; Alpha: Byte = 0); procedure e_DrawFillQuad(X1, Y1, X2, Y2: Integer; Red, Green, Blue, Alpha: Byte; - Blending: TBlending = B_NONE); + Blending: TBlending = TBlending.None); +procedure e_DarkenQuad (x0, y0, x1, y1: Integer; a: Integer); +procedure e_DarkenQuadWH (x, y, w, h: Integer; a: Integer); +function e_CreateTextureImg (var img: TImageData; var ID: DWORD): Boolean; function e_CreateTexture(FileName: string; var ID: DWORD): Boolean; function e_CreateTextureEx(FileName: string; var ID: DWORD; fX, fY, fWidth, fHeight: Word): Boolean; -function e_CreateTextureMem(pData: Pointer; var ID: DWORD): Boolean; -function e_CreateTextureMemEx(pData: Pointer; var ID: DWORD; fX, fY, fWidth, fHeight: Word): Boolean; +function e_CreateTextureMem(pData: Pointer; dataSize: LongInt; var ID: DWORD): Boolean; +function e_CreateTextureMemEx(pData: Pointer; dataSize: LongInt; var ID: DWORD; fX, fY, fWidth, fHeight: Word): Boolean; procedure e_GetTextureSize(ID: DWORD; Width, Height: PWord); -function e_GetTextureSize2(ID: DWORD): TRectWH; procedure e_DeleteTexture(ID: DWORD); procedure e_RemoveAllTextures(); @@ -92,41 +120,51 @@ procedure e_TextureFontKill(FontID: DWORD); procedure e_TextureFontPrint(X, Y: GLint; Text: string; FontID: DWORD); procedure e_TextureFontPrintEx(X, Y: GLint; Text: string; FontID: DWORD; Red, Green, Blue: Byte; Scale: Single; Shadow: Boolean = False); -procedure e_TextureFontPrintFmt(X, Y: GLint; Text: string; FontID: DWORD; Shadow: Boolean = False); -procedure e_TextureFontGetSize(ID: DWORD; var CharWidth, CharHeight: Byte); +procedure e_TextureFontPrintFmt(X, Y: GLint; Text: string; FontID: DWORD; + Shadow: Boolean = False; Newlines: Boolean = False); +procedure e_TextureFontGetSize(ID: DWORD; out CharWidth, CharHeight: Byte); procedure e_RemoveAllTextureFont(); +function e_TextureFontCharWidth (ch: Char; FontID: DWORD): Integer; +procedure e_TextureFontPrintCharEx (X, Y: Integer; Ch: Char; FontID: DWORD; Shadow: Boolean = False); + procedure e_ReleaseEngine(); procedure e_BeginRender(); procedure e_Clear(Mask: TGLbitfield; Red, Green, Blue: Single); overload; procedure e_Clear(); overload; procedure e_EndRender(); -procedure e_SaveGLContext(); -procedure e_RestoreGLContext(); - +{$IFDEF USE_SDL2} function e_GetGamma(win: PSDL_Window): Byte; procedure e_SetGamma(win: PSDL_Window;Gamma: Byte); +{$ENDIF} -procedure e_MakeScreenshot(FileName: string; Width, Height: Word); +procedure e_MakeScreenshot(st: TStream; Width, Height: Word); function _RGB(Red, Green, Blue: Byte): TRGB; function _Point(X, Y: Integer): TPoint2i; function _Rect(X, Y: Integer; Width, Height: Word): TRectWH; function _TRect(L, T, R, B: LongInt): TRect; +//function e_getTextGLId (ID: DWORD): GLuint; var e_Colors: TRGB; + e_NoGraphics: Boolean = False; + e_FastScreenshots: Boolean = true; // it's REALLY SLOW with `false` + g_dbg_scale: Single = 1.0; + r_pixel_scale: Single = 1.0; + implementation +uses + paszlib, crc, utils; + + type TTexture = record - ID: DWORD; - Width: Word; - Height: Word; - Fmt: Word; + tx: GLTexture; end; TTextureFont = record @@ -135,7 +173,8 @@ type Base: Uint32; CharWidth: Byte; CharHeight: Byte; - XC, YC, SPC: Word; + XC, YC: WORD; + SPC: ShortInt; end; TCharFont = record @@ -146,7 +185,7 @@ type end; Space: ShortInt; Height: ShortInt; - Live: Boolean; + alive: Boolean; end; TSavedTexture = record @@ -159,18 +198,30 @@ var e_Textures: array of TTexture = nil; e_TextureFonts: array of TTextureFont = nil; e_CharFonts: array of TCharFont; - e_SavedTextures: array of TSavedTexture; + //e_SavedTextures: array of TSavedTexture; + e_FBO: GLuint = 0; + e_RBO: GLuint = 0; + e_Frame: GLuint = 0; + e_FrameW: Integer = -1; + e_FrameH: Integer = -1; + +//function e_getTextGLId (ID: DWORD): GLuint; begin result := e_Textures[ID].tx.id; end; //------------------------------------------------------------------ // Èíèöèàëèçèðóåò OpenGL //------------------------------------------------------------------ procedure e_InitGL(); begin - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); + if e_NoGraphics then + begin + e_DummyTextures := True; + Exit; + end; e_Colors.R := 255; e_Colors.G := 255; e_Colors.B := 255; + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); glClearColor(0, 0, 0, 0); end; @@ -179,6 +230,7 @@ var mat: Array [0..15] of GLDouble; begin + if e_NoGraphics then Exit; glLoadIdentity(); glScissor(X, Y, Width, Height); glViewport(X, Y, Width, Height); @@ -221,7 +273,7 @@ var begin if e_Textures <> nil then for i := 0 to High(e_Textures) do - if e_Textures[i].Width = 0 then + if e_Textures[i].tx.Width = 0 then begin Result := i; Exit; @@ -249,15 +301,14 @@ var begin Result := False; - e_WriteLog('Loading texture from '+FileName, MSG_NOTIFY); + e_WriteLog('Loading texture from '+FileName, TMsgType.Notify); find_id := FindTexture(); - if not LoadTexture(FileName, e_Textures[find_id].ID, e_Textures[find_id].Width, - e_Textures[find_id].Height, @fmt) then Exit; + if not LoadTexture(FileName, e_Textures[find_id].tx, e_Textures[find_id].tx.Width, + e_Textures[find_id].tx.Height, @fmt) then Exit; ID := find_id; - e_Textures[ID].Fmt := fmt; Result := True; end; @@ -271,18 +322,14 @@ begin find_id := FindTexture(); - if not LoadTextureEx(FileName, e_Textures[find_id].ID, fX, fY, fWidth, fHeight, @fmt) then exit; - - e_Textures[find_id].Width := fWidth; - e_Textures[find_id].Height := fHeight; - e_Textures[find_id].Fmt := fmt; + if not LoadTextureEx(FileName, e_Textures[find_id].tx, fX, fY, fWidth, fHeight, @fmt) then exit; ID := find_id; Result := True; end; -function e_CreateTextureMem(pData: Pointer; var ID: DWORD): Boolean; +function e_CreateTextureMem(pData: Pointer; dataSize: LongInt; var ID: DWORD): Boolean; var find_id: DWORD; fmt: Word; @@ -291,16 +338,14 @@ begin find_id := FindTexture; - if not LoadTextureMem(pData, e_Textures[find_id].ID, e_Textures[find_id].Width, - e_Textures[find_id].Height, @fmt) then exit; + if not LoadTextureMem(pData, dataSize, e_Textures[find_id].tx, e_Textures[find_id].tx.Width, e_Textures[find_id].tx.Height, @fmt) then exit; id := find_id; - e_Textures[id].Fmt := fmt; Result := True; end; -function e_CreateTextureMemEx(pData: Pointer; var ID: DWORD; fX, fY, fWidth, fHeight: Word): Boolean; +function e_CreateTextureMemEx(pData: Pointer; dataSize: LongInt; var ID: DWORD; fX, fY, fWidth, fHeight: Word): Boolean; var find_id: DWORD; fmt: Word; @@ -309,116 +354,73 @@ begin find_id := FindTexture(); - if not LoadTextureMemEx(pData, e_Textures[find_id].ID, fX, fY, fWidth, fHeight, @fmt) then exit; - - e_Textures[find_id].Width := fWidth; - e_Textures[find_id].Height := fHeight; - e_Textures[find_id].Fmt := fmt; + if not LoadTextureMemEx(pData, dataSize, e_Textures[find_id].tx, fX, fY, fWidth, fHeight, @fmt) then exit; ID := find_id; Result := True; end; +function e_CreateTextureImg (var img: TImageData; var ID: DWORD): Boolean; +var + find_id: DWORD; + fmt, tw, th: Word; +begin + result := false; + find_id := FindTexture(); + if not LoadTextureImg(img, e_Textures[find_id].tx, tw, th, @fmt) then exit; + ID := find_id; + result := True; +end; + procedure e_GetTextureSize(ID: DWORD; Width, Height: PWord); begin - if Width <> nil then Width^ := e_Textures[ID].Width; - if Height <> nil then Height^ := e_Textures[ID].Height; + if Width <> nil then Width^ := e_Textures[ID].tx.Width; + if Height <> nil then Height^ := e_Textures[ID].tx.Height; end; -function e_GetTextureSize2(ID: DWORD): TRectWH; -var - data: PChar; - x, y: Integer; - w, h: Word; - a: Boolean; - lastline: Integer; -begin - w := e_Textures[ID].Width; - h := e_Textures[ID].Height; - data := GetMemory(w*h*4); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, e_Textures[ID].ID); - glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - - Result.Y := 0; - Result.X := 0; - Result.Width := w; - Result.Height := h; - - for y := h-1 downto 0 do - begin - lastline := y; - a := True; +procedure e_ResizeFramebuffer(Width, Height: Integer); +begin + glBindTexture(GL_TEXTURE_2D, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); - for x := 1 to w-4 do + if e_Frame > 0 then begin - a := Byte((data+y*w*4+x*4+3)^) <> 0; - if a then Break; + glDeleteTextures(1, @e_Frame); + e_Frame := 0; end; - if a then + if e_RBO > 0 then begin - Result.Y := h-lastline; - Break; + glDeleteRenderbuffers(1, @e_RBO); + e_RBO := 0; end; - end; - - for y := 0 to h-1 do - begin - lastline := y; - a := True; - for x := 1 to w-4 do + if e_FBO > 0 then begin - a := Byte((data+y*w*4+x*4+3)^) <> 0; - if a then Break; + glDeleteFramebuffers(1, @e_FBO); + e_FBO := 0; end; - if a then - begin - Result.Height := h-lastline-Result.Y; - Break; - end; - end; + e_FrameW := Width; + e_FrameH := Height; - for x := 0 to w-1 do - begin - lastline := x; - a := True; + glGenFramebuffers(1, @e_FBO); - for y := 1 to h-4 do - begin - a := Byte((data+y*w*4+x*4+3)^) <> 0; - if a then Break; - end; + glGenTextures(1, @e_Frame); + glBindTexture(GL_TEXTURE_2D, e_Frame); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, Width, Height, 0, GL_RGB, GL_UNSIGNED_BYTE, nil); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - if a then - begin - Result.X := lastline+1; - Break; - end; - end; - - for x := w-1 downto 0 do - begin - lastline := x; - a := True; - - for y := 1 to h-4 do - begin - a := Byte((data+y*w*4+x*4+3)^) <> 0; - if a then Break; - end; - - if a then - begin - Result.Width := lastline-Result.X+1; - Break; - end; - end; + glGenRenderbuffers(1, @e_RBO); + glBindRenderbuffer(GL_RENDERBUFFER, e_RBO); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, Width, Height); - FreeMemory(data); + glBindFramebuffer(GL_FRAMEBUFFER, e_FBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, e_Frame, 0); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, e_RBO); end; procedure e_ResizeWindow(Width, Height: Integer); @@ -428,9 +430,47 @@ begin e_SetViewPort(0, 0, Width, Height); end; +procedure drawTxQuad (x0, y0, w, h, tw, th: Integer; u, v: single; Mirror: TMirrorType); +var + x1, y1, tmp: Integer; +begin + if (w < 1) or (h < 1) then exit; + x1 := x0+w; + y1 := y0+h; + if Mirror = TMirrorType.Horizontal then begin tmp := x1; x1 := x0; x0 := tmp; end + else if Mirror = TMirrorType.Vertical then begin tmp := y1; y1 := y0; y0 := tmp; end; + //HACK: make texture one pixel shorter, so it won't wrap + if (g_dbg_scale <> 1.0) then + begin + u := u*tw/(tw+1); + v := v*th/(th+1); + end; + glTexCoord2f(0, v); glVertex2i(x0, y0); + glTexCoord2f(0, 0); glVertex2i(x0, y1); + glTexCoord2f(u, 0); glVertex2i(x1, y1); + glTexCoord2f(u, v); glVertex2i(x1, y0); +end; + +procedure e_BlitFramebuffer(WinWidth, WinHeight: Integer); +begin + if (e_FBO = 0) or (e_Frame = 0) then exit; + glDisable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, e_Frame); + glColor4ub(255, 255, 255, 255); + e_SetViewPort(0, 0, WinWidth, WinHeight); + glBegin(GL_QUADS); + drawTxQuad(0, 0, WinWidth, WinHeight, e_FrameW, e_FrameH, 1, 1, TMirrorType.None); + glEnd(); + glBindFramebuffer(GL_FRAMEBUFFER, e_FBO); + e_SetViewPort(0, 0, e_FrameW, e_FrameH); +end; + procedure e_Draw(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Mirror: TMirrorType = TMirrorType.None); begin + if e_NoGraphics then Exit; glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); if (Alpha > 0) or (AlphaChannel) or (Blending) then @@ -448,32 +488,39 @@ begin glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, e_Textures[ID].ID); + glBindTexture(GL_TEXTURE_2D, e_Textures[ID].tx.id); glBegin(GL_QUADS); + drawTxQuad(X, Y, e_Textures[id].tx.width, e_Textures[id].tx.height, e_Textures[id].tx.width, e_Textures[id].tx.height, e_Textures[ID].tx.u, e_Textures[ID].tx.v, Mirror); + + //u := e_Textures[ID].tx.u; + //v := e_Textures[ID].tx.v; + + { if Mirror = M_NONE then begin - glTexCoord2i(1, 0); glVertex2i(X + e_Textures[id].Width, Y); - glTexCoord2i(0, 0); glVertex2i(X, Y); - glTexCoord2i(0, -1); glVertex2i(X, Y + e_Textures[id].Height); - glTexCoord2i(1, -1); glVertex2i(X + e_Textures[id].Width, Y + e_Textures[id].Height); + glTexCoord2f(u, 0); glVertex2i(X + e_Textures[id].tx.Width, Y); + glTexCoord2f(0, 0); glVertex2i(X, Y); + glTexCoord2f(0, -v); glVertex2i(X, Y + e_Textures[id].tx.Height); + glTexCoord2f(u, -v); glVertex2i(X + e_Textures[id].tx.Width, Y + e_Textures[id].tx.Height); end else if Mirror = M_HORIZONTAL then begin - glTexCoord2i(1, 0); glVertex2i(X, Y); - glTexCoord2i(0, 0); glVertex2i(X + e_Textures[id].Width, Y); - glTexCoord2i(0, -1); glVertex2i(X + e_Textures[id].Width, Y + e_Textures[id].Height); - glTexCoord2i(1, -1); glVertex2i(X, Y + e_Textures[id].Height); + glTexCoord2f(u, 0); glVertex2i(X, Y); + glTexCoord2f(0, 0); glVertex2i(X + e_Textures[id].tx.Width, Y); + glTexCoord2f(0, -v); glVertex2i(X + e_Textures[id].tx.Width, Y + e_Textures[id].tx.Height); + glTexCoord2f(u, -v); glVertex2i(X, Y + e_Textures[id].tx.Height); end else if Mirror = M_VERTICAL then begin - glTexCoord2i(1, -1); glVertex2i(X + e_Textures[id].Width, Y); - glTexCoord2i(0, -1); glVertex2i(X, Y); - glTexCoord2i(0, 0); glVertex2i(X, Y + e_Textures[id].Height); - glTexCoord2i(1, 0); glVertex2i(X + e_Textures[id].Width, Y + e_Textures[id].Height); + glTexCoord2f(u, -v); glVertex2i(X + e_Textures[id].tx.Width, Y); + glTexCoord2f(0, -v); glVertex2i(X, Y); + glTexCoord2f(0, 0); glVertex2i(X, Y + e_Textures[id].tx.Height); + glTexCoord2f(u, 0); glVertex2i(X + e_Textures[id].tx.Width, Y + e_Textures[id].tx.Height); end; + } glEnd(); @@ -481,8 +528,11 @@ begin end; procedure e_DrawSize(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = TMirrorType.None); +var + u, v: Single; begin + if e_NoGraphics then Exit; glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); if (Alpha > 0) or (AlphaChannel) or (Blending) then @@ -500,21 +550,25 @@ begin glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, e_Textures[ID].ID); + glBindTexture(GL_TEXTURE_2D, e_Textures[ID].tx.id); + + u := e_Textures[ID].tx.u; + v := e_Textures[ID].tx.v; glBegin(GL_QUADS); - glTexCoord2i(0, 1); glVertex2i(X, Y); - glTexCoord2i(1, 1); glVertex2i(X + Width, Y); - glTexCoord2i(1, 0); glVertex2i(X + Width, Y + Height); - glTexCoord2i(0, 0); glVertex2i(X, Y + Height); + glTexCoord2f(0, v); glVertex2i(X, Y); + glTexCoord2f(u, v); glVertex2i(X + Width, Y); + glTexCoord2f(u, 0); glVertex2i(X + Width, Y + Height); + glTexCoord2f(0, 0); glVertex2i(X, Y + Height); glEnd(); glDisable(GL_BLEND); end; procedure e_DrawSizeMirror(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Width, Height: Word; Mirror: TMirrorType = TMirrorType.None); begin + if e_NoGraphics then Exit; glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); if (Alpha > 0) or (AlphaChannel) or (Blending) then @@ -532,85 +586,268 @@ begin glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, e_Textures[ID].ID); + glBindTexture(GL_TEXTURE_2D, e_Textures[ID].tx.id); glBegin(GL_QUADS); - - if Mirror = M_NONE then - begin - glTexCoord2i(1, 0); glVertex2i(X + Width, Y); - glTexCoord2i(0, 0); glVertex2i(X, Y); - glTexCoord2i(0, -1); glVertex2i(X, Y + Height); - glTexCoord2i(1, -1); glVertex2i(X + Width, Y + Height); - end - else - if Mirror = M_HORIZONTAL then - begin - glTexCoord2i(1, 0); glVertex2i(X, Y); - glTexCoord2i(0, 0); glVertex2i(X + Width, Y); - glTexCoord2i(0, -1); glVertex2i(X + Width, Y + Height); - glTexCoord2i(1, -1); glVertex2i(X, Y + Height); - end - else - if Mirror = M_VERTICAL then - begin - glTexCoord2i(1, -1); glVertex2i(X + Width, Y); - glTexCoord2i(0, -1); glVertex2i(X, Y); - glTexCoord2i(0, 0); glVertex2i(X, Y + Height); - glTexCoord2i(1, 0); glVertex2i(X + Width, Y + Height); - end; - + drawTxQuad(X, Y, Width, Height, e_Textures[id].tx.width, e_Textures[id].tx.height, e_Textures[ID].tx.u, e_Textures[ID].tx.v, Mirror); glEnd(); glDisable(GL_BLEND); end; procedure e_DrawFill(ID: DWORD; X, Y: Integer; XCount, YCount: Word; Alpha: Integer; - AlphaChannel: Boolean; Blending: Boolean); + AlphaChannel: Boolean; Blending: Boolean; ambientBlendMode: Boolean=false); var - X2, Y2: Integer; - + X2, Y2, dx, w, h: Integer; + u, v: Single; begin + if e_NoGraphics then Exit; glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); + ambientBlendMode := false; - if (Alpha > 0) or (AlphaChannel) or (Blending) then - glEnable(GL_BLEND) + if (Alpha > 0) or AlphaChannel or Blending then + begin + glEnable(GL_BLEND); + end else - glDisable(GL_BLEND); + begin + if not ambientBlendMode then glDisable(GL_BLEND); + end; + if AlphaChannel or (Alpha > 0) then glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (Alpha > 0) then glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255-Alpha); + if Blending then glBlendFunc(GL_SRC_ALPHA, GL_ONE); - if (AlphaChannel) or (Alpha > 0) then - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (XCount = 0) then XCount := 1; + if (YCount = 0) then YCount := 1; - if Alpha > 0 then - glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255-Alpha); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, e_Textures[ID].tx.id); - if Blending then - glBlendFunc(GL_SRC_ALPHA, GL_ONE); + X2 := X+e_Textures[ID].tx.width*XCount; + Y2 := Y+e_Textures[ID].tx.height*YCount; + + //k8: this SHOULD work... i hope + if (e_Textures[ID].tx.width = e_Textures[ID].tx.glwidth) and (e_Textures[ID].tx.height = e_Textures[ID].tx.glheight) then + begin + glBegin(GL_QUADS); + glTexCoord2i(0, YCount); glVertex2i(X, Y); + glTexCoord2i(XCount, YCount); glVertex2i(X2, Y); + glTexCoord2i(XCount, 0); glVertex2i(X2, Y2); + glTexCoord2i(0, 0); glVertex2i(X, Y2); + glEnd(); + end + else + begin + glBegin(GL_QUADS); + // hard day's night + u := e_Textures[ID].tx.u; + v := e_Textures[ID].tx.v; + w := e_Textures[ID].tx.width; + h := e_Textures[ID].tx.height; + while YCount > 0 do + begin + dx := XCount; + x2 := X; + while dx > 0 do + begin + glTexCoord2f(0, v); glVertex2i(X, Y); + glTexCoord2f(u, v); glVertex2i(X+w, Y); + glTexCoord2f(u, 0); glVertex2i(X+w, Y+h); + glTexCoord2f(0, 0); glVertex2i(X, Y+h); + Inc(X, w); + Dec(dx); + end; + X := x2; + Inc(Y, h); + Dec(YCount); + end; + glEnd(); + end; - if XCount = 0 then - XCount := 1; + glDisable(GL_BLEND); +end; + + +//TODO: overflow checks +function intersectRect (var x0, y0, w0, h0: Integer; const x1, y1, w1, h1: Integer): Boolean; +var + ex0, ey0: Integer; +begin + result := false; + if (w0 < 1) or (h0 < 1) or (w1 < 1) or (h1 < 1) then exit; + // check for intersection + if (x0+w0 <= x1) or (y0+h0 <= y1) or (x1+w1 <= x0) or (y1+h1 <= y0) then exit; + if (x0 >= x1+w1) or (y0 >= y1+h1) or (x1 >= x0+h0) or (y1 >= y0+h0) then exit; + // ok, intersects + ex0 := x0+w0; + ey0 := y0+h0; + if (x0 < x1) then x0 := x1; + if (y0 < y1) then y0 := y1; + if (ex0 > x1+w1) then ex0 := x1+w1; + if (ey0 > y1+h1) then ey0 := y1+h1; + w0 := ex0-x0; + h0 := ey0-y0; + result := (w0 > 0) and (h0 > 0); +end; + + +procedure e_DrawFillX (id: DWORD; x, y, wdt, hgt: Integer; alpha: Integer; alphachannel: Boolean; + blending: Boolean; scale: Single; ambientBlendMode: Boolean=false); +var + x2, y2: Integer; + { + wassc: Boolean; + scxywh: array[0..3] of GLint; + vpxywh: array[0..3] of GLint; + } + w, h, dw, cw, ch, yofs: Integer; + u, v, cu, cv: Single; + onlyOneY: Boolean; + + { + procedure setScissorGLInternal (x, y, w, h: Integer); + begin + //if not scallowed then exit; + x := trunc(x*scale); + y := trunc(y*scale); + w := trunc(w*scale); + h := trunc(h*scale); + y := vpxywh[3]-(y+h); + if not intersectRect(x, y, w, h, scxywh[0], scxywh[1], scxywh[2], scxywh[3]) then + begin + glScissor(0, 0, 0, 0); + end + else + begin + //writeln(' (', x, ',', y, ')-(', w, ',', h, ')'); + glScissor(x, y, w, h); + end; + end; + } - if YCount = 0 then - YCount := 1; +begin + if e_NoGraphics then exit; + ambientBlendMode := false; + + if (wdt < 1) or (hgt < 1) then exit; + + if (wdt mod e_Textures[ID].tx.width = 0) and (hgt mod e_Textures[ID].tx.height = 0) then + begin + e_DrawFill(id, x, y, wdt div e_Textures[ID].tx.width, hgt div e_Textures[ID].tx.height, alpha, alphachannel, blending, ambientBlendMode); + exit; + end; + + glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); + + if (Alpha > 0) or AlphaChannel or Blending then + begin + glEnable(GL_BLEND); + end + else + begin + if not ambientBlendMode then glDisable(GL_BLEND); + end; + if AlphaChannel or (Alpha > 0) then glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + if (Alpha > 0) then glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255-Alpha); + if Blending then glBlendFunc(GL_SRC_ALPHA, GL_ONE); glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, e_Textures[ID].ID); + glBindTexture(GL_TEXTURE_2D, e_Textures[ID].tx.id); - X2 := X + e_Textures[ID].Width * XCount; - Y2 := Y + e_Textures[ID].Height * YCount; + x2 := x+wdt; + y2 := y+hgt; - glBegin(GL_QUADS); - glTexCoord2i(0, YCount); glVertex2i(X, Y); - glTexCoord2i(XCount, YCount); glVertex2i(X2, Y); - glTexCoord2i(XCount, 0); glVertex2i(X2, Y2); - glTexCoord2i(0, 0); glVertex2i(X, Y2); - glEnd(); + //k8: this SHOULD work... i hope + if {false and} (e_Textures[ID].tx.width = e_Textures[ID].tx.glwidth) and (e_Textures[ID].tx.height = e_Textures[ID].tx.glheight) then + begin + glBegin(GL_QUADS); + glTexCoord2f(0, hgt/e_Textures[ID].tx.height); glVertex2i(x, y); + glTexCoord2f(wdt/e_Textures[ID].tx.width, hgt/e_Textures[ID].tx.height); glVertex2i(x2, y); + glTexCoord2f(wdt/e_Textures[ID].tx.width, 0); glVertex2i(x2, y2); + glTexCoord2f(0, 0); glVertex2i(x, y2); + glEnd(); + end + else + begin + // hard day's night; setup scissor + { + glGetIntegerv(GL_VIEWPORT, @vpxywh[0]); + wassc := (glIsEnabled(GL_SCISSOR_TEST) <> 0); + if wassc then glGetIntegerv(GL_SCISSOR_BOX, @scxywh[0]) else glGetIntegerv(GL_VIEWPORT, @scxywh[0]); + //writeln('(', scxywh[0], ',', scxywh[1], ')-(', scxywh[2], ',', scxywh[3], ')'); + //glEnable(GL_SCISSOR_TEST); + setScissorGLInternal(x, y, wdt, hgt); + } + // draw quads + u := e_Textures[ID].tx.u; + v := e_Textures[ID].tx.v; + w := e_Textures[ID].tx.width; + h := e_Textures[ID].tx.height; + x2 := x; + if (hgt > h) then begin y += hgt-h; onlyOneY := false; end else onlyOneY := true; + glBegin(GL_QUADS); + while (hgt > 0) do + begin + if (hgt >= h) then begin ch := h; cv := v; yofs := 0; end else begin ch := hgt; cv := v/(h/hgt); yofs := h-hgt; end; + if onlyOneY then yofs := 0; + Dec(hgt, h); + dw := wdt; + x := x2; + while (dw > 0) do + begin + if (dw >= w) then begin cw := w; cu := u; end else begin cw := dw; cu := u/(w/dw); end; + Dec(dw, w); + glTexCoord2f(0, cv); glVertex2i(X, Y+yofs); + glTexCoord2f(cu, cv); glVertex2i(X+cw, Y+yofs); + glTexCoord2f(cu, 0); glVertex2i(X+cw, Y+ch+yofs); + glTexCoord2f(0, 0); glVertex2i(X, Y+ch+yofs); + Inc(X, w); + end; + Dec(Y, h); + end; + glEnd(); + //if wassc then glEnable(GL_SCISSOR_TEST) else glDisable(GL_SCISSOR_TEST); + end; glDisable(GL_BLEND); end; + +procedure e_AmbientQuad (x, y, w, h: Integer; r, g, b, a: Byte); +begin + if e_NoGraphics then exit; + if (w < 1) or (h < 1) then exit; + if (a <> 255) or ((r or g or b) <> 0) then + begin + glEnable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glColor4ub(r, g, b, a); + if ((r or g or b) <> 0) then + begin + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x+w, y); + glVertex2i(x+w, y+h); + glVertex2i(x, y+h); + glEnd(); + end; + glBlendFunc(GL_ZERO, GL_SRC_ALPHA); + glBegin(GL_QUADS); + glVertex2i(x, y); + glVertex2i(x+w, y); + glVertex2i(x+w, y+h); + glVertex2i(x, y+h); + glEnd(); + glDisable(GL_BLEND); + end; +end; + + procedure e_DrawAdv(ID: DWORD; X, Y: Integer; Alpha: Byte; AlphaChannel: Boolean; - Blending: Boolean; Angle: Single; RC: PPoint; Mirror: TMirrorType = M_NONE); + Blending: Boolean; Angle: Single; RC: PDFPoint; Mirror: TMirrorType = TMirrorType.None); begin + if e_NoGraphics then Exit; + glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); if (Alpha > 0) or (AlphaChannel) or (Blending) then @@ -636,33 +873,10 @@ begin end; glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, e_Textures[id].ID); + glBindTexture(GL_TEXTURE_2D, e_Textures[id].tx.id); glBegin(GL_QUADS); //0-1 1-1 //00 10 - if Mirror = M_NONE then - begin - glTexCoord2i(1, 0); glVertex2i(X + e_Textures[id].Width, Y); - glTexCoord2i(0, 0); glVertex2i(X, Y); - glTexCoord2i(0, -1); glVertex2i(X, Y + e_Textures[id].Height); - glTexCoord2i(1, -1); glVertex2i(X + e_Textures[id].Width, Y + e_Textures[id].Height); - end - else - if Mirror = M_HORIZONTAL then - begin - glTexCoord2i(1, 0); glVertex2i(X, Y); - glTexCoord2i(0, 0); glVertex2i(X + e_Textures[id].Width, Y); - glTexCoord2i(0, -1); glVertex2i(X + e_Textures[id].Width, Y + e_Textures[id].Height); - glTexCoord2i(1, -1); glVertex2i(X, Y + e_Textures[id].Height); - end - else - if Mirror = M_VERTICAL then - begin - glTexCoord2i(1, -1); glVertex2i(X + e_Textures[id].Width, Y); - glTexCoord2i(0, -1); glVertex2i(X, Y); - glTexCoord2i(0, 0); glVertex2i(X, Y + e_Textures[id].Height); - glTexCoord2i(1, 0); glVertex2i(X + e_Textures[id].Width, Y + e_Textures[id].Height); - end; - + drawTxQuad(X, Y, e_Textures[id].tx.width, e_Textures[id].tx.height, e_Textures[id].tx.width, e_Textures[id].tx.height, e_Textures[ID].tx.u, e_Textures[ID].tx.v, Mirror); glEnd(); if Angle <> 0 then @@ -673,6 +887,7 @@ end; procedure e_DrawPoint(Size: Byte; X, Y: Integer; Red, Green, Blue: Byte); begin + if e_NoGraphics then Exit; glDisable(GL_TEXTURE_2D); glColor3ub(Red, Green, Blue); glPointSize(Size); @@ -713,6 +928,7 @@ procedure e_DrawQuad(X1, Y1, X2, Y2: Integer; Red, Green, Blue: Byte; Alpha: Byt var nX1, nY1, nX2, nY2: Integer; begin + if e_NoGraphics then Exit; // Only top-left/bottom-right quad if X1 > X2 then begin @@ -731,13 +947,13 @@ begin begin glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - end else + end + else glDisable(GL_BLEND); glDisable(GL_TEXTURE_2D); glColor4ub(Red, Green, Blue, 255-Alpha); glLineWidth(1); - glBegin(GL_LINES); nX1 := X1; nY1 := Y1; nX2 := X2; nY2 := Y1; @@ -763,27 +979,26 @@ begin glVertex2i(nX1, nY1); glVertex2i(nX2, nY2); glEnd(); - glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); - glDisable(GL_BLEND); end; procedure e_DrawFillQuad(X1, Y1, X2, Y2: Integer; Red, Green, Blue, Alpha: Byte; - Blending: TBlending = B_NONE); + Blending: TBlending = TBlending.None); begin - if (Alpha > 0) or (Blending <> B_NONE) then + if e_NoGraphics then Exit; + if (Alpha > 0) or (Blending <> TBlending.None) then glEnable(GL_BLEND) else glDisable(GL_BLEND); - if Blending = B_BLEND then + if Blending = TBlending.Blend then glBlendFunc(GL_SRC_ALPHA, GL_ONE) else - if Blending = B_FILTER then + if Blending = TBlending.Filter then glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR) else - if Blending = B_INVERT then + if Blending = TBlending.Invert then glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ZERO) else if Alpha > 0 then @@ -807,8 +1022,37 @@ begin glDisable(GL_BLEND); end; + +// ////////////////////////////////////////////////////////////////////////// // +procedure e_DarkenQuad (x0, y0, x1, y1: Integer; a: Integer); +begin + if (a < 0) then a := 0; + if (a > 255) then a := 255; + glEnable(GL_BLEND); + glBlendFunc(GL_ZERO, GL_SRC_ALPHA); + glDisable(GL_TEXTURE_2D); + glColor4ub(0, 0, 0, Byte(255-a)); + glBegin(GL_QUADS); + glVertex2i(x0, y0); + glVertex2i(x1, y0); + glVertex2i(x1, y1); + glVertex2i(x0, y1); + glEnd(); + //glRect(x, y, x+w, y+h); + glColor4ub(1, 1, 1, 1); + glDisable(GL_BLEND); + //glBlendEquation(GL_FUNC_ADD); +end; + +procedure e_DarkenQuadWH (x, y, w, h: Integer; a: Integer); +begin + if (w > 0) and (h > 0) then e_DarkenQuad(x, y, x+w, y+h, a); +end; + + procedure e_DrawLine(Width: Byte; X1, Y1, X2, Y2: Integer; Red, Green, Blue: Byte; Alpha: Byte = 0); begin + if e_NoGraphics then Exit; // Pixel-perfect lines if Width = 1 then e_LineCorrection(X1, Y1, X2, Y2); @@ -823,12 +1067,10 @@ begin glDisable(GL_TEXTURE_2D); glColor4ub(Red, Green, Blue, 255-Alpha); glLineWidth(Width); - glBegin(GL_LINES); glVertex2i(X1, Y1); glVertex2i(X2, Y2); glEnd(); - glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); glDisable(GL_BLEND); @@ -839,10 +1081,11 @@ end; //------------------------------------------------------------------ procedure e_DeleteTexture(ID: DWORD); begin - glDeleteTextures(1, @e_Textures[ID].ID); - e_Textures[ID].ID := 0; - e_Textures[ID].Width := 0; - e_Textures[ID].Height := 0; + if not e_NoGraphics then + glDeleteTextures(1, @e_Textures[ID].tx.id); + e_Textures[ID].tx.id := 0; + e_Textures[ID].tx.Width := 0; + e_Textures[ID].tx.Height := 0; end; //------------------------------------------------------------------ @@ -855,7 +1098,7 @@ begin if e_Textures = nil then Exit; for i := 0 to High(e_Textures) do - if e_Textures[i].Width <> 0 then e_DeleteTexture(i); + if e_Textures[i].tx.Width <> 0 then e_DeleteTexture(i); e_Textures := nil; end; @@ -870,97 +1113,32 @@ end; procedure e_BeginRender(); begin + if e_NoGraphics then Exit; glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.0); end; procedure e_Clear(Mask: TGLbitfield; Red, Green, Blue: Single); overload; begin - glClearColor(Red, Green, Blue, 0); - glClear(Mask); + if e_NoGraphics then Exit; + glClearColor(Red, Green, Blue, 0); + glClear(Mask); end; procedure e_Clear(); overload; begin - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT); + if e_NoGraphics then Exit; + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); end; procedure e_EndRender(); begin + if e_NoGraphics then Exit; glPopMatrix(); end; -procedure e_MakeScreenshot(FileName: String; Width, Height: Word); -begin -end; - -{type - aRGB = Array [0..1] of TRGB; - PaRGB = ^aRGB; - - TByteArray = Array [0..1] of Byte; - PByteArray = ^TByteArray; - -var - FILEHEADER: BITMAPFILEHEADER; - INFOHEADER: BITMAPINFOHEADER; - pixels: PByteArray; - tmp: Byte; - i: Integer; - F: File of Byte; - -begin - if (Width mod 4) > 0 then - Width := Width + 4 - (Width mod 4); - - GetMem(pixels, Width*Height*3); - glReadPixels(0, 0, Width, Height, GL_RGB, GL_UNSIGNED_BYTE, pixels); - - for i := 0 to Width * Height - 1 do - with PaRGB(pixels)[i] do - begin - tmp := R; - R := B; - B := tmp; - end; - - with FILEHEADER do - begin - bfType := $4D42; // "BM" - bfSize := Width*Height*3 + SizeOf(BITMAPFILEHEADER) + SizeOf(BITMAPINFOHEADER); - bfReserved1 := 0; - bfReserved2 := 0; - bfOffBits := SizeOf(BITMAPFILEHEADER) + SizeOf(BITMAPINFOHEADER); - end; - - with INFOHEADER do - begin - biSize := SizeOf(BITMAPINFOHEADER); - biWidth := Width; - biHeight := Height; - biPlanes := 1; - biBitCount := 24; - biCompression := 0; - biSizeImage := Width*Height*3; - biXPelsPerMeter := 0; - biYPelsPerMeter := 0; - biClrUsed := 0; - biClrImportant := 0; - end; - - AssignFile(F, FileName); - Rewrite(F); - - BlockWrite(F, FILEHEADER, SizeOf(FILEHEADER)); - BlockWrite(F, INFOHEADER, SizeOf(INFOHEADER)); - BlockWrite(F, pixels[0], Width*Height*3); - - CloseFile(F); - - FreeMem(pixels); -end;} - +{$IFDEF USE_SDL2} function e_GetGamma(win: PSDL_Window): Byte; var ramp: array [0..256*3-1] of Word; @@ -972,6 +1150,8 @@ var A, B: double; i, j: integer; begin + Result := 0; + if e_NoGraphics then Exit; rgb[0] := 1.0; rgb[1] := 1.0; rgb[2] := 1.0; @@ -1006,6 +1186,7 @@ var r: double; g: double; begin + if e_NoGraphics then Exit; g := (100 - Gamma)*(2.7 - 0.23)/100 + 0.23; for i := 0 to 255 do @@ -1020,18 +1201,19 @@ begin SDL_SetWindowGammaRamp(win, @ramp[0], @ramp[256], @ramp[512]); end; +{$ENDIF} function e_CharFont_Create(sp: ShortInt=0): DWORD; var i, id: DWORD; begin - e_WriteLog('Creating CharFont...', MSG_NOTIFY); + e_WriteLog('Creating CharFont...', TMsgType.Notify); id := DWORD(-1); if e_CharFonts <> nil then for i := 0 to High(e_CharFonts) do - if not e_CharFonts[i].Live then + if not e_CharFonts[i].alive then begin id := i; Break; @@ -1053,7 +1235,7 @@ begin end; Space := sp; - Live := True; + alive := True; end; Result := id; @@ -1072,6 +1254,7 @@ procedure e_CharFont_Print(FontID: DWORD; X, Y: Integer; Text: string); var a: Integer; begin + if e_NoGraphics then Exit; if Text = '' then Exit; if e_CharFonts = nil then Exit; if Integer(FontID) > High(e_CharFonts) then Exit; @@ -1094,6 +1277,7 @@ var a: Integer; c: TRGB; begin + if e_NoGraphics then Exit; if Text = '' then Exit; if e_CharFonts = nil then Exit; if Integer(FontID) > High(e_CharFonts) then Exit; @@ -1128,6 +1312,7 @@ var tc, c: TRGB; w, h: Word; begin + if e_NoGraphics then Exit; if Text = '' then Exit; if e_CharFonts = nil then Exit; if Integer(FontID) > High(e_CharFonts) then Exit; @@ -1237,11 +1422,12 @@ end; procedure e_CharFont_GetSizeFmt(FontID: DWORD; Text: string; var w, h: Word); var a, lines, len: Integer; - h2, w2: Word; + h2, w2, tw, th: Word; begin w2 := 0; - w := 0; - h := 0; + h2 := 0; + tw := 0; + th := 0; if Text = '' then Exit; if e_CharFonts = nil then Exit; @@ -1257,29 +1443,26 @@ begin if Text[a] = #10 then begin Inc(lines); - if w2 > w then - begin - w := w2; - w2 := 0; - end; - continue; - end - else if Text[a] in [#1, #2, #3, #4, #18, #19, #20, #21] then + if w2 > tw then tw := w2; + w2 := 0; continue; + end; with Chars[Ord(Text[a])] do - if TextureID <> -1 then - begin - w2 := w2 + Width + IfThen(a = len, 0, Space); - e_GetTextureSize(TextureID, nil, @h2); - if h2 > h then h := h2; - end; + if TextureID <> -1 then + begin + w2 := w2 + Width + IfThen(a = len, 0, Space); + e_GetTextureSize(TextureID, nil, @h2); + if h2 > th then th := h2; + end; end; end; - if w2 > w then - w := w2; - h := h * lines; + if w2 > tw then + tw := w2; + + w := tw; + h := th * lines; end; function e_CharFont_GetMaxWidth(FontID: DWORD): Word; @@ -1322,7 +1505,7 @@ begin for a := 0 to High(Chars) do if Chars[a].TextureID <> -1 then e_DeleteTexture(Chars[a].TextureID); - e_CharFonts[FontID].Live := False; + e_CharFonts[FontID].alive := False; end; procedure e_CharFont_RemoveAll(); @@ -1340,11 +1523,14 @@ end; procedure e_TextureFontBuild(Tex: DWORD; var FontID: DWORD; XCount, YCount: Word; Space: ShortInt=0); var +{$IFDEF NOGL_LISTS} loop1 : GLuint; cx, cy : real; +{$ENDIF} i, id: DWORD; begin - e_WriteLog('Creating texture font...', MSG_NOTIFY); + if e_NoGraphics then Exit; + e_WriteLog('Creating texture font...', TMsgType.Notify); id := DWORD(-1); @@ -1364,17 +1550,20 @@ begin with e_TextureFonts[id] do begin +{$IFDEF NOGL_LISTS} Base := glGenLists(XCount*YCount); - TextureID := e_Textures[Tex].ID; - CharWidth := (e_Textures[Tex].Width div XCount)+Space; - CharHeight := e_Textures[Tex].Height div YCount; +{$ENDIF} + TextureID := e_Textures[Tex].tx.id; + CharWidth := (e_Textures[Tex].tx.Width div XCount)+Space; + CharHeight := e_Textures[Tex].tx.Height div YCount; XC := XCount; YC := YCount; Texture := Tex; SPC := Space; end; - glBindTexture(GL_TEXTURE_2D, e_Textures[Tex].ID); +{$IFDEF NOGL_LISTS} + glBindTexture(GL_TEXTURE_2D, e_Textures[Tex].tx.id); for loop1 := 0 to XCount*YCount-1 do begin cx := (loop1 mod XCount)/XCount; @@ -1383,74 +1572,76 @@ begin glNewList(e_TextureFonts[id].Base+loop1, GL_COMPILE); glBegin(GL_QUADS); glTexCoord2f(cx, 1.0-cy-1/YCount); - glVertex2d(0, e_Textures[Tex].Height div YCount); + glVertex2i(0, e_Textures[Tex].tx.Height div YCount); glTexCoord2f(cx+1/XCount, 1.0-cy-1/YCount); - glVertex2i(e_Textures[Tex].Width div XCount, e_Textures[Tex].Height div YCount); + glVertex2i(e_Textures[Tex].tx.Width div XCount, e_Textures[Tex].tx.Height div YCount); glTexCoord2f(cx+1/XCount, 1.0-cy); - glVertex2i(e_Textures[Tex].Width div XCount, 0); + glVertex2i(e_Textures[Tex].tx.Width div XCount, 0); glTexCoord2f(cx, 1.0-cy); glVertex2i(0, 0); glEnd(); - glTranslated((e_Textures[Tex].Width div XCount)+Space, 0, 0); + glTranslated((e_Textures[Tex].tx.Width div XCount)+Space, 0, 0); glEndList(); end; +{$ENDIF} FontID := id; end; -procedure e_TextureFontBuildInPlace(id: DWORD); -var - loop1 : GLuint; - cx, cy : real; - XCount, YCount, Space: Integer; - {i,} Tex: DWORD; +procedure e_TextureFontKill(FontID: DWORD); begin - with e_TextureFonts[id] do - begin - Base := glGenLists(XC*YC); - TextureID := e_Textures[Texture].ID; - XCount := XC; - YCount := YC; - Space := SPC; - Tex := Texture; - end; - - glBindTexture(GL_TEXTURE_2D, e_Textures[Tex].ID); - for loop1 := 0 to XCount*YCount-1 do - begin - cx := (loop1 mod XCount)/XCount; - cy := (loop1 div YCount)/YCount; - - glNewList(e_TextureFonts[id].Base+loop1, GL_COMPILE); - glBegin(GL_QUADS); - glTexCoord2f(cx, 1.0-cy-1/YCount); - glVertex2d(0, e_Textures[Tex].Height div YCount); - - glTexCoord2f(cx+1/XCount, 1.0-cy-1/YCount); - glVertex2i(e_Textures[Tex].Width div XCount, e_Textures[Tex].Height div YCount); - - glTexCoord2f(cx+1/XCount, 1.0-cy); - glVertex2i(e_Textures[Tex].Width div XCount, 0); + if e_NoGraphics then Exit; +{$IFDEF NOGL_LISTS} + glDeleteLists(e_TextureFonts[FontID].Base, 256); +{$ENDIF} + e_TextureFonts[FontID].Base := 0; +end; - glTexCoord2f(cx, 1.0-cy); +{$IFNDEF NOGL_LISTS} +procedure e_TextureFontDrawChar(ch: Char; FontID: DWORD); + var + index: Integer; + cx, cy: GLfloat; + Tex: Integer; + Width, Height: Integer; + XCount, YCount: Integer; +begin + index := Ord(ch) - 32; + Tex := e_TextureFonts[FontID].Texture; + Width := e_Textures[Tex].tx.Width; + Height := e_Textures[Tex].tx.Height; + XCount := e_TextureFonts[FontID].XC; + YCount := e_TextureFonts[FontID].YC; + cx := (index mod XCount)/XCount; + cy := (index div YCount)/YCount; + glBegin(GL_QUADS); + glTexCoord2f(cx, 1 - cy - 1/YCount); + glVertex2i(0, Height div YCount); + glTexCoord2f(cx + 1/XCount, 1 - cy - 1/YCount); + glVertex2i(Width div XCount, Height div YCount); + glTexCoord2f(cx + 1/XCount, 1 - cy); + glVertex2i(Width div XCount, 0); + glTexCoord2f(cx, 1 - cy); glVertex2i(0, 0); - glEnd(); - glTranslated((e_Textures[Tex].Width div XCount)+Space, 0, 0); - glEndList(); - end; + glEnd(); + glTranslatef((e_Textures[Tex].tx.Width div XCount) + e_TextureFonts[FontID].SPC, 0, 0); end; -procedure e_TextureFontKill(FontID: DWORD); +procedure e_TextureFontDrawString(Text: String; FontID: DWORD); + var + i: Integer; begin - glDeleteLists(e_TextureFonts[FontID].Base, 256); - e_TextureFonts[FontID].Base := 0; + for i := 1 to High(Text) do + e_TextureFontDrawChar(Text[i], FontID); end; +{$ENDIF} procedure e_TextureFontPrint(X, Y: GLint; Text: string; FontID: DWORD); begin + if e_NoGraphics then Exit; if Integer(FontID) > High(e_TextureFonts) then Exit; if Text = '' then Exit; @@ -1462,9 +1653,13 @@ begin glPushMatrix; glBindTexture(GL_TEXTURE_2D, e_TextureFonts[FontID].TextureID); glEnable(GL_TEXTURE_2D); - glTranslated(x, y, 0); + glTranslatef(x, y, 0); +{$IFDEF NOGL_LISTS} glListBase(DWORD(Integer(e_TextureFonts[FontID].Base)-32)); glCallLists(Length(Text), GL_UNSIGNED_BYTE, PChar(Text)); +{$ELSE} + e_TextureFontDrawString(Text, FontID); +{$ENDIF} glDisable(GL_TEXTURE_2D); glPopMatrix; @@ -1474,30 +1669,59 @@ end; // god forgive me for this, but i cannot figure out how to do it without lists procedure e_TextureFontPrintChar(X, Y: Integer; Ch: Char; FontID: DWORD; Shadow: Boolean = False); begin + if e_NoGraphics then Exit; glPushMatrix; if Shadow then begin glColor4ub(0, 0, 0, 128); - glTranslated(X+1, Y+1, 0); + glTranslatef(X+1, Y+1, 0); +{$IFDEF NOGL_LISTS} glCallLists(1, GL_UNSIGNED_BYTE, @Ch); +{$ELSE} + e_TextureFontDrawChar(Ch, FontID); +{$ENDIF} glPopMatrix; glPushMatrix; end; glColor4ub(e_Colors.R, e_Colors.G, e_Colors.B, 255); - glTranslated(X, Y, 0); + glTranslatef(X, Y, 0); +{$IFDEF NOGL_LISTS} glCallLists(1, GL_UNSIGNED_BYTE, @Ch); +{$ELSE} + e_TextureFontDrawChar(Ch, FontID); +{$ENDIF} glPopMatrix; end; -procedure e_TextureFontPrintFmt(X, Y: Integer; Text: string; FontID: DWORD; Shadow: Boolean = False); +procedure e_TextureFontPrintCharEx (X, Y: Integer; Ch: Char; FontID: DWORD; Shadow: Boolean = False); +begin + glBindTexture(GL_TEXTURE_2D, e_TextureFonts[FontID].TextureID); + glEnable(GL_TEXTURE_2D); + //glListBase(DWORD(Integer(e_TextureFonts[FontID].Base)-32)); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + e_TextureFontPrintChar(X, Y, Ch, FontID, Shadow); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); +end; + +function e_TextureFontCharWidth (ch: Char; FontID: DWORD): Integer; +begin + result := e_TextureFonts[FontID].CharWidth; +end; + +procedure e_TextureFontPrintFmt(X, Y: GLint; Text: string; FontID: DWORD; + Shadow: Boolean = False; Newlines: Boolean = False); var a, TX, TY, len: Integer; tc, c: TRGB; - w: Word; + w, h: Word; begin + if e_NoGraphics then Exit; if Text = '' then Exit; if e_TextureFonts = nil then Exit; if Integer(FontID) > High(e_TextureFonts) then Exit; @@ -1511,12 +1735,16 @@ begin len := Length(Text); w := e_TextureFonts[FontID].CharWidth; + h := e_TextureFonts[FontID].CharHeight; with e_TextureFonts[FontID] do begin glBindTexture(GL_TEXTURE_2D, e_TextureFonts[FontID].TextureID); glEnable(GL_TEXTURE_2D); + +{$IFDEF NOGL_LISTS} glListBase(DWORD(Integer(e_TextureFonts[FontID].Base)-32)); +{$ENDIF} glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); @@ -1524,12 +1752,15 @@ begin for a := 1 to len do begin case Text[a] of - {#10: // line feed + #10: // line feed begin - TX := X; - TY := TY + h; - continue; - end;} + if Newlines then + begin + TX := X; + TY := TY + h; + continue; + end; + end; #1: // black begin c.R := 0; c.G := 0; c.B := 0; @@ -1587,12 +1818,16 @@ end; procedure e_TextureFontPrintEx(X, Y: GLint; Text: string; FontID: DWORD; Red, Green, Blue: Byte; Scale: Single; Shadow: Boolean = False); begin + if e_NoGraphics then Exit; if Text = '' then Exit; glPushMatrix; glBindTexture(GL_TEXTURE_2D, e_TextureFonts[FontID].TextureID); glEnable(GL_TEXTURE_2D); + +{$IFDEF NOGL_LISTS} glListBase(DWORD(Integer(e_TextureFonts[FontID].Base)-32)); +{$ENDIF} glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); @@ -1600,17 +1835,25 @@ begin if Shadow then begin glColor4ub(0, 0, 0, 128); - glTranslated(x+1, y+1, 0); + glTranslatef(x+1, y+1, 0); glScalef(Scale, Scale, 0); +{$IFDEF NOGL_LISTS} glCallLists(Length(Text), GL_UNSIGNED_BYTE, PChar(Text)); +{$ELSE} + e_TextureFontDrawString(Text, FontID); +{$ENDIF} glPopMatrix; glPushMatrix; end; glColor4ub(Red, Green, Blue, 255); - glTranslated(x, y, 0); + glTranslatef(x, y, 0); glScalef(Scale, Scale, 0); +{$IFDEF NOGL_LISTS} glCallLists(Length(Text), GL_UNSIGNED_BYTE, PChar(Text)); +{$ELSE} + e_TextureFontDrawString(Text, FontID); +{$ENDIF} glDisable(GL_TEXTURE_2D); glPopMatrix; @@ -1618,8 +1861,11 @@ begin glDisable(GL_BLEND); end; -procedure e_TextureFontGetSize(ID: DWORD; var CharWidth, CharHeight: Byte); +procedure e_TextureFontGetSize(ID: DWORD; out CharWidth, CharHeight: Byte); begin + CharWidth := 16; + CharHeight := 16; + if e_NoGraphics then Exit; if Integer(ID) > High(e_TextureFonts) then Exit; CharWidth := e_TextureFonts[ID].CharWidth; @@ -1630,106 +1876,21 @@ procedure e_RemoveAllTextureFont(); var i: integer; begin + if e_NoGraphics then Exit; if e_TextureFonts = nil then Exit; for i := 0 to High(e_TextureFonts) do if e_TextureFonts[i].Base <> 0 then begin +{$IFDEF NOGL_LISTS} glDeleteLists(e_TextureFonts[i].Base, 256); +{$ENDIF} e_TextureFonts[i].Base := 0; end; e_TextureFonts := nil; end; -procedure e_SaveGLContext(); -var - PxLen: Cardinal; - i: Integer; -begin - e_WriteLog('Backing up GL context:', MSG_NOTIFY); - - glPushAttrib(GL_ALL_ATTRIB_BITS); - glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); - - if e_Textures <> nil then - begin - e_WriteLog(' Backing up textures...', MSG_NOTIFY); - SetLength(e_SavedTextures, Length(e_Textures)); - for i := Low(e_Textures) to High(e_Textures) do - begin - e_SavedTextures[i].Pixels := nil; - if e_Textures[i].Width > 0 then - begin - with e_SavedTextures[i] do - begin - PxLen := 3; - if e_Textures[i].Fmt = GL_RGBA then Inc(PxLen); - Pixels := GetMem(PxLen * e_Textures[i].Width * e_Textures[i].Height); - glBindTexture(GL_TEXTURE_2D, e_Textures[i].ID); - glGetTexImage(GL_TEXTURE_2D, 0, e_Textures[i].Fmt, GL_UNSIGNED_BYTE, Pixels); - glBindTexture(GL_TEXTURE_2D, 0); - OldID := e_Textures[i].ID; - TexId := i; - end; - end; - end; - end; - - if e_TextureFonts <> nil then - begin - e_WriteLog(' Releasing texturefonts...', MSG_NOTIFY); - for i := 0 to High(e_TextureFonts) do - if e_TextureFonts[i].Base <> 0 then - begin - glDeleteLists(e_TextureFonts[i].Base, 256); - e_TextureFonts[i].Base := 0; - end; - end; -end; - -procedure e_RestoreGLContext(); -var - GLID: GLuint; - i: Integer; -begin - e_WriteLog('Restoring GL context:', MSG_NOTIFY); - - glPopClientAttrib(); - glPopAttrib(); - - if e_SavedTextures <> nil then - begin - e_WriteLog(' Regenerating textures...', MSG_NOTIFY); - for i := Low(e_SavedTextures) to High(e_SavedTextures) do - begin - if e_SavedTextures[i].Pixels <> nil then - with e_SavedTextures[i] do - begin - GLID := CreateTexture(e_Textures[TexID].Width, e_Textures[TexID].Height, - e_Textures[TexID].Fmt, Pixels); - e_Textures[TexID].ID := GLID; - FreeMem(Pixels); - end; - end; - end; - - if e_TextureFonts <> nil then - begin - e_WriteLog(' Regenerating texturefonts...', MSG_NOTIFY); - for i := Low(e_TextureFonts) to High(e_TextureFonts) do - with e_TextureFonts[i] do - begin - TextureID := e_Textures[Texture].ID; - Base := 0; - e_TextureFontBuildInPlace(i); - end; - end; - - SetLength(e_SavedTextures, 0); -end; - - function _RGB(Red, Green, Blue: Byte): TRGB; begin Result.R := Red; @@ -1759,4 +1920,171 @@ begin Result.Bottom := B; end; + +procedure e_MakeScreenshot (st: TStream; Width, Height: Word); +var + pixels, obuf, scln, ps, pd: PByte; + obufsize: Integer; + dlen: Cardinal; + i, x, y, res: Integer; + sign: array [0..7] of Byte; + hbuf: array [0..12] of Byte; + crc: LongWord; + img: TImageData; + clr: TColor32Rec; +begin + if e_NoGraphics then Exit; + obuf := nil; + + // first, extract and pack graphics data + if (Width mod 4) > 0 then Width := Width+4-(Width mod 4); + + GetMem(pixels, Width*Height*3); + try + FillChar(pixels^, Width*Height*3, 0); + glReadPixels(0, 0, Width, Height, GL_RGB, GL_UNSIGNED_BYTE, pixels); + //e_WriteLog('PNG: pixels read', MSG_NOTIFY); + + if e_FastScreenshots then + begin + // create scanlines + GetMem(scln, (Width*3+1)*Height); + try + ps := pixels; + pd := scln; + Inc(ps, (Width*3)*(Height-1)); + for i := 0 to Height-1 do + begin + pd^ := 0; // filter + Inc(pd); + Move(ps^, pd^, Width*3); + Dec(ps, Width*3); + Inc(pd, Width*3); + end; + except + FreeMem(scln); + raise; + end; + FreeMem(pixels); + pixels := scln; + + // pack it + obufsize := (Width*3+1)*Height*2; + GetMem(obuf, obufsize); + try + while true do + begin + dlen := obufsize; + res := compress2(Pointer(obuf), dlen, Pointer(pixels), (Width*3+1)*Height, 9); + if res = Z_OK then break; + if res <> Z_BUF_ERROR then raise Exception.Create('can''t pack data for PNG'); + obufsize := obufsize*2; + FreeMem(obuf); + obuf := nil; + GetMem(obuf, obufsize); + end; + //e_WriteLog(Format('PNG: pixels compressed from %d to %d', [Integer(Width*Height*3), Integer(dlen)]), MSG_NOTIFY); + + // now write PNG + + // signature + sign[0] := 137; + sign[1] := 80; + sign[2] := 78; + sign[3] := 71; + sign[4] := 13; + sign[5] := 10; + sign[6] := 26; + sign[7] := 10; + st.writeBuffer(sign, 8); + //e_WriteLog('PNG: signature written', MSG_NOTIFY); + + // header + writeIntBE(st, LongWord(13)); + sign[0] := 73; + sign[1] := 72; + sign[2] := 68; + sign[3] := 82; + st.writeBuffer(sign, 4); + crc := crc32(0, @sign[0], 4); + hbuf[0] := 0; + hbuf[1] := 0; + hbuf[2] := (Width shr 8) and $ff; + hbuf[3] := Width and $ff; + hbuf[4] := 0; + hbuf[5] := 0; + hbuf[6] := (Height shr 8) and $ff; + hbuf[7] := Height and $ff; + hbuf[8] := 8; // bit depth + hbuf[9] := 2; // RGB + hbuf[10] := 0; // compression method + hbuf[11] := 0; // filter method + hbuf[12] := 0; // no interlace + crc := crc32(crc, @hbuf[0], 13); + st.writeBuffer(hbuf, 13); + writeIntBE(st, crc); + //e_WriteLog('PNG: header written', MSG_NOTIFY); + + // image data + writeIntBE(st, LongWord(dlen)); + sign[0] := 73; + sign[1] := 68; + sign[2] := 65; + sign[3] := 84; + st.writeBuffer(sign, 4); + crc := crc32(0, @sign[0], 4); + crc := crc32(crc, obuf, dlen); + st.writeBuffer(obuf^, dlen); + writeIntBE(st, crc); + //e_WriteLog('PNG: image data written', MSG_NOTIFY); + + // image data end + writeIntBE(st, LongWord(0)); + sign[0] := 73; + sign[1] := 69; + sign[2] := 78; + sign[3] := 68; + st.writeBuffer(sign, 4); + crc := crc32(0, @sign[0], 4); + writeIntBE(st, crc); + //e_WriteLog('PNG: end marker written', MSG_NOTIFY); + finally + if obuf <> nil then FreeMem(obuf); + end; + end + else + begin + Imaging.SetOption(ImagingPNGCompressLevel, 9); + Imaging.SetOption(ImagingPNGPreFilter, 6); + InitImage(img); + try + NewImage(Width, Height, TImageFormat.ifR8G8B8, img); + ps := pixels; + //writeln(stderr, 'moving pixels...'); + for y := Height-1 downto 0 do + begin + for x := 0 to Width-1 do + begin + clr.r := ps^; Inc(ps); + clr.g := ps^; Inc(ps); + clr.b := ps^; Inc(ps); + clr.a := 255; + SetPixel32(img, x, y, clr); + end; + end; + GlobalMetadata.ClearMetaItems(); + GlobalMetadata.ClearMetaItemsForSaving(); + //writeln(stderr, 'compressing image...'); + if not SaveImageToStream('png', st, img) then raise Exception.Create('screenshot writing error'); + //writeln(stderr, 'done!'); + finally + FreeImage(img); + end; + end; + finally + FreeMem(pixels); + end; +end; + + end.