pdfviewer: add indexed rbg image support, enhanche caching(setData) for SkPdfObject

Review URL: https://codereview.chromium.org/21738005

git-svn-id: http://skia.googlecode.com/svn/trunk@10534 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/PdfViewer/SkPdfBasics.h b/experimental/PdfViewer/SkPdfBasics.h
index a681eca..662e745 100644
--- a/experimental/PdfViewer/SkPdfBasics.h
+++ b/experimental/PdfViewer/SkPdfBasics.h
@@ -221,7 +221,8 @@
                                 Group XObjects”). Initial value: None.
  */
     SkPdfSoftMaskDictionary* fSoftMaskDictionary;
-    SkBitmap                 fSMask;
+    // TODO(edisonn): make sMask private, add setter and getter, ref/unref/..., at the moment we most likely leask
+    SkBitmap*                fSMask;
 
 
 /*
diff --git a/experimental/PdfViewer/SkPdfFont.cpp b/experimental/PdfViewer/SkPdfFont.cpp
index ac96f02..5d8cfe6 100644
--- a/experimental/PdfViewer/SkPdfFont.cpp
+++ b/experimental/PdfViewer/SkPdfFont.cpp
@@ -255,10 +255,10 @@
         return NULL;  // TODO(edisonn): report default one?
     }
 
-    if (dict->data() == NULL) {
-        dict->setData(fontFromPdfDictionaryOnce(doc, dict));
+    if (!dict->hasData(SkPdfObject::kFont_Data)) {
+        dict->setData(fontFromPdfDictionaryOnce(doc, dict), SkPdfObject::kFont_Data);
     }
-    return (SkPdfFont*)dict->data();
+    return (SkPdfFont*)dict->data(SkPdfObject::kFont_Data);
 }
 
 
diff --git a/experimental/PdfViewer/SkPdfFont.h b/experimental/PdfViewer/SkPdfFont.h
index 58c91b6..c484727 100644
--- a/experimental/PdfViewer/SkPdfFont.h
+++ b/experimental/PdfViewer/SkPdfFont.h
@@ -315,7 +315,7 @@
 
 class SkPdfType3Font : public SkPdfFont {
     struct Type3FontChar {
-        const SkPdfObject* fObj;
+        SkPdfObject* fObj;
         double fWidth;
     };
 
diff --git a/experimental/PdfViewer/SkPdfRenderer.cpp b/experimental/PdfViewer/SkPdfRenderer.cpp
index 5b8edc3..50b48a0 100644
--- a/experimental/PdfViewer/SkPdfRenderer.cpp
+++ b/experimental/PdfViewer/SkPdfRenderer.cpp
@@ -435,11 +435,11 @@
     return grayColortable;
 }
 
-static SkBitmap transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
+static SkBitmap* transferImageStreamToBitmap(const unsigned char* uncompressedStream, size_t uncompressedStreamLength,
                                      int width, int height, int bytesPerLine,
                                      int bpc, const std::string& colorSpace,
                                      bool transparencyMask) {
-    SkBitmap bitmap;
+    SkBitmap* bitmap = new SkBitmap();
 
     //int components = GetColorSpaceComponents(colorSpace);
 //#define MAX_COMPONENTS 10
@@ -462,8 +462,8 @@
             uncompressedStream += bytesPerLine;
         }
 
-        bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
-        bitmap.setPixels(uncompressedStreamArgb);
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+        bitmap->setPixels(uncompressedStreamArgb);
     }
     else if ((colorSpace == "DeviceGray" || colorSpace == "Gray") && bpc == 8) {
         unsigned char* uncompressedStreamA8 = (unsigned char*)malloc(width * height);
@@ -478,9 +478,9 @@
             uncompressedStream += bytesPerLine;
         }
 
-        bitmap.setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
+        bitmap->setConfig(transparencyMask ? SkBitmap::kA8_Config : SkBitmap::kIndex8_Config,
                          width, height);
-        bitmap.setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
+        bitmap->setPixels(uncompressedStreamA8, transparencyMask ? NULL : getGrayColortable());
     }
 
     // TODO(edisonn): Report Warning, NYI, or error
@@ -496,10 +496,10 @@
 
 // this functions returns the image, it does not look at the smask.
 
-static SkBitmap getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
+static SkBitmap* getImageFromObjectCore(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
     if (image == NULL || !image->hasStream()) {
         // TODO(edisonn): report warning to be used in testing.
-        return SkBitmap();
+        return NULL;
     }
 
     int64_t bpc = image->BitsPerComponent(pdfContext->fPdfDoc);
@@ -507,9 +507,37 @@
     int64_t height = image->Height(pdfContext->fPdfDoc);
     std::string colorSpace = "DeviceRGB";
 
+    bool indexed = false;
+    SkPMColor colors[256];
+    int cnt = 0;
+
     // TODO(edisonn): color space can be an array too!
     if (image->isColorSpaceAName(pdfContext->fPdfDoc)) {
         colorSpace = image->getColorSpaceAsName(pdfContext->fPdfDoc);
+    } else if (image->isColorSpaceAArray(pdfContext->fPdfDoc)) {
+        SkPdfArray* array = image->getColorSpaceAsArray(pdfContext->fPdfDoc);
+        if (array && array->size() == 4 && array->objAtAIndex(0)->isName("Indexed") &&
+                                           (array->objAtAIndex(1)->isName("DeviceRGB") || array->objAtAIndex(1)->isName("RGB")) &&
+                                           array->objAtAIndex(2)->isInteger() &&
+                                           array->objAtAIndex(3)->isHexString()
+                                           ) {
+            // TODO(edisonn): suport only DeviceRGB for now.
+            indexed = true;
+            cnt = array->objAtAIndex(2)->intValue() + 1;
+            if (cnt > 256) {
+                // TODO(edionn): report NYIs
+                return NULL;
+            }
+            SkColorTable colorTable(cnt);
+            NotOwnedString data = array->objAtAIndex(3)->strRef();
+            if (data.fBytes != (unsigned int)cnt * 3) {
+                // TODO(edionn): report error/warning
+                return NULL;
+            }
+            for (int i = 0 ; i < cnt; i++) {
+                colors[i] = SkPreMultiplyARGB(0xff, data.fBuffer[3 * i], data.fBuffer[3 * i + 1], data.fBuffer[3 * i + 2]);
+            }
+        }
     }
 
 /*
@@ -532,7 +560,7 @@
     if (!stream || !stream->GetFilteredStreamRef(&uncompressedStream, &uncompressedStreamLength) ||
             uncompressedStream == NULL || uncompressedStreamLength == 0) {
         // TODO(edisonn): report warning to be used in testing.
-        return SkBitmap();
+        return NULL;
     }
 
     SkPdfStreamCommonDictionary* streamDict = (SkPdfStreamCommonDictionary*)stream;
@@ -543,8 +571,8 @@
                                           streamDict->getFilterAsArray(NULL)->size() > 0 &&
                                           streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->isName() &&
                                           streamDict->getFilterAsArray(NULL)->objAtAIndex(0)->nameValue2() == "DCTDecode"))) {
-        SkBitmap bitmap;
-        SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, &bitmap);
+        SkBitmap* bitmap = new SkBitmap();
+        SkImageDecoder::DecodeMemory(uncompressedStream, uncompressedStreamLength, bitmap);
         return bitmap;
     }
 
@@ -562,6 +590,15 @@
 //        SkImageDecoder::Factory()
 //    }
 
+    // TODO(edisonn): assumes RGB for now, since it is the only onwe implemented
+    if (indexed) {
+        SkBitmap* bitmap = new SkBitmap();
+        bitmap->setConfig(SkBitmap::kIndex8_Config, width, height);
+        SkColorTable* colorTable = new SkColorTable(colors, cnt);
+        bitmap->setPixels((void*)uncompressedStream, colorTable);
+        return bitmap;
+    }
+
     int bytesPerLine = (int)(uncompressedStreamLength / height);
 #ifdef PDF_TRACE
     if (uncompressedStreamLength % height != 0) {
@@ -569,7 +606,7 @@
     }
 #endif
 
-    SkBitmap bitmap = transferImageStreamToBitmap(
+    SkBitmap* bitmap = transferImageStreamToBitmap(
             (unsigned char*)uncompressedStream, uncompressedStreamLength,
             (int)width, (int)height, bytesPerLine,
             (int)bpc, colorSpace,
@@ -578,7 +615,19 @@
     return bitmap;
 }
 
-static SkBitmap getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
+static SkBitmap* getImageFromObject(PdfContext* pdfContext, SkPdfImageDictionary* image, bool transparencyMask) {
+    if (!transparencyMask) {
+        if (!image->hasData(SkPdfObject::kBitmap_Data)) {
+            SkBitmap* bitmap = getImageFromObjectCore(pdfContext, image, transparencyMask);
+            image->setData(bitmap, SkPdfObject::kBitmap_Data);
+        }
+        return (SkBitmap*) image->data(SkPdfObject::kBitmap_Data);
+    } else {
+        return getImageFromObjectCore(pdfContext, image, transparencyMask);
+    }
+}
+
+static SkBitmap* getSmaskFromObject(PdfContext* pdfContext, SkPdfImageDictionary* obj) {
     SkPdfImageDictionary* sMask = obj->SMask(pdfContext->fPdfDoc);
 
     if (sMask) {
@@ -594,8 +643,8 @@
         return kIgnoreError_PdfResult;
     }
 
-    SkBitmap image = getImageFromObject(pdfContext, skpdfimage, false);
-    SkBitmap sMask = getSmaskFromObject(pdfContext, skpdfimage);
+    SkBitmap* image = getImageFromObject(pdfContext, skpdfimage, false);
+    SkBitmap* sMask = getSmaskFromObject(pdfContext, skpdfimage);
 
     canvas->save();
     canvas->setMatrix(pdfContext->fGraphicsState.fCTM);
@@ -616,16 +665,16 @@
     SkRect dst = SkRect::MakeXYWH(SkDoubleToScalar(0.0), SkDoubleToScalar(0.0), SkDoubleToScalar(1.0), SkDoubleToScalar(1.0));
 
     // TODO(edisonn): soft mask type? alpha/luminosity.
-    if (sMask.empty()) {
-        canvas->drawBitmapRect(image, dst, NULL);
+    if (!sMask || sMask->empty()) {
+        canvas->drawBitmapRect(*image, dst, NULL);
     } else {
         canvas->saveLayer(&dst, NULL);
-        canvas->drawBitmapRect(image, dst, NULL);
+        canvas->drawBitmapRect(*image, dst, NULL);
         SkPaint xfer;
         pdfContext->fGraphicsState.applyGraphicsState(&xfer, false);
         // TODO(edisonn): is the blend mode specified already implicitly/explicitly in pdf?
         xfer.setXfermodeMode(SkXfermode::kSrcOut_Mode); // SkXfermode::kSdtOut_Mode
-        canvas->drawBitmapRect(sMask, dst, &xfer);
+        canvas->drawBitmapRect(*sMask, dst, &xfer);
         canvas->restore();
     }
 
@@ -1255,14 +1304,19 @@
                     // TODO(edisonn): constants
                     // TODO(edisonn): colored
                     if (pattern->PaintType(pdfContext->fPdfDoc) == 1) {
-                        int xStep = (int)pattern->XStep(pdfContext->fPdfDoc);
-                        int yStep = (int)pattern->YStep(pdfContext->fPdfDoc);
+                        // TODO(edisonn): don't use abs, iterate as asked, if the cells intersect
+                        // it will change the result iterating in reverse
+                        int xStep = abs((int)pattern->XStep(pdfContext->fPdfDoc));
+                        int yStep = abs((int)pattern->YStep(pdfContext->fPdfDoc));
 
                         SkRect bounds = path.getBounds();
-                        SkScalar x;
-                        SkScalar y;
 
                         // TODO(edisonn): xstep and ystep can be negative, and we need to iterate in reverse
+                        // TODO(edisonn): don't do that!
+                        bounds.sort();
+
+                        SkScalar x;
+                        SkScalar y;
 
                         y = bounds.top();
                         int totalx = 0;
@@ -1961,7 +2015,7 @@
 void skpdfGraphicsStateApplySMask_name(PdfContext* pdfContext, const std::string& sMask) {
     if (sMask == "None") {
         pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
-        pdfContext->fGraphicsState.fSMask = SkBitmap();
+        pdfContext->fGraphicsState.fSMask = NULL;
         return;
     }
 
@@ -1983,7 +2037,7 @@
     }
 
     pdfContext->fGraphicsState.fSoftMaskDictionary = NULL;
-    pdfContext->fGraphicsState.fSMask = SkBitmap();
+    pdfContext->fGraphicsState.fSMask = NULL;
 
     skpdfGraphicsStateApplySMask_dict(pdfContext, obj->asDictionary());
 }
diff --git a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp
index 306bf07..6386f98 100644
--- a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp
+++ b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.cpp
@@ -306,7 +306,7 @@
     }
 
     if (storeCatalog) {
-        const SkPdfObject* ref = trailer->Root(NULL);
+        SkPdfObject* ref = trailer->Root(NULL);
         if (ref == NULL || !ref->isReference()) {
             // TODO(edisonn): oops, we have to fix the corrup pdf file
             return current;
@@ -384,7 +384,7 @@
 }
 
 void SkNativeParsedPDF::fillPages(SkPdfPageTreeNodeDictionary* tree) {
-    const SkPdfArray* kids = tree->Kids(this);
+    SkPdfArray* kids = tree->Kids(this);
     if (kids == NULL) {
         *fPages.append() = (SkPdfPageObjectDictionary*)tree;
         return;
@@ -392,7 +392,7 @@
 
     int cnt = kids->size();
     for (int i = 0; i < cnt; i++) {
-        const SkPdfObject* obj = resolveReference(kids->objAtAIndex(i));
+        SkPdfObject* obj = resolveReference(kids->objAtAIndex(i));
         if (fMapper->mapPageObjectDictionary(obj) != kPageObjectDictionary_SkPdfObjectType) {
             *fPages.append() = (SkPdfPageObjectDictionary*)obj;
         } else {
@@ -506,7 +506,7 @@
 
 // TODO(edisonn): fix infinite loop if ref to itself!
 // TODO(edisonn): perf, fix refs at load, and resolve will simply return fResolvedReference?
-SkPdfObject* SkNativeParsedPDF::resolveReference(const SkPdfObject* ref) {
+SkPdfObject* SkNativeParsedPDF::resolveReference(SkPdfObject* ref) {
     if (ref && ref->isReference()) {
         int id = ref->referenceId();
         // TODO(edisonn): generation/updates not supported now
diff --git a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h
index d073f00..de71bf6 100644
--- a/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h
+++ b/experimental/PdfViewer/pdfparser/native/SkNativeParsedPDF.h
@@ -64,7 +64,7 @@
     // the string does not own the char*
     SkPdfString* createString(const unsigned char* sz, size_t len) const;
 
-    SkPdfObject* resolveReference(const SkPdfObject* ref);
+    SkPdfObject* resolveReference(SkPdfObject* ref);
 
     // Reports an approximation of all the memory usage.
     size_t bytesUsed() const;
@@ -95,7 +95,7 @@
     SkPdfMapper* fMapper;
     const unsigned char* fFileContent;
     size_t fContentLength;
-    const SkPdfObject* fRootCatalogRef;
+    SkPdfObject* fRootCatalogRef;
     SkPdfCatalogDictionary* fRootCatalog;
 
     mutable SkTDArray<PublicObjectEntry> fObjects;
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
index 8dd5d30..637eb4d 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
@@ -32,9 +32,11 @@
 static void TRACE_INDENT(int level, const char* type) {
     static int id = 0;
     id++;
+#if 0
     if (478613 == id) {
         printf("break;\n");
     }
+#endif
     // all types should have 2 letters, so the text is alligned nicely
     printf("\n%10i %15s: ", id, type);
     for (int i = 0 ; i < level; i++) {
@@ -158,7 +160,6 @@
         }
         array->appendInArray(newObj);
     }
-    printf("break;\n");  // DO NOT SUBMIT!
     // TODO(edisonn): report not reached, we should never get here
     // TODO(edisonn): there might be a bug here, enable an assert and run it on files
     // or it might be that the files were actually corrupted
@@ -954,9 +955,11 @@
 #ifdef PDF_TRACE_READ_TOKEN
     static int read_op = 0;
     read_op++;
+#if 0
     if (548 == read_op) {
         printf("break;\n");
     }
+#endif
     printf("%i READ %s %s\n", read_op, token->fType == kKeyword_TokenType ? "Keyword" : "Object", token->fKeyword ? std::string(token->fKeyword, token->fKeywordLength).c_str() : token->fObject->toString().c_str());
 #endif
 
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfObject.cpp b/experimental/PdfViewer/pdfparser/native/SkPdfObject.cpp
index eb342af..a02d789 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfObject.cpp
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfObject.cpp
@@ -6,6 +6,9 @@
 #include "SkStream.h"
 #include "SkPdfNativeTokenizer.h"
 
+#include "SkBitmap.h"
+#include "SkPdfFont.h"
+
 SkPdfObject SkPdfObject::kNull = SkPdfObject::makeNull();
 
 bool SkPdfObject::applyFlateDecodeFilter() {
@@ -86,3 +89,21 @@
 
     return true;
 }
+
+void SkPdfObject::releaseData() {
+    if (fData) {
+        switch (fDataType) {
+            case kFont_Data:
+                delete (SkPdfFont*)fData;
+                break;
+            case kBitmap_Data:
+                delete (SkBitmap*)fData;
+                break;
+            default:
+                SkASSERT(false);
+                break;
+        }
+    }
+    fData = NULL;
+    fDataType = kEmpty_Data;
+}
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
index 29780d0..1fb4e1f 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfObject.h
@@ -49,6 +49,12 @@
          kUndefined_PdfObjectType,  // per 1.4 spec, if the same key appear twice in the dictionary, the value is undefined
      };
 
+     enum DataType {
+         kEmpty_Data,
+         kFont_Data,
+         kBitmap_Data,
+     };
+
 private:
     struct Reference {
         unsigned int fId;
@@ -76,21 +82,33 @@
         Reference fRef;
     };
     SkTDict<SkPdfObject*>* fMap;
+
+    // TODO(edisonn): rename data with cache
     void* fData;
+    DataType fDataType;
 
 
 public:
 
-    SkPdfObject() : fObjectType(kInvalid_PdfObjectType), fMap(NULL), fData(NULL) {}
+    SkPdfObject() : fObjectType(kInvalid_PdfObjectType), fMap(NULL), fData(NULL), fDataType(kEmpty_Data) {}
 
-    inline void* data() {
-        return fData;
+
+    inline bool hasData(DataType type) {
+        return type == fDataType;
     }
 
-    inline void setData(void* data) {
+    inline void* data(DataType type) {
+        return type == fDataType ? fData : NULL;
+    }
+
+    inline void setData(void* data, DataType type) {
+        releaseData();
+        fDataType = type;
         fData = data;
     }
 
+    void releaseData();
+
 //    ~SkPdfObject() {
 //        //reset();  must be called manually!
 //    }
@@ -114,6 +132,7 @@
                 break;
         }
         fObjectType = kInvalid_PdfObjectType;
+        releaseData();
     }
 
     ObjectType type() { return fObjectType; }
@@ -604,6 +623,10 @@
         return fObjectType == kString_PdfObjectType || fObjectType == kHexString_PdfObjectType;
     }
 
+    bool isHexString() const {
+        return fObjectType == kHexString_PdfObjectType;
+    }
+
     bool isMatrix() const {
         return fObjectType == kArray_PdfObjectType && fArray->count() == 6; // NYI + and elems are numbers
     }
@@ -867,7 +890,9 @@
 
             case kHexString_PdfObjectType:
                 str.append("<");
-                str.append((const char*)fStr.fBuffer, fStr.fBytes);
+                for (unsigned int i = 0 ; i < fStr.fBytes; i++) {
+                    str.appendf("%02x", (unsigned int)fStr.fBuffer[i]);
+                }
                 str.append(">");
                 break;
 
@@ -908,9 +933,9 @@
                         const unsigned char* stream = NULL;
                         size_t length = 0;
                         if (GetFilteredStreamRef(&stream, &length)) {
-                            str.append("stream");
+                            str.append("stream\n");
                             str.append((const char*)stream, length > 256 ? 256 : length);
-                            str.append("endstream");
+                            str.append("\nendstream");
                         } else {
                             str.append("stream STREAM_ERROR endstream");
                         }