Kaydet (Commit) ab65925b authored tarafından Luboš Luňák's avatar Luboš Luňák

"area" scaling for opengl that has good results for downscaling

Change-Id: I0e4ad776cbf31f9a130aedf0f9741927560b5ac1
üst c22dbb26
......@@ -10,6 +10,8 @@
$(eval $(call gb_Package_Package,vcl_opengl_shader,$(SRCDIR)/vcl/opengl))
$(eval $(call gb_Package_add_files,vcl_opengl_shader,$(LIBO_ETC_FOLDER)/opengl,\
areaScaleFastFragmentShader.glsl \
areaScaleFragmentShader.glsl \
blendedTextureFragmentShader.glsl \
blendedTextureVertexShader.glsl \
dumbVertexShader.glsl \
......
......@@ -55,6 +55,8 @@ public:
void SetUniform2f( const OString& rName, GLfloat v1, GLfloat v2 );
void SetUniform1fv( const OString& rName, GLsizei nCount, GLfloat* aValues );
void SetUniform2fv( const OString& rName, GLsizei nCount, GLfloat* aValues );
void SetUniform1i( const OString& rName, GLint v1 );
void SetUniform1iv( const OString& rName, GLsizei nCount, GLint* aValues );
void SetColor( const OString& rName, const Color& rColor );
void SetColor( const OString& rName, SalColor nColor, sal_uInt8 nTransparency );
void SetColorf( const OString& rName, SalColor nColor, double fTransparency );
......
......@@ -104,6 +104,7 @@ private:
bool ImplScaleFilter( const double& rScaleX, const double& rScaleY, GLenum nFilter );
void ImplCreateKernel( const double& fScale, const Kernel& rKernel, GLfloat*& pWeights, sal_uInt32& aKernelSize );
bool ImplScaleConvolution( const double& rScaleX, const double& rScaleY, const Kernel& aKernel );
bool ImplScaleArea( double rScaleX, double rScaleY );
public:
......
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* TODO Use textureOffset for newest version of GLSL */
uniform sampler2D sampler;
uniform int xscale;
uniform int yscale;
uniform float xstep;
uniform float ystep;
uniform float ratio; // = 1.0/(xscale*yscale)
varying vec2 tex_coord;
/*
Just make the resulting color the average of all the source pixels
(which is an area (xscale)x(yscale) ).
*/
void main(void)
{
vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );
vec2 offset = vec2( 0.0, 0.0 );
for( int y = 0; y < yscale; ++y )
{
for( int x = 0; x < xscale; ++x )
{
sum += texture2D( sampler, tex_coord.st + offset );
offset.x += xstep;
}
offset.y += ystep;
offset.x = 0.0;
}
sum *= ratio;
gl_FragColor = sum;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* TODO Use textureOffset for newest version of GLSL */
uniform sampler2D sampler;
uniform int swidth;
uniform int sheight;
uniform float xscale;
uniform float yscale;
uniform float xsrcconvert;
uniform float ysrcconvert;
uniform float xdestconvert;
uniform float ydestconvert;
varying vec2 tex_coord;
void main(void)
{
// Convert to pixel coordinates again.
int dx = int( tex_coord.s * xdestconvert );
int dy = int( tex_coord.t * ydestconvert );
// Note: These values are always the same for the same X (or Y),
// so they could be precalculated in C++ and passed to the shader,
// but GLSL has limits on the size of uniforms passed to it,
// so it'd need something like texture buffer objects from newer
// GLSL versions, and it seems the hassle is not really worth it.
// How much each column/row will contribute to the resulting pixel.
// assert( xscale <= 100 ); assert( yscale <= 100 );
float xratio[ 100 + 2 ];
float yratio[ 100 + 2 ];
// For finding the first and last source pixel.
int xpixel[ 100 + 2 ];
int ypixel[ 100 + 2 ];
int xpos = 0;
int ypos = 0;
// Compute the range of source pixels which will make up this destination pixel.
float fsx1 = dx * xscale;
float fsx2 = fsx1 + xscale;
// To whole pixel coordinates.
int sx1 = ceil( fsx1 );
int sx2 = floor( fsx2 );
// Range checking.
sx2 = min( sx2, swidth - 1 );
sx1 = min( sx1, sx2 );
// How much one full column contributes to the resulting pixel.
float width = min( xscale, swidth - fsx1 );
if( sx1 - fsx1 > 0.001 )
{ // The first column contributes only partially.
xpixel[ xpos ] = sx1 - 1;
xratio[ xpos ] = ( sx1 - fsx1 ) / width;
++xpos;
}
for( int sx = sx1; sx < sx2; ++sx )
{ // Columns that fully contribute to the resulting pixel.
xpixel[ xpos ] = sx;
xratio[ xpos ] = 1.0 / width;
++xpos;
}
if( fsx2 - sx2 > 0.001 )
{ // The last column contributes only partially.
xpixel[ xpos ] = sx2;
xratio[ xpos ] = min( min( fsx2 - sx2, 1.0 ) / width, 1.0 );
++xpos;
}
// The same for Y.
float fsy1 = dy * yscale;
float fsy2 = fsy1 + yscale;
int sy1 = ceil( fsy1 );
int sy2 = floor( fsy2 );
sy2 = min( sy2, sheight - 1 );
sy1 = min( sy1, sy2 );
float height = min( yscale, sheight - fsy1 );
if( sy1 - fsy1 > 0.001 )
{
ypixel[ ypos ] = sy1 - 1;
yratio[ ypos ] = ( sy1 - fsy1 ) / height;
++ypos;
}
for( int sy = sy1; sy < sy2; ++sy )
{
ypixel[ ypos ] = sy;
yratio[ ypos ] = 1.0 / height;
++ypos;
}
if( fsy2 - sy2 > 0.001 )
{
ypixel[ ypos ] = sy2;
yratio[ ypos ] = min( min( fsy2 - sy2, 1.0 ) / height, 1.0 );
++ypos;
}
int xstart = xpixel[ 0 ];
int xend = xpixel[ xpos - 1 ];
int ystart = ypixel[ 0 ];
int yend = ypixel[ ypos - 1 ];
vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );
ypos = 0;
for( int y = ystart; y <= yend; ++y, ++ypos )
{
vec4 tmp = vec4( 0.0, 0.0, 0.0, 0.0 );
xpos = 0;
for( int x = xstart; x <= xend; ++x, ++xpos )
{
vec2 offset = vec2( x * xsrcconvert, y * ysrcconvert );
tmp += texture2D( sampler, offset ) * xratio[ xpos ];
}
sum += tmp * yratio[ ypos ];
}
gl_FragColor = sum;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
......@@ -148,6 +148,18 @@ void OpenGLProgram::SetUniform2fv( const OString& rName, GLsizei nCount, GLfloat
glUniform2fv( nUniform, nCount, aValues );
}
void OpenGLProgram::SetUniform1i( const OString& rName, GLint v1 )
{
GLuint nUniform = GetUniformLocation( rName );
glUniform1i( nUniform, v1 );
}
void OpenGLProgram::SetUniform1iv( const OString& rName, GLsizei nCount, GLint* aValues )
{
GLuint nUniform = GetUniformLocation( rName );
glUniform1iv( nUniform, nCount, aValues );
}
void OpenGLProgram::SetColor( const OString& rName, SalColor nColor, sal_uInt8 nTransparency )
{
GLuint nUniform = GetUniformLocation( rName );
......
......@@ -188,6 +188,78 @@ bool OpenGLSalBitmap::ImplScaleConvolution(
return true;
}
/*
"Area" scaling algorithm, which seems to give better results for downscaling
than other algorithms. The principle (taken from opencv, see resize.cl)
is that each resulting pixel is the average of all the source pixel values
it represents. Which is trivial in the case of exact multiples for downscaling,
the generic case needs to also consider that some source pixels contribute
only partially to their resulting pixels (becauses of non-integer multiples).
*/
bool OpenGLSalBitmap::ImplScaleArea( double rScaleX, double rScaleY )
{
int nNewWidth( mnWidth * rScaleX );
int nNewHeight( mnHeight * rScaleY );
if( nNewWidth == mnWidth && nNewHeight == mnHeight )
return true;
double ixscale = 1 / rScaleX;
double iyscale = 1 / rScaleY;
bool fast = ( ixscale == int( ixscale ) && iyscale == int( iyscale )
&& int( nNewWidth * ixscale ) == mnWidth && int( nNewHeight * iyscale ) == mnHeight );
// 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).
if( ixscale > 100 || iyscale > 100 )
fast = true;
// TODO Make sure the framebuffer is alright
OpenGLProgram* pProgram = mpContext->UseProgram( "textureVertexShader",
fast ? OUString( "areaScaleFastFragmentShader" ) : OUString( "areaScaleFragmentShader" ));
if( pProgram == 0 )
return false;
OpenGLTexture aScratchTex = OpenGLTexture( nNewWidth, nNewHeight );
OpenGLFramebuffer* pFramebuffer = mpContext->AcquireFramebuffer( aScratchTex );
if( fast )
{
pProgram->SetUniform1i( "xscale", ixscale );
pProgram->SetUniform1i( "yscale", iyscale );
pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
pProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
}
else
{
pProgram->SetUniform1f( "xscale", ixscale );
pProgram->SetUniform1f( "yscale", iyscale );
pProgram->SetUniform1i( "swidth", mnWidth );
pProgram->SetUniform1i( "sheight", mnHeight );
// For converting between <0,mnWidth-1> and <0.0,1.0> coordinate systems.
pProgram->SetUniform1f( "xsrcconvert", 1.0 / ( mnWidth - 1 ));
pProgram->SetUniform1f( "ysrcconvert", 1.0 / ( mnHeight - 1 ));
pProgram->SetUniform1f( "xdestconvert", 1.0 * ( nNewWidth - 1 ));
pProgram->SetUniform1f( "ydestconvert", 1.0 * ( nNewHeight - 1 ));
}
pProgram->SetTexture( "sampler", maTexture );
pProgram->DrawTexture( maTexture );
pProgram->Clean();
maTexture = aScratchTex;
mpContext->ReleaseFramebuffer( pFramebuffer );
mnWidth = nNewWidth;
mnHeight = nNewHeight;
CHECK_GL_ERROR();
return true;
}
bool OpenGLSalBitmap::ImplScale( const double& rScaleX, const double& rScaleY, sal_uInt32 nScaleFlag )
{
SAL_INFO( "vcl.opengl", "::ImplScale" );
......@@ -209,6 +281,10 @@ bool OpenGLSalBitmap::ImplScale( const double& rScaleX, const double& rScaleY, s
return ImplScaleConvolution( rScaleX, rScaleY, aKernel );
}
else if( nScaleFlag == BMP_SCALE_BESTQUALITY && rScaleX <= 1 && rScaleY <= 1 )
{ // Use are scaling for best quality, but only if downscaling.
return ImplScaleArea( rScaleX, rScaleY );
}
else if( nScaleFlag == BMP_SCALE_LANCZOS || nScaleFlag == BMP_SCALE_BESTQUALITY )
{
const Lanczos3Kernel aKernel;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment