I'm taking my first steps in C, and was trying to make a gradient color function, that draws a bunch of rectangles to the screen (vertically).
This is the code so far:
void draw_gradient(uint32_t start_color, uint32_t end_color) {
int steps = 8;
int draw_height = window_height / 8;
//Change this value inside the loop to write different color
uint32_t loop_color = start_color;
for (int i = 0; i < steps; i++) {
draw_rect(0, i * draw_height, window_width, draw_height, loop_color);
}
}
Ignoring the end_color for now, I want to try and pass a simple red color in like 0xFFFF0000 (ARGB)..and then take the red 'FF' and convert it to an integer or decrease it using the loop_color variable.
I'm not sure how to go get the red value from the hexcode and then minipulate it as a number and then write it back to hex..any ideas?
So in 8 steps the code should for example go in hex from FF to 00 or as integer from 255 to 0.
As you have said, your color is in RGB format. This calculation assumes vertical gradient - meaning from top to the bottom (linear lines).
Steps to do are:
Get number of lines to draw; this is your rectangle height
Get A, R, G, B color components from your start and end colors
uint8_t start_a = start_color >> 24;
uint8_t start_r = start_color >> 16;
uint8_t start_g = start_color >> 8;
uint8_t start_b = start_color >> 0;
uint8_t end_a = end_color >> 24;
uint8_t end_r = end_color >> 16;
uint8_t end_g = end_color >> 8;
uint8_t end_b = end_color >> 0;
Calculate step for each of the components
float step_a = (float)(end_a - start_a) / (float)height;
float step_r = (float)(end_r - start_r) / (float)height;
float step_g = (float)(end_g - start_g) / (float)height;
float step_b = (float)(end_b - start_b) / (float)height;
Run for loop and apply different step for each color
for (int i = 0; i < height; ++i) {
uint32_t color = 0 |
((start_a + i * step_a) & 0xFF) << 24 |
((start_r + i * step_r) & 0xFF) << 16 |
((start_g + i * step_g) & 0xFF) << 8 |
((start_b + i * step_b) & 0xFF) << 0
draw_horizontal_line(i, color);
}
It is better to use float for step_x and multiply/add on each iteration. Otherwise with integer rounding, you may never increase number as it will always get rounded down.
Related
I have created with GIMP a C-Source image dump like the following:
/* GIMP RGBA C-Source image dump (example.c) */
static const struct {
guint width;
guint height;
guint bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */
guint8 pixel_data[304 * 98 * 2 + 1];
} example= {
304, 98, 2,
"\206\061\206\061..... }
Is there a way to convert this image from RG565 to RGB888?
I mean , I have found a way to covert pixel by pixel:
for (i = 0; i < w * h; i++)
{
uint16_t color = *RGB565p++;
uint8_t r = ((color >> 11) & 0x1F);
uint8_t g = ((color >> 5) & 0x3F);
uint8_t b = (color & 0x1F);
r = ((((color >> 11) & 0x1F) * 527) + 23) >> 6;
g = ((((color >> 5) & 0x3F) * 259) + 33) >> 6;
b = (((color & 0x1F) * 527) + 23) >> 6;
uint32_t RGB888 = r << 16 | g << 8 | b;
printf("%d \n", RGB888);
}
the problem is that using this logic I get numbers that are not represented as the one used n the original image:
P3
304 98
255
3223857
3223857
3223857
3223857
3223857
3223857
3223857
3223857
Did I miss something?
EDIT: here you can find the original image:
https://drive.google.com/file/d/1YBphg5_V6M2FA3HWcaFZT4fHqD6yeEOl/view
There are two things you need to do to create a C file similar to the original.
Increase the size of the pixel buffer, because you are creating three bytes per pixel from the original's two bytes.
Write strings that represent the new pixel data
The first part means simply changing the 2 to 3, so you get:
guint8 pixel_data[304 * 98 * 3 + 1];
} example= {
304, 98, 3,
In the second part the simplest method would be to print ALL characters in hexadecimal or octal representation. (The original code has the "printable" characters visible, but the non-printable as octal escape sequences.)
To print ALL the characters in hexadecimal representation, do similar to
for (i = 0; i < w * h; i++)
{
...
R, G and B calculation goes here
...
// Print start of line and string (every 16 pixels)
if (i % 16 == 0)
printf("\n\"");
printf("\\x%02x\\x%02x\\x%02x", r, g, b);
// Print end of string and line (every 16 pixels)
if ((i+1) % 16 == 0)
printf("\"\n");
}
printf("\"\n"); // Termination of last line
This prints three bytes in hex representation \xab\xcd\xef and after 16 pixels, prints end of string and newline.
Note that the byte order might need changing depending on your implementation. So b, g, r instead of r, g, b.
I am a SIMD new, writing a program that converts an image from ARGB to grayscale, and the main operation code is as follows:
void* ptr;
int* pBitmap;
posix_memalign(&ptr, 16, height * width * sizeof(int));
pBitmap = (int*)ptr;
for(row = 0; row < height; row++){
for(col = 0; col < width; col++){
int pixel = pBitmap[col + row * width];
int alpha = (pixel >> 24) & 0xff;
int red = (pixel >> 16) & 0xff;
int green = (pixel >> 8) & 0xff;
int blue = pixel & 0xff;
int bw = (int)(red * 0.299 + green * 0.587 + blue * 0.114);
pBitmap[col + row * width] = (alpha << 24) + (bw << 16) + (bw << 8) + (bw);
}
}
And this is my modified SIMD program, which is much slower than the original one.
__m128i bw;
__m128i* rec;
__m128d blue, grees, red, alpha;
for(int i = 0; i < width * height; i += 2){
rec = (__m128i*)(pBitmap + i);
alpha = _mm_cvtepi32_pd(_mm_srli_epi32(*rec, 24));
red = _mm_cvtepi32_pd(_mm_and_si128(_mm_srli_epi32(*rec, 16), _mm_set1_epi32(0xff)));
green = _mm_cvtepi32_pd(_mm_and_si128(_mm_srli_epi32(*rec, 8), _mm_set1_epi32(0xff)));
blue = _mm_cvtepi32_pd(_mm_and_si128(*rec, _mm_set1_epi32(0xff)));
bw = _mm_add_epi32(_mm_cvtpd_epi32(_mm_mul_pd(reds, _mm_set_pd1(0.299))), _mm_cvtpd_epi32(_mm_mul_pd(greens, _mm_set_pd1(0.587))));
bw = _mm_add_epi32(bws, _mm_cvtpd_epi32(_mm_mul_pd(blues, _mm_set_pd1(0.114))));
*rec = _mm_add_epi32(_mm_add_epi32(_mm_slli_epi32(_mm_cvtpd_epi32(alphas), 24), _mm_slli_epi32(bws, 16)), _mm_add_epi32(_mm_slli_epi32(bws, 8), bws));
}
Is the reason for this result because there are more type conversions? But I don't know where else I can optimize, please help me, thank you.
A few issues with your implementation.
SIMD works best when doing multiple pixels at a time in parallel. Do an Internet search "Arrays of Structures vs. Structures of Arrays" for some examples.
Why use doubles instead of single-precision? That's halving your throughput.
Most compilers do not have way to automatically create data constants from SIMD vectors. All those calls to _mm_set_* intrinsics are doing a lot of things at runtime you should really do at compile time.
Replace all the use of _mm_set_* macros with something like:
union simdConstant
{
float f[4];
__m128 v;
};
static const simdConstant c_luminance = { { 0.299f, 0.587f, 0.114f, 1.f } };
static const simdConstant c_luminanceRed = { { 0.299f, 0.299f, 0.299f, 0.299f } };
Then use c_luminance.v or c_luminanceRed.v instead of _mm_set_ps or _mm_set_ps1.
See also DirectXMath which will provide numerous examples of SIMD implementations.
I’m totally beginner in osdeving. for now i’ve just implemented keyboard and vga screen following the osdevwiki. Now i’d like to draw proper pixel like this
void drawPixel(int x, int y, int rgb)
in freestanding C language.
now, in vga mode the address for printing text and colors is 0xB8000. To paint pixels in the screen how can i do? i don’t have any clue.
Text mode is discussed here:
https://wiki.osdev.org/Text_mode
It has an example here to write a colored character in text mode:
void WriteCharacter(unsigned char c, unsigned char forecolour, unsigned char backcolour, int x, int y)
{
uint16_t attrib = (backcolour << 4) | (forecolour & 0x0F);
volatile uint16_t * where;
where = (volatile uint16_t *)0xB8000 + (y * 80 + x) ;
*where = c | (attrib << 8);
}
If you want to write an RGB pixel in graphics mode you have to switch to a different video mode first.
That is explained here:
https://wiki.osdev.org/Drawing_In_Protected_Mode
Here is the code from that page on how to draw a pixel in graphics mode:
/* only valid for 800x600x16M */
static void putpixel(unsigned char* screen, int x,int y, int color) {
unsigned where = x*3 + y*2400;
screen[where] = color & 255; // BLUE
screen[where + 1] = (color >> 8) & 255; // GREEN
screen[where + 2] = (color >> 16) & 255; // RED
}
/* only valid for 800x600x32bpp */
static void putpixel(unsigned char* screen, int x,int y, int color) {
unsigned where = x*4 + y*3200;
screen[where] = color & 255; // BLUE
screen[where + 1] = (color >> 8) & 255; // GREEN
screen[where + 2] = (color >> 16) & 255; // RED
}
Basically you need to write the three color values to three bytes starting from the video memory and offset by the coordinates multiplied by some values to get to the right line and column.
The values are different for the different video modes.
Be aware that even the video memory address is different for VGA/CGA/EGA modes!
I use this method to draw pixels from text mode.
Set the character to a space character and use the color as the color of the pixel eg.
char* video = (char*)0xb8000;
video [0] = 0x20; // space character
video [1] = 0x12; // color of the pixel
I have coded a chip-8 emulator.Whatever I do, it seems that I cannot show any pixels on the screen.The weird thing is that I have checked the code, top-bottom for 2 days already, and there does not seem to be any problem.It reads the .rom file into memory, and fetches the OP code correctly.
Here is the source code:
SDL_SetRenderDrawColor( renderer, 0, 0, 0, SDL_ALPHA_OPAQUE );
SDL_RenderClear(renderer);
uint32_t pixels[(WINDOW_WIDTH / 10) * (WINDOW_HEIGHT / 10)];
uint16_t i;
for(i = 0; i < 64*32; i++){
pixels[i] = (0x00FFFFFF * display[i]) | 0xFF000000;
}
//upload the pixels to the texture
SDL_UpdateTexture(tex,NULL,pixels, 64 * sizeof(uint32_t));
//Now get the texture to the screen
SDL_RenderCopy(renderer,tex,NULL,NULL);
SDL_RenderPresent(renderer); // Update screen
ch8.drawF = false;
uint16_t x = ch8->V[((ch8->opcode & 0x0F00) >> 8)];
uint16_t y = ch8->V[((ch8->opcode & 0x00F0) >> 4)];
uint8_t n = (ch8->opcode & 0x000F);
for(i = 0; i < n; i++) {
uint8_t pixel= memory[ch8->I.word + i];
for(j = 0; j < 8; j++) {
if((pixel & (0x80 >> j)) != 0){
if(display[x + j + ((y + i) * 64)] == 1) {
ch8->V[0xF] = 1;
}
display[x + j + ((y + i) * 64)] ^= 1;
}
}
}
So basically, the problem was at init() function.I was initially using, SDL_CreateWindow and SDL_CreateRenderer,but now I'm using ,SDL_CreateWindowAndRenderer, which takes pointers to pointers of SDL_Window and SDL_Renderer instead of a pointer to a char and a pointer to a window.
Also there were 3 problems I fixed.
1.I was adding + 0x200 to NNN opcodes,because at firstly I thought that the NNN in ROM's are relative to 0, so I removed +0x200 from each XNNN opcode.Also I forgot a * at SDL_Texture* tex, its supposed to be SDL_Texture** tex, I was merely changing the address the local pointer was poiting too...
2.at opcode 2NNN, instead of (ch8->SP) = ch8->opcode & 0x0FFF; its(ch8->SP) = ch8->PC.word;
3.at opcode FX65 its i <= ((ch8->opcode & 0x0F00) >> 8)
Basically, the differences between SDL_CreateWindowAndRenderer and SDL_CreateWindow&SDL_CreateRenderer had me confused, I should had check'd the documentation first.
Now I only need to make the emulator only redraw the changed pixels, then make the emulator play sound.
This code is supposed to convert a RGB color to an hex in the 5:6:5 format. 5 bits for red, 6 bits for green, 5 bits for blue. I have no idea why this is not picturing the correct color.
Does anyone knows why?
int rgb(unsigned char r, unsigned char g, unsigned char b) {
if (r < 0 || 255 < r || g < 0 || 255 < g || b < 0 || b > 255)
return -1;
int result;
int red = r * 31 / 255;
int green = g * 63/ 255;
int blue = b * 31 / 255;
//int result = (red << 11) | (green << 5) | blue;
green = green << 5;
red = red << 11;
result = red | green | blue;
//tests
printf("\nred: %x", red);
printf("\ngreen: %x", green);
printf("blue: %x\n", blue);
printf("result: %x\n", result);
return result;
}
After another look at your question I don't really know what you're asking about. Anyway, I'm leaving my answer in case you find it useful.
Your rgb(...) function takes three byte arguments - they have 8 bits each.
Let's take "red" component into account first. If you pass XXXX XXXX (8 bits) and want to convert them into a 5-bit equivalent representation, it's enough to shift the value right by 3 bits, so:
int red = r >> 3;
The value XXXXXXXX will be truncated in the place of the pipeline character:
XXXXX|xxx
so that only the bits marked with large Xes will be saved to the red variable.
The same goes for blue, and for the green component, you have to shift it right by two (8 - 6 = 2).
You probably want your function to work like this:
int rgb(unsigned char r, unsigned char g, unsigned char b) {
if (r < 0 || 255 < r || g < 0 || 255 < g || b < 0 || b > 255)
return -1;
unsigned char red = r >> 3;
unsigned char green = g >> 2;
unsigned char blue = b >> 3;
int result = (red << (5 + 6)) | (green << 5) | blue;
//tests
printf("red: %x\n", red);
printf("green: %x\n", green);
printf("blue: %x\n", blue);
printf("result: %x\n", result);
return result;
}
Assuming 8-bit char, your unsigned char arguments must already be in the 0-255 range, so you don't need to check that. And the multiplication you're trying to use to scale the color components is probably not a good approach.
A better approach would be to AND each component with a mask to get the upper 5 bits (6 for green), shift them to the proper positions, and OR them together. When shifting, remember to account for the fact that you're using the upper bits... and for the last component, you won't need to AND with a mask because the unneeded bits are shifted out anyway. So this gets you something like this (as the only line in your function):
return ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
(r & 0xf8) gets the upper 5 bits of r. These are then left shifted by 8 bits, so they move from positions 3..7 into 11..15.
(g & 0xfc) gets the upper 6 bits of g. Those are then left shifted by 3 bits, from 2..7 into 5..10.
b doesn't need to be masked... it's just shifted right 3 bits. Its upper 5 bits are then moved from 3..7 into 0..4, and its lower 3 bits are discarded when they're shifted out.
All those values are then ORed together to get your RGB 5:6:5 value, and returned.
Alternatively, if you prefer shifts over AND, you can use:
return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3);
You might also consider changing the return type to an unsigned 16-bit type and not worry about returning an error value (there isn't really any kind of error condition to check for here).
You need a function that shows you the binary contents, so that you can "count" the bits and better find errors. My approach added a rounding routine:
#include <stdio.h>
#include <math.h>
char* sprint_bin (unsigned a, unsigned count, char* bin)
{
char* p = bin;
unsigned i;
unsigned mask = pow(2,count-1);
unsigned b;
for (i = 0; i<count; ++i)
{
b = (a & mask) ? '1' : '0';
p += sprintf (p, "%c ",b);
mask >>= 1;
}
return bin;
}
unsigned rgb(unsigned char r, unsigned char g, unsigned char b) {
char bin[64];
int result;
printf("r: %s\n", sprint_bin(r,8,bin));
printf("g: %s\n", sprint_bin(g,8,bin));
printf("b: %s\n", sprint_bin(b,8,bin));
// masks
unsigned red = (unsigned)(r & 0xF8) << 8;
unsigned green = (unsigned)(g & 0xFC) << 3;
unsigned blue = (unsigned)(b >> 3);
// rounding
if ((r & 4) && (r<0xF8)) red += 0x0800;
if ((g & 2) && (g<0xFC)) green += 0x20;
if ((b & 4) && (b<0xF8)) blue++;
// 5:6:5
result = red | green | blue;
// test
printf("red: %s\n", sprint_bin(red,16,bin));
printf("green: %s\n", sprint_bin(green,16,bin));
printf("blue: %s\n", sprint_bin(blue,16,bin));
printf("result: %s\n", sprint_bin(result,32,bin));
return result;
}
int main ()
{
rgb (0x81, 0x87, 0x9F);
return 0;
}