48 QScopedPointer<KisAbstractFrameCacheSwapper>
swapper;
74 const int start = it.key();
75 const int length = it.value();
77 bool foundFrameValid =
false;
81 foundFrameValid =
true;
84 int end = start +
length - 1;
85 if (start <= time && time <= end) {
86 foundFrameValid =
true;
90 return foundFrameValid ? start : -1;
100 return frameId >= 0 ?
swapper->loadFrame(frameId) : 0;
121 bool cacheChanged =
false;
127 const int start = it.key();
128 const int length = it.value();
129 const bool frameIsInfinite = (
length == -1);
130 const int end = start +
length - 1;
132 if (start >= range.
start()) {
137 if (!range.
isInfinite() && (frameIsInfinite || end > range.
end())) {
139 int newStart = range.
end() + 1;
140 int newLength = frameIsInfinite ? -1 : (end - newStart + 1);
143 swapper->moveFrame(start, newStart);
153 }
else if (frameIsInfinite || end >= range.
start()) {
154 const int newEnd = range.
start() - 1;
155 *it = newEnd - start + 1;
171 const qreal minLod = -std::log2(qreal(
frameSizeLimit) / maxDimension);
172 const int lodLimit = qMax(0, qCeil(minLod));
178 typedef QMap<KisOpenGLImageTexturesSP, KisAnimationFrameCache*>
CachesMap;
237 m_d->textures->recalculateCache(info,
false);
245 if (oldTime < 0)
return true;
247 const int oldKeyframeStart =
m_d->getFrameIdAtTime(oldTime);
248 if (oldKeyframeStart < 0)
return true;
250 const int oldKeyFrameLength =
m_d->newFrames[oldKeyframeStart];
251 return !(newTime >= oldKeyframeStart && (newTime < oldKeyframeStart + oldKeyFrameLength || oldKeyFrameLength == -1));
264 bool framesChanged =
false;
266 if (
frames.isEmpty())
return framesChanged;
270 for (; it !=
frames.end(); ++it) {
271 if (it.key() + it.value() - 1 >= range.
start()) {
277 if (it.key() > range.
start()) {
279 const int oldStart = it.key();
280 const int newStart = range.
start();
284 it =
frames.insert(newStart, newLength);
286 framesChanged =
true;
291 framesChanged =
true;
292 }
else if (it.value() != -1 && it.key() + it.value() - 1 < range.
end()) {
293 it.value() = range.
end() - it.key() + 1;
294 framesChanged =
true;
299 while (it !=
frames.end()) {
300 if (range.
isInfinite() || (it.value() != -1 && it.key() + it.value() - 1 <= range.
end())) {
303 framesChanged =
true;
304 }
else if (it.key() > range.
end()) {
306 }
else if (it.value() == -1 || it.key() + it.value() - 1 > range.
end()) {
308 int oldStart = it.key();
309 int newStart = range.
end() + 1;
310 int newLength = it.value() == -1 ? -1 : (it.key() + it.value() - 1 - newStart + 1);
313 frames.insert(newStart, newLength);
315 framesChanged =
true;
323 return framesChanged;
337 void moveFrame(
int oldStart,
int newStart)
override {
341 void forgetFrame(
int start)
override{
342 swapper->forgetFrame(start);
346 FramesGluer gluer(
m_d->swapper.data(),
m_d->newFrames);
348 const bool cacheChanged = gluer.glueFrames(range);
368 bool cacheChanged =
m_d->invalidate(range);
377 m_d->newFrames.clear();
408 qWarning() <<
"WARNING: KisAnimationFrameCache::frameReady image's time doesn't coincide with the requested time!";
415 const int lod =
m_d->effectiveLevelOfDetail(requestedRegion.
boundingRect());
419 Q_FOREACH (
const QRect &rc, requestedRegion.
rects()) {
437 m_d->addFrame(info, identicalRange);
445 if (
m_d->newFrames.isEmpty())
return;
447 auto it =
m_d->newFrames.upperBound(range.
start());
451 if (it !=
m_d->newFrames.begin()) it--;
453 while (it !=
m_d->newFrames.end() && it.key() <= range.
end()) {
454 const int frameId = it.key();
455 const int frameLength = it.value();
457 if (frameId + frameLength - 1 < range.
start()) {
462 const QRect frameRect =
m_d->swapper->frameDirtyRect(frameId);
463 const int frameLod =
m_d->swapper->frameLevelOfDetail(frameId);
465 if (frameLod >
m_d->effectiveLevelOfDetail(regionOfInterest) || !frameRect.contains(minimalRect)) {
466 m_d->swapper->forgetFrame(frameId);
467 it =
m_d->newFrames.erase(it);
477 if (
m_d->newFrames.isEmpty())
return false;
479 auto it =
m_d->newFrames.upperBound(range.
start());
481 if (it !=
m_d->newFrames.begin()) it--;
483 int expectedNextFrameStart = it.key();
485 while (it.key() <= range.
end()) {
486 const int frameId = it.key();
487 const int frameLength = it.value();
489 if (frameId + frameLength - 1 < range.
start()) {
490 expectedNextFrameStart = frameId + frameLength;
495 if (expectedNextFrameStart != frameId) {
500 if (!
m_d->swapper->frameDirtyRect(frameId).contains(regionOfInterest)) {
504 expectedNextFrameStart = frameId + frameLength;
qreal length(const QPointF &vec)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
bool tryGlueSameFrames(const KisTimeSpan &range)
QScopedPointer< Private > m_d
static const QList< KisAnimationFrameCache * > caches()
CacheStatus frameStatus(int time) const
KisAnimationFrameCache(KisOpenGLImageTexturesSP textures)
KisOpenGLUpdateInfoSP fetchFrameData(int time, KisImageSP image, const KisRegion &requestedRegion) const
static KisAnimationFrameCacheSP getFrameCache(KisOpenGLImageTexturesSP textures)
void addConvertedFrameData(KisOpenGLUpdateInfoSP info, int time)
void dropLowQualityFrames(const KisTimeSpan &range, const QRect ®ionOfInterest, const QRect &minimalRect)
bool framesHaveValidRoi(const KisTimeSpan &range, const QRect ®ionOfInterest)
bool shouldUploadNewFrame(int newTime, int oldTime) const
~KisAnimationFrameCache() override
static const KisAnimationFrameCacheSP cacheForImage(KisImageWSP image)
void framesChanged(const KisTimeSpan &range, const QRect &rect)
bool uploadFrame(int time)
static KisConfigNotifier * instance()
bool useAnimationCacheFrameSizeLimit(bool defaultValue=false) const
int animationCacheFrameSizeLimit(bool defaultValue=false) const
QString swapDir(bool requestDefault=false)
bool useOnDiskAnimationCacheSwapping(bool defaultValue=false) const
KisImageAnimationInterface * animationInterface() const
KisPaintDeviceSP projection() const
int currentLevelOfDetail() const
QRect bounds() const override
KisOpenGLUpdateInfoSP updateCache(const QRect &rect, KisImageSP srcImage)
KisOpenGLUpdateInfoBuilder & updateInfoBuilder()
bool tryMergeWith(const KisOpenGLUpdateInfo &rhs)
void generateLodCloneDevice(KisPaintDeviceSP dst, const QRect &originalRect, int lod)
const KoColorSpace * colorSpace() const
void prepareClone(KisPaintDeviceSP src)
QRect boundingRect() const
QVector< QRect > rects() const
static KisTimeSpan calculateIdenticalFramesRecursive(const KisNode *node, int time)
#define KIS_SAFE_ASSERT_RECOVER_BREAK(cond)
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
auto maxDimension(Size size) -> decltype(size.width())
bool glueFrames(const KisTimeSpan &range)
QMap< int, int > & frames
virtual void moveFrame(int oldStart, int newStart)=0
virtual void forgetFrame(int start)=0
virtual ~FramesGluerBase()
KisOpenGLUpdateInfoSP openGlFrame
Frame(KisOpenGLUpdateInfoSP info, int length)
QScopedPointer< KisAbstractFrameCacheSwapper > swapper
bool hasFrame(int time) const
int getFrameIdAtTime(int time) const
bool invalidate(const KisTimeSpan &range)
QMap< int, int > newFrames
QMap< KisOpenGLImageTexturesSP, KisAnimationFrameCache * > CachesMap
KisOpenGLUpdateInfoSP getFrame(int time)
Private(KisOpenGLImageTexturesSP _textures)
int effectiveLevelOfDetail(const QRect &rc) const
void addFrame(KisOpenGLUpdateInfoSP info, const KisTimeSpan &range)
KisOpenGLImageTexturesSP textures
KisOpenGLUpdateInfoSP fetchFrameDataImpl(KisImageSP image, const QRect &requestedRect, int lod)
KisOpenGLUpdateInfoSP buildUpdateInfo(const QRect &rect, KisImageSP srcImage, bool convertColorSpace)