I give it a try to the Bresenham algorithm
Some result here (i know there is no reason to be so enthousiast :P)
g2d.setColor( Color.YELLOW );
g2d.fillOval( 400 , 50 , 200 , 100 );
g2d.setColor( Color.YELLOW );
g2d.transform( AffineTransform.identity().rotate( 0.2f , 400 + 100 , 50 + 50 ) );
g2d.fillOval( 400 , 50 , 200 , 100 );
g2d.setTransform( null );
g2d.setColor( Color.YELLOW );
g2d.transform( AffineTransform.identity().rotate( 0.2f , 400 + 100 , 50 + 50 ) );
g2d.drawOval( 400 , 50 , 200 , 100 );
g2d.setTransform( null );
g2d.setPaint( texture ); // set a Texture instead of a color to fill it
g2d.transform( AffineTransform.identity().rotate( 0.2f , 400 + 100 , 50 + 50 ) );
g2d.fillOval( 400 , 50 , 200 , 100 );
g2d.setTransform( null );
Here the implementation using Breshenham.
I just have a difficulty to pre check buffers size of my batcher. So ATM i must check a BufferOverFlowException that is not quite fun…
I don’t do any benchmarks for now but i find this solution much functionnal than the fragment shader (but less fun ^^)
/**
* https://sites.google.com/site/ruslancray/lab/projects/bresenhamscircleellipsedrawingalgorithm/bresenham-s-circle-ellipse-drawing-algorithm
*/
private void paintOvalBresenham(float x0, float y0, float rx, float ry , boolean fill , Graphics2DAttributes g2dAttributes , boolean overflowed ) {
/**
* Basics primitives perform pre check on all buffers of this batcher before pushing datas like:
* // 1 primitive 4 vertices and 6 indices.
* checkBufferSize( 1 , 4 , 6 );
*
* This implementation is quite more complicated and i can't determine how much vertices and indices i will use.
* So, i don't do any pre check but if i have a buffer overflow, i discard this primitive, flush() and restart this paint process
*
* I can assume at least to have 1 primitive enought
*/
checkBufferSize( 1 , 0 , 0 );
/**
* Get the primitive
*/
Primitive p = getPrimitive( ( fill ? GL11.GL_TRIANGLE_FAN : GL11.GL_LINE_LOOP ) , g2dAttributes );
float a2 = rx * rx;
float b2 = ry * ry;
float fa2 = 4 * a2, fb2 = 4 * b2;
/** Bresenham algorithm compute 2 octants and determine 6 others by symetric computations
* - Theses 2 first octants are done without the use of cos/sin operations but use (* +)
* - Theses 6 others octants are done only with translation ( + or - operations)
*
* The difficulty is able to order vertices in the "indices buffer" sinces points are not computed sequentially...
*
* Which is the order of points computation:
* - Each octant is numbered from 1 to 8. We compute octants 1,2,3,4 (multiplexed) and after we compute octants 5,6,7,8 (multiplexed)
* - Some octants as computed in a clockwise and some others in counterclockwise
* - The drawing process draw octants in this order 1 , 5 , 7 , 3 , 4 , 8 , 6 , 2 in the counterclockwise.
* - Some octant are clockwise and some others in counterclockwise by nature of symetric computation used.
* - Octant 1 : counterclockwise
* - Octant 5 : clockwise
* - Octant 7 : counterclockwise
* - Octant 3 : clockwise
* - Octant 4 : counterclockwise
* - Octant 8 : clockwise
* - Octant 6 : counterclockwise
* - Octant 2 : clockwise
*
*
* - Octants topology:
*
* ***************
* ***** \ 4 | 3 / *****
* ** 8 \ | / 7 **
* ** \|/ **
* *---------------X---------------*
* ** 6 / | \ 5 **
* ** / | \ **
* ***** / 2 | 1 \ *****
* ***************
*/
int sizeOctants1234 = 0;
int sizeOctants5678 = 0;
try {
/* first half */
float x, y, sigma;
for (x = 0, y = ry, sigma = 2*b2+a2*(1-2*ry); b2*x <= a2*y; x++)
{
p.pushVertex( x0 + x , y0 + y ); // Octant 1
p.pushVertex( x0 - x , y0 + y ); // Octant 2
p.pushVertex( x0 + x , y0 - y ); // Octant 3
p.pushVertex( x0 - x , y0 - y ); // Octant 4
sizeOctants1234++;
if (sigma >= 0)
{
sigma += fa2 * (1 - y);
y--;
}
sigma += b2 * ((4 * x) + 6);
}
/* second half */
for (x = rx, y = 0, sigma = 2*a2+b2*(1-2*rx); a2*y <= b2*x; y++)
{
p.pushVertex( x0 + x , y0 + y ); // Octant 5
p.pushVertex( x0 - x , y0 + y ); // Octant 6
p.pushVertex( x0 + x , y0 - y ); // Octant 7
p.pushVertex( x0 - x , y0 - y ); // OCtant 8
sizeOctants5678++;
if (sigma >= 0)
{
sigma += fb2 * (1 - x);
x--;
}
sigma += a2 * ((4 * y) + 6);
}
}
catch( BufferOverflowException e ) {
/** Ouch, we have not enough place for put all vertices, we flush this batcher without the primitive
*/
if( overflowed ) throw new IllegalStateException("OpenGLSupport with too small buffer size for this method");
primitiveCount--; // discard this primitive
flush();
paintOvalBresenham(x0, y0, rx, ry, fill, g2dAttributes , true);
return;
}
/** Now we put all vertices, we know how many indices we must have
* We can do a pre check in order to avoid the BufferOverflowException...
*/
if( ! checkBufferSize( 0 , 0 , ( 4 * sizeOctants1234 ) + ( 4 * sizeOctants5678 ) , false ) ) {
if( overflowed ) throw new IllegalStateException("OpenGLSupport with too small buffer size for this method");
primitiveCount--; // discard this primitive
flush();
paintOvalBresenham(x0, y0, rx, ry, fill, g2dAttributes , true);
return;
}
/** Push indices
*/
IntBuffer buffer = indicesBuffer.getBuffer();
final int strideOctants1234 = 4 * sizeOctants1234;
final int strideOctants5678 = 4 * sizeOctants5678;
/**
* Computing where start each octants
*/
final int offsetOctant1 = 0; // forward
final int offsetOctant4 = 3; // forward
final int offsetOctant6 = strideOctants1234 + 1; // forward
final int offsetOctant7 = strideOctants1234 + 2; // forward
final int offsetOctant5 = strideOctants1234 + strideOctants5678 - 4; // backward
final int offsetOctant3 = strideOctants1234 - 2; // backward
final int offsetOctant8 = strideOctants1234 + strideOctants5678 - 1; // backward
final int offsetOctant2 = strideOctants1234 - 3; // backward
// octant 1
int offset = offsetOctant1;
int stride = 4;
for(int i = 0 ; i < strideOctants1234 ; i += stride ) {
buffer.put( offset + i );
}
// octant 5
offset = offsetOctant5;
for(int i = 0 ; i < strideOctants5678 ; i += stride ) {
buffer.put( offset - i );
}
// octant 7
offset = offsetOctant7;
for(int i = 0 ; i < strideOctants5678 ; i += stride ) {
buffer.put( offset + i );
}
// octant 3
offset = offsetOctant3;
for(int i = 0 ; i < strideOctants1234 ; i += stride ) {
buffer.put( offset - i );
}
// octant 4
offset = offsetOctant4;
for(int i = 0 ; i < strideOctants1234 ; i += stride ) {
buffer.put( offset + i );
}
// octant 8
offset = offsetOctant8;
for(int i = 0 ; i < strideOctants5678 ; i += stride ) {
buffer.put( offset - i );
}
// octant 6
offset = offsetOctant6;
for(int i = 0 ; i < strideOctants5678 ; i += stride ) {
buffer.put( offset + i );
}
// octant 2
offset = offsetOctant2;
for(int i = 0 ; i < strideOctants1234 ; i += stride ) {
buffer.put( offset - i );
}
p.end();
}
Sébastien.