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;
}
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;
}