Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / opengl / scale.cxx
blob48b92db18183d8e4bb503366f58457cb333638d0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <vcl/opengl/OpenGLHelper.hxx>
24 #include "vcl/bitmap.hxx"
26 #include "opengl/zone.hxx"
27 #include "opengl/bmpop.hxx"
28 #include "opengl/salbmp.hxx"
29 #include "opengl/program.hxx"
30 #include "opengl/texture.hxx"
32 class ScaleOp : public OpenGLSalBitmapOp
34 private:
35 OpenGLSalBitmap* mpBitmap;
36 double mfScaleX;
37 double mfScaleY;
38 BmpScaleFlag mnScaleFlag;
40 public:
41 ScaleOp( OpenGLSalBitmap* pBitmap, const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag );
43 bool Execute() SAL_OVERRIDE;
44 void GetSize( Size& rSize ) const SAL_OVERRIDE;
47 bool OpenGLSalBitmap::ImplScaleFilter(
48 const double& rScaleX,
49 const double& rScaleY,
50 GLenum nFilter )
52 OpenGLFramebuffer* pFramebuffer;
53 OpenGLProgram* pProgram;
54 GLenum nOldFilter;
55 int nNewWidth( mnWidth * rScaleX );
56 int nNewHeight( mnHeight * rScaleY );
58 pProgram = mpContext->UseProgram( "textureVertexShader",
59 "textureFragmentShader" );
60 if( !pProgram )
61 return false;
63 OpenGLTexture aNewTex(nNewWidth, nNewHeight);
64 pFramebuffer = mpContext->AcquireFramebuffer( aNewTex );
66 pProgram->SetTexture( "sampler", maTexture );
67 nOldFilter = maTexture.GetFilter();
68 maTexture.SetFilter( nFilter );
69 pProgram->ApplyMatrix(mnWidth, mnHeight);
70 pProgram->DrawTexture( maTexture );
71 maTexture.SetFilter( nOldFilter );
72 pProgram->Clean();
74 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
76 mnWidth = nNewWidth;
77 mnHeight = nNewHeight;
78 maTexture = aNewTex;
80 CHECK_GL_ERROR();
81 return true;
84 void OpenGLSalBitmap::ImplCreateKernel(
85 const double& fScale,
86 const Kernel& rKernel,
87 GLfloat*& pWeights,
88 sal_uInt32& aKernelSize )
90 const double fSamplingRadius(rKernel.GetWidth());
91 const double fScaledRadius((fScale < 1.0) ? fSamplingRadius / fScale : fSamplingRadius);
92 const double fFilterFactor((fScale < 1.0) ? fScale : 1.0);
93 int aNumberOfContributions;
94 double aSum( 0 );
96 aNumberOfContributions = (static_cast< sal_uInt32 >(fabs(ceil(fScaledRadius))) * 2) + 1 - 6;
97 aKernelSize = aNumberOfContributions / 2 + 1;
99 // avoid a crash for now; re-think me.
100 if (aKernelSize > 16)
101 aKernelSize = 16;
103 pWeights = new GLfloat[16];
104 memset( pWeights, 0, 16 * sizeof( GLfloat ) );
106 for( sal_uInt32 i(0); i < aKernelSize; i++ )
108 const GLfloat aWeight( rKernel.Calculate( fFilterFactor * i ) );
109 if( fabs( aWeight ) >= 0.0001 )
111 pWeights[i] = aWeight;
112 aSum += i > 0 ? aWeight * 2 : aWeight;
116 for( sal_uInt32 i(0); i < aKernelSize; i++ )
118 pWeights[i] /= aSum;
122 bool OpenGLSalBitmap::ImplScaleConvolution(
123 const double& rScaleX,
124 const double& rScaleY,
125 const Kernel& aKernel )
127 OpenGLFramebuffer* pFramebuffer;
128 OpenGLProgram* pProgram;
129 GLfloat* pWeights( 0 );
130 sal_uInt32 nKernelSize;
131 GLfloat aOffsets[32];
132 int nNewWidth( mnWidth * rScaleX );
133 int nNewHeight( mnHeight * rScaleY );
135 // TODO Make sure the framebuffer is alright
137 pProgram = mpContext->UseProgram( "textureVertexShader",
138 "convolutionFragmentShader" );
139 if( pProgram == 0 )
140 return false;
142 // horizontal scaling in scratch texture
143 if( mnWidth != nNewWidth )
145 OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
147 pFramebuffer = mpContext->AcquireFramebuffer( aScratchTex );
149 for( sal_uInt32 i = 0; i < 16; i++ )
151 aOffsets[i * 2] = i / (double) mnWidth;
152 aOffsets[i * 2 + 1] = 0;
154 ImplCreateKernel( rScaleX, aKernel, pWeights, nKernelSize );
155 pProgram->SetUniform1fv( "kernel", 16, pWeights );
156 pProgram->SetUniform2fv( "offsets", 16, aOffsets );
157 pProgram->SetTexture( "sampler", maTexture );
158 pProgram->DrawTexture( maTexture );
159 pProgram->Clean();
161 maTexture = aScratchTex;
162 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
165 // vertical scaling in final texture
166 if( mnHeight != nNewHeight )
168 OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
170 pFramebuffer = mpContext->AcquireFramebuffer( aScratchTex );
172 for( sal_uInt32 i = 0; i < 16; i++ )
174 aOffsets[i * 2] = 0;
175 aOffsets[i * 2 + 1] = i / (double) mnHeight;
177 ImplCreateKernel( rScaleY, aKernel, pWeights, nKernelSize );
178 pProgram->SetUniform1fv( "kernel", 16, pWeights );
179 pProgram->SetUniform2fv( "offsets", 16, aOffsets );
180 pProgram->SetTexture( "sampler", maTexture );
181 pProgram->DrawTexture( maTexture );
182 pProgram->Clean();
184 maTexture = aScratchTex;
185 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
188 mnWidth = nNewWidth;
189 mnHeight = nNewHeight;
191 CHECK_GL_ERROR();
192 return true;
196 "Area" scaling algorithm, which seems to give better results for downscaling
197 than other algorithms. The principle (taken from opencv, see resize.cl)
198 is that each resulting pixel is the average of all the source pixel values
199 it represents. Which is trivial in the case of exact multiples for downscaling,
200 the generic case needs to also consider that some source pixels contribute
201 only partially to their resulting pixels (becauses of non-integer multiples).
203 bool OpenGLSalBitmap::ImplScaleArea( double rScaleX, double rScaleY )
205 int nNewWidth( mnWidth * rScaleX );
206 int nNewHeight( mnHeight * rScaleY );
208 if( nNewWidth == mnWidth && nNewHeight == mnHeight )
209 return true;
211 double ixscale = 1 / rScaleX;
212 double iyscale = 1 / rScaleY;
213 bool fast = ( ixscale == int( ixscale ) && iyscale == int( iyscale )
214 && int( nNewWidth * ixscale ) == mnWidth && int( nNewHeight * iyscale ) == mnHeight );
216 // The generic case has arrays only up to 100 ratio downscaling, which is hopefully enough
217 // in practice, but protect against buffer overflows in case such an extreme case happens
218 // (and in such case the precision of the generic algorithm probably doesn't matter anyway).
219 if( ixscale > 100 || iyscale > 100 )
220 fast = true;
222 // TODO Make sure the framebuffer is alright
224 OpenGLProgram* pProgram = mpContext->UseProgram( "textureVertexShader",
225 fast ? OUString( "areaScaleFastFragmentShader" ) : OUString( "areaScaleFragmentShader" ));
226 if( pProgram == 0 )
227 return false;
229 OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
231 OpenGLFramebuffer* pFramebuffer = mpContext->AcquireFramebuffer( aScratchTex );
233 // NOTE: This setup is also done in OpenGLSalGraphicsImpl::DrawTransformedTexture().
234 if( fast )
236 pProgram->SetUniform1i( "xscale", ixscale );
237 pProgram->SetUniform1i( "yscale", iyscale );
238 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
239 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
240 pProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
242 else
244 pProgram->SetUniform1f( "xscale", ixscale );
245 pProgram->SetUniform1f( "yscale", iyscale );
246 pProgram->SetUniform1i( "swidth", mnWidth );
247 pProgram->SetUniform1i( "sheight", mnHeight );
248 // For converting between <0,mnWidth-1> and <0.0,1.0> coordinate systems.
249 pProgram->SetUniform1f( "xsrcconvert", 1.0 / ( mnWidth - 1 ));
250 pProgram->SetUniform1f( "ysrcconvert", 1.0 / ( mnHeight - 1 ));
251 pProgram->SetUniform1f( "xdestconvert", 1.0 * ( nNewWidth - 1 ));
252 pProgram->SetUniform1f( "ydestconvert", 1.0 * ( nNewHeight - 1 ));
255 pProgram->SetTexture( "sampler", maTexture );
256 pProgram->DrawTexture( maTexture );
257 pProgram->Clean();
259 maTexture = aScratchTex;
260 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
262 mnWidth = nNewWidth;
263 mnHeight = nNewHeight;
265 CHECK_GL_ERROR();
266 return true;
269 bool OpenGLSalBitmap::ImplScale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
271 VCL_GL_INFO( "vcl.opengl", "::ImplScale" );
273 maUserBuffer.reset();
274 makeCurrent();
276 if( nScaleFlag == BmpScaleFlag::Fast )
278 return ImplScaleFilter( rScaleX, rScaleY, GL_NEAREST );
280 if( nScaleFlag == BmpScaleFlag::BiLinear )
282 return ImplScaleFilter( rScaleX, rScaleY, GL_LINEAR );
284 else if( nScaleFlag == BmpScaleFlag::Super || nScaleFlag == BmpScaleFlag::Default )
286 const Lanczos3Kernel aKernel;
288 return ImplScaleConvolution( rScaleX, rScaleY, aKernel );
290 else if( nScaleFlag == BmpScaleFlag::BestQuality && rScaleX <= 1 && rScaleY <= 1 )
291 { // Use are scaling for best quality, but only if downscaling.
292 return ImplScaleArea( rScaleX, rScaleY );
294 else if( nScaleFlag == BmpScaleFlag::Lanczos || nScaleFlag == BmpScaleFlag::BestQuality )
296 const Lanczos3Kernel aKernel;
298 return ImplScaleConvolution( rScaleX, rScaleY, aKernel );
301 SAL_WARN( "vcl.opengl", "Invalid flag for scaling operation" );
302 return false;
305 ScaleOp::ScaleOp(
306 OpenGLSalBitmap* pBitmap,
307 const double& rScaleX,
308 const double& rScaleY,
309 BmpScaleFlag nScaleFlag )
310 : mpBitmap( pBitmap )
311 , mfScaleX( rScaleX )
312 , mfScaleY( rScaleY )
313 , mnScaleFlag( nScaleFlag )
317 bool ScaleOp::Execute()
319 VCL_GL_INFO( "vcl.opengl", "::Execute" );
320 return mpBitmap->ImplScale( mfScaleX, mfScaleY, mnScaleFlag );
323 void ScaleOp::GetSize( Size& rSize ) const
325 VCL_GL_INFO( "vcl.opengl", "::GetSize" );
326 rSize.setWidth( rSize.Width() * mfScaleX );
327 rSize.setHeight( rSize.Height() * mfScaleY );
330 bool OpenGLSalBitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
332 OpenGLZone aZone;
334 VCL_GL_INFO("vcl.opengl", "::Scale " << int(nScaleFlag)
335 << " from " << mnWidth << "x" << mnHeight
336 << " to " << (mnWidth * rScaleX) << "x" << (mnHeight * rScaleY) );
338 if( nScaleFlag == BmpScaleFlag::Fast ||
339 nScaleFlag == BmpScaleFlag::BiLinear ||
340 nScaleFlag == BmpScaleFlag::Super ||
341 nScaleFlag == BmpScaleFlag::Lanczos ||
342 nScaleFlag == BmpScaleFlag::Default ||
343 nScaleFlag == BmpScaleFlag::BestQuality )
345 makeCurrent();
346 if( mpContext == NULL )
348 VCL_GL_INFO( "vcl.opengl", "Add ScaleOp to pending operations" );
349 maPendingOps.push_back( new ScaleOp( this, rScaleX, rScaleY, nScaleFlag ) );
351 else
353 ImplScale( rScaleX, rScaleY, nScaleFlag );
355 return true;
358 return false;
361 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */