Problem with gdk.Pixbuf in gtk# Mono - mono

I'm creating a small drawing program in Mono gtk# and using the Cairo graphics library. I'm coding and compiling on a MacOs X system. I have a drawable object which I put into Pixbuf at a certain time and then retrieve it later into the drawable object! The idea is to take a "snapshot" of the image in the drawable and then draw on top of it.
The problem is that when I put the Pixbuf back into the drawable it looks obscure, all yellow with stripes and it looks like a portion of the image is missing.
UPDATE: I ran the program on my linux and windows machines and there it works flawlessly! So this error is only on MacOs X.
Here's the code:
// use: gmcs -pkg:gtk-sharp-2.0 -pkg:mono-cairo ttv1.cs
using Gtk;
using Cairo;
using System;
public class Draw : Window
{
DrawingArea canvas;
public Gdk.Pixbuf pixbuf;
public Draw() : base("teikniteink")
{
canvas = new DrawingArea();
canvas.ExposeEvent += canvasExposed;
DeleteEvent += delegate { Application.Quit();};
KeyPressEvent += onKey;
SetDefaultSize(400,400);
SetPosition(WindowPosition.Center);
Add(canvas);
ShowAll();
}
private void onKey(object o, KeyPressEventArgs args)
{
switch (args.Event.Key)
{
case Gdk.Key.w:
Console.WriteLine("Key Pressed {0}", args.Event.Key);
// Send to Pixbuf
pixbuf = Gdk.Pixbuf.FromDrawable(canvas.GdkWindow, Gdk.Colormap.System,0,0,0,0,400,400);
// Save to output.png
pixbuf.Save ("output.png", "png");
break;
case Gdk.Key.e:
Console.WriteLine("Key Pressed {0}", args.Event.Key);
Gdk.GC g = new Gdk.GC(canvas.GdkWindow);
// Retrive from pixbuf
canvas.GdkWindow.DrawPixbuf (g,pixbuf,0,0,0,0,-1,-1,Gdk.RgbDither.Normal,0,0);
break;
}
}
private void canvasExposed(object o, ExposeEventArgs args)
{
using (Cairo.Context ctx = Gdk.CairoHelper.Create(canvas.GdkWindow))
{
PointD start = new PointD(100,100);
PointD end = new PointD(300,300);
double width = Math.Abs(start.X - end.X);
double height = Math.Abs(start.Y - end.Y);
double xcenter = start.X + (end.X - start.X) / 2.0;
double ycenter = start.Y + (end.Y - start.Y) / 2.0;
ctx.Save();
ctx.Translate(xcenter, ycenter);
ctx.Scale(width/2.0, height/2.0);
ctx.Arc(0.0, 0.0, 1.0, 0.0, 2*Math.PI);
ctx.Restore();
ctx.Stroke();
}
}
public static void Main()
{
Application.Init();
new Draw();
Application.Run();
}
}
It would be very much appreciated if someone knows whats going on here and can point me in the right direction to fix it.

I triggered the same problem in this manner:
gw = gtk_widget_get_window(GTK_WIDGET(GLOBALS->mainwindow));
if(gw)
{
gdk_drawable_get_size(gw, &w, &h);
cm = gdk_drawable_get_colormap(gw);
if(cm)
{
dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w, h);
if(dest)
{
dest2 = gdk_pixbuf_get_from_drawable(dest, gw, cm, 0, 0, 0, 0, w, h);
if(dest2)
{
succ = gdk_pixbuf_save (dest2, *GLOBALS->fileselbox_text, "png", &err, NULL);
}
}
}
}
The gdk_pixbuf_get_from_drawable() function when the source drawable is a Quartz window has issues, specifically in how _gdk_quartz_image_copy_to_image() services it. In short, 256 bit vertical strips are converted but the conversion routine assumes the pixels are 24-bit RGB rather than 32-bit RGBA. The following patch fixed the problem for me:
--- gtk+/gdk/quartz/gdkimage-quartz.c 2011-12-03 14:24:03.000000000 -0600
+++ gtk+664894/gdk/quartz/gdkimage-quartz.c 2013-10-15 18:52:24.000000000 -0500
## -150,6 +150,10 ## _gdk_quartz_image_copy_to_image (GdkDraw
data = [rep bitmapData];
size = [rep size];
+ int bpr = [rep bytesPerRow];
+ int wid = size.width;
+ int bpx = bpr/wid;
+
for (y = 0; y < size.height; y++)
{
guchar *src = data + y * [rep bytesPerRow];
## -158,12 +162,15 ## _gdk_quartz_image_copy_to_image (GdkDraw
{
gint32 pixel;
+ if (bpx == 4) // fix gdk_pixbuf_get_from_drawable "yellow stripes"
+ pixel = src[0] << 16 | src[1] << 8 | src[2];
+ else
if (image->byte_order == GDK_LSB_FIRST)
pixel = src[0] << 8 | src[1] << 16 |src[2] << 24;
else
pixel = src[0] << 16 | src[1] << 8 |src[2];
- src += 3;
+ src += bpx;
gdk_image_put_pixel (image, dest_x + x, dest_y + y, pixel);
}
I don't know if this was fixed in future versions of the GTK OSX source. I use my own for producing binaries of gtkwave as I have some necessary patches that were seemingly never integrated into the jhbuild source tree long ago.
-Tony

Related

In vulkan: I want save a depth image to file, but always got a error depth image

I want to save a depth image that from frame buffer render result.
1, I create a stage buffer used to save image data.
2, use vkCmdCopyImageToBuffer copy depth image to stage buffer.
3, use vkMapMemory map this stage buffer memory to host memory.
4, read host memory and write depth data to a file.
but always got an error depth image. I don't know where have wrong.
application window output.
bug depth image file.
(source file)
save depth image function:
VkDeviceSize size = WIDTH * HEIGHT * 4;
VkBuffer dstBuffer;
VkDeviceMemory dstMemory;
createBuffer(
size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
dstBuffer,
dstMemory);
VkCommandBuffer copyCmd = beginSingleTimeCommands();
// depth format -> VK_FORMAT_D32_SFLOAT_S8_UINT
VkBufferImageCopy region = {};
region.bufferOffset = 0;
region.bufferImageHeight = 0;
region.bufferRowLength = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = VkOffset3D{ 0, 0, 0 };
region.imageExtent = VkExtent3D{ swapChainExtent.width, swapChainExtent.height, 1};
vkCmdCopyImageToBuffer(
copyCmd,
depthImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dstBuffer,
1,
&region
);
endSingleTimeCommands(copyCmd);
// Map image memory so we can start copying from it
void *data;
vkMapMemory(device, dstMemory, 0, size, 0, &data);
std::ofstream file(path, std::ios::out | std::ios::binary);
// ppm header
file << "P6\n" << WIDTH << "\n" << HEIGHT << "\n" << 255 << "\n";
float *row = (float*)data;
auto size_v = WIDTH * HEIGHT;
for (uint32_t y = 0; y < size_v; y++) {
file.write((char*)row + 1, 1);
file.write((char*)row + 1, 1);
file.write((char*)row + 1, 1);
row++;
}
file.close();
// Clean up resources
vkUnmapMemory(device, dstMemory);
vkFreeMemory(device, dstMemory, nullptr);
vkDestroyBuffer(device, dstBuffer, nullptr);
hope someone drag me out. thanks!
Assuming you've done all the transfer work correctly, your mapped data is basically an array of floats. This is reflected in your code by this line:
float *row = (float*)data;
However, when you actually write out the file you're treating the data like bytes...
file.write((char*)row + 1, 1);
So you're writing out 8 bytes of a 32 bit float. What you need is some function to convert from the float to a color value.
Assuming the depth value is normalized (I can't remember off the top of my head whether this is the case, or if it's dependent on the pipeline or framebuffer setup) and if you just want greyscale, you could use
uint8_t map(float f) {
return (uint8_t)(f * 255.0f);
}
and inside your file writing loop you'd so something like
uint8_t grey = map(*row);
file.write(&grey, 1);
file.write(&grey, 1);
file.write(&grey, 1);
++row;
Alternatively if you want some sort of color gradient for easier visulization you'd want a more complex mapping function...
vec3 colorWheel(float normalizedHue) {
float v = normalizedHue * 6.f;
if (v < 0.f) {
return vec3(1.f, 0.f, 0.f);
} else if (v < 1.f) {
return vec3(1.f, v, 0.f);
} else if (v < 2.f) {
return vec3(1.f - (v-1.f), 1.f, 0.f);
} else if (v < 3.f) {
return vec3(0.f, 1.f, (v-2.f));
} else if (v < 4.f) {
return vec3(0.f, 1.f - (v-3.f), 1.f );
} else if (v < 5.f) {
return vec3((v-4.f), 0.f, 1.f );
} else if (v < 6.f) {
return vec3(1.f, 0.f, 1.f - (v-5.f));
} else {
return vec3(1.f, 0.f, 0.f);
}
}
and in your file output loop...
vec3 color = colorWheel(*row);
uint8_t r = map(color.r);
uint8_t g = map(color.g);
uint8_t b = map(color.b);
file.write(&r, 1);
file.write(&g, 1);
file.write(&b, 1);
++row;

'Grenade' projection based on angle + bounce

I'm having some trouble with synthesizing an advanced object projection formula. I have already figured out few basic physic simulation formulas such as:
Velocity of object:
x += cos(angle);
y += sin(angle);
*where angle can be obtained by either mouse position or with tan(...target and intial values)
but that only travels straight based on the angle.
Gravity:
Yvelocity = Yvelocity - gravity;
if(!isHitPlatform) {
Obj.y += YVelocity
}
Bounce:// No point if we've not been sized...
if (height > 0) {
// Are we bouncing...
if (bounce) {
// Add the vDelta to the yPos
// vDelta may be postive or negative, allowing
// for both up and down movement...
yPos += vDelta;
// Add the gravity to the vDelta, this will slow down
// the upward movement and speed up the downward movement...
// You may wish to place a max speed to this
vDelta += gDelta;
// If the sprite is not on the ground...
if (yPos + SPRITE_HEIGHT >= height) {
// Seat the sprite on the ground
yPos = height - SPRITE_HEIGHT;
// If the re-bound delta is 0 or more then we've stopped
// bouncing...
if (rbDelta >= 0) {
// Stop bouncing...
bounce = false;
} else {
// Add the re-bound degregation delta to the re-bound delta
rbDelta += rbDegDelta;
// Set the vDelta...
vDelta = rbDelta;
}
}
}
}
I need help way to combine these three formulas to create an efficient and lightweight algorithm that allows an object to be projected in an arch determined by the angle, yet continues to bounce a few times before coming to a stop, all with an acceptable amount of discontinuity between each point. *Note: Having the grenade be determined by a f(x) = -x^2 formula creates a larger jump discontinuity as the slope increases, forcing you to reverse the formula to find x = +-y value (to determine whether + or -, check the bounds).
something like:
class granade
{
private static final double dt = 0.1; // or similar
private double speedx;
private double speedy;
private double positionx;
private double positiony;
public granade(double v, double angle)
{
speedx = v * Math.cos(angle);
speedy = v * Math.sin(angle);
positionx = 0;
positiony = 0;
}
public void nextframe()
{
// update speed: v += a*dt
speedy -= gravity* dt;
// update position: pos += v*dt
positionx += speedx * dt;
double newpositiony = positiony + speedy*dt;
// bounce if hit ground
if (newpositiony > 0)
positiony = newpositiony;
else {
// bounce vertically
speedy *= -1;
positiony = -newpositiony;
}
}
public void draw() { /* TODO */ }
}
OT: avoid Math.atan(y/x), use Math.atan2(y, x)

How to find the Joint coordinates(X,Y,Z) ,also how to draw a locus of the tracked joint?

I am trying to develop a logic to recognize a circle which is made by users right hand, I got the code to draw the skeleton and track from the sample code,
private void SensorSkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
Skeleton[] skeletons = new Skeleton[0];
using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
{
if (skeletonFrame != null)
{
skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];
skeletonFrame.CopySkeletonDataTo(skeletons);
}
}
using (DrawingContext dc = this.drawingGroup.Open())
{
// Draw a transparent background to set the render size
dc.DrawRectangle(Brushes.Black, null, new Rect(0.0, 0.0, RenderWidth, RenderHeight));
if (skeletons.Length != 0)
{
foreach (Skeleton skel in skeletons)
{
RenderClippedEdges(skel, dc);
if (skel.TrackingState == SkeletonTrackingState.Tracked)
{
this.DrawBonesAndJoints(skel, dc);
}
else if (skel.TrackingState == SkeletonTrackingState.PositionOnly)
{
dc.DrawEllipse(
this.centerPointBrush,
null,
this.SkeletonPointToScreen(skel.Position),
BodyCenterThickness,
BodyCenterThickness);
}
}
}
// prevent drawing outside of our render area
this.drawingGroup.ClipGeometry = new RectangleGeometry(new Rect(0.0, 0.0, RenderWidth, RenderHeight));
}
}
What I want to do now is to track the coordinates of users right hand for gesture recognition,
Here is how I am planning to get the job done:
Start the gesture
Draw the circled gesture, Make sure to store the coordinates for start and then keep noting the coordinates for every 45 degree shift of the Joint from the start, for 8 octants we will get 8 samples.
For making a decision that a circle was drawn we can just check the relation ship between the eight samples.
Also, in the depthimage I want to show the locus of the drawn gesture, so as the handpoint moves it leaves a trace behind so at the end we will get a figure which was drawn by an user. I have no idea how to achieve this.
Coordinates for each joint are available for each tracked skeleton during each SkeletonFrameReady event. Inside your foreach loop...
foreach (Skeleton skeleton in skeletons) {
// get the joint
Joint rightHand = skeleton.Joints[JointType.HandRight];
// get the individual points of the right hand
double rightX = rightHand.Position.X;
double rightY = rightHand.Position.Y;
double rightZ = rightHand.Position.Z;
}
You can look at the JointType enum to pull out any of the joints and work with the individual coordinates.
To draw your gesture trail you can use the DrawContext you have in your example or use another way to draw a Path onto the visual layer. With your x/y/z values, you would need to scale them to the window coordinates. The "Coding4Fun" library offers a pre-built function to do it; alternatively you can write your own, for example:
private static double ScaleY(Joint joint)
{
double y = ((SystemParameters.PrimaryScreenHeight / 0.4) * -joint.Position.Y) + (SystemParameters.PrimaryScreenHeight / 2);
return y;
}
private static void ScaleXY(Joint shoulderCenter, bool rightHand, Joint joint, out int scaledX, out int scaledY)
{
double screenWidth = SystemParameters.PrimaryScreenWidth;
double x = 0;
double y = ScaleY(joint);
// if rightHand then place shouldCenter on left of screen
// else place shouldCenter on right of screen
if (rightHand)
{
x = (joint.Position.X - shoulderCenter.Position.X) * screenWidth * 2;
}
else
{
x = screenWidth - ((shoulderCenter.Position.X - joint.Position.X) * (screenWidth * 2));
}
if (x < 0)
{
x = 0;
}
else if (x > screenWidth - 5)
{
x = screenWidth - 5;
}
if (y < 0)
{
y = 0;
}
scaledX = (int)x;
scaledY = (int)y;
}

Android 2D animation drawing: bad performance

I have an app that draws a grid of dots (let's say 5x5). The user is asked to draw lines on that grid. If the user's finger touches one of the dots in the grid, this dot is being colored to show that this dot is part of a path drawn. In addition a line will be drawn between each two touched dots.
The issue - I get very bad performance, which causes few things:
The application gets really slow.
Motion events in event.getAction() get bad granularity. I meanenter code here that instead of registering a movement each 10 pixels for example, it registers movements each 100 pixels. This, in turn, will causes the app to NOT redraw some dots the user had touched.
Sometimes the motion coordinates are simple wrong: lets say the user is moving her finger from pixel 100 to pixel 500, the reading might show 100...200...150...140...300...400. For some reason the touch location gets messed up in some cases.
Look at the example on how the app "misses out" on dots the user have touched and doesn't draw the green dots:
I've tried few thing:
Adding Thread.sleep(100); to else if(event.getAction() == MotionEvent.ACTION_MOVE) inside onTouchEvent(MotionEvent event), I read that this might give the CPU time to catch up on all those touch events - didn't change a thing
Adding this.destroyDrawingCache() to the very end of doDraw() (I use it instead of onDraw, as was suggested by one tutorial I used). I thought this will clear all event/drawing caching which seems to be slowing down the system - didn't change a thing.
I am fairly new to Android animation so I am not sure how to proceed:
I understand I should do as little as possible in doDraw() (my onDraw()) and onTouchEvent().
I read some stuff about invalidate() but not sure how and when to use it. If I understand correctly, my View gets drawn anew each time doDraw() is called. My grid, for instance, is static - how can I avoid redrawing it?
++++++++++++++++++++++++ UPDATE 7th Oct +++++++++++++++++++++
I tried using canvas.drawCircle(xPos, yPos, 8, mNodePaint); instead of canvas.drawBitmap(mBitmap, xPos, yPos, null);. I thought that if I DIDN'T use actual bitmaps this might improve performance. As a matter of fact - it didn't! I am a bit confused how such a simple application can pose such a heavy load on the device. I must be doing something really the wrong way.
++++++++++++++++++++++++ UPDATE 12th Oct +++++++++++++++++++++
I took into account what #LadyWoodi suggested - I've eliminated all variable declarations out of the loops - anyway it is a bad practice and I also got rid of all the "System.Out" lines I use so I can log app behavior to better understand why I get such a lame performance. I am sad to say that if there was a change in performance (I didn't actually measure frame rate change) it is negligible.
Any other ideas?
++++++++++++++++++++++++ UPDATE 13th Oct +++++++++++++++++++++
As I have a static grid of dots (see hollow black/white dots in screenShot) that never changes during the game I did the following:
-Draw the grid once.
-Capture the drawing as bitmap using Bitmap.createBitmap().
-Use canvas.drawBitmap() to draw the bitmap of the static dots grid.
-When my thread runs I check to see it the grid of dots is drawn. If it is running I will NOT recreate the static dots grid. I will only render it from my previously rendered bitmap.
Surprisingly this changed nothing with my performance! Redrawing the dots grid each time didn't have a true visual effect on app performance.
I decided to use canvas = mHolder.lockCanvas(new Rect(50, 50, 150, 150)); inside my drawing thread. It was just for testing purposes to see if I limit the area rendered each time, I can get the performance better. This DID NOT help either.
Then I turned to the DDMS tool in Eclipse to try and profile the app. What it came up with, was that canvas.drawPath(path, mPathPaint); (Canvas.native_drawPath) consumed about 88.5% of CPU time!!!
But why??! My path drawing is rather simple, mGraphics contains a collection of Paths and all I do is figure out if each path is inside the boundaries of the game screen and then I draw a path:
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
Can anyone see any ill programmed lines of code in my examples?
++++++++++++++++++++++++ UPDATE 14th Oct +++++++++++++++++++++
I've made changes to my doDraw()method. Basically what I do is draw the screen ONLY if something was changed. In all other cases I simply store a cached bitmap of the screen and render it. Please take a look:
public void doDraw(Canvas canvas)
{
synchronized (mViewThread.getSurefaceHolder())
{
if(mGraphics.size() > mPathsCount)
{
mPathsCount = mGraphics.size();
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
//nodes that the path goes through, are repainted green
//these nodes are building the drawn pattern
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (PathPoint nodeHit : nodePattern)
{
canvas.drawBitmap(mDotOK, nodeHit.x - ((mDotOK.getWidth()/2) - (mNodeBitmap.getWidth()/2)), nodeHit.y - ((mDotOK.getHeight()/2) - (mNodeBitmap.getHeight()/2)), null);
}
}
mGameField = Bitmap.createBitmap(mGridNodesCount * mNodeGap, mGridNodesCount * mNodeGap, Bitmap.Config.ARGB_8888);
}
else
{
canvas.drawBitmap(mGameField, 0f, 0f, null);
}
Now for the results - as long as the device doesn't have to render no paths and simply draws from a bitmap, stuff goes very fast. But the moment I have to rerender the screen using canvas.drawPath() performance becomes as sluggish as a turtle on morphine... The more paths I have (up to 6 and more, which is NOTHING!) the slower the rendering. How odd is this?? - My paths are even not really curvy - the are all straight lines with an occasional turn. What I mean is that the line is not very "complex".
I've add more code below - if you have any improvements ideas.
Many thanks in advance,
D.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Class "Panel" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
Bitmap mNodeBitmap;
int mNodeBitmapWidthCenter;
int mNodeBitmapHeightCenter;
Bitmap mDotOK;
ViewThread mViewThread;
ArrayList<PathPoint> mPathPoints;
private ArrayList<Path> mGraphics = new ArrayList<Path>(3);
private ArrayList<ArrayList<PathPoint>> mNodesHitPatterns = new ArrayList<ArrayList<PathPoint>>();
private Paint mPathPaint;
Path mPath = new Path();
//private ArrayList<Point> mNodeCoordinates = new ArrayList<Point>();
private int mGridNodesCount = 5;
private int mNodeGap = 100;
PathPoint mNodeCoordinates[][] = new PathPoint[mGridNodesCount][mGridNodesCount];
PathMeasure mPm;
float mStartCoordinates[] = {0f, 0f};
float mEndCoordinates[] = {0f, 0f};
PathPoint mPathPoint;
Boolean mNodesGridDrawn = false;
Bitmap mGameField = null;
public Boolean getNodesGridDrawn() {
return mNodesGridDrawn;
}
public Panel(Context context) {
super(context);
mNodeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot);
mNodeBitmapWidthCenter = mNodeBitmap.getWidth()/2;
mNodeBitmapHeightCenter = mNodeBitmap.getHeight()/2;
mDotOK = BitmapFactory.decodeResource(getResources(), R.drawable.dot_ok);
getHolder().addCallback(this);
mViewThread = new ViewThread(this);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true); //for better color
mPathPaint.setColor(0xFFFFFF00);
mPathPaint.setStyle(Paint.Style.STROKE);
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeCap(Paint.Cap.ROUND);
mPathPaint.setStrokeWidth(5);
}
public ArrayList<ArrayList<PathPoint>> getNodesHitPatterns()
{
return this.mNodesHitPatterns;
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
//setPadding(100, 100, 0, 0);
if (!mViewThread.isAlive()) {
mViewThread = new ViewThread(this);
mViewThread.setRunning(true);
mViewThread.start();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mViewThread.isAlive()) {
mViewThread.setRunning(false);
}
}
//draw the basic nodes grid that the user will use to draw the lines on
//store as bitmap
public void drawNodesGrid(Canvas canvas)
{
canvas.drawColor(Color.WHITE);
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
int xPos = j * mNodeGap;
int yPos = i * mNodeGap;
try
{
//TODO - changed
mNodeCoordinates[i][j] = new PathPoint(xPos, yPos, null);
}
catch (Exception e)
{
e.printStackTrace();
}
canvas.drawBitmap(mNodeBitmap, xPos, yPos, null);
}
}
mNodesGridDrawn = true;
mGameField = Bitmap.createBitmap(mGridNodesCount * mNodeGap, mGridNodesCount * mNodeGap, Bitmap.Config.ARGB_8888);
}
public void doDraw(Canvas canvas)
{
canvas.drawBitmap(mGameField, 0f, 0f, null);
synchronized (mViewThread.getSurefaceHolder())
{
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
//nodes that the path goes through, are repainted green
//these nodes are building the drawn pattern
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (PathPoint nodeHit : nodePattern)
{
canvas.drawBitmap(mDotOK, nodeHit.x - ((mDotOK.getWidth()/2) - (mNodeBitmap.getWidth()/2)), nodeHit.y - ((mDotOK.getHeight()/2) - (mNodeBitmap.getHeight()/2)), null);
}
}
this.destroyDrawingCache();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (mViewThread.getSurefaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
//System.out.println("Action downE x: " + event.getX() + " y: " + event.getY());
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
//TODO - changed
//PathPoint pathPoint = mNodeCoordinates[i][j];
mPathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - mPathPoint.x) <= 35) && (Math.abs((int)event.getY() - mPathPoint.y) <= 35))
{
//mPath.moveTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//System.out.println("Action down x: " + pathPoint.x + " y: " + pathPoint.y);
ArrayList<PathPoint> newNodesPattern = new ArrayList<PathPoint>();
mNodesHitPatterns.add(newNodesPattern);
//mNodesHitPatterns.add(nh);
//pathPoint.setAction("down");
break;
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_MOVE)
{
final int historySize = event.getHistorySize();
//System.out.println("historySize: " + historySize);
//System.out.println("Action moveE x: " + event.getX() + " y: " + event.getY());
coordinateFound:
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
//TODO - changed
//PathPoint pathPoint = mNodeCoordinates[i][j];
mPathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - mPathPoint.x) <= 35) && (Math.abs((int)event.getY() - mPathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!mPathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
else //no current match => try historical
{
if(historySize > 0)
{
for (int k = 0; k < historySize; k++)
{
//System.out.println("Action moveH x: " + event.getHistoricalX(k) + " y: " + event.getHistoricalY(k));
if((Math.abs((int)event.getHistoricalX(k) - mPathPoint.x) <= 35) && (Math.abs((int)event.getHistoricalY(k) - mPathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!mPathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
}
}
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_UP)
{
// for (int i = 0; i < mGridSize; i++) {
//
// for (int j = 0; j < mGridSize; j++) {
//
// PathPoint pathPoint = mNodeCoordinates[i][j];
//
// if((Math.abs((int)event.getX() - pathPoint.x) <= 35) && (Math.abs((int)event.getY() - pathPoint.y) <= 35))
// {
// //the location of the node
// //mPath.lineTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//
// //System.out.println("Action up x: " + pathPoint.x + " y: " + pathPoint.y);
//
// //mGraphics.add(mPath);
// // mNodesHit.add(pathPoint);
// // pathPoint.setAction("up");
// break;
// }
// }
// }
}
//System.out.println(mNodesHitPatterns.toString());
//create mPath
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (int i = 0; i < nodePattern.size(); i++)
{
if(i == 0) //first node in pattern
{
mPath.moveTo(nodePattern.get(i).x + mNodeBitmapWidthCenter, nodePattern.get(i).y + mNodeBitmapHeightCenter);
}
else
{
mPath.lineTo(nodePattern.get(i).x + mNodeBitmapWidthCenter, nodePattern.get(i).y + mNodeBitmapWidthCenter);
}
//mGraphics.add(mPath);
}
}
mGraphics.add(mPath);
return true;
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Class "ViewThread" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class ViewThread extends Thread {
private Panel mPanel;
private SurfaceHolder mHolder;
private boolean mRun = false;
public ViewThread(Panel panel) {
mPanel = panel;
mHolder = mPanel.getHolder();
}
public void setRunning(boolean run) {
mRun = run;
}
public SurfaceHolder getSurefaceHolder()
{
return mHolder;
}
#Override
public void run()
{
Canvas canvas = null;
while (mRun)
{
canvas = mHolder.lockCanvas();
//canvas = mHolder.lockCanvas(new Rect(50, 50, 150, 150));
if (canvas != null)
{
if(!mPanel.getNodesGridDrawn())
{
mPanel.drawNodesGrid(canvas);
}
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
It's just the idea, but I would try to take all the declarations out of the loops. I know that it can be useful to have them localized, however it's usually really time consuming so it could help a little. My second idea was already tested by you in your update so now I am also curious how it will go ;)
You are using a SurfaceView? First of all, I recommend you to use a graphic library for your game... AndEngine for example is pretty easy to use and you will achieve to develop a much more beautiful game than using the Java canvas. The performance is better too.
I canĀ“t find anything wrong with your code, but there is a lot of processing in the draw method, and more important, in the onTouch event. You should avoid to use divisions or heavy math operations in the loops and try to pre-calculate everything before.
But I insist; for something like what you are doing, take a look at this and you will have it up and running in no time!

Using Angus J Clipper library to outline GraphicsPath text in vb.net

I am creating text in a GDI+ GraphicsPath, using DrawString, and then outputting this to PDF as a path.
This is all working perfectly at the moment.
The time I have an issue is when the chosen font causes the outlines to overlap each other. I have an image example though being a new user i can't upload it... (seems pointless..?..)
I found a library and also someone who has achieved the same as what I am looking for in this blog
I have converted the code snippet to vb.net though I keep getting an empty solution from the library.
Has anybody else managed to pass in a graphicsPath containing a string and retrieve outlined text using this or a similar library?
Here's some C# code that works ...
using ClipperLib;
static public void PathToPolygon(GraphicsPath path, Polygons polys, Single scale)
{
GraphicsPathIterator pathIterator = new GraphicsPathIterator(path);
pathIterator.Rewind();
polys.Clear();
PointF[] points = new PointF[pathIterator.Count];
byte[] types = new byte[pathIterator.Count];
pathIterator.Enumerate(ref points, ref types);
int i = 0;
while (i < pathIterator.Count)
{
Polygon pg = new Polygon();
polys.Add(pg);
do {
IntPoint pt = new IntPoint((int)(points[i].X * scale), (int)(points[i].Y * scale));
pg.Add(pt);
i++;
}
while (i < pathIterator.Count && types[i] != 0);
}
}
static private PointF[] PolygonToPointFArray(Polygon pg, float scale)
{
PointF[] result = new PointF[pg.Count];
for (int i = 0; i < pg.Count; ++i)
{
result[i].X = (float)pg[i].X / scale;
result[i].Y = (float)pg[i].Y / scale;
}
return result;
}
private void DrawBitmap()
{
Font f = new Font("Arial", 90);
Pen myPen = new Pen(Color.FromArgb(196, 0xC3, 0xC9, 0xCF), (float)0.6);
SolidBrush myBrush = new SolidBrush(Color.FromArgb(127, 0xDD, 0xDD, 0xF0));
path.Reset();
Polygons polys;
path.AddString("ABC", f.FontFamily, (int)f.Style, f.Size, new Point(100, 100), null);
path.Flatten();
//scale all points up by 100 because Clipper uses integer coordinates
PathToPolygon(path, polys, 100);
path.Reset();
//offset polys remembering to multiply delta by scaling amount ...
polys = Clipper.OffsetPolygons(polys, 7 * 100, JoinType.jtRound);
for (int i = 0; i < polys.Count(); i++)
{
//reverses scaling ...
PointF[] pts2 = PolygonToPointFArray(polys[i], 100);
path.AddPolygon(pts2);
}
newgraphic.FillPath(myBrush, path);
newgraphic.DrawPath(myPen, path);
}