Related
I would like to display, in Processing, one photo fading up and fading down over 15 seconds, with a second photo doing the same one second later, and another, etc, ad infinitum.
This example displays 15 objects, but they all start together:
PImage[] imgs = new PImage[42];
int Timer;
Pic[] pics = new Pic[15];
void setup() {
size(1000, 880);
for (int i = 0; i < pics.length; i++) {
pics[i] = new Pic(int(random(0, 29)), random(0, 800), random(0, height));
}
for (int i = 0; i < imgs.length; i++) {
imgs[i] = loadImage(i+".png");
}
}
void draw() {
background(255);
for (int i = 0; i < pics.length; i++) {
pics[i].display();
}
}
class Pic {
float x;
float y;
int num;
int f = 0;
boolean change = true;
Pic(int tempNum, float tempX, float tempY) {
num = tempNum;
x = tempX;
y = tempY;
}
void display() {
imageMode(CENTER);
if (change)f++;
else f--;
if (f==0||f==555)change=!change;
tint(0, 153, 204, f);
image(imgs[num], x, y);
}
}
Thanks!!!
The main issue is you're updating/rendering all images all Pic instances at once. Perhaps you meant to display one at a time (one after the other).
There other sections that raise questions as well:
Timer is never used: it's a good idea to simplify/cleanup code as much as possible
f is used for both the tint alpha (0-255 range) and to fake a delay across multple frames (==555 check)
num is asigned to a random value which means potentially the same pic may be repeated (potentially even consecutively ?) making it hard to notice the effect
I recommend slowing down and breaking the problem down:
fade a single image in and out
determine when a fade in/out cycle is complete
increment a single index (so the next image can fade in and out)
Ideally you want to take the timing (15 seconds into account).
You can work out across how many frames you'd need to fade over 15 seconds. Processing's frameRate property can help with that:
numAlphaSteps = 15 * frameRate;
(e.g. at 60 fps that would be 15 * 60 = 900 frames)
That being said, it takes a few frames for frameRate to "warm up" and become stable. The safer option would be to call frameRate() passing the resired frames per second and reusing that number for the animation
The next step is to map each increment to an alpha value, as it needs to ramp up and back down. You could use a bit of arithmetic.
If you subtract half the number of total fade frames (e.g. 900 / 2 = 450) from each frame number you'd get a value that goes from -half the number of frames to half the number of frames. Here's a minimal sketch you can try out:
int numFrames = 10;
int numFramesHalf = numFrames / 2;
for(int i = 0 ; i < numFrames; i++){
println("frame index", i, "ramp value", i - numFramesHalf);
}
Here's the same, visualised:
background(0);
int numFrames = 10;
int numFramesHalf = numFrames / 2;
for(int i = 0 ; i < numFrames; i++){
int ramp = i - numFramesHalf;
println("frame index", i, "ramp value", ramp);
// visualise numbers
fill(0, 128, 0);
rect(i * 10, 50, 10, map(ramp, 0, 5, 0, 39));
fill(255);
text(i + "\n" + ramp, i * 10, height / 2);
}
(Feel free to change numFrames to 900, 10 is easier to see in console).
If you pass the subtraction result to abs() you'll get positive values:
int numFrames = 10;
int numFramesHalf = numFrames / 2;
for(int i = 0 ; i < numFrames; i++){
println("frame index", i, "ramp value", abs(i - numFramesHalf));
}
and the visualisation:
background(0);
int numFrames = 10;
int numFramesHalf = numFrames / 2;
for(int i = 0 ; i < numFrames; i++){
int ramp = abs(i - numFramesHalf);
println("frame index", i, "ramp value", ramp);
// visualise numbers
fill(0, 128, 0);
rect(i * 10, 0, 10, map(ramp, 0, 5, 0, 39));
fill(255);
text(i + "\n" + ramp, i * 10, height / 2);
}
If you look in Processing Console you should see values that resemble a linear ramp (e.g. large to 0 then back to large).
This is a range that can be easily remapped to a the 0 to 255 range, required for the alpha value, using map(yourValue, inputMinValue, inputMaxValue, outputMinValue, outputMaxValue).
Here's a sketch visualising the tint value going from 0 to 255 and back to 255:
size(255, 255);
int numFrames = 10;
int numFramesHalf = numFrames / 2;
for(int i = 0 ; i <= numFrames; i++){
int frameIndexToTint = abs(i - numFramesHalf);
float tint = map(frameIndexToTint, 0, numFramesHalf, 255, 0);
println("frame index", i, "ramp value", frameIndexToTint, "tint", tint);
rect((width / numFrames) * i, height, 1, -tint);
}
Now with these "ingredients" it should be possible to switch from a for loop draw():
int numFrames = 180;
int numFramesHalf = numFrames / 2;
int frameIndex = 0;
void setup(){
size(255, 255);
}
void draw(){
// increment frame
frameIndex++;
// reset frame
if(frameIndex > numFrames){
frameIndex = 0;
println("fade transition complete");
}
// compute tint
int frameIndexToTint = abs(frameIndex - numFramesHalf);
float tint = map(frameIndexToTint, 0, numFramesHalf, 255, 0);
// visualise tint as background gray
background(tint);
fill(255 - tint);
text(String.format("frameIndex: %d\ntint: %.2f", frameIndex, tint), 10, 15);
}
Notice that using custom index for the transition frame index makes it easy to know when a transition is complete (so it can be reset): this is useful to also increment to the next image:
int numFrames = 180;
int numFramesHalf = numFrames / 2;
int frameIndex = 0;
int imageIndex = 0;
int maxImages = 15;
void setup(){
size(255, 255);
}
void draw(){
// increment frame
frameIndex++;
// reset frame (if larger than transition frames total)
if(frameIndex >= numFrames){
frameIndex = 0;
// increment image index
imageIndex++;
// reset image index (if larger than total images to display)
if(imageIndex >= maxImages){
imageIndex = 0;
}
println("fade transition complete, next image: ", imageIndex);
}
// compute tint
int frameIndexToTint = abs(frameIndex - numFramesHalf);
float tint = map(frameIndexToTint, 0, numFramesHalf, 255, 0);
// visualise tint as background gray
background(tint);
fill(255 - tint);
text(String.format("frameIndex: %d\ntint: %.2f\nimageIndex: %d", frameIndex, tint, imageIndex), 10, 15);
}
These are the main ingredients for your program. You can easily swap the placeholder background drawing with your images:
int numFrames = 180;
int numFramesHalf = numFrames / 2;
int frameIndex = 0;
int imageIndex = 0;
int maxImages = 15;
PImage[] imgs = new PImage[42];
int[] randomImageIndices = new int[maxImages];
void setup(){
size(255, 255);
// load images
for (int i = 0; i < imgs.length; i++) {
imgs[i] = loadImage(i+".png");
}
// pick random images
for (int i = 0; i < maxImages; i++) {
randomImageIndices[i] = int(random(0, 29));
}
}
void draw(){
// increment frame
frameIndex++;
// reset frame (if larger than transition frames total)
if(frameIndex >= numFrames){
frameIndex = 0;
// increment image index
imageIndex++;
// reset image index (if larger than total images to display)
if(imageIndex >= maxImages){
imageIndex = 0;
}
println("fade transition complete, next image: ", imageIndex);
}
// compute tint
int frameIndexToTint = abs(frameIndex - numFramesHalf);
float tint = map(frameIndexToTint, 0, numFramesHalf, 255, 0);
// visualise tint as background gray
background(0);
PImage randomImage =imgs[randomImageIndices[imageIndex]];
tint(255, tint);
image(randomImage, 0, 0);
//debug info
fill(255);
text(String.format("frameIndex: %d\ntint: %.2f\nimageIndex: %d", frameIndex, tint, imageIndex), 10, 15);
}
The x, y position of the image isn't random, but that should be easy to replicated based on how the random image index is used.
Alternatively you can use a single random index and position that get's reset at the end of each transition:
int numFrames = 180;
int numFramesHalf = numFrames / 2;
int frameIndex = 0;
int imageIndex = 0;
int maxImages = 15;
PImage[] imgs = new PImage[42];
int randomImageIndex;
float randomX, randomY;
void setup(){
size(255, 255);
// load images
for (int i = 0; i < imgs.length; i++) {
imgs[i] = loadImage(i+".png");
}
// pick random index
randomImageIndex = int(random(0, 29));
randomX = random(width);
randomY = random(height);
}
void draw(){
// increment frame
frameIndex++;
// reset frame (if larger than transition frames total)
if(frameIndex >= numFrames){
frameIndex = 0;
// increment image index
imageIndex++;
// reset image index (if larger than total images to display)
if(imageIndex >= maxImages){
imageIndex = 0;
}
// reset random values
randomImageIndex = int(random(0, 29));
randomX = random(width);
randomY = random(height);
println("fade transition complete, next image: ", imageIndex);
}
// compute tint
int frameIndexToTint = abs(frameIndex - numFramesHalf);
float tint = map(frameIndexToTint, 0, numFramesHalf, 255, 0);
// visualise tint as background gray
background(0);
PImage randomImage =imgs[randomImageIndex];
tint(255, tint);
image(randomImage, randomX, randomY);
//debug info
fill(255);
text(String.format("frameIndex: %d\ntint: %.2f\nimageIndex: %d", frameIndex, tint, imageIndex), 10, 15);
}
And you can also encapsulate instructions grouped by functionality into functions (removing the redundant imageIndex since we're using a random index):
int numFrames = 180;
int numFramesHalf = numFrames / 2;
int frameIndex = 0;
int imageIndex = 0;
int maxImages = 15;
PImage[] imgs = new PImage[42];
int randomImageIndex;
float randomX, randomY;
void setup(){
size(255, 255);
// load images
for (int i = 0; i < imgs.length; i++) {
imgs[i] = loadImage(i+".png");
}
// pick random index
randomizeImage();
}
void draw(){
updateFrameAndImageIndices();
background(0);
PImage randomImage =imgs[randomImageIndex];
tint(255, tintFromFrameIndex());
image(randomImage, randomX, randomY);
}
float tintFromFrameIndex(){
int frameIndexToTint = abs(frameIndex - numFramesHalf);
return map(frameIndexToTint, 0, numFramesHalf, 255, 0);
}
void updateFrameAndImageIndices(){
// increment frame
frameIndex++;
// reset frame (if larger than transition frames total)
if(frameIndex >= numFrames){
frameIndex = 0;
// increment image index
imageIndex++;
// reset image index (if larger than total images to display)
if(imageIndex >= maxImages){
imageIndex = 0;
}
// reset random values
randomizeImage();
println("fade transition complete, next image: ", imageIndex);
}
}
void randomizeImage(){
randomImageIndex = int(random(0, 29));
randomX = random(width);
randomY = random(height);
}
If the goal of this coding excecise is to practice using classes, you can easily further encapsulate the image fade related functions and variables into a class:
PImage[] imgs = new PImage[42];
ImagesFader fader;
void setup(){
size(255, 255);
frameRate(60);
// load images
for (int i = 0; i < imgs.length; i++) {
imgs[i] = loadImage(i+".png");
}
// setup fader instance
// constructor args: PImage[] images, float transitionDurationSeconds, int frameRate
// use imgs as the images array, transition in and out within 1s per image at 60 frames per second
fader = new ImagesFader(imgs, 1.0, 60);
}
void draw(){
background(0);
fader.draw();
}
class ImagesFader{
int numFrames;
int numFramesHalf;
int frameIndex = 0;
PImage[] images;
int maxImages = 15;
int randomImageIndex;
float randomX, randomY;
ImagesFader(PImage[] images, float transitionDurationSeconds, int frameRate){
numFrames = (int)(frameRate * transitionDurationSeconds);
numFramesHalf = numFrames / 2;
println(numFrames);
this.images = images;
// safety check: ensure maxImage index isn't larger than the total number of images
maxImages = min(maxImages, images.length - 1);
// pick random index
randomizeImage();
}
void draw(){
updateFrameAndImageIndices();
PImage randomImage = imgs[randomImageIndex];
// isolate drawing style (so only the image fades, not everything in the sketch)
pushStyle();
tint(255, tintFromFrameIndex());
image(randomImage, randomX, randomY);
popStyle();
}
float tintFromFrameIndex(){
int frameIndexToTint = abs(frameIndex - numFramesHalf);
return map(frameIndexToTint, 0, numFramesHalf, 255, 0);
}
void updateFrameAndImageIndices(){
// increment frame
frameIndex++;
// reset frame (if larger than transition frames total)
if(frameIndex >= numFrames){
frameIndex = 0;
// randomize index and position
randomizeImage();
println("fade transition complete, next image: ", randomImageIndex);
}
}
void randomizeImage(){
randomImageIndex = int(random(0, 29));
randomX = random(width);
randomY = random(height);
}
}
If you're comfortable using Processing libraries, you can achieve the same with a tweening library like Ani:
import de.looksgood.ani.*;
import de.looksgood.ani.easing.*;
PImage[] imgs = new PImage[42];
float tintValue;
int randomImageIndex;
float randomX, randomY;
AniSequence fadeInOut;
void setup(){
size(255, 255);
frameRate(60);
// load images
for (int i = 0; i < imgs.length; i++) {
imgs[i] = loadImage(i+".png");
}
randomizeImage();
// Ani.init() must be called always first!
Ani.init(this);
// create a sequence
// dont forget to call beginSequence() and endSequence()
fadeInOut = new AniSequence(this);
fadeInOut.beginSequence();
// fade in
fadeInOut.add(Ani.to(this, 0.5, "tintValue", 255));
// fade out (and call sequenceEnd() when on completion)
fadeInOut.add(Ani.to(this, 0.5, "tintValue", 0, Ani.QUAD_OUT, "onEnd:sequenceEnd"));
fadeInOut.endSequence();
// start the whole sequence
fadeInOut.start();
}
void draw(){
background(0);
tint(255, tintValue);
image(imgs[randomImageIndex], randomX, randomY);
}
void sequenceEnd() {
randomizeImage();
fadeInOut.start();
}
void randomizeImage(){
randomImageIndex = int(random(0, 29));
randomX = random(width);
randomY = random(height);
}
Update Based on your comment regarding loading images, can you try this sketch ?
void setup(){
size(1000, 500);
textAlign(CENTER);
PImage[] images = loadImages("Data","png");
int w = 100;
int h = 100;
for(int i = 0; i < images.length; i++){
float x = i % 10 * w;
float y = i / 10 * h;
image(images[i], x, y, w, h);
text("["+i+"]",x + w / 2, y + h / 2);
}
}
PImage[] loadImages(String dir, String extension){
String[] files = listPaths(dir, "files", "extension=" + extension);
int numFiles = files.length;
PImage[] images = new PImage[numFiles];
for(int i = 0 ; i < numFiles; i++){
images[i] = loadImage(files[i]);
}
return images;
}
It should load images in the "Data" folder (as the comment mentions, not "data" which is commonly used in Processing). If "Data" is a typo, fix the path first (as Processing is key sensitive ("Data" != "data")). If the 50 images load correctly, it should display in a 10x5 grid at 100x100 px each (e.g. disregarding each image's aspect ratio). This should help test if the images load correctly. (Again, breaking the problem down to individual steps).
I'm trying to display number of players detected by Kinect sensor using an WPF application. In addition to displaying number of player I have also coloured the pixels based on their distance from the Kinect. Original goal was to measure the distance of the pixels and display the distance but I would also like to display how many people are in the frame. Here is the code snippets that I'm using now.
PS I have borrowed the idea from THIS tutorial and I'm using SDK 1.8 with XBOX 360 Kinect (1414)
private void _sensor_AllFramesReady(object sender, AllFramesReadyEventArgs e)
{
using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
{
if (depthFrame==null)
{
return;
}
byte[] pixels = GenerateColoredBytes(depthFrame);
int stride = depthFrame.Width * 4;
image.Source = BitmapSource.Create(depthFrame.Width, depthFrame.Height,
96, 96, PixelFormats.Bgr32, null, pixels, stride);
}
}
private byte[] GenerateColoredBytes(DepthImageFrame depthFrame)
{
//get the raw data from kinect with the depth for every pixel
short[] rawDepthData = new short[depthFrame.PixelDataLength];
depthFrame.CopyPixelDataTo(rawDepthData);
/*
Use depthFrame to create the image to display on screen
depthFrame contains color information for all pixels in image
*/
//Height * Width *4 (Red, Green, Blue, Empty byte)
Byte[] pixels = new byte[depthFrame.Height * depthFrame.Width * 4];
//Hardcoded loactions for Blue, Green, Red (BGR) index positions
const int BlueIndex = 0;
const int GreenIndex = 1;
const int RedIndex = 2;
//Looping through all distances and picking a RGB colour based on distance
for (int depthIndex = 0, colorIndex = 0;
depthIndex < rawDepthData.Length &&
colorIndex<pixels.Length; depthIndex++, colorIndex+=4)
{
//Getting player
int player = rawDepthData[depthIndex] & DepthImageFrame.PlayerIndexBitmask;
//Getting depth value
int depth =rawDepthData[depthIndex]>>DepthImageFrame.PlayerIndexBitmaskWidth;
//.9M or 2.95'
if (depth <=900 )
{
//Close distance
pixels[colorIndex + BlueIndex] = 0;
pixels[colorIndex + GreenIndex] = 0;
pixels[colorIndex + RedIndex] = 255;
//textBox.Text = "Close Object";
}
//.9M - 2M OR 2.95' - 6.56'
else if (depth >900 && depth<2000)
{
//Bit further away
pixels[colorIndex + BlueIndex] = 255;
pixels[colorIndex + GreenIndex] = 0;
pixels[colorIndex + RedIndex] = 0;
}
else if (depth > 2000)
{
//Far away
pixels[colorIndex + BlueIndex] = 0;
pixels[colorIndex + GreenIndex] = 255;
pixels[colorIndex + RedIndex] = 0;
}
//Coloring all people in Gold
if (player > 0)
{
pixels[colorIndex + BlueIndex] = Colors.Gold.B;
pixels[colorIndex + GreenIndex] = Colors.Gold.G;
pixels[colorIndex + RedIndex] = Colors.Gold.R;
playersValue.Text = player.ToString();
}
}
return pixels;
}
Current goal is to--
Detect total number of players detected and display them in a textBox
Colour them according to the distance logic i.e depth <=900 is red.
With current code I can detect player and color them in Gold but as soon as a player is detected the image freezes and when the player is out of the frame the image unfreezes and acts normal. Is it because of the loop?
Ideas, guidance, recommendation and criticism all are welcome.
Thanks!
Screenshots:
Get a static variable inside your form code
Then set this variable using your video frame routine (dont define it there).
And then update the textbox view, probaply in your _sensor_AllFramesReady
As the arrival of new frames runs in a different thread
I dont see all code maybe to update call textbox.show
the main loop looks a bit strange though, too complex.
basicly you use it to color every pixel in your image.
as the kinect360 has 320x240 pixels so that makes a depth array of size 76800
You might simply create 2 for next loops loops for X and Y and then inside this loop have a variable increase to pick the proper depth value.
I'm using VisualStudio 2010, coding in C++/CLI, and doing all the graphics by GDI. I have a little app that plot continuously a Gaussian curve with some noise added. Every point is added real-time just like I pointed in this post.
Now, my task is to create a little colored area that I can shrink and increase to select a portion of the plot and do some math.
This kind of task is managed by a MouseMove event just like that:
System::Void Form1::pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
//Recalculate the position of the area,
//clean up the old one and redraw a new.
}
It works actually but I'm experiencing a bit graphic "bug".
As you can see, while I'm moving the area, everything under it is been deleted. The grid is here simply because it is static and I'm refreshing it everytime the green area is redrawn.
Actually it is not a bug, for sure it must go like that. To me, it is kinda obvious. I called it like that because it is not what I'm expecting.
I'm asking if there is a way to the green area as if it is upon a different layer. In this way, I would be able to move the green area while the plot is running without being erased.
I tried handling 2 HDC variables and plot the graph and the grid on the first one and the green area on the second one, but it seems not working.
Do you have some nice idea to get through this bad ( to me ) behaviour - maybe with some multilayer thing or some other fancy solutions - or should I give up and waiting for replotting?
Thanks everyone will give me an answer! :)
EDIT:
Here is how I draw my dataseries:
for(int i = 1; i<=1000; i++ ) {
Gauss[i] = safe_cast<float>(Math::Round( a*s*Math::Exp(-Math::Pow(((0.01*1*(i))-portante), 2)/b), 2));
Rumore[i] = safe_cast<float>(Math::Round(r*generatore->NextDouble(), 2));
SelectObject(hdcPictureBox, LinePen);
MoveToEx(hdcPictureBox, i-1+50, 500-convY*(Gauss[i-1]+Rumore[i-1])+50, NULL);
LineTo(hdcPictureBox, i+50, 500-convY*(Gauss[i]+Rumore[i])+50);
e1 = (i+k)%1000; //Buffer
if(i>DXX-54 && i<SXX-54) {
//ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
label1->Text = Convert::ToString(i);
label1->Refresh();
SelectObject(hdcPictureBox, ErasePen1);
}
else {
SelectObject(hdcPictureBox, ErasePen);
}
//HPEN ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
MoveToEx(hdcPictureBox, e1+50, 500-convY*(Gauss[e1]+Rumore[e1])+50, NULL);
LineTo(hdcPictureBox, e1+1+50, 500-convY*(Gauss[e1+1]+Rumore[e1+1])+50);
}
where DXX and SXX are the X-coordinates of areas - DXX starting, SXX ending.
This is how I'm handling the MouseMove. Do_Chan and Do_Clean are essentially the same thing. Do_Clean draws a bigger area with the background color to erase the old area and allowing Do_Chan to draw a new one.
System::Void Form1::pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
if(e->Button == System::Windows::Forms::MouseButtons::Left) {
double span100 = (SXX-DXX)*85/100;
if (e->X > DXX+((SXX-DXX)/2)-15 && e->X < DXX+((SXX-DXX)/2)+15 && (e->Y >30 && e->Y <50)
|| e->X >DXX+((SXX-DXX)/2)-span100/2 && e->X < DXX+((SXX-DXX)/2)+span100/2 && (e->Y >50 && e->Y <550)) {
HBRUSH brush = CreateSolidBrush(RGB(245,255,250));
Do_Clean(hdcPictureBox, DXX, SXX, brush);
double spawn = SXX-DXX;
DXX = e->X - spawn/2;
SXX = e->X + spawn/2;
if(DXX < 50) {
DXX = 51;
}
if(SXX >1050 ) {
SXX = 1049;
}
spawn = SXX - DXX;
CXX = DXX + spawn/2;
HBRUSH brush1 = CreateSolidBrush(RGB(166,251,178));
Do_Chan(hdcPictureBox2, DXX, SXX, brush1);
int k = 4;
int e1 = 0;
for(int i = 1; i<=1000; i++) {
SelectObject(hdcPictureBox, LinePen);
MoveToEx(hdcPictureBox, i-1+50, 500-250*(Gauss[i-1]+Rumore[i-1])+50, NULL);
LineTo(hdcPictureBox, i+50, 500-250*(Gauss[i]+Rumore[i])+50);
e1 = (i+k)%1000; //Buffer
if(i>DXX-54 && i<SXX-54) {
//ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
SelectObject(hdcPictureBox, ErasePen1);
}
else {
SelectObject(hdcPictureBox, ErasePen);
}
//HPEN ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
MoveToEx(hdcPictureBox, e1+50, 500-250*(Gauss[e1]+Rumore[e1])+50, NULL);
LineTo(hdcPictureBox, e1+1+50, 500-250*(Gauss[e1+1]+Rumore[e1+1])+50);
}
}
}
}
As you can see, after I drew the new area, I redraw all the point of the array Gauss+Rumore.
This is how Do_Chan ( Do_Clean is the same ) works:
void Do_Chan(HDC hdc, int dx, int sx, HBRUSH brush) {
//i = 250, y = 50
int y = 50;
int spawn = sx - dx;
HPEN pen = CreatePen(PS_SOLID, 1, RGB(245, 255, 250));
HPEN penC = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
/*Fai il rettangolo */
SelectObject(hdc, pen);
SelectObject(hdc, brush);
POINT punti[4];
punti[0].x = dx;
punti[0].y = y;
punti[1].x = dx +spawn;
punti[1].y = y;
punti[2].x = dx + spawn;
punti[2].y = y+500;
punti[3].x = dx;
punti[3].y = y+500;
Polygon(hdc, punti, 4);
Ellipse(hdc, dx-10, y-20, dx+10, y);
SelectObject(hdc, penC);
MoveToEx(hdc, dx+spawn/2, 50,NULL);
LineTo(hdc, dx+spawn/2, 550);
SelectObject(hdc, pen);
SelectObject(hdc, brush);
Ellipse(hdc, dx-10+spawn/2, y-20, dx+10+spawn/2, y);
SelectObject(hdc, pen);
SelectObject(hdc, brush);
Ellipse(hdc, dx-10+spawn, y-20, dx+10+spawn, y);
//Plot the axis and the grid
}
I have thought of a possible way to do it and every solution had a drawback. For example
creating a thread. It has a drawback of drawing to picturebox dc from a different thread than the one handling the message queue. Not recomended
Another one:
using a timer and for every tick(lets say 16msec) draw. The DXX and SXX will be global variables. In picturebox move event you will only calculate these values(no drawing), also use some critical sections to protect them, and do all the drawing inside tick of timer. This works but you probable encounter some delay if your movement in picturebox is faster than 60fps.
The solution that i came finally was:
Inside your infinite loop:
get the mouse position and state(down or up). In this way you know if the user is dragging the green area and calculate DXX and SXX.
draw three rectangles with FillRect(): from 0 to DXX with picturebox back color, from DXX to SXX with green color and from SXX to the end with picturebox back color to an in memory dc eg hdcMemBackground
draw the grid lines to hdcMemBackground
Use the Point array and the polyline method i told you and in every loop move all your 999 points in the array one place to the left and add one point in the end of the array. To achieve this fill the array once before the infinite loop and inside it do the previous method
BitBlt hdcMemBackground to picturebox dc
Application::DoEvents();
EDIT (some code)
Create your resources once, when form loads, and release them in the end. Your Do_Chan()
creates considerable memory leaks. At form load:
HPEN hLinePenRed = NULL, hLinePenBlack = NULL, hLinePenWhite = NULL, hLinePenBlackDotted = NULL hPenOld; //Global
HBRUSH hBrushGreen = NULL, hBrushWhite = NULL, hBrushOld = NULL; //Global
HBITMAP hBitmap = NULL, hBitmapOld = NULL; //Global
HDC hdcMemBackground = NULL, hdcPicBox = NULL; //Global
//in form load event:
hLinePenRed = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
hLinePenBlack = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
hLinePenWhite = CreatePen(PS_SOLID, 1, RGB(245, 255, 250));
hLinePenBlackDotted = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
hPenOld = SelectObject(hdcMemBackground, hLinePenRed);
hBrushGreen = CreateSolidBrush(RGB(166, 251, 178));
hBrushWhite = CreateSolidBrush(RGB(245, 255, 250));
hBrushOld = SelectObject(hdcMemBackground, hBrushGreen);
HDC hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcPicBox = GetWindowDC(hwndPicBox);
hdcMemBackground= CreateCompatibleDC(hdc);
hBitmap = CreateCompatibleBitmap(hdc, 1050, 550);
hBitmapOld = SelectObject(hdcMemBackground, hBitmap);
DeleteDC(hdc);
In the end when form closes:
SelectObject(hdcMemBackground, hPenOld);
DeleteObject(hLinePenRed);
DeleteObject(hLinePenBlack);
DeleteObject(hLinePenBlackDotted);
DeleteObject(hLinePenWhite);
SelectObject(hdcMemBackground, hBitmapOld);
DeleteObject(hBitmap);
SelectObject(hdcMemBackground, hBrushOld);
DeleteObject(hBrushGreen);
DeleteObject(hBrushWhite);
DeleteDC(hdcMemBackground);
ReleaseDC(hwndPicBox, hdcPicBox);
How to use FillRect and draw ellipses:
RECT rt;
rt.left = 0; rt.top = 0; rt.right = 1050; rt.bottom = 550;
FillRect(hdcMemBackground, &rt, hBrushWhite);
rt.left = DXX; rt.top = 50; rt.right = SXX; rt.bottom = 550;
FillRect(hdcMemBackground, &rt, hBrushGreen);
SelectObject(hdcMemBackground, hBrushGreen);
SelectObject(hdcMemBackground, hLinePenWhite);
Ellipse(hdcMemBackground, dx-10, y-20, dx+10, y);
Ellipse(hdcMemBackground, dx-10+spawn/2, y-20, dx+10+spawn/2, y);
Ellipse(hdcMemBackground, dx-10+spawn, y-20, dx+10+spawn, y);
//Plot the axis and the grid first and then draw the dotted vertical line
SelectObject(hdcMemBackground, hLinePenBlackDotted);
MoveToEx(hdcMemBackground, dx+spawn/2, 50, NULL);
LineTo(hdcMemBackground, dx+spawn/2, 550);
How to find mouse position and mouse state. This code will be at the beginning of each
iteration to see if user dragged the green area and calculate the new DXX, SXX:
/* It is buggy. My mistake
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwndPicBox, &pt);
if( pt.x >= 0 && pt.x <= picBoxWidth && pt.y >= 0 && pt.y <= picBoxHeight && (GetAsyncKeyState(VK_LBUTTON) & 0x8000) ){ //the mouse is down and inside picturebox
//do your staff
}
*/
Use instead picturebox mouse down, mouse up and mouse move events:
int isScrollingLeft = false; //global, the left circle
int isScrollingRight = false; //global, the right circle
int isScrollingMiddle = false; //global, the middle circle
System::Void Form1::pictureBox1_MouseDown(....){
//check if e.X and e.Y is inside in one of the three circles and set the
//appropriate isScrolling to true
}
System::Void Form1::pictureBox1_MouseMove(....){
if(isScrollingLeft){
//calculate DXX
}
else if(isScrollingRight){
//calculate SXX
}
else if(isScrollingMiddle){ //if you dont scroll this you dont need it
//
}
else{;} //do nothing
}
System::Void Form1::pictureBox1_MouseUp(....){
isScrollingLeft = false;
isScrollingRight = false;
isScrollingMiddle = false; //if you dont scroll this you dont need it
}
The way to move the points one place to the left:
POINT arrayPnt[1000]; //the array of points to be drawn by Polyline()
//the movement
memmove(&arrayPnt[0], &arrayPnt[1], 999 * sizeof(POINT));
//set the last one
arrayPnt[999].x = X;
arrayPnt[999].y = Y;
//Draw the lines
Polyline(hdcMemBackground, &arrayPnt, 1000);
To draw the number i into the label:
HDC hdcLabel1 = NULL; //global
HFONT hFont = NULL, hFontOld = NULL; //global
RECT rtLabel = NULL; //global
char strLabel1[5]; //global
Initialize once at the beggining like everything else
hdcLabel1 = GetWindowDC(label1Hwnd);
SetBkColor(hdcLabel1, RGB(?, ?, ?)); //i believe you use the color of your form
SetTextColor(hdcLabel1, RGB(255, 255, 255));
hFont = CreateFont(21, 0, 0, 0, /* Bold or normal*/FW_NORMAL, /*italic*/0, /*underline*/0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, TEXT("Arial")); //21 is i believe the 16 size in word
hFontOld = SelectObject(hdcLabel1, hFont);
rtLabel.top = 0; rtLabel.left = 0;
rtLabel.right = label1.Width; rtLabel.bottom = label1.Height;
and in the for loop draw the string into the label1
sprintf(strLabel1, "%d", i); //it is toooo slow. I have to think something better
DrawTextEx(hdcLabel1, strLabel1, -1, &rtLabel, DT_VCENTER | DT_SINGLELINE | DT_LEFT, NULL);
In the end ofcource release resources
SelectObject(hdcLabel1, hFontOld);
DeleteObject(hFont);
hFont = NULL;
ReleaseDC(label1Hwnd, hdcLabel1);
If you have any problems do comment.
valter
I am trying to get screenshot of NinevehGL object which is 3D library build on top of OpenGL. So i think OpenGL commands would work here as well.
Problem is screenshot works fine in iOS 5 or earlier but not with iOS6. It works fine in iOS6 simulator as well but not on iOS 6 device. It gives me black background. I have read many posts most of them suggest to put
CAEAGLLayer *eaglLayer = (CAEAGLLayer *) [self theNGLView].layer;
[eaglLayer setDrawableProperties:#{
kEAGLDrawablePropertyRetainedBacking: [NSNumber numberWithBool:YES],
kEAGLDrawablePropertyColorFormat: kEAGLColorFormatRGBA8
}];
in init method. which i did but with no luck.
I am using below method to get screenshot.
-(UIImage *)getImage{
GLint width;
GLint height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
NSLog(#"%d %d",width,height);
NSInteger myDataLength = width * height * 4;
// allocate array and read pixels into it.
GLubyte *buffer = (GLubyte *) malloc(myDataLength);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// gl renders "upside down" so swap top to bottom into new array.
// there's gotta be a better way, but this works.
GLubyte *buffer2 = (GLubyte *) malloc(myDataLength);
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width * 4; x++)
{
buffer2[((height - 1) - y) * width * 4 + x] = buffer[y * 4 * width + x];
}
}
// make data provider with data.
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL);
// prep the ingredients
int bitsPerComponent = 8;
int bitsPerPixel = 32;
int bytesPerRow = 4 * width;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
// make the cgimage
CGImageRef imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent);
// then make the uiimage from that
UIImage *myImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpaceRef);
free(buffer);
free(buffer2);
return myImage;
}
I have a BitmapImage instance in Silverlight, and I am trying to find a way to read the color information of each pixel in the image. How can I do this? I see that there is a CopyPixels() method on this class that writes pixel information into the array that you pass it, but I don't know how to read color information out of that array.
How can I do this?
Look for the WriteableBitmap class in Silverlight 3. This class has a member Pixels which returns the bitmap's pixel data in an array of int's.
An example with a transform. bi is an BitmapImage object.
Image img = new Image();
img.source = bi;
img.Measure(new Size(100, 100));
img.Arrange(new Rect(0, 0, 100, 100));
ScaleTransform scaleTrans = new ScaleTransform();
double scale = (double)500 / (double)Math.Max(bi.PixelHeight, bi.PixelWidth);
scaleTrans.CenterX = 0;
scaleTrans.CenterY = 0;
scaleTrans.ScaleX = scale;
scaleTrans.ScaleY = scale;
WriteableBitmap writeableBitmap = new WriteableBitmap(500, 500);
writeableBitmap.Render(img, scaleTrans);
int[] pixelData = writeableBitmap.Pixels;
This is not possible with the current Bitmap API in the currently released Silverlight 3 beta.
The Silverlight BitmapImage file does not have a CopyPixels method. Please see the MSDN documentation here.
First, you should use WritableBitmap to get pixels collection: WriteableBitmap bmp = new WriteableBitmap(bitmapImageObj);. Each pixel is represented as 32-bit integer. I have made structure, which just helps to splits integer into a four bytes (ARGB).
struct BitmapPixel
{
public byte A;
public byte R;
public byte G;
public byte B;
public BitmapPixel(int pixel)
: this(BitConverter.GetBytes(pixel))
{
}
public BitmapPixel(byte[] pixel)
: this(pixel[3], pixel[2], pixel[1], pixel[0])
{
}
public BitmapPixel(byte a, byte r, byte g, byte b)
{
this.A = a;
this.R = r;
this.G = g;
this.B = b;
}
public int ToInt32()
{
byte[] pixel = new byte[4] { this.B, this.G, this.R, this.A };
return BitConverter.ToInt32(pixel, 0);
}
}
Here is an example of how it could be used to change red value:
BitmapPixel pixel = new BitmapPixel(bmp.Pixels[0]);
pixel.R = 255;
bmp.Pixels[0] = pixel.ToInt32();
Also I would like to mention that WriteableBitmap.Pixels are in Premultiplied RGB format. This article will explain what it means. And here is how compensation done by using BitmapPixel structure:
public static void CompensateRGB(int[] pixels)
{
for (int i = 0; i < pixels.Length; i++)
{
BitmapPixel pixel = new BitmapPixel(pixels[i]);
if (pixel.A == 255 || pixel.A == 0)
continue;
if (pixel.R == 0 && pixel.G == 0 && pixel.B == 0)
{
// color is unrecoverable, get rid of this
// pixel by making it transparent
pixel.A = 0;
}
else
{
double factor = 255.0 / pixel.A;
pixel.A = 255;
pixel.R = (byte)(pixel.R * factor);
pixel.G = (byte)(pixel.G * factor);
pixel.B = (byte)(pixel.B * factor);
}
pixels[i] = pixel.ToInt32();
}
}