Skip to content

Commit

Permalink
Fixed the image cache to check file modification time
Browse files Browse the repository at this point in the history
This fixes problems for image files that are not watched for changes
(like the images used by image layers), or when the watching for changes
is disabled by the user. In that case, a restart of Tiled or explicit
reloading of the tilesets using the hidden Ctrl+T shortcut was needed
before images would be reloaded. Manually changing the image source and
then back, or just reloading the map was not enough since the image
would simply be taken from the cache.

The new behavior is that images will be reloaded whenever they are found
in the cache, but with an out of date "last modified" timestamp.

Closes #2081
  • Loading branch information
bjorn committed May 15, 2019
1 parent ef07f8d commit 5c1a59a
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 20 deletions.
64 changes: 49 additions & 15 deletions src/libtiled/imagecache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "imagecache.h"

#include <QBitmap>
#include <QFileInfo>

namespace Tiled {

Expand All @@ -53,36 +54,62 @@ uint qHash(const TilesheetParameters &key, uint seed) Q_DECL_NOTHROW
return h;
}

LoadedImage::LoadedImage(const QString &fileName)
: image(fileName)
, lastModified(QFileInfo(fileName).lastModified())
{}

QHash<QString, QImage> ImageCache::sLoadedImages;
QHash<QString, QPixmap> ImageCache::sLoadedPixmaps;
QHash<TilesheetParameters, QVector<QPixmap>> ImageCache::sCutTiles;
LoadedPixmap::LoadedPixmap(const LoadedImage &cachedImage)
: pixmap(QPixmap::fromImage(cachedImage))
, lastModified(cachedImage.lastModified)
{}

QImage ImageCache::loadImage(const QString &fileName)

QHash<QString, LoadedImage> ImageCache::sLoadedImages;
QHash<QString, LoadedPixmap> ImageCache::sLoadedPixmaps;
QHash<TilesheetParameters, CutTiles> ImageCache::sCutTiles;

LoadedImage ImageCache::loadImage(const QString &fileName)
{
auto it = sLoadedImages.find(fileName);
if (it == sLoadedImages.end())
it = sLoadedImages.insert(fileName, QImage(fileName));

bool found = it != sLoadedImages.end();
bool old = found && it.value().lastModified < QFileInfo(fileName).lastModified();

if (old)
remove(fileName);
if (old || !found)
it = sLoadedImages.insert(fileName, LoadedImage(fileName));

return it.value();
}

QPixmap ImageCache::loadPixmap(const QString &fileName)
{
auto it = sLoadedPixmaps.find(fileName);
if (it == sLoadedPixmaps.end())
it = sLoadedPixmaps.insert(fileName, QPixmap::fromImage(loadImage(fileName)));

bool found = it != sLoadedPixmaps.end();
bool old = found && it.value().lastModified < QFileInfo(fileName).lastModified();

if (old)
remove(fileName);
if (old || !found)
it = sLoadedPixmaps.insert(fileName, LoadedPixmap(loadImage(fileName)));

return it.value();
}

static QVector<QPixmap> cutTilesImpl(const TilesheetParameters &p)
static CutTiles cutTilesImpl(const TilesheetParameters &p)
{
Q_ASSERT(p.tileWidth > 0 && p.tileHeight > 0);

const QImage image(ImageCache::loadImage(p.fileName));
const LoadedImage loadedImage = ImageCache::loadImage(p.fileName);
const QImage &image = loadedImage.image;
const int stopWidth = image.width() - p.tileWidth;
const int stopHeight = image.height() - p.tileHeight;

QVector<QPixmap> tiles;
CutTiles result;
result.lastModified = loadedImage.lastModified;

for (int y = p.margin; y <= stopHeight; y += p.tileHeight + p.spacing) {
for (int x = p.margin; x <= stopWidth; x += p.tileWidth + p.spacing) {
Expand All @@ -94,18 +121,25 @@ static QVector<QPixmap> cutTilesImpl(const TilesheetParameters &p)
tilePixmap.setMask(QBitmap::fromImage(mask));
}

tiles.append(tilePixmap);
result.tiles.append(tilePixmap);
}
}

return tiles;
return result;
}

QVector<QPixmap> ImageCache::cutTiles(const TilesheetParameters &parameters)
{
auto it = sCutTiles.find(parameters);
if (it == sCutTiles.end())

bool found = it != sCutTiles.end();
bool old = found && it.value().lastModified < QFileInfo(parameters.fileName).lastModified();

if (old)
remove(parameters.fileName);
if (old || !found)
it = sCutTiles.insert(parameters, cutTilesImpl(parameters));

return it.value();
}

Expand All @@ -115,7 +149,7 @@ void ImageCache::remove(const QString &fileName)
sLoadedPixmaps.remove(fileName);

// Also remove any previously cut tiles
QMutableHashIterator<TilesheetParameters, QVector<QPixmap>> it(sCutTiles);
QMutableHashIterator<TilesheetParameters, CutTiles> it(sCutTiles);
while (it.hasNext()) {
if (it.next().key().fileName == fileName)
it.remove();
Expand Down
38 changes: 34 additions & 4 deletions src/libtiled/imagecache.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "tiled_global.h"

#include <QColor>
#include <QDateTime>
#include <QHash>
#include <QImage>
#include <QPixmap>
Expand All @@ -53,18 +54,47 @@ struct TILEDSHARED_EXPORT TilesheetParameters

uint TILEDSHARED_EXPORT qHash(const TilesheetParameters &key, uint seed = 0) Q_DECL_NOTHROW;

struct LoadedImage
{
explicit LoadedImage(const QString &fileName);

operator const QImage &() const { return image; }

QImage image;
QDateTime lastModified;
};

struct LoadedPixmap
{
explicit LoadedPixmap(const LoadedImage &cachedImage);

operator const QPixmap &() const { return pixmap; }

QPixmap pixmap;
QDateTime lastModified;
};

struct CutTiles
{
operator const QVector<QPixmap> &() const { return tiles; }

QVector<QPixmap> tiles;
QDateTime lastModified;
};

class TILEDSHARED_EXPORT ImageCache
{
public:
static QImage loadImage(const QString &fileName);
static LoadedImage loadImage(const QString &fileName);
static QPixmap loadPixmap(const QString &fileName);
static QVector<QPixmap> cutTiles(const TilesheetParameters &parameters);

static void remove(const QString &fileName);

static QHash<QString, QImage> sLoadedImages;
static QHash<QString, QPixmap> sLoadedPixmaps;
static QHash<TilesheetParameters, QVector<QPixmap>> sCutTiles;
private:
static QHash<QString, LoadedImage> sLoadedImages;
static QHash<QString, LoadedPixmap> sLoadedPixmaps;
static QHash<TilesheetParameters, CutTiles> sCutTiles;
};

} // namespace Tiled
Expand Down
2 changes: 1 addition & 1 deletion src/libtiled/tileset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ bool Tileset::loadImage()
p.margin = mMargin;
p.transparentColor = mImageReference.transparentColor;

auto image = ImageCache::loadImage(p.fileName);
QImage image = ImageCache::loadImage(p.fileName);
if (image.isNull()) {
mImageReference.status = LoadingError;
return false;
Expand Down

0 comments on commit 5c1a59a

Please sign in to comment.