Separate and update PDF_DIFF_TRACE_IN_PNG

Move its functionality out of readToken() and into its own class.
Callers of the previous readToken() now call
SkPdfNativeTokenizer::readToken(), which in turn calls a function
for writing the diff to a file, if the caller requests it and
PDF_TRACE_DIFF_IN_PNG is defined.

Do not attempt to draw a diff for compatibility sections, which we
do not draw.

Use SkString to handle string manipulation.

Hide globals only used by PDF_TRACE_DIFF_IN_PNG behind that flag.

Remove hasVisualEffects, which always returns true.

Rename gLastOpKeyword to gOpCounter for clarity.

In SkPdfNativeTokenizer, set fEmpty to true when the entire stream
has been read.

Use SkBitmap::copyTo instead of manually copying an SkBitmap.

Builds on https://codereview.chromium.org/79933003/

R=mtklein@google.com

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

git-svn-id: http://skia.googlecode.com/svn/trunk@12436 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/PdfViewer/inc/SkPdfDiffEncoder.h b/experimental/PdfViewer/inc/SkPdfDiffEncoder.h
new file mode 100644
index 0000000..9acd82f
--- /dev/null
+++ b/experimental/PdfViewer/inc/SkPdfDiffEncoder.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPdfDiffEncoder_DEFINED
+#define SkPdfDiffEncoder_DEFINED
+
+struct PdfToken;
+
+namespace SkPdfDiffEncoder {
+    /**
+     * If PDF_TRACE_DIFF_IN_PNG is defined, the PDF commands so far are written
+     * to a file with the difference created by using this token highlighted.
+     * The file is named "/tmp/log_step_by_step/step-%i-%s.png", where %i is
+     * the number of the command and %s is the name of the command. If
+     * PDF_TRACE_DIFF_IN_PNG is not defined this function does nothing.
+     * TODO(scroggo): Pass SkPdfContext and SkCanvas for info.
+     */
+    void WriteToFile(PdfToken*);
+};
+
+#endif // SkPdfDiffEncoder_DEFINED
diff --git a/experimental/PdfViewer/inc/SkPdfTokenLooper.h b/experimental/PdfViewer/inc/SkPdfTokenLooper.h
index 8911474..8deca17 100644
--- a/experimental/PdfViewer/inc/SkPdfTokenLooper.h
+++ b/experimental/PdfViewer/inc/SkPdfTokenLooper.h
@@ -42,10 +42,4 @@
     }
 };
 
-// Calls SkPdfNativeTokenizer::readToken, and also does debugging help.
-// TODO(edisonn): Pass SkPdfContext and SkCanvas only with the define for instrumentation.
-// FIXME (scroggo): This calls tokenizer->readToken(). The rest of its functionality should
-// be moved to a debugging file.
-bool readToken(SkPdfNativeTokenizer*, PdfToken*);
-
 #endif // SkPdfTokenLooper_DEFINED
diff --git a/experimental/PdfViewer/pdf_viewer_main.cpp b/experimental/PdfViewer/pdf_viewer_main.cpp
index 5ecd2db..6395585 100644
--- a/experimental/PdfViewer/pdf_viewer_main.cpp
+++ b/experimental/PdfViewer/pdf_viewer_main.cpp
@@ -13,6 +13,7 @@
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
 #include "SkOSFile.h"
+#include "SkPdfConfig.h"
 #include "SkPdfRenderer.h"
 #include "SkPicture.h"
 #include "SkStream.h"
@@ -122,8 +123,10 @@
  * @param page -1 means there is only one page (0), and render in a file without page extension
  */
 
+#ifdef PDF_TRACE_DIFF_IN_PNG
 extern "C" SkBitmap* gDumpBitmap;
 extern "C" SkCanvas* gDumpCanvas;
+#endif
 
 #if SK_SUPPORT_GPU
 GrContextFactory gContextFactory;
@@ -192,9 +195,10 @@
         }
         SkCanvas canvas(device);
 
+#ifdef PDF_TRACE_DIFF_IN_PNG
         gDumpBitmap = &bitmap;
-
         gDumpCanvas = &canvas;
+#endif
         renderer.renderPage(page < 0 ? 0 : page, &canvas, rect);
 
         SkString outputPath;
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
index 79935bc..5dfe14e 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "SkPdfConfig.h"
+#include "SkPdfDiffEncoder.h"
 #include "SkPdfNativeObject.h"
 #include "SkPdfNativeTokenizer.h"
 #include "SkPdfUtils.h"
@@ -940,6 +941,7 @@
 
     fUncompressedStream = skipPdfWhiteSpaces(fUncompressedStream, fUncompressedStreamEnd);
     if (fUncompressedStream >= fUncompressedStreamEnd) {
+        fEmpty = true;
         return false;
     }
 
@@ -985,26 +987,33 @@
 #endif
 }
 
-bool SkPdfNativeTokenizer::readToken(PdfToken* token) {
+bool SkPdfNativeTokenizer::readToken(PdfToken* token, bool writeDiff) {
     if (fHasPutBack) {
         *token = fPutBack;
         fHasPutBack = false;
 #ifdef PDF_TRACE_READ_TOKEN
-    printf("READ_BACK %s %s\n", token->fType == kKeyword_TokenType ? "Keyword" : "Object",
-           token->fKeyword ? SkString(token->fKeyword, token->fKeywordLength).c_str() :
-                             token->fObject->toString().c_str());
+        printf("READ_BACK %s %s\n", token->fType == kKeyword_TokenType ? "Keyword" : "Object",
+               token->fKeyword ? SkString(token->fKeyword, token->fKeywordLength).c_str() :
+                                 token->fObject->toString().c_str());
 #endif
+        if (writeDiff) {
+            SkPdfDiffEncoder::WriteToFile(token);
+        }
         return true;
     }
 
     if (fEmpty) {
 #ifdef PDF_TRACE_READ_TOKEN
-    printf("EMPTY TOKENIZER\n");
+        printf("EMPTY TOKENIZER\n");
 #endif
         return false;
     }
 
-    return readTokenCore(token);
+    const bool result = readTokenCore(token);
+    if (result && writeDiff) {
+        SkPdfDiffEncoder::WriteToFile(token);
+    }
+    return result;
 }
 
 #define DECLARE_PDF_NAME(longName) SkPdfName longName((char*)#longName)
diff --git a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.h b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.h
index 8ed354c..79b070f 100644
--- a/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.h
+++ b/experimental/PdfViewer/pdfparser/native/SkPdfNativeTokenizer.h
@@ -165,7 +165,10 @@
     virtual ~SkPdfNativeTokenizer();
 
     // Reads one token. Returns false if there are no more tokens.
-    bool readToken(PdfToken* token);
+    // If writeDiff is true, and a token was read, create a PNG highlighting
+    // the difference caused by this command in /tmp/log_step_by_step.
+    // If PDF_TRACE_DIFF_IN_PNG is not defined, writeDiff does nothing.
+    bool readToken(PdfToken* token, bool writeDiff = false);
 
     // Put back a token to be read in the nextToken read. Only one token is allowed to be put
     // back. Must not necesaarely be the last token read.
diff --git a/experimental/PdfViewer/src/SkPdfContext.cpp b/experimental/PdfViewer/src/SkPdfContext.cpp
index 6904e34..c216255 100644
--- a/experimental/PdfViewer/src/SkPdfContext.cpp
+++ b/experimental/PdfViewer/src/SkPdfContext.cpp
@@ -126,10 +126,7 @@
 
 void PdfMainLooper::loop() {
     PdfToken token;
-    // readToken defined in SkPdfTokenLooper.h
-    // FIXME (scroggo): Remove readToken (which just calls fTokenizer->readToken, plus draws
-    // some debugging info with PDF_DIFF_TRACE_IN_PNG)
-    while (readToken(fTokenizer, &token)) {
+    while (fTokenizer->readToken(&token, true)) {
         this->consumeToken(token);
     }
 }
diff --git a/experimental/PdfViewer/src/SkPdfDiffEncoder.cpp b/experimental/PdfViewer/src/SkPdfDiffEncoder.cpp
new file mode 100644
index 0000000..5d4c432
--- /dev/null
+++ b/experimental/PdfViewer/src/SkPdfDiffEncoder.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPdfDiffEncoder.h"
+#include "SkPdfNativeTokenizer.h"
+
+#ifdef PDF_TRACE_DIFF_IN_PNG
+#include "SkBitmap.h"
+#include "SkBitmapDevice.h"
+#include "SkCanvas.h"
+#include "SkClipStack.h"
+#include "SkColor.h"
+#include "SkImageEncoder.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkScalar.h"
+#include "SkString.h"
+
+extern "C" SkBitmap* gDumpBitmap;
+extern "C" SkCanvas* gDumpCanvas;
+SkBitmap* gDumpBitmap = NULL;
+SkCanvas* gDumpCanvas = NULL;
+static int gReadOp;
+static int gOpCounter;
+static SkString gLastKeyword;
+#endif  // PDF_TRACE_DIFF_IN_PNG
+
+void SkPdfDiffEncoder::WriteToFile(PdfToken* token) {
+#ifdef PDF_TRACE_DIFF_IN_PNG
+    gReadOp++;
+    gOpCounter++;
+
+    // Only attempt to write if the dump bitmap and canvas are non NULL. They are set by
+    // pdf_viewer_main.cpp
+    if (NULL == gDumpBitmap || NULL == gDumpCanvas) {
+        return;
+    }
+
+    // TODO(edisonn): this code is used to make a step by step history of all the draw operations
+    // so we could find the step where something is wrong.
+    if (!gLastKeyword.isEmpty()) {
+        gDumpCanvas->flush();
+
+        // Copy the existing drawing. Then we will draw the difference caused by this command,
+        // highlighted with a blue border.
+        SkBitmap bitmap;
+        if (gDumpBitmap->copyTo(&bitmap, SkBitmap::kARGB_8888_Config)) {
+
+            SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
+            SkCanvas canvas(device);
+
+            // draw context stuff here
+            SkPaint blueBorder;
+            blueBorder.setColor(SK_ColorBLUE);
+            blueBorder.setStyle(SkPaint::kStroke_Style);
+            blueBorder.setTextSize(SkDoubleToScalar(20));
+
+            SkString str;
+
+            const SkClipStack* clipStack = gDumpCanvas->getClipStack();
+            if (clipStack) {
+                SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
+                const SkClipStack::Element* elem;
+                double y = 0;
+                int total = 0;
+                while ((elem = iter.next()) != NULL) {
+                    total++;
+                    y += 30;
+
+                    switch (elem->getType()) {
+                        case SkClipStack::Element::kRect_Type:
+                            canvas.drawRect(elem->getRect(), blueBorder);
+                            canvas.drawText("Rect Clip", strlen("Rect Clip"),
+                                            SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
+                            break;
+                        case SkClipStack::Element::kPath_Type:
+                            canvas.drawPath(elem->getPath(), blueBorder);
+                            canvas.drawText("Path Clip", strlen("Path Clip"),
+                                            SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
+                            break;
+                        case SkClipStack::Element::kEmpty_Type:
+                            canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"),
+                                            SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
+                            break;
+                        default:
+                            canvas.drawText("Unknown Clip!!!", strlen("Unknown Clip!!!"),
+                                            SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
+                            break;
+                    }
+                }
+
+                y += 30;
+                str.printf("Number of clips in stack: %i", total);
+                canvas.drawText(str.c_str(), str.size(),
+                                SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
+            }
+
+            const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
+            SkPath clipPath;
+            if (clipRegion.getBoundaryPath(&clipPath)) {
+                SkPaint redBorder;
+                redBorder.setColor(SK_ColorRED);
+                redBorder.setStyle(SkPaint::kStroke_Style);
+                canvas.drawPath(clipPath, redBorder);
+            }
+
+            canvas.flush();
+
+            SkString out;
+
+            // TODO(edisonn): overlay on top of image inf about the clip , grafic state, the stack
+
+            out.appendf("/tmp/log_step_by_step/step-%i-%s.png", gOpCounter, gLastKeyword.c_str());
+
+            SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
+        }
+    }
+
+    if (token->fType == kKeyword_TokenType && token->fKeyword && token->fKeywordLength > 0) {
+        gLastKeyword.set(token->fKeyword, token->fKeywordLength);
+    } else {
+        gLastKeyword.reset();
+    }
+#endif
+}
+
diff --git a/experimental/PdfViewer/src/SkPdfRenderer.cpp b/experimental/PdfViewer/src/SkPdfRenderer.cpp
index 16e285a..69adbda 100644
--- a/experimental/PdfViewer/src/SkPdfRenderer.cpp
+++ b/experimental/PdfViewer/src/SkPdfRenderer.cpp
@@ -2736,7 +2736,7 @@
     PdfOp_q(fPdfContext, fCanvas, NULL);
 
     PdfToken token;
-    while (readToken(fTokenizer, &token)) {
+    while (fTokenizer->readToken(&token)) {
         if (token.fType == kKeyword_TokenType && strcmp(token.fKeyword, "BX") == 0) {
             SkPdfTokenLooper* looper = new PdfCompatibilitySectionLooper();
             looper->setUp(this);
diff --git a/experimental/PdfViewer/src/SkPdfTokenLooper.cpp b/experimental/PdfViewer/src/SkPdfTokenLooper.cpp
deleted file mode 100644
index 9193e33..0000000
--- a/experimental/PdfViewer/src/SkPdfTokenLooper.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SkPdfTokenLooper.h"
-#include "SkPdfNativeTokenizer.h"
-#include "SkBitmap.h"
-
-#ifdef PDF_TRACE_DIFF_IN_PNG
-#include "SkBitmapDevice.h"
-#include "SkCanvas.h"
-#include "SkClipStack.h"
-#include "SkColor.h"
-#include "SkImageEncoder.h"
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkScalar.h"
-#include "SkString.h"
-#endif  // PDF_TRACE_DIFF_IN_PNG
-
-// FIXME (scroggo): Put behind build flags.
-extern "C" SkBitmap* gDumpBitmap;
-extern "C" SkCanvas* gDumpCanvas;
-SkBitmap* gDumpBitmap = NULL;
-SkCanvas* gDumpCanvas = NULL;
-int gReadOp;
-int gLastOpKeyword;
-char gLastKeyword[100] = "";
-
-#ifdef PDF_TRACE_DIFF_IN_PNG
-// FIXME (scroggo): allOpWithVisualEffects can be local to hasVisualEffect.
-char allOpWithVisualEffects[100] = ",S,s,f,F,f*,B,B*,b,b*,n,Tj,TJ,\',\",d0,d1,sh,EI,Do,EX,";
-// FIXME (scroggo): has_visual_effect
-static bool hasVisualEffect(const char* pdfOp) {
-    return true;
-    if (*pdfOp == '\0') return false;
-
-    char markedPdfOp[100] = ",";
-    strcat(markedPdfOp, pdfOp);
-    strcat(markedPdfOp, ",");
-
-    return (strstr(allOpWithVisualEffects, markedPdfOp) != NULL);
-}
-
-static void setup_bitmap(SkBitmap* bitmap, int width, int height) {
-    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
-
-    bitmap->allocPixels();
-    bitmap->eraseColor(SK_ColorWHITE);
-}
-
-#endif  // PDF_TRACE_DIFF_IN_PNG
-
-// FIXME (scroggo): fTokenizer -> tokenizer.
-bool readToken(SkPdfNativeTokenizer* fTokenizer, PdfToken* token) {
-    bool ret = fTokenizer->readToken(token);
-
-    gReadOp++;
-    gLastOpKeyword++;
-#ifdef PDF_TRACE_DIFF_IN_PNG
-    // TODO(edisonn): this code is used to make a step by step history of all the draw operations
-    // so we could find the step where something is wrong.
-    if (gLastKeyword[0] && hasVisualEffect(gLastKeyword)) {
-        gDumpCanvas->flush();
-
-        // FIXME (scroggo): Could use SkSurface/SkImage?
-        SkBitmap bitmap;
-        setup_bitmap(&bitmap, gDumpBitmap->width(), gDumpBitmap->height());
-
-        memcpy(bitmap.getPixels(), gDumpBitmap->getPixels(), gDumpBitmap->getSize());
-
-        SkAutoTUnref<SkBaseDevice> device(SkNEW_ARGS(SkBitmapDevice, (bitmap)));
-        SkCanvas canvas(device);
-
-        // draw context stuff here
-        SkPaint blueBorder;
-        blueBorder.setColor(SK_ColorBLUE);
-        blueBorder.setStyle(SkPaint::kStroke_Style);
-        blueBorder.setTextSize(SkDoubleToScalar(20));
-
-        SkString str;
-
-        const SkClipStack* clipStack = gDumpCanvas->getClipStack();
-        if (clipStack) {
-            SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart);
-            const SkClipStack::Element* elem;
-            double y = 0;
-            int total = 0;
-            while ((elem = iter.next()) != NULL) {
-                total++;
-                y += 30;
-
-                switch (elem->getType()) {
-                    case SkClipStack::Element::kRect_Type:
-                        canvas.drawRect(elem->getRect(), blueBorder);
-                        canvas.drawText("Rect Clip", strlen("Rect Clip"),
-                                        SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
-                        break;
-                    case SkClipStack::Element::kPath_Type:
-                        canvas.drawPath(elem->getPath(), blueBorder);
-                        canvas.drawText("Path Clip", strlen("Path Clip"),
-                                        SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
-                        break;
-                    case SkClipStack::Element::kEmpty_Type:
-                        canvas.drawText("Empty Clip!!!", strlen("Empty Clip!!!"),
-                                        SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
-                        break;
-                    default:
-                        canvas.drawText("Unkown Clip!!!", strlen("Unkown Clip!!!"),
-                                        SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
-                        break;
-                }
-            }
-
-            y += 30;
-            str.printf("Number of clips in stack: %i", total);
-            canvas.drawText(str.c_str(), str.size(),
-                            SkDoubleToScalar(10), SkDoubleToScalar(y), blueBorder);
-        }
-
-        const SkRegion& clipRegion = gDumpCanvas->getTotalClip();
-        SkPath clipPath;
-        if (clipRegion.getBoundaryPath(&clipPath)) {
-            SkPaint redBorder;
-            redBorder.setColor(SK_ColorRED);
-            redBorder.setStyle(SkPaint::kStroke_Style);
-            canvas.drawPath(clipPath, redBorder);
-        }
-
-        canvas.flush();
-
-        SkString out;
-
-        // TODO(edisonn): overlay on top of image inf about the clip , grafic state, the stack
-
-        out.appendf("/tmp/log_step_by_step/step-%i-%s.png",
-                    gLastOpKeyword, gLastKeyword);
-        SkImageEncoder::EncodeFile(out.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
-    }
-
-    if (ret && token->fType == kKeyword_TokenType &&
-            token->fKeyword && token->fKeywordLength > 0 && token->fKeywordLength < 100) {
-        strncpy(gLastKeyword, token->fKeyword, token->fKeywordLength);
-        gLastKeyword[token->fKeywordLength] = '\0';
-    } else {
-        gLastKeyword[0] = '\0';
-    }
-#endif
-
-    return ret;
-}
-
diff --git a/gyp/pdfviewer_lib.gyp b/gyp/pdfviewer_lib.gyp
index 405d02c..1bfc049 100644
--- a/gyp/pdfviewer_lib.gyp
+++ b/gyp/pdfviewer_lib.gyp
@@ -12,13 +12,14 @@
         # FIXME: Include directory is named "inc" (instead of "include") in
         # order to not be considered the public API.
         '../experimental/PdfViewer/inc/SkPdfContext.h',
+        '../experimental/PdfViewer/inc/SkPdfDiffEncoder.h',
         '../experimental/PdfViewer/inc/SkPdfRenderer.h',
         '../experimental/PdfViewer/inc/SkPdfTokenLooper.h',
 
         '../experimental/PdfViewer/src/SkPdfContext.cpp',
         '../experimental/PdfViewer/src/SkPdfRenderer.cpp',
         '../experimental/PdfViewer/src/SkTDStackNester.h',
-        '../experimental/PdfViewer/src/SkPdfTokenLooper.cpp',
+        '../experimental/PdfViewer/src/SkPdfDiffEncoder.cpp',
 
         '../experimental/PdfViewer/SkPdfGraphicsState.cpp',
         '../experimental/PdfViewer/SkPdfFont.cpp',