Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions apps/Launcher/qml/FileBrowser.qml
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,14 @@ Rectangle {
GradientStop { position: 0.0; color: Style.placeholderTopColor }
GradientStop { position: 1.0; color: Style.placeholderBottomColor }
}
Image {
id: thumbnail
anchors.fill: parent
source: "image://thumbnail/" + filePath
fillMode: Image.PreserveAspectFit
}
visible: thumbnail.status !== Image.Ready
}
Image {
id: thumbnail
anchors.fill: placeholder
fillMode: Image.PreserveAspectFit
cache: false
source: "image://thumbnail/" + filePath
}

Text {
Expand Down
5 changes: 5 additions & 0 deletions doc/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ Changelog {#Changelog}

# git master (1.1.0)

* [46](https://github.com/BlueBrain/Tide/pull/46):
Improved Launcher:
- The creation and caching of file thumbnails is more efficient.
- File thumbnails are displayed with their correct aspect ratio.
Session preview images are of better quality (no longer pixelated).
* [38](https://github.com/BlueBrain/Tide/pull/38):
Fix a segfault that occured when opening grayscale images.
* [36](https://github.com/BlueBrain/Tide/pull/36):
Expand Down
13 changes: 7 additions & 6 deletions tests/cpp/core/StateSerializationTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -414,16 +414,17 @@ BOOST_AUTO_TEST_CASE( testStateSerializationToFile )
BOOST_CHECK_LT( previewError, 0.02f );

// 4) Test restoring
DisplayGroupPtr loadedDisplayGroup = boost::make_shared<DisplayGroup>( wallSize );
StateSerializationHelper loader( loadedDisplayGroup );
DisplayGroupPtr loadedGroup = boost::make_shared<DisplayGroup>( wallSize );
StateSerializationHelper loader( loadedGroup );
BOOST_CHECK( loader.load( TEST_DIR + "/test.dcx" ));

BOOST_REQUIRE_EQUAL( loadedDisplayGroup->getContentWindows().size(),
BOOST_REQUIRE_EQUAL( loadedGroup->getContentWindows().size(),
displayGroup->getContentWindows().size( ));
BOOST_REQUIRE_EQUAL( loadedDisplayGroup->getShowWindowTitles(),
BOOST_REQUIRE_EQUAL( loadedGroup->getShowWindowTitles(),
displayGroup->getShowWindowTitles());
BOOST_REQUIRE_EQUAL( loadedDisplayGroup->getCoordinates(), QRectF( QPointF( 0, 0 ), wallSize ));
checkWindow( loadedDisplayGroup->getContentWindows()[0] );
BOOST_REQUIRE_EQUAL( loadedGroup->getCoordinates(),
QRectF( QPointF( 0, 0 ), wallSize ));
checkWindow( loadedGroup->getContentWindows()[0] );

// 4) Cleanup
cleanupTestDir();
Expand Down
Binary file modified tests/resources/state_v0.dcxpreview
Binary file not shown.
26 changes: 11 additions & 15 deletions tide/core/StatePreview.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project */
/* Copyright (c) 2013-2016, EPFL/Blue Brain Project */
/* Raphael Dumusc <raphael.dumusc@epfl.ch> */
/* All rights reserved. */
/* */
Expand Down Expand Up @@ -55,12 +55,11 @@

namespace
{
static const QSize PREVIEW_IMAGE_SIZE( 512, 512 );
static const QSize THUMBNAIL_SIZE( 128, 128 );
const QSize PREVIEW_IMAGE_SIZE( 512, 512 );
}

StatePreview::StatePreview( const QString &dcxFileName )
: dcxFileName_( dcxFileName )
: _dcxFileName( dcxFileName )
{
}

Expand All @@ -71,20 +70,20 @@ QString StatePreview::getFileExtension()

QImage StatePreview::getImage() const
{
return previewImage_;
return _previewImage;
}

QString StatePreview::previewFilename() const
{
QFileInfo fileinfo( dcxFileName_ );
QFileInfo fileinfo( _dcxFileName );

const QString extension = fileinfo.suffix().toLower();
if( extension != "dcx" )
{
put_flog( LOG_WARN, "wrong state file extension: '%s' for session: '%s'"
"(expected: .dcx)",
extension.toLocal8Bit().constData( ),
dcxFileName_.toLocal8Bit().constData( ));
_dcxFileName.toLocal8Bit().constData( ));
return QString();
}
return fileinfo.path() + "/" + fileinfo.completeBaseName() + getFileExtension();
Expand Down Expand Up @@ -117,20 +116,21 @@ void StatePreview::generateImage( const QSize& wallDimensions,

const QString& filename = window->getContent()->getURI();
ThumbnailGeneratorPtr generator =
ThumbnailGeneratorFactory::getGenerator( filename, THUMBNAIL_SIZE );
ThumbnailGeneratorFactory::getGenerator( filename,
area.size().toSize( ));
const QImage image = generator->generate( filename );

painter.drawImage( area, image );
}

painter.end();

previewImage_ = preview;
_previewImage = preview;
}

bool StatePreview::saveToFile() const
{
const bool success = previewImage_.save( previewFilename(), "PNG" );
const bool success = _previewImage.save( previewFilename(), "PNG" );

if( !success )
put_flog( LOG_ERROR, "Saving StatePreview image failed: '%s'",
Expand All @@ -142,9 +142,5 @@ bool StatePreview::saveToFile() const
bool StatePreview::loadFromFile()
{
QImageReader reader( previewFilename( ));
if ( reader.canRead( ))
{
return reader.read( &previewImage_ );
}
return false;
return reader.canRead() && reader.read( &_previewImage );
}
6 changes: 3 additions & 3 deletions tide/core/StatePreview.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project */
/* Copyright (c) 2013-2016, EPFL/Blue Brain Project */
/* Raphael Dumusc <raphael.dumusc@epfl.ch> */
/* All rights reserved. */
/* */
Expand Down Expand Up @@ -96,8 +96,8 @@ class StatePreview
protected:
QString previewFilename() const;

QString dcxFileName_;
QImage previewImage_;
QString _dcxFileName;
QImage _previewImage;
};

#endif // STATEPREVIEW_H
13 changes: 6 additions & 7 deletions tide/core/thumbnail/DefaultThumbnailGenerator.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project */
/* Copyright (c) 2013-2016, EPFL/Blue Brain Project */
/* Raphael Dumusc <raphael.dumusc@epfl.ch> */
/* All rights reserved. */
/* */
Expand Down Expand Up @@ -39,15 +39,14 @@

#include "DefaultThumbnailGenerator.h"

DefaultThumbnailGenerator::DefaultThumbnailGenerator(const QSize &size)
: ThumbnailGenerator(size)
DefaultThumbnailGenerator::DefaultThumbnailGenerator( const QSize& size )
: ThumbnailGenerator( size )
{
}

QImage DefaultThumbnailGenerator::generate(const QString& filename) const
QImage DefaultThumbnailGenerator::generate( const QString& ) const
{
QImage img = createGradientImage( QColor(Qt::black), QColor(Qt::white) );
paintText(img, "FILE");
addMetadataToImage(img, filename);
QImage img = createGradientImage( QColor( Qt::black ), QColor( Qt::white ));
paintText( img, "FILE" );
return img;
}
8 changes: 4 additions & 4 deletions tide/core/thumbnail/DefaultThumbnailGenerator.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*********************************************************************/
/* Copyright (c) 2013, EPFL/Blue Brain Project */
/* Copyright (c) 2013-2016, EPFL/Blue Brain Project */
/* Raphael Dumusc <raphael.dumusc@epfl.ch> */
/* All rights reserved. */
/* */
Expand Down Expand Up @@ -45,9 +45,9 @@
class DefaultThumbnailGenerator : public ThumbnailGenerator
{
public:
DefaultThumbnailGenerator(const QSize& size);
DefaultThumbnailGenerator( const QSize& size );

QImage generate(const QString& filename) const override;
QImage generate( const QString& filename ) const final;
};

#endif // DEFAULTTHUMBNAILGENERATOR_H
#endif
120 changes: 57 additions & 63 deletions tide/core/thumbnail/FolderThumbnailGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@

#include "FolderThumbnailGenerator.h"

#include <QDir>
#include <QPainter>

#include "ContentFactory.h"
#include "ThumbnailGeneratorFactory.h"
#include "ThumbnailGenerator.h"
#include "ContentFactory.h"
#include "log.h"

#include <QDir>
#include <QPainter>

namespace
{
const int FOLDER_THUMBNAILS_X = 2;
Expand All @@ -57,75 +57,49 @@ const QString FOLDER_TEXT( "folder" );

FolderThumbnailGenerator::FolderThumbnailGenerator( const QSize& size )
: ThumbnailGenerator( size )
{
}
{}

QImage FolderThumbnailGenerator::generate( const QString& filename ) const
{
const QDir dir( filename );
if( dir.exists( ))
return createFolderImage( dir, true );
return _createFolderImage( dir, true );

put_flog( LOG_ERROR, "invalid directory: %s",
filename.toLocal8Bit().constData( ));
return createErrorImage( "folder" );
}

QImage
FolderThumbnailGenerator::generatePlaceholderImage( const QDir& dir ) const
FolderThumbnailGenerator::_createFolderImage( const QDir& dir,
const bool generateThumbnails ) const
{
QImage img = createGradientImage( Qt::black, Qt::white );
addMetadataToImage( img, dir.path( ));
return img;
}

void FolderThumbnailGenerator::addMetadataToImage( QImage& img,
const QString& url ) const
{
img.setText( "dir", "true" );
ThumbnailGenerator::addMetadataToImage( img, url );
}

QVector<QRectF>
FolderThumbnailGenerator::calculatePlacement( int nX, int nY, float padding,
float totalWidth,
float totalHeight ) const
{
const float totalPaddingWidth = padding*(nX+1);
const float imageWidth = (1.0f-totalPaddingWidth)/(float)nX;
const QFileInfoList& fileList = _getSupportedFilesInDir( dir );

const float totalPaddingHeight = padding*(nY+1);
const float imageHeight = (1.0f-totalPaddingHeight)/(float)nY;
if( generateThumbnails && fileList.size() > 0 )
_paintThumbnailsMosaic( img, fileList );
else
paintText( img, FOLDER_TEXT );

QVector<QRectF> rect;
for( int j = 0; j < nY; ++j )
{
const float y = padding + j*(imageHeight+padding);
for( int i = 0; i < nX; ++i )
{
const float x = padding + i*(imageWidth+padding);
rect.append( QRect( x*totalWidth, y*totalHeight,
imageWidth*totalWidth,
imageHeight*totalHeight ));
}
}
return rect;
return img;
}

void FolderThumbnailGenerator::paintThumbnailsMosaic( QImage& img,
const QFileInfoList&
fileList) const
void FolderThumbnailGenerator::_paintThumbnailsMosaic( QImage& img,
const QFileInfoList&
fileList ) const
{
const int numPreviews = std::min( FOLDER_THUMBNAILS_X*FOLDER_THUMBNAILS_Y,
fileList.size( ));
if( numPreviews == 0 )
return;

QVector<QRectF> rect = calculatePlacement( FOLDER_THUMBNAILS_X,
FOLDER_THUMBNAILS_Y,
FOLDER_THUMBNAILS_PADDING,
img.size().width(),
img.size().height( ));
QVector<QRectF> rect = _calculatePlacement( FOLDER_THUMBNAILS_X,
FOLDER_THUMBNAILS_Y,
FOLDER_THUMBNAILS_PADDING,
img.size().width(),
img.size().height( ));
QPainter painter( &img );
for( int i = 0; i < numPreviews; ++i )
{
Expand All @@ -135,33 +109,53 @@ void FolderThumbnailGenerator::paintThumbnailsMosaic( QImage& img,
QImage thumbnail;
// Avoid recursion into subfolders
if( QDir( filename ).exists( ))
thumbnail = createFolderImage( QDir( filename ), false );
thumbnail = _createFolderImage( QDir( filename ), false );
else
thumbnail = ThumbnailGeneratorFactory::getGenerator( filename, size_ )->generate( filename );
{
const auto size = rect[i].size().toSize();
auto generator =
ThumbnailGeneratorFactory::getGenerator( filename, size );
thumbnail = generator->generate( filename );
}

painter.drawImage( rect[i], thumbnail );
// Draw the thumbnail centered in its rectangle, preserving aspect ratio
QSizeF paintedSize( thumbnail.size( ));
paintedSize.scale( rect[i].size(), Qt::KeepAspectRatio );
QRectF paintRect( QPointF(), paintedSize );
paintRect.moveCenter( rect[i].center( ));
painter.drawImage( paintRect, thumbnail );
}
painter.end();
}

QImage
FolderThumbnailGenerator::createFolderImage( const QDir& dir,
const bool generateThumbnails ) const
QVector<QRectF>
FolderThumbnailGenerator::_calculatePlacement( int nX, int nY, float padding,
float totalWidth,
float totalHeight ) const
{
QImage img = generatePlaceholderImage( dir );

const QFileInfoList& fileList = getSupportedFilesInDir( dir );
const float totalPaddingWidth = padding*(nX+1);
const float imageWidth = (1.0f-totalPaddingWidth)/(float)nX;

if( generateThumbnails && fileList.size() > 0 )
paintThumbnailsMosaic(img, fileList);
else
paintText( img, FOLDER_TEXT );
const float totalPaddingHeight = padding*(nY+1);
const float imageHeight = (1.0f-totalPaddingHeight)/(float)nY;

return img;
QVector<QRectF> rect;
for( int j = 0; j < nY; ++j )
{
const float y = padding + j*(imageHeight+padding);
for( int i = 0; i < nX; ++i )
{
const float x = padding + i*(imageWidth+padding);
rect.append( QRect( x*totalWidth, y*totalHeight,
imageWidth*totalWidth,
imageHeight*totalHeight ));
}
}
return rect;
}

QFileInfoList
FolderThumbnailGenerator::getSupportedFilesInDir( QDir dir ) const
FolderThumbnailGenerator::_getSupportedFilesInDir( QDir dir ) const
{
dir.setFilter( QDir::Files | QDir::NoDotAndDotDot );
QStringList filters = ContentFactory::getSupportedFilesFilter();
Expand Down
Loading