Skip to content
This repository has been archived by the owner on Nov 14, 2017. It is now read-only.

Commit

Permalink
Adjust display of image frames by the frame's coordinates in the NPK.
Browse files Browse the repository at this point in the history
Results in smooth transitions when scrolling through frames that
make up an animation.

Fixes #7.
  • Loading branch information
LHCGreg authored and LHCGreg committed Aug 10, 2015
1 parent 4725a3d commit 7d51636
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 56 deletions.
33 changes: 33 additions & 0 deletions DFO.Common/Images/FrameInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,38 @@ public FrameInfo(bool isCompressed, int compressedLength, PixelDataFormat pixelF
MaxWidth = maxWidth;
MaxHeight = maxHeight;
}

public static void GetNormalizedCoordinates(IEnumerable<FrameInfo> frames, out int smallestX, out int largestX, out int smallestY, out int largestY)
{
smallestX = int.MaxValue;
largestX = 0;
smallestY = int.MaxValue;
largestY = 0;

foreach (FrameInfo frame in frames)
{
int startX = frame.LocationX;
int endX = startX + frame.Width - 1;
int startY = frame.LocationY;
int endY = startY + frame.Height - 1;

if (startX < smallestX)
{
smallestX = startX;
}
if (endX > largestX)
{
largestX = endX;
}
if (startY < smallestY)
{
smallestY = startY;
}
if (endY > largestY)
{
largestY = endY;
}
}
}
}
}
43 changes: 5 additions & 38 deletions DFO.Images/GifMaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,10 @@ public void Create(ConstRawAnimation animation, Stream outputStream)
int largestY;

// Frames can have different start positions and widths/heights. Normalize the images to a common coordinate system.
GetNormalizedCoordinates(rawFrames, out smallestX, out largestX, out smallestY, out largestY);
FrameInfo.GetNormalizedCoordinates(rawFrames.Select(image => image.Attributes), out smallestX, out largestX, out smallestY, out largestY);

int normalizedWidth = largestX - smallestX;
int normalizedHeight = largestY - smallestY;
int normalizedWidth = largestX - smallestX + 1;
int normalizedHeight = largestY - smallestY + 1;

List<MagickImage> renderedFrames = new List<MagickImage>();

Expand Down Expand Up @@ -88,42 +88,9 @@ public void Create(ConstRawAnimation animation, Stream outputStream)
}
}

private void GetNormalizedCoordinates(List<Image> rawFrames, out int smallestX, out int largestX, out int smallestY, out int largestY)
{
smallestX = int.MaxValue;
largestX = 0;
smallestY = int.MaxValue;
largestY = 0;

foreach (Image rawFrame in rawFrames)
{
int startX = rawFrame.Attributes.LocationX;
int endX = startX + rawFrame.Attributes.Width;
int startY = rawFrame.Attributes.LocationY;
int endY = startY + rawFrame.Attributes.Height;

if (startX < smallestX)
{
smallestX = startX;
}
if (endX > largestX)
{
largestX = endX;
}
if (startY < smallestY)
{
smallestY = startY;
}
if (endY > largestY)
{
largestY = endY;
}
}
}

private MagickImage RenderFrame(Image rawFrameImage, ConstAnimationFrame frameAnimationInfo, int smallestX, int largestX, int smallestY, int largestY, int normalizedWidth, int normalizedHeight)
{
MagickImage renderedFrame = new MagickImage(new MagickColor(0, 0, 0, 0), (int)normalizedWidth, (int)normalizedHeight);
MagickImage renderedFrame = new MagickImage(new MagickColor(0, 0, 0, 0), normalizedWidth, normalizedHeight);

int normalizedFrameX = rawFrameImage.Attributes.LocationX - smallestX;
int normalizedFrameY = rawFrameImage.Attributes.LocationY - smallestY;
Expand All @@ -142,7 +109,7 @@ private MagickImage RenderFrame(Image rawFrameImage, ConstAnimationFrame frameAn
{
rawFrameMagickImage.Format = MagickFormat.Gif;
rawFrameMagickImage.MatteColor = new MagickColor(0, 0, 0, 0);
renderedFrame.Composite(rawFrameMagickImage, (int)normalizedFrameX, (int)normalizedFrameY, CompositeOperator.Over);
renderedFrame.Composite(rawFrameMagickImage, normalizedFrameX, normalizedFrameY, CompositeOperator.Over);
}
}

Expand Down
101 changes: 83 additions & 18 deletions DFOToolbox/MainWindowViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ public FrameList FrameList
set { _frameList = value; OnPropertyChanged(); }
}

// For all frames together. Set when the selected .img changes.
private int _smallestX;
private int _largestX;
private int _smallestY;
private int _largestY;
private int _width;
private int _height;

private ImageSource _currentFrameImage;
public ImageSource CurrentFrameImage
{
Expand Down Expand Up @@ -113,7 +121,7 @@ public void Open(string npkPath)
{
return;
}

// TODO: async?
try
{
Expand Down Expand Up @@ -177,6 +185,22 @@ private void SelectedInnerFileChanged(object sender, EventArgs e)
return;
}

_smallestX = 0;
_largestX = 0;
_smallestY = 0;
_largestY = 0;
_width = 1;
_height = 1;

List<FrameInfo> nonLinkFrames = frames.Where(f => f.LinkFrame == null).ToList();

if (nonLinkFrames.Count > 0)
{
FrameInfo.GetNormalizedCoordinates(nonLinkFrames, out _smallestX, out _largestX, out _smallestY, out _largestY);
_width = _largestX - _smallestX + 1;
_height = _largestY - _smallestY + 1;
}

// Populate frame list
for (int frameIndex = 0; frameIndex < frames.Count; frameIndex++)
{
Expand All @@ -187,7 +211,7 @@ private void SelectedInnerFileChanged(object sender, EventArgs e)
int linkIndex = frame.LinkFrame.Value;
if (linkIndex < 0 || linkIndex >= frames.Count)
{
// todo: Log error that link is out of range?
// TODO: Log error that link is out of range?
FrameList.Add(new FrameMetadata(frameIndex, 0, 0, 0, 0, linkIndex));
}
else
Expand Down Expand Up @@ -232,33 +256,74 @@ private void SelectedFrameChanged(object sender, EventArgs e)
return;
}

// Adjust position in bounding box according to frame coordinates
// go from (0, 0) based coordinates to (_smallestX, _smallestY) based coordinates
// (0, 0) -> (45, 50)
// (60, 55) -> (15, 5)
// frame x - _smallestX = bounding box X
// frame y - _smallestY = bounding box y
// Paint image in bounding box

// RGBA -> BGRA (for little endian platforms), (BGRA for big endian platforms) - seems to not be reversed for little endian???
// TODO: Make NpkReader able to output in variable format so it doesn't need to be converted
byte[] convertedBytes = new byte[image.PixelData.Length];
byte[] frameBytes = new byte[_width * _height * 4];
bool isLittleEndian = BitConverter.IsLittleEndian;

if (isLittleEndian)
// Get X in frame
// bounding box X + _smallestX = frame x
// (5, 0) 5x5
// (3, 1), 10x6
// smallest x: 3
// smallest y: 0
// (0, 0): 0 + 3
for (int boundingBoxY = 0; boundingBoxY < _height; boundingBoxY++)
{
for (int i = 0; i < image.PixelData.Length; i += 4)
int frameY = boundingBoxY + _smallestY;
int rowOffset = boundingBoxY * _width * 4;

// if this row is above or below the current frame, draw a row of transparent pixels
if (frameY < image.Attributes.LocationY || frameY > image.Attributes.LocationY + image.Attributes.Height - 1)
{
convertedBytes[i] = image.PixelData[i + 2]; // B
convertedBytes[i + 1] = image.PixelData[i + 1]; // G
convertedBytes[i + 2] = image.PixelData[i]; // R
convertedBytes[i + 3] = image.PixelData[i + 3]; // A
for (int boundingBoxX = 0; boundingBoxX < _width; boundingBoxX++)
{
int pixelOffset = rowOffset + (boundingBoxX * 4);
frameBytes[pixelOffset] = 0;
frameBytes[pixelOffset + 1] = 0;
frameBytes[pixelOffset + 2] = 0;
frameBytes[pixelOffset + 3] = 0;
}
}
}
else
{
for (int i = 0; i < image.PixelData.Length; i += 4)
else
{
convertedBytes[i] = image.PixelData[i + 2]; // B
convertedBytes[i + 1] = image.PixelData[i + 1]; // G
convertedBytes[i + 2] = image.PixelData[i]; // R
convertedBytes[i + 3] = image.PixelData[i + 3]; // A
for (int boundingBoxX = 0; boundingBoxX < _width; boundingBoxX++)
{
int frameX = boundingBoxX + _smallestX;
int pixelOffset = rowOffset + (boundingBoxX * 4);

// if this column is to the left or right of the current frame, draw a transparent pixel
if (frameX < image.Attributes.LocationX || frameX > image.Attributes.LocationX + image.Attributes.Width - 1)
{
frameBytes[pixelOffset] = 0;
frameBytes[pixelOffset + 1] = 0;
frameBytes[pixelOffset + 2] = 0;
frameBytes[pixelOffset + 3] = 0;
}
else
{
// RGBA -> BGRA
int zeroBasedFrameY = frameY - image.Attributes.LocationY;
int zeroBasedFrameX = frameX - image.Attributes.LocationX;
int framePixelOffset = zeroBasedFrameY * image.Attributes.Width * 4 + zeroBasedFrameX * 4;
frameBytes[pixelOffset] = image.PixelData[framePixelOffset + 2]; // B
frameBytes[pixelOffset + 1] = image.PixelData[framePixelOffset + 1]; // G
frameBytes[pixelOffset + 2] = image.PixelData[framePixelOffset]; // R
frameBytes[pixelOffset + 3] = image.PixelData[framePixelOffset + 3]; // A
}
}
}
}

CurrentFrameImage = BitmapSource.Create(frame.Width, frame.Height, dpiX: 96, dpiY: 96, pixelFormat: PixelFormats.Bgra32, palette: null, pixels: convertedBytes, stride: 4 * frame.Width);
CurrentFrameImage = BitmapSource.Create(_width, _height, dpiX: 96, dpiY: 96, pixelFormat: PixelFormats.Bgra32, palette: null, pixels: frameBytes, stride: 4 * _width);
}

/// <summary>
Expand Down

0 comments on commit 7d51636

Please sign in to comment.