void draw(SkCanvas* canvas, const SkRect& rect, const SkSize& deviceSize, SkPaint::FilterLevel filterLevel, SkImageFilter* input = NULL) { SkRect dstRect; canvas->getTotalMatrix().mapRect(&dstRect, rect); canvas->save(); SkScalar deviceScaleX = SkScalarDiv(deviceSize.width(), dstRect.width()); SkScalar deviceScaleY = SkScalarDiv(deviceSize.height(), dstRect.height()); canvas->translate(rect.x(), rect.y()); canvas->scale(deviceScaleX, deviceScaleY); canvas->translate(-rect.x(), -rect.y()); SkMatrix matrix; matrix.setScale(SkScalarInvert(deviceScaleX), SkScalarInvert(deviceScaleY)); SkAutoTUnref<SkImageFilter> imageFilter( SkMatrixImageFilter::Create(matrix, filterLevel, input)); SkPaint filteredPaint; filteredPaint.setImageFilter(imageFilter.get()); canvas->saveLayer(&rect, &filteredPaint); SkPaint paint; paint.setColor(0xFF00FF00); SkRect ovalRect = rect; ovalRect.inset(SkIntToScalar(4), SkIntToScalar(4)); canvas->drawOval(ovalRect, paint); canvas->restore(); // for saveLayer canvas->restore(); }
void SkDeferredCanvas::flush_translate(SkScalar* x, SkScalar* y, const SkRect& bounds, const SkPaint* paint) { SkRect tmp = bounds; this->flush_check(&tmp, paint, kNoClip_Flag | kNoScale_Flag); *x += tmp.x() - bounds.x(); *y += tmp.y() - bounds.y(); }
void SkMatrixImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const { SkRect bounds = src; if (getInput(0)) { getInput(0)->computeFastBounds(src, &bounds); } SkMatrix matrix; matrix.setTranslate(-bounds.x(), -bounds.y()); matrix.postConcat(fTransform); matrix.postTranslate(bounds.x(), bounds.y()); matrix.mapRect(dst, bounds); }
void SkSVGDevice::AutoElement::addRectAttributes(const SkRect& rect) { // x, y default to 0 if (rect.x() != 0) { this->addAttribute("x", rect.x()); } if (rect.y() != 0) { this->addAttribute("y", rect.y()); } this->addAttribute("width", rect.width()); this->addAttribute("height", rect.height()); }
void SkDeferredCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { SkRect modRect = rrect.getBounds(); this->flush_check(&modRect, &paint, kNoClip_Flag); fCanvas->drawRRect(make_offset(rrect, modRect.x() - rrect.getBounds().x(), modRect.y() - rrect.getBounds().y()), paint); }
/* * Test the 3 annotation types by recording them into a picture, serializing, and then playing * them back into another canvas. */ DEF_TEST(Annotations, reporter) { SkPictureRecorder recorder; SkCanvas* recordingCanvas = recorder.beginRecording(SkRect::MakeWH(100, 100)); const char* str0 = "rect-with-url"; const SkRect r0 = SkRect::MakeWH(10, 10); sk_sp<SkData> d0(SkData::MakeWithCString(str0)); SkAnnotateRectWithURL(recordingCanvas, r0, d0.get()); const char* str1 = "named-destination"; const SkRect r1 = SkRect::MakeXYWH(5, 5, 0, 0); // collapsed to a point sk_sp<SkData> d1(SkData::MakeWithCString(str1)); SkAnnotateNamedDestination(recordingCanvas, {r1.x(), r1.y()}, d1.get()); const char* str2 = "link-to-destination"; const SkRect r2 = SkRect::MakeXYWH(20, 20, 5, 6); sk_sp<SkData> d2(SkData::MakeWithCString(str2)); SkAnnotateLinkToDestination(recordingCanvas, r2, d2.get()); const AnnotationRec recs[] = { { r0, SkAnnotationKeys::URL_Key(), std::move(d0) }, { r1, SkAnnotationKeys::Define_Named_Dest_Key(), std::move(d1) }, { r2, SkAnnotationKeys::Link_Named_Dest_Key(), std::move(d2) }, }; sk_sp<SkPicture> pict0(recorder.finishRecordingAsPicture()); sk_sp<SkPicture> pict1(copy_picture_via_serialization(pict0.get())); TestAnnotationCanvas canvas(reporter, recs, SK_ARRAY_COUNT(recs)); canvas.drawPicture(pict1); }
static void convolve_gaussian_2d(GrDrawContext* drawContext, const GrClip& clip, const SkRect& srcRect, GrTexture* texture, int radiusX, int radiusY, SkScalar sigmaX, SkScalar sigmaY, const SkRect* srcBounds) { SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height()); SkMatrix localMatrix = SkMatrix::MakeTrans(srcRect.x(), srcRect.y()); SkISize size = SkISize::Make(2 * radiusX + 1, 2 * radiusY + 1); SkIPoint kernelOffset = SkIPoint::Make(radiusX, radiusY); GrPaint paint; SkIRect bounds; if (srcBounds) { srcBounds->roundOut(&bounds); } else { bounds.setEmpty(); } SkAutoTUnref<GrFragmentProcessor> conv(GrMatrixConvolutionEffect::CreateGaussian( texture, bounds, size, 1.0, 0.0, kernelOffset, srcBounds ? GrTextureDomain::kDecal_Mode : GrTextureDomain::kIgnore_Mode, true, sigmaX, sigmaY)); paint.addColorFragmentProcessor(conv); paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode); drawContext->fillRectWithLocalMatrix(clip, paint, SkMatrix::I(), dstRect, localMatrix); }
static void imagescaleproc(SkCanvas* canvas, SkImage* image, const SkBitmap&, const SkIRect& srcIR, const SkRect& dstR) { const int newW = SkScalarRoundToInt(dstR.width()); const int newH = SkScalarRoundToInt(dstR.height()); SkAutoTUnref<SkImage> newImage(image->newImage(newW, newH, &srcIR)); #ifdef SK_DEBUG const SkIRect baseR = SkIRect::MakeWH(image->width(), image->height()); const bool containsSubset = baseR.contains(srcIR); #endif if (newImage) { SkASSERT(containsSubset); canvas->drawImage(newImage, dstR.x(), dstR.y()); } else { // newImage() does not support subsets that are not contained by the original // but drawImageRect does, so we just draw an X in its place to indicate that we are // deliberately not drawing here. SkASSERT(!containsSubset); SkPaint paint; paint.setStyle(SkPaint::kStroke_Style); paint.setStrokeWidth(4); canvas->drawLine(4, 4, newW - 4.0f, newH - 4.0f, paint); canvas->drawLine(4, newH - 4.0f, newW - 4.0f, 4, paint); } }
SkMagnifierImageFilter::SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, sk_sp<SkImageFilter> input) : INHERITED(&input, 1, nullptr) , fSrcRect(srcRect) , fInset(inset) { SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0); }
SkImage* SkImage_Base::onNewImage(int newWidth, int newHeight, const SkIRect* subset, SkFilterQuality quality) const { const bool opaque = this->isOpaque(); const SkImageInfo info = SkImageInfo::Make(newWidth, newHeight, kN32_SkColorType, opaque ? kOpaque_SkAlphaType : kPremul_SkAlphaType); SkAutoTUnref<SkSurface> surface(this->newSurface(info, nullptr)); if (!surface.get()) { return nullptr; } SkRect src; if (subset) { src.set(*subset); } else { src = SkRect::MakeIWH(this->width(), this->height()); } surface->getCanvas()->scale(newWidth / src.width(), newHeight / src.height()); surface->getCanvas()->translate(-src.x(), -src.y()); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setFilterQuality(quality); surface->getCanvas()->drawImage(this, 0, 0, &paint); return surface->newImageSnapshot(); }
sk_sp<SkImageFilter> SkTileImageFilter::Make(const SkRect& srcRect, const SkRect& dstRect, sk_sp<SkImageFilter> input) { if (!SkIsValidRect(srcRect) || !SkIsValidRect(dstRect)) { return nullptr; } if (srcRect.width() == dstRect.width() && srcRect.height() == dstRect.height()) { SkRect ir = dstRect; if (!ir.intersect(srcRect)) { return input; } CropRect cropRect(ir); return SkOffsetImageFilter::Make(dstRect.x() - srcRect.x(), dstRect.y() - srcRect.y(), std::move(input), &cropRect); } return sk_sp<SkImageFilter>(new SkTileImageFilter(srcRect, dstRect, std::move(input))); }
// Return true if the rectangle is aligned to integer boundaries. // See comments for computeBitmapDrawRects() for how this is used. static bool areBoundariesIntegerAligned(const SkRect& rect) { // Value is 1.19209e-007. This is the tolerance threshold. const float epsilon = std::numeric_limits<float>::epsilon(); SkIRect roundedRect = roundedIntRect(rect); return fabs(rect.x() - roundedRect.x()) < epsilon && fabs(rect.y() - roundedRect.y()) < epsilon && fabs(rect.right() - roundedRect.right()) < epsilon && fabs(rect.bottom() - roundedRect.bottom()) < epsilon; }
void drawRect(const SkRect& r, SkColor c) override { CGContextRef cg = (CGContextRef)fCanvas->accessTopRasterHandle(); CGColorRef color = CGColorCreateGenericRGB(SkColorGetR(c)/255.f, SkColorGetG(c)/255.f, SkColorGetB(c)/255.f, SkColorGetA(c)/255.f); CGContextSetFillColorWithColor(cg, color); CGContextFillRect(cg, CGRectMake(r.x(), r.y(), r.width(), r.height())); }
void SkDeferredCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, const SkPaint* paint) { const SkScalar w = SkIntToScalar(bitmap.width()); const SkScalar h = SkIntToScalar(bitmap.height()); SkRect bounds = SkRect::MakeXYWH(x, y, w, h); this->flush_check(&bounds, paint, kNoClip_Flag); if (bounds.width() == w && bounds.height() == h) { fCanvas->drawBitmap(bitmap, bounds.x(), bounds.y(), paint); } else { fCanvas->drawBitmapRect(bitmap, bounds, paint); } }
void SkDeferredCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) { const SkScalar w = SkIntToScalar(image->width()); const SkScalar h = SkIntToScalar(image->height()); SkRect bounds = SkRect::MakeXYWH(x, y, w, h); this->flush_check(&bounds, paint, kNoClip_Flag); if (bounds.width() == w && bounds.height() == h) { fCanvas->drawImage(image, bounds.x(), bounds.y(), paint); } else { fCanvas->drawImageRect(image, bounds, paint); } }
static SkCanvas* trim(SkCanvas* canvas, SkScalar width, SkScalar height, const SkRect* content) { if (content && canvas) { SkRect inner = *content; if (!inner.intersect({0, 0, width, height})) { return nullptr; } canvas->clipRect(inner); canvas->translate(inner.x(), inner.y()); } return canvas; }
void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override { if (rect.width() == 0 && rect.height() == 0) { SkPoint point = getTotalMatrix().mapXY(rect.x(), rect.y()); Operation operation = { DrawPoint, SkRect::MakeXYWH(point.x(), point.y(), 0, 0) }; m_recordedOperations.append(operation); } else { Operation operation = { DrawRect, rect }; getTotalMatrix().mapRect(&operation.rect); m_recordedOperations.append(operation); } }
bool DrawingDisplayItem::equals(const DisplayItem& other) const { if (!DisplayItem::equals(other)) return false; RefPtr<const SkPicture> picture = this->picture(); RefPtr<const SkPicture> otherPicture = static_cast<const DrawingDisplayItem&>(other).picture(); if (!picture && !otherPicture) return true; if (!picture || !otherPicture) return false; switch (getUnderInvalidationCheckingMode()) { case DrawingDisplayItem::CheckPicture: { if (picture->approximateOpCount() != otherPicture->approximateOpCount()) return false; SkDynamicMemoryWStream pictureSerialized; picture->serialize(&pictureSerialized); SkDynamicMemoryWStream otherPictureSerialized; otherPicture->serialize(&otherPictureSerialized); if (pictureSerialized.bytesWritten() != otherPictureSerialized.bytesWritten()) return false; RefPtr<SkData> oldData = adoptRef(otherPictureSerialized.copyToData()); RefPtr<SkData> newData = adoptRef(pictureSerialized.copyToData()); return oldData->equals(newData.get()); } case DrawingDisplayItem::CheckBitmap: { SkRect rect = picture->cullRect(); if (rect != otherPicture->cullRect()) return false; SkBitmap bitmap; bitmap.allocPixels(SkImageInfo::MakeN32Premul(rect.width(), rect.height())); SkCanvas canvas(bitmap); canvas.translate(-rect.x(), -rect.y()); canvas.drawPicture(otherPicture.get()); SkPaint diffPaint; diffPaint.setXfermodeMode(SkXfermode::kDifference_Mode); canvas.drawPicture(picture.get(), nullptr, &diffPaint); return bitmapIsAllZero(bitmap); } default: ASSERT_NOT_REACHED(); } return false; }
// Draws the given bitmap to the given canvas. The subset of the source bitmap // identified by src_rect is drawn to the given destination rect. The bitmap // will be resampled to resample_width * resample_height (this is the size of // the whole image, not the subset). See shouldResampleBitmap for more. // // This does a lot of computation to resample only the portion of the bitmap // that will only be drawn. This is critical for performance since when we are // scrolling, for example, we are only drawing a small strip of the image. // Resampling the whole image every time is very slow, so this speeds up things // dramatically. // // Note: this code is only used when the canvas transformation is limited to // scaling or translation. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "drawResampledBitmap"); #endif // Apply forward transform to destRect to estimate required size of // re-sampled bitmap, and use only in calls required to resize, or that // check for the required size. SkRect destRectTransformed; canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect); SkIRect destRectTransformedRounded; destRectTransformed.round(&destRectTransformedRounded); // Compute the visible portion of our rect. SkRect destRectVisibleSubset; ClipRectToCanvas(canvas, destRect, &destRectVisibleSubset); // ClipRectToCanvas often overshoots, resulting in a larger region than our // original destRect. Intersecting gets us back inside. if (!destRectVisibleSubset.intersect(destRect)) return; // Nothing visible in destRect. // Compute the image-relative (bitmap space) subset. SkRect destBitmapSubset = destRectVisibleSubset; destBitmapSubset.offset(-destRect.x(), -destRect.y()); // Scale the subset to the requested size. The canvas scale can be negative, // but the resampling code is only interested in positive scaling in its normal space. SkMatrix subsetTransform; subsetTransform.setScale(SkScalarAbs(canvas.getTotalMatrix().getScaleX()), SkScalarAbs(canvas.getTotalMatrix().getScaleY())); SkRect destBitmapSubsetTransformed; subsetTransform.mapRect(&destBitmapSubsetTransformed, destBitmapSubset); SkIRect destBitmapSubsetTransformedRounded; destBitmapSubsetTransformed.round(&destBitmapSubsetTransformedRounded); // Transforms above plus rounding may cause destBitmapSubsetTransformedRounded // to go outside the image, so need to clip to avoid problems. if (!destBitmapSubsetTransformedRounded.intersect( 0, 0, destRectTransformedRounded.width(), destRectTransformedRounded.height())) return; // Image is not visible. SkBitmap resampled = bitmap.resizedBitmap(srcIRect, destRectTransformedRounded.width(), destRectTransformedRounded.height(), destBitmapSubsetTransformedRounded); canvas.drawBitmapRect(resampled, 0, destRectVisibleSubset, &paint); }
bool SkMatrixImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) { return false; } SkRect dstRect; SkIRect srcBounds, dstBounds; src.getBounds(&srcBounds); srcBounds.offset(srcOffset); SkRect srcRect = SkRect::Make(srcBounds); SkMatrix matrix; if (!ctx.ctm().invert(&matrix)) { return false; } matrix.postConcat(fTransform); matrix.postConcat(ctx.ctm()); matrix.mapRect(&dstRect, srcRect); dstRect.roundOut(&dstBounds); SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height())); if (NULL == device.get()) { return false; } SkCanvas canvas(device.get()); canvas.translate(-SkIntToScalar(dstBounds.x()), -SkIntToScalar(dstBounds.y())); canvas.concat(matrix); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setFilterLevel(fFilterLevel); canvas.drawBitmap(src, srcRect.x(), srcRect.y(), &paint); *result = device.get()->accessBitmap(false); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; return true; }
void SkDeferredCanvas::flush_translate(SkScalar* x, SkScalar* y, const SkPaint& paint) { SkRect tmp = SkRect::MakeXYWH(*x, *y, 1, 1); this->flush_check(&tmp, &paint, kNoClip_Flag | kNoCull_Flag | kNoScale_Flag); *x = tmp.x(); *y = tmp.y(); }
SkPDFShader::State::State(const SkShader& shader, const SkMatrix& canvasTransform, const SkIRect& bbox, SkScalar rasterScale) : fCanvasTransform(canvasTransform), fBBox(bbox), fPixelGeneration(0) { fInfo.fColorCount = 0; fInfo.fColors = NULL; fInfo.fColorOffsets = NULL; fShaderTransform = shader.getLocalMatrix(); fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode; fType = shader.asAGradient(&fInfo); if (fType == SkShader::kNone_GradientType) { SkShader::BitmapType bitmapType; SkMatrix matrix; bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes); if (bitmapType != SkShader::kDefault_BitmapType) { // Generic fallback for unsupported shaders: // * allocate a bbox-sized bitmap // * shade the whole area // * use the result as a bitmap shader // bbox is in device space. While that's exactly what we want for sizing our bitmap, // we need to map it into shader space for adjustments (to match // SkPDFImageShader::Create's behavior). SkRect shaderRect = SkRect::Make(bbox); if (!inverse_transform_bbox(canvasTransform, &shaderRect)) { fImage.reset(); return; } // Clamp the bitmap size to about 1M pixels static const SkScalar kMaxBitmapArea = 1024 * 1024; SkScalar bitmapArea = rasterScale * bbox.width() * rasterScale * bbox.height(); if (bitmapArea > kMaxBitmapArea) { rasterScale *= SkScalarSqrt(SkScalarDiv(kMaxBitmapArea, bitmapArea)); } SkISize size = SkISize::Make(SkScalarRoundToInt(rasterScale * bbox.width()), SkScalarRoundToInt(rasterScale * bbox.height())); SkSize scale = SkSize::Make(SkIntToScalar(size.width()) / shaderRect.width(), SkIntToScalar(size.height()) / shaderRect.height()); fImage.allocN32Pixels(size.width(), size.height()); fImage.eraseColor(SK_ColorTRANSPARENT); SkPaint p; p.setShader(const_cast<SkShader*>(&shader)); SkCanvas canvas(fImage); canvas.scale(scale.width(), scale.height()); canvas.translate(-shaderRect.x(), -shaderRect.y()); canvas.drawPaint(p); fShaderTransform.setTranslate(shaderRect.x(), shaderRect.y()); fShaderTransform.preScale(1 / scale.width(), 1 / scale.height()); } else { SkASSERT(matrix.isIdentity()); } fPixelGeneration = fImage.getGenerationID(); } else { AllocateGradientInfoStorage(); shader.asAGradient(&fInfo); } }
SkMagnifierImageFilter::SkMagnifierImageFilter(const SkRect& srcRect, SkScalar inset, SkImageFilter* input) : INHERITED(1, &input), fSrcRect(srcRect), fInset(inset) { SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0); }
void DisplayItemList::checkCachedDisplayItemIsUnchanged(const DisplayItem& displayItem, DisplayItemIndicesByClientMap& displayItemIndicesByClient) { ASSERT(RuntimeEnabledFeatures::slimmingPaintUnderInvalidationCheckingEnabled()); if (!displayItem.isDrawing() || displayItem.skippedCache() || !clientCacheIsValid(displayItem.client())) return; // If checking under-invalidation, we always generate new display item even if the client is not invalidated. // Checks if the new picture is the same as the cached old picture. If the new picture is different but // the client is not invalidated, issue error about under-invalidation. size_t index = findMatchingItemFromIndex(displayItem.nonCachedId(), displayItemIndicesByClient, m_currentDisplayItems); if (index == kNotFound) { showUnderInvalidationError("ERROR: under-invalidation: no cached display item", displayItem); ASSERT_NOT_REACHED(); return; } DisplayItems::iterator foundItem = m_currentDisplayItems.begin() + index; RefPtr<const SkPicture> newPicture = static_cast<const DrawingDisplayItem&>(displayItem).picture(); RefPtr<const SkPicture> oldPicture = static_cast<const DrawingDisplayItem&>(*foundItem).picture(); // Invalidate the display item so that we can check if there are any remaining cached display items after merging. foundItem->clearClientForUnderInvalidationChecking(); if (!newPicture && !oldPicture) return; if (newPicture && oldPicture) { switch (static_cast<const DrawingDisplayItem&>(displayItem).underInvalidationCheckingMode()) { case DrawingDisplayItem::CheckPicture: if (newPicture->approximateOpCount() == oldPicture->approximateOpCount()) { SkDynamicMemoryWStream newPictureSerialized; newPicture->serialize(&newPictureSerialized); SkDynamicMemoryWStream oldPictureSerialized; oldPicture->serialize(&oldPictureSerialized); if (newPictureSerialized.bytesWritten() == oldPictureSerialized.bytesWritten()) { RefPtr<SkData> oldData = adoptRef(oldPictureSerialized.copyToData()); RefPtr<SkData> newData = adoptRef(newPictureSerialized.copyToData()); if (oldData->equals(newData.get())) return; } } break; case DrawingDisplayItem::CheckBitmap: if (newPicture->cullRect() == oldPicture->cullRect()) { SkBitmap bitmap; SkRect rect = newPicture->cullRect(); bitmap.allocPixels(SkImageInfo::MakeN32Premul(rect.width(), rect.height())); SkCanvas canvas(bitmap); canvas.translate(-rect.x(), -rect.y()); canvas.drawPicture(oldPicture.get()); SkPaint diffPaint; diffPaint.setXfermodeMode(SkXfermode::kDifference_Mode); canvas.drawPicture(newPicture.get(), nullptr, &diffPaint); if (bitmapIsAllZero(bitmap)) // Contents are the same. return; } default: ASSERT_NOT_REACHED(); } } showUnderInvalidationError("ERROR: under-invalidation: display item changed", displayItem); #ifndef NDEBUG String oldPictureDebugString = oldPicture ? pictureAsDebugString(oldPicture.get()) : "None"; String newPictureDebugString = newPicture ? pictureAsDebugString(newPicture.get()) : "None"; WTFLogAlways("old picture:\n%s\n", oldPictureDebugString.utf8().data()); WTFLogAlways("new picture:\n%s\n", newPictureDebugString.utf8().data()); #endif // NDEBUG ASSERT_NOT_REACHED(); }