c++formatpixelsdl-2raw

SDL2 make pixel array into texture


I trying to render a special image which is just a bunch of pixels with no format. It is a sort of a raw image with 42x 42 pixels. The images are palettes so I am curious how I should handle that situation too.

How can I convert an array of pixels to a texture in SDL2?

Do I apply palettes in the end?

More details: Currently I am opening the image and adding some transparent (black bytes) and storing this into a char array. These pixels I will need to render.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <SDL.h>
#include <SDL_image.h>
#include <stdio.h>
#include <windows.h>
#include <vector>

#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 480


FILE* OpenCelFile(const char * filepath) {
    
    FILE* ptr = fopen(filepath, "rb");
    return ptr;
}

DWORD FrameCount[1]    = { 0 };
DWORD DataStart[1]     = { 0 };
DWORD dummy[1] = { 0 };
signed char  CommandByte[1]   = { 0 };


void SkipToData(FILE* pFile, DWORD* dataStart, int bytesRead)
{
    int MoveForwardBytes = *dataStart - bytesRead;
    fseek(pFile, MoveForwardBytes, SEEK_CUR);

}

// There is more to this , but it isn't 100% important.
void ReadCelHeader(FILE* pFile)
{
    fread((void*)FrameCount, 4, 1, pFile);
    fread((void*)DataStart,  4, 1, pFile);
    SkipToData(pFile, DataStart, 8);
}

void ReadCommandByte(FILE* pFile)
{
    fread((void*)CommandByte, 1, 1, pFile);
}

std::vector<char> backBuffer;


int CreateRawImageBuffer(FILE* pFile) {
    
    ReadCommandByte(pFile);
    int bytesRead = 0;


    // handel transparent bytes;
    if (*(BYTE*)CommandByte >= 0x80) {
        // this is a negative byte
        signed int skipBytes = *(BYTE*)CommandByte - 256;
        // convert it to positive number.
        skipBytes = abs(skipBytes);
        for (skipBytes; skipBytes != NULL; skipBytes--) {
            backBuffer.push_back(0x00);
            bytesRead++;
        }
    }

    // set real pixels
    if (*(BYTE*)CommandByte < 0x80) {
        signed int byteCount = *(BYTE*)CommandByte;
        for (byteCount; byteCount != NULL; byteCount--) {
            
            BYTE t_char[1] = {0x00};
            fread((void*)t_char, 1, 1, pFile);
            backBuffer.push_back(*t_char);
            bytesRead++;
        }
    }
    return bytesRead;
}


int main(int argc, char* args[]) {

    SDL_Window* window = NULL;
    SDL_Surface* screenSurface = NULL;
    bool RunningMainGameLoop = true;

    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
        fprintf(stderr, "could not initialize sdl2: %s\n", SDL_GetError());
        return 1;
    }
    window = SDL_CreateWindow("Renderunkimage", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN );

    if (window == NULL) {
        fprintf(stderr, "could not create window: %s\n", SDL_GetError());
        return 1;
    }


    FILE* ptr = OpenCelFile("C:\\Users\\luzer\\source\\repos\\unkimages\\unkimage.cel");
    ReadCelHeader(ptr);
    int TotalFramePixels = 48 * 48;
    int fc = *FrameCount;
    int pixelsAdded = { 0 };

    for (fc; fc != 0; fc--) {

        for (TotalFramePixels; TotalFramePixels != NULL; TotalFramePixels = TotalFramePixels - pixelsAdded) {
            pixelsAdded = CreateRawImageBuffer(ptr);
        }
    }

    
    screenSurface = SDL_GetWindowSurface(window);
    SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0x00, 0x00, 0x00));
    SDL_UpdateWindowSurface(window);

    while (RunningMainGameLoop)
    {
        SDL_Event event;

        while (SDL_PollEvent(&event)) {
            switch (event.type) {
            case SDL_QUIT:
                RunningMainGameLoop = false;
                break;
            }
        }

        // handle renders...


    }

    
    SDL_DestroyWindow(window);
    SDL_Quit();
    return 0;
}

Solution

  • You need to use SDL_Surface first using the following:

    SDL_CreateRGBSurfaceFrom(data, IMAGE_WIDTH, IMAGE_HEIGHT, 8, IMAGE_WIDTH /*pitch*/, 0, 0, 0, 0);
    

    The images I am loading are really just single byte per colour and then I have to apply a palette to them. there is a sort of RLE encoding or compression which is why I have to load them , otherwise you just load them and pass them to a surface.

    here is the whole code for anyone interested.

    #define _CRT_SECURE_NO_WARNINGS
    #include <iostream>
    #include <SDL.h>
    #include <SDL_image.h>
    #include <stdio.h>
    #include <windows.h>
    #include <vector>
    
    #include <fstream>
    #include <iterator>
    #include <algorithm>
    #include <array>
    
    #define SCREEN_WIDTH 200
    #define SCREEN_HEIGHT 200
    #define TICK_INTERVAL 45
    
    static Uint32 next_time;
    SDL_Color palData[256];
    DWORD FrameCount[1] = { 0 };
    DWORD DataStart[1] = { 0 };
    DWORD dummy[1] = { 0 };
    signed char  CommandByte[1] = { 0 };
    int TotalFramePixels = 48 * 48;
    int IMAGE_WIDTH = 48;
    int IMAGE_HEIGHT = 48;
    
    struct Color {
        uint8_t  r;
        uint8_t  g;
        uint8_t  b;
    };
    
    struct celFrame {
        byte ImageData[2304]; //fixed size of image. 48*48
    
    };
    
    FILE* OpenCelFile(const char * filepath) {
        
        FILE* ptr = fopen(filepath, "rb");
        return ptr;
    }
    
    void LoadPalette(const char* pszFileName)
    {
        FILE* PalFile = fopen(pszFileName, "rb");
    
        for (int idx = 0; idx < 255; idx++) {
            fread(&palData[idx].r, 1, 1, PalFile);
            fread(&palData[idx].g, 1, 1, PalFile);
            fread(&palData[idx].b, 1, 1, PalFile);
        }
    }
    
    void SkipToData(FILE* pFile, DWORD* dataStart, int bytesRead)
    {
        int MoveForwardBytes = *dataStart - bytesRead;
        fseek(pFile, MoveForwardBytes, SEEK_CUR);
    
    }
    
    // There is more to this , but it isn't 100% important.
    void ReadCelHeader(FILE* pFile)
    {
        fread((void*)FrameCount, 4, 1, pFile);
        fread((void*)DataStart,  4, 1, pFile);
        SkipToData(pFile, DataStart, 8);
    }
    
    void ReadCommandByte(FILE* pFile)
    {
        fread((void*)CommandByte, 1, 1, pFile);
    }
    
    int CreateRawImageBuffer(FILE* pFile, struct celFrame* s_celFrame, int bytesRead) {
        
        ReadCommandByte(pFile);
    
        // handel transparent bytes;
        if (*(BYTE*)CommandByte >= 0x80) {
            // this is a negative byte
            signed int skipBytes = *(BYTE*)CommandByte - 256;
            // convert it to positive number.
            skipBytes = abs(skipBytes);
            for (skipBytes; skipBytes != NULL; skipBytes--) {
                s_celFrame->ImageData[bytesRead] = 0xff;
                bytesRead++;
            }
        }
    
        // set real pixels
        if (*(BYTE*)CommandByte < 0x80) {
            signed int byteCount = *(BYTE*)CommandByte;
            for (byteCount; byteCount != NULL; byteCount--) {
                
                BYTE t_char[1] = {0xff};
                fread((void*)t_char, 1, 1, pFile);
                s_celFrame->ImageData[bytesRead] = *t_char;
                bytesRead++;
            }
        }
    
        if (bytesRead >= TotalFramePixels) {
            return bytesRead;
        }
    
        CreateRawImageBuffer(pFile, s_celFrame, bytesRead);
        
    }
    
    Uint32 time_left(void)
    {
        Uint32 now;
    
        now = SDL_GetTicks();
        if (next_time <= now)
            return 0;
        else
            return next_time - now;
    }
    
    
    
    int main(int argc, char* args[]) {
    
        SDL_Window* window = NULL;
        SDL_Surface* screenSurface = NULL;
        SDL_Renderer* renderer = NULL;
        SDL_Surface* PentagramSurface[8] = {0};
        SDL_Texture* pentagramTexture[8] = { 0 };
        int frameCount = 0;
    
        bool RunningMainGameLoop = true;
    
        if (SDL_Init(SDL_INIT_VIDEO) < 0) {
            fprintf(stderr, "could not initialize sdl2: %s\n", SDL_GetError());
            return 1;
        }
        window = SDL_CreateWindow("RenderCEL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN  );
    
        if (window == nullptr) {
            fprintf(stderr, "could not create window: %s\n", SDL_GetError());
            return 1;
        }
    
        renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
        if (renderer == nullptr) {
            std::cout << "SDL_CreateRenderer Error: " << SDL_GetError() << std::endl;
            return 1;
        }
    
    
        FILE* pfile = OpenCelFile("C:\\Users\\luzer\\source\\repos\\RenderCEL\\PentSpin.cel");
        LoadPalette("C:\\Users\\luzer\\source\\luzer\\RenderCEL\\cut2.pal");
        ReadCelHeader(pfile);
    
    
        int fc = *FrameCount;
        int pixelsAdded = { 0 };
        celFrame s_celFrame[8] = {0};
    
        // Create individual frames of each image
        // by storing them in structs
        for (int fidx = 0; fidx != fc; fidx++) {
            int bytes_read = CreateRawImageBuffer(pfile, &s_celFrame[fidx], 0);
    
            PentagramSurface[fidx] = SDL_CreateRGBSurfaceFrom(&s_celFrame[fidx], IMAGE_WIDTH, IMAGE_HEIGHT, 8, IMAGE_WIDTH /*pitch*/, 0, 0, 0, 0);
            SDL_SetPaletteColors(PentagramSurface[fidx]->format->palette, palData, 0, 256);
            pentagramTexture[fidx] = SDL_CreateTextureFromSurface(renderer, PentagramSurface[fidx]);
            SDL_FreeSurface(PentagramSurface[fidx]);
        }
    
        
        screenSurface = SDL_GetWindowSurface(window);
        SDL_FillRect(screenSurface, NULL, SDL_MapRGB(screenSurface->format, 0x00, 0x00, 0x00));
        
    
        while (RunningMainGameLoop)
        {
            next_time = SDL_GetTicks() + TICK_INTERVAL;
            SDL_Event event;
    
            while (SDL_PollEvent(&event)) {
                switch (event.type) {
                case SDL_QUIT:
                    RunningMainGameLoop = false;
                    break;
                }
            }
    
            // handle renders...
            SDL_Rect drect = {0,0,48,48};
            SDL_RenderClear(renderer);
            SDL_RenderCopyEx(renderer, pentagramTexture[frameCount], NULL, NULL, 0, NULL, SDL_FLIP_VERTICAL);
            SDL_RenderPresent(renderer);
    
            SDL_Delay(time_left());
    
            next_time += TICK_INTERVAL;
            
            frameCount++;
            if (frameCount == 8) { frameCount = 0; }
    
        }
    
        for (int idx = 0; idx < 8; idx++) {
            SDL_DestroyTexture(pentagramTexture[idx]);
        }
        
        SDL_DestroyRenderer(renderer);
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 0;
    }