void draw(SkCanvas* canvas) { SkRect rect = SkRect::MakeLTRB(5, 35, 15, 25); SkDebugf("rect: %g, %g, %g, %g isEmpty: %s\n", rect.left(), rect.top(), rect.right(), rect.bottom(), rect.isEmpty() ? "true" : "false"); rect.sort(); SkDebugf("rect: %g, %g, %g, %g isEmpty: %s\n", rect.left(), rect.top(), rect.right(), rect.bottom(), rect.isEmpty() ? "true" : "false"); }
void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no canvas or surface!"); SkRect dirty; mDamageAccumulator.finish(&dirty); // TODO: Re-enable after figuring out cause of b/22592975 // if (dirty.isEmpty() && Properties::skipEmptyFrames) { // mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); // return; // } mCurrentFrameInfo->markIssueDrawCommandsStart(); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", SK_RECT_ARGS(dirty), width, height); dirty.setEmpty(); } profiler().unionDirty(&dirty); } if (!dirty.isEmpty()) { mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { mCanvas->prepare(mOpaque); } Rect outBounds; mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); bool drew = mCanvas->finish(); // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point mCurrentFrameInfo->markSwapBuffers(); if (drew) { swapBuffers(dirty, width, height); } // TODO: Use a fence for real completion? mCurrentFrameInfo->markFrameCompleted(); mJankTracker.addFrame(*mCurrentFrameInfo); mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); }
sk_sp<SkPicture> SkPictureRecorder::finishRecordingAsPicture(uint32_t finishFlags) { fActivelyRecording = false; fRecorder->restoreToCount(1); // If we were missing any restores, add them now. if (fRecord->count() == 0) { if (finishFlags & kReturnNullForEmpty_FinishFlag) { return nullptr; } return fMiniRecorder.detachAsPicture(fCullRect); } // TODO: delay as much of this work until just before first playback? SkRecordOptimize(fRecord); if (fRecord->count() == 0) { if (finishFlags & kReturnNullForEmpty_FinishFlag) { return nullptr; } } SkAutoTUnref<SkLayerInfo> saveLayerData; if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) { saveLayerData.reset(new SkLayerInfo); } SkDrawableList* drawableList = fRecorder->getDrawableList(); SkBigPicture::SnapshotArray* pictList = drawableList ? drawableList->newDrawableSnapshot() : nullptr; if (fBBH.get()) { SkAutoTMalloc<SkRect> bounds(fRecord->count()); if (saveLayerData) { SkRecordComputeLayers(fCullRect, *fRecord, bounds, pictList, saveLayerData); } else { SkRecordFillBounds(fCullRect, *fRecord, bounds); } fBBH->insert(bounds, fRecord->count()); // Now that we've calculated content bounds, we can update fCullRect, often trimming it. // TODO: get updated fCullRect from bounds instead of forcing the BBH to return it? SkRect bbhBound = fBBH->getRootBound(); SkASSERT((bbhBound.isEmpty() || fCullRect.contains(bbhBound)) || (bbhBound.isEmpty() && fCullRect.isEmpty())); fCullRect = bbhBound; } size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures(); for (int i = 0; pictList && i < pictList->count(); i++) { subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); } return sk_make_sp<SkBigPicture>(fCullRect, fRecord.release(), pictList, fBBH.release(), saveLayerData.release(), subPictureBytes); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator compositeOp) { startAnimation(); SkBitmapRef* image = this->nativeImageForCurrentFrame(); if (!image) { // If it's too early we won't have an image yet. return; } // in case we get called with an incomplete bitmap const SkBitmap& bitmap = image->bitmap(); if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { #ifdef TRACE_SKIPPED_BITMAPS SkDebugf("----- skip bitmapimage: [%d %d] pixels %p pixelref %p\n", bitmap.width(), bitmap.height(), bitmap.getPixels(), bitmap.pixelRef()); #endif return; } SkIRect srcR; SkRect dstR; float invScaleX = (float)bitmap.width() / image->origWidth(); float invScaleY = (float)bitmap.height() / image->origHeight(); android_setrect(&dstR, dstRect); android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY); if (srcR.isEmpty() || dstR.isEmpty()) { #ifdef TRACE_SKIPPED_BITMAPS SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n", bitmap.width(), bitmap.height(), srcR.isEmpty(), dstR.isEmpty()); #endif return; } SkCanvas* canvas = ctxt->platformContext()->mCanvas; SkPaint paint; paint.setFilterBitmap(true); paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || bitmap.height() != image->origHeight()) { SkDebugf("--- BitmapImage::draw [%d %d] orig [%d %d]\n", bitmap.width(), bitmap.height(), image->origWidth(), image->origHeight()); } #endif }
void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawRenderNode called on a context with no canvas or surface!"); profiler().markPlaybackStart(); SkRect dirty; mDamageAccumulator.finish(&dirty); EGLint width, height; mEglManager.beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { //ALOGW("Dirty " RECT_STRING " doesn't intersect with 0 0 %d %d ?", // SK_RECT_ARGS(dirty), width, height); dirty.setEmpty(); } profiler().unionDirty(&dirty); } status_t status; if (!dirty.isEmpty()) { status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom, mOpaque); } else { status = mCanvas->prepare(mOpaque); } Rect outBounds; status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); mCanvas->finish(); profiler().markPlaybackEnd(); if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); } else { mEglManager.cancelFrame(); } profiler().finishFrame(); }
bool SkRect::intersect(const SkRect& a, const SkRect& b) { SkASSERT(&a && &b); if (!a.isEmpty() && !b.isEmpty() && a.fLeft < b.fRight && b.fLeft < a.fRight && a.fTop < b.fBottom && b.fTop < a.fBottom) { fLeft = SkMaxScalar(a.fLeft, b.fLeft); fTop = SkMaxScalar(a.fTop, b.fTop); fRight = SkMinScalar(a.fRight, b.fRight); fBottom = SkMinScalar(a.fBottom, b.fBottom); return true; } return false; }
void draw_fill(SkCanvas* canvas, const SkRect& rect, SkScalar width) { if (rect.isEmpty()) { return; } SkPaint paint; paint.setColor(0x1f1f0f0f); paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(width); SkPath path; SkScalar maxSide = SkTMax(rect.width(), rect.height()) / 2; SkPoint center = { rect.fLeft + maxSide, rect.fTop + maxSide }; path.addCircle(center.fX, center.fY, maxSide); canvas->drawPath(path, paint); paint.setStyle(SkPaint::kFill_Style); path.reset(); path.addCircle(center.fX, center.fY, maxSide - width / 2); paint.setColor(0x3f0f1f3f); canvas->drawPath(path, paint); path.reset(); path.setFillType(SkPath::kEvenOdd_FillType); path.addCircle(center.fX, center.fY, maxSide + width / 2); SkRect outside = SkRect::MakeXYWH(center.fX - maxSide - width, center.fY - maxSide - width, (maxSide + width) * 2, (maxSide + width) * 2); path.addRect(outside); canvas->drawPath(path, paint); }
void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) { if (rect.isEmpty() || !rect.isFinite()) { this->setEmpty(); return; } if (!SkScalarsAreFinite(xRad, yRad)) { xRad = yRad = 0; // devolve into a simple rect } if (xRad <= 0 || yRad <= 0) { // all corners are square in this case this->setRect(rect); return; } if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) { SkScalar scale = SkMinScalar(rect.width() / (xRad + xRad), rect.height() / (yRad + yRad)); SkASSERT(scale < SK_Scalar1); xRad = SkScalarMul(xRad, scale); yRad = SkScalarMul(yRad, scale); } fRect = rect; for (int i = 0; i < 4; ++i) { fRadii[i].set(xRad, yRad); } fType = kSimple_Type; if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) { fType = kOval_Type; // TODO: assert that all the x&y radii are already W/2 & H/2 } SkDEBUGCODE(this->validate();) }
SkClipStack::SkClipStack(const SkRect& r) : fDeque(sizeof(Element), kDefaultElementAllocCnt) , fSaveCount(0) { if (!r.isEmpty()) { this->clipDevRect(r, SkRegion::kReplace_Op, false); } }
bool SkBBoxRecord::transformBounds(const SkRect& bounds, const SkPaint* paint) { SkRect outBounds = bounds; outBounds.sort(); if (paint) { // account for stroking, path effects, shadows, etc if (paint->canComputeFastBounds()) { SkRect temp; outBounds = paint->computeFastBounds(outBounds, &temp); } else { // set bounds to current clip if (!this->getClipBounds(&outBounds)) { // current clip is empty return false; } } } if (!outBounds.isEmpty() && !this->quickReject(outBounds)) { this->getTotalMatrix().mapRect(&outBounds); this->handleBBox(outBounds); return true; } return false; }
void OpaqueRegionSkia::markRectAsOpaque(const SkRect& rect) { // We want to keep track of an opaque region but bound its complexity at a constant size. // We keep track of the largest rectangle seen by area. If we can add the new rect to this // rectangle then we do that, as that is the cheapest way to increase the area returned // without increasing the complexity. if (rect.isEmpty()) return; if (m_opaqueRect.contains(rect)) return; if (rect.contains(m_opaqueRect)) { m_opaqueRect = rect; return; } if (rect.fTop <= m_opaqueRect.fTop && rect.fBottom >= m_opaqueRect.fBottom) { if (rect.fLeft < m_opaqueRect.fLeft && rect.fRight >= m_opaqueRect.fLeft) m_opaqueRect.fLeft = rect.fLeft; if (rect.fRight > m_opaqueRect.fRight && rect.fLeft <= m_opaqueRect.fRight) m_opaqueRect.fRight = rect.fRight; } else if (rect.fLeft <= m_opaqueRect.fLeft && rect.fRight >= m_opaqueRect.fRight) { if (rect.fTop < m_opaqueRect.fTop && rect.fBottom >= m_opaqueRect.fTop) m_opaqueRect.fTop = rect.fTop; if (rect.fBottom > m_opaqueRect.fBottom && rect.fTop <= m_opaqueRect.fBottom) m_opaqueRect.fBottom = rect.fBottom; } long opaqueArea = (long)m_opaqueRect.width() * (long)m_opaqueRect.height(); long area = (long)rect.width() * (long)rect.height(); if (area > opaqueArea) m_opaqueRect = rect; }
void OpaqueRegionSkia::applyOpaqueRegionFromLayer(const GraphicsContext* context, const SkRect& layerOpaqueRect, const SkPaint& paint) { SkRect deviceClipRect; bool deviceClipIsARect = getDeviceClipAsRect(context, deviceClipRect); if (deviceClipRect.isEmpty()) return; SkRect sourceOpaqueRect = layerOpaqueRect; // Save the opaque area in the destination, so we can preserve the parts of it under the source opaque area if possible. SkRect destinationOpaqueRect = currentTrackingOpaqueRect(); bool outsideSourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, false); if (!outsideSourceOpaqueRectPreservesOpaque) markRectAsNonOpaque(deviceClipRect); if (!deviceClipIsARect) return; if (!sourceOpaqueRect.intersect(deviceClipRect)) return; bool sourceOpaqueRectDrawsOpaque = paintIsOpaque(paint, FillOnly, 0); bool sourceOpaqueRectXfersOpaque = xfermodeIsOpaque(paint, sourceOpaqueRectDrawsOpaque); bool sourceOpaqueRectPreservesOpaque = xfermodePreservesOpaque(paint, sourceOpaqueRectDrawsOpaque); // If the layer's opaque area is being drawn opaque in the layer below, then mark it opaque. Otherwise, // if it preserves opaque then keep the intersection of the two. if (sourceOpaqueRectXfersOpaque) markRectAsOpaque(sourceOpaqueRect); else if (sourceOpaqueRectPreservesOpaque && sourceOpaqueRect.intersect(destinationOpaqueRect)) markRectAsOpaque(sourceOpaqueRect); }
void SkTileGrid::insert(void* data, const SkRect& fbounds, bool) { SkASSERT(!fbounds.isEmpty()); SkIRect dilatedBounds; if (fbounds.isLargest()) { // Dilating the largest SkIRect will overflow. Other nearly-largest rects may overflow too, // but we don't make active use of them like we do the largest. dilatedBounds.setLargest(); } else { fbounds.roundOut(&dilatedBounds); dilatedBounds.outset(fInfo.fMargin.width(), fInfo.fMargin.height()); dilatedBounds.offset(fInfo.fOffset); } const SkIRect gridBounds = { 0, 0, fInfo.fTileInterval.width() * fXTiles, fInfo.fTileInterval.height() * fYTiles }; if (!SkIRect::Intersects(dilatedBounds, gridBounds)) { return; } // Note: SkIRects are non-inclusive of the right() column and bottom() row, // hence the "-1"s in the computations of maxX and maxY. int minX = SkMax32(0, SkMin32(dilatedBounds.left() / fInfo.fTileInterval.width(), fXTiles - 1)); int minY = SkMax32(0, SkMin32(dilatedBounds.top() / fInfo.fTileInterval.height(), fYTiles - 1)); int maxX = SkMax32(0, SkMin32((dilatedBounds.right() - 1) / fInfo.fTileInterval.width(), fXTiles - 1)); int maxY = SkMax32(0, SkMin32((dilatedBounds.bottom() - 1) / fInfo.fTileInterval.height(), fYTiles - 1)); Entry entry = { fCount++, data }; for (int y = minY; y <= maxY; y++) { for (int x = minX; x <= maxX; x++) { fTiles[y * fXTiles + x].push(entry); } } }
bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) { if (CC_UNLIKELY(Properties::waitForGpuCompletion)) { ATRACE_NAME("Finishing GPU work"); fence(); } EGLint rects[4]; frame.map(screenDirty, rects); eglSwapBuffersWithDamageKHR(mEglDisplay, frame.mSurface, rects, screenDirty.isEmpty() ? 0 : 1); EGLint err = eglGetError(); if (CC_LIKELY(err == EGL_SUCCESS)) { return true; } if (err == EGL_BAD_SURFACE || err == EGL_BAD_NATIVE_WINDOW) { // For some reason our surface was destroyed out from under us // This really shouldn't happen, but if it does we can recover easily // by just not trying to use the surface anymore ALOGW("swapBuffers encountered EGL error %d on %p, halting rendering...", err, frame.mSurface); return false; } LOG_ALWAYS_FATAL("Encountered EGL error %d %s during rendering", err, egl_error_str(err)); // Impossible to hit this, but the compiler doesn't know that return false; }
void SkRRect::setNinePatch(const SkRect& rect, SkScalar leftRad, SkScalar topRad, SkScalar rightRad, SkScalar bottomRad) { if (rect.isEmpty() || !rect.isFinite()) { this->setEmpty(); return; } const SkScalar array[4] = { leftRad, topRad, rightRad, bottomRad }; if (!SkScalarsAreFinite(array, 4)) { this->setRect(rect); // devolve into a simple rect return; } leftRad = SkMaxScalar(leftRad, 0); topRad = SkMaxScalar(topRad, 0); rightRad = SkMaxScalar(rightRad, 0); bottomRad = SkMaxScalar(bottomRad, 0); SkScalar scale = SK_Scalar1; if (leftRad + rightRad > rect.width()) { scale = rect.width() / (leftRad + rightRad); } if (topRad + bottomRad > rect.height()) { scale = SkMinScalar(scale, rect.height() / (topRad + bottomRad)); } if (scale < SK_Scalar1) { leftRad = SkScalarMul(leftRad, scale); topRad = SkScalarMul(topRad, scale); rightRad = SkScalarMul(rightRad, scale); bottomRad = SkScalarMul(bottomRad, scale); } if (leftRad == rightRad && topRad == bottomRad) { if (leftRad >= SkScalarHalf(rect.width()) && topRad >= SkScalarHalf(rect.height())) { fType = kOval_Type; } else if (0 == leftRad || 0 == topRad) { // If the left and (by equality check above) right radii are zero then it is a rect. // Same goes for top/bottom. fType = kRect_Type; leftRad = 0; topRad = 0; rightRad = 0; bottomRad = 0; } else { fType = kSimple_Type; } } else { fType = kNinePatch_Type; } fRect = rect; fRadii[kUpperLeft_Corner].set(leftRad, topRad); fRadii[kUpperRight_Corner].set(rightRad, topRad); fRadii[kLowerRight_Corner].set(rightRad, bottomRad); fRadii[kLowerLeft_Corner].set(leftRad, bottomRad); SkDEBUGCODE(this->validate();) }
bool SkHitTestPath(const SkPath& path, SkRect& target, bool hires) { if (target.isEmpty()) { return false; } bool isInverse = path.isInverseFillType(); if (path.isEmpty()) { return isInverse; } SkRect bounds = path.getBounds(); bool sects = SkRect::Intersects(target, bounds); if (isInverse) { if (!sects) { return true; } } else { if (!sects) { return false; } if (target.contains(bounds)) { return true; } } SkPath devPath; const SkPath* pathPtr; SkRect devTarget; if (hires) { const SkScalar coordLimit = SkIntToScalar(16384); const SkRect limit = { 0, 0, coordLimit, coordLimit }; SkMatrix matrix; matrix.setRectToRect(bounds, limit, SkMatrix::kFill_ScaleToFit); path.transform(matrix, &devPath); matrix.mapRect(&devTarget, target); pathPtr = &devPath; } else { devTarget = target; pathPtr = &path; } SkIRect iTarget; devTarget.round(&iTarget); if (iTarget.isEmpty()) { iTarget.fLeft = SkScalarFloorToInt(devTarget.fLeft); iTarget.fTop = SkScalarFloorToInt(devTarget.fTop); iTarget.fRight = iTarget.fLeft + 1; iTarget.fBottom = iTarget.fTop + 1; } SkRegion clip(iTarget); SkRegion rgn; return rgn.setPath(*pathPtr, clip) ^ isInverse; }
SkPicture* SkPictureRecorder::endRecordingAsPicture() { fActivelyRecording = false; fRecorder->restoreToCount(1); // If we were missing any restores, add them now. if (fRecord->count() == 0) { return fMiniRecorder.detachAsPicture(fCullRect); } // TODO: delay as much of this work until just before first playback? SkRecordOptimize(fRecord); SkAutoTUnref<SkLayerInfo> saveLayerData; if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) { saveLayerData.reset(SkNEW(SkLayerInfo)); } SkDrawableList* drawableList = fRecorder->getDrawableList(); SkBigPicture::SnapshotArray* pictList = drawableList ? drawableList->newDrawableSnapshot() : NULL; if (fBBH.get()) { if (saveLayerData) { SkRecordComputeLayers(fCullRect, *fRecord, pictList, fBBH.get(), saveLayerData); } else { SkRecordFillBounds(fCullRect, *fRecord, fBBH.get()); } SkRect bbhBound = fBBH->getRootBound(); SkASSERT((bbhBound.isEmpty() || fCullRect.contains(bbhBound)) || (bbhBound.isEmpty() && fCullRect.isEmpty())); fCullRect = bbhBound; } size_t subPictureBytes = fRecorder->approxBytesUsedBySubPictures(); for (int i = 0; pictList && i < pictList->count(); i++) { subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]); } return SkNEW_ARGS(SkBigPicture, (fCullRect, fRecord.detach(), pictList, fBBH.detach(), saveLayerData.detach(), subPictureBytes)); }
void MediaTexture::draw(const TransformationMatrix& contentMatrix, const TransformationMatrix& videoMatrix, const SkRect& mediaBounds) { android::Mutex::Autolock lock(m_mediaLock); if (mediaBounds.isEmpty()) return; // draw all the video textures first for (unsigned int i = 0; i < m_videoTextures.size(); i++) { TextureWrapper* video = m_videoTextures[i]; if (!video->surfaceTexture.get() || video->dimensions.isEmpty() || !video->mediaListener->isFrameAvailable()) continue; video->surfaceTexture->updateTexImage(); float surfaceMatrix[16]; video->surfaceTexture->getTransformMatrix(surfaceMatrix); SkRect dimensions = video->dimensions; dimensions.offset(mediaBounds.fLeft, mediaBounds.fTop); #ifdef DEBUG if (!mediaBounds.contains(dimensions)) { ALOGV("The video exceeds is parent's bounds."); } #endif // DEBUG TilesManager::instance()->shader()->drawVideoLayerQuad(videoMatrix, surfaceMatrix, dimensions, video->textureId); } if (!m_contentTexture->mediaListener->isFrameAvailable()) return; m_contentTexture->surfaceTexture->updateTexImage(); sp<GraphicBuffer> buf = m_contentTexture->surfaceTexture->getCurrentBuffer(); PixelFormat f = buf->getPixelFormat(); // only attempt to use alpha blending if alpha channel exists bool forceAlphaBlending = !( PIXEL_FORMAT_RGBX_8888 == f || PIXEL_FORMAT_RGB_888 == f || PIXEL_FORMAT_RGB_565 == f); TextureQuadData data(m_contentTexture->textureId, GL_TEXTURE_EXTERNAL_OES, GL_LINEAR, LayerQuad, &contentMatrix, &mediaBounds, 1.0f, forceAlphaBlending); TilesManager::instance()->shader()->drawQuad(&data); }
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect) { SkBitmapRef* image = this->nativeImageForCurrentFrame(); if (!image) { // If it's too early we won't have an image yet. return; } // in case we get called with an incomplete bitmap const SkBitmap& bitmap = image->bitmap(); if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { return; } SkRect dstR; android_setrect(&dstR, destRect); if (dstR.isEmpty()) { return; } SkCanvas* canvas = ctxt->platformContext()->mCanvas; SkPaint paint; SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); paint.setShader(shader)->unref(); // now paint is the only owner of shader paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); paint.setFilterBitmap(true); SkMatrix matrix(patternTransform); float scaleX = (float)image->origWidth() / bitmap.width(); float scaleY = (float)image->origHeight() / bitmap.height(); matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY)); matrix.postTranslate(SkFloatToScalar(phase.x()), SkFloatToScalar(phase.y())); shader->setLocalMatrix(matrix); canvas->drawRect(dstR, paint); #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || bitmap.height() != image->origHeight()) { SkDebugf("--- Image::drawPattern [%d %d] orig [%d %d] dst [%g %g]\n", bitmap.width(), bitmap.height(), image->origWidth(), image->origHeight(), SkScalarToFloat(dstR.width()), SkScalarToFloat(dstR.height())); } #endif }
static SkRect random_rect(SkRandom& rand) { SkRect rect = {0,0,0,0}; while (rect.isEmpty()) { rect.fLeft = rand.nextRangeF(0, MAX_SIZE); rect.fRight = rand.nextRangeF(0, MAX_SIZE); rect.fTop = rand.nextRangeF(0, MAX_SIZE); rect.fBottom = rand.nextRangeF(0, MAX_SIZE); rect.sort(); } return rect; }
// found and fixed for android: not initializing rect for string's of length 0 static void regression_measureText(skiatest::Reporter* reporter) { SkPaint paint; paint.setTextSize(SkFloatToScalar(12.0f)); SkRect r; r.setLTRB(SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN, SK_ScalarNaN); // test that the rect was reset paint.measureText("", 0, &r, SkFloatToScalar(1.0f)); REPORTER_ASSERT(reporter, r.isEmpty()); }
void LayerAndroid::clipInner(SkTDArray<SkRect>* region, const SkRect& local) const { SkRect localBounds; bounds(&localBounds); localBounds.intersect(local); if (localBounds.isEmpty()) return; if (m_recordingPicture && boundsIsUnique(*region, localBounds)) *region->append() = localBounds; for (int i = 0; i < countChildren(); i++) getChild(i)->clipInner(region, m_haveClip ? localBounds : local); }
void SkScan::FillRect(const SkRect& r, const SkRasterClip& clip, SkBlitter* blitter) { if (clip.isEmpty() || r.isEmpty()) { return; } if (clip.isBW()) { FillRect(r, &clip.bwRgn(), blitter); return; } SkAAClipBlitterWrapper wrapper(clip, blitter); FillRect(r, &wrapper.getRgn(), wrapper.getBlitter()); }
void ShaderProgram::drawQuadInternal(SkRect& geometry, GLint textureId, float opacity, GLint program, GLint projectionMatrixHandle, GLint texSampler, GLenum textureTarget, GLint position, GLint alpha, GLint texFilter, GLint contrast) { glUseProgram(program); if (!geometry.isEmpty()) setProjectionMatrix(geometry, projectionMatrixHandle); else { TransformationMatrix matrix; // Map x,y from (0,1) to (-1, 1) matrix.scale3d(2, 2, 1); matrix.translate3d(-0.5, -0.5, 0); GLfloat projectionMatrix[16]; GLUtils::toGLMatrix(projectionMatrix, matrix); glUniformMatrix4fv(projectionMatrixHandle, 1, GL_FALSE, projectionMatrix); } glActiveTexture(GL_TEXTURE0); glUniform1i(texSampler, 0); glBindTexture(textureTarget, textureId); glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, texFilter); glTexParameteri(textureTarget, GL_TEXTURE_MAG_FILTER, texFilter); glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindBuffer(GL_ARRAY_BUFFER, m_textureBuffer[0]); glEnableVertexAttribArray(position); glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 0, 0); glUniform1f(alpha, opacity); if (contrast != -1) glUniform1f(contrast, m_contrast); setBlendingState(opacity < 1.0); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); }
static void test_isRectFinite(skiatest::Reporter* reporter) { #ifdef SK_SCALAR_IS_FLOAT static const SkPoint gF0[] = { { 0, 0 }, { 1, 1 } }; static const SkPoint gF1[] = { { 0, 0 }, { 1, 1 }, { 99.234f, -42342 } }; static const SkPoint gI0[] = { { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarNaN, 3 }, { 2, 3 }, }; static const SkPoint gI1[] = { { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarNaN }, { 2, 3 }, }; static const SkPoint gI2[] = { { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarInfinity, 3 }, { 2, 3 }, }; static const SkPoint gI3[] = { { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarInfinity }, { 2, 3 }, }; static const struct { const SkPoint* fPts; size_t fCount; bool fIsFinite; } gSets[] = { { gF0, SK_ARRAY_COUNT(gF0), true }, { gF1, SK_ARRAY_COUNT(gF1), true }, { gI0, SK_ARRAY_COUNT(gI0), false }, { gI1, SK_ARRAY_COUNT(gI1), false }, { gI2, SK_ARRAY_COUNT(gI2), false }, { gI3, SK_ARRAY_COUNT(gI3), false }, }; for (size_t i = 0; i < SK_ARRAY_COUNT(gSets); ++i) { SkRect r; r.set(gSets[i].fPts, gSets[i].fCount); bool rectIsFinite = !r.isEmpty(); REPORTER_ASSERT(reporter, gSets[i].fIsFinite == rectIsFinite); } #endif }
DEF_TEST(CanvasState_test_saveLayer_clip, reporter) { const int WIDTH = 100; const int HEIGHT = 100; const int LAYER_WIDTH = 50; const int LAYER_HEIGHT = 50; SkBitmap bitmap; bitmap.allocN32Pixels(WIDTH, HEIGHT); SkCanvas canvas(bitmap); SkRect bounds = SkRect::MakeWH(SkIntToScalar(LAYER_WIDTH), SkIntToScalar(LAYER_HEIGHT)); canvas.clipRect(SkRect::MakeWH(SkIntToScalar(WIDTH), SkIntToScalar(HEIGHT))); // Check that saveLayer without the kClipToLayer_SaveFlag leaves the // clip stack unchanged. canvas.saveLayer(SkCanvas::SaveLayerRec(&bounds, nullptr, SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag)); SkRect clipStackBounds; SkClipStack::BoundsType boundsType; canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType); // The clip stack will return its bounds, or it may be "full" : i.e. empty + inside_out. // Either result is consistent with this test, since the canvas' size is WIDTH/HEIGHT if (SkClipStack::kInsideOut_BoundsType == boundsType) { REPORTER_ASSERT(reporter, clipStackBounds.isEmpty()); } else { REPORTER_ASSERT(reporter, clipStackBounds.width() == WIDTH); REPORTER_ASSERT(reporter, clipStackBounds.height() == HEIGHT); } canvas.restore(); // Check that saveLayer with the kClipToLayer_SaveFlag sets the clip // stack to the layer bounds. canvas.saveLayer(&bounds, nullptr); canvas.getClipStack()->getBounds(&clipStackBounds, &boundsType); REPORTER_ASSERT(reporter, clipStackBounds.width() == LAYER_WIDTH); REPORTER_ASSERT(reporter, clipStackBounds.height() == LAYER_HEIGHT); canvas.restore(); }
void SkDebugCanvas::drawTo(SkCanvas* canvas, int index) { SkASSERT(!fCommandVector.isEmpty()); SkASSERT(index < fCommandVector.count()); int saveCount = canvas->save(); SkRect windowRect = SkRect::MakeWH(SkIntToScalar(canvas->getBaseLayerSize().width()), SkIntToScalar(canvas->getBaseLayerSize().height())); bool pathOpsMode = getAllowSimplifyClip(); canvas->setAllowSimplifyClip(pathOpsMode); canvas->clear(SK_ColorTRANSPARENT); canvas->resetMatrix(); if (!windowRect.isEmpty()) { canvas->clipRect(windowRect, SkRegion::kReplace_Op); } this->applyUserTransform(canvas); if (fPaintFilterCanvas) { fPaintFilterCanvas->addCanvas(canvas); canvas = fPaintFilterCanvas.get(); } if (fMegaVizMode) { this->markActiveCommands(index); } for (int i = 0; i <= index; i++) { if (i == index && fFilter) { canvas->clear(0xAAFFFFFF); } if (fCommandVector[i]->isVisible()) { if (fMegaVizMode && fCommandVector[i]->active()) { // "active" commands execute their visualization behaviors: // All active saveLayers get replaced with saves so all draws go to the // visible canvas. // All active culls draw their cull box fCommandVector[i]->vizExecute(canvas); } else { fCommandVector[i]->setUserMatrix(fUserMatrix); fCommandVector[i]->execute(canvas); } } } if (fMegaVizMode) { canvas->save(); // nuke the CTM canvas->resetMatrix(); // turn off clipping if (!windowRect.isEmpty()) { SkRect r = windowRect; r.outset(SK_Scalar1, SK_Scalar1); canvas->clipRect(r, SkRegion::kReplace_Op); } // visualize existing clips SkDebugClipVisitor visitor(canvas); canvas->replayClips(&visitor); canvas->restore(); } if (pathOpsMode) { this->resetClipStackData(); const SkClipStack* clipStack = canvas->getClipStack(); SkClipStack::Iter iter(*clipStack, SkClipStack::Iter::kBottom_IterStart); const SkClipStack::Element* element; SkPath devPath; while ((element = iter.next())) { SkClipStack::Element::Type type = element->getType(); SkPath operand; if (type != SkClipStack::Element::kEmpty_Type) { element->asPath(&operand); } SkRegion::Op elementOp = element->getOp(); this->addClipStackData(devPath, operand, elementOp); if (elementOp == SkRegion::kReplace_Op) { devPath = operand; } else { Op(devPath, operand, (SkPathOp) elementOp, &devPath); } } this->lastClipStackData(devPath); } fMatrix = canvas->getTotalMatrix(); if (!canvas->getClipDeviceBounds(&fClip)) { fClip.setEmpty(); } canvas->restoreToCount(saveCount); if (fPaintFilterCanvas) { fPaintFilterCanvas->removeAll(); } }
SkPDFImageShader::SkPDFImageShader(SkPDFShader::State* state) : fState(state) { fState.get()->fImage.lockPixels(); SkMatrix finalMatrix = fState.get()->fCanvasTransform; finalMatrix.preConcat(fState.get()->fShaderTransform); SkRect surfaceBBox; surfaceBBox.set(fState.get()->fBBox); transformBBox(finalMatrix, &surfaceBBox); SkMatrix unflip; unflip.setTranslate(0, SkScalarRound(surfaceBBox.height())); unflip.preScale(SK_Scalar1, -SK_Scalar1); SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()), SkScalarRound(surfaceBBox.height())); SkPDFDevice pattern(size, size, unflip); SkCanvas canvas(&pattern); canvas.translate(-surfaceBBox.fLeft, -surfaceBBox.fTop); finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop); const SkBitmap* image = &fState.get()->fImage; int width = image->width(); int height = image->height(); SkShader::TileMode tileModes[2]; tileModes[0] = fState.get()->fImageTileModes[0]; tileModes[1] = fState.get()->fImageTileModes[1]; canvas.drawBitmap(*image, 0, 0); SkRect patternBBox = SkRect::MakeXYWH(-surfaceBBox.fLeft, -surfaceBBox.fTop, width, height); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(*image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(*image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); canvas.drawBitmapMatrix(*image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(surfaceBBox.fLeft, surfaceBBox.fTop, 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(width - 1, height - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0, surfaceBBox.fBottom); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, height - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height); if (surfaceBBox.fLeft < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-surfaceBBox.fLeft, 1); leftMatrix.postTranslate(surfaceBBox.fLeft, 0); canvas.drawBitmapMatrix(left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(left, leftMatrix); } patternBBox.fLeft = 0; } if (surfaceBBox.fRight > width) { SkBitmap right; subset.offset(width - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(surfaceBBox.fRight - width, 1); rightMatrix.postTranslate(width, 0); canvas.drawBitmapMatrix(right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); canvas.drawBitmapMatrix(right, rightMatrix); } patternBBox.fRight = surfaceBBox.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1); if (surfaceBBox.fTop < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -surfaceBBox.fTop); topMatrix.postTranslate(0, surfaceBBox.fTop); canvas.drawBitmapMatrix(top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(top, topMatrix); } patternBBox.fTop = 0; } if (surfaceBBox.fBottom > height) { SkBitmap bottom; subset.offset(0, height - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, surfaceBBox.fBottom - height); bottomMatrix.postTranslate(0, height); canvas.drawBitmapMatrix(bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); canvas.drawBitmapMatrix(bottom, bottomMatrix); } patternBBox.fBottom = surfaceBBox.height(); } } SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray; patternBBoxArray->unref(); // SkRefPtr and new both took a reference. patternBBoxArray->reserve(4); patternBBoxArray->appendScalar(patternBBox.fLeft); patternBBoxArray->appendScalar(patternBBox.fTop); patternBBoxArray->appendScalar(patternBBox.fRight); patternBBoxArray->appendScalar(patternBBox.fBottom); // Put the canvas into the pattern stream (fContent). SkRefPtr<SkStream> content = pattern.content(); content->unref(); // SkRefPtr and content() both took a reference. pattern.getResources(&fResources); setData(content.get()); insertName("Type", "Pattern"); insertInt("PatternType", 1); insertInt("PaintType", 1); insertInt("TilingType", 1); insert("BBox", patternBBoxArray.get()); insertScalar("XStep", patternBBox.width()); insertScalar("YStep", patternBBox.height()); insert("Resources", pattern.getResourceDict()); insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref(); fState.get()->fImage.unlockPixels(); }
SkPDFImageShader* SkPDFImageShader::Create( SkPDFCanon* canon, SkScalar dpi, SkAutoTDelete<SkPDFShader::State>* autoState) { const SkPDFShader::State& state = **autoState; state.fImage.lockPixels(); // The image shader pattern cell will be drawn into a separate device // in pattern cell space (no scaling on the bitmap, though there may be // translations so that all content is in the device, coordinates > 0). // Map clip bounds to shader space to ensure the device is large enough // to handle fake clamping. SkMatrix finalMatrix = state.fCanvasTransform; finalMatrix.preConcat(state.fShaderTransform); SkRect deviceBounds; deviceBounds.set(state.fBBox); if (!inverse_transform_bbox(finalMatrix, &deviceBounds)) { return NULL; } const SkBitmap* image = &state.fImage; SkRect bitmapBounds; image->getBounds(&bitmapBounds); // For tiling modes, the bounds should be extended to include the bitmap, // otherwise the bitmap gets clipped out and the shader is empty and awful. // For clamp modes, we're only interested in the clip region, whether // or not the main bitmap is in it. SkShader::TileMode tileModes[2]; tileModes[0] = state.fImageTileModes[0]; tileModes[1] = state.fImageTileModes[1]; if (tileModes[0] != SkShader::kClamp_TileMode || tileModes[1] != SkShader::kClamp_TileMode) { deviceBounds.join(bitmapBounds); } SkISize size = SkISize::Make(SkScalarRoundToInt(deviceBounds.width()), SkScalarRoundToInt(deviceBounds.height())); SkAutoTUnref<SkPDFDevice> patternDevice( SkPDFDevice::CreateUnflipped(size, dpi, canon)); SkCanvas canvas(patternDevice.get()); SkRect patternBBox; image->getBounds(&patternBBox); // Translate the canvas so that the bitmap origin is at (0, 0). canvas.translate(-deviceBounds.left(), -deviceBounds.top()); patternBBox.offset(-deviceBounds.left(), -deviceBounds.top()); // Undo the translation in the final matrix finalMatrix.preTranslate(deviceBounds.left(), deviceBounds.top()); // If the bitmap is out of bounds (i.e. clamp mode where we only see the // stretched sides), canvas will clip this out and the extraneous data // won't be saved to the PDF. canvas.drawBitmap(*image, 0, 0); SkScalar width = SkIntToScalar(image->width()); SkScalar height = SkIntToScalar(image->height()); // Tiling is implied. First we handle mirroring. if (tileModes[0] == SkShader::kMirror_TileMode) { SkMatrix xMirror; xMirror.setScale(-1, 1); xMirror.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, *image, xMirror); patternBBox.fRight += width; } if (tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix yMirror; yMirror.setScale(SK_Scalar1, -SK_Scalar1); yMirror.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, *image, yMirror); patternBBox.fBottom += height; } if (tileModes[0] == SkShader::kMirror_TileMode && tileModes[1] == SkShader::kMirror_TileMode) { SkMatrix mirror; mirror.setScale(-1, -1); mirror.postTranslate(2 * width, 2 * height); drawBitmapMatrix(&canvas, *image, mirror); } // Then handle Clamping, which requires expanding the pattern canvas to // cover the entire surfaceBBox. // If both x and y are in clamp mode, we start by filling in the corners. // (Which are just a rectangles of the corner colors.) if (tileModes[0] == SkShader::kClamp_TileMode && tileModes[1] == SkShader::kClamp_TileMode) { SkPaint paint; SkRect rect; rect = SkRect::MakeLTRB(deviceBounds.left(), deviceBounds.top(), 0, 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, deviceBounds.top(), deviceBounds.right(), 0); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, 0)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(width, height, deviceBounds.right(), deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(image->width() - 1, image->height() - 1)); canvas.drawRect(rect, paint); } rect = SkRect::MakeLTRB(deviceBounds.left(), height, 0, deviceBounds.bottom()); if (!rect.isEmpty()) { paint.setColor(image->getColor(0, image->height() - 1)); canvas.drawRect(rect, paint); } } // Then expand the left, right, top, then bottom. if (tileModes[0] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height()); if (deviceBounds.left() < 0) { SkBitmap left; SkAssertResult(image->extractSubset(&left, subset)); SkMatrix leftMatrix; leftMatrix.setScale(-deviceBounds.left(), 1); leftMatrix.postTranslate(deviceBounds.left(), 0); drawBitmapMatrix(&canvas, left, leftMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { leftMatrix.postScale(SK_Scalar1, -SK_Scalar1); leftMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, left, leftMatrix); } patternBBox.fLeft = 0; } if (deviceBounds.right() > width) { SkBitmap right; subset.offset(image->width() - 1, 0); SkAssertResult(image->extractSubset(&right, subset)); SkMatrix rightMatrix; rightMatrix.setScale(deviceBounds.right() - width, 1); rightMatrix.postTranslate(width, 0); drawBitmapMatrix(&canvas, right, rightMatrix); if (tileModes[1] == SkShader::kMirror_TileMode) { rightMatrix.postScale(SK_Scalar1, -SK_Scalar1); rightMatrix.postTranslate(0, 2 * height); drawBitmapMatrix(&canvas, right, rightMatrix); } patternBBox.fRight = deviceBounds.width(); } } if (tileModes[1] == SkShader::kClamp_TileMode) { SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1); if (deviceBounds.top() < 0) { SkBitmap top; SkAssertResult(image->extractSubset(&top, subset)); SkMatrix topMatrix; topMatrix.setScale(SK_Scalar1, -deviceBounds.top()); topMatrix.postTranslate(0, deviceBounds.top()); drawBitmapMatrix(&canvas, top, topMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { topMatrix.postScale(-1, 1); topMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, top, topMatrix); } patternBBox.fTop = 0; } if (deviceBounds.bottom() > height) { SkBitmap bottom; subset.offset(0, image->height() - 1); SkAssertResult(image->extractSubset(&bottom, subset)); SkMatrix bottomMatrix; bottomMatrix.setScale(SK_Scalar1, deviceBounds.bottom() - height); bottomMatrix.postTranslate(0, height); drawBitmapMatrix(&canvas, bottom, bottomMatrix); if (tileModes[0] == SkShader::kMirror_TileMode) { bottomMatrix.postScale(-1, 1); bottomMatrix.postTranslate(2 * width, 0); drawBitmapMatrix(&canvas, bottom, bottomMatrix); } patternBBox.fBottom = deviceBounds.height(); } } // Put the canvas into the pattern stream (fContent). SkAutoTDelete<SkStreamAsset> content(patternDevice->content()); SkPDFImageShader* imageShader = SkNEW_ARGS(SkPDFImageShader, (autoState->detach())); imageShader->setData(content.get()); SkAutoTUnref<SkPDFDict> resourceDict( patternDevice->createResourceDict()); populate_tiling_pattern_dict(imageShader, patternBBox, resourceDict.get(), finalMatrix); imageShader->fShaderState->fImage.unlockPixels(); canon->addImageShader(imageShader); return imageShader; }
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "paintSkBitmap"); #endif SkPaint paint; paint.setXfermodeMode(compOp); paint.setAlpha(platformContext->getNormalizedAlpha()); paint.setLooper(platformContext->getDrawLooper()); // only antialias if we're rotated or skewed paint.setAntiAlias(hasNon90rotation(platformContext)); SkCanvas* canvas = platformContext->canvas(); ResamplingMode resampling; if (platformContext->isAccelerated()) resampling = RESAMPLE_LINEAR; else if (platformContext->printing()) resampling = RESAMPLE_NONE; else { // Take into account scale applied to the canvas when computing sampling mode (e.g. CSS scale or page scale). SkRect destRectTarget = destRect; if (!(canvas->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) canvas->getTotalMatrix().mapRect(&destRectTarget, destRect); resampling = computeResamplingMode(canvas->getTotalMatrix(), bitmap, SkScalarToFloat(srcRect.width()), SkScalarToFloat(srcRect.height()), SkScalarToFloat(destRectTarget.width()), SkScalarToFloat(destRectTarget.height())); } if (resampling == RESAMPLE_NONE) { // FIXME: This is to not break tests (it results in the filter bitmap flag // being set to true). We need to decide if we respect RESAMPLE_NONE // being returned from computeResamplingMode. resampling = RESAMPLE_LINEAR; } resampling = limitResamplingMode(platformContext, resampling); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); if (resampling == RESAMPLE_AWESOME) drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); else { // No resampling necessary, we can just draw the bitmap. We want to // filter it if we decided to do linear interpolation above, or if there // is something interesting going on with the matrix (like a rotation). // Note: for serialization, we will want to subset the bitmap first so // we don't send extra pixels. SkIRect enclosingSrcRect; SkRect enclosingDestRect; SkISize bitmapSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height()); bool needsClipping = computeBitmapDrawRects(bitmapSize, srcRect, destRect, &enclosingSrcRect, &enclosingDestRect); if (enclosingSrcRect.isEmpty() || enclosingDestRect.isEmpty()) return; // If destination is enlarged because source rectangle didn't align to // integer boundaries then we draw a slightly larger rectangle and clip // to the original destination rectangle. // See http://crbug.com/145540. if (needsClipping) { platformContext->save(); platformContext->canvas()->clipRect(destRect); } canvas->drawBitmapRect(bitmap.bitmap(), &enclosingSrcRect, enclosingDestRect, &paint); if (needsClipping) platformContext->restore(); } platformContext->didDrawRect(destRect, paint, &bitmap.bitmap()); }