2

I want to get the result from stbir_resize_uint8 (from https://github.com/nothings/stb/blob/master/stb_image_resize.h) compress it to JPG and store that to a buffer without saving it to disk.

I already have the result from stbir_resize_uint8 as a unsigned char* but i don't know how to get a pointer to the data as JPG. Can this be done with stb_image_write.h or i need another library?

I tried using stbi_write_jpg_to_func from https://github.com/nothings/stb/blob/master/stb_image_write.h like this.

//Image.h

static unsigned char memoryJPG[32768];
static int thumbSize = 0;

struct ImageInfo
{
    unsigned char* data;
    int size;
};

class Image 
{
    public:
    unsigned char* writeToMemoryAsJPG(const int quality = 90);

    private:
    unsigned char* mImageData = nullptr;
    int mWidth;
    int mHeight;
    int mDepth;
};

//Image.cpp

ImageInfo Image::writeToMemoryAsJPG(const int quality)
{
    auto rv = stbi_write_jpg_to_func([](void *context, void *data, int size)
    {
        if (size > 32768) size = 32768;
        memcpy(memoryJPG, data, size);
        thumbSize = size;
    }, nullptr, mWidth, mHeight, mDepth, mImageData, quality);

    ImageInfo ii;
    ii.data = memoryJPG;
    ii.size = thumbSize;
    return ii;
}

This doesn't return anything, but even if it did returned a correct result the static variables are really ugly. How can i write this method without any static variables?

Dave
  • 93
  • 2
  • 9

2 Answers2

3

This is what i did but it is not a good solution because if compiler optimizations are enabled this may have memory alignment issues, so if anyone has a better solution please post an answer.

It turn out i was using the stbi_write_jpg_to_funcin the wrong way. Apparently the function gets invoked sequentially and each time it gets called retrieves a piece of data of variable length from the JPG. So each time the function gets called i just add that piece of data to a memory buffer. something like this:

Image.h

struct ImageData
{
    unsigned char data[32768];
    size_t size = 0;
};

class Image 
{
    public:
    unsigned char* writeToMemoryAsJPG(const int quality = 90);

    private:
    unsigned char* mImageData = nullptr;
    int mWidth;
    int mHeight;
    int mDepth;
};

Image.cpp

ImageData Image::writeToMemoryAsJPG(const int quality)
{
    ImageData ii;
    auto rv2 = stbi_write_jpg_to_func(
        [](void *context, void *data, int size)
        {
            memcpy(((ImageData*)context)->data + ((ImageData*)context)->size, data, size);
            ((ImageData*)context)->size += size;
        }
    ,&ii, mWidth, mHeight, mDepth, mImageData, quality);

    return ii;
}
Dave
  • 93
  • 2
  • 9
  • For a webassembly project, I would like to convert a png image to a jpeg image, entierly in memory. How can I do that exactly ? – rambi May 28 '21 at 16:07
1

I found a general solution in the issues of the github page of the stbi project

typedef struct {
    int last_pos;
    void *context;
} custom_stbi_mem_context;

// custom write function
static void custom_stbi_write_mem(void *context, void *data, int size) {
   custom_stbi_mem_context *c = (custom_stbi_mem_context*)context; 
   char *dst = (char *)c->context;
   char *src = (char *)data;
   int cur_pos = c->last_pos;
   for (int i = 0; i < size; i++) {
       dst[cur_pos++] = src[i];
   }
   c->last_pos = cur_pos;
}

// get image, width, height and channels

// how to use it:
custom_stbi_mem_context context;
context.last_pos = 0;
context.context = (void *)buffer;

int result = stbi_write_jpg_to_func(custom_stbi_write_mem, &context, width, height, channels, image, WS_JPEG_COMPRESSION);

If the issue is accepted, the function stbi_write_jpg_to_mem could be added in the header, check it out:

https://github.com/nothings/stb/issues/1132

I hope this can be usefull !

rambi
  • 1,013
  • 1
  • 7
  • 25