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