Is it possible to achieve something like this in Swing :
Notice here A is colored partially.
I know may be its not possible with DefaultHighlighter class alone.
(Text displayed on JTextArea)
Any solutions to achieve this in Swing's alone or have to apply CSS?
EDIT:
If that is not possible with swing, any solution with the below tags ?
Something like this is possible with Java 2D but not with JTextArea. So you can create code which renders this in a frame but I don't think you'll find an easy way to add support for this to the text editors (well, you could always embed an image in a text editor but that's probably not what you want).
[EDIT] For a text display for a karaoke player, Java 2D is the way to go. The Java editor APIs will just get in your way.
Here is an example for rendering text with a gradient:
public void sayWorld(Graphics2D g2D, int x, int y, boolean shear) {
final String txt = "Hello World!";
// gradient color from blue to red
GradientPaint gp = new GradientPaint((float)x, (float)y, Color.blue,
x+100, y+20, Color.red);
g2D.setPaint(gp);
if (shear) g2D.shear(-0.5,0.0);
else g2D.shear(+0.5, 0);
g2D.drawString(txt, x, y);
FontRenderContext frc = new FontRenderContext(null,false,false);
TextLayout tl = new TextLayout(txt, font, frc);
AffineTransform textAt = new AffineTransform();
//textAt.translate(0, (float)tl.getBounds().getHeight());
textAt.translate(x,y);
//textAt.shear(-0.5,0.0);
Shape outline = tl.getOutline(textAt);
g2D.setColor(Color.yellow);
BasicStroke wideStroke = new BasicStroke(2);
g2D.setStroke(wideStroke);
g2D.draw(outline);
}
(source)
Basically, you render get the text layout from a single line of the lyrics, then convert that into a shape (= the outline of each letter). You can then use this shape as a clip area to render the gradient.
In your case, you need an abrupt gradient which is twice as wide as the longest text line that you want to render. Shift it appropriately to get the gradient change in the place of the text where you need it.
Related
I have a canvas and a simple bitmap for background image, fills the whole screen. I created a rect painted black and set it's alpha to 250 in order to make a "dark" effect on the background image. My aim to make a simple circle object that reveals the place it's hovering above. I tried thinking in many ways how to excecute it and failed.
I think the best way is to create a simple circle that manages to decrease the darkness alpha on the position it hovers above, but I have no idea how to do it.
The relevant part of my code:
private ColorFilter filter = new LightingColorFilter(Color.BLACK, 1);
private Paint darkPaint = new Paint(Color.BLACK), paint = new Paint(), paint2 = new Paint();//The style of the text and dark.
public DarkRoomView(Context context) {
super(context);
myChild = this;
darkPaint.setColorFilter(filter);
darkPaint.setAlpha(250);
paint2.setAlpha(10);
paint.setAlpha(50);
}
private void loadGFX() {//Loads all of this view GFX file.
backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.darkroomscreen);
lightImage = BitmapFactory.decodeResource(getResources(), R.drawable.light);
}
private void drawGFX(Canvas canvas) {
canvas.drawBitmap(backgroundImage, 0, 0, paint2);//The backgeound image.
canvas.drawRect(0, 0, WIDTH, HEIGHT, darkPaint);//The darkness.
canvas.drawBitmap(lightImage, 50, 50, paint);//A spotlight.
}
Any ideas how I should get it done?
Thanks!
For the spotlight, you could draw a circle of the original image over the darkness. You'd simply need to find the correct rectangle of the original image (based on where your finger is), and then draw a circle of that particular rectangle over the darkness. Trying to look "through" the darkness won't really get you anywhere; you need to place something over it.
By the time you draw the "spotlight", you've already darkened the image with the rectangle. It would be difficult to recover information lost during that draw.
A more flexible approach would be to draw a dark rectangle with a spotlight in a separate image (that is, compose the "darkness" and spotlight alpha and color mask image first), and then draw that mask image on top of the background as a separate step. This would also let you easily do things like e.g. give the spotlight fuzzy borders.
I can change the color of the font like this
LabelStyle style1 = new LabelStyle(..some font...,
Color.valueOf("FF4500")
);
label.setStyle(style1);
but how do I change the background?
right now the background is the same as the background of whole screen which is set in
render method lke this
Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
Gdx.gl.glClearColor(
1.000f, 0.980f, 0.941f
,1);
Label label = new Label(labelText, skin);
Pixmap labelColor = new Pixmap(labelWidth, labelHeight, Pixmap.Format.RGB888);
labelColor.setColor(<your-color-goes-here>);
labelColor.fill();
label.getStyle().background = new Image(new Texture(labelColor)).getDrawable();
Basically, use the getDrawable() function of the Image class to assign the color of your Label's LabelStyles' background Drawable.
This is the simplest workaround I've been able to come up with and, frankly, it's just silly that there is no setBackground() in the Label class.
Actually, maybe the easiest fix is to hack the Label class and add a setBackground() method to it.
[Edit] Be sure to dispose of Pixmaps when you are done with them; i.e. labelColor.dispose();
[Update] #Mitrakov Artem made a good point: The above solution will affect all instances of this LabelStyle. If that's not what you want you can create a new LabelStyle, use the above method on it, then save it to the Label. Quoting Artem: "So I would recommend to create a new style (LabelStyle style = new LabelStyle(label.getStyle());), change its background and then apply it to the label (label.setStyle(style);)"
Actually you do not change the background of the Lable like that. You did just change the clearcolour. Guess you know that.
To change the background you need to change the background at the style of the label. To do so i'd recommend to use a simple NinePatch as background, (can be a square! if its white you can change the colour of the ninepatch and the background colour changes!)
NinePatch temp = new NinePatch(new Texture(....), 10, 10, 10, 10); //edges
For more information about ninepatch take a look here libgdx wiki ninepatch
You need to add that ninepatch to an Skin objekt. For example like this
Skin skin = new Skin();
skin.add("background",temp)
After that you can get a drawable from the skin that you can set as background of the LabelStyle.
style1.background = skin.getDrawable("background");
see libgdx API LabelStyle
You can also use a simple bitmap but that does get scaled to the label size which causes in most of the cases deformation. A Ninepatch can be scaled without having deformation.
If you need a quick and easy solution, you can use the snippet below. It doesn't work well with multiline text because it doesn't take the text width per line into account.
Anyway, the background is automatically adjusted to the width and height of the label widget (i.e. if your text changes).
private Label label = new Label("text", createLabelStyleWithBackground());
private LabelStyle createLabelStyleWithBackground() {
LabelStyle labelStyle = new LabelStyle();
labelStyle.font = new BitmapFont();
labelStyle.fontColor = Color.WHITE;
labelStyle.background = createBackground();
return labelStyle;
}
private Drawable createBackground() {
Pixmap labelColor = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
Color color = new Color(Color.GRAY);
color.a = 0.75f;
labelColor.setColor(color);
labelColor.fill();
Texture texture = new Texture(labelColor);
return new BaseDrawable() {
#Override
public void draw(Batch batch, float x, float y, float width, float height) {
GlyphLayout layout = label.getGlyphLayout();
x = label.getX();
y = label.getY() - (layout.height + 15) / 2; // +15 is some space
batch.draw(texture, x, y, layout.width, layout.height + 15);
}
};
}
here is an example with a multiline label
I have a certain text, that i want to annotate for the user. Unfortunately i don't even see where to begin. My algorithm gives as output a range on a string. What i am going for is something like this:
I would need two ways to marker the Characters ( Blue line, red line), maybe also to invert the character (Give a character a different background), or make them fat. What is especially difficult is aligning the Pictures (here designated by two black dots) with the characters. Since the characters should be in Courier New, i could know where to put the offset, but i cannot seem to do it.
Lastly i would have to apply a break after X characters and begin a new line, just like in the picture. I have not found any example how to approach this with java yet. With python i could use ImageDraw, but i am out of the water with java.
is it possible to show this in a canvas on the screen and export this as svg or pdf? I dont know any libraries that could do this. So i would be happy to receive some suggestions/examples also along this way.
The key is to deal with the FontMetrics API. The best you can do is looking at this reference doc.
Here is a sample code demonstrating this usage. It draws red and blue lines around an "Hello world" text according a range of chars.
The text is inside a JLabel, but you can adapt the paint method on any component (but you will have to call graphics.drawChars to paint the text.)
(the code is not very nice, but it demonstrates the usage of FontMetrics)
package com.example.swing;
import javax.swing.*;
import java.awt.*;
public class DemoFontMetrics {
public static void main(String[] args){
JFrame frame = new JFrame();
DecoratedLabel label = new DecoratedLabel("hello world !",new int[]{2,4}, new int[]{6,9});
JPanel textContainer = new JPanel(new FlowLayout());
textContainer.setBorder(BorderFactory.createEmptyBorder(5,5,5,5));
textContainer.add(label);
frame.getContentPane().add(textContainer);
frame.pack();
frame.setVisible(true);
}
private static class DecoratedLabel extends JLabel{
int startBlue;
int endBlue;
int startRed;
int endRed;
private DecoratedLabel(String text, int[] blueRange, int[] redRange) {
super(text);
startBlue = blueRange[0];
endBlue = blueRange[1];
startRed = redRange[0];
endRed = redRange[1];
}
#Override
public void paint(Graphics g) {
super.paint(g); //draw text
//set line with : 3
Stroke stroke = new BasicStroke(3f);
((Graphics2D)g).setStroke(stroke);
FontMetrics fm = g.getFontMetrics();
int h = fm.getHeight();
//compute blue line coordonate
fm.stringWidth(getText().substring(0,startBlue));
int x1 = fm.stringWidth(getText().substring(0, startBlue));
int x2 = fm.stringWidth(getText().substring(0, endBlue));
g.setColor(Color.BLUE);
g.drawLine(x1,0,x2,0);// draw blue line
//compute red line coordonates
int x3 = fm.stringWidth(getText().substring(0,startRed));
int x4 = fm.stringWidth(getText().substring(0, endRed));
g.setColor(Color.RED);
g.drawLine(x3,h-1,x4,h-1); // draw redline
}
}
}
If the text is displayed via JTextPane you can conveniently define a custom HighlightPainter, that draws lines above or under the text.
Then you can add highlights to the text pane programmatically by calling:
textPane.getHighlighter().addHighlight(startPos, endPos,
myLineHighlightPainter);
Images may as well easily be added to the pane, by:
textPane.setIcon(myImageIcon);
You can create svg directly for example: http://xmlgraphics.apache.org/batik/ it's an xml based vector graphics format.
EDIT: You can display svg in java, you can create pdf's with it from java. You can publish it in the web (simply as svg).
I've been having a lot of trouble Googling how to draw simple 2D text with Libgdx. Here is the code that I've put together so far:
SpriteBatch spriteBatch;
BitmapFont font;
CharSequence str = "Hello World!";
spriteBatch = new SpriteBatch();
font = new BitmapFont();
spriteBatch.begin();
font.draw(spriteBatch, str, 10, 10);
spriteBatch.end();
The code does draw the Hello World string, however, it messes up all my other drawings. They are there, only brutally mutilated, and move and all that. I've tried Gdx.gl11.glPushMatrix() and Gdx.gl11.glPopMatrix() around just about every subset of statements.
I've narrowed the mutilated drawings down to the font.draw() call, if that's taken out, everything works fine (but of course there is no text then).
I don't see much reason of creating separate batch for text drawing. Using gdxVersion = '1.4.1' (built with gradle in Android Studio) that code draws text successfully:
BitmapFont font = new BitmapFont(); //or use alex answer to use custom font
public void render( float dt )
{
batch.setProjectionMatrix(camera.combined); //or your matrix to draw GAME WORLD, not UI
batch.begin();
//draw background, objects, etc.
for( View view: views )
{
view.draw(batch, dt);
}
font.draw(batch, "Hello World!", 10, 10);
batch.end();
}
Note, that here you draw in game world coordinates, so if your character moves (in platformer, for example), than text will move too. If you want to see text, that it will be fixed on screen, something like Label/TextField or how it is called in different UI frameworks, than I recommend to use Stage (and TextArea for text), see for example on how to use Stage here: http://www.toxsickproductions.com/libgdx/libgdx-basics-create-a-simple-menu/
When I created the Bitmap font it was like this:
font = new BitmapFont(Gdx.files.internal("Calibri.fnt"),Gdx.files.internal("Calibri.png"),false);
try this by downloading some font file(while downloading a font please check if .fnt file and .png file is included for the same)
To create a .fnt file, use hiero which is provided by LibGDX's website.
Set the size of font 150, it will create a .fnt file and a .png file. Copy both files in your assets folder.
To declare the font:
BitmapFont font;
To load the font: (In create method)
font = new BitmapFont(Gdx.files.internal("data/rayanfont.fnt"), false);
//rayanfont is the font name
To render:
batch.begin();
font.setScale(.2f);
font.draw(batch, "hello", x,y);
batch.end();
This will work smoothly.
Try to call multiple batch.begin() and batch.end():
CharSequence str = "Hello World!";
spriteBatch = new SpriteBatch();
font = new BitmapFont();
spriteBatch.begin();
font.draw(spriteBatch, str, 10, 10);
spriteBatch.end();
spriteBatch.begin();
//draw your other sprites here
spriteBatch.draw(...);
spriteBatch.end();
or just use a different instance of SpriteBatch
Is there a friendlier way to get an instance of FontMetrics than
FontMetrics fm = Graphics.getFontMetrics(Font);
I hate this way because of the following example:
If you want to create in a game a menu and you want all the menuitems in the center of the screen you need fontmetrics. But, mostly, menuitems are clickable. So I create an array of Rectangles and all the rectangles fits around the items, so when the mouse is pressed, I can simply use
for (int i = 0; i < rects.length; i++)
if (rects[i].contains(mouseX, mouseY)) { ... }
But to create the rects I also need FontMetrics for their coordinates. So this mean that I have to construct all my rectangles in the paint-method of my menu.
So I want a way to get the FontMetrics so I can construct the Rectangles in a method called by the constructor.
For me the easiest way was to:
Font font = new Font("Helvetica",Font.PLAIN,12);
Canvas c = new Canvas();
FontMetrics fm = c.getFontMetrics(font);
Benefits:
If you call c.getGraphics() it will return null (thus there is no graphics object)
This (canvas) will also work in headless mode.
Now you can easily get height and width...
The really correct answer is to use Toolkit.
Font font = new Font("Courier New", Font.PLAIN, 14);
FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font);
Once the background component, i.e. whatever is behind your menu, has been rendered, it has a Graphics object that you can use to get the metrics for a given font, just once.
You certainly don't want to be doing this in the paint method, which should be as lightweight as possible. I'd hang this code on a listener that gets called when the component is first rendered. It can store the resulting FontMetrics object somewhere where you can later access it, either in a paint method for drawing those menu item boxes.
Rather than determining the measurements of your menu graphics at the last moment, i.e. when painting, it might be a good idea instead to create some components to represent your menu. You can place those components on the Glass Pane more info here so they'll float above everything else, and you'll have the added bonus that those components are all capable of accepting mouse clicks and firing listener events on them, and since they only capture events on their own geometry you don't even have to figure out which part of menu was hit by the click, if at all.
Another advantage of using components here is that you may entirely get around the requirement for fiddling with font metrics. There are ready-made menu items, or you could just use JLabels, and you can specify their alignment, you can use a LayoutManager to size the boxes to the width of the biggest label, and so forth.
Assuming the menu text is fixed, you could pre-draw the text to a BufferedImage with alpha transparency and make your calculations then. Then, when you need the menu text, just draw the image.
You'll still have to do some offset calculations to centre the image (assuming the panel size can change), but these should be relatively lightweight.
I think this is a good solution
private static HashMap<Font, FontMetrics> fontmetrics = new HashMap<Font, FontMetrics>();
public static FontMetrics getFontMetrics(Font font)
{
if (fontmetrics.containsKey(font))
{
return fontmetrics.get(font);
}
FontMetrics fm = createFontMetrics(font);
fontmetrics.put(font, fm);
return fm;
}
private static FontMetrics createFontMetrics(Font font)
{
BufferedImage bi = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB_PRE);
Graphics g = bi.getGraphics();
FontMetrics fm = g.getFontMetrics(font);
g.dispose();
bi = null;
return fm;
}
Adding to what Lonzak said, how about this:
public static FontMetrics getFontMetrics(Font font){
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gd = ge.getDefaultScreenDevice();
GraphicsConfiguration config = gd.getDefaultConfiguration();
Canvas c = new Canvas(config);
return c.getFontMetrics(font);
}
You could store the 'config' variable as a static variable so it is constructed once in some utility font class that contains other font related information for your game/development environment. I guess you could also do this with the canvas variable.
Updated recommendation. FontMetrics is deprecated. Use LineMetrics instead.
String text = "some string";
FontRenderContext frc = new FontRenderContext(font.getTransform(), true, true);
LineMetrics lm = font.getLineMetrics(text, frc);
However, some methods such as SwingUtilities.computeStringWidth require a FontMetrics instance. Another option is to compute the bounds of the String.
Rectangle2D bounds = font.getStringBounds(text, frc);
Then the width and height may be obtained from the bounds.