/************************************************************************** * This file is part of the Fraqtive program * Copyright (C) 2004-2012 Michał Męciński * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . **************************************************************************/ #include "bookmarkmodel.h" #include #ifndef M_PI # define M_PI 3.14159265358979323846 #endif #include #include #include #include "fraqtiveapplication.h" #include "fractaldata.h" #include "jobscheduler.h" #include "datafunctions.h" Q_DECLARE_METATYPE( QModelIndex ) BookmarkModel::BookmarkModel( QObject* parent ) : QAbstractListModel( parent ), m_gradientCache( NULL ), m_enabled( false ), m_activeJobs( 0 ) { qRegisterMetaType(); } BookmarkModel::~BookmarkModel() { QMutexLocker locker( &m_mutex ); cancelJobs(); while ( m_activeJobs > 0 ) m_allJobsDone.wait( &m_mutex ); delete[] m_gradientCache; } void BookmarkModel::setMap( BookmarkMap* map ) { m_map = map; update(); m_enabled = true; m_queue = m_keys; } static const int GradientSize = 16384; void BookmarkModel::setColorSettings( const Gradient& gradient, const QColor& backgroundColor, const ColorMapping& mapping ) { QMutexLocker locker( &m_mutex ); cancelJobs(); if ( !m_gradientCache ) m_gradientCache = new QRgb[ GradientSize ]; DataFunctions::fillGradientCache( gradient, m_gradientCache, GradientSize ); m_backgroundColor = backgroundColor; m_colorMapping = mapping; } void BookmarkModel::abortGeneration() { QMutexLocker locker( &m_mutex ); m_enabled = false; cancelJobs(); } void BookmarkModel::continueGeneration() { QMutexLocker locker( &m_mutex ); m_enabled = true; addJobs(); } void BookmarkModel::invalidateBookmark( const QString& name ) { QMutexLocker locker( &m_mutex ); m_images.remove( name ); if ( !m_queue.contains( name ) ) { m_queue.append( name ); addJobs( 1 ); } } static bool localeAwareLessThan( const QString& s1, const QString& s2 ) { return QString::localeAwareCompare( s1, s2 ) < 0; } void BookmarkModel::update() { QMutexLocker locker( &m_mutex ); cancelJobs(); beginResetModel(); m_keys = m_map->keys(); qSort( m_keys.begin(), m_keys.end(), localeAwareLessThan ); endResetModel(); } int BookmarkModel::rowCount( const QModelIndex& parent ) const { if ( !parent.isValid() ) return m_keys.count(); return 0; } QVariant BookmarkModel::data( const QModelIndex& index, int role ) const { if ( role == Qt::DisplayRole ) return m_keys.at( index.row() ); if ( role == Qt::DecorationRole ) { QMutexLocker locker( &m_mutex ); QString name = m_keys.at( index.row() ); QPixmap pixmap; if ( m_images.contains( name ) ) { pixmap = QPixmap::fromImage( m_images.value( name ) ); } else { pixmap = QPixmap( 48, 48 ); pixmap.fill( QApplication::palette().color( QPalette::Disabled, QPalette::Window ) ); } locker.unlock(); QPainter painter( &pixmap ); QPixmap pixmap2 = pixmap; QPainter painter2( &pixmap2 ); painter.setPen( QApplication::palette().color( QPalette::Dark ) ); painter.drawRect( pixmap.rect().adjusted( 0, 0, -1, -1 ) ); painter2.setPen( QApplication::palette().color( QPalette::Highlight ) ); painter2.drawRect( pixmap.rect().adjusted( 0, 0, -1, -1 ) ); painter2.drawRect( pixmap.rect().adjusted( 1, 1, -2, -2 ) ); QIcon icon; icon.addPixmap( pixmap, QIcon::Normal ); icon.addPixmap( pixmap2, QIcon::Selected ); return icon; } return QVariant(); } int BookmarkModel::priority() const { return 1; } static int roundToCellSize( int size ) { // round up to nearest N * CellSize + 1 return ( ( size - 1 + GeneratorCore::CellSize - 1 ) / GeneratorCore::CellSize ) * GeneratorCore::CellSize + 1; } void BookmarkModel::executeJob() { QMutexLocker locker( &m_mutex ); if ( !m_enabled || m_queue.isEmpty() ) { finishJob(); return; } QString name = m_queue.takeFirst(); if ( !m_map->contains( name ) ) { finishJob(); return; } Bookmark bookmark = m_map->value( name ); locker.unlock(); const int imageSize = 48; const int bufferSize = roundToCellSize( imageSize + 2 ); // 2 pixel margin for anti-aliasing double* buffer = new double[ bufferSize * bufferSize ]; calculate( bookmark, buffer, QSize( bufferSize, bufferSize ), QSize( imageSize, imageSize ) ); FractalData data; data.transferBuffer( buffer, bufferSize, QSize( imageSize, imageSize ) ); QImage image( imageSize, imageSize, QImage::Format_RGB32 ); ViewSettings settings = DataFunctions::defaultViewSettings(); DataFunctions::ColorMapper mapper( m_gradientCache, GradientSize, m_backgroundColor.rgb(), m_colorMapping ); DataFunctions::drawImage( image, &data, image.rect(), mapper, settings.antiAliasing() ); locker.relock(); if ( m_queue.contains( name ) ) { finishJob(); return; } m_images.insert( name, image ); int row = m_keys.indexOf( name ); if ( row >= 0 ) emit dataChanged( index( row ), index( row ) ); finishJob(); } void BookmarkModel::calculate( const Bookmark& bookmark, double* buffer, const QSize& size, const QSize& resolution ) { GeneratorCore::Input input; Position position = bookmark.position(); double scale = pow( 10.0, -position.zoomFactor() ) / (double)resolution.height(); double sa = scale * sin( position.angle() * M_PI / 180.0 ); double ca = scale * cos( position.angle() * M_PI / 180.0 ); double offsetX = -(double)resolution.width() / 2.0 - 0.5; double offsetY = -(double)resolution.height() / 2.0 - 0.5; input.m_sa = sa; input.m_ca = ca; input.m_x = position.center().x() + ca * offsetX + sa * offsetY; input.m_y = position.center().y() - sa * offsetX + ca * offsetY; GeneratorCore::Output output; output.m_buffer = buffer; output.m_width = size.width(); output.m_height = size.height(); output.m_stride = size.width(); GeneratorSettings settings = DataFunctions::defaultGeneratorSettings(); int maxIterations = (int)( pow( 10.0, settings.calculationDepth() ) * qMax( 1.0, 1.45 + position.zoomFactor() ) ); double threshold = settings.detailThreshold(); #if defined( HAVE_SSE2 ) GeneratorCore::FunctorSSE2* functorSSE2 = DataFunctions::createFunctorSSE2( bookmark.fractalType() ); if ( functorSSE2 ) { GeneratorCore::generatePreviewSSE2( input, output, functorSSE2, maxIterations ); GeneratorCore::interpolate( output ); GeneratorCore::generateDetailsSSE2( input, output, functorSSE2, maxIterations, threshold ); delete functorSSE2; return; } #endif GeneratorCore::Functor* functor = DataFunctions::createFunctor( bookmark.fractalType() ); if ( functor ) { GeneratorCore::generatePreview( input, output, functor, maxIterations ); GeneratorCore::interpolate( output ); GeneratorCore::generateDetails( input, output, functor, maxIterations, threshold ); delete functor; } } void BookmarkModel::addJobs( int count /*= -1*/ ) { if ( count < 0 ) count = m_queue.count(); if ( count > 0 ) { fraqtive()->jobScheduler()->addJobs( this, count ); m_activeJobs += count; } } void BookmarkModel::cancelJobs() { int count = fraqtive()->jobScheduler()->cancelAllJobs( this ); m_activeJobs -= count; if ( m_activeJobs == 0 ) m_allJobsDone.wakeAll(); } void BookmarkModel::finishJob() { m_activeJobs--; if ( m_activeJobs == 0 ) m_allJobsDone.wakeAll(); }