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