Draw tbitmap with scale and alpha channel faster - graphics

The following code copies a large bitmap blends it with the correct background and then draws a semi transparent image with a clipped region to save draw time... Images are in an array and prescaled...
This has been through several levels of optimization based on my limited knowledge of C++ and the Builder graphics...
Edit: Updated code... blend();
void blend(Graphics::TBitmap *dst,int x,int y,Graphics::TBitmap *src,BYTE
alpha)
{
const int n=3; // pixel align [Bytes]
int dx0,dy0,dx1,dy1, // dst BBOX
sx0,sy0,sx1,sy1, // src BBOX
dx,dy,sx,sy,i;
BYTE *dp,*sp;
WORD a,_a,sc,dc,da[256],sa[256];
// compute BBOX (handle clipping)
dx=src->Width; dy=src->Height;
dx0=x; sx0=0; dx1=x+dx; sx1=dx;
dy0=y; sy0=0; dy1=y+dy; sy1=dy;
// blend
a=alpha; _a=255-a;
for (i=0;i<256;i++){ da[i]=_a*i; sa[i]=a*i; } // precompute BYTE*a and
BYTE*_a LUTs
for (dy=dy0,sy=sy0;dy<dy1;dy++,sy++) // ScanLines
{
dp=(BYTE*)dst->ScanLine[dy]+(n*dx0);
sp=(BYTE*)src->ScanLine[sy]+(n*sx0);
for (dx=dx0,sx=sx0;dx<dx1;dx++,sx++) // single ScanLine
for (i=0;i<n;i++,dp++,sp++) // RGB
*dp=WORD((sa[*sp]+da[*dp])>>8); // blend function
}
}
//--------------------------------------------------------------------------
det1maps.push_back( new Graphics::TBitmap() );
for (int i = 1; i < 176; i++)
{
det1maps.push_back( new Graphics::TBitmap() );
det1maps[i]->SetSize(t,t);
det1maps[i]->Canvas->StretchDraw(Rect(0, 0, t, t), Det1_bmp.get()); // scale
t = t + 24;
}
//------------------EDIT 3 Current version 1/18
det1maps[ss]->Transparent = true;
Form1->imgTemp->Picture->Assign(layer0_bmap.get()); //why background first?
HRGN MyRgn;
MyRgn = ::CreateRectRgn(0,0,Sw,Sh);
::SelectClipRgn(Form1->imgTemp->Canvas->Handle,MyRgn); //clip
Form1->imgTemp->Canvas->Draw(X3,Y3,det1maps[ss]); // draw det
blend(layer0_bmap.get(),0,0,Form1->imgTemp->Picture->Bitmap,int(obj[index]));

Here small simple C++/VCL ScanLine Alpha Blend example I just put together:
//---------------------------------------------------------------------------
void blend(Graphics::TBitmap *dst,int x,int y,Graphics::TBitmap *src,BYTE alpha)
{
const int n=3; // pixel align [Bytes]
int dx0,dy0,dx1,dy1, // dst BBOX
sx0,sy0,sx1,sy1, // src BBOX
dx,dy,sx,sy,i;
BYTE *dp,*sp;
WORD a,_a,sc,dc,da[256],sa[256];
// compute BBOX (handle clipping)
dx=src->Width; dy=src->Height;
dx0=x; sx0=0; dx1=x+dx; sx1=dx;
dy0=y; sy0=0; dy1=y+dy; sy1=dy;
if (dx0<0){ sx0-=dx0; dx0=0; }
if (dy0<0){ sy0-=dy0; dy0=0; }
dx=dst->Width; dy=dst->Height;
if (dx1>dx){ sx1+=dx-dx1; dx1=dx; }
if (dy1>dy){ sy1+=dy-dy1; dy1=dy; }
// make sure config is compatible with ScanLine[]
dst->HandleType=bmDIB; dst->PixelFormat=pf24bit;
src->HandleType=bmDIB; src->PixelFormat=pf24bit;
// blend
a=alpha; _a=255-a;
for (i=0;i<256;i++){ da[i]=_a*i; sa[i]=a*i; } // precompite BYTE*a and BYTE*_a LUTs
for (dy=dy0,sy=sy0;dy<dy1;dy++,sy++) // ScanLines
{
dp=(BYTE*)dst->ScanLine[dy]+(n*dx0);
sp=(BYTE*)src->ScanLine[sy]+(n*sx0);
for (dx=dx0,sx=sx0;dx<dx1;dx++,sx++) // single ScanLine
for (i=0;i<n;i++,dp++,sp++) // RGB
*dp=WORD((sa[*sp]+da[*dp])>>8); // blend function
}
}
//---------------------------------------------------------------------------
I just process the image on per pixel/channel basis and for each channel (R,G,B) compute:
dst_pixel = ( src_pixel*alpha + dst_pixel*(255-alpha) )/255
where channels and alpha are 8 bit unsigned integers... For speed I used 24 bit pixel format (usually I use 32bit instead).
To avoid *,/ in teh Blending I precomputed 2 LUTs with all posible combinations of number*alpha and number*(255-alpha). The division is done by bit shift >>8.
To improve speed you can remember all ScanLine[] of the dst image into your array once and then use that as the target image will be used many times ...
When I tested this on blending 2 1024x768 images together it took <=9ms on mine setup. The slowest operation is the ScanLine[] access, and images where formated to the pixel format prior to blending...
Here GIF preview (scaled down 1/4 and dithered by my capturer so it fits to imgur 2MByte limit):
This is the code I used for this (single timer VCL App):
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
#include <math.h>
#include <jpeg.hpp>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
Graphics::TBitmap *bmp,*bmp0,*bmp1; // back buffer, image0, image1, ...
//---------------------------------------------------------------------------
void blend(Graphics::TBitmap *dst,int x,int y,Graphics::TBitmap *src,BYTE alpha)
{
const int n=3; // pixel align [Bytes]
int dx0,dy0,dx1,dy1, // dst BBOX
sx0,sy0,sx1,sy1, // src BBOX
dx,dy,sx,sy,i;
BYTE *dp,*sp;
WORD a,_a,sc,dc,da[256],sa[256];
// compute BBOX (handle clipping)
dx=src->Width; dy=src->Height;
dx0=x; sx0=0; dx1=x+dx; sx1=dx;
dy0=y; sy0=0; dy1=y+dy; sy1=dy;
if (dx0<0){ sx0-=dx0; dx0=0; }
if (dy0<0){ sy0-=dy0; dy0=0; }
dx=dst->Width; dy=dst->Height;
if (dx1>dx){ sx1+=dx-dx1; dx1=dx; }
if (dy1>dy){ sy1+=dy-dy1; dy1=dy; }
// make sure config is compatible with ScanLine[]
dst->HandleType=bmDIB; dst->PixelFormat=pf24bit;
src->HandleType=bmDIB; src->PixelFormat=pf24bit;
// blend
a=alpha; _a=255-a;
for (i=0;i<256;i++){ da[i]=_a*i; sa[i]=a*i; } // precompite BYTE*a and BYTE*_a LUTs
for (dy=dy0,sy=sy0;dy<dy1;dy++,sy++) // ScanLines
{
dp=(BYTE*)dst->ScanLine[dy]+(n*dx0);
sp=(BYTE*)src->ScanLine[sy]+(n*sx0);
for (dx=dx0,sx=sx0;dx<dx1;dx++,sx++) // single ScanLine
for (i=0;i<n;i++,dp++,sp++) // RGB
*dp=WORD((sa[*sp]+da[*dp])>>8); // blend function
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void TMain::draw()
{
bmp->Canvas->Draw(0,0,bmp0); // render background bmp0
static float a=0.0; a+=0.025*M_PI;
blend(bmp,0,0,bmp1,fabs(255.0*sin(a))); // alfa blend in bmp1
Main->Canvas->Draw(0,0,bmp); // show result on screen
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
// create bitmaps
bmp=new Graphics::TBitmap;
bmp0=new Graphics::TBitmap;
bmp1=new Graphics::TBitmap;
// laod images
TJPEGImage *jpg=new TJPEGImage;
jpg->LoadFromFile("img0.jpg"); bmp0->Assign(jpg);
jpg->LoadFromFile("img1.jpg"); bmp1->Assign(jpg);
delete jpg;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
// delete bitmaps
delete bmp0;
delete bmp1;
delete bmp;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
{
bmp->Width =ClientWidth;
bmp->Height=ClientHeight;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
And here the images (first nice 1024x768 images I found on Google images):
Here preview of Blending result:
For more info about the ScanLine see:
gfx rendering
If you need even more speed then you should go for GPU Blending (OpenGL or DirectX).
[Edit2] array + rectangle example
After your edited your question its now obvious:
your array of bitmaps is not an array at all
its rather some kind of list template like vector<Graphics::TBitmap*> or similar... So you do not have access to linear array of the bmps like I do. To make your life easier I used mine template with similar properties so you can see how to handle those (sorry I can not share the template code but you just need to change List<T> into Vector<T> or whatever you are using ...
This is the reason why the array pointer did not work for you as you do not have one. Its possibly your template expose it with some member. Mine does it like map.dat so yours might have something similar or not at all if not stored linearly.
You are blending just 2 images not the whole array
so you can use the first example and add the ScanLine preloading as your images are static... Do the same for backbuffer image as that changes only after resize.
When I put all together here the result:
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "win_main.h"
#include <math.h>
#include <jpeg.hpp>
#include "list.h" // mine list<T> template you got probably vector<> or something similar instead
#include "performance.h" // this is mine tbeg/tend/tstr time measurement
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
// [back buffer]
Graphics::TBitmap *bmp; // bitmap
BYTE **bmp_pyx=NULL; // preloaded ScanLines [y][x]
void bmp_init() // create preloaded ScanLines
{
bmp_pyx=new BYTE*[bmp->Height];
for (int y=0;y<bmp->Height;y++)
bmp_pyx[y]=(BYTE*)bmp->ScanLine[y];
}
void bmp_exit() // release preloaded ScanLines
{
delete[] bmp_pyx;
}
//---------------------------------------------------------------------------
// [array of images]
const AnsiString filename[]= // filenames
{
"img0.jpg",
"img1.jpg",
"img2.jpg",
"img3.jpg",
"img4.jpg",
"img5.jpg",
"img6.jpg",
"img7.jpg",
"img8.jpg",
"img9.jpg",
""
};
List<Graphics::TBitmap*> map; // your "array" of bitmaps
int maps=0; // number of images
BYTE ***map_pyx=NULL; // preloaded ScanLines [ix][y][x]
//---------------------------------------------------------------------------
void map_init() // alocate and prepare data
{
int i,y;
Graphics::TBitmap *bmp;
TJPEGImage *jpg=new TJPEGImage;
// create "array" of bmp (you already got this)
for (maps=0;filename[maps]!="";maps++)
{
map.add(new Graphics::TBitmap); // this is like your push_back(new Graphics::TBitmap)
jpg->LoadFromFile(filename[maps]); // filename[] -> jpg -> bmp -> map[]
map[maps]->Assign(jpg); // here you can also rescale or whatever you want to do...
map[maps]->HandleType=bmDIB;
map[maps]->PixelFormat=pf24bit;
}
// create preloaded ScanLines (you need to add this into your app init)
map_pyx=new BYTE**[maps]; // **map_pyx[]
for (i=0;i<maps;i++)
{
map_pyx[i]=new BYTE*[map[i]->Height]; // *map_pyx[][]
for (y=0;y<map[i]->Height;y++) // map_pyx[][]]
map_pyx[i][y]=(BYTE*)map[i]->ScanLine[y];
}
delete jpg;
}
//---------------------------------------------------------------------------
void map_exit() // release data (you need to add this in app exit)
{
int i;
for (i=0;i<maps;i++)
{
delete map[i];
delete[] map_pyx[i];
}
delete[] map_pyx;
}
//---------------------------------------------------------------------------
void blend_rec(BYTE **dp,int x0,int y0,int x1,int y1,BYTE **sp,BYTE alpha)
{
const int n=3; // pixel align [Bytes]
int x,y,i;
BYTE *d,*s;
WORD da[256],sa[256];
// pixelformat align
x0*=n; x1*=n;
// prepare alpha*BYTE and (255-alpha)*BYTE LUTs
y= alpha; for (x=0;x<256;x++) sa[x]=x*y;
y=255-alpha; for (x=0;x<256;x++) da[x]=x*y;
// blend
for (y=y0;y<y1;y++)
{
d=dp[y]+x0;
s=sp[y]+x0;
for (x=x0;x<x1;x++,d++,s++)
*d=WORD((sa[*s]+da[*d])>>8); // blend function
}
// release data
}
//---------------------------------------------------------------------------
void TMain::draw()
{
bmp->Canvas->Draw(0,0,map[0]); // render background bmp[0]
static float a=0.0; a+=0.025*M_PI; // animation ...
BYTE alpha=128+float(127.0*sin(a));
tbeg();
blend_rec(bmp_pyx,200,500,400,600,map_pyx[1],alpha); // add the blended rectangle (except background which is bmp[0]
tend(); Caption=tstr();
Canvas->Draw(0,0,bmp); // show on screen
// bmp->SaveToFile("out.bmp");
}
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
{
// create bitmaps
bmp=new Graphics::TBitmap;
bmp_init();
map_init();
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
// delete bitmaps
delete bmp;
bmp_exit();
map_exit();
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
{
bmp->Width =ClientWidth;
bmp->Height=ClientHeight;
bmp->HandleType=bmDIB;
bmp->PixelFormat=pf24bit;
bmp_exit();
bmp_init();
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
{
draw();
}
//---------------------------------------------------------------------------
The blending is done in less than 0.5ms on my setup for teh rectangle I choosed. As you can see its way faster than original 9ms ... Because if you are using cliping region you will still blend whole image just not copy the result. This approach only Blends and copy what is needed.
Beware I removed the range checks so make sure the rectangle is inside of images ...
If you want to measure the time in the same way I do I am using this code of mine:
Performance.h:
//---------------------------------------------------------------------------
//--- Performance counter time measurement: 2.01 ----------------------------
//---------------------------------------------------------------------------
#ifndef _performance_h
#define _performance_h
//---------------------------------------------------------------------------
const int performance_max=64; // push urovni
double performance_Tms=-1.0, // perioda citaca [ms]
performance_tms=0.0, // zmerany cas po tend [ms]
performance_t0[performance_max]; // zmerane start casy [ms]
int performance_ix=-1; // index aktualneho casu
//---------------------------------------------------------------------------
void tbeg(double *t0=NULL) // mesure start time
{
double t;
LARGE_INTEGER i;
if (performance_Tms<=0.0)
{
for (int j=0;j<performance_max;j++) performance_t0[j]=0.0;
QueryPerformanceFrequency(&i); performance_Tms=1000.0/double(i.QuadPart);
}
QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms;
if (t0) { t0[0]=t; return; }
performance_ix++;
if ((performance_ix>=0)&&(performance_ix<performance_max)) performance_t0[performance_ix]=t;
}
//---------------------------------------------------------------------------
void tpause(double *t0=NULL) // stop counting time between tbeg()..tend() calls
{
double t;
LARGE_INTEGER i;
QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms;
if (t0) { t0[0]=t-t0[0]; return; }
if ((performance_ix>=0)&&(performance_ix<performance_max)) performance_t0[performance_ix]=t-performance_t0[performance_ix];
}
//---------------------------------------------------------------------------
void tresume(double *t0=NULL) // resume counting time between tbeg()..tend() calls
{
double t;
LARGE_INTEGER i;
QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms;
if (t0) { t0[0]=t-t0[0]; return; }
if ((performance_ix>=0)&&(performance_ix<performance_max)) performance_t0[performance_ix]=t-performance_t0[performance_ix];
}
//---------------------------------------------------------------------------
double tend(double *t0=NULL) // return duration [ms] between matching tbeg()..tend() calls
{
double t;
LARGE_INTEGER i;
QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms;
if (t0) { t-=t0[0]; performance_tms=t; return t; }
if ((performance_ix>=0)&&(performance_ix<performance_max)) t-=performance_t0[performance_ix]; else t=0.0;
performance_ix--;
performance_tms=t;
return t;
}
//---------------------------------------------------------------------------
double tper(double *t0=NULL) // return duration [ms] between tper() calls
{
double t,tt;
LARGE_INTEGER i;
if (performance_Tms<=0.0)
{
for (int j=0;j<performance_max;j++) performance_t0[j]=0.0;
QueryPerformanceFrequency(&i); performance_Tms=1000.0/double(i.QuadPart);
}
QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms;
if (t0) { tt=t-t0[0]; t0[0]=t; performance_tms=tt; return tt; }
performance_ix++;
if ((performance_ix>=0)&&(performance_ix<performance_max))
{
tt=t-performance_t0[performance_ix];
performance_t0[performance_ix]=t;
}
else { t=0.0; tt=0.0; };
performance_ix--;
performance_tms=tt;
return tt;
}
//---------------------------------------------------------------------------
AnsiString tstr()
{
AnsiString s;
s=s.sprintf("%8.3lf",performance_tms); while (s.Length()<8) s=" "+s; s="["+s+" ms]";
return s;
}
//---------------------------------------------------------------------------
AnsiString tstr(int N)
{
AnsiString s;
s=s.sprintf("%8.3lf",performance_tms/double(N)); while (s.Length()<8) s=" "+s; s="["+s+" ms]";
return s;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
#endif
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------

Related

'player' : unknown override specifier

Why isn't my code working??? What does this error mean? FYI: I wanted to create a top down shooter game. all my code was basically just copied from different sources(some I just solved on my own).
I intended to put the player class in the game loop or game class. here's the code:
game.cpp
#include "Player.h"
//Private Functions
void game::intVariables()
{
this->window = nullptr;
this->points = 0;
this->enemySpawnTimerMax = 1000.f;
this->enemySpawnTimer = this->enemySpawnTimerMax;
this->maxEnemies = 8;
}
void game::intWindow()
{
this->videoMode.height = 1080;
this->videoMode.width = 1780;
this->window = new sf::RenderWindow(this->videoMode, "Top Down Shooter", sf::Style::Close | sf::Style::Titlebar);
this->window->setFramerateLimit(60);
this->window->setKeyRepeatEnabled(true);
Player player("player.png");
}
void game::intEnemies()
{
this->enemy.setPosition(30.f, 30.f);
this->enemy.setSize(sf::Vector2f(50.f, 50.f));
this->enemy.setFillColor(sf::Color(0, 128, 128));
this->enemy.setOutlineColor(sf::Color::Black);
this->enemy.setOutlineThickness(-4.f);
}
//Constructors or Destructors
game::game()
{
this->intVariables();
this->intWindow();
this->intEnemies();
}
//prevents memory leak
game::~game()
{
delete this->window;
}
//Accessors
const bool game::running() const
{
return this->window->isOpen();
}
//functions
void game::player1()
{
player.drawPlayer(*this->window);
}
void game::spawnEnemy()
{
/*
#return void
Spawns enemies and sets their color and positions.
- select random position
- select random color
- add enemy to vector
*/
this->enemy.setPosition(
static_cast<float>(rand() % static_cast<int>(this->window->getSize().x - this->enemy.getSize().x)),
static_cast<float>(rand() % static_cast<int>(this->window->getSize().y - this->enemy.getSize().y))
);
this->enemy.setFillColor(sf::Color(0, 128, 128));
//spawns the enemy
this->enemies.push_back(this->enemy);
}
void game::pollEvents()
{
//event polling
while (this->window->pollEvent(this->evnt))
{
switch (this->evnt.type)
{
case sf::Event::Closed:
this->window->close();
break;
case sf::Event::KeyPressed:
if (this->evnt.key.code == sf::Keyboard::Escape)
this->window->close();
break;
}
}
}
void game::updateEnemies()
{
/*
#return void
updates the enemy spawn timer and spawns enemies
when the total amount of enemies is smaller than the maximum.
removes the enemies at the edge of the screen. //to do
*/
//Updating the timer for enemy spawning
if (this->enemies.size() < this->maxEnemies)
{
if (this->enemySpawnTimer >= this->enemySpawnTimerMax)
{
//spawn the enemy and reset the timer
this->spawnEnemy();
this->enemySpawnTimer = 0.f;
}
else
this->enemySpawnTimer += 1.f;
}
// Move the enemies
for (auto &e : this->enemies)
{
e.move(0.f, 1.f);
}
}
void game::update()
{
this->pollEvents();
this->updateMousePositions();
this->updateEnemies();
}
void game::renderEnemies()
{
for (auto &e : this->enemies)
{
this->window->draw(e);
}
}
void game::render()
{
/*
- clears old frame
- renders objects
- display frames on window
renders game objects.
*/
this->window->clear(sf::Color(128, 128, 128));
//draw game objects
this->renderEnemies();
this->player1();
this->window->display();
}
void game::updateMousePositions()
{
/*
#return void
updates the mouse position
*mouse postion relative to window (vector2i)*
*/
this->mousePosWindow = sf::Mouse::getPosition(*this->window);
}
game.h
#include <iostream>
#include <vector>
#include <ctime>
#include <SFML/Graphics.hpp>
#include <SFML/Window.hpp>
#include <SFML/System.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>
/* class acts as the game engine
wrapper class.
*/
class game
{
protected:
//variables
//window
sf::RenderWindow* window;
sf::VideoMode videoMode;
sf::Event evnt;
//mouse positions
sf::Vector2i mousePosWindow;
//game logic
int points;
float enemySpawnTimer;
float enemySpawnTimerMax;
int maxEnemies;
//game objects
std::vector<sf::RectangleShape> enemies;
sf::RectangleShape enemy;
Player player;
// Private Functions
void intVariables();
void intWindow();
void intEnemies();
public:
// Constructors or Destructors
game();
virtual ~game();
// Accessors
const bool running() const;
// Functions
void player1();
void spawnEnemy();
void pollEvents();
void updateEnemies();
void update();
void renderEnemies();
void render();
void updateMousePositions();
};
Player.h
#include <iostream>
#include <SFML/Graphics.hpp>
#include "game.h"
class Player
{
public:
Player() {
//default
}
Player(std::string imgDirectory) {
if (!playerTexture.loadFromFile(imgDirectory)) {
std::cerr << "Error\n";
}
playerSprite.setTexture(playerTexture);
}
void drawPlayer(sf::RenderWindow& window) {
window.draw(playerSprite);
}
void movePlayer(char direction, float moveSpeed) {
if (direction == 'u')
{
playerSprite.move(0, -moveSpeed);
}
else if (direction == 'd')
{
playerSprite.move(0, -moveSpeed);
}
else if (direction == 'l')
{
playerSprite.move(-moveSpeed, 0);
}
else if (direction == 'r')
{
playerSprite.move(moveSpeed, 0);
}
}
private:
sf::Texture playerTexture;
sf::Sprite playerSprite;
};

Question regarding color histogram based methods of generating color lookup-up tables

I have a piece of code that needs to conform to a research paper's implementation of a color quantization algorithm for a 256 entry LUT whose 24-bit color entries are derived from a "population count" color histogram algorithm. The problem is that I don't know how the authors originally implemented their histogram algorithm -- the paper is a little ambiguous. Currently, I index a 2^24 entry array of integers based on a pixel's raw RGB 24-bit color triple, and increment the particular indexed entry in the array. I then sort the histogram and then I organize it into an effective 15-bit color space by putting blocks of 512 color counts into bins and taking the arithmetic mean of all the colors in the bin. I then stuff 256 averaged color values, starting with the largest color count, in decreasing order of color count into a 256 entry 24-bit color LUT. The output is very disappointing though and low quality. I know that vector quantization or something like median-cut would be better, but I'm constrained to do it with a histogram. I've extensively searched, using google, for "population count" histogram algorithms, but none of the search results were very helpful.
For reference, I'll include the original 512x512 pixel 24-bit color image along with its histogram based color LUT counterpart :
If anyone could provide some ideas or suggestions of where to look for the right algorithm, I'd be very appreciative.
Thanks,
jdb2
try this Effective gif/image color quantization? its also histogram color quantization based, very similar to your approach but it create the histogram from 15 bit colors directly to spare space and do not use bins instead it sort colors by occurrence and use min distance to already used colors in palette thresholding to avoid almost duplicate colors... I developed it for my GIF encoder lib some years back...
If I take this as input (converted to jpg):
And use mine algo on it without dithering I got this result:
With dithering enabled I got this result:
as you can see on the cat ear the dithering is much better but even without dithering the result is way better than yours.
However over the years the palette computation code evolves a bit (from the one posted in linked answer) into this (C++):
void gif::compute_palette0()
{
int x,y,r0,g0,b0,r,g,b,a,aa,c,e,hists;
DWORD i,i0,cc;
union { DWORD dd; BYTE db[4]; } c0,c1;
DWORD his[32768];
DWORD idx[32768];
// 15bit histogram
for (x=0;x<32768;x++) { his[x]=0; idx[x]=x; }
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
cc=pic.pyx[y][x];
cc=((cc>>3)&0x1F)|((cc>>6)&0x3E0)|((cc>>9)&0x7C00);
if (his[cc]<0xFFFFFFFF) his[cc]++;
}
// add RGB shades combinations for dithering
if (_dither)
{
x=xs*ys; // max possible count to make as start colors in palette
for (r=0;r<32;r+=10)
for (g=0;g<32;g+=10)
for (b=0;b<32;b+=10,x++)
his[(r<<10)|(g<<5)|( b)]=x;
}
// set recolor as unused
for (r0=0;r0<32;r0++)
for (g0=0;g0<32;g0++)
for (b0=0;b0<32;b0++)
recolor[r0][g0][b0]=255;
// remove zeroes
for (x=0,y=0;y<32768;y++)
{
his[x]=his[y];
idx[x]=idx[y];
if (his[x]) x++;
} hists=x;
// sort by hist
for (i=1,e=hists;i;e--)
for (i=0,x=0,y=1;y<e;x++,y++)
if (his[x]<his[y])
{
i=his[x]; his[x]=his[y]; his[y]=i;
i=idx[x]; idx[x]=idx[y]; idx[y]=i; i=1;
}
// set lcolor color palete
for (i0=0,x=0;x<hists;x++) // main colors
{
cc=idx[x];
b= cc &31;
g=(cc>> 5)&31;
r=(cc>>10)&31;
c0.db[0]=b;
c0.db[1]=g;
c0.db[2]=r;
c0.dd=(c0.dd<<3)&0x00F8F8F8;
// skip if similar color already in lcolor[]
for (a=0,i=0;i<i0;i++)
{
c1.dd=lcolor[i];
aa=int(BYTE(c1.db[0]))-int(BYTE(c0.db[0])); if (aa<=0) aa=-aa; a =aa;
aa=int(BYTE(c1.db[1]))-int(BYTE(c0.db[1])); if (aa<=0) aa=-aa; a+=aa;
aa=int(BYTE(c1.db[2]))-int(BYTE(c0.db[2])); if (aa<=0) aa=-aa; a+=aa;
if (a<=16) { a=1; break; } a=0; // *** treshold ***
}
if (a) recolor[r][g][b]=i;
else{
recolor[r][g][b]=i0;
lcolor[i0]=c0.dd; i0++;
if (i0>=DWORD(lcolors)) { x++; break; }
}
} // i0 = new color table size
for (;x<hists;x++) // minor colors
{
cc=idx[x];
b= cc &31;
g=(cc>> 5)&31;
r=(cc>>10)&31;
c0.db[0]=b;
c0.db[1]=g;
c0.db[2]=r;
c0.dd=(c0.dd<<3)&0x00F8F8F8;
// find closest color
int dc=-1; DWORD ii=0;
for (a=0,i=0;i<i0;i++)
{
c1.dd=lcolor[i];
aa=int(BYTE(c1.db[0]))-int(BYTE(c0.db[0])); if (aa<=0) aa=-aa; a =aa;
aa=int(BYTE(c1.db[1]))-int(BYTE(c0.db[1])); if (aa<=0) aa=-aa; a+=aa;
aa=int(BYTE(c1.db[2]))-int(BYTE(c0.db[2])); if (aa<=0) aa=-aa; a+=aa;
if ((dc<0)||(dc>a)) { dc=a; ii=i; }
}
recolor[r][g][b]=ii;
}
encode_palette_compute(true);
if ((frame)&&(hists<lcolors))
for (lcolor_bits=1,lcolors=1<<lcolor_bits;lcolors<hists;lcolors<<=1,lcolor_bits++);
// compute recolor for 16 base colors for all yet unused colors
for (r0=0;r0<32;r0++)
for (g0=0;g0<32;g0++)
for (b0=0;b0<32;b0++)
if (recolor[r0][g0][b0]==255)
{
// find closest color
for (i=0,c=-1;i<16;i++)
{
c0.dd=lcolor[i];
b=WORD(c0.db[0])>>3;
g=WORD(c0.db[1])>>3;
r=WORD(c0.db[2])>>3;
a=(r-r0); aa =a*a;
a=(g-g0); aa+=a*a;
a=(b-b0); aa+=a*a;
if ((c<0)||(e>aa)) { e=aa; c=i; }
}
recolor[r0][g0][b0]=c;
}
}
Where my gif class looks like this (so you can extract config and used variables...):
class gif
{
public:
// IO interface
file_cache<4<<20> fi,fo; // file cache
BYTE dat[256]; // internal buffer 256 Bytes needed
// Performance counter
double Tms,tms,tdec,tenc; // perioda citaca [ms], zmerany cas [ms],cas encodovania [ms]
void tbeg(); // zaciatok merania
void tend(); // koniec merania
// image data
gif_frame32 pic,pic0; // actual and restore to 32bit frames
gif_frame8 pic1; // 8bit input conversion frame
int xs,ys; // resolution
int *py; // interlace table
// colors (colors are computed from color_bits)
DWORD gcolor[256]; //hdr
DWORD lcolor[256]; //img
BYTE recolor[32][32][32]; //encode reduce color table
int scolors,scolor_bits; //hdr screen color depth
int gcolors,gcolor_bits; //hdr global pallete
int lcolors,lcolor_bits; //img/hdr local palette
// info
bool _89a; //hdr extensions present?
bool _interlaced; //img interlaced frame?
bool _gcolor_table; //hdr
bool _gcolor_sorted; //hdr
bool _lcolor_table; //img local palette present?
bool _lcolor_sorted; //img local palette colors sorted?
int cpf,cpf_error; //clears per frame counter,clear_errors total
// debug
bool _draw_palette; //draw pallete?
// animation
int frame,disposal; // frame ix,disposal of frame
double t,tn; // animation time,next frame time
// encode config
int _force_disposal; // -1 or forced disposal
bool _precomputed_palette; // if true recolor and global palete is already set before encoding
bool _dither; // dither colors?
// inter thread comm
volatile bool _image_copied; // flag that source image is not needed anymore while encoding
// temp dictionary for dec/enc
gif_str dict[_gif_maxdecode];
DWORD dicts,code_clr,code_end,code_min;
// temp dictionary speed up tables (encoding)
WORD dtab[256][_gif_maxencode],dnum[256],dmask[256]; // dtab[i][dnum[i]] all dictionary codes (sorted by code) starting with i for encode speed up, 1<<dmask[i]<=dnum[i]
#pragma pack(1)
struct __hdr
{
// Header
BYTE Signature[3]; /* Header Signature (always "GIF") */
BYTE Version[3]; /* GIF format version("87a" or "89a") */
// Logical Screen Descriptor
WORD xs;
WORD ys;
BYTE Packed; /* Screen and Color Map Information */
BYTE BackgroundColor; /* Background Color Index */
BYTE AspectRatio; /* Pixel Aspect Ratio */
__hdr(){}; __hdr(__hdr& a){ *this=a; }; ~__hdr(){}; __hdr* operator = (const __hdr *a) { *this=*a; return this; }; /*__hdr* operator = (const __hdr &a) { ...copy... return this; };*/
};
struct _hdr:__hdr
{
DWORD adr,siz;
_hdr(){}; _hdr(_hdr& a){ *this=a; }; ~_hdr(){}; _hdr* operator = (const _hdr *a) { *this=*a; return this; }; /*_hdr* operator = (const _hdr &a) { ...copy... return this; };*/
} hdr;
struct __img
{
// Logical Image Descriptor
BYTE Separator; /* Image Descriptor identifier 0x2C */
WORD x0; /* X position of image on the display */
WORD y0; /* Y position of image on the display */
WORD xs; /* Width of the image in pixels */
WORD ys; /* Height of the image in pixels */
BYTE Packed; /* Image and Color Table Data Information */
__img(){}; __img(__img& a){ *this=a; }; ~__img(){}; __img* operator = (const __img *a) { *this=*a; return this; }; /*__img* operator = (const __img &a) { ...copy... return this; };*/
};
struct _img:__img
{
DWORD adr,siz;
_img(){}; _img(_img& a){ *this=a; }; ~_img(){}; _img* operator = (const _img *a) { *this=*a; return this; }; /*_img* operator = (const _img &a) { ...copy... return this; };*/
} img;
struct __gfxext
{
BYTE Introducer; /* Extension Introducer (always 21h) */
BYTE Label; /* Graphic Control Label (always F9h) */
BYTE BlockSize; /* Size of remaining fields (always 04h) */
BYTE Packed; /* Method of graphics disposal to use */
WORD DelayTime; /* Hundredths of seconds to wait */
BYTE ColorIndex; /* Transparent Color Index */
BYTE Terminator; /* Block Terminator (always 0) */
__gfxext(){}; __gfxext(__gfxext& a){ *this=a; }; ~__gfxext(){}; __gfxext* operator = (const __gfxext *a) { *this=*a; return this; }; /*__gfxext* operator = (const __gfxext &a) { ...copy... return this; };*/
};
struct _gfxext:__gfxext
{
DWORD adr,siz;
_gfxext(){}; _gfxext(_gfxext& a){ *this=a; }; ~_gfxext(){}; _gfxext* operator = (const _gfxext *a) { *this=*a; return this; }; /*_gfxext* operator = (const _gfxext &a) { ...copy... return this; };*/
} gfxext;
struct __txtext
{
BYTE Introducer; /* Extension Introducer (always 21h) */
BYTE Label; /* Extension Label (always 01h) */
BYTE BlockSize; /* Size of Extension Block (always 0Ch) */
WORD TextGridLeft; /* X position of text grid in pixels */
WORD TextGridTop; /* Y position of text grid in pixels */
WORD TextGridWidth; /* Width of the text grid in pixels */
WORD TextGridHeight; /* Height of the text grid in pixels */
BYTE CellWidth; /* Width of a grid cell in pixels */
BYTE CellHeight; /* Height of a grid cell in pixels */
BYTE TextFgColorIndex; /* Text foreground color index value */
BYTE TextBgColorIndex; /* Text background color index value */
// BYTE *PlainTextData; /* The Plain Text data */
// BYTE Terminator; /* Block Terminator (always 0) */
__txtext(){}; __txtext(__txtext& a){ *this=a; }; ~__txtext(){}; __txtext* operator = (const __txtext *a) { *this=*a; return this; }; /*__txtext* operator = (const __txtext &a) { ...copy... return this; };*/
};
struct _txtext:__txtext
{
DWORD adr,siz;
AnsiString dat;
_txtext(){}; _txtext(_txtext& a){ *this=a; }; ~_txtext(){}; _txtext* operator = (const _txtext *a) { *this=*a; return this; }; /*_txtext* operator = (const _txtext &a) { ...copy... return this; };*/
};
List<_txtext> txtext;
struct __remext
{
BYTE Introducer; /* Extension Introducer (always 21h) */
BYTE Label; /* Comment Label (always FEh) */
// BYTE *CommentData; /* Pointer to Comment Data sub-blocks */
// BYTE Terminator; /* Block Terminator (always 0) */
__remext(){}; __remext(__remext& a){ *this=a; }; ~__remext(){}; __remext* operator = (const __remext *a) { *this=*a; return this; }; /*__remext* operator = (const __remext &a) { ...copy... return this; };*/
};
struct _remext:__remext
{
DWORD adr,siz;
AnsiString dat;
_remext(){}; _remext(_remext& a){ *this=a; }; ~_remext(){}; _remext* operator = (const _remext *a) { *this=*a; return this; }; /*_remext* operator = (const _remext &a) { ...copy... return this; };*/
};
List<_remext> remext;
struct __appext
{
BYTE Introducer; /* Extension Introducer (always 21h) */
BYTE Label; /* Extension Label (always FFh) */
BYTE BlockSize; /* Size of Extension Block (always 0Bh) */
CHAR Identifier[8]; /* Application Identifier */
BYTE AuthentCode[3]; /* Application Authentication Code */
// BYTE *ApplicationData; /* Point to Application Data sub-blocks */
// BYTE Terminator; /* Block Terminator (always 0) */
__appext(){}; __appext(__appext& a){ *this=a; }; ~__appext(){}; __appext* operator = (const __appext *a) { *this=*a; return this; }; /*__appext* operator = (const __appext &a) { ...copy... return this; };*/
};
struct _appext:__appext
{
DWORD adr,siz;
AnsiString dat;
_appext(){}; _appext(_appext& a){ *this=a; }; ~_appext(){}; _appext* operator = (const _appext *a) { *this=*a; return this; }; /*_appext* operator = (const _appext &a) { ...copy... return this; };*/
};
List<_appext> appext;
#pragma pack()
gif();
gif(gif& a);
~gif();
gif* operator = (const gif *a);
//gif* operator = (const gif &a);
void _resize(int _xs,int _ys); // resize buffers
void load_beg(AnsiString filename); // open GIF file for decoding
void decode(int _ignore_delay=0); // decode frame from GIF, if _ignore_delay then ignore realtime
void load_end(); // close GIF file
void save_beg(AnsiString filename); // create new GIF file for encoding
void compute_palette0(); // compute palette from frame method 0
void compute_palette1(); // compute palette from frame method 1
void encode_palette_RGB256(); // set RGB combinations as 256 color palette as predefined global only palette
void encode_palette_VGA256(); // set default 256 color VGA palette as predefined global only palette
void encode_palette_compute(bool _local); // compute recolor[][][] from palette
void encode(const gif_frame32 &src,int dt=0); // encode frame to GIF , dt is delay in [ms] instead of realtime in range <10 .. 655350> [ms]
// void encode(int dst_xs,int dst_ys,TCanvas *src,int src_x0,int src_y0,int src_x1,int src_y1,int dt=0); // encode frame to GIF , dt is delay in [ms] instead of realtime in range <10 .. 655350> [ms]
void save_end(); // finalize and close GIF file
void draw_info(int x,int y,TCanvas *can);
void draw_info(int x,int y,Graphics::TBitmap *bmp);
void configure(gif &src); // set all encoding variables from src (for multithreaded encoding)
};

How to change the background properties of an mfc application

CURRENT UI
NEW UI
I want to change the background color of a button in MFC application. I have created my user interface(UI) in MFC .I have added every controls from the toolbox.But the problem is that i want to change the background and foreground properties of a button and window.How it is possible?
please help me to change the properties of controls in MFC.In windows application we can directly change the properties in the property window.
But in the case of MFC application that is not possible.
please help me..i have not enough experience in MFC application development.....
Thanks in advance......................
code from dialer.h
class CButtonDialer : public CButton
{
// Construction
public:
CButtonDialer();
// Attributes
public:
CButton m_button;
// CButton IDC_KEY_1;
CBrush m_brush;
// Operations
public:
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CButtonDialer)
public:
virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
// Implementation
public:
virtual ~CButtonDialer();
// Generated message map functions
protected:
//{{AFX_MSG(CButtonDialer)
// NOTE - the ClassWizard will add and remove member functions here.
//}}AFX_MSG
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
CFont m_FontLetters;
CMapStringToString m_map;
HTHEME m_hTheme;
void OpenTheme() { m_hTheme = OpenThemeData(m_hWnd, L"Button"); }
void CloseTheme() {
if (m_hTheme) { CloseThemeData(m_hTheme); m_hTheme = NULL; }
}
DECLARE_MESSAGE_MAP()
virtual void PreSubclassWindow();
afx_msg LRESULT OnThemeChanged();
afx_msg void OnMouseMove(UINT,CPoint);
afx_msg void OnSize(UINT type, int w, int h);
};
code from dialer.cpp file
#include "stdafx.h"
#include "ButtonDialer.h"
#include "Strsafe.h"
#include "const.h"
/////////////////////////////////////////////////////////////////////////////
// CButtonDialer
CButtonDialer::CButtonDialer()
{
//255,255,255
m_brush.CreateSolidBrush(
(173, 41, 41));
m_map.SetAt(_T("1"),_T(""));
m_map.SetAt(_T("2"),_T("ABC"));
m_map.SetAt(_T("3"),_T("DEF"));
m_map.SetAt(_T("4"),_T("GHI"));
m_map.SetAt(_T("5"),_T("JKL"));
m_map.SetAt(_T("6"),_T("MNO"));
m_map.SetAt(_T("7"),_T("PQRS"));
m_map.SetAt(_T("8"),_T("TUV"));
m_map.SetAt(_T("9"),_T("WXYZ"));
m_map.SetAt(_T("0"),_T(""));
m_map.SetAt(_T("*"),_T(""));
m_map.SetAt(_T("#"),_T(""));
}
CButtonDialer::~CButtonDialer()
{
CloseTheme();
}
BEGIN_MESSAGE_MAP(CButtonDialer, CButton)
ON_WM_THEMECHANGED()
ON_WM_MOUSEMOVE()
ON_WM_SIZE()
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CButtonDialer message handlers
void CButtonDialer::PreSubclassWindow()
{
OpenTheme();
HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
LOGFONT lf;
GetObject(hFont, sizeof(LOGFONT), &lf);
lf.lfHeight = 14;
StringCchCopy(lf.lfFaceName,LF_FACESIZE,_T("Microsoft Sans Serif"));
m_FontLetters.CreateFontIndirect(&lf);
DWORD dwStyle = ::GetClassLong(m_hWnd, GCL_STYLE);
dwStyle &= ~CS_DBLCLKS;
::SetClassLong(m_hWnd, GCL_STYLE, dwStyle);
}
LRESULT CButtonDialer::OnThemeChanged()
{
CloseTheme();
OpenTheme();
return 0L;
}
void CButtonDialer::OnSize(UINT type, int w, int h)
{
CButton::OnSize(type, w, h);
}
void CButtonDialer::OnMouseMove(UINT nFlags,CPoint point)
{
CRect rect;
GetClientRect(&rect);
if (rect.PtInRect(point)) {
if (GetCapture() != this) {
SetCapture();
Invalidate();
}
}
else {
ReleaseCapture();
Invalidate();
}
CButton::OnMouseMove(nFlags, point);
}
void CButtonDialer::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC dc;
dc.Attach(lpDrawItemStruct->hDC); //Get device context object
CRect rt;
rt = lpDrawItemStruct->rcItem; //Get button rect
dc.FillSolidRect(rt,dc.GetBkColor());
dc.SetBkMode(TRANSPARENT);
CRect rtl = rt;
UINT state = lpDrawItemStruct->itemState; //Get state of the button
if (!m_hTheme) {
UINT uStyle = DFCS_BUTTONPUSH;
if ( (state & ODS_SELECTED) ) {
uStyle |= DFCS_PUSHED;
rtl.left+=1;
rtl.top+=1;
}
dc.DrawFrameControl(rt, DFC_BUTTON, uStyle);
} else {
UINT uStyleTheme = RBS_NORMAL;
if ( (state & ODS_SELECTED) ) {
uStyleTheme = PBS_PRESSED;
} else if (GetCapture()==this) {
uStyleTheme = PBS_HOT;
}
DrawThemeBackground(m_hTheme, dc.m_hDC,
BP_PUSHBUTTON, uStyleTheme,
rt, NULL);
}
CString strTemp;
GetWindowText(strTemp); // Get the caption which have been set
rtl.top += 4;
CString letters;
COLORREF crOldColor;
if (m_map.Lookup(strTemp,letters)) {
rtl.left+=15;
dc.DrawText(strTemp,rtl,DT_LEFT|DT_TOP|DT_SINGLELINE); // Draw out the caption
HFONT hOldFont = (HFONT)SelectObject(dc.m_hDC, m_FontLetters);
// Do your text drawing
rtl.left += 13;
rtl.top += 4;
rtl.right -=4;
crOldColor = dc.SetTextColor(RGB(148, 167, 70));
dc.DrawText(letters,rtl,DT_LEFT | DT_TOP | DT_SINGLELINE);
dc.SetTextColor(crOldColor);
// Always select the old font back into the DC
SelectObject(dc.m_hDC, hOldFont);
} else {
//127,127,127
crOldColor = dc.SetTextColor(RGB(148, 167, 70));
dc.DrawText(strTemp,rtl,DT_CENTER|DT_TOP|DT_SINGLELINE); // Draw out the caption
dc.SetTextColor(crOldColor);
}
if ( (state & ODS_FOCUS ) ) // If the button is focused
{
int iChange = 3;
rt.top += iChange;
rt.left += iChange;
rt.right -= iChange;
rt.bottom -= iChange;
dc.DrawFocusRect(rt);
}
dc.Detach();
}
BOOL CButtonDialer::OnEraseBkgnd(CDC* pDC)
{
CRect rect;
GetClientRect(&rect);
//255,255,255
CBrush myBrush(RGB(173, 41, 41)); // dialog background color
CBrush *pOld = pDC->SelectObject(&myBrush);
BOOL bRes = pDC->PatBlt(0, 0, rect.Width(), rect.Height(), PATCOPY);
pDC->SelectObject(pOld); // restore old brush
return bRes; // CDialog::OnEraseBkgnd(pDC);
}
You need to provide a message handler for the WM_ERASEBKGND message and paint the background yourself.
Put a normal button on the Resource Editor.
On the .h file of the form where you use it, declare a CMFCButton variable like m_btnRead.
In the DoDataExchange method of your form append a line
DDX_Control(pDX, IDC_BUTTON_READ, m_btnRead);
On the method where you initialize your form (OnInitDialog, Create, etc.) append a line
m_btnRead.SetFaceColor(RGB(0, 255, 255));
and now you are done!

Does anyone know of a low level (no frameworks) example of a drag & drop, re-order-able list?

I am looking for code (any language) of a basic graphical list which can be reordered by drag and drop. So exactly this functionality http://jqueryui.com/sortable/ but written directly on the frame buffer/canvas without any frameworks (or low level 'put pixel' libraries at most) and probably not in HTML/JS (unless it's Canvas only without CSS).
The simpler the better as I will be using it in assembler and I don't want to reinvent the wheel if not needed.
heh I hate frameworks so this is easy as pie for me...
this is what I coded for my students during my lectures few years back:
http://ulozto.net/x2b7WLwJ/select-drag-drop-zip
This is the main engine code (complete project + exe is in that zip above):
//---------------------------------------------------------------------------
//--- Constants: ------------------------------------------------------------
//---------------------------------------------------------------------------
const int _select_max_l=16;
const int _select_max_ll=_select_max_l*_select_max_l;
const int _half_size=10;
const int _atoms_max=32;
//---------------------------------------------------------------------------
enum _atom_type_enum //++++
{
_atom_type_non=0,
_atom_type_kruh,
_atom_type_stvorec,
_atom_type_enum_end
};
//---------------------------------------------------------------------------
enum _editor_edit_mode_enum //****
{
_editor_edit_mode_non=0,
_editor_edit_mode_move,
_editor_edit_mode_mov,
_editor_edit_mode_add_kruh,
_editor_edit_mode_add_stvorec,
_editor_edit_mode_del,
_editor_edit_mode_enum_end
};
//---------------------------------------------------------------------------
//--- viewer: ---------------------------------------------------------------
//---------------------------------------------------------------------------
class viewer
{
public: int x0,y0;
viewer() { x0=0; y0=0; }
void world2screen(int &sx,int &sy,int wx,int wy) { sx=wx-x0; sy=wy-y0; }
void screen2world(int &wx,int &wy,int sx,int sy) { wx=sx+x0; wy=sy+y0; }
void world2screen(int &sl,int wl) { sl=wl; }
void screen2world(int &wl,int sl) { wl=sl; }
};
//---------------------------------------------------------------------------
//--- atom kruh: ------------------------------------------------------------
//---------------------------------------------------------------------------
class atom_kruh
{
public: int x,y,r; // world coordinates
TColor col0,col1,col2;
AnsiString str;
atom_kruh() { x=0; y=0; r=_half_size; str=""; col0=clBlue; col1=clAqua; col2=clWhite; }
void draw(TCanvas *scr,const viewer &view)
{
int xx,yy,rr;
view.world2screen(xx,yy,x,y);
view.world2screen(rr,r);
scr->Brush->Color=col0;
scr->Pen ->Color=col1;
scr->Font ->Color=col2;
scr->Ellipse(xx-rr,yy-rr,xx+rr,yy+rr);
scr->Brush->Style=bsClear;
xx-=scr->TextWidth(str)>>1;
yy-=scr->TextHeight(str)>>1;
scr->TextOutA(xx,yy,str);
scr->Brush->Style=bsSolid;
}
bool select(int &ll,int wx,int wy)
{
int qq,xx,yy;
xx=wx-x; xx*=xx;
yy=wy-y; yy*=yy;
qq=xx+yy;
if ((qq<=_select_max_ll)&&((qq<=ll)||(ll<0))) { ll=qq; return true; }
return false;
}
};
//---------------------------------------------------------------------------
//--- atom kruh: ------------------------------------------------------------
//---------------------------------------------------------------------------
class atom_stvorec
{
public: int x,y,r; // world coordinates
TColor col0,col1,col2;
AnsiString str;
atom_stvorec() { x=0; y=0; r=_half_size; str=""; col0=clBlue; col1=clAqua; col2=clWhite; }
void draw(TCanvas *scr,const viewer &view)
{
int xx,yy,rr;
view.world2screen(xx,yy,x,y);
view.world2screen(rr,r);
scr->Brush->Color=col0;
scr->Pen ->Color=col1;
scr->Font ->Color=col2;
scr->Rectangle(xx-rr,yy-rr,xx+rr,yy+rr);
scr->Brush->Style=bsClear;
xx-=scr->TextWidth(str)>>1;
yy-=scr->TextHeight(str)>>1;
scr->TextOutA(xx,yy,str);
scr->Brush->Style=bsSolid;
}
bool select(int &ll,int wx,int wy)
{
int qq,xx,yy;
xx=wx-x; xx*=xx;
yy=wy-y; yy*=yy;
qq=xx+yy;
if ((qq<=_select_max_ll)&&((qq<=ll)||(ll<0))) { ll=qq; return true; }
return false;
}
};
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class editor
{
public: Graphics::TBitmap *bmp; // back buffer
int xs,ys;
int sel_ix,sel_tp; // actual mouse selected item
int edit_mode; // selected edit tool
viewer view; // view
bool redraw; // redraw needed?
bool locked; // edit in progress?
WORD key,key0;
int mx,my,mx0,my0;
TShiftState sh,sh0;
atom_kruh kruh[_atoms_max]; // all object lists
atom_stvorec stvorec[_atoms_max];
int kruhov;
int stvorcov;
editor();
~editor();
void resize(int _xs,int _ys); // interface with window
void draw();
void mouse(int x,int y,TShiftState s) { mx0=mx; my0=my; sh0=sh; mx=x; my=y; sh=s; edit(); }
void keys(WORD k,TShiftState s) { key0=key; sh0=sh; key=k; sh=s; edit(); }
void select(); // helper functions
void edit();
void move (bool q0,bool q1,int x,int y,int dx,int dy);
void mov (bool q0,bool q1,int x,int y,int dx,int dy);
void add_kruh (bool q0,bool q1,int x,int y,int dx,int dy);
void add_stvorec(bool q0,bool q1,int x,int y,int dx,int dy);
void del (bool q0,bool q1,int x,int y,int dx,int dy);
};
//---------------------------------------------------------------------------
editor::editor()
{
bmp=new Graphics::TBitmap;
resize(1,1);
sel_ix=-1;
sel_tp=_atom_type_non;
edit_mode=_editor_edit_mode_non;
key=0; key0=0;
mx=0; mx0=0;
my=0; my0=0;
locked=false;
kruhov=0;
stvorcov=0;
}
//---------------------------------------------------------------------------
editor::~editor()
{
delete bmp;
}
//---------------------------------------------------------------------------
void editor::resize(int _xs,int _ys)
{
bmp->Width=_xs;
bmp->Height=_ys;
xs=bmp->Width;
ys=bmp->Height;
redraw=true;
}
//---------------------------------------------------------------------------
void editor::draw()
{
int i;
if (!redraw) return;
redraw=false;
bmp->Canvas->Brush->Color=clBlack;
bmp->Canvas->FillRect(Rect(0,0,xs,ys));
//++++
for (i=0;i<kruhov ;i++) kruh[i] .draw(bmp->Canvas,view);
for (i=0;i<stvorcov;i++) stvorec[i].draw(bmp->Canvas,view);
}
//---------------------------------------------------------------------------
void editor::select()
{
int i,wx,wy,ll;
int sel_tp0=sel_tp; sel_tp=_atom_type_non;
int sel_ix0=sel_ix; sel_ix=-1;
view.screen2world(wx,wy,mx,my);
//++++
ll=-1;
for (i=0;i<kruhov ;i++) if (kruh[i] .select(ll,wx,wy)) { sel_tp=_atom_type_kruh; sel_ix=i; };
for (i=0;i<stvorcov;i++) if (stvorec[i].select(ll,wx,wy)) { sel_tp=_atom_type_stvorec; sel_ix=i; };
if (sel_tp!=sel_tp0) redraw=true;
if (sel_ix!=sel_ix0) redraw=true;
}
//---------------------------------------------------------------------------
void editor::edit()
{
bool q0,q1;
int x,y,dx,dy;
x=mx; dx=mx-mx0;
y=my; dy=my-my0;
view.screen2world( x, y, x, y);
view.screen2world(dx,dx);
view.screen2world(dy,dy);
q0=sh0.Contains(ssLeft);
q1=sh .Contains(ssLeft);
if (!locked) select();
//****
if(edit_mode==_editor_edit_mode_mov) mov (q0,q1,x,y,dx,dy);
if(edit_mode==_editor_edit_mode_add_kruh) add_kruh (q0,q1,x,y,dx,dy);
if(edit_mode==_editor_edit_mode_add_stvorec)add_stvorec (q0,q1,x,y,dx,dy);
if(edit_mode==_editor_edit_mode_del) del (q0,q1,x,y,dx,dy);
q0=sh0.Contains(ssRight);
q1=sh .Contains(ssRight);
if (!locked) move(q0,q1,x,y,dx,dy);
}
//---------------------------------------------------------------------------
void editor::move (bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
if (q1)
{
view.x0-=dx;
view.y0-=dy;
redraw=true;
}
}
//---------------------------------------------------------------------------
void editor::mov (bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((!locked)&&((sel_ix<0)||(sel_tp==_atom_type_non))) return;
locked=false;
if ((q1)||((q0)&&(!q1)))
{
//++++
if (sel_tp==_atom_type_kruh)
{
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
}
if (sel_tp==_atom_type_stvorec)
{
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
}
locked=true;
}
if (!q1) locked=false;
redraw=true;
}
//---------------------------------------------------------------------------
void editor::add_kruh (bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((!locked)&&(sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
locked=false;
if (kruhov>=_atoms_max) return;
if ((!q0)&&( q1))
{
sel_tp=_atom_type_kruh;
sel_ix=kruhov;
kruhov++;
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
kruh[sel_ix].str=kruhov;
locked=true;
}
if (( q0)&&( q1))
{
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
locked=true;
}
if (( q0)&&(!q1))
{
kruh[sel_ix].x=x;
kruh[sel_ix].y=y;
}
if ((!q0)&&(!q1))
{
}
redraw=true;
}
//---------------------------------------------------------------------------
void editor::add_stvorec(bool q0,bool q1,int x,int y,int dx,int dy)
{
if ((!locked)&&(sel_ix>=0)&&(sel_tp!=_atom_type_non)) return;
locked=false;
if (stvorcov>=_atoms_max) return;
if ((!q0)&&( q1))
{
sel_tp=_atom_type_stvorec;
sel_ix=stvorcov;
stvorcov++;
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
stvorec[sel_ix].str=stvorcov;
locked=true;
}
if (( q0)&&( q1))
{
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
locked=true;
}
if (( q0)&&(!q1))
{
stvorec[sel_ix].x=x;
stvorec[sel_ix].y=y;
}
if ((!q0)&&(!q1))
{
}
redraw=true;
}
//---------------------------------------------------------------------------
void editor::del (bool q0,bool q1,int x,int y,int dx,int dy)
{
locked=false;
if ((sel_ix<0)||(sel_tp==_atom_type_non)) return;
if ((!q0)&&( q1))
{
//++++
if (sel_tp==_atom_type_kruh)
if (kruhov>0)
{
kruhov--;
kruh[sel_ix]=kruh[kruhov];
}
if (sel_tp==_atom_type_stvorec)
if (stvorcov>0)
{
stvorcov--;
stvorec[sel_ix]=stvorec[stvorcov];
}
sel_ix=-1;
sel_tp=_atom_type_non;
}
redraw=true;
}
//---------------------------------------------------------------------------
sorry for that its not entirely in English
kruh means circle
stvorec means square
This is code for window (BDS2006 VCL style)
//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#include "editor.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
editor edit;
int x0,y0;
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
void draw() // redraw app screen
{
edit.draw();
Form1->Canvas->Draw(x0,y0,edit.bmp);
// here just some info print outs
int dy=16,x=x0,y=y0-dy;
Form1->Canvas->Font->Color=clAqua;
Form1->Canvas->Brush->Style=bsClear;
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("locked: %i",edit.locked));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("Key: %d",edit.key));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("sel_tp: %i",edit.sel_tp));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("sel_ix: %i",edit.sel_ix));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("kruhov: %i",edit.kruhov));
Form1->Canvas->TextOutA(x,y+=dy,AnsiString().sprintf("stvorcov: %i",edit.stvorcov));
Form1->Canvas->Brush->Style=bsSolid;
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) // init app
{
// select tool on app start
bt_tool_kruhClick(this);
}
//--- window events: ---------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) { draw(); }
void __fastcall TForm1::FormResize(TObject *Sender) { x0=pan_top->Left; y0=pan_top->Height; edit.resize(ClientWidth-x0,ClientHeight-y0); draw(); }
void __fastcall TForm1::FormActivate(TObject *Sender) { draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift) { edit.keys(Key,Shift); draw(); }
void __fastcall TForm1::FormMouseMove(TObject *Sender, TShiftState Shift, int X, int Y) { edit.mouse(X-x0,Y-y0,Shift); draw(); }
void __fastcall TForm1::FormMouseDown(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y){ edit.mouse(X-x0,Y-y0,Shift); draw(); }
void __fastcall TForm1::FormMouseUp(TObject *Sender, TMouseButton Button, TShiftState Shift, int X, int Y) { edit.mouse(X-x0,Y-y0,Shift); draw(); }
//---------------------------------------------------------------------------
void __fastcall TForm1::bt_tool_kruhClick(TObject *Sender) // event on any tool button click
{
// select editor tool mode ...
edit.edit_mode=_editor_edit_mode_non;
if (bt_tool_kruh ->Down) edit.edit_mode=_editor_edit_mode_add_kruh;
if (bt_tool_stvorec->Down) edit.edit_mode=_editor_edit_mode_add_stvorec;
if (bt_tool_move ->Down) edit.edit_mode=_editor_edit_mode_mov;
if (bt_tool_delete ->Down) edit.edit_mode=_editor_edit_mode_del;
}
//---------------------------------------------------------------------------
Window has only 4 tool buttons (locked together by same guid so only one can be down at a time)
add circle tool
add square tool
move tool
delete tool
Everything is statically allocated for simplicity
[edit1] more info
create gfx object data type/class (atom_xxxx)
it should hold the size,position,shape of visual gfx representation of object. Add connection variables (object type and object index to what it should be connected). Add the real object/data inside
object ID
I am using int tp,ix;
tp means type of object
ix means index in list of object of type tp
editor engine
this should be also class or set of variables and functions. It should hold the whole edited world (lists of objects):
visualization variables like screen/backbufer bitmap or rendering context, mouse position, selection list
add functions/events like onmouse, onkey, draw,...
add edit function it should be able to select,add,del,move (drag&drop) objects. Ideally controlled by set of command to ease up the undo/redo operation
add undo/redo
add save,load
add your desired simulation functionality
and that is all if I did not forgot something.
create Application GUI interface
so create window, add panel with buttons for each tool,menu and whatever you need. Add events for mouse,keyboard,repaint,resize,drag&drop,... My example looks like this:
Add editor edit; to it globally or as a member of it. Member option is better if you want to have MDI later. Add events interface between edit and window (the second source code).
[Notes]
++++ marks part of code where you need add changes if any atom type is added to the system
**** marks part of code where you need add changes if any edit mode is added to the system
Sorry for not add more commented code if you need clarify something comment me.
Hope it helps a little ...

VC++ does not show output

I am new to VC++ and its been a few times now, and this is the third program that does not give output even after it is build succesfully.
#include <AFXWIN.H>
#include <math.h>
#define PI 3.1415926
#define SEGMENTS 500
class CMyApp : public CWinApp {
public:
virtual BOOL InitInstance();
};
class CMainWindow : public CFrameWnd
{
public:
CMainWindow();
protected:
afx_msg void OnPaint();
afx_msg void OnLButtonDown(UINT, CPoint);
DECLARE_MESSAGE_MAP();
};
CMyApp myAPP;
BOOL CMyApp::InitInstance() {
m_pMainWnd = new CMainWindow;
m_pMainWnd->ShowWindow(SW_MAXIMIZE);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP (CMainWindow, CFrameWnd)
ON_WM_PAINT ()
END_MESSAGE_MAP ()
CMainWindow::CMainWindow() {
Create(NULL,"The Hello Application",WS_OVERLAPPEDWINDOW);
}
void CMainWindow::OnPaint() {
CRect rect;
int nWidth = rect.Width();
int nHeight = rect.Height();
CPaintDC dc (this);
CPoint aPoint[SEGMENTS];
for (int i =0; i < SEGMENTS; i++){
aPoint[i].x = ((i*nWidth)/SEGMENTS );
aPoint[i].y= (int)((nHeight/2)* (1-(sin((2*PI*i)/SEGMENTS))));
}
dc.Polyline(aPoint, SEGMENTS);
UpdateData(false);
}
The above program should give Sine curve as the output, except that I get a blank window. And I don't know why does it happen. If it helps, I am using VC++ 6.0
The problem is probably that the rectangle you use to get the width and height is not initialized. You have to get the rectangle from somewhere, see e.g. CWnd::GetClientRect.

Resources