Search code examples
cvisual-studio-2015libjpeg-turboturbojpeg

TurboJPEG API from libjpeg-turbo-1.5.1-vc.exe produces wrong output


I wrote a test-program in C in Visual Studio 2015 for testing libjpeg-turbo TurboJPEG-API.

In this test-program I generate an image of 800x800 RGB pixels and write this to disk. When I use TJSAMP_GRAY at the tjCompress2 function the produced image on the disk looks good:

TJSAMP_GRAY output of TurboJPEG API from libjpeg-turbo version 1.5.1.

When I use TJSAMP_444 instead the image looks weired:

TJSAMP_444 output of TurboJPEG API from libjpeg-turbo version 1.5.1.

However, both images can't be opened in Adobe Photoshop but in MS-Paint, Chrome or Internet Explorer. All other Options than TJSAMP_GRAY result in a weired image, also a quality setting of 100 with TJSAMP_GRAY results in weired output. (Images in this post are copied via screenshot and saved as png for smaller data.) I also wrote a C#-Program using the turbojpeg.dll library via P-Invoke. This program is producing valid output.

Question: Where is my fault?

The source-code in question:

#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "turbojpeg.h"

unsigned char buffer[800 * 800 * 3];

int main(int argc, char** argv)
{
    unsigned long size = 0;

    unsigned char* compressedImage = NULL;

    for (int x = 0; x < 800; x++)
        for (int y = 0; y < 800; y++)
        {
            switch ((x / 80) % 3)
            {
            case 0:
                buffer[(y * 800 + x) * 3] = 255;
                buffer[(y * 800 + x) * 3 + 1] = 0;
                buffer[(y * 800 + x) * 3 + 2] = 0;
                break;
            case 1:
                buffer[(y * 800 + x) * 3] = 0;
                buffer[(y * 800 + x) * 3 + 1] = 255;
                buffer[(y * 800 + x) * 3 + 2] = 0;
                break;
            case 2:
                buffer[(y * 800 + x) * 3] = 0;
                buffer[(y * 800 + x) * 3 + 1] = 0;
                buffer[(y * 800 + x) * 3 + 2] = 255;
                break;
            }
        }

    tjhandle compressor = tjInitCompress();

    compressedImage = tjAlloc(1024 * 1024 * 4);

    size = 1024 * 1024 * 4;

    tjCompress2(compressor, buffer, 800, 0, 800, TJPF_RGB, &compressedImage, &size, TJSAMP_444, 80, TJFLAG_NOREALLOC | TJFLAG_ACCURATEDCT);

    tjDestroy(compressor);

    int handle = _wopen(L"D:\\file.jpeg", _O_CREAT | _O_WRONLY | _O_TRUNC, _S_IREAD | _S_IWRITE);

    printf("LENGTH=%ld\n", size);

    if (_write(handle, compressedImage, size) != size)
        printf("Write Error.\n");

    _close(handle);

    tjFree(compressedImage);

    return 0;
}

Just to be complete, the test C# source-code, which works fine:

class Program
{
    [DllImport("turbojpeg", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr tjInitCompress();

    [DllImport("turbojpeg", CallingConvention = CallingConvention.Cdecl)]
    private static extern IntPtr tjAlloc(int bytes);

    [DllImport("turbojpeg", CallingConvention = CallingConvention.Cdecl)]
    private static extern int tjCompress2(IntPtr handle, IntPtr srcBuf, int width, int pitch, int height, int pixelFormat, ref IntPtr jpegBuf, ref ulong jpegSize, int jpegSubsamp, int jpegQual, int flags);

    [DllImport("turbojpeg", CallingConvention = CallingConvention.Cdecl)]
    private static extern int tjDestroy(IntPtr handle);

    [DllImport("turbojpeg", CallingConvention = CallingConvention.Cdecl)]
    private static extern void tjFree(IntPtr buffer);

    static void Main(string[] args)
    {
        ulong size = 1024 * 1024 * 4;

        byte[] buffer = new byte[800 * 800 * 3];

        for (int x = 0; x < 800; x++)
            for (int y = 0; y < 800; y++)
            {
                switch ((x / 80) % 3)
                {
                    case 0:
                        buffer[(y * 800 + x) * 3] = 255;
                        buffer[(y * 800 + x) * 3 + 1] = 0;
                        buffer[(y * 800 + x) * 3 + 2] = 0;
                        break;
                    case 1:
                        buffer[(y * 800 + x) * 3] = 0;
                        buffer[(y * 800 + x) * 3 + 1] = 255;
                        buffer[(y * 800 + x) * 3 + 2] = 0;
                        break;
                    case 2:
                        buffer[(y * 800 + x) * 3] = 0;
                        buffer[(y * 800 + x) * 3 + 1] = 0;
                        buffer[(y * 800 + x) * 3 + 2] = 255;
                        break;
                }
            }

        IntPtr umBuffer = Marshal.AllocHGlobal(800 * 800 * 3);
        Marshal.Copy(buffer, 0, umBuffer, 800 * 800 * 3);

        IntPtr compressor = tjInitCompress();

        IntPtr compressedImage = tjAlloc(1024 * 1024 * 4);

        tjCompress2(compressor, umBuffer, 800, 0, 800, 0, ref compressedImage, ref size, 0, 80, 5 * 1024);

        byte[] mCPData = new byte[size];

        Marshal.Copy(compressedImage, mCPData, 0, (int)size);

        System.IO.File.WriteAllBytes("D:\\managed.jpeg", mCPData);

        tjFree(compressedImage);

        Marshal.FreeHGlobal(umBuffer);
    }
}

Solution

  • The _wopen-Line is missing _O_BINARY. So, there's no error in the project-configuration or libjpeg-turbo TurboJPEG-API. It was just, that the output written to disc was interpreted as encoded text and therefore was modified by the _write-function or some underlaying instance.

    The correct source-code would be:

    #include <stdio.h>
    #include <io.h>
    #include <fcntl.h>
    #include <sys/stat.h>
    #include "turbojpeg.h"
    
    unsigned char buffer[800 * 800 * 3];
    
    int main(int argc, char** argv)
    {
        unsigned long size = 0;
    
        unsigned char* compressedImage = NULL;
    
        for (int x = 0; x < 800; x++)
            for (int y = 0; y < 800; y++)
            {
                switch ((x / 80) % 3)
                {
                case 0:
                    buffer[(y * 800 + x) * 3] = 255;
                    buffer[(y * 800 + x) * 3 + 1] = 0;
                    buffer[(y * 800 + x) * 3 + 2] = 0;
                    break;
                case 1:
                    buffer[(y * 800 + x) * 3] = 0;
                    buffer[(y * 800 + x) * 3 + 1] = 255;
                    buffer[(y * 800 + x) * 3 + 2] = 0;
                    break;
                case 2:
                    buffer[(y * 800 + x) * 3] = 0;
                    buffer[(y * 800 + x) * 3 + 1] = 0;
                    buffer[(y * 800 + x) * 3 + 2] = 255;
                    break;
                }
            }
    
        tjhandle compressor = tjInitCompress();
    
        compressedImage = tjAlloc(1024 * 1024 * 4);
    
        size = 1024 * 1024 * 4;
    
        tjCompress2(compressor, buffer, 800, 0, 800, TJPF_RGB, &compressedImage, &size, TJSAMP_444, 80, TJFLAG_NOREALLOC | TJFLAG_ACCURATEDCT);
    
        tjDestroy(compressor);
    
        int handle = _wopen(L"D:\\file.jpeg", _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IREAD | _S_IWRITE);
    
        printf("LENGTH=%ld\n", size);
    
        if (_write(handle, compressedImage, size) != size)
            printf("Write Error.\n");
    
        _close(handle);
    
        tjFree(compressedImage);
    
        return 0;
    }