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