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.