Refactor SkPDFImage
authorcommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 23 Aug 2013 19:06:53 +0000 (19:06 +0000)
committercommit-bot@chromium.org <commit-bot@chromium.org@2bbb7eff-a529-9590-31e7-b0007b416f81>
Fri, 23 Aug 2013 19:06:53 +0000 (19:06 +0000)
R=vandebo@chromium.org, edisonn@google.com

Author: richardlin@chromium.org

Review URL: https://chromiumcodereview.appspot.com/22889020

git-svn-id: http://skia.googlecode.com/svn/trunk@10896 2bbb7eff-a529-9590-31e7-b0007b416f81

gyp/pdf.gypi
src/pdf/SkPDFImage.cpp
src/pdf/SkPDFImage.h
src/pdf/SkPDFImageStream.cpp [deleted file]
src/pdf/SkPDFImageStream.h [deleted file]

index ae4a032505b679710ec906b759fead10ae1f3d5c..a4b339214eeef36eceac35c0037c60a376928c6b 100644 (file)
@@ -23,8 +23,6 @@
         '<(skia_src_path)/pdf/SkPDFGraphicState.h',
         '<(skia_src_path)/pdf/SkPDFImage.cpp',
         '<(skia_src_path)/pdf/SkPDFImage.h',
-        '<(skia_src_path)/pdf/SkPDFImageStream.cpp',
-        '<(skia_src_path)/pdf/SkPDFImageStream.h',
         '<(skia_src_path)/pdf/SkPDFPage.cpp',
         '<(skia_src_path)/pdf/SkPDFPage.h',
         '<(skia_src_path)/pdf/SkPDFResourceDict.cpp',
index a5cb4c20d1b9e187948cc6654d590b219df4d686..6ede7aaf515d54b5155ade6f8b9ee6033eb01cec 100644 (file)
 #include "SkBitmap.h"
 #include "SkColor.h"
 #include "SkColorPriv.h"
+#include "SkData.h"
+#include "SkFlate.h"
 #include "SkPDFCatalog.h"
 #include "SkRect.h"
 #include "SkStream.h"
 #include "SkString.h"
 #include "SkUnPreMultiply.h"
 
-namespace {
+static const int kNoColorTransform = 0;
 
-void extractImageData(const SkBitmap& bitmap, const SkIRect& srcRect,
-                      SkStream** imageData, SkStream** alphaData) {
-    SkMemoryStream* image = NULL;
-    SkMemoryStream* alpha = NULL;
-    bool hasAlpha = false;
-    bool isTransparent = false;
+static bool skip_compression(SkPDFCatalog* catalog) {
+    return SkToBool(catalog->getDocumentFlags() &
+                    SkPDFDocument::kFavorSpeedOverSize_Flags);
+}
 
-    bitmap.lockPixels();
+static size_t get_uncompressed_size(const SkBitmap& bitmap,
+                                    const SkIRect& srcRect) {
     switch (bitmap.getConfig()) {
-        case SkBitmap::kIndex8_Config: {
-            const int rowBytes = srcRect.width();
-            image = new SkMemoryStream(rowBytes * srcRect.height());
-            uint8_t* dst = (uint8_t*)image->getMemoryBase();
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
-                dst += rowBytes;
+        case SkBitmap::kIndex8_Config:
+            return srcRect.width() * srcRect.height();
+        case SkBitmap::kARGB_4444_Config:
+            return ((srcRect.width() * 3 + 1) / 2) * srcRect.height();
+        case SkBitmap::kRGB_565_Config:
+            return srcRect.width() * 3 * srcRect.height();
+        case SkBitmap::kARGB_8888_Config:
+            return srcRect.width() * 3 * srcRect.height();
+        case SkBitmap::kA1_Config:
+        case SkBitmap::kA8_Config:
+            return 1;
+        default:
+            SkASSERT(false);
+            return 0;
+    }
+}
+
+static SkStream* extract_index8_image(const SkBitmap& bitmap,
+                                      const SkIRect& srcRect) {
+    const int rowBytes = srcRect.width();
+    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
+                                  (get_uncompressed_size(bitmap, srcRect)));
+    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
+
+    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+        memcpy(dst, bitmap.getAddr8(srcRect.fLeft, y), rowBytes);
+        dst += rowBytes;
+    }
+    return stream;
+}
+
+static SkStream* extract_argb4444_data(const SkBitmap& bitmap,
+                                       const SkIRect& srcRect,
+                                       bool extractAlpha,
+                                       bool* isOpaque,
+                                       bool* isTransparent) {
+    SkStream* stream;
+    uint8_t* dst = NULL;
+    if (extractAlpha) {
+        const int alphaRowBytes = (srcRect.width() + 1) / 2;
+        stream = SkNEW_ARGS(SkMemoryStream,
+                            (alphaRowBytes * srcRect.height()));
+    } else {
+        stream = SkNEW_ARGS(SkMemoryStream,
+                            (get_uncompressed_size(bitmap, srcRect)));
+    }
+    dst = (uint8_t*)stream->getMemoryBase();
+
+    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+        uint16_t* src = bitmap.getAddr16(0, y);
+        int x;
+        for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
+            if (extractAlpha) {
+                dst[0] = (SkGetPackedA4444(src[x]) << 4) |
+                    SkGetPackedA4444(src[x + 1]);
+                *isOpaque &= dst[0] == SK_AlphaOPAQUE;
+                *isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
+                dst++;
+            } else {
+                dst[0] = (SkGetPackedR4444(src[x]) << 4) |
+                    SkGetPackedG4444(src[x]);
+                dst[1] = (SkGetPackedB4444(src[x]) << 4) |
+                    SkGetPackedR4444(src[x + 1]);
+                dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
+                    SkGetPackedB4444(src[x + 1]);
+                dst += 3;
             }
-            break;
         }
-        case SkBitmap::kARGB_4444_Config: {
-            isTransparent = true;
-            const int rowBytes = (srcRect.width() * 3 + 1) / 2;
-            const int alphaRowBytes = (srcRect.width() + 1) / 2;
-            image = new SkMemoryStream(rowBytes * srcRect.height());
-            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
-            uint8_t* dst = (uint8_t*)image->getMemoryBase();
-            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                uint16_t* src = bitmap.getAddr16(0, y);
-                int x;
-                for (x = srcRect.fLeft; x + 1 < srcRect.fRight; x += 2) {
-                    dst[0] = (SkGetPackedR4444(src[x]) << 4) |
-                        SkGetPackedG4444(src[x]);
-                    dst[1] = (SkGetPackedB4444(src[x]) << 4) |
-                        SkGetPackedR4444(src[x + 1]);
-                    dst[2] = (SkGetPackedG4444(src[x + 1]) << 4) |
-                        SkGetPackedB4444(src[x + 1]);
-                    dst += 3;
-                    alphaDst[0] = (SkGetPackedA4444(src[x]) << 4) |
-                        SkGetPackedA4444(src[x + 1]);
-                    if (alphaDst[0] != 0xFF) {
-                        hasAlpha = true;
-                    }
-                    if (alphaDst[0]) {
-                        isTransparent = false;
-                    }
-                    alphaDst++;
-                }
-                if (srcRect.width() & 1) {
-                    dst[0] = (SkGetPackedR4444(src[x]) << 4) |
-                        SkGetPackedG4444(src[x]);
-                    dst[1] = (SkGetPackedB4444(src[x]) << 4);
-                    dst += 2;
-                    alphaDst[0] = (SkGetPackedA4444(src[x]) << 4);
-                    if (alphaDst[0] != 0xF0) {
-                        hasAlpha = true;
-                    }
-                    if (alphaDst[0] & 0xF0) {
-                        isTransparent = false;
-                    }
-                    alphaDst++;
-                }
+        if (srcRect.width() & 1) {
+            if (extractAlpha) {
+                dst[0] = (SkGetPackedA4444(src[x]) << 4);
+                *isOpaque &= dst[0] == (SK_AlphaOPAQUE & 0xF0);
+                *isTransparent &= dst[0] == (SK_AlphaTRANSPARENT & 0xF0);
+                dst++;
+
+            } else {
+                dst[0] = (SkGetPackedR4444(src[x]) << 4) |
+                    SkGetPackedG4444(src[x]);
+                dst[1] = (SkGetPackedB4444(src[x]) << 4);
+                dst += 2;
             }
-            break;
         }
-        case SkBitmap::kRGB_565_Config: {
-            const int rowBytes = srcRect.width() * 3;
-            image = new SkMemoryStream(rowBytes * srcRect.height());
-            uint8_t* dst = (uint8_t*)image->getMemoryBase();
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                uint16_t* src = bitmap.getAddr16(0, y);
-                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-                    dst[0] = SkGetPackedR16(src[x]);
-                    dst[1] = SkGetPackedG16(src[x]);
-                    dst[2] = SkGetPackedB16(src[x]);
-                    dst += 3;
-                }
+    }
+    return stream;
+}
+
+static SkStream* extract_rgb565_image(const SkBitmap& bitmap,
+                                      const SkIRect& srcRect) {
+    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
+                                  (get_uncompressed_size(bitmap,
+                                                     srcRect)));
+    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
+    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+        uint16_t* src = bitmap.getAddr16(0, y);
+        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+            dst[0] = SkGetPackedR16(src[x]);
+            dst[1] = SkGetPackedG16(src[x]);
+            dst[2] = SkGetPackedB16(src[x]);
+            dst += 3;
+        }
+    }
+    return stream;
+}
+
+static SkStream* extract_argb8888_data(const SkBitmap& bitmap,
+                                       const SkIRect& srcRect,
+                                       bool extractAlpha,
+                                       bool* isOpaque,
+                                       bool* isTransparent) {
+    SkStream* stream;
+    if (extractAlpha) {
+        stream = SkNEW_ARGS(SkMemoryStream,
+                            (srcRect.width() * srcRect.height()));
+    } else {
+        stream = SkNEW_ARGS(SkMemoryStream,
+                            (get_uncompressed_size(bitmap, srcRect)));
+    }
+    uint8_t* dst = (uint8_t*)stream->getMemoryBase();
+
+    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+        uint32_t* src = bitmap.getAddr32(0, y);
+        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+            if (extractAlpha) {
+                dst[0] = SkGetPackedA32(src[x]);
+                *isOpaque &= dst[0] == SK_AlphaOPAQUE;
+                *isTransparent &= dst[0] == SK_AlphaTRANSPARENT;
+                dst++;
+            } else {
+                dst[0] = SkGetPackedR32(src[x]);
+                dst[1] = SkGetPackedG32(src[x]);
+                dst[2] = SkGetPackedB32(src[x]);
+                dst += 3;
             }
-            break;
         }
-        case SkBitmap::kARGB_8888_Config: {
-            isTransparent = true;
-            const int rowBytes = srcRect.width() * 3;
-            image = new SkMemoryStream(rowBytes * srcRect.height());
-            alpha = new SkMemoryStream(srcRect.width() * srcRect.height());
-            uint8_t* dst = (uint8_t*)image->getMemoryBase();
-            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                uint32_t* src = bitmap.getAddr32(0, y);
-                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-                    dst[0] = SkGetPackedR32(src[x]);
-                    dst[1] = SkGetPackedG32(src[x]);
-                    dst[2] = SkGetPackedB32(src[x]);
-                    dst += 3;
-                    alphaDst[0] = SkGetPackedA32(src[x]);
-                    if (alphaDst[0] != 0xFF) {
-                        hasAlpha = true;
-                    }
-                    if (alphaDst[0]) {
-                        isTransparent = false;
-                    }
-                    alphaDst++;
-                }
+    }
+    return stream;
+}
+
+static SkStream* extract_a1_alpha(const SkBitmap& bitmap,
+                                  const SkIRect& srcRect,
+                                  bool* isOpaque,
+                                  bool* isTransparent) {
+    const int alphaRowBytes = (srcRect.width() + 7) / 8;
+    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
+                                  (alphaRowBytes * srcRect.height()));
+    uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
+
+    int offset1 = srcRect.fLeft % 8;
+    int offset2 = 8 - offset1;
+
+    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+        uint8_t* src = bitmap.getAddr1(0, y);
+        // This may read up to one byte after src, but the
+        // potentially invalid bits are never used for computation.
+        for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8)  {
+            if (offset1) {
+                alphaDst[0] = src[x / 8] << offset1 |
+                    src[x / 8 + 1] >> offset2;
+            } else {
+                alphaDst[0] = src[x / 8];
             }
-            break;
+            if (x + 7 < srcRect.fRight) {
+                *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
+                *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
+            }
+            alphaDst++;
+        }
+        // Calculate the mask of bits we're interested in within the
+        // last byte of alphaDst.
+        // width mod 8  == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE
+        uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1);
+        if (srcRect.width() % 8) {
+            *isOpaque &= (alphaDst[-1] & mask) == (SK_AlphaOPAQUE & mask);
+            *isTransparent &=
+                    (alphaDst[-1] & mask) == (SK_AlphaTRANSPARENT & mask);
         }
-        case SkBitmap::kA1_Config: {
-            isTransparent = true;
-            image = new SkMemoryStream(1);
-            ((uint8_t*)image->getMemoryBase())[0] = 0;
-
-            const int alphaRowBytes = (srcRect.width() + 7) / 8;
-            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
-            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
-            int offset1 = srcRect.fLeft % 8;
-            int offset2 = 8 - offset1;
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                uint8_t* src = bitmap.getAddr1(0, y);
-                // This may read up to one byte after src, but the potentially
-                // invalid bits are never used for computation.
-                for (int x = srcRect.fLeft; x < srcRect.fRight; x += 8)  {
-                    if (offset1) {
-                        alphaDst[0] = src[x / 8] << offset1 |
-                            src[x / 8 + 1] >> offset2;
-                    } else {
-                        alphaDst[0] = src[x / 8];
-                    }
-                    if (x + 7 < srcRect.fRight && alphaDst[0] != 0xFF) {
-                        hasAlpha = true;
-                    }
-                    if (x + 7 < srcRect.fRight && alphaDst[0]) {
-                        isTransparent = false;
-                    }
-                    alphaDst++;
-                }
-                // Calculate the mask of bits we're interested in within the
-                // last byte of alphaDst.
-                // width mod 8  == 1 -> 0x80 ... width mod 8 == 7 -> 0xFE
-                uint8_t mask = ~((1 << (8 - (srcRect.width() % 8))) - 1);
-                if (srcRect.width() % 8 && (alphaDst[-1] & mask) != mask) {
-                    hasAlpha = true;
-                }
-                if (srcRect.width() % 8 && (alphaDst[-1] & mask)) {
-                    isTransparent = false;
-                }
+    }
+    return stream;
+}
+
+static SkStream* extract_a8_alpha(const SkBitmap& bitmap,
+                                  const SkIRect& srcRect,
+                                  bool* isOpaque,
+                                  bool* isTransparent) {
+    const int alphaRowBytes = srcRect.width();
+    SkStream* stream = SkNEW_ARGS(SkMemoryStream,
+                                  (alphaRowBytes * srcRect.height()));
+    uint8_t* alphaDst = (uint8_t*)stream->getMemoryBase();
+
+    for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
+        uint8_t* src = bitmap.getAddr8(0, y);
+        for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
+            alphaDst[0] = src[x];
+            *isOpaque &= alphaDst[0] == SK_AlphaOPAQUE;
+            *isTransparent &= alphaDst[0] == SK_AlphaTRANSPARENT;
+            alphaDst++;
+        }
+    }
+    return stream;
+}
+
+static SkStream* create_black_image() {
+    SkStream* stream = SkNEW_ARGS(SkMemoryStream, (1));
+    ((uint8_t*)stream->getMemoryBase())[0] = 0;
+    return stream;
+}
+
+/**
+ * Extract either the color or image data from a SkBitmap into a SkStream.
+ * @param bitmap        Bitmap to extract data from.
+ * @param srcRect       Region in the bitmap to extract.
+ * @param extractAlpha  Set to true to extract the alpha data or false to
+ *                      extract the color data.
+ * @param isTransparent Pointer to a bool to output whether the alpha is
+ *                      completely transparent. May be NULL. Only valid when
+ *                      extractAlpha == true.
+ * @return              Unencoded image data, or NULL if either data was not
+ *                      available or alpha data was requested but the image was
+ *                      entirely transparent or opaque.
+ */
+static SkStream* extract_image_data(const SkBitmap& bitmap,
+                                    const SkIRect& srcRect,
+                                    bool extractAlpha, bool* isTransparent) {
+    SkBitmap::Config config = bitmap.config();
+    if (extractAlpha && (config == SkBitmap::kIndex8_Config ||
+            config == SkBitmap::kRGB_565_Config)) {
+        if (isTransparent != NULL) {
+            *isTransparent = false;
+        }
+        return NULL;
+    }
+    bool isOpaque = true;
+    bool transparent = extractAlpha;
+    SkStream* stream = NULL;
+
+    bitmap.lockPixels();
+    switch (config) {
+        case SkBitmap::kIndex8_Config:
+            if (!extractAlpha) {
+                stream = extract_index8_image(bitmap, srcRect);
             }
             break;
-        }
-        case SkBitmap::kA8_Config: {
-            isTransparent = true;
-            image = new SkMemoryStream(1);
-            ((uint8_t*)image->getMemoryBase())[0] = 0;
-
-            const int alphaRowBytes = srcRect.width();
-            alpha = new SkMemoryStream(alphaRowBytes * srcRect.height());
-            uint8_t* alphaDst = (uint8_t*)alpha->getMemoryBase();
-            for (int y = srcRect.fTop; y < srcRect.fBottom; y++) {
-                uint8_t* src = bitmap.getAddr8(0, y);
-                for (int x = srcRect.fLeft; x < srcRect.fRight; x++) {
-                    alphaDst[0] = src[x];
-                    if (alphaDst[0] != 0xFF) {
-                        hasAlpha = true;
-                    }
-                    if (alphaDst[0]) {
-                        isTransparent = false;
-                    }
-                    alphaDst++;
-                }
+        case SkBitmap::kARGB_4444_Config:
+            stream = extract_argb4444_data(bitmap, srcRect, extractAlpha,
+                                           &isOpaque, &transparent);
+            break;
+        case SkBitmap::kRGB_565_Config:
+            if (!extractAlpha) {
+                stream = extract_rgb565_image(bitmap, srcRect);
+            }
+            break;
+        case SkBitmap::kARGB_8888_Config:
+            stream = extract_argb8888_data(bitmap, srcRect, extractAlpha,
+                                           &isOpaque, &transparent);
+            break;
+        case SkBitmap::kA1_Config:
+            if (!extractAlpha) {
+                stream = create_black_image();
+            } else {
+                stream = extract_a1_alpha(bitmap, srcRect,
+                                          &isOpaque, &transparent);
+            }
+            break;
+        case SkBitmap::kA8_Config:
+            if (!extractAlpha) {
+                stream = create_black_image();
+            } else {
+                stream = extract_a8_alpha(bitmap, srcRect,
+                                          &isOpaque, &transparent);
             }
             break;
-        }
         default:
             SkASSERT(false);
     }
     bitmap.unlockPixels();
 
-    if (isTransparent) {
-        SkSafeUnref(image);
-    } else {
-        *imageData = image;
+    if (isTransparent != NULL) {
+        *isTransparent = transparent;
     }
-
-    if (isTransparent || !hasAlpha) {
-        SkSafeUnref(alpha);
-    } else {
-        *alphaData = alpha;
+    if (extractAlpha && (transparent || isOpaque)) {
+        SkSafeUnref(stream);
+        return NULL;
     }
+    return stream;
 }
 
-SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
+static SkPDFArray* make_indexed_color_space(SkColorTable* table) {
     SkPDFArray* result = new SkPDFArray();
     result->reserve(4);
     result->appendName("Indexed");
@@ -229,8 +336,6 @@ SkPDFArray* makeIndexedColorSpace(SkColorTable* table) {
     return result;
 }
 
-};  // namespace
-
 // static
 SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
                                     const SkIRect& srcRect,
@@ -239,24 +344,29 @@ SkPDFImage* SkPDFImage::CreateImage(const SkBitmap& bitmap,
         return NULL;
     }
 
-    SkStream* imageData = NULL;
-    SkStream* alphaData = NULL;
-    extractImageData(bitmap, srcRect, &imageData, &alphaData);
-    SkAutoUnref unrefImageData(imageData);
-    SkAutoUnref unrefAlphaData(alphaData);
-    if (!imageData) {
-        SkASSERT(!alphaData);
+    bool isTransparent = false;
+    SkAutoTUnref<SkStream> alphaData;
+    if (!bitmap.isOpaque()) {
+        // Note that isOpaque is not guaranteed to return false for bitmaps
+        // with alpha support but a completely opaque alpha channel,
+        // so alphaData may still be NULL if we have a completely opaque
+        // (or transparent) bitmap.
+        alphaData.reset(
+                extract_image_data(bitmap, srcRect, true, &isTransparent));
+    }
+    if (isTransparent) {
         return NULL;
     }
 
-    SkPDFImage* image =
-        SkNEW_ARGS(SkPDFImage, (imageData, bitmap, srcRect, false, encoder));
-
-    if (alphaData != NULL) {
-        // Don't try to use DCT compression with alpha because alpha is small
-        // anyway and it could lead to artifacts.
-        image->addSMask(SkNEW_ARGS(SkPDFImage, (alphaData, bitmap, srcRect, true, NULL)))->unref();
+    SkPDFImage* image = SkNEW_ARGS(SkPDFImage, (NULL, bitmap,
+                                                false, srcRect, encoder));
+    if (alphaData.get() != NULL) {
+        SkAutoTUnref<SkPDFImage> mask(
+                SkNEW_ARGS(SkPDFImage, (alphaData.get(), bitmap,
+                                        true, srcRect, NULL)));
+        image->addSMask(mask);
     }
+
     return image;
 }
 
@@ -276,51 +386,62 @@ void SkPDFImage::getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
     GetResourcesHelper(&fResources, knownResourceObjects, newResourceObjects);
 }
 
-SkPDFImage::SkPDFImage(SkStream* imageData,
+SkPDFImage::SkPDFImage(SkStream* stream,
                        const SkBitmap& bitmap,
+                       bool isAlpha,
                        const SkIRect& srcRect,
-                       bool doingAlpha,
                        EncodeToDCTStream encoder)
-        : SkPDFImageStream(imageData, bitmap, srcRect, encoder) {
-    SkBitmap::Config config = bitmap.getConfig();
-    bool alphaOnly = (config == SkBitmap::kA1_Config ||
-                      config == SkBitmap::kA8_Config);
+    : fBitmap(bitmap),
+      fIsAlpha(isAlpha),
+      fSrcRect(srcRect),
+      fEncoder(encoder) {
+
+    if (stream != NULL) {
+        setData(stream);
+        fStreamValid = true;
+    } else {
+        fStreamValid = false;
+    }
+
+    SkBitmap::Config config = fBitmap.getConfig();
 
     insertName("Type", "XObject");
     insertName("Subtype", "Image");
 
-    if (!doingAlpha && alphaOnly) {
+    bool alphaOnly = (config == SkBitmap::kA1_Config ||
+                      config == SkBitmap::kA8_Config);
+
+    if (!isAlpha && alphaOnly) {
         // For alpha only images, we stretch a single pixel of black for
         // the color/shape part.
         SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
         insert("Width", one.get());
         insert("Height", one.get());
     } else {
-        insertInt("Width", srcRect.width());
-        insertInt("Height", srcRect.height());
+        insertInt("Width", fSrcRect.width());
+        insertInt("Height", fSrcRect.height());
     }
 
-    // if (!image mask) {
-    if (doingAlpha || alphaOnly) {
+    if (isAlpha || alphaOnly) {
         insertName("ColorSpace", "DeviceGray");
     } else if (config == SkBitmap::kIndex8_Config) {
-        SkAutoLockPixels alp(bitmap);
+        SkAutoLockPixels alp(fBitmap);
         insert("ColorSpace",
-               makeIndexedColorSpace(bitmap.getColorTable()))->unref();
+               make_indexed_color_space(fBitmap.getColorTable()))->unref();
     } else {
         insertName("ColorSpace", "DeviceRGB");
     }
-    // }
 
     int bitsPerComp = 8;
     if (config == SkBitmap::kARGB_4444_Config) {
         bitsPerComp = 4;
-    } else if (doingAlpha && config == SkBitmap::kA1_Config) {
+    } else if (isAlpha && config == SkBitmap::kA1_Config) {
         bitsPerComp = 1;
     }
     insertInt("BitsPerComponent", bitsPerComp);
 
     if (config == SkBitmap::kRGB_565_Config) {
+        SkASSERT(!isAlpha);
         SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
         SkAutoTUnref<SkPDFScalar> scale5Val(
                 new SkPDFScalar(SkFloatToScalar(8.2258f)));  // 255/2^5-1
@@ -337,3 +458,57 @@ SkPDFImage::SkPDFImage(SkStream* imageData,
         insert("Decode", decodeValue.get());
     }
 }
+
+SkPDFImage::SkPDFImage(SkPDFImage& pdfImage)
+    : SkPDFStream(pdfImage),
+      fBitmap(pdfImage.fBitmap),
+      fIsAlpha(pdfImage.fIsAlpha),
+      fSrcRect(pdfImage.fSrcRect),
+      fEncoder(pdfImage.fEncoder),
+      fStreamValid(pdfImage.fStreamValid) {
+    // Nothing to do here - the image params are already copied in SkPDFStream's
+    // constructor, and the bitmap will be regenerated and encoded in
+    // populate.
+}
+
+bool SkPDFImage::populate(SkPDFCatalog* catalog) {
+    if (getState() == kUnused_State) {
+        // Initializing image data for the first time.
+        SkDynamicMemoryWStream dctCompressedWStream;
+        if (!skip_compression(catalog) && fEncoder &&
+                get_uncompressed_size(fBitmap, fSrcRect) > 1 &&
+                fEncoder(&dctCompressedWStream, fBitmap, fSrcRect) &&
+                dctCompressedWStream.getOffset() <
+                            get_uncompressed_size(fBitmap, fSrcRect)) {
+            SkAutoTUnref<SkData> data(dctCompressedWStream.copyToData());
+            SkAutoTUnref<SkStream> stream(SkNEW_ARGS(SkMemoryStream, (data)));
+            setData(stream.get());
+
+            insertName("Filter", "DCTDecode");
+            insertInt("ColorTransform", kNoColorTransform);
+            insertInt("Length", getData()->getLength());
+            setState(kCompressed_State);
+            return true;
+        }
+        // Fallback method
+        if (!fStreamValid) {
+            SkAutoTUnref<SkStream> stream(
+                    extract_image_data(fBitmap, fSrcRect, fIsAlpha, NULL));
+            setData(stream);
+            fStreamValid = true;
+        }
+        return INHERITED::populate(catalog);
+    } else if (getState() == kNoCompression_State &&
+            !skip_compression(catalog) &&
+            (SkFlate::HaveFlate() || fEncoder)) {
+        // Compression has not been requested when the stream was first created,
+        // but the new catalog wants it compressed.
+        if (!getSubstitute()) {
+            SkPDFStream* substitute = SkNEW_ARGS(SkPDFImage, (*this));
+            setSubstitute(substitute);
+            catalog->setSubstitute(this, substitute);
+        }
+        return false;
+    }
+    return true;
+}
index 31f894086ee3949e20e952d205ed818ac3b01f7d..52d323d0eedd8e969a1c66f2b2b3e231b141d10c 100644 (file)
@@ -11,7 +11,7 @@
 #define SkPDFImage_DEFINED
 
 #include "SkPDFDevice.h"
-#include "SkPDFImageStream.h"
+#include "SkPDFStream.h"
 #include "SkPDFTypes.h"
 #include "SkRefCnt.h"
 
@@ -27,7 +27,7 @@ struct SkIRect;
 // We could play the same trick here as is done in SkPDFGraphicState, storing
 // a copy of the Bitmap object (not the pixels), the pixel generation number,
 // and settings used from the paint to canonicalize image objects.
-class SkPDFImage : public SkPDFImageStream {
+class SkPDFImage : public SkPDFStream {
 public:
     /** Create a new Image XObject to represent the passed bitmap.
      *  @param bitmap   The image to encode.
@@ -48,24 +48,50 @@ public:
      */
     SkPDFImage* addSMask(SkPDFImage* mask);
 
+    bool isEmpty() {
+        return fSrcRect.isEmpty();
+    }
+
     // The SkPDFObject interface.
     virtual void getResources(const SkTSet<SkPDFObject*>& knownResourceObjects,
                               SkTSet<SkPDFObject*>* newResourceObjects);
 
 private:
+    SkBitmap fBitmap;
+    bool fIsAlpha;
+    SkIRect fSrcRect;
+    EncodeToDCTStream fEncoder;
+    bool fStreamValid;
+
     SkTDArray<SkPDFObject*> fResources;
 
     /** Create a PDF image XObject. Entries for the image properties are
      *  automatically added to the stream dictionary.
-     *  @param imageData  The final raw bits representing the image.
-     *  @param bitmap     The image parameters to use (Config, etc).
+     *  @param stream     The image stream. May be NULL. Otherwise, this
+     *                    (instead of the input bitmap) will be used as the
+     *                    PDF's content stream, possibly with lossless encoding.
+     *  @param bitmap     The image. If a stream is not given, its color data
+     *                    will be used as the image. If a stream is given, this
+     *                    is used for configuration only.
+     *  @param isAlpha    Whether or not this is the alpha of an image.
      *  @param srcRect    The clipping applied to bitmap before generating
      *                    imageData.
-     *  @param alpha      Is this the alpha channel of the bitmap.
-     *  @param paint      Used to calculate alpha, masks, etc.
+     *  @param encoder    A function used to encode the bitmap for compression.
+     *                    May be NULL.
+     */
+    SkPDFImage(SkStream* stream, const SkBitmap& bitmap, bool isAlpha,
+               const SkIRect& srcRect, EncodeToDCTStream encoder);
+
+    /** Copy constructor, used to generate substitutes.
+     *  @param image      The SkPDFImage to copy.
      */
-    SkPDFImage(SkStream* imageData, const SkBitmap& bitmap,
-               const SkIRect& srcRect, bool alpha, EncodeToDCTStream encoder);
+    SkPDFImage(SkPDFImage& pdfImage);
+
+    // Populate the stream dictionary.  This method returns false if
+    // fSubstitute should be used.
+    virtual bool populate(SkPDFCatalog* catalog);
+
+    typedef SkPDFStream INHERITED;
 };
 
 #endif
diff --git a/src/pdf/SkPDFImageStream.cpp b/src/pdf/SkPDFImageStream.cpp
deleted file mode 100644 (file)
index 7130f2b..0000000
+++ /dev/null
@@ -1,79 +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 "SkData.h"
-#include "SkFlate.h"
-#include "SkPDFCatalog.h"
-#include "SkPDFImageStream.h"
-#include "SkStream.h"
-
-#define kNoColorTransform 0
-
-static bool skip_compression(SkPDFCatalog* catalog) {
-    return SkToBool(catalog->getDocumentFlags() &
-                    SkPDFDocument::kFavorSpeedOverSize_Flags);
-}
-
-// TODO(edisonn): Use SkData (after removing deprecated constructor in SkPDFStream).
-SkPDFImageStream::SkPDFImageStream(SkStream* stream,
-                                   const SkBitmap& bitmap,
-                                   const SkIRect& srcRect,
-                                   EncodeToDCTStream encoder)
-    : SkPDFStream(stream),
-      fBitmap(bitmap),
-      fSrcRect(srcRect),
-      fEncoder(encoder) {
-}
-
-SkPDFImageStream::SkPDFImageStream(const SkPDFImageStream& pdfStream)
-        : SkPDFStream(pdfStream),
-          fBitmap(pdfStream.fBitmap),
-          fSrcRect(pdfStream.fSrcRect),
-          fEncoder(pdfStream.fEncoder) {
-}
-
-SkPDFImageStream::~SkPDFImageStream() {}
-
-bool SkPDFImageStream::populate(SkPDFCatalog* catalog) {
-    if (getState() == kUnused_State) {
-        if (!skip_compression(catalog)) {
-            SkDynamicMemoryWStream dctCompressedWStream;
-            if (!fEncoder || !fEncoder(&dctCompressedWStream, fBitmap, fSrcRect)) {
-                return INHERITED::populate(catalog);
-            }
-
-            if (dctCompressedWStream.getOffset() < getData()->getLength()) {
-                SkData* data = dctCompressedWStream.copyToData();
-                SkMemoryStream* stream = SkNEW_ARGS(SkMemoryStream, (data));
-                setData(stream);
-                stream->unref();
-                if (data) {
-                    // copyToData and new SkMemoryStream both call ref(), supress one.
-                    data->unref();
-                }
-
-                insertName("Filter", "DCTDecode");
-                insertInt("ColorTransform", kNoColorTransform);
-                setState(kCompressed_State);
-            }
-        }
-        setState(kNoCompression_State);
-        insertInt("Length", getData()->getLength());
-    } else if (getState() == kNoCompression_State && !skip_compression(catalog) &&
-               (SkFlate::HaveFlate() || fEncoder)) {
-        // Compression has not been requested when the stream was first created.
-        // But a new Catalog would want it compressed.
-        if (!getSubstitute()) {
-            SkPDFImageStream* substitute = SkNEW_ARGS(SkPDFImageStream, (*this));
-            setSubstitute(substitute);
-            catalog->setSubstitute(this, substitute);
-        }
-        return false;
-    }
-    return true;
-}
diff --git a/src/pdf/SkPDFImageStream.h b/src/pdf/SkPDFImageStream.h
deleted file mode 100644 (file)
index c518081..0000000
+++ /dev/null
@@ -1,53 +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.
- */
-
-#ifndef SkPDFImageStream_DEFINED
-#define SkPDFImageStream_DEFINED
-
-#include "SkBitmap.h"
-#include "SkPDFDevice.h"
-#include "SkPDFStream.h"
-#include "SkPDFTypes.h"
-#include "SkRect.h"
-#include "SkRefCnt.h"
-#include "SkStream.h"
-#include "SkTemplates.h"
-
-class SkPDFCatalog;
-
-/** \class SkPDFImageStream
-
-    An image stream object in a PDF.  Note, all streams must be indirect objects
-    (via SkObjRef).
-    This class is similar to SkPDFStream, but it is also able to use image
-    specific compression. Currently we support DCT(jpeg) and flate(zip).
-*/
-class SkPDFImageStream : public SkPDFStream {
-public:
-    /** Create a PDF stream with the same content and dictionary entries
-     *  as the passed one.
-     */
-    explicit SkPDFImageStream(const SkPDFImageStream& pdfStream);
-    virtual ~SkPDFImageStream();
-
-protected:
-    SkPDFImageStream(SkStream* stream, const SkBitmap& bitmap,
-                     const SkIRect& srcRect, EncodeToDCTStream encoder);
-
-    // Populate the stream dictionary.  This method returns false if
-    // fSubstitute should be used.
-    virtual bool populate(SkPDFCatalog* catalog);
-
-private:
-    const SkBitmap fBitmap;
-    const SkIRect fSrcRect;
-    EncodeToDCTStream fEncoder;
-
-    typedef SkPDFStream INHERITED;
-};
-
-#endif