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:
parent
6913558f4a
commit
5a88a805d0
@ -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();
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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>
|
||||||
|
@ -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 + "]";
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user