1 /* $Id: PictureController.mm,v 1.11 2005/08/01 15:10:44 titer Exp $
3 This file is part of the HandBrake source code.
4 Homepage: <http://handbrake.fr/>.
5 It may be used under the terms of the GNU General Public License. */
7 #import "PictureController.h"
9 @interface PictureController (Private)
11 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize;
12 - (void)resizeSheetForViewSize: (NSSize)viewSize;
13 - (void)setViewSize: (NSSize)viewSize;
14 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize;
18 @implementation PictureController
20 - (id)initWithDelegate:(id)del
22 if (self = [super initWithWindowNibName:@"PictureSettings"])
24 // NSWindowController likes to lazily load its window. However since
25 // this controller tries to set all sorts of outlets before the window
26 // is displayed, we need it to load immediately. The correct way to do
27 // this, according to the documentation, is simply to invoke the window
30 // If/when we switch a lot of this stuff to bindings, this can probably
35 fPicturePreviews = [[NSMutableDictionary dictionaryWithCapacity: HB_NUM_HBLIB_PICTURES] retain];
42 [fPicturePreviews release];
46 - (void) SetHandle: (hb_handle_t *) handle
50 [fWidthStepper setValueWraps: NO];
51 [fWidthStepper setIncrement: 16];
52 [fWidthStepper setMinValue: 64];
53 [fHeightStepper setValueWraps: NO];
54 [fHeightStepper setIncrement: 16];
55 [fHeightStepper setMinValue: 64];
57 [fCropTopStepper setIncrement: 2];
58 [fCropTopStepper setMinValue: 0];
59 [fCropBottomStepper setIncrement: 2];
60 [fCropBottomStepper setMinValue: 0];
61 [fCropLeftStepper setIncrement: 2];
62 [fCropLeftStepper setMinValue: 0];
63 [fCropRightStepper setIncrement: 2];
64 [fCropRightStepper setMinValue: 0];
67 - (void) SetTitle: (hb_title_t *) title
69 hb_job_t * job = title->job;
73 [fWidthStepper setMaxValue: title->width];
74 [fWidthStepper setIntValue: job->width];
75 [fWidthField setIntValue: job->width];
76 [fHeightStepper setMaxValue: title->height];
77 [fHeightStepper setIntValue: job->height];
78 [fHeightField setIntValue: job->height];
79 [fRatioCheck setState: job->keep_ratio ? NSOnState : NSOffState];
80 [fCropTopStepper setMaxValue: title->height/2-2];
81 [fCropBottomStepper setMaxValue: title->height/2-2];
82 [fCropLeftStepper setMaxValue: title->width/2-2];
83 [fCropRightStepper setMaxValue: title->width/2-2];
85 /* Populate the Anamorphic NSPopUp button here */
86 [fAnamorphicPopUp removeAllItems];
87 [fAnamorphicPopUp addItemWithTitle: @"None"];
88 [fAnamorphicPopUp addItemWithTitle: @"Strict"];
89 if (allowLooseAnamorphic)
91 [fAnamorphicPopUp addItemWithTitle: @"Loose"];
93 [fAnamorphicPopUp selectItemAtIndex: job->pixel_ratio];
95 /* We initially set the previous state of keep ar to on */
96 keepAspectRatioPreviousState = 1;
99 [fCropMatrix selectCellAtRow: 1 column:0];
100 /* If auto, lets set the crop steppers according to current job->crop values */
101 [fCropTopStepper setIntValue: job->crop[0]];
102 [fCropTopField setIntValue: job->crop[0]];
103 [fCropBottomStepper setIntValue: job->crop[1]];
104 [fCropBottomField setIntValue: job->crop[1]];
105 [fCropLeftStepper setIntValue: job->crop[2]];
106 [fCropLeftField setIntValue: job->crop[2]];
107 [fCropRightStepper setIntValue: job->crop[3]];
108 [fCropRightField setIntValue: job->crop[3]];
112 [fCropMatrix selectCellAtRow: 0 column:0];
115 /* Set filters widgets according to the filters struct */
116 [fDetelecineCheck setState:fPictureFilterSettings.detelecine];
117 [fDeinterlacePopUp selectItemAtIndex: fPictureFilterSettings.deinterlace];
118 [fDenoisePopUp selectItemAtIndex: fPictureFilterSettings.denoise];
119 [fDeblockCheck setState: fPictureFilterSettings.deblock];
122 MaxOutputWidth = title->width - job->crop[2] - job->crop[3];
123 MaxOutputHeight = title->height - job->crop[0] - job->crop[1];
124 [self SettingsChanged: nil];
127 /* we use this to setup the initial picture filters upon first launch, after that their states
128 are maintained across different sources */
129 - (void) setInitialPictureFilters
131 /* we use a popup to show the deinterlace settings */
132 [fDeinterlacePopUp removeAllItems];
133 [fDeinterlacePopUp addItemWithTitle: @"None"];
134 [fDeinterlacePopUp addItemWithTitle: @"Fast"];
135 [fDeinterlacePopUp addItemWithTitle: @"Slow"];
136 [fDeinterlacePopUp addItemWithTitle: @"Slower"];
138 /* Set deinterlaces level according to the integer in the main window */
139 [fDeinterlacePopUp selectItemAtIndex: fPictureFilterSettings.deinterlace];
141 /* we use a popup to show the denoise settings */
142 [fDenoisePopUp removeAllItems];
143 [fDenoisePopUp addItemWithTitle: @"None"];
144 [fDenoisePopUp addItemWithTitle: @"Weak"];
145 [fDenoisePopUp addItemWithTitle: @"Medium"];
146 [fDenoisePopUp addItemWithTitle: @"Strong"];
147 /* Set denoises level according to the integer in the main window */
148 [fDenoisePopUp selectItemAtIndex: fPictureFilterSettings.denoise];
150 /* we use a popup to show the decomb settings */
151 [fDecombPopUp removeAllItems];
152 [fDecombPopUp addItemWithTitle: @"None"];
153 [fDecombPopUp addItemWithTitle: @"Default"];
154 [fDecombPopUp addItemWithTitle: @"Custom"];
155 /* Set denoises level according to the integer in the main window */
156 [fDecombPopUp selectItemAtIndex: fPictureFilterSettings.decomb];
160 // Adjusts the window to draw the current picture (fPicture) adjusting its size as
161 // necessary to display as much of the picture as possible.
162 - (void) displayPreview
164 [fPictureView setImage: [self imageForPicture: fPicture]];
166 NSSize displaySize = NSMakeSize( ( CGFloat )fTitle->width, ( CGFloat )fTitle->height );
167 /* Set the picture size display fields below the Preview Picture*/
168 if( fTitle->job->pixel_ratio == 1 ) // Original PAR Implementation
170 output_width = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
171 output_height = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
172 display_width = output_width * fTitle->job->pixel_aspect_width / fTitle->job->pixel_aspect_height;
173 [fInfoField setStringValue:[NSString stringWithFormat:
174 @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d",
175 fTitle->width, fTitle->height, output_width, output_height, display_width, output_height]];
176 displaySize.width *= ( ( CGFloat )fTitle->job->pixel_aspect_width ) / ( ( CGFloat )fTitle->job->pixel_aspect_height );
178 else if (fTitle->job->pixel_ratio == 2) // Loose Anamorphic
180 display_width = output_width * output_par_width / output_par_height;
181 [fInfoField setStringValue:[NSString stringWithFormat:
182 @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d",
183 fTitle->width, fTitle->height, output_width, output_height, display_width, output_height]];
185 displaySize.width = display_width;
187 else // No Anamorphic
189 [fInfoField setStringValue: [NSString stringWithFormat:
190 @"Source: %dx%d, Output: %dx%d", fTitle->width, fTitle->height,
191 fTitle->job->width, fTitle->job->height]];
194 NSSize viewSize = [self optimalViewSizeForImageSize:displaySize];
195 if( [self viewNeedsToResizeToSize:viewSize] )
197 /* In the case of loose anamorphic, do not resize the window when scaling down */
198 if (fTitle->job->pixel_ratio != 2 || [fWidthField intValue] == fTitle->width)
200 [self resizeSheetForViewSize:viewSize];
201 [self setViewSize:viewSize];
205 // Show the scaled text (use the height to check since the width can vary
206 // with anamorphic video).
207 if( ( ( int )viewSize.height ) != fTitle->height )
209 CGFloat scale = viewSize.width / ( ( CGFloat ) fTitle->width );
210 NSString *scaleString = [NSString stringWithFormat:
211 NSLocalizedString( @" (Preview scaled to %.0f%% actual size)",
212 @"String shown when a preview is scaled" ),
214 [fInfoField setStringValue: [[fInfoField stringValue] stringByAppendingString:scaleString]];
217 [fPrevButton setEnabled: ( fPicture > 0 )];
218 [fNextButton setEnabled: ( fPicture < 9 )];
221 - (IBAction) deblockSliderChanged: (id) sender
223 if ([fDeblockSlider floatValue] == 4.0)
225 [fDeblockField setStringValue: [NSString stringWithFormat: @"Off"]];
229 [fDeblockField setStringValue: [NSString stringWithFormat: @"%.0f", [fDeblockSlider floatValue]]];
231 [self SettingsChanged: sender];
234 - (IBAction) SettingsChanged: (id) sender
236 hb_job_t * job = fTitle->job;
238 autoCrop = ( [fCropMatrix selectedRow] == 0 );
239 [fCropTopStepper setEnabled: !autoCrop];
240 [fCropBottomStepper setEnabled: !autoCrop];
241 [fCropLeftStepper setEnabled: !autoCrop];
242 [fCropRightStepper setEnabled: !autoCrop];
246 memcpy( job->crop, fTitle->crop, 4 * sizeof( int ) );
250 job->crop[0] = [fCropTopStepper intValue];
251 job->crop[1] = [fCropBottomStepper intValue];
252 job->crop[2] = [fCropLeftStepper intValue];
253 job->crop[3] = [fCropRightStepper intValue];
256 if( [fAnamorphicPopUp indexOfSelectedItem] > 0 )
258 if ([fAnamorphicPopUp indexOfSelectedItem] == 2) // Loose anamorphic
260 job->pixel_ratio = 2;
261 [fWidthStepper setEnabled: YES];
262 [fWidthField setEnabled: YES];
263 /* We set job->width and call hb_set_anamorphic_size in libhb to do a "dry run" to get
264 * the values to be used by libhb for loose anamorphic
266 /* if the sender is the anamorphic popup, then we know that loose anamorphic has just
267 * been turned on, so snap the width to full width for the source.
269 if (sender == fAnamorphicPopUp)
271 [fWidthStepper setIntValue: fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3]];
272 [fWidthField setIntValue: fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3]];
274 job->width = [fWidthStepper intValue];
275 hb_set_anamorphic_size(job, &output_width, &output_height, &output_par_width, &output_par_height);
276 [fHeightStepper setIntValue: output_height];
277 [fHeightField setIntValue: output_height];
278 job->height = [fHeightStepper intValue];
281 else // must be "1" or strict anamorphic
283 [fWidthStepper setIntValue: fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3]];
284 [fWidthField setIntValue: fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3]];
286 /* This will show correct anamorphic height values, but
287 show distorted preview picture ratio */
288 [fHeightStepper setIntValue: fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1]];
289 [fHeightField setIntValue: fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1]];
290 job->width = [fWidthStepper intValue];
291 job->height = [fHeightStepper intValue];
293 job->pixel_ratio = 1;
294 [fWidthStepper setEnabled: NO];
295 [fWidthField setEnabled: NO];
298 /* if the sender is the Anamorphic checkbox, record the state
299 of KeepAspect Ratio so it can be reset if Anamorphic is unchecked again */
300 if (sender == fAnamorphicPopUp)
302 keepAspectRatioPreviousState = [fRatioCheck state];
304 [fRatioCheck setState:NSOffState];
305 [fRatioCheck setEnabled: NO];
308 [fHeightStepper setEnabled: NO];
309 [fHeightField setEnabled: NO];
314 job->width = [fWidthStepper intValue];
315 job->height = [fHeightStepper intValue];
316 job->pixel_ratio = 0;
317 [fWidthStepper setEnabled: YES];
318 [fWidthField setEnabled: YES];
319 [fHeightStepper setEnabled: YES];
320 [fHeightField setEnabled: YES];
321 [fRatioCheck setEnabled: YES];
322 /* if the sender is the Anamorphic checkbox, we return the
323 keep AR checkbox to its previous state */
324 if (sender == fAnamorphicPopUp)
326 [fRatioCheck setState:keepAspectRatioPreviousState];
331 job->keep_ratio = ( [fRatioCheck state] == NSOnState );
333 fPictureFilterSettings.deinterlace = [fDeinterlacePopUp indexOfSelectedItem];
334 /* if the gui deinterlace settings are fast through slowest, the job->deinterlace
335 value needs to be set to one, for the job as well as the previews showing deinterlacing
336 otherwise set job->deinterlace to 0 or "off" */
337 if (fPictureFilterSettings.deinterlace > 0)
339 job->deinterlace = 1;
343 job->deinterlace = 0;
345 fPictureFilterSettings.denoise = [fDenoisePopUp indexOfSelectedItem];
347 fPictureFilterSettings.detelecine = [fDetelecineCheck state];
349 if ([fDeblockField stringValue] == @"Off")
351 fPictureFilterSettings.deblock = 0;
355 fPictureFilterSettings.deblock = [fDeblockField intValue];
358 fPictureFilterSettings.decomb = [fDecombPopUp indexOfSelectedItem];
360 if( job->keep_ratio )
362 if( sender == fWidthStepper || sender == fRatioCheck ||
363 sender == fCropTopStepper || sender == fCropBottomStepper )
365 hb_fix_aspect( job, HB_KEEP_WIDTH );
366 if( job->height > fTitle->height )
368 job->height = fTitle->height;
369 hb_fix_aspect( job, HB_KEEP_HEIGHT );
374 hb_fix_aspect( job, HB_KEEP_HEIGHT );
375 if( job->width > fTitle->width )
377 job->width = fTitle->width;
378 hb_fix_aspect( job, HB_KEEP_WIDTH );
381 // hb_get_preview can't handle sizes that are larger than the original title
383 if( job->width > fTitle->width )
384 job->width = fTitle->width;
386 if( job->height > fTitle->height )
387 job->height = fTitle->height;
390 [fWidthStepper setIntValue: job->width];
391 [fWidthField setIntValue: job->width];
392 if( [fAnamorphicPopUp indexOfSelectedItem] < 2 )
394 [fHeightStepper setIntValue: job->height];
395 [fHeightField setIntValue: job->height];
397 [fCropTopStepper setIntValue: job->crop[0]];
398 [fCropTopField setIntValue: job->crop[0]];
399 [fCropBottomStepper setIntValue: job->crop[1]];
400 [fCropBottomField setIntValue: job->crop[1]];
401 [fCropLeftStepper setIntValue: job->crop[2]];
402 [fCropLeftField setIntValue: job->crop[2]];
403 [fCropRightStepper setIntValue: job->crop[3]];
404 [fCropRightField setIntValue: job->crop[3]];
405 /* Sanity Check Here for < 16 px preview to avoid
406 crashing hb_get_preview. In fact, just for kicks
407 lets getting previews at a min limit of 32, since
408 no human can see any meaningful detail below that */
409 if (job->width >= 64 && job->height >= 64)
411 // Purge the existing picture previews so they get recreated the next time
413 [self purgeImageCache];
414 [self displayPreview];
418 - (IBAction) PreviousPicture: (id) sender
425 [self displayPreview];
428 - (IBAction) NextPicture: (id) sender
435 [self displayPreview];
438 - (IBAction) ClosePanel: (id) sender
440 if ([delegate respondsToSelector:@selector(pictureSettingsDidChange)])
441 [delegate pictureSettingsDidChange];
443 [NSApp endSheet:[self window]];
444 [[self window] orderOut:self];
451 - (void) setAutoCrop: (BOOL) setting
456 - (BOOL) allowLooseAnamorphic
458 return allowLooseAnamorphic;
461 - (void) setAllowLooseAnamorphic: (BOOL) setting
463 allowLooseAnamorphic = setting;
468 return fPictureFilterSettings.detelecine;
471 - (void) setDetelecine: (int) setting
473 fPictureFilterSettings.detelecine = setting;
478 return fPictureFilterSettings.deinterlace;
481 - (void) setDeinterlace: (int) setting {
482 fPictureFilterSettings.deinterlace = setting;
486 return fPictureFilterSettings.decomb;
489 - (void) setDecomb: (int) setting {
490 fPictureFilterSettings.decomb = setting;
494 return fPictureFilterSettings.denoise;
497 - (void) setDenoise: (int) setting
499 fPictureFilterSettings.denoise = setting;
504 return fPictureFilterSettings.deblock;
507 - (void) setDeblock: (int) setting
509 fPictureFilterSettings.deblock = setting;
512 - (void)showPanelInWindow: (NSWindow *)fWindow forTitle: (hb_title_t *)title
514 [self SetTitle:title];
516 [NSApp beginSheet:[self window]
517 modalForWindow:fWindow
524 // This function converts an image created by libhb (specified via pictureIndex) into
525 // an NSImage suitable for the GUI code to use. If removeBorders is YES,
526 // makeImageForPicture crops the image generated by libhb stripping off the gray
527 // border around the content. This is the low-level method that generates the image.
528 // -imageForPicture calls this function whenever it can't find an image in its cache.
529 + (NSImage *) makeImageForPicture: (int)pictureIndex
530 libhb:(hb_handle_t*)handle
531 title:(hb_title_t*)title
532 removeBorders:(BOOL)removeBorders
536 // |<---------- title->width ----------->|
537 // | |<---- title->job->width ---->| |
539 // .......................................
540 // ....+-----------------------------+....
541 // ....| |....<-- gray border
544 // ....| |<------- image
550 // ....+-----------------------------+....
551 // .......................................
553 static uint8_t * buffer;
554 static int bufferSize;
556 // Make sure we have a big enough buffer to receive the image from libhb. libhb
557 // creates images with a one-pixel border around the original content. Hence we
558 // add 2 pixels horizontally and vertically to the buffer size.
559 int srcWidth = title->width + 2;
560 int srcHeight= title->height + 2;
562 newSize = srcWidth * srcHeight * 4;
563 if( bufferSize < newSize )
565 bufferSize = newSize;
566 buffer = (uint8_t *) realloc( buffer, bufferSize );
569 hb_get_preview( handle, title, pictureIndex, buffer );
571 // Create an NSBitmapImageRep and copy the libhb image into it, converting it from
572 // libhb's format to one suitable for NSImage. Along the way, we'll strip off the
573 // border around libhb's image.
575 // The image data returned by hb_get_preview is 4 bytes per pixel, BGRA format.
578 int dstWidth = title->job->width;
579 int dstHeight = title->job->height;
580 NSBitmapFormat bitmapFormat = (NSBitmapFormat)NSAlphaFirstBitmapFormat;
581 NSBitmapImageRep * imgrep = [[[NSBitmapImageRep alloc]
582 initWithBitmapDataPlanes:nil
586 samplesPerPixel:3 // ignore alpha
589 colorSpaceName:NSCalibratedRGBColorSpace
590 bitmapFormat:bitmapFormat
591 bytesPerRow:dstWidth * 4
592 bitsPerPixel:32] autorelease];
594 int borderTop = (srcHeight - dstHeight) / 2;
595 int borderLeft = (srcWidth - dstWidth) / 2;
597 UInt32 * src = (UInt32 *)buffer;
598 UInt32 * dst = (UInt32 *)[imgrep bitmapData];
599 src += borderTop * srcWidth; // skip top rows in src to get to first row of dst
600 src += borderLeft; // skip left pixels in src to get to first pixel of dst
601 for (int r = 0; r < dstHeight; r++)
603 for (int c = 0; c < dstWidth; c++)
604 #if TARGET_RT_LITTLE_ENDIAN
605 *dst++ = Endian32_Swap(*src++);
609 src += (srcWidth - dstWidth); // skip to next row in src
612 NSImage * img = [[[NSImage alloc] initWithSize: NSMakeSize(dstWidth, dstHeight)] autorelease];
613 [img addRepresentation:imgrep];
619 // Make sure we have big enough buffer
620 static uint8_t * buffer;
621 static int bufferSize;
624 newSize = ( title->width + 2 ) * (title->height + 2 ) * 4;
625 if( bufferSize < newSize )
627 bufferSize = newSize;
628 buffer = (uint8_t *) realloc( buffer, bufferSize );
631 hb_get_preview( handle, title, pictureIndex, buffer );
633 // The image data returned by hb_get_preview is 4 bytes per pixel, BGRA format.
634 // We'll copy that into an NSImage swapping it to ARGB in the process. Alpha is
636 int width = title->width + 2; // hblib adds a one-pixel border to the image
637 int height = title->height + 2;
638 int numPixels = width * height;
639 NSBitmapFormat bitmapFormat = (NSBitmapFormat)NSAlphaFirstBitmapFormat;
640 NSBitmapImageRep * imgrep = [[[NSBitmapImageRep alloc]
641 initWithBitmapDataPlanes:nil
645 samplesPerPixel:3 // ignore alpha
648 colorSpaceName:NSCalibratedRGBColorSpace
649 bitmapFormat:bitmapFormat
650 bytesPerRow:width * 4
651 bitsPerPixel:32] autorelease];
653 UInt32 * src = (UInt32 *)buffer;
654 UInt32 * dst = (UInt32 *)[imgrep bitmapData];
655 for (int i = 0; i < numPixels; i++)
656 #if TARGET_RT_LITTLE_ENDIAN
657 *dst++ = Endian32_Swap(*src++);
662 NSImage * img = [[[NSImage alloc] initWithSize: NSMakeSize(width, height)] autorelease];
663 [img addRepresentation:imgrep];
669 // Returns the preview image for the specified index, retrieving it from its internal
670 // cache or by calling makeImageForPicture if it is not cached. Generally, you should
671 // use imageForPicture so that images are cached. Calling makeImageForPicture will
672 // always generate a new copy of the image.
673 - (NSImage *) imageForPicture: (int) pictureIndex
675 // The preview for the specified index may not currently exist, so this method
676 // generates it if necessary.
677 NSString * key = [NSString stringWithFormat:@"%d", pictureIndex];
678 NSImage * theImage = [fPicturePreviews objectForKey:key];
681 theImage = [PictureController makeImageForPicture:pictureIndex libhb:fHandle title:fTitle removeBorders: NO];
682 [fPicturePreviews setObject:theImage forKey:key];
687 // Purges all images from the cache. The next call to imageForPicture will cause a new
688 // image to be generated.
689 - (void) purgeImageCache
691 [fPicturePreviews removeAllObjects];
696 @implementation PictureController (Private)
699 // -[PictureController(Private) optimalViewSizeForImageSize:]
701 // Given the size of the preview image to be shown, returns the best possible
702 // size for the view.
704 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize
706 // The min size is 320x240
707 CGFloat minWidth = 320.0;
708 CGFloat minHeight = 240.0;
710 // The max size of the view is when the sheet is taking up 85% of the screen.
711 NSSize screenSize = [[NSScreen mainScreen] frame].size;
712 NSSize sheetSize = [[self window] frame].size;
713 NSSize viewAreaSize = [fPictureViewArea frame].size;
714 CGFloat paddingX = sheetSize.width - viewAreaSize.width;
715 CGFloat paddingY = sheetSize.height - viewAreaSize.height;
716 CGFloat maxWidth = (0.85 * screenSize.width) - paddingX;
717 CGFloat maxHeight = (0.85 * screenSize.height) - paddingY;
719 NSSize resultSize = imageSize;
721 // Its better to have a view that's too small than a view that's too big, so
722 // apply the maximum constraints last.
723 if( resultSize.width < minWidth )
725 resultSize.height *= (minWidth / resultSize.width);
726 resultSize.width = minWidth;
728 if( resultSize.height < minHeight )
730 resultSize.width *= (minHeight / resultSize.height);
731 resultSize.height = minHeight;
733 if( resultSize.width > maxWidth )
735 resultSize.height *= (maxWidth / resultSize.width);
736 resultSize.width = maxWidth;
738 if( resultSize.height > maxHeight )
740 resultSize.width *= (maxHeight / resultSize.height);
741 resultSize.height = maxHeight;
748 // -[PictureController(Private) resizePanelForViewSize:animate:]
750 // Resizes the entire sheet to accomodate a view of a particular size.
752 - (void)resizeSheetForViewSize: (NSSize)viewSize
754 // Figure out the deltas for the new frame area
755 NSSize currentSize = [fPictureViewArea frame].size;
756 CGFloat deltaX = viewSize.width - currentSize.width;
757 CGFloat deltaY = viewSize.height - currentSize.height;
759 // Now resize the whole panel by those same deltas, but don't exceed the min
760 NSRect frame = [[self window] frame];
761 NSSize maxSize = [[self window] maxSize];
762 NSSize minSize = [[self window] minSize];
763 frame.size.width += deltaX;
764 frame.size.height += deltaY;
765 if( frame.size.width < minSize.width )
767 frame.size.width = minSize.width;
769 if( frame.size.height < minSize.height )
771 frame.size.height = minSize.height;
774 // But now the sheet is off-center, so also shift the origin to center it and
775 // keep the top aligned.
776 if( frame.size.width != [[self window] frame].size.width )
777 frame.origin.x -= (deltaX / 2.0);
779 if( frame.size.height != [[self window] frame].size.height )
780 frame.origin.y -= deltaY;
782 [[self window] setFrame:frame display:YES animate:YES];
786 // -[PictureController(Private) setViewSize:]
788 // Changes the view's size and centers it vertically inside of its area.
789 // Assumes resizeSheetForViewSize: has already been called.
791 - (void)setViewSize: (NSSize)viewSize
793 [fPictureView setFrameSize:viewSize];
795 // center it vertically
796 NSPoint origin = [fPictureViewArea frame].origin;
797 origin.y += ([fPictureViewArea frame].size.height -
798 [fPictureView frame].size.height) / 2.0;
799 [fPictureView setFrameOrigin:origin];
803 // -[PictureController(Private) viewNeedsToResizeToSize:]
805 // Returns YES if the view will need to resize to match the given size.
807 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize
809 NSSize viewSize = [fPictureView frame].size;
810 return (newSize.width != viewSize.width || newSize.height != viewSize.height);