10#include <QTemporaryDir>
11#include <QElapsedTimer>
19 (!frameCachePath.isEmpty() && QTemporaryDir(frameCachePath +
"/KritaFrameCacheXXXXXX").isValid()
22 +
"/KritaFrameCacheXXXXXX")
24 framesDirObject = QDir(framesDir.path());
25 framesDirObject.makeAbsolute();
30 const int subfolderIndex = frameId & 0xff00;
31 return QString::number(subfolderIndex);
35 return QString(
"frame_%1").arg(frameId);
40 return framesDirObject.filePath(
41 subfolderNameForFrame(frameId) +
'/' +
42 fileNameForFrame(frameId));
51 if (compressionBuffer.size() < size) {
52 compressionBuffer.resize(size);
54 return reinterpret_cast<quint8*
>(compressionBuffer.data());
70 : m_d(new
Private(frameCachePath))
82 const int frameId =
m_d->generateFrameId();
84 const QString frameSubfolder =
m_d->subfolderNameForFrame(frameId);
86 if (!
m_d->framesDirObject.exists(frameSubfolder)) {
87 m_d->framesDirObject.mkpath(frameSubfolder);
90 const QString frameRelativePath = frameSubfolder +
'/' +
m_d->fileNameForFrame(frameId);
92 if (
m_d->framesDirObject.exists(frameRelativePath)) {
93 qWarning() <<
"WARNING: overwriting existing frame file!" << frameRelativePath;
97 const QString frameFilePath =
m_d->framesDirObject.filePath(frameRelativePath);
99 QFile file(frameFilePath);
100 if (!file.open(QFile::WriteOnly)) {
104 QDataStream stream(&file);
110 for (
int i = 0; i < int(frame.
frameTiles.size()); i++) {
117 const int frameByteSize = frame.
pixelSize * tile.
rect.width() * tile.
rect.height();
119 quint8 *buffer =
m_d->getCompressionBuffer(maxBufferSize);
121 const int compressedSize =
122 compression.
compress(tile.
data.
data(), frameByteSize, buffer, maxBufferSize);
126 const bool isCompressed = compressedSize < frameByteSize;
127 stream << isCompressed;
130 stream << compressedSize;
131 stream.writeRawData((
char*)buffer, compressedSize);
133 stream << frameByteSize;
134 stream.writeRawData((
char*)tile.
data.
data(), frameByteSize);
147 QElapsedTimer loadingTime;
150 int loadedFrameId = -1;
153 qint64 compressionTime = 0;
155 const QString framePath =
m_d->filePathForFrame(frameId);
157 QFile file(framePath);
159 if (!file.open(QFile::ReadOnly))
return frame;
161 QDataStream stream(&file);
165 stream >> loadedFrameId;
172 for (
int i = 0; i < numTiles; i++) {
178 const int frameByteSize = frame.
pixelSize * tile.
rect.width() * tile.
rect.height();
182 bool isCompressed =
false;
185 stream >> isCompressed;
190 quint8 *buffer =
m_d->getCompressionBuffer(maxBufferSize);
191 stream.readRawData((
char*)buffer, inputSize);
195 QElapsedTimer compTime;
198 const int decompressedSize =
201 compressionTime += compTime.nsecsElapsed();
211 stream.readRawData((
char*)tile.
data.
data(), inputSize);
217 Q_UNUSED(compressionTime);
226 const QString srcFramePath =
m_d->filePathForFrame(srcFrameId);
227 const QString dstFramePath =
m_d->filePathForFrame(dstFrameId);
231 QFile::remove(dstFramePath);
234 QFile::rename(srcFramePath, dstFramePath);
239 const QString framePath =
m_d->filePathForFrame(frameId);
240 return QFileInfo(framePath).exists();
245 const QString framePath =
m_d->filePathForFrame(frameId);
246 QFile::remove(framePath);
255 int numSampledPixels = 0;
256 int numUniquePixels = 0;
257 const int sampleStep = portion > 0.0 ? qMax(1, qRound(1.0 / portion)) : 0;
259 for (
int i = 0; i < int(lhs.
frameTiles.size()); i++) {
263 if (lhsTile.
col != rhsTile.
col ||
264 lhsTile.
row != rhsTile.
row ||
270 if (sampleStep > 0) {
271 const int numPixels = lhsTile.
rect.width() * lhsTile.
rect.height();
276 for (
int j = 0; j < numPixels; j += sampleStep) {
277 quint8 *lhsDataPtr = lhsTile.
data.
data() + j * pixelSize;
278 quint8 *rhsDataPtr = rhsTile.
data.
data() + j * pixelSize;
280 if (std::memcmp(lhsDataPtr, rhsDataPtr, pixelSize) != 0) {
288 return numSampledPixels > 0 ? qreal(numUniquePixels) / numSampledPixels : 1.0;
291template <
template <
typename U>
class OpPolicy,
typename T>
296 bool unitsAreSame =
true;
298 for (
int j = 0; j < numUnits; j++) {
299 *dst = op(*dst, *src);
302 unitsAreSame =
false;
312template<
template <
typename U>
class OpPolicy>
315 bool framesAreSame =
true;
319 for (
int i = 0; i < int(src.frameTiles.size()); i++) {
320 const FrameTile &srcTile = src.frameTiles[i];
323 const int numBytes = srcTile.
rect.width() * srcTile.
rect.height() * src.pixelSize;
324 const int numQWords = numBytes / 8;
326 const quint64 *srcDataPtr =
reinterpret_cast<const quint64*
>(srcTile.
data.
data());
327 quint64 *dstDataPtr =
reinterpret_cast<quint64*
>(dstTile.
data.
data());
329 framesAreSame &= processData<OpPolicy>(dstDataPtr, srcDataPtr, numQWords);
332 const int tailBytes = numBytes % 8;
333 const quint8 *srcTailDataPtr = srcTile.
data.
data() + numBytes - tailBytes;
334 quint8 *dstTailDataPtr = dstTile.
data.
data() + numBytes - tailBytes;
336 framesAreSame &= processData<OpPolicy>(dstTailDataPtr, srcTailDataPtr, tailBytes);
339 return framesAreSame;
344 return processFrames<std::minus>(dst, src);
350 (
void) processFrames<std::plus>(dst, src);
bool processData(T *dst, const T *src, int numUnits)
void allocate(int pixelSize)
qint32 compress(const quint8 *input, qint32 inputLength, quint8 *output, qint32 outputLength) override
qint32 decompress(const quint8 *input, qint32 inputLength, quint8 *output, qint32 outputLength) override
qint32 outputBufferSize(qint32 dataSize) override
#define KIS_SAFE_ASSERT_RECOVER(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
typedef void(QOPENGLF_APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC)(GLuint buffer)
std::vector< FrameTile > frameTiles
bool hasFrame(int frameId) const
QByteArray compressionBuffer
quint8 * getCompressionBuffer(int size)
static boost::optional< qreal > estimateFrameUniqueness(const Frame &lhs, const Frame &rhs, qreal portion)
~KisFrameDataSerializer()
static void addFrames(Frame &dst, const Frame &src)
void forgetFrame(int frameId)
const QScopedPointer< Private > m_d
static bool subtractFrames(Frame &dst, const Frame &src)
QString subfolderNameForFrame(int frameId)
Frame loadFrame(int frameId, KisTextureTileInfoPoolSP pool)
void moveFrame(int srcFrameId, int dstFrameId)
QString filePathForFrame(int frameId)
Private(const QString &frameCachePath)
int saveFrame(const Frame &frame)
static bool processFrames(KisFrameDataSerializer::Frame &dst, const KisFrameDataSerializer::Frame &src)
QString fileNameForFrame(int frameId)