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 "bookmarkmodel.h"
20
21 #include <math.h>
22
23 #ifndef M_PI
24 # define M_PI 3.14159265358979323846
25 #endif
26
27 #include <QPalette>
28 #include <QPainter>
29 #include <QIcon>
30
31 #include "fraqtiveapplication.h"
32 #include "fractaldata.h"
33 #include "jobscheduler.h"
34 #include "datafunctions.h"
35
Q_DECLARE_METATYPE(QModelIndex)36 Q_DECLARE_METATYPE( QModelIndex )
37
38 BookmarkModel::BookmarkModel( QObject* parent ) : QAbstractListModel( parent ),
39 m_gradientCache( NULL ),
40 m_enabled( false ),
41 m_activeJobs( 0 )
42 {
43 qRegisterMetaType<QModelIndex>();
44 }
45
~BookmarkModel()46 BookmarkModel::~BookmarkModel()
47 {
48 QMutexLocker locker( &m_mutex );
49
50 cancelJobs();
51
52 while ( m_activeJobs > 0 )
53 m_allJobsDone.wait( &m_mutex );
54
55 delete[] m_gradientCache;
56 }
57
setMap(BookmarkMap * map)58 void BookmarkModel::setMap( BookmarkMap* map )
59 {
60 m_map = map;
61
62 update();
63
64 m_enabled = true;
65 m_queue = m_keys;
66 }
67
68 static const int GradientSize = 16384;
69
setColorSettings(const Gradient & gradient,const QColor & backgroundColor,const ColorMapping & mapping)70 void BookmarkModel::setColorSettings( const Gradient& gradient, const QColor& backgroundColor, const ColorMapping& mapping )
71 {
72 QMutexLocker locker( &m_mutex );
73
74 cancelJobs();
75
76 if ( !m_gradientCache )
77 m_gradientCache = new QRgb[ GradientSize ];
78
79 DataFunctions::fillGradientCache( gradient, m_gradientCache, GradientSize );
80
81 m_backgroundColor = backgroundColor;
82 m_colorMapping = mapping;
83 }
84
abortGeneration()85 void BookmarkModel::abortGeneration()
86 {
87 QMutexLocker locker( &m_mutex );
88
89 m_enabled = false;
90
91 cancelJobs();
92 }
93
continueGeneration()94 void BookmarkModel::continueGeneration()
95 {
96 QMutexLocker locker( &m_mutex );
97
98 m_enabled = true;
99
100 addJobs();
101 }
102
invalidateBookmark(const QString & name)103 void BookmarkModel::invalidateBookmark( const QString& name )
104 {
105 QMutexLocker locker( &m_mutex );
106
107 m_images.remove( name );
108
109 if ( !m_queue.contains( name ) ) {
110 m_queue.append( name );
111 addJobs( 1 );
112 }
113 }
114
localeAwareLessThan(const QString & s1,const QString & s2)115 static bool localeAwareLessThan( const QString& s1, const QString& s2 )
116 {
117 return QString::localeAwareCompare( s1, s2 ) < 0;
118 }
119
update()120 void BookmarkModel::update()
121 {
122 QMutexLocker locker( &m_mutex );
123
124 cancelJobs();
125
126 beginResetModel();
127
128 m_keys = m_map->keys();
129 qSort( m_keys.begin(), m_keys.end(), localeAwareLessThan );
130
131 endResetModel();
132 }
133
rowCount(const QModelIndex & parent) const134 int BookmarkModel::rowCount( const QModelIndex& parent ) const
135 {
136 if ( !parent.isValid() )
137 return m_keys.count();
138 return 0;
139 }
140
data(const QModelIndex & index,int role) const141 QVariant BookmarkModel::data( const QModelIndex& index, int role ) const
142 {
143 if ( role == Qt::DisplayRole )
144 return m_keys.at( index.row() );
145
146 if ( role == Qt::DecorationRole ) {
147 QMutexLocker locker( &m_mutex );
148
149 QString name = m_keys.at( index.row() );
150
151 QPixmap pixmap;
152 if ( m_images.contains( name ) ) {
153 pixmap = QPixmap::fromImage( m_images.value( name ) );
154 } else {
155 pixmap = QPixmap( 48, 48 );
156 pixmap.fill( QApplication::palette().color( QPalette::Disabled, QPalette::Window ) );
157 }
158
159 locker.unlock();
160
161 QPainter painter( &pixmap );
162
163 QPixmap pixmap2 = pixmap;
164 QPainter painter2( &pixmap2 );
165
166 painter.setPen( QApplication::palette().color( QPalette::Dark ) );
167 painter.drawRect( pixmap.rect().adjusted( 0, 0, -1, -1 ) );
168
169 painter2.setPen( QApplication::palette().color( QPalette::Highlight ) );
170 painter2.drawRect( pixmap.rect().adjusted( 0, 0, -1, -1 ) );
171 painter2.drawRect( pixmap.rect().adjusted( 1, 1, -2, -2 ) );
172
173 QIcon icon;
174 icon.addPixmap( pixmap, QIcon::Normal );
175 icon.addPixmap( pixmap2, QIcon::Selected );
176
177 return icon;
178 }
179
180 return QVariant();
181 }
182
priority() const183 int BookmarkModel::priority() const
184 {
185 return 1;
186 }
187
roundToCellSize(int size)188 static int roundToCellSize( int size )
189 {
190 // round up to nearest N * CellSize + 1
191 return ( ( size - 1 + GeneratorCore::CellSize - 1 ) / GeneratorCore::CellSize ) * GeneratorCore::CellSize + 1;
192 }
193
executeJob()194 void BookmarkModel::executeJob()
195 {
196 QMutexLocker locker( &m_mutex );
197
198 if ( !m_enabled || m_queue.isEmpty() ) {
199 finishJob();
200 return;
201 }
202
203 QString name = m_queue.takeFirst();
204
205 if ( !m_map->contains( name ) ) {
206 finishJob();
207 return;
208 }
209
210 Bookmark bookmark = m_map->value( name );
211
212 locker.unlock();
213
214 const int imageSize = 48;
215 const int bufferSize = roundToCellSize( imageSize + 2 ); // 2 pixel margin for anti-aliasing
216
217 double* buffer = new double[ bufferSize * bufferSize ];
218
219 calculate( bookmark, buffer, QSize( bufferSize, bufferSize ), QSize( imageSize, imageSize ) );
220
221 FractalData data;
222 data.transferBuffer( buffer, bufferSize, QSize( imageSize, imageSize ) );
223
224 QImage image( imageSize, imageSize, QImage::Format_RGB32 );
225
226 ViewSettings settings = DataFunctions::defaultViewSettings();
227
228 DataFunctions::ColorMapper mapper( m_gradientCache, GradientSize, m_backgroundColor.rgb(), m_colorMapping );
229 DataFunctions::drawImage( image, &data, image.rect(), mapper, settings.antiAliasing() );
230
231 locker.relock();
232
233 if ( m_queue.contains( name ) ) {
234 finishJob();
235 return;
236 }
237
238 m_images.insert( name, image );
239
240 int row = m_keys.indexOf( name );
241 if ( row >= 0 )
242 emit dataChanged( index( row ), index( row ) );
243
244 finishJob();
245 }
246
calculate(const Bookmark & bookmark,double * buffer,const QSize & size,const QSize & resolution)247 void BookmarkModel::calculate( const Bookmark& bookmark, double* buffer, const QSize& size, const QSize& resolution )
248 {
249 GeneratorCore::Input input;
250
251 Position position = bookmark.position();
252
253 double scale = pow( 10.0, -position.zoomFactor() ) / (double)resolution.height();
254
255 double sa = scale * sin( position.angle() * M_PI / 180.0 );
256 double ca = scale * cos( position.angle() * M_PI / 180.0 );
257
258 double offsetX = -(double)resolution.width() / 2.0 - 0.5;
259 double offsetY = -(double)resolution.height() / 2.0 - 0.5;
260
261 input.m_sa = sa;
262 input.m_ca = ca;
263 input.m_x = position.center().x() + ca * offsetX + sa * offsetY;
264 input.m_y = position.center().y() - sa * offsetX + ca * offsetY;
265
266 GeneratorCore::Output output;
267
268 output.m_buffer = buffer;
269 output.m_width = size.width();
270 output.m_height = size.height();
271 output.m_stride = size.width();
272
273 GeneratorSettings settings = DataFunctions::defaultGeneratorSettings();
274
275 int maxIterations = (int)( pow( 10.0, settings.calculationDepth() ) * qMax( 1.0, 1.45 + position.zoomFactor() ) );
276 double threshold = settings.detailThreshold();
277
278 #if defined( HAVE_SSE2 )
279 GeneratorCore::FunctorSSE2* functorSSE2 = DataFunctions::createFunctorSSE2( bookmark.fractalType() );
280 if ( functorSSE2 ) {
281 GeneratorCore::generatePreviewSSE2( input, output, functorSSE2, maxIterations );
282 GeneratorCore::interpolate( output );
283 GeneratorCore::generateDetailsSSE2( input, output, functorSSE2, maxIterations, threshold );
284 delete functorSSE2;
285 return;
286 }
287 #endif
288
289 GeneratorCore::Functor* functor = DataFunctions::createFunctor( bookmark.fractalType() );
290 if ( functor ) {
291 GeneratorCore::generatePreview( input, output, functor, maxIterations );
292 GeneratorCore::interpolate( output );
293 GeneratorCore::generateDetails( input, output, functor, maxIterations, threshold );
294 delete functor;
295 }
296 }
297
addJobs(int count)298 void BookmarkModel::addJobs( int count /*= -1*/ )
299 {
300 if ( count < 0 )
301 count = m_queue.count();
302 if ( count > 0 ) {
303 fraqtive()->jobScheduler()->addJobs( this, count );
304 m_activeJobs += count;
305 }
306 }
307
cancelJobs()308 void BookmarkModel::cancelJobs()
309 {
310 int count = fraqtive()->jobScheduler()->cancelAllJobs( this );
311 m_activeJobs -= count;
312
313 if ( m_activeJobs == 0 )
314 m_allJobsDone.wakeAll();
315 }
316
finishJob()317 void BookmarkModel::finishJob()
318 {
319 m_activeJobs--;
320
321 if ( m_activeJobs == 0 )
322 m_allJobsDone.wakeAll();
323 }
324