Drawing PixelFormat32bppPARGB images with GDI+ uses conventional formula instead of premultiplied one - windows

Here is some minimal code to show an issue:
static const int MAX_WIDTH = 320;
static const int MAX_HEIGHT = 320;
Gdiplus::Bitmap foregroundImg(MAX_WIDTH,MAX_HEIGHT,PixelFormat32bppPARGB);
{
Gdiplus::Graphics g(&foregroundImg);
g.Clear(Gdiplus::Color(10,255,255,255));
}
Gdiplus::Bitmap softwareBitmap(MAX_WIDTH,MAX_HEIGHT,PixelFormat32bppPARGB);
Gdiplus::Graphics g(&softwareBitmap);
g.SetCompositingMode(Gdiplus::CompositingModeSourceOver);
g.SetCompositingQuality(Gdiplus::CompositingQualityDefault);
g.Clear(Gdiplus::Color(255,0,0,0));
g.DrawImage(foregroundImg,0,0);
CLSID encoder;
GetEncoderClsid(L"image/png",&encoder);
softwareBitmap.Save(L"d:\\image.png",&encoder);
As result I'm getting image filled by RGB values equals to 10. It seems GDI+ uses the conventional algorithm:
255*(10/255) + 0*(1-10/255) == 10.
But I'm expecting that premultiplied algorithm will be used (because foreground image has the premultiplied PixelFormat32bppPARGB format):
255 + 0*(1-10/255) == 255
So my question, why GDI+ uses conventional formula when image is in premultiplied alpha format? And is there any workaround to make GDI+ to use the premultiplied alpha algorithm?

The format of your foreground image doesn't matter (given that it has alpha) because you're setting it to a Gdiplus::Color. Color values are defined as non-premultiplied, so gdiplus multiplies the components by the alpha value when it clears the foreground image. The alternative would be for Color values to have different meaning depending on the format of the render target, and that way lies madness.
You might be able to do what you intend by setting the source image bits directly, or you might not. Components with values greater than 100% aren't really valid in gdiplus's rendering model, so I'd not be surprised if it caps them during rendering. If you really want this level of control over the rendering, you'll have to lock the bitmap bits and do it yourself.

Related

Visual C++: Good way to draw and animated fill path to screen?

I want to use Visual C++ to animate fill paths to screen. I have done it with C# before, but now switch to C++ for better perfomance and want do more complex works in the future.
Here is the concept in C#:
In a Canvas I have a number of Path. These paths are closed geometries combine of LineTo and QuadraticBezierTo functions.
Firstly, I fill Silver color for all path.
Then for each path, I fill Green color from one end to other end (up/down/left/right direction) (imagine a progress bar increase its value from min to max). I do it by set the Fill brush of the path to a LinearGradientBrush with two color Green and Silver with same offset, then increase the offset from 0 to 1 by Timer.
When a path is fully green, continue with next path.
When all path is fill with Green, come back first step.
I want to do same thing in Visual C++. I need to know an effective way to:
Create and store paths in a collection to reuse. Because the path is quite lot of point, recreate them repeatly take lots of CPU usage.
Draw all paths to a window.
Do animation fill like step 2, 3, 4 in above concept.
So, what I need is:
A suitable way to create and store closed paths. Note: paths are combine of points connect by functions same with C# LineTo and QuadraticBezierTo function.
Draw and animated fill the paths to screen.
Can you please suggest one way to do above step? (outline what I have to read, then I can study about it myself). I know basic of Visual C++, Win32 GUI and a little about draw context (HDC) and GDI, but only start to learn Graphic/Drawing.
Sorry about my English! If anythings I explain dont clear, please let me know.
how many is quite lot of point ? what is the target framerate? for low enough counts you can use GDI for this otherwise you need HW acceleration like OpenGL,DirectX.
I assume 2D so You need:
store your path as list of segments
for example like this:
struct path_segment
{
int p0[2],p1[2],p2[2]; // points
int type; // line/bezier
float length; // length in pixels or whatever
};
const int MAX=1024; // max number of segments
path_segment path[MAX]; // list of segments can use any template like List<path_segment> path; instead
int paths=0; // actual number of segments;
float length=0.0; // while path length in pixels or whatever
write functions to load and render path[]
The render is just for visual check if you load is OK ... for now atlest
rewrite the render so
it take float t=<0,1> as input parameter which will render path below t with one color and the rest with other. something like this:
int i;
float l=0.0,q,l0=t*length; // separation length;
for (i=0;i<paths;i++)
{
q=l+path[i].length;
if (q>=l0)
{
// split/render path[i] to < 0,l-l0> with color1
// split/render path[i] to <l-l0,q-l0> with color2
// if you need split parameter in <0,1> then =(l-l0)/path[i].length;
i++; break;
}
else
{
//render path[i] with color1
}
l=q;
}
for (;i<paths;i++)
{
//render path[i] with color2
}
use backbuffer for speedup
so render whole path with color1 to some bitmap. On each animation step just render the newly added color1 stuff. And on each redraw just copy the bitmap to screen instead of rendering the same geometry over and over. Of coarse if you have zoom/pan/resize capabilities you need to redraw the bitmap fully on each of those changes ...

Colors are not being correctly displayed

Just to learn Windows API, I'm trying to use a cheap fingerprint device I bought.
The library that came with it captures the fingerprint as an 8bit bitmap of 256x280 pixels and stores the raw pixels in a buffer.
I'm trying to copy this raw pixel stream into a Device Independent Bitmap (DIB) and then trying to use this DIB to draw onto a window.
I managed to display the image but the colors are all wrong. Heres the piece of code that handles the painting.
PAINTSTRUCT ps;
HDC hdc,memDC;
HBITMAP cp_bmp;
HBITMAP di_bmp;
BITMAPINFO di_bmp_info;
void *di_bmp_data;
int ptr;
int x,y;
int aux;
ZeroMemory(&di_bmp_info,sizeof(BITMAPINFO));
di_bmp_info.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
di_bmp_info.bmiHeader.biWidth=256;
di_bmp_info.bmiHeader.biHeight=280;
di_bmp_info.bmiHeader.biPlanes=1;
di_bmp_info.bmiHeader.biBitCount=8;
di_bmp_info.bmiHeader.biCompression=BI_RGB;
hdc=BeginPaint(hwnd,&ps);
// create the DIB
di_bmp=CreateDIBSection(hdc,&di_bmp_info,DIB_RGB_COLORS,&di_bmp_data,NULL,0);
// Copy the original bitstream onto the DIB
CopyMemory(di_bmp_data,fingerprint,256*280);
// create the mem dc
memDC=CreateCompatibleDC(hdc);
// create the DDB
cp_bmp=CreateCompatibleBitmap(hdc,256,280);
SelectObject(memDC,cp_bmp);
SetDIBits(memDC,cp_bmp,0,280,di_bmp_data,&di_bmp_info,DIB_RGB_COLORS);
BitBlt(hdc,10,10,256,280,memDC,0,0,SRCCOPY);
DeleteObject(cp_bmp);
EndPaint(hwnd,&ps);
Curious fact is that when I change the di_bmp_info.bmiHeader.biBitCount to 32, the colors display perfectly but the image gets smaller in size and gets repeated around 5 or six times horizontally.
Im stuck!. Thanks in advance.
In short, you have an indexed bitmap (each pixel is not an RGB value but instead an index into a defined palette of colors) and you haven't provided a palette for it to use.
BITMAPINFO is a variable sized structure - a BITMAPINFOHEADER, followed by at least one but potentially more RGBQUAD structures. For indexed bitmap depths (8 bpp and below), a palette needs to be provided following the BITMAPINFOHEADER in memory.
The number of palette entries required is determined by the biBitCount and biClrUsed fields. If biClrUsed is 0 the number of palette entries must be 1 << biBitCount, or 256 in the case of 8bpp. Setting biClrUsed to something other than 0 lets you provide fewer palette entries if the bitmap doesn't need them.
Because you haven't set biClrUsed a full 256 color palette is assumed to follow the BITMAPINFOHEADER in memory and essentially random memory is being used.
BITMAPINFO provides only a single RGBQUAD itself so you need to extend the structure to provide the rest, for example:
struct MyBitmapInfo
{
BITMAPINFOHEADER bmiHeader;
RGBQUAD palette[256];
};
Where you actually get the palette from is up to you, but you could e.g. use a grayscale palette like this:
struct MyBitmapInfo di_bmp_info;
ZeroMemory(&di_bmp_info,sizeof(struct MyBitmapInfo));
di_bmp_info.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
di_bmp_info.bmiHeader.biWidth=256;
di_bmp_info.bmiHeader.biHeight=280;
di_bmp_info.bmiHeader.biPlanes=1;
di_bmp_info.bmiHeader.biBitCount=8;
di_bmp_info.bmiHeader.biCompression=BI_RGB;
// initialise greyscale palette
for (int i = 0; i < 256; i++)
{
di_bmp_info.palette[i].rgbRed =
di_bmp_info.palette[i].rgbGreen =
di_bmp_info.palette[i].rgnBlue = i;
}

What's good algorithm for getting color to make text on image outstanding?

For instance, below image, a photo is background, and there is a character 'Iniesta' at the center of the photo.
But the character is some hard to read because the color is not good.
Is there any good algorithm for getting color to make character on image outstanding?
Instead of making a rectangular background (that indeed doesn't look very nice) you could use this trick:
Pick two contrasting colors (e.g. white and black)
Stroke the text using first color and a wide pen (e.g. 5 pixels)
Draw the text filled using the second color
If you cannot stroke the text with a wide pen an alternative is drawing the text multiple times with small offsets; e.g. (Python)
for dy in range(-3, 4):
for dx in range(-3, 4):
draw_text(color1, x+dx, y+dy, message)
draw_text(color2, x, y, message)
For example a one-pixel border would look like this...
Here's another idea: choose a box around your text area, or the whole image if it's relatively homogeneous in color. Now compute the average color in this area by summing up the color values and dividing by the number of pixels.
Now choose a color which contrasts well with this average color. For instance, you could use the simple formula (255-r,255-g,255-b) to get a contrasting color. However, this would fail if the average color is close to 128-gray, so you'd need to special-case on that.
Another way is to convert the average color into HSL or HSV color space, and then play with the hue; for instance, just add 180 to it, and/or invert the "lightness" (value/luminosity). Again, you'd need to special-case the grayscale (saturation close to 0) cases.
There are many approaches that differs for static and dynamic scene. I assume static image so i focus on that. here some basic approaches:
Paper area
clear space behind text with paper color and render text with ink color as you mentioned this is not cool for complex images.
Stroke,shadows,3D
render text with 2 colors. Inside with text color and stroke with contrast color to it. If you do not have the capability then you can render character with bigger font/boldness size in stroke color first and then overwrite with smaller font size with inside color +/- shift the position to center the fonts. The shifting method determine if you render stroke or shadow or even 3D text can be achieved like this
Transparency
Instead of render pixels of text by constant color add some color value to the original pixel color. You can also substract color or use alpha mixing instead ...
XOR
instead render pixel with color XOR the original color with another color or by white
This is how it looks like:
Here the source in VCL/C++:
void TMain::draw()
{
if (!_redraw) return;
// needed variables
AnsiString txt,a;
TColor c0=clBlack,c1=clWhite;
int tx=50,ty=50,tys=30,dty=tys*1.5;
int x,y,x0,x1,y0,y1,i,b;
// clear whole image
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(TRect(0,0,xs,ys));
// copy photo
bmp->Canvas->Draw(0,0,in);
// render texts ...
txt="Paper area ";
bmp->Canvas->Font ->Size=tys;
bmp->Canvas->Font ->Color=c1;
bmp->Canvas->Brush->Color=c0;
bmp->Canvas->Brush->Style=bsSolid;
bmp->Canvas->TextOutA(tx,ty,txt); ty+=dty;
txt="Stroke";
bmp->Canvas->Brush->Style=bsClear;
for (x=tx,y=ty,i=1;i<=txt.Length();i++)
{
a=txt[i];
// stroked char
bmp->Canvas->Font ->Size=tys+3;
bmp->Canvas->Font ->Color=c0;
bmp->Canvas->Font ->Style=TFontStyles()<<fsBold;
x0=bmp->Canvas->TextWidth(a);
y0=bmp->Canvas->TextHeight(a);
bmp->Canvas->TextOutA(x,y,a);
// inside char
bmp->Canvas->Font ->Size=tys;
bmp->Canvas->Font ->Color=c1;
bmp->Canvas->Font ->Style=TFontStyles();
x1=bmp->Canvas->TextWidth(a);
y1=bmp->Canvas->TextHeight(a);
bmp->Canvas->TextOutA(x+((x0-x1)>>1),y+((y0-y1)>>1),a);
// next char position
x+=x0;
} ty+=dty;
txt="Shadow/3D text";
bmp->Canvas->Brush->Style=bsClear;
bmp->Canvas->Font ->Size=tys;
bmp->Canvas->Font ->Color=c0;
for (i=0;i<5;i++)
bmp->Canvas->TextOutA(tx-i,ty+i,txt);
bmp->Canvas->Font ->Color=c1;
bmp->Canvas->TextOutA(tx,ty,txt); ty+=dty;
Graphics::TBitmap *tmp=new Graphics::TBitmap;
tmp->PixelFormat=pf32bit;
tmp->HandleType=bmDIB;
txt="Transparent";
tmp->Canvas->Brush->Style=bsSolid;
tmp->Canvas->Brush->Color=0x00000000; // max black
tmp->Canvas->Font ->Size=tys;
tmp->Canvas->Font ->Color=0x00808080; // color offset
x=bmp->Canvas->TextWidth(txt);
y=bmp->Canvas->TextHeight(txt);
tmp->SetSize(x,y);
tmp->Canvas->FillRect(TRect(0,0,x,y));
tmp->Canvas->TextOutA(0,0,txt);
union col { DWORD dd; BYTE db[4]; } *p0,*p1;
for (y0=0,y1=ty;y0<y;y0++,y1++)
{
p0=(col*)tmp->ScanLine[y0];
p1=(col*)bmp->ScanLine[y1];
for (x0=0,x1=tx;x0<x;x0++,x1++)
for (i=0;i<4;i++)
{
b=WORD(p0[x0].db[i])+WORD(p1[x1].db[i]);
if (b>255) b=255;
p1[x1].db[i]=b;
}
} ty+=dty;
txt="XOR";
tmp->Canvas->Brush->Style=bsSolid;
tmp->Canvas->Brush->Color=0x00000000; // max black
tmp->Canvas->Font ->Size=tys;
tmp->Canvas->Font ->Color=0x00FFFFFF; // max white
x0=bmp->Canvas->TextWidth(txt);
y0=bmp->Canvas->TextHeight(txt);
tmp->SetSize(x0,y0);
tmp->Canvas->FillRect(TRect(0,0,x0,y0));
tmp->Canvas->TextOutA(0,0,txt);
bmp->Canvas->CopyMode=cmSrcInvert;
bmp->Canvas->Draw(tx,ty,tmp); ty+=dty;
bmp->Canvas->CopyMode=cmSrcCopy;
delete tmp;
// render backbuffer
Main->Canvas->Draw(0,0,bmp);
_redraw=false;
}
it uses just VCL encapsulated GDI things so it should be clear enough...
bmp is backbuffer image
in is your image
tmp is temp image for custom combining the text and image

alpha channel blending issue in GLES2

I have a need where I need to have separate blending for alpha channel but its not working.
Let me explain with my test.
Alpha blending is enabled and I have set color and alpha write mask to true.
1) I am clearing my buffer with glClear(0,0,0,1)
2) I am drawing a big 2D quad which will have its color blend to half and will "set destination alpha values to zero" (-> this is what I need to achieve !)
Src Color is (1,1,1,0.5)
Here are the blend parameters I am setting
transparencyData.color_blendfunc = BlendInfo.BLEND_FUNC_ADD
transparencyData.alpha_blendfunc = BlendInfo.BLEND_FUNC_ADD
transparencyData.colorSrc_blendfactor = BlendInfo.BLEND_FACTOR_SRC_ALPHA
transparencyData.colorDst_blendfactor = BlendInfo.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA
transparencyData.alphaSrc_blendfactor = BlendInfo.BLEND_FACTOR_ZERO
transparencyData.alphaDst_blendfactor = BlendInfo.BLEND_FACTOR_ZERO
3) I am drawing a smaller 2D quad inside the above quad
Src Color is (1,1,1,1)
Here are the blend parameters I am setting
transparencyData.color_blendfunc = BlendInfo.BLEND_FUNC_ADD
transparencyData.alpha_blendfunc = BlendInfo.BLEND_FUNC_ADD
transparencyData.colorSrc_blendfactor = BlendInfo.BLEND_FACTOR_ONE_MINUS_DST_ALPHA
transparencyData.colorDst_blendfactor = BlendInfo.BLEND_FACTOR_DST_ALPHA
transparencyData.alphaSrc_blendfactor = BlendInfo.BLEND_FACTOR_ONE
transparencyData.alphaDst_blendfactor = BlendInfo.BLEND_FACTOR_ZERO
Now while quad rendered by step 2 appears half blended, my desired need where it sets the final alpha value of the destination to zero is not happening.
How I know this is because the quad rendered in step 3 should appear if destination alpha is zero and will not appear if destination alpha is one.
This is making me believe that either the alpha blending which might be happening is not working and getting written to the destination or I am doing something not right !!!
I am using glBlendEquationSeparate and glBlendFuncSeparate for this purpose.
Can you please shed light and help me in what I am trying to do ?

CreatePatternBrush and screen color depth

I am creating a brush using CreatePatternBrush with a bitmap created with CreateBitmap.
The bitmap is 1 pixel wide and 24 pixels tall, I have the RGB value for each pixel, so I create an array of rgbquads and pass that to CreateBitmap.
This works fine when the screen color depth is 32bpp, since the bitmap I create is also 32bpp.
When the screen color depth is not 32bpp, this fails, and I understand why it does, since I should be creating a compatible bitmap instead.
It seems I should use CreateCompatibleBitmap instead, but how do I put the pixel data I have into that bitmap?
I have also read about CreateDIBPatternBrushPt, CreateDIBitmap, CreateDIBSection, etc.
I donĀ“t understand what is a DIBSection, and find the subject generally confusing.
I do understand that I need a bitmap with the same color depth as the screen, but how do I create it having only the 32bpp pixel data?
You could create a DIB because you can use a Device Independent Bitmap independently of the screen color depth. See CreateDIBSection().
How can you create it having only the 32bpp pixel data? A DIB can be created with 32bpp data. As you can read in the documentation:
The CreateDIBSection function creates
a DIB that applications can write to
directly. The function gives you a
pointer to the location of the bitmap
bit values.
If hSection is NULL, the system
allocates memory for the DIB. If the
function succeeds, the return value is
a handle to the newly created DIB, and
*ppvBits points to the bitmap bit values.
Try something like this:
VOID *ppvBits = NULL;
BITMAPINFO BitmapInfo;
memset(&BitmapInfo, 0, sizeof(BITMAPINFOHEADER));
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = 1;
BitmapInfo.bmiHeader.biHeight = 24;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
HBITMAP hBitmap = CreateDIBSection(hDC, &BitmapInfo, DIB_RGB_COLORS, &ppvBits, NULL, 0);
In our case *ppvBits points to 1 * 24 * (32 / 8) allocated bytes.
It is important to know that if biHeight is positive, the bitmap is a bottom-up DIB and its origin is the lower-left corner. See BITMAPINFOHEADER Structure for more info.
I solved it by using CreateCompatibleBitmap and SetPixel. Not the best option I guess, but it works.

Resources