I'm trying to generate two random colors from a single array which are supposed not to be same. One of the colors is the bg color of the label, the other one is the text color of the text on top of it. But curiously i fail. Here is the code:
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class ColorText : MonoBehaviour {
public GUISkin mySkin;
public GameObject obj;
private Color clr;
private Color txtClr;
private string clrName;
Color[] colorList = new Color[13]{
new Color(0,0,255,255), // mavi
new Color(165,42,42,255), // kahverengi
new Color(192,192,192,255), // gri (192,192,192 gümüş)
new Color(0,128,0,255), // yeşil
new Color(128,0,0,255), // bordo
new Color(255,165,0,255), // turuncu
new Color(255,192,203,255), // pembe
new Color(128,0,128,255), // mor
new Color(255,0,0,255), // kırmızı
new Color(64,224,208,255), // turkuaz
new Color(255,255,0,255), // sarı
new Color(255,255,255,255), // beyaz
new Color(0,0,0,255), // siyah
};
string[] colorNames = new string[13]{
"Mavi",
"Kahverengi",
"Gri", // gri (192,192,192 gümüş)
"Yeşil", // yeşil
"Bordo", // bordo
"Turuncu", // turuncu
"Pembe", // pembe
"Mor", // mor
"Kırmızı", // kırmızı
"Turkuaz", // turkuaz
"Sarı", // sarı
"Beyaz", // beyaz
"Siyah", // siyah
};
// Use this for initialization
void Start () {
InvokeRepeating ("changeColors", 0, 1f);
}
void changeColors(){
int a, b;
a = (int)Random.Range (0, colorList.Length);
b = (int)Random.Range (0, colorList.Length);
if (a == b) {
do{
a = (int)Random.Range (0, colorList.Length);
b = (int)Random.Range (0, colorList.Length);
}while(a == b);
clr = colorList [a];
txtClr = colorList [b];
clrName = colorNames [Random.Range (0, colorNames.Length)];
} else {
clr = colorList [a];
txtClr = colorList [b];
clrName = colorNames [Random.Range (0, colorNames.Length)];
}
}
// Update is called once per frame
void Update () {
}
void OnGUI() {
GUI.skin = mySkin;
mySkin.box.fontStyle = FontStyle.Bold;
mySkin.box.normal.textColor = txtClr;
//GUI.backgroundColor = Color.blue;
Texture2D texture = new Texture2D(1, 1);
//Set new texture as background
mySkin.box.normal.background = texture;
//Set the texture color
texture.SetPixel(1,1,clr);
// Apply all SetPixel calls
texture.Apply();
/*float scalex = (float)(Screen.width) / 480.0f;
float scaley = (float)(Screen.height) / 800.0f;
GUI.matrix = Matrix4x4.TRS(new Vector3(0, 0, 0), Quaternion.identity, new Vector3(scalex, scaley, 1));*/
GUI.Box(new Rect(Screen.width/2 - 400,10,800,200), clrName, "box");
//GUI.Label(new Rect(Screen.width/2, 15, 100, 20), "Hello World!");
}
}
Any spotted point of i've been missing? Thanks for your help in advance.
Edit: I checked the indices to compare. They aren't same.
I have a slightly less efficient way for you to do this. First clone your array of colors to a temporary list that you can change however you want. Then remove the index of the first random color. Here is an example:
void changeColors()
{
List<Color> tempColors = new List<Color>(colorList);
// Get the first random index.
int a = (int)Random.Range (0, tempColors.Length);
// Assign the colour.
clr = tempColors[a];
// Remove the colour from the list so that it is impossible to select the same again.
tempColors.RemoveAt(a);
// Get second index.
int b = (int)Random.Range (0, tempColors.Length);
// Assign second colour.
txtClr = tempColors[b];
}
I hope this can help you.
EDIT:
If the problem turns out to be something else I think a code cleanup is appropriate here. Just to show you a different way of writing:
void changeColors(){
int a,b;
a = b = 0;
while (a == b)
{
a = (int)Random.Range (0, colorList.Length);
b = (int)Random.Range (0, colorList.Length);
}
clr = colorList [a];
txtClr = colorList [b];
clrName = colorNames [Random.Range (0, colorNames.Length)];
}
This should do exactly the same as your current code if I'm not mistaken.
Why did you putted it in invokerepeating if you only need two colors only one time and as a side note for invokerepeating start time don't put zero if you want it to start right away put a small value in start time
void Start () {
Changecolors();
}
"Color uses float values between 0 and 1, Color32 uses byte values between 0 and 255.
http://docs.unity3d.com/ScriptReference/Color-ctor.html
http://docs.unity3d.com/ScriptReference/Color32.html
Answer
alucardj solved it.
Related
I keep getting the error "func.h:23: error: cannot cast 'int' to 'struct Texture'" even though im inputting text as tile in renderTiles(). Am i just being dumb here? ¯_(ツ)_/¯ Im new to C so this might just be me. i copied and pasted the basic window example to create this. i know there isnt char before tile in "renderTiles(tile)" i tried that as well and it still did not work
main.c:
*
* raylib [core] example - Basic window
*
* Welcome to raylib!
*
* To test examples, just press F6 and execute raylib_compile_execute script
* Note that compiled executable is placed in the same folder as .c file
*
* You can find all basic examples on C:\raylib\raylib\examples folder or
* raylib official webpage: www.raylib.com
*
* Enjoy using raylib. :)
*
* Example originally created with raylib 1.0, last time updated with raylib 1.0
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2013-2022 Ramon Santamaria (#raysan5)
*
********************************************************************************************/
#include "raylib.h"
#include "func.h"
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 768;
const int screenHeight = 576;
InitWindow(screenWidth, screenHeight, "raylib [core] example - basic window");
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// textures
Texture2D grass = LoadTexture("grass.png");
Texture2D stone = LoadTexture("stone.png");
Texture2D sand = LoadTexture("sand.png");
Texture2D stone_oasis = LoadTexture("stone-oasis.png");
Texture2D sand_oasis = LoadTexture("sand-oasis.png");
Texture2D UI = LoadTexture("ui.png");
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
DrawTexture(UI, 0, 0, RAYWHITE);
renderTiles("grass");
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
func.h
#ifndef FUNC
#define FUNC
const int screenWidth = 768;
const int screenHeight = 576;
int centerX(int x)
{
int ox = x + (screenWidth / 2);
return (ox);
}
int centerY(int y)
{
int oy = y + (screenHeight / 2);
return(oy);
}
void renderTiles(tile)
{
for( int b = 0; b < 12; b = b + 1)
{
for( int a = 0; a < 12; a = a + 1 ){
DrawTexture(tile, (a * 48) + 96, (b * 48), RAYWHITE);
}
}
}
#endif
Answering the question title, you have already converted the string to a texture struct with
Texture2D grass = LoadTexture("grass.png");
The raylib cheat sheet shows the function you are calling to be
void DrawTexture(Texture2D texture, int posX, int posY, Color tint);
but you have passed the char* value "grass" to your function, which has an untyped argument, so the compiler assumes it to be type int.
Your function should be
/* void renderTiles(tile) */
void renderTiles(Texture2D tile)
and you should call it with
/* renderTiles("grass"); */
renderTiles(grass);
Also, you should not have executable code in a header file.
We are migrating from Lucene 4.0 to Solr Cloud 8.5.1, and I have a bounding polygon(shape) query that was generated using JTS. LatLong has 2 variable latitude and longitude that are double.
When the List<LatLong> points are passed to the method, it checks to see number of points passed and determines on the shape. For example, if only 1 point is passed, it generates a circle using the distance as radius. If more than 2 points are passed, it generates a linerrings etc.
I was able to generate Solr Query when 1 point is passed. How can I generate the Solr Query for LinerRings and other criteria.
private List<IndexSearchResult> getResultsInBoundingPolygon(List<LatLong> points, Integer distance) {
if (points.isEmpty()) {
return new ArrayList<IndexSearchResult>();
}
AirQuerySearcher searcher = (AirQuerySearcher) IndexServiceFactory.getIndexService(
AirLuceneSearchService.AIR_INDEX_SERVICE).getSearcher();
Shape shape;
SpatialArgs spatialArgs;
if(points.size() == 1){
spatialArgs = new SpatialArgs(SpatialOperation.Intersects,
searcher.getSpatialCtx().makeCircle(
points.get(0).getLongitude(),
points.get(0).getLatitude(),
DistanceUtils.dist2Degrees(distanceToSearch, DistanceUtils.EARTH_EQUATORIAL_RADIUS_MI)));
}else{
if (points.size() > 2) {
Coordinate[] coords = new Coordinate[points.size() + 1];
int i = 0;
for (LatLong point : points) {
coords[i] = new Coordinate(point.getLongitude(), point.getLatitude());
i++;
}
/*
* LinearRings apparently need to be closed rings, so this adds a
* copy of the first point at the end of the list to close the
* figure.
*/
coords[points.size()] = new Coordinate(coords[0].x, coords[0].y);
GeometryFactory fact = new GeometryFactory();
LinearRing linear = fact.createLinearRing(coords);
Polygon poly = new Polygon(linear, null, fact);
shape = (Shape) new JtsGeometry(poly, (JtsSpatialContext) searcher.getSpatialCtx(),true, true);
} else if (points.size() == 2) {
/*
* For two points, generates a circle (technically a configurable
* n-sided regular polygon defaulting to a dodecagon, but whatever)
* for each point, then takes the intersection of those and searches
* within the resulting shape.
*/
GeometryFactory fact = new GeometryFactory();
Point point1 = fact.createPoint(new Coordinate(points.get(0).getLongitude(), points.get(0).getLatitude()));
Point point2 = fact.createPoint(new Coordinate(points.get(1).getLongitude(), points.get(1).getLatitude()));
Geometry circle1 = point1.buffer(AirSearchConfig.getSinglePointSearchDegreeDiff(),
AirSearchConfig.getQuadrantSegments());
Geometry circle2 = point2.buffer(AirSearchConfig.getSinglePointSearchDegreeDiff(),
AirSearchConfig.getQuadrantSegments());
Geometry overlap = OverlayOp.overlayOp(circle1, circle2, OverlayOp.INTERSECTION);
shape = (Shape) new JtsGeometry(overlap, (JtsSpatialContext) searcher.getSpatialCtx(), true,true);
} else {
shape = searcher.getSpatialCtx().makeCircle(points.get(0).getLongitude(), points.get(0).getLatitude(),
AirSearchConfig.getSinglePointSearchDegreeDiff());
}
spatialArgs = new SpatialArgs(SpatialOperation.IsWithin, shape);
}
Filter filter = searcher.getSpatialStrategy().makeFilter(spatialArgs);
List<IndexSearchResult> results = searcher.executeQuery(new MatchAllDocsQuery(), filter,
AirSearchConfig.getMaxResultsInBoundingPolygon(), null, null);
if (results != null) {
logger.info("getResultsInBoundingPolygon-> Found " + results.size() + " results");
} else {
logger.info("getResultsInBoundingPolygon-> Result is empty");
}
return results;
}
I am super new to creating efi application. My aim is to create a small application in efi, that displays some text on a background. But I am stuck with trying to display text on the display (Great would be to have a custom font, but that is not necessary at this stage). I want the app (also) to run on apple systems (to boot from a usb)
How do I find good documentation on the EFI functions? It seems super hard to find good examples etc.
How can I display a text on a background with EFI?
This is what I got so far. I change the background to a color using the graphics protocol. How do I display a text on it. The Output String doesn't seem to work.
#include "efibind.h"
#include "efidef.h"
#include "efidevp.h"
#include "eficon.h"
#include "efiapi.h"
#include "efierr.h"
#include "efiprot.h"
static EFI_GUID GraphicsOutputProtocolGUID = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
/**
* efi_main - The entry point for the EFI application
* #image: firmware-allocated handle that identifies the image
* #SystemTable: EFI system table
*/
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systemTable) {
EFI_BOOT_SERVICES *bs = systemTable->BootServices;
EFI_STATUS status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *graphicsProtocol;
SIMPLE_TEXT_OUTPUT_INTERFACE *conOut = systemTable->ConOut;
EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *info;
UINTN SizeOfInfo, sWidth, sHeight;
status = bs->LocateProtocol(&GraphicsOutputProtocolGUID, NULL,
(void**)&graphicsProtocol);
if (EFI_ERROR(status) || graphicsProtocol == NULL) {
conOut->OutputString(conOut, L"Failed to init gfx!\r\n");
return status;
}
conOut->ClearScreen(conOut);
//Switch to current mode so gfx is started.
status = graphicsProtocol->SetMode(graphicsProtocol, graphicsProtocol->Mode->Mode);
if (EFI_ERROR(status)) {
conOut->OutputString(conOut, L"Failed to set default mode!\r\n");
return status;
}
EFI_GRAPHICS_OUTPUT_BLT_PIXEL p;
p.Red = 200;
p.Green = 77;
p.Blue = 13;
graphicsProtocol->QueryMode(graphicsProtocol, graphicsProtocol->Mode->Mode, &SizeOfInfo, &info);
sWidth = info->HorizontalResolution;
sHeight = info->VerticalResolution;
status = graphicsProtocol->Blt(graphicsProtocol, &p, EfiBltVideoFill, 0, 0, 0, 0, sWidth, sHeight, 0);
while (1) {
conOut->OutputString(conOut, L"Some text that I want to display\r\n");
bs->Stall(500000);
}
return EFI_SUCCESS;
}
UEFI supports graphics output. It also supports text output (which can mean either output to a serial console, or text rendered to a graphical console, or both). But there is no defined way to interact between these in a controlled manner.
Applications that provide a graphical environment with text elements (BIOS configuration menu, GRUB) generally do this using their own frameworks to draw text on the graphical console using GRAPHICS_OUTPUT_PROTOCOL.
This is a short example of a text renderer using the font module from LVGL (which can be used standalone, replace #include "../../lv_conf.h" in the lv_font.h file with #define USE_LV_FONT_DEJAVU_20 8) and the Blt method from the GRAPHICS_OUTPUT_PROTOCOL
#include <Uefi.h>
#include <Library\UefiLib.h>
#include <Protocol\GraphicsOutput.h>
#include "lv_font.h"
#define LETTER_SPACE 2
#define WAIT_SECONDS 10
#define FONT &lv_font_dejavu_20
static EFI_BOOT_SERVICES *gBS;
static EFI_RUNTIME_SERVICES *gRT;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *gGOP = (EFI_GRAPHICS_OUTPUT_PROTOCOL *)NULL;
static EFI_GRAPHICS_OUTPUT_BLT_PIXEL gWhite = { 255,255,255,0 };
static void _util_render_glyph(UINT32 x, UINT32 y, CHAR8 letter)
{
UINT32 height;
UINT32 width;
UINT32 pm_x;
UINT32 pm_y;
UINT32 index;
const UINT8* bitmap;
EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixelmap;
if (gGOP == NULL) {
return;
}
height = lv_font_get_height(FONT);
width = lv_font_get_width(FONT, letter);
// glyph is not defined in this font
if (width == 0) {
return;
}
bitmap = lv_font_get_bitmap(FONT, letter);
// using 8 bpp for simplicity
if (EFI_ERROR(gBS->AllocatePool(EfiLoaderData, height * width * sizeof(*pixelmap), (VOID**)&pixelmap))) {
return;
}
gBS->SetMem((VOID*)pixelmap, height * width * sizeof(*pixelmap), 0);
// get the current content of the framebuffer to allow 'transparent' blt operations
gGOP->Blt(gGOP, pixelmap, EfiBltVideoToBltBuffer, x, y, 0, 0, width, height, 0);
for (pm_y = 0; pm_y < height; pm_y++) {
for (pm_x = 0; pm_x < width; pm_x++) {
index = width * pm_y + pm_x;
if (bitmap[index] > 200) {
pixelmap[index].Red = 0;
pixelmap[index].Blue = 0;
pixelmap[index].Green = 0;
pixelmap[index].Reserved = 0;
}
else if (bitmap[index] > 100) {
pixelmap[index].Red = 105;
pixelmap[index].Blue = 105;
pixelmap[index].Green = 105;
pixelmap[index].Reserved = 0;
}
}
}
gGOP->Blt(gGOP, pixelmap, EfiBltBufferToVideo, 0, 0, x, y, width, height, 0);
gBS->FreePool(pixelmap);
}
static void _util_render_text(UINT32 x, UINT32 y, const CHAR8 *string)
{
UINT32 index;
UINTN length;
UINT32 scr_w;
UINT32 scr_h;
UINT32 str_x;
UINT32 gly_w;
UINT32 gly_h;
if (string == NULL) {
return;
}
if (gGOP == NULL) {
return;
}
scr_w = gGOP->Mode->Info->HorizontalResolution;
scr_h = gGOP->Mode->Info->VerticalResolution;
length = AsciiStrnLenS(string, 32);
gly_h = lv_font_get_height(FONT);
// check if the string can be printed
if ((y + gly_h) > scr_h) {
return;
}
if (x > scr_w) {
return;
}
// print the string glyph by glyph
str_x = x;
for (index = 0; index < length; index++) {
// check if the glyph can be printed
gly_w = lv_font_get_width(FONT, string[index]);
if ((str_x + gly_w) > scr_w) {
break;
}
// print the glyph
_util_render_glyph(str_x, y, string[index]);
// calculate the position of the next glyph
str_x += gly_w + LETTER_SPACE;
}
}
static void _util_fill_screen(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *color)
{
if (gGOP == NULL) {
return;
}
gGOP->Blt(gGOP, color, EfiBltVideoFill, 0, 0, 0, 0, gGOP->Mode->Info->HorizontalResolution, gGOP->Mode->Info->VerticalResolution, 0);
}
static void _util_wait(UINT32 seconds)
{
EFI_TIME time;
UINT8 current_second = 255;
UINT32 elapsed_seconds = 0;
//wait for some seconds
while (elapsed_seconds <= WAIT_SECONDS) {
if (!EFI_ERROR(gRT->GetTime(&time, (EFI_TIME_CAPABILITIES*)NULL))) {
if (current_second != time.Second) {
elapsed_seconds++;
current_second = time.Second;
}
}
else {
break;
}
CpuPause();
}
}
EFI_STATUS
EFIAPI
UefiMain(
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable)
{
EFI_STATUS eRc;
gBS = SystemTable->BootServices;
gRT = SystemTable->RuntimeServices;
eRc = gBS->LocateProtocol(
&gEfiGraphicsOutputProtocolGuid,
NULL,
(VOID**)&gGOP);
if (EFI_ERROR(eRc) || gGOP == NULL) {
return EFI_SUCCESS;
}
_util_fill_screen(&gWhite);
_util_render_text(0, 0, "HELLO WORLD!");
_util_wait(WAIT_SECONDS);
return EFI_SUCCESS;
}
I tested it on a pc and on a mac it runs on both. Using the tools provided by LVGL on their website you can use any font you want.
If you target MacEFI specifically, you'll need an additional protocol call to force the console into text mode, like this.
I wrote a program in c++ using Windows forms. I use two Forms. The first form only contains a button. When it is pressed, a second form opens. This form contains a panel where a simple OpenGL simulation is played (rotated with the help of a timer provided by windows forms). The second form can be closed and opend again by pressing the botton in the first Form. The more often this is done, the slower the 3D-OpenGL-object rotates. After doing this for about 6 times the 3D-OpenGL-object starts to flicker totally crazy. I think it has to do with the fact, that the OpenGL-Object I construct is not destroyed properly and at a certain point the memory is full (in a more complicated version of the project it was flickering between the current 3D-object and a 3D object that should have been destroyed after the window was closed).
Here is a video of the Problem.
here is the code of OpenGL:
namespace OpenGLForm
{
public ref class COpenGL: public System::Windows::Forms::NativeWindow
{
public:
// Position/Orientation of 3D Mesh in Mesh-Viewer Window
float meshPos_x;
float meshPos_y;
float meshPos_z;
float meshOri_x;
float meshOri_y;
float meshOri_z;
COpenGL(System::Windows::Forms::Panel ^ parentForm, GLsizei iWidth, GLsizei iHeight)
{
// initialize all parameter / set pointers (for pointers of type MAT)
meshPos_x = 0.0;
meshPos_y = 0.0;
meshPos_z = -2.0;
meshOri_x = -63.0;
meshOri_y = 0.0;
meshOri_z = 0.0;
CreateParams^ cp = gcnew CreateParams;
m_hDC = GetDC((HWND)parentForm->Handle.ToPointer());
System::String^ filename = "C:/Advantech/Desktop/Const.txt";
System::IO::StreamWriter^ csvWriter = gcnew System::IO::StreamWriter(filename, false, System::Text::Encoding::UTF8);
csvWriter->Write("Const");
csvWriter->Close();
if(m_hDC)
{
MySetPixelFormat(m_hDC);
ReSizeGLScene(iWidth, iHeight);
InitGL();
}
}
//custom function for transformations
System::Void Transform(float xTrans, float yTrans, float zTrans, float xRot, float yRot, float zRot)
{
//translate object
glTranslatef(xTrans, yTrans, zTrans);
//rotate along x-axis
glRotatef(xRot,1.0f,0.0f,0.0f);
//rotate along y-axis
glRotatef(yRot,0.0f,1.0f,0.0f);
//rotate along z-axis
glRotatef(zRot,0.0f,0.0f,1.0f);
}
System::Void Render(System::Void)
{
// Initial Settings
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear screen and depth buffer
glLoadIdentity();
meshOri_z = meshOri_z + 1;
// Set position and orientation of 3D mesh
Transform( meshPos_x,
meshPos_y,
meshPos_z,
meshOri_x,
meshOri_y,
meshOri_z );
glScalef( 0.05, 0.05, 0.05 );
int meshSize = 200;
// create 3D mesh Toplayer
for (int x = 1; x < meshSize; x++) {
for (int z = 1; z < meshSize; z++) {
glBegin(GL_QUADS);
int dm = 1;
glColor3f(dm,dm,dm);
glVertex3f( x, z, dm );
glVertex3f( (x+1), z, dm );
glVertex3f( (x+1), (z+1), dm );
glVertex3f( x, (z+1), dm );
glEnd();
}
}
}
System::Void SwapOpenGLBuffers(System::Void)
{
SwapBuffers(m_hDC) ;
}
private:
HDC m_hDC;
HGLRC m_hglrc;
protected:
~COpenGL(System::Void)
{
System::String^ filename = "C:/Advantech/Desktop/Dest.txt";
System::IO::StreamWriter^ csvWriter = gcnew System::IO::StreamWriter(filename, false, System::Text::Encoding::UTF8);
csvWriter->Write("Dest");
csvWriter->Close();
wglDeleteContext(m_hglrc);
DeleteDC(m_hDC);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
this->DestroyHandle();
}
GLint MySetPixelFormat(HDC hdc)
{
static PIXELFORMATDESCRIPTOR pfd= // pfd Tells Windows How We Want Things To Be
{
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
16, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
16, // 16Bit Z-Buffer (Depth Buffer)
0, // No Stencil Buffer
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
GLint iPixelFormat;
// get the device context's best, available pixel format match
if((iPixelFormat = ChoosePixelFormat(hdc, &pfd)) == 0)
{
MessageBox::Show("ChoosePixelFormat Failed");
return 0;
}
// make that match the device context's current pixel format
if(SetPixelFormat(hdc, iPixelFormat, &pfd) == FALSE)
{
MessageBox::Show("SetPixelFormat Failed");
return 0;
}
if((m_hglrc = wglCreateContext(m_hDC)) == NULL)
{
MessageBox::Show("wglCreateContext Failed");
return 0;
}
if((wglMakeCurrent(m_hDC, m_hglrc)) == NULL)
{
MessageBox::Show("wglMakeCurrent Failed");
return 0;
}
return 1;
}
bool InitGL(GLvoid) // All setup for opengl goes here
{
glShadeModel(GL_SMOOTH); // Enable smooth shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black background
glClearDepth(1.0f); // Depth buffer setup
glEnable(GL_DEPTH_TEST); // Enables depth testing
glDepthFunc(GL_LEQUAL); // The type of depth testing to do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really nice perspective calculations
return TRUE; // Initialisation went ok
}
GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize and initialise the gl window
{
if (height==0) // Prevent A Divide By Zero By
{
height=1; // Making Height Equal One
}
glViewport(0,0,width,height); // Reset The Current Viewport
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
}
System::Void detectBlack(){
}
};
}
the first form only contains a button to open the second form
private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) {
Form1^ freaker = gcnew Form1();
freaker->ShowDialog();
}
and here is the code of the second form:
private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) {
}
void Form1_Closing( Object^ /*sender*/, System::ComponentModel::CancelEventArgs^ e )
{
this->OpenGL->~COpenGL();
}
//Time tick for play button option
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e){
UNREFERENCED_PARAMETER(sender);
UNREFERENCED_PARAMETER(e);
OpenGL->Render();
OpenGL->SwapOpenGLBuffers();
}
After one week of searching and trying around here the answer:
I found a class to destroy openGL objects in Windows (wgl). This class alone did not solve the problem. I had to call all the commands, then switch buffers by calling SwapBuffers(m_hDC); and call all the commands again. Later on I recognized that it is enough only to call the commands g_hDC=NULL; and g_hWnd=NULL;. So this is how my destructor looks like now:
~COpenGL(System::Void){
if (g_bFullscreen) // Are We In Fullscreen Mode?
{
ChangeDisplaySettings(NULL,0); // If So Switch Back To The Desktop
ShowCursor(TRUE); // Show Mouse Pointer
}
if (g_hRC) // Do We Have A Rendering Context?
{
if (!wglMakeCurrent(NULL,NULL)) // Are We Able To Release The DC And RC Contexts?
{
MessageBox(NULL,TEXT("Release Of DC And RC Failed."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
}
if (!wglDeleteContext(g_hRC)) // Are We Able To Delete The RC?
{
MessageBox(NULL,TEXT("Release Rendering Context Failed."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
}
g_hRC=NULL; // Set RC To NULL
}
if (g_hDC && !ReleaseDC(g_hWnd,g_hDC)) // Are We Able To Release The DC
{
MessageBox(NULL,TEXT("Release Device Context Failed."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
g_hDC=NULL; // Set DC To NULL
}
if (g_hWnd && !DestroyWindow(g_hWnd)) // Are We Able To Destroy The Window?
{
MessageBox(NULL,TEXT("Could Not Release hWnd."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
g_hWnd=NULL; // Set hWnd To NULL
}
if (!UnregisterClass(TEXT("OpenGL"),g_hInstance)) // Are We Able To Unregister Class
{
MessageBox(NULL,TEXT("Could Not Unregister Class."),TEXT("SHUTDOWN ERROR"),MB_OK | MB_ICONINFORMATION);
g_hInstance=NULL; // Set hInstance To NULL
}
SwapBuffers(g_hDC);
g_hDC=NULL;
g_hWnd=NULL;
}
I'm working on an application in Visual Studio 2010 and I'm coding in C++/CLI.
In my previous questions I had issue with data series from a serial port. Now it seems ok and now I'm trying to plot them.
I don't want to use Chart, so I would like to use handmade functions that use the class Graphics. In order to test my code, I created two arrays: the first one is filled with values coming from a Gaussian. The second one is filled with random numbers.
When I plot the values, I would like to see my plot growing and updating just like an oscilloscope. The second Do_Plot manages to "remove point" plotting them by the BackColor.
All the code works as intended but I'm experiencing problem with performances. If I run the code on my Pc, my series is plotted every 500/700 ms.
Sometimes it slows down to 1500ms and then it comes back faster.
I tried to run the code on my coworkers' Pc and I noticed the series is plotted every 170ms on the first one, whereas the series is plotted every 950ms on the second one.
This is the code:
System::Void Form1::button1_Click(System::Object^ sender, System::EventArgs^ e) {
button1->Enabled = false;
array<float,1>^ Gauss = gcnew array<float,1>(1001);
array<float,1>^ Rumore = gcnew array<float,1>(1001);
/*Some useful variables*/
Random^ generatore = gcnew Random;
float a = safe_cast<float>(Math::Round(5/(SIGMA*Math::Sqrt(2*PI)), 2));
float b = safe_cast<float>(2*(SIGMA*SIGMA));
/*Start */
float portante;
float r;
float s;
int convX =1000/1000;
int convY =500/2;
/*time variables */
int bias = 50;
int dif =600;
/*Gap between drawing and removing*/
int k = 3;
int e1 = 0;
for ( ; ; ) {
/*Start*/
clock_t Start = clock();
if(textBox1->Text==""){
portn = 5;
}
else
portn = float::Parse(textBox1->Text);
/*temp variables to go out the for cycle */
portante = portn;
r = rand;
s = sig;
/ckeck state is OK */
check = 0;
for(int i = 1; i<=1000; i++) {
Gauss[i] = safe_cast<float>(Math::Round( a*s*Math::Exp(-Math::Pow(((0.01*1*(i))-portante), 2)/b), 2));
Rumore[i] = safe_cast<float>(Math::Round(r*generatore->NextDouble(), 2));
bool clipSup = ClipSup(2, Gauss[i]+Rumore[i]);
if(clipSup==true) {
Gauss[i] = 1.99f;
Rumore[i] = 0;
}
Do_Plot(g, disegna, i-1, Gauss[i-1]+Rumore[i-1], i, Gauss[i]+Rumore[i], convX, convY);
e1 =(k+i)%1000;
Do_Plot(g, rimuovi, e1, Gauss[e1]+Rumore[e1], e1+1, Gauss[e1+1]+Rumore[e1+1], convX, convY);
/*Ckeck if go out for cycle*/
if(check == CODE_1 ) {
portante = portn;
break;
}
if(check == CODE_2 ) {
r = rand;
break;
}
if(check == CODE_3 ) {
s = sig;
break;
}
}
clock_t Stop = clock();
int Diff = Stop-Start;
label8->Text = Convert::ToString(Diff);
int tempDiff = (Stop-Start)+bias;
if(tempDiff>dif)
{
//Do_Axes(g); /*Do Axes*/
//Do_Grid(g); /*Do Grid */
Application::DoEvents();
dif = 600;
bias = 0;
}
else
bias +=50; //Else bias grows
}
}
Where Do_Plot is:
void Do_Plot(Graphics^ g, Pen^ penna, int Xi, float Yi, int Xf, float Yf, int convX, int convY) {
g->DrawLine(penna, (convX*Xi+50), safe_cast<int>(500-(Yi*convY)+50),
(convX*Xf+50), safe_cast<int>(500-(Yf*convY)+50));
}
I have declared Graphics^ g here:
public ref class Form1 : public System::Windows::Forms::Form
{
Graphics^ g;
public:
Form1(void)
{
InitializeComponent();
//
//TODO: aggiungere qui il codice del costruttore.
//
g = pictureBox1->CreateGraphics();
}
Onestly I don't know why my code works so differently when it runs on another Pc. I think the problem is g = pictureBox1->CreateGraphics(); but I'm just doing some hypothesis. Any kind of help would be really appreciated cause I'm stuck on this one since the previous week!!
Thanks a lot!
Emiliano
I am going to give you the code in c/c++. Will then find how to do it in c++/cli
First initialize the objects we will use (begining of button_click):
RECT rt = {0, 0, 1200, 600};
HBITMAP hBitmap = NULL;
HPEN hLinePen = NULL;
HDC hdcPctrBox = NULL, hdc = NULL, hdcMemTempImage = NULL;
HBRUSH hBrush = NULL;
POINT arrayPnt[1000]; //array of points
hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcPctrBox = GetWindowDC(hwndPctrBox); //hwndPctrBox is the handle of picturebox
hdcMemTempImage = CreateCompatibleDC(hdc);
hBitmap = CreateCompatibleBitmap(hdc, 1200, 600);
SelectObject(hdcMemTempImage, hBitmap);
hLinePen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0)); //width of pen = 1,and color (255, 0, 0)
SelectObject(hdcMemTempImage, hLinePen);
hBrush = CreateSolidBrush(RGB(100, 100, 255)); //color of picturebox
Then:
for ( ; ; ) {
/*Start*/
clock_t Start = clock();
FillRect(hdcMemTempImage, &rt, hBrush); //clear the hdcMemTempImage with the picturebox color
//your code continues...
In the for(int i = 1; i<=1000; i++) add one point at a time:
arrayPnt[i - 1].x = X; //calculate X
arrayPnt[i - 1].x = Y; ////calculate Y
And finaly we draw it, after the for loop and before the clock_t Stop (we dont need the Do_Plot()):
Polyline(hdcMemTempImage, &arrayPnt, 1000);
BitBlt(hdcPctrBox, 0, 0, 1200, 600, hdcMemTempImage, 0, 0, SRCCOPY);
clock_t Stop = clock();
//your code continues...
EDIT: use your original code(you dont need rt, hBitmap, hBrush, hdcMemTempImage, arrayPnt and hdc) and instead of calling Do_Plot() add this code:
//create two pens, one with the color you want hLinePen, and another with the color of picturebox hLinePenErase
//inside for(int i = 1; i<=1000; i++)
(some code...)
if(clipSup==true) {
Gauss[i] = 1.99f;
Rumore[i] = 0;
}
SelectObject(hdcPctrBox, hLinePen); //the pen to draw the line
MoveToEx(hdcPctrBox, xStart, yStart, NULL); //start point of line
LineTo(hdcPctrBox, xEnd, yEnd); //end point of line
SelectObject(hdcPctrBox, hLinePenErase); //the pen to erase the line
MoveToEx(hdcPctrBox, xStartErase, yStartErase, NULL); //start point of line to erase
LineTo(hdcPctrBox, xEndErase, yEndErase); //end point of line to erase
(code continues...)
To delete your resources:
HPEN hLinePen = NULL, hLinePenErase = NULL, originalPen = NULL;
HDC hdcPctrBox = NULL;
hdcPctrBox = GetWindowDC(hwndPctrBox); //hwndPctrBox is the handle of picturebox
originalPen = SelectObject(hdcPctrBox, GetStockObject(DC_PEN));
//create the two pens
//in the end release resources
SelectObject(hdcPctrBox, originalPen);
DeleteObject(hLinePen);
DeleteObject(hLinePenErase);
ReleaseDC(hwndPctrBox, hdcPctrBox);
valter
I suggest to implement your own painting class. Derive your own class from "Panel" or "UserControl" and do the following in the constructor, which activates double buffering and optimized drawing:
public ref class MyPaintControl : public System::Windows::Forms::UserControl
{
public:
MyPaintControl()
{
this->SetStyle(System::Windows::Forms::ControlStyles::AllPaintingInWmPaint, true);
this->SetStyle(System::Windows::Forms::ControlStyles::DoubleBuffer, true);
this->SetStyle(System::Windows::Forms::ControlStyles::ResizeRedraw, true);
this->SetStyle(System::Windows::Forms::ControlStyles::UserPaint, true);
}
// And then implement your painting in OnPaint:
protected:
virtual void OnPaint(PaintEventArgs^ e)
{
// Do painting...
System::Drawing::Brush^ br = gcnew System::Drawing::SolidBrush(this->BackColor);
e->Graphics->FillRectangle(br, this->ClientRectangle);
delete br;
// Do other paintings...
}
// This function might be called if new data arrived...
public:
void UpdateArea(int daten) // pass data here, if you want...
{
// Store the data in a member variable...
// force a redraw:
this->InvalidateRect(); // this will force a "OnPaint"
}
}
Then embedd this control in your UI. This should improve your painting very much!
Also this works, even if you move or resize your window. Be aware, that painting should always be done in "OnPaint"!
I would like to update the state of my coding.
I used the method posted by Jochen. It is very fast but it does not suit my application or maybe I'm not able to adapt it. What I need to do, is to update constantly my plot. At time t0, I should draw a line. At time t1, when I store a new line, I should draw the new line and add this to the previous one. The plot must be "alive". I tried to derive a panel class and override the OnPaintevent but my plot refreshing only if all lines are stored and displayed.