tdf#98960 - DrawTransformedTexture adapted to the new area scale shader
Change-Id: I7c911f2aaccbffacfa5673b120b6177b8bea0672
Reviewed-on: https://gerrit.libreoffice.org/23864
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
diff --git a/vcl/opengl/gdiimpl.cxx b/vcl/opengl/gdiimpl.cxx
index 5773676..29ef7ca 100644
--- a/vcl/opengl/gdiimpl.cxx
+++ b/vcl/opengl/gdiimpl.cxx
@@ -1363,6 +1363,47 @@ void OpenGLSalGraphicsImpl::DrawTexture( OpenGLTexture& rTexture, const SalTwoRe
mpProgram->Clean();
}
namespace {
bool scaleTexture(const rtl::Reference< OpenGLContext > &xContext,
OpenGLTexture& rOutTexture, const double& ixscale, const double& iyscale, OpenGLTexture& rTexture)
{
int nWidth = rTexture.GetWidth();
int nHeight = rTexture.GetHeight();
int nNewWidth = nWidth / ixscale;
int nNewHeight = nHeight / iyscale;
OpenGLProgram* pProgram = xContext->UseProgram("textureVertexShader", OUString("areaScaleFragmentShader"));
if (pProgram == nullptr)
return false;
OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer(aScratchTex);
pProgram->SetUniform1f("xscale", ixscale);
pProgram->SetUniform1f("yscale", iyscale);
pProgram->SetUniform1i("swidth", nWidth);
pProgram->SetUniform1i("sheight", nHeight);
// For converting between <0,nWidth-1> and <0.0,1.0> coordinate systems.
pProgram->SetUniform1f("xsrcconvert", 1.0 / (nWidth - 1));
pProgram->SetUniform1f("ysrcconvert", 1.0 / (nHeight - 1));
pProgram->SetUniform1f("xdestconvert", 1.0 * (nNewWidth - 1));
pProgram->SetUniform1f("ydestconvert", 1.0 * (nNewHeight - 1));
pProgram->SetTexture("sampler", rTexture);
pProgram->DrawTexture(rTexture);
pProgram->Clean();
OpenGLContext::ReleaseFramebuffer(pFramebuffer);
CHECK_GL_ERROR();
rOutTexture = aScratchTex;
return true;
}
}
void OpenGLSalGraphicsImpl::DrawTransformedTexture(
OpenGLTexture& rTexture,
OpenGLTexture& rMask,
@@ -1377,10 +1418,6 @@ void OpenGLSalGraphicsImpl::DrawTransformedTexture(
(float) rTexture.GetWidth(), 0, (float) rTexture.GetWidth(), (float) rTexture.GetHeight() };
GLfloat aTexCoord[8];
// If downscaling at a higher scale ratio, use the area scaling algorithm rather
// than plain OpenGL's scaling, for better results.
// See OpenGLSalBitmap::ImplScaleArea().
const long nDestWidth = basegfx::fround(basegfx::B2DVector(rX - rNull).getLength());
const long nDestHeight = basegfx::fround(basegfx::B2DVector(rY - rNull).getLength());
@@ -1388,19 +1425,24 @@ void OpenGLSalGraphicsImpl::DrawTransformedTexture(
if( nDestHeight == 0 || nDestWidth == 0 )
return;
const double ixscale = rTexture.GetWidth() / double(nDestWidth);
const double iyscale = rTexture.GetHeight() / double(nDestHeight);
// inverted scale ratios
double ixscale = rTexture.GetWidth() / double(nDestWidth);
double iyscale = rTexture.GetHeight() / double(nDestHeight);
// If downscaling at a higher scale ratio, use the area scaling algorithm rather
// than plain OpenGL's scaling (texture mapping), for better results.
// See OpenGLSalBitmap::ImplScaleArea().
bool areaScaling = false;
bool fastAreaScaling = false;
OUString textureFragmentShader;
if( ixscale >= 2 && iyscale >= 2 ) // Downscaling to 50% or less? (inverted scale ratios)
if( ixscale >= 2 && iyscale >= 2 ) // scale ratio less than 50%
{
areaScaling = true;
fastAreaScaling = ( ixscale == int( ixscale ) && iyscale == int( iyscale ));
// The generic case has arrays only up to 100 ratio downscaling, which is hopefully enough
// in practice, but protect against buffer overflows in case such an extreme case happens
// (and in such case the precision of the generic algorithm probably doesn't matter anyway).
// The generic case has arrays only up to 16 ratio downscaling and is performed in 2 passes,
// when the ratio is in the 16-100 range, which is hopefully enough in practice, but protect
// against buffer overflows in case such an extreme case happens (and in such case the precision
// of the generic algorithm probably doesn't matter anyway).
if( ixscale > 100 || iyscale > 100 )
fastAreaScaling = true;
if( fastAreaScaling )
@@ -1409,17 +1451,69 @@ void OpenGLSalGraphicsImpl::DrawTransformedTexture(
textureFragmentShader = "areaScaleFragmentShader";
}
if( rMask )
OpenGLTexture aInTexture = rTexture;
OpenGLTexture aInMask = rMask;
// When using the area scaling algorithm we need to reduce the texture size in 2 passes
// in order to not use a big array inside the fragment shader.
if (areaScaling && !fastAreaScaling)
{
// Perform a first texture downscaling by an inverted scale ratio equal to
// the square root of the whole inverted scale ratio.
if (ixscale > 16 || iyscale > 16)
{
// The scissor area is set to the current window size in PreDraw,
// so if we do not disable the scissor test, the texture produced
// by the first downscaling is clipped to the current window size.
glDisable(GL_SCISSOR_TEST);
CHECK_GL_ERROR();
// Maybe it can give problems too.
glDisable(GL_STENCIL_TEST);
CHECK_GL_ERROR();
// the square root of the whole inverted scale ratio
double ixscalesqrt = std::floor(std::sqrt(ixscale));
double iyscalesqrt = std::floor(std::sqrt(iyscale));
ixscale /= ixscalesqrt; // second pass inverted x-scale factor
iyscale /= iyscalesqrt; // second pass inverted y-scale factor
scaleTexture(mpContext, aInTexture, ixscalesqrt, iyscalesqrt, rTexture);
if (rMask) // we need to downscale the mask too
{
scaleTexture(mpContext, aInMask, ixscalesqrt, iyscalesqrt, rMask);
}
// We need to re-acquire the off-screen texture.
CheckOffscreenTexture();
CHECK_GL_ERROR();
// Re-enable scissor and stencil tests if needed.
if (mbUseScissor)
{
glEnable(GL_SCISSOR_TEST);
CHECK_GL_ERROR();
}
if (mbUseStencil)
{
glEnable(GL_STENCIL_TEST);
CHECK_GL_ERROR();
}
}
}
if( aInMask )
{
if( !UseProgram( "transformedTextureVertexShader",
textureFragmentShader.isEmpty() ? "maskedTextureFragmentShader" : textureFragmentShader,
"#define MASKED" ) )
return;
mpProgram->SetTexture( "mask", rMask );
mpProgram->SetTexture( "mask", aInMask );
GLfloat aMaskCoord[8];
rMask.GetWholeCoord(aMaskCoord);
aInMask.GetWholeCoord(aMaskCoord);
mpProgram->SetMaskCoord(aMaskCoord);
rMask.SetFilter( GL_LINEAR );
aInMask.SetFilter( GL_LINEAR );
mpProgram->SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
else
@@ -1429,42 +1523,46 @@ void OpenGLSalGraphicsImpl::DrawTransformedTexture(
return;
}
int mnWidth = rTexture.GetWidth();
int mnHeight = rTexture.GetHeight();
if(areaScaling )
if(areaScaling)
{
int nWidth = aInTexture.GetWidth();
int nHeight = aInTexture.GetHeight();
// From OpenGLSalBitmap::ImplScaleArea().
if (fastAreaScaling && mnWidth && mnHeight)
if (fastAreaScaling && nWidth && nHeight)
{
mpProgram->SetUniform1i( "xscale", ixscale );
mpProgram->SetUniform1i( "yscale", iyscale );
mpProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
mpProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
mpProgram->SetUniform1f( "xstep", 1.0 / nWidth );
mpProgram->SetUniform1f( "ystep", 1.0 / nHeight );
mpProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
}
else if (mnHeight > 1 && mnWidth > 1)
else if (nHeight > 1 && nWidth > 1)
{
mpProgram->SetUniform1f( "xscale", ixscale );
mpProgram->SetUniform1f( "yscale", iyscale );
mpProgram->SetUniform1i( "swidth", mnWidth );
mpProgram->SetUniform1i( "sheight", mnHeight );
// For converting between <0,mnWidth-1> and <0.0,1.0> coordinate systems.
mpProgram->SetUniform1f( "xsrcconvert", 1.0 / ( mnWidth - 1 ));
mpProgram->SetUniform1f( "ysrcconvert", 1.0 / ( mnHeight - 1 ));
mpProgram->SetUniform1f( "xdestconvert", 1.0 * (( mnWidth / ixscale ) - 1 ));
mpProgram->SetUniform1f( "ydestconvert", 1.0 * (( mnHeight / iyscale ) - 1 ));
mpProgram->SetUniform1i( "swidth", nWidth );
mpProgram->SetUniform1i( "sheight", nHeight );
// For converting between <0,nWidth-1> and <0.0,1.0> coordinate systems.
mpProgram->SetUniform1f( "xsrcconvert", 1.0 / ( nWidth - 1 ));
mpProgram->SetUniform1f( "ysrcconvert", 1.0 / ( nHeight - 1 ));
mpProgram->SetUniform1f( "xdestconvert", 1.0 * (( nWidth / ixscale ) - 1 ));
mpProgram->SetUniform1f( "ydestconvert", 1.0 * (( nHeight / iyscale ) - 1 ));
}
}
ApplyProgramMatrices();
mpProgram->SetUniform2f( "viewport", GetWidth(), GetHeight() );
// Here, in order to get the correct transformation we need to pass the original texture,
// since it has been used for initializing the rectangle vertices.
mpProgram->SetTransform( "transform", rTexture, rNull, rX, rY );
rTexture.GetWholeCoord( aTexCoord );
mpProgram->SetTexture( "sampler", rTexture );
rTexture.SetFilter( GL_LINEAR );
aInTexture.GetWholeCoord(aTexCoord);
mpProgram->SetTexture("sampler", aInTexture);
aInTexture.SetFilter(GL_LINEAR);
mpProgram->SetTextureCoord( aTexCoord );
mpProgram->SetVertices( &aVertices[0] );
mpProgram->SetVertices( aVertices );
glDrawArrays( GL_TRIANGLE_FAN, 0, 4 );
CHECK_GL_ERROR();
mpProgram->Clean();
}