1// 2// MyDocument.m 3// PRICE 4// 5// Created by Riccardo Mottola on Thu Dec 12 2002. 6// Copyright (c) 2002-2015 Carduus. All rights reserved. 7// 8// This application is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 9// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 10 11#import "MyDocument.h" 12#include <limits.h> 13 14#import "PRGrayscaleFilter.h" 15#import "PRInvert.h" 16#import "PRConvolve55.h" 17#import "PRTransforms.h" 18#import "PRFourier.h" 19#import "PRDFTLowPass.h" 20#import "PRDFTHighPass.h" 21#import "PREqualize.h" 22#import "PRTraceEdges.h" 23#import "PRCustTraceEdges.h" 24#import "PRMedian.h" 25#import "PRScale.h" 26#import "PRCrop.h" 27#import "PRBriCon.h" 28#import "PRCurves.h" 29 30 31/* changeSaveType is an undocument API call, not exported in AppKit headers */ 32@interface NSDocument(Hidden) 33- (void)changeSaveType:(id)sender; 34@end 35 36 37@implementation MyDocument 38 39 40- (NSData *)dataRepresentationOfType:(NSString *)aType 41{ 42 NSData *dataOfRep; 43 NSDictionary *repProperties; 44 45 dataOfRep = nil; 46 if ([aType isEqualToString:@"TIFF"] || [aType isEqualToString:@"public.tiff"]) 47 { 48 NSLog(@"data representation of type TIFF"); 49 repProperties = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:NSTIFFCompressionLZW] forKey:NSImageCompressionMethod]; 50 dataOfRep = [[activeImage bitmapRep] representationUsingType: NSTIFFFileType properties:repProperties]; 51 } 52 else if ([aType isEqualToString:@"JPEG"] || [aType isEqualToString:@"public.jpeg"]) 53 { 54 float level; 55 56 level = [windowController compressionLevel]; 57 NSLog(@"data representation of type JPEG, %f", level); 58 repProperties = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:level] forKey:NSImageCompressionFactor]; 59 dataOfRep = [[activeImage bitmapRep] representationUsingType: NSJPEGFileType properties:repProperties]; 60 } 61 return dataOfRep; 62} 63 64- (BOOL)loadDataRepresentation:(NSData *)data ofType:(NSString *)aType 65{ 66 PRImage *tempImage; 67 68 tempImage = [[PRImage alloc] initWithData:data]; 69 if (tempImage != nil) 70 { 71 NSBitmapImageRep *tmpImageRep; 72 BOOL convertColorSpace; 73 BOOL convertPlanar; 74 NSMutableDictionary *imgProps; 75 76#if !defined (GNUSTEP) && (MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_3) 77 /* since 10.4 we have different Alpha format position, let's convert it */ 78 tmpImageRep = [tempImage bitmapRep]; 79 if ([tmpImageRep bitmapFormat] != 0) 80 { 81 NSInteger x, y; 82 NSInteger w, h; 83 BOOL alphaFirst; 84 BOOL nonPremultipliedA; 85 BOOL floatingPoint; 86 PRImage *destImage; 87 NSBitmapImageRep *destImageRep; 88 NSInteger srcBytesPerRow; 89 NSInteger destBytesPerRow; 90 NSInteger srcBytesPerPixel; 91 NSInteger destBytesPerPixel; 92 93 94 NSLog(@"We have a non-standard format, let's try to convert it"); 95 alphaFirst = [tmpImageRep bitmapFormat] & NSAlphaFirstBitmapFormat; 96 nonPremultipliedA = [tmpImageRep bitmapFormat] & NSAlphaNonpremultipliedBitmapFormat; 97 floatingPoint = [tmpImageRep bitmapFormat] & NSFloatingPointSamplesBitmapFormat; 98 if ([tmpImageRep bitsPerSample] == 8) 99 { 100 unsigned char *srcData; 101 unsigned char *destData; 102 unsigned char *p1; 103 unsigned char *p2; 104 105 /* swap Alpha is hopefully only for chunky images */ 106 if (alphaFirst) 107 { 108 imgProps = [[NSMutableDictionary alloc] init]; 109 [imgProps setValue:[tmpImageRep valueForProperty:NSImageCompressionMethod] forKey:NSImageCompressionMethod]; 110 [imgProps setValue:[tmpImageRep valueForProperty:NSImageCompressionFactor] forKey:NSImageCompressionFactor]; 111 [imgProps setValue:[tmpImageRep valueForProperty:NSImageEXIFData] forKey:NSImageEXIFData]; 112 113 w = [tmpImageRep pixelsWide]; 114 h = [tmpImageRep pixelsHigh]; 115 116 srcBytesPerRow = [tmpImageRep bytesPerRow]; 117 srcBytesPerPixel = [tmpImageRep bitsPerPixel] / 8; 118 destImage = [[PRImage alloc] initWithSize:NSMakeSize(w, h)]; 119 destImageRep = [[NSBitmapImageRep alloc] 120 initWithBitmapDataPlanes:NULL 121 pixelsWide:w 122 pixelsHigh:h 123 bitsPerSample:8 124 samplesPerPixel:[tmpImageRep samplesPerPixel] 125 hasAlpha:[tmpImageRep hasAlpha] 126 isPlanar:NO 127 colorSpaceName:[tmpImageRep colorSpaceName] 128 bytesPerRow:0 129 bitsPerPixel:0]; 130 131 destBytesPerRow = [destImageRep bytesPerRow]; 132 destBytesPerPixel = [destImageRep bitsPerPixel] / 8; 133 srcData = [tmpImageRep bitmapData]; 134 destData = [destImageRep bitmapData]; 135 if (![tempImage hasColor]) 136 { 137 for (y = 0; y < h; y++) 138 for (x = 0; x < w; x++) 139 { 140 p1 = srcData + srcBytesPerRow * y + srcBytesPerPixel * x; 141 p2 = destData + destBytesPerRow * y + destBytesPerPixel * x; 142 p2[0] = p1[1]; 143 p2[1] = p1[0]; 144 } 145 } 146 else 147 { 148 for (y = 0; y < h; y++) 149 for (x = 0; x < w; x++) 150 { 151 p1 = srcData + srcBytesPerRow * y + srcBytesPerPixel * x; 152 p2 = destData + destBytesPerRow * y + destBytesPerPixel * x; 153 p2[0] = p1[1]; 154 p2[1] = p1[2]; 155 p2[2] = p1[3]; 156 p2[3] = p1[0]; 157 } 158 } 159 [destImageRep setProperty:NSImageEXIFData withValue:[imgProps objectForKey:NSImageEXIFData]]; 160 [destImage setBitmapRep:destImageRep]; 161 [destImageRep release]; 162 [tempImage release]; 163 tempImage = destImage; 164 [imgProps release]; 165 } 166 } 167 else /* for 16 bit */ 168 { 169 } 170 } 171#endif /* Image format conversion */ 172 173 /* if the loaded image is in BlackColorSpace we convert it to WhiteColorSpace */ 174 /* which is the only TIFF rep used internally and generated by PRICE */ 175 /* we also need to convert Planar images into a meshed configuration */ 176 tmpImageRep = [tempImage bitmapRep]; 177 178 imgProps = [[NSMutableDictionary alloc] init]; 179 [imgProps setValue:[tmpImageRep valueForProperty:NSImageCompressionMethod] forKey:NSImageCompressionMethod]; 180 [imgProps setValue:[tmpImageRep valueForProperty:NSImageCompressionFactor] forKey:NSImageCompressionFactor]; 181 [imgProps setValue:[tmpImageRep valueForProperty:NSImageEXIFData] forKey:NSImageEXIFData]; 182 183 184 NSLog(@"Properties: %@", imgProps); 185 convertColorSpace = [[tmpImageRep colorSpaceName] isEqualToString: NSCalibratedBlackColorSpace] || [[tmpImageRep colorSpaceName] isEqualToString: NSDeviceBlackColorSpace]; 186 convertPlanar = [tmpImageRep isPlanar]; 187 188 if (convertColorSpace || convertPlanar) 189 { 190 unsigned char *dataPtr; 191 unsigned char *dataPtr2; 192 NSInteger k; 193 NSInteger w, h; 194 PRImage *newImage; 195 NSBitmapImageRep *newImageRep; 196 NSInteger destSamplesPerPixel; 197 198 199 NSLog(@"Converting color space"); 200 w = [tmpImageRep pixelsWide]; 201 h = [tmpImageRep pixelsHigh]; 202 destSamplesPerPixel = [tmpImageRep samplesPerPixel]; 203 /* converting the colorspace */ 204 newImage = [[PRImage alloc] initWithSize:NSMakeSize(w, h)]; 205 newImageRep = [[NSBitmapImageRep alloc] 206 initWithBitmapDataPlanes:NULL 207 pixelsWide:w 208 pixelsHigh:h 209 bitsPerSample:8 210 samplesPerPixel:destSamplesPerPixel 211 hasAlpha:NO 212 isPlanar:NO 213 colorSpaceName:NSCalibratedWhiteColorSpace 214 bytesPerRow:w*destSamplesPerPixel 215 bitsPerPixel:0]; 216 dataPtr = [tmpImageRep bitmapData]; 217 dataPtr2 = [newImageRep bitmapData]; 218 if (convertPlanar) 219 { 220 NSInteger x, y; 221 NSInteger xp; 222 if (convertColorSpace) 223 { 224 for (y = 0; y < h; y++) 225 { 226 xp = 0; 227 for (x = 0; x < w*3; x += 3) 228 { 229 dataPtr2[y*(w*3) + x] = UCHAR_MAX - dataPtr[y*w + x]; 230 dataPtr2[y*(w*3) + x + 1] = UCHAR_MAX - dataPtr[y*w*2 + x]; 231 dataPtr2[y*(w*3) + x + 2] = UCHAR_MAX - dataPtr[y*w*3 + x]; 232 xp++; 233 } 234 } 235 } 236 else 237 { 238 for (y = 0; y < h; y++) 239 { 240 xp = 0; 241 for (x = 0; x < w*3; x += 3) 242 { 243 dataPtr2[y*(w*3) + x] = dataPtr[y*w + x]; 244 dataPtr2[y*(w*3) + x + 1] = dataPtr[y*w*2 + x]; 245 dataPtr2[y*(w*3) + x + 2] = dataPtr[y*w*3 + x]; 246 xp++; 247 } 248 } 249 } 250 } 251 else 252 { 253 if (convertColorSpace) 254 { 255 NSInteger s; 256 257 s = w * h; 258 for (k = 0; k < s; k++) 259 *dataPtr2++ = UCHAR_MAX - *dataPtr++; 260 } else 261 { 262 /* shall never happen */ 263 NSLog(@"Internal error: tried to convert image when it wasn't necessary"); 264 } 265 } 266 // FIXME: should we remove the color space from the EXIF data? 267 [newImageRep setColorSpaceName:NSCalibratedWhiteColorSpace]; 268 [newImageRep setProperty:NSImageEXIFData withValue:[imgProps objectForKey:NSImageEXIFData]]; 269 [newImage setBitmapRep:newImageRep]; 270 [newImageRep release]; 271 [tempImage release]; 272 tempImage = newImage; 273 } 274 [imgProps release]; 275 } 276 277 oldImage = nil; 278 /* returns a bool to be able to know if loading was successul */ 279 if (tempImage != nil) 280 { 281 /* we setActiveImage won't set the image info yet (why?) */ 282 [self setActiveImage:tempImage]; 283 [tempImage release]; 284 return YES; 285 } else 286 return NO; 287} 288 289 290- (void)makeWindowControllers 291/* instantiate PRWindowController */ 292{ 293 windowController = [[PRWindowController alloc] initWithWindowNibName:@"PRWindow"]; 294 [self addWindowController:windowController]; 295 296 /* set undo levels */ 297 [[self undoManager] setLevelsOfUndo:1]; 298} 299 300- (NSWindow *)window 301{ 302 return [windowController window]; 303} 304 305- (NSView *)view 306{ 307 return [windowController view]; 308} 309 310- (PRImage *)activeImage 311/* method to access the active image */ 312{ 313 return activeImage; 314} 315 316- (void)setActiveImage: (PRImage *)theImage 317/* method to set the active image */ 318{ 319 if (activeImage != nil) 320 [activeImage release]; 321 322 activeImage = [theImage retain]; 323 NSLog(@"set active, per pixel: %d %d", [[activeImage bitmapRep] bitsPerSample], [[activeImage bitmapRep] bitsPerPixel]); 324 325 /* window controller is still nil here the first time we load an image 326 * thus the info must be manually set after the nib finished loading */ 327 [windowController setImageToDraw:activeImage]; 328} 329 330- (void)copy:(id)sender 331{ 332 NSPasteboard *pboard; 333 334 pboard = [NSPasteboard generalPasteboard]; 335 336 [pboard declareTypes:[NSArray arrayWithObjects:NSTIFFPboardType, nil] owner:nil]; 337 [pboard setData:[activeImage TIFFRepresentation] forType:NSTIFFPboardType]; 338} 339 340- (void)paste:(id)sender 341{ 342 NSUndoManager *uMgr; 343 NSPasteboard *pboard; 344 NSString *type; 345 NSData *tempData; 346 PRImage *tempImage; 347 348 pboard = [NSPasteboard generalPasteboard]; 349 type = [pboard availableTypeFromArray:[NSArray arrayWithObjects:NSTIFFPboardType, nil]]; 350 351 if (type != nil) 352 { 353 if ([type isEqualToString:NSTIFFPboardType]) 354 { 355 /* get the clipboard data */ 356 tempData = [pboard dataForType:NSTIFFPboardType]; 357 if (tempData != nil) 358 { 359 uMgr = [self undoManager]; 360 /* save the method on the undo stack */ 361 [[uMgr prepareWithInvocationTarget: self] restoreLastImage]; 362 [uMgr setActionName:@"Paste"]; 363 364 /* save the current image */ 365 [self saveCurrentImage]; 366 367 tempImage = [[PRImage alloc] initWithData:tempData]; 368 [self setActiveImage: tempImage]; 369 [tempImage release]; 370 [[windowController view] setFrameSize:[activeImage size]]; 371 [[windowController view] setNeedsDisplay:YES]; 372 } else 373 { 374 /* guidelines say I should put a panel */ 375 /* #### fixme */ 376 NSLog(@"something went wrong in paste"); 377 } 378 } else 379 NSLog(@"received a paste of unhandled type: %@", type); 380 } 381} 382 383- (void)runFilter:(PRFilter *)filter with:(NSArray *)parameters 384{ 385 NSUndoManager *uMgr; 386 PRCProgress *filterProgr; 387 NSMutableDictionary *imgProps; 388 id tempVal; 389 NSLog(@"before running filter, per pixel: %d %d", [[activeImage bitmapRep] bitsPerSample], [[activeImage bitmapRep] bitsPerPixel]); 390 391 uMgr = [self undoManager]; 392 /* save the method on the undo stack */ 393 [[uMgr prepareWithInvocationTarget: self] restoreLastImage]; 394 [uMgr setActionName:[filter actionName]]; 395 396 filterProgr = nil; 397 if ([filter displayProgress]) 398 { 399 filterProgr = [[PRCProgress alloc] init]; 400 [filterProgr showProgress:self]; 401 [filterProgr setTitle: [filter actionName]]; 402 } 403 404 /* save the current image */ 405 [self saveCurrentImage]; 406 407 /* save image properties */ 408 imgProps = [[NSMutableDictionary alloc] init]; 409 tempVal = [[activeImage bitmapRep] valueForProperty:NSImageCompressionMethod]; 410 if (tempVal) 411 [imgProps setObject:tempVal forKey:NSImageCompressionMethod]; 412 tempVal = [[activeImage bitmapRep] valueForProperty:NSImageCompressionFactor]; 413 if (tempVal) 414 [imgProps setObject:tempVal forKey:NSImageCompressionFactor]; 415 tempVal = [[activeImage bitmapRep] valueForProperty:NSImageEXIFData]; 416 if (tempVal) 417 [imgProps setObject:tempVal forKey:NSImageEXIFData]; 418 NSLog(@"beforeFilter: %@", [[activeImage bitmapRep] valueForProperty:NSImageEXIFData]); 419 420 /* instantiate and run the filter */ 421 [self setActiveImage: [filter filterImage: activeImage with:parameters progressPanel:filterProgr]]; 422 423 /* reset image properties */ 424 if ([imgProps objectForKey:NSImageEXIFData] != nil) 425 { 426 NSMutableDictionary *exifDict; 427 428 exifDict = [NSMutableDictionary dictionaryWithDictionary:[imgProps objectForKey:NSImageEXIFData]]; 429 NSLog(@"we have EXIF Data: %@", exifDict); 430 if ([exifDict objectForKey:@"PixelXDimension"]) 431 { 432 NSNumber *w = [NSNumber numberWithInt:[activeImage width]]; 433 [exifDict setObject:w forKey:@"PixelXDimension"]; 434 } 435 if ([exifDict objectForKey:@"PixelYDimension"]) 436 { 437 NSNumber *w = [NSNumber numberWithInt:[activeImage height]]; 438 [exifDict setObject:w forKey:@"PixelYDimension"]; 439 } 440 NSLog(@"New EXIF Data: %@", exifDict); 441 [imgProps setObject:exifDict forKey:NSImageEXIFData]; 442 } 443 [[activeImage bitmapRep] setProperty:NSImageEXIFData withValue:[imgProps objectForKey:NSImageEXIFData]]; 444 [imgProps release]; 445 NSLog(@"afterFilter: %@", [[activeImage bitmapRep] valueForProperty:NSImageEXIFData]); 446 447 [filterProgr release]; 448 449 /* reset the selected zoom ration, this will also cause a view update */ 450 [windowController scaleFromMenu:nil]; 451} 452 453- (void)restoreLastImage 454{ 455 PRImage *tempImage; 456 457 tempImage = [activeImage copy]; 458 [self setActiveImage: oldImage]; 459 [oldImage release]; 460 oldImage = tempImage; 461 [[[self undoManager] prepareWithInvocationTarget: self] restoreLastImage]; 462 463 [windowController scaleFromMenu:nil]; 464} 465 466- (void)saveCurrentImage 467{ 468 if (activeImage != nil) 469 { 470 if (oldImage != nil) 471 [oldImage release]; 472 NSLog(@"save active, per pixel: %d %d", [[activeImage bitmapRep] bitsPerSample], [[activeImage bitmapRep] bitsPerPixel]); 473 oldImage = [activeImage copy]; 474 NSLog(@"saved, per pixel: %d %d", [[oldImage bitmapRep] bitsPerSample], [[oldImage bitmapRep] bitsPerPixel]); 475 476 } 477} 478 479- (void)dealloc 480{ 481 [activeImage release]; 482 [windowController release]; 483 [super dealloc]; 484} 485 486 487- (void)setPrintInfo:(NSPrintInfo *)anObject 488{ 489 if (printInfo != anObject) 490 { 491 [printInfo autorelease]; 492 printInfo = [anObject copyWithZone:[self zone]]; 493 } 494} 495 496- (NSPrintInfo *)printInfo 497{ 498 if (printInfo == nil) 499 { 500 [self setPrintInfo:[NSPrintInfo sharedPrintInfo]]; 501 [printInfo setHorizontallyCentered:YES]; 502 [printInfo setVerticallyCentered:YES]; 503 [printInfo setLeftMargin:5.0]; 504 [printInfo setRightMargin:5.0]; 505 [printInfo setTopMargin:5.0]; 506 [printInfo setBottomMargin:5.0]; 507 } 508 return printInfo; 509} 510 511 512- (void)printShowingPrintPanel:(BOOL)showPanels 513{ 514 NSPrintOperation *op = [NSPrintOperation printOperationWithView:[windowController view] printInfo:[self printInfo]]; 515 [op setShowPanels:showPanels]; 516 [op runOperationModalForWindow:[self window] delegate:nil didRunSelector:NULL contextInfo:NULL]; 517} 518 519/* file panel methods */ 520 521/* undocumented API call which works fine for our purpose to intercept 522 the change of the filetype and update the view information accordingly */ 523- (void)changeSaveType:(id)sender 524{ 525 NSLog(@"MyDocument changeSaveType"); 526 [super changeSaveType:sender]; 527 528 [windowController changeSaveType:sender]; 529} 530 531 532/* we override this for GNUstep */ 533#if defined(GNUSTEP_GUI_VERSION) && GNU_GNUSTEP_GUI_MAJOR_VERSION == 0 && GNUSTEP_GUI_MINOR_VERSION <= 22 534- (NSInteger) runModalSavePanel: (NSSavePanel*)savePanel withAccessoryView: (NSView*)accessoryView 535 536{ 537 NSLog(@"runModalSavePanel: withAccessoryView. We should see this only on GS gui <= 0.22"); 538 [windowController prepareSavePanel: savePanel]; 539 [windowController setWritableFileTypes:[MyDocument writableTypes]]; 540 [windowController setCompressionType:[self fileType]]; 541 542 /* we finally call super, but reget the accessory view since we changed it */ 543 return [super runModalSavePanel:savePanel withAccessoryView:[savePanel accessoryView]]; 544} 545#endif 546 547/* we override this for Cocoa */ 548- (BOOL) prepareSavePanel:(NSSavePanel *) panel 549{ 550 BOOL r; 551 552 r = [windowController prepareSavePanel: panel]; 553 [windowController setWritableFileTypes:[MyDocument writableTypes]]; 554 [windowController setCompressionType:[self fileType]]; 555 return r; 556} 557 558@end 559