void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp) { CGImageRef image = frameAtIndex(m_currentFrame); if (!image) // If it's too early we won't have an image yet. return; if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), compositeOp); return; } CGContextRef context = ctxt->platformContext(); ctxt->save(); // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image // and then set a clip to the portion that we want to display. CGSize selfSize = size(); FloatRect adjustedDestRect = destRect; if (srcRect.width() != selfSize.width || srcRect.height() != selfSize.height) { // A subportion of the image is drawing. Adjust the destination rect to // account for this. float xScale = srcRect.width() / destRect.width(); float yScale = srcRect.height() / destRect.height(); adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale)); adjustedDestRect.setSize(FloatSize(size().width() / xScale, size().height() / yScale)); CGContextClipToRect(context, destRect); } // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. float currHeight = CGImageGetHeight(image); if (currHeight < selfSize.height) adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height); // Flip the coords. ctxt->setCompositeOperation(compositeOp); CGContextTranslateCTM(context, adjustedDestRect.x(), adjustedDestRect.bottom()); CGContextScaleCTM(context, 1, -1); adjustedDestRect.setLocation(FloatPoint()); // Draw the image. CGContextDrawImage(context, adjustedDestRect, image); ctxt->restore(); startAnimation(); if (imageObserver()) imageObserver()->didDraw(this); }
static inline void calculateGlyphBoundaries(const QueryData* queryData, const SVGTextFragment& fragment, int startPosition, FloatRect& extent) { float scalingFactor = queryData->textLayoutObject->scalingFactor(); ASSERT(scalingFactor); FloatPoint glyphPosition = calculateGlyphPositionWithoutTransform(queryData, fragment, startPosition); glyphPosition.move(0, -queryData->textLayoutObject->scaledFont().fontMetrics().floatAscent() / scalingFactor); extent.setLocation(glyphPosition); // Use the SVGTextMetrics computed by SVGTextMetricsBuilder (which spends // time attempting to compute more correct glyph bounds already, handling // cursive scripts to some degree.) const Vector<SVGTextMetrics>& textMetricsValues = queryData->textLayoutObject->layoutAttributes()->textMetricsValues(); const SVGTextMetrics& metrics = findMetricsForCharacter(textMetricsValues, fragment, startPosition); // TODO(fs): Negative glyph extents seems kind of weird to have, but // presently it can occur in some cases (like Arabic.) FloatSize glyphSize(std::max<float>(metrics.width(), 0), std::max<float>(metrics.height(), 0)); extent.setSize(glyphSize); // If RTL, adjust the starting point to align with the LHS of the glyph bounding box. if (!queryData->textBox->isLeftToRightDirection()) { if (queryData->isVerticalText) extent.move(0, -glyphSize.height()); else extent.move(-glyphSize.width(), 0); } AffineTransform fragmentTransform; fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); extent = fragmentTransform.mapRect(extent); }
bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, FloatRect& indicatorRect, FloatRect& replacementTextRect, FloatRect& arrowRect, FontCascade& font, TextRun& run, float& textWidth) const { bool includesArrow = shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason); contentRect = contentBoxRect(); contentRect.moveBy(roundedIntPoint(accumulatedOffset)); FontCascadeDescription fontDescription; RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription); fontDescription.setWeight(FontWeightBold); fontDescription.setRenderingMode(frame().settings().fontRenderingMode()); fontDescription.setComputedSize(12); font = FontCascade(fontDescription, 0, 0); font.update(0); run = TextRun(m_unavailablePluginReplacementText); textWidth = font.width(run); replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftTextMargin + (includesArrow ? replacementTextRoundedRectRightTextMarginWithArrow : replacementTextRoundedRectRightTextMargin), replacementTextRoundedRectHeight)); float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x(); float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); replacementTextRect.setLocation(FloatPoint(x, y)); indicatorRect = replacementTextRect; // Expand the background rect to include the arrow, if it will be used. if (includesArrow) { arrowRect = indicatorRect; arrowRect.setX(ceilf(arrowRect.maxX() + replacementArrowLeftMargin)); arrowRect.setWidth(arrowRect.height()); indicatorRect.unite(arrowRect); } return true; }
bool RenderEmbeddedObject::getReplacementTextGeometry(int tx, int ty, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, Font& font, TextRun& run, float& textWidth) { contentRect = contentBoxRect(); contentRect.move(tx, ty); FontDescription fontDescription; RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription); fontDescription.setWeight(FontWeightBold); Settings* settings = document()->settings(); ASSERT(settings); if (!settings) return false; fontDescription.setRenderingMode(settings->fontRenderingMode()); fontDescription.setComputedSize(fontDescription.specifiedSize()); font = Font(fontDescription, 0, 0); font.update(0); run = TextRun(m_replacementText.characters(), m_replacementText.length()); run.disableRoundingHacks(); textWidth = font.floatWidth(run); replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight)); float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x(); float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); replacementTextRect.setLocation(FloatPoint(x, y)); path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); return true; }
static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const SVGTextFragment& fragment, int startPosition, FloatRect& extent) { float scalingFactor = queryData->textRenderer->scalingFactor(); ASSERT(scalingFactor); extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor)); if (startPosition) { SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition); if (queryData->isVerticalText) extent.move(0, metrics.height()); else extent.move(metrics.width(), 0); } SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, 1); extent.setSize(FloatSize(metrics.width(), metrics.height())); AffineTransform fragmentTransform; fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); if (fragmentTransform.isIdentity()) return; extent = fragmentTransform.mapRect(extent); }
void drawNativeImage(const NativeImagePtr& image, GraphicsContext& context, const FloatRect& destRect, const FloatRect& srcRect, const IntSize&, CompositeOperator op, BlendMode mode, const ImageOrientation& orientation) { context.save(); // Set the compositing operation. if (op == CompositeSourceOver && mode == BlendModeNormal && !nativeImageHasAlpha(image)) context.setCompositeOperation(CompositeCopy); else context.setCompositeOperation(op, mode); #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) IntSize scaledSize = nativeImageSize(image); FloatRect adjustedSrcRect = adjustSourceRectForDownSampling(srcRect, scaledSize); #else FloatRect adjustedSrcRect(srcRect); #endif FloatRect adjustedDestRect = destRect; if (orientation != DefaultImageOrientation) { // ImageOrientation expects the origin to be at (0, 0). context.translate(destRect.x(), destRect.y()); adjustedDestRect.setLocation(FloatPoint()); context.concatCTM(orientation.transformFromDefault(adjustedDestRect.size())); if (orientation.usesWidthAsHeight()) { // The destination rectangle 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. adjustedDestRect.setSize(adjustedDestRect.size().transposedSize()); } } context.platformContext()->drawSurfaceToContext(image.get(), adjustedDestRect, adjustedSrcRect, context); context.restore(); }
bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, Font& font, TextRun& run, float& textWidth) const { contentRect = contentBoxRect(); contentRect.moveBy(roundedIntPoint(accumulatedOffset)); FontDescription fontDescription; RenderTheme::theme().systemFont(CSSValueWebkitSmallControl, fontDescription); fontDescription.setWeight(FontWeightBold); Settings* settings = document().settings(); ASSERT(settings); if (!settings) return false; fontDescription.setComputedSize(fontDescription.specifiedSize()); font = Font(fontDescription, 0, 0); font.update(0); run = TextRun(m_unavailablePluginReplacementText); textWidth = font.width(run); replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight)); float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x(); float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); replacementTextRect.setLocation(FloatPoint(x, y)); path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); return true; }
GraphicsContext* FilterEffectRendererHelper::beginFilterEffect(RenderLayer* renderLayer, GraphicsContext* oldContext, const LayoutRect& filterRect) { ASSERT(m_haveFilterEffect && renderLayer->filter()); m_savedGraphicsContext = oldContext; m_renderLayer = renderLayer; m_paintOffset = filterRect.location(); FloatRect filterSourceRect = filterRect; filterSourceRect.setLocation(LayoutPoint()); FilterEffectRenderer* filter = renderLayer->filter(); filter->updateBackingStore(filterSourceRect); filter->prepare(); // Paint into the context that represents the SourceGraphic of the filter. GraphicsContext* sourceGraphicsContext = filter->inputContext(); if (!sourceGraphicsContext) { // Could not allocate a new graphics context. Disable the filters and continue. m_haveFilterEffect = false; return m_savedGraphicsContext; } sourceGraphicsContext->save(); sourceGraphicsContext->translate(-filterRect.x(), -filterRect.y()); sourceGraphicsContext->clearRect(filterRect); return sourceGraphicsContext; }
bool RenderTheme::paintMeter(RenderObject* renderObject, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) { // Some platforms do not have a native gauge widget, so we draw here a default implementation. RenderMeter* renderMeter = toRenderMeter(renderObject); RenderStyle* style = renderObject->style(); int left = style->borderLeft().width() + style->paddingLeft().value(); int top = style->borderTop().width() + style->paddingTop().value(); int right = style->borderRight().width() + style->paddingRight().value(); int bottom = style->borderBottom().width() + style->paddingBottom().value(); FloatRect innerRect(rect.x() + left, rect.y() + top, rect.width() - left - right, rect.height() - top - bottom); HTMLMeterElement* element = static_cast<HTMLMeterElement*>(renderMeter->node()); double min = element->min(); double max = element->max(); double value = element->value(); if (min >= max) { paintInfo.context->fillRect(innerRect, Color::black, style->colorSpace()); return false; } // Paint the background first paintInfo.context->fillRect(innerRect, Color::lightGray, style->colorSpace()); FloatRect valueRect; if (rect.width() < rect.height()) { // Vertical gauge double scale = innerRect.height() / (max - min); valueRect.setLocation(FloatPoint(innerRect.x(), innerRect.y() + narrowPrecisionToFloat((max - value) * scale))); valueRect.setSize(FloatSize(innerRect.width(), narrowPrecisionToFloat((value - min) * scale))); } else if (renderMeter->style()->direction() == RTL) { // right to left horizontal gauge double scale = innerRect.width() / (max - min); valueRect.setLocation(FloatPoint(innerRect.x() + narrowPrecisionToFloat((max - value) * scale), innerRect.y())); valueRect.setSize(FloatSize(narrowPrecisionToFloat((value - min) * scale), innerRect.height())); } else { // left to right horizontal gauge double scale = innerRect.width() / (max - min); valueRect.setLocation(innerRect.location()); valueRect.setSize(FloatSize(narrowPrecisionToFloat((value - min)) * scale, innerRect.height())); } if (!valueRect.isEmpty()) paintInfo.context->fillRect(valueRect, Color::black, style->colorSpace()); return false; }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, const FloatRect& srcRect, SkXfermode::Mode compositeOp, RespectImageOrientationEnum shouldRespectImageOrientation) { TRACE_EVENT0("skia", "BitmapImage::draw"); SkBitmap bitmap; if (!bitmapForCurrentFrame(&bitmap)) return; // It's too early and we don't have an image yet. FloatRect normDstRect = adjustForNegativeSize(dstRect); FloatRect normSrcRect = adjustForNegativeSize(srcRect); normSrcRect.intersect(FloatRect(0, 0, bitmap.width(), 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()); } } bool isLazyDecoded = DeferredImageDecoder::isLazyDecoded(bitmap); bool isOpaque = bitmap.isOpaque(); { SkPaint paint; SkRect skSrcRect = normSrcRect; int initialSaveCount = ctxt->preparePaintForDrawRectToRect(&paint, skSrcRect, normDstRect, compositeOp, !isOpaque, isLazyDecoded, bitmap.isImmutable()); // We want to filter it if we decided to do 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. ctxt->drawBitmapRect(bitmap, &skSrcRect, normDstRect, &paint); ctxt->canvas()->restoreToCount(initialSaveCount); } if (isLazyDecoded) PlatformInstrumentation::didDrawLazyPixelRef(bitmap.getGenerationID()); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); startAnimation(); }
void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op, BlendMode, RespectImageOrientationEnum shouldRespectImageOrientation) { if (!dst.width() || !dst.height() || !src.width() || !src.height()) return; startAnimation(); NativeImageCairo* nativeImage = frameAtIndex(m_currentFrame); if (!nativeImage) // If it's too early we won't have an image yet. return; if (mayFillWithSolidColor()) { fillWithSolidColor(context, dst, solidColor(), styleColorSpace, op); return; } context->save(); // Set the compositing operation. if (op == CompositeSourceOver && !frameHasAlphaAtIndex(m_currentFrame)) context->setCompositeOperation(CompositeCopy); else context->setCompositeOperation(op); #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) cairo_surface_t* surface = nativeImage->surface(); IntSize scaledSize(cairo_image_surface_get_width(surface), cairo_image_surface_get_height(surface)); FloatRect adjustedSrcRect = adjustSourceRectForDownSampling(src, scaledSize); #else FloatRect adjustedSrcRect(src); #endif ImageOrientation orientation = DefaultImageOrientation; if (shouldRespectImageOrientation == RespectImageOrientation) orientation = frameOrientationAtIndex(m_currentFrame); FloatRect dstRect = dst; if (orientation != DefaultImageOrientation) { // ImageOrientation expects the origin to be at (0, 0). context->translate(dstRect.x(), dstRect.y()); dstRect.setLocation(FloatPoint()); context->concatCTM(orientation.transformFromDefault(dstRect.size())); if (orientation.usesWidthAsHeight()) { // The destination rectangle 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. dstRect = FloatRect(dstRect.x(), dstRect.y(), dstRect.height(), dstRect.width()); } } context->platformContext()->drawSurfaceToContext(nativeImage->surface(), dstRect, adjustedSrcRect, context); context->restore(); if (imageObserver()) imageObserver()->didDraw(this); }
void BitmapImage::draw(GraphicsContext& context, const FloatRect& dst, const FloatRect& src, CompositeOperator op, BlendMode blendMode, ImageOrientationDescription description) { if (!dst.width() || !dst.height() || !src.width() || !src.height()) return; startAnimation(); auto surface = frameImageAtIndex(m_currentFrame); if (!surface) // If it's too early we won't have an image yet. return; Color color = singlePixelSolidColor(); if (color.isValid()) { fillWithSolidColor(context, dst, color, op); return; } context.save(); // Set the compositing operation. if (op == CompositeSourceOver && blendMode == BlendModeNormal && !frameHasAlphaAtIndex(m_currentFrame)) context.setCompositeOperation(CompositeCopy); else context.setCompositeOperation(op, blendMode); #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) IntSize scaledSize = cairoSurfaceSize(surface.get()); FloatRect adjustedSrcRect = adjustSourceRectForDownSampling(src, scaledSize); #else FloatRect adjustedSrcRect(src); #endif ImageOrientation frameOrientation(description.imageOrientation()); if (description.respectImageOrientation() == RespectImageOrientation) frameOrientation = frameOrientationAtIndex(m_currentFrame); FloatRect dstRect = dst; if (frameOrientation != DefaultImageOrientation) { // ImageOrientation expects the origin to be at (0, 0). context.translate(dstRect.x(), dstRect.y()); dstRect.setLocation(FloatPoint()); context.concatCTM(frameOrientation.transformFromDefault(dstRect.size())); if (frameOrientation.usesWidthAsHeight()) { // The destination rectangle 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. dstRect = FloatRect(dstRect.x(), dstRect.y(), dstRect.height(), dstRect.width()); } } context.platformContext()->drawSurfaceToContext(surface.get(), dstRect, adjustedSrcRect, context); context.restore(); if (imageObserver()) imageObserver()->didDraw(this); }
FloatRect SVGSVGElement::viewport() const { FloatRect viewRectangle; if (!isOutermostSVG()) viewRectangle.setLocation(FloatPoint(x().value(this), y().value(this))); viewRectangle.setSize(FloatSize(width().value(this), height().value(this))); return viewBoxToViewTransform(viewRectangle.width(), viewRectangle.height()).mapRect(viewRectangle); }
FloatRect SVGSVGElement::viewport() const { // FIXME: This method doesn't follow the spec and is basically untested. Parent documents are not considered here. SVGLengthContext lengthContext(this); FloatRect viewRectangle; if (!isOutermostSVG()) viewRectangle.setLocation(FloatPoint(x().value(lengthContext), y().value(lengthContext))); viewRectangle.setSize(FloatSize(width().value(lengthContext), height().value(lengthContext))); return viewBoxToViewTransform(viewRectangle.width(), viewRectangle.height()).mapRect(viewRectangle); }
void RenderEmbeddedObject::paintReplaced(PaintInfo& paintInfo, int tx, int ty) { if (!m_replacementText) return; if (paintInfo.phase == PaintPhaseSelection) return; GraphicsContext* context = paintInfo.context; if (context->paintingDisabled()) return; FloatRect pluginRect = contentBoxRect(); pluginRect.move(tx, ty); FontDescription fontDescription; RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription); fontDescription.setWeight(FontWeightBold); Settings* settings = document()->settings(); ASSERT(settings); if (!settings) return; fontDescription.setRenderingMode(settings->fontRenderingMode()); fontDescription.setComputedSize(fontDescription.specifiedSize()); Font font(fontDescription, 0, 0); font.update(0); TextRun run(m_replacementText.characters(), m_replacementText.length()); run.disableRoundingHacks(); float textWidth = font.floatWidth(run); FloatRect replacementTextRect; replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight)); replacementTextRect.setLocation(FloatPoint((pluginRect.size().width() / 2 - replacementTextRect.size().width() / 2) + pluginRect.location().x(), (pluginRect.size().height() / 2 - replacementTextRect.size().height() / 2) + pluginRect.location().y())); Path path = Path::createRoundedRectangle(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); context->save(); context->clip(pluginRect); context->beginPath(); context->addPath(path); context->setAlpha(replacementTextRoundedRectOpacity); context->setFillColor(Color::white, style()->colorSpace()); context->fillPath(); FloatPoint labelPoint(roundf(replacementTextRect.location().x() + (replacementTextRect.size().width() - textWidth) / 2), roundf(replacementTextRect.location().y()+ (replacementTextRect.size().height() - font.height()) / 2 + font.ascent())); context->setAlpha(replacementTextTextOpacity); context->setFillColor(Color::black, style()->colorSpace()); context->drawBidiText(font, run, labelPoint); context->restore(); }
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); }
void PlatformCALayer::drawRepaintIndicator(CGContextRef context, PlatformCALayer* platformCALayer, int repaintCount, CGColorRef customBackgroundColor) { char text[16]; // that's a lot of repaints snprintf(text, sizeof(text), "%d", repaintCount); FloatRect indicatorBox = platformCALayer->bounds();\ indicatorBox.setLocation( { 1, 1 } ); indicatorBox.setSize(FloatSize(12 + 10 * strlen(text), 27)); CGContextStateSaver stateSaver(context); CGContextSetAlpha(context, 0.5f); CGContextBeginTransparencyLayerWithRect(context, indicatorBox, 0); if (customBackgroundColor) CGContextSetFillColorWithColor(context, customBackgroundColor); else CGContextSetRGBFillColor(context, 0, 0.5f, 0.25f, 1); if (platformCALayer->isOpaque()) CGContextFillRect(context, indicatorBox); else { Path boundsPath; boundsPath.moveTo(indicatorBox.maxXMinYCorner()); boundsPath.addLineTo(indicatorBox.maxXMaxYCorner()); boundsPath.addLineTo(indicatorBox.minXMaxYCorner()); const float cornerChunk = 8; boundsPath.addLineTo(FloatPoint(indicatorBox.x(), indicatorBox.y() + cornerChunk)); boundsPath.addLineTo(FloatPoint(indicatorBox.x() + cornerChunk, indicatorBox.y())); boundsPath.closeSubpath(); CGContextAddPath(context, boundsPath.platformPath()); CGContextFillPath(context); } if (platformCALayer->owner()->isUsingDisplayListDrawing(platformCALayer)) { CGContextSetRGBStrokeColor(context, 0, 0, 0, 0.65); CGContextSetLineWidth(context, 2); CGContextStrokeRect(context, indicatorBox); } if (platformCALayer->acceleratesDrawing()) CGContextSetRGBFillColor(context, 1, 0, 0, 1); else CGContextSetRGBFillColor(context, 1, 1, 1, 1); platformCALayer->drawTextAtPoint(context, indicatorBox.x() + 5, indicatorBox.y() + 22, CGSizeMake(1, -1), 22, text, strlen(text)); CGContextEndTransparencyLayer(context); }
void BitmapImage::draw(SkCanvas* canvas, const SkPaint& paint, const FloatRect& dstRect, const FloatRect& srcRect, RespectImageOrientationEnum shouldRespectImageOrientation, ImageClampingMode clampMode) { TRACE_EVENT0("skia", "BitmapImage::draw"); RefPtr<SkImage> image = imageForCurrentFrame(); if (!image) return; // It's too early and we don't have an image yet. FloatRect adjustedSrcRect = srcRect; adjustedSrcRect.intersect(FloatRect(0, 0, image->width(), image->height())); if (adjustedSrcRect.isEmpty() || dstRect.isEmpty()) return; // Nothing to draw. ImageOrientation orientation = DefaultImageOrientation; if (shouldRespectImageOrientation == RespectImageOrientation) orientation = frameOrientationAtIndex(m_currentFrame); int initialSaveCount = canvas->getSaveCount(); FloatRect adjustedDstRect = dstRect; if (orientation != DefaultImageOrientation) { canvas->save(); // ImageOrientation expects the origin to be at (0, 0) canvas->translate(adjustedDstRect.x(), adjustedDstRect.y()); adjustedDstRect.setLocation(FloatPoint()); canvas->concat(affineTransformToSkMatrix(orientation.transformFromDefault(adjustedDstRect.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. adjustedDstRect = FloatRect(adjustedDstRect.x(), adjustedDstRect.y(), adjustedDstRect.height(), adjustedDstRect.width()); } } SkRect skSrcRect = adjustedSrcRect; canvas->drawImageRect(image.get(), skSrcRect, adjustedDstRect, &paint, WebCoreClampingModeToSkiaRectConstraint(clampMode)); canvas->restoreToCount(initialSaveCount); if (currentFrameIsLazyDecoded()) PlatformInstrumentation::didDrawLazyPixelRef(image->uniqueID()); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); startAnimation(); }
bool RenderEmbeddedObject::getReplacementTextGeometry(const LayoutPoint& accumulatedOffset, FloatRect& contentRect, Path& path, FloatRect& replacementTextRect, FloatRect& arrowRect, Font& font, TextRun& run, float& textWidth) const { contentRect = contentBoxRect(); contentRect.moveBy(roundedIntPoint(accumulatedOffset)); FontDescription fontDescription; RenderTheme::defaultTheme()->systemFont(CSSValueWebkitSmallControl, fontDescription); fontDescription.setWeight(FontWeightBold); Settings* settings = document()->settings(); ASSERT(settings); if (!settings) return false; fontDescription.setRenderingMode(settings->fontRenderingMode()); fontDescription.setComputedSize(fontDescription.specifiedSize()); font = Font(fontDescription, 0, 0); font.update(0); run = TextRun(m_unavailablePluginReplacementText); textWidth = font.width(run); replacementTextRect.setSize(FloatSize(textWidth + replacementTextRoundedRectLeftRightTextMargin * 2, replacementTextRoundedRectHeight)); float x = (contentRect.size().width() / 2 - replacementTextRect.size().width() / 2) + contentRect.location().x(); float y = (contentRect.size().height() / 2 - replacementTextRect.size().height() / 2) + contentRect.location().y(); replacementTextRect.setLocation(FloatPoint(x, y)); replacementTextRect.setHeight(replacementTextRect.height() + replacementTextRoundedRectBottomTextPadding); path.addRoundedRect(replacementTextRect, FloatSize(replacementTextRoundedRectRadius, replacementTextRoundedRectRadius)); if (shouldUnavailablePluginMessageBeButton(document(), m_pluginUnavailabilityReason)) { arrowRect = path.boundingRect(); arrowRect.setX(ceilf(arrowRect.maxX() + replacementArrowLeftMargin)); arrowRect.setWidth(arrowRect.height()); arrowRect.inflate(-0.5); path.addEllipse(arrowRect); addReplacementArrowPath(path, arrowRect); } return true; }
void TileController::adjustTileCoverageRect(FloatRect& coverageRect, const FloatSize& newSize, const FloatRect& previousVisibleRect, const FloatRect& visibleRect, float contentsScale) const { // If the page is not in a window (for example if it's in a background tab), we limit the tile coverage rect to the visible rect. if (!m_isInWindow) { coverageRect = visibleRect; return; } #if PLATFORM(IOS) // FIXME: unify the iOS and Mac code. UNUSED_PARAM(previousVisibleRect); if (m_tileCoverage == CoverageForVisibleArea || MemoryPressureHandler::singleton().isUnderMemoryPressure()) { coverageRect = visibleRect; return; } double horizontalMargin = kDefaultTileSize / contentsScale; double verticalMargin = kDefaultTileSize / contentsScale; double currentTime = monotonicallyIncreasingTime(); double timeDelta = currentTime - m_velocity.lastUpdateTime; FloatRect futureRect = visibleRect; futureRect.setLocation(FloatPoint( futureRect.location().x() + timeDelta * m_velocity.horizontalVelocity, futureRect.location().y() + timeDelta * m_velocity.verticalVelocity)); if (m_velocity.horizontalVelocity) { futureRect.setWidth(futureRect.width() + horizontalMargin); if (m_velocity.horizontalVelocity < 0) futureRect.setX(futureRect.x() - horizontalMargin); } if (m_velocity.verticalVelocity) { futureRect.setHeight(futureRect.height() + verticalMargin); if (m_velocity.verticalVelocity < 0) futureRect.setY(futureRect.y() - verticalMargin); } if (!m_velocity.horizontalVelocity && !m_velocity.verticalVelocity) { if (m_velocity.scaleChangeRate > 0) { coverageRect = visibleRect; return; } futureRect.setWidth(futureRect.width() + horizontalMargin); futureRect.setHeight(futureRect.height() + verticalMargin); futureRect.setX(futureRect.x() - horizontalMargin / 2); futureRect.setY(futureRect.y() - verticalMargin / 2); } // Can't use m_tileCacheLayer->bounds() here, because the size of the underlying platform layer // hasn't been updated for the current commit. IntSize contentSize = expandedIntSize(newSize); if (futureRect.maxX() > contentSize.width()) futureRect.setX(contentSize.width() - futureRect.width()); if (futureRect.maxY() > contentSize.height()) futureRect.setY(contentSize.height() - futureRect.height()); if (futureRect.x() < 0) futureRect.setX(0); if (futureRect.y() < 0) futureRect.setY(0); coverageRect.unite(futureRect); return; #else UNUSED_PARAM(contentsScale); // FIXME: look at how far the document can scroll in each dimension. FloatSize coverageSize = visibleRect.size(); bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect); // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height. // These values were chosen because it's more common to have tall pages and to scroll vertically, // so we keep more tiles above and below the current area. float widthScale = 1; float heightScale = 1; if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange) widthScale = 2; if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange) heightScale = 3; coverageSize.scale(widthScale, heightScale); FloatRect coverageBounds = boundsForSize(newSize); FloatRect coverage = expandRectWithinRect(visibleRect, coverageSize, coverageBounds); LOG_WITH_STREAM(Scrolling, stream << "TileController::computeTileCoverageRect newSize=" << newSize << " mode " << m_tileCoverage << " expanded to " << coverageSize << " bounds with margin " << coverageBounds << " coverage " << coverage); coverageRect.unite(coverage); #endif }
PassOwnPtr<ImageBuffer> RenderSVGResourcePattern::createTileImage(PatternData* patternData, const SVGPatternElement* patternElement, RenderObject* object) const { PatternAttributes attributes = patternElement->collectPatternProperties(); // If we couldn't determine the pattern content element root, stop here. if (!attributes.patternContentElement()) return 0; FloatRect objectBoundingBox = object->objectBoundingBox(); FloatRect patternBoundaries = calculatePatternBoundaries(attributes, objectBoundingBox, patternElement); AffineTransform patternTransform = attributes.patternTransform(); AffineTransform viewBoxCTM = patternElement->viewBoxToViewTransform(patternElement->viewBox(), patternElement->preserveAspectRatio(), patternBoundaries.width(), patternBoundaries.height()); FloatRect patternBoundariesIncludingOverflow = calculatePatternBoundariesIncludingOverflow(attributes, objectBoundingBox, viewBoxCTM, patternBoundaries); IntSize imageSize(lroundf(patternBoundariesIncludingOverflow.width()), lroundf(patternBoundariesIncludingOverflow.height())); // FIXME: We should be able to clip this more, needs investigation clampImageBufferSizeToViewport(object->document()->view(), imageSize); // Don't create ImageBuffers with image size of 0 if (imageSize.isEmpty()) return 0; OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(imageSize); GraphicsContext* context = tileImage->context(); ASSERT(context); context->save(); // Translate to pattern start origin if (patternBoundariesIncludingOverflow.location() != patternBoundaries.location()) { context->translate(patternBoundaries.x() - patternBoundariesIncludingOverflow.x(), patternBoundaries.y() - patternBoundariesIncludingOverflow.y()); patternBoundaries.setLocation(patternBoundariesIncludingOverflow.location()); } // Process viewBox or boundingBoxModeContent correction if (!viewBoxCTM.isIdentity()) context->concatCTM(viewBoxCTM); else if (attributes.boundingBoxModeContent()) { context->translate(objectBoundingBox.x(), objectBoundingBox.y()); context->scale(FloatSize(objectBoundingBox.width(), objectBoundingBox.height())); } // Render subtree into ImageBuffer for (Node* node = attributes.patternContentElement()->firstChild(); node; node = node->nextSibling()) { if (!node->isSVGElement() || !static_cast<SVGElement*>(node)->isStyled() || !node->renderer()) continue; renderSubtreeToImage(tileImage.get(), node->renderer()); } patternData->boundaries = patternBoundaries; // Compute pattern transformation patternData->transform.translate(patternBoundaries.x(), patternBoundaries.y()); patternData->transform.multiply(patternTransform); context->restore(); return tileImage.release(); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, ColorSpace styleColorSpace, CompositeOperator compositeOp) { startAnimation(); RetainPtr<CGImageRef> image = frameAtIndex(m_currentFrame); if (!image) // If it's too early we won't have an image yet. return; if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), styleColorSpace, compositeOp); return; } float currHeight = CGImageGetHeight(image.get()); if (currHeight <= srcRect.y()) return; CGContextRef context = ctxt->platformContext(); ctxt->save(); bool shouldUseSubimage = false; // If the source rect is a subportion of the image, then we compute an inflated destination rect that will hold the entire image // and then set a clip to the portion that we want to display. FloatRect adjustedDestRect = destRect; FloatSize selfSize = currentFrameSize(); if (srcRect.size() != selfSize) { CGInterpolationQuality interpolationQuality = CGContextGetInterpolationQuality(context); // When the image is scaled using high-quality interpolation, we create a temporary CGImage // containing only the portion we want to display. We need to do this because high-quality // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed // into the destination rect. See <rdar://problem/6112909>. shouldUseSubimage = (interpolationQuality == kCGInterpolationHigh || interpolationQuality == kCGInterpolationDefault) && (srcRect.size() != destRect.size() || !ctxt->getCTM().isIdentityOrTranslationOrFlipped()); float xScale = srcRect.width() / destRect.width(); float yScale = srcRect.height() / destRect.height(); if (shouldUseSubimage) { FloatRect subimageRect = srcRect; float leftPadding = srcRect.x() - floorf(srcRect.x()); float topPadding = srcRect.y() - floorf(srcRect.y()); subimageRect.move(-leftPadding, -topPadding); adjustedDestRect.move(-leftPadding / xScale, -topPadding / yScale); subimageRect.setWidth(ceilf(subimageRect.width() + leftPadding)); adjustedDestRect.setWidth(subimageRect.width() / xScale); subimageRect.setHeight(ceilf(subimageRect.height() + topPadding)); adjustedDestRect.setHeight(subimageRect.height() / yScale); image.adoptCF(CGImageCreateWithImageInRect(image.get(), subimageRect)); if (currHeight < srcRect.bottom()) { ASSERT(CGImageGetHeight(image.get()) == currHeight - CGRectIntegral(srcRect).origin.y); adjustedDestRect.setHeight(CGImageGetHeight(image.get()) / yScale); } } else { adjustedDestRect.setLocation(FloatPoint(destRect.x() - srcRect.x() / xScale, destRect.y() - srcRect.y() / yScale)); adjustedDestRect.setSize(FloatSize(selfSize.width() / xScale, selfSize.height() / yScale)); } CGContextClipToRect(context, destRect); } // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. if (!shouldUseSubimage && currHeight < selfSize.height()) adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height()); ctxt->setCompositeOperation(compositeOp); // Flip the coords. CGContextScaleCTM(context, 1, -1); adjustedDestRect.setY(-adjustedDestRect.bottom()); // Adjust the color space. image = imageWithColorSpace(image.get(), styleColorSpace); // Draw the image. CGContextDrawImage(context, adjustedDestRect, image.get()); ctxt->restore(); if (imageObserver()) imageObserver()->didDraw(this); }
PassOwnPtr<ExclusionShape> ExclusionShape::createExclusionShape(const BasicShape* basicShape, float logicalBoxWidth, float logicalBoxHeight, WritingMode writingMode) { ASSERT(basicShape); bool horizontalWritingMode = isHorizontalWritingMode(writingMode); float boxWidth = horizontalWritingMode ? logicalBoxWidth : logicalBoxHeight; float boxHeight = horizontalWritingMode ? logicalBoxHeight : logicalBoxWidth; OwnPtr<ExclusionShape> exclusionShape; switch (basicShape->type()) { case BasicShape::BASIC_SHAPE_RECTANGLE: { const BasicShapeRectangle* rectangle = static_cast<const BasicShapeRectangle*>(basicShape); FloatRect bounds( floatValueForLength(rectangle->x(), boxWidth), floatValueForLength(rectangle->y(), boxHeight), floatValueForLength(rectangle->width(), boxWidth), floatValueForLength(rectangle->height(), boxHeight)); Length radiusXLength = rectangle->cornerRadiusX(); Length radiusYLength = rectangle->cornerRadiusY(); FloatSize cornerRadii( radiusXLength.isUndefined() ? 0 : floatValueForLength(radiusXLength, boxWidth), radiusYLength.isUndefined() ? 0 : floatValueForLength(radiusYLength, boxHeight)); FloatRect logicalBounds = physicalRectToLogical(bounds, logicalBoxHeight, writingMode); exclusionShape = createExclusionRectangle(logicalBounds, physicalSizeToLogical(cornerRadii, writingMode)); exclusionShape->m_boundingBox = logicalBounds; break; } case BasicShape::BASIC_SHAPE_CIRCLE: { const BasicShapeCircle* circle = static_cast<const BasicShapeCircle*>(basicShape); float centerX = floatValueForLength(circle->centerX(), boxWidth); float centerY = floatValueForLength(circle->centerY(), boxHeight); float radius = floatValueForLength(circle->radius(), std::max(boxHeight, boxWidth)); FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxHeight, writingMode); exclusionShape = createExclusionCircle(logicalCenter, radius); exclusionShape->m_boundingBox = FloatRect(logicalCenter.x() - radius, logicalCenter.y() - radius, radius * 2, radius * 2); break; } case BasicShape::BASIC_SHAPE_ELLIPSE: { const BasicShapeEllipse* ellipse = static_cast<const BasicShapeEllipse*>(basicShape); float centerX = floatValueForLength(ellipse->centerX(), boxWidth); float centerY = floatValueForLength(ellipse->centerY(), boxHeight); float radiusX = floatValueForLength(ellipse->radiusX(), boxWidth); float radiusY = floatValueForLength(ellipse->radiusY(), boxHeight); FloatPoint logicalCenter = physicalPointToLogical(FloatPoint(centerX, centerY), logicalBoxHeight, writingMode); FloatSize logicalRadii = physicalSizeToLogical(FloatSize(radiusX, radiusY), writingMode); exclusionShape = createExclusionEllipse(logicalCenter, logicalRadii); exclusionShape->m_boundingBox = FloatRect(logicalCenter - logicalRadii, logicalRadii + logicalRadii); break; } case BasicShape::BASIC_SHAPE_POLYGON: { const BasicShapePolygon* polygon = static_cast<const BasicShapePolygon*>(basicShape); const Vector<Length>& values = polygon->values(); size_t valuesSize = values.size(); ASSERT(!(valuesSize % 2)); FloatRect boundingBox; Vector<FloatPoint>* vertices = new Vector<FloatPoint>(valuesSize / 2); for (unsigned i = 0; i < valuesSize; i += 2) { FloatPoint vertex( floatValueForLength(values.at(i), boxWidth), floatValueForLength(values.at(i + 1), boxHeight)); (*vertices)[i / 2] = physicalPointToLogical(vertex, logicalBoxHeight, writingMode); if (!i) boundingBox.setLocation(vertex); else boundingBox.extend(vertex); } exclusionShape = createExclusionPolygon(adoptPtr(vertices), polygon->windRule()); exclusionShape->m_boundingBox = boundingBox; break; } default: ASSERT_NOT_REACHED(); } exclusionShape->m_logicalBoxWidth = logicalBoxWidth; exclusionShape->m_logicalBoxHeight = logicalBoxHeight; exclusionShape->m_writingMode = writingMode; return exclusionShape.release(); }
FloatRect TileController::computeTileCoverageRect(const FloatSize& newSize, const FloatRect& previousVisibleRect, const FloatRect& visibleRect, float contentsScale) const { // If the page is not in a window (for example if it's in a background tab), we limit the tile coverage rect to the visible rect. if (!m_isInWindow) return visibleRect; #if PLATFORM(IOS) // FIXME: unify the iOS and Mac code. UNUSED_PARAM(previousVisibleRect); if (m_tileCoverage == CoverageForVisibleArea || MemoryPressureHandler::singleton().isUnderMemoryPressure()) return visibleRect; double horizontalMargin = tileSize().width() / contentsScale; double verticalMargin = tileSize().height() / contentsScale; double currentTime = monotonicallyIncreasingTime(); double timeDelta = currentTime - m_velocity.lastUpdateTime; FloatRect futureRect = visibleRect; futureRect.setLocation(FloatPoint( futureRect.location().x() + timeDelta * m_velocity.horizontalVelocity, futureRect.location().y() + timeDelta * m_velocity.verticalVelocity)); if (m_velocity.horizontalVelocity) { futureRect.setWidth(futureRect.width() + horizontalMargin); if (m_velocity.horizontalVelocity < 0) futureRect.setX(futureRect.x() - horizontalMargin); } if (m_velocity.verticalVelocity) { futureRect.setHeight(futureRect.height() + verticalMargin); if (m_velocity.verticalVelocity < 0) futureRect.setY(futureRect.y() - verticalMargin); } if (!m_velocity.horizontalVelocity && !m_velocity.verticalVelocity) { if (m_velocity.scaleChangeRate > 0) return visibleRect; futureRect.setWidth(futureRect.width() + horizontalMargin); futureRect.setHeight(futureRect.height() + verticalMargin); futureRect.setX(futureRect.x() - horizontalMargin / 2); futureRect.setY(futureRect.y() - verticalMargin / 2); } // Can't use m_tileCacheLayer->bounds() here, because the size of the underlying platform layer // hasn't been updated for the current commit. IntSize contentSize = expandedIntSize(newSize); if (futureRect.maxX() > contentSize.width()) futureRect.setX(contentSize.width() - futureRect.width()); if (futureRect.maxY() > contentSize.height()) futureRect.setY(contentSize.height() - futureRect.height()); if (futureRect.x() < 0) futureRect.setX(0); if (futureRect.y() < 0) futureRect.setY(0); return futureRect; #else UNUSED_PARAM(contentsScale); // FIXME: look at how far the document can scroll in each dimension. float coverageHorizontalSize = visibleRect.width(); float coverageVerticalSize = visibleRect.height(); bool largeVisibleRectChange = !previousVisibleRect.isEmpty() && !visibleRect.intersects(previousVisibleRect); // Inflate the coverage rect so that it covers 2x of the visible width and 3x of the visible height. // These values were chosen because it's more common to have tall pages and to scroll vertically, // so we keep more tiles above and below the current area. if (m_tileCoverage & CoverageForHorizontalScrolling && !largeVisibleRectChange) coverageHorizontalSize *= 2; if (m_tileCoverage & CoverageForVerticalScrolling && !largeVisibleRectChange) coverageVerticalSize *= 3; coverageVerticalSize += topMarginHeight() + bottomMarginHeight(); coverageHorizontalSize += leftMarginWidth() + rightMarginWidth(); // Can't use m_tileCacheLayer->bounds() here, because the size of the underlying platform layer // hasn't been updated for the current commit. FloatRect coverageBounds = boundsForSize(newSize); float coverageLeft = visibleRect.x() - (coverageHorizontalSize - visibleRect.width()) / 2; coverageLeft = std::min(coverageLeft, coverageBounds.maxX() - coverageHorizontalSize); coverageLeft = std::max(coverageLeft, coverageBounds.x()); float coverageTop = visibleRect.y() - (coverageVerticalSize - visibleRect.height()) / 2; coverageTop = std::min(coverageTop, coverageBounds.maxY() - coverageVerticalSize); coverageTop = std::max(coverageTop, coverageBounds.y()); return FloatRect(coverageLeft, coverageTop, coverageHorizontalSize, coverageVerticalSize); #endif }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatRect& src, ColorSpace styleColorSpace, CompositeOperator op) { if (!m_source.initialized()) return; if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, dst, solidColor(), styleColorSpace, op); return; } #if USE(WXGC) wxGCDC* context = (wxGCDC*)ctxt->platformContext(); wxGraphicsContext* gc = context->GetGraphicsContext(); wxGraphicsBitmap* bitmap = frameAtIndex(m_currentFrame); #else wxDC* context = ctxt->platformContext(); wxBitmap* bitmap = frameAtIndex(m_currentFrame); #endif startAnimation(); if (!bitmap) // If it's too early we won't have an image yet. return; // If we're drawing a sub portion of the image or scaling then create // a pattern transformation on the image and draw the transformed pattern. // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html // FIXME: NYI ctxt->save(); // Set the compositing operation. ctxt->setCompositeOperation(op); #if USE(WXGC) float scaleX = src.width() / dst.width(); float scaleY = src.height() / dst.height(); FloatRect adjustedDestRect = dst; FloatSize selfSize = currentFrameSize(); if (src.size() != selfSize) { adjustedDestRect.setLocation(FloatPoint(dst.x() - src.x() / scaleX, dst.y() - src.y() / scaleY)); adjustedDestRect.setSize(FloatSize(selfSize.width() / scaleX, selfSize.height() / scaleY)); } gc->Clip(dst.x(), dst.y(), dst.width(), dst.height()); #if wxCHECK_VERSION(2,9,0) gc->DrawBitmap(*bitmap, adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.width(), adjustedDestRect.height()); #else gc->DrawGraphicsBitmap(*bitmap, adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.width(), adjustedDestRect.height()); #endif #else // USE(WXGC) bitmap = getCachedResizedBitmap(bitmap, dst.size(), src); context->DrawBitmap(*bitmap, dst.x(), dst.y(), true); #endif ctxt->restore(); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, const FloatRect& src, CompositeOperator op) { if (!m_source.initialized()) return; if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, dst, solidColor(), op); return; } #if USE(WXGC) wxGCDC* context = (wxGCDC*)ctxt->platformContext(); wxGraphicsContext* gc = context->GetGraphicsContext(); #else wxWindowDC* context = ctxt->platformContext(); #endif startAnimation(); wxBitmap* bitmap = frameAtIndex(m_currentFrame); if (!bitmap) // If it's too early we won't have an image yet. return; // If we're drawing a sub portion of the image or scaling then create // a pattern transformation on the image and draw the transformed pattern. // Test using example site at http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html // FIXME: NYI ctxt->save(); // Set the compositing operation. ctxt->setCompositeOperation(op); #if USE(WXGC) float scaleX = src.width() / dst.width(); float scaleY = src.height() / dst.height(); FloatRect adjustedDestRect = dst; FloatSize selfSize = currentFrameSize(); if (src.size() != selfSize) { adjustedDestRect.setLocation(FloatPoint(dst.x() - src.x() / scaleX, dst.y() - src.y() / scaleY)); adjustedDestRect.setSize(FloatSize(selfSize.width() / scaleX, selfSize.height() / scaleY)); } // If the image is only partially loaded, then shrink the destination rect that we're drawing into accordingly. int currHeight = bitmap->GetHeight(); if (currHeight < selfSize.height()) adjustedDestRect.setHeight(adjustedDestRect.height() * currHeight / selfSize.height()); gc->DrawBitmap(*bitmap, adjustedDestRect.x(), adjustedDestRect.y(), adjustedDestRect.width(), adjustedDestRect.height()); #else IntRect srcIntRect(src); IntRect dstIntRect(dst); bool rescaling = false; if ((dstIntRect.width() != srcIntRect.width()) || (dstIntRect.height() != srcIntRect.height())) { rescaling = true; wxImage img = bitmap->ConvertToImage(); img.Rescale(dstIntRect.width(), dstIntRect.height()); bitmap = new wxBitmap(img); } wxMemoryDC mydc; ASSERT(bitmap->GetRefData()); mydc.SelectObject(*bitmap); context->Blit((wxCoord)dstIntRect.x(),(wxCoord)dstIntRect.y(), (wxCoord)dstIntRect.width(), (wxCoord)dstIntRect.height(), &mydc, (wxCoord)srcIntRect.x(), (wxCoord)srcIntRect.y(), wxCOPY, true); mydc.SelectObject(wxNullBitmap); // NB: delete is causing crashes during page load, but not during the deletion // itself. It occurs later on when a valid bitmap created in frameAtIndex // suddenly becomes invalid after returning. It's possible these errors deal // with reentrancy and threding problems. //delete bitmap; if (rescaling) { delete bitmap; bitmap = NULL; } #endif ctxt->restore(); }