/* * Copyright (C) 2014 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "DragImage.h" #if PLATFORM(IOS) #import "Document.h" #import "Element.h" #import "FloatRoundedRect.h" #import "FontCascade.h" #import "FontPlatformData.h" #import "Frame.h" #import "GeometryUtilities.h" #import "GraphicsContext.h" #import "Image.h" #import "NotImplemented.h" #import "Page.h" #import "Range.h" #import "StringTruncator.h" #import "TextIndicator.h" #import "TextRun.h" #import <CoreGraphics/CoreGraphics.h> #import <CoreText/CoreText.h> #import <UIKit/UIColor.h> #import <UIKit/UIFont.h> #import <UIKit/UIGraphicsImageRenderer.h> #import <UIKit/UIImage.h> #import <wtf/NeverDestroyed.h> #import <wtf/SoftLinking.h> #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wnullability-completeness" SOFT_LINK_FRAMEWORK(UIKit) SOFT_LINK_CLASS(UIKit, UIFont) SOFT_LINK_CLASS(UIKit, UIGraphicsImageRenderer) SOFT_LINK(UIKit, UIGraphicsBeginImageContextWithOptions, void, (CGSize size, BOOL opaque, CGFloat scale), (size, opaque, scale)) SOFT_LINK(UIKit, UIGraphicsGetCurrentContext, CGContextRef, (void), ()) SOFT_LINK(UIKit, UIGraphicsGetImageFromCurrentImageContext, UIImage *, (void), ()) SOFT_LINK(UIKit, UIGraphicsEndImageContext, void, (void), ()) #pragma clang diagnostic pop namespace WebCore { #if ENABLE(DRAG_SUPPORT) IntSize dragImageSize(DragImageRef image) { return IntSize(CGImageGetWidth(image.get()), CGImageGetHeight(image.get())); } DragImageRef scaleDragImage(DragImageRef image, FloatSize scale) { CGSize imageSize = CGSizeMake(scale.width() * CGImageGetWidth(image.get()), scale.height() * CGImageGetHeight(image.get())); CGRect imageRect = { CGPointZero, imageSize }; RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageSize]); UIImage *imageCopy = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) { CGContextRef context = rendererContext.CGContext; CGContextTranslateCTM(context, 0, imageSize.height); CGContextScaleCTM(context, 1, -1); CGContextDrawImage(context, imageRect, image.get()); }]; return imageCopy.CGImage; } static float maximumAllowedDragImageArea = 600 * 1024; DragImageRef createDragImageFromImage(Image* image, ImageOrientationDescription orientation) { if (!image || !image->width() || !image->height()) return nil; float adjustedImageScale = 1; CGSize imageSize(image->size()); if (imageSize.width * imageSize.height > maximumAllowedDragImageArea) { auto adjustedSize = roundedIntSize(sizeWithAreaAndAspectRatio(maximumAllowedDragImageArea, imageSize.width / imageSize.height)); adjustedImageScale = adjustedSize.width() / imageSize.width; imageSize = adjustedSize; } RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageSize]); UIImage *imageCopy = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) { GraphicsContext context(rendererContext.CGContext); context.translate(0, imageSize.height); context.scale({ adjustedImageScale, -adjustedImageScale }); ImagePaintingOptions paintingOptions; paintingOptions.m_orientationDescription = orientation; context.drawImage(*image, FloatPoint(), paintingOptions); }]; return imageCopy.CGImage; } void deleteDragImage(DragImageRef) { } static TextIndicatorOptions defaultLinkIndicatorOptions = TextIndicatorOptionTightlyFitContent | TextIndicatorOptionRespectTextColor | TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges | TextIndicatorOptionExpandClipBeyondVisibleRect | TextIndicatorOptionComputeEstimatedBackgroundColor; DragImageRef createDragImageForLink(Element& linkElement, URL& url, const String& title, TextIndicatorData& indicatorData, FontRenderingMode, float) { // FIXME: Most of this can go away once we can use UIURLDragPreviewView unconditionally. static CGFloat dragImagePadding = 10; static LazyNeverDestroyed<FontCascade> titleFontCascade; static LazyNeverDestroyed<FontCascade> urlFontCascade; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { UIFont *titleFont = [getUIFontClass() systemFontOfSize:16]; UIFont *urlFont = [getUIFontClass() systemFontOfSize:14]; titleFontCascade.construct(FontPlatformData(CTFontCreateWithName((CFStringRef)titleFont.fontName, titleFont.pointSize, nil), titleFont.pointSize), AutoSmoothing); urlFontCascade.construct(FontPlatformData(CTFontCreateWithName((CFStringRef)urlFont.fontName, urlFont.pointSize, nil), urlFont.pointSize), AutoSmoothing); }); String topString(title.stripWhiteSpace()); String bottomString([(NSURL *)url absoluteString]); if (topString.isEmpty()) { topString = bottomString; bottomString = emptyString(); } static CGFloat maxTextWidth = 320; auto truncatedTopString = StringTruncator::rightTruncate(topString, maxTextWidth, titleFontCascade); auto truncatedBottomString = StringTruncator::centerTruncate(bottomString, maxTextWidth, urlFontCascade); CGFloat textWidth = std::max(StringTruncator::width(truncatedTopString, titleFontCascade), StringTruncator::width(truncatedBottomString, urlFontCascade)); CGFloat textHeight = truncatedBottomString.isEmpty() ? 22 : 44; CGRect imageRect = CGRectMake(0, 0, textWidth + 2 * dragImagePadding, textHeight + 2 * dragImagePadding); RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size]); UIImage *image = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) { GraphicsContext context(rendererContext.CGContext); context.translate(0, CGRectGetHeight(imageRect)); context.scale({ 1, -1 }); context.fillRoundedRect(FloatRoundedRect(imageRect, FloatRoundedRect::Radii(4)), { 255, 255, 255 }); titleFontCascade->drawText(context, TextRun(truncatedTopString), FloatPoint(dragImagePadding, 18 + dragImagePadding)); if (!truncatedBottomString.isEmpty()) urlFontCascade->drawText(context, TextRun(truncatedBottomString), FloatPoint(dragImagePadding, 40 + dragImagePadding)); }]; auto linkRange = rangeOfContents(linkElement); if (auto textIndicator = TextIndicator::createWithRange(linkRange, defaultLinkIndicatorOptions, TextIndicatorPresentationTransition::None, FloatSize())) indicatorData = textIndicator->data(); return image.CGImage; } DragImageRef createDragImageIconForCachedImageFilename(const String&) { notImplemented(); return nullptr; } DragImageRef platformAdjustDragImageForDeviceScaleFactor(DragImageRef image, float) { // On iOS, we just create the drag image at the right device scale factor, so we don't need to scale it by 1 / deviceScaleFactor later. return image; } static TextIndicatorOptions defaultSelectionDragImageTextIndicatorOptions = TextIndicatorOptionExpandClipBeyondVisibleRect | TextIndicatorOptionPaintAllContent | TextIndicatorOptionUseSelectionRectForSizing | TextIndicatorOptionComputeEstimatedBackgroundColor; DragImageRef createDragImageForSelection(Frame& frame, TextIndicatorData& indicatorData, bool forceBlackText) { if (auto document = frame.document()) document->updateLayout(); TextIndicatorOptions options = defaultSelectionDragImageTextIndicatorOptions; if (!forceBlackText) options |= TextIndicatorOptionRespectTextColor; auto textIndicator = TextIndicator::createWithSelectionInFrame(frame, options, TextIndicatorPresentationTransition::None, FloatSize()); if (!textIndicator) return nullptr; auto image = textIndicator->contentImage(); if (image) indicatorData = textIndicator->data(); else return nullptr; FloatRect imageRect(0, 0, image->width(), image->height()); if (auto page = frame.page()) imageRect.scale(1 / page->deviceScaleFactor()); RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size()]); UIImage *finalImage = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) { GraphicsContext context(rendererContext.CGContext); // FIXME: The context flip here should not be necessary, and suggests that somewhere else in the regular // drag initiation flow, we unnecessarily flip the graphics context. context.translate(0, imageRect.height()); context.scale({ 1, -1 }); context.drawImage(*image, imageRect); }]; return finalImage.CGImage; } DragImageRef dissolveDragImageToFraction(DragImageRef image, float) { notImplemented(); return image; } DragImageRef createDragImageForRange(Frame& frame, Range& range, bool forceBlackText) { if (auto document = frame.document()) document->updateLayout(); if (range.collapsed()) return nil; TextIndicatorOptions options = defaultSelectionDragImageTextIndicatorOptions; if (!forceBlackText) options |= TextIndicatorOptionRespectTextColor; auto textIndicator = TextIndicator::createWithRange(range, options, TextIndicatorPresentationTransition::None); if (!textIndicator || !textIndicator->contentImage()) return nil; auto& image = *textIndicator->contentImage(); auto render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:image.size()]); UIImage *finalImage = [render.get() imageWithActions:[&image](UIGraphicsImageRendererContext *rendererContext) { GraphicsContext context(rendererContext.CGContext); context.drawImage(image, FloatPoint()); }]; return finalImage.CGImage; } #else void deleteDragImage(RetainPtr<CGImageRef>) { // Since this is a RetainPtr, there's nothing additional we need to do to // delete it. It will be released when it falls out of scope. } // FIXME: fix signature of dragImageSize() to avoid copying the argument. IntSize dragImageSize(RetainPtr<CGImageRef> image) { return IntSize(CGImageGetWidth(image.get()), CGImageGetHeight(image.get())); } RetainPtr<CGImageRef> scaleDragImage(RetainPtr<CGImageRef>, FloatSize) { return nullptr; } RetainPtr<CGImageRef> createDragImageFromImage(Image*, ImageOrientationDescription) { return nullptr; } DragImageRef createDragImageForRange(Frame&, Range&, bool) { return nullptr; } #endif } // namespace WebCore #endif // PLATFORM(IOS)