OSDN Git Service

WinGui:
[handbrake-jp/handbrake-jp-git.git] / macosx / PictureController.mm
1 /* $Id: PictureController.mm,v 1.11 2005/08/01 15:10:44 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.m0k.org/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include "PictureController.h"
8
9 @interface PictureController (Private)
10
11 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize;
12 - (void)resizeSheetForViewSize: (NSSize)viewSize;
13 - (void)setViewSize: (NSSize)viewSize;
14 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize;
15
16 @end
17
18 static int GetAlignedSize( int size )
19 {
20     int result = 1;
21     while( result < size )
22     {
23         result *= 2;
24     }
25     return result;
26 }
27
28 @implementation PictureController
29
30 - (id)initWithDelegate:(id)del
31 {
32         if (self = [super init])
33         {
34                 delegate = del;
35         [self loadMyNibFile];
36         }
37         return self;
38 }
39
40 - (void) SetHandle: (hb_handle_t *) handle
41 {
42     fHandle = handle;
43
44     fHasQE = CGDisplayUsesOpenGLAcceleration( kCGDirectMainDisplay );
45
46     fBuffer     = NULL;
47     fBufferSize = 0;
48     fTexBuf[0]  = NULL;
49     fTexBuf[1]  = NULL;
50     fTexBufSize = 0;
51
52     [fWidthStepper  setValueWraps: NO];
53     [fWidthStepper  setIncrement: 16];
54     [fWidthStepper  setMinValue: 64];
55     [fHeightStepper setValueWraps: NO];
56     [fHeightStepper setIncrement: 16];
57     [fHeightStepper setMinValue: 64];
58
59     [fCropTopStepper    setIncrement: 2];
60     [fCropTopStepper    setMinValue:  0];
61     [fCropBottomStepper setIncrement: 2];
62     [fCropBottomStepper setMinValue:  0];
63     [fCropLeftStepper   setIncrement: 2];
64     [fCropLeftStepper   setMinValue:  0];
65     [fCropRightStepper  setIncrement: 2];
66     [fCropRightStepper  setMinValue:  0];
67 }
68
69 - (void) SetTitle: (hb_title_t *) title
70 {
71     hb_job_t * job = title->job;
72
73     fTitle = title;
74
75     /* Make sure we have big enough buffers */
76     int newSize;
77     newSize = ( title->width + 2 ) * (title->height + 2 ) * 4;
78     if( fBufferSize < newSize )
79     {
80         fBufferSize = newSize;
81         fBuffer     = (uint8_t *) realloc( fBuffer, fBufferSize );
82     }
83     if( !fHasQE )
84     {
85         newSize = ( GetAlignedSize( title->width + 2 ) *
86             GetAlignedSize( title->height + 2 ) * 4 );
87     }
88     if( fTexBufSize < newSize )
89     {
90         fTexBufSize = newSize;
91         fTexBuf[0]  = (uint8_t *) realloc( fTexBuf[0], fTexBufSize );
92         fTexBuf[1]  = (uint8_t *) realloc( fTexBuf[1], fTexBufSize );
93     }
94
95
96     [fWidthStepper      setMaxValue: title->width];
97     [fWidthStepper      setIntValue: job->width];
98     [fWidthField        setIntValue: job->width];
99     [fHeightStepper     setMaxValue: title->height];
100     [fHeightStepper     setIntValue: job->height];
101     [fHeightField       setIntValue: job->height];
102     [fRatioCheck        setState:    job->keep_ratio ? NSOnState : NSOffState];
103     [fCropTopStepper    setMaxValue: title->height/2-2];
104     [fCropBottomStepper setMaxValue: title->height/2-2];
105     [fCropLeftStepper   setMaxValue: title->width/2-2];
106     [fCropRightStepper  setMaxValue: title->width/2-2];
107     
108         
109         /* we use a popup to show the deinterlace settings */
110         [fDeinterlacePopUp removeAllItems];
111     [fDeinterlacePopUp addItemWithTitle: @"None"];
112     [fDeinterlacePopUp addItemWithTitle: @"Fast"];
113     [fDeinterlacePopUp addItemWithTitle: @"Slow"];
114         [fDeinterlacePopUp addItemWithTitle: @"Slower"];
115         [fDeinterlacePopUp addItemWithTitle: @"Slowest"];
116     
117         /* Set deinterlaces level according to the integer in the main window */
118         [fDeinterlacePopUp selectItemAtIndex: fPictureFilterSettings.deinterlace];
119
120         [fPARCheck setState:(job->pixel_ratio ? NSOnState : NSOffState)];
121     /* We initially set the previous state of keep ar to on */
122     keepAspectRatioPreviousState = 1;
123         if (!autoCrop)
124         {
125         [fCropMatrix  selectCellAtRow: 1 column:0];
126         /* If auto, lets set the crop steppers according to current job->crop values */
127         [fCropTopStepper    setIntValue: job->crop[0]];
128         [fCropTopField      setIntValue: job->crop[0]];
129         [fCropBottomStepper setIntValue: job->crop[1]];
130         [fCropBottomField   setIntValue: job->crop[1]];
131         [fCropLeftStepper   setIntValue: job->crop[2]];
132         [fCropLeftField     setIntValue: job->crop[2]];
133         [fCropRightStepper  setIntValue: job->crop[3]];
134         [fCropRightField    setIntValue: job->crop[3]];
135         }
136         else
137         {
138         [fCropMatrix  selectCellAtRow: 0 column:0];
139         }
140         
141         /* set the detelecine state according to the state in main window */
142         /* if framerate is 23.976 we do not allow detelecine, otherwise, enable and set according to fDetelecineMainWindow outlet */ 
143         if (fTitle->rate_base == 1126125)
144         {
145                 [fDetelecineCheck setEnabled: NO];
146                 [fDetelecineCheck setState: NSOffState];
147                 
148         }
149         else
150         {
151         [fDetelecineCheck setEnabled: YES];
152         [fDetelecineCheck setState: fPictureFilterSettings.detelecine];
153         }
154         
155         /* we use a popup to show the denoise settings */
156         [fDenoisePopUp removeAllItems];
157     [fDenoisePopUp addItemWithTitle: @"None"];
158     [fDenoisePopUp addItemWithTitle: @"Weak"];
159         [fDenoisePopUp addItemWithTitle: @"Medium"];
160     [fDenoisePopUp addItemWithTitle: @"Strong"];
161         /* Set denoises level according to the integer in the main window */
162         [fDenoisePopUp selectItemAtIndex: fPictureFilterSettings.denoise];
163         
164     MaxOutputWidth = job->width;
165         MaxOutputHeight = job->height;
166     fPicture = 0;
167
168     [self SettingsChanged: nil];
169 }
170
171 - (void) Display: (int) anim
172 {
173     hb_get_preview( fHandle, fTitle, fPicture, fBuffer );
174
175     /* Backup previous picture (for effects) */
176     memcpy( fTexBuf[1], fTexBuf[0], fTexBufSize );
177
178     if( fHasQE )
179     {
180         /* Simply copy */
181         memcpy( fTexBuf[0], fBuffer, fTexBufSize );
182     }
183     else
184     {
185         /* Copy line by line */
186         uint8_t * in  = fBuffer;
187         uint8_t * out = fTexBuf[0];
188                 
189         for( int i = fTitle->height + 2; i--; )
190         {
191             memcpy( out, in, 4 * ( fTitle->width + 2 ) );
192             in  += 4 * ( fTitle->width + 2 );
193             out += 4 * GetAlignedSize( fTitle->width + 2 );
194         }
195         
196     }
197
198     if( [fEffectsCheck state] == NSOffState )
199     {
200         anim = HB_ANIMATE_NONE;
201     }
202     else if( [[NSApp currentEvent] modifierFlags] & NSShiftKeyMask )
203     {
204         anim |= HB_ANIMATE_SLOW;
205     }
206
207     [fPictureGLView Display: anim buffer1: fTexBuf[0]
208         buffer2: fTexBuf[1] width: ( fTitle->width + 2 )
209         height: ( fTitle->height + 2 )];
210         
211         /* Set the Output Display below the Preview Picture*/
212         int titlewidth = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
213         int arpwidth = fTitle->job->pixel_aspect_width;
214         int arpheight = fTitle->job->pixel_aspect_height;
215         int displayparwidth = titlewidth * arpwidth / arpheight;
216         int displayparheight = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
217
218     NSSize displaySize = NSMakeSize( (float)fTitle->width, (float)fTitle->height );
219     if( fTitle->job->pixel_ratio == 1 )
220     {
221         [fInfoField setStringValue:[NSString stringWithFormat:
222                             @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d",
223                             fTitle->width, fTitle->height, titlewidth,
224                             displayparheight, displayparwidth, displayparheight]];
225         displaySize.width *= ((float)arpwidth) / ((float)arpheight);
226     }
227     else
228     {
229     [fInfoField setStringValue: [NSString stringWithFormat:
230         @"Source: %dx%d, Output: %dx%d", fTitle->width, fTitle->height,
231         fTitle->job->width, fTitle->job->height]];
232     }
233
234     NSSize viewSize = [self optimalViewSizeForImageSize:displaySize];
235     if( [self viewNeedsToResizeToSize:viewSize] )
236     {
237         [self resizeSheetForViewSize:viewSize];
238         [self setViewSize:viewSize];
239     }
240     
241     // Show the scaled text (use the height to check since the width can vary
242     // with anamorphic video).
243     if( ((int)viewSize.height) != fTitle->height )
244     {
245         float scale = viewSize.width / ((float)fTitle->width);
246         NSString *scaleString = [NSString stringWithFormat:
247             NSLocalizedString( @" (Preview scaled to %.0f%% actual size)",
248                                @"String shown when a preview is scaled" ),
249             scale * 100.0];
250         [fInfoField setStringValue:
251             [[fInfoField stringValue] stringByAppendingString:scaleString]];
252     }
253
254     [fPrevButton setEnabled: ( fPicture > 0 )];
255     [fNextButton setEnabled: ( fPicture < 9 )];
256 }
257
258 - (IBAction) SettingsChanged: (id) sender
259 {
260     hb_job_t * job = fTitle->job;
261     
262         if( [fPARCheck state] == NSOnState )
263         {
264         [fWidthStepper      setIntValue: MaxOutputWidth];
265         [fWidthField        setIntValue: MaxOutputWidth];
266         
267         /* This will show correct anamorphic height values, but
268             show distorted preview picture ratio */
269         [fHeightStepper      setIntValue: fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1]];
270         [fHeightField        setIntValue: fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1]];
271
272         /* if the sender is the Anamorphic checkbox, record the state
273            of KeepAspect Ratio so it can be reset if Anamorphic is unchecked again */
274         if (sender == fPARCheck)
275         {
276         keepAspectRatioPreviousState = [fRatioCheck state];
277         }
278         [fRatioCheck setState:NSOffState];
279         [fRatioCheck setEnabled: NO];
280         
281         [fWidthStepper setEnabled: NO];
282         [fWidthField setEnabled: NO];
283         [fHeightStepper setEnabled: NO];
284         [fHeightField setEnabled: NO];
285         
286     }
287     else
288         {
289         [fWidthStepper setEnabled: YES];
290         [fWidthField setEnabled: YES];
291         [fHeightStepper setEnabled: YES];
292         [fHeightField setEnabled: YES];
293         [fRatioCheck setEnabled: YES];
294         /* if the sender is the Anamorphic checkbox, we return the
295            keep AR checkbox to its previous state */
296         if (sender == fPARCheck)
297         {
298         [fRatioCheck setState:keepAspectRatioPreviousState];
299         }
300         
301         }
302         
303     job->width       = [fWidthStepper  intValue];
304     job->height      = [fHeightStepper intValue];
305     job->keep_ratio  = ( [fRatioCheck state] == NSOnState );
306     
307         fPictureFilterSettings.deinterlace = [fDeinterlacePopUp indexOfSelectedItem];
308     /* if the gui deinterlace settings are fast through slowest, the job->deinterlace
309        value needs to be set to one, for the job as well as the previews showing deinterlacing
310        otherwise set job->deinterlace to 0 or "off" */
311     if (fPictureFilterSettings.deinterlace > 0)
312     {
313     job->deinterlace  = 1;
314     }
315     else
316     {
317     job->deinterlace  = 0;
318     }
319     fPictureFilterSettings.denoise     = [fDenoisePopUp indexOfSelectedItem];
320     fPictureFilterSettings.detelecine  = [fDetelecineCheck state];
321         job->pixel_ratio = ( [fPARCheck state] == NSOnState );
322
323     autoCrop = ( [fCropMatrix selectedRow] == 0 );
324     [fCropTopStepper    setEnabled: !autoCrop];
325     [fCropBottomStepper setEnabled: !autoCrop];
326     [fCropLeftStepper   setEnabled: !autoCrop];
327     [fCropRightStepper  setEnabled: !autoCrop];
328
329     if( autoCrop )
330     {
331         memcpy( job->crop, fTitle->crop, 4 * sizeof( int ) );
332     }
333     else
334     {
335         job->crop[0] = [fCropTopStepper    intValue];
336         job->crop[1] = [fCropBottomStepper intValue];
337         job->crop[2] = [fCropLeftStepper   intValue];
338         job->crop[3] = [fCropRightStepper  intValue];
339     }
340
341     if( job->keep_ratio )
342     {
343         if( sender == fWidthStepper || sender == fRatioCheck ||
344             sender == fCropTopStepper || sender == fCropBottomStepper )
345         {
346             hb_fix_aspect( job, HB_KEEP_WIDTH );
347             if( job->height > fTitle->height )
348             {
349                 job->height = fTitle->height;
350                 hb_fix_aspect( job, HB_KEEP_HEIGHT );
351             }
352         }
353         else
354         {
355             hb_fix_aspect( job, HB_KEEP_HEIGHT );
356             if( job->width > fTitle->width )
357             {
358                 job->width = fTitle->width;
359                 hb_fix_aspect( job, HB_KEEP_WIDTH );
360             }
361         }
362     }
363     
364     [fWidthStepper      setIntValue: job->width];
365     [fWidthField        setIntValue: job->width];
366     [fHeightStepper     setIntValue: job->height];
367     [fHeightField       setIntValue: job->height];
368     [fCropTopStepper    setIntValue: job->crop[0]];
369     [fCropTopField      setIntValue: job->crop[0]];
370     [fCropBottomStepper setIntValue: job->crop[1]];
371     [fCropBottomField   setIntValue: job->crop[1]];
372     [fCropLeftStepper   setIntValue: job->crop[2]];
373     [fCropLeftField     setIntValue: job->crop[2]];
374     [fCropRightStepper  setIntValue: job->crop[3]];
375     [fCropRightField    setIntValue: job->crop[3]];
376     /* Sanity Check Here for < 16 px preview to avoid
377        crashing hb_get_preview. In fact, just for kicks
378        lets getting previews at a min limit of 32, since
379        no human can see any meaningful detail below that */
380     if (job->width >= 64 && job->height >= 64)
381     {
382         [self Display: HB_ANIMATE_NONE];
383     }
384 }
385
386 - (IBAction) PreviousPicture: (id) sender
387 {   
388     if( fPicture <= 0 )
389     {
390         return;
391     }
392     fPicture--;
393     [self Display: HB_ANIMATE_BACKWARD];
394 }
395
396 - (IBAction) NextPicture: (id) sender
397 {
398     if( fPicture >= 9 )
399     {
400         return;
401     }
402     fPicture++;
403     [self Display: HB_ANIMATE_FORWARD];
404 }
405
406 - (IBAction) ClosePanel: (id) sender
407 {
408     if ([delegate respondsToSelector:@selector(pictureSettingsDidChange)])
409         [delegate pictureSettingsDidChange];
410         
411     [NSApp endSheet: fPicturePanel];
412     [fPicturePanel orderOut: self];
413 }
414
415 - (BOOL) autoCrop
416 {
417     return autoCrop;
418 }
419 - (void) setAutoCrop: (BOOL) setting
420 {
421     autoCrop = setting;
422 }
423
424 - (int) detelecine
425 {
426     return fPictureFilterSettings.detelecine;
427 }
428
429 - (void) setDetelecine: (int) setting
430 {
431     fPictureFilterSettings.detelecine = setting;
432 }
433
434 - (int) deinterlace
435 {
436     return fPictureFilterSettings.deinterlace;
437 }
438
439 - (void) setDeinterlace: (int) setting {
440     fPictureFilterSettings.deinterlace = setting;
441 }
442
443 - (int) denoise
444 {
445     return fPictureFilterSettings.denoise;
446 }
447
448 - (void) setDenoise: (int) setting
449 {
450     fPictureFilterSettings.denoise = setting;
451 }
452
453 - (void)showPanelInWindow: (NSWindow *)fWindow forTitle: (hb_title_t *)title
454 {
455     [self SetTitle:title];
456     
457     [NSApp beginSheet:fPicturePanel
458        modalForWindow:fWindow
459         modalDelegate:nil
460        didEndSelector:nil
461           contextInfo:NULL];
462 }
463
464 - (BOOL) loadMyNibFile
465 {
466     if(![NSBundle loadNibNamed:@"PictureSettings" owner:self])
467     {
468         NSLog(@"Warning! Could not load myNib file.\n");
469         return NO;
470     }
471     
472     return YES;
473 }
474
475 @end
476
477 @implementation PictureController (Private)
478
479 //
480 // -[PictureController(Private) optimalViewSizeForImageSize:]
481 //
482 // Given the size of the preview image to be shown, returns the best possible
483 // size for the OpenGL view.
484 //
485 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize
486 {
487     // The min size is 320x240
488     float minWidth = 320.0;
489     float minHeight = 240.0;
490     
491     // The max size of the view is when the sheet is taking up 85% of the screen.
492     NSSize screenSize = [[NSScreen mainScreen] frame].size;
493     NSSize sheetSize = [fPicturePanel frame].size;
494     NSSize viewAreaSize = [fPictureGLViewArea frame].size;
495     float paddingX = sheetSize.width - viewAreaSize.width;
496     float paddingY = sheetSize.height - viewAreaSize.height;
497     float maxWidth = (0.85 * screenSize.width) - paddingX;
498     float maxHeight = (0.85 * screenSize.height) - paddingY;
499     
500     NSSize resultSize = imageSize;
501     
502     // Its better to have a view that's too small than a view that's too big, so
503     // apply the maximum constraints last.
504     if( resultSize.width < minWidth )
505     {
506         resultSize.height *= (minWidth / resultSize.width);
507         resultSize.width = minWidth;
508     }
509     if( resultSize.height < minHeight )
510     {
511         resultSize.width *= (minHeight / resultSize.height);
512         resultSize.height = minHeight;
513     }
514     if( resultSize.width > maxWidth )
515     {
516         resultSize.height *= (maxWidth / resultSize.width);
517         resultSize.width = maxWidth;
518     }
519     if( resultSize.height > maxHeight )
520     {
521         resultSize.width *= (maxHeight / resultSize.height);
522         resultSize.height = maxHeight;
523     }
524     
525     return resultSize;
526 }
527
528 //
529 // -[PictureController(Private) resizePanelForViewSize:animate:]
530 //
531 // Resizes the entire sheet to accomodate an OpenGL view of a particular size.
532 //
533 - (void)resizeSheetForViewSize: (NSSize)viewSize
534 {
535     // Figure out the deltas for the new frame area
536     NSSize currentSize = [fPictureGLViewArea frame].size;
537     float deltaX = viewSize.width - currentSize.width;
538     float deltaY = viewSize.height - currentSize.height;
539     
540     // Now resize the whole panel by those same deltas, but don't exceed the min
541     NSRect frame = [fPicturePanel frame];
542     NSSize maxSize = [fPicturePanel maxSize];
543     NSSize minSize = [fPicturePanel minSize];
544     frame.size.width += deltaX;
545     frame.size.height += deltaY;
546     if( frame.size.width < minSize.width )
547     {
548         frame.size.width = minSize.width;
549     }
550     if( frame.size.height < minSize.height )
551     {
552         frame.size.height = minSize.height;
553     }
554
555     // But now the sheet is off-center, so also shift the origin to center it and
556     // keep the top aligned.
557     frame.origin.x -= (deltaX / 2.0);
558     frame.origin.y -= deltaY;
559
560     [fPicturePanel setFrame:frame display:YES animate:YES];
561 }
562
563 //
564 // -[PictureController(Private) setViewSize:]
565 //
566 // Changes the OpenGL view's size and centers it vertially inside of its area.
567 // Assumes resizeSheetForViewSize: has already been called.
568 //
569 - (void)setViewSize: (NSSize)viewSize
570 {
571     [fPictureGLView setFrameSize:viewSize];
572     
573     // center it vertically
574     NSPoint origin = [fPictureGLViewArea frame].origin;
575     origin.y += ([fPictureGLViewArea frame].size.height -
576                  [fPictureGLView frame].size.height) / 2.0;
577     [fPictureGLView setFrameOrigin:origin];
578 }
579
580 //
581 // -[PictureController(Private) viewNeedsToResizeToSize:]
582 //
583 // Returns YES if the view will need to resize to match the given size.
584 //
585 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize
586 {
587     NSSize viewSize = [fPictureGLView frame].size;
588     return (newSize.width != viewSize.width || newSize.height != viewSize.height);
589 }
590
591
592 @end