¶Video filter: resize
It isn't fair for me just to pick on other peoples' UIs, so time for an example from my own program.
Rolling a configuration dialog by hand isn't a lot of fun. You need to code at least two paths for data, one going into the dialog controls, and another going out; if the settings are persistent, you need another two paths to and from the Registry or some other backing store. Also, typically not all controls are pertinent for all configurations, so additional logic is needed to track changes and enable or disable controls as needed. Finally, the settings in the dialog need to be validated against acceptable bounds for each datum. This adds up to a lot of code, which makes it annoying to add features to the existing UI, and I've been working on ways to reduce this. One such way is to implement "data exchange" routines that can either read or write data to a particular store; this then reduces the number of places where you have to keep track of variables, UI control IDs, etc. for each dialog.
The "resize" video filter has been in need of an overhaul for a while, so I decided to add some needed features and use it as a test bed for some of the new methods. Unfortunately, wrapping methods alone isn't enough to cure some of the usability problems that cropped up.
The main problem with the current resize filter is that it only allows absolute sizes. This is fine when you're targeting a particular size, but not when you want to preserve or target a particular aspect ratio, or want a certain multiple of the current size. After spending a while hashing out all the little issues with adding features to resize, this is what I came up with:
There are a few things missing yet in the dialog implementation, such as the % buttons not being hooked up yet and accelerators being absent, and it hasn't been appropriately squished down in size yet, but it's mostly functional.
Now, there is one important problem with adding features to resize that should be noted here: compatibility with existing scripts has to be preserved. This requirement arises since VirtualDub scripts request filters by name and I try to write the internal filters in the same way as external ones, i.e. little or no special-case support from the engine. That means the new resize filter still has to accept an image size, image filter mode, interlacing checkbox, frame size, and fill color, in order to avoid breaking existing job queues and scripts. If this were an onerous issue I'd just fork the resize filter, as the main resampling engine has already been refactored out, but I don't think it'll be a big deal to avoid this.
The problems addressed by the new design are:
- You can now specify a new size as relative from the source size.
- The new size can also be computed from either the source aspect ratio, or a specified aspect ratio. In fact, matching aspect ratio is the default.
- A different default can be saved.
- The frame size too can be computed by ratio as well as absolute size.
- The frame size can also be used to crop instead of just letterboxing, avoiding the need for a second filter instance to crop and calculating centered parameters for that crop.
There are a couple more features missing that I want to get in, primarily an option to round the frame size up to multiples of 4/8/16, but that shouldn't be too hard. The trickiest part is trying to cram them in without making the dialog too big or too cluttered.
Unfortunately, when I implemented the dialog and actually tried using it, I found the following problems, mostly related to the filter preview window:
- VirtualDub filters can be configured without an input source video. This allows a filter chain to be set up beforehand for multiple videos without having to be recreated for each. Unfortunately, this causes problems for the relative % buttons. If the image width is set to 640 pixels and Width% is suddenly checked, you probably don't want the filter preview new relative width to be 640%. And having two different absolute and relative size fields that correspond to the same pair of edit boxes feels like a baaaaad idea.
- Similarly, when Width% is unchecked, you don't want the new width to be 100 pixels. But what to set it to, if no source frame size is available?
- To avoid really bad cases, the dialog updates on KILLFOCUS notifications for the text edit windows instead of CHANGE. This works decently well for the width/height widgets... except if you happen to type 640240 and select another control before you've fixed the mistake. Then VirtualDub tries to allocate a 640,240 x 240 bitmap....
- The default for the aspect ratio is 4:3, and another common desired ratio is 16:9. Well, if you type 16 and then hit TAB, the preview window now updates with a ratio of 16:3, which tends to cause widths in excess of 3,000 pixels.
- Some fields can be temporarily invalid. Setting the aspect ratio denominator to zero is invalid, except if you're going toward a radio button to disable it. The current video resize dialog validates width/height on KILLFOCUS (yuck).
- The current resize filter requires the frame size to be at least as large as the image size. This can't be validated in the dialog anymore, because it's possible to make the image size relative and then change the source size outside of the dialog.
- Radio buttons in Windows have to be contiguous in order to find each other for auto-check functionality. This meant I couldn't place the letterbox/crop frame size edit boxes in between the frame mode options, breaking my normal book-order rule for tab order.
- The first set of aspect ratio options determines whether height is specified or computed, so it enables and disables the "new height" field. Unfortunately, this field is above the aspect ratio options, which feels counterintuitive -- I normally prefer all enabling to go downward, with the presumption that options are set from the top down. I don't think inverting this would be any better; size feels best at the top.
- Aspect ratio and size options are somewhat duplicated between the image size (top half) and the frame size (bottom half). I couldn't see a way to combine them, though, given conversions between 4:3, 16:9, and 16:10, though, and splitting them apart into separate filters is even worse (compatibility problems with the current resize filter, and unnecessary slowdowns for the crop case).
At this point it's tempting to crawl back into bed and just stick my head under a pillow. It's nice and warm there.
Here's what I'm thinking so far for solutions:
- Modify the core video filter engine so that FilterActivation::src contains valid width/size if known when configureProc is called on the filter. This may actually already be mostly true, but it's not guaranteed by the API, and the values are probably random if no video is loaded. Once this is done, the resize filter can have more sensible behavior with regard to toggling the Width% and Height% options.
- Modify the video preview window to refuse to auto-preview if the frame size output from the current filter exceeds a certain size, something like max(screen size, 1920x1080). This would prevent any filter from blowing up in the preview on typing accidents. An option would be added to Preferences to disable this behavior, and the error that would result otherwise would note the presence of this option.
- Let random garbage be entered in any of the fields. Validate only on OK, when (re)initializing the filter, and when saving settings.
- The frame size can be smaller than the image size. If it happens, it's no longer an error and a centered crop occurs instead.
I think these will help address the worst usability problems, although I won't know for sure until implementation.
I'm also looking at simplifying the filter modes list. In particular, the idea would be to eliminate the Bilinear and Bicubic options and alias them to the Precise Bilinear and Precise Bicubic (A=-0.75) settings for compatibility. The reasoning is that purposely aliasing a multi-tap filter probably isn't a common need, and for interpolation cases the settings are equivalent anyway. Eliminating the "precise" tag might cause confusion with existing how-tos that people have written, though.
Note that one feature I haven't considered here is presets, besides the one you get via "save as default." The reason I haven't considered presets is that those could probably be done in the core for all filters, not just resize, so it makes more sense to implement it there.