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 file.open(QFile::WriteOnly);
102 QDataStream stream(&file);
108 for (
int i = 0; i < int(frame.
frameTiles.size()); i++) {
115 const int frameByteSize = frame.
pixelSize * tile.
rect.width() * tile.
rect.height();
117 quint8 *buffer =
m_d->getCompressionBuffer(maxBufferSize);
119 const int compressedSize =
120 compression.
compress(tile.
data.
data(), frameByteSize, buffer, maxBufferSize);
124 const bool isCompressed = compressedSize < frameByteSize;
125 stream << isCompressed;
128 stream << compressedSize;
129 stream.writeRawData((
char*)buffer, compressedSize);
131 stream << frameByteSize;
132 stream.writeRawData((
char*)tile.
data.
data(), frameByteSize);
145 QElapsedTimer loadingTime;
148 int loadedFrameId = -1;
151 qint64 compressionTime = 0;
153 const QString framePath =
m_d->filePathForFrame(frameId);
155 QFile file(framePath);
157 if (!file.open(QFile::ReadOnly))
return frame;
159 QDataStream stream(&file);
163 stream >> loadedFrameId;
170 for (
int i = 0; i < numTiles; i++) {
176 const int frameByteSize = frame.
pixelSize * tile.
rect.width() * tile.
rect.height();
180 bool isCompressed =
false;
183 stream >> isCompressed;
188 quint8 *buffer =
m_d->getCompressionBuffer(maxBufferSize);
189 stream.readRawData((
char*)buffer, inputSize);
193 QElapsedTimer compTime;
196 const int decompressedSize =
199 compressionTime += compTime.nsecsElapsed();
209 stream.readRawData((
char*)tile.
data.
data(), inputSize);
215 Q_UNUSED(compressionTime);
224 const QString srcFramePath =
m_d->filePathForFrame(srcFrameId);
225 const QString dstFramePath =
m_d->filePathForFrame(dstFrameId);
229 QFile::remove(dstFramePath);
232 QFile::rename(srcFramePath, dstFramePath);
237 const QString framePath =
m_d->filePathForFrame(frameId);
238 return QFileInfo(framePath).exists();
243 const QString framePath =
m_d->filePathForFrame(frameId);
244 QFile::remove(framePath);
253 int numSampledPixels = 0;
254 int numUniquePixels = 0;
255 const int sampleStep = portion > 0.0 ? qMax(1, qRound(1.0 / portion)) : 0;
257 for (
int i = 0; i < int(lhs.
frameTiles.size()); i++) {
261 if (lhsTile.
col != rhsTile.
col ||
262 lhsTile.
row != rhsTile.
row ||
268 if (sampleStep > 0) {
269 const int numPixels = lhsTile.
rect.width() * lhsTile.
rect.height();
274 for (
int j = 0; j < numPixels; j += sampleStep) {
275 quint8 *lhsDataPtr = lhsTile.
data.
data() + j * pixelSize;
276 quint8 *rhsDataPtr = rhsTile.
data.
data() + j * pixelSize;
278 if (std::memcmp(lhsDataPtr, rhsDataPtr, pixelSize) != 0) {
286 return numSampledPixels > 0 ? qreal(numUniquePixels) / numSampledPixels : 1.0;
289template <
template <
typename U>
class OpPolicy,
typename T>
294 bool unitsAreSame =
true;
296 for (
int j = 0; j < numUnits; j++) {
297 *dst = op(*dst, *src);
300 unitsAreSame =
false;
310template<
template <
typename U>
class OpPolicy>
313 bool framesAreSame =
true;
317 for (
int i = 0; i < int(src.frameTiles.size()); i++) {
318 const FrameTile &srcTile = src.frameTiles[i];
321 const int numBytes = srcTile.
rect.width() * srcTile.
rect.height() * src.pixelSize;
322 const int numQWords = numBytes / 8;
324 const quint64 *srcDataPtr =
reinterpret_cast<const quint64*
>(srcTile.
data.
data());
325 quint64 *dstDataPtr =
reinterpret_cast<quint64*
>(dstTile.
data.
data());
327 framesAreSame &= processData<OpPolicy>(dstDataPtr, srcDataPtr, numQWords);
330 const int tailBytes = numBytes % 8;
331 const quint8 *srcTailDataPtr = srcTile.
data.
data() + numBytes - tailBytes;
332 quint8 *dstTailDataPtr = dstTile.
data.
data() + numBytes - tailBytes;
334 framesAreSame &= processData<OpPolicy>(dstTailDataPtr, srcTailDataPtr, tailBytes);
337 return framesAreSame;
342 return processFrames<std::minus>(dst, src);
348 (
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)