1/* 2 PPDocumentLayer.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 "PPDocumentLayer.h" 26 27#import "NSImage_PPUtilities.h" 28#import "NSBitmapImageRep_PPUtilities.h" 29#import "PPGeometry.h" 30#import "PPDefines.h" 31 32 33#define kDrawingLayerCodingKey_Size @"Size" 34#define kDrawingLayerCodingKey_Name @"Name" 35#define kDrawingLayerCodingKey_TIFFData @"TIFFData" 36#define kDrawingLayerCodingKey_Opacity @"Opacity" 37#define kDrawingLayerCodingKey_IsEnabled @"IsEnabled" 38 39#define kOpacityStepSize 0.1f 40 41 42@interface PPDocumentLayer (PrivateMethods) 43 44- (void) setOpacity: (float) opacity andRegisterUndo: (bool) shouldRegisterUndo; 45 46- (void) notifyDelegateDidChangeNameFromOldValue: (NSString *) oldName; 47- (void) notifyDelegateDidChangeEnabledFlagFromOldValue: (bool) oldEnabledFlag; 48- (void) notifyDelegateDidChangeOpacityAndShouldRegisterUndo: (bool) shouldRegisterUndo; 49 50@end 51 52@implementation PPDocumentLayer 53 54+ layerWithSize: (NSSize) size 55 andName: (NSString *) name 56{ 57 return [[[self alloc] initWithSize: size 58 name: name 59 tiffData: nil 60 opacity: 1.0f 61 isEnabled: YES] 62 autorelease]; 63} 64 65+ layerWithSize: (NSSize) size 66 name: (NSString *) name 67 tiffData: (NSData *) tiffData 68{ 69 return [[[self alloc] initWithSize: size 70 name: name 71 tiffData: tiffData 72 opacity: 1.0f 73 isEnabled: YES] 74 autorelease]; 75} 76 77- initWithSize: (NSSize) size 78 name: (NSString *) name 79 tiffData: (NSData *) tiffData 80 opacity: (float) opacity 81 isEnabled: (bool) isEnabled 82{ 83 self = [super init]; 84 85 if (!self) 86 goto ERROR; 87 88 if (![name length]) 89 { 90 goto ERROR; 91 } 92 93 size = PPGeometry_SizeClippedToIntegerValues(size); 94 95 if ((size.width < kMinCanvasDimension) 96 || (size.width > kMaxCanvasDimension) 97 || (size.height < kMinCanvasDimension) 98 || (size.height > kMaxCanvasDimension)) 99 { 100 goto ERROR; 101 } 102 103 _size = size; 104 105 _name = [name copy]; 106 _bitmap = [[NSBitmapImageRep ppImageBitmapOfSize: size] retain]; 107 _image = [[NSImage ppImageWithBitmap: _bitmap] retain]; 108 109 if (!_name || !_bitmap || !_image) 110 { 111 goto ERROR; 112 } 113 114 if (tiffData) 115 { 116 [_bitmap ppCenteredCopyFromBitmap: 117 [NSBitmapImageRep ppImageBitmapWithImportedData: tiffData]]; 118 } 119 120 if (opacity > 1.0f) 121 { 122 opacity = 1.0f; 123 } 124 else if (opacity < 0.0f) 125 { 126 opacity = 0.0f; 127 } 128 129 _opacity = _lastOpacity = opacity; 130 _isEnabled = (isEnabled) ? YES : NO; 131 132 return self; 133 134ERROR: 135 [self release]; 136 137 return nil; 138} 139 140- init 141{ 142 return [self initWithSize: NSZeroSize name: nil tiffData: nil opacity: 1.0f isEnabled: YES]; 143} 144 145- (void) dealloc 146{ 147 [_name release]; 148 [_bitmap release]; 149 [_image release]; 150 151 [_linearBlendingBitmap release]; 152 153 [super dealloc]; 154} 155 156- (NSBitmapImageRep *) bitmap 157{ 158 return _bitmap; 159} 160 161- (NSImage *) image 162{ 163 return _image; 164} 165 166// handleUpdateToBitmapInRect: method is a patch target on GNUstep 167// (PPGNUstepGlue_ImageRecacheSpeedups) 168 169- (void) handleUpdateToBitmapInRect: (NSRect) updateRect 170{ 171 [_image recache]; 172 173 if (_linearBlendingBitmap) 174 { 175 [_linearBlendingBitmap ppLinearCopyFromImageBitmap: _bitmap inBounds: updateRect]; 176 } 177} 178 179- (bool) isEnabled 180{ 181 return _isEnabled; 182} 183 184- (void) setEnabled: (bool) enabled 185{ 186 if (enabled == _isEnabled) 187 { 188 return; 189 } 190 191 _isEnabled = (enabled) ? YES : NO; 192 193 [self notifyDelegateDidChangeEnabledFlagFromOldValue: enabled ? NO : YES]; 194} 195 196- (NSString *) name 197{ 198 return _name; 199} 200 201- (void) setName: (NSString *) name 202{ 203 NSString *oldName; 204 205 name = [[name copy] autorelease]; 206 207 if (!name) 208 { 209 name = @""; 210 } 211 212 if ((_name == name) || [_name isEqualToString: name]) 213 { 214 return; 215 } 216 217 oldName = [[_name retain] autorelease]; 218 219 [_name release]; 220 _name = [name retain]; 221 222 [self notifyDelegateDidChangeNameFromOldValue: oldName]; 223} 224 225- (float) opacity 226{ 227 return _opacity; 228} 229 230- (void) setOpacity: (float) opacity 231{ 232 [self setOpacity: opacity andRegisterUndo: YES]; 233} 234 235- (void) setOpacityWithoutRegisteringUndo: (float) opacity 236{ 237 [self setOpacity: opacity andRegisterUndo: NO]; 238} 239 240- (void) increaseOpacity 241{ 242 if (![self canIncreaseOpacity]) 243 { 244 return; 245 } 246 247 [self setOpacity: _opacity + kOpacityStepSize]; 248} 249 250- (bool) canIncreaseOpacity 251{ 252 return (_opacity < 1.0f) ? YES : NO; 253} 254 255- (void) decreaseOpacity 256{ 257 if (![self canDecreaseOpacity]) 258 { 259 return; 260 } 261 262 [self setOpacity: _opacity - kOpacityStepSize]; 263} 264 265- (bool) canDecreaseOpacity 266{ 267 return (_opacity > 0.0f) ? YES : NO; 268} 269 270- (NSSize) size 271{ 272 return _size; 273} 274 275- (PPDocumentLayer *) layerResizedToSize: (NSSize) newSize shouldScale: (bool) shouldScale 276{ 277 NSBitmapImageRep *resizedBitmap; 278 NSData *resizedBitmapData; 279 PPDocumentLayer *resizedLayer; 280 281 newSize = PPGeometry_SizeClippedToIntegerValues(newSize); 282 283 resizedBitmap = [_bitmap ppBitmapResizedToSize: newSize shouldScale: shouldScale]; 284 285 if (!resizedBitmap) 286 goto ERROR; 287 288 resizedBitmapData = [resizedBitmap TIFFRepresentation]; 289 290 if (!resizedBitmapData) 291 goto ERROR; 292 293 resizedLayer = [[[PPDocumentLayer alloc] initWithSize: newSize 294 name: _name 295 tiffData: resizedBitmapData 296 opacity: _opacity 297 isEnabled: _isEnabled] 298 autorelease]; 299 300 return resizedLayer; 301 302ERROR: 303 return nil; 304} 305 306- (PPDocumentLayer *) layerCroppedToBounds: (NSRect) croppingBounds 307{ 308 NSData *croppedBitmapTIFFData; 309 PPDocumentLayer *croppedLayer; 310 311 croppingBounds = NSIntersectionRect(PPGeometry_PixelBoundsCoveredByRect(croppingBounds), 312 [_bitmap ppFrameInPixels]); 313 314 if (NSIsEmptyRect(croppingBounds)) 315 { 316 goto ERROR; 317 } 318 319 croppedBitmapTIFFData = [_bitmap ppCompressedTIFFDataFromBounds: croppingBounds]; 320 321 if (!croppedBitmapTIFFData) 322 goto ERROR; 323 324 croppedLayer = [[[PPDocumentLayer alloc] initWithSize: croppingBounds.size 325 name: _name 326 tiffData: croppedBitmapTIFFData 327 opacity: _opacity 328 isEnabled: _isEnabled] 329 autorelease]; 330 331 return croppedLayer; 332 333ERROR: 334 return nil; 335} 336 337- (bool) enableLinearBlendingBitmap: (bool) enableLinearBlendingBitmap 338{ 339 if (enableLinearBlendingBitmap) 340 { 341 if (!_linearBlendingBitmap) 342 { 343 _linearBlendingBitmap = [[_bitmap ppLinearRGB16BitmapFromImageBitmap] retain]; 344 } 345 346 return (_linearBlendingBitmap) ? YES : NO; 347 } 348 else // (!enableLinearBlendingBitmap) 349 { 350 if (_linearBlendingBitmap) 351 { 352 [_linearBlendingBitmap release]; 353 _linearBlendingBitmap = nil; 354 } 355 356 return YES; 357 } 358} 359 360- (NSBitmapImageRep *) linearBlendingBitmap 361{ 362 return _linearBlendingBitmap; 363} 364 365- (id) delegate 366{ 367 return _delegate; 368} 369 370- (void) setDelegate: (id) delegate 371{ 372 _delegate = delegate; 373} 374 375#pragma mark NSCoding protocol 376 377- (id) initWithCoder: (NSCoder *) aDecoder 378{ 379 return [self initWithSize: [aDecoder decodeSizeForKey: kDrawingLayerCodingKey_Size] 380 name: [aDecoder decodeObjectForKey: kDrawingLayerCodingKey_Name] 381 tiffData: [aDecoder decodeObjectForKey: kDrawingLayerCodingKey_TIFFData] 382 opacity: [aDecoder decodeFloatForKey: kDrawingLayerCodingKey_Opacity] 383 isEnabled: [aDecoder decodeBoolForKey: kDrawingLayerCodingKey_IsEnabled]]; 384} 385 386- (void) encodeWithCoder: (NSCoder *) coder 387{ 388 [coder encodeSize: _size forKey: kDrawingLayerCodingKey_Size]; 389 [coder encodeObject: _name forKey: kDrawingLayerCodingKey_Name]; 390 391 [coder encodeObject: [_bitmap ppCompressedTIFFData] 392 forKey: kDrawingLayerCodingKey_TIFFData]; 393 394 [coder encodeFloat: _opacity forKey: kDrawingLayerCodingKey_Opacity]; 395 [coder encodeBool: _isEnabled forKey: kDrawingLayerCodingKey_IsEnabled]; 396} 397 398#pragma mark NSCopying protocol 399 400- (id) copyWithZone: (NSZone *) zone 401{ 402 PPDocumentLayer *layerCopy; 403 bool needToCopyLayerBitmapData = YES; 404 405 layerCopy = [[[self class] allocWithZone: zone] initWithSize: _size 406 name: _name 407 tiffData: nil 408 opacity: _opacity 409 isEnabled: _isEnabled]; 410 411 if (!layerCopy) 412 goto ERROR; 413 414 layerCopy->_delegate = _delegate; 415 416 if (zone && (zone != NSDefaultMallocZone())) 417 { 418 if ([layerCopy->_name zone] != zone) 419 { 420 NSString *zonedName = [[_name copyWithZone: zone] autorelease]; 421 422 if (zonedName) 423 { 424 [layerCopy->_name release]; 425 layerCopy->_name = [zonedName retain]; 426 } 427 } 428 429 if (([layerCopy->_bitmap zone] != zone) 430 || ([layerCopy->_image zone] != zone)) 431 { 432 NSBitmapImageRep *zonedBitmap = [[_bitmap copyWithZone: zone] autorelease]; 433 NSImage *zonedImage = 434 [[[NSImage allocWithZone: zone] initWithSize: _size] autorelease]; 435 436 if (zonedBitmap && zonedImage) 437 { 438 [zonedImage addRepresentation: zonedBitmap]; 439 440 [layerCopy->_bitmap release]; 441 layerCopy->_bitmap = [zonedBitmap retain]; 442 443 [layerCopy->_image release]; 444 layerCopy->_image = [zonedImage retain]; 445 446 needToCopyLayerBitmapData = NO; 447 } 448 } 449 } 450 451 if (needToCopyLayerBitmapData) 452 { 453 [layerCopy->_bitmap ppCopyFromBitmap: _bitmap toPoint: NSZeroPoint]; 454 [layerCopy handleUpdateToBitmapInRect: PPGeometry_OriginRectOfSize(_size)]; 455 } 456 457 // Don't need to enable _linearBlendingBitmap in the copy - the linear bitmap will be 458 // enabled automatically if the copy's added to a PPDocument that's in linear blending mode, 459 // otherwise, the linear bitmap's currently unused in unattached layers. 460 461 return layerCopy; 462 463ERROR: 464 return nil; 465} 466 467#pragma mark Private methods 468 469- (void) setOpacity: (float) opacity andRegisterUndo: (bool) shouldRegisterUndo 470{ 471 if (opacity > 1.0f) 472 { 473 opacity = 1.0f; 474 } 475 else if (opacity < 0.0f) 476 { 477 opacity = 0.0f; 478 } 479 480 if (!shouldRegisterUndo && (_opacity == opacity)) 481 { 482 return; 483 } 484 485 _opacity = opacity; 486 487 [self notifyDelegateDidChangeOpacityAndShouldRegisterUndo: shouldRegisterUndo]; 488 489 if (shouldRegisterUndo) 490 { 491 _lastOpacity = _opacity; 492 } 493} 494 495#pragma mark Delegate notifiers 496 497- (void) notifyDelegateDidChangeNameFromOldValue: (NSString *) oldName 498{ 499 if ([_delegate respondsToSelector: @selector(layer:changedNameFromOldValue:)]) 500 { 501 [_delegate layer: self changedNameFromOldValue: oldName]; 502 } 503} 504 505- (void) notifyDelegateDidChangeEnabledFlagFromOldValue: (bool) oldEnabledFlag 506{ 507 if ([_delegate respondsToSelector: @selector(layer:changedEnabledFlagFromOldValue:)]) 508 { 509 [_delegate layer: self changedEnabledFlagFromOldValue: oldEnabledFlag]; 510 } 511} 512 513- (void) notifyDelegateDidChangeOpacityAndShouldRegisterUndo: (bool) shouldRegisterUndo 514{ 515 if ([_delegate respondsToSelector: @selector(layer:changedOpacityFromOldValue: 516 shouldRegisterUndo:)]) 517 { 518 [_delegate layer: self 519 changedOpacityFromOldValue: _lastOpacity 520 shouldRegisterUndo: shouldRegisterUndo]; 521 } 522} 523 524@end 525