How Come My Drawing Code Keeps Resulting in Fuzzy Shapes

How come my drawing code keeps resulting in fuzzy shapes?

UIGraphicsBeginImageContext:

... is equivalent to calling the
UIGraphicsBeginImageContextWithOptions function with the opaque
parameter set to NO and a scale factor of 1.0.

However, UIGraphicsBeginImageContextWithOptions defines its scale parameter as:

scale

The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.

So your use of UIGraphicsBeginImageContextWithOptions is creating a context with a scale of 1.0. That's correct for e.g. last year's iPad Mini but all retina-class devices have a 'native' scale of 2.0 — two pixels per logical point. UIGraphicsBeginImageContextWithOptions introduced the special case of 0.0 so that you don't have to mess about dealing with getting the correct number for that device for yourself (though [[UIScreen mainScreen] scale] would be an easy way).

So switch your exiting call to UIGraphicsBeginImageContext(CGSizeMake(1000, 1000)) to:

UIGraphicsBeginImageContextWithOptions(CGSizeMake(1000, 1000), NO, 0.0)

Otherwise the context you create will have a quarter as many pixels as the screen does in the corresponding amount of space.

Blurred edges keep regaining 100% opacity (Processing)

Create a function which draw a single dot to a PGraphics object:

void DrawPen(PGraphics pg, int cptX, int cptY, int r) {
pg.beginDraw();
for (int x = 0; x < r; ++x) {
for (int y = 0; y < r; ++y) {
float distance = sqrt(x*x + y*y);
float alpha = 255-map(distance,0,r,0,255);
if (distance < r) {
pg.set(cptX+x,cptY+y,color(255,255,255, alpha));
pg.set(cptX-x,cptY+y,color(255,255,255, alpha));
pg.set(cptX+x,cptY-y,color(255,255,255, alpha));
pg.set(cptX-x,cptY-y,color(255,255,255, alpha));
}
}
}
pg.endDraw();
}

Draw a dot to a separate PGraphics object in setup

PGraphics pg;
PGraphics pg_pen;
int rad = 20;

void setup (){
size(800, 800, P2D);

pg = createGraphics(800, 800, JAVA2D);
pg.beginDraw();
// [...]
pg.endDraw();

pg_pen = createGraphics(2*rad, 2*rad, JAVA2D);
DrawPen(pg_pen, rad, rad, rad);
}

When the mouse is dragged then blend the pg_pen to the common PGraphics object (pg) at the current mouse position:

void mouseDragged(){
pg.beginDraw();
pg.image(pg_pen, mouseX-rad, mouseY-rad);
pg.endDraw();
}

For the seek of completeness the draw function:

void draw () {
background(0);
image(pg,0,0);
}

Sample Image


[...] and tried to get the color from the white part to draw on the black part.

Add a color parameter to the DrawPen function and clear the pen PGraphics before drawing to it:

void DrawPen(PGraphics pg, int cptX, int cptY, int r, color c) {
pg.beginDraw();
pg.clear();
for (int x = 0; x < r; ++x) {
for (int y = 0; y < r; ++y) {
float distance = sqrt(x*x + y*y);
float alpha = 255-map(distance,0,r,0,255);
if (distance < r) {
color pc = color(red(c),green(c),blue(c), alpha);
pg.set(cptX+x,cptY+y,pc);
pg.set(cptX-x,cptY+y,pc);
pg.set(cptX+x,cptY-y,pc);
pg.set(cptX-x,cptY-y,pc);
}
}
}
pg.endDraw();
}

Get the color in the mouse pressed event call back and change the color of the pen:

void mousePressed() {
color c = pg.get(mouseX, mouseY);
println(c);

DrawPen(pg_pen, rad, rad, rad, c);
}

Sample Image

Note, the color is get from the pg object and not from the screen. If you want to get the color from the screen then it has to be (without .pg):

color c = get(mouseX, mouseY);

Further the color is changed any time when any mouse is pressed (pressed not dragged). Possibly you want to change the color when the right mouse button is pressed and paint when the left mouse button is pressed:

void mousePressed() {
if (mouseButton == RIGHT) {
color c = pg.get(mouseX, mouseY);
println(c);
DrawPen(pg_pen, rad, rad, rad, c);
}
}

Canvas drawings, like lines, are blurry

When drawing lines in canvas, you actually need to straddle the pixels. It was a bizarre choice in the API in my opinion, but easy to work with:

Instead of this:

context.moveTo(10, 0);
context.lineTo(10, 30);

Do this:

context.moveTo(10.5, 0);
context.lineTo(10.5, 30);

Dive into HTML5's canvas chapter talks about this nicely

Why does drawing a shape to a bitmap reduce quality?

Bitmap is a rasterized computer image, consisting of points (pixels).

The Canvas class holds the "draw" calls.

A shape is an abstract definition of shape (vector-like).

drawing the shape straight to the canvas

That's not accurate, maybe you mean straight to the screen of device, which is Bitmap too.

To use Canvas for drawing, you need the canvas to host the calls, some bitmap as target, and some drawing primitive (shape is one), and Paint (contains information how to paint the drawing primitive).

Once you draw some shape into the target bitmap, it will be aliased to the pixels of target bitmap. Ie. an circle will turn into some approximation created by rectangular pixels of target bitmap.

What you probably see in your particular case is, that your Bitmap has lower resolution than screen, and when you draw that low-res bitmap to the target screen bitmap, it is upscaled by some filter which makes the upscaled picture a bit blurry to avoid big rectangular pixelation (or it may be also other way, downscaling hi-res bitmap, which contains too sharp/thin contours, which will be aliased second time when downscaled, and even with anti-aliasing filtering it will get blurred a bit). Or maybe you use some paint with settings causing blur (unlikely, can't think of creating one by accident, you would knew).

If you will use for both targets Bitmap of identical density (resolution of single pixel), and the same paint method, then the result will be same, and also drawing the shape from bitmap to bitmap as long as you will use whole pixel coordinates/size, and no filtering, again the result will be same as drawing directly to screen bitmap.

So start first by checking the size of your bitmap vs screen bitmap, and then check paint settings and additional canvas arguments to draw the bitmap, whether you don't upscale/downscale it with some kind of filtering.

Filling UIBezierPath results in pixelated image from image context

Step 1. Read the UIGraphicsBeginImageContext documentation:

This function is equivalent to calling the UIGraphicsBeginImageContextWithOptions function with the opaque parameter set to false and a scale factor of 1.0.

Step 2. Follow the link to the UIGraphicsBeginImageContextWithOptions documentation, which says:

scale The scale factor to apply to the bitmap. If you specify a value of 0.0, the scale factor is set to the scale factor of the device’s main screen.

Step 3. Try using UIGraphicsBeginImageContextWithOptions with a scale of 0.0:

UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0)

Step 4. Profit…?

Trying to SKETCH a rectangle on canvas, preferably in Javascript?

First of all I think it's more clear if you store the coordinates in separated variables instead of an array (startX, startY, lastX, lastY).

Also you should subtract the canvas position from the mouse position to get the coordinate inside the canvas, right now it kind of work because the canvas is at the top of the page.

I see you want to keep the old content, I used a canvas to store the previous drawn content, then before you draw the current rectangle you must redraw the old content (using ctx.drawImage).

You should add 0.5 to the coordinates so the lines do not get blurry, this only happens if you draw a line with an odd width (1, 3, 5). this is because if you try to draw 1px line on say coordinate x=1 you must draw half a pixel to both sides (0.5 to 1.5), so it looks blurry.

but if you draw it at say x=0.5 you draw the line from 0 to 1 which is exactly one pixel.

var isDown = false;var startX, startY;var lastX, lastY;
var canvas = document.getElementById('myCanvas');var ctx = canvas.getContext('2d');var canvasRect = canvas.getBoundingClientRect();
var backBuffer = canvas.cloneNode(true);var backBufferCtx = backBuffer.getContext('2d');
canvas.addEventListener('mousedown', function down() { startX = event.clientX - canvasRect.left; startY = event.clientY - canvasRect.top; isDown = true;});
canvas.addEventListener('mouseup', function up() { isDown = false;
// Draw Current Canvas Content updateCanvas();
// Save current content backBufferCtx.clearRect(0, 0, backBuffer.width, backBuffer.height); backBufferCtx.drawImage(canvas, 0, 0);});
canvas.addEventListener('mousemove', function move() { if (! isDown) return;
lastX = event.clientX - canvasRect.left; lastY = event.clientY - canvasRect.top; updateCanvas();});
function updateCanvas() { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw Current Canvas Content ctx.drawImage(backBuffer, 0, 0);
// Draw New Rectangle ctx.beginPath(); // add 0.5 so the line do not get blurry ctx.rect(startX + 0.5, startY + 0.5, lastX - startX, lastY - startY); ctx.stroke();}
<canvas id="myCanvas" width="200" height="100" style="border:1px solid #000000; margin-top: 100px"></canvas>


Related Topics



Leave a reply



Submit