PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/kvirc-4.2.0/src/kvilib/ext/KviAnimatedPixmapCache.cpp

#
C++ | 264 lines | 174 code | 42 blank | 48 comment | 40 complexity | 137088f04050add59c3e88b1e641b3be MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, AGPL-1.0
  1. //=============================================================================
  2. //
  3. // File : KviAnimatedPixmapCache.cpp
  4. // Creation date : Thu Jul 31 2008 01:45:21 CEST by Alexey Uzhva
  5. //
  6. // This file is part of the KVIrc irc client distribution
  7. // Copyright (C) 2008 Alexey Uzhva (wizard at opendoor dot ru)
  8. //
  9. // This program is FREE software. You can redistribute it and/or
  10. // modify it under the terms of the GNU General Public License
  11. // as published by the Free Software Foundation; either version 2
  12. // of the License, or (at your opinion) any later version.
  13. //
  14. // This program is distributed in the HOPE that it will be USEFUL,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  17. // See the GNU General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License
  20. // along with this program. If not, write to the Free Software Foundation,
  21. // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22. //
  23. //=============================================================================
  24. #include "KviAnimatedPixmapCache.h"
  25. #include "KviTimeUtils.h"
  26. #include <QImageReader>
  27. #include <QImage>
  28. #define FRAME_DELAY 100
  29. KviAnimatedPixmapCache * KviAnimatedPixmapCache::m_pInstance = NULL;
  30. static QPixmap * g_pDummyPixmap = NULL;
  31. KviAnimatedPixmapCache::KviAnimatedPixmapCache()
  32. {
  33. m_pInstance = this;
  34. m_animationTimer.setInterval(FRAME_DELAY);
  35. connect(&m_animationTimer,SIGNAL(timeout()),this,SLOT(timeoutEvent()));
  36. }
  37. KviAnimatedPixmapCache::~KviAnimatedPixmapCache()
  38. {
  39. if(g_pDummyPixmap)
  40. {
  41. delete g_pDummyPixmap;
  42. g_pDummyPixmap = NULL;
  43. }
  44. m_pInstance = NULL;
  45. }
  46. void KviAnimatedPixmapCache::init()
  47. {
  48. if(m_pInstance)
  49. return;
  50. m_pInstance = new KviAnimatedPixmapCache();
  51. }
  52. void KviAnimatedPixmapCache::done()
  53. {
  54. if(!m_pInstance)
  55. return;
  56. delete m_pInstance;
  57. m_pInstance = NULL;
  58. }
  59. KviAnimatedPixmapCache::Data* KviAnimatedPixmapCache::internalLoad(const QString &szFile,int iWidth,int iHeight)
  60. {
  61. m_cacheMutex.lock();
  62. Data* newData = 0;
  63. QMultiHash<QString, Data*>::iterator i = m_hCache.find(szFile);
  64. while (i != m_hCache.end() && i.key() == szFile && !newData)
  65. {
  66. if (!i.value()->resized)
  67. newData = i.value();
  68. ++i;
  69. }
  70. if (!newData) {
  71. newData = new Data(szFile);
  72. QImageReader reader(szFile);
  73. newData->size = reader.size();
  74. QImage buffer;
  75. QPixmap* framePixmap;
  76. while(reader.canRead())
  77. {
  78. uint delay = reader.nextImageDelay();
  79. reader.read(&buffer);
  80. if(!buffer.isNull())
  81. {
  82. if (iHeight && iWidth) framePixmap = new QPixmap(QPixmap::fromImage(buffer).scaled(iWidth,iHeight,Qt::KeepAspectRatio,Qt::SmoothTransformation));
  83. else framePixmap = new QPixmap(QPixmap::fromImage(buffer));
  84. newData->append(FrameInfo(framePixmap,delay));
  85. }
  86. }
  87. m_hCache.insert(szFile,newData);
  88. }
  89. newData->refs++;
  90. m_cacheMutex.unlock();
  91. return newData;
  92. }
  93. KviAnimatedPixmapCache::Data* KviAnimatedPixmapCache::internalResize(Data* data,const QSize &size)
  94. {
  95. m_cacheMutex.lock();
  96. bool hasToBeResized = false;
  97. Data* newData = 0;
  98. QMultiHash<QString, Data*>::iterator i = m_hCache.find(data->file);
  99. while (i != m_hCache.end() && i.key() == data->file && !newData) {
  100. if (i.value()->size == size) {
  101. newData = i.value();
  102. }
  103. ++i;
  104. }
  105. if(!newData)
  106. {
  107. newData = new Data(*data);
  108. newData->size = size;
  109. m_hCache.insert(newData->file,newData);
  110. hasToBeResized = true;
  111. newData->resized = true;
  112. }
  113. newData->refs++;
  114. m_cacheMutex.unlock();
  115. internalFree(data);
  116. if(hasToBeResized)
  117. {
  118. for(int i=0;i<newData->count();i++) {
  119. QPixmap* old = newData->at(i).pixmap;
  120. newData->operator [](i).pixmap=new QPixmap(old->scaled(size,Qt::IgnoreAspectRatio,Qt::SmoothTransformation));
  121. delete old;
  122. }
  123. }
  124. return newData;
  125. }
  126. void KviAnimatedPixmapCache::internalFree(Data* data)
  127. {
  128. if(!data)
  129. return;
  130. m_cacheMutex.lock();
  131. data->refs--;
  132. if(data->refs==0)
  133. {
  134. m_hCache.remove(data->file,data);
  135. for(int i=0;i<data->count();i++)
  136. {
  137. delete data->operator [](i).pixmap;
  138. }
  139. delete data;
  140. }
  141. m_cacheMutex.unlock();
  142. }
  143. void KviAnimatedPixmapCache::internalScheduleFrameChange(uint delay,KviAnimatedPixmapInterface* receiver)
  144. {
  145. //qDebug("Adding %i - %i",(uint)KviTimeUtils::getCurrentTimeMills()+delay,receiver);
  146. m_timerMutex.lock();
  147. long long when = KviTimeUtils::getCurrentTimeMills()+delay;
  148. m_timerData.insert(when,receiver);
  149. if(!m_animationTimer.isActive())
  150. m_animationTimer.start();
  151. m_timerMutex.unlock();
  152. }
  153. void KviAnimatedPixmapCache::timeoutEvent()
  154. {
  155. /*
  156. * We are adding 15msecs to the current time. This MAY lead to the situation,
  157. * when the current frame will be painted a bit earlier, then i should.
  158. * But we are just playing animated gifs, not a HDTV video. So it should be ok.
  159. *
  160. * But it makes good speedup if there will be event, sceduled in such order:
  161. * 1 event at time X
  162. * 3 events at time X+2msec
  163. * 2 events at time X+6msec
  164. * 1 event at time X+8msec
  165. * 1 event at time X+9msec
  166. * 1 event at time X+12msec
  167. * ...
  168. *
  169. * Such situation is possible if we will have lots of smiles with almost the same
  170. * frame delays.
  171. *
  172. * So it will not emit additional 5 events.
  173. */
  174. long long now = KviTimeUtils::getCurrentTimeMills() + FRAME_DELAY;
  175. //m_timerMutex.lock();
  176. QMultiMap<long long, KviAnimatedPixmapInterface*>::iterator i =
  177. m_timerData.begin();
  178. QList<KviAnimatedPixmapInterface*> processed;
  179. while (i != m_timerData.end() && i.key() <= now)
  180. {
  181. if(processed.contains(i.value()))
  182. {
  183. // increase the frame index but do not emit the signals as we'll be emitting it later
  184. i.value()->nextFrame(false);
  185. } else {
  186. // we'll be emitting the signal later
  187. processed.append(i.value());
  188. }
  189. i = m_timerData.erase(i);
  190. }
  191. for(int i = 0; i < processed.size(); ++i)
  192. {
  193. // increase the frame index and emit the signal
  194. processed.at(i)->nextFrame(true);
  195. }
  196. // m_timerMutex.unlock();
  197. if(m_timerData.empty())
  198. m_animationTimer.stop();
  199. }
  200. QPixmap* KviAnimatedPixmapCache::dummyPixmap()
  201. {
  202. if(!g_pDummyPixmap)
  203. g_pDummyPixmap = new QPixmap();
  204. return g_pDummyPixmap;
  205. }
  206. void KviAnimatedPixmapCache::internalNotifyDelete(
  207. KviAnimatedPixmapInterface* receiver
  208. )
  209. {
  210. m_timerMutex.lock();
  211. QMultiMap<long long, KviAnimatedPixmapInterface*>::iterator i = m_timerData.begin();
  212. while(i != m_timerData.end())
  213. {
  214. if (i.value() == receiver)
  215. {
  216. i = m_timerData.erase(i);
  217. } else {
  218. i++;
  219. }
  220. }
  221. m_timerMutex.unlock();
  222. }