void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& transform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode) { if (tileRect.isEmpty()) return; if (!ctxt->drawLuminanceMask()) { Image::drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode); return; } if (!m_cachedImage) { OwnPtr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(tileRect.size())); ASSERT(buffer.get()); ImageObserver* observer = imageObserver(); ASSERT(observer); // Temporarily reset image observer, we don't want to receive any changeInRect() calls due to this relayout. setImageObserver(0); draw(buffer->context(), tileRect, tileRect, styleColorSpace, op, blendMode); setImageObserver(observer); buffer->convertToLuminanceMask(); m_cachedImage = buffer->copyImage(DontCopyBackingStore, Unscaled); m_cachedImage->setSpaceSize(spaceSize()); setImageObserver(observer); } ctxt->setDrawLuminanceMask(false); m_cachedImage->drawPattern(ctxt, tileRect, transform, phase, styleColorSpace, op, destRect, blendMode); }
void SVGImage::drawPatternForContainer(GraphicsContext* context, const FloatSize containerSize, float zoom, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace colorSpace, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode blendMode) { FloatRect zoomedContainerRect = FloatRect(FloatPoint(), containerSize); zoomedContainerRect.scale(zoom); // The ImageBuffer size needs to be scaled to match the final resolution. AffineTransform transform = context->getCTM(); FloatSize imageBufferScale = FloatSize(transform.xScale(), transform.yScale()); ASSERT(imageBufferScale.width()); ASSERT(imageBufferScale.height()); FloatRect imageBufferSize = zoomedContainerRect; imageBufferSize.scale(imageBufferScale.width(), imageBufferScale.height()); std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(expandedIntSize(imageBufferSize.size()), 1); if (!buffer) // Failed to allocate buffer. return; drawForContainer(buffer->context(), containerSize, zoom, imageBufferSize, zoomedContainerRect, ColorSpaceDeviceRGB, CompositeSourceOver, BlendModeNormal); if (context->drawLuminanceMask()) buffer->convertToLuminanceMask(); RefPtr<Image> image = buffer->copyImage(DontCopyBackingStore, Unscaled); image->setSpaceSize(spaceSize()); // Adjust the source rect and transform due to the image buffer's scaling. FloatRect scaledSrcRect = srcRect; scaledSrcRect.scale(imageBufferScale.width(), imageBufferScale.height()); AffineTransform unscaledPatternTransform(patternTransform); unscaledPatternTransform.scale(1 / imageBufferScale.width(), 1 / imageBufferScale.height()); context->setDrawLuminanceMask(false); image->drawPattern(context, scaledSrcRect, unscaledPatternTransform, phase, colorSpace, compositeOp, dstRect, blendMode); }
void GeneratorGeneratedImage::drawPattern(GraphicsContext* destContext, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator compositeOp, const FloatRect& destRect, BlendMode) { // Allow the generator to provide visually-equivalent tiling parameters for better performance. IntSize adjustedSize = m_size; FloatRect adjustedSrcRect = srcRect; m_gradient->adjustParametersForTiledDrawing(adjustedSize, adjustedSrcRect); // Factor in the destination context's scale to generate at the best resolution AffineTransform destContextCTM = destContext->getCTM(GraphicsContext::DefinitelyIncludeDeviceScale); double xScale = fabs(destContextCTM.xScale()); double yScale = fabs(destContextCTM.yScale()); AffineTransform adjustedPatternCTM = patternTransform; adjustedPatternCTM.scale(1.0 / xScale, 1.0 / yScale); adjustedSrcRect.scale(xScale, yScale); unsigned generatorHash = m_gradient->hash(); if (!m_cachedImageBuffer || m_cachedGeneratorHash != generatorHash || m_cachedAdjustedSize != adjustedSize || !destContext->isCompatibleWithBuffer(m_cachedImageBuffer.get())) { m_cachedImageBuffer = destContext->createCompatibleBuffer(adjustedSize, m_gradient->hasAlpha()); if (!m_cachedImageBuffer) return; // Fill with the generated image. m_cachedImageBuffer->context()->fillRect(FloatRect(FloatPoint(), adjustedSize), *m_gradient); m_cachedGeneratorHash = generatorHash; m_cachedAdjustedSize = adjustedSize; } m_cachedImageBuffer->setSpaceSize(spaceSize()); // Tile the image buffer into the context. m_cachedImageBuffer->drawPattern(destContext, adjustedSrcRect, adjustedPatternCTM, phase, styleColorSpace, compositeOp, destRect); }
void BackgroundImageGeometry::setRepeatY(const FillLayer& fillLayer, LayoutUnit unsnappedTileHeight, LayoutUnit snappedAvailableHeight, LayoutUnit unsnappedAvailableHeight, LayoutUnit extraOffset) { // We would like to identify the phase as a fraction of the image size in the // absence of snapping, then re-apply it to the snapped values. This is to // handle large positions. if (unsnappedTileHeight) { LayoutUnit computedYPosition = roundedMinimumValueForLength( fillLayer.yPosition(), unsnappedAvailableHeight); if (fillLayer.backgroundYOrigin() == BottomEdge) { float numberOfTilesInPosition = (snappedAvailableHeight - computedYPosition + extraOffset).toFloat() / unsnappedTileHeight.toFloat(); float fractionalPositionWithinTile = numberOfTilesInPosition - truncf(numberOfTilesInPosition); setPhaseY(LayoutUnit( roundf(fractionalPositionWithinTile * tileSize().height()))); } else { float numberOfTilesInPosition = (computedYPosition + extraOffset).toFloat() / unsnappedTileHeight.toFloat(); float fractionalPositionWithinTile = 1.0f - (numberOfTilesInPosition - truncf(numberOfTilesInPosition)); setPhaseY(LayoutUnit( roundf(fractionalPositionWithinTile * tileSize().height()))); } } else { setPhaseY(LayoutUnit()); } setSpaceSize(LayoutSize(spaceSize().width(), LayoutUnit())); }
void BackgroundImageGeometry::setNoRepeatY(LayoutUnit yOffset) { int roundedOffset = roundToInt(yOffset); m_destRect.move(0, std::max(roundedOffset, 0)); setPhaseY(LayoutUnit(-std::min(roundedOffset, 0))); m_destRect.setHeight(m_tileSize.height() + std::min(roundedOffset, 0)); setSpaceSize(LayoutSize(spaceSize().width(), LayoutUnit())); }
void BackgroundImageGeometry::setNoRepeatX(LayoutUnit xOffset) { int roundedOffset = roundToInt(xOffset); m_destRect.move(std::max(roundedOffset, 0), 0); setPhaseX(LayoutUnit(-std::min(roundedOffset, 0))); m_destRect.setWidth(m_tileSize.width() + std::min(roundedOffset, 0)); setSpaceSize(LayoutSize(LayoutUnit(), spaceSize().height())); }
void BackgroundImageGeometry::setSpaceY(LayoutUnit space, LayoutUnit availableHeight, LayoutUnit extraOffset) { LayoutUnit computedYPosition = roundedMinimumValueForLength(Length(), availableHeight); setSpaceSize(LayoutSize(spaceSize().width().toInt(), space.round())); LayoutUnit actualHeight = tileSize().height() + space; setPhaseY(actualHeight ? LayoutUnit(roundf( actualHeight - fmodf((computedYPosition + extraOffset), actualHeight))) : LayoutUnit()); }
void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, ColorSpace styleColorSpace, CompositeOperator op, BlendMode blendMode) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, op); return; } ASSERT(!isBitmapImage() || notSolidColor()); FloatSize intrinsicTileSize = size(); if (hasRelativeWidth()) intrinsicTileSize.setWidth(scaledTileSize.width()); if (hasRelativeHeight()) intrinsicTileSize.setHeight(scaledTileSize.height()); FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); FloatRect oneTileRect; FloatSize actualTileSize(scaledTileSize.width() + spaceSize().width(), scaledTileSize.height() + spaceSize().height()); oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), actualTileSize.width()) - actualTileSize.width(), actualTileSize.width())); oneTileRect.setY(destRect.y() + fmodf(fmodf(-srcPoint.y(), actualTileSize.height()) - actualTileSize.height(), actualTileSize.height())); oneTileRect.setSize(scaledTileSize); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. if (oneTileRect.contains(destRect) && !ctxt->drawLuminanceMask()) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, styleColorSpace, op, blendMode); return; } AffineTransform patternTransform = AffineTransform().scaleNonUniform(scale.width(), scale.height()); FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), styleColorSpace, op, destRect, blendMode); startAnimation(); }
void SVGImageForContainer::drawPattern(GraphicsContext* context, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace colorSpace, CompositeOperator compositeOp, const FloatRect& dstRect, BlendMode) { m_image->setSpaceSize(spaceSize()); m_image->drawPatternForContainer(context, m_containerSize, m_zoom, srcRect, patternTransform, phase, colorSpace, compositeOp, dstRect); }
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace styleColorSpace, CompositeOperator op, const FloatRect& destRect, BlendMode blendMode) { if (!nativeImageForCurrentFrame()) return; if (!patternTransform.isInvertible()) return; CGContextRef context = ctxt->platformContext(); GraphicsContextStateSaver stateSaver(*ctxt); CGContextClipToRect(context, destRect); ctxt->setCompositeOperation(op, blendMode); CGContextTranslateCTM(context, destRect.x(), destRect.y() + destRect.height()); CGContextScaleCTM(context, 1, -1); // Compute the scaled tile size. float scaledTileHeight = tileRect.height() * narrowPrecisionToFloat(patternTransform.d()); // We have to adjust the phase to deal with the fact we're in Cartesian space now (with the bottom left corner of destRect being // the origin). float adjustedX = phase.x() - destRect.x() + tileRect.x() * narrowPrecisionToFloat(patternTransform.a()); // We translated the context so that destRect.x() is the origin, so subtract it out. float adjustedY = destRect.height() - (phase.y() - destRect.y() + tileRect.y() * narrowPrecisionToFloat(patternTransform.d()) + scaledTileHeight); CGImageRef tileImage = nativeImageForCurrentFrame(); float h = CGImageGetHeight(tileImage); RetainPtr<CGImageRef> subImage; if (tileRect.size() == size()) subImage = tileImage; else { // Copying a sub-image out of a partially-decoded image stops the decoding of the original image. It should never happen // because sub-images are only used for border-image, which only renders when the image is fully decoded. ASSERT(h == height()); subImage = adoptCF(CGImageCreateWithImageInRect(tileImage, tileRect)); } // Adjust the color space. subImage = Image::imageWithColorSpace(subImage.get(), styleColorSpace); // Leopard has an optimized call for the tiling of image patterns, but we can only use it if the image has been decoded enough that // its buffer is the same size as the overall image. Because a partially decoded CGImageRef with a smaller width or height than the // overall image buffer needs to tile with "gaps", we can't use the optimized tiling call in that case. // FIXME: We cannot use CGContextDrawTiledImage with scaled tiles on Leopard, because it suffers from rounding errors. Snow Leopard is ok. float scaledTileWidth = tileRect.width() * narrowPrecisionToFloat(patternTransform.a()); float w = CGImageGetWidth(tileImage); if (w == size().width() && h == size().height() && !spaceSize().width() && !spaceSize().height()) CGContextDrawTiledImage(context, FloatRect(adjustedX, adjustedY, scaledTileWidth, scaledTileHeight), subImage.get()); else { // On Leopard and newer, this code now only runs for partially decoded images whose buffers do not yet match the overall size of the image. static const CGPatternCallbacks patternCallbacks = { 0, drawPatternCallback, patternReleaseCallback }; CGAffineTransform matrix = CGAffineTransformMake(narrowPrecisionToCGFloat(patternTransform.a()), 0, 0, narrowPrecisionToCGFloat(patternTransform.d()), adjustedX, adjustedY); matrix = CGAffineTransformConcat(matrix, CGContextGetCTM(context)); // The top of a partially-decoded image is drawn at the bottom of the tile. Map it to the top. matrix = CGAffineTransformTranslate(matrix, 0, size().height() - h); #if PLATFORM(IOS) matrix = CGAffineTransformScale(matrix, 1, -1); matrix = CGAffineTransformTranslate(matrix, 0, -h); #endif CGImageRef platformImage = CGImageRetain(subImage.get()); RetainPtr<CGPatternRef> pattern = adoptCF(CGPatternCreate(platformImage, CGRectMake(0, 0, tileRect.width(), tileRect.height()), matrix, tileRect.width() + spaceSize().width() * (1 / narrowPrecisionToFloat(patternTransform.a())), tileRect.height() + spaceSize().height() * (1 / narrowPrecisionToFloat(patternTransform.d())), kCGPatternTilingConstantSpacing, true, &patternCallbacks)); if (!pattern) return; RetainPtr<CGColorSpaceRef> patternSpace = adoptCF(CGColorSpaceCreatePattern(0)); CGFloat alpha = 1; RetainPtr<CGColorRef> color = adoptCF(CGColorCreateWithPattern(patternSpace.get(), pattern.get(), &alpha)); CGContextSetFillColorSpace(context, patternSpace.get()); // FIXME: Really want a public API for this. It is just CGContextSetBaseCTM(context, CGAffineTransformIdentiy). wkSetBaseCTM(context, CGAffineTransformIdentity); CGContextSetPatternPhase(context, CGSizeZero); CGContextSetFillColorWithColor(context, color.get()); CGContextFillRect(context, CGContextGetClipBoundingBox(context)); } stateSaver.restore(); if (imageObserver()) imageObserver()->didDraw(this); }