Scan image - pixel format

I can’t see where I can read an image pixel by pixel?! Like for taking an image and having it slowly come apart.

If your image has been rendered, SDL_RenderReadPixels() will read individual pixels, but it’s slow. You will probably want to read the entire image to a buffer and then access the individual pixels there (still slow, but less so).

If your image hasn’t been rendered, then of course accessing its pixels will depend on how that image is stored, for example as a surface or a texture.

I would like a minimal example? With, like, what to cast void* to.

I’m not sure what pitch is for (at the end).

I get the error: Texture not created with SDL_TEXTUREACCESS_TARGET

How do I change texture target?

        SDL_SetRenderTarget(gRenderer, mImg.mImg);
        scope(exit)
            SDL_SetRenderTarget(gRenderer, null);
        
        SDL_RenderCopy(gRenderer, mImg.mImg, null, null);

        ubyte[] data;
        data.length = 100 * 100 * 4;
        if (! SDL_RenderReadPixels(gRenderer, null, SDL_PIXELFORMAT_RGBA8888, &data, 4)) {
            writeln("Read pixels failure: ", SDL_GetError().fromStringz);
            return;
        }

You show create mImg.mImg with SDL_TEXTUREACCESS_TARGET, if you want to call SDL_SetRenderTarget on it.

But I used

SDL_CreateTextureFromSurface

I understand that you did, but the texture access will be set to SDL_TEXTUREACCESS_STATIC. You won’t be able to set it as a render target.

I don’t see how to load an image and use it with SDL_RenderReadPixels.

What I said in my original reply was “if your image has been rendered, SDL_RenderReadPixels will read individual pixels”. If you are wanting to read the pixels without rendering you can’t read them from the texture, but you could read them from the surface from which the texture was created (a surface’s bitmap is directly accessible via its pixels member).

I got it working, yay. Slow as a dog on a chain pulling an elephant! But opens up more possibilities.

        auto path = "fireSmall.png";
        SDL_Surface* suf = IMG_Load( path.toStringz );
        if( suf is null )
        {
            writef( "Unable to load image %s! SDL_image Error: %s\n", path, IMG_GetError() );
        }
        else {
            scope(exit)
                SDL_FreeSurface(suf);
            int w,h;
            w = suf.w;
            h = suf.h;
            int c;
            import std.range : iota;
            auto data = cast(ubyte*)suf.pixels;
            mBats.length = w * h;
            foreach(y; 0 .. h) {
                foreach(x; 0 .. w) {
                    mBats[c] = Bat(gStartPosX + x * gGraphW, gStartPosY + y * gGraphH,1,-1,
                        data[c * 4], data[c * 4 + 1], data[c * 4 + 2]);
                    c += 1;
                }
            }

Great. Just a word of caution though: you shouldn’t assume that the rows of pixels are consecutive in memory. In some pixel formats each row may be ‘padded’ at the end to ensure the next row is aligned; that’s what the surface->pitch member is for. It’s very unlikely to be an issue when each pixel occupies 4 bytes, as in your case, but in general you should be aware of it.

This better?

mBats[c] = Bat(gStartPosX + x * gGraphW, gStartPosY + y * gGraphH,1,-1,
    data[c * suf.pitch], data[c * suf.pitch + 1], data[c * suf.pitch + 2]);

Oh, that didn’t work!

This works though - better? (better than a magic number).

            SDL_PixelFormat* fmt = suf.format;
            int bytesPerPixel = fmt.BytesPerPixel;
            foreach(y; 0 .. h) {
                foreach(x; 0 .. w) {
                    mBats[c] = Bat(gStartPosX + x * gGraphW, gStartPosY + y * gGraphH,1,-1,
                        data[c * bytesPerPixel], data[c * bytesPerPixel + 1], data[c * bytesPerPixel + 2]);

It’s funny - I’m interrupting a video on code (in maths), to do code.

I still don’t see the surface->pitch member being used. I would expect the offset into the bitmap to be something like (y * suf->pitch + x * bytesPerPixel).

Ok, I think this is good - thanks.

auto data = cast(ubyte*)suf.pixels;
mBats.length = suf.w * suf.h;
immutable bytesPerPixel = suf.format.BytesPerPixel;
foreach(y; 0 .. suf.h) {
    immutable line = y * suf.pitch;
    foreach(x; 0 .. suf.w) {
        immutable xpos = x * bytesPerPixel;
        mBats[y * suf.w + x] = Bat(gStartPosX + x * gGraphW, gStartPosY + y * gGraphH,1,-1,
            data[line + xpos],
            data[line + xpos + 1],
            data[line + xpos + 2]);
    }
}

With SDL_RenderReadPixels() what do I put for pitch with SDL_Texture?