Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / pdf / SkPDFDevice.cpp
index 77aa7a3..d227dd7 100644 (file)
@@ -27,6 +27,7 @@
 #include "SkPDFTypes.h"
 #include "SkPDFUtils.h"
 #include "SkRect.h"
+#include "SkRRect.h"
 #include "SkString.h"
 #include "SkTextFormatParams.h"
 #include "SkTemplates.h"
@@ -49,6 +50,8 @@ struct TypefaceFallbackData {
 };
 #endif
 
+#define DPI_FOR_RASTER_SCALE_ONE 72
+
 // Utility functions
 
 static void emit_pdf_color(SkColor color, SkWStream* result) {
@@ -120,7 +123,7 @@ static void align_text(SkDrawCacheProc glyphCacheProc, const SkPaint& paint,
     *y = *y - yAdj;
 }
 
-static size_t max_glyphid_for_typeface(SkTypeface* typeface) {
+static int max_glyphid_for_typeface(SkTypeface* typeface) {
     SkAutoResolveDefaultTypeface autoResolve(typeface);
     typeface = autoResolve.get();
     return typeface->countGlyphs() - 1;
@@ -229,15 +232,14 @@ GraphicStateEntry::GraphicStateEntry() : fColor(SK_ColorBLACK),
     fMatrix.reset();
 }
 
-bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& b) {
-    return fColor == b.fColor &&
-           fShaderIndex == b.fShaderIndex &&
-           fGraphicStateIndex == b.fGraphicStateIndex &&
-           fMatrix == b.fMatrix &&
-           fClipStack == b.fClipStack &&
-               (fTextScaleX == 0 ||
-                b.fTextScaleX == 0 ||
-                (fTextScaleX == b.fTextScaleX && fTextFill == b.fTextFill));
+bool GraphicStateEntry::compareInitialState(const GraphicStateEntry& cur) {
+    return fColor == cur.fColor &&
+           fShaderIndex == cur.fShaderIndex &&
+           fGraphicStateIndex == cur.fGraphicStateIndex &&
+           fMatrix == cur.fMatrix &&
+           fClipStack == cur.fClipStack &&
+           (fTextScaleX == 0 ||
+               (fTextScaleX == cur.fTextScaleX && fTextFill == cur.fTextFill));
 }
 
 class GraphicStackState {
@@ -742,7 +744,7 @@ SkPDFDevice::SkPDFDevice(const SkISize& pageSize, const SkISize& contentSize,
       fLastMarginContentEntry(NULL),
       fClipStack(NULL),
       fEncoder(NULL),
-      fRasterDpi(SkFloatToScalar(72.0f)) {
+      fRasterDpi(72.0f) {
     // Just report that PDF does not supports perspective in the
     // initial transform.
     NOT_IMPLEMENTED(initialTransform.hasPerspective(), true);
@@ -773,7 +775,7 @@ SkPDFDevice::SkPDFDevice(const SkISize& layerSize,
       fLastMarginContentEntry(NULL),
       fClipStack(NULL),
       fEncoder(NULL),
-      fRasterDpi(SkFloatToScalar(72.0f)) {
+      fRasterDpi(72.0f) {
     fInitialTransform.reset();
     this->init();
 }
@@ -967,6 +969,13 @@ void SkPDFDevice::drawRect(const SkDraw& d, const SkRect& rect,
                           &content.entry()->fContent);
 }
 
+void SkPDFDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect,
+                            const SkPaint& paint) {
+    SkPath  path;
+    path.addRRect(rrect);
+    this->drawPath(draw, path, paint, NULL, true);
+}
+
 void SkPDFDevice::drawPath(const SkDraw& d, const SkPath& origPath,
                            const SkPaint& paint, const SkMatrix* prePathMatrix,
                            bool pathIsMutable) {
@@ -1331,7 +1340,7 @@ void SkPDFDevice::drawVertices(const SkDraw& d, SkCanvas::VertexMode,
     if (d.fClip->isEmpty()) {
         return;
     }
-    NOT_IMPLEMENTED("drawVerticies", true);
+    // TODO: implement drawVertices
 }
 
 void SkPDFDevice::drawDevice(const SkDraw& d, SkBaseDevice* device,
@@ -1865,11 +1874,12 @@ ContentEntry* SkPDFDevice::setUpContentEntry(const SkClipStack* clipStack,
     return entry;
 }
 
-void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
+void SkPDFDevice::finishContentEntry(SkXfermode::Mode xfermode,
                                      SkPDFFormXObject* dst,
                                      SkPath* shape) {
     if (xfermode != SkXfermode::kClear_Mode       &&
             xfermode != SkXfermode::kSrc_Mode     &&
+            xfermode != SkXfermode::kDstOver_Mode &&
             xfermode != SkXfermode::kSrcIn_Mode   &&
             xfermode != SkXfermode::kDstIn_Mode   &&
             xfermode != SkXfermode::kSrcOut_Mode  &&
@@ -1880,6 +1890,18 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
         SkASSERT(!dst);
         return;
     }
+    if (xfermode == SkXfermode::kDstOver_Mode) {
+        SkASSERT(!dst);
+        ContentEntry* firstContentEntry = getContentEntries()->get();
+        if (firstContentEntry->fContent.getOffset() == 0) {
+            // For DstOver, an empty content entry was inserted before the rest
+            // of the content entries. If nothing was drawn, it needs to be
+            // removed.
+            SkAutoTDelete<ContentEntry>* contentEntries = getContentEntries();
+            contentEntries->reset(firstContentEntry->fNext.detach());
+        }
+        return;
+    }
     if (!dst) {
         SkASSERT(xfermode == SkXfermode::kSrc_Mode ||
                  xfermode == SkXfermode::kSrcOut_Mode);
@@ -1889,22 +1911,40 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
     ContentEntry* contentEntries = getContentEntries()->get();
     SkASSERT(dst);
     SkASSERT(!contentEntries->fNext.get());
-    // We have to make a copy of these here because changing the current
-    // content into a form-xobject will destroy them.
+    // Changing the current content into a form-xobject will destroy the clip
+    // objects which is fine since the xobject will already be clipped. However
+    // if source has shape, we need to clip it too, so a copy of the clip is
+    // saved.
     SkClipStack clipStack = contentEntries->fState.fClipStack;
     SkRegion clipRegion = contentEntries->fState.fClipRegion;
 
+    SkMatrix identity;
+    identity.reset();
+    SkPaint stockPaint;
+
     SkAutoTUnref<SkPDFFormXObject> srcFormXObject;
     if (isContentEmpty()) {
-        SkASSERT(xfermode == SkXfermode::kClear_Mode);
+        // If nothing was drawn and there's no shape, then the draw was a
+        // no-op, but dst needs to be restored for that to be true.
+        // If there is shape, then an empty source with Src, SrcIn, SrcOut,
+        // DstIn, DstAtop or Modulate reduces to Clear and DstOut or SrcAtop
+        // reduces to Dst.
+        if (shape == NULL || xfermode == SkXfermode::kDstOut_Mode ||
+                xfermode == SkXfermode::kSrcATop_Mode) {
+            ScopedContentEntry content(this, &fExistingClipStack,
+                                       fExistingClipRegion, identity,
+                                       stockPaint);
+            SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
+                                        &content.entry()->fContent);
+            return;
+        } else {
+            xfermode = SkXfermode::kClear_Mode;
+        }
     } else {
         SkASSERT(!fContentEntries->fNext.get());
         srcFormXObject.reset(createFormXObjectFromDevice());
     }
 
-    SkMatrix identity;
-    identity.reset();
-
     // TODO(vandebo) srcFormXObject may contain alpha, but here we want it
     // without alpha.
     if (xfermode == SkXfermode::kSrcATop_Mode) {
@@ -1913,7 +1953,7 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
         // the non-transparent parts of the device and the outlines (shape) of
         // all images and devices drawn.
         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
-                                &clipStack, clipRegion,
+                                &fExistingClipStack, fExistingClipRegion,
                                 SkXfermode::kSrcOver_Mode, true);
     } else {
         SkAutoTUnref<SkPDFFormXObject> dstMaskStorage;
@@ -1932,18 +1972,17 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
             dstMaskStorage.reset(createFormXObjectFromDevice());
             dstMask = dstMaskStorage.get();
         }
-        drawFormXObjectWithMask(addXObjectResource(dst), dstMask, &clipStack,
-                                clipRegion, SkXfermode::kSrcOver_Mode, true);
+        drawFormXObjectWithMask(addXObjectResource(dst), dstMask,
+                                &fExistingClipStack, fExistingClipRegion,
+                                SkXfermode::kSrcOver_Mode, true);
     }
 
-    SkPaint stockPaint;
-
     if (xfermode == SkXfermode::kClear_Mode) {
         return;
     } else if (xfermode == SkXfermode::kSrc_Mode ||
             xfermode == SkXfermode::kDstATop_Mode) {
-        ScopedContentEntry content(this, &clipStack, clipRegion, identity,
-                                   stockPaint);
+        ScopedContentEntry content(this, &fExistingClipStack,
+                                   fExistingClipRegion, identity, stockPaint);
         if (content.entry()) {
             SkPDFUtils::DrawFormXObject(
                     this->addXObjectResource(srcFormXObject.get()),
@@ -1953,8 +1992,8 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
             return;
         }
     } else if (xfermode == SkXfermode::kSrcATop_Mode) {
-        ScopedContentEntry content(this, &clipStack, clipRegion, identity,
-                                   stockPaint);
+        ScopedContentEntry content(this, &fExistingClipStack,
+                                   fExistingClipRegion, identity, stockPaint);
         if (content.entry()) {
             SkPDFUtils::DrawFormXObject(this->addXObjectResource(dst),
                                         &content.entry()->fContent);
@@ -1969,30 +2008,24 @@ void SkPDFDevice::finishContentEntry(const SkXfermode::Mode xfermode,
              xfermode == SkXfermode::kDstATop_Mode ||
              xfermode == SkXfermode::kModulate_Mode);
 
-    ScopedContentEntry inShapeContentEntry(this, &fExistingClipStack,
-                                           fExistingClipRegion, identity,
-                                           stockPaint);
-    if (!inShapeContentEntry.entry()) {
-        return;
-    }
-
     if (xfermode == SkXfermode::kSrcIn_Mode ||
             xfermode == SkXfermode::kSrcOut_Mode ||
             xfermode == SkXfermode::kSrcATop_Mode) {
         drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()), dst,
-                                &clipStack, clipRegion,
+                                &fExistingClipStack, fExistingClipRegion,
                                 SkXfermode::kSrcOver_Mode,
                                 xfermode == SkXfermode::kSrcOut_Mode);
     } else {
         SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode;
         if (xfermode == SkXfermode::kModulate_Mode) {
             drawFormXObjectWithMask(addXObjectResource(srcFormXObject.get()),
-                                    dst, &clipStack, clipRegion,
+                                    dst, &fExistingClipStack,
+                                    fExistingClipRegion,
                                     SkXfermode::kSrcOver_Mode, false);
             mode = SkXfermode::kMultiply_Mode;
         }
         drawFormXObjectWithMask(addXObjectResource(dst), srcFormXObject.get(),
-                                &clipStack, clipRegion, mode,
+                                &fExistingClipStack, fExistingClipRegion, mode,
                                 xfermode == SkXfermode::kDstOut_Mode);
     }
 }
@@ -2163,6 +2196,9 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
 
     // Rasterize the bitmap using perspective in a new bitmap.
     if (origMatrix.hasPerspective()) {
+        if (fRasterDpi == 0) {
+            return;
+        }
         SkBitmap* subsetBitmap;
         if (srcRect) {
             if (!origBitmap.extractSubset(&tmpSubsetBitmap, *srcRect)) {
@@ -2175,7 +2211,8 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
         }
         srcRect = NULL;
 
-        // Transform the bitmap in the new space.
+        // Transform the bitmap in the new space, without taking into
+        // account the initial transform.
         SkPath perspectiveOutline;
         perspectiveOutline.addRect(
                 SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()),
@@ -2186,8 +2223,24 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
         // Retrieve the bounds of the new shape.
         SkRect bounds = perspectiveOutline.getBounds();
 
-        // TODO(edisonn): add DPI settings. Currently 1 pixel/point, which does
-        // not look great, but it is not producing large PDFs.
+        // Transform the bitmap in the new space, taking into
+        // account the initial transform.
+        SkMatrix total = origMatrix;
+        total.postConcat(fInitialTransform);
+        total.postScale(SkIntToScalar(fRasterDpi) /
+                            SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE),
+                        SkIntToScalar(fRasterDpi) /
+                            SkIntToScalar(DPI_FOR_RASTER_SCALE_ONE));
+        SkPath physicalPerspectiveOutline;
+        physicalPerspectiveOutline.addRect(
+                SkRect::MakeWH(SkIntToScalar(subsetBitmap->width()),
+                               SkIntToScalar(subsetBitmap->height())));
+        physicalPerspectiveOutline.transform(total);
+
+        SkScalar scaleX = physicalPerspectiveOutline.getBounds().width() /
+                              bounds.width();
+        SkScalar scaleY = physicalPerspectiveOutline.getBounds().height() /
+                              bounds.height();
 
         // TODO(edisonn): A better approach would be to use a bitmap shader
         // (in clamp mode) and draw a rect over the entire bounding box. Then
@@ -2196,10 +2249,11 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
         // the image.  Avoiding alpha will reduce the pdf size and generation
         // CPU time some.
 
-        perspectiveBitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                                    SkScalarCeilToInt(bounds.width()),
-                                    SkScalarCeilToInt(bounds.height()));
-        perspectiveBitmap.allocPixels();
+        const int w = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().width());
+        const int h = SkScalarCeilToInt(physicalPerspectiveOutline.getBounds().height());
+        if (!perspectiveBitmap.allocPixels(SkImageInfo::MakeN32Premul(w, h))) {
+            return;
+        }
         perspectiveBitmap.eraseColor(SK_ColorTRANSPARENT);
 
         SkBitmapDevice device(perspectiveBitmap);
@@ -2210,6 +2264,7 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
 
         SkMatrix offsetMatrix = origMatrix;
         offsetMatrix.postTranslate(-deltaX, -deltaY);
+        offsetMatrix.postScale(scaleX, scaleY);
 
         // Translate the draw in the new canvas, so we perfectly fit the
         // shape in the bitmap.
@@ -2220,8 +2275,11 @@ void SkPDFDevice::internalDrawBitmap(const SkMatrix& origMatrix,
         // Make sure the final bits are in the bitmap.
         canvas.flush();
 
-        // In the new space, we use the identity matrix translated.
-        matrix.setTranslate(deltaX, deltaY);
+        // In the new space, we use the identity matrix translated
+        // and scaled to reflect DPI.
+        matrix.setScale(1 / scaleX, 1 / scaleY);
+        matrix.postTranslate(deltaX, deltaY);
+
         perspectiveBounds.setRect(
                 SkIRect::MakeXYWH(SkScalarFloorToInt(bounds.x()),
                                   SkScalarFloorToInt(bounds.y()),
@@ -2271,6 +2329,6 @@ bool SkPDFDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
     return false;
 }
 
-bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
+bool SkPDFDevice::allowImageFilter(const SkImageFilter*) {
     return false;
 }