blob: d1eadadf9dcbfb9345a22ed8048111ea4c42ccce [file] [log] [blame]
/*
* 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_FAMILY)
#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 "GraphicsContextCG.h"
#import "Image.h"
#import "NotImplemented.h"
#import "Page.h"
#import "Range.h"
#import "SimpleRange.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 <pal/ios/UIKitSoftLink.h>
#import <wtf/NeverDestroyed.h>
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([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageSize]);
UIImage *imageCopy = [render 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, ImageOrientation 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([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageSize]);
UIImage *imageCopy = [render imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
GraphicsContextCG context(rendererContext.CGContext);
context.translate(0, imageSize.height);
context.scale({ adjustedImageScale, -adjustedImageScale });
context.drawImage(*image, FloatPoint(), { orientation });
}];
return imageCopy.CGImage;
}
void deleteDragImage(DragImageRef)
{
}
static FontCascade cascadeForSystemFont(CGFloat size)
{
UIFont *font = [PAL::getUIFontClass() systemFontOfSize:size];
return FontCascade(FontPlatformData(adoptCF(CTFontCreateWithName((CFStringRef)font.fontName, font.pointSize, nil)), font.pointSize));
}
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.
constexpr CGFloat dragImagePadding = 10;
static const NeverDestroyed titleFontCascade = cascadeForSystemFont(16);
static const NeverDestroyed urlFontCascade = cascadeForSystemFont(14);
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);
auto renderer = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size]);
auto image = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
GraphicsContextCG context(rendererContext.CGContext);
context.translate(0, CGRectGetHeight(imageRect));
context.scale({ 1, -1 });
context.fillRoundedRect(FloatRoundedRect(imageRect, FloatRoundedRect::Radii(4)), Color::white);
titleFontCascade.get().drawText(context, TextRun(truncatedTopString), FloatPoint(dragImagePadding, 18 + dragImagePadding));
if (!truncatedBottomString.isEmpty())
urlFontCascade.get().drawText(context, TextRun(truncatedBottomString), FloatPoint(dragImagePadding, 40 + dragImagePadding));
}];
constexpr OptionSet<TextIndicatorOption> defaultLinkIndicatorOptions {
TextIndicatorOption::TightlyFitContent,
TextIndicatorOption::RespectTextColor,
TextIndicatorOption::UseBoundingRectAndPaintAllContentForComplexRanges,
TextIndicatorOption::ExpandClipBeyondVisibleRect,
TextIndicatorOption::ComputeEstimatedBackgroundColor
};
if (auto textIndicator = TextIndicator::createWithRange(makeRangeSelectingNodeContents(linkElement), 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;
}
constexpr OptionSet<TextIndicatorOption> defaultSelectionDragImageTextIndicatorOptions {
TextIndicatorOption::ExpandClipBeyondVisibleRect,
TextIndicatorOption::PaintAllContent,
TextIndicatorOption::UseSelectionRectForSizing,
TextIndicatorOption::ComputeEstimatedBackgroundColor
};
DragImageRef createDragImageForSelection(Frame& frame, TextIndicatorData& indicatorData, bool forceBlackText)
{
if (auto document = frame.document())
document->updateLayout();
auto options = defaultSelectionDragImageTextIndicatorOptions;
if (!forceBlackText)
options.add(TextIndicatorOption::RespectTextColor);
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());
auto renderer = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size()]);
return [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
GraphicsContextCG 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);
}].CGImage;
}
DragImageRef dissolveDragImageToFraction(DragImageRef image, float)
{
notImplemented();
return image;
}
DragImageRef createDragImageForRange(Frame& frame, const SimpleRange& range, bool forceBlackText)
{
if (auto document = frame.document())
document->updateLayout();
if (range.collapsed())
return nil;
auto options = defaultSelectionDragImageTextIndicatorOptions;
if (!forceBlackText)
options.add(TextIndicatorOption::RespectTextColor);
auto textIndicator = TextIndicator::createWithRange(range, options, TextIndicatorPresentationTransition::None);
if (!textIndicator || !textIndicator->contentImage())
return nil;
auto& image = *textIndicator->contentImage();
auto render = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:image.size()]);
UIImage *finalImage = [render imageWithActions:[&image](UIGraphicsImageRendererContext *rendererContext) {
GraphicsContextCG context(rendererContext.CGContext);
context.drawImage(image, FloatPoint());
}];
return finalImage.CGImage;
}
DragImageRef createDragImageForColor(const Color& color, const FloatRect& elementRect, float pageScaleFactor, Path& visiblePath)
{
FloatRect imageRect { 0, 0, elementRect.width() * pageScaleFactor, elementRect.height() * pageScaleFactor };
FloatRoundedRect swatch { imageRect, FloatRoundedRect::Radii(ColorSwatchCornerRadius * pageScaleFactor) };
auto render = adoptNS([PAL::allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size()]);
UIImage *image = [render imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
GraphicsContextCG context { rendererContext.CGContext };
context.translate(0, CGRectGetHeight(imageRect));
context.scale({ 1, -1 });
context.fillRoundedRect(swatch, color);
}];
visiblePath.addRoundedRect(swatch);
return image.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*, ImageOrientation)
{
return nullptr;
}
DragImageRef createDragImageForRange(Frame&, const SimpleRange&, bool)
{
return nullptr;
}
#endif
} // namespace WebCore
#endif // PLATFORM(IOS_FAMILY)