OSDN Git Service

640479b65cb9436d8e5a06b08cd9231c15d00067
[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         
142         
143         /* we use a popup to show the denoise settings */
144         [fDenoisePopUp removeAllItems];
145     [fDenoisePopUp addItemWithTitle: @"None"];
146     [fDenoisePopUp addItemWithTitle: @"Weak"];
147         [fDenoisePopUp addItemWithTitle: @"Medium"];
148     [fDenoisePopUp addItemWithTitle: @"Strong"];
149         /* Set denoises level according to the integer in the main window */
150         [fDenoisePopUp selectItemAtIndex: fPictureFilterSettings.denoise];
151         
152     MaxOutputWidth = job->width;
153         MaxOutputHeight = job->height;
154     fPicture = 0;
155
156     [self SettingsChanged: nil];
157 }
158
159 - (void) Display: (int) anim
160 {
161     hb_get_preview( fHandle, fTitle, fPicture, fBuffer );
162
163     /* Backup previous picture (for effects) */
164     memcpy( fTexBuf[1], fTexBuf[0], fTexBufSize );
165
166     if( fHasQE )
167     {
168         /* Simply copy */
169         memcpy( fTexBuf[0], fBuffer, fTexBufSize );
170     }
171     else
172     {
173         /* Copy line by line */
174         uint8_t * in  = fBuffer;
175         uint8_t * out = fTexBuf[0];
176                 
177         for( int i = fTitle->height + 2; i--; )
178         {
179             memcpy( out, in, 4 * ( fTitle->width + 2 ) );
180             in  += 4 * ( fTitle->width + 2 );
181             out += 4 * GetAlignedSize( fTitle->width + 2 );
182         }
183         
184     }
185
186     if( [fEffectsCheck state] == NSOffState )
187     {
188         anim = HB_ANIMATE_NONE;
189     }
190     else if( [[NSApp currentEvent] modifierFlags] & NSShiftKeyMask )
191     {
192         anim |= HB_ANIMATE_SLOW;
193     }
194
195     [fPictureGLView Display: anim buffer1: fTexBuf[0]
196         buffer2: fTexBuf[1] width: ( fTitle->width + 2 )
197         height: ( fTitle->height + 2 )];
198         
199         /* Set the Output Display below the Preview Picture*/
200         int titlewidth = fTitle->width-fTitle->job->crop[2]-fTitle->job->crop[3];
201         int arpwidth = fTitle->job->pixel_aspect_width;
202         int arpheight = fTitle->job->pixel_aspect_height;
203         int displayparwidth = titlewidth * arpwidth / arpheight;
204         int displayparheight = fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1];
205
206     NSSize displaySize = NSMakeSize( (float)fTitle->width, (float)fTitle->height );
207     if( fTitle->job->pixel_ratio == 1 )
208     {
209         [fInfoField setStringValue:[NSString stringWithFormat:
210                             @"Source: %dx%d, Output: %dx%d, Anamorphic: %dx%d",
211                             fTitle->width, fTitle->height, titlewidth,
212                             displayparheight, displayparwidth, displayparheight]];
213         displaySize.width *= ((float)arpwidth) / ((float)arpheight);
214     }
215     else
216     {
217     [fInfoField setStringValue: [NSString stringWithFormat:
218         @"Source: %dx%d, Output: %dx%d", fTitle->width, fTitle->height,
219         fTitle->job->width, fTitle->job->height]];
220     }
221
222     NSSize viewSize = [self optimalViewSizeForImageSize:displaySize];
223     if( [self viewNeedsToResizeToSize:viewSize] )
224     {
225         [self resizeSheetForViewSize:viewSize];
226         [self setViewSize:viewSize];
227     }
228     
229     // Show the scaled text (use the height to check since the width can vary
230     // with anamorphic video).
231     if( ((int)viewSize.height) != fTitle->height )
232     {
233         float scale = viewSize.width / ((float)fTitle->width);
234         NSString *scaleString = [NSString stringWithFormat:
235             NSLocalizedString( @" (Preview scaled to %.0f%% actual size)",
236                                @"String shown when a preview is scaled" ),
237             scale * 100.0];
238         [fInfoField setStringValue:
239             [[fInfoField stringValue] stringByAppendingString:scaleString]];
240     }
241
242     [fPrevButton setEnabled: ( fPicture > 0 )];
243     [fNextButton setEnabled: ( fPicture < 9 )];
244 }
245
246 - (IBAction) SettingsChanged: (id) sender
247 {
248     hb_job_t * job = fTitle->job;
249     
250         if( [fPARCheck state] == NSOnState )
251         {
252         [fWidthStepper      setIntValue: MaxOutputWidth];
253         [fWidthField        setIntValue: MaxOutputWidth];
254         
255         /* This will show correct anamorphic height values, but
256             show distorted preview picture ratio */
257         [fHeightStepper      setIntValue: fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1]];
258         [fHeightField        setIntValue: fTitle->height-fTitle->job->crop[0]-fTitle->job->crop[1]];
259
260         /* if the sender is the Anamorphic checkbox, record the state
261            of KeepAspect Ratio so it can be reset if Anamorphic is unchecked again */
262         if (sender == fPARCheck)
263         {
264         keepAspectRatioPreviousState = [fRatioCheck state];
265         }
266         [fRatioCheck setState:NSOffState];
267         [fRatioCheck setEnabled: NO];
268         
269         [fWidthStepper setEnabled: NO];
270         [fWidthField setEnabled: NO];
271         [fHeightStepper setEnabled: NO];
272         [fHeightField setEnabled: NO];
273         
274     }
275     else
276         {
277         [fWidthStepper setEnabled: YES];
278         [fWidthField setEnabled: YES];
279         [fHeightStepper setEnabled: YES];
280         [fHeightField setEnabled: YES];
281         [fRatioCheck setEnabled: YES];
282         /* if the sender is the Anamorphic checkbox, we return the
283            keep AR checkbox to its previous state */
284         if (sender == fPARCheck)
285         {
286         [fRatioCheck setState:keepAspectRatioPreviousState];
287         }
288         
289         }
290         
291     job->width       = [fWidthStepper  intValue];
292     job->height      = [fHeightStepper intValue];
293     job->keep_ratio  = ( [fRatioCheck state] == NSOnState );
294     
295         fPictureFilterSettings.deinterlace = [fDeinterlacePopUp indexOfSelectedItem];
296     /* if the gui deinterlace settings are fast through slowest, the job->deinterlace
297        value needs to be set to one, for the job as well as the previews showing deinterlacing
298        otherwise set job->deinterlace to 0 or "off" */
299     if (fPictureFilterSettings.deinterlace > 0)
300     {
301     job->deinterlace  = 1;
302     }
303     else
304     {
305     job->deinterlace  = 0;
306     }
307     fPictureFilterSettings.denoise     = [fDenoisePopUp indexOfSelectedItem];
308     fPictureFilterSettings.detelecine  = [fDetelecineCheck state];
309     fPictureFilterSettings.deblock  = [fDeblockCheck state];
310         job->pixel_ratio = ( [fPARCheck state] == NSOnState );
311
312     autoCrop = ( [fCropMatrix selectedRow] == 0 );
313     [fCropTopStepper    setEnabled: !autoCrop];
314     [fCropBottomStepper setEnabled: !autoCrop];
315     [fCropLeftStepper   setEnabled: !autoCrop];
316     [fCropRightStepper  setEnabled: !autoCrop];
317
318     if( autoCrop )
319     {
320         memcpy( job->crop, fTitle->crop, 4 * sizeof( int ) );
321     }
322     else
323     {
324         job->crop[0] = [fCropTopStepper    intValue];
325         job->crop[1] = [fCropBottomStepper intValue];
326         job->crop[2] = [fCropLeftStepper   intValue];
327         job->crop[3] = [fCropRightStepper  intValue];
328     }
329
330     if( job->keep_ratio )
331     {
332         if( sender == fWidthStepper || sender == fRatioCheck ||
333             sender == fCropTopStepper || sender == fCropBottomStepper )
334         {
335             hb_fix_aspect( job, HB_KEEP_WIDTH );
336             if( job->height > fTitle->height )
337             {
338                 job->height = fTitle->height;
339                 hb_fix_aspect( job, HB_KEEP_HEIGHT );
340             }
341         }
342         else
343         {
344             hb_fix_aspect( job, HB_KEEP_HEIGHT );
345             if( job->width > fTitle->width )
346             {
347                 job->width = fTitle->width;
348                 hb_fix_aspect( job, HB_KEEP_WIDTH );
349             }
350         }
351     }
352     
353     [fWidthStepper      setIntValue: job->width];
354     [fWidthField        setIntValue: job->width];
355     [fHeightStepper     setIntValue: job->height];
356     [fHeightField       setIntValue: job->height];
357     [fCropTopStepper    setIntValue: job->crop[0]];
358     [fCropTopField      setIntValue: job->crop[0]];
359     [fCropBottomStepper setIntValue: job->crop[1]];
360     [fCropBottomField   setIntValue: job->crop[1]];
361     [fCropLeftStepper   setIntValue: job->crop[2]];
362     [fCropLeftField     setIntValue: job->crop[2]];
363     [fCropRightStepper  setIntValue: job->crop[3]];
364     [fCropRightField    setIntValue: job->crop[3]];
365     /* Sanity Check Here for < 16 px preview to avoid
366        crashing hb_get_preview. In fact, just for kicks
367        lets getting previews at a min limit of 32, since
368        no human can see any meaningful detail below that */
369     if (job->width >= 64 && job->height >= 64)
370     {
371         [self Display: HB_ANIMATE_NONE];
372     }
373 }
374
375 - (IBAction) PreviousPicture: (id) sender
376 {   
377     if( fPicture <= 0 )
378     {
379         return;
380     }
381     fPicture--;
382     [self Display: HB_ANIMATE_BACKWARD];
383 }
384
385 - (IBAction) NextPicture: (id) sender
386 {
387     if( fPicture >= 9 )
388     {
389         return;
390     }
391     fPicture++;
392     [self Display: HB_ANIMATE_FORWARD];
393 }
394
395 - (IBAction) ClosePanel: (id) sender
396 {
397     if ([delegate respondsToSelector:@selector(pictureSettingsDidChange)])
398         [delegate pictureSettingsDidChange];
399         
400     [NSApp endSheet: fPicturePanel];
401     [fPicturePanel orderOut: self];
402 }
403
404 - (BOOL) autoCrop
405 {
406     return autoCrop;
407 }
408 - (void) setAutoCrop: (BOOL) setting
409 {
410     autoCrop = setting;
411 }
412
413 - (int) detelecine
414 {
415     return fPictureFilterSettings.detelecine;
416 }
417
418 - (void) setDetelecine: (int) setting
419 {
420     fPictureFilterSettings.detelecine = setting;
421 }
422
423 - (int) deinterlace
424 {
425     return fPictureFilterSettings.deinterlace;
426 }
427
428 - (void) setDeinterlace: (int) setting {
429     fPictureFilterSettings.deinterlace = setting;
430 }
431
432 - (int) denoise
433 {
434     return fPictureFilterSettings.denoise;
435 }
436
437 - (void) setDenoise: (int) setting
438 {
439     fPictureFilterSettings.denoise = setting;
440 }
441
442 - (int) deblock
443 {
444     return fPictureFilterSettings.deblock;
445 }
446
447 - (void) setDeblock: (int) setting
448 {
449     fPictureFilterSettings.deblock = setting;
450 }
451
452 - (void)showPanelInWindow: (NSWindow *)fWindow forTitle: (hb_title_t *)title
453 {
454     [self SetTitle:title];
455     
456     [NSApp beginSheet:fPicturePanel
457        modalForWindow:fWindow
458         modalDelegate:nil
459        didEndSelector:nil
460           contextInfo:NULL];
461 }
462
463 - (BOOL) loadMyNibFile
464 {
465     if(![NSBundle loadNibNamed:@"PictureSettings" owner:self])
466     {
467         NSLog(@"Warning! Could not load myNib file.\n");
468         return NO;
469     }
470     
471     return YES;
472 }
473
474 @end
475
476 @implementation PictureController (Private)
477
478 //
479 // -[PictureController(Private) optimalViewSizeForImageSize:]
480 //
481 // Given the size of the preview image to be shown, returns the best possible
482 // size for the OpenGL view.
483 //
484 - (NSSize)optimalViewSizeForImageSize: (NSSize)imageSize
485 {
486     // The min size is 320x240
487     float minWidth = 320.0;
488     float minHeight = 240.0;
489     
490     // The max size of the view is when the sheet is taking up 85% of the screen.
491     NSSize screenSize = [[NSScreen mainScreen] frame].size;
492     NSSize sheetSize = [fPicturePanel frame].size;
493     NSSize viewAreaSize = [fPictureGLViewArea frame].size;
494     float paddingX = sheetSize.width - viewAreaSize.width;
495     float paddingY = sheetSize.height - viewAreaSize.height;
496     float maxWidth = (0.85 * screenSize.width) - paddingX;
497     float maxHeight = (0.85 * screenSize.height) - paddingY;
498     
499     NSSize resultSize = imageSize;
500     
501     // Its better to have a view that's too small than a view that's too big, so
502     // apply the maximum constraints last.
503     if( resultSize.width < minWidth )
504     {
505         resultSize.height *= (minWidth / resultSize.width);
506         resultSize.width = minWidth;
507     }
508     if( resultSize.height < minHeight )
509     {
510         resultSize.width *= (minHeight / resultSize.height);
511         resultSize.height = minHeight;
512     }
513     if( resultSize.width > maxWidth )
514     {
515         resultSize.height *= (maxWidth / resultSize.width);
516         resultSize.width = maxWidth;
517     }
518     if( resultSize.height > maxHeight )
519     {
520         resultSize.width *= (maxHeight / resultSize.height);
521         resultSize.height = maxHeight;
522     }
523     
524     return resultSize;
525 }
526
527 //
528 // -[PictureController(Private) resizePanelForViewSize:animate:]
529 //
530 // Resizes the entire sheet to accomodate an OpenGL view of a particular size.
531 //
532 - (void)resizeSheetForViewSize: (NSSize)viewSize
533 {
534     // Figure out the deltas for the new frame area
535     NSSize currentSize = [fPictureGLViewArea frame].size;
536     float deltaX = viewSize.width - currentSize.width;
537     float deltaY = viewSize.height - currentSize.height;
538     
539     // Now resize the whole panel by those same deltas, but don't exceed the min
540     NSRect frame = [fPicturePanel frame];
541     NSSize maxSize = [fPicturePanel maxSize];
542     NSSize minSize = [fPicturePanel minSize];
543     frame.size.width += deltaX;
544     frame.size.height += deltaY;
545     if( frame.size.width < minSize.width )
546     {
547         frame.size.width = minSize.width;
548     }
549     if( frame.size.height < minSize.height )
550     {
551         frame.size.height = minSize.height;
552     }
553
554     // But now the sheet is off-center, so also shift the origin to center it and
555     // keep the top aligned.
556     frame.origin.x -= (deltaX / 2.0);
557     frame.origin.y -= deltaY;
558
559     [fPicturePanel setFrame:frame display:YES animate:YES];
560 }
561
562 //
563 // -[PictureController(Private) setViewSize:]
564 //
565 // Changes the OpenGL view's size and centers it vertially inside of its area.
566 // Assumes resizeSheetForViewSize: has already been called.
567 //
568 - (void)setViewSize: (NSSize)viewSize
569 {
570     [fPictureGLView setFrameSize:viewSize];
571     
572     // center it vertically
573     NSPoint origin = [fPictureGLViewArea frame].origin;
574     origin.y += ([fPictureGLViewArea frame].size.height -
575                  [fPictureGLView frame].size.height) / 2.0;
576     [fPictureGLView setFrameOrigin:origin];
577 }
578
579 //
580 // -[PictureController(Private) viewNeedsToResizeToSize:]
581 //
582 // Returns YES if the view will need to resize to match the given size.
583 //
584 - (BOOL)viewNeedsToResizeToSize: (NSSize)newSize
585 {
586     NSSize viewSize = [fPictureGLView frame].size;
587     return (newSize.width != viewSize.width || newSize.height != viewSize.height);
588 }
589
590
591 @end