1/* 2 PPDocument_Pasteboard.m 3 4 Copyright 2013-2018 Josh Freeman 5 http://www.twilightedge.com 6 7 This file is part of PikoPixel for Mac OS X and GNUstep. 8 PikoPixel is a graphical application for drawing & editing pixel-art images. 9 10 PikoPixel is free software: you can redistribute it and/or modify it under 11 the terms of the GNU Affero General Public License as published by the 12 Free Software Foundation, either version 3 of the License, or (at your 13 option) any later version approved for PikoPixel by its copyright holder (or 14 an authorized proxy). 15 16 PikoPixel is distributed in the hope that it will be useful, but WITHOUT ANY 17 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18 FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more 19 details. 20 21 You should have received a copy of the GNU Affero General Public License 22 along with this program. If not, see <http://www.gnu.org/licenses/>. 23*/ 24 25#import "PPDocument.h" 26 27#import "PPGeometry.h" 28#import "PPDocumentLayer.h" 29#import "NSBitmapImageRep_PPUtilities.h" 30#import "NSPasteboard_PPUtilities.h" 31 32 33@interface PPDocument (PasteboardPrivateMethods) 34 35- (bool) getPasteboardImageBitmap: (NSBitmapImageRep **) returnedImageBitmap 36 maskBitmap: (NSBitmapImageRep **) returnedMaskBitmap 37 boundsOnCanvas: (NSRect *) returnedBoundsOnCanvas 38 opacity: (float *) returnedOpacity; 39 40- (bool) cropImageBitmapToCanvasFrame: (NSBitmapImageRep **) inOutImageBitmap 41 withMaskBitmap: (NSBitmapImageRep **) inOutMaskBitmap 42 andAdjustBoundsOnCanvas: (NSRect *) inOutBoundsOnCanvas; 43 44@end 45 46@implementation PPDocument (Pasteboard) 47 48- (bool) canReadFromPasteboard 49{ 50 return [NSPasteboard ppPasteboardHasBitmap]; 51} 52 53- (bool) canWriteToPasteboard 54{ 55 return _hasSelection; 56} 57 58- (void) copySelectionToPasteboardFromTarget: (PPLayerOperationTarget) operationTarget 59{ 60 NSBitmapImageRep *targetBitmap, *croppedTargetBitmap, *croppedMask; 61 float selectionOpacity; 62 63 if (!_hasSelection) 64 goto ERROR; 65 66 if (operationTarget == kPPLayerOperationTarget_DrawingLayerOnly) 67 { 68 targetBitmap = _drawingLayerBitmap; 69 selectionOpacity = [_drawingLayer opacity]; 70 } 71 else 72 { 73 targetBitmap = _mergedVisibleLayersBitmap; 74 selectionOpacity = 1.0f; 75 } 76 77 croppedTargetBitmap = [targetBitmap ppBitmapCroppedToBounds: _selectionBounds]; 78 croppedMask = [_selectionMask ppBitmapCroppedToBounds: _selectionBounds]; 79 80 if (!croppedTargetBitmap || !croppedMask) 81 { 82 goto ERROR; 83 } 84 85 [NSPasteboard ppSetImageBitmap: croppedTargetBitmap 86 maskBitmap: croppedMask 87 bitmapOrigin: _selectionBounds.origin 88 canvasSize: _canvasFrame.size 89 andOpacity: selectionOpacity]; 90 91 return; 92 93ERROR: 94 return; 95} 96 97- (void) cutSelectionToPasteboardFromTarget: (PPLayerOperationTarget) operationTarget 98{ 99 [self copySelectionToPasteboardFromTarget: operationTarget]; 100 101 [self noninteractiveEraseSelectedAreaInTarget: operationTarget 102 andClearSelectionMask: YES]; 103} 104 105- (void) pasteNewLayerFromPasteboard 106{ 107 NSBitmapImageRep *pasteboardImageBitmap; 108 NSRect pasteboardBoundsOnCanvas; 109 float pasteboardOpacity; 110 PPDocumentLayer *layer; 111 112 if (![self getPasteboardImageBitmap: &pasteboardImageBitmap 113 maskBitmap: NULL 114 boundsOnCanvas: &pasteboardBoundsOnCanvas 115 opacity: &pasteboardOpacity]) 116 { 117 goto ERROR; 118 } 119 120 layer = [PPDocumentLayer layerWithSize: _canvasFrame.size andName: @"Pasted Layer"]; 121 122 if (!layer) 123 return; 124 125 [[layer bitmap] ppCopyFromBitmap: pasteboardImageBitmap 126 toPoint: pasteboardBoundsOnCanvas.origin]; 127 128 [layer handleUpdateToBitmapInRect: pasteboardBoundsOnCanvas]; 129 130 [layer setOpacity: pasteboardOpacity]; 131 132 [self insertLayer: layer atIndex: _indexOfDrawingLayer + 1 andSetAsDrawingLayer: YES]; 133 134 [[self undoManager] setActionName: @"Paste as New Layer"]; 135 136 return; 137 138ERROR: 139 return; 140} 141 142- (void) pasteIntoDrawingLayerFromPasteboard 143{ 144 NSBitmapImageRep *pasteboardImageBitmap, *pasteboardMaskBitmap, *updateBitmap; 145 NSRect pasteboardBoundsOnCanvas; 146 147 if (![self getPasteboardImageBitmap: &pasteboardImageBitmap 148 maskBitmap: &pasteboardMaskBitmap 149 boundsOnCanvas: &pasteboardBoundsOnCanvas 150 opacity: NULL]) 151 { 152 goto ERROR; 153 } 154 155 // returned pasteboardMaskBitmap can be nil 156 if (!pasteboardMaskBitmap) 157 { 158 pasteboardMaskBitmap = 159 [pasteboardImageBitmap ppMaskBitmapForVisiblePixelsInImageBitmap]; 160 161 if (!pasteboardMaskBitmap) 162 goto ERROR; 163 } 164 165 updateBitmap = [_drawingLayerBitmap ppBitmapCroppedToBounds: pasteboardBoundsOnCanvas]; 166 167 if (!updateBitmap) 168 goto ERROR; 169 170 [updateBitmap ppMaskedCopyFromImageBitmap: pasteboardImageBitmap 171 usingMask: pasteboardMaskBitmap]; 172 173 [self copyImageBitmapToDrawingLayer: updateBitmap atPoint: pasteboardBoundsOnCanvas.origin]; 174 175 if (_hasSelection) 176 { 177 [self deselectAll]; 178 } 179 180 [self setSelectionMaskAreaWithBitmap: pasteboardMaskBitmap 181 atPoint: pasteboardBoundsOnCanvas.origin]; 182 183 [[self undoManager] setActionName: @"Paste into Draw Layer"]; 184 185 return; 186 187ERROR: 188 return; 189} 190 191+ (PPDocument *) ppDocumentFromPasteboard 192{ 193 NSBitmapImageRep *imageBitmap, *maskBitmap; 194 float opacity; 195 PPDocumentLayer *layer; 196 NSArray *layersArray; 197 PPDocument *ppDocument; 198 199 if (![NSPasteboard ppGetImageBitmap: &imageBitmap 200 maskBitmap: &maskBitmap 201 bitmapOrigin: NULL 202 canvasSize: NULL 203 andOpacity: &opacity]) 204 { 205 goto ERROR; 206 } 207 208 layer = [[[PPDocumentLayer alloc] initWithSize: [imageBitmap ppSizeInPixels] 209 name: @"Main Layer" 210 tiffData: [imageBitmap TIFFRepresentation] 211 opacity: opacity 212 isEnabled: YES] 213 autorelease]; 214 215 if (!layer) 216 goto ERROR; 217 218 layersArray = [NSArray arrayWithObject: layer]; 219 220 if (!layersArray) 221 goto ERROR; 222 223 ppDocument = [[[PPDocument alloc] init] autorelease]; 224 225 if (!ppDocument) 226 goto ERROR; 227 228 [ppDocument setLayers: layersArray]; 229 230 if (maskBitmap) 231 { 232 [ppDocument setSelectionMask: maskBitmap]; 233 } 234 235 [[ppDocument undoManager] removeAllActions]; 236 237 return ppDocument; 238 239ERROR: 240 return nil; 241} 242 243#pragma mark Private methods 244 245- (bool) getPasteboardImageBitmap: (NSBitmapImageRep **) returnedImageBitmap 246 maskBitmap: (NSBitmapImageRep **) returnedMaskBitmap 247 boundsOnCanvas: (NSRect *) returnedBoundsOnCanvas 248 opacity: (float *) returnedOpacity 249{ 250 NSBitmapImageRep *imageBitmap; 251 NSPoint bitmapOrigin; 252 NSSize bitmapCanvasSize; 253 254 if (![NSPasteboard ppGetImageBitmap: &imageBitmap 255 maskBitmap: returnedMaskBitmap 256 bitmapOrigin: &bitmapOrigin 257 canvasSize: &bitmapCanvasSize 258 andOpacity: returnedOpacity]) 259 { 260 goto ERROR; 261 } 262 263 if (returnedBoundsOnCanvas) 264 { 265 NSRect boundsOnCanvas; 266 267 boundsOnCanvas.origin = bitmapOrigin; 268 boundsOnCanvas.size = [imageBitmap ppSizeInPixels]; 269 270 if (!NSEqualSizes(bitmapCanvasSize, _canvasFrame.size)) 271 { 272 boundsOnCanvas = PPGeometry_CenterRectInRect(boundsOnCanvas, _canvasFrame); 273 } 274 275 if (![self cropImageBitmapToCanvasFrame: &imageBitmap 276 withMaskBitmap: returnedMaskBitmap 277 andAdjustBoundsOnCanvas: &boundsOnCanvas]) 278 { 279 goto ERROR; 280 } 281 282 *returnedBoundsOnCanvas = boundsOnCanvas; 283 } 284 285 if (returnedImageBitmap) 286 { 287 *returnedImageBitmap = imageBitmap; 288 } 289 290 return YES; 291 292ERROR: 293 return NO; 294} 295 296- (bool) cropImageBitmapToCanvasFrame: (NSBitmapImageRep **) inOutImageBitmap 297 withMaskBitmap: (NSBitmapImageRep **) inOutMaskBitmap 298 andAdjustBoundsOnCanvas: (NSRect *) inOutBoundsOnCanvas 299{ 300 NSBitmapImageRep *imageBitmap, *newImageBitmap; 301 NSRect boundsOnCanvas, newBoundsOnCanvas, croppingBounds; 302 303 if (!inOutImageBitmap || !inOutBoundsOnCanvas) 304 { 305 goto ERROR; 306 } 307 308 imageBitmap = *inOutImageBitmap; 309 boundsOnCanvas = *inOutBoundsOnCanvas; 310 311 if (!imageBitmap || NSIsEmptyRect(boundsOnCanvas)) 312 { 313 goto ERROR; 314 } 315 316 if (NSContainsRect(_canvasFrame, boundsOnCanvas)) 317 { 318 return YES; 319 } 320 321 newBoundsOnCanvas = NSIntersectionRect(_canvasFrame, boundsOnCanvas); 322 323 if (NSIsEmptyRect(newBoundsOnCanvas)) 324 { 325 goto ERROR; 326 } 327 328 croppingBounds.size = newBoundsOnCanvas.size; 329 croppingBounds.origin = 330 PPGeometry_PointDifference(newBoundsOnCanvas.origin, boundsOnCanvas.origin); 331 332 newImageBitmap = [imageBitmap ppBitmapCroppedToBounds: croppingBounds]; 333 334 if (!newImageBitmap) 335 goto ERROR; 336 337 if (inOutMaskBitmap) 338 { 339 NSBitmapImageRep *maskBitmap = *inOutMaskBitmap; 340 341 if (maskBitmap) 342 { 343 maskBitmap = [maskBitmap ppBitmapCroppedToBounds: croppingBounds]; 344 345 if (!maskBitmap) 346 goto ERROR; 347 348 *inOutMaskBitmap = maskBitmap; 349 } 350 } 351 352 *inOutImageBitmap = newImageBitmap; 353 *inOutBoundsOnCanvas = newBoundsOnCanvas; 354 355 return YES; 356 357ERROR: 358 return NO; 359} 360 361@end 362