RecordingCanvas.cpp revision 6fe991e5e76f9af9dab960100d5768d96d5f4daa
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "RecordingCanvas.h" 18 19#include "RecordedOp.h" 20#include "RenderNode.h" 21 22namespace android { 23namespace uirenderer { 24 25RecordingCanvas::RecordingCanvas(size_t width, size_t height) 26 : mState(*this) 27 , mResourceCache(ResourceCache::getInstance()) { 28 reset(width, height); 29} 30 31RecordingCanvas::~RecordingCanvas() { 32 LOG_ALWAYS_FATAL_IF(mDisplayList, 33 "Destroyed a RecordingCanvas during a record!"); 34} 35 36void RecordingCanvas::reset(int width, int height) { 37 LOG_ALWAYS_FATAL_IF(mDisplayList, 38 "prepareDirty called a second time during a recording!"); 39 mDisplayList = new DisplayList(); 40 41 mState.initializeSaveStack(width, height, 0, 0, width, height, Vector3()); 42 43 mDeferredBarrierType = kBarrier_InOrder; 44 mState.setDirtyClip(false); 45 mRestoreSaveCount = -1; 46} 47 48DisplayList* RecordingCanvas::finishRecording() { 49 mPaintMap.clear(); 50 mRegionMap.clear(); 51 mPathMap.clear(); 52 DisplayList* displayList = mDisplayList; 53 mDisplayList = nullptr; 54 mSkiaCanvasProxy.reset(nullptr); 55 return displayList; 56} 57 58SkCanvas* RecordingCanvas::asSkCanvas() { 59 LOG_ALWAYS_FATAL_IF(!mDisplayList, 60 "attempting to get an SkCanvas when we are not recording!"); 61 if (!mSkiaCanvasProxy) { 62 mSkiaCanvasProxy.reset(new SkiaCanvasProxy(this)); 63 } 64 65 // SkCanvas instances default to identity transform, but should inherit 66 // the state of this Canvas; if this code was in the SkiaCanvasProxy 67 // constructor, we couldn't cache mSkiaCanvasProxy. 68 SkMatrix parentTransform; 69 getMatrix(&parentTransform); 70 mSkiaCanvasProxy.get()->setMatrix(parentTransform); 71 72 return mSkiaCanvasProxy.get(); 73} 74 75// ---------------------------------------------------------------------------- 76// CanvasStateClient implementation 77// ---------------------------------------------------------------------------- 78 79void RecordingCanvas::onViewportInitialized() { 80 81} 82 83void RecordingCanvas::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { 84 if (removed.flags & Snapshot::kFlagIsFboLayer) { 85 addOp(new (alloc()) EndLayerOp()); 86 } 87} 88 89// ---------------------------------------------------------------------------- 90// android/graphics/Canvas state operations 91// ---------------------------------------------------------------------------- 92// Save (layer) 93int RecordingCanvas::save(SkCanvas::SaveFlags flags) { 94 return mState.save((int) flags); 95} 96 97void RecordingCanvas::RecordingCanvas::restore() { 98 if (mRestoreSaveCount < 0) { 99 restoreToCount(getSaveCount() - 1); 100 return; 101 } 102 103 mRestoreSaveCount--; 104 mState.restore(); 105} 106 107void RecordingCanvas::restoreToCount(int saveCount) { 108 mRestoreSaveCount = saveCount; 109 mState.restoreToCount(saveCount); 110} 111 112int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, 113 SkCanvas::SaveFlags flags) { 114 if (!(flags & SkCanvas::kClipToLayer_SaveFlag)) { 115 LOG_ALWAYS_FATAL("unclipped layers not supported"); 116 } 117 // force matrix/clip isolation for layer 118 flags |= SkCanvas::kClip_SaveFlag | SkCanvas::kMatrix_SaveFlag; 119 120 121 const Snapshot& previous = *mState.currentSnapshot(); 122 123 // initialize the snapshot as though it almost represents an FBO layer so deferred draw 124 // operations will be able to store and restore the current clip and transform info, and 125 // quick rejection will be correct (for display lists) 126 127 const Rect untransformedBounds(left, top, right, bottom); 128 129 // determine clipped bounds relative to previous viewport. 130 Rect visibleBounds = untransformedBounds; 131 previous.transform->mapRect(visibleBounds); 132 133 134 visibleBounds.doIntersect(previous.getRenderTargetClip()); 135 visibleBounds.snapToPixelBoundaries(); 136 137 Rect previousViewport(0, 0, previous.getViewportWidth(), previous.getViewportHeight()); 138 visibleBounds.doIntersect(previousViewport); 139 140 // Map visible bounds back to layer space, and intersect with parameter bounds 141 Rect layerBounds = visibleBounds; 142 Matrix4 inverse; 143 inverse.loadInverse(*previous.transform); 144 inverse.mapRect(layerBounds); 145 layerBounds.doIntersect(untransformedBounds); 146 147 int saveValue = mState.save((int) flags); 148 Snapshot& snapshot = *mState.writableSnapshot(); 149 150 // layerBounds is now original bounds, but with clipped to clip 151 // and viewport to ensure it's minimal size. 152 if (layerBounds.isEmpty() || untransformedBounds.isEmpty()) { 153 // Don't bother recording layer, since it's been rejected 154 snapshot.resetClip(0, 0, 0, 0); 155 return saveValue; 156 } 157 158 snapshot.flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer; 159 snapshot.initializeViewport(untransformedBounds.getWidth(), untransformedBounds.getHeight()); 160 snapshot.resetTransform(-untransformedBounds.left, -untransformedBounds.top, 0.0f); 161 162 Rect clip = layerBounds; 163 clip.translate(-untransformedBounds.left, -untransformedBounds.top); 164 snapshot.resetClip(clip.left, clip.top, clip.right, clip.bottom); 165 snapshot.roundRectClipState = nullptr; 166 167 addOp(new (alloc()) BeginLayerOp( 168 Rect(left, top, right, bottom), 169 *previous.transform, // transform to *draw* with 170 previous.getRenderTargetClip(), // clip to *draw* with 171 refPaint(paint))); 172 173 return saveValue; 174} 175 176// Matrix 177void RecordingCanvas::rotate(float degrees) { 178 if (degrees == 0) return; 179 180 mState.rotate(degrees); 181} 182 183void RecordingCanvas::scale(float sx, float sy) { 184 if (sx == 1 && sy == 1) return; 185 186 mState.scale(sx, sy); 187} 188 189void RecordingCanvas::skew(float sx, float sy) { 190 mState.skew(sx, sy); 191} 192 193void RecordingCanvas::translate(float dx, float dy) { 194 if (dx == 0 && dy == 0) return; 195 196 mState.translate(dx, dy, 0); 197} 198 199// Clip 200bool RecordingCanvas::getClipBounds(SkRect* outRect) const { 201 Rect bounds = mState.getLocalClipBounds(); 202 *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom); 203 return !(outRect->isEmpty()); 204} 205bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const { 206 return mState.quickRejectConservative(left, top, right, bottom); 207} 208bool RecordingCanvas::quickRejectPath(const SkPath& path) const { 209 SkRect bounds = path.getBounds(); 210 return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); 211} 212bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { 213 return mState.clipRect(left, top, right, bottom, op); 214} 215bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) { 216 return mState.clipPath(path, op); 217} 218bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { 219 return mState.clipRegion(region, op); 220} 221 222// ---------------------------------------------------------------------------- 223// android/graphics/Canvas draw operations 224// ---------------------------------------------------------------------------- 225void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) { 226 SkPaint paint; 227 paint.setColor(color); 228 paint.setXfermodeMode(mode); 229 drawPaint(paint); 230} 231 232void RecordingCanvas::drawPaint(const SkPaint& paint) { 233 // TODO: more efficient recording? 234 Matrix4 identity; 235 identity.loadIdentity(); 236 237 addOp(new (alloc()) RectOp( 238 mState.getRenderTargetClipBounds(), 239 identity, 240 mState.getRenderTargetClipBounds(), 241 refPaint(&paint))); 242} 243 244// Geometry 245void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { 246 LOG_ALWAYS_FATAL("TODO!"); 247} 248void RecordingCanvas::drawLines(const float* points, int count, const SkPaint& paint) { 249 LOG_ALWAYS_FATAL("TODO!"); 250} 251void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { 252 addOp(new (alloc()) RectOp( 253 Rect(left, top, right, bottom), 254 *(mState.currentSnapshot()->transform), 255 mState.getRenderTargetClipBounds(), 256 refPaint(&paint))); 257} 258 259void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) { 260 if (rects == nullptr) return; 261 262 Vertex* rectData = (Vertex*) mDisplayList->allocator.alloc(vertexCount * sizeof(Vertex)); 263 Vertex* vertex = rectData; 264 265 float left = FLT_MAX; 266 float top = FLT_MAX; 267 float right = FLT_MIN; 268 float bottom = FLT_MIN; 269 for (int index = 0; index < vertexCount; index += 4) { 270 float l = rects[index + 0]; 271 float t = rects[index + 1]; 272 float r = rects[index + 2]; 273 float b = rects[index + 3]; 274 275 Vertex::set(vertex++, l, t); 276 Vertex::set(vertex++, r, t); 277 Vertex::set(vertex++, l, b); 278 Vertex::set(vertex++, r, b); 279 280 left = std::min(left, l); 281 top = std::min(top, t); 282 right = std::max(right, r); 283 bottom = std::max(bottom, b); 284 } 285 addOp(new (alloc()) SimpleRectsOp( 286 Rect(left, top, right, bottom), 287 *(mState.currentSnapshot()->transform), 288 mState.getRenderTargetClipBounds(), 289 refPaint(paint), rectData, vertexCount)); 290} 291 292void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { 293 if (paint.getStyle() == SkPaint::kFill_Style 294 && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) { 295 int count = 0; 296 Vector<float> rects; 297 SkRegion::Iterator it(region); 298 while (!it.done()) { 299 const SkIRect& r = it.rect(); 300 rects.push(r.fLeft); 301 rects.push(r.fTop); 302 rects.push(r.fRight); 303 rects.push(r.fBottom); 304 count += 4; 305 it.next(); 306 } 307 drawSimpleRects(rects.array(), count, &paint); 308 } else { 309 SkRegion::Iterator it(region); 310 while (!it.done()) { 311 const SkIRect& r = it.rect(); 312 drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); 313 it.next(); 314 } 315 } 316} 317void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, 318 float rx, float ry, const SkPaint& paint) { 319 LOG_ALWAYS_FATAL("TODO!"); 320} 321void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { 322 LOG_ALWAYS_FATAL("TODO!"); 323} 324void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { 325 LOG_ALWAYS_FATAL("TODO!"); 326} 327void RecordingCanvas::drawArc(float left, float top, float right, float bottom, 328 float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { 329 LOG_ALWAYS_FATAL("TODO!"); 330} 331void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 332 LOG_ALWAYS_FATAL("TODO!"); 333} 334 335// Bitmap-based 336void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { 337 save(SkCanvas::kMatrix_SaveFlag); 338 translate(left, top); 339 drawBitmap(&bitmap, paint); 340 restore(); 341} 342 343void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, 344 const SkPaint* paint) { 345 if (matrix.isIdentity()) { 346 drawBitmap(&bitmap, paint); 347 } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) 348 && MathUtils::isPositive(matrix.getScaleX()) 349 && MathUtils::isPositive(matrix.getScaleY())) { 350 // SkMatrix::isScaleTranslate() not available in L 351 SkRect src; 352 SkRect dst; 353 bitmap.getBounds(&src); 354 matrix.mapRect(&dst, src); 355 drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, 356 dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); 357 } else { 358 save(SkCanvas::kMatrix_SaveFlag); 359 concat(matrix); 360 drawBitmap(&bitmap, paint); 361 restore(); 362 } 363} 364void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, 365 float srcRight, float srcBottom, float dstLeft, float dstTop, 366 float dstRight, float dstBottom, const SkPaint* paint) { 367 if (srcLeft == 0 && srcTop == 0 368 && srcRight == bitmap.width() 369 && srcBottom == bitmap.height() 370 && (srcBottom - srcTop == dstBottom - dstTop) 371 && (srcRight - srcLeft == dstRight - dstLeft)) { 372 // transform simple rect to rect drawing case into position bitmap ops, since they merge 373 save(SkCanvas::kMatrix_SaveFlag); 374 translate(dstLeft, dstTop); 375 drawBitmap(&bitmap, paint); 376 restore(); 377 } else { 378 LOG_ALWAYS_FATAL("TODO!"); 379 } 380} 381void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, 382 const float* vertices, const int* colors, const SkPaint* paint) { 383 LOG_ALWAYS_FATAL("TODO!"); 384} 385void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk, 386 float dstLeft, float dstTop, float dstRight, float dstBottom, 387 const SkPaint* paint) { 388 LOG_ALWAYS_FATAL("TODO!"); 389} 390 391// Text 392void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int count, 393 const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, 394 float boundsRight, float boundsBottom, float totalAdvance) { 395 LOG_ALWAYS_FATAL("TODO!"); 396} 397void RecordingCanvas::drawPosText(const uint16_t* text, const float* positions, int count, 398 int posCount, const SkPaint& paint) { 399 LOG_ALWAYS_FATAL("TODO!"); 400} 401void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, 402 float hOffset, float vOffset, const SkPaint& paint) { 403 LOG_ALWAYS_FATAL("TODO!"); 404} 405 406void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { 407 addOp(new (alloc()) BitmapOp( 408 Rect(0, 0, bitmap->width(), bitmap->height()), 409 *(mState.currentSnapshot()->transform), 410 mState.getRenderTargetClipBounds(), 411 refPaint(paint), refBitmap(*bitmap))); 412} 413void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { 414 RenderNodeOp* op = new (alloc()) RenderNodeOp( 415 Rect(0, 0, renderNode->getWidth(), renderNode->getHeight()), // are these safe? they're theoretically dynamic 416 *(mState.currentSnapshot()->transform), 417 mState.getRenderTargetClipBounds(), 418 renderNode); 419 int opIndex = addOp(op); 420 int childIndex = mDisplayList->addChild(op); 421 422 // update the chunk's child indices 423 DisplayList::Chunk& chunk = mDisplayList->chunks.back(); 424 chunk.endChildIndex = childIndex + 1; 425 426 if (renderNode->stagingProperties().isProjectionReceiver()) { 427 // use staging property, since recording on UI thread 428 mDisplayList->projectionReceiveIndex = opIndex; 429 } 430} 431 432size_t RecordingCanvas::addOp(RecordedOp* op) { 433 // TODO: validate if "addDrawOp" quickrejection logic is useful before adding 434 int insertIndex = mDisplayList->ops.size(); 435 mDisplayList->ops.push_back(op); 436 if (mDeferredBarrierType != kBarrier_None) { 437 // op is first in new chunk 438 mDisplayList->chunks.emplace_back(); 439 DisplayList::Chunk& newChunk = mDisplayList->chunks.back(); 440 newChunk.beginOpIndex = insertIndex; 441 newChunk.endOpIndex = insertIndex + 1; 442 newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder); 443 444 int nextChildIndex = mDisplayList->children.size(); 445 newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; 446 mDeferredBarrierType = kBarrier_None; 447 } else { 448 // standard case - append to existing chunk 449 mDisplayList->chunks.back().endOpIndex = insertIndex + 1; 450 } 451 return insertIndex; 452} 453 454void RecordingCanvas::refBitmapsInShader(const SkShader* shader) { 455 if (!shader) return; 456 457 // If this paint has an SkShader that has an SkBitmap add 458 // it to the bitmap pile 459 SkBitmap bitmap; 460 SkShader::TileMode xy[2]; 461 if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) { 462 refBitmap(bitmap); 463 return; 464 } 465 SkShader::ComposeRec rec; 466 if (shader->asACompose(&rec)) { 467 refBitmapsInShader(rec.fShaderA); 468 refBitmapsInShader(rec.fShaderB); 469 return; 470 } 471} 472 473}; // namespace uirenderer 474}; // namespace android 475