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