# Spot the error.. need help converting a c libturbojpeg example

**URL:** <https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827>\
**Category:** General Usage\
**Created:** [December 31, 2019, 6:29pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827 "2019-12-31T18:29:48Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ianshmean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ianshmean/32/216042_2.png) [@ianshmean](https://discourse.julialang.org/u/ianshmean)\
**Post date:** [December 31, 2019, 6:29pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/1 "2019-12-31T18:29:48Z")

</div>

I’m trying to wrap this c example of libturbojpeg compression, but while `tjCompress2()` doesn’t error, its supposed to write into `jpegBuf` and set `jpegSize` to the length of `jpegBuf` but currently both are unchanged. It seems like `tjCompress2()` isn’t being passed the `srcBuf` properly, or `jpegBuf` isn’t initialized properly…

I’m following the docs here: [libjpeg.turbojpeg source code](http://jpeg-turbo.dpldocs.info/source/libjpeg.turbojpeg.d.html#L502)

C example (from [here](https://stackoverflow.com/a/49142321)):

```julia
#include "turbojpeg.h"
#include <iostream>
#include <string.h>
#include <errno.h>

using namespace std;

int main(void)
{
    unsigned char *srcBuf; //passed in as a param containing pixel data in RGB pixel interleaved format
    tjhandle handle = tjInitCompress();

    if(handle == NULL)
    {
        const char *err = (const char *) tjGetErrorStr();
        cerr << "TJ Error: " << err << " UNABLE TO INIT TJ Compressor Object\n";
        return -1;
    }
    int jpegQual =92;
    int width = 128;
    int height = 128;
    int nbands = 3;
    int flags = 0;
    unsigned char* jpegBuf = NULL;
    int pitch = width * nbands;
    int pixelFormat = TJPF_GRAY;
    int jpegSubsamp = TJSAMP_GRAY;
    if(nbands == 3)
    {
        pixelFormat = TJPF_RGB;
        jpegSubsamp = TJSAMP_411;
    }
    unsigned long jpegSize = 0;

    srcBuf = new unsigned char[width * height * nbands];
    for(int j = 0; j < height; j++)
    {
        for(int i = 0; i < width; i++)
        {
            srcBuf[(j * width + i) * nbands + 0] = (i) % 256;
            srcBuf[(j * width + i) * nbands + 1] = (j) % 256;
            srcBuf[(j * width + i) * nbands + 2] = (j + i) % 256;
        }
    }

    int tj_stat = tjCompress2( handle, srcBuf, width, pitch, height,
        pixelFormat, &(jpegBuf), &jpegSize, jpegSubsamp, jpegQual, flags);
    if(tj_stat != 0)
    {
        const char *err = (const char *) tjGetErrorStr();
        cerr << "TurboJPEG Error: " << err << " UNABLE TO COMPRESS JPEG IMAGE\n";
        tjDestroy(handle);
        handle = NULL;
        return -1;
    }

    FILE *file = fopen("out.jpg", "wb");
    if (!file) {
        cerr << "Could not open JPEG file: " << strerror(errno);
        return -1;
    }
    if (fwrite(jpegBuf, jpegSize, 1, file) < 1) {
        cerr << "Could not write JPEG file: " << strerror(errno);
        return -1;
    }
    fclose(file);

    //write out the compress date to the image file
    //cleanup
    int tjstat = tjDestroy(handle); //should deallocate data buffer
    handle = 0;
}

```

Julia attempt (with a simpler source image), using a Clang-generated wrapper ([src here](https://github.com/ianshmean/ImageIO.jl/blob/master/src/jpeg/example.jl)) :

```julia
using JpegTurbo_jll
libjpeg_wrap_dir = joinpath(@ __DIR__ , "..", "..", "gen", "libturbojpeg")
using CEnum
include(joinpath(libjpeg_wrap_dir, "ctypes.jl"))
export Ctm, Ctime_t, Cclock_t
include(joinpath(libjpeg_wrap_dir, "libturbojpeg_common.jl"))
include(joinpath(libjpeg_wrap_dir, "libturbojpeg_api.jl"))

using ColorTypes, ImageCore, FixedPointNumbers

img = rand(RGB{N0f8}, 10, 10)
rawimg = rawview(channelview(img))
srcBuf = collect(vec(rawimg))

nbands, width, height = size(rawimg)
pitch = Int(width * nbands)
pixelFormat = nbands == 3 ? TJPF_RGB : TJPF_GRAY
jpegSubsamp = nbands == 3 ? TJSAMP_411 : TJSAMP_GRAY
jpegQual = 92
flags = 0

handle = tjInitCompress()
handle == C_NULL && error("TurboJPEG Error: Unable to initialize compressor object: $(unsafe_string(tjGetErrorStr()))")

jpegBuf = Ptr{UInt8}(C_NULL)
jpegSize = Culong(0)

tj_stat = tjCompress2(handle, srcBuf, width, pitch, height, pixelFormat,
                    Ref(jpegBuf), Ref(jpegSize), jpegSubsamp, jpegQual, flags)

if tj_stat != 0
    err = tjGetErrorStr()
    tjDestroy(handle)
    handle = C_NULL
    error("TurboJPEG Error: $(unsafe_string(err))")
end
@show jpegSize

jpegBuf_j = unsafe_wrap(Array, jpegBuf, jpegSize; own = false)
open(joinpath(@ __DIR__ , "out.jpg"), "w") do io
   write(io, jpegBuf_j)
end

tjstat = tjDestroy(handle) #should deallocate data buffer
handle = nothing
tjFree(jpegBuf)

```

The `@show jpegSize` returns `0x0000000000000000`, indicating that `tjCompress2()` hasn’t errored, but that no data has been written to `jpegBuf`

---

<div class="post-metadata">

**Author:** ![ianshmean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ianshmean/32/216042_2.png) [@ianshmean](https://discourse.julialang.org/u/ianshmean)\
**Post date:** [December 31, 2019, 8:07pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/2 "2019-12-31T20:07:31Z")

</div>

Found a workaround by pre-allocating the memory to the maximum possible size, as per the 1st option in the docs [libjpeg.turbojpeg source code](http://jpeg-turbo.dpldocs.info/source/libjpeg.turbojpeg.d.html#L522)

```julia
    jpegSize = tjBufSize(width, height, jpegSubsamp)
    jpegBuf = tjAlloc(jpegSize)

```

It’s not ideal as it requires the allocation to be the max possible, which is actually larger than the source image size…

---

<div class="post-metadata">

**Author:** ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)\
**Post date:** [January 6, 2021, 8:10pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/3 "2021-01-06T20:10:09Z")

</div>

Hi Ian,

was this question part of the work on exposing libturbojpeg to julia?

---

<div class="post-metadata">

**Author:** ![ianshmean](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ianshmean/32/216042_2.png) [@ianshmean](https://discourse.julialang.org/u/ianshmean)\
**Post date:** [January 6, 2021, 11:04pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/4 "2021-01-06T23:04:56Z")

</div>

Hi! Indeed. It’s been a while, but I was trying to make a wrapper to add JPEG io functionality to what now is [ImageIO](https://github.com/JuliaIO/ImageIO.jl)

Here’s some of the work in progress from back then [https://github.com/ianshmean/ImageIODevelopment.jl](https://github.com/ianshmean/ImageIODevelopment.jl)

It’d be great to add JPEG to ImageIO if that’s what you’re working on!

---

<div class="post-metadata">

**Author:** ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)\
**Post date:** [January 9, 2021, 12:27pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/5 "2021-01-09T12:27:30Z")

</div>

Unfortunately, i do not have really a time. I still sometimes work on steganography, where an access to raw dct coefficients is a must. But I have too many things to work on. But it is good to know this exists, may be, one day i will have a bright student.

---

<div class="post-metadata">

**Author:** ![louie4825](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/louie4825/32/27359_2.png) [@louie4825](https://discourse.julialang.org/u/louie4825)\
**Post date:** [December 26, 2021, 8:57am UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/6 "2021-12-26T08:57:24Z")

</div>

I’m encountering the same problem here, also with auto-generated [Clang.jl](https://github.com/JuliaInterop/Clang.jl) wrappers using the [JpegTurbo\_jll](https://github.com/JuliaPackaging/Yggdrasil/blob/master/J/JpegTurbo/build_tarballs.jl) package.

I’ve tracked down where libjpeg-turbo allocates the buffer to this code in [jdatadst-tj.c](https://github.com/libjpeg-turbo/libjpeg-turbo/blob/main/jdatadst-tj.c):

```julia
if (*outbuffer == NULL || *outsize == 0) {
    if (alloc) {
      /* Allocate initial buffer */
      dest->newbuffer = *outbuffer = (unsigned char *)malloc(OUTPUT_BUF_SIZE);
      if (dest->newbuffer == NULL)
        ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
      *outsize = OUTPUT_BUF_SIZE;
    } else
      ERREXIT(cinfo, JERR_BUFFER_SIZE);
  }

```

Is something going wrong with the pointer assignment here? Is it because we replace the address referenced by the pointer, and that messes up Julia’s C interop?

I don’t know C very well, so please correct me if I’m wrong.

Any help would be appreciated here!

---

<div class="post-metadata">

**Author:** ![louie4825](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/louie4825/32/27359_2.png) [@louie4825](https://discourse.julialang.org/u/louie4825)\
**Post date:** [December 26, 2021, 3:13pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/7 "2021-12-26T15:13:33Z")

</div>

I actually found a solution! Sort of…

I found two workarounds, one using `Ref`s and another using Julia vectors:

```julia
include("TurboJpeg.jl") # Generated via Clang.jl
using Images

img = load("img_file.png")
# Convert to raw RGB pixel data
imgA = reshape(rawview(channelview(PermutedDimsArray(img, [2,1]))), *(size(img)..., 3)) |> collect

# Start interfacing with libjpeg-turbo
tjInstance = TurboJpeg.tjInitCompress() ; height, width = size(img)

# You have to specify Ref{Ptr{UInt8}} since the automatic type
# Ref{Ptr{Nothing}} fails to convert to Ptr{Ptr{UInt8}}
jpegBufPtr = Ref{Ptr{UInt8}}(C_NULL)
jpegSizePtr = Ref(Culong(0))

# Alternatively, you can also use Vectors:
# jpegBufPtr = [C_NULL]
# jpegSizePtr = [Culong(0)]

TurboJpeg.tjCompress2(tjInstance, imgA, width, width*3, height, TurboJpeg.TJPF_RGB, jpegBufPtr, jpegSizePtr, TurboJpeg.TJSAMP_420, 89, 0)

outbytes = unsafe_wrap(Vector{UInt8}, jpegBufPtr[], jpegSizePtr[])
# For the Vector version:
# outbytes = unsafe_wrap(Vector{UInt8}, jpegBufPtr[1], jpegSizePtr[1])

```

I’m not sure if this causes any memory leaks though since we aren’t calling `tjFree()` on the array once it’s passed back to Julia.

---

<div class="post-metadata">

**Author:** ![paulmelis](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/paulmelis/32/35063_2.png) [@paulmelis](https://discourse.julialang.org/u/paulmelis)\
**Post date:** [December 27, 2021, 12:37pm UTC](https://discourse.julialang.org/t/spot-the-error-need-help-converting-a-c-libturbojpeg-example/32827/8 "2021-12-27T12:37:17Z")

</div>

> [@ianshmean](#):
>
> The `@show jpegSize` returns `0x0000000000000000` , indicating that `tjCompress2()` hasn’t errored, but that no data has been written to `jpegBuf`

The use of `Ref` works slightly different (see e.g. [this section](https://docs.julialang.org/en/v1/manual/calling-c-and-fortran-code/#Passing-Pointers-for-Modifying-Inputs) in the docs), the comment above also uses this. Here’s an updated version of your example that works for me, I highlighted the changes:

```julia
using JpegTurbo_jll
libjpeg_wrap_dir = joinpath(@ __DIR__ , "..", "..", "gen", "libturbojpeg")
using CEnum
include(joinpath(libjpeg_wrap_dir, "ctypes.jl"))
export Ctm, Ctime_t, Cclock_t
include(joinpath(libjpeg_wrap_dir, "libturbojpeg_common.jl"))
include(joinpath(libjpeg_wrap_dir, "libturbojpeg_api.jl"))

using ColorTypes, ImageCore, FixedPointNumbers

img = rand(RGB{N0f8}, 10, 10)
rawimg = rawview(channelview(img))
srcBuf = collect(vec(rawimg))

nbands, width, height = size(rawimg)
pitch = Int(width * nbands)
pixelFormat = nbands == 3 ? TJPF_RGB : TJPF_GRAY
jpegSubsamp = nbands == 3 ? TJSAMP_411 : TJSAMP_GRAY
jpegQual = 92
flags = 0

handle = tjInitCompress()
handle == C_NULL && error("TurboJPEG Error: Unable to initialize compressor object: $(unsafe_string(tjGetErrorStr()))")

jpegBuf = Ptr{UInt8}(C_NULL)
jpegSize = Culong(0)

# Create the Ref wrappers, but make sure to save them
ptr_jpegBuf = Ref(jpegBuf)
ptr_jpegSize = Ref(jpegSize)

tj_stat = tjCompress2(handle, srcBuf, width, pitch, height, pixelFormat,
                    ptr_jpegBuf, ptr_jpegSize, jpegSubsamp, jpegQual, flags)

if tj_stat != 0
    err = tjGetErrorStr2()
    tjDestroy(handle)
    handle = C_NULL
    error("TurboJPEG Error: $(unsafe_string(err))")
end

# Get the actual value pointed to by the Ref, using the [] operator
@show ptr_jpegSize[]

# Same when retrieving the resulting image
jpegBuf_j = unsafe_wrap(Array, ptr_jpegBuf[], ptr_jpegSize[]; own = false)
open(joinpath(@ __DIR__ , "out.jpg"), "w") do io
   write(io, jpegBuf_j)
end

tjstat = tjDestroy(handle) #should deallocate data buffer
handle = nothing
tjFree(jpegBuf)

```

When running this I get:

```julia
melis@juggle 13:35:~/c/ImageIODevelopment.jl/src/jpeg$ j --project=../../ example_old.jl 
ptr_jpegSize[] = 0x000000000000036d

melis@juggle 13:35:~/c/ImageIODevelopment.jl/src/jpeg$ ls -l out.jpg 
-rw-r--r-- 1 melis users 877 Dec 27 13:35 out.jpg

```

Random 10x10 output image: ![image](https://global.discourse-cdn.com/julialang/original/3X/e/4/e422c836b4b470e4e4c4efd5909acfc2d608b878.png) 🙂
