void FindIndicator::draw(GraphicsContext& graphicsContext, const IntRect& /*dirtyRect*/) { #if ENABLE(LEGACY_FIND_INDICATOR_STYLE) for (size_t i = 0; i < m_textRectsInSelectionRectCoordinates.size(); ++i) { FloatRect textRect = m_textRectsInSelectionRectCoordinates[i]; textRect.move(leftBorderThickness, topBorderThickness); FloatRect outerPathRect = inflateRect(textRect, horizontalOutsetToCenterOfLightBorder, verticalOutsetToCenterOfLightBorder); FloatRect innerPathRect = inflateRect(textRect, horizontalPaddingInsideLightBorder, verticalPaddingInsideLightBorder); { GraphicsContextStateSaver stateSaver(graphicsContext); graphicsContext.setShadow(FloatSize(shadowOffsetX, shadowOffsetY), shadowBlurRadius, shadowColor(), ColorSpaceSRGB); graphicsContext.setFillColor(lightBorderColor(), ColorSpaceDeviceRGB); graphicsContext.fillPath(pathWithRoundedRect(outerPathRect, cornerRadius)); } { GraphicsContextStateSaver stateSaver(graphicsContext); graphicsContext.clip(pathWithRoundedRect(innerPathRect, cornerRadius)); RefPtr<Gradient> gradient = Gradient::create(FloatPoint(innerPathRect.x(), innerPathRect.y()), FloatPoint(innerPathRect.x(), innerPathRect.maxY())); gradient->addColorStop(0, gradientLightColor()); gradient->addColorStop(1, gradientDarkColor()); graphicsContext.setFillGradient(gradient.releaseNonNull()); graphicsContext.fillRect(outerPathRect); } { GraphicsContextStateSaver stateSaver(graphicsContext); graphicsContext.translate(FloatSize(roundf(leftBorderThickness), roundf(topBorderThickness))); IntRect contentImageRect = enclosingIntRect(m_textRectsInSelectionRectCoordinates[i]); m_contentImage->paint(graphicsContext, m_contentImageScaleFactor, contentImageRect.location(), contentImageRect); } } #else for (auto& textRect : m_textRectsInSelectionRectCoordinates) { FloatRect blurRect = textRect; blurRect.move(flatShadowBlurRadius + flatStyleHorizontalBorder, flatShadowBlurRadius + flatStyleVerticalBorder); FloatRect outerPathRect = inflateRect(blurRect, flatStyleHorizontalBorder, flatStyleVerticalBorder); { GraphicsContextStateSaver stateSaver(graphicsContext); graphicsContext.setShadow(FloatSize(), flatRimShadowBlurRadius, flatRimShadowColor(), ColorSpaceSRGB); graphicsContext.setFillColor(flatHighlightColor(), ColorSpaceSRGB); graphicsContext.fillRect(outerPathRect); graphicsContext.setShadow(FloatSize(flatShadowOffsetX, flatShadowOffsetY), flatShadowBlurRadius, flatDropShadowColor(), ColorSpaceSRGB); graphicsContext.fillRect(outerPathRect); } { GraphicsContextStateSaver stateSaver(graphicsContext); graphicsContext.translate(FloatSize(flatShadowBlurRadius + flatStyleHorizontalBorder, flatShadowBlurRadius + flatStyleVerticalBorder)); IntRect contentImageRect = enclosingIntRect(textRect); m_contentImage->paint(graphicsContext, m_contentImageScaleFactor, contentImageRect.location(), contentImageRect); } } #endif }
void SVGPreserveAspectRatio::transformRect(FloatRect& destRect, FloatRect& srcRect) { if (m_align == SVG_PRESERVEASPECTRATIO_NONE) return; FloatSize imageSize = srcRect.size(); float origDestWidth = destRect.width(); float origDestHeight = destRect.height(); switch (m_meetOrSlice) { case SVGPreserveAspectRatio::SVG_MEETORSLICE_UNKNOWN: break; case SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET: { float widthToHeightMultiplier = srcRect.height() / srcRect.width(); if (origDestHeight > origDestWidth * widthToHeightMultiplier) { destRect.setHeight(origDestWidth * widthToHeightMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: destRect.setY(destRect.y() + origDestHeight / 2 - destRect.height() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: destRect.setY(destRect.y() + origDestHeight - destRect.height()); break; default: break; } } if (origDestWidth > origDestHeight / widthToHeightMultiplier) { destRect.setWidth(origDestHeight / widthToHeightMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: destRect.setX(destRect.x() + origDestWidth / 2 - destRect.width() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: destRect.setX(destRect.x() + origDestWidth - destRect.width()); break; default: break; } } break; } case SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE: { float widthToHeightMultiplier = srcRect.height() / srcRect.width(); // if the destination height is less than the height of the image we'll be drawing if (origDestHeight < origDestWidth * widthToHeightMultiplier) { float destToSrcMultiplier = srcRect.width() / destRect.width(); srcRect.setHeight(destRect.height() * destToSrcMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: srcRect.setY(srcRect.y() + imageSize.height() / 2 - srcRect.height() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: srcRect.setY(srcRect.y() + imageSize.height() - srcRect.height()); break; default: break; } } // if the destination width is less than the width of the image we'll be drawing if (origDestWidth < origDestHeight / widthToHeightMultiplier) { float destToSrcMultiplier = srcRect.height() / destRect.height(); srcRect.setWidth(destRect.width() * destToSrcMultiplier); switch (m_align) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: srcRect.setX(srcRect.x() + imageSize.width() / 2 - srcRect.width() / 2); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: srcRect.setX(srcRect.x() + imageSize.width() - srcRect.width()); break; default: break; } } break; } } }
bool PolygonShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const { float minIntervalTop = minLogicalIntervalTop; float minIntervalHeight = minLogicalIntervalSize.height(); float minIntervalWidth = minLogicalIntervalSize.width(); const FloatPolygon& polygon = shapePaddingBounds(); const FloatRect boundingBox = polygon.boundingBox(); if (minIntervalWidth > boundingBox.width()) return false; float minY = std::max(boundingBox.y(), minIntervalTop); float maxY = minY + minIntervalHeight; if (maxY > boundingBox.maxY()) return false; Vector<const FloatPolygonEdge*> edges; polygon.overlappingEdges(minIntervalTop, boundingBox.maxY(), edges); float dx = minIntervalWidth / 2; float dy = minIntervalHeight / 2; Vector<OffsetPolygonEdge> offsetEdges; for (unsigned i = 0; i < edges.size(); ++i) { const FloatPolygonEdge& edge = *(edges[i]); const FloatPoint& vertex0 = edge.previousEdge().vertex1(); const FloatPoint& vertex1 = edge.vertex1(); const FloatPoint& vertex2 = edge.vertex2(); Vector<OffsetPolygonEdge> offsetEdgeBuffer; if (vertex2.y() > vertex1.y() ? vertex2.x() >= vertex1.x() : vertex1.x() >= vertex2.x()) { offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, -dy))); offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, dy))); } else { offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(dx, dy))); offsetEdgeBuffer.append(OffsetPolygonEdge(edge, FloatSize(-dx, -dy))); } if (isReflexVertex(vertex0, vertex1, vertex2)) { if (vertex2.x() <= vertex1.x() && vertex0.x() <= vertex1.x()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(dx, -dy), FloatSize(dx, dy))); else if (vertex2.x() >= vertex1.x() && vertex0.x() >= vertex1.x()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(-dx, dy))); if (vertex2.y() <= vertex1.y() && vertex0.y() <= vertex1.y()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, dy), FloatSize(dx, dy))); else if (vertex2.y() >= vertex1.y() && vertex0.y() >= vertex1.y()) offsetEdgeBuffer.append(OffsetPolygonEdge(vertex1, FloatSize(-dx, -dy), FloatSize(dx, -dy))); } for (unsigned j = 0; j < offsetEdgeBuffer.size(); ++j) if (offsetEdgeBuffer[j].maxY() >= minY) offsetEdges.append(offsetEdgeBuffer[j]); } offsetEdges.append(OffsetPolygonEdge(polygon, minIntervalTop, FloatSize(0, dy))); FloatPoint offsetEdgesIntersection; FloatRect firstFitRect; bool firstFitFound = false; for (unsigned i = 0; i < offsetEdges.size() - 1; ++i) { for (unsigned j = i + 1; j < offsetEdges.size(); ++j) { if (offsetEdges[i].intersection(offsetEdges[j], offsetEdgesIntersection)) { FloatPoint potentialFirstFitLocation(offsetEdgesIntersection.x() - dx, offsetEdgesIntersection.y() - dy); FloatRect potentialFirstFitRect(potentialFirstFitLocation, minLogicalIntervalSize); if ((offsetEdges[i].basis() == OffsetPolygonEdge::LineTop || offsetEdges[j].basis() == OffsetPolygonEdge::LineTop || potentialFirstFitLocation.y() >= minIntervalTop) && (!firstFitFound || aboveOrToTheLeft(potentialFirstFitRect, firstFitRect)) && polygon.contains(offsetEdgesIntersection) && firstFitRectInPolygon(polygon, potentialFirstFitRect, offsetEdges[i].edgeIndex(), offsetEdges[j].edgeIndex())) { firstFitFound = true; firstFitRect = potentialFirstFitRect; } } } } if (firstFitFound) result = ceiledLayoutUnit(firstFitRect.y()); return firstFitFound; }
void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio) { float origDestWidth = destRect.width(); float origDestHeight = destRect.height(); if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) { float widthToHeightMultiplier = srcRect.height() / srcRect.width(); if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) { destRect.setHeight(origDestWidth * widthToHeightMultiplier); switch(aspectRatio->align()) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: destRect.setY(destRect.y() + origDestHeight - destRect.height()); break; } } if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) { destRect.setWidth(origDestHeight / widthToHeightMultiplier); switch(aspectRatio->align()) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: destRect.setX(destRect.x() + origDestWidth - destRect.width()); break; } } } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) { float widthToHeightMultiplier = srcRect.height() / srcRect.width(); // if the destination height is less than the height of the image we'll be drawing if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) { float destToSrcMultiplier = srcRect.width() / destRect.width(); srcRect.setHeight(destRect.height() * destToSrcMultiplier); switch(aspectRatio->align()) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: srcRect.setY(destRect.y() + image()->height() - srcRect.height()); break; } } // if the destination width is less than the width of the image we'll be drawing if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) { float destToSrcMultiplier = srcRect.height() / destRect.height(); srcRect.setWidth(destRect.width() * destToSrcMultiplier); switch(aspectRatio->align()) { case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f); break; case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: srcRect.setX(destRect.x() + image()->width() - srcRect.width()); break; } } } }
PassRefPtr<PictureSnapshot> PictureSnapshot::load(const Vector<RefPtr<TilePictureStream>>& tiles) { ASSERT(!tiles.isEmpty()); Vector<sk_sp<SkPicture>> pictures; pictures.reserveCapacity(tiles.size()); FloatRect unionRect; for (const auto& tileStream : tiles) { SkMemoryStream stream(tileStream->data.begin(), tileStream->data.size()); sk_sp<SkPicture> picture = SkPicture::MakeFromStream(&stream, decodeBitmap); if (!picture) return nullptr; FloatRect cullRect(picture->cullRect()); cullRect.moveBy(tileStream->layerOffset); unionRect.unite(cullRect); pictures.append(std::move(picture)); } if (tiles.size() == 1) return adoptRef(new PictureSnapshot(fromSkSp(std::move(pictures[0])))); SkPictureRecorder recorder; SkCanvas* canvas = recorder.beginRecording(unionRect.width(), unionRect.height(), 0, 0); for (size_t i = 0; i < pictures.size(); ++i) { canvas->save(); canvas->translate(tiles[i]->layerOffset.x() - unionRect.x(), tiles[i]->layerOffset.y() - unionRect.y()); pictures[i]->playback(canvas, 0); canvas->restore(); } return adoptRef(new PictureSnapshot(fromSkSp(recorder.finishRecordingAsPicture()))); }
bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(object); ASSERT(context); #ifndef NDEBUG ASSERT(resourceMode == ApplyToDefaultMode); #else UNUSED_PARAM(resourceMode); #endif // Returning false here, to avoid drawings onto the context. We just want to // draw the stored filter output, not the unfiltered object as well. if (m_filter.contains(object)) { FilterData* filterData = m_filter.get(object); if (filterData->builded) return false; delete m_filter.take(object); // Oops, have to rebuild, go through normal code path } OwnPtr<FilterData> filterData(new FilterData); FloatRect targetBoundingBox = object->objectBoundingBox(); SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); filterData->boundaries = filterElement->filterBoundingBox(targetBoundingBox); if (filterData->boundaries.isEmpty()) return false; // Determine absolute transformation matrix for filter. AffineTransform absoluteTransform; SVGImageBufferTools::calculateTransformationToOutermostSVGCoordinateSystem(object, absoluteTransform); if (!absoluteTransform.isInvertible()) return false; // Eliminate shear of the absolute transformation matrix, to be able to produce unsheared tile images for feTile. filterData->shearFreeAbsoluteTransform = AffineTransform(absoluteTransform.xScale(), 0, 0, absoluteTransform.yScale(), absoluteTransform.e(), absoluteTransform.f()); // Determine absolute boundaries of the filter and the drawing region. FloatRect absoluteFilterBoundaries = filterData->shearFreeAbsoluteTransform.mapRect(filterData->boundaries); FloatRect drawingRegion = object->strokeBoundingBox(); drawingRegion.intersect(filterData->boundaries); FloatRect absoluteDrawingRegion = filterData->shearFreeAbsoluteTransform.mapRect(drawingRegion); // Create the SVGFilter object. bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; filterData->filter = SVGFilter::create(filterData->shearFreeAbsoluteTransform, absoluteDrawingRegion, targetBoundingBox, filterData->boundaries, primitiveBoundingBoxMode); // Create all relevant filter primitives. filterData->builder = buildPrimitives(filterData->filter.get()); if (!filterData->builder) return false; // Calculate the scale factor for the use of filterRes. // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion FloatSize scale(1, 1); if (filterElement->hasAttribute(SVGNames::filterResAttr)) { scale.setWidth(filterElement->filterResX() / absoluteFilterBoundaries.width()); scale.setHeight(filterElement->filterResY() / absoluteFilterBoundaries.height()); } if (scale.isEmpty()) return false; // Determine scale factor for filter. The size of intermediate ImageBuffers shouldn't be bigger than kMaxFilterSize. FloatRect tempSourceRect = absoluteDrawingRegion; tempSourceRect.scale(scale.width(), scale.height()); fitsInMaximumImageSize(tempSourceRect.size(), scale); // Set the scale level in SVGFilter. filterData->filter->setFilterResolution(scale); FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect) return false; RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect, filterData->filter.get()); FloatRect subRegion = lastEffect->maxEffectRect(); // At least one FilterEffect has a too big image size, // recalculate the effect sizes with new scale factors. if (!fitsInMaximumImageSize(subRegion.size(), scale)) { filterData->filter->setFilterResolution(scale); RenderSVGResourceFilterPrimitive::determineFilterPrimitiveSubregion(lastEffect, filterData->filter.get()); } // If the drawingRegion is empty, we have something like <g filter=".."/>. // Even if the target objectBoundingBox() is empty, we still have to draw the last effect result image in postApplyResource. if (drawingRegion.isEmpty()) { ASSERT(!m_filter.contains(object)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); return false; } absoluteDrawingRegion.scale(scale.width(), scale.height()); OwnPtr<ImageBuffer> sourceGraphic; if (!SVGImageBufferTools::createImageBuffer(absoluteDrawingRegion, absoluteDrawingRegion, sourceGraphic, ColorSpaceLinearRGB)) { ASSERT(!m_filter.contains(object)); filterData->savedContext = context; m_filter.set(object, filterData.leakPtr()); return false; } GraphicsContext* sourceGraphicContext = sourceGraphic->context(); ASSERT(sourceGraphicContext); sourceGraphicContext->translate(-absoluteDrawingRegion.x(), -absoluteDrawingRegion.y()); if (scale.width() != 1 || scale.height() != 1) sourceGraphicContext->scale(scale); sourceGraphicContext->concatCTM(filterData->shearFreeAbsoluteTransform); sourceGraphicContext->clearRect(FloatRect(FloatPoint(), absoluteDrawingRegion.size())); filterData->sourceGraphicBuffer = sourceGraphic.release(); filterData->savedContext = context; context = sourceGraphicContext; ASSERT(!m_filter.contains(object)); m_filter.set(object, filterData.leakPtr()); return true; }
void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const FloatRect& srcRect, CompositeOperator compositeOp) { startAnimation(); 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; } float currHeight = CGImageGetHeight(image); 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(); 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 = CGImageCreateWithImageInRect(image, subimageRect); if (currHeight < srcRect.bottom()) { ASSERT(CGImageGetHeight(image) == currHeight - CGRectIntegral(srcRect).origin.y); adjustedDestRect.setHeight(CGImageGetHeight(image) / 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()); // Draw the image. CGContextDrawImage(context, adjustedDestRect, image); if (shouldUseSubimage) CGImageRelease(image); ctxt->restore(); if (imageObserver()) imageObserver()->didDraw(this); }
static inline bool overlapsYRange(const FloatRect& rect, float y1, float y2) { return !rect.isEmpty() && y2 >= y1 && y2 >= rect.y() && y1 <= rect.maxY(); }
// FIXME: Merge with the other drawTiled eventually, since we need a combination of both for some things. void Image::drawTiled(GraphicsContext& ctxt, const FloatRect& dstRect, const FloatRect& srcRect, const FloatSize& tileScaleFactor, TileRule hRule, TileRule vRule, CompositeOperator op) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, dstRect, solidColor(), op); return; } FloatSize tileScale = tileScaleFactor; FloatSize spacing; // FIXME: These rules follow CSS border-image rules, but they should not be down here in Image. bool centerOnGapHorizonally = false; bool centerOnGapVertically = false; switch (hRule) { case RoundTile: { int numItems = std::max<int>(floorf(dstRect.width() / srcRect.width()), 1); tileScale.setWidth(dstRect.width() / (srcRect.width() * numItems)); break; } case SpaceTile: { int numItems = floorf(dstRect.width() / srcRect.width()); if (!numItems) return; spacing.setWidth((dstRect.width() - srcRect.width() * numItems) / (numItems + 1)); tileScale.setWidth(1); centerOnGapHorizonally = !(numItems & 1); break; } case StretchTile: case RepeatTile: break; } switch (vRule) { case RoundTile: { int numItems = std::max<int>(floorf(dstRect.height() / srcRect.height()), 1); tileScale.setHeight(dstRect.height() / (srcRect.height() * numItems)); break; } case SpaceTile: { int numItems = floorf(dstRect.height() / srcRect.height()); if (!numItems) return; spacing.setHeight((dstRect.height() - srcRect.height() * numItems) / (numItems + 1)); tileScale.setHeight(1); centerOnGapVertically = !(numItems & 1); break; } case StretchTile: case RepeatTile: break; } AffineTransform patternTransform = AffineTransform().scaleNonUniform(tileScale.width(), tileScale.height()); // We want to construct the phase such that the pattern is centered (when stretch is not // set for a particular rule). float hPhase = tileScale.width() * srcRect.x(); float vPhase = tileScale.height() * srcRect.y(); float scaledTileWidth = tileScale.width() * srcRect.width(); float scaledTileHeight = tileScale.height() * srcRect.height(); if (centerOnGapHorizonally) hPhase -= spacing.width(); else if (hRule == Image::RepeatTile || hRule == Image::SpaceTile) hPhase -= (dstRect.width() - scaledTileWidth) / 2; if (centerOnGapVertically) vPhase -= spacing.height(); else if (vRule == Image::RepeatTile || vRule == Image::SpaceTile) vPhase -= (dstRect.height() - scaledTileHeight) / 2; FloatPoint patternPhase(dstRect.x() - hPhase, dstRect.y() - vPhase); drawPattern(ctxt, srcRect, patternTransform, patternPhase, spacing, op, dstRect); #if PLATFORM(IOS) startAnimation(DoNotCatchUp); #else startAnimation(); #endif }
void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { if (destRect.isEmpty()) return; SDL_Surface* image = nativeImageForCurrentFrame(); if (!image) // If it's too early we won't have an image yet. return; SDL_Surface* cr = context->platformContext(); context->save(); context->setCompositeOperation(op); // Check and see if a single draw of the image can cover the entire area we are supposed to tile. // save context info context->clip(IntRect(destRect)); // don't draw outside this IntRect dest(IntPoint(), IntSize(image->w, image->h)); IntRect src(static_cast<int>(phase.x()), static_cast<int>(phase.y()), static_cast<int>(tileRect.size().width()), static_cast<int>(tileRect.size().height())); int xMax = static_cast<int>(destRect.x() + destRect.width()); int yMax = static_cast<int>(destRect.y() + destRect.height()); SDL_Rect srcRect, dstRect; srcRect.x = 0; srcRect.y = 0; if (0 == src.width()) srcRect.w = image->w; else srcRect.w = static_cast<Uint16>(src.width()); if (0 == src.height()) srcRect.h = image->h; else srcRect.h = static_cast<Uint16>(src.height()); dstRect.x = static_cast<Sint16>(dest.x()); dstRect.y = static_cast<Sint16>(dest.y()); dstRect.w = static_cast<Sint16>(dest.width()); dstRect.h = static_cast<Sint16>(dest.height()); //compute ratio of the zoomed part: double ratioW = (((double)dest.width() / (double)srcRect.w)); double ratioH = ((double)dest.height() / ((double)srcRect.h)); SDL_Surface *surface = NULL; if ((ratioW != 1.0)||(ratioH != 1.0)) { surface = zoomSurface(image, ratioW, ratioH, SMOOTHING_OFF); //adjust offset to the new referentiel (zoomed) srcRect.x = static_cast<Sint16>(src.x() * ratioW); srcRect.y = static_cast<Sint16>(src.y() * ratioH); } for (int x = static_cast<int>(phase.x()); x < xMax; x += image->w) { for (int y = static_cast<int>(phase.y()); y < yMax; y += image->h) { dest.setLocation(IntPoint(x, y) + IntSize(context->origin().x(), context->origin().y())); dstRect.x = static_cast<Sint16>(dest.x()); dstRect.y = static_cast<Sint16>(dest.y()); dstRect.w = static_cast<Sint16>(dest.width()); dstRect.h = static_cast<Sint16>(dest.height()); if (surface) { if (context->transparencyLayer() == 1.0) SDL_BlitSurface(surface, &srcRect, cr, &dstRect); else { SDL_Surface *surfaceWithAlpha = applyTransparency(surface, static_cast<int> (context->transparencyLayer() * 255)); SDL_BlitSurface(surfaceWithAlpha, &srcRect, cr, &dstRect); SDL_FreeSurface(surfaceWithAlpha); } } else { if (context->transparencyLayer() == 1.0) SDL_BlitSurface(image, &srcRect, cr, &dstRect); else { SDL_Surface *surfaceWithAlpha = applyTransparency(image, static_cast<int> (context->transparencyLayer() * 255)); SDL_BlitSurface(surfaceWithAlpha, &srcRect, cr, &dstRect); SDL_FreeSurface(surfaceWithAlpha); } } } } if(surface) SDL_FreeSurface(surface); context->restore(); if (imageObserver()) imageObserver()->didDraw(this); }
void Path::addEllipse(const FloatRect& r) { m_path.addEllipse(r.x(), r.y(), r.width(), r.height()); }
void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const FloatRect& src, CompositeOperator op) { SDL_Surface* image = frameAtIndex(m_currentFrame); if (!image) // If it's too early we won't have an image yet. return; FloatRect sourceRect(src); FloatRect destRect(dst); if (mayFillWithSolidColor()) { fillWithSolidColor(context, destRect, solidColor(), op); return; } // Set the compositing operation. if (op == CompositeSourceOver && !frameHasAlphaAtIndex(m_currentFrame)) context->setCompositeOperation(CompositeCopy); else context->setCompositeOperation(op); SDL_Surface* cr = context->platformContext(); float scaleX = destRect.width() / sourceRect.width(); float scaleY = destRect.height() / sourceRect.height(); // Draw the image. SDL_Rect srcRect, dstRect; srcRect.x = static_cast<Sint16>(src.x()); srcRect.y = static_cast<Sint16>(src.y()); if (0 == sourceRect.width()) srcRect.w = image->w; else srcRect.w = static_cast<Uint16>(sourceRect.width()); if (0 == sourceRect.height()) srcRect.h = image->h; else srcRect.h = static_cast<Uint16>(sourceRect.height()); dstRect.x = static_cast<Sint16>(destRect.x() + context->origin().x()); dstRect.y = static_cast<Sint16>(destRect.y() + context->origin().y()); dstRect.w = static_cast<Sint16>(destRect.width()); dstRect.h = static_cast<Sint16>(destRect.height()); if ((scaleX != 1.0)||(scaleY != 1.0)) { srcRect.x = static_cast<Sint16>(src.x() * scaleX); srcRect.y = static_cast<Sint16>(src.y() * scaleY); srcRect.w = dstRect.w; srcRect.h = dstRect.h; } if (sourceRect != destRect) { if (context->transparencyLayer() == 1.0) { if ((scaleX != 1.0) || (scaleY != 1.0)) { SDL_Surface *surface = zoomSurface(image, scaleX, scaleY, SMOOTHING_OFF); SDL_BlitSurface(surface, &srcRect, cr, &dstRect); SDL_FreeSurface(surface); } else SDL_BlitSurface(image, &srcRect, cr, &dstRect); } else { SDL_Surface *surfaceWithAlpha = applyTransparency(image, static_cast<int> (context->transparencyLayer() * 255)); if ((scaleX != 1.0) || (scaleY != 1.0)) { SDL_Surface *surface = zoomSurface(surfaceWithAlpha, scaleX, scaleY, SMOOTHING_OFF); SDL_BlitSurface(surface, &srcRect, cr, &dstRect); SDL_FreeSurface(surface); } else SDL_BlitSurface(surfaceWithAlpha, &srcRect, cr, &dstRect); SDL_FreeSurface(surfaceWithAlpha); } } else if (context->transparencyLayer() == 1.0) SDL_BlitSurface(image, &srcRect, cr, &dstRect); else { SDL_Surface *surfaceWithAlpha = applyTransparency(image, static_cast<int> (context->transparencyLayer() * 255)); SDL_BlitSurface(surfaceWithAlpha, &srcRect, cr, &dstRect); SDL_FreeSurface(surfaceWithAlpha); } startAnimation(); if (imageObserver()) imageObserver()->didDraw(this); }
bool SVGPaintServerGradient::setup(GraphicsContext*& context, const RenderObject* object, SVGPaintTargetType type, bool isPaintingText) const { m_ownerElement->buildGradient(); cairo_t* cr = context->platformContext(); cairo_pattern_t* pattern; cairo_matrix_t matrix; cairo_matrix_init_identity (&matrix); const cairo_matrix_t gradient_matrix = gradientTransform(); if (this->type() == LinearGradientPaintServer) { const SVGPaintServerLinearGradient* linear = static_cast<const SVGPaintServerLinearGradient*>(this); if (boundingBoxMode()) { FloatRect bbox = object->relativeBBox(false); cairo_matrix_translate(&matrix, bbox.x(), bbox.y()); cairo_matrix_scale(&matrix, bbox.width(), bbox.height()); } double x0 = linear->gradientStart().x(); double y0 = linear->gradientStart().y(); double x1 = linear->gradientEnd().x(); double y1 = linear->gradientEnd().y(); pattern = cairo_pattern_create_linear(x0, y0, x1, y1); } else if (this->type() == RadialGradientPaintServer) { const SVGPaintServerRadialGradient* radial = static_cast<const SVGPaintServerRadialGradient*>(this); if (boundingBoxMode()) { FloatRect bbox = object->relativeBBox(false); cairo_matrix_translate(&matrix, bbox.x(), bbox.y()); cairo_matrix_scale(&matrix, bbox.width(), bbox.height()); } double cx = radial->gradientCenter().x(); double cy = radial->gradientCenter().y(); double radius = radial->gradientRadius(); double fx = radial->gradientFocal().x(); double fy = radial->gradientFocal().y(); fx -= cx; fy -= cy; double fradius = 0.0; if (sqrt(fx * fx + fy * fy) > radius) { double angle = atan2(fy, fx); if ((fx + cx) < cx) fx = int(cos(angle) * radius) + 1; else fx = int(cos(angle) * radius) - 1; if ((fy + cy) < cy) fy = int(sin(angle) * radius) + 1; else fy = int(sin(angle) * radius) - 1; } pattern = cairo_pattern_create_radial(fx + cx, fy + cy, fradius, cx, cy, radius); } else { return false; } cairo_pattern_set_filter(pattern, CAIRO_FILTER_BILINEAR); switch (spreadMethod()) { case SPREADMETHOD_PAD: cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); break; case SPREADMETHOD_REFLECT: cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT); break; case SPREADMETHOD_REPEAT: cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); break; default: cairo_pattern_set_extend(pattern, CAIRO_EXTEND_NONE); break; } cairo_matrix_multiply(&matrix, &matrix, &gradient_matrix); cairo_matrix_invert(&matrix); cairo_pattern_set_matrix(pattern, &matrix); const Vector<SVGGradientStop>& stops = gradientStops(); for (unsigned i = 0; i < stops.size(); ++i) { float offset = stops[i].first; Color color = stops[i].second; cairo_pattern_add_color_stop_rgba(pattern, offset, color.red() / 255.0, color.green() / 255.0, color.blue() / 255.0, color.alpha() / 255.0); } cairo_set_source(cr, pattern); cairo_pattern_destroy(pattern); return true; }
inline unsigned WidthIterator::advanceInternal(TextIterator& textIterator, GlyphBuffer* glyphBuffer) { bool rtl = m_run.rtl(); bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled(); float widthSinceLastRounding = m_runWidthSoFar; m_runWidthSoFar = floorf(m_runWidthSoFar); widthSinceLastRounding -= m_runWidthSoFar; float lastRoundingWidth = m_finalRoundingWidth; FloatRect bounds; const SimpleFontData* primaryFont = m_font->primaryFont(); const SimpleFontData* lastFontData = primaryFont; int lastGlyphCount = glyphBuffer ? glyphBuffer->size() : 0; UChar32 character = 0; unsigned clusterLength = 0; CharactersTreatedAsSpace charactersTreatedAsSpace; String normalizedSpacesStringCache; while (textIterator.consume(character, clusterLength)) { unsigned advanceLength = clusterLength; int currentCharacter = textIterator.currentCharacter(); const GlyphData& glyphData = glyphDataForCharacter(character, rtl, currentCharacter, advanceLength, normalizedSpacesStringCache); Glyph glyph = glyphData.glyph; const SimpleFontData* fontData = glyphData.fontData; ASSERT(fontData); // Now that we have a glyph and font data, get its width. float width; if (character == '\t' && m_run.allowTabs()) width = m_font->tabWidth(*fontData, m_run.tabSize(), m_run.xPos() + m_runWidthSoFar + widthSinceLastRounding); else { width = fontData->widthForGlyph(glyph); // SVG uses horizontalGlyphStretch(), when textLength is used to stretch/squeeze text. width *= m_run.horizontalGlyphStretch(); // We special case spaces in two ways when applying word rounding. // First, we round spaces to an adjusted width in all fonts. // Second, in fixed-pitch fonts we ensure that all characters that // match the width of the space character have the same width as the space character. if (m_run.applyWordRounding() && width == fontData->spaceWidth() && (fontData->pitch() == FixedPitch || glyph == fontData->spaceGlyph())) width = fontData->adjustedSpaceWidth(); } if (fontData != lastFontData && width) { if (shouldApplyFontTransforms()) { m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace); lastGlyphCount = glyphBuffer->size(); // applyFontTransforms doesn't update when there had been only one glyph. } lastFontData = fontData; if (m_fallbackFonts && fontData != primaryFont) { // FIXME: This does a little extra work that could be avoided if // glyphDataForCharacter() returned whether it chose to use a small caps font. if (!m_font->isSmallCaps() || character == u_toupper(character)) m_fallbackFonts->add(fontData); else { const GlyphData& uppercaseGlyphData = m_font->glyphDataForCharacter(u_toupper(character), rtl); if (uppercaseGlyphData.fontData != primaryFont) m_fallbackFonts->add(uppercaseGlyphData.fontData); } } } if (hasExtraSpacing) { // Account for letter-spacing. if (width && m_font->letterSpacing()) width += m_font->letterSpacing(); static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText(); bool treatAsSpace = Font::treatAsSpace(character); if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(character))) { // Distribute the run's total expansion evenly over all expansion opportunities in the run. if (m_expansion) { float previousExpansion = m_expansion; if (!treatAsSpace && !m_isAfterExpansion) { // Take the expansion opportunity before this ideograph. m_expansion -= m_expansionPerOpportunity; float expansionAtThisOpportunity = !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); m_runWidthSoFar += expansionAtThisOpportunity; if (glyphBuffer) { if (glyphBuffer->isEmpty()) { if (m_forTextEmphasis) glyphBuffer->add(fontData->zeroWidthSpaceGlyph(), fontData, m_expansionPerOpportunity, currentCharacter); else glyphBuffer->add(fontData->spaceGlyph(), fontData, expansionAtThisOpportunity, currentCharacter); } else glyphBuffer->expandLastAdvance(expansionAtThisOpportunity); } previousExpansion = m_expansion; } if (m_run.allowsTrailingExpansion() || (m_run.ltr() && currentCharacter + advanceLength < static_cast<size_t>(m_run.length())) || (m_run.rtl() && currentCharacter)) { m_expansion -= m_expansionPerOpportunity; width += !m_run.applyWordRounding() ? m_expansionPerOpportunity : roundf(previousExpansion) - roundf(m_expansion); m_isAfterExpansion = true; } } else m_isAfterExpansion = false; // Account for word spacing. // We apply additional space between "words" by adding width to the space character. if (treatAsSpace && (character != '\t' || !m_run.allowTabs()) && (currentCharacter || character == noBreakSpace) && m_font->wordSpacing()) width += m_font->wordSpacing(); } else m_isAfterExpansion = false; } if (shouldApplyFontTransforms() && glyphBuffer && Font::treatAsSpace(character)) charactersTreatedAsSpace.append(std::make_pair(glyphBuffer->size(), OriginalAdvancesForCharacterTreatedAsSpace(character == ' ', glyphBuffer->size() ? glyphBuffer->advanceAt(glyphBuffer->size() - 1).width() : 0, width))); if (m_accountForGlyphBounds) { bounds = fontData->boundsForGlyph(glyph); if (!currentCharacter) m_firstGlyphOverflow = std::max<float>(0, -bounds.x()); } if (m_forTextEmphasis && !Font::canReceiveTextEmphasis(character)) glyph = 0; // Advance past the character we just dealt with. textIterator.advance(advanceLength); float oldWidth = width; // Force characters that are used to determine word boundaries for the rounding hack // to be integer width, so following words will start on an integer boundary. if (m_run.applyWordRounding() && Font::isRoundingHackCharacter(character)) { width = ceilf(width); // Since widthSinceLastRounding can lose precision if we include measurements for // preceding whitespace, we bypass it here. m_runWidthSoFar += width; // Since this is a rounding hack character, we should have reset this sum on the previous // iteration. ASSERT(!widthSinceLastRounding); } else { // Check to see if the next character is a "rounding hack character", if so, adjust // width so that the total run width will be on an integer boundary. if ((m_run.applyWordRounding() && textIterator.currentCharacter() < m_run.length() && Font::isRoundingHackCharacter(*(textIterator.characters()))) || (m_run.applyRunRounding() && textIterator.currentCharacter() >= m_run.length())) { float totalWidth = widthSinceLastRounding + width; widthSinceLastRounding = ceilf(totalWidth); width += widthSinceLastRounding - totalWidth; m_runWidthSoFar += widthSinceLastRounding; widthSinceLastRounding = 0; } else widthSinceLastRounding += width; } if (glyphBuffer) glyphBuffer->add(glyph, fontData, (rtl ? oldWidth + lastRoundingWidth : width), currentCharacter); lastRoundingWidth = width - oldWidth; if (m_accountForGlyphBounds) { m_maxGlyphBoundingBoxY = std::max(m_maxGlyphBoundingBoxY, bounds.maxY()); m_minGlyphBoundingBoxY = std::min(m_minGlyphBoundingBoxY, bounds.y()); m_lastGlyphOverflow = std::max<float>(0, bounds.maxX() - width); } } if (shouldApplyFontTransforms()) m_runWidthSoFar += applyFontTransforms(glyphBuffer, m_run.ltr(), lastGlyphCount, lastFontData, *this, m_typesettingFeatures, charactersTreatedAsSpace); unsigned consumedCharacters = textIterator.currentCharacter() - m_currentCharacter; m_currentCharacter = textIterator.currentCharacter(); m_runWidthSoFar += widthSinceLastRounding; m_finalRoundingWidth = lastRoundingWidth; return consumedCharacters; }
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 Image::drawTiled(GraphicsContext& ctxt, const FloatRect& destRect, const FloatPoint& srcPoint, const FloatSize& scaledTileSize, const FloatSize& spacing, CompositeOperator op, BlendMode blendMode) { if (mayFillWithSolidColor()) { fillWithSolidColor(ctxt, destRect, solidColor(), op); return; } ASSERT(!isBitmapImage() || notSolidColor()); #if PLATFORM(IOS) FloatSize intrinsicTileSize = originalSize(); #else FloatSize intrinsicTileSize = size(); #endif 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() + spacing.width(), scaledTileSize.height() + spacing.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, op, blendMode, ImageOrientationDescription()); return; } #if PLATFORM(IOS) // When using accelerated drawing on iOS, it's faster to stretch an image than to tile it. if (ctxt.isAcceleratedContext()) { if (size().width() == 1 && intersection(oneTileRect, destRect).height() == destRect.height()) { FloatRect visibleSrcRect; visibleSrcRect.setX(0); visibleSrcRect.setY((destRect.y() - oneTileRect.y()) / scale.height()); visibleSrcRect.setWidth(1); visibleSrcRect.setHeight(destRect.height() / scale.height()); draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription()); return; } if (size().height() == 1 && intersection(oneTileRect, destRect).width() == destRect.width()) { FloatRect visibleSrcRect; visibleSrcRect.setX((destRect.x() - oneTileRect.x()) / scale.width()); visibleSrcRect.setY(0); visibleSrcRect.setWidth(destRect.width() / scale.width()); visibleSrcRect.setHeight(1); draw(ctxt, destRect, visibleSrcRect, op, BlendModeNormal, ImageOrientationDescription()); return; } } #endif // Patterned images and gradients can use lots of memory for caching when the // tile size is large (<rdar://problem/4691859>, <rdar://problem/6239505>). // Memory consumption depends on the transformed tile size which can get // larger than the original tile if user zooms in enough. #if PLATFORM(IOS) const float maxPatternTilePixels = 512 * 512; #else const float maxPatternTilePixels = 2048 * 2048; #endif FloatRect transformedTileSize = ctxt.getCTM().mapRect(FloatRect(FloatPoint(), scaledTileSize)); float transformedTileSizePixels = transformedTileSize.width() * transformedTileSize.height(); FloatRect currentTileRect = oneTileRect; if (transformedTileSizePixels > maxPatternTilePixels) { GraphicsContextStateSaver stateSaver(ctxt); ctxt.clip(destRect); currentTileRect.shiftYEdgeTo(destRect.y()); float toY = currentTileRect.y(); while (toY < destRect.maxY()) { currentTileRect.shiftXEdgeTo(destRect.x()); float toX = currentTileRect.x(); while (toX < destRect.maxX()) { FloatRect toRect(toX, toY, currentTileRect.width(), currentTileRect.height()); FloatRect fromRect(toFloatPoint(currentTileRect.location() - oneTileRect.location()), currentTileRect.size()); fromRect.scale(1 / scale.width(), 1 / scale.height()); draw(ctxt, toRect, fromRect, op, BlendModeNormal, ImageOrientationDescription()); toX += currentTileRect.width(); currentTileRect.shiftXEdgeTo(oneTileRect.x()); } toY += currentTileRect.height(); currentTileRect.shiftYEdgeTo(oneTileRect.y()); } return; } AffineTransform patternTransform = AffineTransform().scaleNonUniform(scale.width(), scale.height()); FloatRect tileRect(FloatPoint(), intrinsicTileSize); drawPattern(ctxt, tileRect, patternTransform, oneTileRect.location(), spacing, op, destRect, blendMode); #if PLATFORM(IOS) startAnimation(DoNotCatchUp); #else startAnimation(); #endif }
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, const AffineTransform& patternTransform, const FloatPoint& phase, ColorSpace, CompositeOperator, const FloatRect& dstRect) { #if USE(WXGC) wxGCDC* context = (wxGCDC*)ctxt->platformContext(); wxGraphicsBitmap* bitmap = nativeImageForCurrentFrame(); #else wxDC* context = ctxt->platformContext(); wxBitmap* bitmap = nativeImageForCurrentFrame(); #endif if (!bitmap) // If it's too early we won't have an image yet. return; ctxt->save(); ctxt->clip(IntRect(dstRect.x(), dstRect.y(), dstRect.width(), dstRect.height())); float currentW = 0; float currentH = 0; #if USE(WXGC) wxGraphicsContext* gc = context->GetGraphicsContext(); float adjustedX = phase.x() + srcRect.x() * narrowPrecisionToFloat(patternTransform.a()); float adjustedY = phase.y() + srcRect.y() * narrowPrecisionToFloat(patternTransform.d()); gc->ConcatTransform(patternTransform); #else float adjustedX = phase.x(); float adjustedY = phase.y(); wxMemoryDC mydc; mydc.SelectObject(*bitmap); ctxt->concatCTM(patternTransform); #endif //wxPoint origin(context->GetDeviceOrigin()); AffineTransform mat(ctxt->getCTM()); wxPoint origin(mat.mapPoint(IntPoint(0, 0))); wxSize clientSize(context->GetSize()); wxCoord w = srcRect.width(); wxCoord h = srcRect.height(); wxCoord srcx = srcRect.x(); wxCoord srcy = srcRect.y(); while (currentW < dstRect.right() - phase.x() && origin.x + adjustedX + currentW < clientSize.x) { while (currentH < dstRect.bottom() - phase.y() && origin.y + adjustedY + currentH < clientSize.y) { #if USE(WXGC) #if wxCHECK_VERSION(2,9,0) gc->DrawBitmap(*bitmap, adjustedX + currentW, adjustedY + currentH, (wxDouble)srcRect.width(), (wxDouble)srcRect.height()); #else gc->DrawGraphicsBitmap(*bitmap, adjustedX + currentW, adjustedY + currentH, (wxDouble)srcRect.width(), (wxDouble)srcRect.height()); #endif #else context->Blit(adjustedX + currentW, adjustedY + currentH, w, h, &mydc, srcx, srcy, wxCOPY, true); #endif currentH += srcRect.height(); } currentW += srcRect.width(); currentH = 0; } ctxt->restore(); #if !USE(WXGC) mydc.SelectObject(wxNullBitmap); #endif // 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; startAnimation(); if (ImageObserver* observer = imageObserver()) observer->didDraw(this); }
bool RenderSVGResourceFilter::applyResource(RenderObject* object, RenderStyle*, GraphicsContext*& context, unsigned short resourceMode) { ASSERT(object); ASSERT(context); #ifndef NDEBUG ASSERT(resourceMode == ApplyToDefaultMode); #else UNUSED_PARAM(resourceMode); #endif // Returning false here, to avoid drawings onto the context. We just want to // draw the stored filter output, not the unfiltered object as well. if (m_filter.contains(object)) { FilterData* filterData = m_filter.get(object); if (filterData->builded) return false; delete m_filter.take(object); // Oops, have to rebuild, go through normal code path } OwnPtr<FilterData> filterData(new FilterData); filterData->builder = buildPrimitives(); if (!filterData->builder) return false; FloatRect paintRect = object->strokeBoundingBox(); // Calculate the scale factor for the use of filterRes. // Also see http://www.w3.org/TR/SVG/filters.html#FilterEffectsRegion SVGFilterElement* filterElement = static_cast<SVGFilterElement*>(node()); filterData->boundaries = filterElement->filterBoundingBox(object->objectBoundingBox()); if (filterData->boundaries.isEmpty()) return false; FloatSize scale(1.0f, 1.0f); if (filterElement->hasAttribute(SVGNames::filterResAttr)) { scale.setWidth(filterElement->filterResX() / filterData->boundaries.width()); scale.setHeight(filterElement->filterResY() / filterData->boundaries.height()); } if (scale.isEmpty()) return false; // clip sourceImage to filterRegion FloatRect clippedSourceRect = paintRect; clippedSourceRect.intersect(filterData->boundaries); // scale filter size to filterRes FloatRect tempSourceRect = clippedSourceRect; // scale to big sourceImage size to kMaxFilterSize tempSourceRect.scale(scale.width(), scale.height()); fitsInMaximumImageSize(tempSourceRect.size(), scale); // prepare Filters bool primitiveBoundingBoxMode = filterElement->primitiveUnits() == SVGUnitTypes::SVG_UNIT_TYPE_OBJECTBOUNDINGBOX; filterData->filter = SVGFilter::create(paintRect, filterData->boundaries, primitiveBoundingBoxMode); filterData->filter->setFilterResolution(scale); FilterEffect* lastEffect = filterData->builder->lastEffect(); if (!lastEffect) return false; lastEffect->calculateEffectRect(filterData->filter.get()); // At least one FilterEffect has a too big image size, // recalculate the effect sizes with new scale factors. if (!fitsInMaximumImageSize(filterData->filter->maxImageSize(), scale)) { filterData->filter->setFilterResolution(scale); lastEffect->calculateEffectRect(filterData->filter.get()); } clippedSourceRect.scale(scale.width(), scale.height()); // Draw the content of the current element and it's childs to a imageBuffer to get the SourceGraphic. // The size of the SourceGraphic is clipped to the size of the filterRegion. IntRect bufferRect = enclosingIntRect(clippedSourceRect); OwnPtr<ImageBuffer> sourceGraphic(ImageBuffer::create(bufferRect.size(), LinearRGB)); if (!sourceGraphic.get()) return false; GraphicsContext* sourceGraphicContext = sourceGraphic->context(); sourceGraphicContext->translate(-clippedSourceRect.x(), -clippedSourceRect.y()); sourceGraphicContext->scale(scale); sourceGraphicContext->clearRect(FloatRect(FloatPoint(), paintRect.size())); filterData->sourceGraphicBuffer = sourceGraphic.release(); filterData->savedContext = context; context = sourceGraphicContext; ASSERT(!m_filter.contains(object)); m_filter.set(object, filterData.leakPtr()); return true; }
void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec) { ASSERT(canvas); ec = 0; FloatRect srcCanvasRect = FloatRect(FloatPoint(), canvas->size()); if (!(srcCanvasRect.contains(srcRect) && srcRect.width() >= 0 && srcRect.height() >= 0 && dstRect.width() >= 0 && dstRect.height() >= 0)) { ec = INDEX_SIZE_ERR; return; } if (srcRect.isEmpty() || dstRect.isEmpty()) return; GraphicsContext* c = drawingContext(); if (!c) return; FloatRect sourceRect = c->roundToDevicePixels(srcRect); FloatRect destRect = c->roundToDevicePixels(dstRect); // FIXME: Do this through platform-independent GraphicsContext API. #if PLATFORM(CG) CGImageRef platformImage = canvas->createPlatformImage(); if (!platformImage) return; willDraw(destRect); float iw = CGImageGetWidth(platformImage); float ih = CGImageGetHeight(platformImage); if (sourceRect.x() == 0 && sourceRect.y() == 0 && iw == sourceRect.width() && ih == sourceRect.height()) { // Fast path, yay! CGContextDrawImage(c->platformContext(), destRect, platformImage); } else { // Slow path, boo! // Create a new bitmap of the appropriate size and then draw that into our context. size_t csw = static_cast<size_t>(ceilf(sourceRect.width())); size_t csh = static_cast<size_t>(ceilf(sourceRect.height())); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); size_t bytesPerRow = csw * 4; void* buffer = fastMalloc(csh * bytesPerRow); CGContextRef clippedSourceContext = CGBitmapContextCreate(buffer, csw, csh, 8, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast); CGColorSpaceRelease(colorSpace); CGContextTranslateCTM(clippedSourceContext, -sourceRect.x(), -sourceRect.y()); CGContextDrawImage(clippedSourceContext, CGRectMake(0, 0, iw, ih), platformImage); CGImageRef clippedSourceImage = CGBitmapContextCreateImage(clippedSourceContext); CGContextRelease(clippedSourceContext); CGContextDrawImage(c->platformContext(), destRect, clippedSourceImage); CGImageRelease(clippedSourceImage); fastFree(buffer); } CGImageRelease(platformImage); #elif PLATFORM(QT) QPixmap px = canvas->createPlatformImage(); if (px.isNull()) return; willDraw(dstRect); QPainter* painter = static_cast<QPainter*>(c->platformContext()); painter->drawPixmap(dstRect, px, srcRect); #endif }
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_EVENT("Image::drawPattern", this, 0); #endif 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); resampling = limitResamplingMode(context->platformContext(), resampling); // 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); }
void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { if (!nativeImageForCurrentFrame()) return; ASSERT(patternTransform.isInvertible()); if (!patternTransform.isInvertible()) // Avoid a hang under CGContextDrawTiledImage on release builds. return; CGContextRef context = ctxt->platformContext(); ctxt->save(); CGContextClipToRect(context, destRect); ctxt->setCompositeOperation(op); 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)); } #ifndef BUILDING_ON_TIGER // 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: Could create WebKitSystemInterface SPI for CGCreatePatternWithImage2 and probably make Tiger tile faster as well. // 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); #ifdef BUILDING_ON_LEOPARD if (w == size().width() && h == size().height() && scaledTileWidth == tileRect.width() && scaledTileHeight == tileRect.height()) #else if (w == size().width() && h == size().height()) #endif CGContextDrawTiledImage(context, FloatRect(adjustedX, adjustedY, scaledTileWidth, scaledTileHeight), subImage.get()); else { #endif // On Leopard, this code now only runs for partially decoded images whose buffers do not yet match the overall size of the image. // On Tiger this code runs all the time. This code is suboptimal because the pattern does not reference the image directly, and the // pattern is destroyed before exiting the function. This means any decoding the pattern does doesn't end up cached anywhere, so we // redecode every time we paint. static const CGPatternCallbacks patternCallbacks = { 0, drawPatternCallback, NULL }; 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); RetainPtr<CGPatternRef> pattern(AdoptCF, CGPatternCreate(subImage.get(), CGRectMake(0, 0, tileRect.width(), tileRect.height()), matrix, tileRect.width(), tileRect.height(), kCGPatternTilingConstantSpacing, true, &patternCallbacks)); if (!pattern) { ctxt->restore(); 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). wkSetPatternBaseCTM(context, CGAffineTransformIdentity); CGContextSetPatternPhase(context, CGSizeZero); CGContextSetFillColorWithColor(context, color.get()); CGContextFillRect(context, CGContextGetClipBoundingBox(context)); #ifndef BUILDING_ON_TIGER } #endif ctxt->restore(); if (imageObserver()) imageObserver()->didDraw(this); }
bool FloatRect::contains(const FloatRect& other) const { return x() <= other.x() && right() >= other.right() && y() <= other.y() && bottom() >= other.bottom(); }
bool Surface::tryUpdateSurface(Surface* oldSurface) { if (!needsTexture() || !oldSurface->needsTexture()) return false; // merge surfaces based on first layer ID if (getFirstLayer()->uniqueId() != oldSurface->getFirstLayer()->uniqueId()) return false; m_surfaceBacking = oldSurface->m_surfaceBacking; SkSafeRef(m_surfaceBacking); ALOGV("%p taking old SurfBack %p from surface %p, nt %d", this, m_surfaceBacking, oldSurface, oldSurface->needsTexture()); if (!m_surfaceBacking) { // no SurfBack to inval, so don't worry about it. return true; } SkRegion invalRegion; bool fullInval = false; if (singleLayer() && oldSurface->singleLayer()) { // both are single matching layers, simply apply inval SkRegion* layerInval = getFirstLayer()->getInvalRegion(); invalRegion = *layerInval; if (isBase()) { // the base layer paints outside it's content area to ensure the // viewport is convered, so fully invalidate all tiles if its size // changes to ensure no stale content remains LayerContent* newContent = getFirstLayer()->content(); LayerContent* oldContent = oldSurface->getFirstLayer()->content(); fullInval = newContent->width() != oldContent->width() || newContent->height() != oldContent->height(); } } else { fullInval = m_layers.size() != oldSurface->m_layers.size(); if (!fullInval) { for (unsigned int i = 0; i < m_layers.size(); i++) { if ((m_layers[i]->uniqueId() != oldSurface->m_layers[i]->uniqueId()) || (m_layers[i]->fullContentAreaMapped() != oldSurface->m_layers[i]->fullContentAreaMapped())) { // layer list has changed, fully invalidate // TODO: partially invalidate based on layer size/position fullInval = true; break; } else if (!m_layers[i]->getInvalRegion()->isEmpty()) { // merge layer inval - translate the layer's inval region into surface coordinates // TODO: handle scale/3d transform mapping FloatRect layerPos = m_layers[i]->fullContentAreaMapped(); m_layers[i]->getInvalRegion()->translate(layerPos.x(), layerPos.y()); invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op); } } } } if (fullInval) invalRegion.setRect(-1e8, -1e8, 2e8, 2e8); m_surfaceBacking->markAsDirty(invalRegion); return true; }
void RenderPath::paint(PaintInfo& paintInfo, int, int) { if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty()) return; paintInfo.context->save(); paintInfo.context->concatCTM(localToParentTransform()); SVGResourceFilter* filter = 0; FloatRect boundingBox = repaintRectInLocalCoordinates(); if (paintInfo.phase == PaintPhaseForeground) { PaintInfo savedInfo(paintInfo); prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES) paintInfo.context->setShouldAntialias(false); fillAndStrokePath(m_path, paintInfo.context, style(), this); if (static_cast<SVGStyledElement*>(node())->supportsMarkers()) m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path); finishRenderSVGContent(this, paintInfo, filter, savedInfo.context); } if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) paintOutline(paintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style()); paintInfo.context->restore(); }
void PlatformContextCairo::drawSurfaceToContext(cairo_surface_t* surface, const FloatRect& destRect, const FloatRect& originalSrcRect, GraphicsContext& context) { // Avoid invalid cairo matrix with small values. if (std::fabs(destRect.width()) < 0.5f || std::fabs(destRect.height()) < 0.5f) return; FloatRect srcRect = originalSrcRect; // We need to account for negative source dimensions by flipping the rectangle. if (originalSrcRect.width() < 0) { srcRect.setX(originalSrcRect.x() + originalSrcRect.width()); srcRect.setWidth(std::fabs(originalSrcRect.width())); } if (originalSrcRect.height() < 0) { srcRect.setY(originalSrcRect.y() + originalSrcRect.height()); srcRect.setHeight(std::fabs(originalSrcRect.height())); } RefPtr<cairo_surface_t> patternSurface = surface; float leftPadding = 0; float topPadding = 0; if (srcRect.x() || srcRect.y() || srcRect.size() != cairoSurfaceSize(surface)) { // Cairo subsurfaces don't support floating point boundaries well, so we expand the rectangle. IntRect expandedSrcRect(enclosingIntRect(srcRect)); // We use a subsurface here so that we don't end up sampling outside the originalSrcRect rectangle. // See https://bugs.webkit.org/show_bug.cgi?id=58309 patternSurface = adoptRef(cairo_surface_create_for_rectangle(surface, expandedSrcRect.x(), expandedSrcRect.y(), expandedSrcRect.width(), expandedSrcRect.height())); leftPadding = static_cast<float>(expandedSrcRect.x()) - floorf(srcRect.x()); topPadding = static_cast<float>(expandedSrcRect.y()) - floorf(srcRect.y()); } RefPtr<cairo_pattern_t> pattern = adoptRef(cairo_pattern_create_for_surface(patternSurface.get())); ASSERT(m_state); switch (m_state->m_imageInterpolationQuality) { case InterpolationNone: case InterpolationLow: cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_FAST); break; case InterpolationMedium: case InterpolationDefault: cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_GOOD); break; case InterpolationHigh: cairo_pattern_set_filter(pattern.get(), CAIRO_FILTER_BEST); break; } cairo_pattern_set_extend(pattern.get(), CAIRO_EXTEND_PAD); // The pattern transformation properly scales the pattern for when the source rectangle is a // different size than the destination rectangle. We also account for any offset we introduced // by expanding floating point source rectangle sizes. It's important to take the absolute value // of the scale since the original width and height might be negative. float scaleX = std::fabs(srcRect.width() / destRect.width()); float scaleY = std::fabs(srcRect.height() / destRect.height()); cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, leftPadding, topPadding }; cairo_pattern_set_matrix(pattern.get(), &matrix); ShadowBlur& shadow = context.platformContext()->shadowBlur(); if (shadow.type() != ShadowBlur::NoShadow) { if (GraphicsContext* shadowContext = shadow.beginShadowLayer(context, destRect)) { drawPatternToCairoContext(shadowContext->platformContext()->cr(), pattern.get(), destRect, 1); shadow.endShadowLayer(context); } } cairo_save(m_cr.get()); drawPatternToCairoContext(m_cr.get(), pattern.get(), destRect, globalAlpha()); cairo_restore(m_cr.get()); }
IntRect FilterEffect::calculateDrawingIntRect(const FloatRect& effectRect) { IntPoint location = roundedIntPoint(FloatPoint(scaledSubRegion().x() - effectRect.x(), scaledSubRegion().y() - effectRect.y())); return IntRect(location, resultImage()->size()); }
void PDFDocumentImage::draw(GraphicsContext* context, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator op) { if (!m_document || m_currentPage == -1) return; context->save(); context->setCompositeOperation(op); float hScale = dstRect.width() / srcRect.width(); float vScale = dstRect.height() / srcRect.height(); // Scale and translate so the document is rendered in the correct location, // including accounting for the fact that a GraphicsContext is always flipped // and doing appropriate flipping. CGContextTranslateCTM(context->platformContext(), dstRect.x() - srcRect.x() * hScale, dstRect.y() - srcRect.y() * vScale); CGContextScaleCTM(context->platformContext(), hScale, vScale); CGContextScaleCTM(context->platformContext(), 1, -1); CGContextTranslateCTM(context->platformContext(), 0, -srcRect.height()); CGContextClipToRect(context->platformContext(), CGRectIntegral(srcRect)); // Rotate translate image into position according to doc properties. adjustCTM(context); // Media box may have non-zero origin which we ignore. Pass 1 for the page number. CGContextDrawPDFDocument(context->platformContext(), FloatRect(FloatPoint(), m_mediaBox.size()), m_document, m_currentPage + 1); context->restore(); if (imageObserver()) imageObserver()->didDraw(this); }
FloatRect FilterEffect::calculateDrawingRect(const FloatRect& srcRect) { FloatPoint startPoint = FloatPoint(srcRect.x() - scaledSubRegion().x(), srcRect.y() - scaledSubRegion().y()); FloatRect drawingRect = FloatRect(startPoint, srcRect.size()); return drawingRect; }
void GraphicsContextPlatformPrivate::clip(const FloatRect& clipRect) { if (!m_hdc) return; IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.maxX(), clipRect.maxY()); }
bool VertexPair::overlapsRect(const FloatRect& rect) const { bool boundsOverlap = (minX() < rect.maxX()) && (maxX() > rect.x()) && (minY() < rect.maxY()) && (maxY() > rect.y()); if (!boundsOverlap) return false; float leftSideValues[4] = { leftSide(vertex1(), vertex2(), rect.minXMinYCorner()), leftSide(vertex1(), vertex2(), rect.maxXMinYCorner()), leftSide(vertex1(), vertex2(), rect.minXMaxYCorner()), leftSide(vertex1(), vertex2(), rect.maxXMaxYCorner()) }; int currentLeftSideSign = 0; for (unsigned i = 0; i < 4; ++i) { if (!leftSideValues[i]) continue; int leftSideSign = leftSideValues[i] > 0 ? 1 : -1; if (!currentLeftSideSign) currentLeftSideSign = leftSideSign; else if (currentLeftSideSign != leftSideSign) return true; } return false; }