- finally managed to merge in the original texture system commit.

Game compiles and runs but transparency doesn't work yet.

# Conflicts:
#	source/CMakeLists.txt
#	source/core/menu/menu.cpp
#	source/core/textures/buildtiles.cpp
This commit is contained in:
Christoph Oelckers 2020-05-24 21:19:33 +02:00
parent 0179029ed1
commit 6bffdf80a1
48 changed files with 30050 additions and 138 deletions

View file

@ -542,6 +542,24 @@ else()
set( OTHER_SYSTEM_SOURCES ${PLAT_WIN32_SOURCES} ${PLAT_OSX_SOURCES} ${PLAT_COCOA_SOURCES} )
endif()
if( HAVE_MMX )
add_definitions( -DHAVE_MMX=1 )
set( SYSTEM_SOURCES ${SYSTEM_SOURCES}
common/textures/hires/hqnx_asm/hq2x_asm.cpp
common/textures/hires/hqnx_asm/hq3x_asm.cpp
common/textures/hires/hqnx_asm/hq4x_asm.cpp
common/textures/hires/hqnx_asm/hqnx_asm_Image.cpp)
if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
set_source_files_properties(
common/textures/hires/hqnx_asm/hq2x_asm.cpp
common/textures/hires/hqnx_asm/hq3x_asm.cpp
common/textures/hires/hqnx_asm/hq4x_asm.cpp
common/textures/hires/hqresize.cpp
PROPERTIES COMPILE_FLAGS "-mmmx" )
endif( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE )
endif( HAVE_MMX )
if( HAVE_PARALLEL_FOR )
add_definitions( -DHAVE_PARALLEL_FOR=1 )
@ -611,6 +629,9 @@ file( GLOB HEADER_FILES
common/objects/*.h
common/filesystem/*.h
common/textures/*.h
common/textures/hires/hqnx/*.h
common/textures/hires/hqnx_asm/*.h
common/textures/hires/xbr/*.h
common/thirdparty/*.h
common/thirdparty/rapidjson/*.h
common/thirdparty/math/*h
@ -660,6 +681,12 @@ set( VM_JIT_SOURCES
set( FASTMATH_SOURCES
common/rendering/gl_load/gl_load.c
common/textures/hires/hqnx/init.cpp
common/textures/hires/hqnx/hq2x.cpp
common/textures/hires/hqnx/hq3x.cpp
common/textures/hires/hqnx/hq4x.cpp
common/textures/hires/xbr/xbrz.cpp
common/textures/hires/xbr/xbrz_old.cpp
# This should better be made a library subproject later, once things are working.
libsmackerdec/src/BitReader.cpp
libsmackerdec/src/FileStream.cpp
@ -749,18 +776,23 @@ set (PCH_SOURCES
common/thirdparty/sfmt/SFMT.cpp
common/textures/texture.cpp
common/textures/animtexture.cpp
common/textures/hw_ihwtexture.cpp
common/textures/hw_material.cpp
common/fonts/v_text.cpp
common/textures/bitmap.cpp
common/textures/m_png.cpp
common/textures/texture.cpp
common/textures/image.cpp
common/textures/imagetexture.cpp
common/textures/texturemanager.cpp
common/textures/hw_ihwtexture.cpp
common/textures/formats/fontchars.cpp
common/textures/multipatchtexturebuilder.cpp
common/textures/skyboxtexture.cpp
common/textures/formats/automaptexture.cpp
common/textures/formats/brightmaptexture.cpp
common/textures/formats/buildtexture.cpp
common/textures/formats/ddstexture.cpp
common/textures/formats/flattexture.cpp
common/textures/formats/fontchars.cpp
common/textures/formats/imgztexture.cpp
common/textures/formats/jpegtexture.cpp
common/textures/formats/md5check.cpp
@ -773,6 +805,7 @@ set (PCH_SOURCES
common/textures/formats/shadertexture.cpp
common/textures/formats/tgatexture.cpp
common/textures/formats/stbtexture.cpp
common/textures/hires/hqresize.cpp
common/console/c_commandline.cpp
common/console/c_buttons.cpp
common/console/c_bind.cpp
@ -842,8 +875,7 @@ set (PCH_SOURCES
core/textures/buildtiles.cpp
core/textures/imagetexture.cpp
core/textures/imagehelpers.cpp
#core/textures/texture.cpp
core/music/s_advsound.cpp
@ -982,6 +1014,7 @@ include_directories(
common/thirdparty
common/textures
common/textures/formats
common/textures/hires
common/filesystem
common/utility
common/console
@ -1119,7 +1152,11 @@ source_group("Common\\Scripting\\VM" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE
source_group("Common\\Rendering" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/.+")
source_group("Common\\Rendering\\OpenGL Loader" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/rendering/gl_load/.+")
source_group("Common\\Textures" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/.+")
source_group("Common\\Textures\\Formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/formats.+")
source_group("Common\\Textures\\Hires" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/hires/.+")
source_group("Common\\Textures\\Hires\\HQ Resize" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/hires/hqnx/.+")
source_group("Common\\Textures\\Hires\\HQ Resize MMX version" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/hires/hqnx_asm/.+")
source_group("Common\\Textures\\Hires\\XBRZ" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/hires/xbr/.+")
source_group("Common\\Textures\\Formats" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/textures/formats/.+")
source_group("Common\\Third Party" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/.+")
source_group("Common\\Third Party\\Math" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/math/.+")
source_group("Common\\Third Party\\RapidJSON" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/thirdparty/rapidjson/.+")

View file

@ -15,6 +15,7 @@
#define IDP3_MAGIC 0x33504449
class FTexture;
class FHardwareTexture;
struct mdmodel_t
{
@ -36,7 +37,7 @@ struct mdskinmap_t
{
uint8_t palette, flags, filler[2]; // Build palette number, flags the same as hightiles
int32_t skinnum, surfnum; // Skin identifier, surface number
FTexture* texture;
FTextureID texture;
mdskinmap_t* next;
float param, specpower, specfactor;
};

View file

@ -49,7 +49,7 @@ void VPXTexture::SetFrame(const void *data_, int width, int height)
Width = width;
Height = height;
data = data_;
DeleteHardwareTextures();
SystemTextures.Clean(true, true);
}
//===========================================================================

View file

@ -17,6 +17,7 @@
#include "bitmap.h"
#include "v_video.h"
#include "flatvertices.h"
#include "texturemanager.h"
#include "../../glbackend/glbackend.h"
static int32_t curextra=MAXTILES;
@ -395,8 +396,8 @@ int32_t md_defineskin(int32_t modelid, const char *skinfn, int32_t palnum, int32
sk->param = param;
sk->specpower = specpower;
sk->specfactor = specfactor;
sk->texture = TileFiles.GetTexture(skinfn);
if (!sk->texture)
sk->texture = TexMan.CheckForTexture(skinfn, ETextureType::Any);
if (!sk->texture.isValid())
{
Printf("Unable to load %s as model skin\n", skinfn);
}
@ -481,7 +482,7 @@ FTexture *mdloadskin(idmodel_t *m, int32_t number, int32_t pal, int32_t surf, bo
{
if (exact) *exact = true;
//Printf("Using exact match skin (pal=%d,skinnum=%d,surfnum=%d) %s\n",pal,number,surf,skinfile);
return sk->texture;
return TexMan.GetTexture(sk->texture);
}
//If no match, give highest priority to number, then pal.. (Parkar's request, 02/27/2005)
else if ((sk->palette == 0) && (sk->skinnum == number) && (sk->surfnum == surf) && (i < 5)) { i = 5; skzero = sk; }
@ -500,7 +501,7 @@ FTexture *mdloadskin(idmodel_t *m, int32_t number, int32_t pal, int32_t surf, bo
{
//Printf("Using def skin 0,0 as fallback, pal=%d\n", pal);
if (exact) *exact = false;
return skzero->texture;
return TexMan.GetTexture(skzero->texture);
}
else
return nullptr;
@ -883,8 +884,8 @@ static md2model_t *md2load(FileReader & fil, const char *filnam)
if (m->numskins > 0)
{
FStringf fn("%s%s", m->basepath, m->skinfn);
sk->texture = TileFiles.GetTexture(fn);
if (!sk->texture)
sk->texture = TexMan.CheckForTexture(fn, ETextureType::Any);
if (!sk->texture.isValid())
{
Printf("Unable to load %s as model skin\n", m->skinfn);
}

View file

@ -20,6 +20,7 @@
#include "palutil.h"
#include "colormatcher.h"
#include "m_swap.h"
#include "v_colortables.h"
#include "../../glbackend/glbackend.h"
// FString is a nice and convenient way to have automatically managed shared storage.

View file

@ -40,18 +40,17 @@
//
//==========================================================================
void AnimTexture::SetSize(int width, int height)
void AnimTexture::SetFrameSize(int width, int height)
{
Width = width;
Height = height;
FTexture::SetSize(width, height);
Image.Resize(width*height);
}
void AnimTexture::SetFrame(const uint8_t *palette, const void *data_)
{
memcpy(Palette, palette, 768);
memcpy(Image.Data(), data_, Width*Height);
DeleteHardwareTextures();
memcpy(Image.Data(), data_, Width * Height);
SystemTextures.Clean(true, true);
}
//===========================================================================
@ -101,8 +100,8 @@ AnimTextures::~AnimTextures()
void AnimTextures::SetSize(int width, int height)
{
tex[0]->SetSize(width, height);
tex[1]->SetSize(width, height);
tex[0]->SetFrameSize(width, height);
tex[1]->SetFrameSize(width, height);
}
void AnimTextures::SetFrame(const uint8_t *palette, const void* data)

View file

@ -9,7 +9,7 @@ class AnimTexture : public FTexture
TArray<uint8_t> Image;
public:
AnimTexture() = default;
void SetSize(int width, int height);
void SetFrameSize(int width, int height);
void SetFrame(const uint8_t *palette, const void* data);
virtual FBitmap GetBgraBitmap(const PalEntry* remap, int* trans) override;
};

View file

@ -118,3 +118,28 @@ struct BuildInfo
};
class FMultipatchTextureBuilder
{
FTextureManager &TexMan;
TArray<BuildInfo> BuiltTextures;
void(*progressFunc)();
void(*checkForHacks)(BuildInfo&);
void MakeTexture(BuildInfo &buildinfo, ETextureType usetype);
void BuildTexture(const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum, ETextureType usetyoe);
void AddTexturesLump(const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1);
void ParsePatch(FScanner &sc, BuildInfo &info, TexPart &part, TexInit &init);
void ResolvePatches(BuildInfo &buildinfo);
public:
FMultipatchTextureBuilder(FTextureManager &texMan, void(*progressFunc_)(), void(*checkForHacks_)(BuildInfo &)) : TexMan(texMan), progressFunc(progressFunc_), checkForHacks(checkForHacks_)
{
}
void AddTexturesLumps(int lump1, int lump2, int patcheslump);
void ParseTexture(FScanner &sc, ETextureType usetype);
void ResolveAllPatches();
};

View file

@ -0,0 +1,141 @@
/*
* Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com )
*
* Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
* Copyright (C) 2011 Francois Gannaz <mytskine@gmail.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __HQX_COMMON_H_
#define __HQX_COMMON_H_
#include <stdlib.h>
#include <stdint.h>
#define MASK_2 0x0000FF00
#define MASK_13 0x00FF00FF
#define MASK_RGB 0x00FFFFFF
#define MASK_ALPHA 0xFF000000
#define Ymask 0x00FF0000
#define Umask 0x0000FF00
#define Vmask 0x000000FF
#define trY 0x00300000
#define trU 0x00000700
#define trV 0x00000006
/* RGB to YUV lookup table */
extern uint32_t *RGBtoYUV;
static inline uint32_t rgb_to_yuv(uint32_t c)
{
// Mask against MASK_RGB to discard the alpha channel
return RGBtoYUV[MASK_RGB & c];
}
/* Test if there is difference in color */
static inline int yuv_diff(uint32_t yuv1, uint32_t yuv2) {
return (( abs((int32_t)(yuv1 & Ymask) - (int32_t)(yuv2 & Ymask)) > trY ) ||
( abs((int32_t)(yuv1 & Umask) - (int32_t)(yuv2 & Umask)) > trU ) ||
( abs((int32_t)(yuv1 & Vmask) - (int32_t)(yuv2 & Vmask)) > trV ) );
}
static inline int Diff(uint32_t c1, uint32_t c2)
{
return yuv_diff(rgb_to_yuv(c1), rgb_to_yuv(c2));
}
/* Interpolate functions */
static inline uint32_t Interpolate_2(uint32_t c1, int w1, uint32_t c2, int w2, int s)
{
if (c1 == c2) {
return c1;
}
return
(((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2) << (24-s)) & MASK_ALPHA) +
((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2) >> s) & MASK_2) +
((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2) >> s) & MASK_13);
}
static inline uint32_t Interpolate_3(uint32_t c1, int w1, uint32_t c2, int w2, uint32_t c3, int w3, int s)
{
return
(((((c1 & MASK_ALPHA) >> 24) * w1 + ((c2 & MASK_ALPHA) >> 24) * w2 + ((c3 & MASK_ALPHA) >> 24) * w3) << (24-s)) & MASK_ALPHA) +
((((c1 & MASK_2) * w1 + (c2 & MASK_2) * w2 + (c3 & MASK_2) * w3) >> s) & MASK_2) +
((((c1 & MASK_13) * w1 + (c2 & MASK_13) * w2 + (c3 & MASK_13) * w3) >> s) & MASK_13);
}
static inline uint32_t Interp1(uint32_t c1, uint32_t c2)
{
//(c1*3+c2) >> 2;
return Interpolate_2(c1, 3, c2, 1, 2);
}
static inline uint32_t Interp2(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*2+c2+c3) >> 2;
return Interpolate_3(c1, 2, c2, 1, c3, 1, 2);
}
static inline uint32_t Interp3(uint32_t c1, uint32_t c2)
{
//(c1*7+c2)/8;
return Interpolate_2(c1, 7, c2, 1, 3);
}
static inline uint32_t Interp4(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*2+(c2+c3)*7)/16;
return Interpolate_3(c1, 2, c2, 7, c3, 7, 4);
}
static inline uint32_t Interp5(uint32_t c1, uint32_t c2)
{
//(c1+c2) >> 1;
return Interpolate_2(c1, 1, c2, 1, 1);
}
static inline uint32_t Interp6(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*5+c2*2+c3)/8;
return Interpolate_3(c1, 5, c2, 2, c3, 1, 3);
}
static inline uint32_t Interp7(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*6+c2+c3)/8;
return Interpolate_3(c1, 6, c2, 1, c3, 1, 3);
}
static inline uint32_t Interp8(uint32_t c1, uint32_t c2)
{
//(c1*5+c2*3)/8;
return Interpolate_2(c1, 5, c2, 3, 3);
}
static inline uint32_t Interp9(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*2+(c2+c3)*3)/8;
return Interpolate_3(c1, 2, c2, 3, c3, 3, 3);
}
static inline uint32_t Interp10(uint32_t c1, uint32_t c2, uint32_t c3)
{
//(c1*14+c2+c3)/16;
return Interpolate_3(c1, 14, c2, 1, c3, 1, 4);
}
#endif

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,55 @@
/*
* Copyright (C) 2003 Maxim Stepin ( maxst@hiend3d.com )
*
* Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __HQX_H_
#define __HQX_H_
#include <stdint.h>
#if defined( __GNUC__ )
#ifdef __MINGW32__
#define HQX_CALLCONV __stdcall
#else
#define HQX_CALLCONV
#endif
#else
#define HQX_CALLCONV
#endif
#if 0 //defined(_WIN32)
#ifdef DLL_EXPORT
#define HQX_API __declspec(dllexport)
#else
#define HQX_API __declspec(dllimport)
#endif
#else
#define HQX_API
#endif
HQX_API void HQX_CALLCONV hqxInit(void);
HQX_API void HQX_CALLCONV hq2x_32( uint32_t * src, uint32_t * dest, int width, int height );
HQX_API void HQX_CALLCONV hq3x_32( uint32_t * src, uint32_t * dest, int width, int height );
HQX_API void HQX_CALLCONV hq4x_32( uint32_t * src, uint32_t * dest, int width, int height );
HQX_API void HQX_CALLCONV hq2x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
HQX_API void HQX_CALLCONV hq3x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
HQX_API void HQX_CALLCONV hq4x_32_rb( uint32_t * src, uint32_t src_rowBytes, uint32_t * dest, uint32_t dest_rowBytes, int width, int height );
#endif

View file

@ -0,0 +1,38 @@
/*
* Copyright (C) 2010 Cameron Zemek ( grom@zeminvaders.net)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "hqx.h"
uint32_t *RGBtoYUV;
uint32_t YUV1, YUV2;
HQX_API void HQX_CALLCONV hqxInit(void)
{
/* Initalize RGB to YUV lookup table */
uint32_t c, r, g, b, y, u, v;
RGBtoYUV = new uint32_t[16777216];
for (c = 0; c < 16777215; c++) {
r = (c & 0xFF0000) >> 16;
g = (c & 0x00FF00) >> 8;
b = c & 0x0000FF;
y = (uint32_t)(0.299*r + 0.587*g + 0.114*b);
u = (uint32_t)(-0.169*r - 0.331*g + 0.5*b) + 128;
v = (uint32_t)(0.5*r - 0.419*g - 0.081*b) + 128;
RGBtoYUV[c] = (y << 16) + (u << 8) + v;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,234 @@
//hqnx filter library
//----------------------------------------------------------
//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
//Copyright (C) 2009 Benjamin Berkels
//Copyright (C) 2012-2014 Alexey Lysiuk
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#ifndef __HQNX_H__
#define __HQNX_H__
#ifdef _MSC_VER
#pragma warning(disable:4799)
#endif // _MSC_VER
#include "hqnx_asm_Image.h"
// IMPORTANT NOTE!
// The following is not a generic vectorized math class
// Each member function or overloaded operator does specific task to simplify client code
// To re-implement this class for different platform you need check very carefully
// the Intel C++ Intrinsic Reference at http://software.intel.com/file/18072/
#if defined _MSC_VER && defined _M_X64
// Implementation via SSE2 intrinsics
// MSVC doesn't support MMX intrinsics on x64
#include <emmintrin.h>
class hq_vec
{
public:
hq_vec(const int value)
: m_value(_mm_cvtsi32_si128(value))
{
}
static hq_vec load(const int source)
{
return _mm_unpacklo_epi8(_mm_cvtsi32_si128(source), _mm_cvtsi32_si128(0));
}
static hq_vec expand(const short source)
{
return _mm_set_epi16(source, source, source, source, source, source, source, source);
}
void store(unsigned char* const destination) const
{
*reinterpret_cast<int*>(destination) = _mm_cvtsi128_si32(_mm_packus_epi16(m_value, _mm_cvtsi32_si128(0)));
}
static void reset()
{
}
hq_vec& operator+=(const hq_vec& right)
{
m_value = _mm_add_epi16(m_value, right.m_value);
return *this;
}
hq_vec& operator*=(const hq_vec& right)
{
m_value = _mm_mullo_epi16(m_value, right.m_value);
return *this;
}
hq_vec& operator<<(const int count)
{
m_value = _mm_sll_epi16(m_value, _mm_cvtsi32_si128(count));
return *this;
}
hq_vec& operator>>(const int count)
{
m_value = _mm_srl_epi16(m_value, _mm_cvtsi32_si128(count));
return *this;
}
private:
__m128i m_value;
hq_vec(const __m128i value)
: m_value(value)
{
}
friend hq_vec operator- (const hq_vec&, const hq_vec&);
friend hq_vec operator* (const hq_vec&, const hq_vec&);
friend hq_vec operator| (const hq_vec&, const hq_vec&);
friend bool operator!=(const int, const hq_vec&);
};
inline hq_vec operator-(const hq_vec& left, const hq_vec& right)
{
return _mm_subs_epu8(left.m_value, right.m_value);
}
inline hq_vec operator*(const hq_vec& left, const hq_vec& right)
{
return _mm_mullo_epi16(left.m_value, right.m_value);
}
inline hq_vec operator|(const hq_vec& left, const hq_vec& right)
{
return _mm_or_si128(left.m_value, right.m_value);
}
inline bool operator!=(const int left, const hq_vec& right)
{
return left != _mm_cvtsi128_si32(right.m_value);
}
#else // _M_X64
// Implementation via MMX intrinsics
#include <mmintrin.h>
class hq_vec
{
public:
hq_vec(const int value)
: m_value(_mm_cvtsi32_si64(value))
{
}
static hq_vec load(const int source)
{
return _mm_unpacklo_pi8(_mm_cvtsi32_si64(source), _mm_cvtsi32_si64(0));
}
static hq_vec expand(const short source)
{
return _mm_set_pi16(source, source, source, source);
}
void store(unsigned char* const destination) const
{
*reinterpret_cast<int*>(destination) = _mm_cvtsi64_si32(_mm_packs_pu16(m_value, _mm_cvtsi32_si64(0)));
}
static void reset()
{
_mm_empty();
}
hq_vec& operator+=(const hq_vec& right)
{
m_value = _mm_add_pi16(m_value, right.m_value);
return *this;
}
hq_vec& operator*=(const hq_vec& right)
{
m_value = _mm_mullo_pi16(m_value, right.m_value);
return *this;
}
hq_vec& operator<<(const int count)
{
m_value = _mm_sll_pi16(m_value, _mm_cvtsi32_si64(count));
return *this;
}
hq_vec& operator>>(const int count)
{
m_value = _mm_srl_pi16(m_value, _mm_cvtsi32_si64(count));
return *this;
}
private:
__m64 m_value;
hq_vec(const __m64 value)
: m_value(value)
{
}
friend hq_vec operator- (const hq_vec&, const hq_vec&);
friend hq_vec operator* (const hq_vec&, const hq_vec&);
friend hq_vec operator| (const hq_vec&, const hq_vec&);
friend bool operator!=(const int, const hq_vec&);
};
inline hq_vec operator-(const hq_vec& left, const hq_vec& right)
{
return _mm_subs_pu8(left.m_value, right.m_value);
}
inline hq_vec operator*(const hq_vec& left, const hq_vec& right)
{
return _mm_mullo_pi16(left.m_value, right.m_value);
}
inline hq_vec operator|(const hq_vec& left, const hq_vec& right)
{
return _mm_or_si64(left.m_value, right.m_value);
}
inline bool operator!=(const int left, const hq_vec& right)
{
return left != _mm_cvtsi64_si32(right.m_value);
}
#endif // _MSC_VER && _M_X64
namespace HQnX_asm
{
void DLL hq2x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL );
void DLL hq3x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL );
void DLL hq4x_32( int * pIn, unsigned char * pOut, int Xres, int Yres, int BpL );
int DLL hq4x_32 ( CImage &ImageIn, CImage &ImageOut );
void DLL InitLUTs();
}
#endif //__HQNX_H__

View file

@ -0,0 +1,111 @@
//CImage class - loading and saving BMP and TGA files
//----------------------------------------------------------
//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include <stdlib.h>
#include <string.h>
#include "hqnx_asm_Image.h"
#ifndef _MSC_VER
#define _stricmp strcasecmp
#endif
namespace HQnX_asm
{
DLL CImage::CImage()
{
m_Xres = m_Yres = m_NumPixel = 0;
m_pBitmap = NULL;
}
DLL CImage::~CImage()
{
Destroy();
}
int DLL CImage::Init( int X, int Y, unsigned short BitPerPixel )
{
if (m_pBitmap != NULL)
free(m_pBitmap);
m_Xres = X;
m_Yres = Y;
m_BitPerPixel = BitPerPixel<=8 ? 8 : BitPerPixel<=16 ? 16 : BitPerPixel<=24 ? 24 : 32;
m_BytePerPixel = m_BitPerPixel >> 3;
m_NumPixel = m_Xres*m_Yres;
int size = m_NumPixel*((m_BitPerPixel+7)/8);
m_pBitmap=(unsigned char *)malloc(size);
return (m_pBitmap != NULL) ? 0 : 1;
}
int DLL CImage::SetImage(unsigned char *img, int width, int height, int bpp)
{
Init(width, height, bpp);
memcpy(m_pBitmap, img, m_NumPixel * m_BytePerPixel);
return 0;
}
int DLL CImage::Destroy()
{
if (m_pBitmap)
{
free(m_pBitmap);
m_pBitmap = NULL;
}
m_Xres = 0;
m_Yres = 0;
m_NumPixel = 0;
m_BitPerPixel = 0;
return 0;
}
int DLL CImage::Convert32To17( void )
{
int nRes = eConvUnknownFormat;
if ( m_BitPerPixel == 32 )
{
if ( m_pBitmap != NULL )
{
unsigned char * pTemp8 = m_pBitmap;
unsigned int * pTemp32 = (unsigned int *)m_pBitmap;
unsigned int a, r, g, b;
for ( int i=0; i<m_NumPixel; i++ )
{
b = (*(pTemp8++)) >> 3;
g = (*(pTemp8++)) >> 2;
r = (*(pTemp8++)) >> 3;
a = *(pTemp8++);
*pTemp32 = (r << 11) + (g << 5) + b + (a > 127 ? 0x10000 : 0);
pTemp32++;
}
}
else
nRes = eConvSourceMemory;
nRes = 0;
}
return nRes;
}
}

View file

@ -0,0 +1,77 @@
//CImage class - loading and saving BMP and TGA files
//----------------------------------------------------------
//Copyright (C) 2003 MaxSt ( maxst@hiend3d.com )
//
//This program is free software; you can redistribute it and/or
//modify it under the terms of the GNU Lesser General Public
//License as published by the Free Software Foundation; either
//version 2.1 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
//Lesser General Public License for more details.
//
//You should have received a copy of the GNU Lesser General Public
//License along with this program; if not, write to the Free Software
//Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//#ifdef WIN32
//#define DLL __declspec(dllexport)
//#else
#define DLL
//#endif
#include <stdio.h>
#pragma once
#ifdef _MSC_VER
#pragma warning(disable: 4103)
#endif // _MSC_VER
#pragma pack(1)
namespace HQnX_asm
{
typedef struct { unsigned char b, g, r; } _BGR;
typedef struct { unsigned char b, g, r, a; } _BGRA;
class CImage
{
public:
DLL CImage();
DLL ~CImage();
enum CImageErrors
{
eConvUnknownFormat = 10,
eConvSourceMemory = 11,
eConvDestMemory = 12,
};
public:
int DLL Init( int Xres, int Yres, unsigned short BitPerPixel );
int DLL SetImage(unsigned char *img, int width, int height, int bpp);
int DLL Destroy();
int DLL Convert32To17( void );
private:
public:
int m_Xres, m_Yres;
unsigned short m_BitPerPixel;
unsigned short m_BytePerPixel;
unsigned char * m_pBitmap;
_BGR m_Pal[256];
private:
int m_NumPixel;
FILE * f;
int m_nCount;
char m_cBuf[32768];
};
#pragma pack()
}

View file

@ -0,0 +1,511 @@
/*
** gl_hqresize.cpp
** Contains high quality upsampling functions.
** So far Scale2x/3x/4x as described in http://scale2x.sourceforge.net/
** are implemented.
**
**---------------------------------------------------------------------------
** Copyright 2008 Benjamin Berkels
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "c_cvars.h"
#include "hqnx/hqx.h"
#ifdef HAVE_MMX
#include "hqnx_asm/hqnx_asm.h"
#endif
#include "xbr/xbrz.h"
#include "xbr/xbrz_old.h"
#include "parallel_for.h"
#include "textures.h"
#include "texturemanager.h"
#include "printf.h"
EXTERN_CVAR(Int, gl_texture_hqresizemult)
CUSTOM_CVAR(Int, gl_texture_hqresizemode, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (self < 0 || self > 6)
self = 0;
if ((gl_texture_hqresizemult > 4) && (self < 4) && (self > 0))
gl_texture_hqresizemult = 4;
TexMan.FlushAll();
}
CUSTOM_CVAR(Int, gl_texture_hqresizemult, 1, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (self < 1 || self > 6)
self = 1;
if ((self > 4) && (gl_texture_hqresizemode < 4) && (gl_texture_hqresizemode > 0))
self = 4;
TexMan.FlushAll();
}
CUSTOM_CVAR(Int, gl_texture_hqresize_maxinputsize, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
if (self > 1024) self = 1024;
TexMan.FlushAll();
}
CUSTOM_CVAR(Int, gl_texture_hqresize_targets, 7, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL)
{
TexMan.FlushAll();
}
CVAR (Flag, gl_texture_hqresize_textures, gl_texture_hqresize_targets, 1);
CVAR (Flag, gl_texture_hqresize_sprites, gl_texture_hqresize_targets, 2);
CVAR (Flag, gl_texture_hqresize_fonts, gl_texture_hqresize_targets, 4);
CVAR(Bool, gl_texture_hqresize_multithread, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG);
CUSTOM_CVAR(Int, gl_texture_hqresize_mt_width, 16, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 2) self = 2;
if (self > 1024) self = 1024;
}
CUSTOM_CVAR(Int, gl_texture_hqresize_mt_height, 4, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
if (self < 2) self = 2;
if (self > 1024) self = 1024;
}
CVAR(Int, xbrz_colorformat, 0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
static void xbrzApplyOptions()
{
if (gl_texture_hqresizemult != 0 && (gl_texture_hqresizemode == 4 || gl_texture_hqresizemode == 5))
{
if (xbrz_colorformat == 0)
{
Printf("Changing xBRZ options requires a restart when buffered color format is used.\n"
"To avoid this at cost of scaling performance, set xbrz_colorformat CVAR to non-zero value.");
}
else
{
TexMan.FlushAll();
}
}
}
#define XBRZ_CVAR(NAME, VALUE) \
CUSTOM_CVAR(Float, xbrz_##NAME, VALUE, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_NOINITCALL) { xbrzApplyOptions(); }
XBRZ_CVAR(luminanceweight, 1.f)
XBRZ_CVAR(equalcolortolerance, 30.f)
XBRZ_CVAR(centerdirectionbias, 4.f)
XBRZ_CVAR(dominantdirectionthreshold, 3.6f)
XBRZ_CVAR(steepdirectionthreshold, 2.2f)
#undef XBRZ_CVAR
static void scale2x ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth, int inHeight )
{
const int width = 2* inWidth;
const int height = 2 * inHeight;
for ( int i = 0; i < inWidth; ++i )
{
const int iMinus = (i > 0) ? (i-1) : 0;
const int iPlus = (i < inWidth - 1 ) ? (i+1) : i;
for ( int j = 0; j < inHeight; ++j )
{
const int jMinus = (j > 0) ? (j-1) : 0;
const int jPlus = (j < inHeight - 1 ) ? (j+1) : j;
const uint32_t A = inputBuffer[ iMinus +inWidth*jMinus];
const uint32_t B = inputBuffer[ iMinus +inWidth*j ];
const uint32_t C = inputBuffer[ iMinus +inWidth*jPlus];
const uint32_t D = inputBuffer[ i +inWidth*jMinus];
const uint32_t E = inputBuffer[ i +inWidth*j ];
const uint32_t F = inputBuffer[ i +inWidth*jPlus];
const uint32_t G = inputBuffer[ iPlus +inWidth*jMinus];
const uint32_t H = inputBuffer[ iPlus +inWidth*j ];
const uint32_t I = inputBuffer[ iPlus +inWidth*jPlus];
if (B != H && D != F) {
outputBuffer[2*i + width*2*j ] = D == B ? D : E;
outputBuffer[2*i + width*(2*j+1)] = B == F ? F : E;
outputBuffer[2*i+1 + width*2*j ] = D == H ? D : E;
outputBuffer[2*i+1 + width*(2*j+1)] = H == F ? F : E;
} else {
outputBuffer[2*i + width*2*j ] = E;
outputBuffer[2*i + width*(2*j+1)] = E;
outputBuffer[2*i+1 + width*2*j ] = E;
outputBuffer[2*i+1 + width*(2*j+1)] = E;
}
}
}
}
static void scale3x ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth, int inHeight )
{
const int width = 3* inWidth;
const int height = 3 * inHeight;
for ( int i = 0; i < inWidth; ++i )
{
const int iMinus = (i > 0) ? (i-1) : 0;
const int iPlus = (i < inWidth - 1 ) ? (i+1) : i;
for ( int j = 0; j < inHeight; ++j )
{
const int jMinus = (j > 0) ? (j-1) : 0;
const int jPlus = (j < inHeight - 1 ) ? (j+1) : j;
const uint32_t A = inputBuffer[ iMinus +inWidth*jMinus];
const uint32_t B = inputBuffer[ iMinus +inWidth*j ];
const uint32_t C = inputBuffer[ iMinus +inWidth*jPlus];
const uint32_t D = inputBuffer[ i +inWidth*jMinus];
const uint32_t E = inputBuffer[ i +inWidth*j ];
const uint32_t F = inputBuffer[ i +inWidth*jPlus];
const uint32_t G = inputBuffer[ iPlus +inWidth*jMinus];
const uint32_t H = inputBuffer[ iPlus +inWidth*j ];
const uint32_t I = inputBuffer[ iPlus +inWidth*jPlus];
if (B != H && D != F) {
outputBuffer[3*i + width*3*j ] = D == B ? D : E;
outputBuffer[3*i + width*(3*j+1)] = (D == B && E != C) || (B == F && E != A) ? B : E;
outputBuffer[3*i + width*(3*j+2)] = B == F ? F : E;
outputBuffer[3*i+1 + width*3*j ] = (D == B && E != G) || (D == H && E != A) ? D : E;
outputBuffer[3*i+1 + width*(3*j+1)] = E;
outputBuffer[3*i+1 + width*(3*j+2)] = (B == F && E != I) || (H == F && E != C) ? F : E;
outputBuffer[3*i+2 + width*3*j ] = D == H ? D : E;
outputBuffer[3*i+2 + width*(3*j+1)] = (D == H && E != I) || (H == F && E != G) ? H : E;
outputBuffer[3*i+2 + width*(3*j+2)] = H == F ? F : E;
} else {
outputBuffer[3*i + width*3*j ] = E;
outputBuffer[3*i + width*(3*j+1)] = E;
outputBuffer[3*i + width*(3*j+2)] = E;
outputBuffer[3*i+1 + width*3*j ] = E;
outputBuffer[3*i+1 + width*(3*j+1)] = E;
outputBuffer[3*i+1 + width*(3*j+2)] = E;
outputBuffer[3*i+2 + width*3*j ] = E;
outputBuffer[3*i+2 + width*(3*j+1)] = E;
outputBuffer[3*i+2 + width*(3*j+2)] = E;
}
}
}
}
static void scale4x ( uint32_t* inputBuffer, uint32_t* outputBuffer, int inWidth, int inHeight )
{
int width = 2* inWidth;
int height = 2 * inHeight;
uint32_t * buffer2x = new uint32_t[width*height];
scale2x ( reinterpret_cast<uint32_t*> ( inputBuffer ), reinterpret_cast<uint32_t*> ( buffer2x ), inWidth, inHeight );
width *= 2;
height *= 2;
scale2x ( reinterpret_cast<uint32_t*> ( buffer2x ), reinterpret_cast<uint32_t*> ( outputBuffer ), 2*inWidth, 2*inHeight );
delete[] buffer2x;
}
static unsigned char *scaleNxHelper( void (*scaleNxFunction) ( uint32_t* , uint32_t* , int , int),
const int N,
unsigned char *inputBuffer,
const int inWidth,
const int inHeight,
int &outWidth,
int &outHeight )
{
outWidth = N * inWidth;
outHeight = N *inHeight;
unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4];
scaleNxFunction ( reinterpret_cast<uint32_t*> ( inputBuffer ), reinterpret_cast<uint32_t*> ( newBuffer ), inWidth, inHeight );
delete[] inputBuffer;
return newBuffer;
}
static unsigned char *normalNx(const int N,
unsigned char *inputBuffer,
const int inWidth,
const int inHeight,
int &outWidth,
int &outHeight )
{
outWidth = N * inWidth;
outHeight = N *inHeight;
unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4];
uint32_t *const inBuffer = reinterpret_cast<uint32_t *>(inputBuffer);
uint32_t *const outBuffer = reinterpret_cast<uint32_t *>(newBuffer);
for (int y = 0; y < inHeight; ++y)
{
const int inRowPos = inWidth * y;
const int outRowPos = outWidth * N * y;
for (int x = 0; x < inWidth; ++x)
{
std::fill_n(&outBuffer[outRowPos + N * x], N, inBuffer[inRowPos + x]);
}
for (int c = 1; c < N; ++c)
{
std::copy_n(&outBuffer[outRowPos], outWidth, &outBuffer[outRowPos + outWidth * c]);
}
}
delete[] inputBuffer;
return newBuffer;
}
#ifdef HAVE_MMX
static unsigned char *hqNxAsmHelper( void (*hqNxFunction) ( int*, unsigned char*, int, int, int ),
const int N,
unsigned char *inputBuffer,
const int inWidth,
const int inHeight,
int &outWidth,
int &outHeight )
{
outWidth = N * inWidth;
outHeight = N *inHeight;
static int initdone = false;
if (!initdone)
{
HQnX_asm::InitLUTs();
initdone = true;
}
HQnX_asm::CImage cImageIn;
cImageIn.SetImage(inputBuffer, inWidth, inHeight, 32);
cImageIn.Convert32To17();
unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4];
hqNxFunction( reinterpret_cast<int*>(cImageIn.m_pBitmap), newBuffer, cImageIn.m_Xres, cImageIn.m_Yres, outWidth*4 );
delete[] inputBuffer;
return newBuffer;
}
#endif
static unsigned char *hqNxHelper( void (HQX_CALLCONV *hqNxFunction) ( unsigned*, unsigned*, int, int ),
const int N,
unsigned char *inputBuffer,
const int inWidth,
const int inHeight,
int &outWidth,
int &outHeight )
{
static int initdone = false;
if (!initdone)
{
hqxInit();
initdone = true;
}
outWidth = N * inWidth;
outHeight = N *inHeight;
unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4];
hqNxFunction( reinterpret_cast<unsigned*>(inputBuffer), reinterpret_cast<unsigned*>(newBuffer), inWidth, inHeight );
delete[] inputBuffer;
return newBuffer;
}
template <typename ConfigType>
void xbrzSetupConfig(ConfigType& cfg);
template <>
void xbrzSetupConfig(xbrz::ScalerCfg& cfg)
{
cfg.luminanceWeight = xbrz_luminanceweight;
cfg.equalColorTolerance = xbrz_equalcolortolerance;
cfg.centerDirectionBias = xbrz_centerdirectionbias;
cfg.dominantDirectionThreshold = xbrz_dominantdirectionthreshold;
cfg.steepDirectionThreshold = xbrz_steepdirectionthreshold;
}
template <>
void xbrzSetupConfig(xbrz_old::ScalerCfg& cfg)
{
cfg.luminanceWeight_ = xbrz_luminanceweight;
cfg.equalColorTolerance_ = xbrz_equalcolortolerance;
cfg.dominantDirectionThreshold = xbrz_dominantdirectionthreshold;
cfg.steepDirectionThreshold = xbrz_steepdirectionthreshold;
}
template <typename ConfigType>
static unsigned char *xbrzHelper( void (*xbrzFunction) ( size_t, const uint32_t*, uint32_t*, int, int, xbrz::ColorFormat, const ConfigType&, int, int ),
const int N,
unsigned char *inputBuffer,
const int inWidth,
const int inHeight,
int &outWidth,
int &outHeight )
{
outWidth = N * inWidth;
outHeight = N *inHeight;
unsigned char * newBuffer = new unsigned char[outWidth*outHeight*4];
const int thresholdWidth = gl_texture_hqresize_mt_width;
const int thresholdHeight = gl_texture_hqresize_mt_height;
ConfigType cfg;
xbrzSetupConfig(cfg);
const xbrz::ColorFormat colorFormat = xbrz_colorformat == 0
? xbrz::ColorFormat::ARGB
: xbrz::ColorFormat::ARGB_UNBUFFERED;
if (gl_texture_hqresize_multithread
&& inWidth > thresholdWidth
&& inHeight > thresholdHeight)
{
parallel_for(inHeight, thresholdHeight, [=, &cfg](int sliceY)
{
xbrzFunction(N, reinterpret_cast<uint32_t*>(inputBuffer), reinterpret_cast<uint32_t*>(newBuffer),
inWidth, inHeight, colorFormat, cfg, sliceY, sliceY + thresholdHeight);
});
}
else
{
xbrzFunction(N, reinterpret_cast<uint32_t*>(inputBuffer), reinterpret_cast<uint32_t*>(newBuffer),
inWidth, inHeight, colorFormat, cfg, 0, std::numeric_limits<int>::max());
}
delete[] inputBuffer;
return newBuffer;
}
static void xbrzOldScale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, xbrz::ColorFormat colFmt, const xbrz_old::ScalerCfg& cfg, int yFirst, int yLast)
{
xbrz_old::scale(factor, src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
}
//===========================================================================
//
// [BB] Upsamples the texture in texbuffer.mBuffer, frees texbuffer.mBuffer and returns
// the upsampled buffer.
//
//===========================================================================
void FTexture::CreateUpsampledTextureBuffer(FTextureBuffer &texbuffer, bool hasAlpha, bool checkonly)
{
// [BB] Make sure that inWidth and inHeight denote the size of
// the returned buffer even if we don't upsample the input buffer.
int inWidth = texbuffer.mWidth;
int inHeight = texbuffer.mHeight;
// [BB] Don't resample if width * height of the input texture is bigger than gl_texture_hqresize_maxinputsize squared.
const int maxInputSize = gl_texture_hqresize_maxinputsize;
if (inWidth * inHeight > maxInputSize * maxInputSize)
return;
// [BB] Don't try to upsample textures based off FCanvasTexture. (This should never get here in the first place!)
if (bHasCanvas)
return;
// already scaled?
if (Scale.X >= 2 && Scale.Y >= 2)
return;
switch (UseType)
{
case ETextureType::Sprite:
case ETextureType::SkinSprite:
if (!(gl_texture_hqresize_targets & 2)) return;
break;
case ETextureType::FontChar:
if (!(gl_texture_hqresize_targets & 4)) return;
break;
default:
if (!(gl_texture_hqresize_targets & 1)) return;
break;
}
int type = gl_texture_hqresizemode;
int mult = gl_texture_hqresizemult;
#ifdef HAVE_MMX
// hqNx MMX does not preserve the alpha channel so fall back to C-version for such textures
if (hasAlpha && type == 3)
{
type = 2;
}
#endif
// These checks are to ensure consistency of the content ID.
if (mult < 2 || mult > 6 || type < 1 || type > 6) return;
if (type < 4 && mult > 4) mult = 4;
if (!checkonly)
{
if (type == 1)
{
if (mult == 2)
texbuffer.mBuffer = scaleNxHelper(&scale2x, 2, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (mult == 3)
texbuffer.mBuffer = scaleNxHelper(&scale3x, 3, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (mult == 4)
texbuffer.mBuffer = scaleNxHelper(&scale4x, 4, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else return;
}
else if (type == 2)
{
if (mult == 2)
texbuffer.mBuffer = hqNxHelper(&hq2x_32, 2, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (mult == 3)
texbuffer.mBuffer = hqNxHelper(&hq3x_32, 3, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (mult == 4)
texbuffer.mBuffer = hqNxHelper(&hq4x_32, 4, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else return;
}
#ifdef HAVE_MMX
else if (type == 3)
{
if (mult == 2)
texbuffer.mBuffer = hqNxAsmHelper(&HQnX_asm::hq2x_32, 2, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (mult == 3)
texbuffer.mBuffer = hqNxAsmHelper(&HQnX_asm::hq3x_32, 3, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (mult == 4)
texbuffer.mBuffer = hqNxAsmHelper(&HQnX_asm::hq4x_32, 4, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else return;
}
#endif
else if (type == 4)
texbuffer.mBuffer = xbrzHelper(xbrz::scale, mult, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (type == 5)
texbuffer.mBuffer = xbrzHelper(xbrzOldScale, mult, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else if (type == 6)
texbuffer.mBuffer = normalNx(mult, texbuffer.mBuffer, inWidth, inHeight, texbuffer.mWidth, texbuffer.mHeight);
else
return;
}
else
{
texbuffer.mWidth *= mult;
texbuffer.mHeight *= mult;
}
// Encode the scaling method in the content ID.
FContentIdBuilder contentId;
contentId.id = texbuffer.mContentId;
contentId.scaler = type;
contentId.scalefactor = mult;
texbuffer.mContentId = contentId.id;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,79 @@
// ****************************************************************************
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef XBRZ_HEADER_3847894708239054
#define XBRZ_HEADER_3847894708239054
#include <cstddef> //size_t
#include <cstdint> //uint32_t
#include <limits>
#include "xbrz_config.h"
namespace xbrz
{
/*
-------------------------------------------------------------------------
| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju |
-------------------------------------------------------------------------
using a modified approach of xBR:
http://board.byuu.org/viewtopic.php?f=10&t=2248
- new rule set preserving small image features
- highly optimized for performance
- support alpha channel
- support multithreading
- support 64-bit architectures
- support processing image slices
- support scaling up to 6xBRZ
*/
enum class ColorFormat //from high bits -> low bits, 8 bit per channel
{
RGB, //8 bit for each red, green, blue, upper 8 bits unused
ARGB, //including alpha channel, BGRA byte order on little-endian machines
ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
};
const int SCALE_FACTOR_MAX = 6;
/*
-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only
-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image:
Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis)
CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition
in the target image data if you are using multiple threads for processing each enlarged slice!
THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap!
- there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows
*/
void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
ColorFormat colFmt,
const ScalerCfg& cfg = ScalerCfg(),
int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
/**/ uint32_t* trg, int trgWidth, int trgHeight);
//parameter tuning
bool equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, double luminanceWeight, double equalColorTolerance);
}
#endif

View file

@ -0,0 +1,35 @@
// ****************************************************************************
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef XBRZ_CONFIG_HEADER_284578425345
#define XBRZ_CONFIG_HEADER_284578425345
//do NOT include any headers here! used by xBRZ_dll!!!
namespace xbrz
{
struct ScalerCfg
{
double luminanceWeight = 1;
double equalColorTolerance = 30;
double centerDirectionBias = 4;
double dominantDirectionThreshold = 3.6;
double steepDirectionThreshold = 2.2;
double newTestAttribute = 0; //unused; test new parameters
};
}
#endif

View file

@ -0,0 +1,40 @@
// ****************************************************************************
// * This file is part of the HqMAME project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the MAME library (or with modified *
// * versions of MAME that use the same license as MAME), and distribute *
// * linked combinations including the two. You must obey the GNU General *
// * Public License in all respects for all of the code used other than MAME. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef __XBRZ_CONFIG_OLD_HEADER_INCLUDED__
#define __XBRZ_CONFIG_OLD_HEADER_INCLUDED__
//do NOT include any headers here! used by xBRZ_dll!!!
namespace xbrz_old
{
struct ScalerCfg
{
ScalerCfg() :
luminanceWeight_(1),
equalColorTolerance_(30),
dominantDirectionThreshold(3.6),
steepDirectionThreshold(2.2),
newTestAttribute_(0) {}
double luminanceWeight_;
double equalColorTolerance_;
double dominantDirectionThreshold;
double steepDirectionThreshold;
double newTestAttribute_; //unused; test new parameters
};
}
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,87 @@
// ****************************************************************************
// * This file is part of the HqMAME project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the MAME library (or with modified *
// * versions of MAME that use the same license as MAME), and distribute *
// * linked combinations including the two. You must obey the GNU General *
// * Public License in all respects for all of the code used other than MAME. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef __XBRZ_OLD_HEADER_INCLUDED__
#define __XBRZ_OLD_HEADER_INCLUDED__
#include <cstddef> //size_t
#include <cstdint> //uint32_t
#include <limits>
#include "xbrz_config_old.h"
namespace xbrz_old
{
/*
-------------------------------------------------------------------------
| xBRZ: "Scale by rules" - high quality image upscaling filter by Zenju |
-------------------------------------------------------------------------
using a modified approach of xBR:
http://board.byuu.org/viewtopic.php?f=10&t=2248
- new rule set preserving small image features
- support multithreading
- support 64 bit architectures
- support processing image slices
*/
/*
-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only
-> color format: ARGB (BGRA byte order), alpha channel unused
-> support for source/target pitch in bytes!
-> if your emulator changes only a few image slices during each cycle (e.g. Dosbox) then there's no need to run xBRZ on the complete image:
Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis)
Caveat: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition
if you are using multiple threads for processing each enlarged slice!
THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap!
- there is a minor inefficiency for the first row of a slice, so avoid processing single rows only
*/
void scale(size_t factor, //valid range: 2 - 5
const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
const ScalerCfg& cfg = ScalerCfg(),
int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
uint32_t* trg, int trgWidth, int trgHeight);
enum SliceType
{
NN_SCALE_SLICE_SOURCE,
NN_SCALE_SLICE_TARGET,
};
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch, //pitch in bytes!
uint32_t* trg, int trgWidth, int trgHeight, int trgPitch,
SliceType st, int yFirst, int yLast);
//parameter tuning
bool equalColor(uint32_t col1, uint32_t col2, double luminanceWeight, double equalColorTolerance);
//########################### implementation ###########################
inline
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
uint32_t* trg, int trgWidth, int trgHeight)
{
nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
NN_SCALE_SLICE_TARGET, 0, trgHeight);
}
}
#endif

View file

@ -0,0 +1,270 @@
// ****************************************************************************
// * This file is part of the xBRZ project. It is distributed under *
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// * *
// * Additionally and as a special exception, the author gives permission *
// * to link the code of this program with the following libraries *
// * (or with modified versions that use the same licenses), and distribute *
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
// * You must obey the GNU General Public License in all respects for all of *
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
// * If you modify this file, you may extend this exception to your version *
// * of the file, but you are not obligated to do so. If you do not wish to *
// * do so, delete this exception statement from your version. *
// ****************************************************************************
#ifndef XBRZ_TOOLS_H_825480175091875
#define XBRZ_TOOLS_H_825480175091875
#include <cassert>
#include <algorithm>
#include <type_traits>
namespace xbrz
{
template <uint32_t N> inline
unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); }
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); }
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); }
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; }
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; }
inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); }
inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); }
inline uint16_t rgb888to555(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 9) | ((pix & 0x00F800) >> 6) | ((pix & 0x0000F8) >> 3)); }
inline uint16_t rgb888to565(uint32_t pix) { return static_cast<uint16_t>(((pix & 0xF80000) >> 8) | ((pix & 0x00FC00) >> 5) | ((pix & 0x0000F8) >> 3)); }
template <class Pix> inline
Pix* byteAdvance(Pix* ptr, int bytes)
{
using PixNonConst = typename std::remove_cv<Pix>::type;
using PixByte = typename std::conditional<std::is_same<Pix, PixNonConst>::value, char, const char>::type;
static_assert(std::is_integral<PixNonConst>::value, "Pix* is expected to be cast-able to char*");
return reinterpret_cast<Pix*>(reinterpret_cast<PixByte*>(ptr) + bytes);
}
//fill block with the given color
template <class Pix> inline
void fillBlock(Pix* trg, int pitch /*[bytes]*/, Pix col, int blockWidth, int blockHeight)
{
//for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
// std::fill(trg, trg + blockWidth, col);
for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
for (int x = 0; x < blockWidth; ++x)
trg[x] = col;
}
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
for (int y = yFirst; y < yLast; ++y)
{
const int ySrc = srcHeight * y / trgHeight;
const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch);
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
for (int x = 0; x < trgWidth; ++x)
{
const int xSrc = srcWidth * x / trgWidth;
trgLine[x] = pixCvrt(srcLine[xSrc]);
}
}
}
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
template <class PixSrc, class PixTrg, class PixConverter>
void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
{
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, srcHeight);
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
for (int y = yFirst; y < yLast; ++y)
{
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
//keep within for loop to support MT input slices!
const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
const int blockHeight = yTrgLast - yTrgFirst;
if (blockHeight > 0)
{
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
/**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch);
int xTrgFirst = 0;
for (int x = 0; x < srcWidth; ++x)
{
const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
const int blockWidth = xTrgLast - xTrgFirst;
if (blockWidth > 0)
{
xTrgFirst = xTrgLast;
const auto trgPix = pixCvrt(srcLine[x]);
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
trgLine += blockWidth;
}
}
}
}
}
template <class PixTrg, class PixConverter>
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch,
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/)
{
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format");
if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) ||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
{
assert(false);
return;
}
yFirst = std::max(yFirst, 0);
yLast = std::min(yLast, trgHeight);
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
const double scaleX = static_cast<double>(trgWidth ) / srcWidth;
const double scaleY = static_cast<double>(trgHeight) / srcHeight;
//perf notes:
// -> double-based calculation is (slightly) faster than float
// -> pre-calculation gives significant boost; std::vector<> memory allocation is negligible!
struct CoeffsX
{
int x1 = 0;
int x2 = 0;
double xx1 = 0;
double x2x = 0;
};
std::vector<CoeffsX> buf(trgWidth);
for (int x = 0; x < trgWidth; ++x)
{
const int x1 = srcWidth * x / trgWidth;
int x2 = x1 + 1;
if (x2 == srcWidth) --x2;
const double xx1 = x / scaleX - x1;
const double x2x = 1 - xx1;
CoeffsX& bx = buf[x];
bx.x1 = x1;
bx.x2 = x2;
bx.xx1 = xx1;
bx.x2x = x2x;
}
for (int y = yFirst; y < yLast; ++y)
{
const int y1 = srcHeight * y / trgHeight;
int y2 = y1 + 1;
if (y2 == srcHeight) --y2;
const double yy1 = y / scaleY - y1;
const double y2y = 1 - yy1;
const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch);
const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch);
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
for (int x = 0; x < trgWidth; ++x)
{
//perf: do NOT "simplify" the variable layout without measurement!
const int x1 = buf[x].x1;
const int x2 = buf[x].x2;
const double xx1 = buf[x].xx1;
const double x2x = buf[x].x2x;
const double x2xy2y = x2x * y2y;
const double xx1y2y = xx1 * y2y;
const double x2xyy1 = x2x * yy1;
const double xx1yy1 = xx1 * yy1;
auto interpolate = [=](int offset) -> double
{
/* https://en.wikipedia.org/wiki/Bilinear_interpolation
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
(c12(x2 - x) + c22(x - x1)) * (y - y1) */
const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff;
const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff;
const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff;
const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff;
return c11 * x2xy2y + c21 * xx1y2y +
c12 * x2xyy1 + c22 * xx1yy1;
};
const double bi = interpolate(0);
const double gi = interpolate(1);
const double ri = interpolate(2);
const double ai = interpolate(3);
const auto b = static_cast<uint32_t>(bi + 0.5);
const auto g = static_cast<uint32_t>(gi + 0.5);
const auto r = static_cast<uint32_t>(ri + 0.5);
const auto a = static_cast<uint32_t>(ai + 0.5);
const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b;
trgLine[x] = pixCvrt(trgPix);
}
}
}
}
#endif //XBRZ_TOOLS_H_825480175091875

View file

@ -0,0 +1,410 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2004-2016 Christoph Oelckers
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this program. If not, see http://www.gnu.org/licenses/
//
//--------------------------------------------------------------------------
//
#include "filesystem.h"
#include "m_png.h"
#include "c_dispatch.h"
#include "hw_ihwtexture.h"
#include "hw_material.h"
#include "texturemanager.h"
#include "c_cvars.h"
EXTERN_CVAR(Bool, gl_texture_usehires)
IHardwareTexture* CreateHardwareTexture();
//===========================================================================
//
// Constructor
//
//===========================================================================
FMaterial::FMaterial(FTexture * tx, bool expanded)
{
mShaderIndex = SHADER_Default;
sourcetex = tex = tx;
if (tx->UseType == ETextureType::SWCanvas && static_cast<FWrapperTexture*>(tx)->GetColorFormat() == 0)
{
mShaderIndex = SHADER_Paletted;
}
else if (tx->isWarped())
{
mShaderIndex = tx->isWarped(); // This picks SHADER_Warp1 or SHADER_Warp2
}
else if (tx->isHardwareCanvas())
{
if (tx->shaderindex >= FIRST_USER_SHADER)
{
mShaderIndex = tx->shaderindex;
}
// no brightmap for cameratexture
}
else
{
if (tx->Normal && tx->Specular)
{
for (auto &texture : { tx->Normal, tx->Specular })
{
mTextureLayers.Push(texture);
}
mShaderIndex = SHADER_Specular;
}
else if (tx->Normal && tx->Metallic && tx->Roughness && tx->AmbientOcclusion)
{
for (auto &texture : { tx->Normal, tx->Metallic, tx->Roughness, tx->AmbientOcclusion })
{
mTextureLayers.Push(texture);
}
mShaderIndex = SHADER_PBR;
}
tx->CreateDefaultBrightmap();
if (tx->Brightmap)
{
mTextureLayers.Push(tx->Brightmap);
if (mShaderIndex == SHADER_Specular)
mShaderIndex = SHADER_SpecularBrightmap;
else if (mShaderIndex == SHADER_PBR)
mShaderIndex = SHADER_PBRBrightmap;
else
mShaderIndex = SHADER_Brightmap;
}
if (tx->shaderindex >= FIRST_USER_SHADER)
{
const UserShaderDesc &usershader = usershaders[tx->shaderindex - FIRST_USER_SHADER];
if (usershader.shaderType == mShaderIndex) // Only apply user shader if it matches the expected material
{
for (auto &texture : tx->CustomShaderTextures)
{
if (texture == nullptr) continue;
mTextureLayers.Push(texture);
}
mShaderIndex = tx->shaderindex;
}
}
}
mWidth = tx->GetTexelWidth();
mHeight = tx->GetTexelHeight();
mLeftOffset = tx->GetLeftOffset(0); // These only get used by decals and decals should not use renderer-specific offsets.
mTopOffset = tx->GetTopOffset(0);
mRenderWidth = tx->GetScaledWidth();
mRenderHeight = tx->GetScaledHeight();
mSpriteU[0] = mSpriteV[0] = 0.f;
mSpriteU[1] = mSpriteV[1] = 1.f;
mExpanded = expanded;
if (expanded)
{
int oldwidth = mWidth;
int oldheight = mHeight;
mTrimResult = TrimBorders(trim); // get the trim size before adding the empty frame
mWidth += 2;
mHeight += 2;
mRenderWidth = mRenderWidth * mWidth / oldwidth;
mRenderHeight = mRenderHeight * mHeight / oldheight;
}
SetSpriteRect();
mTextureLayers.ShrinkToFit();
tx->Material[expanded] = this;
if (tx->isHardwareCanvas()) tx->bTranslucent = 0;
}
//===========================================================================
//
// Destructor
//
//===========================================================================
FMaterial::~FMaterial()
{
}
//===========================================================================
//
// Set the sprite rectangle
//
//===========================================================================
void FMaterial::SetSpriteRect()
{
auto leftOffset = tex->GetLeftOffsetHW();
auto topOffset = tex->GetTopOffsetHW();
float fxScale = (float)tex->Scale.X;
float fyScale = (float)tex->Scale.Y;
// mSpriteRect is for positioning the sprite in the scene.
mSpriteRect.left = -leftOffset / fxScale;
mSpriteRect.top = -topOffset / fyScale;
mSpriteRect.width = mWidth / fxScale;
mSpriteRect.height = mHeight / fyScale;
if (mExpanded)
{
// a little adjustment to make sprites look better with texture filtering:
// create a 1 pixel wide empty frame around them.
int oldwidth = mWidth - 2;
int oldheight = mHeight - 2;
leftOffset += 1;
topOffset += 1;
// Reposition the sprite with the frame considered
mSpriteRect.left = -leftOffset / fxScale;
mSpriteRect.top = -topOffset / fyScale;
mSpriteRect.width = mWidth / fxScale;
mSpriteRect.height = mHeight / fyScale;
if (mTrimResult)
{
mSpriteRect.left += trim[0] / fxScale;
mSpriteRect.top += trim[1] / fyScale;
mSpriteRect.width -= (oldwidth - trim[2]) / fxScale;
mSpriteRect.height -= (oldheight - trim[3]) / fyScale;
mSpriteU[0] = trim[0] / (float)mWidth;
mSpriteV[0] = trim[1] / (float)mHeight;
mSpriteU[1] -= (oldwidth - trim[0] - trim[2]) / (float)mWidth;
mSpriteV[1] -= (oldheight - trim[1] - trim[3]) / (float)mHeight;
}
}
}
//===========================================================================
//
// Finds empty space around the texture.
// Used for sprites that got placed into a huge empty frame.
//
//===========================================================================
bool FMaterial::TrimBorders(uint16_t *rect)
{
auto texbuffer = sourcetex->CreateTexBuffer(0);
int w = texbuffer.mWidth;
int h = texbuffer.mHeight;
auto Buffer = texbuffer.mBuffer;
if (texbuffer.mBuffer == nullptr)
{
return false;
}
if (w != mWidth || h != mHeight)
{
// external Hires replacements cannot be trimmed.
return false;
}
int size = w*h;
if (size == 1)
{
// nothing to be done here.
rect[0] = 0;
rect[1] = 0;
rect[2] = 1;
rect[3] = 1;
return true;
}
int first, last;
for(first = 0; first < size; first++)
{
if (Buffer[first*4+3] != 0) break;
}
if (first >= size)
{
// completely empty
rect[0] = 0;
rect[1] = 0;
rect[2] = 1;
rect[3] = 1;
return true;
}
for(last = size-1; last >= first; last--)
{
if (Buffer[last*4+3] != 0) break;
}
rect[1] = first / w;
rect[3] = 1 + last/w - rect[1];
rect[0] = 0;
rect[2] = w;
unsigned char *bufferoff = Buffer + (rect[1] * w * 4);
h = rect[3];
for(int x = 0; x < w; x++)
{
for(int y = 0; y < h; y++)
{
if (bufferoff[(x+y*w)*4+3] != 0) goto outl;
}
rect[0]++;
}
outl:
rect[2] -= rect[0];
for(int x = w-1; rect[2] > 1; x--)
{
for(int y = 0; y < h; y++)
{
if (bufferoff[(x+y*w)*4+3] != 0)
{
return true;
}
}
rect[2]--;
}
return true;
}
//===========================================================================
//
//
//
//===========================================================================
IHardwareTexture *FMaterial::GetLayer(int i, int translation, FTexture **pLayer)
{
FTexture *layer = i == 0 ? tex : mTextureLayers[i - 1];
if (pLayer) *pLayer = layer;
if (layer && layer->UseType!=ETextureType::Null)
{
IHardwareTexture *hwtex = layer->SystemTextures.GetHardwareTexture(translation, mExpanded);
if (hwtex == nullptr)
{
hwtex = CreateHardwareTexture();
layer->SystemTextures.AddHardwareTexture(translation, mExpanded, hwtex);
}
return hwtex;
}
return nullptr;
}
//===========================================================================
//
//
//
//===========================================================================
int FMaterial::GetAreas(FloatRect **pAreas) const
{
if (mShaderIndex == SHADER_Default) // texture splitting can only be done if there's no attached effects
{
*pAreas = sourcetex->areas;
return sourcetex->areacount;
}
else
{
return 0;
}
}
//==========================================================================
//
// Gets a texture from the texture manager and checks its validity for
// GL rendering.
//
//==========================================================================
FMaterial * FMaterial::ValidateTexture(FTexture * tex, bool expand, bool create)
{
again:
if (tex && tex->isValid())
{
if (tex->bNoExpand) expand = false;
FMaterial *hwtex = tex->Material[expand];
if (hwtex == NULL && create)
{
if (expand)
{
if (tex->isWarped() || tex->isHardwareCanvas() || tex->shaderindex >= FIRST_USER_SHADER || (tex->shaderindex >= SHADER_Specular && tex->shaderindex <= SHADER_PBRBrightmap))
{
tex->bNoExpand = true;
goto again;
}
if (tex->Brightmap != NULL &&
(tex->GetTexelWidth() != tex->Brightmap->GetTexelWidth() ||
tex->GetTexelHeight() != tex->Brightmap->GetTexelHeight())
)
{
// do not expand if the brightmap's size differs.
tex->bNoExpand = true;
goto again;
}
}
hwtex = new FMaterial(tex, expand);
}
return hwtex;
}
return NULL;
}
FMaterial * FMaterial::ValidateTexture(FTextureID no, bool expand, bool translate, bool create)
{
return ValidateTexture(TexMan.GetTexture(no, translate), expand, create);
}
void DeleteMaterial(FMaterial* mat)
{
delete mat;
}
//-----------------------------------------------------------------------------
//
// Make sprite offset adjustment user-configurable per renderer.
//
//-----------------------------------------------------------------------------
extern int r_spriteadjustSW, r_spriteadjustHW;
CUSTOM_CVAR(Int, r_spriteadjust, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
{
r_spriteadjustHW = !!(self & 2);
r_spriteadjustSW = !!(self & 1);
for (int i = 0; i < TexMan.NumTextures(); i++)
{
auto tex = TexMan.GetTexture(FSetTextureID(i));
if (tex->GetTexelLeftOffset(0) != tex->GetTexelLeftOffset(1) || tex->GetTexelTopOffset(0) != tex->GetTexelTopOffset(1))
{
for (int i = 0; i < 2; i++)
{
auto mat = tex->GetMaterial(i);
if (mat != nullptr) mat->SetSpriteRect();
}
}
}
}

View file

@ -0,0 +1,140 @@
#ifndef __GL_MATERIAL_H
#define __GL_MATERIAL_H
#include "m_fixed.h"
#include "textures.h"
struct FRemapTable;
class IHardwareTexture;
enum
{
CLAMP_NONE = 0,
CLAMP_X = 1,
CLAMP_Y = 2,
CLAMP_XY = 3,
CLAMP_XY_NOMIP = 4,
CLAMP_NOFILTER = 5,
CLAMP_CAMTEX = 6,
};
//===========================================================================
//
// this is the material class for OpenGL.
//
//===========================================================================
class FMaterial
{
TArray<FTexture*> mTextureLayers;
int mShaderIndex;
short mLeftOffset;
short mTopOffset;
short mWidth;
short mHeight;
short mRenderWidth;
short mRenderHeight;
bool mExpanded;
bool mTrimResult;
uint16_t trim[4];
float mSpriteU[2], mSpriteV[2];
FloatRect mSpriteRect;
bool TrimBorders(uint16_t *rect);
public:
FTexture *tex;
FTexture *sourcetex; // in case of redirection this is different from tex.
FMaterial(FTexture *tex, bool forceexpand);
~FMaterial();
void SetSpriteRect();
int GetShaderIndex() const { return mShaderIndex; }
void AddTextureLayer(FTexture *tex)
{
ValidateTexture(tex, false);
mTextureLayers.Push(tex);
}
bool isMasked() const
{
return sourcetex->bMasked;
}
bool isExpanded() const
{
return mExpanded;
}
int GetLayers() const
{
return mTextureLayers.Size() + 1;
}
bool hasCanvas()
{
return tex->isHardwareCanvas();
}
IHardwareTexture *GetLayer(int i, int translation, FTexture **pLayer = nullptr);
// Patch drawing utilities
void GetSpriteRect(FloatRect * r) const
{
*r = mSpriteRect;
}
// This is scaled size in integer units as needed by walls and flats
int TextureHeight() const { return mRenderHeight; }
int TextureWidth() const { return mRenderWidth; }
int GetAreas(FloatRect **pAreas) const;
int GetWidth() const
{
return mWidth;
}
int GetHeight() const
{
return mHeight;
}
int GetLeftOffset() const
{
return mLeftOffset;
}
int GetTopOffset() const
{
return mTopOffset;
}
// Get right/bottom UV coordinates for patch drawing
float GetUL() const { return 0; }
float GetVT() const { return 0; }
float GetUR() const { return 1; }
float GetVB() const { return 1; }
float GetU(float upix) const { return upix/(float)mWidth; }
float GetV(float vpix) const { return vpix/(float)mHeight; }
float GetSpriteUL() const { return mSpriteU[0]; }
float GetSpriteVT() const { return mSpriteV[0]; }
float GetSpriteUR() const { return mSpriteU[1]; }
float GetSpriteVB() const { return mSpriteV[1]; }
static FMaterial *ValidateTexture(FTexture * tex, bool expand, bool create = true);
static FMaterial *ValidateTexture(FTextureID no, bool expand, bool trans, bool create = true);
const TArray<FTexture*> &GetLayerArray() const
{
return mTextureLayers;
}
};
#endif

View file

@ -1,9 +1,7 @@
#pragma once
#include <stdint.h>
#include "zstring.h"
#include "tarray.h"
#include "textures.h"
#include "bitmap.h"
#include "memarena.h"
@ -169,4 +167,6 @@ protected:
};
FTexture* CreateImageTexture(FImageSource* img, const char *name = nullptr) noexcept;
class FTexture;
FTexture* CreateImageTexture(FImageSource* img, const char *name = nullptr) noexcept;

View file

@ -0,0 +1,927 @@
/*
** multipatchtexturebuilder.cpp
** Texture class for standard Doom multipatch textures
**
**---------------------------------------------------------------------------
** Copyright 2004-2006 Randy Heit
** Copyright 2006-2018 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/
#include <ctype.h>
#include "files.h"
#include "printf.h"
#include "filesystem.h"
#include "engineerrors.h"
#include "bitmap.h"
#include "textures.h"
#include "image.h"
#include "formats/multipatchtexture.h"
#include "texturemanager.h"
// On the Alpha, accessing the shorts directly if they aren't aligned on a
// 4-byte boundary causes unaligned access warnings. Why it does this at
// all and only while initing the textures is beyond me.
#ifdef ALPHA
#define SAFESHORT(s) ((short)(((uint8_t *)&(s))[0] + ((uint8_t *)&(s))[1] * 256))
#else
#define SAFESHORT(s) LittleShort(s)
#endif
//--------------------------------------------------------------------------
//
// Data structures for the TEXTUREx lumps
//
//--------------------------------------------------------------------------
//
// Each texture is composed of one or more patches, with patches being lumps
// stored in the WAD. The lumps are referenced by number, and patched into
// the rectangular texture space using origin and possibly other attributes.
//
struct mappatch_t
{
int16_t originx;
int16_t originy;
int16_t patch;
int16_t stepdir;
int16_t colormap;
};
//
// A wall texture is a list of patches which are to be combined in a
// predefined order.
//
struct maptexture_t
{
uint8_t name[8];
uint16_t Flags; // [RH] Was unused
uint8_t ScaleX; // [RH] Scaling (8 is normal)
uint8_t ScaleY; // [RH] Same as above
int16_t width;
int16_t height;
uint8_t columndirectory[4]; // OBSOLETE
int16_t patchcount;
mappatch_t patches[1];
};
#define MAPTEXF_WORLDPANNING 0x8000
// Strife uses versions of the above structures that remove all unused fields
struct strifemappatch_t
{
int16_t originx;
int16_t originy;
int16_t patch;
};
//
// A wall texture is a list of patches which are to be combined in a
// predefined order.
//
struct strifemaptexture_t
{
uint8_t name[8];
uint16_t Flags; // [RH] Was unused
uint8_t ScaleX; // [RH] Scaling (8 is normal)
uint8_t ScaleY; // [RH] Same as above
int16_t width;
int16_t height;
int16_t patchcount;
strifemappatch_t patches[1];
};
//==========================================================================
//
// In-memory representation of a single PNAMES lump entry
//
//==========================================================================
struct FPatchLookup
{
FString Name;
};
void FMultipatchTextureBuilder::MakeTexture(BuildInfo &buildinfo, ETextureType usetype)
{
FImageTexture *tex = new FImageTexture(nullptr, buildinfo.Name);
tex->SetUseType(usetype);
tex->bMultiPatch = true;
tex->Width = buildinfo.Width;
tex->Height = buildinfo.Height;
tex->_LeftOffset[0] = buildinfo.LeftOffset[0];
tex->_LeftOffset[1] = buildinfo.LeftOffset[1];
tex->_TopOffset[0] = buildinfo.TopOffset[0];
tex->_TopOffset[1] = buildinfo.TopOffset[1];
tex->Scale = buildinfo.Scale;
tex->bMasked = true; // we do not really know yet.
tex->bTranslucent = -1;
tex->bWorldPanning = buildinfo.bWorldPanning;
tex->bNoDecals = buildinfo.bNoDecals;
tex->SourceLump = buildinfo.DefinitionLump;
buildinfo.tex = tex;
TexMan.AddTexture(tex);
}
//==========================================================================
//
// The reader for TEXTUREx
//
//==========================================================================
//==========================================================================
//
// FMultiPatchTexture :: FMultiPatchTexture
//
//==========================================================================
void FMultipatchTextureBuilder::BuildTexture(const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum, ETextureType usetype)
{
BuildInfo &buildinfo = BuiltTextures[BuiltTextures.Reserve(1)];
union
{
const maptexture_t *d;
const strifemaptexture_t *s;
}
mtexture;
union
{
const mappatch_t *d;
const strifemappatch_t *s;
}
mpatch;
int i;
mtexture.d = (const maptexture_t *)texdef;
int NumParts;
if (strife)
{
NumParts = SAFESHORT(mtexture.s->patchcount);
}
else
{
NumParts = SAFESHORT(mtexture.d->patchcount);
}
if (NumParts < 0)
{
I_Error("Bad texture directory");
}
buildinfo.Parts.Resize(NumParts);
buildinfo.Inits.Resize(NumParts);
buildinfo.Width = SAFESHORT(mtexture.d->width);
buildinfo.Height = SAFESHORT(mtexture.d->height);
buildinfo.Name = (char *)mtexture.d->name;
buildinfo.Scale.X = mtexture.d->ScaleX ? mtexture.d->ScaleX / 8. : 1.;
buildinfo.Scale.Y = mtexture.d->ScaleY ? mtexture.d->ScaleY / 8. : 1.;
if (mtexture.d->Flags & MAPTEXF_WORLDPANNING)
{
buildinfo.bWorldPanning = true;
}
if (strife)
{
mpatch.s = &mtexture.s->patches[0];
}
else
{
mpatch.d = &mtexture.d->patches[0];
}
for (i = 0; i < NumParts; ++i)
{
if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum))
{
I_Error("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.",
maxpatchnum, buildinfo.Name.GetChars(), LittleShort(mpatch.d->patch) + 1);
}
buildinfo.Parts[i].OriginX = LittleShort(mpatch.d->originx);
buildinfo.Parts[i].OriginY = LittleShort(mpatch.d->originy);
buildinfo.Parts[i].Image = nullptr;
buildinfo.Inits[i].TexName = patchlookup[LittleShort(mpatch.d->patch)].Name;
buildinfo.Inits[i].UseType = ETextureType::WallPatch;
if (strife)
mpatch.s++;
else
mpatch.d++;
}
if (NumParts == 0)
{
Printf("Texture %s is left without any patches\n", buildinfo.Name.GetChars());
}
// Insert the incomplete texture right here so that it's in the correct place.
buildinfo.DefinitionLump = deflumpnum;
MakeTexture(buildinfo, usetype);
}
//==========================================================================
//
// FTextureManager :: AddTexturesLump
//
//==========================================================================
void FMultipatchTextureBuilder::AddTexturesLump(const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1)
{
TArray<FPatchLookup> patchlookup;
int i;
uint32_t numpatches;
if (firstdup == 0)
{
firstdup = (int)TexMan.NumTextures();
}
{
auto pnames = fileSystem.OpenFileReader(patcheslump);
numpatches = pnames.ReadUInt32();
// Check whether the amount of names reported is correct.
if ((signed)numpatches < 0)
{
Printf("Corrupt PNAMES lump found (negative amount of entries reported)\n");
return;
}
// Check whether the amount of names reported is correct.
int lumplength = fileSystem.FileLength(patcheslump);
if (numpatches > uint32_t((lumplength - 4) / 8))
{
Printf("PNAMES lump is shorter than required (%u entries reported but only %d bytes (%d entries) long\n",
numpatches, lumplength, (lumplength - 4) / 8);
// Truncate but continue reading. Who knows how many such lumps exist?
numpatches = (lumplength - 4) / 8;
}
// Catalog the patches these textures use so we know which
// textures they represent.
patchlookup.Resize(numpatches);
for (uint32_t i = 0; i < numpatches; ++i)
{
char pname[9];
pnames.Read(pname, 8);
pname[8] = '\0';
patchlookup[i].Name = pname;
}
}
bool isStrife = false;
const uint32_t *maptex, *directory;
uint32_t maxoff;
int numtextures;
uint32_t offset = 0; // Shut up, GCC!
maptex = (const uint32_t *)lumpdata;
numtextures = LittleLong(*maptex);
maxoff = lumpsize;
if (maxoff < uint32_t(numtextures + 1) * 4)
{
Printf("Texture directory is too short\n");
return;
}
// Scan the texture lump to decide if it contains Doom or Strife textures
for (i = 0, directory = maptex + 1; i < numtextures; ++i)
{
offset = LittleLong(directory[i]);
if (offset > maxoff)
{
Printf("Bad texture directory\n");
return;
}
maptexture_t *tex = (maptexture_t *)((uint8_t *)maptex + offset);
// There is bizzarely a Doom editing tool that writes to the
// first two elements of columndirectory, so I can't check those.
if (SAFESHORT(tex->patchcount) < 0 ||
tex->columndirectory[2] != 0 ||
tex->columndirectory[3] != 0)
{
isStrife = true;
break;
}
}
// Textures defined earlier in the lump take precedence over those defined later,
// but later TEXTUREx lumps take precedence over earlier ones.
for (i = 1, directory = maptex; i <= numtextures; ++i)
{
if (i == 1 && texture1)
{
// The very first texture is just a dummy. Copy its dimensions to texture 0.
// It still needs to be created in case someone uses it by name.
offset = LittleLong(directory[1]);
const maptexture_t *tex = (const maptexture_t *)((const uint8_t *)maptex + offset);
FTexture *tex0 = TexMan.ByIndex(0);
tex0->SetSize(SAFESHORT(tex->width), SAFESHORT(tex->height));
}
offset = LittleLong(directory[i]);
if (offset > maxoff)
{
Printf("Bad texture directory\n");
return;
}
// If this texture was defined already in this lump, skip it
// This could cause problems with animations that use the same name for intermediate
// textures. Should I be worried?
int j;
for (j = (int)TexMan.NumTextures() - 1; j >= firstdup; --j)
{
if (strnicmp(TexMan.ByIndex(j)->GetName(), (const char *)maptex + offset, 8) == 0)
break;
}
if (j + 1 == firstdup)
{
BuildTexture((const uint8_t *)maptex + offset, patchlookup.Data(), numpatches, isStrife, deflumpnum, (i == 1 && texture1) ? ETextureType::FirstDefined : ETextureType::Wall);
progressFunc();
}
}
}
//==========================================================================
//
// FTextureManager :: AddTexturesLumps
//
//==========================================================================
void FMultipatchTextureBuilder::AddTexturesLumps(int lump1, int lump2, int patcheslump)
{
int firstdup = (int)TexMan.NumTextures();
if (lump1 >= 0)
{
FileData texdir = fileSystem.ReadFile(lump1);
AddTexturesLump(texdir.GetMem(), fileSystem.FileLength(lump1), lump1, patcheslump, firstdup, true);
}
if (lump2 >= 0)
{
FileData texdir = fileSystem.ReadFile(lump2);
AddTexturesLump(texdir.GetMem(), fileSystem.FileLength(lump2), lump2, patcheslump, firstdup, false);
}
}
//==========================================================================
//
// THe reader for the textual format
//
//==========================================================================
//==========================================================================
//
//
//
//==========================================================================
void FMultipatchTextureBuilder::ParsePatch(FScanner &sc, BuildInfo &info, TexPart & part, TexInit &init)
{
FString patchname;
int Mirror = 0;
sc.MustGetString();
init.TexName = sc.String;
sc.MustGetStringName(",");
sc.MustGetNumber();
part.OriginX = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
part.OriginY = sc.Number;
if (sc.CheckString("{"))
{
while (!sc.CheckString("}"))
{
sc.MustGetString();
if (sc.Compare("flipx"))
{
Mirror |= 1;
}
else if (sc.Compare("flipy"))
{
Mirror |= 2;
}
else if (sc.Compare("rotate"))
{
sc.MustGetNumber();
sc.Number = (((sc.Number + 90) % 360) - 90);
if (sc.Number != 0 && sc.Number != 90 && sc.Number != 180 && sc.Number != -90)
{
sc.ScriptError("Rotation must be a multiple of 90 degrees.");
}
part.Rotate = (sc.Number / 90) & 3;
}
else if (sc.Compare("Translation"))
{
int match;
info.bComplex = true;
if (part.Translation != NULL) delete part.Translation;
part.Translation = NULL;
part.Blend = 0;
static const char *maps[] = { "gold", "red", "green", "blue", "inverse", NULL };
sc.MustGetString();
match = sc.MatchString(maps);
if (match >= 0)
{
part.Blend = BLEND_SPECIALCOLORMAP1 + match + 1;
}
else if (sc.Compare("ICE"))
{
part.Blend = BLEND_ICEMAP;
}
else if (sc.Compare("DESATURATE"))
{
sc.MustGetStringName(",");
sc.MustGetNumber();
part.Blend = BLEND_DESATURATE1 + clamp(sc.Number - 1, 0, 30);
}
else
{
sc.UnGet();
part.Translation = new FRemapTable;
part.Translation->MakeIdentity();
do
{
sc.MustGetString();
try
{
part.Translation->AddToTranslation(sc.String);
}
catch (CRecoverableError &err)
{
sc.ScriptMessage("Error in translation '%s':\n" TEXTCOLOR_YELLOW "%s\n", sc.String, err.GetMessage());
}
} while (sc.CheckString(","));
}
}
else if (sc.Compare("Colormap"))
{
float r1, g1, b1;
float r2, g2, b2;
sc.MustGetFloat();
r1 = (float)sc.Float;
sc.MustGetStringName(",");
sc.MustGetFloat();
g1 = (float)sc.Float;
sc.MustGetStringName(",");
sc.MustGetFloat();
b1 = (float)sc.Float;
if (!sc.CheckString(","))
{
part.Blend = AddSpecialColormap(GPalette.BaseColors, 0, 0, 0, r1, g1, b1);
}
else
{
sc.MustGetFloat();
r2 = (float)sc.Float;
sc.MustGetStringName(",");
sc.MustGetFloat();
g2 = (float)sc.Float;
sc.MustGetStringName(",");
sc.MustGetFloat();
b2 = (float)sc.Float;
part.Blend = AddSpecialColormap(GPalette.BaseColors, r1, g1, b1, r2, g2, b2);
}
}
else if (sc.Compare("Blend"))
{
info.bComplex = true;
if (part.Translation != NULL) delete part.Translation;
part.Translation = NULL;
part.Blend = 0;
if (!sc.CheckNumber())
{
sc.MustGetString();
part.Blend = V_GetColor(NULL, sc);
}
else
{
int r, g, b;
r = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
g = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
b = sc.Number;
//sc.MustGetStringName(","); This was never supposed to be here.
part.Blend = MAKERGB(r, g, b);
}
// Blend.a may never be 0 here.
if (sc.CheckString(","))
{
sc.MustGetFloat();
if (sc.Float > 0.f)
part.Blend.a = clamp<int>(int(sc.Float * 255), 1, 254);
else
part.Blend = 0;
}
else part.Blend.a = 255;
}
else if (sc.Compare("alpha"))
{
sc.MustGetFloat();
part.Alpha = clamp<blend_t>(int(sc.Float * BLENDUNIT), 0, BLENDUNIT);
// bComplex is not set because it is only needed when the style is not OP_COPY.
}
else if (sc.Compare("style"))
{
static const char *styles[] = { "copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL };
sc.MustGetString();
part.op = sc.MustMatchString(styles);
info.bComplex |= (part.op != OP_COPY);
}
else if (sc.Compare("useoffsets"))
{
init.UseOffsets = true;
}
}
}
if (Mirror & 2)
{
part.Rotate = (part.Rotate + 2) & 3;
Mirror ^= 1;
}
if (Mirror & 1)
{
part.Rotate |= 4;
}
}
//==========================================================================
//
// Constructor for text based multipatch definitions
//
//==========================================================================
void FMultipatchTextureBuilder::ParseTexture(FScanner &sc, ETextureType UseType)
{
BuildInfo &buildinfo = BuiltTextures[BuiltTextures.Reserve(1)];
bool bSilent = false;
buildinfo.textual = true;
sc.SetCMode(true);
sc.MustGetString();
const char *textureName = nullptr;
if (sc.Compare("optional"))
{
bSilent = true;
sc.MustGetString();
if (sc.Compare(","))
{
// this is not right. Apparently a texture named 'optional' is being defined right now...
sc.UnGet();
textureName = "optional";
bSilent = false;
}
}
buildinfo.Name = !textureName ? sc.String : textureName;
buildinfo.Name.ToUpper();
sc.MustGetStringName(",");
sc.MustGetNumber();
buildinfo.Width = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
buildinfo.Height = sc.Number;
bool offset2set = false;
if (sc.CheckString("{"))
{
while (!sc.CheckString("}"))
{
sc.MustGetString();
if (sc.Compare("XScale"))
{
sc.MustGetFloat();
buildinfo.Scale.X = sc.Float;
if (buildinfo.Scale.X == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", buildinfo.Name.GetChars());
}
else if (sc.Compare("YScale"))
{
sc.MustGetFloat();
buildinfo.Scale.Y = sc.Float;
if (buildinfo.Scale.Y == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", buildinfo.Name.GetChars());
}
else if (sc.Compare("WorldPanning"))
{
buildinfo.bWorldPanning = true;
}
else if (sc.Compare("NullTexture"))
{
UseType = ETextureType::Null;
}
else if (sc.Compare("NoDecals"))
{
buildinfo.bNoDecals = true;
}
else if (sc.Compare("Patch"))
{
TexPart part;
TexInit init;
ParsePatch(sc, buildinfo, part, init);
if (init.TexName.IsNotEmpty())
{
buildinfo.Parts.Push(part);
init.UseType = ETextureType::WallPatch;
init.Silent = bSilent;
init.HasLine = true;
init.sc = sc;
buildinfo.Inits.Push(init);
}
part.Image = nullptr;
part.Translation = nullptr;
}
else if (sc.Compare("Sprite"))
{
TexPart part;
TexInit init;
ParsePatch(sc, buildinfo, part, init);
if (init.TexName.IsNotEmpty())
{
buildinfo.Parts.Push(part);
init.UseType = ETextureType::Sprite;
init.Silent = bSilent;
init.HasLine = true;
init.sc = sc;
buildinfo.Inits.Push(init);
}
part.Image = nullptr;
part.Translation = nullptr;
}
else if (sc.Compare("Graphic"))
{
TexPart part;
TexInit init;
ParsePatch(sc, buildinfo, part, init);
if (init.TexName.IsNotEmpty())
{
buildinfo.Parts.Push(part);
init.UseType = ETextureType::MiscPatch;
init.Silent = bSilent;
init.HasLine = true;
init.sc = sc;
buildinfo.Inits.Push(init);
}
part.Image = nullptr;
part.Translation = nullptr;
}
else if (sc.Compare("Offset"))
{
sc.MustGetNumber();
buildinfo.LeftOffset[0] = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
buildinfo.TopOffset[0] = sc.Number;
if (!offset2set)
{
buildinfo.LeftOffset[1] = buildinfo.LeftOffset[0];
buildinfo.TopOffset[1] = buildinfo.TopOffset[0];
}
}
else if (sc.Compare("Offset2"))
{
sc.MustGetNumber();
buildinfo.LeftOffset[1] = sc.Number;
sc.MustGetStringName(",");
sc.MustGetNumber();
buildinfo.TopOffset[1] = sc.Number;
offset2set = true;
}
else
{
sc.ScriptError("Unknown texture property '%s'", sc.String);
}
}
}
if (buildinfo.Width <= 0 || buildinfo.Height <= 0)
{
UseType = ETextureType::Null;
Printf("Texture %s has invalid dimensions (%d, %d)\n", buildinfo.Name.GetChars(), buildinfo.Width, buildinfo.Height);
buildinfo.Width = buildinfo.Height = 1;
}
MakeTexture(buildinfo, UseType);
sc.SetCMode(false);
}
//==========================================================================
//
//
//
//==========================================================================
void FMultipatchTextureBuilder::ResolvePatches(BuildInfo &buildinfo)
{
for (unsigned i = 0; i < buildinfo.Inits.Size(); i++)
{
FTextureID texno = TexMan.CheckForTexture(buildinfo.Inits[i].TexName, buildinfo.Inits[i].UseType);
if (texno == buildinfo.tex->id) // we found ourselves. Try looking for another one with the same name which is not a multipatch texture itself.
{
TArray<FTextureID> list;
TexMan.ListTextures(buildinfo.Inits[i].TexName, list, true);
for (int i = list.Size() - 1; i >= 0; i--)
{
if (list[i] != buildinfo.tex->id && !TexMan.GetTexture(list[i])->bMultiPatch)
{
texno = list[i];
break;
}
}
if (texno == buildinfo.tex->id)
{
if (buildinfo.Inits[i].HasLine) buildinfo.Inits[i].sc.Message(MSG_WARNING, "Texture '%s' references itself as patch\n", buildinfo.Inits[i].TexName.GetChars());
else Printf(TEXTCOLOR_YELLOW "Texture '%s' references itself as patch\n", buildinfo.Inits[i].TexName.GetChars());
continue;
}
else
{
// If it could be resolved, just print a developer warning.
DPrintf(DMSG_WARNING, "Resolved self-referencing texture by picking an older entry for %s\n", buildinfo.Inits[i].TexName.GetChars());
}
}
if (!texno.isValid())
{
if (!buildinfo.Inits[i].Silent)
{
if (buildinfo.Inits[i].HasLine) buildinfo.Inits[i].sc.Message(MSG_WARNING, "Unknown patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars());
else Printf(TEXTCOLOR_YELLOW "Unknown patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars());
}
}
else
{
FTexture *tex = TexMan.GetTexture(texno);
if (tex != nullptr && tex->isValid())
{
//We cannot set the image source yet. First all textures need to be resolved.
buildinfo.Inits[i].Texture = tex;
buildinfo.tex->bComplex |= tex->bComplex;
buildinfo.bComplex |= tex->bComplex;
if (buildinfo.Inits[i].UseOffsets)
{
buildinfo.Parts[i].OriginX -= tex->GetLeftOffset(0);
buildinfo.Parts[i].OriginY -= tex->GetTopOffset(0);
}
}
else
{
// The patch is bogus. Remove it.
if (buildinfo.Inits[i].HasLine) buildinfo.Inits[i].sc.Message(MSG_WARNING, "Invalid patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars());
else Printf(TEXTCOLOR_YELLOW "Invalid patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars());
i--;
}
}
}
for (unsigned i = 0; i < buildinfo.Inits.Size(); i++)
{
if (buildinfo.Inits[i].Texture == nullptr)
{
buildinfo.Inits.Delete(i);
buildinfo.Parts.Delete(i);
i--;
}
}
checkForHacks(buildinfo);
}
void FMultipatchTextureBuilder::ResolveAllPatches()
{
for (auto &bi : BuiltTextures)
{
ResolvePatches(bi);
}
// Now try to resolve the images. We only can do this at the end when all multipatch textures are set up.
int i = 0;
// reverse the list so that the Delete operation in the loop below deletes at the end.
// For normal sized lists this is of no real concern, but Total Chaos has over 250000 textures where this becomes a performance issue.
for (unsigned i = 0; i < BuiltTextures.Size() / 2; i++)
{
// std::swap is VERY inefficient here...
BuiltTextures[i].swap(BuiltTextures[BuiltTextures.Size() - 1 - i]);
}
while (BuiltTextures.Size() > 0)
{
bool donesomething = false;
for (int i = BuiltTextures.Size()-1; i>= 0; i--)
{
auto &buildinfo = BuiltTextures[i];
bool hasEmpty = false;
for (unsigned j = 0; j < buildinfo.Inits.Size(); j++)
{
if (buildinfo.Parts[j].Image == nullptr)
{
auto image = buildinfo.Inits[j].Texture->GetImage();
if (image != nullptr)
{
buildinfo.Parts[j].Image = image;
donesomething = true;
}
else hasEmpty = true;
}
}
if (!hasEmpty)
{
// If this texture is just a wrapper around a single patch, we can simply
// use that patch's image directly here.
bool done = false;
if (buildinfo.Parts.Size() == 1)
{
if (buildinfo.Parts[0].OriginX == 0 && buildinfo.Parts[0].OriginY == 0 &&
buildinfo.Parts[0].Image->GetWidth() == buildinfo.Width &&
buildinfo.Parts[0].Image->GetHeight() == buildinfo.Height &&
buildinfo.Parts[0].Rotate == 0 &&
!buildinfo.bComplex)
{
buildinfo.tex->SetImage(buildinfo.Parts[0].Image);
done = true;
}
}
if (!done)
{
auto img = new FMultiPatchTexture(buildinfo.Width, buildinfo.Height, buildinfo.Parts, buildinfo.bComplex, buildinfo.textual);
buildinfo.tex->SetImage(img);
}
BuiltTextures.Delete(i);
donesomething = true;
}
}
if (!donesomething)
{
Printf("%d Unresolved textures remain\n", BuiltTextures.Size());
for (auto &b : BuiltTextures)
{
Printf("%s\n", b.Name.GetChars());
b.tex->SetUseType(ETextureType::Null);
}
break;
}
}
}

View file

@ -36,6 +36,7 @@
#include "textures.h"
#include "skyboxtexture.h"
#include "bitmap.h"
#include "texturemanager.h"
@ -48,6 +49,15 @@
FSkyBox::FSkyBox(const char *name)
: FTexture(name)
{
FTextureID texid = TexMan.CheckForTexture(name, ETextureType::Wall);
previous = nullptr;
if (texid.isValid())
{
previous = TexMan.GetTexture(texid);
CopySize(previous);
}
faces[0]=faces[1]=faces[2]=faces[3]=faces[4]=faces[5] = nullptr;
UseType = ETextureType::Override;
bSkybox = true;
fliptop = false;
}
@ -58,9 +68,20 @@ FSkyBox::FSkyBox(const char *name)
//
//-----------------------------------------------------------------------------
FBitmap FSkyBox::GetBgraBitmap(PalEntry *p, int *trans)
TArray<uint8_t> FSkyBox::Get8BitPixels(bool alphatex)
{
return faces[0]->GetBgraBitmap(p, trans);
return previous->Get8BitPixels(alphatex);
}
//-----------------------------------------------------------------------------
//
//
//
//-----------------------------------------------------------------------------
FBitmap FSkyBox::GetBgraBitmap(const PalEntry *p, int *trans)
{
return previous->GetBgraBitmap(p, trans);
}
//-----------------------------------------------------------------------------
@ -71,5 +92,5 @@ FBitmap FSkyBox::GetBgraBitmap(PalEntry *p, int *trans)
FImageSource *FSkyBox::GetImage() const
{
return faces[0]->GetImage();
return previous->GetImage();
}

View file

@ -12,17 +12,23 @@ class FSkyBox : public FTexture
{
public:
FTexture* faces[6] = {};
FTexture *previous;
FTexture * faces[6];
bool fliptop;
FSkyBox(const char *name);
TArray<uint8_t> Get8BitPixels(bool alphatex);
FBitmap GetBgraBitmap(const PalEntry *, int *trans) override;
FImageSource *GetImage() const override;
void SetSize()
{
CopySize(faces[0]);
if (!previous && faces[0]) previous = faces[0];
if (previous)
{
CopySize(previous);
}
}
bool Is3Face() const

View file

@ -49,8 +49,8 @@
#include "texturemanager.h"
// Wrappers to keep the definitions of these classes out of here.
void DeleteMaterial(FMaterial* mat) {}
void DeleteSoftwareTexture(FSoftwareTexture* swtex) {}
void DeleteMaterial(FMaterial* mat);
void DeleteSoftwareTexture(FSoftwareTexture* swtex);
IHardwareTexture* CreateHardwareTexture();

View file

@ -119,12 +119,9 @@ void FTextureManager::FlushAll()
{
for (int j = 0; j < 2; j++)
{
Textures[i].Texture->DeleteHardwareTextures();
#if 0
Textures[i].Texture->SystemTextures.Clean(true, true);
DeleteSoftwareTexture(Textures[i].Texture->SoftwareTexture);
Textures[i].Texture->SoftwareTexture = nullptr;
#endif
}
}
}
@ -217,6 +214,7 @@ FTextureID FTextureManager::CheckForTexture (const char *name, ETextureType uset
}
}
if (!(flags & TEXMAN_ShortNameOnly))
{
// We intentionally only look for textures in subdirectories.
@ -234,7 +232,7 @@ FTextureID FTextureManager::CheckForTexture (const char *name, ETextureType uset
tex = FTexture::CreateTexture("", lump, ETextureType::Override);
if (tex != NULL)
{
//tex->AddAutoMaterials();
tex->AddAutoMaterials();
fileSystem.SetLinkedTexture(lump, tex);
return AddTexture(tex);
}
@ -575,7 +573,6 @@ void FTextureManager::AddHiresTextures (int wadnum)
FTexture * oldtex = Textures[tlist[i].GetIndex()].Texture;
// Replace the entire texture and adjust the scaling and offset factors.
#if 0
newtex->bWorldPanning = true;
newtex->SetDisplaySize(oldtex->GetDisplayWidth(), oldtex->GetDisplayHeight());
newtex->_LeftOffset[0] = int(oldtex->GetScaledLeftOffset(0) * newtex->Scale.X);
@ -583,7 +580,6 @@ void FTextureManager::AddHiresTextures (int wadnum)
newtex->_TopOffset[0] = int(oldtex->GetScaledTopOffset(0) * newtex->Scale.Y);
newtex->_TopOffset[1] = int(oldtex->GetScaledTopOffset(1) * newtex->Scale.Y);
ReplaceTexture(tlist[i], newtex, true);
#endif
}
}
}
@ -616,7 +612,6 @@ void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname, FMultipa
void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build)
{
#if 0
TArray<FTextureID> tlist;
FScanner sc(lump);
@ -770,7 +765,6 @@ void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build
sc.ScriptError("Texture definition expected, found '%s'", sc.String);
}
}
#endif
}
//==========================================================================
@ -811,7 +805,6 @@ void FTextureManager::AddPatches (int lumpnum)
void FTextureManager::LoadTextureX(int wadnum, FMultipatchTextureBuilder &build)
{
#if 0
// Use the most recent PNAMES for this WAD.
// Multiple PNAMES in a WAD will be ignored.
int pnames = fileSystem.CheckNumForName("PNAMES", ns_global, wadnum, false);
@ -829,7 +822,6 @@ void FTextureManager::LoadTextureX(int wadnum, FMultipatchTextureBuilder &build)
int texlump1 = fileSystem.CheckNumForName ("TEXTURE1", ns_global, wadnum);
int texlump2 = fileSystem.CheckNumForName ("TEXTURE2", ns_global, wadnum);
build.AddTexturesLumps (texlump1, texlump2, pnames);
#endif
}
//==========================================================================
@ -1102,7 +1094,6 @@ void FTextureManager::Init(void (*progressFunc_)(), void (*checkForHacks)(BuildI
auto nulltex = new FImageTexture(nullptr, "");
nulltex->SetUseType(ETextureType::Null);
AddTexture (nulltex);
#if 0 // stuff for later. Not needed to get things going.
// some special textures used in the game.
AddTexture(CreateShaderTexture(false, false));
AddTexture(CreateShaderTexture(false, true));
@ -1142,7 +1133,6 @@ void FTextureManager::Init(void (*progressFunc_)(), void (*checkForHacks)(BuildI
glPart = TexMan.CheckForTexture("glstuff/glpart.png", ETextureType::MiscPatch);
mirrorTexture = TexMan.CheckForTexture("glstuff/mirror.png", ETextureType::MiscPatch);
AddLocalizedVariants();
#endif
}
//==========================================================================
@ -1153,7 +1143,6 @@ void FTextureManager::Init(void (*progressFunc_)(), void (*checkForHacks)(BuildI
void FTextureManager::InitPalettedVersions()
{
#if 0
int lump, lastlump = 0;
while ((lump = fileSystem.FindLump("PALVERS", &lastlump)) != -1)
@ -1182,7 +1171,6 @@ void FTextureManager::InitPalettedVersions()
}
}
}
#endif
}
//==========================================================================
@ -1311,7 +1299,7 @@ int FTextureManager::CountLumpTextures (int lumpnum)
void FTextureManager::AdjustSpriteOffsets()
{
int /*lump,*/ lastlump = 0;
int lump, lastlump = 0;
int sprid;
TMap<int, bool> donotprocess;
@ -1335,7 +1323,6 @@ void FTextureManager::AdjustSpriteOffsets()
}
}
#if 0
while ((lump = fileSystem.FindLump("SPROFS", &lastlump, false)) != -1)
{
FScanner sc;
@ -1383,7 +1370,6 @@ void FTextureManager::AdjustSpriteOffsets()
}
}
}
#endif
}

View file

@ -30,9 +30,7 @@ private:
if ((unsigned)texnum >= Textures.Size()) return nullptr;
if (animate) texnum = Translation[texnum];
if (localize && Textures[texnum].HasLocalization) texnum = ResolveLocalizedTexture(texnum);
#if 0
if (palettesubst) texnum = PalCheck(texnum);
#endif
return Textures[texnum].Texture;
}
public:

View file

@ -406,7 +406,6 @@ protected:
double GetScaledHeightDouble () { return Height / Scale.Y; }
double GetScaleY() const { return Scale.Y; }
// Now with improved offset adjustment.
int GetLeftOffset(int adjusted) { return _LeftOffset[adjusted]; }
int GetTopOffset(int adjusted) { return _TopOffset[adjusted]; }

View file

@ -604,8 +604,8 @@ double GetBottomAlignOffset(FFont *font, int c)
FTexture *tex_zero = font->GetChar('0', CR_UNDEFINED, &w);
FTexture *texc = font->GetChar(c, CR_UNDEFINED, &w);
double offset = 0;
if (texc) offset += texc->GetDisplayTopOffset();
if (tex_zero) offset += -tex_zero->GetDisplayTopOffset() + tex_zero->GetDisplayHeight();
if (texc) offset += texc->GetDisplayTopOffsetDouble();
if (tex_zero) offset += -tex_zero->GetDisplayTopOffsetDouble() + tex_zero->GetDisplayHeightDouble();
return offset;
}

View file

@ -54,6 +54,7 @@
#include "statistics.h"
#include "input/m_joy.h"
#include "raze_sound.h"
#include "texturemanager.h"
void RegisterDukeMenus();
void RegisterRedneckMenus();
@ -265,9 +266,10 @@ bool DMenu::MouseEventBack(int type, int x, int y)
{
if (m_show_backbutton >= 0)
{
FTexture* tex = TileFiles.GetTexture("engine/graphics/m_back.png");
if (tex != NULL)
auto texid = TexMan.CheckForTexture("engine/graphics/m_back.png", ETextureType::Any);
if (texid.isValid())
{
auto tex = TexMan.GetTexture(texid);
if (m_show_backbutton&1) x -= screen->GetWidth() - tex->GetDisplayWidth() * CleanXfac;
if (m_show_backbutton&2) y -= screen->GetHeight() - tex->GetDisplayHeight() * CleanYfac;
mBackbuttonSelected = ( x >= 0 && x < tex->GetDisplayWidth() * CleanXfac &&
@ -321,9 +323,11 @@ void DMenu::Drawer ()
{
if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
{
FTexture* tex = TileFiles.GetTexture("engine/graphics/m_back.png");
auto texid = TexMan.CheckForTexture("engine/graphics/m_back.png", ETextureType::Any);
if (texid.isValid())
if (tex)
{
auto tex = TexMan.GetTexture(texid);
int w = tex->GetDisplayWidth() * CleanXfac;
int h = tex->GetDisplayHeight() * CleanYfac;
int x = (!(m_show_backbutton & 1)) ? 0 : screen->GetWidth() - w;

View file

@ -48,7 +48,7 @@
#include "optionmenuitems.h"
#include "i_soundfont.h"
#include "zstring.h"
#include "buildtiles.h"
#include "texturemanager.h"
#include <zmusic.h>
// Menu-relevant content that gets filled in by scripts. This will get processed after the game has loaded.
@ -105,14 +105,13 @@ void M_DeinitMenus()
static FTexture* GetMenuTexture(const char* const name)
{
auto tex = TileFiles.GetTexture(name);
if (!tex)
auto texid = TexMan.CheckForTexture(name, ETextureType::Any);
if (!texid.isValid())
{
Printf("Missing menu texture: \"%s\"\n", name);
}
return tex;
return TexMan.GetTexture(texid);
}
//=============================================================================

View file

@ -177,7 +177,7 @@ void FGLRenderer::BindToFrameBuffer(FTexture *mat)
if (BaseLayer == nullptr)
{
// must create the hardware texture first
BaseLayer = new ::FHardwareTexture();
BaseLayer = new ::FHardwareTexture;
BaseLayer->CreateTexture(mat->GetTexelWidth()*4, mat->GetTexelHeight()*4, ::FHardwareTexture::TrueColor, false);
mat->SystemTextures.AddHardwareTexture(0, false, BaseLayer);
}

View file

@ -63,7 +63,7 @@ BuildTiles TileFiles;
picanm_t tileConvertAnimFormat(int32_t const picanimraw, int* lo, int* to)
{
// Unpack a 4 byte packed anim descriptor into the internal 5 byte format.
// Unpack a 4 byte packed anim descriptor into something more accessible
picanm_t anm;
anm.num = picanimraw & 63;
*lo = (int8_t)((picanimraw >> 8) & 255);
@ -111,6 +111,14 @@ TArray<uint8_t> FTileTexture::CreatePalettedPixels(int conversion)
static FTexture* GetTileTexture(const char* name, const TArray<uint8_t>& backingstore, uint32_t offset, int width, int height)
{
auto tex = new FArtTile(backingstore, offset, width, height);
auto p = &backingstore[offset];
auto siz = width * height;
for (int i = 0; i < siz; i++, p++)
{
// move transparent color to index 0 to get in line with the rest of the texture management.
if (*p == 0) *p = 255;
else if (*p == 255) *p = 0;
}
if (tex)
{
@ -295,11 +303,11 @@ void BuildTiles::ClearTextureCache(bool artonly)
{
for (auto tex : AllTiles)
{
tex->DeleteHardwareTextures();
tex->SystemTextures.Clean(true, true);
}
for (auto tex : AllMapTiles)
{
tex->DeleteHardwareTextures();
tex->SystemTextures.Clean(true, true);
}
if (!artonly)
{
@ -307,7 +315,7 @@ void BuildTiles::ClearTextureCache(bool artonly)
decltype(textures)::Pair* pair;
while (it.NextPair(pair))
{
pair->Value->DeleteHardwareTextures();
pair->Value->SystemTextures.Clean(true, true);
}
}
}
@ -323,14 +331,15 @@ void BuildTiles::InvalidateTile(int num)
if ((unsigned) num < MAXTILES)
{
auto tex = tiledata[num].texture;
tex->DeleteHardwareTextures();
tex->SystemTextures.Clean(true, true);
for (auto &rep : tiledata[num].Hightiles)
{
for (auto &reptex : rep.faces)
{
if (reptex) reptex->DeleteHardwareTextures();
if (reptex) reptex->SystemTextures.Clean(true, true);
}
}
tiledata[num].rawCache.data.Clear();
}
}
@ -534,7 +543,27 @@ int32_t tileGetCRC32(int tileNum)
auto size = tile->GetWidth() * tile->GetHeight();
if (size == 0) return 0;
return crc32(0, (const Bytef*)pixels, size);
// Temporarily revert the data to its original form with 255 being transparent. Otherwise the CRC won't match.
auto p = pixels;
for (int i = 0; i < size; i++, p++)
{
// move transparent color to index 0 to get in line with the rest of the texture management.
if (*p == 0) *p = 255;
else if (*p == 255) *p = 0;
}
auto crc = crc32(0, (const Bytef*)pixels, size);
// ... and back again.
p = pixels;
for (int i = 0; i < size; i++, p++)
{
// move transparent color to index 0 to get in line with the rest of the texture management.
if (*p == 0) *p = 255;
else if (*p == 255) *p = 0;
}
return crc;
}
@ -692,6 +721,7 @@ void tileDelete(int tile)
void tileRemoveReplacement(int tile)
{
if ((unsigned)tile >= MAXTILES) return;
TileFiles.DeleteReplacements(tile);
}
@ -781,23 +811,6 @@ void tileSetAnim(int tile, const picanm_t& anm)
//
//==========================================================================
FTexture* BuildTiles::GetTexture(const char* path)
{
// let this go away.
auto res = textures.CheckKey(path);
if (res) return *res;
auto lump = fileSystem.FindFile(path);
auto tex = FTexture::CreateTexture(path, lump, ETextureType::Override);
if (tex) textures.Insert(path, tex);
return tex;
}
//==========================================================================
//
//
//
//==========================================================================
void BuildTiles::CloseAll()
{
decltype(textures)::Iterator it(textures);
@ -831,7 +844,7 @@ int tileSetHightileReplacement(int picnum, int palnum, const char* filename, flo
HightileReplacement replace = {};
FTextureID texid = TexMan.CheckForTexture(filename, ETextureType::Any);
if (!texid.isValid())
if (!texid.isValid())
{
Printf("%s: Replacement for tile %d does not exist or is invalid\n", filename, picnum);
return -1;
@ -839,7 +852,7 @@ int tileSetHightileReplacement(int picnum, int palnum, const char* filename, flo
replace.faces[0] = TexMan.GetTexture(texid);
if (replace.faces[0] == nullptr)
replace.alphacut = min(alphacut, 1.f);
replace.alphacut = min(alphacut,1.f);
replace.scale = { xscale, yscale };
replace.specpower = specpower; // currently unused
replace.specfactor = specfactor; // currently unused
@ -871,12 +884,13 @@ int tileSetSkybox(int picnum, int palnum, const char **facenames, int flags )
for (auto &face : replace.faces)
{
face = TileFiles.GetTexture(*facenames);
if (face == nullptr)
FTextureID texid = TexMan.CheckForTexture(*facenames, ETextureType::Any);
if (!texid.isValid())
{
Printf("%s: Skybox image for tile %d does not exist or is invalid\n", *facenames, picnum);
return -1;
}
face = TexMan.GetTexture(texid);
}
replace.flags = flags;
replace.palnum = (uint16_t)palnum;

View file

@ -1,46 +0,0 @@
/*
** imagehelpers.cpp
** Utilities for image conversion - mostly 8 bit paletted baggage
**
**---------------------------------------------------------------------------
** Copyright 2004-2007 Randy Heit
** Copyright 2006-2018 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
**
*/
#include "imagehelpers.h"
#include "palettecontainer.h"
#include "colormatcher.h"
namespace ImageHelpers
{
int alphaThreshold;
}

View file

@ -412,4 +412,21 @@ bool GLInstance::SetNamedTexture(FTexture* tex, int palette, int sampler)
return true;
}
// stand-ins for the texture system. Nothing of this is used right now, but needs to be present to satisfy the linker
int PalCheck(int tex)
{
return tex;
}
void DeleteSoftwareTexture(FSoftwareTexture *)
{
}
void InitBuildTiles()
{
}
TArray<UserShaderDesc> usershaders;

View file

@ -659,8 +659,8 @@ bool I_SetCursor(FTexture *cursorpic)
return false;
}
// Fixme: This should get a raw image, not a texture. (Once raw images get implemented.)
int lo = cursorpic->GetDisplayLeftOffset();
int to = cursorpic->GetDisplayTopOffset();
int lo = cursorpic->GetTexelLeftOffset(0);
int to = cursorpic->GetTexelTopOffset(0);
cursor = CreateAlphaCursor(image, lo, to);
if (cursor == NULL)