1/*
2 * Copyright 2011 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 "SkDevice.h"
9#include "SkDeviceProperties.h"
10#include "SkDraw.h"
11#include "SkDrawFilter.h"
12#include "SkImage_Base.h"
13#include "SkMetaData.h"
14#include "SkPatchUtils.h"
15#include "SkPathMeasure.h"
16#include "SkRasterClip.h"
17#include "SkShader.h"
18#include "SkTextBlob.h"
19#include "SkTextToPathIter.h"
20
21SkBaseDevice::SkBaseDevice()
22    : fLeakyProperties(SkNEW_ARGS(SkDeviceProperties, (SkDeviceProperties::kLegacyLCD_InitType)))
23#ifdef SK_DEBUG
24    , fAttachedToCanvas(false)
25#endif
26{
27    fOrigin.setZero();
28    fMetaData = NULL;
29}
30
31SkBaseDevice::SkBaseDevice(const SkDeviceProperties& dp)
32    : fLeakyProperties(SkNEW_ARGS(SkDeviceProperties, (dp)))
33#ifdef SK_DEBUG
34    , fAttachedToCanvas(false)
35#endif
36{
37    fOrigin.setZero();
38    fMetaData = NULL;
39}
40
41SkBaseDevice::~SkBaseDevice() {
42    SkDELETE(fLeakyProperties);
43    SkDELETE(fMetaData);
44}
45
46SkMetaData& SkBaseDevice::getMetaData() {
47    // metadata users are rare, so we lazily allocate it. If that changes we
48    // can decide to just make it a field in the device (rather than a ptr)
49    if (NULL == fMetaData) {
50        fMetaData = new SkMetaData;
51    }
52    return *fMetaData;
53}
54
55SkImageInfo SkBaseDevice::imageInfo() const {
56    return SkImageInfo::MakeUnknown();
57}
58
59const SkBitmap& SkBaseDevice::accessBitmap(bool changePixels) {
60    const SkBitmap& bitmap = this->onAccessBitmap();
61    if (changePixels) {
62        bitmap.notifyPixelsChanged();
63    }
64    return bitmap;
65}
66
67SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info,
68                                                         TileUsage tileUsage,
69                                                         SkPixelGeometry geo) {
70    switch (tileUsage) {
71        case kPossible_TileUsage:
72            // (we think) for compatibility with old clients, we assume this layer can support LCD
73            // even though they may not have marked it as opaque... seems like we should update
74            // our callers (reed/robertphilips).
75            break;
76        case kNever_TileUsage:
77            if (info.alphaType() != kOpaque_SkAlphaType) {
78                geo = kUnknown_SkPixelGeometry;
79            }
80            break;
81    }
82    return geo;
83}
84
85void SkBaseDevice::initForRootLayer(SkPixelGeometry geo) {
86    // For now we don't expect to change the geometry for the root-layer, but we make the call
87    // anyway to document logically what is going on.
88    //
89    fLeakyProperties->setPixelGeometry(CreateInfo::AdjustGeometry(this->imageInfo(),
90                                                                  kPossible_TileUsage,
91                                                                  geo));
92}
93
94SkSurface* SkBaseDevice::newSurface(const SkImageInfo&, const SkSurfaceProps&) { return NULL; }
95
96const void* SkBaseDevice::peekPixels(SkImageInfo*, size_t*) { return NULL; }
97
98void SkBaseDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
99                              const SkRRect& inner, const SkPaint& paint) {
100    SkPath path;
101    path.addRRect(outer);
102    path.addRRect(inner);
103    path.setFillType(SkPath::kEvenOdd_FillType);
104
105    const SkMatrix* preMatrix = NULL;
106    const bool pathIsMutable = true;
107    this->drawPath(draw, path, paint, preMatrix, pathIsMutable);
108}
109
110void SkBaseDevice::drawPatch(const SkDraw& draw, const SkPoint cubics[12], const SkColor colors[4],
111                             const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
112    SkPatchUtils::VertexData data;
113
114    SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, draw.fMatrix);
115
116    // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
117    // If it fails to generate the vertices, then we do not draw.
118    if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
119        this->drawVertices(draw, SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
120                           data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
121                           paint);
122    }
123}
124
125void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y,
126                                const SkPaint &paint, SkDrawFilter* drawFilter) {
127
128    SkPaint runPaint = paint;
129
130    SkTextBlob::RunIterator it(blob);
131    for (;!it.done(); it.next()) {
132        size_t textLen = it.glyphCount() * sizeof(uint16_t);
133        const SkPoint& offset = it.offset();
134        // applyFontToPaint() always overwrites the exact same attributes,
135        // so it is safe to not re-seed the paint for this reason.
136        it.applyFontToPaint(&runPaint);
137
138        if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
139            // A false return from filter() means we should abort the current draw.
140            runPaint = paint;
141            continue;
142        }
143
144        runPaint.setFlags(this->filterTextFlags(runPaint));
145
146        switch (it.positioning()) {
147        case SkTextBlob::kDefault_Positioning:
148            this->drawText(draw, it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
149            break;
150        case SkTextBlob::kHorizontal_Positioning:
151            this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 1,
152                              SkPoint::Make(x, y + offset.y()), runPaint);
153            break;
154        case SkTextBlob::kFull_Positioning:
155            this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 2,
156                              SkPoint::Make(x, y), runPaint);
157            break;
158        default:
159            SkFAIL("unhandled positioning mode");
160        }
161
162        if (drawFilter) {
163            // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
164            runPaint = paint;
165        }
166    }
167}
168
169void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
170                             const SkPaint& paint) {
171    // Default impl : turns everything into raster bitmap
172    SkBitmap bm;
173    if (as_IB(image)->getROPixels(&bm)) {
174        this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
175    }
176}
177
178void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
179                                 const SkRect& dst, const SkPaint& paint) {
180    // Default impl : turns everything into raster bitmap
181    SkBitmap bm;
182    if (as_IB(image)->getROPixels(&bm)) {
183        this->drawBitmapRect(draw, bm, src, dst, paint, SkCanvas::kNone_DrawBitmapRectFlag);
184    }
185}
186
187bool SkBaseDevice::readPixels(const SkImageInfo& info, void* dstP, size_t rowBytes, int x, int y) {
188#ifdef SK_DEBUG
189    SkASSERT(info.width() > 0 && info.height() > 0);
190    SkASSERT(dstP);
191    SkASSERT(rowBytes >= info.minRowBytes());
192    SkASSERT(x >= 0 && y >= 0);
193
194    const SkImageInfo& srcInfo = this->imageInfo();
195    SkASSERT(x + info.width() <= srcInfo.width());
196    SkASSERT(y + info.height() <= srcInfo.height());
197#endif
198    return this->onReadPixels(info, dstP, rowBytes, x, y);
199}
200
201bool SkBaseDevice::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
202                               int x, int y) {
203#ifdef SK_DEBUG
204    SkASSERT(info.width() > 0 && info.height() > 0);
205    SkASSERT(pixels);
206    SkASSERT(rowBytes >= info.minRowBytes());
207    SkASSERT(x >= 0 && y >= 0);
208
209    const SkImageInfo& dstInfo = this->imageInfo();
210    SkASSERT(x + info.width() <= dstInfo.width());
211    SkASSERT(y + info.height() <= dstInfo.height());
212#endif
213    return this->onWritePixels(info, pixels, rowBytes, x, y);
214}
215
216bool SkBaseDevice::onWritePixels(const SkImageInfo&, const void*, size_t, int, int) {
217    return false;
218}
219
220bool SkBaseDevice::onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) {
221    return false;
222}
223
224void* SkBaseDevice::accessPixels(SkImageInfo* info, size_t* rowBytes) {
225    SkImageInfo tmpInfo;
226    size_t tmpRowBytes;
227    if (NULL == info) {
228        info = &tmpInfo;
229    }
230    if (NULL == rowBytes) {
231        rowBytes = &tmpRowBytes;
232    }
233    return this->onAccessPixels(info, rowBytes);
234}
235
236void* SkBaseDevice::onAccessPixels(SkImageInfo* info, size_t* rowBytes) {
237    return NULL;
238}
239
240bool SkBaseDevice::EXPERIMENTAL_drawPicture(SkCanvas*, const SkPicture*, const SkMatrix*,
241                                            const SkPaint*) {
242    // The base class doesn't perform any accelerated picture rendering
243    return false;
244}
245
246//////////////////////////////////////////////////////////////////////////////////////////
247
248static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
249                        SkPathMeasure& meas, const SkMatrix& matrix) {
250    SkMatrix::MapXYProc proc = matrix.getMapXYProc();
251
252    for (int i = 0; i < count; i++) {
253        SkPoint pos;
254        SkVector tangent;
255
256        proc(matrix, src[i].fX, src[i].fY, &pos);
257        SkScalar sx = pos.fX;
258        SkScalar sy = pos.fY;
259
260        if (!meas.getPosTan(sx, &pos, &tangent)) {
261            // set to 0 if the measure failed, so that we just set dst == pos
262            tangent.set(0, 0);
263        }
264
265        /*  This is the old way (that explains our approach but is way too slow
266         SkMatrix    matrix;
267         SkPoint     pt;
268
269         pt.set(sx, sy);
270         matrix.setSinCos(tangent.fY, tangent.fX);
271         matrix.preTranslate(-sx, 0);
272         matrix.postTranslate(pos.fX, pos.fY);
273         matrix.mapPoints(&dst[i], &pt, 1);
274         */
275        dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
276                   pos.fY + SkScalarMul(tangent.fX, sy));
277    }
278}
279
280/*  TODO
281
282 Need differentially more subdivisions when the follow-path is curvy. Not sure how to
283 determine that, but we need it. I guess a cheap answer is let the caller tell us,
284 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
285 */
286static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
287                      const SkMatrix& matrix) {
288    SkPath::Iter    iter(src, false);
289    SkPoint         srcP[4], dstP[3];
290    SkPath::Verb    verb;
291
292    while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
293        switch (verb) {
294            case SkPath::kMove_Verb:
295                morphpoints(dstP, srcP, 1, meas, matrix);
296                dst->moveTo(dstP[0]);
297                break;
298            case SkPath::kLine_Verb:
299                // turn lines into quads to look bendy
300                srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
301                srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
302                morphpoints(dstP, srcP, 2, meas, matrix);
303                dst->quadTo(dstP[0], dstP[1]);
304                break;
305            case SkPath::kQuad_Verb:
306                morphpoints(dstP, &srcP[1], 2, meas, matrix);
307                dst->quadTo(dstP[0], dstP[1]);
308                break;
309            case SkPath::kCubic_Verb:
310                morphpoints(dstP, &srcP[1], 3, meas, matrix);
311                dst->cubicTo(dstP[0], dstP[1], dstP[2]);
312                break;
313            case SkPath::kClose_Verb:
314                dst->close();
315                break;
316            default:
317                SkDEBUGFAIL("unknown verb");
318                break;
319        }
320    }
321}
322
323void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t byteLength,
324                                  const SkPath& follow, const SkMatrix* matrix,
325                                  const SkPaint& paint) {
326    SkASSERT(byteLength == 0 || text != NULL);
327
328    // nothing to draw
329    if (text == NULL || byteLength == 0 || draw.fRC->isEmpty()) {
330        return;
331    }
332
333    SkTextToPathIter    iter((const char*)text, byteLength, paint, true);
334    SkPathMeasure       meas(follow, false);
335    SkScalar            hOffset = 0;
336
337    // need to measure first
338    if (paint.getTextAlign() != SkPaint::kLeft_Align) {
339        SkScalar pathLen = meas.getLength();
340        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
341            pathLen = SkScalarHalf(pathLen);
342        }
343        hOffset += pathLen;
344    }
345
346    const SkPath*   iterPath;
347    SkScalar        xpos;
348    SkMatrix        scaledMatrix;
349    SkScalar        scale = iter.getPathScale();
350
351    scaledMatrix.setScale(scale, scale);
352
353    while (iter.next(&iterPath, &xpos)) {
354        if (iterPath) {
355            SkPath      tmp;
356            SkMatrix    m(scaledMatrix);
357
358            tmp.setIsVolatile(true);
359            m.postTranslate(xpos + hOffset, 0);
360            if (matrix) {
361                m.postConcat(*matrix);
362            }
363            morphpath(&tmp, *iterPath, meas, m);
364            this->drawPath(draw, tmp, iter.getPaint(), NULL, true);
365        }
366    }
367}
368
369//////////////////////////////////////////////////////////////////////////////////////////
370
371uint32_t SkBaseDevice::filterTextFlags(const SkPaint& paint) const {
372    uint32_t flags = paint.getFlags();
373
374    if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
375        return flags;
376    }
377
378    if (kUnknown_SkPixelGeometry == fLeakyProperties->pixelGeometry()
379        || this->onShouldDisableLCD(paint)) {
380
381        flags &= ~SkPaint::kLCDRenderText_Flag;
382        flags |= SkPaint::kGenA8FromLCD_Flag;
383    }
384
385    return flags;
386}
387
388