kornelski / pngquant

Lossy PNG compressor — pngquant command based on libimagequant library
https://pngquant.org
Other
5.25k stars 486 forks source link

Read image from stdin and send result to stdout freeze when image size is less than 4000bytes #348

Open FilX opened 5 years ago

FilX commented 5 years ago

I try call windows binary from c#. I use stdin and stdout and everything is works except small images (size is less than 4000 bytes). When I use call with image path everything works fine.

Code example and image example.

`
var startInfo = new ProcessStartInfo() { CreateNoWindow = true, FileName = PngquantPath, Arguments = "256 --speed 3 --quality=45-85 --skip-if-larger -", RedirectStandardInput = true, RedirectStandardOutput = true, UseShellExecute = false, };

        using (Process process = Process.Start(startInfo))
        {
            using (var outputStream = new MemoryStream())
            {
                process.StandardInput.BaseStream.Write(image.Bytes, 0, image.Bytes.Length);
                process.WaitForExit(5000);
                var err = process.StandardError.ReadToEnd();
                await process.StandardOutput.BaseStream.CopyToAsync(outputStream);

                image.Bytes = outputStream.ToArray();
            }
        }

testx `

kornelski commented 5 years ago

When you use stdout & stderr you have to read both of them in parallel, while the program is still running. Anything else will deadlock, it's just a matter of luck.

You will get a deadlock when pngquant is waiting to send out more stdout output, and you wait for pngquant to finish sending stderr first, before you acknowledge you've got stdout.

FilX commented 5 years ago

Thank you for response,

The line var err = process.StandardError.ReadToEnd(); is just for test. When I don't use stderr then I got deadlock (freeze) as well.

Please, could you send me example, how to do it?

There is no problem with bigger images (4000 bytes +) but program freeze when I try smaller image :(

I created nasty workaround like … if(bytes < 4000) create temp file, call pngquant with file argument and read optimized file to bytearray ...

jibsen commented 5 years ago

One possible idea would be it (C# runtime or the OS) is buffering the input and not forwarding it until it has 4k. You could try flushing the input stream or closing it right after writing to it.

FilX commented 5 years ago

@jibsen Great !!!!!! thank you so much !!!

Flush resolved this issue.

await process.StandardInput.BaseStream.WriteAsync(image.Bytes, 0, image.Bytes.Length);

await process.StandardInput.BaseStream.FlushAsync();

kornelski commented 5 years ago

If you don't read stderr, you also risk deadlocks. When stderr buffer fills up, pngquant will be forced to wait for the buffer to be emptied by your process. If you never empty the buffer, pngquant may never finish.