libvips/php-vips

Allow to specify length on Image::newFromMemory()

benconda opened this issue · 6 comments

Hello,

I got struggle with Image::newFromMemory(), because I can't specify the data length. It use the strlen function to get the length but for my need it doesn't return the right value. I'm actually need to do that to have it working properly (using directly FFI::vips()) :

        $pointer = FFI::vips()->vips_image_new_from_memory_copy(
            $pixels,
            $pageWidth * $pageHeight * 4,
            $pageWidth,
            $pageHeight,
            4,
            BandFormat::UCHAR
        );

        return new Image($pointer);

In this example the length is $width * $height * $bands, and not what is returned by strlen. Because Image constructor is internal, don't feel great for doing this 😅 This example works wells, the $pixels is a C style void * pointer from another C library using FFI aswell.

It could be great to have the possibility to specify the length (or compute it with $width * $length * $bands ?)

Hey @benconda,

I think the danger of having an extra length parameter is that it makes it so easy to crash the whole server. One tiny error and everything explodes :(

I guess count($pixels) > $width * $height * $bands * sizeof(pixel) in your case? I suppose that's safe. Maybe we could swap the == for >?

Also, I'm curious, how come length is wrong in your code? Could you resize the string as a workaround?

In my case, $pixels is already a FFI Cdata void * pointer.
I try to cast it in char * using \FFI::cast('char *', $pixels); but got this error, when passing the \FFI::string($chars) casted pixels to Image::newFromMemory() :

VipsImage: memory area too small --- should be 163310336 bytes, you passed 546838

So here strlen get 546838, instead of 163310336 bytes expected.

The image width is 1568 and height is : 26038
1568×26038×4 = 163310336

I finally get it working using the FFI::string second parameter to set the proper size

        $chars = \FFI::cast('char *', $pixels);

        return Image::newFromMemory(
            \FFI::string($chars, $pageWidth * $pageHeight * 4),
            $pageWidth,
            $pageHeight,
            4,
            BandFormat::UCHAR
        );

So i guess my issue is that FFI doesn't detect well the end of the string in memory, so I have to set the size explicitly in \FFI::string(). I think we can close this issue 👍

Yes, string lengths aren't stored in the string with PHP, you have to track them separately.

@jcupitt Just wanted to add that using the \FFI::string($chars, $pageWidth * $pageHeight * 4) will generate the whole bitmap in a string (so in php memory) and sometimes reach PHP memory limit on large picture. So I move back to my first example by setting the length manually, because this way I can reuse the existing pointer and avoid the not necessary conversion to php string.

Python and Ruby have refcounted memory areas, so you can share a block of RAM between two libraries and not be forced to make a copy. I'm not sure php-ffi has this, unfortunately :(

If you could make a complete, runnable program which shows the issue, it would help work out exactly what could be improved.