Improve use of NeverDestroyed
[WebKit-https.git] / Source / WebCore / platform / ios / DragImageIOS.mm
1 /*
2  * Copyright (C) 2014 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "DragImage.h"
28
29 #if PLATFORM(IOS)
30
31 #import "Document.h"
32 #import "Element.h"
33 #import "FloatRoundedRect.h"
34 #import "FontCascade.h"
35 #import "FontPlatformData.h"
36 #import "Frame.h"
37 #import "GeometryUtilities.h"
38 #import "GraphicsContext.h"
39 #import "Image.h"
40 #import "NotImplemented.h"
41 #import "Page.h"
42 #import "Range.h"
43 #import "StringTruncator.h"
44 #import "TextIndicator.h"
45 #import "TextRun.h"
46 #import <CoreGraphics/CoreGraphics.h>
47 #import <CoreText/CoreText.h>
48 #import <UIKit/UIColor.h>
49 #import <UIKit/UIFont.h>
50 #import <UIKit/UIGraphicsImageRenderer.h>
51 #import <UIKit/UIImage.h>
52 #import <wtf/NeverDestroyed.h>
53 #import <wtf/SoftLinking.h>
54
55 #pragma clang diagnostic push
56 #pragma clang diagnostic ignored "-Wnullability-completeness"
57
58 SOFT_LINK_FRAMEWORK(UIKit)
59 SOFT_LINK_CLASS(UIKit, UIFont)
60 SOFT_LINK_CLASS(UIKit, UIGraphicsImageRenderer)
61 SOFT_LINK(UIKit, UIGraphicsBeginImageContextWithOptions, void, (CGSize size, BOOL opaque, CGFloat scale), (size, opaque, scale))
62 SOFT_LINK(UIKit, UIGraphicsGetCurrentContext, CGContextRef, (void), ())
63 SOFT_LINK(UIKit, UIGraphicsGetImageFromCurrentImageContext, UIImage *, (void), ())
64 SOFT_LINK(UIKit, UIGraphicsEndImageContext, void, (void), ())
65
66 #pragma clang diagnostic pop
67
68 namespace WebCore {
69
70 #if ENABLE(DRAG_SUPPORT)
71
72 IntSize dragImageSize(DragImageRef image)
73 {
74     return IntSize(CGImageGetWidth(image.get()), CGImageGetHeight(image.get()));
75 }
76
77 DragImageRef scaleDragImage(DragImageRef image, FloatSize scale)
78 {
79     CGSize imageSize = CGSizeMake(scale.width() * CGImageGetWidth(image.get()), scale.height() * CGImageGetHeight(image.get()));
80     CGRect imageRect = { CGPointZero, imageSize };
81
82     RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageSize]);
83     UIImage *imageCopy = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
84         CGContextRef context = rendererContext.CGContext;
85         CGContextTranslateCTM(context, 0, imageSize.height);
86         CGContextScaleCTM(context, 1, -1);
87         CGContextDrawImage(context, imageRect, image.get());
88     }];
89     return imageCopy.CGImage;
90 }
91
92 static float maximumAllowedDragImageArea = 600 * 1024;
93
94 DragImageRef createDragImageFromImage(Image* image, ImageOrientationDescription orientation)
95 {
96     if (!image || !image->width() || !image->height())
97         return nil;
98
99     float adjustedImageScale = 1;
100     CGSize imageSize(image->size());
101     if (imageSize.width * imageSize.height > maximumAllowedDragImageArea) {
102         auto adjustedSize = roundedIntSize(sizeWithAreaAndAspectRatio(maximumAllowedDragImageArea, imageSize.width / imageSize.height));
103         adjustedImageScale = adjustedSize.width() / imageSize.width;
104         imageSize = adjustedSize;
105     }
106
107     RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageSize]);
108     UIImage *imageCopy = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
109         GraphicsContext context(rendererContext.CGContext);
110         context.translate(0, imageSize.height);
111         context.scale({ adjustedImageScale, -adjustedImageScale });
112         ImagePaintingOptions paintingOptions;
113         paintingOptions.m_orientationDescription = orientation;
114         context.drawImage(*image, FloatPoint(), paintingOptions);
115     }];
116     return imageCopy.CGImage;
117 }
118
119 void deleteDragImage(DragImageRef)
120 {
121 }
122
123 static const TextIndicatorOptions defaultLinkIndicatorOptions = TextIndicatorOptionTightlyFitContent | TextIndicatorOptionRespectTextColor | TextIndicatorOptionUseBoundingRectAndPaintAllContentForComplexRanges | TextIndicatorOptionExpandClipBeyondVisibleRect | TextIndicatorOptionComputeEstimatedBackgroundColor;
124
125 static FontCascade cascadeForSystemFont(CGFloat size)
126 {
127     UIFont *font = [getUIFontClass() systemFontOfSize:16];
128     return FontPlatformData(CTFontCreateWithName((CFStringRef)font.fontName, font.pointSize, nil), font.pointSize), AutoSmoothing);
129 }
130
131 DragImageRef createDragImageForLink(Element& linkElement, URL& url, const String& title, TextIndicatorData& indicatorData, FontRenderingMode, float)
132 {
133     // FIXME: Most of this can go away once we can use UIURLDragPreviewView unconditionally.
134     static const CGFloat dragImagePadding = 10;
135     static const auto titleFontCascade = makeNeverDestroyed(cascadeForSystemFont(16));
136     static const auto urlFontCascade = makeNeverDestroyed(cascadeForSystemFont(14));
137
138     String topString(title.stripWhiteSpace());
139     String bottomString([(NSURL *)url absoluteString]);
140     if (topString.isEmpty()) {
141         topString = bottomString;
142         bottomString = emptyString();
143     }
144
145     static CGFloat maxTextWidth = 320;
146     auto truncatedTopString = StringTruncator::rightTruncate(topString, maxTextWidth, titleFontCascade);
147     auto truncatedBottomString = StringTruncator::centerTruncate(bottomString, maxTextWidth, urlFontCascade);
148     CGFloat textWidth = std::max(StringTruncator::width(truncatedTopString, titleFontCascade), StringTruncator::width(truncatedBottomString, urlFontCascade));
149     CGFloat textHeight = truncatedBottomString.isEmpty() ? 22 : 44;
150
151     CGRect imageRect = CGRectMake(0, 0, textWidth + 2 * dragImagePadding, textHeight + 2 * dragImagePadding);
152
153     RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size]);
154     UIImage *image = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
155         GraphicsContext context(rendererContext.CGContext);
156         context.translate(0, CGRectGetHeight(imageRect));
157         context.scale({ 1, -1 });
158         context.fillRoundedRect(FloatRoundedRect(imageRect, FloatRoundedRect::Radii(4)), { 255, 255, 255 });
159         titleFontCascade->drawText(context, TextRun(truncatedTopString), FloatPoint(dragImagePadding, 18 + dragImagePadding));
160         if (!truncatedBottomString.isEmpty())
161             urlFontCascade->drawText(context, TextRun(truncatedBottomString), FloatPoint(dragImagePadding, 40 + dragImagePadding));
162     }];
163
164     auto linkRange = rangeOfContents(linkElement);
165     if (auto textIndicator = TextIndicator::createWithRange(linkRange, defaultLinkIndicatorOptions, TextIndicatorPresentationTransition::None, FloatSize()))
166         indicatorData = textIndicator->data();
167
168     return image.CGImage;
169 }
170
171 DragImageRef createDragImageIconForCachedImageFilename(const String&)
172 {
173     notImplemented();
174     return nullptr;
175 }
176
177 DragImageRef platformAdjustDragImageForDeviceScaleFactor(DragImageRef image, float)
178 {
179     // 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.
180     return image;
181 }
182
183 static TextIndicatorOptions defaultSelectionDragImageTextIndicatorOptions = TextIndicatorOptionExpandClipBeyondVisibleRect | TextIndicatorOptionPaintAllContent | TextIndicatorOptionUseSelectionRectForSizing | TextIndicatorOptionComputeEstimatedBackgroundColor;
184
185 DragImageRef createDragImageForSelection(Frame& frame, TextIndicatorData& indicatorData, bool forceBlackText)
186 {
187     if (auto document = frame.document())
188         document->updateLayout();
189
190     TextIndicatorOptions options = defaultSelectionDragImageTextIndicatorOptions;
191     if (!forceBlackText)
192         options |= TextIndicatorOptionRespectTextColor;
193
194     auto textIndicator = TextIndicator::createWithSelectionInFrame(frame, options, TextIndicatorPresentationTransition::None, FloatSize());
195     if (!textIndicator)
196         return nullptr;
197
198     auto image = textIndicator->contentImage();
199     if (image)
200         indicatorData = textIndicator->data();
201     else
202         return nullptr;
203
204     FloatRect imageRect(0, 0, image->width(), image->height());
205     if (auto page = frame.page())
206         imageRect.scale(1 / page->deviceScaleFactor());
207
208
209     RetainPtr<UIGraphicsImageRenderer> render = adoptNS([allocUIGraphicsImageRendererInstance() initWithSize:imageRect.size()]);
210     UIImage *finalImage = [render.get() imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
211         GraphicsContext context(rendererContext.CGContext);
212         context.translate(0, imageRect.height());
213         context.scale({ 1, -1 });
214         context.drawImage(*image, imageRect);
215     }];
216
217     return finalImage.CGImage;
218 }
219
220 DragImageRef dissolveDragImageToFraction(DragImageRef image, float)
221 {
222     notImplemented();
223     return image;
224 }
225
226 #else
227
228 void deleteDragImage(RetainPtr<CGImageRef>)
229 {
230     // Since this is a RetainPtr, there's nothing additional we need to do to
231     // delete it. It will be released when it falls out of scope.
232 }
233
234 // FIXME: fix signature of dragImageSize() to avoid copying the argument.
235 IntSize dragImageSize(RetainPtr<CGImageRef> image)
236 {
237     return IntSize(CGImageGetWidth(image.get()), CGImageGetHeight(image.get()));
238 }
239
240 RetainPtr<CGImageRef> scaleDragImage(RetainPtr<CGImageRef>, FloatSize)
241 {
242     return nullptr;
243 }
244
245 RetainPtr<CGImageRef> createDragImageFromImage(Image*, ImageOrientationDescription)
246 {
247     return nullptr;
248 }
249
250 #endif
251
252 } // namespace WebCore
253
254 #endif // PLATFORM(IOS)