Windows/.NET的System.Drawing.Save(Stream,ImageFormat)中的错误.产生了错误的PNG [英] Bug in Windows /.NET's System.Drawing.Save(Stream, ImageFormat). Corrupt PNG produced

查看:40
本文介绍了Windows/.NET的System.Drawing.Save(Stream,ImageFormat)中的错误.产生了错误的PNG的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在某些特定情况下,System.Drawing.Save(Stream,Imageformat)创建损坏的PNG图像.

In some very specific situations, System.Drawing.Save(Stream, Imageformat) creates corrupt PNG images.

是否有避免这种情况的方法,例如:

Is there a way to avoid it, such that:

  1. 我不需要使用第三方库,并且
  2. 我不需要检查PNG字节来知道是否需要修复"某些东西?

复制步骤

  1. 创建一个System.Drawing.BitMap
  2. 向图像添加内容,以使其具有非常特定的PNG文件大小(何时发生")
  3. 调用Save(Stream,Imageformat)-选择PNG格式

出了什么问题?

问题是最后一个图像数据之后的IDAT块不正确.它不包含任何数据,但长度字节为00 00 ff f4. 可以使用 https://github.com/jsummers/tweakpng 进行检测.我们注意到Linux上的图像库(不确定哪个)无法处理此类错误.据我们所知,在Windows中此错误将被忽略,您将不会注意到任何问题.

The problem is an incorrect IDAT chunk after the last image data. It contains NO data, but has length bytes 00 00 ff f4. It can be detected with https://github.com/jsummers/tweakpng. We noticed that image libraries on Linux (not sure which ones) cannot deal with such mistakes. As far as we have seen, in Windows this mistake is ignored, and you will not notice any problem.

何时发生?

这取决于PNG文件的大小.仅当生成的PNG文件大小(字节)为 0x1001C + n * 0x10000,其中n为0、1、2、3、4,并且可能更大.

It depends on the PNG-file size. The problem only occurs if the resulting PNG-file size in bytes is 0x1001C + n * 0x10000 with n 0, 1, 2, 3, 4, and likely larger.

它是可复制的

可以对第二步进行调整,以生成特定的PNG文件大小(例如,在原本为空的BitMap中为不同数量的像素着色).当大小如上所述时,始终出现该错误.

Step two can be tweaked to produce a specific PNG-file size (eg. color a varying amount of pixels in an otherwise empty BitMap). When the size was as described above, the error consistently occurred.

要复制的代码

在干净的控制台应用程序中替换Program.cs的内容.运行该程序时,它将尝试创建具有指定大小的PNG,并将其另存为"constructed.png".

Replace the contents of Program.cs in a clean Console Application. When running the program, it will try to create a PNG with the exact specified size and save it as "constructed.png".

顺便说一句:第二个PNG保存在"constructedReExport.png"中:这是通过加载第一个PNG并再次保存来创建的.我想我记得这是一个潜在的解决方法,但是当我现在运行它时,第二个文件包含相同的错误...

As an aside: a second PNG is saved "constructedReExport.png": this is created by loading the first and Saving it again. I think I remember this was a potential workaround, but when I run it now, the second file contains the same error...

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;

namespace CreateCorruptPng
{
    class Program
    {
        static void Main(string[] args)
        {
            // Choose 0x1001C + 0x10000 * n; with n = 0, 1, 2, 3, 4 to get corrupt PNG
            int targetOutputSizeBytes = 0x5001C;

            // You may need to fiddle with these parameters to 
            // successfully create an image of the exact size.
            int widthPixels = 2000;
            int height = 1200;

            var creator = new PngCreator(widthPixels, height);

            string outputPath = ".";
            creator.TryCreateWithSize(targetOutputSizeBytes);
            creator.SaveCurrentImage(Path.Combine(outputPath, "constructed.png"));
            creator.SaveAfterSecondExport(Path.Combine(outputPath, "constructedReExport.png"));
        }
    }

    public class PngCreator
    {
        Bitmap _img;
        int _width;
        int _height;
        int _maxPixcount;

        public PngCreator(int w, int h)
        {
            _width = w;
            _height = h;
            _maxPixcount = w * h;
        }

        public void TryCreateWithSize(int requiredByteCount)
        {
            Console.WriteLine($"Attempting to create png file of exactly {requiredByteCount} bytes.");
            Console.WriteLine($"Image size (w x h) = {_width} x {_height}.");

            int lowerBound = 0;
            int upperBound = _maxPixcount;

            bool success = false;
            while (upperBound > lowerBound + 1)
            {
                InitImage();
                int pixelCount = (upperBound + lowerBound) / 2;
                AddPixels(pixelCount);

                int currentSize = GetPngByteCount();
                if (currentSize == requiredByteCount)
                {
                    success = true;
                    break;
                }

                if (currentSize < requiredByteCount)
                    lowerBound = pixelCount;
                else
                    upperBound = pixelCount;
            }
            Console.WriteLine("Search stopped.");

            if (success)
                Console.WriteLine($"SUCCESS.\n   Created PNG with exact file size {requiredByteCount} bytes.");
            else
                Console.WriteLine($"WARNING.\n" +
                    $"   Could not produce PNG with file size {requiredByteCount} bytes.\n" +
                    "   Try to run again with different resolution.\n" +
                    "   If the file size in the last iteration is too small, try larger resolution.");
        }

        private void InitImage()
        {
            _img?.Dispose();
            _img = new Bitmap(_width, _height, PixelFormat.Format16bppArgb1555);
        }

        private void AddPixels(int n)
        {
            Console.WriteLine($"Coloring {n} pixels...");
            for (int i = 0; i < n; i++)
            {
                int x = i % _width;
                int y = i / _width;
                _img.SetPixel(x, y, Color.FromArgb((i / 2) % 255, 0, 0));
            }
        }

        private int GetPngByteCount()
        {
            using (MemoryStream s = new MemoryStream())
            {
                _img.Save(s, ImageFormat.Png);

                byte[] imgBytes = s.ToArray();
                Console.WriteLine($"Png file size {imgBytes.Length}");
                return imgBytes.Length;
            }
        }

        public void SaveCurrentImage(string path)
        {
            SaveImage(path, _img);
        }

        public void SaveAfterSecondExport(string path)
        {
            using (Bitmap imgReimported = ToPngBytesAndLoadAgain(_img))
            {
                SaveImage(path, imgReimported);
            }
        }

        private Bitmap ToPngBytesAndLoadAgain(Bitmap img)
        {
            return new Bitmap(new MemoryStream(ToPngBytes(img)));
        }

        private static byte[] ToPngBytes(Bitmap img)
        {
            using (MemoryStream s = new MemoryStream())
            {
                img.Save(s, ImageFormat.Png);
                return s.ToArray();
            }
        }

        private static void SaveImage(string path, Bitmap img)
        {
            Console.WriteLine($"Saving file to {path}");
            File.WriteAllBytes(path, ToPngBytes(img));

        }
    }
}

推荐答案

在处理由Adobe产品创建的某些文件时,我看到了一些问题,不同的adobe产品具有不同的引擎,有时这会破坏GDI +的解决方法,以保留文件.使用不使用GDI +的第三方图像处理工具可能是最好的选择,例如ImageMagic(或.net包装器Magic.Net)

I have seen issues when manipulating some files created by Adobe products, different adobe products have different engines and sometimes this breaks GDI+ with a similar work-around to reserve the file. Using a 3rd party image manipulation tool that does not use GDI+ is probably the best option, eg ImageMagic (or the .net wrapper Magic.Net)

在广告内容时,是指绘制图像还是添加文件,请确保添加的文件不是问题.

When you ad content, do you mean drawing to the image or adding a file, check that the file added is not the issue.

这篇关于Windows/.NET的System.Drawing.Save(Stream,ImageFormat)中的错误.产生了错误的PNG的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆