1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-09-29 01:59:59 +00:00

Fix proportions for animated GIFs

As with still images, animations are rendered at original size and
then scaled with HTML properties.

Also, fixed the blurry scaling on animation thumbnails.  I couldn't
find a way to do nearest-neighbor scaling in the code-behind without
resorting to System.Drawing (WinForms), so I added an overlay image
to the various grids.
This commit is contained in:
Andy McFadden 2019-12-25 10:07:42 -08:00
parent 6913558f4a
commit 5a88a805d0
8 changed files with 70 additions and 47 deletions

View File

@ -94,7 +94,9 @@ namespace CommonWPF {
/// Converts the list of frames into an animated GIF, and writes it to the stream. /// Converts the list of frames into an animated GIF, and writes it to the stream.
/// </summary> /// </summary>
/// <param name="stream">Output stream.</param> /// <param name="stream">Output stream.</param>
public void Save(Stream stream) { public void Save(Stream stream, out int maxWidth, out int maxHeight) {
maxWidth = maxHeight = -1;
if (Frames.Count == 0) { if (Frames.Count == 0) {
// nothing to do // nothing to do
Debug.Assert(false); Debug.Assert(false);
@ -129,8 +131,6 @@ namespace CommonWPF {
// otherwise *possible*, but we'd need to decode, update palettes and pixels, and // otherwise *possible*, but we'd need to decode, update palettes and pixels, and
// re-encode.) // re-encode.)
// //
int maxWidth = -1;
int maxHeight = -1;
foreach (UnpackedGif gif in gifs) { foreach (UnpackedGif gif in gifs) {
//gif.DebugDump(); //gif.DebugDump();

View File

@ -715,8 +715,11 @@ namespace SourceGen {
".gif"; ".gif";
string pathName = Path.Combine(mImageDirPath, fileName); string pathName = Path.Combine(mImageDirPath, fileName);
int dispWidth, dispHeight;
Visualization vis = visSet[index]; Visualization vis = visSet[index];
if (vis is VisualizationAnimation) { if (vis is VisualizationAnimation) {
// Animated visualization.
VisualizationAnimation visAnim = (VisualizationAnimation)vis; VisualizationAnimation visAnim = (VisualizationAnimation)vis;
int frameDelay = PluginCommon.Util.GetFromObjDict(visAnim.VisGenParams, int frameDelay = PluginCommon.Util.GetFromObjDict(visAnim.VisGenParams,
VisualizationAnimation.FRAME_DELAY_MSEC_PARAM, 330); VisualizationAnimation.FRAME_DELAY_MSEC_PARAM, 330);
@ -745,14 +748,17 @@ namespace SourceGen {
// Create new or replace existing image file. // Create new or replace existing image file.
try { try {
using (FileStream stream = new FileStream(pathName, FileMode.Create)) { using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
encoder.Save(stream); encoder.Save(stream, out dispWidth, out dispHeight);
} }
} catch (Exception ex) { } catch (Exception ex) {
// TODO: add an error report // TODO: add an error report
Debug.WriteLine("Error creating animated GIF file '" + pathName + "': " + Debug.WriteLine("Error creating animated GIF file '" + pathName + "': " +
ex.Message); ex.Message);
dispWidth = dispHeight = 1;
} }
} else { } else {
// Bitmap visualization.
//
// Encode a GIF the same size as the original bitmap. // Encode a GIF the same size as the original bitmap.
GifBitmapEncoder encoder = new GifBitmapEncoder(); GifBitmapEncoder encoder = new GifBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(vis.CachedImage)); encoder.Frames.Add(BitmapFrame.Create(vis.CachedImage));
@ -770,6 +776,9 @@ namespace SourceGen {
Debug.WriteLine("Error creating GIF file '" + pathName + "': " + Debug.WriteLine("Error creating GIF file '" + pathName + "': " +
ex.Message); ex.Message);
} }
dispWidth = (int)vis.CachedImage.Width;
dispHeight = (int)vis.CachedImage.Height;
} }
// Output thumbnail-size IMG element, preserving proportions. I'm assuming // Output thumbnail-size IMG element, preserving proportions. I'm assuming
@ -778,17 +787,17 @@ namespace SourceGen {
// across all visualization lines, but that can create some monsters (e.g. // across all visualization lines, but that can create some monsters (e.g.
// a bitmap that's 1 pixel high and 40 wide), so we cap the width. // a bitmap that's 1 pixel high and 40 wide), so we cap the width.
int dimMult = IMAGE_SIZE; int dimMult = IMAGE_SIZE;
double maxDim = vis.CachedImage.Height; double maxDim = dispHeight;
if (vis.CachedImage.Width > vis.CachedImage.Height * 2) { if (dispWidth > dispHeight * 2) {
// Too proportionally wide, so use the width as the limit. Allow it to // Too proportionally wide, so use the width as the limit. Allow it to
// up to 2x the max width (which can't cause the thumb height to exceed // up to 2x the max width (which can't cause the thumb height to exceed
// the height limit). // the height limit).
maxDim = vis.CachedImage.Width; maxDim = dispWidth;
dimMult *= 2; dimMult *= 2;
} }
int thumbWidth = (int)Math.Round(dimMult * (vis.CachedImage.Width / maxDim)); int thumbWidth = (int)Math.Round(dimMult * (dispWidth / maxDim));
int thumbHeight = (int)Math.Round(dimMult * (vis.CachedImage.Height / maxDim)); int thumbHeight = (int)Math.Round(dimMult * (dispHeight / maxDim));
//Debug.WriteLine(vis.CachedImage.Width + "x" + vis.CachedImage.Height + " --> " + //Debug.WriteLine(dispWidth + "x" + dispHeight + " --> " +
// thumbWidth + "x" + thumbHeight + " (" + maxDim + ")"); // thumbWidth + "x" + thumbHeight + " (" + maxDim + ")");
if (index != 0) { if (index != 0) {

View File

@ -189,13 +189,15 @@ stored in a straightforward fashion.</p>
<ul> <ul>
<li><b>Sprite</b> - basic 1xN sprite, converted to an image 8 pixels <li><b>Sprite</b> - basic 1xN sprite, converted to an image 8 pixels
wide.</li> wide. Square pixels are assumed.</li>
<li><b>Playfield</b> - assumes PF0,PF1,PF2 are stored in that order, <li><b>Playfield</b> - assumes PF0,PF1,PF2 are stored in that order,
multiple entries following each other. Specify the number of multiple entries following each other. Specify the number of
3-byte entries as the height. 3-byte entries as the height.
Since most playfields aren't the full height of the screen, Since most playfields aren't the full height of the screen,
it will tend to look squashed. Use the "row thickness" feature it will tend to look squashed. Use the "row thickness" feature
to repeat each row N times to adjust the proportions.</li> to repeat each row N times to adjust the proportions.
The "Reflected" checkbox determines whether the right-side image is
repeated as-is or flipped.</li>
</ul> </ul>
<h3>Commodore 64 - Commodore/VisC64</h3> <h3>Commodore 64 - Commodore/VisC64</h3>

View File

@ -72,6 +72,11 @@ namespace SourceGen {
/// </remarks> /// </remarks>
public BitmapSource CachedImage { get; set; } public BitmapSource CachedImage { get; set; }
/// <summary>
/// Image overlaid on CachedImage. Used to identify thumbnails as animations.
/// </summary>
public BitmapSource OverlayImage { get; set; }
/// <summary> /// <summary>
/// True if CachedImage has something other than the default value. /// True if CachedImage has something other than the default value.
/// </summary> /// </summary>
@ -93,6 +98,8 @@ namespace SourceGen {
internal static readonly BitmapSource ANIM_OVERLAY_IMAGE = internal static readonly BitmapSource ANIM_OVERLAY_IMAGE =
VisualizationAnimation.GenerateAnimOverlayImage(); VisualizationAnimation.GenerateAnimOverlayImage();
internal static readonly BitmapSource BLANK_IMAGE = GenerateBlankImage();
/// <summary> /// <summary>
/// Serial number, for reference from other Visualization objects. Not serialized. /// Serial number, for reference from other Visualization objects. Not serialized.
/// </summary> /// </summary>
@ -138,6 +145,7 @@ namespace SourceGen {
VisGenIdent = visGenIdent; VisGenIdent = visGenIdent;
VisGenParams = visGenParams; VisGenParams = visGenParams;
CachedImage = BROKEN_IMAGE; CachedImage = BROKEN_IMAGE;
OverlayImage = BLANK_IMAGE;
if (oldObj == null) { if (oldObj == null) {
// not worried about multiple threads // not worried about multiple threads
@ -210,6 +218,12 @@ namespace SourceGen {
return image; return image;
} }
private static BitmapSource GenerateBlankImage() {
RenderTargetBitmap bmp = new RenderTargetBitmap(1, 1, 96.0, 96.0,
PixelFormats.Pbgra32);
return bmp;
}
public override string ToString() { public override string ToString() {
return "[Vis: " + Tag + " (" + VisGenIdent + ") count=" + VisGenParams.Count + "]"; return "[Vis: " + Tag + " (" + VisGenIdent + ") count=" + VisGenParams.Count + "]";

View File

@ -80,42 +80,28 @@ namespace SourceGen {
mSerialNumbers.Add(serial); mSerialNumbers.Add(serial);
} }
CachedImage = ANIM_OVERLAY_IMAGE; // default to this CachedImage = BLANK_IMAGE;
OverlayImage = ANIM_OVERLAY_IMAGE;
} }
/// <summary>
/// Generates a cached image for the animation.
/// </summary>
/// <remarks>
/// Currently just using the first frame. We could do fancy things, like make a
/// poster with the first N images.
/// </remarks>
/// <param name="visSets">List of visualization sets.</param>
public void GenerateImage(SortedList<int, VisualizationSet> visSets) { public void GenerateImage(SortedList<int, VisualizationSet> visSets) {
const int IMAGE_SIZE = 64; CachedImage = BLANK_IMAGE;
CachedImage = ANIM_OVERLAY_IMAGE;
if (mSerialNumbers.Count == 0) { if (mSerialNumbers.Count == 0) {
return; return;
} }
Visualization vis = VisualizationSet.FindVisualizationBySerial(visSets, Visualization vis = VisualizationSet.FindVisualizationBySerial(visSets,
mSerialNumbers[0]); mSerialNumbers[0]);
if (vis == null) { if (vis != null) {
return; CachedImage = vis.CachedImage;
} }
double maxDim = Math.Max(vis.CachedImage.Width, vis.CachedImage.Height);
double dimMult = IMAGE_SIZE / maxDim;
double adjWidth = vis.CachedImage.Width * dimMult;
double adjHeight = vis.CachedImage.Height * dimMult;
Rect imgBounds = new Rect((IMAGE_SIZE - adjWidth) / 2, (IMAGE_SIZE - adjHeight) / 2,
adjWidth, adjHeight);
DrawingVisual visual = new DrawingVisual();
//RenderOptions.SetBitmapScalingMode(visual, BitmapScalingMode.NearestNeighbor);
using (DrawingContext dc = visual.RenderOpen()) {
dc.DrawImage(vis.CachedImage, imgBounds);
dc.DrawImage(ANIM_OVERLAY_IMAGE, new Rect(0, 0, IMAGE_SIZE, IMAGE_SIZE));
}
RenderTargetBitmap bmp = new RenderTargetBitmap(IMAGE_SIZE, IMAGE_SIZE, 96.0, 96.0,
PixelFormats.Pbgra32);
bmp.Render(visual);
CachedImage = bmp;
//Debug.WriteLine("RENDERED " + Tag);
} }
/// <summary> /// <summary>

View File

@ -109,8 +109,11 @@ See also https://github.com/fadden/DisasmUiTest
<DataTemplate> <DataTemplate>
<Border BorderThickness="1" Padding="1" <Border BorderThickness="1" Padding="1"
Background="{StaticResource BitmapBackground}"> Background="{StaticResource BitmapBackground}">
<Image Width="64" Height="64" Source="{Binding CachedImage}" <Grid>
RenderOptions.BitmapScalingMode="NearestNeighbor"/> <Image Width="64" Height="64" Source="{Binding CachedImage}"
RenderOptions.BitmapScalingMode="NearestNeighbor"/>
<Image Width="64" Height="64" Source="{Binding OverlayImage}"/>
</Grid>
</Border> </Border>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>

View File

@ -89,8 +89,11 @@ limitations under the License.
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Border BorderThickness="0" Background="{StaticResource BitmapBackground}"> <Border BorderThickness="0" Background="{StaticResource BitmapBackground}">
<Image Source="{Binding CachedImage}" Width="48" Height="48" <Grid>
RenderOptions.BitmapScalingMode="NearestNeighbor"/> <Image Source="{Binding CachedImage}" Width="48" Height="48"
RenderOptions.BitmapScalingMode="NearestNeighbor"/>
<Image Source="{Binding OverlayImage}" Width="48" Height="48"/>
</Grid>
</Border> </Border>
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
@ -123,8 +126,11 @@ limitations under the License.
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Border BorderThickness="0" Background="{StaticResource BitmapBackground}"> <Border BorderThickness="0" Background="{StaticResource BitmapBackground}">
<Image Source="{Binding CachedImage}" Width="48" Height="48" <Grid>
RenderOptions.BitmapScalingMode="NearestNeighbor"/> <Image Source="{Binding CachedImage}" Width="48" Height="48"
RenderOptions.BitmapScalingMode="NearestNeighbor"/>
<Image Source="{Binding OverlayImage}" Width="48" Height="48"/>
</Grid>
</Border> </Border>
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>

View File

@ -75,8 +75,11 @@ limitations under the License.
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Border BorderThickness="0" Background="{StaticResource BitmapBackground}"> <Border BorderThickness="0" Background="{StaticResource BitmapBackground}">
<Image Source="{Binding CachedImage}" Width="48" Height="48" <Grid>
RenderOptions.BitmapScalingMode="NearestNeighbor"/> <Image Source="{Binding CachedImage}" Width="48" Height="48"
RenderOptions.BitmapScalingMode="NearestNeighbor"/>
<Image Source="{Binding OverlayImage}" Width="48" Height="48"/>
</Grid>
</Border> </Border>
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>