JOGL glut text not resizing with window and primitives - java

I'm currently exploring opengl through the use of the JOGL library (the java wrappers for openGL) which I'm using to create 2d/3d graphs. At the minute I'm having a little issue with text I've rendered through the "glutBitmapString" method, it isn't resizing in respect of the window as shown in the screenshot below. Unfortunately the job spec I've been given is that this must be done in Java, so I can't jump to any other language that has a better supported version of openGL.
Everything else in the window resizes correctly so I'm assuming the issue is in the code I've posted below, if not then I'll be happy to post code you feel is relevant to the issue.
Here is a snippet of my code I'm using to render the text
GL gl = drawable.getGL();
GLUT glut = new GLUT();
float textPosx = -0.4f;
float textPosy = -2.1f;
gl.glColor3f(1.0f, 0.0f, 0.0f);
// Move to rastering position
gl.glRasterPos2f(textPosx, textPosy);
// convert text to bitmap and tell what string to put
glut.glutBitmapString(GLUT.BITMAP_HELVETICA_12, "0");
textPosx = 1.75f;
textPosy = -2.15f;
// Move to rastering position
gl.glRasterPos2f(textPosx, textPosy);
// convert text to bitmap and tell what string to put
glut.glutBitmapString(GLUT.BITMAP_HELVETICA_18, "TIME");
textPosx = -1.0f;
textPosy = 1.0f;
gl.glColor3f(0.0f, 1.0f, 0.0f);
// Move to rastering position
gl.glRasterPos2f(textPosx, textPosy);
// convert text to bitmap and tell what string to put
glut.glutBitmapString(GLUT.BITMAP_HELVETICA_18, "ERRORS");

glutBitmapString draws text in 2D. 2D text size is based on font size. So, if you set the font size to 18, as you have in this example, then it will be standard 18 pt font size on the screen, no matter how large you make the window or how close you zoom in. This is not a Java issue. Java is not actually drawing anything. Everything is being drawn by the OpenGL native libraries, which are written in C++, so it will be exactly the same in C++ as it is in Java.
There are two ways you could work around this. One would be to change the font size of the text as you zoom in or out. This would be kind of a awkward, and may be difficult to get right. A better option, imo, would be to simply use 3D text. In JOGL you use the TextRenderer object to draw 3D text.
In your init method create a global variable like so:
textr = new TextRenderer(new Font("SansSerif", Font.PLAIN, 18));
Obviously, change the font settings to whatever you prefer. Then in your display loop:
textr.setColor(Color.GREEN);
textr.begin3DRendering();
textr.draw3D("ERRORS", xLocation, yLocation, zLocation, scale);
textr.end3DRendering();
Personally, I prefer to use a large font size and then scale it down some, that way, when you zoom in, it doesn't get pixelated.
Also, unlike 2D text, 3D text will not always face the screen. You have to do that manually. It depends on how your camera is set up, but if you're using basic rotations to move the camera around, usually you can just negate those rotations on the 3D text object to make it face the camera.
For the x, y, and z locations, those are the locations within the current object (local coordinates). Think of the beginRendering() to endRendering() as one object with its own local coordinate system. Usually, I prefer to draw my text at 0, 0, 0 local coordinates, then move the entire object to the proper location. That way rotations are easier to understand.

You could try using TextRenderer. Works fine.

Related

How to draw text in world coordinate space?

I'm creating a game in libGDX. I want to create some UI elements (buttons and stuff), because of my app design, I would like to draw them in the world space, as other game objects.
I'm using Freetype generator that generates a bitmap font from true type font files(.ttf). The problem is that the dimension of the font is in pixels.
Orthographic camera that I use to to render the world, has viewport size of approximately 10x10, so when I generate a font at the size of 10, it covers almost whole screen(too big) and also looks very ugly because generated bitmap for the font is too small (too few pixels).
What I want is to create sprite, draw it at same size(world space) and draw text over it, and basicly create a button.
Is there some well established way how to deal with this?
Thanks to clarifying comments, I've came up with the solution.
I took a point at which I wanted to draw the text, projected it to the screen space by my world camera. Then I flipped y axis by:
point.y = viewportHeight - point.y;
Then I unprojected it with ScreenViewport (separate viewport for drawing the text, is uses camera of the size of the screen so 1unit == 1pixel).
Now I can draw text in projection where 1unit = 1pixel, on the point that is at the same place on the screen as previously chosen point in world space.
I also wanted to be able to draw text inside rectangular boundaries. For this I chose another point. At this point text should end. Did the same procedure as with start point, and then calculated width
targetWidth = endpoint.x - startpoint.x;
Then I used GlypthLayout class to get actual width of my text at some(generated) font size.
actualWidth = glyphLayout.width;
And when I scaled font like this
font.getData().setScale(targetWidth / actualWidth);
my font get scaled so drawed text is wide as target width.
But be aware of another problem! When I generate bimap font via FreetypeGenerator with size bigger when approximately 300, some letters don't draw, and are missing. (probably bug).

Optimizing performance of GrabCut in opencv-java

Recently I have been given a project, where I have to extract face (face+hair) from a given image.
I am solving this problem in the following ways.
I am extracting face locations from given image. [I am getting a rectangle]
I am extracting that rectangle and placing it in another image of same dimensions as input image.[face_image]
I am applying grabCut algorithm on the face_image of step 2.
When the face_image contains smooth background then the algorithm grabCut it working well but when the background of face_image is complex then the algorithm grabCut extracts some part of background too in the processed image.
Here is a snapshot of the results that I am getting.
Here is my code of grabCut:
public void extractFace(Mat image, String fileNameWithCompletePath,
int xOne, int xTwo, int yOne, int yTwo) throws CvException {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Rect rectangle = new Rect(xOne, yOne, xTwo, yTwo);
Mat result = new Mat();
Mat bgdModel = new Mat();
Mat fgdModel = new Mat();
Mat source = new Mat(1, 1, CvType.CV_8U, new Scalar(3));
Imgproc.grabCut(image, result, rectangle, bgdModel, fgdModel, 8, Imgproc.GC_INIT_WITH_RECT);
Core.compare(result, source, result, Core.CMP_EQ);
Mat foreground = new Mat(image.size(), CvType.CV_8UC3, new Scalar(255, 255, 255));
image.copyTo(foreground, result);
Imgcodecs.imwrite(fileNameWithCompletePath, foreground);
}
How can I improve performance of grabCut algorithm so that it will detect only face and hair from given image?
You should be able to do this by "helping" grabCut know a little about the foreground and background. There is a python tutorial that shows how this is done manually by selecting the foreground and background.
To do this automatically, you will need to find programmatic ways to detect the foreground and background. The foreground consists mostly of hair and skin so you will need to detect them.
Skin - There are several papers and blogs on how to do this. Some of them are pretty simple and this OpenCV tutorial may also help. I've found plain hue/saturation to get me pretty far.
Hair - This is trickier but is definitely still doable. You may be able to hair and just use skin and background if this turns out to be too much work.
Background - You should be able to use range() to find things in the image that are purple, green, and blue. You know for sure that these things are not skin or hair and are therefore part of the background.
Use thresholding to create a mask of the areas that are most likely skin, hair, and background. You can then use them as bgdModel and fgdModel (or the skin and hair masks) instead of Mat().
Sorry this is so high-level. I hope it helps!
Another approach, since you have already detected the face, is to simply choose a better initial mask for initialising GrabCut - e.g. by using an oval instead of a rectangle.
Detect face rectangle (as you are already doing)
Create a mask:
a) Create a new black image of the same size as your input image
b) Draw a white-filled ellipse with the same height, width, top and left positions as the face rectangle
Call GrabCut with GC_INIT_WITH_MASK instead of GC_INIT_WITH_RECT:
Imgproc.grabCut(image, mask, rectangle, bgdModel, fgdModel, 8, Imgproc.GC_INIT_WITH_MASK);
This initializes the foreground with a better model because faces are more oval-shaped than rectangle-shaped, so it should include less of the background to begin with.
I would suggest to "play" with the rectangle coordinates (int xOne, int xTwo, int yOne, int yTwo). Using your code and these coordinates 1, 400, 30, 400 I was able to avoid the background. (I tried to post the images I successfully cropped but I need at least 10 reputation to do so)
The best optimization that can be done to any Java routine is conversion to a native language.

Swing issue: drawing rectangles over the pdf image and keep their size and positions consistent when zooming in and out

I have a rather pressing question in regards to Swing, which I haven't touched for many years.
I have a code that allows the user to draw different rectangles on the pdf document (contained within JPanel). I draw them, move them, resize them, select them, and even write text on them. What I can't do is to keep them consistent when I zoom the document. As the document gets bigger, the rectangles I've drawn stay at the same position and the same size.
I was wondering if there's a relatively easy logic to track the zooming level and, most importantly, update the rectangles accordingly. I can retrieve zoom factor, it's a float, but, unfortunately, I'm using Rectangle object, which uses int for x, y, height, and width. It will be a hassle to convert it to Rectangle.Float, and I wanted to save it for a last resort.
I've tried to use AffineTransform, but I'm not quite familiar with it, for some reason I'm getting the wrong coordinates for y. Can anyone explain to me:
What's the best way to control the Rectangle object, as the pdf document gets zoomed in and out?
If AffineTransform is the best way, how should I handle it (maybe there's a link to a good explanation, if so - I couldn't find it)?
This is the only issue I've been struggling with and it's getting a bit frustrating now.
To scale using an AffineTransform:
Get the transform T of the Graphics object G
Create an AffineTransform object A
Set the scale of A
Set the transform of the G to A
Draw the shapes
Set the transform of G back to T
Translated into code - assuming scale is the value to scale by:
#Override
protected void paintComponent(Graphics gr){
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
AffineTransform prevTransform = g.getTransform();
AffineTransform at = new AffineTransform(prevTransform);
at.scale(scale, scale);
g.setTransform(at);
g.drawRect(...);
g.setTransform(prevTransform);
}

Graphics2D: How to create consistent padding around an irregular shape?

I'm using Java Graphics2D to generate this map with some sort of tinted red overlay over it. As you can see, the overlay gets cut off along the image boundary on the left side:-
After demo'ing this to my project stakeholders, what they want is for this overlay to clip along the map boundary with some consistent padding around it. The simple reason for this is to give users the idea that the overlay extends outside the map.
So, my initial thought was to perform a "zoom and shift", by creating another larger map that serves as a "cookie cutter", here's my simplified code:-
// polygon of the map
Polygon minnesotaPolygon = ...;
// convert polygon to area
Area minnesotaArea = new Area();
minnesotaArea.add(new Area(minnesotaPolygon));
// this represents the whole image
Area wholeImageArea = new Area(new Rectangle(mapWidth, mapHeight));
// zoom in by 8%
double zoom = 1.08;
// performing "zoom and shift"
Rectangle bound = minnesotaArea.getBounds();
AffineTransform affineTransform = new AffineTransform(g.getTransform());
affineTransform.translate(-((bound.getWidth() * zoom) - bound.getWidth()) / 2,
-((bound.getHeight() * zoom) - bound.getHeight()) / 2);
affineTransform.scale(zoom, zoom);
minnesotaArea.transform(affineTransform);
// using it as a cookie cutter
wholeImageArea.subtract(minnesotaArea);
g.setColor(Color.GREEN);
g.fill(wholeImageArea);
The reason I'm filling the outside part with green is to allow me to see if the cookie cutter is implemented properly. Here's the result:-
As you can see, "zoom and shift" doesn't work in this case. There is absolutely no padding at the bottom right. Then, I realized that this technique will not work for irregular shape, like the map... and it only works on simpler shapes like square, circle, etc.
What I want is to create consistent padding/margin around the map before clipping the rest off. To make sure you understand what I'm saying here, I photoshopped this image below (albeit, poorly done) to explain what I'm trying to accomplish here:-
I'm not sure how to proceed from here, and I hope you guys can give me some guidance on this.
Thanks.
I'll just explain the logic, as I don't have time to write the code myself. The short answer is that you should step through each pixel of the map image and if any pixels in the surrounding area (i.e. a certain distance away) are considered "land" then you register the current pixel as part of the padding area.
For the long answer, here are 9 steps to achieve your goal.
1. Decide on the size of the padding. Let's say 6 pixels.
2. Create an image of the map in monochrome (black is "water", white is "land"). Leave a margin of at least 6 pixels around the edge. This is the input image: (it isn't to scale)
3. Create an image of a circle which is 11 pixels in diameter (11 = 6*2-1). Again, black is empty/transparent, white is solid. This is the hit-area image:
4. Create a third picture which is all black (to start with). Make it the same size as the input image. It will be used as the output image.
5. Iterate each pixel of the input image.
6. At that pixel overlay the hit-area image (only do this virtually, via calculation), so that the center of the hit-area (the white circle) is over the current input image pixel.
7. Now iterate each pixel of the hit-area image.
8. If the any white pixel of the hit-area image intersects a white pixel of the input image then draw a white pixel (where the center of the circle is) into the output image.
9. Go to step 5.
Admittedly, from step 6 onward it isn't so simple, but it should be fairly easy to implement. Hopefully you understand the logic. If my explanation is too confusing (sorry) then I could spend some time and write the full solution (in Javascript, C# or Haskell).

how to increase opacity in gaussian blur

I have a Java application where I need to draw text on top of an image. The text, the font, and the image are all determined at run time. The text needs to look nice, yet be readable (sufficiently contrastive) on top of the image.
To meet these requirements, I create a drop shadow. This is done by drawing the text in opaque black, on a blank/transparent BufferedImage, then applying a Gaussian blur filter. I then draw the text again, in opaque white, on top of the drop shadow. So I have opaque white text, with a black blurred shadow around it that quickly fades to full transparency. I can then draw this image on top of the background image.
The problem I'm trying to solve is that the drop shadow seems too transparent. So against bright, busy backgrounds, it doesn't give the white text enough separation.
So how to increase the opacity of the shadow? I've tried increasing the radius of the gaussian blur, and that makes the shadow wider, but doesn't make it more opaque.
The code I'm using is based on this DropShadowDemo by Romain Guy. I use his createDropShadow() and gaussianBlurFilter(). But instead of painting the drop shadow and the text separately during paintComponent(), I draw them both onto a BufferedImage in advance; and I draw this single BufferedImage on top of the background during paintComponent(). Maybe that's my problem? But I don't see how that would decrease the opacity of the shadow. I'm not using g2.setComposite() during paintComponent().
I've looked at adjusting the opacity of the drop shadow using some kind of BufferedImageOp, such as a LookupOp. But it seems like a lot of work for a simple adjustment (creating four arrays of numbers, I guess). I don't think a RescaleOp would work, since I want the result alpha to fall in the same range (0 to 1) as the source alpha. If I could specify a BufferedImageOp that sets new alpha = sqrt(old alpha), or something like that, that would be ideal. But I don't know an easy way to do that.
Details of the code can be seen here:
ShadowedText.java (creates the text-with-drop-shadow image)
SetBeforeMe.java (implements paintComponent() that draws the image)
I would include relevant code blocks here, but it seems like the relevant amount of code is too big (wall of code)... might as well just give links to the source files.
Update
It looks like Change the alpha value of a BufferedImage? would be a way to change the opacity of the drop shadow... basically recalculating the alpha value of each pixel, one by one. TBD: whether it's portable (to 64-bit machines, e.g.), and whether it's fast enough. If I do a = sqrt(a) or a = sin(a * pi * 0.5) on every pixel (thinking of a in the range 0 to 1), will that be slow? I would be happy to know if there's a simpler way that takes advantage of available optimizations, like the built-in BufferedImageOps presumably do. Maybe the answer is building arrays for LookupOp after all. Anybody know of some example code for that?
Final update
Solved using a LookupOp; see code below.
Below is the code I ended up with to make a BufferedImage more opaque. I decided to go ahead and use a LookupOp, rather than a potentially unportable and slow loop over getRGB / setRGB on each pixel. The work to set up Lookup arrays wasn't so bad.
/* Make alpha channel more opaque.
* Modify the alpha (opacity) channel so that values are higher, but still
* continuous and monotonically increasing.
*/
private static void adjustOpacity(BufferedImage shadowImage) {
// Use a lookup table with four arrays;
// the three for RGB are identity arrays (no change).
byte identityArray[] = new byte[256];
for (int i=0; i < 256; i++)
identityArray[i] = (byte)i;
byte alphaArray[] = new byte[256];
// map the range (0..256] to (0..pi/2]
double mapTo90Deg = Math.PI / 2.0 / 256.0;
for (int i=0; i < 256; i++) {
alphaArray[i] = (byte)(Math.sin(i * mapTo90Deg) * 256);
}
byte[][] tables = {
identityArray,
identityArray,
identityArray,
alphaArray
};
ByteLookupTable blut = new ByteLookupTable(0, tables);
LookupOp op = new LookupOp(blut, null);
// With LookupOp, it's ok for src and dest to be the same image.
op.filter(shadowImage, shadowImage);
}
It seems to work (though I haven't taken before-and-after screenshots for comparison).

Categories