Fix the build.
[WebKit-https.git] / Source / WebKit / UIProcess / ios / WKDrawingView.mm
1 /*
2  * Copyright (C) 2018 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WKDrawingView.h"
28
29 #if HAVE(PENCILKIT)
30
31 #import "EditableImageController.h"
32 #import "WKContentViewInteraction.h"
33 #import "WKDrawingCoordinator.h"
34 #import "WKInkPickerView.h"
35 #import <wtf/OSObjectPtr.h>
36 #import <wtf/RetainPtr.h>
37
38 #import "PencilKitSoftLink.h"
39
40 @interface WKDrawingView () <PKCanvasViewDelegate>
41 @end
42
43 @implementation WKDrawingView {
44     RetainPtr<PKCanvasView> _pencilView;
45
46 #if !PLATFORM(IOS_FAMILY_SIMULATOR)
47     OSObjectPtr<dispatch_queue_t> _renderQueue;
48     RetainPtr<PKImageRenderer> _renderer;
49 #endif
50
51     __weak WKContentView *_contentView;
52 }
53
54 - (instancetype)initWithEmbeddedViewID:(WebCore::GraphicsLayer::EmbeddedViewID)embeddedViewID contentView:(WKContentView *)contentView
55 {
56     self = [super initWithEmbeddedViewID:embeddedViewID];
57     if (!self)
58         return nil;
59
60     _contentView = contentView;
61
62     _pencilView = adoptNS([WebKit::allocPKCanvasViewInstance() initWithFrame:CGRectZero]);
63
64     [_pencilView setFingerDrawingEnabled:NO];
65     [_pencilView setUserInteractionEnabled:YES];
66     [_pencilView setOpaque:NO];
67     [_pencilView setDelegate:self];
68     [_pencilView setRulerHostingDelegate:_contentView._drawingCoordinator];
69
70     [self addSubview:_pencilView.get()];
71
72     return self;
73 }
74
75 - (void)layoutSubviews
76 {
77     if (!CGRectEqualToRect([_pencilView frame], self.bounds)) {
78         [_pencilView setFrame:self.bounds];
79
80 #if !PLATFORM(IOS_FAMILY_SIMULATOR)
81         // The renderer is instantiated for a particular size output; if
82         // the size changes, we need to re-create the renderer.
83         _renderer = nil;
84 #endif
85
86         [self invalidateAttachment];
87     }
88 }
89
90 static UIImage *emptyImage()
91 {
92     UIGraphicsBeginImageContext(CGSizeMake(1, 1));
93     CGContextClearRect(UIGraphicsGetCurrentContext(), CGRectMake(0, 0, 1, 1));
94     UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
95     UIGraphicsEndImageContext();
96
97     return resultImage;
98 }
99
100 - (UIImage *)renderedDrawing
101 {
102 #if PLATFORM(IOS_FAMILY_SIMULATOR)
103     // PKImageRenderer currently doesn't work in the simulator. In order to
104     // allow strokes to persist regardless (mostly for testing), we'll
105     // synthesize an empty 1x1 image.
106     return emptyImage();
107 #else
108     if (!self.bounds.size.width || !self.bounds.size.height || !self.window.screen.scale)
109         return emptyImage();
110
111     if (!_renderQueue)
112         _renderQueue = adoptOSObject(dispatch_queue_create("com.apple.WebKit.WKDrawingView.Rendering", DISPATCH_QUEUE_SERIAL));
113
114     if (!_renderer)
115         _renderer = adoptNS([WebKit::allocPKImageRendererInstance() initWithSize:self.bounds.size scale:self.window.screen.scale renderQueue:_renderQueue.get()]);
116
117     __block RetainPtr<UIImage> resultImage;
118
119 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
120     [_renderer renderDrawing:[_pencilView drawing] completion:^(UIImage *image) {
121         resultImage = image;
122     }];
123 ALLOW_DEPRECATED_DECLARATIONS_END
124
125     // FIXME: Ideally we would not synchronously wait for this rendering,
126     // but NSFileWrapper requires data synchronously, and our clients expect
127     // an NSFileWrapper to be available synchronously.
128     dispatch_sync(_renderQueue.get(), ^{ });
129
130     return resultImage.autorelease();
131 #endif
132 }
133
134 - (NSData *)PNGRepresentation
135 {
136     RetainPtr<UIImage> image = [self renderedDrawing];
137     RetainPtr<NSMutableData> PNGData = adoptNS([[NSMutableData alloc] init]);
138     RetainPtr<CGImageDestinationRef> imageDestination = adoptCF(CGImageDestinationCreateWithData((__bridge CFMutableDataRef)PNGData.get(), kUTTypePNG, 1, nil));
139 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
140     NSString *base64Drawing = [[[_pencilView drawing] serialize] base64EncodedStringWithOptions:0];
141 ALLOW_DEPRECATED_DECLARATIONS_END
142     NSDictionary *properties = nil;
143     if (base64Drawing) {
144         // FIXME: We should put this somewhere less user-facing than the EXIF User Comment field.
145         properties = @{
146             (__bridge NSString *)kCGImagePropertyExifDictionary : @{
147                 (__bridge NSString *)kCGImagePropertyExifUserComment : base64Drawing
148             }
149         };
150     }
151     CGImageDestinationSetProperties(imageDestination.get(), (__bridge CFDictionaryRef)properties);
152     CGImageDestinationAddImage(imageDestination.get(), [image CGImage], (__bridge CFDictionaryRef)properties);
153     CGImageDestinationFinalize(imageDestination.get());
154
155     return PNGData.autorelease();
156 }
157
158 - (void)loadDrawingFromPNGRepresentation:(NSData *)PNGData
159 {
160     RetainPtr<CGImageSourceRef> imageSource = adoptCF(CGImageSourceCreateWithData((__bridge CFDataRef)PNGData, nullptr));
161     if (!imageSource)
162         return;
163     RetainPtr<NSDictionary> properties = adoptNS((__bridge NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource.get(), 0, nil));
164     NSString *base64Drawing = [[properties objectForKey:(NSString *)kCGImagePropertyExifDictionary] objectForKey:(NSString *)kCGImagePropertyExifUserComment];
165     if (!base64Drawing)
166         return;
167     RetainPtr<NSData> drawingData = adoptNS([[NSData alloc] initWithBase64EncodedString:base64Drawing options:0]);
168     RetainPtr<PKDrawing> drawing = adoptNS([WebKit::allocPKDrawingInstance() initWithData:drawingData.get() error:nil]);
169 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
170     [_pencilView setDrawing:drawing.get()];
171 ALLOW_DEPRECATED_DECLARATIONS_END
172 }
173
174 - (void)canvasViewDrawingDidChange:(PKCanvasView *)canvasView
175 {
176     [self invalidateAttachment];
177 }
178
179 - (void)_canvasViewWillBeginDrawing:(PKCanvasView *)canvasView
180 {
181 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
182     [_pencilView setInk:_contentView._drawingCoordinator.inkPicker.ink];
183 ALLOW_DEPRECATED_DECLARATIONS_END
184 }
185
186 - (void)invalidateAttachment
187 {
188     if (!_contentView.page)
189         return;
190     auto& page = *_contentView.page;
191
192     page.editableImageController().invalidateAttachmentForEditableImage(self.embeddedViewID);
193 }
194
195 - (void)didChangeRulerState:(BOOL)rulerEnabled
196 {
197     [_pencilView setRulerEnabled:rulerEnabled];
198 }
199
200 - (void)didChangeInk:(PKInk *)ink
201 {
202 ALLOW_DEPRECATED_DECLARATIONS_BEGIN
203     [_pencilView setInk:ink];
204 ALLOW_DEPRECATED_DECLARATIONS_END
205 }
206
207 @end
208
209 #endif // HAVE(PENCILKIT)