summaryrefslogtreecommitdiff
path: root/src/renderercommon
diff options
context:
space:
mode:
authorIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
committerIronClawTrem <louie.nutman@gmail.com>2020-02-16 03:40:06 +0000
commit425decdf7e9284d15aa726e3ae96b9942fb0e3ea (patch)
tree6c0dd7edfefff1be7b9e75fe0b3a0a85fe1595f3 /src/renderercommon
parentccb0b2e4d6674a7a00c9bf491f08fc73b6898c54 (diff)
create tremded branch
Diffstat (limited to 'src/renderercommon')
-rw-r--r--src/renderercommon/CMakeLists.txt77
-rw-r--r--src/renderercommon/iqm.h131
-rw-r--r--src/renderercommon/qgl.h570
-rw-r--r--src/renderercommon/tr_common.h166
-rw-r--r--src/renderercommon/tr_font.cpp562
-rw-r--r--src/renderercommon/tr_image_bmp.cpp240
-rw-r--r--src/renderercommon/tr_image_jpg.cpp479
-rw-r--r--src/renderercommon/tr_image_pcx.cpp177
-rw-r--r--src/renderercommon/tr_image_png.cpp2486
-rw-r--r--src/renderercommon/tr_image_tga.cpp317
-rw-r--r--src/renderercommon/tr_noise.cpp93
-rw-r--r--src/renderercommon/tr_public.h199
-rw-r--r--src/renderercommon/tr_types.h222
13 files changed, 5719 insertions, 0 deletions
diff --git a/src/renderercommon/CMakeLists.txt b/src/renderercommon/CMakeLists.txt
new file mode 100644
index 0000000..6464d32
--- /dev/null
+++ b/src/renderercommon/CMakeLists.txt
@@ -0,0 +1,77 @@
+include(${CMAKE_SOURCE_DIR}/cmake/SDL2.cmake)
+
+include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c ${SDL2_INCLUDE_DIRS} )
+
+add_library(
+ renderercommon STATIC
+ iqm.h
+ qgl.h
+ tr_common.h
+ tr_font.cpp
+ tr_image_bmp.cpp
+ tr_image_jpg.cpp
+ tr_image_pcx.cpp
+ tr_image_png.cpp
+ tr_image_tga.cpp
+ tr_noise.cpp
+ tr_public.h
+ tr_types.h
+ ${CMAKE_CURRENT_SOURCE_DIR}/../sdl/sdl_gamma.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/../sdl/sdl_glimp.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jaricom.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcapimin.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcapistd.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcarith.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jccoefct.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jccolor.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcdctmgr.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jchuff.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcinit.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcmainct.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcmarker.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcmaster.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcomapi.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcparam.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcprepct.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jcsample.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jctrans.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdapimin.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdapistd.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdarith.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdatadst.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdatasrc.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdcoefct.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdcolor.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jddctmgr.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdhuff.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdinput.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdmainct.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdmarker.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdmaster.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdmerge.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdpostct.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdsample.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jdtrans.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jerror.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jfdctflt.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jfdctfst.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jfdctint.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jidctflt.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jidctfst.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jidctint.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jmemmgr.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jmemnobs.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jquant1.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jquant2.c
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../external/jpeg-8c/jutils.c
+ )
+
+add_definitions(
+ -DFLOATING_POINT
+ -DHAVE_LRINTF
+ -DUSE_INTERNAL_JPEG
+ -DUSE_RENDERER_DLOPEN
+ ${SDL2_DEFINES}
+ )
+
+target_link_libraries( renderercommon ${SDL2_LIBRARIES} )
diff --git a/src/renderercommon/iqm.h b/src/renderercommon/iqm.h
new file mode 100644
index 0000000..3011efc
--- /dev/null
+++ b/src/renderercommon/iqm.h
@@ -0,0 +1,131 @@
+/*
+===========================================================================
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+
+#ifndef __IQM_H__
+#define __IQM_H__
+
+#define IQM_MAGIC "INTERQUAKEMODEL"
+#define IQM_VERSION 2
+
+#define IQM_MAX_JOINTS 128
+
+typedef struct iqmheader
+{
+ char magic[16];
+ unsigned int version;
+ unsigned int filesize;
+ unsigned int flags;
+ unsigned int num_text, ofs_text;
+ unsigned int num_meshes, ofs_meshes;
+ unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays;
+ unsigned int num_triangles, ofs_triangles, ofs_adjacency;
+ unsigned int num_joints, ofs_joints;
+ unsigned int num_poses, ofs_poses;
+ unsigned int num_anims, ofs_anims;
+ unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds;
+ unsigned int num_comment, ofs_comment;
+ unsigned int num_extensions, ofs_extensions;
+} iqmHeader_t;
+
+typedef struct iqmmesh
+{
+ unsigned int name;
+ unsigned int material;
+ unsigned int first_vertex, num_vertexes;
+ unsigned int first_triangle, num_triangles;
+} iqmMesh_t;
+
+enum
+{
+ IQM_POSITION = 0,
+ IQM_TEXCOORD = 1,
+ IQM_NORMAL = 2,
+ IQM_TANGENT = 3,
+ IQM_BLENDINDEXES = 4,
+ IQM_BLENDWEIGHTS = 5,
+ IQM_COLOR = 6,
+ IQM_CUSTOM = 0x10
+};
+
+enum
+{
+ IQM_BYTE = 0,
+ IQM_UBYTE = 1,
+ IQM_SHORT = 2,
+ IQM_USHORT = 3,
+ IQM_INT = 4,
+ IQM_UINT = 5,
+ IQM_HALF = 6,
+ IQM_FLOAT = 7,
+ IQM_DOUBLE = 8,
+};
+
+typedef struct iqmtriangle
+{
+ unsigned int vertex[3];
+} iqmTriangle_t;
+
+typedef struct iqmjoint
+{
+ unsigned int name;
+ int parent;
+ float translate[3], rotate[4], scale[3];
+} iqmJoint_t;
+
+typedef struct iqmpose
+{
+ int parent;
+ unsigned int mask;
+ float channeloffset[10];
+ float channelscale[10];
+} iqmPose_t;
+
+typedef struct iqmanim
+{
+ unsigned int name;
+ unsigned int first_frame, num_frames;
+ float framerate;
+ unsigned int flags;
+} iqmAnim_t;
+
+enum
+{
+ IQM_LOOP = 1<<0
+};
+
+typedef struct iqmvertexarray
+{
+ unsigned int type;
+ unsigned int flags;
+ unsigned int format;
+ unsigned int size;
+ unsigned int offset;
+} iqmVertexArray_t;
+
+typedef struct iqmbounds
+{
+ float bbmin[3], bbmax[3];
+ float xyradius, radius;
+} iqmBounds_t;
+
+#endif
diff --git a/src/renderercommon/qgl.h b/src/renderercommon/qgl.h
new file mode 100644
index 0000000..4411daf
--- /dev/null
+++ b/src/renderercommon/qgl.h
@@ -0,0 +1,570 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+/*
+** QGL.H
+*/
+
+#ifndef __QGL_H__
+#define __QGL_H__
+
+#ifdef USE_LOCAL_HEADERS
+# include "SDL_opengl.h"
+#else
+# include <SDL_opengl.h>
+#endif
+
+extern void (APIENTRYP qglActiveTextureARB) (GLenum texture);
+extern void (APIENTRYP qglClientActiveTextureARB) (GLenum texture);
+extern void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t);
+
+extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count);
+extern void (APIENTRYP qglUnlockArraysEXT) (void);
+
+
+//===========================================================================
+
+#define qglAccum glAccum
+#define qglAlphaFunc glAlphaFunc
+#define qglAreTexturesResident glAreTexturesResident
+#define qglArrayElement glArrayElement
+#define qglBegin glBegin
+#define qglBindTexture glBindTexture
+#define qglBitmap glBitmap
+#define qglBlendFunc glBlendFunc
+#define qglCallList glCallList
+#define qglCallLists glCallLists
+#define qglClear glClear
+#define qglClearAccum glClearAccum
+#define qglClearColor glClearColor
+#define qglClearDepth glClearDepth
+#define qglClearIndex glClearIndex
+#define qglClearStencil glClearStencil
+#define qglClipPlane glClipPlane
+#define qglColor3b glColor3b
+#define qglColor3bv glColor3bv
+#define qglColor3d glColor3d
+#define qglColor3dv glColor3dv
+#define qglColor3f glColor3f
+#define qglColor3fv glColor3fv
+#define qglColor3i glColor3i
+#define qglColor3iv glColor3iv
+#define qglColor3s glColor3s
+#define qglColor3sv glColor3sv
+#define qglColor3ub glColor3ub
+#define qglColor3ubv glColor3ubv
+#define qglColor3ui glColor3ui
+#define qglColor3uiv glColor3uiv
+#define qglColor3us glColor3us
+#define qglColor3usv glColor3usv
+#define qglColor4b glColor4b
+#define qglColor4bv glColor4bv
+#define qglColor4d glColor4d
+#define qglColor4dv glColor4dv
+#define qglColor4f glColor4f
+#define qglColor4fv glColor4fv
+#define qglColor4i glColor4i
+#define qglColor4iv glColor4iv
+#define qglColor4s glColor4s
+#define qglColor4sv glColor4sv
+#define qglColor4ub glColor4ub
+#define qglColor4ubv glColor4ubv
+#define qglColor4ui glColor4ui
+#define qglColor4uiv glColor4uiv
+#define qglColor4us glColor4us
+#define qglColor4usv glColor4usv
+#define qglColorMask glColorMask
+#define qglColorMaterial glColorMaterial
+#define qglColorPointer glColorPointer
+#define qglCopyPixels glCopyPixels
+#define qglCopyTexImage1D glCopyTexImage1D
+#define qglCopyTexImage2D glCopyTexImage2D
+#define qglCopyTexSubImage1D glCopyTexSubImage1D
+#define qglCopyTexSubImage2D glCopyTexSubImage2D
+#define qglCullFace glCullFace
+#define qglDeleteLists glDeleteLists
+#define qglDeleteTextures glDeleteTextures
+#define qglDepthFunc glDepthFunc
+#define qglDepthMask glDepthMask
+#define qglDepthRange glDepthRange
+#define qglDisable glDisable
+#define qglDisableClientState glDisableClientState
+#define qglDrawArrays glDrawArrays
+#define qglDrawBuffer glDrawBuffer
+#define qglDrawElements glDrawElements
+#define qglDrawPixels glDrawPixels
+#define qglEdgeFlag glEdgeFlag
+#define qglEdgeFlagPointer glEdgeFlagPointer
+#define qglEdgeFlagv glEdgeFlagv
+#define qglEnable glEnable
+#define qglEnableClientState glEnableClientState
+#define qglEnd glEnd
+#define qglEndList glEndList
+#define qglEvalCoord1d glEvalCoord1d
+#define qglEvalCoord1dv glEvalCoord1dv
+#define qglEvalCoord1f glEvalCoord1f
+#define qglEvalCoord1fv glEvalCoord1fv
+#define qglEvalCoord2d glEvalCoord2d
+#define qglEvalCoord2dv glEvalCoord2dv
+#define qglEvalCoord2f glEvalCoord2f
+#define qglEvalCoord2fv glEvalCoord2fv
+#define qglEvalMesh1 glEvalMesh1
+#define qglEvalMesh2 glEvalMesh2
+#define qglEvalPoint1 glEvalPoint1
+#define qglEvalPoint2 glEvalPoint2
+#define qglFeedbackBuffer glFeedbackBuffer
+#define qglFinish glFinish
+#define qglFlush glFlush
+#define qglFogf glFogf
+#define qglFogfv glFogfv
+#define qglFogi glFogi
+#define qglFogiv glFogiv
+#define qglFrontFace glFrontFace
+#define qglFrustum glFrustum
+#define qglGenLists glGenLists
+#define qglGenTextures glGenTextures
+#define qglGetBooleanv glGetBooleanv
+#define qglGetClipPlane glGetClipPlane
+#define qglGetDoublev glGetDoublev
+#define qglGetError glGetError
+#define qglGetFloatv glGetFloatv
+#define qglGetIntegerv glGetIntegerv
+#define qglGetLightfv glGetLightfv
+#define qglGetLightiv glGetLightiv
+#define qglGetMapdv glGetMapdv
+#define qglGetMapfv glGetMapfv
+#define qglGetMapiv glGetMapiv
+#define qglGetMaterialfv glGetMaterialfv
+#define qglGetMaterialiv glGetMaterialiv
+#define qglGetPixelMapfv glGetPixelMapfv
+#define qglGetPixelMapuiv glGetPixelMapuiv
+#define qglGetPixelMapusv glGetPixelMapusv
+#define qglGetPointerv glGetPointerv
+#define qglGetPolygonStipple glGetPolygonStipple
+#define qglGetString glGetString
+#define qglGetTexGendv glGetTexGendv
+#define qglGetTexGenfv glGetTexGenfv
+#define qglGetTexGeniv glGetTexGeniv
+#define qglGetTexImage glGetTexImage
+#define qglGetTexLevelParameterfv glGetTexLevelParameterfv
+#define qglGetTexLevelParameteriv glGetTexLevelParameteriv
+#define qglGetTexParameterfv glGetTexParameterfv
+#define qglGetTexParameteriv glGetTexParameteriv
+#define qglHint glHint
+#define qglIndexMask glIndexMask
+#define qglIndexPointer glIndexPointer
+#define qglIndexd glIndexd
+#define qglIndexdv glIndexdv
+#define qglIndexf glIndexf
+#define qglIndexfv glIndexfv
+#define qglIndexi glIndexi
+#define qglIndexiv glIndexiv
+#define qglIndexs glIndexs
+#define qglIndexsv glIndexsv
+#define qglIndexub glIndexub
+#define qglIndexubv glIndexubv
+#define qglInitNames glInitNames
+#define qglInterleavedArrays glInterleavedArrays
+#define qglIsEnabled glIsEnabled
+#define qglIsList glIsList
+#define qglIsTexture glIsTexture
+#define qglLightModelf glLightModelf
+#define qglLightModelfv glLightModelfv
+#define qglLightModeli glLightModeli
+#define qglLightModeliv glLightModeliv
+#define qglLightf glLightf
+#define qglLightfv glLightfv
+#define qglLighti glLighti
+#define qglLightiv glLightiv
+#define qglLineStipple glLineStipple
+#define qglLineWidth glLineWidth
+#define qglListBase glListBase
+#define qglLoadIdentity glLoadIdentity
+#define qglLoadMatrixd glLoadMatrixd
+#define qglLoadMatrixf glLoadMatrixf
+#define qglLoadName glLoadName
+#define qglLogicOp glLogicOp
+#define qglMap1d glMap1d
+#define qglMap1f glMap1f
+#define qglMap2d glMap2d
+#define qglMap2f glMap2f
+#define qglMapGrid1d glMapGrid1d
+#define qglMapGrid1f glMapGrid1f
+#define qglMapGrid2d glMapGrid2d
+#define qglMapGrid2f glMapGrid2f
+#define qglMaterialf glMaterialf
+#define qglMaterialfv glMaterialfv
+#define qglMateriali glMateriali
+#define qglMaterialiv glMaterialiv
+#define qglMatrixMode glMatrixMode
+#define qglMultMatrixd glMultMatrixd
+#define qglMultMatrixf glMultMatrixf
+#define qglNewList glNewList
+#define qglNormal3b glNormal3b
+#define qglNormal3bv glNormal3bv
+#define qglNormal3d glNormal3d
+#define qglNormal3dv glNormal3dv
+#define qglNormal3f glNormal3f
+#define qglNormal3fv glNormal3fv
+#define qglNormal3i glNormal3i
+#define qglNormal3iv glNormal3iv
+#define qglNormal3s glNormal3s
+#define qglNormal3sv glNormal3sv
+#define qglNormalPointer glNormalPointer
+#define qglOrtho glOrtho
+#define qglPassThrough glPassThrough
+#define qglPixelMapfv glPixelMapfv
+#define qglPixelMapuiv glPixelMapuiv
+#define qglPixelMapusv glPixelMapusv
+#define qglPixelStoref glPixelStoref
+#define qglPixelStorei glPixelStorei
+#define qglPixelTransferf glPixelTransferf
+#define qglPixelTransferi glPixelTransferi
+#define qglPixelZoom glPixelZoom
+#define qglPointSize glPointSize
+#define qglPolygonMode glPolygonMode
+#define qglPolygonOffset glPolygonOffset
+#define qglPolygonStipple glPolygonStipple
+#define qglPopAttrib glPopAttrib
+#define qglPopClientAttrib glPopClientAttrib
+#define qglPopMatrix glPopMatrix
+#define qglPopName glPopName
+#define qglPrioritizeTextures glPrioritizeTextures
+#define qglPushAttrib glPushAttrib
+#define qglPushClientAttrib glPushClientAttrib
+#define qglPushMatrix glPushMatrix
+#define qglPushName glPushName
+#define qglRasterPos2d glRasterPos2d
+#define qglRasterPos2dv glRasterPos2dv
+#define qglRasterPos2f glRasterPos2f
+#define qglRasterPos2fv glRasterPos2fv
+#define qglRasterPos2i glRasterPos2i
+#define qglRasterPos2iv glRasterPos2iv
+#define qglRasterPos2s glRasterPos2s
+#define qglRasterPos2sv glRasterPos2sv
+#define qglRasterPos3d glRasterPos3d
+#define qglRasterPos3dv glRasterPos3dv
+#define qglRasterPos3f glRasterPos3f
+#define qglRasterPos3fv glRasterPos3fv
+#define qglRasterPos3i glRasterPos3i
+#define qglRasterPos3iv glRasterPos3iv
+#define qglRasterPos3s glRasterPos3s
+#define qglRasterPos3sv glRasterPos3sv
+#define qglRasterPos4d glRasterPos4d
+#define qglRasterPos4dv glRasterPos4dv
+#define qglRasterPos4f glRasterPos4f
+#define qglRasterPos4fv glRasterPos4fv
+#define qglRasterPos4i glRasterPos4i
+#define qglRasterPos4iv glRasterPos4iv
+#define qglRasterPos4s glRasterPos4s
+#define qglRasterPos4sv glRasterPos4sv
+#define qglReadBuffer glReadBuffer
+#define qglReadPixels glReadPixels
+#define qglRectd glRectd
+#define qglRectdv glRectdv
+#define qglRectf glRectf
+#define qglRectfv glRectfv
+#define qglRecti glRecti
+#define qglRectiv glRectiv
+#define qglRects glRects
+#define qglRectsv glRectsv
+#define qglRenderMode glRenderMode
+#define qglRotated glRotated
+#define qglRotatef glRotatef
+#define qglScaled glScaled
+#define qglScalef glScalef
+#define qglScissor glScissor
+#define qglSelectBuffer glSelectBuffer
+#define qglShadeModel glShadeModel
+#define qglStencilFunc glStencilFunc
+#define qglStencilMask glStencilMask
+#define qglStencilOp glStencilOp
+#define qglTexCoord1d glTexCoord1d
+#define qglTexCoord1dv glTexCoord1dv
+#define qglTexCoord1f glTexCoord1f
+#define qglTexCoord1fv glTexCoord1fv
+#define qglTexCoord1i glTexCoord1i
+#define qglTexCoord1iv glTexCoord1iv
+#define qglTexCoord1s glTexCoord1s
+#define qglTexCoord1sv glTexCoord1sv
+#define qglTexCoord2d glTexCoord2d
+#define qglTexCoord2dv glTexCoord2dv
+#define qglTexCoord2f glTexCoord2f
+#define qglTexCoord2fv glTexCoord2fv
+#define qglTexCoord2i glTexCoord2i
+#define qglTexCoord2iv glTexCoord2iv
+#define qglTexCoord2s glTexCoord2s
+#define qglTexCoord2sv glTexCoord2sv
+#define qglTexCoord3d glTexCoord3d
+#define qglTexCoord3dv glTexCoord3dv
+#define qglTexCoord3f glTexCoord3f
+#define qglTexCoord3fv glTexCoord3fv
+#define qglTexCoord3i glTexCoord3i
+#define qglTexCoord3iv glTexCoord3iv
+#define qglTexCoord3s glTexCoord3s
+#define qglTexCoord3sv glTexCoord3sv
+#define qglTexCoord4d glTexCoord4d
+#define qglTexCoord4dv glTexCoord4dv
+#define qglTexCoord4f glTexCoord4f
+#define qglTexCoord4fv glTexCoord4fv
+#define qglTexCoord4i glTexCoord4i
+#define qglTexCoord4iv glTexCoord4iv
+#define qglTexCoord4s glTexCoord4s
+#define qglTexCoord4sv glTexCoord4sv
+#define qglTexCoordPointer glTexCoordPointer
+#define qglTexEnvf glTexEnvf
+#define qglTexEnvfv glTexEnvfv
+#define qglTexEnvi glTexEnvi
+#define qglTexEnviv glTexEnviv
+#define qglTexGend glTexGend
+#define qglTexGendv glTexGendv
+#define qglTexGenf glTexGenf
+#define qglTexGenfv glTexGenfv
+#define qglTexGeni glTexGeni
+#define qglTexGeniv glTexGeniv
+#define qglTexImage1D glTexImage1D
+#define qglTexImage2D glTexImage2D
+#define qglTexParameterf glTexParameterf
+#define qglTexParameterfv glTexParameterfv
+#define qglTexParameteri glTexParameteri
+#define qglTexParameteriv glTexParameteriv
+#define qglTexSubImage1D glTexSubImage1D
+#define qglTexSubImage2D glTexSubImage2D
+#define qglTranslated glTranslated
+#define qglTranslatef glTranslatef
+#define qglVertex2d glVertex2d
+#define qglVertex2dv glVertex2dv
+#define qglVertex2f glVertex2f
+#define qglVertex2fv glVertex2fv
+#define qglVertex2i glVertex2i
+#define qglVertex2iv glVertex2iv
+#define qglVertex2s glVertex2s
+#define qglVertex2sv glVertex2sv
+#define qglVertex3d glVertex3d
+#define qglVertex3dv glVertex3dv
+#define qglVertex3f glVertex3f
+#define qglVertex3fv glVertex3fv
+#define qglVertex3i glVertex3i
+#define qglVertex3iv glVertex3iv
+#define qglVertex3s glVertex3s
+#define qglVertex3sv glVertex3sv
+#define qglVertex4d glVertex4d
+#define qglVertex4dv glVertex4dv
+#define qglVertex4f glVertex4f
+#define qglVertex4fv glVertex4fv
+#define qglVertex4i glVertex4i
+#define qglVertex4iv glVertex4iv
+#define qglVertex4s glVertex4s
+#define qglVertex4sv glVertex4sv
+#define qglVertexPointer glVertexPointer
+#define qglViewport glViewport
+
+// GL function loader, based on https://gist.github.com/rygorous/16796a0c876cf8a5f542caddb55bce8a
+
+// OpenGL 1.3, was GL_ARB_texture_compression
+#define QGL_1_3_PROCS \
+ GLE(void, ActiveTexture, GLenum texture) \
+ GLE(void, CompressedTexImage2D, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void *data) \
+ GLE(void, CompressedTexSubImage2D, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const void *data) \
+
+// OpenGL 1.5, was GL_ARB_vertex_buffer_object and GL_ARB_occlusion_query
+#define QGL_1_5_PROCS \
+ GLE(void, GenQueries, GLsizei n, GLuint *ids) \
+ GLE(void, DeleteQueries, GLsizei n, const GLuint *ids) \
+ GLE(void, BeginQuery, GLenum target, GLuint id) \
+ GLE(void, EndQuery, GLenum target) \
+ GLE(void, GetQueryObjectiv, GLuint id, GLenum pname, GLint *params) \
+ GLE(void, GetQueryObjectuiv, GLuint id, GLenum pname, GLuint *params) \
+ GLE(void, BindBuffer, GLenum target, GLuint buffer) \
+ GLE(void, DeleteBuffers, GLsizei n, const GLuint *buffers) \
+ GLE(void, GenBuffers, GLsizei n, GLuint *buffers) \
+ GLE(void, BufferData, GLenum target, GLsizeiptr size, const void *data, GLenum usage) \
+ GLE(void, BufferSubData, GLenum target, GLintptr offset, GLsizeiptr size, const void *data) \
+
+// OpenGL 2.0, was GL_ARB_shading_language_100, GL_ARB_vertex_program, GL_ARB_shader_objects, and GL_ARB_vertex_shader
+#define QGL_2_0_PROCS \
+ GLE(void, AttachShader, GLuint program, GLuint shader) \
+ GLE(void, BindAttribLocation, GLuint program, GLuint index, const GLchar *name) \
+ GLE(void, CompileShader, GLuint shader) \
+ GLE(GLuint, CreateProgram, void) \
+ GLE(GLuint, CreateShader, GLenum type) \
+ GLE(void, DeleteProgram, GLuint program) \
+ GLE(void, DeleteShader, GLuint shader) \
+ GLE(void, DetachShader, GLuint program, GLuint shader) \
+ GLE(void, DisableVertexAttribArray, GLuint index) \
+ GLE(void, EnableVertexAttribArray, GLuint index) \
+ GLE(void, GetActiveUniform, GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) \
+ GLE(void, GetProgramiv, GLuint program, GLenum pname, GLint *params) \
+ GLE(void, GetProgramInfoLog, GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog) \
+ GLE(void, GetShaderiv, GLuint shader, GLenum pname, GLint *params) \
+ GLE(void, GetShaderInfoLog, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog) \
+ GLE(void, GetShaderSource, GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source) \
+ GLE(GLint, GetUniformLocation, GLuint program, const GLchar *name) \
+ GLE(void, LinkProgram, GLuint program) \
+ GLE(void, ShaderSource, GLuint shader, GLsizei count, const GLchar* *string, const GLint *length) \
+ GLE(void, UseProgram, GLuint program) \
+ GLE(void, Uniform1f, GLint location, GLfloat v0) \
+ GLE(void, Uniform2f, GLint location, GLfloat v0, GLfloat v1) \
+ GLE(void, Uniform3f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) \
+ GLE(void, Uniform4f, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) \
+ GLE(void, Uniform1i, GLint location, GLint v0) \
+ GLE(void, Uniform1fv, GLint location, GLsizei count, const GLfloat *value) \
+ GLE(void, UniformMatrix4fv, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) \
+ GLE(void, ValidateProgram, GLuint program) \
+ GLE(void, VertexAttribPointer, GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) \
+
+// GL_NVX_gpu_memory_info
+#ifndef GL_NVX_gpu_memory_info
+#define GL_NVX_gpu_memory_info
+#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047
+#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048
+#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049
+#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A
+#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B
+#endif
+
+// GL_ATI_meminfo
+#ifndef GL_ATI_meminfo
+#define GL_ATI_meminfo
+#define GL_VBO_FREE_MEMORY_ATI 0x87FB
+#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC
+#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD
+#endif
+
+// GL_ARB_texture_float
+#ifndef GL_ARB_texture_float
+#define GL_ARB_texture_float
+#define GL_TEXTURE_RED_TYPE_ARB 0x8C10
+#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11
+#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12
+#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13
+#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14
+#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15
+#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16
+#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17
+#define GL_RGBA32F_ARB 0x8814
+#define GL_RGB32F_ARB 0x8815
+#define GL_ALPHA32F_ARB 0x8816
+#define GL_INTENSITY32F_ARB 0x8817
+#define GL_LUMINANCE32F_ARB 0x8818
+#define GL_LUMINANCE_ALPHA32F_ARB 0x8819
+#define GL_RGBA16F_ARB 0x881A
+#define GL_RGB16F_ARB 0x881B
+#define GL_ALPHA16F_ARB 0x881C
+#define GL_INTENSITY16F_ARB 0x881D
+#define GL_LUMINANCE16F_ARB 0x881E
+#define GL_LUMINANCE_ALPHA16F_ARB 0x881F
+#endif
+
+#ifndef GL_ARB_half_float_pixel
+#define GL_ARB_half_float_pixel
+#define GL_HALF_FLOAT_ARB 0x140B
+#endif
+
+// OpenGL 3.0 specific
+#define QGL_3_0_PROCS \
+ GLE(const GLubyte *, GetStringi, GLenum name, GLuint index) \
+
+// GL_ARB_framebuffer_object, built-in to OpenGL 3.0
+#define QGL_ARB_framebuffer_object_PROCS \
+ GLE(void, BindRenderbuffer, GLenum target, GLuint renderbuffer) \
+ GLE(void, DeleteRenderbuffers, GLsizei n, const GLuint *renderbuffers) \
+ GLE(void, GenRenderbuffers, GLsizei n, GLuint *renderbuffers) \
+ GLE(void, RenderbufferStorage, GLenum target, GLenum internalformat, GLsizei width, GLsizei height) \
+ GLE(void, BindFramebuffer, GLenum target, GLuint framebuffer) \
+ GLE(void, DeleteFramebuffers, GLsizei n, const GLuint *framebuffers) \
+ GLE(void, GenFramebuffers, GLsizei n, GLuint *framebuffers) \
+ GLE(GLenum, CheckFramebufferStatus, GLenum target) \
+ GLE(void, FramebufferTexture2D, GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) \
+ GLE(void, FramebufferRenderbuffer, GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) \
+ GLE(void, GenerateMipmap, GLenum target) \
+ GLE(void, BlitFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter) \
+ GLE(void, RenderbufferStorageMultisample, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) \
+
+// GL_ARB_vertex_array_object, built-in to OpenGL 3.0
+#define QGL_ARB_vertex_array_object_PROCS \
+ GLE(void, BindVertexArray, GLuint array) \
+ GLE(void, DeleteVertexArrays, GLsizei n, const GLuint *arrays) \
+ GLE(void, GenVertexArrays, GLsizei n, GLuint *arrays) \
+
+#ifndef GL_ARB_texture_compression_rgtc
+#define GL_ARB_texture_compression_rgtc
+#define GL_COMPRESSED_RED_RGTC1 0x8DBB
+#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC
+#define GL_COMPRESSED_RG_RGTC2 0x8DBD
+#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE
+#endif
+
+#ifndef GL_ARB_texture_compression_bptc
+#define GL_ARB_texture_compression_bptc
+#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C
+#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D
+#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E
+#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F
+#endif
+
+#ifndef GL_ARB_depth_clamp
+#define GL_ARB_depth_clamp
+#define GL_DEPTH_CLAMP 0x864F
+#endif
+
+#ifndef GL_ARB_seamless_cube_map
+#define GL_ARB_seamless_cube_map
+#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F
+#endif
+
+// GL_EXT_direct_state_access
+#define QGL_EXT_direct_state_access_PROCS \
+ GLE(GLvoid, BindMultiTextureEXT, GLenum texunit, GLenum target, GLuint texture) \
+ GLE(GLvoid, TextureParameterfEXT, GLuint texture, GLenum target, GLenum pname, GLfloat param) \
+ GLE(GLvoid, TextureParameteriEXT, GLuint texture, GLenum target, GLenum pname, GLint param) \
+ GLE(GLvoid, TextureImage2DEXT, GLuint texture, GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels) \
+ GLE(GLvoid, TextureSubImage2DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels) \
+ GLE(GLvoid, CopyTextureSubImage2DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) \
+ GLE(GLvoid, CompressedTextureImage2DEXT, GLuint texture, GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data) \
+ GLE(GLvoid, CompressedTextureSubImage2DEXT, GLuint texture, GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data) \
+ GLE(GLvoid, GenerateTextureMipmapEXT, GLuint texture, GLenum target) \
+ GLE(GLvoid, ProgramUniform1iEXT, GLuint program, GLint location, GLint v0) \
+ GLE(GLvoid, ProgramUniform1fEXT, GLuint program, GLint location, GLfloat v0) \
+ GLE(GLvoid, ProgramUniform2fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1) \
+ GLE(GLvoid, ProgramUniform3fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2) \
+ GLE(GLvoid, ProgramUniform4fEXT, GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) \
+ GLE(GLvoid, ProgramUniform1fvEXT, GLuint program, GLint location, GLsizei count, const GLfloat *value) \
+ GLE(GLvoid, ProgramUniformMatrix4fvEXT, GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value) \
+ GLE(GLvoid, NamedRenderbufferStorageEXT, GLuint renderbuffer, GLenum internalformat, GLsizei width, GLsizei height) \
+ GLE(GLvoid, NamedRenderbufferStorageMultisampleEXT, GLuint renderbuffer, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height) \
+ GLE(GLenum, CheckNamedFramebufferStatusEXT, GLuint framebuffer, GLenum target) \
+ GLE(GLvoid, NamedFramebufferTexture2DEXT, GLuint framebuffer, GLenum attachment, GLenum textarget, GLuint texture, GLint level) \
+ GLE(GLvoid, NamedFramebufferRenderbufferEXT, GLuint framebuffer, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) \
+
+#define GLE(ret, name, ...) typedef ret APIENTRY name##proc(__VA_ARGS__); extern name##proc * qgl##name;
+QGL_1_3_PROCS;
+QGL_1_5_PROCS;
+QGL_2_0_PROCS;
+QGL_3_0_PROCS;
+QGL_ARB_framebuffer_object_PROCS;
+QGL_ARB_vertex_array_object_PROCS;
+QGL_EXT_direct_state_access_PROCS;
+#undef GLE
+
+#endif
diff --git a/src/renderercommon/tr_common.h b/src/renderercommon/tr_common.h
new file mode 100644
index 0000000..68e012f
--- /dev/null
+++ b/src/renderercommon/tr_common.h
@@ -0,0 +1,166 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+#ifndef TR_COMMON_H
+#define TR_COMMON_H
+
+#include "qcommon/cvar.h"
+#include "qcommon/q_shared.h"
+#include "sys/sys_shared.h"
+
+#include "iqm.h"
+#include "qgl.h"
+#include "tr_public.h"
+
+typedef enum
+{
+ IMGTYPE_COLORALPHA, // for color, lightmap, diffuse, and specular
+ IMGTYPE_NORMAL,
+ IMGTYPE_NORMALHEIGHT,
+ IMGTYPE_DELUXE, // normals are swizzled, deluxe are not
+} imgType_t;
+
+typedef enum
+{
+ IMGFLAG_NONE = 0x0000,
+ IMGFLAG_MIPMAP = 0x0001,
+ IMGFLAG_PICMIP = 0x0002,
+ IMGFLAG_CUBEMAP = 0x0004,
+ IMGFLAG_NO_COMPRESSION = 0x0010,
+ IMGFLAG_NOLIGHTSCALE = 0x0020,
+ IMGFLAG_CLAMPTOEDGE = 0x0040,
+ IMGFLAG_GENNORMALMAP = 0x0100,
+} imgFlags_t;
+
+typedef struct image_s {
+ char imgName[MAX_QPATH]; // game path, including extension
+ int width, height; // source image
+ int uploadWidth, uploadHeight; // after power of two and picmip but not including clamp to MAX_TEXTURE_SIZE
+ GLuint texnum; // gl texture binding
+
+ int frameUsed; // for texture usage in frame statistics
+
+ int internalFormat;
+ int TMU; // only needed for voodoo2
+
+ imgType_t type;
+ int /*imgFlags_t*/ flags;
+
+ struct image_s* next;
+} image_t;
+
+// any change in the LIGHTMAP_* defines here MUST be reflected in
+// R_FindShader() in tr_bsp.c
+#define LIGHTMAP_2D -4 // shader is for 2D rendering
+#define LIGHTMAP_BY_VERTEX -3 // pre-lit triangle models
+#define LIGHTMAP_WHITEIMAGE -2
+#define LIGHTMAP_NONE -1
+
+extern refimport_t ri;
+extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared during ref re-init
+
+//
+// cvars
+//
+extern cvar_t *r_stencilbits; // number of desired stencil bits
+extern cvar_t *r_depthbits; // number of desired depth bits
+extern cvar_t *r_colorbits; // number of desired color bits, only relevant for fullscreen
+extern cvar_t *r_alphabits; // number of desired alpha bits
+extern cvar_t *r_texturebits; // number of desired texture bits
+extern cvar_t *r_ext_multisample;
+ // 0 = use framebuffer depth
+ // 16 = use 16-bit textures
+ // 32 = use 32-bit textures
+ // all else = error
+
+extern cvar_t *r_width;
+extern cvar_t *r_height;
+extern cvar_t *r_pixelAspect;
+extern cvar_t *r_noborder;
+extern cvar_t *r_fullscreen;
+extern cvar_t *r_ignorehwgamma; // overrides hardware gamma capabilities
+extern cvar_t *r_drawBuffer;
+extern cvar_t *r_swapInterval;
+
+extern cvar_t *r_allowExtensions; // global enable/disable of OpenGL extensions
+extern cvar_t *r_ext_compressed_textures; // these control use of specific extensions
+extern cvar_t *r_ext_multitexture;
+extern cvar_t *r_ext_compiled_vertex_array;
+extern cvar_t *r_ext_texture_env_add;
+
+extern cvar_t *r_ext_texture_filter_anisotropic;
+extern cvar_t *r_ext_max_anisotropy;
+
+extern cvar_t *r_stereoEnabled;
+
+extern cvar_t *r_saveFontData;
+
+qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode );
+
+float R_NoiseGet4f( float x, float y, float z, double t );
+void R_NoiseInit( void );
+
+image_t *R_FindImageFile( const char *name, imgType_t type, int/*imgFlags_t*/ flags );
+image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, int flags, int internalFormat );
+
+void R_IssuePendingRenderCommands( void );
+qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex );
+qhandle_t RE_RegisterShader( const char *name );
+qhandle_t RE_RegisterShaderNoMip( const char *name );
+qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage);
+
+// font stuff
+void R_InitFreeType( void );
+void R_DoneFreeType( void );
+void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font);
+
+/*
+=============================================================
+
+IMAGE LOADERS
+
+=============================================================
+*/
+
+void R_LoadBMP( const char *name, byte **pic, int *width, int *height );
+void R_LoadJPG( const char *name, byte **pic, int *width, int *height );
+void R_LoadPCX( const char *name, byte **pic, int *width, int *height );
+void R_LoadPNG( const char *name, byte **pic, int *width, int *height );
+void R_LoadTGA( const char *name, byte **pic, int *width, int *height );
+
+/*
+====================================================================
+
+IMPLEMENTATION SPECIFIC FUNCTIONS
+
+====================================================================
+*/
+
+void GLimp_Init( bool );
+void GLimp_Shutdown( void );
+void GLimp_EndFrame( void );
+void GLimp_LogComment(const char *comment);
+void GLimp_Minimize(void);
+void GLimp_SetGamma( unsigned char red[256], unsigned char green[256], unsigned char blue[256] );
+
+#endif
diff --git a/src/renderercommon/tr_font.cpp b/src/renderercommon/tr_font.cpp
new file mode 100644
index 0000000..c93b462
--- /dev/null
+++ b/src/renderercommon/tr_font.cpp
@@ -0,0 +1,562 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+// tr_font.c
+//
+//
+// The font system uses FreeType 2.x to render TrueType fonts for use within the game.
+// As of this writing ( Nov, 2000 ) Team Arena uses these fonts for all of the ui and
+// about 90% of the cgame presentation. A few areas of the CGAME were left uses the old
+// fonts since the code is shared with standard Q3A.
+//
+// If you include this font rendering code in a commercial product you MUST include the
+// following somewhere with your product, see www.freetype.org for specifics or changes.
+// The Freetype code also uses some hinting techniques that MIGHT infringe on patents
+// held by apple so be aware of that also.
+//
+// As of Q3A 1.25+ and Team Arena, we are shipping the game with the font rendering code
+// disabled. This removes any potential patent issues and it keeps us from having to
+// distribute an actual TrueTrype font which is 1. expensive to do and 2. seems to require
+// an act of god to accomplish.
+//
+// What we did was pre-render the fonts using FreeType ( which is why we leave the FreeType
+// credit in the credits ) and then saved off the glyph data and then hand touched up the
+// font bitmaps so they scale a bit better in GL.
+//
+// There are limitations in the way fonts are saved and reloaded in that it is based on
+// point size and not name. So if you pre-render Helvetica in 18 point and Impact in 18 point
+// you will end up with a single 18 point data file and image set. Typically you will want to
+// choose 3 sizes to best approximate the scaling you will be doing in the ui scripting system
+//
+// In the UI Scripting code, a scale of 1.0 is equal to a 48 point font. In Team Arena, we
+// use three or four scales, most of them exactly equaling the specific rendered size. We
+// rendered three sizes in Team Arena, 12, 16, and 20.
+//
+// To generate new font data you need to go through the following steps.
+// 1. delete the fontImage_x_xx.tga files and fontImage_xx.dat files from the fonts path.
+// 2. in a ui script, specificy a font, smallFont, and bigFont keyword with font name and
+// point size. the original TrueType fonts must exist in fonts at this point.
+// 3. run the game, you should see things normally.
+// 4. Exit the game and there will be three dat files and at least three tga files. The
+// tga's are in 256x256 pages so if it takes three images to render a 24 point font you
+// will end up with fontImage_0_24.tga through fontImage_2_24.tga
+// 5. In future runs of the game, the system looks for these images and data files when a s
+// specific point sized font is rendered and loads them for use.
+// 6. Because of the original beta nature of the FreeType code you will probably want to hand
+// touch the font bitmaps.
+//
+// Currently a define in the project turns on or off the FreeType code which is currently
+// defined out. To pre-render new fonts you need enable the define ( BUILD_FREETYPE ) and
+// uncheck the exclude from build check box in the FreeType2 area of the Renderer project.
+
+#include "tr_common.h"
+
+#include "qcommon/qcommon.h"
+
+#ifdef BUILD_FREETYPE
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_ERRORS_H
+#include FT_SYSTEM_H
+#include FT_IMAGE_H
+#include FT_OUTLINE_H
+
+#define _FLOOR(x) ((x) & -64)
+#define _CEIL(x) (((x)+63) & -64)
+#define _TRUNC(x) ((x) >> 6)
+
+FT_Library ftLibrary = NULL;
+#endif
+
+#define MAX_FONTS 6
+static int registeredFontCount = 0;
+static fontInfo_t registeredFont[MAX_FONTS];
+
+#ifdef BUILD_FREETYPE
+void R_GetGlyphInfo(FT_GlyphSlot glyph, int *left, int *right, int *width, int *top, int *bottom, int *height, int *pitch) {
+ *left = _FLOOR( glyph->metrics.horiBearingX );
+ *right = _CEIL( glyph->metrics.horiBearingX + glyph->metrics.width );
+ *width = _TRUNC(*right - *left);
+
+ *top = _CEIL( glyph->metrics.horiBearingY );
+ *bottom = _FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height );
+ *height = _TRUNC( *top - *bottom );
+ *pitch = ( qtrue ? (*width+3) & -4 : (*width+7) >> 3 );
+}
+
+
+FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) {
+ FT_Bitmap *bit2;
+ int left, right, width, top, bottom, height, pitch, size;
+
+ R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch);
+
+ if ( glyph->format == ft_glyph_format_outline ) {
+ size = pitch*height;
+
+ bit2 = ri.Malloc(sizeof(FT_Bitmap));
+
+ bit2->width = width;
+ bit2->rows = height;
+ bit2->pitch = pitch;
+ bit2->pixel_mode = ft_pixel_mode_grays;
+ //bit2->pixel_mode = ft_pixel_mode_mono;
+ bit2->buffer = ri.Malloc(pitch*height);
+ bit2->num_grays = 256;
+
+ Com_Memset( bit2->buffer, 0, size );
+
+ FT_Outline_Translate( &glyph->outline, -left, -bottom );
+
+ FT_Outline_Get_Bitmap( ftLibrary, &glyph->outline, bit2 );
+
+ glyphOut->height = height;
+ glyphOut->pitch = pitch;
+ glyphOut->top = (glyph->metrics.horiBearingY >> 6) + 1;
+ glyphOut->bottom = bottom;
+
+ return bit2;
+ } else {
+ ri.Printf(PRINT_ALL, "Non-outline fonts are not supported\n");
+ }
+ return NULL;
+}
+
+void WriteTGA (char *filename, byte *data, int width, int height) {
+ byte *buffer;
+ int i, c;
+ int row;
+ unsigned char *flip;
+ unsigned char *src, *dst;
+
+ buffer = ri.Malloc(width*height*4 + 18);
+ Com_Memset (buffer, 0, 18);
+ buffer[2] = 2; // uncompressed type
+ buffer[12] = width&255;
+ buffer[13] = width>>8;
+ buffer[14] = height&255;
+ buffer[15] = height>>8;
+ buffer[16] = 32; // pixel size
+
+ // swap rgb to bgr
+ c = 18 + width * height * 4;
+ for (i=18 ; i<c ; i+=4)
+ {
+ buffer[i] = data[i-18+2]; // blue
+ buffer[i+1] = data[i-18+1]; // green
+ buffer[i+2] = data[i-18+0]; // red
+ buffer[i+3] = data[i-18+3]; // alpha
+ }
+
+ // flip upside down
+ flip = (unsigned char *)ri.Malloc(width*4);
+ for(row = 0; row < height/2; row++)
+ {
+ src = buffer + 18 + row * 4 * width;
+ dst = buffer + 18 + (height - row - 1) * 4 * width;
+
+ Com_Memcpy(flip, src, width*4);
+ Com_Memcpy(src, dst, width*4);
+ Com_Memcpy(dst, flip, width*4);
+ }
+ ri.Free(flip);
+
+ ri.FS_WriteFile(filename, buffer, c);
+
+ //f = fopen (filename, "wb");
+ //fwrite (buffer, 1, c, f);
+ //fclose (f);
+
+ ri.Free (buffer);
+}
+
+static glyphInfo_t *RE_ConstructGlyphInfo(unsigned char *imageOut, int *xOut, int *yOut, int *maxHeight, FT_Face face, const unsigned char c, qboolean calcHeight) {
+ int i;
+ static glyphInfo_t glyph;
+ unsigned char *src, *dst;
+ float scaled_width, scaled_height;
+ FT_Bitmap *bitmap = NULL;
+
+ Com_Memset(&glyph, 0, sizeof(glyphInfo_t));
+ // make sure everything is here
+ if (face != NULL) {
+ FT_Load_Glyph(face, FT_Get_Char_Index( face, c), FT_LOAD_DEFAULT );
+ bitmap = R_RenderGlyph(face->glyph, &glyph);
+ if (bitmap) {
+ glyph.xSkip = (face->glyph->metrics.horiAdvance >> 6) + 1;
+ } else {
+ return &glyph;
+ }
+
+ if (glyph.height > *maxHeight) {
+ *maxHeight = glyph.height;
+ }
+
+ if (calcHeight) {
+ ri.Free(bitmap->buffer);
+ ri.Free(bitmap);
+ return &glyph;
+ }
+
+/*
+ // need to convert to power of 2 sizes so we do not get
+ // any scaling from the gl upload
+ for (scaled_width = 1 ; scaled_width < glyph.pitch ; scaled_width<<=1)
+ ;
+ for (scaled_height = 1 ; scaled_height < glyph.height ; scaled_height<<=1)
+ ;
+*/
+
+ scaled_width = glyph.pitch;
+ scaled_height = glyph.height;
+
+ // we need to make sure we fit
+ if (*xOut + scaled_width + 1 >= 255) {
+ *xOut = 0;
+ *yOut += *maxHeight + 1;
+ }
+
+ if (*yOut + *maxHeight + 1 >= 255) {
+ *yOut = -1;
+ *xOut = -1;
+ ri.Free(bitmap->buffer);
+ ri.Free(bitmap);
+ return &glyph;
+ }
+
+
+ src = bitmap->buffer;
+ dst = imageOut + (*yOut * 256) + *xOut;
+
+ if (bitmap->pixel_mode == ft_pixel_mode_mono) {
+ for (i = 0; i < glyph.height; i++) {
+ int j;
+ unsigned char *_src = src;
+ unsigned char *_dst = dst;
+ unsigned char mask = 0x80;
+ unsigned char val = *_src;
+ for (j = 0; j < glyph.pitch; j++) {
+ if (mask == 0x80) {
+ val = *_src++;
+ }
+ if (val & mask) {
+ *_dst = 0xff;
+ }
+ mask >>= 1;
+
+ if ( mask == 0 ) {
+ mask = 0x80;
+ }
+ _dst++;
+ }
+
+ src += glyph.pitch;
+ dst += 256;
+ }
+ } else {
+ for (i = 0; i < glyph.height; i++) {
+ Com_Memcpy(dst, src, glyph.pitch);
+ src += glyph.pitch;
+ dst += 256;
+ }
+ }
+
+ // we now have an 8 bit per pixel grey scale bitmap
+ // that is width wide and pf->ftSize->metrics.y_ppem tall
+
+ glyph.imageHeight = scaled_height;
+ glyph.imageWidth = scaled_width;
+ glyph.s = (float)*xOut / 256;
+ glyph.t = (float)*yOut / 256;
+ glyph.s2 = glyph.s + (float)scaled_width / 256;
+ glyph.t2 = glyph.t + (float)scaled_height / 256;
+
+ *xOut += scaled_width + 1;
+
+ ri.Free(bitmap->buffer);
+ ri.Free(bitmap);
+ }
+
+ return &glyph;
+}
+#endif
+
+static int fdOffset;
+static byte *fdFile;
+
+int readInt( void ) {
+ int i = fdFile[fdOffset]+(fdFile[fdOffset+1]<<8)+(fdFile[fdOffset+2]<<16)+(fdFile[fdOffset+3]<<24);
+ fdOffset += 4;
+ return i;
+}
+
+typedef union {
+ byte fred[4];
+ float ffred;
+} poor;
+
+float readFloat( void ) {
+ poor me;
+#if defined Q3_BIG_ENDIAN
+ me.fred[0] = fdFile[fdOffset+3];
+ me.fred[1] = fdFile[fdOffset+2];
+ me.fred[2] = fdFile[fdOffset+1];
+ me.fred[3] = fdFile[fdOffset+0];
+#elif defined Q3_LITTLE_ENDIAN
+ me.fred[0] = fdFile[fdOffset+0];
+ me.fred[1] = fdFile[fdOffset+1];
+ me.fred[2] = fdFile[fdOffset+2];
+ me.fred[3] = fdFile[fdOffset+3];
+#endif
+ fdOffset += 4;
+ return me.ffred;
+}
+
+void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) {
+#ifdef BUILD_FREETYPE
+ FT_Face face;
+ int j, k, xOut, yOut, lastStart, imageNumber;
+ int scaledSize, newSize, maxHeight, left;
+ unsigned char *out, *imageBuff;
+ glyphInfo_t *glyph;
+ image_t *image;
+ qhandle_t h;
+ float max;
+ float dpi = 72;
+ float glyphScale;
+#endif
+ void *faceData;
+ int i, len;
+ char name[1024];
+
+ if (!fontName) {
+ ri.Printf(PRINT_ALL, "RE_RegisterFont: called with empty name\n");
+ return;
+ }
+
+ if (pointSize <= 0) {
+ pointSize = 12;
+ }
+
+ R_IssuePendingRenderCommands();
+
+ if (registeredFontCount >= MAX_FONTS) {
+ ri.Printf(PRINT_WARNING, "RE_RegisterFont: Too many fonts registered already.\n");
+ return;
+ }
+
+ Com_sprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize);
+ for (i = 0; i < registeredFontCount; i++) {
+ if (Q_stricmp(name, registeredFont[i].name) == 0) {
+ Com_Memcpy(font, &registeredFont[i], sizeof(fontInfo_t));
+ return;
+ }
+ }
+
+ len = ri.FS_ReadFile(name, NULL);
+ if (len == sizeof(fontInfo_t)) {
+ ri.FS_ReadFile(name, &faceData);
+ fdOffset = 0;
+ fdFile = (byte*)faceData;
+ for(i=0; i<GLYPHS_PER_FONT; i++) {
+ font->glyphs[i].height = readInt();
+ font->glyphs[i].top = readInt();
+ font->glyphs[i].bottom = readInt();
+ font->glyphs[i].pitch = readInt();
+ font->glyphs[i].xSkip = readInt();
+ font->glyphs[i].imageWidth = readInt();
+ font->glyphs[i].imageHeight = readInt();
+ font->glyphs[i].s = readFloat();
+ font->glyphs[i].t = readFloat();
+ font->glyphs[i].s2 = readFloat();
+ font->glyphs[i].t2 = readFloat();
+ font->glyphs[i].glyph = readInt();
+ Q_strncpyz(font->glyphs[i].shaderName, (const char *)&fdFile[fdOffset], sizeof(font->glyphs[i].shaderName));
+ fdOffset += sizeof(font->glyphs[i].shaderName);
+ }
+ font->glyphScale = readFloat();
+ Com_Memcpy(font->name, &fdFile[fdOffset], MAX_QPATH);
+
+// Com_Memcpy(font, faceData, sizeof(fontInfo_t));
+ Q_strncpyz(font->name, name, sizeof(font->name));
+ for (i = GLYPH_START; i <= GLYPH_END; i++) {
+ font->glyphs[i].glyph = RE_RegisterShaderNoMip(font->glyphs[i].shaderName);
+ }
+ Com_Memcpy(&registeredFont[registeredFontCount++], font, sizeof(fontInfo_t));
+ ri.FS_FreeFile(faceData);
+ return;
+ }
+
+#ifndef BUILD_FREETYPE
+ ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType code not available\n");
+#else
+ if (ftLibrary == NULL) {
+ ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType not initialized.\n");
+ return;
+ }
+
+ len = ri.FS_ReadFile(fontName, &faceData);
+ if (len <= 0) {
+ ri.Printf(PRINT_WARNING, "RE_RegisterFont: Unable to read font file '%s'\n", fontName);
+ return;
+ }
+
+ // allocate on the stack first in case we fail
+ if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) {
+ ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to allocate new face.\n");
+ return;
+ }
+
+
+ if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) {
+ ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to set face char size.\n");
+ return;
+ }
+
+ //*font = &registeredFonts[registeredFontCount++];
+
+ // make a 256x256 image buffer, once it is full, register it, clean it and keep going
+ // until all glyphs are rendered
+
+ out = ri.Malloc(256*256);
+ if (out == NULL) {
+ ri.Printf(PRINT_WARNING, "RE_RegisterFont: ri.Malloc failure during output image creation.\n");
+ return;
+ }
+ Com_Memset(out, 0, 256*256);
+
+ maxHeight = 0;
+
+ for (i = GLYPH_START; i <= GLYPH_END; i++) {
+ RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qtrue);
+ }
+
+ xOut = 0;
+ yOut = 0;
+ i = GLYPH_START;
+ lastStart = i;
+ imageNumber = 0;
+
+ while ( i <= GLYPH_END + 1 ) {
+
+ if ( i == GLYPH_END + 1 ) {
+ // upload/save current image buffer
+ xOut = yOut = -1;
+ } else {
+ glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qfalse);
+ }
+
+ if (xOut == -1 || yOut == -1) {
+ // ran out of room
+ // we need to create an image from the bitmap, set all the handles in the glyphs to this point
+ //
+
+ scaledSize = 256*256;
+ newSize = scaledSize * 4;
+ imageBuff = ri.Malloc(newSize);
+ left = 0;
+ max = 0;
+ for ( k = 0; k < (scaledSize) ; k++ ) {
+ if (max < out[k]) {
+ max = out[k];
+ }
+ }
+
+ if (max > 0) {
+ max = 255/max;
+ }
+
+ for ( k = 0; k < (scaledSize) ; k++ ) {
+ imageBuff[left++] = 255;
+ imageBuff[left++] = 255;
+ imageBuff[left++] = 255;
+
+ imageBuff[left++] = ((float)out[k] * max);
+ }
+
+ Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i.tga", imageNumber++, pointSize);
+ if (r_saveFontData->integer) {
+ WriteTGA(name, imageBuff, 256, 256);
+ }
+
+ //Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i", imageNumber++, pointSize);
+ image = R_CreateImage(name, imageBuff, 256, 256, IMGTYPE_COLORALPHA, IMGFLAG_CLAMPTOEDGE, 0 );
+ h = RE_RegisterShaderFromImage(name, LIGHTMAP_2D, image, qfalse);
+ for (j = lastStart; j < i; j++) {
+ font->glyphs[j].glyph = h;
+ Q_strncpyz(font->glyphs[j].shaderName, name, sizeof(font->glyphs[j].shaderName));
+ }
+ lastStart = i;
+ Com_Memset(out, 0, 256*256);
+ xOut = 0;
+ yOut = 0;
+ ri.Free(imageBuff);
+ if ( i == GLYPH_END + 1 )
+ i++;
+ } else {
+ Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t));
+ i++;
+ }
+ }
+
+ // change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 )
+ glyphScale = 72.0f / dpi;
+
+ // we also need to adjust the scale based on point size relative to 48 points as the ui scaling is based on a 48 point font
+ glyphScale *= 48.0f / pointSize;
+
+ registeredFont[registeredFontCount].glyphScale = glyphScale;
+ font->glyphScale = glyphScale;
+ Com_Memcpy(&registeredFont[registeredFontCount++], font, sizeof(fontInfo_t));
+
+ if (r_saveFontData->integer) {
+ ri.FS_WriteFile(va("fonts/fontImage_%i.dat", pointSize), font, sizeof(fontInfo_t));
+ }
+
+ ri.Free(out);
+
+ ri.FS_FreeFile(faceData);
+#endif
+}
+
+
+
+void R_InitFreeType(void) {
+#ifdef BUILD_FREETYPE
+ if (FT_Init_FreeType( &ftLibrary )) {
+ ri.Printf(PRINT_WARNING, "R_InitFreeType: Unable to initialize FreeType.\n");
+ }
+#endif
+ registeredFontCount = 0;
+}
+
+
+void R_DoneFreeType(void) {
+#ifdef BUILD_FREETYPE
+ if (ftLibrary) {
+ FT_Done_FreeType( ftLibrary );
+ ftLibrary = NULL;
+ }
+#endif
+ registeredFontCount = 0;
+}
diff --git a/src/renderercommon/tr_image_bmp.cpp b/src/renderercommon/tr_image_bmp.cpp
new file mode 100644
index 0000000..b3e51b7
--- /dev/null
+++ b/src/renderercommon/tr_image_bmp.cpp
@@ -0,0 +1,240 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+
+#include "tr_common.h"
+
+typedef struct
+{
+ char id[2];
+ unsigned fileSize;
+ unsigned reserved0;
+ unsigned bitmapDataOffset;
+ unsigned bitmapHeaderSize;
+ unsigned width;
+ unsigned height;
+ unsigned short planes;
+ unsigned short bitsPerPixel;
+ unsigned compression;
+ unsigned bitmapDataSize;
+ unsigned hRes;
+ unsigned vRes;
+ unsigned colors;
+ unsigned importantColors;
+ unsigned char palette[256][4];
+} BMPHeader_t;
+
+void R_LoadBMP( const char *name, byte **pic, int *width, int *height )
+{
+ int columns, rows;
+ unsigned numPixels;
+ byte *pixbuf;
+ int row, column;
+ byte *buf_p;
+ byte *end;
+ union {
+ byte *b;
+ void *v;
+ } buffer;
+ int length;
+ BMPHeader_t bmpHeader;
+ byte *bmpRGBA;
+
+ *pic = NULL;
+
+ if(width)
+ *width = 0;
+
+ if(height)
+ *height = 0;
+
+ //
+ // load the file
+ //
+ length = ri.FS_ReadFile( ( char * ) name, &buffer.v);
+ if (!buffer.b || length < 0) {
+ return;
+ }
+
+ if (length < 54)
+ {
+ ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name );
+ }
+
+ buf_p = buffer.b;
+ end = buffer.b + length;
+
+ bmpHeader.id[0] = *buf_p++;
+ bmpHeader.id[1] = *buf_p++;
+ bmpHeader.fileSize = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.width = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.height = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.planes = LittleShort( * ( short * ) buf_p );
+ buf_p += 2;
+ bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p );
+ buf_p += 2;
+ bmpHeader.compression = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.hRes = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.vRes = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.colors = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+ bmpHeader.importantColors = LittleLong( * ( int * ) buf_p );
+ buf_p += 4;
+
+ if ( bmpHeader.bitsPerPixel == 8 )
+ {
+ if (buf_p + sizeof(bmpHeader.palette) > end)
+ ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name );
+
+ Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) );
+ }
+
+ if (buffer.b + bmpHeader.bitmapDataOffset > end)
+ {
+ ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name );
+ }
+
+ buf_p = buffer.b + bmpHeader.bitmapDataOffset;
+
+ if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name );
+ }
+ if ( bmpHeader.fileSize != length )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name );
+ }
+ if ( bmpHeader.compression != 0 )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name );
+ }
+ if ( bmpHeader.bitsPerPixel < 8 )
+ {
+ ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", name );
+ }
+
+ switch ( bmpHeader.bitsPerPixel )
+ {
+ case 8:
+ case 16:
+ case 24:
+ case 32:
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'", bmpHeader.bitsPerPixel, name );
+ break;
+ }
+
+ columns = bmpHeader.width;
+ rows = bmpHeader.height;
+ if ( rows < 0 )
+ rows = -rows;
+ numPixels = columns * rows;
+
+ if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF
+ || ((numPixels * 4) / columns) / 4 != rows)
+ {
+ ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size", name);
+ }
+ if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end)
+ {
+ ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", name);
+ }
+
+ if ( width )
+ *width = columns;
+ if ( height )
+ *height = rows;
+
+ bmpRGBA = (byte*)ri.Malloc( numPixels * 4 );
+ *pic = bmpRGBA;
+
+
+ for ( row = rows-1; row >= 0; row-- )
+ {
+ pixbuf = bmpRGBA + row*columns*4;
+
+ for ( column = 0; column < columns; column++ )
+ {
+ unsigned char red, green, blue, alpha;
+ int palIndex;
+ unsigned short shortPixel;
+
+ switch ( bmpHeader.bitsPerPixel )
+ {
+ case 8:
+ palIndex = *buf_p++;
+ *pixbuf++ = bmpHeader.palette[palIndex][2];
+ *pixbuf++ = bmpHeader.palette[palIndex][1];
+ *pixbuf++ = bmpHeader.palette[palIndex][0];
+ *pixbuf++ = 0xff;
+ break;
+ case 16:
+ shortPixel = * ( unsigned short * ) pixbuf;
+ pixbuf += 2;
+ *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7;
+ *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2;
+ *pixbuf++ = ( shortPixel & ( 31 ) ) << 3;
+ *pixbuf++ = 0xff;
+ break;
+
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alpha = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alpha;
+ break;
+ }
+ }
+ }
+
+ ri.FS_FreeFile( buffer.v );
+
+}
diff --git a/src/renderercommon/tr_image_jpg.cpp b/src/renderercommon/tr_image_jpg.cpp
new file mode 100644
index 0000000..9b6c1df
--- /dev/null
+++ b/src/renderercommon/tr_image_jpg.cpp
@@ -0,0 +1,479 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+
+#include <setjmp.h>
+
+#include "tr_common.h"
+
+/*
+ * Include file for users of JPEG library.
+ * You will need to have included system headers that define at least
+ * the typedefs FILE and size_t before you can include jpeglib.h.
+ * (stdio.h is sufficient on ANSI-conforming systems.)
+ * You may also wish to include "jerror.h".
+ */
+
+#ifdef USE_INTERNAL_JPEG
+# define JPEG_INTERNALS
+#endif
+
+#include <jpeglib.h>
+
+#ifndef USE_INTERNAL_JPEG
+# if JPEG_LIB_VERSION < 80 && !defined(MEM_SRCDST_SUPPORTED)
+# error Need system libjpeg >= 80 or jpeg_mem_ support
+# endif
+#endif
+
+/* Catching errors, as done in libjpeg's example.c */
+typedef struct q_jpeg_error_mgr_s
+{
+ struct jpeg_error_mgr pub; /* "public" fields */
+
+ jmp_buf setjmp_buffer; /* for return to caller */
+} q_jpeg_error_mgr_t;
+
+static void R_JPGErrorExit(j_common_ptr cinfo)
+{
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* cinfo->err really points to a q_jpeg_error_mgr_s struct, so coerce pointer */
+ q_jpeg_error_mgr_t *jerr = (q_jpeg_error_mgr_t *)cinfo->err;
+
+ (*cinfo->err->format_message) (cinfo, buffer);
+
+ ri.Printf(PRINT_ALL, "Error: %s", buffer);
+
+ /* Return control to the setjmp point */
+ longjmp(jerr->setjmp_buffer, 1);
+}
+
+static void R_JPGOutputMessage(j_common_ptr cinfo)
+{
+ char buffer[JMSG_LENGTH_MAX];
+
+ /* Create the message */
+ (*cinfo->err->format_message) (cinfo, buffer);
+
+ /* Send it to stderr, adding a newline */
+ ri.Printf(PRINT_ALL, "%s\n", buffer);
+}
+
+void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height)
+{
+ /* This struct contains the JPEG decompression parameters and pointers to
+ * working space (which is allocated as needed by the JPEG library).
+ */
+ struct jpeg_decompress_struct cinfo = {};
+
+ /* We use our private extension JPEG error handler.
+ * Note that this struct must live as long as the main JPEG parameter
+ * struct, to avoid dangling-pointer problems.
+ */
+ /* This struct represents a JPEG error handler. It is declared separately
+ * because applications often want to supply a specialized error handler
+ * (see the second half of this file for an example). But here we just
+ * take the easy way out and use the standard error handler, which will
+ * print a message on stderr and call exit() if compression fails.
+ * Note that this struct must live as long as the main JPEG parameter
+ * struct, to avoid dangling-pointer problems.
+ */
+ q_jpeg_error_mgr_t jerr;
+ /* More stuff */
+ JSAMPARRAY buffer; /* Output row buffer */
+ unsigned int row_stride; /* physical row width in output buffer */
+ unsigned int pixelcount, memcount;
+ unsigned int sindex, dindex;
+ byte *out;
+ int len;
+ union {
+ byte *b;
+ void *v;
+ } fbuffer;
+ byte *buf;
+
+ /* In this example we want to open the input file before doing anything else,
+ * so that the setjmp() error recovery below can assume the file is open.
+ * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
+ * requires it in order to read binary files.
+ */
+
+ len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v);
+ if (!fbuffer.b || len < 0) {
+ return;
+ }
+
+ /* Step 1: allocate and initialize JPEG decompression object */
+
+ /* We have to set up the error handler first, in case the initialization
+ * step fails. (Unlikely, but it could happen if you are out of memory.)
+ * This routine fills in the contents of struct jerr, and returns jerr's
+ * address which we place into the link field in cinfo.
+ */
+ cinfo.err = jpeg_std_error(&jerr.pub);
+ cinfo.err->error_exit = R_JPGErrorExit;
+ cinfo.err->output_message = R_JPGOutputMessage;
+
+ /* Establish the setjmp return context for R_JPGErrorExit to use. */
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object, close the input file, and return.
+ */
+ jpeg_destroy_decompress(&cinfo);
+ ri.FS_FreeFile(fbuffer.v);
+
+ /* Append the filename to the error for easier debugging */
+ ri.Printf(PRINT_ALL, ", loading file %s\n", filename);
+ return;
+ }
+
+ /* Now we can initialize the JPEG decompression object. */
+ jpeg_create_decompress(&cinfo);
+
+ /* Step 2: specify data source (eg, a file) */
+
+ jpeg_mem_src(&cinfo, fbuffer.b, len);
+
+ /* Step 3: read file parameters with jpeg_read_header() */
+
+ (void) jpeg_read_header(&cinfo, TRUE);
+ /* We can ignore the return value from jpeg_read_header since
+ * (a) suspension is not possible with the stdio data source, and
+ * (b) we passed TRUE to reject a tables-only JPEG file as an error.
+ * See libjpeg.doc for more info.
+ */
+
+ /* Step 4: set parameters for decompression */
+
+ /*
+ * Make sure it always converts images to RGB color space. This will
+ * automatically convert 8-bit greyscale images to RGB as well.
+ */
+ cinfo.out_color_space = JCS_RGB;
+
+ /* Step 5: Start decompressor */
+
+ (void) jpeg_start_decompress(&cinfo);
+ /* We can ignore the return value since suspension is not possible
+ * with the stdio data source.
+ */
+
+ /* We may need to do some setup of our own at this point before reading
+ * the data. After jpeg_start_decompress() we have the correct scaled
+ * output image dimensions available, as well as the output colormap
+ * if we asked for color quantization.
+ * In this example, we need to make an output work buffer of the right size.
+ */
+ /* JSAMPLEs per row in output buffer */
+
+ pixelcount = cinfo.output_width * cinfo.output_height;
+
+ if(!cinfo.output_width || !cinfo.output_height
+ || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height
+ || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3
+ )
+ {
+ // Free the memory to make sure we don't leak memory
+ ri.FS_FreeFile (fbuffer.v);
+ jpeg_destroy_decompress(&cinfo);
+
+ ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename,
+ cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components);
+ }
+
+ memcount = pixelcount * 4;
+ row_stride = cinfo.output_width * cinfo.output_components;
+
+ out = (byte*)ri.Malloc(memcount);
+
+ *width = cinfo.output_width;
+ *height = cinfo.output_height;
+
+ /* Step 6: while (scan lines remain to be read) */
+ /* jpeg_read_scanlines(...); */
+
+ /* Here we use the library's state variable cinfo.output_scanline as the
+ * loop counter, so that we don't have to keep track ourselves.
+ */
+ while (cinfo.output_scanline < cinfo.output_height) {
+ /* jpeg_read_scanlines expects an array of pointers to scanlines.
+ * Here the array is only one element long, but you could ask for
+ * more than one scanline at a time if that's more convenient.
+ */
+ buf = ((out+(row_stride*cinfo.output_scanline)));
+ buffer = &buf;
+ (void) jpeg_read_scanlines(&cinfo, buffer, 1);
+ }
+
+ buf = out;
+
+ // Expand from RGB to RGBA
+ sindex = pixelcount * cinfo.output_components;
+ dindex = memcount;
+
+ do
+ {
+ buf[--dindex] = 255;
+ buf[--dindex] = buf[--sindex];
+ buf[--dindex] = buf[--sindex];
+ buf[--dindex] = buf[--sindex];
+ } while(sindex);
+
+ *pic = out;
+
+ /* Step 7: Finish decompression */
+
+ jpeg_finish_decompress(&cinfo);
+ /* We can ignore the return value since suspension is not possible
+ * with the stdio data source.
+ */
+
+ /* Step 8: Release JPEG decompression object */
+
+ /* This is an important step since it will release a good deal of memory. */
+ jpeg_destroy_decompress(&cinfo);
+
+ /* After finish_decompress, we can close the input file.
+ * Here we postpone it until after no more JPEG errors are possible,
+ * so as to simplify the setjmp error logic above. (Actually, I don't
+ * think that jpeg_destroy can do an error exit, but why assume anything...)
+ */
+ ri.FS_FreeFile (fbuffer.v);
+
+ /* At this point you may want to check to see whether any corrupt-data
+ * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
+ */
+
+ /* And we're done! */
+}
+
+
+/* Expanded data destination object for stdio output */
+
+typedef struct {
+ struct jpeg_destination_mgr pub; /* public fields */
+
+ byte* outfile; /* target stream */
+ int size;
+} my_destination_mgr;
+
+typedef my_destination_mgr * my_dest_ptr;
+
+
+/*
+ * Initialize destination --- called by jpeg_start_compress
+ * before any data is actually written.
+ */
+
+static void
+init_destination (j_compress_ptr cinfo)
+{
+ my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+ dest->pub.next_output_byte = dest->outfile;
+ dest->pub.free_in_buffer = dest->size;
+}
+
+
+/*
+ * Empty the output buffer --- called whenever buffer fills up.
+ *
+ * In typical applications, this should write the entire output buffer
+ * (ignoring the current state of next_output_byte & free_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been dumped.
+ *
+ * In applications that need to be able to suspend compression due to output
+ * overrun, a FALSE return indicates that the buffer cannot be emptied now.
+ * In this situation, the compressor will return to its caller (possibly with
+ * an indication that it has not accepted all the supplied scanlines). The
+ * application should resume compression after it has made more room in the
+ * output buffer. Note that there are substantial restrictions on the use of
+ * suspension --- see the documentation.
+ *
+ * When suspending, the compressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_output_byte & free_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point will be regenerated after resumption, so do not
+ * write it out when emptying the buffer externally.
+ */
+
+static boolean
+empty_output_buffer (j_compress_ptr cinfo)
+{
+ my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+ jpeg_destroy_compress(cinfo);
+
+ // Make crash fatal or we would probably leak memory.
+ ri.Error(ERR_FATAL, "Output buffer for encoded JPEG image has insufficient size of %d bytes",
+ dest->size);
+
+ return FALSE;
+}
+
+/*
+ * Terminate destination --- called by jpeg_finish_compress
+ * after all data has been written. Usually needs to flush buffer.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+static void term_destination(j_compress_ptr cinfo)
+{
+}
+
+
+/*
+ * Prepare for output to a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing compression.
+ */
+
+static void
+jpegDest (j_compress_ptr cinfo, byte* outfile, int size)
+{
+ my_dest_ptr dest;
+
+ /* The destination object is made permanent so that multiple JPEG images
+ * can be written to the same file without re-executing jpeg_stdio_dest.
+ * This makes it dangerous to use this manager and a different destination
+ * manager serially with the same JPEG object, because their private object
+ * sizes may be different. Caveat programmer.
+ */
+ if (cinfo->dest == NULL) { /* first time for this JPEG object? */
+ cinfo->dest = (struct jpeg_destination_mgr *)
+ (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+ sizeof(my_destination_mgr));
+ }
+
+ dest = (my_dest_ptr) cinfo->dest;
+ dest->pub.init_destination = init_destination;
+ dest->pub.empty_output_buffer = empty_output_buffer;
+ dest->pub.term_destination = term_destination;
+ dest->outfile = outfile;
+ dest->size = size;
+}
+
+/*
+=================
+SaveJPGToBuffer
+
+Encodes JPEG from image in image_buffer and writes to buffer.
+Expects RGB input data
+=================
+*/
+size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality,
+ int image_width, int image_height, byte *image_buffer, int padding)
+{
+ struct jpeg_compress_struct cinfo;
+ q_jpeg_error_mgr_t jerr;
+ JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
+ my_dest_ptr dest;
+ int row_stride; /* physical row width in image buffer */
+ size_t outcount;
+
+ /* Step 1: allocate and initialize JPEG compression object */
+ cinfo.err = jpeg_std_error(&jerr.pub);
+ cinfo.err->error_exit = R_JPGErrorExit;
+ cinfo.err->output_message = R_JPGOutputMessage;
+
+ /* Establish the setjmp return context for R_JPGErrorExit to use. */
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ /* If we get here, the JPEG code has signaled an error.
+ * We need to clean up the JPEG object and return.
+ */
+ jpeg_destroy_compress(&cinfo);
+
+ ri.Printf(PRINT_ALL, "\n");
+ return 0;
+ }
+
+ /* Now we can initialize the JPEG compression object. */
+ jpeg_create_compress(&cinfo);
+
+ /* Step 2: specify data destination (eg, a file) */
+ /* Note: steps 2 and 3 can be done in either order. */
+ jpegDest(&cinfo, buffer, bufSize);
+
+ /* Step 3: set parameters for compression */
+ cinfo.image_width = image_width; /* image width and height, in pixels */
+ cinfo.image_height = image_height;
+ cinfo.input_components = 3; /* # of color components per pixel */
+ cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
+
+ jpeg_set_defaults(&cinfo);
+ jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
+ /* If quality is set high, disable chroma subsampling */
+ if (quality >= 85) {
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ }
+
+ /* Step 4: Start compressor */
+ jpeg_start_compress(&cinfo, TRUE);
+
+ /* Step 5: while (scan lines remain to be written) */
+ /* jpeg_write_scanlines(...); */
+ row_stride = image_width * cinfo.input_components + padding; /* JSAMPLEs per row in image_buffer */
+
+ while (cinfo.next_scanline < cinfo.image_height) {
+ /* jpeg_write_scanlines expects an array of pointers to scanlines.
+ * Here the array is only one element long, but you could pass
+ * more than one scanline at a time if that's more convenient.
+ */
+ row_pointer[0] = &image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride];
+ (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+
+ /* Step 6: Finish compression */
+ jpeg_finish_compress(&cinfo);
+
+ dest = (my_dest_ptr) cinfo.dest;
+ outcount = dest->size - dest->pub.free_in_buffer;
+
+ /* Step 7: release JPEG compression object */
+ jpeg_destroy_compress(&cinfo);
+
+ /* And we're done! */
+ return outcount;
+}
+
+void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding)
+{
+ byte *out;
+ size_t bufSize;
+
+ bufSize = image_width * image_height * 3;
+ out = (byte*)ri.Hunk_AllocateTempMemory(bufSize);
+
+ bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding);
+ ri.FS_WriteFile(filename, out, bufSize);
+
+ ri.Hunk_FreeTempMemory(out);
+}
diff --git a/src/renderercommon/tr_image_pcx.cpp b/src/renderercommon/tr_image_pcx.cpp
new file mode 100644
index 0000000..9308cfe
--- /dev/null
+++ b/src/renderercommon/tr_image_pcx.cpp
@@ -0,0 +1,177 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+ 2008 Ludwig Nussel
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+
+#include "tr_common.h"
+
+/*
+========================================================================
+
+PCX files are used for 8 bit images
+
+========================================================================
+*/
+
+typedef struct {
+ char manufacturer;
+ char version;
+ char encoding;
+ char bits_per_pixel;
+ unsigned short xmin,ymin,xmax,ymax;
+ unsigned short hres,vres;
+ unsigned char palette[48];
+ char reserved;
+ char color_planes;
+ unsigned short bytes_per_line;
+ unsigned short palette_type;
+ unsigned short hscreensize, vscreensize;
+ char filler[54];
+ unsigned char data[];
+} pcx_t;
+
+void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height)
+{
+ union {
+ byte *b;
+ void *v;
+ } raw;
+ byte *end;
+ pcx_t *pcx;
+ int len;
+ unsigned char dataByte = 0, runLength = 0;
+ byte *out, *pix;
+ unsigned short w, h;
+ byte *pic8;
+ byte *palette;
+ int i;
+ unsigned size = 0;
+
+ if (width)
+ *width = 0;
+ if (height)
+ *height = 0;
+ *pic = NULL;
+
+ //
+ // load the file
+ //
+ len = ri.FS_ReadFile( ( char * ) filename, &raw.v);
+ if (!raw.b || len < 0) {
+ return;
+ }
+
+ if((unsigned)len < sizeof(pcx_t))
+ {
+ ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename);
+ ri.FS_FreeFile (raw.v);
+ return;
+ }
+
+ //
+ // parse the PCX file
+ //
+ pcx = (pcx_t *)raw.b;
+ end = raw.b+len;
+
+ w = LittleShort(pcx->xmax)+1;
+ h = LittleShort(pcx->ymax)+1;
+ size = w*h;
+
+ if (pcx->manufacturer != 0x0a
+ || pcx->version != 5
+ || pcx->encoding != 1
+ || pcx->color_planes != 1
+ || pcx->bits_per_pixel != 8
+ || w >= 1024
+ || h >= 1024)
+ {
+ ri.Printf (PRINT_ALL, "Bad or unsupported pcx file %s (%dx%d@%d)\n", filename, w, h, pcx->bits_per_pixel);
+ return;
+ }
+
+ pix = pic8 = (byte*)ri.Malloc ( size );
+
+ raw.b = pcx->data;
+ // FIXME: should use bytes_per_line but original q3 didn't do that either
+ while(pix < pic8+size)
+ {
+ if(runLength > 0) {
+ *pix++ = dataByte;
+ --runLength;
+ continue;
+ }
+
+ if(raw.b+1 > end)
+ break;
+ dataByte = *raw.b++;
+
+ if((dataByte & 0xC0) == 0xC0)
+ {
+ if(raw.b+1 > end)
+ break;
+ runLength = dataByte & 0x3F;
+ dataByte = *raw.b++;
+ }
+ else
+ runLength = 1;
+ }
+
+ if(pix < pic8+size)
+ {
+ ri.Printf (PRINT_ALL, "PCX file truncated: %s\n", filename);
+ ri.FS_FreeFile (pcx);
+ ri.Free (pic8);
+ }
+
+ if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c)
+ {
+ ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename);
+ ri.FS_FreeFile (pcx);
+ ri.Free (pic8);
+ return;
+ }
+
+ palette = end-768;
+
+ pix = out = (byte*)ri.Malloc(4 * size );
+ for (i = 0 ; i < size ; i++)
+ {
+ unsigned char p = pic8[i];
+ pix[0] = palette[p*3];
+ pix[1] = palette[p*3 + 1];
+ pix[2] = palette[p*3 + 2];
+ pix[3] = 255;
+ pix += 4;
+ }
+
+ if (width)
+ *width = w;
+ if (height)
+ *height = h;
+
+ *pic = out;
+
+ ri.FS_FreeFile (pcx);
+ ri.Free (pic8);
+}
diff --git a/src/renderercommon/tr_image_png.cpp b/src/renderercommon/tr_image_png.cpp
new file mode 100644
index 0000000..6a3f6e8
--- /dev/null
+++ b/src/renderercommon/tr_image_png.cpp
@@ -0,0 +1,2486 @@
+/*
+===========================================================================
+ioquake3 png decoder
+Copyright (C) 2007,2008 Joerg Dietrich
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 3
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <http://www.gnu.org/licenses/>.
+===========================================================================
+*/
+
+#include "tr_common.h"
+
+#include "qcommon/puff.h"
+
+// we could limit the png size to a lower value here
+#ifndef INT_MAX
+#define INT_MAX 0x1fffffff
+#endif
+
+/*
+=================
+PNG LOADING
+=================
+*/
+
+/*
+ * Quake 3 image format : RGBA
+ */
+
+#define Q3IMAGE_BYTESPERPIXEL (4)
+
+/*
+ * PNG specifications
+ */
+
+/*
+ * The first 8 Bytes of every PNG-File are a fixed signature
+ * to identify the file as a PNG.
+ */
+
+#define PNG_Signature "\x89\x50\x4E\x47\xD\xA\x1A\xA"
+#define PNG_Signature_Size (8)
+
+/*
+ * After the signature diverse chunks follow.
+ * A chunk consists of a header and if Length
+ * is bigger than 0 a body and a CRC of the body follow.
+ */
+
+struct PNG_ChunkHeader
+{
+ uint32_t Length;
+ uint32_t Type;
+};
+
+#define PNG_ChunkHeader_Size (8)
+
+typedef uint32_t PNG_ChunkCRC;
+
+#define PNG_ChunkCRC_Size (4)
+
+/*
+ * We use the following ChunkTypes.
+ * All others are ignored.
+ */
+
+#define MAKE_CHUNKTYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d)))
+
+#define PNG_ChunkType_IHDR MAKE_CHUNKTYPE('I', 'H', 'D', 'R')
+#define PNG_ChunkType_PLTE MAKE_CHUNKTYPE('P', 'L', 'T', 'E')
+#define PNG_ChunkType_IDAT MAKE_CHUNKTYPE('I', 'D', 'A', 'T')
+#define PNG_ChunkType_IEND MAKE_CHUNKTYPE('I', 'E', 'N', 'D')
+#define PNG_ChunkType_tRNS MAKE_CHUNKTYPE('t', 'R', 'N', 'S')
+
+/*
+ * Per specification the first chunk after the signature SHALL be IHDR.
+ */
+
+struct PNG_Chunk_IHDR
+{
+ uint32_t Width;
+ uint32_t Height;
+ uint8_t BitDepth;
+ uint8_t ColourType;
+ uint8_t CompressionMethod;
+ uint8_t FilterMethod;
+ uint8_t InterlaceMethod;
+};
+
+#define PNG_Chunk_IHDR_Size (13)
+
+/*
+ * ColourTypes
+ */
+
+#define PNG_ColourType_Grey (0)
+#define PNG_ColourType_True (2)
+#define PNG_ColourType_Indexed (3)
+#define PNG_ColourType_GreyAlpha (4)
+#define PNG_ColourType_TrueAlpha (6)
+
+/*
+ * number of colour components
+ *
+ * Grey : 1 grey
+ * True : 1 R, 1 G, 1 B
+ * Indexed : 1 index
+ * GreyAlpha : 1 grey, 1 alpha
+ * TrueAlpha : 1 R, 1 G, 1 B, 1 alpha
+ */
+
+#define PNG_NumColourComponents_Grey (1)
+#define PNG_NumColourComponents_True (3)
+#define PNG_NumColourComponents_Indexed (1)
+#define PNG_NumColourComponents_GreyAlpha (2)
+#define PNG_NumColourComponents_TrueAlpha (4)
+
+/*
+ * For the different ColourTypes
+ * different BitDepths are specified.
+ */
+
+#define PNG_BitDepth_1 ( 1)
+#define PNG_BitDepth_2 ( 2)
+#define PNG_BitDepth_4 ( 4)
+#define PNG_BitDepth_8 ( 8)
+#define PNG_BitDepth_16 (16)
+
+/*
+ * Only one valid CompressionMethod is standardized.
+ */
+
+#define PNG_CompressionMethod_0 (0)
+
+/*
+ * Only one valid FilterMethod is currently standardized.
+ */
+
+#define PNG_FilterMethod_0 (0)
+
+/*
+ * This FilterMethod defines 5 FilterTypes
+ */
+
+#define PNG_FilterType_None (0)
+#define PNG_FilterType_Sub (1)
+#define PNG_FilterType_Up (2)
+#define PNG_FilterType_Average (3)
+#define PNG_FilterType_Paeth (4)
+
+/*
+ * Two InterlaceMethods are standardized :
+ * 0 - NonInterlaced
+ * 1 - Interlaced
+ */
+
+#define PNG_InterlaceMethod_NonInterlaced (0)
+#define PNG_InterlaceMethod_Interlaced (1)
+
+/*
+ * The Adam7 interlace method uses 7 passes.
+ */
+
+#define PNG_Adam7_NumPasses (7)
+
+/*
+ * The compressed data starts with a header ...
+ */
+
+struct PNG_ZlibHeader
+{
+ uint8_t CompressionMethod;
+ uint8_t Flags;
+};
+
+#define PNG_ZlibHeader_Size (2)
+
+/*
+ * ... and is followed by a check value
+ */
+
+#define PNG_ZlibCheckValue_Size (4)
+
+/*
+ * Some support functions for buffered files follow.
+ */
+
+/*
+ * buffered file representation
+ */
+
+struct BufferedFile
+{
+ byte *Buffer;
+ int Length;
+ byte *Ptr;
+ int BytesLeft;
+};
+
+/*
+ * Read a file into a buffer.
+ */
+
+static struct BufferedFile *ReadBufferedFile(const char *name)
+{
+ struct BufferedFile *BF;
+ union {
+ byte *b;
+ void *v;
+ } buffer;
+
+ /*
+ * input verification
+ */
+
+ if(!name)
+ {
+ return(NULL);
+ }
+
+ /*
+ * Allocate control struct.
+ */
+
+ BF = (struct BufferedFile*)ri.Malloc(sizeof(struct BufferedFile));
+ if(!BF)
+ {
+ return(NULL);
+ }
+
+ /*
+ * Initialize the structs components.
+ */
+
+ BF->Length = 0;
+ BF->Buffer = NULL;
+ BF->Ptr = NULL;
+ BF->BytesLeft = 0;
+
+ /*
+ * Read the file.
+ */
+
+ BF->Length = ri.FS_ReadFile((char *) name, &buffer.v);
+ BF->Buffer = buffer.b;
+
+ /*
+ * Did we get it? Is it big enough?
+ */
+
+ if(!(BF->Buffer && (BF->Length > 0)))
+ {
+ ri.Free(BF);
+
+ return(NULL);
+ }
+
+ /*
+ * Set the pointers and counters.
+ */
+
+ BF->Ptr = BF->Buffer;
+ BF->BytesLeft = BF->Length;
+
+ return(BF);
+}
+
+/*
+ * Close a buffered file.
+ */
+
+static void CloseBufferedFile(struct BufferedFile *BF)
+{
+ if(BF)
+ {
+ if(BF->Buffer)
+ {
+ ri.FS_FreeFile(BF->Buffer);
+ }
+
+ ri.Free(BF);
+ }
+}
+
+/*
+ * Get a pointer to the requested bytes.
+ */
+
+static void *BufferedFileRead(struct BufferedFile *BF, unsigned Length)
+{
+ void *RetVal;
+
+ /*
+ * input verification
+ */
+
+ if(!(BF && Length))
+ {
+ return(NULL);
+ }
+
+ /*
+ * not enough bytes left
+ */
+
+ if(Length > BF->BytesLeft)
+ {
+ return(NULL);
+ }
+
+ /*
+ * the pointer to the requested data
+ */
+
+ RetVal = BF->Ptr;
+
+ /*
+ * Raise the pointer and counter.
+ */
+
+ BF->Ptr += Length;
+ BF->BytesLeft -= Length;
+
+ return(RetVal);
+}
+
+/*
+ * Rewind the buffer.
+ */
+
+static qboolean BufferedFileRewind(struct BufferedFile *BF, unsigned Offset)
+{
+ unsigned BytesRead;
+
+ /*
+ * input verification
+ */
+
+ if(!BF)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * special trick to rewind to the beginning of the buffer
+ */
+
+ if(Offset == (unsigned)-1)
+ {
+ BF->Ptr = BF->Buffer;
+ BF->BytesLeft = BF->Length;
+
+ return(qtrue);
+ }
+
+ /*
+ * How many bytes do we have already read?
+ */
+
+ BytesRead = BF->Ptr - BF->Buffer;
+
+ /*
+ * We can only rewind to the beginning of the BufferedFile.
+ */
+
+ if(Offset > BytesRead)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * lower the pointer and counter.
+ */
+
+ BF->Ptr -= Offset;
+ BF->BytesLeft += Offset;
+
+ return(qtrue);
+}
+
+/*
+ * Skip some bytes.
+ */
+
+static qboolean BufferedFileSkip(struct BufferedFile *BF, unsigned Offset)
+{
+ /*
+ * input verification
+ */
+
+ if(!BF)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * We can only skip to the end of the BufferedFile.
+ */
+
+ if(Offset > BF->BytesLeft)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * lower the pointer and counter.
+ */
+
+ BF->Ptr += Offset;
+ BF->BytesLeft -= Offset;
+
+ return(qtrue);
+}
+
+/*
+ * Find a chunk
+ */
+
+static qboolean FindChunk(struct BufferedFile *BF, uint32_t ChunkType)
+{
+ struct PNG_ChunkHeader *CH;
+
+ uint32_t Length;
+ uint32_t Type;
+
+ /*
+ * input verification
+ */
+
+ if(!BF)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * cycle trough the chunks
+ */
+
+ while(qtrue)
+ {
+ /*
+ * Read the chunk-header.
+ */
+
+ CH = (struct PNG_ChunkHeader*)BufferedFileRead(BF, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Do not swap the original types
+ * they might be needed later.
+ */
+
+ Length = BigLong(CH->Length);
+ Type = BigLong(CH->Type);
+
+ /*
+ * We found it!
+ */
+
+ if(Type == ChunkType)
+ {
+ /*
+ * Rewind to the start of the chunk.
+ */
+
+ BufferedFileRewind(BF, PNG_ChunkHeader_Size);
+
+ break;
+ }
+ else
+ {
+ /*
+ * Skip the rest of the chunk.
+ */
+
+ if(Length)
+ {
+ if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size))
+ {
+ return(qfalse);
+ }
+ }
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * Decompress all IDATs
+ */
+
+static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer)
+{
+ uint8_t *DecompressedData;
+ uint32_t DecompressedDataLength;
+
+ uint8_t *CompressedData;
+ uint8_t *CompressedDataPtr;
+ uint32_t CompressedDataLength;
+
+ struct PNG_ChunkHeader *CH;
+
+ uint32_t Length;
+ uint32_t Type;
+
+ int BytesToRewind;
+
+ int32_t puffResult;
+ uint8_t *puffDest;
+ uint32_t puffDestLen;
+ uint8_t *puffSrc;
+ uint32_t puffSrcLen;
+
+ /*
+ * input verification
+ */
+
+ if(!(BF && Buffer))
+ {
+ return(-1);
+ }
+
+ /*
+ * some zeroing
+ */
+
+ DecompressedData = NULL;
+ *Buffer = DecompressedData;
+
+ CompressedData = NULL;
+ CompressedDataLength = 0;
+
+ BytesToRewind = 0;
+
+ /*
+ * Find the first IDAT chunk.
+ */
+
+ if(!FindChunk(BF, PNG_ChunkType_IDAT))
+ {
+ return(-1);
+ }
+
+ /*
+ * Count the size of the uncompressed data
+ */
+
+ while(qtrue)
+ {
+ /*
+ * Read chunk header
+ */
+
+ CH = (struct PNG_ChunkHeader*)BufferedFileRead(BF, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ /*
+ * Rewind to the start of this adventure
+ * and return unsuccessfull
+ */
+
+ BufferedFileRewind(BF, BytesToRewind);
+
+ return(-1);
+ }
+
+ /*
+ * Length and Type of chunk
+ */
+
+ Length = BigLong(CH->Length);
+ Type = BigLong(CH->Type);
+
+ /*
+ * We have reached the end of the IDAT chunks
+ */
+
+ if(!(Type == PNG_ChunkType_IDAT))
+ {
+ BufferedFileRewind(BF, PNG_ChunkHeader_Size);
+
+ break;
+ }
+
+ /*
+ * Add chunk header to count.
+ */
+
+ BytesToRewind += PNG_ChunkHeader_Size;
+
+ /*
+ * Skip to next chunk
+ */
+
+ if(Length)
+ {
+ if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size))
+ {
+ BufferedFileRewind(BF, BytesToRewind);
+
+ return(-1);
+ }
+
+ BytesToRewind += Length + PNG_ChunkCRC_Size;
+ CompressedDataLength += Length;
+ }
+ }
+
+ BufferedFileRewind(BF, BytesToRewind);
+
+ CompressedData = (uint8_t*)ri.Malloc(CompressedDataLength);
+ if(!CompressedData)
+ {
+ return(-1);
+ }
+
+ CompressedDataPtr = CompressedData;
+
+ /*
+ * Collect the compressed Data
+ */
+
+ while(qtrue)
+ {
+ /*
+ * Read chunk header
+ */
+
+ CH = (struct PNG_ChunkHeader*)BufferedFileRead(BF, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Length and Type of chunk
+ */
+
+ Length = BigLong(CH->Length);
+ Type = BigLong(CH->Type);
+
+ /*
+ * We have reached the end of the IDAT chunks
+ */
+
+ if(!(Type == PNG_ChunkType_IDAT))
+ {
+ BufferedFileRewind(BF, PNG_ChunkHeader_Size);
+
+ break;
+ }
+
+ /*
+ * Copy the Data
+ */
+
+ if(Length)
+ {
+ uint8_t *OrigCompressedData;
+
+ OrigCompressedData = (uint8_t*)BufferedFileRead(BF, Length);
+ if(!OrigCompressedData)
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ if(!BufferedFileSkip(BF, PNG_ChunkCRC_Size))
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ memcpy(CompressedDataPtr, OrigCompressedData, Length);
+ CompressedDataPtr += Length;
+ }
+ }
+
+ /*
+ * Let puff() calculate the decompressed data length.
+ */
+
+ puffDest = NULL;
+ puffDestLen = 0;
+
+ /*
+ * The zlib header and checkvalue don't belong to the compressed data.
+ */
+
+ puffSrc = CompressedData + PNG_ZlibHeader_Size;
+ puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size;
+
+ /*
+ * first puff() to calculate the size of the uncompressed data
+ */
+
+ puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen);
+ if(!((puffResult == 0) && (puffDestLen > 0)))
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Allocate the buffer for the uncompressed data.
+ */
+
+ DecompressedData = (uint8_t*)ri.Malloc(puffDestLen);
+ if(!DecompressedData)
+ {
+ ri.Free(CompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Set the input again in case something was changed by the last puff() .
+ */
+
+ puffDest = DecompressedData;
+ puffSrc = CompressedData + PNG_ZlibHeader_Size;
+ puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size;
+
+ /*
+ * decompression puff()
+ */
+
+ puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen);
+
+ /*
+ * The compressed data is not needed anymore.
+ */
+
+ ri.Free(CompressedData);
+
+ /*
+ * Check if the last puff() was successfull.
+ */
+
+ if(!((puffResult == 0) && (puffDestLen > 0)))
+ {
+ ri.Free(DecompressedData);
+
+ return(-1);
+ }
+
+ /*
+ * Set the output of this function.
+ */
+
+ DecompressedDataLength = puffDestLen;
+ *Buffer = DecompressedData;
+
+ return(DecompressedDataLength);
+}
+
+/*
+ * the Paeth predictor
+ */
+
+static uint8_t PredictPaeth(uint8_t a, uint8_t b, uint8_t c)
+{
+ /*
+ * a == Left
+ * b == Up
+ * c == UpLeft
+ */
+
+ uint8_t Pr;
+ int p;
+ int pa, pb, pc;
+
+ p = ((int) a) + ((int) b) - ((int) c);
+ pa = abs(p - ((int) a));
+ pb = abs(p - ((int) b));
+ pc = abs(p - ((int) c));
+
+ if((pa <= pb) && (pa <= pc))
+ {
+ Pr = a;
+ }
+ else if(pb <= pc)
+ {
+ Pr = b;
+ }
+ else
+ {
+ Pr = c;
+ }
+
+ return(Pr);
+
+}
+
+/*
+ * Reverse the filters.
+ */
+
+static qboolean UnfilterImage(uint8_t *DecompressedData,
+ uint32_t ImageHeight,
+ uint32_t BytesPerScanline,
+ uint32_t BytesPerPixel)
+{
+ uint8_t *DecompPtr;
+ uint8_t FilterType;
+ uint8_t *PixelLeft, *PixelUp, *PixelUpLeft;
+ uint32_t w, h, p;
+
+ /*
+ * some zeros for the filters
+ */
+
+ uint8_t Zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
+
+ /*
+ * input verification
+ */
+
+ if(!(DecompressedData && BytesPerPixel))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * ImageHeight and BytesPerScanline can be zero in small interlaced images.
+ */
+
+ if((!ImageHeight) || (!BytesPerScanline))
+ {
+ return(qtrue);
+ }
+
+ /*
+ * Set the pointer to the start of the decompressed Data.
+ */
+
+ DecompPtr = DecompressedData;
+
+ /*
+ * Un-filtering is done in place.
+ */
+
+ /*
+ * Go trough all scanlines.
+ */
+
+ for(h = 0; h < ImageHeight; h++)
+ {
+ /*
+ * Every scanline starts with a FilterType byte.
+ */
+
+ FilterType = *DecompPtr;
+ DecompPtr++;
+
+ /*
+ * Left pixel of the first byte in a scanline is zero.
+ */
+
+ PixelLeft = Zeros;
+
+ /*
+ * Set PixelUp to previous line only if we are on the second line or above.
+ *
+ * Plus one byte for the FilterType
+ */
+
+ if(h > 0)
+ {
+ PixelUp = DecompPtr - (BytesPerScanline + 1);
+ }
+ else
+ {
+ PixelUp = Zeros;
+ }
+
+ /*
+ * The pixel left to the first pixel of the previous scanline is zero too.
+ */
+
+ PixelUpLeft = Zeros;
+
+ /*
+ * Cycle trough all pixels of the scanline.
+ */
+
+ for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++)
+ {
+ /*
+ * Cycle trough the bytes of the pixel.
+ */
+
+ for(p = 0; p < BytesPerPixel; p++)
+ {
+ switch(FilterType)
+ {
+ case PNG_FilterType_None :
+ {
+ /*
+ * The byte is unfiltered.
+ */
+
+ break;
+ }
+
+ case PNG_FilterType_Sub :
+ {
+ DecompPtr[p] += PixelLeft[p];
+
+ break;
+ }
+
+ case PNG_FilterType_Up :
+ {
+ DecompPtr[p] += PixelUp[p];
+
+ break;
+ }
+
+ case PNG_FilterType_Average :
+ {
+ DecompPtr[p] += ((uint8_t) ((((uint16_t) PixelLeft[p]) + ((uint16_t) PixelUp[p])) / 2));
+
+ break;
+ }
+
+ case PNG_FilterType_Paeth :
+ {
+ DecompPtr[p] += PredictPaeth(PixelLeft[p], PixelUp[p], PixelUpLeft[p]);
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+ }
+
+ PixelLeft = DecompPtr;
+
+ /*
+ * We only have an upleft pixel if we are on the second line or above.
+ */
+
+ if(h > 0)
+ {
+ PixelUpLeft = DecompPtr - (BytesPerScanline + 1);
+ }
+
+ /*
+ * Skip to the next pixel.
+ */
+
+ DecompPtr += BytesPerPixel;
+
+ /*
+ * We only have a previous line if we are on the second line and above.
+ */
+
+ if(h > 0)
+ {
+ PixelUp = DecompPtr - (BytesPerScanline + 1);
+ }
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * Convert a raw input pixel to Quake 3 RGA format.
+ */
+
+static qboolean ConvertPixel(struct PNG_Chunk_IHDR *IHDR,
+ byte *OutPtr,
+ uint8_t *DecompPtr,
+ qboolean HasTransparentColour,
+ uint8_t *TransparentColour,
+ uint8_t *OutPal)
+{
+ /*
+ * input verification
+ */
+
+ if(!(IHDR && OutPtr && DecompPtr && TransparentColour && OutPal))
+ {
+ return(qfalse);
+ }
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ uint8_t Step;
+ uint8_t GreyValue;
+
+ Step = 0xFF / ((1 << IHDR->BitDepth) - 1);
+
+ GreyValue = DecompPtr[0] * Step;
+
+ OutPtr[0] = GreyValue;
+ OutPtr[1] = GreyValue;
+ OutPtr[2] = GreyValue;
+ OutPtr[3] = 0xFF;
+
+ /*
+ * Grey supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if(TransparentColour[1] == DecompPtr[0])
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[0];
+ OutPtr[2] = DecompPtr[0];
+ OutPtr[3] = 0xFF;
+
+ /*
+ * Grey supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if(IHDR->BitDepth == PNG_BitDepth_8)
+ {
+ if(TransparentColour[1] == DecompPtr[0])
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+ else
+ {
+ if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]))
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[1];
+ OutPtr[2] = DecompPtr[2];
+ OutPtr[3] = 0xFF;
+
+ /*
+ * True supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if((TransparentColour[1] == DecompPtr[0]) &&
+ (TransparentColour[3] == DecompPtr[1]) &&
+ (TransparentColour[5] == DecompPtr[2]))
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+
+ break;
+ }
+
+ case PNG_BitDepth_16 :
+ {
+ /*
+ * We use only the upper byte.
+ */
+
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[2];
+ OutPtr[2] = DecompPtr[4];
+ OutPtr[3] = 0xFF;
+
+ /*
+ * True supports full transparency for one specified colour
+ */
+
+ if(HasTransparentColour)
+ {
+ if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]) &&
+ (TransparentColour[2] == DecompPtr[2]) && (TransparentColour[3] == DecompPtr[3]) &&
+ (TransparentColour[4] == DecompPtr[4]) && (TransparentColour[5] == DecompPtr[5]))
+ {
+ OutPtr[3] = 0x00;
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ OutPtr[0] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 0];
+ OutPtr[1] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 1];
+ OutPtr[2] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 2];
+ OutPtr[3] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 3];
+
+ break;
+ }
+
+ case PNG_ColourType_GreyAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[0];
+ OutPtr[2] = DecompPtr[0];
+ OutPtr[3] = DecompPtr[1];
+
+ break;
+ }
+
+ case PNG_BitDepth_16 :
+ {
+ /*
+ * We use only the upper byte.
+ */
+
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[0];
+ OutPtr[2] = DecompPtr[0];
+ OutPtr[3] = DecompPtr[2];
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_TrueAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ {
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[1];
+ OutPtr[2] = DecompPtr[2];
+ OutPtr[3] = DecompPtr[3];
+
+ break;
+ }
+
+ case PNG_BitDepth_16 :
+ {
+ /*
+ * We use only the upper byte.
+ */
+
+ OutPtr[0] = DecompPtr[0];
+ OutPtr[1] = DecompPtr[2];
+ OutPtr[2] = DecompPtr[4];
+ OutPtr[3] = DecompPtr[6];
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ return(qtrue);
+}
+
+
+/*
+ * Decode a non-interlaced image.
+ */
+
+static qboolean DecodeImageNonInterlaced(struct PNG_Chunk_IHDR *IHDR,
+ byte *OutBuffer,
+ uint8_t *DecompressedData,
+ uint32_t DecompressedDataLength,
+ qboolean HasTransparentColour,
+ uint8_t *TransparentColour,
+ uint8_t *OutPal)
+{
+ uint32_t IHDR_Width;
+ uint32_t IHDR_Height;
+ uint32_t BytesPerScanline, BytesPerPixel, PixelsPerByte;
+ uint32_t w, h, p;
+ byte *OutPtr;
+ uint8_t *DecompPtr;
+
+ /*
+ * input verification
+ */
+
+ if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * byte swapping
+ */
+
+ IHDR_Width = BigLong(IHDR->Width);
+ IHDR_Height = BigLong(IHDR->Height);
+
+ /*
+ * information for un-filtering
+ */
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ {
+ BytesPerPixel = PNG_NumColourComponents_Indexed;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_GreyAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_TrueAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ /*
+ * Calculate the size of one scanline
+ */
+
+ BytesPerScanline = (IHDR_Width * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte;
+
+ /*
+ * Check if we have enough data for the whole image.
+ */
+
+ if(!(DecompressedDataLength == ((BytesPerScanline + 1) * IHDR_Height)))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Unfilter the image.
+ */
+
+ if(!UnfilterImage(DecompressedData, IHDR_Height, BytesPerScanline, BytesPerPixel))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Set the working pointers to the beginning of the buffers.
+ */
+
+ OutPtr = OutBuffer;
+ DecompPtr = DecompressedData;
+
+ /*
+ * Create the output image.
+ */
+
+ for(h = 0; h < IHDR_Height; h++)
+ {
+ /*
+ * Count the pixels on the scanline for those multipixel bytes
+ */
+
+ uint32_t CurrPixel;
+
+ /*
+ * skip FilterType
+ */
+
+ DecompPtr++;
+
+ /*
+ * Reset the pixel count.
+ */
+
+ CurrPixel = 0;
+
+ for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++)
+ {
+ if(PixelsPerByte > 1)
+ {
+ uint8_t Mask;
+ uint32_t Shift;
+ uint8_t SinglePixel;
+
+ for(p = 0; p < PixelsPerByte; p++)
+ {
+ if(CurrPixel < IHDR_Width)
+ {
+ Mask = (1 << IHDR->BitDepth) - 1;
+ Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth;
+
+ SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift);
+
+ if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+
+ OutPtr += Q3IMAGE_BYTESPERPIXEL;
+ CurrPixel++;
+ }
+ }
+
+ }
+ else
+ {
+ if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+
+
+ OutPtr += Q3IMAGE_BYTESPERPIXEL;
+ }
+
+ DecompPtr += BytesPerPixel;
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * Decode an interlaced image.
+ */
+
+static qboolean DecodeImageInterlaced(struct PNG_Chunk_IHDR *IHDR,
+ byte *OutBuffer,
+ uint8_t *DecompressedData,
+ uint32_t DecompressedDataLength,
+ qboolean HasTransparentColour,
+ uint8_t *TransparentColour,
+ uint8_t *OutPal)
+{
+ uint32_t IHDR_Width;
+ uint32_t IHDR_Height;
+ uint32_t BytesPerScanline[PNG_Adam7_NumPasses], BytesPerPixel, PixelsPerByte;
+ uint32_t PassWidth[PNG_Adam7_NumPasses], PassHeight[PNG_Adam7_NumPasses];
+ uint32_t WSkip[PNG_Adam7_NumPasses], WOffset[PNG_Adam7_NumPasses], HSkip[PNG_Adam7_NumPasses], HOffset[PNG_Adam7_NumPasses];
+ uint32_t w, h, p, a;
+ byte *OutPtr;
+ uint8_t *DecompPtr;
+ uint32_t TargetLength;
+
+ /*
+ * input verification
+ */
+
+ if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * byte swapping
+ */
+
+ IHDR_Width = BigLong(IHDR->Width);
+ IHDR_Height = BigLong(IHDR->Height);
+
+ /*
+ * Skip and Offset for the passes.
+ */
+
+ WSkip[0] = 8;
+ WOffset[0] = 0;
+ HSkip[0] = 8;
+ HOffset[0] = 0;
+
+ WSkip[1] = 8;
+ WOffset[1] = 4;
+ HSkip[1] = 8;
+ HOffset[1] = 0;
+
+ WSkip[2] = 4;
+ WOffset[2] = 0;
+ HSkip[2] = 8;
+ HOffset[2] = 4;
+
+ WSkip[3] = 4;
+ WOffset[3] = 2;
+ HSkip[3] = 4;
+ HOffset[3] = 0;
+
+ WSkip[4] = 2;
+ WOffset[4] = 0;
+ HSkip[4] = 4;
+ HOffset[4] = 2;
+
+ WSkip[5] = 2;
+ WOffset[5] = 1;
+ HSkip[5] = 2;
+ HOffset[5] = 0;
+
+ WSkip[6] = 1;
+ WOffset[6] = 0;
+ HSkip[6] = 2;
+ HOffset[6] = 1;
+
+ /*
+ * Calculate the sizes of the passes.
+ */
+
+ PassWidth[0] = (IHDR_Width + 7) / 8;
+ PassHeight[0] = (IHDR_Height + 7) / 8;
+
+ PassWidth[1] = (IHDR_Width + 3) / 8;
+ PassHeight[1] = (IHDR_Height + 7) / 8;
+
+ PassWidth[2] = (IHDR_Width + 3) / 4;
+ PassHeight[2] = (IHDR_Height + 3) / 8;
+
+ PassWidth[3] = (IHDR_Width + 1) / 4;
+ PassHeight[3] = (IHDR_Height + 3) / 4;
+
+ PassWidth[4] = (IHDR_Width + 1) / 2;
+ PassHeight[4] = (IHDR_Height + 1) / 4;
+
+ PassWidth[5] = (IHDR_Width + 0) / 2;
+ PassHeight[5] = (IHDR_Height + 1) / 2;
+
+ PassWidth[6] = (IHDR_Width + 0) / 1;
+ PassHeight[6] = (IHDR_Height + 0) / 2;
+
+ /*
+ * information for un-filtering
+ */
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_1 :
+ case PNG_BitDepth_2 :
+ case PNG_BitDepth_4 :
+ {
+ BytesPerPixel = 1;
+ PixelsPerByte = 8 / IHDR->BitDepth;
+
+ break;
+ }
+
+ case PNG_BitDepth_8 :
+ {
+ BytesPerPixel = PNG_NumColourComponents_Indexed;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_GreyAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ case PNG_ColourType_TrueAlpha :
+ {
+ switch(IHDR->BitDepth)
+ {
+ case PNG_BitDepth_8 :
+ case PNG_BitDepth_16 :
+ {
+ BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha;
+ PixelsPerByte = 1;
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ break;
+ }
+
+ default :
+ {
+ return(qfalse);
+ }
+ }
+
+ /*
+ * Calculate the size of the scanlines per pass
+ */
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ BytesPerScanline[a] = (PassWidth[a] * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte;
+ }
+
+ /*
+ * Calculate the size of all passes
+ */
+
+ TargetLength = 0;
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ TargetLength += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]);
+ }
+
+ /*
+ * Check if we have enough data for the whole image.
+ */
+
+ if(!(DecompressedDataLength == TargetLength))
+ {
+ return(qfalse);
+ }
+
+ /*
+ * Unfilter the image.
+ */
+
+ DecompPtr = DecompressedData;
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ if(!UnfilterImage(DecompPtr, PassHeight[a], BytesPerScanline[a], BytesPerPixel))
+ {
+ return(qfalse);
+ }
+
+ DecompPtr += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]);
+ }
+
+ /*
+ * Set the working pointers to the beginning of the buffers.
+ */
+
+ DecompPtr = DecompressedData;
+
+ /*
+ * Create the output image.
+ */
+
+ for(a = 0; a < PNG_Adam7_NumPasses; a++)
+ {
+ for(h = 0; h < PassHeight[a]; h++)
+ {
+ /*
+ * Count the pixels on the scanline for those multipixel bytes
+ */
+
+ uint32_t CurrPixel;
+
+ /*
+ * skip FilterType
+ * but only when the pass has a width bigger than zero
+ */
+
+ if(BytesPerScanline[a])
+ {
+ DecompPtr++;
+ }
+
+ /*
+ * Reset the pixel count.
+ */
+
+ CurrPixel = 0;
+
+ for(w = 0; w < (BytesPerScanline[a] / BytesPerPixel); w++)
+ {
+ if(PixelsPerByte > 1)
+ {
+ uint8_t Mask;
+ uint32_t Shift;
+ uint8_t SinglePixel;
+
+ for(p = 0; p < PixelsPerByte; p++)
+ {
+ if(CurrPixel < PassWidth[a])
+ {
+ Mask = (1 << IHDR->BitDepth) - 1;
+ Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth;
+
+ SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift);
+
+ OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((CurrPixel * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL);
+
+ if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+
+ CurrPixel++;
+ }
+ }
+
+ }
+ else
+ {
+ OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((w * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL);
+
+ if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal))
+ {
+ return(qfalse);
+ }
+ }
+
+ DecompPtr += BytesPerPixel;
+ }
+ }
+ }
+
+ return(qtrue);
+}
+
+/*
+ * The PNG loader
+ */
+
+void R_LoadPNG(const char *name, byte **pic, int *width, int *height)
+{
+ struct BufferedFile *ThePNG;
+ byte *OutBuffer;
+ uint8_t *Signature;
+ struct PNG_ChunkHeader *CH;
+ uint32_t ChunkHeaderLength;
+ uint32_t ChunkHeaderType;
+ struct PNG_Chunk_IHDR *IHDR;
+ uint32_t IHDR_Width;
+ uint32_t IHDR_Height;
+ PNG_ChunkCRC *CRC;
+ uint8_t *InPal;
+ uint8_t *DecompressedData;
+ uint32_t DecompressedDataLength;
+ uint32_t i;
+
+ /*
+ * palette with 256 RGBA entries
+ */
+
+ uint8_t OutPal[1024];
+
+ /*
+ * transparent colour from the tRNS chunk
+ */
+
+ qboolean HasTransparentColour = qfalse;
+ uint8_t TransparentColour[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+ /*
+ * input verification
+ */
+
+ if(!(name && pic))
+ {
+ return;
+ }
+
+ /*
+ * Zero out return values.
+ */
+
+ *pic = NULL;
+
+ if(width)
+ {
+ *width = 0;
+ }
+
+ if(height)
+ {
+ *height = 0;
+ }
+
+ /*
+ * Read the file.
+ */
+
+ ThePNG = ReadBufferedFile(name);
+ if(!ThePNG)
+ {
+ return;
+ }
+
+ /*
+ * Read the siganture of the file.
+ */
+
+ Signature = (uint8_t*)BufferedFileRead(ThePNG, PNG_Signature_Size);
+ if(!Signature)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Is it a PNG?
+ */
+
+ if(memcmp(Signature, PNG_Signature, PNG_Signature_Size))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the first chunk-header.
+ */
+
+ CH = (struct PNG_ChunkHeader*)BufferedFileRead(ThePNG, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * PNG multi-byte types are in Big Endian
+ */
+
+ ChunkHeaderLength = BigLong(CH->Length);
+ ChunkHeaderType = BigLong(CH->Type);
+
+ /*
+ * Check if the first chunk is an IHDR.
+ */
+
+ if(!((ChunkHeaderType == PNG_ChunkType_IHDR) && (ChunkHeaderLength == PNG_Chunk_IHDR_Size)))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the IHDR.
+ */
+
+ IHDR = (struct PNG_Chunk_IHDR*)BufferedFileRead(ThePNG, PNG_Chunk_IHDR_Size);
+ if(!IHDR)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the CRC for IHDR
+ */
+
+ CRC = (PNG_ChunkCRC*)BufferedFileRead(ThePNG, PNG_ChunkCRC_Size);
+ if(!CRC)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Here we could check the CRC if we wanted to.
+ */
+
+ /*
+ * multi-byte type swapping
+ */
+
+ IHDR_Width = BigLong(IHDR->Width);
+ IHDR_Height = BigLong(IHDR->Height);
+
+ /*
+ * Check if Width and Height are valid.
+ */
+
+ if(!((IHDR_Width > 0) && (IHDR_Height > 0))
+ || IHDR_Width > INT_MAX / Q3IMAGE_BYTESPERPIXEL / IHDR_Height)
+ {
+ CloseBufferedFile(ThePNG);
+
+ ri.Printf( PRINT_WARNING, "%s: invalid image size\n", name );
+
+ return;
+ }
+
+ /*
+ * Do we need to check if the dimensions of the image are valid for Quake3?
+ */
+
+ /*
+ * Check if CompressionMethod and FilterMethod are valid.
+ */
+
+ if(!((IHDR->CompressionMethod == PNG_CompressionMethod_0) && (IHDR->FilterMethod == PNG_FilterMethod_0)))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Check if InterlaceMethod is valid.
+ */
+
+ if(!((IHDR->InterlaceMethod == PNG_InterlaceMethod_NonInterlaced) || (IHDR->InterlaceMethod == PNG_InterlaceMethod_Interlaced)))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read palette for an indexed image.
+ */
+
+ if(IHDR->ColourType == PNG_ColourType_Indexed)
+ {
+ /*
+ * We need the palette first.
+ */
+
+ if(!FindChunk(ThePNG, PNG_ChunkType_PLTE))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the chunk-header.
+ */
+
+ CH = (struct PNG_ChunkHeader*)BufferedFileRead(ThePNG, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * PNG multi-byte types are in Big Endian
+ */
+
+ ChunkHeaderLength = BigLong(CH->Length);
+ ChunkHeaderType = BigLong(CH->Type);
+
+ /*
+ * Check if the chunk is a PLTE.
+ */
+
+ if(!(ChunkHeaderType == PNG_ChunkType_PLTE))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Check if Length is divisible by 3
+ */
+
+ if(ChunkHeaderLength % 3)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the raw palette data
+ */
+
+ InPal = (uint8_t*)BufferedFileRead(ThePNG, ChunkHeaderLength);
+ if(!InPal)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the CRC for the palette
+ */
+
+ CRC = (PNG_ChunkCRC*)BufferedFileRead(ThePNG, PNG_ChunkCRC_Size);
+ if(!CRC)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Set some default values.
+ */
+
+ for(i = 0; i < 256; i++)
+ {
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = 0x00;
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = 0x00;
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = 0x00;
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF;
+ }
+
+ /*
+ * Convert to the Quake3 RGBA-format.
+ */
+
+ for(i = 0; i < (ChunkHeaderLength / 3); i++)
+ {
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = InPal[i*3+0];
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = InPal[i*3+1];
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = InPal[i*3+2];
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF;
+ }
+ }
+
+ /*
+ * transparency information is sometimes stored in a tRNS chunk
+ */
+
+ /*
+ * Let's see if there is a tRNS chunk
+ */
+
+ if(FindChunk(ThePNG, PNG_ChunkType_tRNS))
+ {
+ uint8_t *Trans;
+
+ /*
+ * Read the chunk-header.
+ */
+
+ CH = (struct PNG_ChunkHeader*)BufferedFileRead(ThePNG, PNG_ChunkHeader_Size);
+ if(!CH)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * PNG multi-byte types are in Big Endian
+ */
+
+ ChunkHeaderLength = BigLong(CH->Length);
+ ChunkHeaderType = BigLong(CH->Type);
+
+ /*
+ * Check if the chunk is a tRNS.
+ */
+
+ if(!(ChunkHeaderType == PNG_ChunkType_tRNS))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the transparency information.
+ */
+
+ Trans = (uint8_t*)BufferedFileRead(ThePNG, ChunkHeaderLength);
+ if(!Trans)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Read the CRC.
+ */
+
+ CRC = (PNG_ChunkCRC*)BufferedFileRead(ThePNG, PNG_ChunkCRC_Size);
+ if(!CRC)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Only for Grey, True and Indexed ColourType should tRNS exist.
+ */
+
+ switch(IHDR->ColourType)
+ {
+ case PNG_ColourType_Grey :
+ {
+ if(ChunkHeaderLength != 2)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ HasTransparentColour = qtrue;
+
+ /*
+ * Grey can have one colour which is completely transparent.
+ * This colour is always stored in 16 bits.
+ */
+
+ TransparentColour[0] = Trans[0];
+ TransparentColour[1] = Trans[1];
+
+ break;
+ }
+
+ case PNG_ColourType_True :
+ {
+ if(ChunkHeaderLength != 6)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ HasTransparentColour = qtrue;
+
+ /*
+ * True can have one colour which is completely transparent.
+ * This colour is always stored in 16 bits.
+ */
+
+ TransparentColour[0] = Trans[0];
+ TransparentColour[1] = Trans[1];
+ TransparentColour[2] = Trans[2];
+ TransparentColour[3] = Trans[3];
+ TransparentColour[4] = Trans[4];
+ TransparentColour[5] = Trans[5];
+
+ break;
+ }
+
+ case PNG_ColourType_Indexed :
+ {
+ /*
+ * Maximum of 256 one byte transparency entries.
+ */
+
+ if(ChunkHeaderLength > 256)
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ HasTransparentColour = qtrue;
+
+ /*
+ * alpha values for palette entries
+ */
+
+ for(i = 0; i < ChunkHeaderLength; i++)
+ {
+ OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = Trans[i];
+ }
+
+ break;
+ }
+
+ /*
+ * All other ColourTypes should not have tRNS chunks
+ */
+
+ default :
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+ }
+ }
+
+ /*
+ * Rewind to the start of the file.
+ */
+
+ if(!BufferedFileRewind(ThePNG, -1))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Skip the signature
+ */
+
+ if(!BufferedFileSkip(ThePNG, PNG_Signature_Size))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Decompress all IDAT chunks
+ */
+
+ DecompressedDataLength = DecompressIDATs(ThePNG, &DecompressedData);
+ if(!(DecompressedDataLength && DecompressedData))
+ {
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Allocate output buffer.
+ */
+
+ OutBuffer = (byte*)ri.Malloc(IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL);
+ if(!OutBuffer)
+ {
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ /*
+ * Interlaced and Non-interlaced images need to be handled differently.
+ */
+
+ switch(IHDR->InterlaceMethod)
+ {
+ case PNG_InterlaceMethod_NonInterlaced :
+ {
+ if(!DecodeImageNonInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal))
+ {
+ ri.Free(OutBuffer);
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ break;
+ }
+
+ case PNG_InterlaceMethod_Interlaced :
+ {
+ if(!DecodeImageInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal))
+ {
+ ri.Free(OutBuffer);
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+
+ break;
+ }
+
+ default :
+ {
+ ri.Free(OutBuffer);
+ ri.Free(DecompressedData);
+ CloseBufferedFile(ThePNG);
+
+ return;
+ }
+ }
+
+ /*
+ * update the pointer to the image data
+ */
+
+ *pic = OutBuffer;
+
+ /*
+ * Fill width and height.
+ */
+
+ if(width)
+ {
+ *width = IHDR_Width;
+ }
+
+ if(height)
+ {
+ *height = IHDR_Height;
+ }
+
+ /*
+ * DecompressedData is not needed anymore.
+ */
+
+ ri.Free(DecompressedData);
+
+ /*
+ * We have all data, so close the file.
+ */
+
+ CloseBufferedFile(ThePNG);
+}
diff --git a/src/renderercommon/tr_image_tga.cpp b/src/renderercommon/tr_image_tga.cpp
new file mode 100644
index 0000000..c7f587d
--- /dev/null
+++ b/src/renderercommon/tr_image_tga.cpp
@@ -0,0 +1,317 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+
+#include "tr_common.h"
+
+/*
+========================================================================
+
+TGA files are used for 24/32 bit images
+
+========================================================================
+*/
+
+typedef struct _TargaHeader {
+ unsigned char id_length, colormap_type, image_type;
+ unsigned short colormap_index, colormap_length;
+ unsigned char colormap_size;
+ unsigned short x_origin, y_origin, width, height;
+ unsigned char pixel_size, attributes;
+} TargaHeader;
+
+void R_LoadTGA ( const char *name, byte **pic, int *width, int *height)
+{
+ unsigned columns, rows, numPixels;
+ byte *pixbuf;
+ int row, column;
+ byte *buf_p;
+ byte *end;
+ union {
+ byte *b;
+ void *v;
+ } buffer;
+ TargaHeader targa_header;
+ byte *targa_rgba;
+ int length;
+
+ *pic = NULL;
+
+ if(width)
+ *width = 0;
+ if(height)
+ *height = 0;
+
+ //
+ // load the file
+ //
+ length = ri.FS_ReadFile ( ( char * ) name, &buffer.v);
+ if (!buffer.b || length < 0) {
+ return;
+ }
+
+ if(length < 18)
+ {
+ ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
+ }
+
+ buf_p = buffer.b;
+ end = buffer.b + length;
+
+ targa_header.id_length = buf_p[0];
+ targa_header.colormap_type = buf_p[1];
+ targa_header.image_type = buf_p[2];
+
+ memcpy(&targa_header.colormap_index, &buf_p[3], 2);
+ memcpy(&targa_header.colormap_length, &buf_p[5], 2);
+ targa_header.colormap_size = buf_p[7];
+ memcpy(&targa_header.x_origin, &buf_p[8], 2);
+ memcpy(&targa_header.y_origin, &buf_p[10], 2);
+ memcpy(&targa_header.width, &buf_p[12], 2);
+ memcpy(&targa_header.height, &buf_p[14], 2);
+ targa_header.pixel_size = buf_p[16];
+ targa_header.attributes = buf_p[17];
+
+ targa_header.colormap_index = LittleShort(targa_header.colormap_index);
+ targa_header.colormap_length = LittleShort(targa_header.colormap_length);
+ targa_header.x_origin = LittleShort(targa_header.x_origin);
+ targa_header.y_origin = LittleShort(targa_header.y_origin);
+ targa_header.width = LittleShort(targa_header.width);
+ targa_header.height = LittleShort(targa_header.height);
+
+ buf_p += 18;
+
+ if (targa_header.image_type!=2
+ && targa_header.image_type!=10
+ && targa_header.image_type != 3 )
+ {
+ ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported");
+ }
+
+ if ( targa_header.colormap_type != 0 )
+ {
+ ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" );
+ }
+
+ if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 )
+ {
+ ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)");
+ }
+
+ columns = targa_header.width;
+ rows = targa_header.height;
+ numPixels = columns * rows * 4;
+
+ if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows)
+ {
+ ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name);
+ }
+
+
+ targa_rgba = (byte*)ri.Malloc (numPixels);
+
+ if (targa_header.id_length != 0)
+ {
+ if (buf_p + targa_header.id_length > end)
+ ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name );
+
+ buf_p += targa_header.id_length; // skip TARGA image comment
+ }
+
+ if ( targa_header.image_type==2 || targa_header.image_type == 3 )
+ {
+ if(buf_p + columns*rows*targa_header.pixel_size/8 > end)
+ {
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
+ }
+
+ // Uncompressed RGB or gray scale image
+ for(row=rows-1; row>=0; row--)
+ {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; column++)
+ {
+ unsigned char red,green,blue,alphabyte;
+ switch (targa_header.pixel_size)
+ {
+
+ case 8:
+ blue = *buf_p++;
+ green = blue;
+ red = blue;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
+ break;
+ }
+ }
+ }
+ }
+ else if (targa_header.image_type==10) { // Runlength encoded RGB images
+ unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
+
+ for(row=rows-1; row>=0; row--) {
+ pixbuf = targa_rgba + row*columns*4;
+ for(column=0; column<columns; ) {
+ if(buf_p + 1 > end)
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
+ packetHeader= *buf_p++;
+ packetSize = 1 + (packetHeader & 0x7f);
+ if (packetHeader & 0x80) { // run-length packet
+ if(buf_p + targa_header.pixel_size/8 > end)
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
+ break;
+ }
+
+ for(j=0;j<packetSize;j++) {
+ *pixbuf++=red;
+ *pixbuf++=green;
+ *pixbuf++=blue;
+ *pixbuf++=alphabyte;
+ column++;
+ if (column==columns) { // run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ else { // non run-length packet
+
+ if(buf_p + targa_header.pixel_size/8*packetSize > end)
+ ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name);
+ for(j=0;j<packetSize;j++) {
+ switch (targa_header.pixel_size) {
+ case 24:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = 255;
+ break;
+ case 32:
+ blue = *buf_p++;
+ green = *buf_p++;
+ red = *buf_p++;
+ alphabyte = *buf_p++;
+ *pixbuf++ = red;
+ *pixbuf++ = green;
+ *pixbuf++ = blue;
+ *pixbuf++ = alphabyte;
+ break;
+ default:
+ ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name );
+ break;
+ }
+ column++;
+ if (column==columns) { // pixel packet run spans across rows
+ column=0;
+ if (row>0)
+ row--;
+ else
+ goto breakOut;
+ pixbuf = targa_rgba + row*columns*4;
+ }
+ }
+ }
+ }
+ breakOut:;
+ }
+ }
+
+#if 0
+ // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs
+ // bit 5 set => top-down
+ if (targa_header.attributes & 0x20) {
+ unsigned char *flip = (unsigned char*)malloc (columns*4);
+ unsigned char *src, *dst;
+
+ for (row = 0; row < rows/2; row++) {
+ src = targa_rgba + row * 4 * columns;
+ dst = targa_rgba + (rows - row - 1) * 4 * columns;
+
+ memcpy (flip, src, columns*4);
+ memcpy (src, dst, columns*4);
+ memcpy (dst, flip, columns*4);
+ }
+ free (flip);
+ }
+#endif
+ // instead we just print a warning
+ if (targa_header.attributes & 0x20) {
+ ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name);
+ }
+
+ if (width)
+ *width = columns;
+ if (height)
+ *height = rows;
+
+ *pic = targa_rgba;
+
+ ri.FS_FreeFile (buffer.v);
+}
diff --git a/src/renderercommon/tr_noise.cpp b/src/renderercommon/tr_noise.cpp
new file mode 100644
index 0000000..b31016b
--- /dev/null
+++ b/src/renderercommon/tr_noise.cpp
@@ -0,0 +1,93 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+// tr_noise.c
+#include "tr_common.h"
+
+#define NOISE_SIZE 256
+#define NOISE_MASK ( NOISE_SIZE - 1 )
+
+#define VAL( a ) s_noise_perm[ ( a ) & ( NOISE_MASK )]
+#define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) )
+
+static float s_noise_table[NOISE_SIZE];
+static int s_noise_perm[NOISE_SIZE];
+
+static float GetNoiseValue( int x, int y, int z, int t )
+{
+ int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t );
+
+ return s_noise_table[index];
+}
+
+void R_NoiseInit( void )
+{
+ int i;
+
+ for ( i = 0; i < NOISE_SIZE; i++ )
+ {
+ s_noise_table[i] = ( float ) ( ( ( rand() / ( float ) RAND_MAX ) * 2.0 - 1.0 ) );
+ s_noise_perm[i] = ( unsigned char ) ( rand() / ( float ) RAND_MAX * 255 );
+ }
+}
+
+float R_NoiseGet4f( float x, float y, float z, double t )
+{
+ int i;
+ int ix, iy, iz, it;
+ float fx, fy, fz, ft;
+ float front[4];
+ float back[4];
+ float fvalue, bvalue, value[2], finalvalue;
+
+ ix = ( int ) floor( x );
+ fx = x - ix;
+ iy = ( int ) floor( y );
+ fy = y - iy;
+ iz = ( int ) floor( z );
+ fz = z - iz;
+ it = ( int ) floor( t );
+ ft = t - it;
+
+ for ( i = 0; i < 2; i++ )
+ {
+ front[0] = GetNoiseValue( ix, iy, iz, it + i );
+ front[1] = GetNoiseValue( ix+1, iy, iz, it + i );
+ front[2] = GetNoiseValue( ix, iy+1, iz, it + i );
+ front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i );
+
+ back[0] = GetNoiseValue( ix, iy, iz + 1, it + i );
+ back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i );
+ back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i );
+ back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i );
+
+ fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy );
+ bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy );
+
+ value[i] = LERP( fvalue, bvalue, fz );
+ }
+
+ finalvalue = LERP( value[0], value[1], ft );
+
+ return finalvalue;
+}
diff --git a/src/renderercommon/tr_public.h b/src/renderercommon/tr_public.h
new file mode 100644
index 0000000..9d926d0
--- /dev/null
+++ b/src/renderercommon/tr_public.h
@@ -0,0 +1,199 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+#ifndef __TR_PUBLIC_H
+#define __TR_PUBLIC_H
+
+#include "renderercommon/tr_types.h"
+
+#define REF_API_VERSION 8
+
+// AVI files have the start of pixel lines 4 byte-aligned
+#define AVI_LINE_PADDING 4
+
+//
+// these are the functions exported by the refresh module
+//
+typedef struct {
+ // called before the library is unloaded
+ // if the system is just reconfiguring, pass destroyWindow = qfalse,
+ // which will keep the screen from flashing to the desktop.
+ void (*Shutdown)( bool destroyWindow );
+
+ // All data that will be used in a level should be
+ // registered before rendering any frames to prevent disk hits,
+ // but they can still be registered at a later time
+ // if necessary.
+ //
+ // BeginRegistration makes any existing media pointers invalid
+ // and returns the current gl configuration, including screen width
+ // and height, which can be used by the client to intelligently
+ // size display elements
+ void (*BeginRegistration)( glconfig_t *config );
+ qhandle_t (*RegisterModel)( const char *name );
+ qhandle_t (*RegisterSkin)( const char *name );
+ qhandle_t (*RegisterShader)( const char *name );
+ qhandle_t (*RegisterShaderNoMip)( const char *name );
+ void (*LoadWorld)( const char *name );
+
+ // the vis data is a large enough block of data that we go to the trouble
+ // of sharing it with the clipmodel subsystem
+ void (*SetWorldVisData)( const byte *vis );
+
+ // EndRegistration will draw a tiny polygon with each texture, forcing
+ // them to be loaded into card memory
+ void (*EndRegistration)( void );
+
+ // a scene is built up by calls to R_ClearScene and the various R_Add functions.
+ // Nothing is drawn until R_RenderScene is called.
+ void (*ClearScene)( void );
+ void (*AddRefEntityToScene)( const refEntity_t *re );
+ void (*AddPolyToScene)( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num );
+ bool (*LightForPoint)( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir );
+ void (*AddLightToScene)( const vec3_t org, float intensity, float r, float g, float b );
+ void (*AddAdditiveLightToScene)( const vec3_t org, float intensity, float r, float g, float b );
+ void (*RenderScene)( const refdef_t *fd );
+
+ void (*SetColor)( const float *rgba ); // NULL = 1,1,1,1
+ void (*SetClipRegion)( const float *region );
+ void (*DrawStretchPic) ( float x, float y, float w, float h,
+ float s1, float t1, float s2, float t2, qhandle_t hShader ); // 0 = white
+
+ // Draw images for cinematic rendering, pass as 32 bit rgba
+ void (*DrawStretchRaw) (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, bool dirty);
+ void (*UploadCinematic) (int w, int h, int cols, int rows, const byte *data, int client, bool dirty);
+
+ void (*BeginFrame)( stereoFrame_t stereoFrame );
+
+ // if the pointers are not NULL, timing info will be returned
+ void (*EndFrame)( int *frontEndMsec, int *backEndMsec );
+
+
+ int (*MarkFragments)( int numPoints, const vec3_t *points, const vec3_t projection,
+ int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer );
+
+ int (*LerpTag)( orientation_t *tag, qhandle_t model, int startFrame, int endFrame,
+ float frac, const char *tagName );
+ void (*ModelBounds)( qhandle_t model, vec3_t mins, vec3_t maxs );
+
+#ifdef __USEA3D
+ void (*A3D_RenderGeometry) (void *pVoidA3D, void *pVoidGeom, void *pVoidMat, void *pVoidGeomStatus);
+#endif
+ void (*RegisterFont)(const char *fontName, int pointSize, fontInfo_t *font);
+ void (*RemapShader)(const char *oldShader, const char *newShader, const char *offsetTime);
+ bool (*GetEntityToken)( char *buffer, int size );
+ bool (*inPVS)( const vec3_t p1, const vec3_t p2 );
+
+ void (*TakeVideoFrame)( int h, int w, byte* captureBuffer, byte *encodeBuffer, bool motionJpeg );
+} refexport_t;
+
+//
+// these are the functions imported by the refresh module
+//
+typedef struct {
+ // print message on the local console
+ void (QDECL *Printf)( int printLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
+
+ // abort the game
+ void (QDECL *Error)( int errorLevel, const char *fmt, ...) __attribute__ ((noreturn, format (printf, 2, 3)));
+
+ // milliseconds should only be used for profiling, never
+ // for anything game related. Get time from the refdef
+ int (*Milliseconds)( void );
+
+ // stack based memory allocation for per-level things that
+ // won't be freed
+#ifdef HUNK_DEBUG
+ void *(*Hunk_AllocDebug)( int size, ha_pref pref, const char *label, const char *file, int line );
+#else
+ void *(*Hunk_Alloc)( int size, ha_pref pref );
+#endif
+ void *(*Hunk_AllocateTempMemory)( int size );
+ void (*Hunk_FreeTempMemory)( void *block );
+
+ // dynamic memory allocator for things that need to be freed
+ void *(*Malloc)( int bytes );
+ void (*Free)( void *buf );
+
+ cvar_t *(*Cvar_Get)( const char *name, const char *value, int flags );
+ void (*Cvar_Set)( const char *name, const char *value );
+ void (*Cvar_SetValue) (const char *name, float value);
+ void (*Cvar_CheckRange)( cvar_t *cv, float minVal, float maxVal, bool shouldBeIntegral );
+ void (*Cvar_SetDescription)( cvar_t *cv, const char *description );
+
+ int (*Cvar_VariableIntegerValue) (const char *var_name);
+
+ void (*Cmd_AddCommand)( const char *name, void(*cmd)(void) );
+ void (*Cmd_RemoveCommand)( const char *name );
+
+ int (*Cmd_Argc) (void);
+ const char* (*Cmd_Argv) (int i);
+
+ void (*Cmd_ExecuteText) (int exec_when, const char *text);
+
+ byte *(*CM_ClusterPVS)(int cluster);
+
+ // visualization for debugging collision detection
+ void (*CM_DrawDebugSurface)( void (*drawPoly)(int color, int numPoints, float *points) );
+
+ // a -1 return means the file does not exist
+ // NULL can be passed for buf to just determine existance
+ int (*FS_FileIsInPAK)( const char *name, int *pCheckSum );
+ long (*FS_ReadFile)( const char *name, void **buf );
+ void (*FS_FreeFile)( void *buf );
+ char ** (*FS_ListFiles)( const char *name, const char *extension, int *numfilesfound );
+ void (*FS_FreeFileList)( char **filelist );
+ void (*FS_WriteFile)( const char *qpath, const void *buffer, int size );
+ bool (*FS_FileExists)( const char *file );
+
+ // cinematic stuff
+ void (*CIN_UploadCinematic)(int handle);
+ int (*CIN_PlayCinematic)( const char *arg0, int xpos, int ypos, int width, int height, int bits);
+ e_status (*CIN_RunCinematic) (int handle);
+
+ void (*CL_WriteAVIVideoFrame)( const byte *buffer, int size );
+
+ // input event handling
+ void (*IN_Init)( void *windowData );
+ void (*IN_Shutdown)( void );
+ void (*IN_Restart)( void );
+
+ // system stuff
+ void (*Sys_GLimpSafeInit)( void );
+ void (*Sys_GLimpInit)( void );
+ bool (*Sys_LowPhysicalMemory)( void );
+} refimport_t;
+
+
+// this is the only function actually exported at the linker level
+// If the module can't init to a valid rendering state, NULL will be
+// returned.
+#ifdef USE_RENDERER_DLOPEN
+extern "C" {
+typedef refexport_t* (QDECL *GetRefAPI_t) (int apiVersion, refimport_t * rimp);
+}
+#else
+refexport_t* GetRefAPI( int apiVersion, refimport_t *rimp );
+#endif
+
+#endif // __TR_PUBLIC_H
diff --git a/src/renderercommon/tr_types.h b/src/renderercommon/tr_types.h
new file mode 100644
index 0000000..67f3733
--- /dev/null
+++ b/src/renderercommon/tr_types.h
@@ -0,0 +1,222 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2013 Darklegion Development
+Copyright (C) 2015-2019 GrangerHub
+
+This file is part of Tremulous.
+
+Tremulous is free software; you can redistribute it
+and/or modify it under the terms of the GNU General Public License as
+published by the Free Software Foundation; either version 3 of the License,
+or (at your option) any later version.
+
+Tremulous 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 Tremulous; if not, see <https://www.gnu.org/licenses/>
+
+===========================================================================
+*/
+
+#ifndef __TR_TYPES_H
+#define __TR_TYPES_H
+
+#define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces
+
+#define REFENTITYNUM_BITS 10 // can't be increased without changing drawsurf bit packing
+#define REFENTITYNUM_MASK ((1<<REFENTITYNUM_BITS) - 1)
+// the last N-bit number (2^REFENTITYNUM_BITS - 1) is reserved for the special world refentity,
+// and this is reflected by the value of MAX_REFENTITIES (which therefore is not a power-of-2)
+#define MAX_REFENTITIES ((1<<REFENTITYNUM_BITS) - 1)
+#define REFENTITYNUM_WORLD ((1<<REFENTITYNUM_BITS) - 1)
+
+// renderfx flags
+#define RF_MINLIGHT 0x0001 // allways have some light (viewmodel, some items)
+#define RF_THIRD_PERSON 0x0002 // don't draw through eyes, only mirrors (player bodies, chat sprites)
+#define RF_FIRST_PERSON 0x0004 // only draw through eyes (view weapon, damage blood blob)
+#define RF_DEPTHHACK 0x0008 // for view weapon Z crunching
+
+#define RF_CROSSHAIR 0x0010 // This item is a cross hair and will draw over everything similar to
+ // DEPTHHACK in stereo rendering mode, with the difference that the
+ // projection matrix won't be hacked to reduce the stereo separation as
+ // is done for the gun.
+
+#define RF_NOSHADOW 0x0040 // don't add stencil shadows
+
+#define RF_LIGHTING_ORIGIN 0x0080 // use refEntity->lightingOrigin instead of refEntity->origin
+ // for lighting. This allows entities to sink into the floor
+ // with their origin going solid, and allows all parts of a
+ // player to get the same lighting
+
+#define RF_SHADOW_PLANE 0x0100 // use refEntity->shadowPlane
+#define RF_WRAP_FRAMES 0x0200 // mod the model frames by the maxframes to allow continuous
+ // animation without needing to know the frame count
+
+// refdef flags
+#define RDF_NOWORLDMODEL 0x0001 // used for player configuration screen
+#define RDF_HYPERSPACE 0x0004 // teleportation effect
+
+typedef struct {
+ vec3_t xyz;
+ float st[2];
+ byte modulate[4];
+} polyVert_t;
+
+typedef struct poly_s {
+ qhandle_t hShader;
+ int numVerts;
+ polyVert_t *verts;
+} poly_t;
+
+typedef enum {
+ RT_MODEL,
+ RT_POLY,
+ RT_SPRITE,
+ RT_BEAM,
+ RT_RAIL_CORE,
+ RT_RAIL_RINGS,
+ RT_LIGHTNING,
+ RT_PORTALSURFACE, // doesn't draw anything, just info for portals
+
+ RT_MAX_REF_ENTITY_TYPE
+} refEntityType_t;
+
+typedef struct {
+ refEntityType_t reType;
+ int renderfx;
+
+ qhandle_t hModel; // opaque type outside refresh
+
+ // most recent data
+ vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN)
+ float shadowPlane; // projection shadows go here, stencils go slightly lower
+
+ vec3_t axis[3]; // rotation vectors
+ qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale
+ float origin[3]; // also used as MODEL_BEAM's "from"
+ int frame; // also used as MODEL_BEAM's diameter
+
+ // previous data for frame interpolation
+ float oldorigin[3]; // also used as MODEL_BEAM's "to"
+ int oldframe;
+ float backlerp; // 0.0 = current, 1.0 = old
+
+ // texturing
+ int skinNum; // inline skin index
+ qhandle_t customSkin; // NULL for default skin
+ qhandle_t customShader; // use one image for the entire thing
+
+ // misc
+ byte shaderRGBA[4]; // colors used by rgbgen entity shaders
+ float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers
+ float shaderTime; // subtracted from refdef time to control effect start times
+
+ // extra sprite information
+ float radius;
+ float rotation;
+} refEntity_t;
+
+
+#define MAX_RENDER_STRINGS 8
+#define MAX_RENDER_STRING_LENGTH 32
+
+typedef struct {
+ int x, y, width, height;
+ float fov_x, fov_y;
+ vec3_t vieworg;
+ vec3_t viewaxis[3]; // transformation matrix
+
+ // time in milliseconds for shader effects and other time dependent rendering issues
+ int time;
+
+ int rdflags; // RDF_NOWORLDMODEL, etc
+
+ // 1 bits will prevent the associated area from rendering at all
+ byte areamask[MAX_MAP_AREA_BYTES];
+
+ // text messages for deform text shaders
+ char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH];
+} refdef_t;
+
+
+typedef enum {
+ STEREO_CENTER,
+ STEREO_LEFT,
+ STEREO_RIGHT
+} stereoFrame_t;
+
+
+/*
+** glconfig_t
+**
+** Contains variables specific to the OpenGL configuration
+** being run right now. These are constant once the OpenGL
+** subsystem is initialized.
+*/
+typedef enum {
+ TC_NONE,
+ TC_S3TC, // this is for the GL_S3_s3tc extension.
+ TC_S3TC_ARB // this is for the GL_EXT_texture_compression_s3tc extension.
+} textureCompression_t;
+
+typedef enum {
+ GLDRV_ICD, // driver is integrated with window system
+ // WARNING: there are tests that check for
+ // > GLDRV_ICD for minidriverness, so this
+ // should always be the lowest value in this
+ // enum set
+ GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver
+ GLDRV_VOODOO // driver is a 3Dfx standalone driver
+} glDriverType_t;
+
+typedef enum {
+ GLHW_GENERIC, // where everthing works the way it should
+ GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is
+ // the hardware type then there can NOT exist a secondary
+ // display adapter
+ GLHW_RIVA128, // where you can't interpolate alpha
+ GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures
+ GLHW_PERMEDIA2 // where you don't have src*dst
+} glHardwareType_t;
+
+typedef struct {
+ char renderer_string[MAX_STRING_CHARS];
+ char vendor_string[MAX_STRING_CHARS];
+ char version_string[MAX_STRING_CHARS];
+ char extensions_string[BIG_INFO_STRING];
+
+ int maxTextureSize; // queried from GL
+ int numTextureUnits; // multitexture ability
+
+ int colorBits, depthBits, stencilBits;
+
+ glDriverType_t driverType;
+ glHardwareType_t hardwareType;
+
+ qboolean deviceSupportsGamma;
+ int/*textureCompression_t*/ textureCompression;
+ qboolean textureEnvAddAvailable;
+
+ int vidWidth, vidHeight;
+ // aspect is the screen's physical width / height, which may be different
+ // than scrWidth / scrHeight if the pixels are non-square
+ // normal screens should be 4/3, but wide aspect monitors may be 16/9
+ float windowAspect;
+ float displayAspect;
+
+ int displayFrequency;
+
+ // synonymous with "does rendering consume the entire screen?", therefore
+ // a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that
+ // used CDS.
+ qboolean isFullscreen;
+ qboolean stereoEnabled;
+ qboolean textureFilterAnisotropic;
+ int maxAnisotropy;
+} glconfig_t;
+
+#endif // __TR_TYPES_H