Im trying to draw lines (GWT, Context2d) that are 1 pixel thick, code snippet below:
context.beginPath();
context.setStrokeStyle("rgb(255,0,0)");
context.setLineWidth(1f);
double x = 0;
double gridSize = 10.0f;
while (x < w){
x += gridSize;
context.moveTo(x, 0);
context.lineTo(x, h);
}
context.stroke();
This code draws lines that are at least 2 pixel thick.
Any ideas?
Try to add 0.5 to your coordinates.
Browsers apply antialiasing this may cause blurriness or "2 pixel thick lines".
Related
I'm designing a simple paint program and I need to make a color picker view for users to select background and paintbrush colors. I thought the best approach would be to split the rgb component of the color int in two and add them across a rectangle with vertical pixels being the 'rb' part and horizontal ones as the 'bg' part. For example: green - ff00ff00 would be broken into 00f and f00 and then added and OR'd with ff000000 to preserve the alpha. Maybe I'm doing the math wrong because I get a series of squares instead of a continuous gradient. Here is the relevant code:
int width = 500, height = 500;
int color = 0, xColor = 0, yColor = 0, MAX_HALF = 0xfff;
bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
xColor = x * MAX_HALF / width;
yColor = (y * MAX_HALF / height) << 12;
color = xColor | yColor | 0xff000000;
bitmap.setPixel(x,y,color);
}
}
canvas.drawBitmap(bitmap,0,0,null);
This is the image I get.
Is it the code or the math?
There are two main ways you can accomplish that with a drawable:
Use two linear gradients
Use two radial gradients
check this link
Android: How to draw a two dimensional gradient
I've made a lighting engine which allows for shadows. It works on a grid system where each pixel has a light value stored as an integer in an array. Here is a demonstration of what it looks like:
The shadow and the actual pixel coloring works fine. The only problem is the unlit pixels further out in the circle, which for some reason makes a very interesting pattern(you may need to zoom into the image to see it). Here is the code which draws the light.
public void implementLighting(){
lightLevels = new int[Game.WIDTH*Game.HEIGHT];
//Resets the light level map to replace it with the new lighting
for(LightSource lightSource : lights) {
//Iterates through all light sources in the world
double circumference = (Math.PI * lightSource.getRadius() * 2),
segmentToDegrees = 360 / circumference, distanceToLighting = lightSource.getLightLevel() / lightSource.getRadius();
//Degrades in brightness further out
for (double i = 0; i < circumference; i++) {
//Draws a ray to every outer pixel of the light source's reach
double radians = Math.toRadians(i*segmentToDegrees),
sine = Math.sin(radians),
cosine = Math.cos(radians),
x = lightSource.getVector().getScrX() + cosine,
y = lightSource.getVector().getScrY() + sine,
nextLit = 0;
for (double j = 0; j < lightSource.getRadius(); j++) {
int lighting = (int)(distanceToLighting * (lightSource.getRadius() - j));
double pixelHeight = super.getPixelHeight((int) x, (int)y);
if((int)j==(int)nextLit) addLighting((int)x, (int)y, lighting);
//If light is projected to have hit the pixel
if(pixelHeight > 0) {
double slope = (lightSource.getEmittingHeight() - pixelHeight) / (0 - j);
nextLit = (-lightSource.getRadius()) / slope;
/*If something is blocking it
* Using heightmap and emitting height, project where next lit pixel will be
*/
}
else nextLit++;
//Advances the light by one pixel if nothing is blocking it
x += cosine;
y += sine;
}
}
}
lights = new ArrayList<>();
}
The algorithm i'm using should account for every pixel within the radius of the light source not blocked by an object, so i'm not sure why some of the outer pixels are missing.
Thanks.
EDIT: What I found is, the unlit pixels within the radius of the light source are actually just dimmer than the other ones. This is a consequence of the addLighting method not simply changing the lighting of a pixel, but adding it to the value that's already there. This means that the "unlit" are the ones only being added to once.
To test this hypothesis, I made a program that draws a circle in the same way it is done to generate lighting. Here is the code that draws the circle:
BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
double radius = 100,
x = (WIDTH-radius)/2,
y = (HEIGHT-radius)/2,
circumference = Math.PI*2*radius,
segmentToRadians = (360*Math.PI)/(circumference*180);
for(double i = 0; i < circumference; i++){
double radians = segmentToRadians*i,
cosine = Math.cos(radians),
sine = Math.sin(radians),
xPos = x + cosine,
yPos = y + sine;
for (int j = 0; j < radius; j++) {
if(xPos >= 0 && xPos < WIDTH && yPos >= 0 && yPos < HEIGHT) {
int rgb = image.getRGB((int) Math.round(xPos), (int) Math.round(yPos));
if (rgb == Color.white.getRGB()) image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), 0);
else image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), Color.red.getRGB());
}
xPos += cosine;
yPos += sine;
}
}
Here is the result:
The white pixels are pixels not colored
The black pixels are pixels colored once
The red pixels are pixels colored 2 or more times
So its actually even worse than I originally proposed. It's a combination of unlit pixels, and pixels lit multiple times.
You should iterate over real image pixels, not polar grid points.
So correct pixel-walking code might look as
for(int x = 0; x < WIDTH; ++x) {
for(int y = 0; y < HEIGHT; ++y) {
double distance = Math.hypot(x - xCenter, y - yCenter);
if(distance <= radius) {
image.setRGB(x, y, YOUR_CODE_HERE);
}
}
}
Of course this snippet can be optimized choosing good filling polygon instead of rectangle.
This can be solved by anti-aliasing.
Because you push float-coordinate information and compress it , some lossy sampling occur.
double x,y ------(snap)---> lightLevels[int ?][int ?]
To totally solve that problem, you have to draw transparent pixel (i.e. those that less lit) around that line with a correct light intensity. It is quite hard to calculate though. (see https://en.wikipedia.org/wiki/Spatial_anti-aliasing)
Workaround
An easier (but dirty) approach is to draw another transparent thicker line over the line you draw, and tune the intensity as needed.
Or just make your line thicker i.e. using bigger blurry point but less lit to compensate.
It should make the glitch less obvious.
(see algorithm at how do I create a line of arbitrary thickness using Bresenham?)
An even better approach is to change your drawing approach.
Drawing each line manually is very expensive.
You may draw a circle using 2D sprite.
However, it is not applicable if you really want the ray-cast like in this image : http://www.iforce2d.net/image/explosions-raycast1.png
Split graphic - gameplay
For best performance and appearance, you may prefer GPU to render instead, but use more rough algorithm to do ray-cast for the gameplay.
Nonetheless, it is a very complex topic. (e.g. http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/ )
Reference
Here are more information:
http://what-when-how.com/opengl-programming-guide/antialiasing-blending-antialiasing-fog-and-polygon-offset-opengl-programming/ (opengl-antialias with image)
DirectX11 Non-Solid wireframe (a related question about directx11 with image)
i have created a 10x10 grid in the center of the screen in android, now i would like to give each square in the grid a coordinate. for example top left square in the grid would be 0 then 1, 2,3 and so on. But i dont know how to do this. i am trying to do this in a draw class which extends view. my code of what i am trying is below
public int coordinates(int posX, int posY){
int startX = (screenWidth / 2) - (rectSide / 2);
int startY = (screenHeight / 2) - (rectSide / 2);
//for(int i=0; i<=10000; i+=100){
xCoord = (startX + (posX*100));
yCoord = (startY + (posY*100));
}
You know you start at point 0,0 top left. So assuming you have equally spaces squares you can just do the screen height / 10 to get how far apart each square should be in the y direction. And then do the same for the x direction. Say your screen was 1000 pixels tall.
Then your grid at position (0,1) would be at (0,100) pixels. (0,2) would be (0,200) you are just multiplying the y coordinate by the height of each square in the grid.
I'm trying to draw a circle on my canvas. Pseudocode of my algorithm looks like that
double R = 1.0;
// Draw 11 points, lying on the circle with the radius of 1
// and angle from 0 to 90 degrees
for(int i=0; i<=10; i++)
{
drawPoint( R*cos(PI*i/20), R*sin(PI*i/20) );
}
// Draw a circle with center at the (0; 0) and with the radius of 1
drawCircle(0, 0, R);
That's what I've got:
Looks fine, but there is one problem. When I increase radius only points with angles 0, 45 and 90 lie on a circle.
That's how it looks 72 degrees:
There is no any info about accuracy of the method drawCircle on developer.android.com.
I guess that it draws, based on the values at points with angles 0, 45, 90, ..., and calculate line in other positions very approximately.
I know, that I can draw circle as accurate as I want to, if I'll draw it like a polyline with tiny step, but it will work very slow.
So I want to find out - is there any methods to draw circle accurate in Android?
UPD 1:
How do I draw a points:
int x, y;
x = getPixelX(point.getX());
y = getPixelY(point.getY());
canvas.drawCircle(x, y, point.radius, paint);
Where getPixelX and getPixelY takes a coorditate of the point on plane and returns the same coordinate on the screen, basing on scale and offset.
I thought that I could make some mistake in those methods, but they work perfectly with the lines. I can zoom in lines and there is no error, all the points lies just on the line.
UPD 2:
There are comments, that probably I make a mistake in my calculations. I do not argue, perhaps you're right. So here is my "calculations".
How do I zoom in:
public void mouseWheelMoved(MouseWheelEvent e) {
// zoomQ is 0.9 or 1.1
double zoomQ = (e.getWheelRotation() + 10) / 10.0;
scaleX *= zoomQ;
scaleY *= zoomQ;
}
How do I move the plane:
public void mouseDragged(MouseEvent e) {
centerX -= (e.getX() - lastMouseX)/scaleX;
centerY -= (e.getY() - lastMouseY)/scaleY;
lastMouseX = e.getX();
lastMouseY = e.getY();
}
How do getPixelX/Y works:
public int getPixelX(double planeX) {
return (int)Math.round( scaleX*(planeX - centerX) + ScreenWidth/2 );
}
public int getPixelY(double planeY) {
return (int)Math.round( scaleY*(planeY - centerY) + ScreenHeight/2 );
}
The applet used is like the first quadrant of a Cartisian Plane with the domain and range (0, 200). My assignment is to draw a house and a sun in this applet.
I am trying to draw the circle for the sun. I really have no idea where to start. We are learning about for loops and nested loops so it probably pertains to that. We haven't got to arrays and general functions like draw.circle do not exist for this applet. If it helps, here is how I drew my roof for the house (two right triangles): Notice it is drawn pixel by pixel. I suspect my teacher wants the same kind of thing for the circle.
//roof
//left side
double starty = 100;
for(double x = 16; x <= 63; x++){
for(int y = 100; y <= starty; y++){
img.set(x, y, JRaster.purple);
}
starty += 1;
}
//right side
double startx = 110;
for(int y = 100; y <= 147; y++){
for(double x = 63; x <= startx; x++){
img.set(x , y, JRaster.purple);
}
startx -= 1;
}
Here's how I would draw the north-east quarter of a circle, pixel by pixel. You can just repeat this with slight variations for the other three quarters. No trigonometry required!
Start by drawing the eastern most point of the circle. Then you'll draw more pixels, moving northwards and westwards, until you get to the northern most point of the circle.
Calculate the distance of the point you've just drawn from the centre. If it's more than the radius, then your next pixel will be one to the left, otherwise, your next pixel will be the one above.
Repeat the previous step till you get to the northern most point.
Post a comment if you get stuck, with converting this to Java, or with adjusting it for the other three quarters of the circle.
I won't give you code, but you should remember how a circle is made. Going from theta=0 to theta=2*pi, the circle is traced by x=cos x, y=sin x.
So, using a for loop that increments a double(here called theta) by something like 0.01 until 2*pi(2*Math.PI or roughly 6.28) plot off Math.cos(theta), Math.sin(theta).