How are pixels arranged in SDL2? - c++

I have 2x2 image
Shouldn't the pixels be arranged like this?
1 2 // each number is a pixel
3 4
I'm having trouble accessing a pixel with x and y because
when x = 1 and y = 0 i get index 2 but prints the rgb values of pixel 4
so it's something like?
1 2 // each number is a pixel
4 3
Here's the code that I use
index = y + x * s->w;
c = s->format->palette->colors[index]; // c is an SDL_Color and s is an SDL_Surface*
I also use this for loop and still prints the same
for (Uint8 i = *(Uint8 *)s->pixels; i < s->w*s->h; i++) {
c = s->format->palette->colors[i];
printf("%u %u %u %u \n", i, c.r , c.g , c.b);
}
SDL_Surface struct definition from the SDL documentation
typedef struct SDL_Surface {
Uint32 flags; /* Read-only */
SDL_PixelFormat *format; /* Read-only */
int w, h; /* Read-only */
Uint16 pitch; /* Read-only */
void *pixels; /* Read-write */
/* clipping information */
SDL_Rect clip_rect; /* Read-only */
/* Reference count -- used when freeing surface */
int refcount; /* Read-mostly */
/* This structure also contains private fields not shown here */
} SDL_Surface;

Uint8 *pixel = (Uint8 *)s->pixels,*index;
index = &pixel[y + x * s->pitch];
c = s->format->palette->colors[*index];
Got it doing using #holyblackcat's suggestion.

Related

What is the srcData for ID2D1Bitmap::CreateBitmap

I need to create a bitmap from an array of pixels for a raycaster I'm working on in Direct2D. However, I'm having trouble understanding how to use the CreateBitmap function. Specifically, I'm not sure what the srcData parameter is supposed to be. I'm pretty sure/hoping it's a pointer to an array of pixels, but I'm not sure how to set up that array. What kind of array is it supposed to be? What data type? Etc.
Here's what I've tried:
int width = 400, height = 400;
D2D1::ColorF * arr = (D2D1::ColorF*)calloc(width * height * 4, sizeof(D2D1::ColorF));
for (int i = 0; i < width * height * 4; i++) { arr[i] = D2D1::ColorF(0.0f, 1.0f, 0.0f); }
// Create the bitmap and draw it on the screen
ID2D1Bitmap * bmp;
HRESULT hr;
hr = renderTarget->CreateBitmap(
D2D1::SizeU(width, height),
arr,
width * sizeof(int) * 4,
D2D1::BitmapProperties(),
&bmp);
if (hr != S_OK) { return; } // I've tested and found that hr does not equal S_OK
// Draw the bitmap...
What should the second and third lines look like? Is there anything else I'm doing incorrectly?
Syntax:
HRESULT CreateBitmap(
D2D1_SIZE_U size,
const void *srcData,
UINT32 pitch,
const D2D1_BITMAP_PROPERTIES & bitmapProperties,
ID2D1Bitmap **bitmap
);
Your code:
hr = renderTarget->CreateBitmap(
D2D1::SizeU(width, height),
arr, // <<--- Wrong, see (a) below
width * sizeof(int) * 4, // <<--- Close but wrong, see (b) below
D2D1::BitmapProperties(), // <<--- Wrong, see (c) below
&bmp);
(a) - you are supposed to provide an array of pixel data here, where the format depends on format of the bitmap. Note that this is optional an d you can create a bitmap without initialization. The pixels are not D2D1::ColorF exactly. They could be 4 byte RGBA data if you request respective bitmap format, see (c) below.
(b) - this is distance between rows in bytes, if your pixels are supposed to be 32-bit values you would normally want Width * 4 here
(c) - this requests DXGI_FORMAT_UNKNOWN D2D1_ALPHA_MODE_UNKNOWN and results in bitmap creation error. You need a real format here such as DXGI_FORMAT_B8G8R8A8_UNORM (see Pixel Formats and also Supported Pixel Formats and Alpha Modes)
The first link above shows how exactly bytes in memory map to pixel colors, and you are supposed to prepare your data respectively.
UPD
With DXGI_FORMAT_B8G8R8A8_UNORM your initialization structure is this:
UINT8* Data = malloc(Height * Width * 4);
for(UINT Y = 0; Y < Height; Y++)
for(UINT X = 0; X < Width; X++)
{
UINT8* PixelData = Data + ((Y * Width) + X) * 4;
PixelData[0] = unsigned integer blue in range 0..255;
PixelData[1] = unsigned integer red in range 0..255;
PixelData[2] = unsigned integer green in range 0..255;
PixelData[3] = 255;
}

ZLib PNG Compression with a bit depth of 1

I have a byte array where each byte correlates to a pixel value of my image. These byte values are either exactly 0 or 255. The array is packed from left to right and then top to bottom. From this array I want to create a grey-scale .PNG image with a bit depth of 1. I cannot use any wrappers to zlib.
The function works as it creates a valid png file. However the image created is not correct. I am fairly certain (although possibly wrong) the problem lies with the packing of data into a byte array that I pass to the Zlib deflate function. I have read through the spec: https://www.w3.org/TR/PNG/ to no avail.
I have the following code:
#include "zlib.h"
enum E_PNGImageType
{
eGreyScale = 0,
eTrueColour = 2,
eIndexedColour = 3,
eGreyScaleAlpha = 4,
eTrueColourAlpha = 6
};
enum E_PNGBitDepth
{
eOne = 1,
eTwo = 2,
eFour = 4,
eEight = 8,
eSixteen = 16
};
enum E_PNGCompressionMethod
{
eDeflate = 0
};
enum E_PNGFilterMethod
{
eAdaptive = 0
};
enum E_PNGInterlaceMethod
{
eNone = 0,
eAdam7 = 1
};
void CreatePNG(BYTE *pData, int iWidth, int iHeight)
{
/* Convert each byte to a bit and package the bits into a byte */
std::vector<BYTE> vBitData;
int bit = 0;
BYTE value = 0;
vBitData.clear();
for (int h = 0; h < iHeight; h++)
{
for (int w = 0; w < iWidth; w++)
{
if (pData[(h * iWidth) + w] > 0)
{
value += (1 << (7 - bit));
}
bit++;
if (bit == 8)
{
bit = 0;
vBitData.push_back(value);
value = 0;
}
}
}
if (bit > 0)
{
vBitData.push_back(value);
}
GeneratePNGData(vBitData.data(), iWidth, iHeight, vBitData.size(), &vPNGData);
}
void GeneratePNGData(BYTE *pData, unsigned int uiWidth, unsigned int uiHeight, unsigned int uiSize, std::vector<BYTE> *pPNGData)
{
int iCRCStartIndex;
int iSize;
unsigned int uiCRC;
z_stream strm;
const int C_BUFFER_SIZE = 20000;
unsigned char tempBuffer[C_BUFFER_SIZE];
int iLengthDataIndex;
int iRes;
pPNGData->clear();
/* PNG Signature */
pPNGData->push_back(137);
pPNGData->push_back(80);
pPNGData->push_back(78);
pPNGData->push_back(71);
pPNGData->push_back(13);
pPNGData->push_back(10);
pPNGData->push_back(26);
pPNGData->push_back(10);
/* IDHR Image Header */
/* 4 Bytes: Chunk Length */
pPNGData->push_back(0);
pPNGData->push_back(0);
pPNGData->push_back(0);
pPNGData->push_back(13);
/* Checksum Start Index */
iCRCStartIndex = pPNGData->size();
/* 4 Bytes: Chunk Type */
pPNGData->push_back(73);
pPNGData->push_back(72);
pPNGData->push_back(68);
pPNGData->push_back(82);
/* 4 Bytes: Chunk Data - Width */
pPNGData->push_back(((BYTE*)&uiWidth)[3]);
pPNGData->push_back(((BYTE*)&uiWidth)[2]);
pPNGData->push_back(((BYTE*)&uiWidth)[1]);
pPNGData->push_back(((BYTE*)&uiWidth)[0]);
/* 4 Bytes: Chunk Data - Height */
pPNGData->push_back(((BYTE*)&uiHeight)[3]);
pPNGData->push_back(((BYTE*)&uiHeight)[2]);
pPNGData->push_back(((BYTE*)&uiHeight)[1]);
pPNGData->push_back(((BYTE*)&uiHeight)[0]);
/* 1 Byte: Chunk Data - Bit Depth */
pPNGData->push_back(E_PNGBitDepth::eOne);
/* 1 Byte: Chunk Data - Colour Type */
pPNGData->push_back(E_PNGImageType::eGreyScale);
/* 1 Byte: Chunk Data - Compression Method */
pPNGData->push_back(E_PNGCompressionMethod::eDeflate);
/* 1 Byte: Chunk Data - Filter Method */
pPNGData->push_back(E_PNGFilterMethod::eAdaptive);
/* 1 Byte: Chunk Data - Interlace Method */
pPNGData->push_back(E_PNGInterlaceMethod::eNone);
/* Size of Data to Perform Checksum Over */
iSize = pPNGData->size() - iCRCStartIndex;
/* 4 Bytes: CRC */
uiCRC = AddPNGChecksumData(&pPNGData->data()[iCRCStartIndex], iSize);
pPNGData->push_back(((BYTE*)&uiCRC)[3]);
pPNGData->push_back(((BYTE*)&uiCRC)[2]);
pPNGData->push_back(((BYTE*)&uiCRC)[1]);
pPNGData->push_back(((BYTE*)&uiCRC)[0]);
/* IDAT Image Data */
/* Length Data Offset */
iLengthDataIndex = pPNGData->size();
/* 4 Bytes: Chunk Length */
pPNGData->push_back(0);
pPNGData->push_back(0);
pPNGData->push_back(0);
pPNGData->push_back(0);
/* Checksum Start Index */
iCRCStartIndex = pPNGData->size();
/* 4 Bytes: Chunk Type */
pPNGData->push_back(73);
pPNGData->push_back(68);
pPNGData->push_back(65);
pPNGData->push_back(84);
/* Length Bytes: Chunk Data - Compressed Image Data */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
deflateInit(&strm, Z_DEFLATED);
strm.avail_in = uiSize;
strm.next_in = pData;
strm.avail_out = C_BUFFER_SIZE;
strm.next_out = tempBuffer;
iRes = deflate(&strm, Z_FINISH);
if (iRes != Z_STREAM_END) MessageBox(NULL, "Error", "Error", 0);
pPNGData->insert(pPNGData->end(), tempBuffer, tempBuffer + strm.total_out);
deflateEnd(&strm);
/* Now Length Is Know Edit Length Field */
(*pPNGData)[iLengthDataIndex + 0] = ((BYTE*)&strm.total_out)[3];
(*pPNGData)[iLengthDataIndex + 1] = ((BYTE*)&strm.total_out)[2];
(*pPNGData)[iLengthDataIndex + 2] = ((BYTE*)&strm.total_out)[1];
(*pPNGData)[iLengthDataIndex + 3] = ((BYTE*)&strm.total_out)[0];
/* Size of Data to Perform Checksum Over */
iSize = pPNGData->size() - iCRCStartIndex;
/* 4 Bytes: CRC */
uiCRC = AddPNGChecksumData(&pPNGData->data()[iCRCStartIndex], iSize);
pPNGData->push_back(((BYTE*)&uiCRC)[3]);
pPNGData->push_back(((BYTE*)&uiCRC)[2]);
pPNGData->push_back(((BYTE*)&uiCRC)[1]);
pPNGData->push_back(((BYTE*)&uiCRC)[0]);
/* IEND Image trailer */
/* 4 Bytes: Chunk Length */
pPNGData->push_back(0);
pPNGData->push_back(0);
pPNGData->push_back(0);
pPNGData->push_back(0);
/* Checksum Start Index */
iCRCStartIndex = pPNGData->size();
/* 4 Bytes: Chunk Type */
pPNGData->push_back(73);
pPNGData->push_back(69);
pPNGData->push_back(78);
pPNGData->push_back(68);
/* Size of Data to Perform Checksum Over */
iSize = pPNGData->size() - iCRCStartIndex;
/* 4 Bytes: CRC */
uiCRC = AddPNGChecksumData(&pPNGData->data()[iCRCStartIndex], iSize);
pPNGData->push_back(((BYTE*)&uiCRC)[3]);
pPNGData->push_back(((BYTE*)&uiCRC)[2]);
pPNGData->push_back(((BYTE*)&uiCRC)[1]);
pPNGData->push_back(((BYTE*)&uiCRC)[0]);
/* Temp Debug Code */
FILE* pFile;
fopen_s(&pFile, "DEBUG_IMAGES\\zzz_test_output.png", "wb");
fwrite((*pPNGData).data(), 1, pPNGData->size(), pFile);
fclose(pFile);
}
You need to push an 8-bit zero at the beginning of each row (at the beginning of the loop on h in your code). That's the "filter type" byte, which in your case should be 0, meaning "no filtering".
The filter method "0" in IHDR simply says that the IDAT contains a filter byte at the beginning of each row. The option is there in IHDR to allow other methods, such as omitting those bytes and assuming a "0" filter type for each row. In the end, the PNG authors never approved any method other than the filter-byte-per-row method.
Also the check on bit>0 needs to be done at the end of the loop on h -- just move the closing bracket on the h-loop down below it, and reset value=0 there as well, so each row will be padded out to fill the final byte.

How to access every pixel?

If I have a PixelBuffer object of size (200 * 200 * 3) where each pixel has three consecutive spots for the RGB colors. How can I index them so that if i am trying to implement the DDA line drawing algorithm. I have seen a lot on the web that uses PutPixel(x,y) but im not sure how I can access the pixels in this method.
The pixels will be arranged row by row, with each pixel using 3 bytes. To address a point (x, y), you basically just need to multiply the y value by the size of a row (which is the width multiplied by 3), multiply the x value by the size of a pixel (3).
With a few constants for readability, the code for the function could look like this:
const int IMG_WIDTH = 200;
const int IMG_HEIGHT = 200;
const int BYTES_PER_PIXEL = 3;
const int BYTES_PER_ROW = IMG_WIDTH * BYTES_PER_PIXEL;
void PutPixel(uint8_t* pImgData, int x, int y, const uint8_t color[3])
{
uint8_t pPixel = pImgData + y * BYTES_PER_ROW + x * BYTES_PER_PIXEL;
for (int iByte = 0; iByte < BYTES_PER_PIXEL; ++iByte)
{
pPixel[iByte] = color[iByte];
}
}
Example how this function could be used:
// Allocate image data.
uint8_t* pImgData = new uint8_t[IMG_WIDTH * IMG_HEIGHT];
// Initialize image data, unless you are planning to set all pixels.
...
// Set pixel (50, 30) to yellow.
uint8_t yellow[3] = {255, 255, 0};
PutPixel(pImgData, 50, 30, yellow);
Once you have your image built in memory, you can store the content in a pixel buffer object using glBufferData():
GLuint bufId = 0;
glGenBuffers(1, &bufId);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, bufId);
glBufferData(GL_PIXEL_UNPACK_BUFFER, IMG_HEIGHT * BYTES_PER_ROW,
pImgData, GL_STATIC_DRAW);

How to set a pixel in a SDL_surface?

I need to use the following function from this page. The SDL_Surface structure is defined as
typedef struct SDL_Surface {
Uint32 flags; /* Read-only */
SDL_PixelFormat *format; /* Read-only */
int w, h; /* Read-only */
Uint16 pitch; /* Read-only */
void *pixels; /* Read-write */
SDL_Rect clip_rect; /* Read-only */
int refcount; /* Read-mostly */
} SDL_Surface;
The function is:
void set_pixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
Uint8 *target_pixel = (Uint8 *)surface->pixels + y * surface->pitch + x * 4;
*(Uint32 *)target_pixel = pixel;
}
Here I have few doubts, may be due to the lack of a real picture.
Why do we need to multiply surface->pitch by y, and x by 4?
What is the necessity of declaring target_pixel as an 8-bit integer pointer first, then casting it into a 32-bit integer pointer later?
How does target_pixel retain the pixel value after the set_pixel function return?
Since each pixel has size 4 (the surface is using Uint32-valued pixels), but the computation is being made in Uint8. The 4 is ugly, see below.
To make the address calculation be in bytes.
Since the pixel to be written really is 32-bit, the pointer must be 32-bit to make it a single write.
The calculation has to be in bytes since the surface's pitch field is in bytes.
Here's a (less aggressive than my initial attempt) re-write:
void set_pixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
{
Uint32 * const target_pixel = (Uint32 *) ((Uint8 *) surface->pixels
+ y * surface->pitch
+ x * surface->format->BytesPerPixel);
*target_pixel = pixel;
}
Note how we use surface->format->BytesPerPixel to factor out the 4. Magic constants are not a good idea. Also note that the above assumes that the surface really is using 32-bit pixels.
You can use the code below:
unsigned char* pixels = (unsigned char*)surface -> pixels;
pixels[4 * (y * surface -> w + x) + c] = 255;
x is the x of the point you want, y is the y of the point and c shows what information you want:
c=0 corresponds to blue
c=1 corresponds to green
c=2 corresponds to red
c=3 corresponds to alpha(opacity)

How to change RGB values in SDL surface?

In my application, once I load an image into an SDL_Surface object, I need to go through each RGB value in the image and replace it with another RGB value from a lookup function.
(rNew, gNew, bNew) = lookup(rCur, gCur, bCur);
It seems surface->pixels gets me the pixels. I would appreciate it if someone can explain to me how to obtain R, G, and B values from the pixel and replace it with the new RGB value.
Use built-in functions SDL_GetRGB and SDL_MapRGB
#include <stdint.h>
/*
...
*/
short int x = 200 ;
short int y = 350 ;
uint32_t pixel = *( ( uint32_t * )screen->pixels + y * screen->w + x ) ;
uint8_t r ;
uint8_t g ;
uint8_t b ;
SDL_GetRGB( pixel, screen->format , &r, &g, &b );
screen->format deals with the format so you don't have to.
You can also use SDL_Color instead of writing r,g,b variables separately.
Depending on the format of the surface, the pixels are arranged as an array in the buffer.
For typical 32 bit surfaces, it is R G B A R G B A
where each component is 8 bit, and every 4 are a pixel
First of all you need to lock the surface to safely access the data for modification. Now to manipulate the array you need to know the numbers of bit per pixels, and the alignment of the channels (A, R, G, B). As Photon said if is 32 bits per pixel the array can be RGBARGBA.... if it is 24 the array can be RGBRGB.... (can also be BGR, BGR, blue first)
//i assume the signature of lookup to be
int lookup(Uint8 r, Uint8 g, Uint8 b, Uint8 *rnew, Uint8* gnew, Uint8* bnew);
SDL_LockSurface( surface );
/* Surface is locked */
/* Direct pixel access on surface here */
Uint8 byteincrement = surface->format->BytesPerPixel;
int position;
for(position = 0; position < surface->w * surface->h* byteincrement; position += byteincrement )
{
Uint8* curpixeldata = (Uint8*)surface->data + position;
/* assuming RGB, you need to know the position of channels otherwise the code is overly complex. for instance, can be BGR */
Uint8* rdata = curpixeldata +1;
Uint8* gdata = curpixeldata +2;
Uint8* bdata = curpixeldata +3;
/* those pointers point to r, g, b, use it as you want */
lookup(*rdata, *gdata, *bdata, rdata,gdata,bdata);
}
.
SDL_LockSurface( surface );