Hey everyone,
So, I'm currently trying the implement the APNG Specification, but am having some trouble with the frame rendering. My function is
private void UpdateUI()
{
foreach (PictureBox pb in pics)
{
APNGBox box = (APNGBox)pb.Tag;
APNGLib.APNG png = box.png;
if (box.buffer == null)
{
box.buffer = new Bitmap((int)png.Width, (int)png.Height);
}
APNGLib.Frame f = png.GetFrame(box.frameNum);
using (Graphics g = Graphics.FromImage(box.buffer))
{
switch (f.DisposeOp)
{
case APNGLib.Frame.DisposeOperation.NONE:
break;
case APNGLib.Frame.DisposeOperation.BACKGROUND:
g.Clear(Color.Transparent);
break;
case APNGLib.Frame.DisposeOperation.PREVIOUS:
if (box.prevBuffer != null)
{
g.DrawImage(box.prevBuffer, Point.Empty);
}
else
{
g.Clear(Color.Transparent);
}
break;
default:
break;
}
Bitmap read = png.ToBitmap(box.frameNum++);
switch (f.BlendOp)
{
case APNGLib.Frame.BlendOperation.OVER:
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
break;
case APNGLib.Frame.BlendOperation.SOURCE:
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
break;
default:
break;
}
g.DrawImage(read, new Point((int)f.XOffset, (int)f.YOffset));
}
box.prevBuffer = box.buffer;
pb.Image = box.buffer;
if (box.frameNum >= box.png.FrameCount)
{
box.frameNum = 0;
box.buffer = null;
box.prevBuffer = null;
}
}
}
The APNGBox is
internal class APNGBox
{
public int frameNum = 0;
public APNGLib.APNG png;
public Bitmap buffer;
public Bitmap prevBuffer;
}
I think that this is mostly correct, as I've run it against all the images in the APNG Gallery. However, some of them are rendering incorrectly (This one has artifacting issues, and this one does not retain the red bar on the left consistently). Please note, you'll have the view the page in Firefox 3 or higher to view the animations.
I'm lead to believe the issue has to do with how I handle the DISPOSE_OP_PREVIOUS, but I can't figure out what I'm doing wrong. Can anyone suggest what I might be missing?
So, I was able to figure it out in the end, and will post here in case anyone in the future comes across a similar problem. Turns out the issue was largely threefold:
One should not change the 'previous buffer' if the frame type is 'PREVIOUS'
One should use the previous frame's dispose_op, not the dispose_op of the current frame, to determine how it disposes
One should be sure to dispose only the old frame's region, not the whole frame
The new code is:
internal class APNGBox
{
public int frameNum { get; set; }
public APNGLib.APNG apng { get; set; }
public Bitmap buffer { get; set; }
public Bitmap prevBuffer { get; set; }
public APNGBox(APNGLib.APNG png)
{
frameNum = 0;
apng = png;
buffer = apng.ToBitmap(0);
prevBuffer = null;
}
}
private void UpdateUI()
{
foreach (PictureBox pb in pics)
{
APNGBox box = (APNGBox)pb.Tag;
APNGLib.APNG png = box.apng;
if (!png.IsAnimated)
{
if (pb.Image == null)
{
pb.Image = png.ToBitmap();
}
}
else
{
if (box.frameNum != png.FrameCount - 1)
{
Bitmap prev = box.prevBuffer == null ? null : new Bitmap(box.prevBuffer);
APNGLib.Frame f1 = png.GetFrame(box.frameNum);
if (f1.DisposeOp != APNGLib.Frame.DisposeOperation.PREVIOUS)
{
box.prevBuffer = new Bitmap(box.buffer);
}
DisposeBuffer(box.buffer, new Rectangle((int)f1.XOffset, (int)f1.YOffset, (int)f1.Width, (int)f1.Height), f1.DisposeOp, prev);
box.frameNum++;
APNGLib.Frame f2 = png.GetFrame(box.frameNum);
RenderNextFrame(box.buffer, new Point((int)f2.XOffset, (int)f2.YOffset), png.ToBitmap(box.frameNum), f2.BlendOp);
}
else
{
box.frameNum = 0;
box.prevBuffer = null;
ClearFrame(box.buffer);
RenderNextFrame(box.buffer, Point.Empty, png.ToBitmap(box.frameNum), APNGLib.Frame.BlendOperation.SOURCE);
}
pb.Invalidate();
}
}
}
private void DisposeBuffer(Bitmap buffer, Rectangle region, APNGLib.Frame.DisposeOperation dispose, Bitmap prevBuffer)
{
using (Graphics g = Graphics.FromImage(buffer))
{
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
Brush b = new SolidBrush(Color.Transparent);
switch (dispose)
{
case APNGLib.Frame.DisposeOperation.NONE:
break;
case APNGLib.Frame.DisposeOperation.BACKGROUND:
g.FillRectangle(b, region);
break;
case APNGLib.Frame.DisposeOperation.PREVIOUS:
if(prevBuffer != null)
{
g.FillRectangle(b, region);
g.DrawImage(prevBuffer, region, region, GraphicsUnit.Pixel);
}
break;
default:
break;
}
}
}
private void RenderNextFrame(Bitmap buffer, Point point, Bitmap nextFrame, APNGLib.Frame.BlendOperation blend)
{
using(Graphics g = Graphics.FromImage(buffer))
{
switch(blend)
{
case APNGLib.Frame.BlendOperation.OVER:
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;
break;
case APNGLib.Frame.BlendOperation.SOURCE:
g.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
break;
default:
break;
}
g.DrawImage(nextFrame, point);
}
}
private void ClearFrame(Bitmap buffer)
{
using(Graphics g = Graphics.FromImage(buffer))
{
g.Clear(Color.Transparent);
}
}
Related
I wrote some code to draw something in a iOS App, if the server send some new coordinates to it.
I have a callback function that draw the coordinates. But when I create a new instance of a class in this function the callback exit without any error...
Does someone else have this problem before?
Here is my code if this is helping
CGPath pathtotal;
List<CGPath> path;
CGPoint initialPoint;
CGPoint latestPoint;
DrawDrawerDraw drawDrawerDraw;
public DrawGuessView(IntPtr handle) : base(handle)
{
BackgroundColor = UIColor.White;
pathtotal = new CGPath();
SocketEventHandler.Add("draw:drawer:draw", onDrawDrawerDraw);
}
public void onDrawDrawerDraw(dynamic obj)
{
drawDrawerDraw = (DrawDrawerDraw)obj;
for (int i = 0; i <= drawDrawerDraw.coords.Count; i++)
{
if (initialPoint.X != (nfloat)drawDrawerDraw.coords[i].x0 && initialPoint.Y != (nfloat)drawDrawerDraw.coords[i].y0)
{
path[i] = new CGPath();
}
initialPoint.X = (nfloat)drawDrawerDraw.coords[i].x0;
initialPoint.Y = (nfloat)drawDrawerDraw.coords[i].y0;
latestPoint.X = (nfloat)drawDrawerDraw.coords[i].x1;
latestPoint.Y = (nfloat)drawDrawerDraw.coords[i].y1;
//add lines to the touch points
if (path[i].IsEmpty)
{
path[i].AddLines(new CGPoint[] { initialPoint, latestPoint });
}
else
{
path[i].AddLineToPoint(latestPoint);
}
}
SetNeedsDisplay();
}
public override void Draw(CGRect rect)
{
base.Draw(rect);
try
{
foreach (var item in path)
{
if (!initialPoint.IsEmpty)
{
//get graphics context
using (CGContext g = UIGraphics.GetCurrentContext())
{
//set up drawing attributes
g.SetLineWidth(2);
UIColor.Black.SetStroke();
//add geometry to graphics context and draw it
pathtotal.AddPath(item);
g.AddPath(pathtotal);
g.DrawPath(CGPathDrawingMode.Stroke);
}
}
}
}
catch (Exception e) { }
}
}
There are two points you need to modify.
Initialize path in DrawGuessView method
public DrawGuessView(IntPtr handle) : base(handle)
{
BackgroundColor = UIColor.White;
pathtotal = new CGPath();
List<CGPath> path = new List<CGPath>();
SocketEventHandler.Add("draw:drawer:draw", onDrawDrawerDraw);
}
path[i] = new CGPath() will cause the ArgumentOutOfRangeException, we can't set value to the item in List by this way.
Modify the loop
CGPath pathItem = null;
for (int i = 0; i <= drawDrawerDraw.coords.Count; i++)
{
if (initialPoint.X != (nfloat)drawDrawerDraw.coords[i].x0 && initialPoint.Y != (nfloat)drawDrawerDraw.coords[i].y0)
{
pathItem = new CGPath();
}
initialPoint.X = (nfloat)drawDrawerDraw.coords[i].x0;
initialPoint.Y = (nfloat)drawDrawerDraw.coords[i].y0;
latestPoint.X = (nfloat)drawDrawerDraw.coords[i].x1;
latestPoint.Y = (nfloat)drawDrawerDraw.coords[i].y1;
//add lines to the touch points
if (pathItem.IsEmpty)
{
pathItem.AddLines(new CGPoint[] { initialPoint, latestPoint });
}
else
{
pathItem.AddLineToPoint(latestPoint);
}
path.Add(pathItem);
}
I'm using a picture box in my C# code with a given image.
i do all the paint actions in the Pain event (see code below).
when the image needs to be updated (ading dots on it) i call Invalidate to make it repaint. however after some time the image and the dots dissapear and are replaced by e big red X in a red border filling up the picture box.
note that the image is saved in the save folder as the exe file. and is appearing ok at first (same with the dots).
what am i doing wrong?
Crossthread problem?
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
setImage = Image.FromFile("IMG_1612.png");
Pen p = new Pen(Color.Red);
var g = Graphics.FromImage(setImage);//e.Graphics;
g.DrawImage(setImage,0,0);
foreach (Circles element in _circles)
{
g.FillEllipse(new SolidBrush(element.color), element.Punt.X, element.Punt.Y, _CIRCLESIZE, _CIRCLESIZE);
}
this.pictureBox1.Image = setImage;
g.Dispose();
}
delegate void PicturBoxUpdate(Control control);
private void UpdatePictureBox(Control control)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (control.InvokeRequired)
{
PicturBoxUpdate d = new PicturBoxUpdate(UpdatePictureBox);
this.Invoke(d, new object[] { control});
}
else
{
control.Invalidate();
control.Update();
}
}
private void DataProcessing(string data)
{
data = data.Replace('<', ' ').Replace('>',' ').Trim();
string[] processingdata = data.Split(';');
Circles tempCircle;
for (int i = 0; i < processingdata.Length-2;i++)
{
_data[i] = Convert.ToByte(processingdata[i], 16);
BitArray localdata = new BitArray(BitConverter.GetBytes(_data[i]).ToArray());
switch(i)
{
case 0:
if (_data[i] == 0xAA)
{
tempCircle = _circles[5];
tempCircle.color = Color.Green;
_circles[5] = tempCircle;
tempCircle = _circles[4];
tempCircle.color = Color.Green;
_circles[4] = tempCircle;
}
else
{
if (localdata.Get(1) & !localdata.Get(2))
{
tempCircle = _circles[5];
tempCircle.color = Color.Orange;
_circles[5] = tempCircle;
tempCircle = _circles[4];
tempCircle.color = Color.Orange;
_circles[4] = tempCircle;
}
if (localdata.Get(3) & !localdata.Get(0))
{
tempCircle = _circles[5];
tempCircle.color = Color.Blue;
_circles[5] = tempCircle;
}
if (localdata.Get(5) & !localdata.Get(4))
{
tempCircle = _circles[4];
tempCircle.color = Color.Blue;
_circles[4] = tempCircle;
}
if (localdata.Get(7) & !localdata.Get(6))
{
tempCircle = _circles[4];
tempCircle.color = Color.Purple;
_circles[4] = tempCircle;
tempCircle = _circles[5];
tempCircle.color = Color.Purple;
_circles[5] = tempCircle;
}
}
break;
case 1:
break;
default:
break;
}
}
UpdatePictureBox(pictureBox1);
GC.Collect();
}
You definitely shouldn't be using Image.FromFile() in the Paint() event like that; set it once and simple draw your circles using the supplied Graphics in the Paint() event:
private Image setImage;
private void Form1_Load(object sender, EventArgs e)
{
setImage = Image.FromFile("IMG_1612.png");
pictureBox1.Image = setImage;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
foreach (Circles element in _circles)
{
using (SolidBrush B = new SolidBrush(element.Color))
{
g.FillEllipse(B, element.Punt.X, element.Punt.Y, _CIRCLESIZE, _CIRCLESIZE);
}
}
}
I want to detect hand state if it gripped or released using Kinect SDK v1.7 implemented with C#, but I'm tired from this an exception:
Exception thrown: 'System.InvalidOperationException' in
Microsoft.Kinect.Toolkit.Interaction.dll
The code:
private void InteractionStreamOnInteractionFrameReady(object sender,InteractionFrameReadyEventArgs e)
{
using (InteractionFrame frame = e.OpenInteractionFrame())
{
if (frame != null)
{
if (this.userInfos == null)
{
this.userInfos = new UserInfo[InteractionFrame.UserInfoArrayLength];
}
frame.CopyInteractionDataTo(this.userInfos);
}
else
{ return;}
}
foreach (UserInfo userInfo in this.userInfos)
{
foreach (InteractionHandPointer handPointer in userInfo.HandPointers)
{
string action = null;
switch (handPointer.HandEventType)
{
case InteractionHandEventType.Grip:
action = "gripped";
break;
case InteractionHandEventType.GripRelease:
action = "released";
break;
}
if (action != null)
{
string handSide = "unknown";
switch (handPointer.HandType)
{
case InteractionHandType.Left:
handSide = "left";
break;
case InteractionHandType.Right:
handSide = "right";
break;
}
if (handSide == "left")
{
if (action == "released")
{
// left hand released code here
//MessageBox.Show("left hand released");
System.Console.WriteLine("left hand released");
}
else
{
// left hand gripped code here
System.Console.WriteLine("left hand gripped");
}
}
else
{
if (action == "released")
{
// right hand released code here
System.Console.WriteLine("right hand released");
}
else
{
// right hand gripped code here
System.Console.WriteLine("right hand gripped");
}
}
}
}
}
}
private void WindowLoaded(object sender, RoutedEventArgs e)
{
// Create the drawing group we'll use for drawing
this.drawingGroup = new DrawingGroup();
// Create an image source that we can use in our image control
this.imageSource = new DrawingImage(this.drawingGroup);
// Display the drawing using our image control
Image.Source = this.imageSource;
sensor = KinectSensor.KinectSensors.FirstOrDefault();
if (null != this.sensor )
{
System.Console.WriteLine("Start streaming");
skeletons = new
Skeleton[sensor.SkeletonStream.FrameSkeletonArrayLength];
userInfos = new
UserInfo[InteractionFrame.UserInfoArrayLength];
this.sensor.SkeletonStream.Enable();
this.interactionStream = new InteractionStream(this.sensor,
new
DummyInteractionClient());
this.interactionStream.InteractionFrameReady +=
InteractionStreamOnInteractionFrameReady;
sensor.SkeletonFrameReady += SensorSkeletonFrameReady;
// Start the sensor !
try
{
this.sensor.Start();
}
catch (IOException)
{
//////// this.sensor = null;
}
}
if (null == this.sensor)
{
System.Console.WriteLine("NoKinectReady");
}
}
Rotating a bitmapImage using UriSource works using the following code
private void RotateDocumentImageSourceRight()
{
var biOriginal = (BitmapImage)documentImage.Source;
if (biOriginal == null)
return;
var biRotated = new BitmapImage();
biRotated.BeginInit();
biRotated.UriSource = biOriginal.UriSource;
switch (biOriginal.Rotation)
{
case Rotation.Rotate0:
biRotated.Rotation = Rotation.Rotate270;
break;
case Rotation.Rotate270:
biRotated.Rotation = Rotation.Rotate180;
break;
case Rotation.Rotate180:
biRotated.Rotation = Rotation.Rotate90;
break;
case Rotation.Rotate90:
biRotated.Rotation = Rotation.Rotate0;
break;
default:
break;
}
biRotated.EndInit();
documentImage.Source = biRotated;
}
But when I change the way a bitmapImage is stored to StreamSource it doesn't work and the image disappears
private void RotateDocumentImageSourceRight()
{
...same code...
biRotated.StreamSource= biOriginal.StreamSource;
...same code...
}
You can use a TransformedBitmap, which is used by BitmapImage internally when you specify the Rotation property. It will create a new image, but it will be faster (it won't need to read the stream again).
private void RotateDocumentImageSourceRight()
{
var biOriginal = (BitmapSource)documentImage.Source;
if (biOriginal == null)
return;
var angle = 0.0;
var biRotated = biOriginal as TransformedBitmap;
if (biRotated != null)
{
biOriginal = biRotated.Source;
angle = ((RotateTransform)biRotated.Transform).Angle;
}
angle -= 90;
if (angle < 0) angle += 360;
biRotated = new TransformedBitmap(biOriginal, new RotateTransform(angle));
documentImage.Source = biRotated;
}
So Im making tetris and I dont know how to draw the blocks( L,I,Z etc) I have one block as Texture2D and every class for the blocks look like this:
namespace Tetris
{
public class ZBlock
{
Color Color;
const int x = 4;
const int y = 4;
bool[,] vorm;
public bool[,] zblock()
{
vorm = new bool[x, y];
for(int i=0; i< x; i++)
for (int j=0; j<y; j++)
{
vorm[i, j] = false;
vorm[0, 0] = true;
vorm[1, 0] = true;
vorm[1, 1] = true;
vorm[2, 1] = true;
}
Color = Color.Purple;
return vorm;
}
}
and this is the block class:
namespace Tetris
{
public class Block
{
Texture2D block;
Vector2 BlockPosition = new Vector2(30, 30);
float FallTimer;
Random Random = new Random();
ZBlock zBlock = new ZBlock();
TBlock tBlock = new TBlock();
SBlock sBlock = new SBlock();
OBlock oBlock = new OBlock();
JBlock jBlock = new JBlock();
LBlock lBlock = new LBlock();
IBlock iblock = new IBlock();
public bool[,] blockvorm()
{
bool[,] vorm;
vorm = new bool[4, 4];
vorm[3, 3] = false;
int r = Random.Next(7);
if (r == 0)
{
ZBlock.zblock();
}
else if (r == 1)
{
TBlock.tblock();
}
else if (r == 2)
{
SBlock.sblock();
}
else if (r == 3)
{
OBlock.oblock();
}
else if (r == 4)
{
JBlock.jblock();
}
else if (r == 5)
{
LBlock.lblock();
}
else if (r == 6)
{
IBlock.iblock();
}
return vorm;
}
public TBlock TBlock
{
get { return tBlock; }
}
public ZBlock ZBlock
{
get { return zBlock; }
}
public SBlock SBlock
{
get { return sBlock; }
}
public OBlock OBlock
{
get { return oBlock; }
}
public JBlock JBlock
{
get { return jBlock; }
}
public LBlock LBlock
{
get { return lBlock; }
}
public IBlock IBlock
{
get { return iblock; }
}
public void Draw(GameTime gameTime, SpriteBatch spriteBatch, ContentManager Content)
{
block = Content.Load<Texture2D>("Block");
int[,] Grid = Tetris.GameWorld.TetrisGrid;
spriteBatch.Begin();
spriteBatch.Draw(?????????????);
spriteBatch.End();
}
So the problem is: I dont know how to draw those blocks (I know how to draw one block but I want the complete ones). I thought maybe ZBlock.vorm or ZBLock.zblock but both give errors.
Does anyone know how to draw the blocks?
Ok so here is a partial answer. What you want to do is basically just draw each block with a certain offset from the next block equal to: blockWidth / 2 in pixels. This means that the blocks will be correctly orientated without overlap.
Here is what you should put in the draw statement:
public void Draw(int theXPosition, int theYPosition, Color theColor, SpriteBatch theSpriteBatch, Texture2D theBlockTexture)
{
int aTextureStartX = Color * Convert.ToInt32(mBlockSize);
for (int aBlock = 0; aBlock < mNumberOfBlocks; aBlock++)
{
int aXPosition = (int)(theXPosition + (CurrentShape[Rotation, aBlock, 0] * mBlockSize));
int aYPosition = (int)(theYPosition + (CurrentShape[Rotation, aBlock, 1] * mBlockSize));
theSpriteBatch.Draw(theBlockTexture, new Rectangle(aXPosition, aYPosition, mBlockSize, mBlockSize), new Rectangle(aTextureStartX, 0, mBlockSize, mBlockSize),
}
}
This is from a blog: http://www.xnadevelopment.com/tutorials/fallingblocksyoumovetomakelines/fallingblocksyoumovetomakelines.shtml
The source code is at the top of the page.