The technique shown in a similar question is a rectangular bubble. How to draw one in an oval shape? i.e.:
I would do it in two iterations.
First get the context and begin a path. Fill an ellipse and then a custom path that encloses a triangle with three lines. I assumed the following dimensions: 70 width, 62 height. Override draw rect in a subclass of UIView and instantiate in a subclassed UIViewController:
-(void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(ctx, 0.0, 0.0, 1.0, 1.0);
CGContextFillEllipseInRect(ctx, CGRectMake(0.0, 0.0, 70.0, 50.0)); //oval shape
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 8.0, 40.0);
CGContextAddLineToPoint(ctx, 6.0, 50.0);
CGContextAddLineToPoint(ctx, 18.0, 45.0);
CGContextClosePath(ctx);
CGContextFillPath(ctx);
}
Produces this in the iPhone simulator when added against a gray backdrop:
This second code example will almost duplicate what you produced above. I implemented this using flexible sizes that could be supplied to the UIView frame when you instantiate it. Essentially, the white portion of the speech bubble is drawn with a black stroke over lay to follow.
-(void)drawRect:(CGRect)rect {
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect aRect = CGRectMake(2.0, 2.0, (self.bounds.size.width * 0.95f), (self.bounds.size.width * 0.60f)); // set the rect with inset.
CGContextSetRGBFillColor(ctx, 1.0, 1.0, 1.0, 1.0); //white fill
CGContextSetRGBStrokeColor(ctx, 0.0, 0.0, 0.0, 1.0); //black stroke
CGContextSetLineWidth(ctx, 2.0);
CGContextFillEllipseInRect(ctx, aRect);
CGContextStrokeEllipseInRect(ctx, aRect);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, (self.bounds.size.width * 0.10), (self.bounds.size.width * 0.48f));
CGContextAddLineToPoint(ctx, 3.0, (self.bounds.size.height *0.80f));
CGContextAddLineToPoint(ctx, 20.0, (self.bounds.size.height *0.70f));
CGContextClosePath(ctx);
CGContextFillPath(ctx);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, (self.bounds.size.width * 0.10), (self.bounds.size.width * 0.48f));
CGContextAddLineToPoint(ctx, 3.0, (self.bounds.size.height *0.80f));
CGContextStrokePath(ctx);
CGContextBeginPath(ctx);
CGContextMoveToPoint(ctx, 3.0, (self.bounds.size.height *0.80f));
CGContextAddLineToPoint(ctx, 20.0, (self.bounds.size.height *0.70f));
CGContextStrokePath(ctx);
}
I have another way - however I dont have any time to properly explain it.
However, my example was written in .NET for use in a Windows application.
My version creates the entire speach bubble as 2D Polygon Mesh and is partially customizable. It is a single drawn path instead of multiple parts.
While our platforms are not the same - the technique uses common math routines and procedural loop. I believe the technique could be translated to other programming languages or platforms.
Private Sub Generate(ByVal Resolution As Integer, Optional ByVal SpeachPointerAngle As Integer = (45 * 3), Optional ByVal PointerBend As Decimal = 15)
'Generated the same way we create vector (wireframe) mesh for an Ellipse but...
'... at a predefined defined angle we create
'the size of the Pointer TIP/Corner portion of the speach bubble
'in relation to the EDGE of the ELLIPSE (average)
Dim SpeachPointerSize As Integer = 30
If PointerBend > 10 Then PointerBend = 10
If PointerBend < -10 Then PointerBend = -10
'as a variable offset that should be limited to max +/- -15 to 15 degrees relative to current angle as a safe range
'- were speach pointer angle determins which side the the pointer appears
Dim PointerOffsetValue As Decimal = PointerBend
Dim ActualPointerAngle As Decimal
'SpeachPointerAngle = 360 - SpeachPointerAngle ' adjust orientation so that 0 degrees is SOUTH
'Ellipse Size:
Dim Size_X As Decimal = 80
Dim Size_Y As Decimal = 50
If Resolution < 30 Then Resolution = 30
Dim Result As Vector2()
'size of each angle step based on resolution (number of vectors ) - Mesh Quality in otherwords.
Dim _Step As Decimal = 360 / Resolution
'Our current angle as we step through the loop
Dim _CurrentAngle As Decimal = 0
'rounded values
Dim _LastAngle As Decimal = 0
Dim _NextAngle As Decimal = _Step
Dim SpeachDrawn As Boolean = False ' prevent creating more than 1 point to be safe
Dim I2 As Integer = 0 'need a stepper because of skipped IDS
'build the ellipse mesh
For i = 0 To Resolution - 1
_LastAngle = _CurrentAngle - 15
_NextAngle = _CurrentAngle + 15
ActualPointerAngle = _CurrentAngle 'side
ActualPointerAngle += PointerOffsetValue ' acual angle of point
Dim EX As Decimal = System.Math.Cos(Math.Deg2Rad(_CurrentAngle)) * Size_X
Dim EY As Decimal = System.Math.Sin(Math.Deg2Rad(_CurrentAngle)) * Size_Y
'Point extrusion size ( trying to be even size all around )
Dim ExtrudeX As Decimal = System.Math.Cos(Math.Deg2Rad(_CurrentAngle)) * (Size_X + SpeachPointerSize)
Dim ExtrudeY As Decimal = System.Math.Sin(Math.Deg2Rad(_CurrentAngle)) * (Size_Y + SpeachPointerSize)
'is Pointer angle between Last and Next?
If SpeachPointerAngle > _LastAngle And SpeachPointerAngle < _NextAngle Then
If (SpeachDrawn = False) Then
' the point for the speachbubble tip
Array.Resize(Result, I2 + 1)
Result(I2) = New Vector2(ExtrudeX, ExtrudeY)
SpeachDrawn = True
I2 += 1
Else
'skip
End If
Else
'normal ellipse vector
Array.Resize(Result, I2 + 1)
Result(I2) = New Vector2(EX, EY)
I2 += 1
End If
_CurrentAngle += _Step
Next
_Vectors = Result
End Sub
The above code generated this - drawn to a bitmap using GDI+ [DrawPolygon/FillPolygon]:
https://fbcdn-sphotos-a.akamaihd.net/hphotos-ak-ash4/380262_10151202393414692_590995900_n.jpg
(Sorry - I can't post the image here directly as I have never posted here before. I don't have the reputation yet )
This is a Primitive in a Graphics Assembly I am developing for .NET which uses my own Vector2.
This speach bubble supports transparency when drawn - as it is a single polygon shape instead of multiple shapes.
Basically we draw an ellipse programatically and then extrude a speach point out on a desired side of the ellipse.
A similar approach could be applied using PointF structures instead.
All shapes in the code are generated around Origin 0,0.
Arrays are also resized incrementally as vectors are added prevent gaps in the array.
EG - the center of the speach bubble is Origin 0.0.
I apologize for not explaining my code properly - I just don't have the time.
But it probably isnt too hard to understand.
Related
I am writing an extension / widgets for gnome-shell.So I want to show some information using text. Actually I want to put text in middle of the circle using pangoCairo but I can't figure out how can I put text using pangoCairo?
here is my code of drawing circles with a function to set text middle of the circles but the text is not showing in the middle.
draw_stuff(canvas, cr, width, height) {
cr.setSourceRGBA(1,1,1,0.2);
cr.save ();
cr.setOperator (Cairo.Operator.CLEAR);
cr.paint ();
cr.restore ();
cr.setOperator (Cairo.Operator.OVER);
cr.scale (width, height);
cr.setLineCap (Cairo.LineCap.BUTT);
//test
cr.setLineWidth (0.08);
cr.translate (0.5, 0.5);
cr.arc (0, 0, 0.4, 0, Math.PI * 2);
cr.stroke ();
cr.setSourceRGBA(1,1,1,0.8);
cr.rotate(-Math.PI/2);
cr.save();
cr.arc (0,0, 0.4, 0, this.currentR /100 * 2 * Math.PI);
cr.stroke();
cr.setSourceRGBA(1,1,1,0.9);
cr.save();
cr.arc (0,0, 0.25, 0, 2 * Math.PI);
cr.fill();
cr.moveTo(0, 0);
cr.setSourceRGBA(0,0,0,0.9);
cr.save();
this.text_show(cr, "WoW");
cr.restore();
return true; }
update() { this.currentR = 60;
this._canvas.connect ("draw", this.draw_stuff.bind(this));
this._canvas.invalidate();}
text_show(cr, showtext, font = "DejaVuSerif Bold 16") {
let pl = PangoCairo.create_layout(cr);
pl.set_text(showtext, -1);
pl.set_font_description(Pango.FontDescription.from_string(font));
PangoCairo.update_layout(cr, pl);
let [w, h] = pl.get_pixel_size();
cr.relMoveTo(-w / 2, 0);
PangoCairo.show_layout(cr, pl);
cr.relMoveTo(w / 2, 0);}
I dont know what's wrong here?
let [w, h] = pl.get_pixel_size();
cr.relMoveTo(-w / 2, 0);
You are placing your text something like 1000 pixels off the screen. get_pixel_size() is documented as:
pango_layout_get_size() returns the width and height scaled by PANGO_SCALE.
and PANGO_SCALE has the value 1024:
Thus, you need to change the code above to
let [w, h] = pl.get_pixel_size();
cr.relMoveTo(-w / 2048, 0);
Bonus points for not hardcoding the value of PANGO_SCALE and instead getting it from your Pango API bindings. However, for that I don't know enough about gnome-shell-extensions. Is this code in Javascript? Dunno.
I am trying to draw ellipse on a rotated PDF but the border of the ellipse appears thick and thin in center
Sample Code:
PdfAnnotation annotation = PdfAnnotation.createSquareCircle(stamper.getWriter(), rect, null, false);
annotation.setFlags(PdfAnnotation.FLAGS_PRINT);
annotation.setColor(getColor(annot.getBorderColor()));
// annotation.setBorder(new PdfBorderArray(2, 2, 2));
// annotation.setColor(getColor(annot.getBorderColor()));
annotation.setBorderStyle(new PdfBorderDictionary(3.5f, PdfBorderDictionary.STYLE_SOLID));
PdfContentByte cb = stamper.getOverContent(page);
if ((int) (orientation % 360) == 90 || (int) (orientation % 360) == 270)
{
w = rect.getHeight();
h = rect.getWidth();
}
else
{
w = rect.getWidth();
h = rect.getHeight();
}
PdfAppearance app = cb.createAppearance(w + 3.5f, h + 3.5f);
app.setColorStroke(getColor(annot.getBorderColor()));
app.setLineWidth(3.5);
app.ellipse(rect.getLeft() + 1.5, rect.getBottom() + 1.5, rect.getRight() - 1.5, rect.getTop() - 1.5);
app.stroke();
annotation.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, app);
stamper.addAnnotation(annotation, page);
As already assumed in a comment a feature of iText (for rotated pages it attempts to pretend to the user that he has an upright coordinate system, not a rotated one) gets into your way. This feature unfortunately does only affect the dimensions of the annotation itself, not of the appearances you create for it.
If a page has a 90° or 270° rotation, therefore, you have to use appearance dimensions whose width and height are switched compared to the annotation dimensions you used.
Furthermore, one also has to consider the switched dimensions when drawing the ellipse on the appearance template. The code in the question and in the example code shared via google drive forgot this.
Finally, one shall not override the switched dimensions of the appearance template using PdfAppearance.setBoundingBox. The code in the example code shared via google drive did this.
False Example
If I create an ellipse annotation with appearance on a rotated page like this:
Rectangle rect = new Rectangle(202 + 6f, 300, 200 + 100, 300 + 150);
PdfAnnotation annotation = PdfAnnotation.createSquareCircle(stamper.getWriter(), rect, null, false);
annotation.setFlags(PdfAnnotation.FLAGS_PRINT);
annotation.setColor(BaseColor.RED);
annotation.setBorderStyle(new PdfBorderDictionary(3.5f, PdfBorderDictionary.STYLE_SOLID));
PdfContentByte cb = stamper.getOverContent(1);
PdfAppearance app = cb.createAppearance(rect.getWidth(), rect.getHeight());
app.setColorStroke(BaseColor.RED);
app.setLineWidth(3.5);
app.ellipse( 1.5, 1.5, rect.getWidth() - 1.5, rect.getHeight() - 1.5);
app.stroke();
annotation.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, app);
stamper.addAnnotation(annotation, 1);
(CreateEllipse test testCreateEllipseAppearanceOnRotated)
I indeed get a funny ellipse:
Correct Example
If I create an ellipse annotation with appearance on a rotated page with switched dimensions like this, though:
Rectangle rect = new Rectangle(202 + 6f, 300, 200 + 100, 300 + 150);
PdfAnnotation annotation = PdfAnnotation.createSquareCircle(stamper.getWriter(), rect, null, false);
annotation.setFlags(PdfAnnotation.FLAGS_PRINT);
annotation.setColor(BaseColor.RED);
annotation.setBorderStyle(new PdfBorderDictionary(3.5f, PdfBorderDictionary.STYLE_SOLID));
PdfContentByte cb = stamper.getOverContent(1);
// switched appearance dimensions
PdfAppearance app = cb.createAppearance(rect.getHeight(), rect.getWidth());
app.setColorStroke(BaseColor.RED);
app.setLineWidth(3.5);
// draw ellipse using switched appearance dimensions
app.ellipse( 1.5, 1.5, rect.getHeight() - 1.5, rect.getWidth() - 1.5);
app.stroke();
annotation.setAppearance(PdfAnnotation.APPEARANCE_NORMAL, app);
stamper.addAnnotation(annotation, 1);
(CreateEllipse test testCreateCorrectEllipseAppearanceOnRotated)
I get the expected ellipse:
Is it possible to have a roundRectangle with no border? The following code creates roundRectangles and rectangles with border sizes of 0f and 1f. For the roundRectangle there is still a visible border when the lineWidth is set to 0f, but this is not true for a rectangle with a border of 0f.
Here is the code I'm using:
magazine = new Document(PageSize.LETTER,0,0,0,0);
pdfw = PdfWriter.getInstance(magazine, new FileOutputStream("out.pdf"));
magazine.open();
canvas = pdfw.getDirectContent();
canvas.rectangle(0,0,600,750);
canvas.setColorFill(BaseColor.ORANGE);
canvas.fillStroke();
canvas.setColorStroke(BaseColor.BLACK);
canvas.setColorFill(BaseColor.GRAY);
canvas.setLineWidth(1f);
llx = 100;
lly = 100;
wid = 100;
hei = 100;
canvas.roundRectangle(llx,lly, wid, hei, 10);
canvas.fillStroke();
llx = 100;
lly = 210;
wid = 100;
hei = 100;
canvas.rectangle(llx,lly, wid, hei);
canvas.fillStroke();
canvas.setColorStroke(BaseColor.BLACK);
canvas.setColorFill(BaseColor.WHITE);
canvas.setLineWidth(0f);
llx = 210;
lly = 100;
wid = 100;
hei = 100;
canvas.roundRectangle(llx,lly, wid, hei, 10);
canvas.fillStroke();
llx = 210;
lly = 210;
wid = 100;
hei = 100;
canvas.rectangle(llx,lly, wid, hei );
canvas.fillStroke();
When you draw lines and shapes in PDF, you use path construction operators. The following method introduces an re (rectangle) operator to construct a rectangle.
canvas.rectangle(0,0,600,750);
iText also provides convenience methods. For instance: the following method introduces a sequence of m (move to), l (line to), c (curve to),... operators:
canvas.roundRectangle(llx,lly, wid, hei, 10);
As soon as you have constructed the path, you can use a path painting operator to actually draw something. iText has different fill(), stroke() and fillStroke() variations.
You are using this method:
canvas.fillStroke();
This means that you fill the path with the fill color and stroke the path with the stroke color. In your question, you indicate that you only want to fill the path (you want to color what's inside the rounded rectangle); you do not want to stroke it (you don't want to draw the border of the rounded rectangle).
This is easy to achieve. Just replace fillStroke() by fill():
canvas.fill();
Now you will only fill the rounded rectangle and not stroke its border.
Comment by mkl:
A line width of 0 shall denote the thinnest line that can be rendered at device resolution: 1 device pixel wide.
This is correct. It is a common misconception that changing the width of a line to 0 means that the line isn't drawn when invoking stroke(). If you don't want to see a line, the solution is simple: don't stroke it.
I am trying to draw a linear CGGradient on an angle. Because "CGContextDrawLinearGradientWithAngle()" does not exist, I am trying to use CGContextDrawLinearGradient(CGContextRef, CGGradientRef, CGPoint startPoint, CGPoint endPoint, CGGradientDrawingOptions).
With that in mind, I need to convert an angle (degrees) into a starting point and an ending point. I would like to mimic NSGradient's drawInBezierPath:angle. (As a part of AppKit, NSGradient is sadly not available to iOS developers.) Fortunately, the documentation tells us how to get the starting gradient:
- (CGPoint)startingPointForAngle:(CGFloat)angle rect:(CGRect)rect {
CGPoint point = CGPointZero;
if (angle < 90.0f)
point = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
else if (angle < 180.0f)
point = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));
else if (angle < 270.0f)
point = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
else
point = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
return point;
}
Unfortunately, the documentation does not tell us how to get the ending point. (Using either the height or the width of the rect as the distance is only sufficient for certain angles.) Several sites out there tells us how we can find the ending point. Unfortunately, the distance needs to be known before I can compute the ending point. Yet the ending point needs to be computed to get the distance. There is clearly more to it, as NSGradient seems to have it figured out.
- (CGPoint)endingPointForAngle:(CGFloat)angle rect:(CGRect)rect startingPoint:(CGPoint)startingPoint {
//http://www.zahniser.net/~russell/computer/index.php?title=Angle%20and%20Coordinates
//(x + distance * cos(a), y + distance * sin(a))
CGFloat angleInRadians = (CGFloat)M_PI/180.0f * angle;
CGFloat distance = ????????;
CGPoint point = CGPointMake(startingPoint.x + distance * cosf(angleInRadians), startingPoint.y + distance * sinf(angleInRadians));
return point;
}
CGPoint startingGradientPoint = [self startingPointForAngle:self.fillGradientAngle rect:rect];
CGPoint endingGradientPoint = [self endingPointForAngle:self.fillGradientAngle rect:rect startingPoint:startingGradientPoint];
CGContextDrawLinearGradient(graphicsContext, self.fillGradient, startingGradientPoint, endingGradientPoint, 0);
Any ideas.
I'm dealing with the same problem, with a little different way, I use the center point and and angle, and extend the side left and right to find it's points on the edge, my problem was there will be white space if the angel is not pointing any axis, and the drawing option the functions provide a "kCGGradientDrawsBeforeStartLocation or kCGGradientDrawsAfterEndLocation" so it looks like one side will be empty.
But it turns out I can combine the two options with '|', so problem solved, here's my code:
CGGradientRef grRef = CGGradientCreateWithColors(colorSpace, (__bridge CFArrayRef)gradient.colorArray, NULL);
CGFloat degree = angle * M_PI / 180;
CGPoint center = CGPointMake(width/2, height/2);
CGPoint startPoint = CGPointMake(center.x - cos (degree) * width/2, center.y - sin(degree) * height/2);
CGPoint endPoint = CGPointMake(center.x + cos (degree) * width/2, center.y + sin(degree) * height/2);
NSLog(#"start point:%# \n, end point: %#",NSStringFromCGPoint(startPoint),NSStringFromCGPoint(endPoint));
CGContextDrawLinearGradient(gradientContext, grRef, startPoint, endPoint, kCGGradientDrawsBeforeStartLocation|kCGGradientDrawsAfterEndLocation);
I'm not 100% sure how this gradient thing works but from what you've written I'm assuming that you basically just want the length of a line from the starting point until it hits the side of the rectangle.
If this is the case you simply need to do some trigonometry. Lets call the distance x and the angle a.
Between 0 and 45 degrees: width = xcos(a) so x = width/cos(a)
Between 45 and 90 degress: height = xsin(a) so x = height/sin(a)
Between 90 and 135 degrees we have moved to a new corner. Here x = height/cos(a-90).
Between 135 and 180 x = width/sin(a-90)
Between 180 and 225 we have again moved corner. Here x = width/cos(a-180).
Between 225 and 270 x = height/sin(a-180)
Last corner! Between 270 and 315 x = height/sin(a-270)
And finally between 315 and 360 x = width/cos(a-270)
Some of these probably simplify but its easiest to think about the line starting in the bottom left corner pointing right and sweeping round anticlockwise which is what appears to happen in your starting point calculation.
I would appreciate some help with the following. I'm trying to render a ring shape on top of another object in OpenGL ES 1.1 for an iPhone game. The ring is essentially the difference between two circles.
I have a graphic prepared for the ring itself, which is transparent in the centre.
I had hoped to just create a circle, and apply the texture to that. The texture is a picture of the ring that occupies the full size of the texture (i.e. the outside of the ring touches the four sides of the texture). The centre of the ring is transparent in the graphic being used.
It needs to be transparent in the centre to let the object underneath show through. The ring is rendering correctly, but is a solid black mass in the centre, not transparent. I'd appreciate any help to solve this.
Code that I'm using to render the circle is as follows (not optimised at all: I will move the coords in proper buffers etc for later code, but I have written it this way to just try and get it working...)
if (!m_circleEffects.empty())
{
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_DEPTH_TEST);
glEnable(GL_TEXTURE_2D);
int segments = 360;
for (int i = 0; i < m_circleEffects.size(); i++)
{
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(m_circleEffects[i].position.x, m_circleEffects[i].position.y, 0);
glBindTexture(GL_TEXTURE_2D, m_Texture);
float radius = 1.764706;
GLfloat circlePoints[segments * 3];
GLfloat textureCoords[segments * 2];
int circCount = 3;
int texCount = 2;
for (GLfloat i = 0; i < 360.0f; i += (360.0f / segments))
{
GLfloat pos1 = cosf(i * M_PI / 180);
GLfloat pos2 = sinf(i * M_PI / 180);
circlePoints[circCount] = pos1 * radius;
circlePoints[circCount+1] = pos2 * radius;
circlePoints[circCount+2] = (float)z + 5.0f;
circCount += 3;
textureCoords[texCount] = pos1 * 0.5 + 0.5;
textureCoords[texCount+1] = pos2 * 0.5 + 0.5;
texCount += 2;
}
glVertexPointer(3, GL_FLOAT, 0, circlePoints);
glTexCoordPointer(2, GL_FLOAT, 0, textureCoords);
glDrawArrays(GL_TRIANGLE_FAN, 0, segments);
}
m_circleEffects.clear();
glDisable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
I've been experimenting with trying to create a ring rather than a circle, but I haven't been able to get this right yet.
I guess that the best approach is actually to not create a circle, but a ring, and then get the equivalent texture coordinates as well. I'm still experimenting with the width of the ring, but, it is likely that the radius of the ring is 1/4 width of the total circle.
Still a noob at OpenGL and trying to wrap my head around it. Thanks in advance for any pointers / snippets that might help.
Thanks.
What you need to do is use alpha blending, which blends colors into each other based on their alpha values (which you say are zero in the texture center, meaning transparent). So you have to enable blending by:
glEnable(GL_BLEND);
and set the standard blending functions for using a color's alpha component as opacity:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
But always keep in mind in order to see the transparent object correctly blended over the object behind, you need to render your objects in back to front order.
But if you only use the alpha as a object/no-object indicator (only values of either 0 or 1) and don't need partially transparent colors (like glass, for example), you don't need to sort your objects. In this case you should use the alpha test to discard fragments based on their alpha values, so that they don't pollute the depth-buffer and prevent the behind lying object from being rendered. An alpha test set with
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f);
will only render fragments (~pixels) that have an alpha of more than 0.5 and will completely discard all other fragments. If you only have alpha values of 0 (no object) or 1 (object), this is exactly what you need and in this case you don't actually need to enable blending or even sort your objects back to front.