Afternoon everyone,
I was wondering if there's any way I could create a custom bitmap with alpha channel
bitmap = al_create_bitmap(30, 30);
al_set_target_bitmap(bitmap);
al_clear_to_color(al_map_rgb(255,255,255));
....
al_draw_tinted_bitmap(bitmap, al_map_rgba(0, 0, 0, 0.5), X, Y, 0);
I'm sure that I'm either not creating or drawing the bitmap correctly, so I could really use some advice.
Thanks in advance,
Alex
The only thing wrong with your code snippet is:
al_map_rgba(0, 0, 0, 0.5)
should be:
al_map_rgba_f(0, 0, 0, 0.5)
The former range is an integer from 0 to 255.
Also, keep in mind that Allegro's default blender is pre-multiplied alpha. So if you wanted to tint red at 50%, you'd use:
float a = 0.5;
... al_map_rgba_f(1.0 * a, 0.0 * a, 0.0 * a, a) ...
If you're not thinking about it, you're probably assuming it's interpolating. i.e., the more intuitive blender for most people seems to be:
al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA)
but that is not the default for the reasons mentioned in the above link.
after I set the
al_set_blender(ALLEGRO_ADD, ALLEGRO_ALPHA, ALLEGRO_INVERSE_ALPHA);
it allowed me to draw my "bouncer" bitmap and change its alpha channel using the below function:
al_draw_tinted_bitmap(bouncer, al_map_rgba_f(1, 1, 1, alpha) 40, 0, 0);
This previously did not work , so I guess adding the al_set_blender solved the "mistery".
Thanks for all your help.
Related
I've been trying, but failing to do it with this code
UTextureRenderTarget2D *renderTarget; // In header
void UMyActorComponent::BeginPlay()
{
Super::BeginPlay();
texture = UTexture2D::CreateTransient(1, 1);
FTexture2DMipMap& Mip = texture->PlatformData->Mips[0];
void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);
uint8 red[4] = { 255, 255, 255, 255 };
FMemory::Memcpy(Data, &red, sizeof(red));
Mip.BulkData.Unlock();
texture->UpdateResource();
renderTarget->UpdateTexture2D(texture, TSF_BGRA8);
}
I at least know for a fact I have the correct handle to the renderTarget, because I've been able to clear the render target. I might be missing something simple any help is appreciated.
I found a solution and it seems like its the correct way to do it.
Disclaimer
Using a texture render target might not be the best approach since generally a render target conveys you’re rendering from the gpu to the gpu. And in this case I’m writing data from the cpu to the gpu. So you might run into some unforseen problems down the line if you take this approach. Probably creating a transient texture is the proper way to do this.
Solution
Dynamically initializing the Render Target's Resource
So first you'll need to initialize your UTextureRenderTarget2D to your desired resolution using this function. Put this where it makes sense.
RenderTarget->InitCustomFormat(2, 2, PF_B8G8R8A8, true);
I'm using a 2x2 image with a BGRA8 pixel format and linear color space for this example
Function to update the Render Target
Then you'll want to place this function somewhere accessible in your code
void UpdateTextureRegion(FTexture2DRHIRef TextureRHI, int32 MipIndex, uint32 NumRegions, FUpdateTextureRegion2D Region, uint32 SrcPitch, uint32 SrcBpp, uint8* SrcData, TFunction<void(uint8* SrcData)> DataCleanupFunc = [](uint8*) {})
{
ENQUEUE_RENDER_COMMAND(UpdateTextureRegionsData)(
[=](FRHICommandListImmediate& RHICmdList)
{
check(TextureRHI.IsValid());
RHIUpdateTexture2D(
TextureRHI,
MipIndex,
Region,
SrcPitch,
SrcData
+ Region.SrcY * SrcPitch
+ Region.SrcX * SrcBpp
);
DataCleanupFunc(SrcData);
});
}
Updating the Render Target
This code will write a red, green, blue, and white
static uint8 rgbw[4 * 4] = {
0, 0, 255, 255,
0, 255, 0, 255,
255, 0, 0, 255,
255, 255, 255, 255
};
auto region = FUpdateTextureRegion2D(0, 0, 0, 0, 2, 2);
UpdateTextureRegion(RenderTarget, 0, 1, region, 2*4, 4, rgbw);
Attach the Render Target to a plane in the editor and it should hopefully look like this when all is said and done
And with that you should have some functioning code to play with.
So for a quick sample in pictures:
This is normal:
this is after rotating 180 deg on either the X or Y axis:
I don't see why this is happening at all. I'm using OpenTK to render a simple Bullet physics scene. The code is straight forward and it almost seems like there's something wrong in the way the matrix is handled. It's straight-forward render code:
GL.PushMatrix();
GL.MultMatrix(Body.MotionState.WorldTransform.ToArray());
GL.Scale(HalfX * 2, HalfY * 2, HalfZ * 2);
GL.Color4(Color4.Blue);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(0, 0, 0);
GL.Vertex3(0, 0, 10);
GL.End();
GL.Color4(Color4.Yellow);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(0, 0, 0);
GL.Vertex3(0, 10, 0);
GL.End();
GL.Color4(Color4.Green);
GL.Begin(PrimitiveType.Lines);
GL.Vertex3(0, 0, 0);
GL.Vertex3(10, 0, 0);
GL.End();
if (Body.ActivationState == ActivationState.ActiveTag)
{
GL.Color4(Color4.Blue);
}
else if (Mass == 0)
{
GL.Color4(Color4.Red);
}
else
{
GL.Color4(Color4.Green);
}
model.Draw();
GL.PopMatrix();
I've tried breaking it down to it's components: the translation vector is fine, scaling is fine, rotating on the Z axis appears fine... it's when you add rotations on the X or Y axis that it starts flying. I have console output going: the box is at exactly 6.9999 on the Z axis in both images.
Where am I going wrong? What am I missing? How do I fix this?!
Okay so... PushAttrib(AttribMask.AllAttribBits); PushMatrix(); in my /TEXTFONT LOADING CODE/ fixed it. Somehow, some weird attribute set in the code that loads fonts to later render made GL.Rotate rotate around (0, 0, 1) instead of (0, 0, 0)... OpenGL sure is temperamental...
So the lesson here is... never assume unrelated code is truly unrelated when dealing with OpenGL.
I am writing an application using Xlib. I set the foreground of the window up like this:
XSetForeground (dpy, gc, WhitePixel (dpy, scr));
But now I need to change the drawing colour to something else, I first wanted to do that like this:
void update_window (Display* d, Window w, GC gc, Colormap cmap)
{
XWindowAttributes winatt;
XColor bcolor;
char bar_color[] = "#4E4E4E";
XGetWindowAttributes (d, w, &winatt);
XParseColor(d, cmap, bar_color, &bcolor);
XAllocColor(d, cmap, &bcolor);
// Draws the menu bar.
XFillRectangle (d, w, gc, 0, 0, winatt.width, 30);
XFreeColormap (d, cmap);
}
But this doesn't work. What does XParseColor and XAllocColor do then? And do I need to use XSetForeground again to change the colour?
You need to use XSetForeground. Try something like this:
XColor xcolour;
// I guess XParseColor will work here
xcolour.red = 32000; xcolour.green = 65000; xcolour.blue = 32000;
xcolour.flags = DoRed | DoGreen | DoBlue;
XAllocColor(d, cmap, &xcolour);
XSetForeground(d, gc, xcolour.pixel);
XFillRectangle(d, w, gc, 0, 0, winatt.width, 30);
XFlush(d);
Also, I don't think you can use that color string. Take a look into this page:
A numerical color specification consists of a color space name and a set of values in the following syntax:
<color_space_name>:<value>/.../<value>
The following are examples of valid color strings.
"CIEXYZ:0.3227/0.28133/0.2493"
"RGBi:1.0/0.0/0.0"
"rgb:00/ff/00"
"CIELuv:50.0/0.0/0.0"
Edit/Update: as #JoL mentions in the comment, you can still use the old syntax, but the usage is discouraged:
For backward compatibility, an older syntax for RGB Device is supported, but its continued use is not encouraged. The syntax is an initial sharp sign character followed by a numeric specification, in one of the following formats:
//I write additional function _RGB(...) where r,g,b is components in range 0...255
unsigned long _RGB(int r,int g, int b)
{
return b + (g<<8) + (r<<16);
}
void some_fun()
{
//sample set color, where r=255 g=0 b=127
XSetForeground(display, gc, _RGB(255,0,127));
//draw anything
XFillRectangle( display, window, gc, x, y, len, hei );
}
All colour changes are done with respect to a certain GC. That GC is then used for drawing. Yes XSetForeground is the most convenient way to do that.
You can have several GCs if you have a handful of colours you use often.
I try to draw a round rectangle with drawRoundedRect method directly in a QPixmap (no render engine involve here exept pure Qt one ...), I double check the size of the rectangle versus the size of my pixmap :
Pixmap : QSize(50, 73)
Rectangle: QRect(0,0 48x11)
See plenty of space ...
EDIT: some code
pixmap = QPixmap(50,73); //example size that match my case
QRectF rect(0,0,48,11);
QPainter painter(&pixmap);
painter.setRenderHint(QPainter::TextAntialiasing);
painter.setWorldMatrixEnabled(false);
painter.setPen(QPen()); //no pen
painter.setBrush(QBrush(color));
painter.drawRoundedRect(rect, 2.0, 2.0);
I disabled world transformation ...
I set set transformation to unity ...
I tried several radius (1.0,2.0,3.0,4.0) ...
I change pen width, brush color ...
But it always ends with a rectamgle with 4 diferent corners ! Like that :
I directly ouptut the pixmap to a file to be sure I wasn't scraping it during the display ... same shape.
Anyone know about Qt round rectangle with small radius ? I saw somthing about it a long time ago but I don't remenber how to deal with it !
It looks like you're not using anti-aliasing (i.e. the QPainter::Antialiasing render hint). This is a Qt quirk that occurs without it. From what I've seen/heard, the Qt devs aren't terribly concerned with fixing this (most people want anti-aliasing anyway).
The work-around (besides just using anti-aliasing) is to draw the rect yourself with QPainter::drawLine() and QPainter::drawArc(). You might have to play with numbers until it looks right -- straight calculations tend to come out a pixel or two off. Also, you might find that even with this method the lower right corner is never exactly the same as the other corners.
If you're feeling mildly ambitious, you could try fixing this and submitting a patch to Qt.
Update: Arc drawing results changed in Qt 5. In my experience, it's a big improvement.
I know this is an old problem but for Qt5 users calling setRenderHint(QPainter::Qt4CompatiblePainting); on the QPainter seems to solve the problem.
Edit:
I found a solution for generating a perfect rounded rectangle together with border color and it looks the same as the rounded rectangles used by QPushButton's border for example. This is how I implemented the paintEvent to achieve this:
void MyButtonGroup::paintEvent(QPaintEvent * e)
{
int borderSize = 5;
QColor borderColor = Qt::red;
QColor backgroundColor = Qt::blue;
int borderRadius = 3;
QPen pen;
pen.setWidth(borderSize);
pen.setColor(borderColor);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(pen);
QRectF rect(rect().x() + borderSize / 2,
rect().y() + borderSize / 2,
rect().width() - borderSize,
rect().height() - borderSize);
if(borderSize % 2 == 0)
{
painter.drawRoundedRect(rect,
borderSize,
borderSize);
}
else
{
painter.drawRoundedRect(rect.translated(0.5, 0.5),
borderRadius,
borderRadius);
}
QBrush brush(backgroundColor);
pen.setBrush(brush);
painter.setBrush(brush);
if(borderSize % 2 == 0)
{
painter.drawRoundedRect(rect,
borderRadius,
borderRadius);
}
else
{
painter.drawRoundedRect(rect.translated(0.5, 0.5),
borderRadius,
borderRadius);
}
QWidget::paintEvent(e);
}
I'm posting this because I found it a bit hard to achieve this result:
Try adding half a pixel offset (e.g.: rect.translated(0.5,0.5) ):
QRectF rect(0,0,48,11);
painter.setRenderHint(QPainter::Antialiasing,false);
painter.drawRoundedRect( rect.translated(0.5,0.5), 2.0, 2.0 );
I suppose this has to do with the coordinate system placing an integer value between two pixels.
If you draw with antialiasing and use a pen of 1 pixel width then drawing at exact integer coordinates results in lines of 2 pixel width instead.
Only with this 0.5 pixel offset you'll get lines that are exactly 1 pixel wide.
QRectF rect(0,0,48,11);
painter.setRenderHint(QPainter::Antialiasing,true);
painter.setBrush(Qt::NoBrush);
painter.setPen( Qt::white );
painter.drawRoundedRect( rect.translated(0.5,0.5), 2.0,2.0 );
Best way do draw RoundRect is Path.
http://developer.nokia.com/community/wiki/Qt_rounded_rect_widget
void fillRoundRect(QPainter& painter, QRect r, int radius)
{
painter.setRenderHint(QPainter::Antialiasing,true);
QPainterPath rounded_rect;
rounded_rect.addRoundRect(r, radius, radius);
painter.setClipPath(rounded_rect);
painter.fillPath(rounded_rect,painter.brush());
painter.drawPath(rounded_rect);
}
try to play with render hints
1) disable antiAliasing;
2) enable SmoothPixmapTransform
but still no guarantee that it will help.
I have tried all tips from answers here but nothing works for me. But based on these code snippets I have found following solution:
As default set m_pPainter->setRenderHint(QPainter::Qt4CompatiblePainting, true) and only for rounded rectangles with width%2==0 disable it.
QRect rect = ConvertRectangle(rectangle);
int nPenWidth = m_pPainter->pen().width();
if ( nPenWidth % 2 == 0 )
m_pPainter->setRenderHint(QPainter::Qt4CompatiblePainting, false);
m_pPainter->drawRoundedRect(rect, dbRadiusX, dbRadiusY);
if ( nPenWidth % 2 == 0 )
m_pPainter->setRenderHint(QPainter::Qt4CompatiblePainting, true);
Imagine the following scenario: you have a set of RPG character spritesheets in PNG format and you want to use them in an OpenGL application.
The separate characters are (usually) 16 by 24 pixels in size (that is, 24 pixels tall) and may be at any width and height without leaving padding. Kinda like this:
(source: kafuka.org)
I already have the code to determine an integer-based clipping rectangle given a frame index and size:
int framesPerRow = sheet.Width / cellWidth;
int framesPerColumn = sheet.Height / cellHeight;
framesTotal = framesPerRow * framesPerColumn;
int left = frameIndex % framesPerRow;
int top = frameIndex / framesPerRow;
//Clipping rect's width and height are obviously cellWidth and cellHeight.
Running this code with frameIndex = 11, cellWidth = 16, cellHeight = 24 would return a cliprect (32, 24)-(48, 48) assuming it's Right/Bottom opposed to Width/Height.
The actual question
Now, given a clipping rectangle and an X/Y coordinate to place the sprite on, how do I draw this in OpenGL? Having the zero coordinate in the top left is preferred.
You have to start thinking in "texture space" where the coordinates are in the range [0, 1].
So if you have a sprite sheet:
class SpriteSheet {
int spriteWidth, spriteHeight;
int texWidth, texHeight;
int tex;
public:
SpriteSheet(int t, int tW, int tH, int sW, int sH)
: tex(t), texWidth(tW), texHeight(tH), spriteWidth(sW), spriteHeight(sH)
{}
void drawSprite(float posX, float posY, int frameIndex);
};
All you have to do is submit both vertices and texture vertices to OpenGL:
void SpriteSheet::drawSprite(float posX, float posY, int frameIndex) {
const float verts[] = {
posX, posY,
posX + spriteWidth, posY,
posX + spriteWidth, posY + spriteHeight,
posX, posY + spriteHeight
};
const float tw = float(spriteWidth) / texWidth;
const float th = float(spriteHeight) / texHeight;
const int numPerRow = texWidth / spriteWidth;
const float tx = (frameIndex % numPerRow) * tw;
const float ty = (frameIndex / numPerRow + 1) * th;
const float texVerts[] = {
tx, ty,
tx + tw, ty,
tx + tw, ty + th,
tx, ty + th
};
// ... Bind the texture, enable the proper arrays
glVertexPointer(2, GL_FLOAT, verts);
glTexCoordPointer(2, GL_FLOAT, texVerts);
glDrawArrays(GL_TRI_STRIP, 0, 4);
}
};
Franks solution is already very good.
Just a (very important) sidenote, since some of the comments suggested otherwise.
Please don't ever use glBegin/glEnd.
Don't ever tell someone to use it.
The only time it is OK to use glBegin/glEnd is in your very first OpenGL program.
Arrays are not much harder to handle, but...
... they are faster.
... they will still work with newer OpenGL versions.
... they will work with GLES.
... loading them from files is much easier.
I'm assuming you're learning OpenGL and only needs to get this to work somehow. If you need raw speed, there's shaders and vertex buffers and all sorts of both neat and complicated things.
The simplest way is to load the PNG into a texture (assuming you have the ability to load images into memory, you do need htat), then draw it with a quad setting appropriate texture coordinates (they go from 0 to 1 with floating point coordinates, so you need to divide by texture width or height accordingly).
Use glBegin(GL_QUADS), glTexcoord2f(), glVertex2f(), glEnd() for the simplest (but not fastest) way to draw this.
For making zero top left, either use gluOrtho() to set up the view matrix differently from normal GL (look up the docs for that function, set top to 0 and bottom to 1 or screen_height if you want integer coords) or just make change your drawing loop and just do glVertex2f(x/screen_width, 1-y/screen_height).
There are better and faster ways to do this, but this is probably one of the easiest if you're learning raw OpenGL from scratch.
A suggestion, if I may. I use SDL to load my textures, so what I did is :
1. I loaded the texture
2. I determined how to separate the spritesheet into separate sprites.
3. I split them into separate surfaces
4. I make a texture for each one (I have a sprite class to manage them).
5. Free the surfaces.
This takes more time (obviously) on loading, but pays of later.
This way it's a lot easier (and faster), as you only have to calculate the index of the texture you want to display, and then display it. Then, you can scale/translate it as you like and call a display list to render it to whatever you want. Or, you could do it in immediate mode, either works :)