/// <summary> /// Save an image for reference. /// </summary> /// <param name="fileName">The image name.</param> /// <param name="size">The image size.</param> /// <param name="pixels">The image pixels.</param> public static void SaveReferenceImage(string fileName, Vector2 size, byte[] pixels) { if (!_runnerFolderCreated) { Directory.CreateDirectory(RunnerReferenceImageFolder); _runnerFolderCreated = true; } string filePath = Path.Join(RunnerReferenceImageFolder, fileName); byte[] file = PngFormat.Encode(ImageUtil.FlipImageYNoMutate(pixels, (int)size.Y), size, PixelFormat.Rgba); File.WriteAllBytes(filePath, file); }
private void LoadFile(OtherAsset f) { _status = "Loading..."; byte[] data = f.Content; if (!PngFormat.IsPng(data)) { _status = $"The provided file {f.Name} is not a PNG file."; return; } byte[] pixels = PngFormat.Decode(data, out PngFileHeader header); byte[] output = PngFormat.Encode(pixels, header.Width, header.Height); bool saved = Engine.AssetLoader.Save(output, "Player" + "/" + f.Name, false); _status = saved ? "Done!" : "Error when saving the file. Check logs."; }
private void RenderSaveSection(RenderComposer composer) { // Saving ImGui.InputText("Name", ref _saveName, 100); ImGui.SameLine(); if (string.IsNullOrEmpty(_saveName)) { ImGui.TextDisabled("Save"); ImGui.SameLine(); ImGui.TextDisabled("SaveToFile"); } else { ImGui.SameLine(); if (ImGui.Button("SaveToFile")) { string saveName = _saveName.ToLower(); if (!saveName.Contains(".anim")) saveName += ".anim"; // Fixups if (AnimController?.MirrorXAnchors != null) { var emptyMirrorAnchors = true; for (var i = 0; i < AnimController.MirrorXAnchors.Length; i++) { if (AnimController.MirrorXAnchors[i] == Vector2.Zero) continue; emptyMirrorAnchors = false; break; } if (emptyMirrorAnchors) AnimController.MirrorXAnchors = null; } try { string saveData; // ReSharper disable once ConvertIfStatementToConditionalTernaryExpression if (AnimController != null) saveData = XMLFormat.To(AnimController); else saveData = XMLFormat.To(Animation); Engine.AssetLoader.Save(Encoding.UTF8.GetBytes(saveData), saveName); } catch (Exception ex) { Engine.Log.Error(ex); } } if (ImGui.Button("Save Packed Texture")) { string saveName = _saveName.ToLower(); if (!saveName.Contains(".png")) saveName += ".png"; Rectangle[] frames = AnimController != null ? AnimController.AnimTex.Frames : Animation.Frames; var preBinnedFrames = new Rectangle[frames.Length]; Array.Copy(frames, preBinnedFrames, frames.Length); Texture spriteSheetTexture = Animation.Texture; var spacing = 2; for (var i = 0; i < frames.Length; i++) { Rectangle frame = frames[i]; frames[i] = frame.Inflate(spacing, spacing); } Vector2 totalSize = Binning.FitRectangles(frames, true); FrameBuffer texture = new FrameBuffer(totalSize).WithColor(); composer.RenderTo(texture); for (var i = 0; i < frames.Length; i++) { composer.RenderSprite(frames[i].Deflate(spacing, spacing), Color.White, spriteSheetTexture, preBinnedFrames[i]); } composer.RenderTo(null); byte[] pixelsDownload = texture.Sample(new Rectangle(0, 0, totalSize), PixelFormat.Rgba); ImageUtil.FlipImageY(pixelsDownload, (int) totalSize.Y); byte[] pngFile = PngFormat.Encode(pixelsDownload, totalSize, PixelFormat.Rgba); Engine.AssetLoader.Save(pngFile, saveName); } } }
/// <summary> /// Verify two images. /// </summary> /// <param name="compareName"> /// The name of the comparison. Should be unique for all tests. Is used to store information on /// the disk about the comparison. /// </param> /// <param name="originalImage">The original image. Must be in a four component format.</param> /// <param name="comparisonImage">The image comparing it to. Must be in a four component format.</param> /// <param name="comparisonSize">The size of the second image.</param> public static void VerifyImages(string compareName, byte[] originalImage, byte[] comparisonImage, Vector2 comparisonSize) { // Runner reference image folders should be created only for runners who verify images. if (!_runnerFolderCreated) { Directory.CreateDirectory(RunnerReferenceImageFolder); _runnerFolderCreated = true; } // Invent a name for this comparison and a folder to store data in, in case it is derived. string fileName; if (_comparisonImageDuplicate.ContainsKey(compareName)) { fileName = $"{compareName}{_comparisonImageDuplicate[compareName]}.png"; _comparisonImageDuplicate[compareName]++; } else { fileName = $"{compareName}.png"; _comparisonImageDuplicate.Add(compareName, 1); } // We want to store the comparison image for possible manual comparison. SaveReferenceImage(fileName, comparisonSize, comparisonImage); // Check if the original image is missing, in which case we just store the comparison image. if (originalImage == null) { return; } Engine.Log.Info($" Comparing images {compareName}...", TestRunnerLogger.TestRunnerSrc); float derivedPixelPercentage; byte[] derivationImage = null; // If the size isn't the same, it is all derived. if (originalImage.Length == comparisonImage.Length) { derivedPixelPercentage = CalculateImageDerivation(originalImage, comparisonImage, out derivationImage) * 100; } else { derivedPixelPercentage = 100; } if (derivedPixelPercentage == 0) { Engine.Log.Info(" No derivation.", TestRunnerLogger.TestRunnerSrc); return; } // Save a derivation image showing the differences. if (derivationImage != null) { string directory = Path.Join(RunnerReferenceImageFolder, $"Comparison_{fileName}"); Directory.CreateDirectory(directory); byte[] derivedFile = PngFormat.Encode(ImageUtil.FlipImageYNoMutate(derivationImage, (int)comparisonSize.Y), comparisonSize, PixelFormat.Rgba); File.WriteAllBytes(Path.Join(directory, "derivation.png"), derivedFile); } // Assert derivation is not higher than tolerable. This is not done using the Emotion.Test assert so it doesn't stop the test from continuing. if (derivedPixelPercentage > PixelDerivationTolerance) { throw new ImageDerivationException($" Failed derivation check. Derivation is {derivedPixelPercentage}%."); } Engine.Log.Info($" Derivation is {derivedPixelPercentage}%.", TestRunnerLogger.TestRunnerSrc); }
public void DebugDump(string fileName) { byte[] bytes = ImageUtil.AToRgba(Pixels); bytes = PngFormat.Encode(bytes, (int)Size.X, (int)Size.Y); File.WriteAllBytes(fileName, bytes); }
protected override void RenderContent(RenderComposer composer) { // File selection. if (ImGui.Button("Choose Texture File")) { var explorer = new FileExplorer <TextureAsset>(f => { _file = f; _pixelData = null; }); Parent.AddWindow(explorer); } if (_file == null) { return; } _previewTexture ??= new Texture(new Vector2(100, 100), _file.Texture.PixelFormat); if (_pixelData == null) { _pixelData = new byte[(int)(_file.Texture.Size.X * _file.Texture.Size.Y * 4)]; unsafe { fixed(void *p = &_pixelData[0]) { Texture.EnsureBound(_file.Texture.Pointer); Gl.GetTexImage(TextureTarget.Texture2d, 0, _file.Texture.PixelFormat, PixelType.UnsignedByte, new IntPtr(p)); } } _removedPixelData = new byte[_pixelData.Length]; DetectRogueAlpha(); UpdatePreview("normal"); } ImGui.Image(new IntPtr(_file.Texture.Pointer), _file.Texture.Size); int thresholdInput = _threshold; ImGui.InputInt("Threshold", ref thresholdInput, 1, 5); if (thresholdInput != _threshold) { _threshold = (byte)thresholdInput; DetectRogueAlpha(); UpdatePreview(); } ImGui.Text($"Detected Rogue Alpha: {_rogueAlphaPixels}/{_file.Texture.Size.X * _file.Texture.Size.Y} pixels"); ImGui.Text("Preview"); ImGui.Image(new IntPtr(_previewTexture.Pointer), _previewTexture.Size); if (ImGui.Button("Normal Preview")) { UpdatePreview("normal"); } ImGui.SameLine(); if (ImGui.Button("Alpha Preview")) { UpdatePreview("alpha"); } if (ImGui.Button("Apply Changes")) { UpdatePreview("export"); byte[] pngData = PngFormat.Encode(_removedPixelData, _previewTexture.Size, _file.Texture.PixelFormat); Engine.AssetLoader.Save(pngData, _file.Name); } }
protected override void RenderContent(RenderComposer composer) { // File selection. if (ImGui.Button("Choose Texture File")) { var explorer = new FileExplorer <TextureAsset>(f => { _file = f; _pixelData = null; }); Parent.AddWindow(explorer); } if (_file == null) { return; } if (_previewTexture == null) { _previewTexture = new Texture(new System.Numerics.Vector2(100, 100)); } if (_pixelData == null) { _pixelData = new byte[(int)(_file.Texture.Size.X * _file.Texture.Size.Y * 4)]; unsafe { fixed(void *p = &_pixelData[0]) { Texture.EnsureBound(_file.Texture.Pointer); Gl.GetTexImage(TextureTarget.Texture2d, 0, PixelFormat.Bgra, PixelType.UnsignedByte, new IntPtr(p)); } } _removedPixelData = new byte[_pixelData.Length]; DetectRogueAlpha(); UpdatePreview("normal"); } ImGui.Image(new IntPtr(_file.Texture.Pointer), _file.Texture.Size); int thresholdInput = _threshold; ImGui.InputInt("Threshold", ref thresholdInput, 1, 5); if (thresholdInput != _threshold) { _threshold = (byte)thresholdInput; DetectRogueAlpha(); UpdatePreview(); } ImGui.Text($"Detected Rogue Alpha: {_rogueAlphaPixels}/{_file.Texture.Size.X * _file.Texture.Size.Y} pixels"); ImGui.Text("Preview"); ImGui.Image(new IntPtr(_previewTexture.Pointer), _previewTexture.Size); if (ImGui.Button("Normal Preview")) { UpdatePreview("normal"); } ImGui.SameLine(); if (ImGui.Button("Alpha Preview")) { UpdatePreview("alpha"); } if (ImGui.Button("Apply Changes")) { UpdatePreview("export"); byte[] pngData = PngFormat.Encode(_removedPixelData, (int)_previewTexture.Size.X, (int)_previewTexture.Size.Y); System.IO.File.WriteAllBytes(Helpers.CrossPlatformPath($"Assets/rogueAlphaRemoved_{System.IO.Path.GetFileName(_file.Name)}"), pngData); } }