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