mirror of
https://github.com/ZDoom/raze-gles.git
synced 2024-12-27 20:20:40 +00:00
- 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:
parent
0179029ed1
commit
6bffdf80a1
48 changed files with 30050 additions and 138 deletions
|
@ -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/.+")
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -40,10 +40,9 @@
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -51,7 +50,7 @@ void AnimTexture::SetFrame(const uint8_t *palette, const void *data_)
|
|||
{
|
||||
memcpy(Palette, palette, 768);
|
||||
memcpy(Image.Data(), data_, Width * Height);
|
||||
DeleteHardwareTextures();
|
||||
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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
141
source/common/textures/hires/hqnx/common.h
Normal file
141
source/common/textures/hires/hqnx/common.h
Normal 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
|
2808
source/common/textures/hires/hqnx/hq2x.cpp
Normal file
2808
source/common/textures/hires/hqnx/hq2x.cpp
Normal file
File diff suppressed because it is too large
Load diff
3786
source/common/textures/hires/hqnx/hq3x.cpp
Normal file
3786
source/common/textures/hires/hqnx/hq3x.cpp
Normal file
File diff suppressed because it is too large
Load diff
5232
source/common/textures/hires/hqnx/hq4x.cpp
Normal file
5232
source/common/textures/hires/hqnx/hq4x.cpp
Normal file
File diff suppressed because it is too large
Load diff
55
source/common/textures/hires/hqnx/hqx.h
Normal file
55
source/common/textures/hires/hqnx/hqx.h
Normal 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
|
38
source/common/textures/hires/hqnx/init.cpp
Normal file
38
source/common/textures/hires/hqnx/init.cpp
Normal 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;
|
||||
}
|
||||
}
|
2943
source/common/textures/hires/hqnx_asm/hq2x_asm.cpp
Normal file
2943
source/common/textures/hires/hqnx_asm/hq2x_asm.cpp
Normal file
File diff suppressed because it is too large
Load diff
3843
source/common/textures/hires/hqnx_asm/hq3x_asm.cpp
Normal file
3843
source/common/textures/hires/hqnx_asm/hq3x_asm.cpp
Normal file
File diff suppressed because it is too large
Load diff
5419
source/common/textures/hires/hqnx_asm/hq4x_asm.cpp
Normal file
5419
source/common/textures/hires/hqnx_asm/hq4x_asm.cpp
Normal file
File diff suppressed because it is too large
Load diff
234
source/common/textures/hires/hqnx_asm/hqnx_asm.h
Normal file
234
source/common/textures/hires/hqnx_asm/hqnx_asm.h
Normal 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__
|
111
source/common/textures/hires/hqnx_asm/hqnx_asm_Image.cpp
Normal file
111
source/common/textures/hires/hqnx_asm/hqnx_asm_Image.cpp
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
77
source/common/textures/hires/hqnx_asm/hqnx_asm_Image.h
Normal file
77
source/common/textures/hires/hqnx_asm/hqnx_asm_Image.h
Normal 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()
|
||||
|
||||
}
|
511
source/common/textures/hires/hqresize.cpp
Normal file
511
source/common/textures/hires/hqresize.cpp
Normal 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;
|
||||
}
|
1364
source/common/textures/hires/xbr/xbrz.cpp
Normal file
1364
source/common/textures/hires/xbr/xbrz.cpp
Normal file
File diff suppressed because it is too large
Load diff
79
source/common/textures/hires/xbr/xbrz.h
Normal file
79
source/common/textures/hires/xbr/xbrz.h
Normal 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
|
35
source/common/textures/hires/xbr/xbrz_config.h
Normal file
35
source/common/textures/hires/xbr/xbrz_config.h
Normal 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
|
40
source/common/textures/hires/xbr/xbrz_config_old.h
Normal file
40
source/common/textures/hires/xbr/xbrz_config_old.h
Normal 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
|
1300
source/common/textures/hires/xbr/xbrz_old.cpp
Normal file
1300
source/common/textures/hires/xbr/xbrz_old.cpp
Normal file
File diff suppressed because it is too large
Load diff
87
source/common/textures/hires/xbr/xbrz_old.h
Normal file
87
source/common/textures/hires/xbr/xbrz_old.h
Normal 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
|
270
source/common/textures/hires/xbr/xbrz_tools.h
Normal file
270
source/common/textures/hires/xbr/xbrz_tools.h
Normal 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
|
410
source/common/textures/hw_material.cpp
Normal file
410
source/common/textures/hw_material.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
140
source/common/textures/hw_material.h
Normal file
140
source/common/textures/hw_material.h
Normal 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
|
||||
|
||||
|
|
@ -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:
|
|||
};
|
||||
|
||||
|
||||
class FTexture;
|
||||
|
||||
FTexture* CreateImageTexture(FImageSource* img, const char *name = nullptr) noexcept;
|
||||
|
|
927
source/common/textures/multipatchtexturebuilder.cpp
Normal file
927
source/common/textures/multipatchtexturebuilder.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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]; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue