Canvas的drawImage()方法是用于在画布上绘制图像的方法。它可以绘制图像、视频的某一帧图像以及其他画布的内容。此外,还可以对绘制的图像进行裁剪,代码如下:
//frameworks/base/graphics/java/android/graphic/Canvas.java
public class Canvas extends BaseCanvas {
public void drawImage(Image image, float x, float y, SamplingOptions sampling) {
nDrawImage(mNativeInstance, image.getNativeInstance(), x, y,
sampling.getNativeDesc(), sampling.getCubicCoeffB(), sampling.getCubicCoeffC());
}
}
调用nDrawImage方法,该方法是一个Native方法,在Canvas.cpp中定义,通过查调用的是Canvas_DrawImage方法:
//external/skia/modules/androidkit/src/Canvas.cpp
void Canvas_DrawImage(JNIEnv* env, jobject, jlong native_instance, jlong native_image,
jfloat x, jfloat y,
jint sampling_desc, jfloat sampling_b, jfloat sampling_c) {
auto* canvas = reinterpret_cast<SkCanvas*>(native_instance);
auto* image = reinterpret_cast<SkImage *>(native_image);
if (canvas && image) {
canvas->drawImage(image, x, y,
androidkit::utils::SamplingOptions(sampling_desc, sampling_b, sampling_c));
}
}
SkCanvas drawImage
调用SkCanvas的drawImage方法:
//external/skia/src/core/SkiaCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y,
const SkSamplingOptions& sampling, const SkPaint* paint) {
TRACE_EVENT0("skia", TRACE_FUNC);
RETURN_ON_NULL(image);
this->onDrawImage2(image, x, y, sampling, paint);
}
调用SkCanvas的onDrawImage2方法:
//external/skia/include/core/SkCanvas.cpp
class SK_API SkCanvas {
void SkCanvas::onDrawImage2(const SkImage* image, SkScalar x, SkScalar y,
const SkSamplingOptions& sampling, const SkPaint* paint) {
SkPaint realPaint = clean_paint_for_drawImage(paint);
SkRect bounds = SkRect::MakeXYWH(x, y, image->width(), image->height());
if (this->internalQuickReject(bounds, realPaint)) {
return;
}
if (realPaint.getImageFilter() &&
this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), sampling, realPaint) &&
!image_to_color_filter(&realPaint)) {
// Evaluate the image filter directly on the input image and then draw the result, instead
// of first drawing the image to a temporary layer and filtering.
SkBaseDevice* device = this->topDevice();
sk_sp<SkSpecialImage> special;
if ((special = device->makeSpecial(image))) {
sk_sp<SkImageFilter> filter = realPaint.refImageFilter();
realPaint.setImageFilter(nullptr);
// TODO(michaelludwig) - Many filters could probably be evaluated like this even if the
// CTM is not translate-only; the post-transformation of the filtered image by the CTM
// will probably look just as good and not require an extra layer.
// TODO(michaelludwig) - Once image filter implementations can support source images
// with non-(0,0) origins, we can just mark the origin as (x,y) instead of doing a
// pre-concat here.
SkMatrix layerToDevice = device->localToDevice();
layerToDevice.preTranslate(x, y);
SkMatrix deviceToLayer;
if (!layerToDevice.invert(&deviceToLayer)) {
return; // bad ctm, draw nothing
}
skif::Mapping mapping(layerToDevice, deviceToLayer, SkMatrix::Translate(-x, -y));
if (this->predrawNotify()) {
device->drawFilteredImage(mapping, special.get(), filter.get(), sampling,realPaint);
}
return;
} // else fall through to regular drawing path
}
auto layer = this->aboutToDraw(this, realPaint, &bounds);
if (layer) {
this->topDevice()->drawImageRect(image, nullptr, bounds, sampling,
layer->paint(), kStrict_SrcRectConstraint);
}
}
调用SkCanvas的topDevice方法,返回SkBaseDevice对象:
//external/skia/include/core/SkCanvas.cpp
SkBaseDevice* SkCanvas::topDevice() const {
SkASSERT(fMCRec->fDevice);
return fMCRec->fDevice;
}
SkBitmapDevice drawImageRect
调用SkBaseDevice的drawImageRect,由SkBaseDevice的子类SkBitmapDevice实现drawImageRect方法:
//external/skia/src/core/SkBitmapDevice.cpp
class SkBitmapDevice : public SkBaseDevice {
void SkBitmapDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
const SkSamplingOptions& sampling, const SkPaint& paint,
SkCanvas::SrcRectConstraint constraint) {
SkASSERT(dst.isFinite());
SkASSERT(dst.isSorted());
SkBitmap bitmap;
// TODO: Elevate direct context requirement to public API and remove cheat.
auto dContext = as_IB(image)->directContext();
if (!as_IB(image)->getROPixels(dContext, &bitmap)) {
return;
}
SkRect bitmapBounds, tmpSrc, tmpDst;
SkBitmap tmpBitmap;
bitmapBounds.setIWH(bitmap.width(), bitmap.height());
// Compute matrix from the two rectangles
if (src) {
tmpSrc = *src;
} else {
tmpSrc = bitmapBounds;
}
SkMatrix matrix = SkMatrix::RectToRect(tmpSrc, dst);
const SkRect* dstPtr = &dst;
const SkBitmap* bitmapPtr = &bitmap;
// clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
// needed (if the src was clipped). No check needed if src==null.
if (src) {
if (!bitmapBounds.contains(*src)) {
if (!tmpSrc.intersect(bitmapBounds)) {
return; // nothing to draw
}
// recompute dst, based on the smaller tmpSrc
matrix.mapRect(&tmpDst, tmpSrc);
if (!tmpDst.isFinite()) {
return;
}
dstPtr = &tmpDst;
}
}
if (src && !src->contains(bitmapBounds) &&
SkCanvas::kFast_SrcRectConstraint == constraint &&
sampling != SkSamplingOptions()) {
// src is smaller than the bounds of the bitmap, and we are filtering, so we don't know
// how much more of the bitmap we need, so we can't use extractSubset or drawBitmap,
// but we must use a shader w/ dst bounds (which can access all of the bitmap needed).
goto USE_SHADER;
}
if (src) {
// since we may need to clamp to the borders of the src rect within
// the bitmap, we extract a subset.
const SkIRect srcIR = tmpSrc.roundOut();
if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
return;
}
bitmapPtr = &tmpBitmap;
// Since we did an extract, we need to adjust the matrix accordingly
SkScalar dx = 0, dy = 0;
if (srcIR.fLeft > 0) {
dx = SkIntToScalar(srcIR.fLeft);
}
if (srcIR.fTop > 0) {
dy = SkIntToScalar(srcIR.fTop);
}
if (dx || dy) {
matrix.preTranslate(dx, dy);
}
#ifdef SK_DRAWBITMAPRECT_FAST_OFFSET
SkRect extractedBitmapBounds = SkRect::MakeXYWH(dx, dy,
SkIntToScalar(bitmapPtr->width()),
SkIntToScalar(bitmapPtr->height()));
#else
SkRect extractedBitmapBounds;
extractedBitmapBounds.setIWH(bitmapPtr->width(), bitmapPtr->height());
#endif
if (extractedBitmapBounds == tmpSrc) {
// no fractional part in src, we can just call drawBitmap
goto USE_DRAWBITMAP;
}
} else {
USE_DRAWBITMAP:
// We can go faster by just calling drawBitmap, which will concat the
// matrix with the CTM, and try to call drawSprite if it can. If not,
// it will make a shader and call drawRect, as we do below.
if (CanApplyDstMatrixAsCTM(matrix, paint)) {
this->drawBitmap(*bitmapPtr, matrix, dstPtr, sampling, paint);
return;
}
}
USE_SHADER:
// construct a shader, so we can call drawRect with the dst
auto s = SkMakeBitmapShaderForPaint(paint, *bitmapPtr, SkTileMode::kClamp, SkTileMode::kClamp,
sampling, &matrix, kNever_SkCopyPixelsMode);
if (!s) {
return;
}
SkPaint paintWithShader(paint);
paintWithShader.setStyle(SkPaint::kFill_Style);
paintWithShader.setShader(std::move(s));
// Call ourself, in case the subclass wanted to share this setup code
// but handle the drawRect code themselves.
this->drawRect(*dstPtr, paintWithShader);
}
}
SkDraw drawBitmap
调用SkDraw的drawBitmap方法:
//external/skia/src/core/SkDraw.cpp
class SkDraw : public SkGlyphRunListPainter::BitmapDevicePainter {
void SkDraw::drawBitmap(const SkBitmap& bitmap, const SkMatrix& prematrix,
const SkRect* dstBounds, const SkSamplingOptions& sampling,
const SkPaint& origPaint) const {
SkDEBUGCODE(this->validate();)
// nothing to draw
if (fRC->isEmpty() ||
bitmap.width() == 0 || bitmap.height() == 0 ||
bitmap.colorType() == kUnknown_SkColorType) {
return;
}
SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
if (origPaint.getStyle() != SkPaint::kFill_Style) {
paint.writable()->setStyle(SkPaint::kFill_Style);
}
SkPreConcatMatrixProvider matrixProvider(*fMatrixProvider, prematrix);
SkMatrix matrix = matrixProvider.localToDevice();
if (clipped_out(matrix, *fRC, bitmap.width(), bitmap.height())) {
return;
}
if (!SkColorTypeIsAlphaOnly(bitmap.colorType()) &&
SkTreatAsSprite(matrix, bitmap.dimensions(), sampling, *paint)) {
//
// It is safe to call lock pixels now, since we know the matrix is
// (more or less) identity.
//
SkPixmap pmap;
if (!bitmap.peekPixels(&pmap)) {
return;
}
int ix = SkScalarRoundToInt(matrix.getTranslateX());
int iy = SkScalarRoundToInt(matrix.getTranslateY());
if (clipHandlesSprite(*fRC, ix, iy, pmap)) {
SkSTArenaAlloc<kSkBlitterContextSize> allocator;
// blitter will be owned by the allocator.
SkBlitter* blitter = SkBlitter::ChooseSprite(fDst, *paint, pmap, ix, iy, &allocator,
fRC->clipShader());
if (blitter) {
SkScan::FillIRect(SkIRect::MakeXYWH(ix, iy, pmap.width(), pmap.height()),
*fRC, blitter);
return;
}
// if !blitter, then we fall-through to the slower case
}
}
// now make a temp draw on the stack, and use it
//
SkDraw draw(*this);
draw.fMatrixProvider = &matrixProvider;
// For a long time, the CPU backend treated A8 bitmaps as coverage, rather than alpha. This was
// inconsistent with the GPU backend (skbug.com/9692). When this was fixed, it altered behavior
// for some Android apps (b/231400686). Thus: keep the old behavior in the framework.
#if defined(SK_SUPPORT_LEGACY_ALPHA_BITMAP_AS_COVERAGE)
if (bitmap.colorType() == kAlpha_8_SkColorType && !paint->getColorFilter()) {
draw.drawBitmapAsMask(bitmap, sampling, *paint);
return;
}
#endif
SkPaint paintWithShader = make_paint_with_image(*paint, bitmap, sampling);
const SkRect srcBounds = SkRect::MakeIWH(bitmap.width(), bitmap.height());
if (dstBounds) {
this->drawRect(srcBounds, paintWithShader, &prematrix, dstBounds);
} else {
draw.drawRect(srcBounds, paintWithShader);
}
}
}
SkScan FillPath
调用SkScan的FillPath方法:
//external/skia/src/core/SkScan.cpp
class SkScan {
void SkScan::FillPath(const SkPath& path, const SkRegion& origClip,
SkBlitter* blitter) {
if (origClip.isEmpty()) {
return;
}
// Our edges are fixed-point, and don't like the bounds of the clip to
// exceed that. Here we trim the clip just so we don't overflow later on
const SkRegion* clipPtr = &origClip;
SkRegion finiteClip;
if (clip_to_limit(origClip, &finiteClip)) {
if (finiteClip.isEmpty()) {
return;
}
clipPtr = &finiteClip;
}
// don't reference "origClip" any more, just use clipPtr
SkRect bounds = path.getBounds();
bool irPreClipped = false;
if (!SkRectPriv::MakeLargeS32().contains(bounds)) {
if (!bounds.intersect(SkRectPriv::MakeLargeS32())) {
bounds.setEmpty();
}
irPreClipped = true;
}
SkIRect ir = conservative_round_to_int(bounds);
if (ir.isEmpty()) {
if (path.isInverseFillType()) {
blitter->blitRegion(*clipPtr);
}
return;
}
SkScanClipper clipper(blitter, clipPtr, ir, path.isInverseFillType(), irPreClipped);
blitter = clipper.getBlitter();
if (blitter) {
// we have to keep our calls to blitter in sorted order, so we
// must blit the above section first, then the middle, then the bottom.
if (path.isInverseFillType()) {
sk_blit_above(blitter, ir, *clipPtr);
}
SkASSERT(clipper.getClipRect() == nullptr ||
*clipper.getClipRect() == clipPtr->getBounds());
sk_fill_path(path, clipPtr->getBounds(), blitter, ir.fTop, ir.fBottom,
0, clipper.getClipRect() == nullptr);
if (path.isInverseFillType()) {
sk_blit_below(blitter, ir, *clipPtr);
}
} else {
// what does it mean to not have a blitter if path.isInverseFillType???
}
}
}
调用sk_fill_path方法:
//external/skia/src/core/SkScan_Path.cpp
void sk_fill_path(const SkPath& path, const SkIRect& clipRect, SkBlitter* blitter,
int start_y, int stop_y, int shiftEdgesUp, bool pathContainedInClip) {
SkASSERT(blitter);
SkIRect shiftedClip = clipRect;
shiftedClip.fLeft = SkLeftShift(shiftedClip.fLeft, shiftEdgesUp);
shiftedClip.fRight = SkLeftShift(shiftedClip.fRight, shiftEdgesUp);
shiftedClip.fTop = SkLeftShift(shiftedClip.fTop, shiftEdgesUp);
shiftedClip.fBottom = SkLeftShift(shiftedClip.fBottom, shiftEdgesUp);
SkBasicEdgeBuilder builder(shiftEdgesUp);
int count = builder.buildEdges(path, pathContainedInClip ? nullptr : &shiftedClip);
SkEdge** list = builder.edgeList();
if (0 == count) {
if (path.isInverseFillType()) {
/*
* Since we are in inverse-fill, our caller has already drawn above
* our top (start_y) and will draw below our bottom (stop_y). Thus
* we need to restrict our drawing to the intersection of the clip
* and those two limits.
*/
SkIRect rect = clipRect;
if (rect.fTop < start_y) {
rect.fTop = start_y;
}
if (rect.fBottom > stop_y) {
rect.fBottom = stop_y;
}
if (!rect.isEmpty()) {
blitter->blitRect(rect.fLeft << shiftEdgesUp,
rect.fTop << shiftEdgesUp,
rect.width() << shiftEdgesUp,
rect.height() << shiftEdgesUp);
}
}
return;
}
SkEdge headEdge, tailEdge, *last;
// this returns the first and last edge after they're sorted into a dlink list
SkEdge* edge = sort_edges(list, count, &last);
headEdge.fPrev = nullptr;
headEdge.fNext = edge;
headEdge.fFirstY = kEDGE_HEAD_Y;
headEdge.fX = SK_MinS32;
edge->fPrev = &headEdge;
tailEdge.fPrev = last;
tailEdge.fNext = nullptr;
tailEdge.fFirstY = kEDGE_TAIL_Y;
last->fNext = &tailEdge;
// now edge is the head of the sorted linklist
start_y = SkLeftShift(start_y, shiftEdgesUp);
stop_y = SkLeftShift(stop_y, shiftEdgesUp);
if (!pathContainedInClip && start_y < shiftedClip.fTop) {
start_y = shiftedClip.fTop;
}
if (!pathContainedInClip && stop_y > shiftedClip.fBottom) {
stop_y = shiftedClip.fBottom;
}
InverseBlitter ib;
PrePostProc proc = nullptr;
if (path.isInverseFillType()) {
ib.setBlitter(blitter, clipRect, shiftEdgesUp);
blitter = &ib;
proc = PrePostInverseBlitterProc;
}
// count >= 2 is required as the convex walker does not handle missing right edges
if (path.isConvex() && (nullptr == proc) && count >= 2) {
walk_simple_edges(&headEdge, blitter, start_y, stop_y);
} else {
walk_edges(&headEdge, path.getFillType(), blitter, start_y, stop_y, proc,
shiftedClip.right());
}
}
调用SkBlitter的blitRect方法:
//external/skia/src/core/SkBlitter.cpp
class SkBlitter {
void SkBlitter::blitRect(int x, int y, int width, int height) {
SkASSERT(width > 0);
while (--height >= 0) {
this->blitH(x, y++, width);
}
}
}