Index: core/src/fxcodec/jbig2/JBig2_Context.cpp |
diff --git a/core/src/fxcodec/jbig2/JBig2_Context.cpp b/core/src/fxcodec/jbig2/JBig2_Context.cpp |
index 83ded6fbe18f15ac81bde0b7d0de95d9b25ad2be..fec6b5fb68152ea41d9cbcc8279e9c787ec12339 100644 |
--- a/core/src/fxcodec/jbig2/JBig2_Context.cpp |
+++ b/core/src/fxcodec/jbig2/JBig2_Context.cpp |
@@ -4,7 +4,21 @@ |
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com |
+#include <map> |
+#include <list> |
#include "JBig2_Context.h" |
+ |
+// Implement a very small least recently used (LRU) cache. It is very |
+// common for a JBIG2 dictionary to span multiple pages in a PDF file, |
+// and we do not want to decode the same dictionary over and over |
+// again. We key off of the memory location of the dictionary. The |
+// list keeps track of the freshness of entries, with freshest ones |
+// at the front. Even a tiny cache size like 2 makes a dramatic |
+// difference for typical JBIG2 documents. |
+const int kSymbolDictCacheMaxSize = 2; |
+typedef std::pair<FX_BYTE*, CJBig2_SymbolDict*> CJBig2_CachePair; |
+static std::list<CJBig2_CachePair> SymbolDictCache; |
+ |
void OutputBitmap(CJBig2_Image* pImage) |
{ |
if(!pImage) { |
@@ -614,6 +628,8 @@ FX_INT32 CJBig2_Context::parseSymbolDict(CJBig2_Segment *pSegment, IFX_Pause* pP |
JBig2ArithCtx *gbContext = NULL, *grContext = NULL; |
CJBig2_ArithDecoder *pArithDecoder; |
JBIG2_ALLOC(pSymbolDictDecoder, CJBig2_SDDProc()); |
+ FX_BYTE *key = pSegment->m_pData; |
+ FX_BOOL cache_hit = false; |
if(m_pStream->readShortInteger(&wFlags) != 0) { |
m_pModule->JBig2_Error("symbol dictionary segment : data header too short."); |
nRet = JBIG2_ERROR_TOO_SHORT; |
@@ -791,23 +807,43 @@ FX_INT32 CJBig2_Context::parseSymbolDict(CJBig2_Segment *pSegment, IFX_Pause* pP |
} |
} |
pSegment->m_nResultType = JBIG2_SYMBOL_DICT_POINTER; |
- if(pSymbolDictDecoder->SDHUFF == 0) { |
- JBIG2_ALLOC(pArithDecoder, CJBig2_ArithDecoder(m_pStream)); |
- pSegment->m_Result.sd = pSymbolDictDecoder->decode_Arith(pArithDecoder, gbContext, grContext); |
- delete pArithDecoder; |
- if(pSegment->m_Result.sd == NULL) { |
- nRet = JBIG2_ERROR_FETAL; |
- goto failed; |
+ for(std::list<CJBig2_CachePair>::iterator it = |
+ SymbolDictCache.begin(); it != SymbolDictCache.end(); ++it) { |
+ if (it->first == key) { |
+ pSegment->m_Result.sd = it->second->DeepCopy(); |
+ SymbolDictCache.push_front(*it); |
+ SymbolDictCache.erase(it); |
+ cache_hit = true; |
+ break; |
} |
- m_pStream->alignByte(); |
- m_pStream->offset(2); |
- } else { |
- pSegment->m_Result.sd = pSymbolDictDecoder->decode_Huffman(m_pStream, gbContext, grContext, pPause); |
- if(pSegment->m_Result.sd == NULL) { |
- nRet = JBIG2_ERROR_FETAL; |
- goto failed; |
+ } |
+ if (!cache_hit) { |
+ if(pSymbolDictDecoder->SDHUFF == 0) { |
+ JBIG2_ALLOC(pArithDecoder, CJBig2_ArithDecoder(m_pStream)); |
+ pSegment->m_Result.sd = pSymbolDictDecoder->decode_Arith(pArithDecoder, gbContext, grContext); |
+ delete pArithDecoder; |
+ if(pSegment->m_Result.sd == NULL) { |
+ nRet = JBIG2_ERROR_FETAL; |
+ goto failed; |
+ } |
+ m_pStream->alignByte(); |
+ m_pStream->offset(2); |
+ } else { |
+ pSegment->m_Result.sd = pSymbolDictDecoder->decode_Huffman(m_pStream, gbContext, grContext, pPause); |
+ if(pSegment->m_Result.sd == NULL) { |
+ nRet = JBIG2_ERROR_FETAL; |
+ goto failed; |
+ } |
+ m_pStream->alignByte(); |
+ } |
+ CJBig2_SymbolDict *value = pSegment->m_Result.sd->DeepCopy(); |
+ if (value) { |
+ while (SymbolDictCache.size() >= kSymbolDictCacheMaxSize) { |
+ delete SymbolDictCache.back().second; |
+ SymbolDictCache.pop_back(); |
+ } |
+ SymbolDictCache.push_front(CJBig2_CachePair(key, value)); |
} |
- m_pStream->alignByte(); |
} |
if(wFlags & 0x0200) { |
pSegment->m_Result.sd->m_bContextRetained = TRUE; |