RecordingCanvas.cpp revision b565df13a9e5c7b1d7d93bdfa4a793752d66d3cc
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(mDisplayListData, 33 "Destroyed a RecordingCanvas during a record!"); 34} 35 36void RecordingCanvas::reset(int width, int height) { 37 LOG_ALWAYS_FATAL_IF(mDisplayListData, 38 "prepareDirty called a second time during a recording!"); 39 mDisplayListData = new DisplayListData(); 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 48DisplayListData* RecordingCanvas::finishRecording() { 49 mPaintMap.clear(); 50 mRegionMap.clear(); 51 mPathMap.clear(); 52 DisplayListData* data = mDisplayListData; 53 mDisplayListData = nullptr; 54 mSkiaCanvasProxy.reset(nullptr); 55 return data; 56} 57 58SkCanvas* RecordingCanvas::asSkCanvas() { 59 LOG_ALWAYS_FATAL_IF(!mDisplayListData, 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// android/graphics/Canvas state operations 77// ---------------------------------------------------------------------------- 78// Save (layer) 79int RecordingCanvas::save(SkCanvas::SaveFlags flags) { 80 return mState.save((int) flags); 81} 82 83void RecordingCanvas::RecordingCanvas::restore() { 84 if (mRestoreSaveCount < 0) { 85 restoreToCount(getSaveCount() - 1); 86 return; 87 } 88 89 mRestoreSaveCount--; 90 mState.restore(); 91} 92 93void RecordingCanvas::restoreToCount(int saveCount) { 94 mRestoreSaveCount = saveCount; 95 mState.restoreToCount(saveCount); 96} 97 98int RecordingCanvas::saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, 99 SkCanvas::SaveFlags flags) { 100 LOG_ALWAYS_FATAL("TODO"); 101 return 0; 102} 103 104// Matrix 105void RecordingCanvas::rotate(float degrees) { 106 if (degrees == 0) return; 107 108 mState.rotate(degrees); 109} 110 111void RecordingCanvas::scale(float sx, float sy) { 112 if (sx == 1 && sy == 1) return; 113 114 mState.scale(sx, sy); 115} 116 117void RecordingCanvas::skew(float sx, float sy) { 118 mState.skew(sx, sy); 119} 120 121void RecordingCanvas::translate(float dx, float dy) { 122 if (dx == 0 && dy == 0) return; 123 124 mState.translate(dx, dy, 0); 125} 126 127// Clip 128bool RecordingCanvas::getClipBounds(SkRect* outRect) const { 129 Rect bounds = mState.getLocalClipBounds(); 130 *outRect = SkRect::MakeLTRB(bounds.left, bounds.top, bounds.right, bounds.bottom); 131 return !(outRect->isEmpty()); 132} 133bool RecordingCanvas::quickRejectRect(float left, float top, float right, float bottom) const { 134 return mState.quickRejectConservative(left, top, right, bottom); 135} 136bool RecordingCanvas::quickRejectPath(const SkPath& path) const { 137 SkRect bounds = path.getBounds(); 138 return mState.quickRejectConservative(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom); 139} 140bool RecordingCanvas::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { 141 return mState.clipRect(left, top, right, bottom, op); 142} 143bool RecordingCanvas::clipPath(const SkPath* path, SkRegion::Op op) { 144 return mState.clipPath(path, op); 145} 146bool RecordingCanvas::clipRegion(const SkRegion* region, SkRegion::Op op) { 147 return mState.clipRegion(region, op); 148} 149 150// ---------------------------------------------------------------------------- 151// android/graphics/Canvas draw operations 152// ---------------------------------------------------------------------------- 153void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) { 154 SkPaint paint; 155 paint.setColor(color); 156 paint.setXfermodeMode(mode); 157 drawPaint(paint); 158} 159 160void RecordingCanvas::drawPaint(const SkPaint& paint) { 161 // TODO: more efficient recording? 162 Matrix4 identity; 163 identity.loadIdentity(); 164 165 addOp(new (alloc()) RectOp( 166 mState.getRenderTargetClipBounds(), 167 identity, 168 mState.getRenderTargetClipBounds(), 169 refPaint(&paint))); 170} 171 172// Geometry 173void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { 174 LOG_ALWAYS_FATAL("TODO!"); 175} 176void RecordingCanvas::drawLines(const float* points, int count, const SkPaint& paint) { 177 LOG_ALWAYS_FATAL("TODO!"); 178} 179void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { 180 addOp(new (alloc()) RectOp( 181 Rect(left, top, right, bottom), 182 *(mState.currentSnapshot()->transform), 183 mState.getRenderTargetClipBounds(), 184 refPaint(&paint))); 185} 186 187void RecordingCanvas::drawSimpleRects(const float* rects, int vertexCount, const SkPaint* paint) { 188 if (rects == nullptr) return; 189 190 Vertex* rectData = (Vertex*) mDisplayListData->allocator.alloc(vertexCount * sizeof(Vertex)); 191 Vertex* vertex = rectData; 192 193 float left = FLT_MAX; 194 float top = FLT_MAX; 195 float right = FLT_MIN; 196 float bottom = FLT_MIN; 197 for (int index = 0; index < vertexCount; index += 4) { 198 float l = rects[index + 0]; 199 float t = rects[index + 1]; 200 float r = rects[index + 2]; 201 float b = rects[index + 3]; 202 203 Vertex::set(vertex++, l, t); 204 Vertex::set(vertex++, r, t); 205 Vertex::set(vertex++, l, b); 206 Vertex::set(vertex++, r, b); 207 208 left = std::min(left, l); 209 top = std::min(top, t); 210 right = std::max(right, r); 211 bottom = std::max(bottom, b); 212 } 213 addOp(new (alloc()) SimpleRectsOp( 214 Rect(left, top, right, bottom), 215 *(mState.currentSnapshot()->transform), 216 mState.getRenderTargetClipBounds(), 217 refPaint(paint), rectData, vertexCount)); 218} 219 220void RecordingCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { 221 if (paint.getStyle() == SkPaint::kFill_Style 222 && (!paint.isAntiAlias() || mState.currentTransform()->isSimple())) { 223 int count = 0; 224 Vector<float> rects; 225 SkRegion::Iterator it(region); 226 while (!it.done()) { 227 const SkIRect& r = it.rect(); 228 rects.push(r.fLeft); 229 rects.push(r.fTop); 230 rects.push(r.fRight); 231 rects.push(r.fBottom); 232 count += 4; 233 it.next(); 234 } 235 drawSimpleRects(rects.array(), count, &paint); 236 } else { 237 SkRegion::Iterator it(region); 238 while (!it.done()) { 239 const SkIRect& r = it.rect(); 240 drawRect(r.fLeft, r.fTop, r.fRight, r.fBottom, paint); 241 it.next(); 242 } 243 } 244} 245void RecordingCanvas::drawRoundRect(float left, float top, float right, float bottom, 246 float rx, float ry, const SkPaint& paint) { 247 LOG_ALWAYS_FATAL("TODO!"); 248} 249void RecordingCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) { 250 LOG_ALWAYS_FATAL("TODO!"); 251} 252void RecordingCanvas::drawOval(float left, float top, float right, float bottom, const SkPaint& paint) { 253 LOG_ALWAYS_FATAL("TODO!"); 254} 255void RecordingCanvas::drawArc(float left, float top, float right, float bottom, 256 float startAngle, float sweepAngle, bool useCenter, const SkPaint& paint) { 257 LOG_ALWAYS_FATAL("TODO!"); 258} 259void RecordingCanvas::drawPath(const SkPath& path, const SkPaint& paint) { 260 LOG_ALWAYS_FATAL("TODO!"); 261} 262 263// Bitmap-based 264void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float left, float top, const SkPaint* paint) { 265 save(SkCanvas::kMatrix_SaveFlag); 266 translate(left, top); 267 drawBitmap(&bitmap, paint); 268 restore(); 269} 270 271void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, 272 const SkPaint* paint) { 273 if (matrix.isIdentity()) { 274 drawBitmap(&bitmap, paint); 275 } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) 276 && MathUtils::isPositive(matrix.getScaleX()) 277 && MathUtils::isPositive(matrix.getScaleY())) { 278 // SkMatrix::isScaleTranslate() not available in L 279 SkRect src; 280 SkRect dst; 281 bitmap.getBounds(&src); 282 matrix.mapRect(&dst, src); 283 drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom, 284 dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint); 285 } else { 286 save(SkCanvas::kMatrix_SaveFlag); 287 concat(matrix); 288 drawBitmap(&bitmap, paint); 289 restore(); 290 } 291} 292void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop, 293 float srcRight, float srcBottom, float dstLeft, float dstTop, 294 float dstRight, float dstBottom, const SkPaint* paint) { 295 if (srcLeft == 0 && srcTop == 0 296 && srcRight == bitmap.width() 297 && srcBottom == bitmap.height() 298 && (srcBottom - srcTop == dstBottom - dstTop) 299 && (srcRight - srcLeft == dstRight - dstLeft)) { 300 // transform simple rect to rect drawing case into position bitmap ops, since they merge 301 save(SkCanvas::kMatrix_SaveFlag); 302 translate(dstLeft, dstTop); 303 drawBitmap(&bitmap, paint); 304 restore(); 305 } else { 306 LOG_ALWAYS_FATAL("TODO!"); 307 } 308} 309void RecordingCanvas::drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight, 310 const float* vertices, const int* colors, const SkPaint* paint) { 311 LOG_ALWAYS_FATAL("TODO!"); 312} 313void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_png_9patch& chunk, 314 float dstLeft, float dstTop, float dstRight, float dstBottom, 315 const SkPaint* paint) { 316 LOG_ALWAYS_FATAL("TODO!"); 317} 318 319// Text 320void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int count, 321 const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, 322 float boundsRight, float boundsBottom, float totalAdvance) { 323 LOG_ALWAYS_FATAL("TODO!"); 324} 325void RecordingCanvas::drawPosText(const uint16_t* text, const float* positions, int count, 326 int posCount, const SkPaint& paint) { 327 LOG_ALWAYS_FATAL("TODO!"); 328} 329void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, 330 float hOffset, float vOffset, const SkPaint& paint) { 331 LOG_ALWAYS_FATAL("TODO!"); 332} 333 334void RecordingCanvas::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { 335 addOp(new (alloc()) BitmapOp( 336 Rect(0, 0, bitmap->width(), bitmap->height()), 337 *(mState.currentSnapshot()->transform), 338 mState.getRenderTargetClipBounds(), 339 refPaint(paint), refBitmap(*bitmap))); 340} 341void RecordingCanvas::drawRenderNode(RenderNode* renderNode) { 342 RenderNodeOp* op = new (alloc()) RenderNodeOp( 343 Rect(0, 0, renderNode->getWidth(), renderNode->getHeight()), // are these safe? they're theoretically dynamic 344 *(mState.currentSnapshot()->transform), 345 mState.getRenderTargetClipBounds(), 346 renderNode); 347 int opIndex = addOp(op); 348 int childIndex = mDisplayListData->addChild(op); 349 350 // update the chunk's child indices 351 DisplayListData::Chunk& chunk = mDisplayListData->chunks.back(); 352 chunk.endChildIndex = childIndex + 1; 353 354 if (renderNode->stagingProperties().isProjectionReceiver()) { 355 // use staging property, since recording on UI thread 356 mDisplayListData->projectionReceiveIndex = opIndex; 357 } 358} 359 360size_t RecordingCanvas::addOp(RecordedOp* op) { 361 // TODO: validate if "addDrawOp" quickrejection logic is useful before adding 362 int insertIndex = mDisplayListData->ops.size(); 363 mDisplayListData->ops.push_back(op); 364 if (mDeferredBarrierType != kBarrier_None) { 365 // op is first in new chunk 366 mDisplayListData->chunks.emplace_back(); 367 DisplayListData::Chunk& newChunk = mDisplayListData->chunks.back(); 368 newChunk.beginOpIndex = insertIndex; 369 newChunk.endOpIndex = insertIndex + 1; 370 newChunk.reorderChildren = (mDeferredBarrierType == kBarrier_OutOfOrder); 371 372 int nextChildIndex = mDisplayListData->children().size(); 373 newChunk.beginChildIndex = newChunk.endChildIndex = nextChildIndex; 374 mDeferredBarrierType = kBarrier_None; 375 } else { 376 // standard case - append to existing chunk 377 mDisplayListData->chunks.back().endOpIndex = insertIndex + 1; 378 } 379 return insertIndex; 380} 381 382void RecordingCanvas::refBitmapsInShader(const SkShader* shader) { 383 if (!shader) return; 384 385 // If this paint has an SkShader that has an SkBitmap add 386 // it to the bitmap pile 387 SkBitmap bitmap; 388 SkShader::TileMode xy[2]; 389 if (shader->asABitmap(&bitmap, nullptr, xy) == SkShader::kDefault_BitmapType) { 390 refBitmap(bitmap); 391 return; 392 } 393 SkShader::ComposeRec rec; 394 if (shader->asACompose(&rec)) { 395 refBitmapsInShader(rec.fShaderA); 396 refBitmapsInShader(rec.fShaderB); 397 return; 398 } 399} 400 401}; // namespace uirenderer 402}; // namespace android 403