blob: 90161b4eb386d3db240b1cc67e19f7fc572ba5b8 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
robertphillips@google.com53238bc2013-08-30 13:12:10 +00007
reed71c3c762015-06-24 10:29:17 -07008#include "SkColorFilter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +00009#include "SkDevice.h"
dandovecfff212014-08-04 10:02:00 -070010#include "SkDraw.h"
fmalita024f9962015-03-03 19:08:17 -080011#include "SkDrawFilter.h"
reeda85d4d02015-05-06 12:56:48 -070012#include "SkImage_Base.h"
reed@google.coma7d94852011-03-30 21:23:07 +000013#include "SkMetaData.h"
reed4c21dc52015-06-25 12:32:03 -070014#include "SkNinePatchIter.h"
dandovecfff212014-08-04 10:02:00 -070015#include "SkPatchUtils.h"
reedf87fe782015-02-17 10:33:54 -080016#include "SkPathMeasure.h"
17#include "SkRasterClip.h"
reed71c3c762015-06-24 10:29:17 -070018#include "SkRSXform.h"
fmalita84833262014-09-19 11:40:51 -070019#include "SkShader.h"
fmalitaaa1b9122014-08-28 14:32:24 -070020#include "SkTextBlob.h"
reedf87fe782015-02-17 10:33:54 -080021#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000022
robertphillipsfcf78292015-06-19 11:49:52 -070023SkBaseDevice::SkBaseDevice(const SkSurfaceProps& surfaceProps)
robertphillips7b05ff12015-06-19 14:14:54 -070024 : fSurfaceProps(surfaceProps)
reedb2db8982014-11-13 12:41:02 -080025#ifdef SK_DEBUG
26 , fAttachedToCanvas(false)
27#endif
28{
29 fOrigin.setZero();
30 fMetaData = NULL;
31}
32
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000033SkBaseDevice::~SkBaseDevice() {
reede010f1c2014-09-17 10:49:38 -070034 SkDELETE(fMetaData);
reed@google.coma7d94852011-03-30 21:23:07 +000035}
36
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000037SkMetaData& SkBaseDevice::getMetaData() {
reed@google.coma7d94852011-03-30 21:23:07 +000038 // metadata users are rare, so we lazily allocate it. If that changes we
39 // can decide to just make it a field in the device (rather than a ptr)
40 if (NULL == fMetaData) {
41 fMetaData = new SkMetaData;
42 }
43 return *fMetaData;
44}
45
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000046SkImageInfo SkBaseDevice::imageInfo() const {
reedf252f642014-06-14 04:24:56 -070047 return SkImageInfo::MakeUnknown();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000048}
49
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000050const SkBitmap& SkBaseDevice::accessBitmap(bool changePixels) {
51 const SkBitmap& bitmap = this->onAccessBitmap();
reed@android.com8a1c16f2008-12-17 15:59:43 +000052 if (changePixels) {
junov@chromium.org1f9767c2012-02-07 16:27:57 +000053 bitmap.notifyPixelsChanged();
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 }
junov@chromium.org1f9767c2012-02-07 16:27:57 +000055 return bitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +000056}
57
reedb2db8982014-11-13 12:41:02 -080058SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info,
reed76033be2015-03-14 10:54:31 -070059 TileUsage tileUsage,
reedb2db8982014-11-13 12:41:02 -080060 SkPixelGeometry geo) {
reed76033be2015-03-14 10:54:31 -070061 switch (tileUsage) {
62 case kPossible_TileUsage:
reed8dc0ccb2015-03-20 06:32:52 -070063 // (we think) for compatibility with old clients, we assume this layer can support LCD
64 // even though they may not have marked it as opaque... seems like we should update
65 // our callers (reed/robertphilips).
reedb2db8982014-11-13 12:41:02 -080066 break;
reed76033be2015-03-14 10:54:31 -070067 case kNever_TileUsage:
reedb2db8982014-11-13 12:41:02 -080068 if (info.alphaType() != kOpaque_SkAlphaType) {
69 geo = kUnknown_SkPixelGeometry;
70 }
71 break;
reedb2db8982014-11-13 12:41:02 -080072 }
73 return geo;
74}
75
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +000076void SkBaseDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
77 const SkRRect& inner, const SkPaint& paint) {
78 SkPath path;
79 path.addRRect(outer);
80 path.addRRect(inner);
81 path.setFillType(SkPath::kEvenOdd_FillType);
82
83 const SkMatrix* preMatrix = NULL;
84 const bool pathIsMutable = true;
85 this->drawPath(draw, path, paint, preMatrix, pathIsMutable);
86}
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +000087
dandovb3c9d1c2014-08-12 08:34:29 -070088void SkBaseDevice::drawPatch(const SkDraw& draw, const SkPoint cubics[12], const SkColor colors[4],
89 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
90 SkPatchUtils::VertexData data;
dandovecfff212014-08-04 10:02:00 -070091
dandovb3c9d1c2014-08-12 08:34:29 -070092 SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, draw.fMatrix);
dandovecfff212014-08-04 10:02:00 -070093
94 // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
dandov45f78422014-08-15 06:06:47 -070095 // If it fails to generate the vertices, then we do not draw.
96 if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
97 this->drawVertices(draw, SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
98 data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
99 paint);
100 }
dandovecfff212014-08-04 10:02:00 -0700101}
102
fmalitaaa1b9122014-08-28 14:32:24 -0700103void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y,
fmalita024f9962015-03-03 19:08:17 -0800104 const SkPaint &paint, SkDrawFilter* drawFilter) {
fmalitaaa1b9122014-08-28 14:32:24 -0700105
fmalita84833262014-09-19 11:40:51 -0700106 SkPaint runPaint = paint;
fmalitaaa1b9122014-08-28 14:32:24 -0700107
fmalitaaa1b9122014-08-28 14:32:24 -0700108 SkTextBlob::RunIterator it(blob);
fmalita024f9962015-03-03 19:08:17 -0800109 for (;!it.done(); it.next()) {
fmalitaaa1b9122014-08-28 14:32:24 -0700110 size_t textLen = it.glyphCount() * sizeof(uint16_t);
111 const SkPoint& offset = it.offset();
112 // applyFontToPaint() always overwrites the exact same attributes,
fmalita024f9962015-03-03 19:08:17 -0800113 // so it is safe to not re-seed the paint for this reason.
fmalitaaa1b9122014-08-28 14:32:24 -0700114 it.applyFontToPaint(&runPaint);
fmalita024f9962015-03-03 19:08:17 -0800115
116 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
117 // A false return from filter() means we should abort the current draw.
118 runPaint = paint;
119 continue;
120 }
121
fmalita112e7e22014-11-13 14:05:58 -0800122 runPaint.setFlags(this->filterTextFlags(runPaint));
fmalitaaa1b9122014-08-28 14:32:24 -0700123
124 switch (it.positioning()) {
125 case SkTextBlob::kDefault_Positioning:
fmalita05c4a432014-09-29 06:29:53 -0700126 this->drawText(draw, it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
fmalitaaa1b9122014-08-28 14:32:24 -0700127 break;
128 case SkTextBlob::kHorizontal_Positioning:
fmalita05c4a432014-09-29 06:29:53 -0700129 this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 1,
130 SkPoint::Make(x, y + offset.y()), runPaint);
131 break;
fmalitaaa1b9122014-08-28 14:32:24 -0700132 case SkTextBlob::kFull_Positioning:
fmalita05c4a432014-09-29 06:29:53 -0700133 this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 2,
134 SkPoint::Make(x, y), runPaint);
fmalitaaa1b9122014-08-28 14:32:24 -0700135 break;
136 default:
137 SkFAIL("unhandled positioning mode");
138 }
139
fmalita024f9962015-03-03 19:08:17 -0800140 if (drawFilter) {
141 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
142 runPaint = paint;
143 }
fmalitaaa1b9122014-08-28 14:32:24 -0700144 }
145}
146
reeda85d4d02015-05-06 12:56:48 -0700147void SkBaseDevice::drawImage(const SkDraw& draw, const SkImage* image, SkScalar x, SkScalar y,
148 const SkPaint& paint) {
149 // Default impl : turns everything into raster bitmap
150 SkBitmap bm;
151 if (as_IB(image)->getROPixels(&bm)) {
152 this->drawBitmap(draw, bm, SkMatrix::MakeTrans(x, y), paint);
153 }
154}
155
156void SkBaseDevice::drawImageRect(const SkDraw& draw, const SkImage* image, const SkRect* src,
157 const SkRect& dst, const SkPaint& paint) {
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, SkCanvas::kNone_DrawBitmapRectFlag);
162 }
163}
164
reed4c21dc52015-06-25 12:32:03 -0700165void 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);
172 }
173}
174
175void 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::kNone_DrawBitmapRectFlag);
182 }
183}
184
reed71c3c762015-06-24 10:29:17 -0700185void 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 pnt.setShader(atlas->newShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode,
201 &localM))->unref();
202 if (colors && colors[i] != SK_ColorWHITE) {
203 SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(colors[i], mode));
204 pnt.setColorFilter(cf);
205 }
206
207 path.rewind();
208 path.addPoly(quad, 4, true);
209 path.setConvexity(SkPath::kConvex_Convexity);
210 this->drawPath(draw, path, pnt, NULL, true);
211 }
212}
213
214///////////////////////////////////////////////////////////////////////////////////////////////////
215
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000216bool SkBaseDevice::readPixels(const SkImageInfo& info, void* dstP, size_t rowBytes, int x, int y) {
217#ifdef SK_DEBUG
218 SkASSERT(info.width() > 0 && info.height() > 0);
219 SkASSERT(dstP);
220 SkASSERT(rowBytes >= info.minRowBytes());
221 SkASSERT(x >= 0 && y >= 0);
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000222
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000223 const SkImageInfo& srcInfo = this->imageInfo();
224 SkASSERT(x + info.width() <= srcInfo.width());
225 SkASSERT(y + info.height() <= srcInfo.height());
226#endif
227 return this->onReadPixels(info, dstP, rowBytes, x, y);
228}
229
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000230bool SkBaseDevice::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
231 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000232#ifdef SK_DEBUG
233 SkASSERT(info.width() > 0 && info.height() > 0);
234 SkASSERT(pixels);
235 SkASSERT(rowBytes >= info.minRowBytes());
236 SkASSERT(x >= 0 && y >= 0);
237
238 const SkImageInfo& dstInfo = this->imageInfo();
239 SkASSERT(x + info.width() <= dstInfo.width());
240 SkASSERT(y + info.height() <= dstInfo.height());
241#endif
242 return this->onWritePixels(info, pixels, rowBytes, x, y);
243}
244
245bool SkBaseDevice::onWritePixels(const SkImageInfo&, const void*, size_t, int, int) {
246 return false;
247}
248
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000249bool SkBaseDevice::onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) {
commit-bot@chromium.org2cccf832014-03-12 03:04:08 +0000250 return false;
251}
252
reedd5fa1a42014-08-09 11:08:05 -0700253bool SkBaseDevice::EXPERIMENTAL_drawPicture(SkCanvas*, const SkPicture*, const SkMatrix*,
254 const SkPaint*) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000255 // The base class doesn't perform any accelerated picture rendering
256 return false;
257}
reedb2db8982014-11-13 12:41:02 -0800258
reed884e97c2015-05-26 11:31:54 -0700259bool SkBaseDevice::accessPixels(SkPixmap* pmap) {
260 SkPixmap tempStorage;
261 if (NULL == pmap) {
262 pmap = &tempStorage;
263 }
264 return this->onAccessPixels(pmap);
265}
266
267bool SkBaseDevice::peekPixels(SkPixmap* pmap) {
268 SkPixmap tempStorage;
269 if (NULL == pmap) {
270 pmap = &tempStorage;
271 }
272 return this->onPeekPixels(pmap);
273}
274
reedb2db8982014-11-13 12:41:02 -0800275//////////////////////////////////////////////////////////////////////////////////////////
276
reedf87fe782015-02-17 10:33:54 -0800277static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
278 SkPathMeasure& meas, const SkMatrix& matrix) {
279 SkMatrix::MapXYProc proc = matrix.getMapXYProc();
280
281 for (int i = 0; i < count; i++) {
282 SkPoint pos;
283 SkVector tangent;
284
285 proc(matrix, src[i].fX, src[i].fY, &pos);
286 SkScalar sx = pos.fX;
287 SkScalar sy = pos.fY;
288
289 if (!meas.getPosTan(sx, &pos, &tangent)) {
290 // set to 0 if the measure failed, so that we just set dst == pos
291 tangent.set(0, 0);
292 }
293
294 /* This is the old way (that explains our approach but is way too slow
295 SkMatrix matrix;
296 SkPoint pt;
297
298 pt.set(sx, sy);
299 matrix.setSinCos(tangent.fY, tangent.fX);
300 matrix.preTranslate(-sx, 0);
301 matrix.postTranslate(pos.fX, pos.fY);
302 matrix.mapPoints(&dst[i], &pt, 1);
303 */
304 dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
305 pos.fY + SkScalarMul(tangent.fX, sy));
306 }
307}
308
309/* TODO
310
311 Need differentially more subdivisions when the follow-path is curvy. Not sure how to
312 determine that, but we need it. I guess a cheap answer is let the caller tell us,
313 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
314 */
315static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
316 const SkMatrix& matrix) {
317 SkPath::Iter iter(src, false);
318 SkPoint srcP[4], dstP[3];
319 SkPath::Verb verb;
320
321 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
322 switch (verb) {
323 case SkPath::kMove_Verb:
324 morphpoints(dstP, srcP, 1, meas, matrix);
325 dst->moveTo(dstP[0]);
326 break;
327 case SkPath::kLine_Verb:
328 // turn lines into quads to look bendy
329 srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
330 srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
331 morphpoints(dstP, srcP, 2, meas, matrix);
332 dst->quadTo(dstP[0], dstP[1]);
333 break;
334 case SkPath::kQuad_Verb:
335 morphpoints(dstP, &srcP[1], 2, meas, matrix);
336 dst->quadTo(dstP[0], dstP[1]);
337 break;
338 case SkPath::kCubic_Verb:
339 morphpoints(dstP, &srcP[1], 3, meas, matrix);
340 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
341 break;
342 case SkPath::kClose_Verb:
343 dst->close();
344 break;
345 default:
346 SkDEBUGFAIL("unknown verb");
347 break;
348 }
349 }
350}
351
352void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t byteLength,
353 const SkPath& follow, const SkMatrix* matrix,
354 const SkPaint& paint) {
355 SkASSERT(byteLength == 0 || text != NULL);
356
357 // nothing to draw
358 if (text == NULL || byteLength == 0 || draw.fRC->isEmpty()) {
359 return;
360 }
361
362 SkTextToPathIter iter((const char*)text, byteLength, paint, true);
363 SkPathMeasure meas(follow, false);
364 SkScalar hOffset = 0;
365
366 // need to measure first
367 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
368 SkScalar pathLen = meas.getLength();
369 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
370 pathLen = SkScalarHalf(pathLen);
371 }
372 hOffset += pathLen;
373 }
374
375 const SkPath* iterPath;
376 SkScalar xpos;
377 SkMatrix scaledMatrix;
378 SkScalar scale = iter.getPathScale();
379
380 scaledMatrix.setScale(scale, scale);
381
382 while (iter.next(&iterPath, &xpos)) {
383 if (iterPath) {
384 SkPath tmp;
385 SkMatrix m(scaledMatrix);
386
387 tmp.setIsVolatile(true);
388 m.postTranslate(xpos + hOffset, 0);
389 if (matrix) {
390 m.postConcat(*matrix);
391 }
392 morphpath(&tmp, *iterPath, meas, m);
393 this->drawPath(draw, tmp, iter.getPaint(), NULL, true);
394 }
395 }
396}
397
398//////////////////////////////////////////////////////////////////////////////////////////
399
fmalita112e7e22014-11-13 14:05:58 -0800400uint32_t SkBaseDevice::filterTextFlags(const SkPaint& paint) const {
401 uint32_t flags = paint.getFlags();
402
reedb2db8982014-11-13 12:41:02 -0800403 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
fmalita112e7e22014-11-13 14:05:58 -0800404 return flags;
reedb2db8982014-11-13 12:41:02 -0800405 }
406
robertphillips7b05ff12015-06-19 14:14:54 -0700407 if (kUnknown_SkPixelGeometry == fSurfaceProps.pixelGeometry()
fmalita112e7e22014-11-13 14:05:58 -0800408 || this->onShouldDisableLCD(paint)) {
409
410 flags &= ~SkPaint::kLCDRenderText_Flag;
411 flags |= SkPaint::kGenA8FromLCD_Flag;
reedb2db8982014-11-13 12:41:02 -0800412 }
413
fmalita112e7e22014-11-13 14:05:58 -0800414 return flags;
reedb2db8982014-11-13 12:41:02 -0800415}
416