blob: d76a1808628418246f183b51e6bd59b2acf4948e [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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008#include "SkDevice.h"
reede010f1c2014-09-17 10:49:38 -07009#include "SkDeviceProperties.h"
dandovecfff212014-08-04 10:02:00 -070010#include "SkDraw.h"
fmalita024f9962015-03-03 19:08:17 -080011#include "SkDrawFilter.h"
reed@google.coma7d94852011-03-30 21:23:07 +000012#include "SkMetaData.h"
dandovecfff212014-08-04 10:02:00 -070013#include "SkPatchUtils.h"
reedf87fe782015-02-17 10:33:54 -080014#include "SkPathMeasure.h"
15#include "SkRasterClip.h"
fmalita84833262014-09-19 11:40:51 -070016#include "SkShader.h"
fmalitaaa1b9122014-08-28 14:32:24 -070017#include "SkTextBlob.h"
reedf87fe782015-02-17 10:33:54 -080018#include "SkTextToPathIter.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000019
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000020SkBaseDevice::SkBaseDevice()
reed4a8126e2014-09-22 07:29:03 -070021 : fLeakyProperties(SkNEW_ARGS(SkDeviceProperties, (SkDeviceProperties::kLegacyLCD_InitType)))
bungeman@google.com532470f2013-01-22 19:25:14 +000022#ifdef SK_DEBUG
23 , fAttachedToCanvas(false)
robertphillips@google.com40a1ae42012-07-13 15:36:15 +000024#endif
25{
reed@google.comaf951c92011-06-16 19:10:39 +000026 fOrigin.setZero();
27 fMetaData = NULL;
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000028}
29
reedb2db8982014-11-13 12:41:02 -080030SkBaseDevice::SkBaseDevice(const SkDeviceProperties& dp)
31 : fLeakyProperties(SkNEW_ARGS(SkDeviceProperties, (dp)))
32#ifdef SK_DEBUG
33 , fAttachedToCanvas(false)
34#endif
35{
36 fOrigin.setZero();
37 fMetaData = NULL;
38}
39
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000040SkBaseDevice::~SkBaseDevice() {
reede010f1c2014-09-17 10:49:38 -070041 SkDELETE(fLeakyProperties);
42 SkDELETE(fMetaData);
reed@google.coma7d94852011-03-30 21:23:07 +000043}
44
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000045SkMetaData& SkBaseDevice::getMetaData() {
reed@google.coma7d94852011-03-30 21:23:07 +000046 // metadata users are rare, so we lazily allocate it. If that changes we
47 // can decide to just make it a field in the device (rather than a ptr)
48 if (NULL == fMetaData) {
49 fMetaData = new SkMetaData;
50 }
51 return *fMetaData;
52}
53
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000054SkImageInfo SkBaseDevice::imageInfo() const {
reedf252f642014-06-14 04:24:56 -070055 return SkImageInfo::MakeUnknown();
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000056}
57
robertphillips@google.com1f2f3382013-08-29 11:54:56 +000058const SkBitmap& SkBaseDevice::accessBitmap(bool changePixels) {
59 const SkBitmap& bitmap = this->onAccessBitmap();
reed@android.com8a1c16f2008-12-17 15:59:43 +000060 if (changePixels) {
junov@chromium.org1f9767c2012-02-07 16:27:57 +000061 bitmap.notifyPixelsChanged();
reed@android.com8a1c16f2008-12-17 15:59:43 +000062 }
junov@chromium.org1f9767c2012-02-07 16:27:57 +000063 return bitmap;
reed@android.com8a1c16f2008-12-17 15:59:43 +000064}
65
reedb2db8982014-11-13 12:41:02 -080066SkPixelGeometry SkBaseDevice::CreateInfo::AdjustGeometry(const SkImageInfo& info,
reed1182d9a2015-03-13 07:16:09 -070067 TileUsage tileUsage,
reedb2db8982014-11-13 12:41:02 -080068 SkPixelGeometry geo) {
reed1182d9a2015-03-13 07:16:09 -070069 switch (tileUsage) {
70 case kPossible_TileUsage:
reedb2db8982014-11-13 12:41:02 -080071 break;
reed1182d9a2015-03-13 07:16:09 -070072 case kNever_TileUsage:
reedb2db8982014-11-13 12:41:02 -080073 if (info.alphaType() != kOpaque_SkAlphaType) {
74 geo = kUnknown_SkPixelGeometry;
75 }
76 break;
reedb2db8982014-11-13 12:41:02 -080077 }
78 return geo;
79}
80
81void SkBaseDevice::initForRootLayer(SkPixelGeometry geo) {
82 // For now we don't expect to change the geometry for the root-layer, but we make the call
83 // anyway to document logically what is going on.
84 //
85 fLeakyProperties->setPixelGeometry(CreateInfo::AdjustGeometry(this->imageInfo(),
reed1182d9a2015-03-13 07:16:09 -070086 kPossible_TileUsage,
reedb2db8982014-11-13 12:41:02 -080087 geo));
reed4a8126e2014-09-22 07:29:03 -070088}
89
90SkSurface* SkBaseDevice::newSurface(const SkImageInfo&, const SkSurfaceProps&) { return NULL; }
commit-bot@chromium.orgc3bd8af2014-02-13 17:14:46 +000091
92const void* SkBaseDevice::peekPixels(SkImageInfo*, size_t*) { return NULL; }
commit-bot@chromium.orged9806f2014-02-21 02:32:36 +000093
94void SkBaseDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
95 const SkRRect& inner, const SkPaint& paint) {
96 SkPath path;
97 path.addRRect(outer);
98 path.addRRect(inner);
99 path.setFillType(SkPath::kEvenOdd_FillType);
100
101 const SkMatrix* preMatrix = NULL;
102 const bool pathIsMutable = true;
103 this->drawPath(draw, path, paint, preMatrix, pathIsMutable);
104}
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000105
dandovb3c9d1c2014-08-12 08:34:29 -0700106void SkBaseDevice::drawPatch(const SkDraw& draw, const SkPoint cubics[12], const SkColor colors[4],
107 const SkPoint texCoords[4], SkXfermode* xmode, const SkPaint& paint) {
108 SkPatchUtils::VertexData data;
dandovecfff212014-08-04 10:02:00 -0700109
dandovb3c9d1c2014-08-12 08:34:29 -0700110 SkISize lod = SkPatchUtils::GetLevelOfDetail(cubics, draw.fMatrix);
dandovecfff212014-08-04 10:02:00 -0700111
112 // It automatically adjusts lodX and lodY in case it exceeds the number of indices.
dandov45f78422014-08-15 06:06:47 -0700113 // If it fails to generate the vertices, then we do not draw.
114 if (SkPatchUtils::getVertexData(&data, cubics, colors, texCoords, lod.width(), lod.height())) {
115 this->drawVertices(draw, SkCanvas::kTriangles_VertexMode, data.fVertexCount, data.fPoints,
116 data.fTexCoords, data.fColors, xmode, data.fIndices, data.fIndexCount,
117 paint);
118 }
dandovecfff212014-08-04 10:02:00 -0700119}
120
fmalitaaa1b9122014-08-28 14:32:24 -0700121void SkBaseDevice::drawTextBlob(const SkDraw& draw, const SkTextBlob* blob, SkScalar x, SkScalar y,
fmalita024f9962015-03-03 19:08:17 -0800122 const SkPaint &paint, SkDrawFilter* drawFilter) {
fmalitaaa1b9122014-08-28 14:32:24 -0700123
fmalita84833262014-09-19 11:40:51 -0700124 SkPaint runPaint = paint;
fmalitaaa1b9122014-08-28 14:32:24 -0700125
fmalitaaa1b9122014-08-28 14:32:24 -0700126 SkTextBlob::RunIterator it(blob);
fmalita024f9962015-03-03 19:08:17 -0800127 for (;!it.done(); it.next()) {
fmalitaaa1b9122014-08-28 14:32:24 -0700128 size_t textLen = it.glyphCount() * sizeof(uint16_t);
129 const SkPoint& offset = it.offset();
130 // applyFontToPaint() always overwrites the exact same attributes,
fmalita024f9962015-03-03 19:08:17 -0800131 // so it is safe to not re-seed the paint for this reason.
fmalitaaa1b9122014-08-28 14:32:24 -0700132 it.applyFontToPaint(&runPaint);
fmalita024f9962015-03-03 19:08:17 -0800133
134 if (drawFilter && !drawFilter->filter(&runPaint, SkDrawFilter::kText_Type)) {
135 // A false return from filter() means we should abort the current draw.
136 runPaint = paint;
137 continue;
138 }
139
fmalita112e7e22014-11-13 14:05:58 -0800140 runPaint.setFlags(this->filterTextFlags(runPaint));
fmalitaaa1b9122014-08-28 14:32:24 -0700141
142 switch (it.positioning()) {
143 case SkTextBlob::kDefault_Positioning:
fmalita05c4a432014-09-29 06:29:53 -0700144 this->drawText(draw, it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint);
fmalitaaa1b9122014-08-28 14:32:24 -0700145 break;
146 case SkTextBlob::kHorizontal_Positioning:
fmalita05c4a432014-09-29 06:29:53 -0700147 this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 1,
148 SkPoint::Make(x, y + offset.y()), runPaint);
149 break;
fmalitaaa1b9122014-08-28 14:32:24 -0700150 case SkTextBlob::kFull_Positioning:
fmalita05c4a432014-09-29 06:29:53 -0700151 this->drawPosText(draw, it.glyphs(), textLen, it.pos(), 2,
152 SkPoint::Make(x, y), runPaint);
fmalitaaa1b9122014-08-28 14:32:24 -0700153 break;
154 default:
155 SkFAIL("unhandled positioning mode");
156 }
157
fmalita024f9962015-03-03 19:08:17 -0800158 if (drawFilter) {
159 // A draw filter may change the paint arbitrarily, so we must re-seed in this case.
160 runPaint = paint;
161 }
fmalitaaa1b9122014-08-28 14:32:24 -0700162 }
163}
164
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000165bool SkBaseDevice::readPixels(const SkImageInfo& info, void* dstP, size_t rowBytes, int x, int y) {
166#ifdef SK_DEBUG
167 SkASSERT(info.width() > 0 && info.height() > 0);
168 SkASSERT(dstP);
169 SkASSERT(rowBytes >= info.minRowBytes());
170 SkASSERT(x >= 0 && y >= 0);
skia.committer@gmail.comdb0c8752014-03-18 03:02:11 +0000171
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000172 const SkImageInfo& srcInfo = this->imageInfo();
173 SkASSERT(x + info.width() <= srcInfo.width());
174 SkASSERT(y + info.height() <= srcInfo.height());
175#endif
176 return this->onReadPixels(info, dstP, rowBytes, x, y);
177}
178
commit-bot@chromium.org4ef54f82014-03-17 17:03:18 +0000179bool SkBaseDevice::writePixels(const SkImageInfo& info, const void* pixels, size_t rowBytes,
180 int x, int y) {
commit-bot@chromium.org4cd9e212014-03-07 03:25:16 +0000181#ifdef SK_DEBUG
182 SkASSERT(info.width() > 0 && info.height() > 0);
183 SkASSERT(pixels);
184 SkASSERT(rowBytes >= info.minRowBytes());
185 SkASSERT(x >= 0 && y >= 0);
186
187 const SkImageInfo& dstInfo = this->imageInfo();
188 SkASSERT(x + info.width() <= dstInfo.width());
189 SkASSERT(y + info.height() <= dstInfo.height());
190#endif
191 return this->onWritePixels(info, pixels, rowBytes, x, y);
192}
193
194bool SkBaseDevice::onWritePixels(const SkImageInfo&, const void*, size_t, int, int) {
195 return false;
196}
197
commit-bot@chromium.orga713f9c2014-03-17 21:31:26 +0000198bool SkBaseDevice::onReadPixels(const SkImageInfo&, void*, size_t, int x, int y) {
commit-bot@chromium.org2cccf832014-03-12 03:04:08 +0000199 return false;
200}
201
reed@google.com9c135db2014-03-12 18:28:35 +0000202void* SkBaseDevice::accessPixels(SkImageInfo* info, size_t* rowBytes) {
203 SkImageInfo tmpInfo;
204 size_t tmpRowBytes;
205 if (NULL == info) {
206 info = &tmpInfo;
207 }
208 if (NULL == rowBytes) {
209 rowBytes = &tmpRowBytes;
210 }
211 return this->onAccessPixels(info, rowBytes);
212}
213
214void* SkBaseDevice::onAccessPixels(SkImageInfo* info, size_t* rowBytes) {
215 return NULL;
216}
217
reedd5fa1a42014-08-09 11:08:05 -0700218bool SkBaseDevice::EXPERIMENTAL_drawPicture(SkCanvas*, const SkPicture*, const SkMatrix*,
219 const SkPaint*) {
commit-bot@chromium.org145d1c02014-03-16 19:46:36 +0000220 // The base class doesn't perform any accelerated picture rendering
221 return false;
222}
reedb2db8982014-11-13 12:41:02 -0800223
224//////////////////////////////////////////////////////////////////////////////////////////
225
reedf87fe782015-02-17 10:33:54 -0800226static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
227 SkPathMeasure& meas, const SkMatrix& matrix) {
228 SkMatrix::MapXYProc proc = matrix.getMapXYProc();
229
230 for (int i = 0; i < count; i++) {
231 SkPoint pos;
232 SkVector tangent;
233
234 proc(matrix, src[i].fX, src[i].fY, &pos);
235 SkScalar sx = pos.fX;
236 SkScalar sy = pos.fY;
237
238 if (!meas.getPosTan(sx, &pos, &tangent)) {
239 // set to 0 if the measure failed, so that we just set dst == pos
240 tangent.set(0, 0);
241 }
242
243 /* This is the old way (that explains our approach but is way too slow
244 SkMatrix matrix;
245 SkPoint pt;
246
247 pt.set(sx, sy);
248 matrix.setSinCos(tangent.fY, tangent.fX);
249 matrix.preTranslate(-sx, 0);
250 matrix.postTranslate(pos.fX, pos.fY);
251 matrix.mapPoints(&dst[i], &pt, 1);
252 */
253 dst[i].set(pos.fX - SkScalarMul(tangent.fY, sy),
254 pos.fY + SkScalarMul(tangent.fX, sy));
255 }
256}
257
258/* TODO
259
260 Need differentially more subdivisions when the follow-path is curvy. Not sure how to
261 determine that, but we need it. I guess a cheap answer is let the caller tell us,
262 but that seems like a cop-out. Another answer is to get Rob Johnson to figure it out.
263 */
264static void morphpath(SkPath* dst, const SkPath& src, SkPathMeasure& meas,
265 const SkMatrix& matrix) {
266 SkPath::Iter iter(src, false);
267 SkPoint srcP[4], dstP[3];
268 SkPath::Verb verb;
269
270 while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
271 switch (verb) {
272 case SkPath::kMove_Verb:
273 morphpoints(dstP, srcP, 1, meas, matrix);
274 dst->moveTo(dstP[0]);
275 break;
276 case SkPath::kLine_Verb:
277 // turn lines into quads to look bendy
278 srcP[0].fX = SkScalarAve(srcP[0].fX, srcP[1].fX);
279 srcP[0].fY = SkScalarAve(srcP[0].fY, srcP[1].fY);
280 morphpoints(dstP, srcP, 2, meas, matrix);
281 dst->quadTo(dstP[0], dstP[1]);
282 break;
283 case SkPath::kQuad_Verb:
284 morphpoints(dstP, &srcP[1], 2, meas, matrix);
285 dst->quadTo(dstP[0], dstP[1]);
286 break;
287 case SkPath::kCubic_Verb:
288 morphpoints(dstP, &srcP[1], 3, meas, matrix);
289 dst->cubicTo(dstP[0], dstP[1], dstP[2]);
290 break;
291 case SkPath::kClose_Verb:
292 dst->close();
293 break;
294 default:
295 SkDEBUGFAIL("unknown verb");
296 break;
297 }
298 }
299}
300
301void SkBaseDevice::drawTextOnPath(const SkDraw& draw, const void* text, size_t byteLength,
302 const SkPath& follow, const SkMatrix* matrix,
303 const SkPaint& paint) {
304 SkASSERT(byteLength == 0 || text != NULL);
305
306 // nothing to draw
307 if (text == NULL || byteLength == 0 || draw.fRC->isEmpty()) {
308 return;
309 }
310
311 SkTextToPathIter iter((const char*)text, byteLength, paint, true);
312 SkPathMeasure meas(follow, false);
313 SkScalar hOffset = 0;
314
315 // need to measure first
316 if (paint.getTextAlign() != SkPaint::kLeft_Align) {
317 SkScalar pathLen = meas.getLength();
318 if (paint.getTextAlign() == SkPaint::kCenter_Align) {
319 pathLen = SkScalarHalf(pathLen);
320 }
321 hOffset += pathLen;
322 }
323
324 const SkPath* iterPath;
325 SkScalar xpos;
326 SkMatrix scaledMatrix;
327 SkScalar scale = iter.getPathScale();
328
329 scaledMatrix.setScale(scale, scale);
330
331 while (iter.next(&iterPath, &xpos)) {
332 if (iterPath) {
333 SkPath tmp;
334 SkMatrix m(scaledMatrix);
335
336 tmp.setIsVolatile(true);
337 m.postTranslate(xpos + hOffset, 0);
338 if (matrix) {
339 m.postConcat(*matrix);
340 }
341 morphpath(&tmp, *iterPath, meas, m);
342 this->drawPath(draw, tmp, iter.getPaint(), NULL, true);
343 }
344 }
345}
346
347//////////////////////////////////////////////////////////////////////////////////////////
348
fmalita112e7e22014-11-13 14:05:58 -0800349uint32_t SkBaseDevice::filterTextFlags(const SkPaint& paint) const {
350 uint32_t flags = paint.getFlags();
351
reedb2db8982014-11-13 12:41:02 -0800352 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) {
fmalita112e7e22014-11-13 14:05:58 -0800353 return flags;
reedb2db8982014-11-13 12:41:02 -0800354 }
355
fmalita112e7e22014-11-13 14:05:58 -0800356 if (kUnknown_SkPixelGeometry == fLeakyProperties->pixelGeometry()
357 || this->onShouldDisableLCD(paint)) {
358
359 flags &= ~SkPaint::kLCDRenderText_Flag;
360 flags |= SkPaint::kGenA8FromLCD_Flag;
reedb2db8982014-11-13 12:41:02 -0800361 }
362
fmalita112e7e22014-11-13 14:05:58 -0800363 return flags;
reedb2db8982014-11-13 12:41:02 -0800364}
365