Version 5.2.0.0.beta1, tag libreoffice-5.2.0.0.beta1
[LibreOffice.git] / vcl / opengl / scale.cxx
blob9feb933d0ed4f56ac3c56c636a57bd92cb704c40
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/salbmp.hxx"
28 #include "opengl/program.hxx"
29 #include "opengl/texture.hxx"
30 #include "opengl/RenderState.hxx"
32 #include <ResampleKernel.hxx>
34 using vcl::Kernel;
35 using vcl::Lanczos3Kernel;
37 bool OpenGLSalBitmap::ImplScaleFilter(
38 const rtl::Reference< OpenGLContext > &xContext,
39 const double& rScaleX,
40 const double& rScaleY,
41 GLenum nFilter )
43 OpenGLFramebuffer* pFramebuffer;
44 OpenGLProgram* pProgram;
45 GLenum nOldFilter;
46 int nNewWidth( mnWidth * rScaleX );
47 int nNewHeight( mnHeight * rScaleY );
49 pProgram = xContext->UseProgram( "textureVertexShader",
50 "textureFragmentShader" );
51 if( !pProgram )
52 return false;
54 OpenGLTexture aNewTex(nNewWidth, nNewHeight);
55 pFramebuffer = xContext->AcquireFramebuffer( aNewTex );
57 pProgram->SetTexture( "sampler", maTexture );
58 nOldFilter = maTexture.GetFilter();
59 maTexture.SetFilter( nFilter );
60 pProgram->ApplyMatrix(mnWidth, mnHeight);
61 pProgram->DrawTexture( maTexture );
62 maTexture.SetFilter( nOldFilter );
63 pProgram->Clean();
65 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
67 mnWidth = nNewWidth;
68 mnHeight = nNewHeight;
69 maTexture = aNewTex;
71 CHECK_GL_ERROR();
72 return true;
75 void OpenGLSalBitmap::ImplCreateKernel(
76 const double& fScale,
77 const Kernel& rKernel,
78 GLfloat*& pWeights,
79 sal_uInt32& aKernelSize )
81 const double fSamplingRadius(rKernel.GetWidth());
82 const double fScaledRadius((fScale < 1.0) ? fSamplingRadius / fScale : fSamplingRadius);
83 const double fFilterFactor((fScale < 1.0) ? fScale : 1.0);
84 int aNumberOfContributions;
85 double aSum( 0 );
87 aNumberOfContributions = (static_cast< sal_uInt32 >(fabs(ceil(fScaledRadius))) * 2) + 1 - 6;
88 aKernelSize = aNumberOfContributions / 2 + 1;
90 // avoid a crash for now; re-think me.
91 if (aKernelSize > 16)
92 aKernelSize = 16;
94 pWeights = new GLfloat[16];
95 memset( pWeights, 0, 16 * sizeof( GLfloat ) );
97 for( sal_uInt32 i(0); i < aKernelSize; i++ )
99 const GLfloat aWeight( rKernel.Calculate( fFilterFactor * i ) );
100 if( fabs( aWeight ) >= 0.0001 )
102 pWeights[i] = aWeight;
103 aSum += i > 0 ? aWeight * 2 : aWeight;
107 for( sal_uInt32 i(0); i < aKernelSize; i++ )
109 pWeights[i] /= aSum;
113 bool OpenGLSalBitmap::ImplScaleConvolution(
114 const rtl::Reference< OpenGLContext > &xContext,
115 const double& rScaleX,
116 const double& rScaleY,
117 const Kernel& aKernel )
119 OpenGLFramebuffer* pFramebuffer;
120 OpenGLProgram* pProgram;
121 GLfloat* pWeights( nullptr );
122 sal_uInt32 nKernelSize;
123 GLfloat aOffsets[32];
124 int nNewWidth( mnWidth * rScaleX );
125 int nNewHeight( mnHeight * rScaleY );
127 // TODO Make sure the framebuffer is alright
129 pProgram = xContext->UseProgram( "textureVertexShader",
130 "convolutionFragmentShader" );
131 if( pProgram == nullptr )
132 return false;
134 // horizontal scaling in scratch texture
135 if( mnWidth != nNewWidth )
137 OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
139 pFramebuffer = xContext->AcquireFramebuffer( aScratchTex );
141 for( sal_uInt32 i = 0; i < 16; i++ )
143 aOffsets[i * 2] = i / (double) mnWidth;
144 aOffsets[i * 2 + 1] = 0;
146 ImplCreateKernel( rScaleX, aKernel, pWeights, nKernelSize );
147 pProgram->SetUniform1fv( "kernel", 16, pWeights );
148 pProgram->SetUniform2fv( "offsets", 16, aOffsets );
149 pProgram->SetTexture( "sampler", maTexture );
150 pProgram->DrawTexture( maTexture );
151 pProgram->Clean();
153 maTexture = aScratchTex;
154 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
157 // vertical scaling in final texture
158 if( mnHeight != nNewHeight )
160 OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
162 pFramebuffer = xContext->AcquireFramebuffer( aScratchTex );
164 for( sal_uInt32 i = 0; i < 16; i++ )
166 aOffsets[i * 2] = 0;
167 aOffsets[i * 2 + 1] = i / (double) mnHeight;
169 ImplCreateKernel( rScaleY, aKernel, pWeights, nKernelSize );
170 pProgram->SetUniform1fv( "kernel", 16, pWeights );
171 pProgram->SetUniform2fv( "offsets", 16, aOffsets );
172 pProgram->SetTexture( "sampler", maTexture );
173 pProgram->DrawTexture( maTexture );
174 pProgram->Clean();
176 maTexture = aScratchTex;
177 OpenGLContext::ReleaseFramebuffer( pFramebuffer );
180 mnWidth = nNewWidth;
181 mnHeight = nNewHeight;
183 CHECK_GL_ERROR();
184 return true;
188 "Area" scaling algorithm, which seems to give better results for downscaling
189 than other algorithms. The principle (taken from opencv, see resize.cl)
190 is that each resulting pixel is the average of all the source pixel values
191 it represents. Which is trivial in the case of exact multiples for downscaling,
192 the generic case needs to also consider that some source pixels contribute
193 only partially to their resulting pixels (because of non-integer multiples).
195 bool OpenGLSalBitmap::ImplScaleArea( const rtl::Reference< OpenGLContext > &xContext,
196 double rScaleX, double rScaleY )
198 int nNewWidth( mnWidth * rScaleX );
199 int nNewHeight( mnHeight * rScaleY );
201 if( nNewWidth == mnWidth && nNewHeight == mnHeight )
202 return true;
204 double ixscale = 1 / rScaleX;
205 double iyscale = 1 / rScaleY;
206 bool fast = ( ixscale == int( ixscale ) && iyscale == int( iyscale )
207 && int( nNewWidth * ixscale ) == mnWidth && int( nNewHeight * iyscale ) == mnHeight );
209 bool bTwoPasses = false;
211 // The generic case has arrays only up to 100 ratio downscaling, which is hopefully enough
212 // in practice, but protect against buffer overflows in case such an extreme case happens
213 // (and in such case the precision of the generic algorithm probably doesn't matter anyway).
214 if( ixscale > 100 || iyscale > 100 )
216 fast = true;
218 else
220 if (ixscale > 16 || iyscale > 16)
222 ixscale = std::floor(std::sqrt(ixscale));
223 iyscale = std::floor(std::sqrt(iyscale));
224 nNewWidth = int(mnWidth / ixscale);
225 rScaleX *= ixscale; // second pass x-scale factor
226 nNewHeight = int(mnHeight / iyscale);
227 rScaleY *= iyscale; // second pass y-scale factor
228 bTwoPasses = true;
232 // TODO Make sure the framebuffer is alright
234 OpenGLProgram* pProgram = xContext->UseProgram( "textureVertexShader",
235 fast ? OUString( "areaScaleFastFragmentShader" ) : OUString( "areaScaleFragmentShader" ));
236 if( pProgram == nullptr )
237 return false;
239 OpenGLTexture aScratchTex(nNewWidth, nNewHeight);
241 OpenGLFramebuffer* pFramebuffer = xContext->AcquireFramebuffer( aScratchTex );
243 // NOTE: This setup is also done in OpenGLSalGraphicsImpl::DrawTransformedTexture().
244 if( fast )
246 pProgram->SetUniform1i( "xscale", ixscale );
247 pProgram->SetUniform1i( "yscale", iyscale );
248 pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
249 pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
250 pProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
252 else
254 pProgram->SetUniform1f( "xscale", ixscale );
255 pProgram->SetUniform1f( "yscale", iyscale );
256 pProgram->SetUniform1i( "swidth", mnWidth );
257 pProgram->SetUniform1i( "sheight", mnHeight );
258 // For converting between <0,mnWidth-1> and <0.0,1.0> coordinate systems.
259 pProgram->SetUniform1f( "xsrcconvert", 1.0 / ( mnWidth - 1 ));
260 pProgram->SetUniform1f( "ysrcconvert", 1.0 / ( mnHeight - 1 ));
261 pProgram->SetUniform1f( "xdestconvert", 1.0 * ( nNewWidth - 1 ));
262 pProgram->SetUniform1f( "ydestconvert", 1.0 * ( nNewHeight - 1 ));
265 pProgram->SetTexture( "sampler", maTexture );
266 pProgram->DrawTexture( maTexture );
267 pProgram->Clean();
269 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
271 CHECK_GL_ERROR();
273 if (bTwoPasses)
275 mnWidth = nNewWidth;
276 mnHeight = nNewHeight;
278 nNewWidth = int(mnWidth * rScaleX);
279 nNewHeight = int (mnHeight * rScaleY);
281 ixscale = 1 / rScaleX;
282 iyscale = 1 / rScaleY;
284 pProgram = xContext->UseProgram("textureVertexShader", "areaScaleFragmentShader");
285 if (pProgram == nullptr)
286 return false;
288 OpenGLTexture aScratchTex2(nNewWidth, nNewHeight);
290 pFramebuffer = xContext->AcquireFramebuffer(aScratchTex2);
292 pProgram->SetUniform1f("xscale", ixscale);
293 pProgram->SetUniform1f("yscale", iyscale);
294 pProgram->SetUniform1i("swidth", mnWidth);
295 pProgram->SetUniform1i("sheight", mnHeight);
296 // For converting between <0,mnWidth-1> and <0.0,1.0> coordinate systems.
297 pProgram->SetUniform1f("xsrcconvert", 1.0 / (mnWidth - 1));
298 pProgram->SetUniform1f("ysrcconvert", 1.0 / (mnHeight - 1));
299 pProgram->SetUniform1f("xdestconvert", 1.0 * (nNewWidth - 1));
300 pProgram->SetUniform1f("ydestconvert", 1.0 * (nNewHeight - 1));
302 pProgram->SetTexture("sampler", aScratchTex);
303 pProgram->DrawTexture(aScratchTex);
304 pProgram->Clean();
306 OpenGLContext::ReleaseFramebuffer(pFramebuffer);
308 CHECK_GL_ERROR();
310 maTexture = aScratchTex2;
311 mnWidth = nNewWidth;
312 mnHeight = nNewHeight;
314 else
316 maTexture = aScratchTex;
317 mnWidth = nNewWidth;
318 mnHeight = nNewHeight;
321 return true;
324 bool OpenGLSalBitmap::ImplScale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
326 VCL_GL_INFO( "::ImplScale" );
328 mpUserBuffer.reset();
329 OpenGLVCLContextZone aContextZone;
330 rtl::Reference<OpenGLContext> xContext = OpenGLContext::getVCLContext();
331 xContext->state()->scissor().disable();
332 xContext->state()->stencil().disable();
334 if (rScaleX <= 1 && rScaleY <= 1)
336 nScaleFlag = BmpScaleFlag::BestQuality;
339 if( nScaleFlag == BmpScaleFlag::Fast )
341 return ImplScaleFilter( xContext, rScaleX, rScaleY, GL_NEAREST );
343 if( nScaleFlag == BmpScaleFlag::BiLinear )
345 return ImplScaleFilter( xContext, rScaleX, rScaleY, GL_LINEAR );
347 else if( nScaleFlag == BmpScaleFlag::Super || nScaleFlag == BmpScaleFlag::Default )
349 const Lanczos3Kernel aKernel;
351 return ImplScaleConvolution( xContext, rScaleX, rScaleY, aKernel );
353 else if( nScaleFlag == BmpScaleFlag::BestQuality && rScaleX <= 1 && rScaleY <= 1 )
354 { // Use are scaling for best quality, but only if downscaling.
355 return ImplScaleArea( xContext, rScaleX, rScaleY );
357 else if( nScaleFlag == BmpScaleFlag::Lanczos || nScaleFlag == BmpScaleFlag::BestQuality )
359 const Lanczos3Kernel aKernel;
361 return ImplScaleConvolution( xContext, rScaleX, rScaleY, aKernel );
364 SAL_WARN( "vcl.opengl", "Invalid flag for scaling operation" );
365 return false;
368 bool OpenGLSalBitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
370 OpenGLVCLContextZone aContextZone;
372 VCL_GL_INFO("::Scale " << int(nScaleFlag)
373 << " from " << mnWidth << "x" << mnHeight
374 << " to " << (mnWidth * rScaleX) << "x" << (mnHeight * rScaleY) );
376 if( nScaleFlag == BmpScaleFlag::Fast ||
377 nScaleFlag == BmpScaleFlag::BiLinear ||
378 nScaleFlag == BmpScaleFlag::Super ||
379 nScaleFlag == BmpScaleFlag::Lanczos ||
380 nScaleFlag == BmpScaleFlag::Default ||
381 nScaleFlag == BmpScaleFlag::BestQuality )
383 ImplScale( rScaleX, rScaleY, nScaleFlag );
384 return true;
387 return false;
390 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */