/// <summary> /// Creates an animated GIF and writes it to <paramref name="outputStream"/>. /// </summary> /// <param name="animation"></param> /// <param name="outputStream"></param> public void Create(ConstRawAnimation animation, Stream outputStream) { List <Image> rawFrames = new List <Image>(); // Load each image in the animation. foreach (ConstAnimationFrame frame in animation.Frames) { NpkPath frameImagePath = frame.Image.ImageFilePath; DFO.Common.Images.Image rawFrame = m_imageSource.GetImage(frameImagePath, frame.Image.FrameIndex); rawFrames.Add(rawFrame); } int smallestX; int largestX; int smallestY; int largestY; // Frames can have different start positions and widths/heights. Normalize the images to a common coordinate system. FrameInfo.GetNormalizedCoordinates(rawFrames.Select(image => image.Attributes), out smallestX, out largestX, out smallestY, out largestY); int normalizedWidth = largestX - smallestX + 1; int normalizedHeight = largestY - smallestY + 1; List <MagickImage> renderedFrames = new List <MagickImage>(); try { // Composite each frame on top of a canvas of normalized width and height. for (int frameIndex = 0; frameIndex < animation.Frames.Count; frameIndex++) { Image rawFrameImage = rawFrames[frameIndex]; ConstAnimationFrame frameAnimationInfo = animation.Frames[frameIndex]; MagickImage renderedFrame = RenderFrame(rawFrameImage, frameAnimationInfo, smallestX, largestX, smallestY, largestY, normalizedWidth, normalizedHeight); renderedFrames.Add(renderedFrame); } // Make the GIF from the frames and write it out to the stream. using (MagickImageCollection frameCollection = new GraphicsMagick.MagickImageCollection(renderedFrames)) { frameCollection.Write(outputStream, MagickFormat.Gif); } } finally { renderedFrames.ForEach(f => f.Dispose()); } }
private void RefreshFrameList() { // Clear frame list FrameList.Clear(); // Get current selected inner file InnerNpkFile selectedFile = InnerFileList.Current; // If none selected nothing else to do here if (selectedFile == null) { return; } // Get the list of frames. Since it's lazy loaded, there could be a read error. List <FrameInfo> frames; try { frames = _editor.Frames[selectedFile.Path].ToList(); } catch (Exception ex) { // TODO: Better way of displaying than modal? // At least get the view to show it instead of the view model MessageBox.Show(string.Format("Error reading frames from NPK file: {0}", ex.Message)); 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++) { FrameInfo frame = frames[frameIndex]; // if linked frame, follow link if (frame.LinkFrame != null) { int linkIndex = frame.LinkFrame.Value; if (linkIndex < 0 || linkIndex >= frames.Count) { // TODO: Log error that link is out of range? FrameList.Add(new FrameMetadata(frameIndex, 0, 0, 0, 0, linkIndex)); } else { FrameInfo linkedFrame = frames[linkIndex]; FrameList.Add(new FrameMetadata(linkedFrame, frameIndex, linkIndex)); } } else { FrameList.Add(new FrameMetadata(frame, frameIndex, linkFrameIndex: null)); } } }