OpenGL Renderer:

- Texture sampling now works with bilinear filtering, mipmapping, and anisotropic filtering! These texture smoothing features can be used by enabling the new CommonSettings.GFX3D_Renderer_TextureSmoothing flag.
This commit is contained in:
rogerman 2016-06-10 03:57:32 +00:00
parent ce5765006f
commit 9a9f006397
9 changed files with 495 additions and 117 deletions

View File

@ -483,8 +483,9 @@ extern struct TCommonSettings {
, GFX3D_LineHack(true)
, GFX3D_Zelda_Shadow_Depth_Hack(0)
, GFX3D_Renderer_Multisample(false)
, GFX3D_Renderer_TextureDeposterize(false)
, GFX3D_Renderer_TextureScalingFactor(1) // Possible values: 1, 2, 4
, GFX3D_Renderer_TextureDeposterize(false)
, GFX3D_Renderer_TextureSmoothing(false)
, GFX3D_TXTHack(false)
, GFX3D_PrescaleHD(1)
, jit_max_block_size(100)
@ -545,8 +546,9 @@ extern struct TCommonSettings {
bool GFX3D_LineHack;
int GFX3D_Zelda_Shadow_Depth_Hack;
bool GFX3D_Renderer_Multisample;
bool GFX3D_Renderer_TextureDeposterize;
int GFX3D_Renderer_TextureScalingFactor;
bool GFX3D_Renderer_TextureDeposterize;
bool GFX3D_Renderer_TextureSmoothing;
bool GFX3D_TXTHack;
//may not want this on OSX port

View File

@ -293,10 +293,11 @@ static const char *fragmentShader_100 = {"\
uniform int polyMode; \n\
uniform bool polyEnableDepthWrite;\n\
uniform bool polySetNewDepthForTranslucent;\n\
uniform int polyID; \n\
uniform int polyID;\n\
\n\
uniform bool polyEnableTexture; \n\
uniform bool polyEnableTexture;\n\
uniform bool polyEnableFog;\n\
uniform bool texSingleBitAlpha;\n\
\n\
vec3 packVec3FromFloat(const float value)\n\
{\n\
@ -308,6 +309,20 @@ static const char *fragmentShader_100 = {"\
void main() \n\
{ \n\
vec4 mainTexColor = (polyEnableTexture) ? texture2D(texRenderObject, vtxTexCoord) : vec4(1.0, 1.0, 1.0, 1.0); \n\
\n\
if (texSingleBitAlpha)\n\
{\n\
if (mainTexColor.a < 0.500)\n\
{\n\
mainTexColor.a = 0.0;\n\
}\n\
else\n\
{\n\
mainTexColor.rgb = mainTexColor.rgb / mainTexColor.a;\n\
mainTexColor.a = 1.0;\n\
}\n\
}\n\
\n\
vec4 newFragColor = mainTexColor * vtxColor; \n\
\n\
if(polyMode == 1) \n\
@ -328,7 +343,7 @@ static const char *fragmentShader_100 = {"\
} \n\
} \n\
\n\
if (newFragColor.a == 0.0 || (stateEnableAlphaTest && newFragColor.a < stateAlphaTestRef)) \n\
if (newFragColor.a < 0.001 || (stateEnableAlphaTest && newFragColor.a < stateAlphaTestRef)) \n\
{ \n\
discard; \n\
} \n\
@ -902,8 +917,15 @@ GPU3DInterface gpu3Dgl_3_2 = {
OpenGLRenderer::OpenGLRenderer()
{
_renderID = RENDERID_OPENGL_AUTO;
_renderName = "OpenGL";
_deviceInfo.renderID = RENDERID_OPENGL_AUTO;
_deviceInfo.renderName = "OpenGL";
_deviceInfo.isTexturingSupported = true;
_deviceInfo.isEdgeMarkSupported = true;
_deviceInfo.isFogSupported = true;
_deviceInfo.isTextureSmoothingSupported = true;
_deviceInfo.maxAnisotropy = 1.0f;
_deviceInfo.maxSamples = 0;
_internalRenderingFormat = NDSColorFormat_BGR888_Rev;
versionMajor = 0;
@ -1361,6 +1383,15 @@ Render3DError OpenGLRenderer_1_2::InitExtensions()
std::set<std::string> oglExtensionSet;
this->GetExtensionSet(&oglExtensionSet);
// Get host GPU device properties
GLfloat maxAnisotropyOGL = 1.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyOGL);
this->_deviceInfo.maxAnisotropy = maxAnisotropyOGL;
GLint maxSamplesOGL = 0;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamplesOGL);
this->_deviceInfo.maxSamples = (u8)maxSamplesOGL;
// Initialize OpenGL
this->InitTables();
@ -1395,21 +1426,32 @@ Render3DError OpenGLRenderer_1_2::InitExtensions()
framebufferOutputRGBA8888FragShaderString);
if (error != OGLERROR_NOERR)
{
this->_deviceInfo.isEdgeMarkSupported = false;
this->_deviceInfo.isFogSupported = false;
INFO("OpenGL: Edge mark and fog require OpenGL v2.0 or later. These features will be disabled.\n");
}
}
else
{
this->_deviceInfo.isEdgeMarkSupported = false;
this->_deviceInfo.isFogSupported = false;
this->_deviceInfo.isTextureSmoothingSupported = false;
this->isShaderSupported = false;
}
}
else
{
this->_deviceInfo.isEdgeMarkSupported = false;
this->_deviceInfo.isFogSupported = false;
this->_deviceInfo.isTextureSmoothingSupported = false;
this->isShaderSupported = false;
}
}
else
{
this->_deviceInfo.isEdgeMarkSupported = false;
this->_deviceInfo.isFogSupported = false;
this->_deviceInfo.isTextureSmoothingSupported = false;
INFO("OpenGL: Shaders are unsupported. Disabling shaders and using fixed-function pipeline. Some emulation features will be disabled.\n");
}
@ -1600,6 +1642,7 @@ Render3DError OpenGLRenderer_1_2::InitGeometryProgramShaderLocations()
OGLRef.uniformPolyEnableTexture = glGetUniformLocation(OGLRef.programGeometryID, "polyEnableTexture");
OGLRef.uniformPolyEnableFog = glGetUniformLocation(OGLRef.programGeometryID, "polyEnableFog");
OGLRef.uniformTexSingleBitAlpha = glGetUniformLocation(OGLRef.programGeometryID, "texSingleBitAlpha");
return OGLERROR_NOERR;
}
@ -2058,8 +2101,7 @@ Render3DError OpenGLRenderer_1_2::CreateMultisampledFBO()
// Check the maximum number of samples that the driver supports and use that.
// Since our target resolution is only 256x192 pixels, using the most samples
// possible is the best thing to do.
GLint maxSamples = 0;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);
GLint maxSamples = (GLint)this->_deviceInfo.maxSamples;
if (maxSamples < 2)
{
@ -3071,6 +3113,7 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT
if (this->isShaderSupported)
{
glUniform1i(OGLRef.uniformPolyEnableTexture, GL_FALSE);
glUniform1i(OGLRef.uniformTexSingleBitAlpha, GL_FALSE);
}
else
{
@ -3084,6 +3127,7 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT
if (this->isShaderSupported)
{
glUniform1i(OGLRef.uniformPolyEnableTexture, GL_TRUE);
glUniform1i(OGLRef.uniformTexSingleBitAlpha, (params.texFormat != TEXMODE_A3I5 && params.texFormat != TEXMODE_A5I3) ? GL_TRUE : GL_FALSE);
}
else
{
@ -3108,8 +3152,6 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT
OGLRef.freeTextureIDs.pop();
glBindTexture(GL_TEXTURE_2D, (GLuint)this->currTexture->texid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (params.enableRepeatS ? (params.enableMirroredRepeatS ? OGLRef.stateTexMirroredRepeat : GL_REPEAT) : GL_CLAMP_TO_EDGE));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (params.enableRepeatT ? (params.enableMirroredRepeatT ? OGLRef.stateTexMirroredRepeat : GL_REPEAT) : GL_CLAMP_TO_EDGE));
@ -3125,25 +3167,81 @@ Render3DError OpenGLRenderer_1_2::SetupTexture(const POLY &thePoly, bool enableT
switch (this->_textureScalingFactor)
{
case 1:
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (this->_textureSmooth) ? this->_deviceInfo.maxAnisotropy : 1.0f);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
break;
}
case 2:
{
if (this->_textureSmooth)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy);
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
textureSrc = this->_textureUpscaleBuffer;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
}
break;
}
case 4:
{
if (this->_textureSmooth)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy);
this->TextureUpscale<4>(textureSrc, texWidth, texHeight);
textureSrc = this->_textureUpscaleBuffer;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
texWidth = currTexture->sizeX;
texHeight = currTexture->sizeY;
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
this->TextureUpscale<4>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
}
break;
}
default:
break;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
@ -3313,8 +3411,7 @@ Render3DError OpenGLRenderer_1_2::SetFramebufferSize(size_t w, size_t h)
if (this->isMultisampledFBOSupported)
{
GLint maxSamples = 0;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);
GLint maxSamples = (GLint)this->_deviceInfo.maxSamples;
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGColorID);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, w, h);
@ -3493,8 +3590,7 @@ Render3DError OpenGLRenderer_1_3::SetFramebufferSize(size_t w, size_t h)
if (this->isMultisampledFBOSupported)
{
GLint maxSamples = 0;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamples);
GLint maxSamples = (GLint)this->_deviceInfo.maxSamples;
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, OGLRef.rboMSGColorID);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, maxSamples, GL_RGBA, w, h);
@ -3819,6 +3915,15 @@ Render3DError OpenGLRenderer_2_0::InitExtensions()
std::set<std::string> oglExtensionSet;
this->GetExtensionSet(&oglExtensionSet);
// Get host GPU device properties
GLfloat maxAnisotropyOGL = 1.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyOGL);
this->_deviceInfo.maxAnisotropy = maxAnisotropyOGL;
GLint maxSamplesOGL = 0;
glGetIntegerv(GL_MAX_SAMPLES_EXT, &maxSamplesOGL);
this->_deviceInfo.maxSamples = (u8)maxSamplesOGL;
// Initialize OpenGL
this->InitTables();
@ -3859,6 +3964,8 @@ Render3DError OpenGLRenderer_2_0::InitExtensions()
{
this->DestroyGeometryProgram();
this->isShaderSupported = false;
this->_deviceInfo.isEdgeMarkSupported = false;
this->_deviceInfo.isFogSupported = false;
}
this->isVBOSupported = true;
@ -4694,10 +4801,12 @@ Render3DError OpenGLRenderer_2_0::SetupTexture(const POLY &thePoly, bool enableT
if (params.texFormat == TEXMODE_NONE || !enableTexturing)
{
glUniform1i(OGLRef.uniformPolyEnableTexture, GL_FALSE);
glUniform1i(OGLRef.uniformTexSingleBitAlpha, GL_FALSE);
return OGLERROR_NOERR;
}
glUniform1i(OGLRef.uniformPolyEnableTexture, GL_TRUE);
glUniform1i(OGLRef.uniformTexSingleBitAlpha, (params.texFormat != TEXMODE_A3I5 && params.texFormat != TEXMODE_A5I3) ? GL_TRUE : GL_FALSE);
TexCacheItem *newTexture = TexCache_SetTexture(TexFormat_32bpp, thePoly.texParam, thePoly.texPalette);
if(newTexture != this->currTexture)
@ -4717,8 +4826,6 @@ Render3DError OpenGLRenderer_2_0::SetupTexture(const POLY &thePoly, bool enableT
OGLRef.freeTextureIDs.pop();
glBindTexture(GL_TEXTURE_2D, (GLuint)this->currTexture->texid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (params.enableRepeatS ? (params.enableMirroredRepeatS ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (params.enableRepeatT ? (params.enableMirroredRepeatT ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE));
@ -4734,25 +4841,81 @@ Render3DError OpenGLRenderer_2_0::SetupTexture(const POLY &thePoly, bool enableT
switch (this->_textureScalingFactor)
{
case 1:
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (this->_textureSmooth) ? this->_deviceInfo.maxAnisotropy : 1.0f);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
break;
}
case 2:
{
if (this->_textureSmooth)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy);
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
textureSrc = this->_textureUpscaleBuffer;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
}
break;
}
case 4:
{
if (this->_textureSmooth)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy);
this->TextureUpscale<4>(textureSrc, texWidth, texHeight);
textureSrc = this->_textureUpscaleBuffer;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
texWidth = currTexture->sizeX;
texHeight = currTexture->sizeY;
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
this->TextureUpscale<4>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
}
break;
}
default:
break;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{

View File

@ -389,7 +389,7 @@ struct OGLPolyStates
union
{
struct { GLubyte texSizeS, texSizeT, texParamPad[2]; };
struct { GLubyte texSizeS, texSizeT, texSingleBitAlpha, texParamPad[1]; };
GLubyte texParam[4];
};
};
@ -481,6 +481,7 @@ struct OGLRenderRef
GLint uniformPolyEnableTexture;
GLint uniformPolyEnableFog;
GLint uniformTexSingleBitAlpha;
GLint uniformPolyStateIndex;

View File

@ -122,6 +122,7 @@ static const char *GeometryVtxShader_150 = {"\
flat out uint polySetNewDepthForTranslucent;\n\
flat out uint polyMode;\n\
flat out uint polyID;\n\
flat out uint texSingleBitAlpha;\n\
\n\
void main() \n\
{ \n\
@ -138,6 +139,7 @@ static const char *GeometryVtxShader_150 = {"\
polySetNewDepthForTranslucent = polyStateFlags[3];\n\
polyMode = polyStateValues[1];\n\
polyID = polyStateValues[2];\n\
texSingleBitAlpha = polyStateTexParams[2];\n\
\n\
mat2 texScaleMtx = mat2( vec2(polyTexScale.x, 0.0), \n\
vec2( 0.0, polyTexScale.y)); \n\
@ -163,6 +165,7 @@ static const char *GeometryFragShader_150 = {"\
flat in uint polySetNewDepthForTranslucent;\n\
flat in uint polyMode;\n\
flat in uint polyID;\n\
flat in uint texSingleBitAlpha;\n\
\n\
layout (std140) uniform RenderStates\n\
{\n\
@ -202,6 +205,20 @@ static const char *GeometryFragShader_150 = {"\
void main() \n\
{ \n\
vec4 mainTexColor = bool(polyEnableTexture) ? texture(texRenderObject, vtxTexCoord) : vec4(1.0, 1.0, 1.0, 1.0);\n\
\n\
if (bool(texSingleBitAlpha))\n\
{\n\
if (mainTexColor.a < 0.500)\n\
{\n\
mainTexColor.a = 0.0;\n\
}\n\
else\n\
{\n\
mainTexColor.rgb = mainTexColor.rgb / mainTexColor.a;\n\
mainTexColor.a = 1.0;\n\
}\n\
}\n\
\n\
vec4 newFragColor = mainTexColor * vtxColor; \n\
\n\
if (polyMode == 1u) \n\
@ -222,10 +239,10 @@ static const char *GeometryFragShader_150 = {"\
} \n\
} \n\
\n\
if (newFragColor.a == 0.0 || (state.enableAlphaTest && newFragColor.a < state.alphaTestRef)) \n\
{ \n\
discard; \n\
} \n\
if (newFragColor.a < 0.001 || (state.enableAlphaTest && newFragColor.a < state.alphaTestRef))\n\
{\n\
discard;\n\
}\n\
\n\
float vertW = (vtxPosition.w == 0.0) ? 0.00000001 : vtxPosition.w; \n\
// hack: when using z-depth, drop some LSBs so that the overworld map in Dragon Quest IV shows up correctly\n\
@ -502,6 +519,18 @@ Render3DError OpenGLRenderer_3_2::InitExtensions()
std::set<std::string> oglExtensionSet;
this->GetExtensionSet(&oglExtensionSet);
// Get host GPU device properties
GLfloat maxAnisotropyOGL = 1.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAnisotropyOGL);
this->_deviceInfo.maxAnisotropy = (float)maxAnisotropyOGL;
GLint maxSamplesOGL = 0;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamplesOGL);
this->_deviceInfo.maxSamples = (u8)maxSamplesOGL;
_deviceInfo.isEdgeMarkSupported = true;
_deviceInfo.isFogSupported = true;
// Initialize OpenGL
this->InitTables();
@ -955,8 +984,7 @@ Render3DError OpenGLRenderer_3_2::CreateMultisampledFBO()
// Check the maximum number of samples that the GPU supports and use that.
// Since our target resolution is only 256x192 pixels, using the most samples
// possible is the best thing to do.
GLint maxSamples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
GLint maxSamples = (GLint)this->_deviceInfo.maxSamples;
if (maxSamples < 2)
{
@ -1387,6 +1415,7 @@ Render3DError OpenGLRenderer_3_2::BeginRender(const GFX3D &engine)
polyStates[i].polyID = polyAttr.polygonID;
polyStates[i].texSizeS = texParams.sizeS;
polyStates[i].texSizeT = texParams.sizeT;
polyStates[i].texSingleBitAlpha = (texParams.texFormat != TEXMODE_A3I5 && texParams.texFormat != TEXMODE_A5I3) ? GL_TRUE : GL_FALSE;
for (size_t j = 0; j < polyType; j++)
{
@ -1684,8 +1713,6 @@ Render3DError OpenGLRenderer_3_2::SetupTexture(const POLY &thePoly, bool enableT
OGLRef.freeTextureIDs.pop();
glBindTexture(GL_TEXTURE_2D, (GLuint)this->currTexture->texid);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (params.enableRepeatS ? (params.enableMirroredRepeatS ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (params.enableRepeatT ? (params.enableMirroredRepeatT ? GL_MIRRORED_REPEAT : GL_REPEAT) : GL_CLAMP_TO_EDGE));
@ -1701,25 +1728,81 @@ Render3DError OpenGLRenderer_3_2::SetupTexture(const POLY &thePoly, bool enableT
switch (this->_textureScalingFactor)
{
case 1:
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (this->_textureSmooth) ? GL_LINEAR : GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (this->_textureSmooth) ? this->_deviceInfo.maxAnisotropy : 1.0f);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
break;
}
case 2:
{
if (this->_textureSmooth)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy);
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
textureSrc = this->_textureUpscaleBuffer;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
}
break;
}
case 4:
{
if (this->_textureSmooth)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->_deviceInfo.maxAnisotropy);
this->TextureUpscale<4>(textureSrc, texWidth, texHeight);
textureSrc = this->_textureUpscaleBuffer;
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
texWidth = currTexture->sizeX;
texHeight = currTexture->sizeY;
this->TextureUpscale<2>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, currTexture->sizeX, currTexture->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
this->TextureUpscale<4>(textureSrc, texWidth, texHeight);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->_textureUpscaleBuffer);
}
break;
}
default:
break;
}
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureSrc);
}
else
{
@ -1779,8 +1862,7 @@ Render3DError OpenGLRenderer_3_2::SetFramebufferSize(size_t w, size_t h)
if (this->isMultisampledFBOSupported)
{
GLint maxSamples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
GLint maxSamples = (GLint)this->_deviceInfo.maxSamples;
glBindRenderbuffer(GL_RENDERBUFFER, OGLRef.rboMSGColorID);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, maxSamples, GL_RGBA, w, h);

View File

@ -2336,7 +2336,9 @@ void gfx3d_VBlankEndSignal(bool skipFrame)
if (CommonSettings.showGpu.main)
{
CurrentRenderer->SetRenderNeedsFinish(true);
CurrentRenderer->SetTextureProcessingProperties(CommonSettings.GFX3D_Renderer_TextureDeposterize, CommonSettings.GFX3D_Renderer_TextureScalingFactor);
CurrentRenderer->SetTextureProcessingProperties(CommonSettings.GFX3D_Renderer_TextureScalingFactor,
CommonSettings.GFX3D_Renderer_TextureDeposterize,
CommonSettings.GFX3D_Renderer_TextureSmoothing);
CurrentRenderer->Render(gfx3d);
}
else

View File

@ -1165,8 +1165,14 @@ GPU3DInterface gpu3DRasterize = {
SoftRasterizerRenderer::SoftRasterizerRenderer()
{
_renderID = RENDERID_SOFTRASTERIZER;
_renderName = "SoftRasterizer";
_deviceInfo.renderID = RENDERID_SOFTRASTERIZER;
_deviceInfo.renderName = "SoftRasterizer";
_deviceInfo.isTexturingSupported = true;
_deviceInfo.isEdgeMarkSupported = true;
_deviceInfo.isFogSupported = true;
_deviceInfo.isTextureSmoothingSupported = false;
_deviceInfo.maxAnisotropy = 1.0f;
_deviceInfo.maxSamples = 0;
_debug_drawClippedUserPoly = -1;
clippedPolys = clipper.clippedPolys = new GFX3D_Clipper::TClippedPoly[POLYLIST_SIZE*2];

View File

@ -265,8 +265,14 @@ void Render3D::operator delete(void *ptr)
Render3D::Render3D()
{
_renderID = RENDERID_NULL;
_renderName = "None";
_deviceInfo.renderID = RENDERID_NULL;
_deviceInfo.renderName = "None";
_deviceInfo.isTexturingSupported = false;
_deviceInfo.isEdgeMarkSupported = false;
_deviceInfo.isFogSupported = false;
_deviceInfo.isTextureSmoothingSupported = false;
_deviceInfo.maxAnisotropy = 1.0f;
_deviceInfo.maxSamples = 0;
static bool needTableInit = true;
@ -292,6 +298,7 @@ Render3D::Render3D()
_willFlushFramebufferRGBA5551 = true;
_textureScalingFactor = 1;
_textureSmooth = false;
_textureDeposterizeBuffer = NULL;
_textureUpscaleBuffer = NULL;
@ -303,14 +310,19 @@ Render3D::~Render3D()
// Do nothing.
}
const Render3DDeviceInfo& Render3D::GetDeviceInfo()
{
return this->_deviceInfo;
}
RendererID Render3D::GetRenderID()
{
return this->_renderID;
return this->_deviceInfo.renderID;
}
std::string Render3D::GetName()
{
return this->_renderName;
return this->_deviceInfo.renderName;
}
FragmentColor* Render3D::GetFramebuffer()
@ -381,7 +393,7 @@ void Render3D::SetRenderNeedsFinish(const bool renderNeedsFinish)
this->_renderNeedsFinish = renderNeedsFinish;
}
void Render3D::SetTextureProcessingProperties(bool willDeposterize, size_t scalingFactor)
void Render3D::SetTextureProcessingProperties(size_t scalingFactor, bool willDeposterize, bool willSmooth)
{
const bool isScaleValid = ( (scalingFactor == 2) || (scalingFactor == 4) );
const size_t newScalingFactor = (isScaleValid) ? scalingFactor : 1;
@ -416,6 +428,13 @@ void Render3D::SetTextureProcessingProperties(bool willDeposterize, size_t scali
needTexCacheReset = true;
}
if (willSmooth != this->_textureSmooth)
{
this->_textureSmooth = willSmooth;
needTexCacheReset = true;
}
if (needTexCacheReset)
{
TexCache_Reset();

View File

@ -98,11 +98,24 @@ struct FragmentAttributesBuffer
void SetAll(const FragmentAttributes &attr);
};
struct Render3DDeviceInfo
{
RendererID renderID;
std::string renderName;
bool isTexturingSupported;
bool isEdgeMarkSupported;
bool isFogSupported;
bool isTextureSmoothingSupported;
float maxAnisotropy;
u8 maxSamples;
};
class Render3D
{
protected:
RendererID _renderID;
std::string _renderName;
Render3DDeviceInfo _deviceInfo;
size_t _framebufferWidth;
size_t _framebufferHeight;
@ -116,6 +129,7 @@ protected:
bool _willFlushFramebufferRGBA5551;
size_t _textureScalingFactor;
bool _textureSmooth;
u32 *_textureDeposterizeBuffer;
u32 *_textureUpscaleBuffer;
@ -147,6 +161,7 @@ public:
Render3D();
~Render3D();
const Render3DDeviceInfo& GetDeviceInfo();
RendererID GetRenderID();
std::string GetName();
@ -179,7 +194,7 @@ public:
bool GetRenderNeedsFinish() const;
void SetRenderNeedsFinish(const bool renderNeedsFinish);
void SetTextureProcessingProperties(bool willDeposterize, size_t scalingFactor);
void SetTextureProcessingProperties(size_t scalingFactor, bool willDeposterize, bool willSmooth);
};
#ifdef ENABLE_SSE2

View File

@ -399,6 +399,10 @@ public:
//Texture conversion
//============================================================================
// Whenever a 1-bit alpha or no-alpha texture is unpacked (this means any texture
// format that is not A3I5 or A5I3), set all transparent pixels to 0 so that 3D
// renderers can assume that the transparent color is 0 during texture sampling.
const u8 opaqueColor = (TEXFORMAT == TexFormat_32bpp) ? 0xFF : 0x1F;
const u8 palZeroTransparent = ( 1 - ((format>>29) & 1) ) * opaqueColor;
@ -406,7 +410,8 @@ public:
{
case TEXMODE_A3I5:
{
for(int j=0;j<ms.numItems;j++) {
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
@ -424,7 +429,10 @@ public:
case TEXMODE_I2:
{
for(int j=0;j<ms.numItems;j++) {
if (palZeroTransparent == 0)
{
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
@ -433,28 +441,63 @@ public:
bits = (*adr)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
*dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor);
bits = ((*adr)>>2)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
*dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor);
bits = ((*adr)>>4)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
*dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor);
bits = ((*adr)>>6)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
*dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor);
adr++;
}
}
}
else
{
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u8 bits;
u16 c;
bits = (*adr)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,opaqueColor);
bits = ((*adr)>>2)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,opaqueColor);
bits = ((*adr)>>4)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,opaqueColor);
bits = ((*adr)>>6)&0x3;
c = pal[bits];
*dwdst++ = CONVERT(c,opaqueColor);
adr++;
}
}
}
break;
}
case TEXMODE_I4:
{
for(int j=0;j<ms.numItems;j++) {
if (palZeroTransparent == 0)
{
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
@ -463,29 +506,70 @@ public:
bits = (*adr)&0xF;
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
*dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor);
bits = ((*adr)>>4);
c = pal[bits];
*dwdst++ = CONVERT(c,(bits == 0) ? palZeroTransparent : opaqueColor);
*dwdst++ = (bits == 0) ? 0 : CONVERT(c,opaqueColor);
adr++;
}
}
}
else
{
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; x++)
{
u8 bits;
u16 c;
bits = (*adr)&0xF;
c = pal[bits];
*dwdst++ = CONVERT(c,opaqueColor);
bits = ((*adr)>>4);
c = pal[bits];
*dwdst++ = CONVERT(c,opaqueColor);
adr++;
}
}
}
break;
}
case TEXMODE_I8:
{
for(int j=0;j<ms.numItems;j++) {
if (palZeroTransparent == 0)
{
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; ++x)
{
u16 c = pal[*adr];
*dwdst++ = CONVERT(c,(*adr == 0) ? palZeroTransparent : opaqueColor);
*dwdst++ = (*adr == 0) ? 0 : CONVERT(c,opaqueColor);
adr++;
}
}
}
else
{
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; ++x)
{
u16 c = pal[*adr];
*dwdst++ = CONVERT(c,opaqueColor);
adr++;
}
}
}
break;
}
case TEXMODE_4X4:
{
if(ms.numItems != 1) {
@ -544,7 +628,7 @@ public:
{
case 0:
tmp_col[2] = RGB15TO32( PAL4X4(pal1offset+2), 0xFF );
tmp_col[3] = RGB15TO32(0x7FFF, 0x00);
tmp_col[3] = 0x00000000;
break;
case 1:
@ -553,12 +637,12 @@ public:
( (((tmp_col[0] & 0x00FF0000) + (tmp_col[1] & 0x00FF0000)) >> 1) & 0x00FF0000 ) |
( (((tmp_col[0] & 0x0000FF00) + (tmp_col[1] & 0x0000FF00)) >> 1) & 0x0000FF00 ) |
0x000000FF;
tmp_col[3] = 0xFFFFFF00;
tmp_col[3] = 0x00000000;
#else
tmp_col[2] = ( (((tmp_col[0] & 0x00FF00FF) + (tmp_col[1] & 0x00FF00FF)) >> 1) & 0x00FF00FF ) |
( (((tmp_col[0] & 0x0000FF00) + (tmp_col[1] & 0x0000FF00)) >> 1) & 0x0000FF00 ) |
0xFF000000;
tmp_col[3] = 0x00FFFFFF;
tmp_col[3] = 0x00000000;
#endif
break;
@ -634,9 +718,11 @@ public:
}
break;
}
case TEXMODE_A5I3:
{
for(int j=0;j<ms.numItems;j++) {
for(int j=0;j<ms.numItems;j++)
{
adr = ms.items[j].ptr;
for(u32 x = 0; x < ms.items[j].len; ++x)
{
@ -651,16 +737,18 @@ public:
}
break;
}
case TEXMODE_16BPP:
{
for(int j=0;j<ms.numItems;j++) {
for(int j=0;j<ms.numItems;j++)
{
u16* map = (u16*)ms.items[j].ptr;
int len = ms.items[j].len>>1;
for(int x = 0; x < len; ++x)
{
u16 c = map[x];
int alpha = ((c&0x8000)?opaqueColor:0);
*dwdst++ = CONVERT(c&0x7FFF,alpha);
*dwdst++ = (c & 0x8000) ? CONVERT(c&0x7FFF,opaqueColor) : 0;
}
}
break;