I need to create a linear gradient brush to paint a surface with GDI+. On the input, I receive the following parameters:
Viewport rect: x = 0, y = 0, width = 744.09448819f, height = 1052.3622047f
Fill rect: x = 13.040037f, y = 17.478735f, width = 721.3703f, height = 1009.1535f
Gradient start: x = 384.8, y = 611.46
Gradient stop: x = 378.93, y = 474.96
Start color: a = 255, r = 179, g = 221, b = 253 (between 0 and 255)
End color: a = 255, r = 51, g = 102, b = 152 (between 0 and 255)
Matrix to apply: a = 7.7430985, b = 0, c = 0, d = 7.2926249, e = -2439.6639, f = -3446.2263
Wrap mode: clamp
These values were extracted from a SVG file. When I open it with my browser, I get the following result:
https://drive.google.com/open?id=0B7C-RwCTA9qaNnFsaGJpenlseGc
Now I try to draw the exact same gradient with GDI+. I use Embarcadero RAD Studio c++ builder to do that. I wrote the following code:
void DrawGradient()
{
std::auto_ptr<TBitmap> pBitmap(new TBitmap());
pBitmap->Width = 744.09448819f;
pBitmap->Height = 1052.3622047f;
pBitmap->Canvas->Brush->Color = clWhite;
pBitmap->Canvas->FillRect(TRect(0, 0, pBitmap->Width, pBitmap->Height));
Gdiplus::Graphics graphics(pBitmap->Canvas->Handle);
Gdiplus::LinearGradientBrush brush(
Gdiplus::PointF(384.8, 611.46),
Gdiplus::PointF(378.93, 474.96),
Gdiplus::Color(255, 179, 221, 253),
Gdiplus::Color(255, 51, 102, 152)
);
Gdiplus::Matrix matrix(7.7430985, 0, 0, 7.2926249, -2439.6639, -3446.2263);
brush.SetTransform(&matrix);
graphics.FillRectangle(&brush, 13.040037f, 17.478735f, 721.3703f, 1009.1535f);
pBitmap->SaveToFile(L"test.bmp");
}
However I get this result:
https://drive.google.com/open?id=0B7C-RwCTA9qaU1BhSTJxay16Z00
Putting aside the question of clamping, for which I know that GDI + offers no simple solution (like e.g. putting the Gdiplus::WrapModeClamp in the SetWrapMode() function, that could be so easy for the users), the resulting drawing should at least be closer to the expected result, or I'm wrong?
Somebody can explain to me why I get a so different result?
NOTE I already tweaked all the parameters, I obtain just several variations of the same wrong result
NOTE The original SVG that I refer above can be obtained here:
https://drive.google.com/open?id=0B7C-RwCTA9qaYkNRQ3lJNC1fNmM
Regards
Related
what is the meaning of bayer pattern codes in Opencv?
For example I have this code:
CV_BayerGR2BGR
my questions are:
What is the color of first pixel in matrix (so what is the color of pixel 0,0).
what is the color of pixel (x,y)?
Is there any simple and efficient way to find the color of pixel (x,y) based on a specific Bayer pattern? (so extend the code that I am writing to be used with every Bayer pattern).
Update:
As Miki pointed out, OpenCv document says:
The two letters C_1 and C_2 in the conversion constants CV_Bayer C_1 C_2 2BGR and CV_Bayer C_1 C_2 2RGB indicate the particular pattern type. These are components from the second row, second and third columns, respectively. For example, the above pattern has a very popular “BG” type.
which answers my first question, but what about the other two questions?
Knowing the type of the pattern, COLOR_Bayer<type>2BGR, you can easily find the color of each pixel checking if the coordinates are odd or even, since the pattern is simply a 2x2 that repeats over the whole image.
The OpenCV patterns are:
COLOR_BayerBG2BGR = 46,
COLOR_BayerGB2BGR = 47,
COLOR_BayerRG2BGR = 48,
COLOR_BayerGR2BGR = 49,
COLOR_BayerBG2RGB = COLOR_BayerRG2BGR,
COLOR_BayerGB2RGB = COLOR_BayerGR2BGR,
COLOR_BayerRG2RGB = COLOR_BayerBG2BGR,
COLOR_BayerGR2RGB = COLOR_BayerGB2BGR,
so you can simply check the first four.
The function
#define BLUE 0
#define GREEN 1
#define RED 2
int getColorFromBayer(int r, int c, int type)
will output the color, given the row r, the column c, and the type of the pattern type.
The following code shows how to recover the color of each pixel, and generates a BGR color image of the Bayer pattern.
#include <opencv2\opencv.hpp>
using namespace cv;
//COLOR_BayerBG2BGR = 46,
//COLOR_BayerGB2BGR = 47,
//COLOR_BayerRG2BGR = 48,
//COLOR_BayerGR2BGR = 49,
//
//COLOR_BayerBG2RGB = COLOR_BayerRG2BGR,
//COLOR_BayerGB2RGB = COLOR_BayerGR2BGR,
//COLOR_BayerRG2RGB = COLOR_BayerBG2BGR,
//COLOR_BayerGR2RGB = COLOR_BayerGB2BGR,
#define BLUE 0
#define GREEN 1
#define RED 2
int getColorFromBayer(int r, int c, int type)
{
static int bg[] = { RED, GREEN, GREEN, BLUE };
static int gb[] = { GREEN, RED, BLUE, GREEN };
static int rg[] = { BLUE, GREEN, GREEN, RED };
static int gr[] = { GREEN, BLUE, RED, GREEN };
int rr = r % 2;
int cc = c % 2;
switch (type)
{
case COLOR_BayerBG2BGR: return bg[2 * rr + cc];
case COLOR_BayerGB2BGR: return gb[2 * rr + cc];
case COLOR_BayerRG2BGR: return rg[2 * rr + cc];
case COLOR_BayerGR2BGR: return gr[2 * rr + cc];
}
return -1;
}
int main()
{
Mat3b bayer(10,10, Vec3b(0,0,0));
// Create bayer pattern BG
for (int r = 0; r < bayer.rows; ++r)
{
for (int c = 0; c < bayer.cols; ++c)
{
int color = getColorFromBayer(r,c,COLOR_BayerBG2BGR);
switch (color)
{
case BLUE : bayer(r, c) = Vec3b(255, 0, 0); break;
case GREEN: bayer(r, c) = Vec3b(0, 255, 0); break;
case RED : bayer(r, c) = Vec3b(0, 0, 255); break;
}
}
}
return 0;
}
Something like this pseudocode?
(note that CV_BayerBG2RGB = CV_BayerRG2BGR and work with one triplet type only )
if ((x+y) && 1) <> (CV_Bayerxxxxxx && 1)) then
C(x,y) = G
else
if (Pattern is CV_BayerBGxxx || CV_BayerGBxxx) == (y && 1) then
C(x,y) = B
else
C(x,y) = R
I am trying to create a text wrapper function using SDL1.2 + SDL_TTF. I am aware that SDL2.0 already has a readily available text wrapping function (TTF_RenderText_Blended_Wrapped) but I am using SDL1.2, therefore I am trying to create the wrapping function manually.
Basically I have two surfaces, one which is the wrapper surface and the other one a temporary surface which contains the rendered string. The rendered string is a string between two white spaces taken from the entire string which is to be wrapped. Each temporary surface (string part) is rendered and blit onto the wrapper surface and automatically jumps to the next line if the wrapper width is exceeded.
The string is eventually wrapped properly, however the problem comes with the wrapper surface. Initially I create a colored surface with SDL_CreateRGBSurface() because to my understanding I cannot blit the string parts onto an empty surface. After the wrapping, I want to remove the unused space on the wrapper surface so that the the surface is transparent besides the text.
For this I used color-keying but noticed that that is a poor solution since it also removes some of the text color. My other attempt was to use the alpha values on both surfaces but using SDL_SetAlpha() but this somehow resulted into the same result with the text color being all wrong.
Below is an example of the code I wrote, I would really appreciate some alternative ideas or solutions.
TTF_Font* textFont = nullptr;
SDL_Color textColor = {0, 0, 0};
std::string wrapperText = "This text is to be wrapped with transparency";
SDL_Surface* wrapperSurface = nullptr;
SDL_Rect wrapperRect = {0, 0, 160, 50};
std::string tempString = "";
SDL_Surface* tempSurface = nullptr;
SDL_Rect tempRect = {0, 0, 0, 0};
int lineWidthPx = 0;
int lineHeightPx = 0;
int textWidthPx = 0;
int textHeightPx = 0;
std::size_t textBegin = 0;
std::size_t textEnd = 0;
textFont = TTF_OpenFont("res/fonts/Nunito-Black.ttf", 14);
wrapperSurface = SDL_CreateRGBSurface(SDL_HWSURFACE, wrapperRect.w, wrapperRect.h, 32, 255, 255, 255, 255);
SDL_SetAlpha(wrapperSurface, SDL_SRCALPHA | SDL_RLEACCEL, SDL_ALPHA_TRANSPARENT);
while (textEnd < wrapperText.size())
{
textBegin = textEnd + 1;
textEnd = wrapperText.find(" ", textBegin);
if (textEnd == std::string::npos)
textEnd = wrapperText.size(); // Reached end of text
tempString = wrapperText.substr(textBegin - 1, textEnd - textBegin + 1);
TTF_SizeText(textFont, tempString.c_str(), &textWidthPx, &textHeightPx);
lineWidthPx += textWidthPx;
if (lineWidthPx > wrapperRect.w)
{
tempString = wrapperText.substr(textBegin, textEnd - textBegin);
lineWidthPx = 0 + textWidthPx;
lineHeightPx += textHeightPx; // Next line
if (lineHeightPx > wrapperRect.h)
break; // Text is too large for wrapper
}
tempSurface = TTF_RenderUTF8_Solid(textFont, tempString.c_str(), textColor);
SDL_SetAlpha(tempSurface, SDL_SRCALPHA | SDL_RLEACCEL, SDL_ALPHA_OPAQUE);
tempRect = { static_cast<Sint16>(lineWidthPx-textWidthPx), static_cast<Sint16>(lineHeightPx), static_cast<Uint16>(textWidthPx), static_cast<Uint16>(textHeightPx) };
SDL_BlitSurface(tempSurface, NULL, wrapperSurface, &tempRect);
}
TTF_CloseFont(textFont);
textFont = nullptr;
// Remove temporary surface
SDL_FreeSurface(tempSurface);
tempSurface = nullptr;
I have a code, which converts plain BGR data to sf::Texture. "ifs" is opened ifstream to file which contains byte triplets of BGR colors (header of source file is omitted). And Width & Height 100% valid. In my example image is 800x600.
struct h3pcx_color_bgr { uint8_t b, uint8_t g, uint8_t r };
sf::Uint8* pixels = new sf::Uint8[width * height * 4];
h3pcx_color_bgr* fileData = new h3pcx_color_bgr[width*height];
ifs.read((char*)fileData, width * height * sizeof(h3pcx_color_bgr));
for (uint32_t i = 0; i < width*height; ++i) {
pixels[i * 4] = fileData[i].r;
pixels[i * 4 + 1] = fileData[i].g;
pixels[i * 4 + 2] = fileData[i].b;
pixels[i * 4 + 3] = 255;
}
This code works good, problem comes after. Once I draw my texture:
m_tex.update(pixels); //sf::Texture
m_sprite.setTexture(m_tex); //sf::Sprite
m_window->draw(m_sprite); // m_window is sf::RenderWindow
I have this annoying grey line in image below:
What I did:
Verified, that pixels contains valid value
Code snippet below (700 * 595 is inside "grey area") shows, that both pixels and fileData contains valid data (not grey color, which is appears just uninitialized memory).
auto f = fileData[700 * 595]; // 32, 31, 38
auto r = pixels[700 * 595 * 4]; // 38
auto g = pixels[700 * 595 * 4 + 1]; // 31
auto b = pixels[700 * 595 * 4 + 2]; // 32
"Grey" color is 204, 204, 204.
Tried to use sf::Image
If we do something like this:
img.create(width, height, pixels); // img is sf::Image
img.setPixel(700, 595, sf::Color::Blue);
And then convert it to sf::Texture, and draw. Result will be same image with grey area, but pixel 700, 585 will be blue!
If I get color value from "grey area":
auto clr = img.getPixel(700,600); //sf::Color(204,204,204)
So, it looks like, there are some hard-limit(???) on quantity of pixels (but I doubt it, since I've looked on actual SFML code, and did not found anything suspicious) or my stupid mistake. I would be very grateful, if someone can point out - why this grey line appears.
In the code:
auto f = fileData[700 * 595];
You are accessing pixel 500, 520. To access the pixel 700, 595 you have to use:
auto f = fileData[700 + 595 * 800]; // x + y * width
I would write this as a comment, but I lack the necessary reputation.
If any1 is wondering - It's just file is wrong, with this exact grey color in the end. Code is correct.
I have a list of buttons(currently images) and i want to change the image color depending on the value that is taken from the list
buttons = {"btn1","btn2","btn3","btn4"}
local buttonSheetData = {
width = 150,
height = 150,
numFrames = 2,
sheetContentWidth = 300,
sheetcontentheight = 150,
}
local buttonSheet = graphics.newImageSheet("image/buttonSS.png", buttonSheetData)
local sequenceData = {
{name = "black", start = 1, count = 1},
{name = "red", start = 2, count = 1}
}
local btn1 = display.newSprite(buttonSheet, sequenceData)
btn1.x = 100
btn1.y = 90
local btn2 = display.newSprite(buttonSheet, sequenceData)
btn2.x = 200
btn2.y = 230
local btn3 = display.newSprite(buttonSheet, sequenceData)
btn3.x = 300
btn3.y = 90
local btn4 = display.newSprite(buttonSheet, sequenceData)
btn4.x = 400
btn4.y = 230
x = buttons[math.random(#buttons)]
x:setFrame(2)
The circles are currently black. every time i run the code i want it to take a random value from the list and change the color to red. so there is a different red circle when i run the code
I keep getting the error:
"Attemp to call method 'setSequence' (a nill value)"
You're code is treating strings and variable names as if they are interchangeable. The first line:
buttons = {"btn1","btn2","btn3","btn4"}
creates a table of strings, so the line:
x = buttons[math.random(#buttons)]
will set x to be a random entry of buttons which are strings so the next line (x:setFrame(2)) is calling a method that doesn't exist on a string.
Instead, create a table of your buttons:
buttons = {btn1,btn2,btn3,btn4} -- creates a table of buttons
x = buttons[math.random(#buttons)] -- x is a random entry of buttons (a button)
I have a problem due to my terrible math abilities, that I cannot figure out how to scale a graph based on the maximum and minimum values so that the whole graph will fit onto the graph-area (400x420) without parts of it being off the screen (based on a given equation by user).
Let's say I have this code, and it automatically draws squares and then the line graph based on these values. What is the formula (what do I multiply) to scale it so that it fits into the small graphing area?
vector<int> m_x;
vector<int> m_y; // gets automatically filled by user equation or values
int HeightInPixels = 420;// Graphing area size!!
int WidthInPixels = 400;
int best_max_y = GetMaxOfVector(m_y);
int best_min_y = GetMinOfVector(m_y);
m_row = 0;
m_col = 0;
y_magnitude = (HeightInPixels/(best_max_y+best_min_y)); // probably won't work
x_magnitude = (WidthInPixels/(int)m_x.size());
m_col = m_row = best_max_y; // number of vertical/horizontal lines to draw
////x_magnitude = (WidthInPixels/(int)m_x.size())/2; Doesn't work well
////y_magnitude = (HeightInPixels/(int)m_y.size())/2; Doesn't work well
ready = true; // we have values, graph it
Invalidate(); // uses WM_PAINT
////////////////////////////////////////////
/// Construction of Graph layout on WM_PAINT, before painting line graph
///////////////////////////////////////////
CPen pSilver(PS_SOLID, 1, RGB(150, 150, 150) ); // silver
CPen pDarkSilver(PS_SOLID, 2, RGB(120, 120, 120) ); // dark silver
dc.SelectObject( pSilver ); // silver color
CPoint pt( 620, 620 ); // origin
int left_side = 310;
int top_side = 30;
int bottom_side = 450;
int right_side = 710; // create a rectangle border
dc.Rectangle(left_side,top_side,right_side,bottom_side);
int origin = 310;
int xshift = 30;
int yshift = 30;
// draw scaled rows and columns
for(int r = 1; r <= colrow; r++){ // draw rows
pt.x = left_side;
pt.y = (ymagnitude)*r+top_side;
dc.MoveTo( pt );
pt.x = right_side;
dc.LineTo( pt );
for(int c = 1; c <= colrow; c++){
pt.x = left_side+c*(magnitude);
pt.y = top_side;
dc.MoveTo(pt);
pt.y = bottom_side;
dc.LineTo(pt);
} // draw columns
}
// grab the center of the graph on x and y dimension
int top_center = ((right_side-left_side)/2)+left_side;
int bottom_center = ((bottom_side-top_side)/2)+top_side;
You are using ax^2 + bx + c (quadratic equation). You will get list of (X,Y) values inserted by user.
Let us say 5 points you get are
(1,1)
(2,4)
(4,1)
(5,6)
(6,7)
So, here your best_max_y will be 7 and best_min_y will be 1.
Now you have total graph area is
Dx = right_side - left_side //here, 400 (710 - 310)
Dy = bottom_side - top_side //here, 420 (450 - 30)
So, you can calculate x_magnitude and y_magnitude using following equation :
x_magnitude = WidthInPixels / Dx;
y_magnitude = HeightInPixels / Dy;
What I did was to determine how many points I had going in the x and y directions, and then divide that by the x and y dimensions, then divide that by 3, as I wanted each minimum point to be three pixels, so it could be seen.
The trick then is that you have to aggregate the data so that you are showing several points with one point, so it may be the average of them, but that depends on what you are displaying.
Without knowing more about what you are doing it is hard to make a suggestion.
For this part, subtract, don't add:
best_max_y+best_min_y as you want the difference.
The only other thing would be to divide y_magnitude and x_magnitude by 3. That was an arbitrary number I came up with, just so the users could see the points, you may find some other number to work better.