1 /**************************************************************************
2 * This file is part of the Fraqtive program
3 * Copyright (C) 2004-2012 Michał Męciński
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 **************************************************************************/
18 
19 #include "datafunctions.h"
20 #include "fractaldata.h"
21 
22 #include <QPainterPath>
23 
24 namespace DataFunctions
25 {
26 
defaultPosition(const FractalType & type)27 Position defaultPosition( const FractalType& type )
28 {
29     Position position;
30     position.setZoomFactor( -0.45 );
31 
32     if ( type.fractal() == MandelbrotFractal && type.exponentType() == IntegralExponent ) {
33         if ( type.variant() == GeneratorCore::NormalVariant && type.integralExponent() == 2 ) {
34             position.setCenter( QPointF( -0.7, 0 ) );
35         } else if ( type.variant() == GeneratorCore::NormalVariant && type.integralExponent() == 4 ) {
36             position.setCenter( QPointF( -0.2, 0 ) );
37         } else if ( type.variant() == GeneratorCore::ConjugateVariant && type.integralExponent() == 2 ) {
38             position.setCenter( QPointF( -0.3, 0 ) );
39             position.setZoomFactor( -0.6 );
40         } else if ( type.variant() == GeneratorCore::AbsoluteVariant && type.integralExponent() == 2 ) {
41             position.setCenter( QPointF( -0.5, -0.5 ) );
42             position.setZoomFactor( -0.5 );
43         } else if ( type.variant() == GeneratorCore::AbsoluteVariant && type.integralExponent() == 3 ) {
44             position.setCenter( QPointF( -0.15, 0.15 ) );
45         } else if ( type.variant() == GeneratorCore::AbsoluteImVariant && type.integralExponent() == 2 ) {
46             position.setCenter( QPointF( -0.5, 0 ) );
47             position.setZoomFactor( -0.5 );
48         } else if ( type.variant() == GeneratorCore::AbsoluteImVariant && type.integralExponent() == 3 ) {
49             position.setCenter( QPointF( 0, 0.15 ) );
50         }
51     }
52 
53     return position;
54 }
55 
defaultGradient()56 Gradient defaultGradient()
57 {
58     QPolygonF red;
59     red.append( QPointF( 0.0, 0.0 ) );
60     red.append( QPointF( 0.1667, 1.0 ) );
61     red.append( QPointF( 0.5, 0.0 ) );
62     red.append( QPointF( 0.8333, 1.0 ) );
63     red.append( QPointF( 1.0, 0.0 ) );
64 
65     QPolygonF green;
66     green.append( QPointF( 0.0, 0.0 ) );
67     green.append( QPointF( 0.25, 1.0 ) );
68     green.append( QPointF( 0.5, 0.0 ) );
69     green.append( QPointF( 0.6667, 1.0 ) );
70     green.append( QPointF( 1.0, 0.0 ) );
71 
72     QPolygonF blue;
73     blue.append( QPointF( 0.0, 0.0 ) );
74     blue.append( QPointF( 0.3333, 1.0 ) );
75     blue.append( QPointF( 0.5, 0.0 ) );
76     blue.append( QPointF( 0.75, 1.0 ) );
77     blue.append( QPointF( 1.0, 0.0 ) );
78 
79     Gradient gradient;
80     gradient.setRed( red );
81     gradient.setGreen( green );
82     gradient.setBlue( blue );
83 
84     return gradient;
85 }
86 
defaultColorMapping()87 ColorMapping defaultColorMapping()
88 {
89     ColorMapping mapping;
90 
91     mapping.setScale( 0.056 );
92 
93     return mapping;
94 }
95 
defaultGeneratorSettings()96 GeneratorSettings defaultGeneratorSettings()
97 {
98     GeneratorSettings settings;
99 
100     settings.setCalculationDepth( 2.5 );
101     settings.setDetailThreshold( 0.9 );
102 
103     return settings;
104 }
105 
defaultViewSettings()106 ViewSettings defaultViewSettings()
107 {
108     ViewSettings settings;
109 
110     settings.setAntiAliasing( MediumAntiAliasing );
111     settings.setMeshResolution( MediumResolution );
112     settings.setHeightScale( 0.14 );
113     settings.setCameraZoom( 24.0 );
114 
115     return settings;
116 }
117 
118 
interpolateCubic(const QPolygonF & polygon)119 static QPolygonF interpolateCubic( const QPolygonF& polygon )
120 {
121     QPolygonF interpolated;
122 
123     if ( !polygon.isEmpty() )
124         interpolated.append( polygon.first() );
125 
126     for ( int i = 1; i < polygon.count(); i++ ) {
127         QPointF p1 = polygon.at( i - 1 );
128         QPointF p2 = polygon.at( i );
129         if ( ( p2.x() - p1.x() ) > 0.03 ) {
130             QPainterPath path;
131             path.moveTo( p1 );
132             path.cubicTo( ( p1.x() + p2.x() ) / 2, p1.y(), ( p1.x() + p2.x() ) / 2, p2.y(), p2.x(), p2.y() );
133             int segments = qBound( 1, qRound( ( p2.x() - p1.x() ) * 50.0 ), 10 );
134             for ( int j = 1; j <= segments; j++ ) {
135                 QPointF point = path.pointAtPercent( (double)j / (double)segments );
136                 interpolated.append( point );
137             }
138         } else {
139             interpolated.append( p2 );
140         }
141     }
142 
143     return interpolated;
144 }
145 
lerp(const QPolygonF & points,int index,double x)146 static double lerp( const QPolygonF& points, int index, double x )
147 {
148     QLineF line( points.at( index - 1 ), points.at( index ) );
149     if ( line.dx() == 0.0 )
150         return line.y2();
151     return line.y1() + line.dy() * ( x - line.x1() ) / line.dx();
152 }
153 
makeStop(double x,double ry,double gy,double by)154 static QGradientStop makeStop( double x, double ry, double gy, double by )
155 {
156     return QGradientStop( x, QColor( qRound( 255.0 * ry ), qRound( 255.0 * gy ), qRound( 255.0 * by ) ) );
157 }
158 
calculateGradientStops(const Gradient & gradient)159 QGradientStops calculateGradientStops( const Gradient& gradient )
160 {
161     QPolygonF red = interpolateCubic( gradient.red() );
162     QPolygonF green = interpolateCubic( gradient.green() );
163     QPolygonF blue = interpolateCubic( gradient.blue() );
164 
165     QGradientStops stops;
166 
167     if ( red.isEmpty() || green.isEmpty() || blue.isEmpty() )
168         return stops;
169 
170     stops.append( makeStop( 0.0, red.first().y(), green.first().y(), blue.first().y() ) );
171 
172     double x = 0.0;
173     int ri = 1, gi = 1, bi = 1;
174     while ( ri < red.count() && gi < green.count() && bi < blue.count() ) {
175         double rx = red.at( ri ).x();
176         double gx = green.at( gi ).x();
177         double bx = blue.at( bi ).x();
178         x = qMin( qMin( rx, gx ), bx );
179         stops.append( makeStop( x, lerp( red, ri, x ), lerp( green, gi, x ), lerp( blue, bi, x ) ) );
180         if ( rx == x )
181             ri++;
182         if ( gx == x )
183             gi++;
184         if ( bx == x )
185             bi++;
186     }
187 
188     return stops;
189 }
190 
fillGradientCache(const Gradient & gradient,QRgb * cache,int size)191 void fillGradientCache( const Gradient& gradient, QRgb* cache, int size )
192 {
193     if ( gradient.isEmpty() ) {
194         for ( int i = 0; i < size; i++ )
195             cache[ i ] = qRgb( 0, 0, 0 );
196         return;
197     }
198 
199     QPolygonF red = interpolateCubic( gradient.red() );
200     QPolygonF green = interpolateCubic( gradient.green() );
201     QPolygonF blue = interpolateCubic( gradient.blue() );
202 
203     int ri = 1, gi = 1, bi = 1;
204     for ( int i = 0; i < size; i++ ) {
205         double x = (double)i / (double)( size - 1 );
206         while ( red.at( ri ).x() < x )
207             ri++;
208         while ( green.at( gi ).x() < x )
209             gi++;
210         while ( blue.at( bi ).x() < x )
211             bi++;
212         double ry = lerp( red, ri, x );
213         double gy = lerp( green, gi, x );
214         double by = lerp( blue, bi, x );
215         cache[ i ] = qRgb( qRound( 255.0 * ry ), qRound( 255.0 * gy ), qRound( 255.0 * by ) );
216     }
217 }
218 
ColorMapper(const QRgb * gradientCache,int gradientSize,QRgb backgroundColor,const ColorMapping & mapping)219 ColorMapper::ColorMapper( const QRgb* gradientCache, int gradientSize, QRgb backgroundColor, const ColorMapping& mapping ) :
220     m_gradientCache( gradientCache ),
221     m_gradientSize( gradientSize ),
222     m_backgroundColor( backgroundColor ),
223     m_mapping( mapping )
224 {
225 }
226 
map(double value) const227 QRgb ColorMapper::map( double value ) const
228 {
229     if ( value == 0.0 )
230         return m_backgroundColor;
231 
232     int index;
233     if ( m_mapping.isMirrored() ) {
234         int scaled = (int)( ( m_mapping.scale() * value + 2 * m_mapping.offset() ) * m_gradientSize );
235         index = scaled % ( 2 * m_gradientSize );
236         if ( index >= m_gradientSize )
237             index = 2 * m_gradientSize - index - 1;
238     } else {
239         int scaled = (int)( ( m_mapping.scale() * value + m_mapping.offset() ) * m_gradientSize );
240         index = scaled % m_gradientSize;
241     }
242 
243     if ( m_mapping.isReversed() )
244         index = m_gradientSize - index - 1;
245 
246     return m_gradientCache[ index ];
247 }
248 
249 template<int A, int B, int C>
maskedSum(QRgb color[3][3],int mask)250 static inline QRgb maskedSum( QRgb color[ 3 ][ 3 ], int mask )
251 {
252     QRgb middle = ( color[ 1 ][ 1 ] & mask );
253     QRgb sides = ( color[ 0 ][ 1 ] & mask ) + ( color[ 2 ][ 1 ] & mask ) + ( color[ 1 ][ 0 ] & mask ) + ( color[ 1 ][ 2 ] & mask );
254     QRgb corners = ( color[ 0 ][ 0 ] & mask ) + ( color[ 2 ][ 0 ] & mask ) + ( color[ 0 ][ 2 ] & mask ) + ( color[ 2 ][ 2 ] & mask );
255     return A * middle + B * sides + C * corners;
256 }
257 
258 template<int A, int B, int C>
calcAntiAliased(QRgb color[3][3])259 static inline QRgb calcAntiAliased( QRgb color[ 3 ][ 3 ] )
260 {
261     QRgb sum = maskedSum<A, B, C>( color, 0xf0f0f0 );
262     QRgb carry = maskedSum<A, B, C>( color, 0x0f0f0f ) & 0xf0f0f0;
263     return ( ( sum + carry ) >> 4 ) | 0xff000000;
264 }
265 
266 template<int A, int B, int C>
drawAntiAliased(QImage & image,const QPoint & point,const FractalData * data,const QRect & region,const ColorMapper & mapper)267 static void drawAntiAliased( QImage& image, const QPoint& point, const FractalData* data, const QRect& region, const ColorMapper& mapper )
268 {
269     QRgb color[ 3 ][ 3 ];
270 
271     int stride = data->stride();
272     int width = region.width();
273 
274     for ( int y = 0; y < region.height(); y++ ) {
275         const double* src = data->buffer() + ( y + region.top() ) * stride + region.left() ;
276         QRgb* dest = reinterpret_cast<QRgb*>( image.scanLine( y + point.y() ) ) + point.x();
277 
278         for ( int i = 0; i < 3; i++ ) {
279             color[ i ][ 1 ] = mapper.map( src[ i * stride ] );
280             color[ i ][ 2 ] = mapper.map( src[ i * stride + 1 ] );
281         }
282 
283         for ( int x = 0; x < width; x++ ) {
284             for ( int i = 0; i < 3; i++ ) {
285                 color[ i ][ 0 ] = color[ i ][ 1 ];
286                 color[ i ][ 1 ] = color[ i ][ 2 ];
287                 color[ i ][ 2 ] = mapper.map( src[ i * stride + x + 2 ] );
288             }
289             dest[ x ] = calcAntiAliased<A, B, C>( color );
290         }
291     }
292 }
293 
drawImage(QImage & image,const FractalData * data,const QRect & region,const ColorMapper & mapper,AntiAliasing antiAliasing)294 void drawImage( QImage& image, const FractalData* data, const QRect& region, const ColorMapper& mapper, AntiAliasing antiAliasing )
295 {
296     drawImage( image, region.topLeft(), data, region, mapper, antiAliasing );
297 }
298 
drawImage(QImage & image,const QPoint & point,const FractalData * data,const QRect & region,const ColorMapper & mapper,AntiAliasing antiAliasing)299 void drawImage( QImage& image, const QPoint& point, const FractalData* data, const QRect& region, const ColorMapper& mapper, AntiAliasing antiAliasing )
300 {
301     switch ( antiAliasing ) {
302         case NoAntiAliasing:
303             drawAntiAliased<16, 0, 0>( image, point, data, region, mapper );
304             break;
305         case LowAntiAliasing:
306             drawAntiAliased<12, 1, 0>( image, point, data, region, mapper );
307             break;
308         case MediumAntiAliasing:
309             drawAntiAliased<8, 1, 1>( image, point, data, region, mapper );
310             break;
311         case HighAntiAliasing:
312             drawAntiAliased<4, 2, 1>( image, point, data, region, mapper );
313             break;
314     }
315 }
316 
createFunctor(const FractalType & type)317 GeneratorCore::Functor* createFunctor( const FractalType& type )
318 {
319     switch ( type.exponentType() ) {
320         case IntegralExponent:
321             if ( type.fractal() == MandelbrotFractal ) {
322                 return GeneratorCore::createMandelbrotFastFunctor( type.integralExponent(), type.variant() );
323             } else {
324                 return GeneratorCore::createJuliaFastFunctor( type.parameter().x(),
325                     type.parameter().y(), type.integralExponent(), type.variant() );
326             }
327             break;
328 
329         case RealExponent:
330             if ( type.fractal() == MandelbrotFractal ) {
331                 return GeneratorCore::createMandelbrotFunctor( type.realExponent(), type.variant() );
332             } else if ( type.fractal() == JuliaFractal ) {
333                 return GeneratorCore::createJuliaFunctor( type.parameter().x(),
334                     type.parameter().y(), type.realExponent(), type.variant() );
335             }
336             break;
337     }
338 
339     return NULL;
340 }
341 
342 #if defined( HAVE_SSE2 )
343 
createFunctorSSE2(const FractalType & type)344 GeneratorCore::FunctorSSE2* createFunctorSSE2( const FractalType& type )
345 {
346     switch ( type.exponentType() ) {
347         case IntegralExponent:
348             if ( GeneratorCore::isSSE2Available() ) {
349                 if ( type.fractal() == MandelbrotFractal ) {
350                     return GeneratorCore::createMandelbrotFunctorSSE2( type.integralExponent(), type.variant() );
351                 } else if ( type.fractal() == JuliaFractal ) {
352                     return GeneratorCore::createJuliaFunctorSSE2( type.parameter().x(),
353                         type.parameter().y(), type.integralExponent(), type.variant() );
354                 }
355             }
356             break;
357 
358         default:
359             break;
360     }
361 
362     return NULL;
363 }
364 
365 #endif
366 
367 } // namespace DataFunctions
368