Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 : #include <sal/config.h>
21 :
22 : #include <vcl/opengl/OpenGLHelper.hxx>
23 :
24 : #include "vcl/bitmap.hxx"
25 :
26 : #include "opengl/bmpop.hxx"
27 : #include "opengl/salbmp.hxx"
28 : #include "opengl/program.hxx"
29 : #include "opengl/texture.hxx"
30 :
31 0 : class ScaleOp : public OpenGLSalBitmapOp
32 : {
33 : private:
34 : OpenGLSalBitmap* mpBitmap;
35 : double mfScaleX;
36 : double mfScaleY;
37 : BmpScaleFlag mnScaleFlag;
38 :
39 : public:
40 : ScaleOp( OpenGLSalBitmap* pBitmap, const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag );
41 :
42 : bool Execute() SAL_OVERRIDE;
43 : void GetSize( Size& rSize ) const SAL_OVERRIDE;
44 : };
45 :
46 0 : bool OpenGLSalBitmap::ImplScaleFilter(
47 : const double& rScaleX,
48 : const double& rScaleY,
49 : GLenum nFilter )
50 : {
51 : OpenGLFramebuffer* pFramebuffer;
52 : OpenGLProgram* pProgram;
53 : GLenum nOldFilter;
54 0 : int nNewWidth( mnWidth * rScaleX );
55 0 : int nNewHeight( mnHeight * rScaleY );
56 :
57 : pProgram = mpContext->UseProgram( "textureVertexShader",
58 0 : "textureFragmentShader" );
59 0 : if( !pProgram )
60 0 : return false;
61 :
62 0 : OpenGLTexture aNewTex = OpenGLTexture( nNewWidth, nNewHeight );
63 0 : pFramebuffer = mpContext->AcquireFramebuffer( aNewTex );
64 :
65 0 : pProgram->SetTexture( "sampler", maTexture );
66 0 : nOldFilter = maTexture.GetFilter();
67 0 : maTexture.SetFilter( nFilter );
68 0 : pProgram->DrawTexture( maTexture );
69 0 : maTexture.SetFilter( nOldFilter );
70 0 : pProgram->Clean();
71 :
72 0 : OpenGLContext::ReleaseFramebuffer( pFramebuffer );
73 :
74 0 : mnWidth = nNewWidth;
75 0 : mnHeight = nNewHeight;
76 0 : maTexture = aNewTex;
77 :
78 0 : CHECK_GL_ERROR();
79 0 : return true;
80 : }
81 :
82 0 : void OpenGLSalBitmap::ImplCreateKernel(
83 : const double& fScale,
84 : const Kernel& rKernel,
85 : GLfloat*& pWeights,
86 : sal_uInt32& aKernelSize )
87 : {
88 0 : const double fSamplingRadius(rKernel.GetWidth());
89 0 : const double fScaledRadius((fScale < 1.0) ? fSamplingRadius / fScale : fSamplingRadius);
90 0 : const double fFilterFactor((fScale < 1.0) ? fScale : 1.0);
91 : int aNumberOfContributions;
92 0 : double aSum( 0 );
93 :
94 0 : aNumberOfContributions = (static_cast< sal_uInt32 >(fabs(ceil(fScaledRadius))) * 2) + 1 - 6;
95 0 : aKernelSize = aNumberOfContributions / 2 + 1;
96 :
97 : // avoid a crash for now; re-think me.
98 0 : if (aKernelSize > 16)
99 0 : aKernelSize = 16;
100 :
101 0 : pWeights = new GLfloat[16];
102 0 : memset( pWeights, 0, 16 * sizeof( GLfloat ) );
103 :
104 0 : for( sal_uInt32 i(0); i < aKernelSize; i++ )
105 : {
106 0 : const GLfloat aWeight( rKernel.Calculate( fFilterFactor * i ) );
107 0 : if( fabs( aWeight ) >= 0.0001 )
108 : {
109 0 : pWeights[i] = aWeight;
110 0 : aSum += i > 0 ? aWeight * 2 : aWeight;
111 : }
112 : }
113 :
114 0 : for( sal_uInt32 i(0); i < aKernelSize; i++ )
115 : {
116 0 : pWeights[i] /= aSum;
117 : }
118 0 : }
119 :
120 0 : bool OpenGLSalBitmap::ImplScaleConvolution(
121 : const double& rScaleX,
122 : const double& rScaleY,
123 : const Kernel& aKernel )
124 : {
125 : OpenGLFramebuffer* pFramebuffer;
126 : OpenGLProgram* pProgram;
127 0 : GLfloat* pWeights( 0 );
128 : sal_uInt32 nKernelSize;
129 : GLfloat aOffsets[32];
130 0 : int nNewWidth( mnWidth * rScaleX );
131 0 : int nNewHeight( mnHeight * rScaleY );
132 :
133 : // TODO Make sure the framebuffer is alright
134 :
135 : pProgram = mpContext->UseProgram( "textureVertexShader",
136 0 : "convolutionFragmentShader" );
137 0 : if( pProgram == 0 )
138 0 : return false;
139 :
140 : // horizontal scaling in scratch texture
141 0 : if( mnWidth != nNewWidth )
142 : {
143 0 : OpenGLTexture aScratchTex = OpenGLTexture( nNewWidth, mnHeight );
144 0 : pFramebuffer = mpContext->AcquireFramebuffer( aScratchTex );
145 :
146 0 : for( sal_uInt32 i = 0; i < 16; i++ )
147 : {
148 0 : aOffsets[i * 2] = i / (double) mnWidth;
149 0 : aOffsets[i * 2 + 1] = 0;
150 : }
151 0 : ImplCreateKernel( rScaleX, aKernel, pWeights, nKernelSize );
152 0 : pProgram->SetUniform1fv( "kernel", 16, pWeights );
153 0 : pProgram->SetUniform2fv( "offsets", 16, aOffsets );
154 0 : pProgram->SetTexture( "sampler", maTexture );
155 0 : pProgram->DrawTexture( maTexture );
156 0 : pProgram->Clean();
157 :
158 0 : maTexture = aScratchTex;
159 0 : OpenGLContext::ReleaseFramebuffer( pFramebuffer );
160 : }
161 :
162 : // vertical scaling in final texture
163 0 : if( mnHeight != nNewHeight )
164 : {
165 0 : OpenGLTexture aScratchTex = OpenGLTexture( nNewWidth, nNewHeight );
166 0 : pFramebuffer = mpContext->AcquireFramebuffer( aScratchTex );
167 :
168 0 : for( sal_uInt32 i = 0; i < 16; i++ )
169 : {
170 0 : aOffsets[i * 2] = 0;
171 0 : aOffsets[i * 2 + 1] = i / (double) mnHeight;
172 : }
173 0 : ImplCreateKernel( rScaleY, aKernel, pWeights, nKernelSize );
174 0 : pProgram->SetUniform1fv( "kernel", 16, pWeights );
175 0 : pProgram->SetUniform2fv( "offsets", 16, aOffsets );
176 0 : pProgram->SetTexture( "sampler", maTexture );
177 0 : pProgram->DrawTexture( maTexture );
178 0 : pProgram->Clean();
179 :
180 0 : maTexture = aScratchTex;
181 0 : OpenGLContext::ReleaseFramebuffer( pFramebuffer );
182 : }
183 :
184 0 : mnWidth = nNewWidth;
185 0 : mnHeight = nNewHeight;
186 :
187 0 : CHECK_GL_ERROR();
188 0 : return true;
189 : }
190 :
191 : /*
192 : "Area" scaling algorithm, which seems to give better results for downscaling
193 : than other algorithms. The principle (taken from opencv, see resize.cl)
194 : is that each resulting pixel is the average of all the source pixel values
195 : it represents. Which is trivial in the case of exact multiples for downscaling,
196 : the generic case needs to also consider that some source pixels contribute
197 : only partially to their resulting pixels (becauses of non-integer multiples).
198 : */
199 0 : bool OpenGLSalBitmap::ImplScaleArea( double rScaleX, double rScaleY )
200 : {
201 0 : int nNewWidth( mnWidth * rScaleX );
202 0 : int nNewHeight( mnHeight * rScaleY );
203 :
204 0 : if( nNewWidth == mnWidth && nNewHeight == mnHeight )
205 0 : return true;
206 :
207 0 : double ixscale = 1 / rScaleX;
208 0 : double iyscale = 1 / rScaleY;
209 0 : bool fast = ( ixscale == int( ixscale ) && iyscale == int( iyscale )
210 0 : && int( nNewWidth * ixscale ) == mnWidth && int( nNewHeight * iyscale ) == mnHeight );
211 :
212 : // The generic case has arrays only up to 100 ratio downscaling, which is hopefully enough
213 : // in practice, but protect against buffer overflows in case such an extreme case happens
214 : // (and in such case the precision of the generic algorithm probably doesn't matter anyway).
215 0 : if( ixscale > 100 || iyscale > 100 )
216 0 : fast = true;
217 :
218 : // TODO Make sure the framebuffer is alright
219 :
220 : OpenGLProgram* pProgram = mpContext->UseProgram( "textureVertexShader",
221 0 : fast ? OUString( "areaScaleFastFragmentShader" ) : OUString( "areaScaleFragmentShader" ));
222 0 : if( pProgram == 0 )
223 0 : return false;
224 :
225 0 : OpenGLTexture aScratchTex = OpenGLTexture( nNewWidth, nNewHeight );
226 0 : OpenGLFramebuffer* pFramebuffer = mpContext->AcquireFramebuffer( aScratchTex );
227 :
228 : // NOTE: This setup is also done in OpenGLSalGraphicsImpl::DrawTransformedTexture().
229 0 : if( fast )
230 : {
231 0 : pProgram->SetUniform1i( "xscale", ixscale );
232 0 : pProgram->SetUniform1i( "yscale", iyscale );
233 0 : pProgram->SetUniform1f( "xstep", 1.0 / mnWidth );
234 0 : pProgram->SetUniform1f( "ystep", 1.0 / mnHeight );
235 0 : pProgram->SetUniform1f( "ratio", 1.0 / ( ixscale * iyscale ));
236 : }
237 : else
238 : {
239 0 : pProgram->SetUniform1f( "xscale", ixscale );
240 0 : pProgram->SetUniform1f( "yscale", iyscale );
241 0 : pProgram->SetUniform1i( "swidth", mnWidth );
242 0 : pProgram->SetUniform1i( "sheight", mnHeight );
243 : // For converting between <0,mnWidth-1> and <0.0,1.0> coordinate systems.
244 0 : pProgram->SetUniform1f( "xsrcconvert", 1.0 / ( mnWidth - 1 ));
245 0 : pProgram->SetUniform1f( "ysrcconvert", 1.0 / ( mnHeight - 1 ));
246 0 : pProgram->SetUniform1f( "xdestconvert", 1.0 * ( nNewWidth - 1 ));
247 0 : pProgram->SetUniform1f( "ydestconvert", 1.0 * ( nNewHeight - 1 ));
248 : }
249 :
250 0 : pProgram->SetTexture( "sampler", maTexture );
251 0 : pProgram->DrawTexture( maTexture );
252 0 : pProgram->Clean();
253 :
254 0 : maTexture = aScratchTex;
255 0 : OpenGLContext::ReleaseFramebuffer( pFramebuffer );
256 :
257 0 : mnWidth = nNewWidth;
258 0 : mnHeight = nNewHeight;
259 :
260 0 : CHECK_GL_ERROR();
261 0 : return true;
262 : }
263 :
264 0 : bool OpenGLSalBitmap::ImplScale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
265 : {
266 : SAL_INFO( "vcl.opengl", "::ImplScale" );
267 :
268 0 : maUserBuffer.reset();
269 0 : makeCurrent();
270 :
271 0 : if( nScaleFlag == BmpScaleFlag::Fast )
272 : {
273 0 : return ImplScaleFilter( rScaleX, rScaleY, GL_NEAREST );
274 : }
275 0 : if( nScaleFlag == BmpScaleFlag::BiLinear )
276 : {
277 0 : return ImplScaleFilter( rScaleX, rScaleY, GL_LINEAR );
278 : }
279 0 : else if( nScaleFlag == BmpScaleFlag::Super || nScaleFlag == BmpScaleFlag::Default )
280 : {
281 0 : const Lanczos3Kernel aKernel;
282 :
283 0 : return ImplScaleConvolution( rScaleX, rScaleY, aKernel );
284 : }
285 0 : else if( nScaleFlag == BmpScaleFlag::BestQuality && rScaleX <= 1 && rScaleY <= 1 )
286 : { // Use are scaling for best quality, but only if downscaling.
287 0 : return ImplScaleArea( rScaleX, rScaleY );
288 : }
289 0 : else if( nScaleFlag == BmpScaleFlag::Lanczos || nScaleFlag == BmpScaleFlag::BestQuality )
290 : {
291 0 : const Lanczos3Kernel aKernel;
292 :
293 0 : return ImplScaleConvolution( rScaleX, rScaleY, aKernel );
294 : }
295 :
296 : SAL_WARN( "vcl.opengl", "Invalid flag for scaling operation" );
297 0 : return false;
298 : }
299 :
300 0 : ScaleOp::ScaleOp(
301 : OpenGLSalBitmap* pBitmap,
302 : const double& rScaleX,
303 : const double& rScaleY,
304 : BmpScaleFlag nScaleFlag )
305 : : mpBitmap( pBitmap )
306 : , mfScaleX( rScaleX )
307 : , mfScaleY( rScaleY )
308 0 : , mnScaleFlag( nScaleFlag )
309 : {
310 0 : }
311 :
312 0 : bool ScaleOp::Execute()
313 : {
314 : SAL_INFO( "vcl.opengl", "::Execute" );
315 0 : return mpBitmap->ImplScale( mfScaleX, mfScaleY, mnScaleFlag );
316 : }
317 :
318 0 : void ScaleOp::GetSize( Size& rSize ) const
319 : {
320 : SAL_INFO( "vcl.opengl", "::GetSize" );
321 0 : rSize.setWidth( rSize.Width() * mfScaleX );
322 0 : rSize.setHeight( rSize.Height() * mfScaleY );
323 0 : }
324 :
325 0 : bool OpenGLSalBitmap::Scale( const double& rScaleX, const double& rScaleY, BmpScaleFlag nScaleFlag )
326 : {
327 : SAL_INFO( "vcl.opengl", "::Scale " << static_cast<int>(nScaleFlag) );
328 :
329 0 : if( nScaleFlag == BmpScaleFlag::Fast ||
330 0 : nScaleFlag == BmpScaleFlag::BiLinear ||
331 0 : nScaleFlag == BmpScaleFlag::Super ||
332 0 : nScaleFlag == BmpScaleFlag::Lanczos ||
333 0 : nScaleFlag == BmpScaleFlag::Default ||
334 : nScaleFlag == BmpScaleFlag::BestQuality )
335 : {
336 0 : makeCurrent();
337 0 : if( mpContext == NULL )
338 : {
339 : SAL_INFO( "vcl.opengl", "Add ScaleOp to pending operations" );
340 0 : maPendingOps.push_back( new ScaleOp( this, rScaleX, rScaleY, nScaleFlag ) );
341 : }
342 : else
343 : {
344 0 : ImplScale( rScaleX, rScaleY, nScaleFlag );
345 : }
346 0 : return true;
347 : }
348 :
349 0 : return false;
350 : }
351 :
352 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|