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