From e87f307ac1893cbf7097b00470672b1d5e8ecf7a Mon Sep 17 00:00:00 2001 From: Sergey Degtyar Date: Mon, 6 Apr 2026 04:59:21 +0300 Subject: [PATCH] engine: image: fix blue halo around sprays --- common/com_image.h | 2 ++ common/render_api.h | 2 +- engine/common/imagelib/img_main.c | 3 +++ engine/common/imagelib/img_wad.c | 2 ++ ref/gl/gl_decals.c | 4 ++++ ref/gl/gl_image.c | 19 +++++++++++++++++++ ref/gl/gl_rsurf.c | 8 ++++++-- 7 files changed, 37 insertions(+), 3 deletions(-) diff --git a/common/com_image.h b/common/com_image.h index 1fb4ac68..4e2563f7 100644 --- a/common/com_image.h +++ b/common/com_image.h @@ -116,6 +116,8 @@ typedef enum IMAGE_MULTILAYER = BIT(8), // to differentiate from 3D texture IMAGE_ONEBIT_ALPHA = BIT(9), // binary alpha IMAGE_QUAKEPAL = BIT(10), // image has quake1 palette + IMAGE_PREMULTIPLIED = BIT(11), + IMAGE_PLAYERDECAL = BIT(12), // loaded with IL_LOAD_PLAYER_DECAL (custom player spray) // Image_Process manipulation flags IMAGE_FLIP_X = BIT(16), // flip the image by width diff --git a/common/render_api.h b/common/render_api.h index 9236bbde..9371a403 100644 --- a/common/render_api.h +++ b/common/render_api.h @@ -101,7 +101,7 @@ typedef enum TF_TEXTURE_3D = (1<<20), // this is GL_TEXTURE_3D TF_ATLAS_PAGE = (1<<21), // bit who indicate lightmap page or deluxemap page TF_ALPHACONTRAST = (1<<22), // special texture mode for A2C -// reserved + TF_PREMULTIPLIED = (1<<23), // RGBA was stored premultiplied (HL gradient / indexalpha) // reserved TF_IMG_UPLOADED = (1<<25), // this is set for first time when called glTexImage, otherwise it will be call glTexSubImage TF_ARB_FLOAT = (1<<26), // float textures diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 7cc538b5..59fa83fb 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -173,6 +173,9 @@ static MALLOC_LIKE( FS_FreeImage, 1 ) rgbdata_t *ImagePack( const char *name ) Image_ReportLookupsCount( name ); + if( Image_CheckFlag( IL_LOAD_PLAYER_DECAL )) + SetBits( image.flags, IMAGE_PLAYERDECAL ); + // clear any force flags image.force_flags = 0; diff --git a/engine/common/imagelib/img_wad.c b/engine/common/imagelib/img_wad.c index 165fd0e2..fa045f53 100644 --- a/engine/common/imagelib/img_wad.c +++ b/engine/common/imagelib/img_wad.c @@ -250,6 +250,8 @@ qboolean Image_LoadSPR( const char *name, const byte *buffer, fs_offset_t filesi SetBits( image.flags, IMAGE_ONEBIT_ALPHA ); // intentionally fallthrough case LUMP_GRADIENT: + SetBits( image.flags, IMAGE_HAS_ALPHA ); + break; case LUMP_QUAKE1: SetBits( image.flags, IMAGE_HAS_ALPHA ); break; diff --git a/ref/gl/gl_decals.c b/ref/gl/gl_decals.c index c56d7895..66c79ff7 100644 --- a/ref/gl/gl_decals.c +++ b/ref/gl/gl_decals.c @@ -868,6 +868,10 @@ void DrawSingleDecal( decal_t *pDecal, msurface_t *fa ) GL_Bind( XASH_TEXTURE0, pDecal->texture ); + if( FBitSet( R_GetTexture( pDecal->texture )->flags, TF_PREMULTIPLIED )) + pglBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); + else pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + pglBegin( GL_POLYGON ); for( i = 0; i < numVerts; i++, v += VERTEXSIZE ) diff --git a/ref/gl/gl_image.c b/ref/gl/gl_image.c index 8f1006da..f5f89275 100644 --- a/ref/gl/gl_image.c +++ b/ref/gl/gl_image.c @@ -1238,6 +1238,8 @@ static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic ) // force upload texture as RGB or RGBA (detail textures requires this) if( tex->flags & TF_FORCE_COLOR ) pic->flags |= IMAGE_HAS_COLOR; if( pic->flags & IMAGE_HAS_ALPHA ) tex->flags |= TF_HAS_ALPHA; + if( FBitSet( pic->flags, IMAGE_PREMULTIPLIED )) + SetBits( tex->flags, TF_PREMULTIPLIED ); tex->encode = pic->encode; // share encode method @@ -1275,6 +1277,23 @@ static void GL_ProcessImage( gl_texture_t *tex, rgbdata_t *pic ) // processing image before uploading (force to rgba, make luma etc) if( pic->buffer ) gEngfuncs.Image_Process( &pic, 0, 0, img_flags, 0 ); + if( FBitSet( pic->flags, IMAGE_PLAYERDECAL ) && pic->buffer && pic->type == PF_RGBA_32 && FBitSet( pic->flags, IMAGE_HAS_ALPHA ) + && !FBitSet( pic->flags, IMAGE_PREMULTIPLIED )) + { + int i, cnt = (int)( pic->width * pic->height ); + byte *p = pic->buffer; + + for( i = 0; i < cnt; i++, p += 4 ) + { + int a = p[3]; + p[0] = ( p[0] * a + 127 ) / 255; + p[1] = ( p[1] * a + 127 ) / 255; + p[2] = ( p[2] * a + 127 ) / 255; + } + SetBits( pic->flags, IMAGE_PREMULTIPLIED ); + SetBits( tex->flags, TF_PREMULTIPLIED ); + } + if( FBitSet( tex->flags, TF_LUMINANCE )) ClearBits( pic->flags, IMAGE_HAS_COLOR ); } diff --git a/ref/gl/gl_rsurf.c b/ref/gl/gl_rsurf.c index ca265a7d..e93e2fcb 100644 --- a/ref/gl/gl_rsurf.c +++ b/ref/gl/gl_rsurf.c @@ -2775,7 +2775,9 @@ static void R_DrawDlightedDecals( vboarray_t *vbo, msurface_t *newsurf, msurface if( glt->fogParams[3] > 230 ) pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); else pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + if( FBitSet( glt->flags, TF_PREMULTIPLIED )) + pglBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); + else pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } else { @@ -3197,7 +3199,9 @@ static void R_DrawStaticDecals( vboarray_t *vbo, qboolean drawlightmap, int ilig if( glt->fogParams[3] > 230 ) pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); else pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); + if( FBitSet( glt->flags, TF_PREMULTIPLIED )) + pglBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); + else pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); } else {