virtual void onDrawContent(SkCanvas* canvas) { SkBitmap bitmap; create_bitmap(&bitmap); int x = bitmap.width() / 2; int y = bitmap.height() / 2; SkBitmap subset; bitmap.extractSubset(&subset, SkIRect::MakeXYWH(x, y, x, y)); canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); canvas->drawBitmap(bitmap, 0, 0); canvas->drawBitmap(subset, 0, 0); // Now do the same but with a device bitmap as source image SkRefPtr<SkDevice> primaryDevice(canvas->getDevice()); SkRefPtr<SkDevice> secondDevice(canvas->createCompatibleDevice( SkBitmap::kARGB_8888_Config, bitmap.width(), bitmap.height(), true)); secondDevice->unref(); SkCanvas secondCanvas(secondDevice.get()); secondCanvas.writePixels(bitmap, 0, 0); SkBitmap deviceBitmap = secondDevice->accessBitmap(false); SkBitmap deviceSubset; deviceBitmap.extractSubset(&deviceSubset, SkIRect::MakeXYWH(x, y, x, y)); canvas->translate(SkIntToScalar(120), SkIntToScalar(0)); canvas->drawBitmap(deviceBitmap, 0, 0); canvas->drawBitmap(deviceSubset, 0, 0); }
// Tests that SkNewImageFromBitmap obeys pixelref origin. DEF_TEST(SkImageFromBitmap_extractSubset, reporter) { SkAutoTUnref<SkImage> image; { SkBitmap srcBitmap; srcBitmap.allocN32Pixels(gWidth, gHeight); srcBitmap.eraseColor(SK_ColorRED); SkBitmapDevice dev(srcBitmap); SkCanvas canvas(&dev); SkIRect r = SkIRect::MakeXYWH(5, 5, gWidth - 5, gWidth - 5); SkPaint p; p.setColor(SK_ColorGREEN); canvas.drawIRect(r, p); SkBitmap dstBitmap; srcBitmap.extractSubset(&dstBitmap, r); image.reset(SkNewImageFromBitmap(dstBitmap, true, NULL)); } SkBitmap tgt; tgt.allocN32Pixels(gWidth, gHeight); SkBitmapDevice dev(tgt); SkCanvas canvas(&dev); canvas.clear(SK_ColorTRANSPARENT); canvas.drawImage(image, 0, 0, NULL); uint32_t pixel = 0; SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1); canvas.readPixels(info, &pixel, 4, 0, 0); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); canvas.readPixels(info, &pixel, 4, gWidth - 6, gWidth - 6); REPORTER_ASSERT(reporter, pixel == SK_ColorGREEN); canvas.readPixels(info, &pixel, 4, gWidth - 5, gWidth - 5); REPORTER_ASSERT(reporter, pixel == SK_ColorTRANSPARENT); }
TiledPipeController::TiledPipeController(const SkBitmap& bitmap, SkPicture::InstallPixelRefProc proc, const SkMatrix* initial) : INHERITED(NULL, proc) { int32_t top = 0; int32_t bottom; int32_t height = bitmap.height() / NumberOfTiles; SkIRect rect; for (int i = 0; i < NumberOfTiles; i++) { bottom = i + 1 == NumberOfTiles ? bitmap.height() : top + height; rect.setLTRB(0, top, bitmap.width(), bottom); top = bottom; SkDEBUGCODE(bool extracted = )bitmap.extractSubset(&fBitmaps[i], rect); SkASSERT(extracted); SkBaseDevice* device = new SkBitmapDevice(fBitmaps[i]); SkCanvas* canvas = new SkCanvas(device); device->unref(); if (initial != NULL) { canvas->setMatrix(*initial); } canvas->translate(SkIntToScalar(-rect.left()), SkIntToScalar(-rect.top())); if (0 == i) { fReader.setCanvas(canvas); } else { fReaders[i - 1].setCanvas(canvas); fReaders[i - 1].setBitmapDecoder(proc); } canvas->unref(); } }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const FloatSize& scale, const FloatPoint& phase, SkXfermode::Mode compositeOp, const FloatRect& destRect, const IntSize& repeatSpacing) { TRACE_EVENT0("skia", "Image::drawPattern"); SkBitmap bitmap; if (!bitmapForCurrentFrame(&bitmap)) return; FloatRect normSrcRect = floatSrcRect; normSrcRect.intersect(FloatRect(0, 0, bitmap.width(), bitmap.height())); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw SkMatrix localMatrix; // We also need to translate it such that the origin of the pattern is the // origin of the destination rect, which is what WebKit expects. Skia uses // the coordinate system origin as the base for the pattern. If WebKit wants // a shifted image, it will shift it from there using the localMatrix. const float adjustedX = phase.x() + normSrcRect.x() * scale.width(); const float adjustedY = phase.y() + normSrcRect.y() * scale.height(); localMatrix.setTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); // Because no resizing occurred, the shader transform should be // set to the pattern's transform, which just includes scale. localMatrix.preScale(scale.width(), scale.height()); SkBitmap bitmapToPaint; bitmap.extractSubset(&bitmapToPaint, enclosingIntRect(normSrcRect)); if (!repeatSpacing.isZero()) { SkScalar ctmScaleX = 1.0; SkScalar ctmScaleY = 1.0; if (!RuntimeEnabledFeatures::slimmingPaintEnabled()) { AffineTransform ctm = context->getCTM(); ctmScaleX = ctm.xScale(); ctmScaleY = ctm.yScale(); } bitmapToPaint = createBitmapWithSpace( bitmapToPaint, repeatSpacing.width() * ctmScaleX / scale.width(), repeatSpacing.height() * ctmScaleY / scale.height()); } RefPtr<SkShader> shader = adoptRef(SkShader::CreateBitmapShader(bitmapToPaint, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &localMatrix)); bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap); { SkPaint paint; int initialSaveCount = context->preparePaintForDrawRectToRect(&paint, floatSrcRect, destRect, compositeOp, !bitmap.isOpaque(), isLazyDecoded, bitmap.isImmutable()); paint.setShader(shader.get()); context->drawRect(destRect, paint); context->canvas()->restoreToCount(initialSaveCount); } if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap.getGenerationID()); }
bool SkTileImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const { SkBitmap source = src; SkImageFilter* input = getInput(0); SkIPoint srcOffset = SkIPoint::Make(0, 0); if (input && !input->filterImage(proxy, src, ctx, &source, &srcOffset)) { return false; } SkRect dstRect; ctx.ctm().mapRect(&dstRect, fDstRect); const SkIRect dstIRect = dstRect.roundOut(); int w = dstIRect.width(); int h = dstIRect.height(); if (!fSrcRect.width() || !fSrcRect.height() || !w || !h) { return false; } SkRect srcRect; ctx.ctm().mapRect(&srcRect, fSrcRect); SkIRect srcIRect; srcRect.roundOut(&srcIRect); srcIRect.offset(-srcOffset); SkBitmap subset; SkIRect bounds; source.getBounds(&bounds); if (!srcIRect.intersect(bounds)) { offset->fX = offset->fY = 0; return true; } else if (!source.extractSubset(&subset, srcIRect)) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(w, h)); if (NULL == device.get()) { return false; } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); SkMatrix shaderMatrix; shaderMatrix.setTranslate(SkIntToScalar(srcOffset.fX), SkIntToScalar(srcOffset.fY)); SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(subset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &shaderMatrix)); paint.setShader(shader); canvas.translate(-dstRect.fLeft, -dstRect.fTop); canvas.drawRect(dstRect, paint); *dst = device->accessBitmap(false); offset->fX = dstIRect.fLeft; offset->fY = dstIRect.fTop; return true; }
void draw(SkCanvas* canvas) { SkBitmap original; SkImageInfo info = SkImageInfo::Make(25, 35, kRGBA_8888_SkColorType, kOpaque_SkAlphaType); if (original.tryAllocPixels(info)) { original.setIsVolatile(true); SkBitmap copy; original.extractSubset(©, {5, 10, 15, 20}); SkDebugf("original is " "%s" "volatile\n", original.isVolatile() ? "" : "not "); SkDebugf("copy is " "%s" "volatile\n", copy.isImmutable() ? "" : "not "); } }
/** * Helper function to write a bitmap subset to a file. Only called if subsets were created * and a writePath was provided. Behaves differently depending on * FLAGS_writeChecksumBasedFilenames. If true: * Writes the image to a PNG file named according to the digest hash, as described in * write_bitmap. * If false: * Creates a subdirectory called 'subsets' and writes a PNG to that directory. Also * creates a subdirectory called 'extracted' and writes a bitmap created using * extractSubset to a PNG in that directory. Both files will represent the same * subrectangle and have the same name for convenient comparison. In this case, the * digest is ignored. * * @param writePath Parent directory to hold the folders for the PNG files to write. Must * not be NULL. * @param subsetName Basename of the original file, with the dimensions of the subset tacked * on. Used to name the new file/folder. * @param bitmapAndDigestFromDecodeSubset SkBitmap (with digest) created by * SkImageDecoder::DecodeSubset, using rect as the area to decode. * @param rect Rectangle of the area decoded into bitmapFromDecodeSubset. Used to call * extractSubset on originalBitmap to create a bitmap with the same dimensions/pixels as * bitmapFromDecodeSubset (assuming decodeSubset worked properly). * @param originalBitmap SkBitmap decoded from the same stream as bitmapFromDecodeSubset, * using SkImageDecoder::decode to get the entire image. Used to create a PNG file for * comparison to the PNG created by bitmapAndDigestFromDecodeSubset's bitmap. * @return bool Whether the function succeeded at drawing the decoded subset and the extracted * subset to files. */ static bool write_subset(const char* writePath, const SkString& subsetName, const skiagm::BitmapAndDigest bitmapAndDigestFromDecodeSubset, SkIRect rect, const SkBitmap& originalBitmap) { // All parameters must be valid. SkASSERT(writePath != NULL); SkString subsetPath; if (FLAGS_writeChecksumBasedFilenames) { subsetPath.set(writePath); } else { // Create a subdirectory to hold the results of decodeSubset. subsetPath = SkOSPath::SkPathJoin(writePath, "subsets"); if (!sk_mkdir(subsetPath.c_str())) { gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but " "failed to create a directory to write to.", subsetName.c_str()); return false; } } SkAssertResult(write_bitmap(subsetPath.c_str(), subsetName.c_str(), bitmapAndDigestFromDecodeSubset)); gSuccessfulSubsetDecodes.push_back().printf("\twrote %s", subsetName.c_str()); if (!FLAGS_writeChecksumBasedFilenames) { // FIXME: The goal of extracting the subset is for visual comparison/using skdiff/skpdiff. // Currently disabling for writeChecksumBasedFilenames since it will be trickier to // determine which files to compare. // Also use extractSubset from the original for visual comparison. // Write the result to a file in a separate subdirectory. SkBitmap extractedSubset; if (!originalBitmap.extractSubset(&extractedSubset, rect)) { gFailedSubsetDecodes.push_back().printf("Successfully decoded subset %s, but failed " "to extract a similar subset for comparison.", subsetName.c_str()); return false; } SkString dirExtracted = SkOSPath::SkPathJoin(writePath, "extracted"); if (!sk_mkdir(dirExtracted.c_str())) { gFailedSubsetDecodes.push_back().printf("Successfully decoded subset%s, but failed " "to create a directory for extractSubset " "comparison.", subsetName.c_str()); return false; } skiagm::BitmapAndDigest bitmapAndDigestFromExtractSubset(extractedSubset); SkAssertResult(write_bitmap(dirExtracted.c_str(), subsetName.c_str(), bitmapAndDigestFromExtractSubset)); } return true; }
static void bitmapsubsetproc(SkCanvas* canvas, SkImage*, const SkBitmap& bm, const SkIRect& srcR, const SkRect& dstR, const SkPaint* paint) { if (!bm.bounds().contains(srcR)) { bitmapproc(canvas, nullptr, bm, srcR, dstR, paint); return; } SkBitmap subset; if (bm.extractSubset(&subset, srcR)) { canvas->drawBitmapRect(subset, dstR, paint); } }
NinePatchView() { SkImageDecoder::DecodeFile("/skimages/btn_default_normal_disable.9.png", &fBM); // trim off the edge guide-lines SkBitmap tmp; SkIRect r; r.set(1, 1, fBM.width() - 1, fBM.height() - 1); fBM.extractSubset(&tmp, r); fBM.swap(tmp); fX = SkIntToScalar(fBM.width()); fY = 0; }
void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, const SkIRect* srcRect, const SkMatrix& matrix, const SkPaint& paint) { SkBitmap tmp; // storage if we need a subset of bitmap const SkBitmap* bitmapPtr = &bitmap; if (srcRect) { if (!bitmap.extractSubset(&tmp, *srcRect)) { return; // extraction failed } bitmapPtr = &tmp; } draw.drawBitmap(*bitmapPtr, matrix, paint); }
virtual void onDrawContent(SkCanvas* canvas) { SkBitmap bitmap; create_bitmap(&bitmap); int x = bitmap.width() / 2; int y = bitmap.height() / 2; SkBitmap subset; bitmap.extractSubset(&subset, SkIRect::MakeXYWH(x, y, x, y)); canvas->translate(SkIntToScalar(20), SkIntToScalar(20)); canvas->writePixels(bitmap, 0, 0); canvas->writePixels(subset, 0, 0); }
bool SkBlurImageFilter::filterImageGPUDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { #if SK_SUPPORT_GPU SkBitmap input = src; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!this->filterInputGPUDeprecated(0, proxy, src, ctx, &input, &srcOffset)) { return false; } SkIRect srcBounds = input.bounds(); srcBounds.offset(srcOffset); SkIRect dstBounds; if (!this->applyCropRect(this->mapContext(ctx), srcBounds, &dstBounds)) { return false; } if (!srcBounds.intersect(dstBounds)) { return false; } SkVector sigma = map_sigma(fSigma, ctx.ctm()); if (sigma.x() == 0 && sigma.y() == 0) { input.extractSubset(result, srcBounds); offset->fX = srcBounds.x(); offset->fY = srcBounds.y(); return true; } offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; srcBounds.offset(-srcOffset); dstBounds.offset(-srcOffset); SkRect srcBoundsF(SkRect::Make(srcBounds)); GrTexture* inputTexture = input.getTexture(); SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(inputTexture->getContext(), inputTexture, false, SkRect::Make(dstBounds), &srcBoundsF, sigma.x(), sigma.y())); if (!tex) { return false; } GrWrapTextureInBitmap(tex, dstBounds.width(), dstBounds.height(), false, result); return true; #else SkDEBUGFAIL("Should not call in GPU-less build"); return false; #endif }
static void test_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image, SkImage::LegacyBitmapMode mode) { SkBitmap bitmap; REPORTER_ASSERT(reporter, image->asLegacyBitmap(&bitmap, mode)); check_legacy_bitmap(reporter, image, bitmap, mode); // Test subsetting to exercise the rowBytes logic. SkBitmap tmp; REPORTER_ASSERT(reporter, bitmap.extractSubset(&tmp, SkIRect::MakeWH(image->width() / 2, image->height() / 2))); SkAutoTUnref<SkImage> subsetImage(SkImage::NewFromBitmap(tmp)); REPORTER_ASSERT(reporter, subsetImage); SkBitmap subsetBitmap; REPORTER_ASSERT(reporter, subsetImage->asLegacyBitmap(&subsetBitmap, mode)); check_legacy_bitmap(reporter, subsetImage, subsetBitmap, mode); }
bool SkMorphologyImageFilter::filterImageGPUGeneric(bool dilate, Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* result, SkIPoint* offset) const { SkBitmap input = src; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (this->getInput(0) && !this->getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &bounds, &input)) { return false; } SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), SkIntToScalar(this->radius().height())); ctx.ctm().mapVectors(&radius, 1); int width = SkScalarFloorToInt(radius.fX); int height = SkScalarFloorToInt(radius.fY); if (width < 0 || height < 0) { return false; } SkIRect srcBounds = bounds; srcBounds.offset(-srcOffset); if (width == 0 && height == 0) { input.extractSubset(result, srcBounds); offset->fX = bounds.left(); offset->fY = bounds.top(); return true; } GrMorphologyEffect::MorphologyType type = dilate ? GrMorphologyEffect::kDilate_MorphologyType : GrMorphologyEffect::kErode_MorphologyType; if (!apply_morphology(input, srcBounds, type, SkISize::Make(width, height), result)) { return false; } offset->fX = bounds.left(); offset->fY = bounds.top(); return true; }
bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y, SkCanvas::Config8888 config8888) { if (SkBitmap::kARGB_8888_Config != bitmap->config() || NULL != bitmap->getTexture()) { return false; } const SkBitmap& src = this->accessBitmap(false); SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap->width(), bitmap->height()); SkIRect devbounds = SkIRect::MakeWH(src.width(), src.height()); if (!srcRect.intersect(devbounds)) { return false; } SkBitmap tmp; SkBitmap* bmp; if (bitmap->isNull()) { tmp.setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(), bitmap->height()); if (!tmp.allocPixels()) { return false; } bmp = &tmp; } else { bmp = bitmap; } SkIRect subrect = srcRect; subrect.offset(-x, -y); SkBitmap bmpSubset; bmp->extractSubset(&bmpSubset, subrect); bool result = this->onReadPixels(bmpSubset, srcRect.fLeft, srcRect.fTop, config8888); if (result && bmp == &tmp) { tmp.swap(*bitmap); } return result; }
void svg_renderer::update_cache_item (const renderable_item *item, const render_cache_id &cache_id, const QTransform &transform, renderer_config &cfg, int total_x, int total_y) { int block_size = rendered_items_cache::block_pixel_size (); SkBitmap bitmap; bitmap.setConfig (SkBitmap::kARGB_8888_Config, block_size * total_x, block_size * total_y); bitmap.allocPixels (); SkBitmapDevice device (bitmap); SkCanvas canvas (&device); canvas.drawColor (SK_ColorTRANSPARENT, SkXfermode::kSrc_Mode); QRectF local_rect = cache_id.pixel_rect (transform); QTransform last_inverted = transform.inverted (); QPointF last_pos_local = last_inverted.map (QPointF (0, 0)); QPointF cur_pos_local = last_inverted.map (QPointF (local_rect.x (), local_rect.y ())); QPointF diff = -cur_pos_local + last_pos_local; QTransform pixmap_transform = QTransform (transform).translate (diff.x (), diff.y ()); renderer_state state (QRect (0, 0, block_size * total_x, block_size * total_y), pixmap_transform); draw_item (item, canvas, state, cfg); if (m_queue->is_interrupted ()) return; if (total_x == total_y && total_x == 1) m_cache->add_bitmap (cache_id, bitmap, cfg.use_new_cache ()); else { for (int i = 0; i < total_x; i++) for (int j = 0; j < total_y; j++) { render_cache_id cur_id (cache_id.x () + i, cache_id.y () + j, cache_id.object_type ()); SkBitmap bitmap_part; DEBUG_ASSERT (bitmap.extractSubset (&bitmap_part, SkIRect::MakeXYWH (i * block_size, j * block_size, block_size, block_size))); m_cache->add_bitmap (cur_id, bitmap_part, cfg.use_new_cache ()); } } }
bool SkMorphologyImageFilter::filterImageGeneric(SkMorphologyImageFilter::Proc procX, SkMorphologyImageFilter::Proc procY, Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* dst, 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; } if (src.colorType() != kN32_SkColorType) { return false; } SkIRect bounds; if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } if (!dst->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) { return false; } SkVector radius = SkVector::Make(SkIntToScalar(this->radius().width()), SkIntToScalar(this->radius().height())); ctx.ctm().mapVectors(&radius, 1); int width = SkScalarFloorToInt(radius.fX); int height = SkScalarFloorToInt(radius.fY); if (width < 0 || height < 0) { return false; } SkIRect srcBounds = bounds; srcBounds.offset(-srcOffset); if (width == 0 && height == 0) { src.extractSubset(dst, srcBounds); offset->fX = bounds.left(); offset->fY = bounds.top(); return true; } SkBitmap temp; if (!temp.tryAllocPixels(dst->info())) { return false; } if (width > 0 && height > 0) { callProcX(procX, src, &temp, width, srcBounds); SkIRect tmpBounds = SkIRect::MakeWH(srcBounds.width(), srcBounds.height()); callProcY(procY, temp, dst, height, tmpBounds); } else if (width > 0) { callProcX(procX, src, dst, width, srcBounds); } else if (height > 0) { callProcY(procY, src, dst, height, srcBounds); } offset->fX = bounds.left(); offset->fY = bounds.top(); return true; }
void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::DrawBitmapRectFlags flags) { SkMatrix matrix; SkRect bitmapBounds, tmpSrc, tmpDst; SkBitmap tmpBitmap; bitmapBounds.isetWH(bitmap.width(), bitmap.height()); // Compute matrix from the two rectangles if (src) { tmpSrc = *src; } else { tmpSrc = bitmapBounds; } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); 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); dstPtr = &tmpDst; } // since we may need to clamp to the borders of the src rect within // the bitmap, we extract a subset. SkIRect srcIR; tmpSrc.roundOut(&srcIR); 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); } SkRect extractedBitmapBounds; extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); 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. this->drawBitmap(draw, *bitmapPtr, matrix, paint); return; } // construct a shader, so we can call drawRect with the dst SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix); if (NULL == s) { return; } SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); paintWithShader.setShader(s)->unref(); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. this->drawRect(draw, *dstPtr, paintWithShader); }
bool SkDilateImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source, const SkMatrix& ctm, SkBitmap* dst, SkIPoint* offset) { SkBitmap src = source; if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) { return false; } if (src.config() != SkBitmap::kARGB_8888_Config) { return false; } SkIRect bounds; src.getBounds(&bounds); if (!this->applyCropRect(&bounds, ctm)) { return false; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } dst->setConfig(src.config(), bounds.width(), bounds.height()); dst->allocPixels(); if (!dst->getPixels()) { return false; } int width = radius().width(); int height = radius().height(); if (width < 0 || height < 0) { return false; } if (width == 0 && height == 0) { src.extractSubset(dst, bounds); offset->fX += bounds.left(); offset->fY += bounds.top(); return true; } SkBitmap temp; temp.setConfig(dst->config(), dst->width(), dst->height()); if (!temp.allocPixels()) { return false; } if (width > 0 && height > 0) { dilateX(src, &temp, width, bounds); SkIRect tmpBounds = SkIRect::MakeWH(bounds.width(), bounds.height()); dilateY(temp, dst, height, tmpBounds); } else if (width > 0) { dilateX(src, dst, width, bounds); } else if (height > 0) { dilateY(src, dst, height, bounds); } offset->fX += bounds.left(); offset->fY += bounds.top(); return true; }
void SkDevice::writePixels(const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) { if (bitmap.isNull() || bitmap.getTexture()) { return; } const SkBitmap* sprite = &bitmap; // check whether we have to handle a config8888 that doesn't match SkPMColor if (SkBitmap::kARGB_8888_Config == bitmap.config() && SkCanvas::kNative_Premul_Config8888 != config8888 && kPMColorAlias != config8888) { // We're going to have to convert from a config8888 to the native config // First we clip to the device bounds. SkBitmap dstBmp = this->accessBitmap(true); SkIRect spriteRect = SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()); SkIRect devRect = SkIRect::MakeWH(dstBmp.width(), dstBmp.height()); if (!spriteRect.intersect(devRect)) { return; } // write directly to the device if it has pixels and is SkPMColor bool drawSprite; if (SkBitmap::kARGB_8888_Config == dstBmp.config() && !dstBmp.isNull()) { // we can write directly to the dst when doing the conversion dstBmp.extractSubset(&dstBmp, spriteRect); drawSprite = false; } else { // we convert to a temporary bitmap and draw that as a sprite dstBmp.setConfig(SkBitmap::kARGB_8888_Config, spriteRect.width(), spriteRect.height()); if (!dstBmp.allocPixels()) { return; } drawSprite = true; } // copy pixels to dstBmp and convert from config8888 to native config. SkAutoLockPixels alp(bitmap); uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x, spriteRect.fTop - y); SkCopyConfig8888ToBitmap(dstBmp, srcPixels, bitmap.rowBytes(), config8888); if (drawSprite) { // we've clipped the sprite when we made a copy x = spriteRect.fLeft; y = spriteRect.fTop; sprite = &dstBmp; } else { return; } } SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); SkRasterClip clip(SkIRect::MakeWH(fBitmap.width(), fBitmap.height())); SkDraw draw; draw.fRC = &clip; draw.fClip = &clip.bwRgn(); draw.fBitmap = &fBitmap; // canvas should have already called accessBitmap draw.fMatrix = &SkMatrix::I(); this->drawSprite(draw, *sprite, x, y, paint); }
bool SkTileImageFilter::onFilterImageDeprecated(Proxy* proxy, const SkBitmap& src, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const { SkBitmap source = src; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!this->filterInputDeprecated(0, proxy, src, ctx, &source, &srcOffset)) { return false; } SkRect dstRect; ctx.ctm().mapRect(&dstRect, fDstRect); if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) { offset->fX = offset->fY = 0; return true; } const SkIRect dstIRect = dstRect.roundOut(); int w = dstIRect.width(); int h = dstIRect.height(); if (!fSrcRect.width() || !fSrcRect.height() || !w || !h) { return false; } SkRect srcRect; ctx.ctm().mapRect(&srcRect, fSrcRect); SkIRect srcIRect; srcRect.roundOut(&srcIRect); srcIRect.offset(-srcOffset); SkBitmap subset; SkIRect srcBounds; source.getBounds(&srcBounds); if (!SkIRect::Intersects(srcIRect, srcBounds)) { offset->fX = offset->fY = 0; return true; } if (srcBounds.contains(srcIRect)) { if (!source.extractSubset(&subset, srcIRect)) { return false; } } else { SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(srcIRect.width(), srcIRect.height(), kPossible_TileUsage)); if (!device) { return false; } SkCanvas canvas(device); canvas.drawBitmap(src, SkIntToScalar(srcOffset.x()), SkIntToScalar(srcOffset.y())); subset = device->accessBitmap(false); } SkASSERT(subset.width() == srcIRect.width()); SkASSERT(subset.height() == srcIRect.height()); SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(w, h)); if (nullptr == device.get()) { return false; } SkCanvas canvas(device); SkPaint paint; paint.setXfermodeMode(SkXfermode::kSrc_Mode); paint.setShader(SkShader::MakeBitmapShader(subset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode)); canvas.translate(-dstRect.fLeft, -dstRect.fTop); canvas.drawRect(dstRect, paint); *dst = device->accessBitmap(false); offset->fX = dstIRect.fLeft; offset->fY = dstIRect.fTop; return true; }
void SkBitmapDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { SkMatrix matrix; SkRect bitmapBounds, tmpSrc, tmpDst; SkBitmap tmpBitmap; bitmapBounds.isetWH(bitmap.width(), bitmap.height()); // Compute matrix from the two rectangles if (src) { tmpSrc = *src; } else { tmpSrc = bitmapBounds; } matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); LogDrawScaleFactor(SkMatrix::Concat(*draw.fMatrix, matrix), paint.getFilterQuality()); 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); dstPtr = &tmpDst; } } if (src && !src->contains(bitmapBounds) && SkCanvas::kFast_SrcRectConstraint == constraint && paint.getFilterQuality() != kNone_SkFilterQuality) { // 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); } SkRect extractedBitmapBounds; extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); 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)) { draw.drawBitmap(*bitmapPtr, matrix, dstPtr, paint); return; } } USE_SHADER: // Since the shader need only live for our stack-frame, pass in a custom allocator. This // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap // if its mutable, since that precaution is not needed (give the short lifetime of the shader). SkTBlitterAllocator allocator; // construct a shader, so we can call drawRect with the dst auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, &matrix, kNever_SkCopyPixelsMode, &allocator); if (!s) { return; } // we deliberately add a ref, since the allocator wants to be the last owner s.get()->ref(); SkPaint paintWithShader(paint); paintWithShader.setStyle(SkPaint::kFill_Style); paintWithShader.setShader(s); // Call ourself, in case the subclass wanted to share this setup code // but handle the drawRect code themselves. this->drawRect(draw, *dstPtr, paintWithShader); }
bool CopyTilesRenderer::render(SkBitmap** out) { int i = 0; bool success = true; SkBitmap dst; for (int x = 0; x < this->getViewWidth(); x += fLargeTileWidth) { for (int y = 0; y < this->getViewHeight(); y += fLargeTileHeight) { SkAutoCanvasRestore autoRestore(fCanvas, true); // Translate so that we draw the correct portion of the picture. // Perform a postTranslate so that the scaleFactor does not interfere with the // positioning. SkMatrix mat(fCanvas->getTotalMatrix()); mat.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); fCanvas->setMatrix(mat); // Draw the picture if (fUseMultiPictureDraw) { SkMultiPictureDraw mpd; mpd.add(fCanvas, fPicture); mpd.draw(); } else { fCanvas->drawPicture(fPicture); } // Now extract the picture into tiles SkBitmap baseBitmap; fCanvas->readPixels(SkIRect::MakeSize(fCanvas->getBaseLayerSize()), &baseBitmap); SkIRect subset; for (int tileY = 0; tileY < fLargeTileHeight; tileY += this->getTileHeight()) { for (int tileX = 0; tileX < fLargeTileWidth; tileX += this->getTileWidth()) { subset.set(tileX, tileY, tileX + this->getTileWidth(), tileY + this->getTileHeight()); SkDEBUGCODE(bool extracted =) baseBitmap.extractSubset(&dst, subset); SkASSERT(extracted); if (!fWritePath.isEmpty()) { // Similar to write() in PictureRenderer.cpp, but just encodes // a bitmap directly. // TODO: Share more common code with write() to do this, to properly // write out the JSON summary, etc. SkString pathWithNumber = SkOSPath::Join(fWritePath.c_str(), fInputFilename.c_str()); pathWithNumber.remove(pathWithNumber.size() - 4, 4); pathWithNumber.appendf("%i.png", i++); SkBitmap copy; #if SK_SUPPORT_GPU if (isUsingGpuDevice()) { dst.pixelRef()->readPixels(©, &subset); } else { #endif dst.copyTo(©); #if SK_SUPPORT_GPU } #endif success &= SkImageEncoder::EncodeFile(pathWithNumber.c_str(), copy, SkImageEncoder::kPNG_Type, 100); } } } } } return success; }
void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, SkDevice* dstDevice, const IntSize& size) { ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); int originX = sourceRect.x(); int destX = destPoint.x() + sourceRect.x(); ASSERT(destX >= 0); ASSERT(destX < size.width()); ASSERT(originX >= 0); ASSERT(originX < sourceRect.maxX()); int endX = destPoint.x() + sourceRect.maxX(); ASSERT(endX <= size.width()); int numColumns = endX - destX; int originY = sourceRect.y(); int destY = destPoint.y() + sourceRect.y(); ASSERT(destY >= 0); ASSERT(destY < size.height()); ASSERT(originY >= 0); ASSERT(originY < sourceRect.maxY()); int endY = destPoint.y() + sourceRect.maxY(); ASSERT(endY <= size.height()); int numRows = endY - destY; unsigned srcBytesPerRow = 4 * sourceSize.width(); SkBitmap deviceBitmap = dstDevice->accessBitmap(true); SkAutoLockPixels deviceAutoLock(deviceBitmap); // If the device's bitmap doesn't have pixels we will make a temp and call writePixels on the device. bool temporaryBitmap = !deviceBitmap.getPixels(); SkBitmap destBitmap; if (temporaryBitmap) { destBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow); if (!destBitmap.allocPixels()) CRASH(); } else deviceBitmap.extractSubset(&destBitmap, SkIRect::MakeXYWH(destX, destY, numColumns, numRows)); // Whether we made a temporary or not destBitmap is always configured to be written at 0,0 SkAutoLockPixels destAutoLock(destBitmap); const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { SkPMColor* destRow = destBitmap.getAddr32(0, y); for (int x = 0; x < numColumns; ++x) { const unsigned char* srcPixel = &srcRow[x * 4]; if (multiplied == Unmultiplied) { unsigned char alpha = srcPixel[3]; unsigned char r = SkMulDiv255Ceiling(srcPixel[0], alpha); unsigned char g = SkMulDiv255Ceiling(srcPixel[1], alpha); unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha); destRow[x] = SkPackARGB32(alpha, r, g, b); } else destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); } srcRow += srcBytesPerRow; } // If we used a temporary then write it to the device if (temporaryBitmap) dstDevice->writePixels(destBitmap, destX, destY); }
bool SkBlurImageFilter::onFilterImageDeprecated(Proxy* proxy, const SkBitmap& source, const Context& ctx, SkBitmap* dst, SkIPoint* offset) const { SkBitmap src = source; SkIPoint srcOffset = SkIPoint::Make(0, 0); if (!this->filterInputDeprecated(0, proxy, source, ctx, &src, &srcOffset)) { return false; } if (src.colorType() != kN32_SkColorType) { return false; } SkIRect srcBounds = src.bounds(); srcBounds.offset(srcOffset); SkIRect dstBounds; if (!this->applyCropRect(this->mapContext(ctx), srcBounds, &dstBounds)) { return false; } if (!srcBounds.intersect(dstBounds)) { return false; } SkVector sigma = map_sigma(fSigma, ctx.ctm()); int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX; int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY; getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX); getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY); if (kernelSizeX < 0 || kernelSizeY < 0) { return false; } if (kernelSizeX == 0 && kernelSizeY == 0) { src.extractSubset(dst, srcBounds); offset->fX = srcBounds.x(); offset->fY = srcBounds.y(); return true; } SkAutoLockPixels alp(src); if (!src.getPixels()) { return false; } SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(dstBounds.width(), dstBounds.height())); if (!device) { return false; } *dst = device->accessBitmap(false); SkAutoLockPixels alp_dst(*dst); SkAutoTUnref<SkBaseDevice> tempDevice(proxy->createDevice(dst->width(), dst->height())); if (!tempDevice) { return false; } SkBitmap temp = tempDevice->accessBitmap(false); SkAutoLockPixels alpTemp(temp); offset->fX = dstBounds.fLeft; offset->fY = dstBounds.fTop; SkPMColor* t = temp.getAddr32(0, 0); SkPMColor* d = dst->getAddr32(0, 0); int w = dstBounds.width(), h = dstBounds.height(); const SkPMColor* s = src.getAddr32(srcBounds.x() - srcOffset.x(), srcBounds.y() - srcOffset.y()); srcBounds.offset(-dstBounds.x(), -dstBounds.y()); dstBounds.offset(-dstBounds.x(), -dstBounds.y()); SkIRect srcBoundsT = SkIRect::MakeLTRB(srcBounds.top(), srcBounds.left(), srcBounds.bottom(), srcBounds.right()); SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width()); int sw = src.rowBytesAsPixels(); /** * * In order to make memory accesses cache-friendly, we reorder the passes to * use contiguous memory reads wherever possible. * * For example, the 6 passes of the X-and-Y blur case are rewritten as * follows. Instead of 3 passes in X and 3 passes in Y, we perform * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X, * then 1 pass in X transposed to Y on write. * * +----+ +----+ +----+ +---+ +---+ +---+ +----+ * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB | * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+ * +---+ +---+ +---+ * * In this way, two of the y-blurs become x-blurs applied to transposed * images, and all memory reads are contiguous. */ if (kernelSizeX > 0 && kernelSizeY > 0) { SkOpts::box_blur_xx(s, sw, srcBounds, t, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xy(d, w, dstBounds, t, kernelSizeX3, highOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(t, h, dstBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } else if (kernelSizeX > 0) { SkOpts::box_blur_xx(s, sw, srcBounds, d, kernelSizeX, lowOffsetX, highOffsetX, w, h); SkOpts::box_blur_xx(d, w, dstBounds, t, kernelSizeX, highOffsetX, lowOffsetX, w, h); SkOpts::box_blur_xx(t, w, dstBounds, d, kernelSizeX3, highOffsetX, highOffsetX, w, h); } else if (kernelSizeY > 0) { SkOpts::box_blur_yx(s, sw, srcBoundsT, d, kernelSizeY, lowOffsetY, highOffsetY, h, w); SkOpts::box_blur_xx(d, h, dstBoundsT, t, kernelSizeY, highOffsetY, lowOffsetY, h, w); SkOpts::box_blur_xy(t, h, dstBoundsT, d, kernelSizeY3, highOffsetY, highOffsetY, h, w); } return true; }