1/*
2 * Copyright 2013 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkPDFDeviceFlattener.h"
9#include "SkDraw.h"
10
11static SkISize SkSizeToISize(const SkSize& size) {
12    return SkISize::Make(SkScalarRoundToInt(size.width()), SkScalarRoundToInt(size.height()));
13}
14
15SkPDFDeviceFlattener::SkPDFDeviceFlattener(const SkSize& pageSize, const SkRect* trimBox)
16            : SkPDFDevice(SkSizeToISize(pageSize),
17                          SkSizeToISize(pageSize),
18                          SkMatrix::I()) {
19    // TODO(edisonn): store the trimbox on emit.
20}
21
22SkPDFDeviceFlattener::~SkPDFDeviceFlattener() {
23}
24
25static void flattenPaint(const SkDraw& d, SkPaint* paint) {
26    if (paint->getShader()) {
27        SkAutoTUnref<SkShader> lms(SkShader::CreateLocalMatrixShader(paint->getShader(),
28                                                                     *d.fMatrix));
29        paint->setShader(lms);
30    }
31}
32
33void SkPDFDeviceFlattener::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
34                                      size_t count, const SkPoint points[],
35                                      const SkPaint& paint) {
36    if (!mustFlatten(d)) {
37        INHERITED::drawPoints(d, mode, count, points, paint);
38        return;
39    }
40
41    SkPaint paintFlatten(paint);
42    flattenPaint(d, &paintFlatten);
43
44    SkPoint* flattenedPoints = SkNEW_ARRAY(SkPoint, count);
45    d.fMatrix->mapPoints(flattenedPoints, points, SkToS32(count));
46    SkDraw draw(d);
47    SkMatrix identity = SkMatrix::I();
48    draw.fMatrix = &identity;
49    INHERITED::drawPoints(draw, mode, count, flattenedPoints, paintFlatten);
50    SkDELETE_ARRAY(flattenedPoints);
51}
52
53void SkPDFDeviceFlattener::drawRect(const SkDraw& d, const SkRect& r, const SkPaint& paint) {
54    if (!mustFlatten(d)) {
55        INHERITED::drawRect(d, r, paint);
56        return;
57    }
58
59    SkPath path;
60    path.addRect(r);
61    path.transform(*d.fMatrix);
62    SkDraw draw(d);
63    SkMatrix matrix = SkMatrix::I();
64    draw.fMatrix = &matrix;
65
66    SkPaint paintFlatten(paint);
67    flattenPaint(d, &paintFlatten);
68
69    INHERITED::drawPath(draw, path, paintFlatten, NULL, true);
70}
71
72void SkPDFDeviceFlattener::drawPath(const SkDraw& d, const SkPath& origPath,
73                                    const SkPaint& paint, const SkMatrix* prePathMatrix,
74                                    bool pathIsMutable) {
75    if (!mustFlatten(d) && !(prePathMatrix && prePathMatrix->hasPerspective())) {
76        INHERITED::drawPath(d, origPath, paint, prePathMatrix, pathIsMutable);
77        return;
78    }
79
80    SkPath* pathPtr = (SkPath*)&origPath;
81    SkPath tmpPath;
82
83    if (!pathIsMutable) {
84        tmpPath = origPath;
85        pathPtr = &tmpPath;
86    }
87
88    if (prePathMatrix) {
89        pathPtr->transform(*prePathMatrix);
90    }
91
92    SkPaint paintFlatten(paint);
93    flattenPaint(d, &paintFlatten);
94
95    bool fill = paintFlatten.getFillPath(*pathPtr, &tmpPath);
96    SkDEBUGCODE(pathPtr = (SkPath*)0x12345678);  // Don't use pathPtr after this point.
97
98    paintFlatten.setPathEffect(NULL);
99    if (fill) {
100        paintFlatten.setStyle(SkPaint::kFill_Style);
101    } else {
102        paintFlatten.setStyle(SkPaint::kStroke_Style);
103        paintFlatten.setStrokeWidth(0);
104    }
105
106    tmpPath.transform(*d.fMatrix);
107
108    SkDraw draw(d);
109    SkMatrix matrix = SkMatrix::I();
110    draw.fMatrix = &matrix;
111
112    INHERITED::drawPath(draw, tmpPath, paintFlatten, NULL, true);
113}
114
115void SkPDFDeviceFlattener::drawText(const SkDraw& d, const void* text, size_t len,
116                                    SkScalar x, SkScalar y, const SkPaint& paint) {
117    if (mustPathText(d, paint)) {
118        d.drawText_asPaths((const char*)text, len, x, y, paint);
119        return;
120    }
121
122    INHERITED::drawText(d, text, len, x, y, paint);
123}
124
125void SkPDFDeviceFlattener::drawPosText(const SkDraw& d, const void* text, size_t len,
126                                       const SkScalar pos[], SkScalar constY,
127                                       int scalarsPerPos, const SkPaint& paint) {
128    if (mustPathText(d, paint)) {
129        d.drawPosText_asPaths((const char*)text, len, pos, constY, scalarsPerPos, paint);
130        return;
131    }
132    INHERITED::drawPosText(d, text, len, pos, constY,scalarsPerPos, paint);
133}
134
135void SkPDFDeviceFlattener::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
136                                          const SkPath& path, const SkMatrix* matrix,
137                                          const SkPaint& paint) {
138    if (mustPathText(d, paint) || (matrix && matrix->hasPerspective())) {
139        d.drawTextOnPath((const char*)text, len, path, matrix, paint);
140        return;
141    }
142    INHERITED::drawTextOnPath(d, text, len, path, matrix, paint);
143}
144
145bool SkPDFDeviceFlattener::mustFlatten(const SkDraw& d) const {
146    // TODO(edisonn): testability, add flag to force return true.
147    return d.fMatrix->hasPerspective();
148}
149
150bool SkPDFDeviceFlattener::mustPathText(const SkDraw& d, const SkPaint&) {
151    // TODO(edisonn): testability, add flag to force return true.
152    // TODO(edisonn): TBD: How to flatten MaskFilter.
153    return d.fMatrix->hasPerspective();
154}
155