I want to register a texture array created with OpenGL to CUDA. For that I simply use the interoperability function cudaGraphicsGLRegisterImage (see CUDA documentation) :
void registerTextureInCUDA()
{
// _textureDepth = 2 here
GLenum target = _textureDepth < 2 ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY;
GLuint texture = 0;
GLsizei width = 2;
GLsizei height = 2;
GLsizei layerCount = 2;
GLsizei mipLevelCount = 1;
// Read you texels here. In the current example, we have 2*2*2 = 8 texels, with each texel being 4 GLubytes.
GLubyte texels[32] =
{
// Texels for first image.
0, 0, 0, 255,
255, 0, 0, 255,
0, 255, 0, 255,
0, 0, 255, 255,
// Texels for second image.
255, 255, 255, 255,
255, 255, 0, 255,
0, 255, 255, 255,
255, 0, 255, 255,
};
glGenTextures(1,&texture);
glBindTexture(target,texture);
// No error after this call
GL_CHECK();
CUDA_CHECK(cudaGraphicsGLRegisterImage(&_pGraphicsResource, texture, target, cudaGraphicsRegisterFlagsWriteDiscard));
}
For simple GL_TEXTURE_2D I have no error and I can write into the texture normally, but with GL_TEXTURE_2D_ARRAY I have the following error :
Cuda error: 1 invalid argument
In CUDA documentation this type of return does not seem to be expected ?
What argument could be in cause here ?
I found the problem. I didn't allocate the storage for the texture, I just need to add this line before registering the texture in cuda :
glTexStorage3D(target, mipLevelCount, GL_RGBA8, width, height, layerCount);
I'm trying to render text using SDL. Obviously SDL does not support rendering text by itself, so I went with this approach:
load font file
raster glyphs in the font to a bitmap
pack all bitmaps in a large texture, forming a spritesheet of glyphs
render text as a sequence of glyph-sprites: copy rectangles from the texture to the target
First steps are handled using FreeType library. It can generate bitmaps for many kinds of fonts and provide a lot of extra info about the glyphs. FreeType-generated bitmaps are (by default) alpha channel only. For every glyph I basically get a 2D array of A values in range 0 - 255. For simplicity reasons the MCVE below needs only SDL, I already embedded FreeType-generated bitmap in the source code.
Now, the question is: how should I manage the texture that consists of such bitmaps?
What blending mode should I use?
What modulation should I use?
What should the texture be filled with? FreeType provides alpha channel only, SDL generally wants a texture of RGBA pixels. What values should I use for RGB?
How do I draw text in specific color? I don't want to make a separate texture for each color.
FreeType documentation says: For optimal rendering on a screen the bitmap should be used as an alpha channel in linear blending with gamma correction. SDL blending mode documentation doesn't list anything named linear blending so I used a custom one but I'm not sure if I got it right.
I'm not sure if I got some of SDL calls right as some of them are poorly documented (I already know that locking with empty rectangles crashes on Direct3D), especially how to copy data using SDL_LockTexture.
#include <string>
#include <stdexcept>
#include <SDL.h>
constexpr unsigned char pixels[] = {
0, 0, 0, 0, 0, 0, 0, 30, 33, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 169, 255, 155, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 83, 255, 255, 229, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 189, 233, 255, 255, 60, 0, 0, 0, 0, 0,
0, 0, 0, 0, 33, 254, 83, 250, 255, 148, 0, 0, 0, 0, 0,
0, 0, 0, 0, 129, 227, 2, 181, 255, 232, 3, 0, 0, 0, 0,
0, 0, 0, 2, 224, 138, 0, 94, 255, 255, 66, 0, 0, 0, 0,
0, 0, 0, 68, 255, 48, 0, 15, 248, 255, 153, 0, 0, 0, 0,
0, 0, 0, 166, 213, 0, 0, 0, 175, 255, 235, 4, 0, 0, 0,
0, 0, 16, 247, 122, 0, 0, 0, 88, 255, 255, 71, 0, 0, 0,
0, 0, 105, 255, 192, 171, 171, 171, 182, 255, 255, 159, 0, 0, 0,
0, 0, 203, 215, 123, 123, 123, 123, 123, 196, 255, 239, 6, 0, 0,
0, 44, 255, 108, 0, 0, 0, 0, 0, 75, 255, 255, 77, 0, 0,
0, 142, 252, 22, 0, 0, 0, 0, 0, 5, 238, 255, 164, 0, 0,
5, 234, 184, 0, 0, 0, 0, 0, 0, 0, 156, 255, 242, 8, 0,
81, 255, 95, 0, 0, 0, 0, 0, 0, 0, 68, 255, 255, 86, 0,
179, 249, 14, 0, 0, 0, 0, 0, 0, 0, 3, 245, 255, 195, 0
};
[[noreturn]] inline void throw_error(const char* desc, const char* sdl_err)
{
throw std::runtime_error(std::string(desc) + sdl_err);
}
void update_pixels(
SDL_Texture& texture,
const SDL_Rect& texture_rect,
const unsigned char* src_alpha,
int src_size_x,
int src_size_y)
{
void* pixels;
int pitch;
if (SDL_LockTexture(&texture, &texture_rect, &pixels, &pitch))
throw_error("could not lock texture: ", SDL_GetError());
auto pixel_buffer = reinterpret_cast<unsigned char*>(pixels);
for (int y = 0; y < src_size_y; ++y) {
for (int x = 0; x < src_size_x; ++x) {
// this assumes SDL_PIXELFORMAT_RGBA8888
unsigned char* const rgba = pixel_buffer + x * 4;
unsigned char& r = rgba[0];
unsigned char& g = rgba[1];
unsigned char& b = rgba[2];
unsigned char& a = rgba[3];
r = 0xff;
g = 0xff;
b = 0xff;
a = src_alpha[x];
}
src_alpha += src_size_y;
pixel_buffer += pitch;
}
SDL_UnlockTexture(&texture);
}
int main(int /* argc */, char* /* argv */[])
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
throw_error("could not init SDL: ", SDL_GetError());
SDL_Window* window = SDL_CreateWindow("Hello World",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
1024, 768,
SDL_WINDOW_RESIZABLE);
if (!window)
throw_error("could not create window: ", SDL_GetError());
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
if (!renderer)
throw_error("could not create renderer: ", SDL_GetError());
SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, 512, 512);
if (!texture)
throw_error("could not create texture: ", SDL_GetError());
SDL_SetTextureColorMod(texture, 255, 0, 0);
SDL_Rect src_rect;
src_rect.x = 0;
src_rect.y = 0;
src_rect.w = 15;
src_rect.h = 17;
update_pixels(*texture, src_rect, pixels, src_rect.w, src_rect.h);
/*
* FreeType documentation: For optimal rendering on a screen the bitmap should be used as
* an alpha channel in linear blending with gamma correction.
*
* The blending used is therefore:
* dstRGB = (srcRGB * srcA) + (dstRGB * (1 - srcA))
* dstA = (srcA * 0) + (dstA * 1) = dstA
*/
SDL_BlendMode blend_mode = SDL_ComposeCustomBlendMode(
SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, SDL_BLENDOPERATION_ADD,
SDL_BLENDFACTOR_ZERO, SDL_BLENDFACTOR_ONE, SDL_BLENDOPERATION_ADD);
if (SDL_SetTextureBlendMode(texture, blend_mode))
throw_error("could not set texture blending: ", SDL_GetError());
while (true) {
SDL_SetRenderDrawColor(renderer, 255, 255, 0, 255);
SDL_RenderClear(renderer);
SDL_Rect dst_rect;
dst_rect.x = 100;
dst_rect.y = 100;
dst_rect.w = src_rect.w;
dst_rect.h = src_rect.h;
SDL_RenderCopy(renderer, texture, &src_rect, &dst_rect);
SDL_RenderPresent(renderer);
SDL_Delay(16);
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_KEYUP:
switch (event.key.keysym.sym) {
case SDLK_ESCAPE:
return 0;
}
break;
case SDL_QUIT:
return 0;
}
}
}
return 0;
}
Expected result: red letter "A" on yellow background.
Actual result: malformed red lines inside black square on yellow background.
I suspect that lines are broken because there is a bug within pointer arithmetics inside update_pixels but I have no idea what's causing the black square.
First of all, part of this stuff is already done in SDL_ttf library. You could use it to rasterise glyphs to surfaces or generate multichar text surface.
Your src_alpha += src_size_y; is incorrect - you copy row by row, but skip by column length, not row length. It should be src_size_x. That results in incorrect offset on each row and only first row of your copied image is correct.
Your colour packing when writing to texture is backwards. See https://wiki.libsdl.org/SDL_PixelFormatEnum#order - Packed component order (high bit -> low bit): SDL_PACKEDORDER_RGBA, meaning R is packed at highest bits while A is at lowest. So, when representing it with unsigned char*, First byte is A and fourth is R:
unsigned char& r = rgba[3];
unsigned char& g = rgba[2];
unsigned char& b = rgba[1];
unsigned char& a = rgba[0];
You don't need custom blending, use SDL_BLENDMODE_BLEND, that is 'standard' "src-alpha, one-minus-src-alpha" formula everyone uses (note that it does not blend dst alpha channel itself, nor uses it to any extent; when blending, we only care about src alpha).
Finally one more approach to this: you could put your glyph luminance value (alpha, whatever it is called, the point is it only have one channel) and put it into every channel. That way you could do additive blending without using alpha at all, don't even need RGBA texture. Glyph colour could still be multiplied with colour mod. SDL_ttf implements just that.
I would like to load an image in sfml from a 2d array containing 3 values for each pixel (RGB). The array would look something like this:
{
{{255, 255, 255}, {255, 255, 255}},
{{255, 255, 255}, {255, 255, 255}}
}
The array above describes a 2x2 image which is white. How can I turn this into an Image in sfml (sf::Image)?
If you want to create an sf::Image object from a pixel array, then you are interested in the sf::Image::create() member function overload that takes a const Uint8 *:
void sf::Image::create(unsigned int width, unsigned int height, const Uint8 * pixels);
As the name suggests, the last parameter, pixels, corresponds to the array of pixels you want to create the sf::Image from. Note that this pixel array is assumed to be in the RGBA format (this contrasts with the RGB format suggested in the code of the question). That is, the array must hold four Uint8s for each pixel – i.e., a Uint8 for each component: red, green, blue and alpha.
As example, consider the following pixel array, pixels, made up of six pixels:
const unsigned numPixels = 6;
sf::Uint8 pixels[4 * numPixels] = {
0, 0, 0, 255, // black
255, 0, 0, 255, // red
0, 255, 0, 255, // green
0, 0, 255, 255, // blue
255, 255, 255, 255, // white
128, 128, 128, 255, // gray
};
Then, we can create an sf::Image object from the pixels array of pixels:
sf::Image image;
image.create(3, 2, pixels);
The pixels of the sf::Image created above will correspond to these:
This is a 3x2-pixel image, However, flipping the image's width and height arguments passed to the sf::Image::create() as done in:
sf::Image image;
image.create(2, 3, pixels);
This results in a 2x3-pixel image instead:
Note, however, that both sf::Image objects above are created from the same array of pixels, pixels, and they both are made up of six pixels – the pixels are just arranged differently because the images have different dimensions. Nevertheless, the pixels are the same: a black, a red, a green, a blue, a white and a gray pixel.
this is a bmp file header:
unsigned char header[54] = {
0x42, // identity : B
0x4d, // identity : M
0, 0, 0, 0, // file size
0, 0, // reserved1
0, 0, // reserved2
54, 0, 0, 0, // RGB data offset
40, 0, 0, 0, // struct BITMAPINFOHEADER size
0, 0, 0, 0, // bmp width
0, 0, 0, 0, // bmp height
1, 0, // planes
24, 0, // bit per pixel
0, 0, 0, 0, // compression
0, 0, 0, 0, // data size
0, 0, 0, 0, // h resolution
0, 0, 0, 0, // v resolution
0, 0, 0, 0, // used colors
0, 0, 0, 0 // important colors
};
Now, how can i access data in a .bmp file and store them to an array?
The bitmap header is a section in the file that holds information about the image and type of bitmap. If you want to read the individual pixels of a 24-bit bitmap file, you gather information about its size from the header, and then you use that to traverse the file and get your data.
From the header, you can clearly see that the pixel data starts at position 54 in the file. This is a 24-bit bitmap, so each pixel is represented by 3 bytes.
You also need to put padding into account. If the width of a bitmap is not divisible by 4, filler bytes are added to. +Correction: if the width * (bytes-per-pixel) is not divisible by 4.
If you really want to see how a bitmap is structured and to make it easier to visualize when you code this, get a hex editor and create small bitmap files and open them in the hex editor. You can see how the bytes are arranged and where the header begins and ends, among other information.