void GraphicsLayerChromium::setContentsToImage(Image* image) { bool childrenChanged = false; if (image) { if (m_contentsLayer.isNull() || m_contentsLayerPurpose != ContentsLayerForImage) { WebKit::WebImageLayer imageLayer = WebKit::WebImageLayer::create(); setupContentsLayer(imageLayer); m_contentsLayerPurpose = ContentsLayerForImage; childrenChanged = true; } WebKit::WebImageLayer imageLayer = m_contentsLayer.to<WebKit::WebImageLayer>(); NativeImageSkia* nativeImage = image->nativeImageForCurrentFrame(); imageLayer.setBitmap(nativeImage->bitmap()); imageLayer.setOpaque(image->isBitmapImage() && !image->currentFrameHasAlpha()); updateContentsRect(); } else { if (!m_contentsLayer.isNull()) { childrenChanged = true; // The old contents layer will be removed via updateChildList. m_contentsLayer.reset(); } } if (childrenChanged) updateChildList(); }
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& srcRect, const SkRect& destRect, const SkXfermode::Mode& compOp) { #if PLATFORM(CHROMIUM) TRACE_EVENT("paintSkBitmap", platformContext, 0); #endif SkPaint paint; paint.setXfermodeMode(compOp); paint.setFilterBitmap(true); 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 resampling = platformContext->printing() ? RESAMPLE_NONE : computeResamplingMode(platformContext, bitmap, srcRect.width(), srcRect.height(), SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height())); 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. canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint); } platformContext->didDrawRect(destRect, paint, &bitmap.bitmap()); }
void GraphicsLayerChromium::setContentsToImage(Image* image) { bool childrenChanged = false; NativeImageSkia* nativeImage = image ? image->nativeImageForCurrentFrame() : 0; if (nativeImage) { if (m_contentsLayerPurpose != ContentsLayerForImage) { m_imageLayer = adoptPtr(Platform::current()->compositorSupport()->createImageLayer()); registerContentsLayer(m_imageLayer->layer()); setupContentsLayer(m_imageLayer->layer()); m_contentsLayerPurpose = ContentsLayerForImage; childrenChanged = true; } m_imageLayer->setBitmap(nativeImage->bitmap()); m_imageLayer->layer()->setOpaque(image->isBitmapImage() && !image->currentFrameHasAlpha()); updateContentsRect(); } else { if (m_imageLayer) { childrenChanged = true; unregisterContentsLayer(m_imageLayer->layer()); m_imageLayer.clear(); } // The old contents layer will be removed via updateChildList. m_contentsLayer = 0; } if (childrenChanged) updateChildList(); }
static void loadBufferingImageData() { static bool loaded = false; if (!loaded) { static Image* bufferingIcon = Image::loadPlatformResource("vidbuffer").leakRef(); NativeImageSkia* nativeImage = bufferingIcon->nativeImageForCurrentFrame(); if (!nativeImage) return; if (!nativeImage->isDataComplete()) return; loaded = true; nativeImage->bitmap().lockPixels(); int bufSize = nativeImage->bitmap().width() * nativeImage->bitmap().height() * 4; s_bufferingImageWidth = nativeImage->bitmap().width(); s_bufferingImageHeight = nativeImage->bitmap().height(); s_bufferingImageData = static_cast<char*>(malloc(bufSize)); memcpy(s_bufferingImageData, nativeImage->bitmap().getPixels(), bufSize); nativeImage->bitmap().unlockPixels(); bufferingIcon->deref(); } }
DragImageRef createDragImageFromImage(Image* image) { if (!image) return 0; NativeImageSkia* bitmap = image->nativeImageForCurrentFrame(); if (!bitmap) return 0; SkBitmap* dragImage = new SkBitmap(); bitmap->bitmap().copyTo(dragImage, SkBitmap::kARGB_8888_Config); return dragImage; }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, ColorSpace colorSpace, CompositeOperator compositeOp, RespectImageOrientationEnum shouldRespectImageOrientation) { if (!m_source.initialized()) return; // Spin the animation to the correct frame before we try to draw it, so we // don't draw an old frame and then immediately need to draw a newer one, // causing flicker and wasting CPU. startAnimation(); NativeImageSkia* bm = nativeImageForCurrentFrame(); if (!bm) return; // It's too early and we don't have an image yet. FloatRect normDstRect = normalizeRect(dstRect); FloatRect normSrcRect = normalizeRect(srcRect); normSrcRect.intersect(FloatRect(0, 0, bm->bitmap().width(), bm->bitmap().height())); if (normSrcRect.isEmpty() || normDstRect.isEmpty()) return; // Nothing to draw. ImageOrientation orientation = DefaultImageOrientation; if (shouldRespectImageOrientation == RespectImageOrientation) orientation = frameOrientationAtIndex(m_currentFrame); GraphicsContextStateSaver saveContext(*ctxt, false); if (orientation != DefaultImageOrientation) { saveContext.save(); // ImageOrientation expects the origin to be at (0, 0) ctxt->translate(normDstRect.x(), normDstRect.y()); normDstRect.setLocation(FloatPoint()); ctxt->concatCTM(orientation.transformFromDefault(normDstRect.size())); if (orientation.usesWidthAsHeight()) { // The destination rect will have it's width and height already reversed for the orientation of // the image, as it was needed for page layout, so we need to reverse it back here. normDstRect = FloatRect(normDstRect.x(), normDstRect.y(), normDstRect.height(), normDstRect.width()); } } paintSkBitmap(ctxt->platformContext(), *bm, normSrcRect, normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); }
static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, const SkIRect& 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, srcRect.width(), 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. canvas->drawBitmapRect(bitmap.bitmap(), &srcRect, destRect, &paint); } platformContext->didDrawRect(destRect, paint, &bitmap.bitmap()); }
void FrameLoaderClientBlackBerry::dispatchDidReceiveIcon() { String url = m_frame->document()->url().string(); Image* img = iconDatabase().synchronousIconForPageURL(url, IntSize(10, 10)); if (!img || !img->data()) return; NativeImageSkia* bitmap = img->nativeImageForCurrentFrame(); if (!bitmap) return; bitmap->lockPixels(); String iconUrl = iconDatabase().synchronousIconURLForPageURL(url); m_webPagePrivate->m_client->setFavicon(img->width(), img->height(), (unsigned char*)bitmap->getPixels(), iconUrl.utf8().data()); bitmap->unlockPixels(); }
DragImageRef createDragImageFromImage(Image* image, RespectImageOrientationEnum) { if (!image) return 0; NativeImageSkia* bitmap = image->nativeImageForCurrentFrame(); if (!bitmap) return 0; SkBitmap* dragImage = new SkBitmap(); bitmap->bitmap().copyTo(dragImage, SkBitmap::kARGB_8888_Config); DragImageChromium* dragImageChromium = new DragImageChromium; dragImageChromium->bitmap = dragImage; dragImageChromium->resolutionScale = bitmap->resolutionScale(); return dragImageChromium; }
DragImageRef createDragImageFromImage(Image* image, RespectImageOrientationEnum shouldRespectImageOrientation) { if (!image) return 0; NativeImageSkia* bitmap = image->nativeImageForCurrentFrame(); if (!bitmap) return 0; DragImageChromium* dragImageChromium = new DragImageChromium; dragImageChromium->bitmap = new SkBitmap(); dragImageChromium->resolutionScale = bitmap->resolutionScale(); if (image->isBitmapImage()) { ImageOrientation orientation = DefaultImageOrientation; BitmapImage* bitmapImage = static_cast<BitmapImage*>(image); IntSize sizeRespectingOrientation = bitmapImage->sizeRespectingOrientation(); if (shouldRespectImageOrientation == RespectImageOrientation) orientation = bitmapImage->currentFrameOrientation(); if (orientation != DefaultImageOrientation) { // Construct a correctly-rotated copy of the image to use as the drag image. dragImageChromium->bitmap->setConfig( SkBitmap::kARGB_8888_Config, sizeRespectingOrientation.width(), sizeRespectingOrientation.height()); dragImageChromium->bitmap->allocPixels(); FloatRect destRect(FloatPoint(), sizeRespectingOrientation); SkCanvas canvas(*dragImageChromium->bitmap); canvas.concat(orientation.transformFromDefault(sizeRespectingOrientation)); if (orientation.usesWidthAsHeight()) destRect = FloatRect(destRect.x(), destRect.y(), destRect.height(), destRect.width()); canvas.drawBitmapRect(bitmap->bitmap(), 0, destRect); return dragImageChromium; } } bitmap->bitmap().copyTo(dragImageChromium->bitmap, SkBitmap::kARGB_8888_Config); return dragImageChromium; }
// This function is used to scale an image and extract a scaled fragment. // // ALGORITHM // // Because the scaled image size has to be integers, we approximate the real // scale with the following formula (only X direction is shown): // // scaledImageWidth = round(scaleX * imageRect.width()) // approximateScaleX = scaledImageWidth / imageRect.width() // // With this method we maintain a constant scale factor among fragments in // the scaled image. This allows fragments to stitch together to form the // full scaled image. The downside is there will be a small difference // between |scaleX| and |approximateScaleX|. // // A scaled image fragment is identified by: // // - Scaled image size // - Scaled image fragment rectangle (IntRect) // // Scaled image size has been determined and the next step is to compute the // rectangle for the scaled image fragment which needs to be an IntRect. // // scaledSrcRect = srcRect * (approximateScaleX, approximateScaleY) // enclosingScaledSrcRect = enclosingIntRect(scaledSrcRect) // // Finally we extract the scaled image fragment using // (scaledImageSize, enclosingScaledSrcRect). // static SkBitmap extractScaledImageFragment(const NativeImageSkia& bitmap, const SkRect& srcRect, float scaleX, float scaleY, SkRect* scaledSrcRect, SkIRect* enclosingScaledSrcRect) { SkISize imageSize = SkISize::Make(bitmap.bitmap().width(), bitmap.bitmap().height()); SkISize scaledImageSize = SkISize::Make(clampToInteger(roundf(imageSize.width() * scaleX)), clampToInteger(roundf(imageSize.height() * scaleY))); SkRect imageRect = SkRect::MakeWH(imageSize.width(), imageSize.height()); SkRect scaledImageRect = SkRect::MakeWH(scaledImageSize.width(), scaledImageSize.height()); SkMatrix scaleTransform; scaleTransform.setRectToRect(imageRect, scaledImageRect, SkMatrix::kFill_ScaleToFit); scaleTransform.mapRect(scaledSrcRect, srcRect); scaledSrcRect->intersect(scaledImageRect); *enclosingScaledSrcRect = enclosingIntRect(*scaledSrcRect); // |enclosingScaledSrcRect| can be larger than |scaledImageSize| because // of float inaccuracy so clip to get inside. enclosingScaledSrcRect->intersect(SkIRect::MakeSize(scaledImageSize)); return bitmap.resizedBitmap(scaledImageSize, *enclosingScaledSrcRect); }
bool FEComponentTransfer::platformApplySkia() { FilterEffect* in = inputEffect(0); ImageBuffer* resultImage = createImageBufferResult(); if (!resultImage) return false; RefPtr<Image> image = in->asImageBuffer()->copyImage(DontCopyBackingStore); NativeImageSkia* nativeImage = image->nativeImageForCurrentFrame(); if (!nativeImage) return false; unsigned char rValues[256], gValues[256], bValues[256], aValues[256]; getValues(rValues, gValues, bValues, aValues); SkPaint paint; paint.setColorFilter(SkTableColorFilter::CreateARGB(aValues, rValues, gValues, bValues))->unref(); paint.setXfermodeMode(SkXfermode::kSrc_Mode); resultImage->context()->platformContext()->drawBitmap(nativeImage->bitmap(), 0, 0, &paint); return true; }
bool GraphicsContext3D::getImageData(Image* image, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool ignoreGammaAndColorProfile, Vector<uint8_t>& outputVector) { if (!image) return false; OwnPtr<NativeImageSkia> pixels; NativeImageSkia* skiaImage = 0; AlphaOp neededAlphaOp = AlphaDoNothing; bool hasAlpha = image->isBitmapImage() ? static_cast<BitmapImage*>(image)->frameHasAlphaAtIndex(0) : true; if ((ignoreGammaAndColorProfile || (hasAlpha && !premultiplyAlpha)) && image->data()) { ImageSource decoder(ImageSource::AlphaNotPremultiplied, ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied); // Attempt to get raw unpremultiplied image data decoder.setData(image->data(), true); if (!decoder.frameCount() || !decoder.frameIsCompleteAtIndex(0)) return false; hasAlpha = decoder.frameHasAlphaAtIndex(0); pixels = adoptPtr(decoder.createFrameAtIndex(0)); if (!pixels.get() || !pixels->isDataComplete() || !pixels->width() || !pixels->height()) return false; SkBitmap::Config skiaConfig = pixels->config(); if (skiaConfig != SkBitmap::kARGB_8888_Config) return false; skiaImage = pixels.get(); if (hasAlpha && premultiplyAlpha) neededAlphaOp = AlphaDoPremultiply; } else { skiaImage = image->nativeImageForCurrentFrame(); if (!premultiplyAlpha && hasAlpha) neededAlphaOp = AlphaDoUnmultiply; } if (!skiaImage) return false; SkBitmap& skiaImageRef = *skiaImage; SkAutoLockPixels lock(skiaImageRef); ASSERT(skiaImage->rowBytes() == skiaImage->width() * 4); outputVector.resize(skiaImage->rowBytes() * skiaImage->height()); return packPixels(reinterpret_cast<const uint8_t*>(skiaImage->getPixels()), SourceFormatBGRA8, skiaImage->width(), skiaImage->height(), 0, format, type, neededAlphaOp, outputVector.data()); }
// 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 GraphicsContext3D::getImageData(Image* image, unsigned int format, unsigned int type, bool premultiplyAlpha, bool ignoreGammaAndColorProfile, Vector<uint8_t>& outputVector) { if (!image) return false; OwnPtr<NativeImageSkia> pixels; NativeImageSkia* skiaImage = 0; AlphaOp neededAlphaOp = AlphaDoNothing; if (image->data()) { ImageSource decoder(ImageSource::AlphaNotPremultiplied, ignoreGammaAndColorProfile ? ImageSource::GammaAndColorProfileIgnored : ImageSource::GammaAndColorProfileApplied); decoder.setData(image->data(), true); if (!decoder.frameCount() || !decoder.frameIsCompleteAtIndex(0)) return false; bool hasAlpha = decoder.frameHasAlphaAtIndex(0); pixels = adoptPtr(decoder.createFrameAtIndex(0)); if (!pixels.get() || !pixels->isDataComplete() || !pixels->width() || !pixels->height()) return false; SkBitmap::Config skiaConfig = pixels->config(); if (skiaConfig != SkBitmap::kARGB_8888_Config) return false; skiaImage = pixels.get(); if (hasAlpha && premultiplyAlpha) neededAlphaOp = AlphaDoPremultiply; } else { // This is a special case for texImage2D with HTMLCanvasElement input. skiaImage = image->nativeImageForCurrentFrame(); if (!premultiplyAlpha) neededAlphaOp = AlphaDoUnmultiply; } if (!skiaImage) return false; SkBitmap& skiaImageRef = *skiaImage; SkAutoLockPixels lock(skiaImageRef); ASSERT(skiaImage->rowBytes() == skiaImage->width() * 4); outputVector.resize(skiaImage->rowBytes() * skiaImage->height()); return packPixels(reinterpret_cast<const uint8_t*>(skiaImage->getPixels()), SourceFormatBGRA8, skiaImage->width(), skiaImage->height(), 0, format, type, neededAlphaOp, outputVector.data()); }
// 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_EVENT("drawResampledBitmap", &canvas, 0); #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 transformed (screen space) portion of the visible portion for // use below. SkRect destRectVisibleSubsetTransformed; canvas.getTotalMatrix().mapRect(&destRectVisibleSubsetTransformed, destRectVisibleSubset); SkRect destBitmapSubsetTransformed = destRectVisibleSubsetTransformed; destBitmapSubsetTransformed.offset(-destRectTransformed.fLeft, -destRectTransformed.fTop); 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 GraphicsContext3D::getImageData(Image* image, Vector<uint8_t>& outputVector, bool premultiplyAlpha, bool* hasAlphaChannel, AlphaOp* neededAlphaOp, unsigned int* format) { if (!image) return false; NativeImageSkia* skiaImage = image->nativeImageForCurrentFrame(); if (!skiaImage) return false; SkBitmap::Config skiaConfig = skiaImage->config(); // FIXME: must support more image configurations. if (skiaConfig != SkBitmap::kARGB_8888_Config) return false; SkBitmap& skiaImageRef = *skiaImage; SkAutoLockPixels lock(skiaImageRef); int height = skiaImage->height(); int rowBytes = skiaImage->rowBytes(); ASSERT(rowBytes == skiaImage->width() * 4); uint8_t* pixels = reinterpret_cast<uint8_t*>(skiaImage->getPixels()); outputVector.resize(rowBytes * height); int size = rowBytes * height; memcpy(outputVector.data(), pixels, size); *hasAlphaChannel = true; if (!premultiplyAlpha) // FIXME: must fetch the image data before the premultiplication step *neededAlphaOp = kAlphaDoUnmultiply; // Convert from BGRA to RGBA. FIXME: add GL_BGRA extension support // to all underlying OpenGL implementations. for (int i = 0; i < size; i += 4) std::swap(outputVector[i], outputVector[i + 2]); *format = RGBA; return true; }
static ResamplingMode computeResamplingMode(const SkMatrix& matrix, const NativeImageSkia& bitmap, float srcWidth, float srcHeight, float destWidth, float destHeight) { // The percent change below which we will not resample. This usually means // an off-by-one error on the web page, and just doing nearest neighbor // sampling is usually good enough. const float kFractionalChangeThreshold = 0.025f; // Images smaller than this in either direction are considered "small" and // are not resampled ever (see below). const int kSmallImageSizeThreshold = 8; // The amount an image can be stretched in a single direction before we // say that it is being stretched so much that it must be a line or // background that doesn't need resampling. const float kLargeStretch = 3.0f; // Figure out if we should resample this image. We try to prune out some // common cases where resampling won't give us anything, since it is much // slower than drawing stretched. float diffWidth = fabs(destWidth - srcWidth); float diffHeight = fabs(destHeight - srcHeight); bool widthNearlyEqual = diffWidth < std::numeric_limits<float>::epsilon(); bool heightNearlyEqual = diffHeight < std::numeric_limits<float>::epsilon(); // We don't need to resample if the source and destination are the same. if (widthNearlyEqual && heightNearlyEqual) return RESAMPLE_NONE; if (srcWidth <= kSmallImageSizeThreshold || srcHeight <= kSmallImageSizeThreshold || destWidth <= kSmallImageSizeThreshold || destHeight <= kSmallImageSizeThreshold) { // Never resample small images. These are often used for borders and // rules (think 1x1 images used to make lines). return RESAMPLE_NONE; } if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) { // Large image detected. // Don't resample if it is being stretched a lot in only one direction. // This is trying to catch cases where somebody has created a border // (which might be large) and then is stretching it to fill some part // of the page. if (widthNearlyEqual || heightNearlyEqual) return RESAMPLE_NONE; // The image is growing a lot and in more than one direction. Resampling // is slow and doesn't give us very much when growing a lot. return RESAMPLE_LINEAR; } if ((diffWidth / srcWidth < kFractionalChangeThreshold) && (diffHeight / srcHeight < kFractionalChangeThreshold)) { // It is disappointingly common on the web for image sizes to be off by // one or two pixels. We don't bother resampling if the size difference // is a small fraction of the original size. return RESAMPLE_NONE; } // When the image is not yet done loading, use linear. We don't cache the // partially resampled images, and as they come in incrementally, it causes // us to have to resample the whole thing every time. if (!bitmap.isDataComplete()) return RESAMPLE_LINEAR; // Everything else gets resampled. // High quality interpolation only enabled for scaling and translation. if (!(matrix.getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) return RESAMPLE_AWESOME; return RESAMPLE_LINEAR; }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect) { #if PLATFORM(CHROMIUM) TRACE_EVENT0("skia", "Image::drawPattern"); #endif FloatRect normSrcRect = normalizeRect(floatSrcRect); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw NativeImageSkia* bitmap = nativeImageForCurrentFrame(); if (!bitmap) return; SkMatrix ctm = context->platformContext()->canvas()->getTotalMatrix(); SkMatrix totalMatrix; totalMatrix.setConcat(ctm, patternTransform); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. SkRect destRectTarget; totalMatrix.mapRect(&destRectTarget, normSrcRect); float destBitmapWidth = SkScalarToFloat(destRectTarget.width()); float destBitmapHeight = SkScalarToFloat(destRectTarget.height()); // Compute the resampling mode. ResamplingMode resampling; if (context->platformContext()->isAccelerated() || context->platformContext()->printing()) resampling = RESAMPLE_LINEAR; else resampling = computeResamplingMode(totalMatrix, *bitmap, normSrcRect.width(), normSrcRect.height(), destBitmapWidth, destBitmapHeight); resampling = limitResamplingMode(context->platformContext(), resampling); // Load the transform WebKit requested. SkMatrix matrix(patternTransform); SkShader* shader; if (resampling == RESAMPLE_AWESOME) { // Do nice resampling. float scaleX = destBitmapWidth / normSrcRect.width(); float scaleY = destBitmapHeight / normSrcRect.height(); SkRect scaledSrcRect; SkIRect enclosingScaledSrcRect; // The image fragment generated here is not exactly what is // requested. The scale factor used is approximated and image // fragment is slightly larger to align to integer // boundaries. SkBitmap resampled = extractScaledImageFragment(*bitmap, normSrcRect, scaleX, scaleY, &scaledSrcRect, &enclosingScaledSrcRect); shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); // Since we just resized the bitmap, we need to remove the scale // applied to the pixels in the bitmap shader. This means we need // CTM * patternTransform to have identity scale. Since we // can't modify CTM (or the rectangle will be drawn in the wrong // place), we must set patternTransform's scale to the inverse of // CTM scale. matrix.setScaleX(ctm.getScaleX() ? 1 / ctm.getScaleX() : 1); matrix.setScaleY(ctm.getScaleY() ? 1 / ctm.getScaleY() : 1); } else { // No need to do nice resampling. SkBitmap srcSubset; bitmap->bitmap().extractSubset(&srcSubset, enclosingIntRect(normSrcRect)); shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); } // 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 patter. If WebKit wants // a shifted image, it will shift it from there using the patternTransform. float adjustedX = phase.x() + normSrcRect.x() * narrowPrecisionToFloat(patternTransform.a()); float adjustedY = phase.y() + normSrcRect.y() * narrowPrecisionToFloat(patternTransform.d()); matrix.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); shader->setLocalMatrix(matrix); SkPaint paint; paint.setShader(shader)->unref(); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); context->platformContext()->paintSkPaint(destRect, paint); }
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)); 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 (!(platformContext->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) platformContext->getTotalMatrix().mapRect(&destRectTarget, destRect); resampling = computeResamplingMode(platformContext->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(platformContext, 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->clipRect(destRect); } platformContext->drawBitmapRect(bitmap.bitmap(), &enclosingSrcRect, enclosingDestRect, &paint); if (needsClipping) platformContext->restore(); } platformContext->didDrawRect(destRect, paint, &bitmap.bitmap()); }
static ResamplingMode computeResamplingMode(PlatformContextSkia* platformContext, const NativeImageSkia& bitmap, int srcWidth, int srcHeight, float destWidth, float destHeight) { if (platformContext->hasImageResamplingHint()) { IntSize srcSize; FloatSize dstSize; platformContext->getImageResamplingHint(&srcSize, &dstSize); srcWidth = srcSize.width(); srcHeight = srcSize.height(); destWidth = dstSize.width(); destHeight = dstSize.height(); } int destIWidth = static_cast<int>(destWidth); int destIHeight = static_cast<int>(destHeight); // The percent change below which we will not resample. This usually means // an off-by-one error on the web page, and just doing nearest neighbor // sampling is usually good enough. const float kFractionalChangeThreshold = 0.025f; // Images smaller than this in either direction are considered "small" and // are not resampled ever (see below). const int kSmallImageSizeThreshold = 8; // The amount an image can be stretched in a single direction before we // say that it is being stretched so much that it must be a line or // background that doesn't need resampling. const float kLargeStretch = 3.0f; // Figure out if we should resample this image. We try to prune out some // common cases where resampling won't give us anything, since it is much // slower than drawing stretched. if (srcWidth == destIWidth && srcHeight == destIHeight) { // We don't need to resample if the source and destination are the same. return RESAMPLE_NONE; } if (srcWidth <= kSmallImageSizeThreshold || srcHeight <= kSmallImageSizeThreshold || destWidth <= kSmallImageSizeThreshold || destHeight <= kSmallImageSizeThreshold) { // Never resample small images. These are often used for borders and // rules (think 1x1 images used to make lines). return RESAMPLE_NONE; } if (srcHeight * kLargeStretch <= destHeight || srcWidth * kLargeStretch <= destWidth) { // Large image detected. // Don't resample if it is being stretched a lot in only one direction. // This is trying to catch cases where somebody has created a border // (which might be large) and then is stretching it to fill some part // of the page. if (srcWidth == destWidth || srcHeight == destHeight) return RESAMPLE_NONE; // The image is growing a lot and in more than one direction. Resampling // is slow and doesn't give us very much when growing a lot. return RESAMPLE_LINEAR; } if ((fabs(destWidth - srcWidth) / srcWidth < kFractionalChangeThreshold) && (fabs(destHeight - srcHeight) / srcHeight < kFractionalChangeThreshold)) { // It is disappointingly common on the web for image sizes to be off by // one or two pixels. We don't bother resampling if the size difference // is a small fraction of the original size. return RESAMPLE_NONE; } // When the image is not yet done loading, use linear. We don't cache the // partially resampled images, and as they come in incrementally, it causes // us to have to resample the whole thing every time. if (!bitmap.isDataComplete()) return RESAMPLE_LINEAR; // Everything else gets resampled. // If the platform context permits high quality interpolation, use it. // High quality interpolation only enabled for scaling and translation. if (platformContext->interpolationQuality() == InterpolationHigh && !(platformContext->canvas()->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) return RESAMPLE_AWESOME; return RESAMPLE_LINEAR; }
void Image::drawPattern(GraphicsContext* context, const FloatRect& floatSrcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect) { FloatRect normSrcRect = normalizeRect(floatSrcRect); if (destRect.isEmpty() || normSrcRect.isEmpty()) return; // nothing to draw NativeImageSkia* bitmap = nativeImageForCurrentFrame(); if (!bitmap) return; SkIRect srcRect = enclosingIntRect(normSrcRect); // Figure out what size the bitmap will be in the destination. The // destination rect is the bounds of the pattern, we need to use the // matrix to see how big it will be. float destBitmapWidth, destBitmapHeight; TransformDimensions(patternTransform, srcRect.width(), srcRect.height(), &destBitmapWidth, &destBitmapHeight); // Compute the resampling mode. ResamplingMode resampling; if (context->platformContext()->isAccelerated() || context->platformContext()->printing()) resampling = RESAMPLE_LINEAR; else resampling = computeResamplingMode(context->platformContext(), *bitmap, srcRect.width(), srcRect.height(), destBitmapWidth, destBitmapHeight); // Load the transform WebKit requested. SkMatrix matrix(patternTransform); SkShader* shader; if (resampling == RESAMPLE_AWESOME) { // Do nice resampling. int width = static_cast<int>(destBitmapWidth); int height = static_cast<int>(destBitmapHeight); SkBitmap resampled = bitmap->resizedBitmap(srcRect, width, height); shader = SkShader::CreateBitmapShader(resampled, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); // Since we just resized the bitmap, we need to undo the scale set in // the image transform. matrix.setScaleX(SkIntToScalar(1)); matrix.setScaleY(SkIntToScalar(1)); } else { // No need to do nice resampling. SkBitmap srcSubset; bitmap->bitmap().extractSubset(&srcSubset, srcRect); shader = SkShader::CreateBitmapShader(srcSubset, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode); } // 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 patter. If WebKit wants // a shifted image, it will shift it from there using the patternTransform. float adjustedX = phase.x() + normSrcRect.x() * narrowPrecisionToFloat(patternTransform.a()); float adjustedY = phase.y() + normSrcRect.y() * narrowPrecisionToFloat(patternTransform.d()); matrix.postTranslate(SkFloatToScalar(adjustedX), SkFloatToScalar(adjustedY)); shader->setLocalMatrix(matrix); SkPaint paint; paint.setShader(shader)->unref(); paint.setXfermodeMode(WebCoreCompositeToSkiaComposite(compositeOp)); paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); context->platformContext()->paintSkPaint(destRect, paint); }
// 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. static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeImageSkia& bitmap, const SkIRect& srcIRect, const SkRect& destRect) { // First get the subset we need. This is efficient and does not copy pixels. SkBitmap subset; bitmap.extractSubset(&subset, srcIRect); SkRect srcRect; srcRect.set(srcIRect); // Whether we're doing a subset or using the full source image. bool srcIsFull = srcIRect.fLeft == 0 && srcIRect.fTop == 0 && srcIRect.width() == bitmap.width() && srcIRect.height() == bitmap.height(); // We will always draw in integer sizes, so round the destination rect. SkIRect destRectRounded; destRect.round(&destRectRounded); SkIRect resizedImageRect = // Represents the size of the resized image. { 0, 0, destRectRounded.width(), destRectRounded.height() }; if (srcIsFull && bitmap.hasResizedBitmap(destRectRounded.width(), destRectRounded.height())) { // Yay, this bitmap frame already has a resized version. SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height()); canvas.drawBitmapRect(resampled, 0, destRect, &paint); return; } // Compute the visible portion of our rect. SkRect destBitmapSubsetSk; ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); // The matrix inverting, etc. could have introduced rounding error which // causes the bounds to be outside of the resized bitmap. We round outward // so we always lean toward it being larger rather than smaller than we // need, and then clamp to the bitmap bounds so we don't get any invalid // data. SkIRect destBitmapSubsetSkI; destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); if (!destBitmapSubsetSkI.intersect(resizedImageRect)) return; // Resized image does not intersect. if (srcIsFull && bitmap.shouldCacheResampling( resizedImageRect.width(), resizedImageRect.height(), destBitmapSubsetSkI.width(), destBitmapSubsetSkI.height())) { // We're supposed to resize the entire image and cache it, even though // we don't need all of it. SkBitmap resampled = bitmap.resizedBitmap(destRectRounded.width(), destRectRounded.height()); canvas.drawBitmapRect(resampled, 0, destRect, &paint); } else { // We should only resize the exposed part of the bitmap to do the // minimal possible work. // Resample the needed part of the image. SkBitmap resampled = skia::ImageOperations::Resize(subset, skia::ImageOperations::RESIZE_LANCZOS3, destRectRounded.width(), destRectRounded.height(), destBitmapSubsetSkI); // Compute where the new bitmap should be drawn. Since our new bitmap // may be smaller than the original, we have to shift it over by the // same amount that we cut off the top and left. destBitmapSubsetSkI.offset(destRect.fLeft, destRect.fTop); SkRect offsetDestRect; offsetDestRect.set(destBitmapSubsetSkI); canvas.drawBitmapRect(resampled, 0, offsetDestRect, &paint); } }