I am trying to create a program that renders blocks of text as moving object to be used in Box2D. You start by typing some words, the words themselves scale with the amount of characters. Then, when pressing enter, the program separates the words and put each of them inside an Array List. It's all good after the first enter, however when typing text after that, the text doesn't function as intended. It's all wonky. But when I remove the part that displays the text inside the TEMPTEXT class, and only displays the rectangle, it all works perfectly.
Here is my code:
PFont xanhMono;
String quote = "";
String tempQuote;
ArrayList<TEMPTEXT> box;
float tw;
float ts=200;
float qtX, qtY, qtW, qtH;
int qtStart, qtEnd, qtNum;
void setup() {
size(800, 800);
pixelDensity(displayDensity());
xanhMono = createFont("XanhMono-Regular.ttf", 1000);
box = new ArrayList<TEMPTEXT>();
}
void draw() {
background(240);
textFont(xanhMono);
textSize(int(ts));
textLeading(int(ts));
textAlign(LEFT, TOP);
fill(0);
text(quote, 0, 0, width+50, height);
if (frameCount%60<30) {
float twq = textWidth(quote);
line(twq, 0, twq, ts);
}
for (TEMPTEXT txt : box) {
txt.display();
}
}
class TEMPTEXT{
float x;
float y;
float speed;
float gravity;
float h;
float w;
String q;
TEMPTEXT(float tempX, float tempY, float tempW, float tempH, String tempQ){
x = tempX;
y = tempY;
w = tempW;
h = tempH;
q = tempQ;
speed = 0;
gravity = 0.1;
}
void display(){
noFill();
stroke(2);
rect(x, y, w, h);
//if i delete the next 3 lines, everything is all good
textSize(h);
textAlign(LEFT,TOP);
text(q, x, y);
}
}
void keyTyped() {
if (key == '\n' ) {
quote = trim(quote);
qtStart = 0;
qtEnd = 0;
qtNum = 0;
qtX = 0;
qtY = 0;
qtW = 0;
tempQuote = "";
for (int i = 0; i < quote.length(); i++) {
if (quote.charAt(i)==' ') {
qtEnd = i;
tempQuote = quote.substring(qtStart, qtEnd);
qtW = textWidth(tempQuote);
qtH = ts;
qtX = ts/2*qtStart;
TEMPTEXT p = new TEMPTEXT(qtX, qtY, qtW, qtH, tempQuote);
box.add(p);
qtNum++;
qtStart = i+1;
} else if (i==quote.length()-1) {
tempQuote = quote.substring(qtStart, quote.length());
qtW = textWidth(tempQuote);
qtH = ts;
qtX=ts/2*qtStart;
TEMPTEXT p = new TEMPTEXT(qtX, qtY, qtW, qtH, tempQuote);
box.add(p);
}
}
quote = "";
} else if (key == BACKSPACE) {
if (quote.length() > 0) {
quote = quote.substring(0, quote.length()-1);
}
} else {
quote=quote+key;
}
tw=textWidth(quote);
if (tw!=0) {
ts*=width/tw;
if (ts>=height) {
ts=height/1.5;
}
}
}
Related
I am working on a project that finds the average color of each scene from a movie and if those colors are within a certain threshold value of red then the frame will be saved as a png. The atnm1.mov reads that RuntimeException: Could not load movie file antm1.mov, and the files do not save?
import processing.video.*;
color trackColor;
float threshold;
Movie video;
void setup() {
size(1200,900);
video = new Movie(this, "antm1.mov");
video.loop();
trackColor = color(255, 0, 0);
}
void movieEvent(Movie video) {
video.read();
}
void draw() {
video.loadPixels();
image(video, 0, 0);
threshold = 80;
float avgX = 0;
float avgY = 0;
int count = 0;
// Begin loop to walk through every pixel
for (int x = 0; x < video.width; x++ ) {
for (int y = 0; y < video.height; y++ ) {
int loc = x + y * video.width;
// What is current color
color currentColor = video.pixels[loc];
float r1 = red(currentColor);
float g1 = green(currentColor);
float b1 = blue(currentColor);
float r2 = red(trackColor);
float g2 = green(trackColor);
float b2 = blue(trackColor);
float d = dist(r1, g1, b1, r2, g2, b2);
if (d < threshold*threshold) {
avgX += x;
avgY += y;
count++;
}
}
}
if (count >5) {
avgX = avgX / count;
avgY = avgY / count;
saveFrame("output/mov1####.png");
}
}
I wrote a program to render a Julia Set. The single threaded code is pretty straightforward and is essentially like so:
private Image drawFractal() {
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
for (int x = 0; x < WIDTH; x++) {
for (int y = 0; y < HEIGHT; y++) {
double X = map(x,0,WIDTH,-2.0,2.0);
double Y = map(y,0,HEIGHT,-1.0,1.0);
int color = getPixelColor(X,Y);
img.setRGB(x,y,color);
}
}
return img;
}
private int getPixelColor(double x, double y) {
float hue;
float saturation = 1f;
float brightness;
ComplexNumber z = new ComplexNumber(x, y);
int i;
for (i = 0; i < maxiter; i++) {
z.square();
z.add(c);
if (z.mod() > blowup) {
break;
}
}
brightness = (i < maxiter) ? 1f : 0;
hue = (i%maxiter)/(float)maxiter;
int rgb = Color.getHSBColor(hue,saturation,brightness).getRGB();
return rgb;
}
As you can see it is highly inefficient. Thus I went for Parallelizing this code using the fork/join framework in Java and this is what I came up with:
private Image drawFractal() {
BufferedImage img = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
ForkCalculate fork = new ForkCalculate(img, 0, WIDTH, HEIGHT);
ForkJoinPool forkPool = new ForkJoinPool();
forkPool.invoke(fork);
return img;
}
//ForkCalculate.java
public class ForkCalculate extends RecursiveAction {
BufferedImage img;
int minWidth;
int maxWidth;
int height;
int threshold;
int numPixels;
ForkCalculate(BufferedImage b, int minW, int maxW, int h) {
img = b;
minWidth = minW;
maxWidth = maxW;
height = h;
threshold = 100000; //TODO : Experiment with this value.
numPixels = (maxWidth - minWidth) * height;
}
void computeDirectly() {
for (int x = minWidth; x < maxWidth; x++) {
for (int y = 0; y < height; y++) {
double X = map(x,0,Fractal.WIDTH,-2.0,2.0);
double Y = map(y,0,Fractal.HEIGHT,-1.0,1.0);
int color = getPixelColor(X,Y);
img.setRGB(x,y,color);
}
}
}
#Override
protected void compute() {
if(numPixels < threshold) {
computeDirectly();
return;
}
int split = (minWidth + maxWidth)/2;
invokeAll(new ForkCalculate(img, minWidth, split, height), new ForkCalculate(img, split, maxWidth, height));
}
private int getPixelColor(double x, double y) {
float hue;
float saturation = 1f;
float brightness;
ComplexNumber z = new ComplexNumber(x, y);
int i;
for (i = 0; i < Fractal.maxiter; i++) {
z.square();
z.add(Fractal.c);
if (z.mod() > Fractal.blowup) {
break;
}
}
brightness = (i < Fractal.maxiter) ? 1f : 0;
hue = (i%Fractal.maxiter)/(float)Fractal.maxiter;
int rgb = Color.getHSBColor(hue*5,saturation,brightness).getRGB();
return rgb;
}
private double map(double x, double in_min, double in_max, double out_min, double out_max) {
return (x-in_min)*(out_max-out_min)/(in_max-in_min) + out_min;
}
}
I tested with a range of values varying the maxiter, blowup and threshold.
I made the threshold such that the number of threads are around the same as the number of cores that I have (4).
I measured the runtimes in both cases and expected some optimization in parallelized code. However the code ran in the same time if not slower sometimes. This has me baffled. Is this happening because the problem size isn't big enough? I also tested with varying image sizes ranging from 640*400 to 1020*720.
Why is this happening? How can I run the code parallely so that it runs faster as it should?
Edit
If you want to checkout the code in its entirety head over to my Github
The master branch has the single threaded code.
The branch with the name Multicore has the Parallelized code.
Edit 2 Image of the fractal for reference.
Here is your code rewritten to use concurrency. I found that my Lenovo Yoga misreported the number of processors by double. Also Windows 10 seems to take up an enormous amount of processing, so the results on my laptop are dubious. If you have more cores or a decent OS, it should be much better.
package au.net.qlt.canvas.test;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
public class TestConcurrency extends JPanel {
private BufferedImage screen;
final Fractal fractal;
private TestConcurrency(final Fractal f, Size size) {
fractal = f;
screen = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_ARGB);
setBackground(Color.BLACK);
setPreferredSize(new Dimension(size.width,size.height));
}
public void test(boolean CONCURRENT) {
int count = CONCURRENT ? Runtime.getRuntime().availableProcessors()/2 : 1;
Scheduler scheduler = new Scheduler(fractal.size);
Thread[] threads = new Thread[count];
long startTime = System.currentTimeMillis();
for (int p = 0; p < count; p++) {
threads[p] = new Thread() {
public void run() {
scheduler.schedule(fractal,screen);
}
};
threads[p].start();
try {
threads[p].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
DEBUG("test threads: %d - elasped time: %dms", count, (System.currentTimeMillis()-startTime));
}
#Override public void paint(Graphics g) {
if(g==null) return;
g.drawImage(screen, 0,0, null);
}
public static void main(String[]args) {
JFrame frame = new JFrame("FRACTAL");
Size size = new Size(1024, 768);
Fractal fractal = new Fractal(size);
TestConcurrency test = new TestConcurrency(fractal, size);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
frame.add(test);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
for(int i=1; i<=10; i++) {
DEBUG("--- Test %d ------------------", i);
test.test(false);
test.repaint();
test.test(true);
test.repaint();
}
}
public static void DEBUG(String str, Object ... args) { Also System.out.println(String.format(str, args)); }
}
class Fractal {
ComplexNumber C;
private int maxiter;
private int blowup;
private double real;
private double imaginary;
private static double xs = -2.0, xe = 2.0, ys = -1.0, ye = 1.0;
public Size size;
Fractal(Size sz){
size = sz;
real = -0.8;
imaginary = 0.156;
C = new ComplexNumber(real, imaginary);
maxiter = 400;
blowup = 4;
}
public int getPixelColor(Ref ref) {
float hue;
float saturation = 1f;
float brightness;
double X = map(ref.x,0,size.width,xs,xe);
double Y = map(ref.y,0,size.height,ys,ye);
ComplexNumber Z = new ComplexNumber(X, Y);
int i;
for (i = 0; i < maxiter; i++) {
Z.square();
Z.add(C);
if (Z.mod() > blowup) {
break;
}
}
brightness = (i < maxiter) ? 1f : 0;
hue = (i%maxiter)/(float)maxiter;
return Color.getHSBColor(hue*5,saturation,brightness).getRGB();
}
private double map(double n, double in_min, double in_max, double out_min, double out_max) {
return (n-in_min)*(out_max-out_min)/(in_max-in_min) + out_min;
}
}
class Size{
int width, height, length;
public Size(int w, int h) { width = w; height = h; length = h*w; }
}
class ComplexNumber {
private double real;
private double imaginary;
ComplexNumber(double a, double b) {
real = a;
imaginary = b;
}
void square() {
double new_real = Math.pow(real,2) - Math.pow(imaginary,2);
double new_imaginary = 2*real*imaginary;
this.real = new_real;
this.imaginary = new_imaginary;
}
double mod() {
return Math.sqrt(Math.pow(real,2) + Math.pow(imaginary,2));
}
void add(ComplexNumber c) {
this.real += c.real;
this.imaginary += c.imaginary;
}
}
class Scheduler {
private Size size;
private int x, y, index;
private final Object nextSync = 4;
public Scheduler(Size sz) { size = sz; }
/**
* Update the ref object with next available coords,
* return false if no more coords to be had (image is rendered)
*
* #param ref Ref object to be updated
* #return false if end of image reached
*/
public boolean next(Ref ref) {
synchronized (nextSync) {
// load passed in ref
ref.x = x;
ref.y = y;
ref.index = index;
if (++index > size.length) return false; // end of the image
// load local counters for next access
if (++x >= size.width) {
x = 0;
y++;
}
return true; // there are more pixels to be had
}
}
public void schedule(Fractal fractal, BufferedImage screen) {
for(Ref ref = new Ref(); next(ref);)
screen.setRGB(ref.x, ref.y, fractal.getPixelColor(ref));
}
}
class Ref {
public int x, y, index;
public Ref() {}
}
I'm trying to make a honeycomb flow with buttons in JavaFX with so far a FlowPane
So far I got it half working by using a negative VGap on my FlowPane, but as soon as I resize it and make the 3-4 row go to 3-3-1 it obviously goes wrong
I'm trying to find a solution that will keep the honeycomb layout with variable amounts of buttons, but so far I havent had much success. So I was wondering if anyone knows a solution for this.
edit: here is the basic code I have at the moment
FlowPane root = new FlowPane();
root.setHgap(3.0); root.setVgap(-23.0);
root.setAlignment(Pos.CENTER);
Button[] array = new Button[7];
for(int i = 0; i <= 6; i++){
Button button = new Button();
button.setShape(polygon); (made a polygon in the same of a hexagon)
array[i] = button;
}
root.getChildren().addAll(array);
Predetermined columns/ rows
This is a bit more convenient than using a FlowPane to place the fields.
You can observe that using column spans you can place the Buttons in a GridPane: Every field fills 2 columns of sqrt(3/4) times the field height; the odd/even rows start at column 0/1 respectively. Every field fills 3 rows and the size of the column constraints alternate between one quarter and one half of the field height.
Example
public static GridPane createHoneyComb(int rows, int columns, double size) {
double[] points = new double[12];
for (int i = 0; i < 12; i += 2) {
double angle = Math.PI * (0.5 + i / 6d);
points[i] = Math.cos(angle);
points[i + 1] = Math.sin(angle);
}
Polygon polygon = new Polygon(points);
GridPane result = new GridPane();
RowConstraints rc1 = new RowConstraints(size / 4);
rc1.setFillHeight(true);
RowConstraints rc2 = new RowConstraints(size / 2);
rc2.setFillHeight(true);
double width = Math.sqrt(0.75) * size;
ColumnConstraints cc = new ColumnConstraints(width/2);
cc.setFillWidth(true);
for (int i = 0; i < columns; i++) {
result.getColumnConstraints().addAll(cc, cc);
}
for (int r = 0; r < rows; r++) {
result.getRowConstraints().addAll(rc1, rc2);
int offset = r % 2;
int count = columns - offset;
for (int c = 0; c < count; c++) {
Button b = new Button();
b.setPrefSize(width, size);
b.setShape(polygon);
result.add(b, 2 * c + offset, 2 * r, 2, 3);
}
}
result.getRowConstraints().add(rc1);
return result;
}
FlowPane-like behavior
Making the x position depend on the row the child is added is not a good idea in a FlowPane. Instead I recommend extending Pane and overriding layoutChildren method place the children at custom positions.
In your case the following class could be used:
public class OffsetPane extends Pane {
public interface PositionFunction {
public Point2D getNextPosition(int index, double x, double y, double width, double height);
}
private static final PositionFunction DEFAULT_FUNCTION = new PositionFunction() {
#Override
public Point2D getNextPosition(int index, double x, double y, double width, double height) {
return new Point2D(x, y);
}
};
private final ObjectProperty<PositionFunction> hPositionFunction;
private final ObjectProperty<PositionFunction> vPositionFunction;
private ObjectProperty<PositionFunction> createPosProperty(String name) {
return new SimpleObjectProperty<PositionFunction>(this, name, DEFAULT_FUNCTION) {
#Override
public void set(PositionFunction newValue) {
if (newValue == null) {
throw new IllegalArgumentException();
} else if (get() != newValue) {
super.set(newValue);
requestLayout();
}
}
};
}
public OffsetPane() {
this.hPositionFunction = createPosProperty("hPositionFunction");
this.vPositionFunction = createPosProperty("vPositionFunction");
}
#Override
protected void layoutChildren() {
super.layoutChildren();
double width = getWidth();
List<Node> children = getManagedChildren();
final int childSize = children.size();
if (childSize > 0) {
int row = 0;
Node lastRowStart = children.get(0);
Node lastNode = lastRowStart;
lastRowStart.relocate(0, 0);
PositionFunction hFunc = getHPositionFunction();
PositionFunction vFunc = getVPositionFunction();
int index = 1;
int columnIndex = 0;
while (index < childSize) {
Node child = children.get(index);
Bounds lastBounds = lastNode.getLayoutBounds();
Bounds bounds = child.getLayoutBounds();
Point2D pt = hFunc.getNextPosition(columnIndex, lastNode.getLayoutX(), lastNode.getLayoutY(), lastBounds.getWidth(), lastBounds.getHeight());
if (pt.getX() + bounds.getWidth() > width) {
// break row
lastBounds = lastRowStart.getLayoutBounds();
pt = vFunc.getNextPosition(row, lastRowStart.getLayoutX(), lastRowStart.getLayoutY(), lastBounds.getWidth(), lastBounds.getHeight());
child.relocate(pt.getX(), pt.getY());
lastRowStart = child;
row++;
columnIndex = 0;
} else {
child.relocate(pt.getX(), pt.getY());
columnIndex++;
}
lastNode = child;
index++;
}
}
}
public final PositionFunction getHPositionFunction() {
return this.hPositionFunction.get();
}
public final void setHPositionFunction(PositionFunction value) {
this.hPositionFunction.set(value);
}
public final ObjectProperty<PositionFunction> hPositionFunctionProperty() {
return this.hPositionFunction;
}
public final PositionFunction getVPositionFunction() {
return this.vPositionFunction.get();
}
public final void setVPositionFunction(PositionFunction value) {
this.vPositionFunction.set(value);
}
public final ObjectProperty<PositionFunction> vPositionFunctionProperty() {
return this.vPositionFunction;
}
}
double[] points = new double[12];
for (int i = 0; i < 12; i += 2) {
double angle = Math.PI * (0.5 + i / 6d);
points[i] = Math.cos(angle);
points[i + 1] = Math.sin(angle);
}
Polygon polygon = new Polygon(points);
OffsetPane op = new OffsetPane();
double fieldHeight = 100;
double fieldWidth = Math.sqrt(0.75) * fieldHeight;
for (int i = 0; i < 23; i++) {
Button button = new Button();
button.setShape(polygon);
button.setPrefSize(fieldWidth, fieldHeight);
op.getChildren().add(button);
}
// horizontal placement just right of the last element
op.setHPositionFunction((int index, double x, double y, double width, double height) -> new Point2D(x + width, y));
// vertical position half the size left/right depending on index and
// 1/4 the node height above the bottom of the last node
op.setVPositionFunction((int index, double x, double y, double width, double height) -> new Point2D(x + (index % 2 == 0 ? width : -width) / 2, y + height * 0.75));
I'm trying make fonts for a game but whenever I load it I get this error:
Exception in thread "main" java.lang.NullPointerException
at image.ImageFont.load(ImageFont.java:78)
at image.ImageFont.<init>(ImageFont.java:47)
at image.ImageFontTest.init(ImageFontTest.java:26)
at image.GameCore.run(GameCore.java:65)
at image.ImageFontTest.main(ImageFontTest.java:11)
I also have the files for it:
ImageFontTest
public class ImageFontTest extends GameCore {
public static void main(String[] args) {
new ImageFontTest().run();
}
private static final long TOTAL_TIME = 6500;
private ImageFont bigFont;
private ImageFont medFont;
private long remainingTime;
private CharMovement[] charMovement;
public void init() {
super.init();
remainingTime = TOTAL_TIME;
// load image fonts
bigFont = new ImageFont("fonts/big");
medFont = new ImageFont("fonts/medium");
String message = "Good Times!";
int stringWidth = medFont.stringWidth(message);
charMovement = new CharMovement[message.length()];
for (int i=0; i<message.length(); i++) {
charMovement[i] = new CharMovement(message, i,
(screen.getWidth() - stringWidth) / 2,
screen.getHeight() / 2);
}
}
public void update(long elapsedTime) {
remainingTime -= elapsedTime;
if (remainingTime <= 0) {
stop();
}
}
public void draw(Graphics2D g) {
// erase background
g.setColor(Color.BLACK);
g.fillRect(0,0,screen.getWidth(), screen.getHeight());
// draw some aligned text
medFont.drawString(g, "Left", 0, 0,
ImageFont.LEFT | ImageFont.TOP);
medFont.drawString(g, "Center", screen.getWidth()/2, 0,
ImageFont.HCENTER | ImageFont.TOP);
medFont.drawString(g, "Right", screen.getWidth(), 0,
ImageFont.RIGHT | ImageFont.TOP);
// draw seconds remaining
String timeLeft = "" + (remainingTime / 1000);
bigFont.drawString(g, timeLeft, 0, screen.getHeight());
// draw moving characters
double p = (double)(TOTAL_TIME - remainingTime) / TOTAL_TIME;
for (int i=0; i<charMovement.length; i++) {
charMovement[i].draw(g, p);
}
}
/**
Simple animation class to animate a character along a
path.
*/
public class CharMovement {
char ch;
Point[] path;
public CharMovement(String s, int charIndex, int x, int y) {
int stringWidth = medFont.stringWidth(s);
for (int i=0; i<charIndex; i++) {
x+=medFont.charWidth(s.charAt(i));
}
ch = s.charAt(charIndex);
path = new Point[4];
// start offscreen
path[0] = new Point(x-2000, y);
// move to the center of the screen and pause there
path[1] = new Point(x, y);
path[2] = path[1];
// "explode" at a random angle far away
double angle = Math.random() * 2 * Math.PI;
double distance = 1000 + 1000*Math.random();
path[3] = new Point(
(int)Math.round(x + Math.cos(angle) * distance),
(int)Math.round(y + Math.sin(angle) * distance));
}
/**
Draws this character in the path, where p is the
location in the path from 0 to 1.
*/
public void draw(Graphics g, double p) {
int points = path.length - 1;
int index = (int)(p*points);
p = p * points - index;
Point start = path[index % path.length];
Point goal = path[(index + 1) % path.length];
int x = (int)Math.round(goal.x * p + start.x * (1-p));
int y = (int)Math.round(goal.y * p + start.y * (1-p));
medFont.drawChar(g, ch, x, y);
}
}
}
ImageFont
/**
The ImageFont class allows loading and drawing of text
using images for the characters.
Reads all the png images in a directory in the form
"charXX.png" where "XX" is a decimal unicode value.
Characters can have different widths and heights.
*/
public class ImageFont {
public static final int HCENTER = 1;
public static final int VCENTER = 2;
public static final int LEFT = 4;
public static final int RIGHT = 8;
public static final int TOP = 16;
public static final int BOTTOM = 32;
private char firstChar;
private Image[] characters;
private Image invalidCharacter;
/**
Creates a new ImageFont with no characters.
*/
public ImageFont() {
this(null);
firstChar = 0;
characters = new Image[0];
}
/**
Creates a new ImageFont and loads character images from
the specified path.
*/
public ImageFont(String path) {
if (path != null) {
load(path);
}
// make the character used for invalid characters
invalidCharacter =
new BufferedImage(10, 10, BufferedImage.TYPE_INT_RGB);
Graphics g = invalidCharacter.getGraphics();
g.setColor(Color.RED);
g.fillRect(0,0,10,10);
g.dispose();
}
/**
Loads the image files for each character from the
specified path. For example, if "../fonts/large"
is the path, this method searches for all the images
names "charXX.png" in that path, where "XX" is a
decimal unicode value. Not every character image needs
to exist; you can only do numbers or uppercase letters,
for example.
*/
public void load(String path) throws NumberFormatException {
// in this directory:
// load every png file that starts with 'char'
File dir = new File(path);
File[] files = dir.listFiles();
// find min and max chars
char minChar = Character.MAX_VALUE;
char maxChar = Character.MIN_VALUE;
for (int i=0; i<files.length; i++) {
int unicodeValue = getUnicodeValue(files[i]);
if (unicodeValue != -1) {
minChar = (char)Math.min(minChar, unicodeValue);
maxChar = (char)Math.max(maxChar, unicodeValue);
}
}
// load the images
if (minChar < maxChar) {
firstChar = minChar;
characters = new Image[maxChar - minChar + 1];
for (int i=0; i<files.length; i++) {
int unicodeValue = getUnicodeValue(files[i]);
if (unicodeValue != -1) {
int index = unicodeValue - firstChar;
characters[index] = new ImageIcon(
files[i].getAbsolutePath()).getImage();
}
}
}
}
private int getUnicodeValue(File file)
throws NumberFormatException
{
String name = file.getName().toLowerCase();
if (name.startsWith("char") && name.endsWith(".png")) {
String unicodeString =
name.substring(4, name.length() - 4);
return Integer.parseInt(unicodeString);
}
return -1;
}
/**
Gets the image for a specific character. If no image for
the character exists, a special "invalid" character image
is returned.
*/
public Image getImage(char ch) {
int index = ch - firstChar;
if (index < 0 || index >= characters.length ||
characters[index] == null)
{
return invalidCharacter;
}
else {
return characters[index];
}
}
/**
Gets the string width, in pixels, for the specified string.
*/
public int stringWidth(String s) {
int width = 0;
for (int i=0; i<s.length(); i++) {
width += charWidth(s.charAt(i));
}
return width;
}
/**
Gets the char width, in pixels, for the specified char.
*/
public int charWidth(char ch) {
return getImage(ch).getWidth(null);
}
/**
Gets the char height, in pixels, for the specified char.
*/
public int charHeight(char ch) {
return getImage(ch).getHeight(null);
}
/**
Draws the specified string at the (x, y) location.
*/
public void drawString(Graphics g, String s, int x, int y) {
drawString(g, s, x, y, LEFT | BOTTOM);
}
/**
Draws the specified string at the (x, y) location.
*/
public void drawString(Graphics g, String s, int x, int y,
int anchor)
{
if ((anchor & HCENTER) != 0) {
x-=stringWidth(s) / 2;
}
else if ((anchor & RIGHT) != 0) {
x-=stringWidth(s);
}
// clear horizontal flags for char drawing
anchor &= ~HCENTER;
anchor &= ~RIGHT;
// draw the characters
for (int i=0; i<s.length(); i++) {
drawChar(g, s.charAt(i), x, y, anchor);
x+=charWidth(s.charAt(i));
}
}
/**
Draws the specified character at the (x, y) location.
*/
public void drawChar(Graphics g, char ch, int x, int y) {
drawChar(g, ch, x, y, LEFT | BOTTOM);
}
/**
Draws the specified character at the (x, y) location.
*/
public void drawChar(Graphics g, char ch, int x, int y,
int anchor)
{
if ((anchor & HCENTER) != 0) {
x-=charWidth(ch) / 2;
}
else if ((anchor & RIGHT) != 0) {
x-=charWidth(ch);
}
if ((anchor & VCENTER) != 0) {
y-=charHeight(ch) / 2;
}
else if ((anchor & BOTTOM) != 0) {
y-=charHeight(ch);
}
g.drawImage(getImage(ch), x, y, null);
}
}
I see it says the problem is at load in ImageFont but I dont know how to fix it.
Exception in thread "main" java.lang.NullPointerException
at image.ImageFont.load(ImageFont.java:78)
You have a NPE (Null Pointer Exception): this means that you have used a variable or an object which has not been initialized yet.
Example:
Button b;
b.setText("Text in my button"); // NULL POINTER EXCEPTION: b is a button but which button?!?
Solution
Button b = new Button();
b.setText("Text in my button"); // b now is a brand new button and it can be used.
You sir have this kind of problem :)
I've searched around quite a bit for ways to do this and I finally decided to just write text by loading it from a bitmap image. This is my first go at loading separate regions of an image and using a bitmap, so I know I have some mistakes in my algorithm. (Some major ones I would bet) but I did it the best way I could think of how. IF someone would tell me what I'm doing wrong and point me in the right direction to figuring this out that would be great. From what I can tell when I send the information into the render it is correct, I get the right character and the right x,y position to translate it too, and the texture is loading just fine as well. I just can't seem to figure out why I'm not getting the right part of the image for the letter, nothing is showing up.
Here is my code.
public class StringText {
private ArrayList<TextChar> fontChar;
private final int CHAR_SIZE = 8;
private final int MARGINE = 1;
private Texture fontTexture;
private String lower;
private String upper;
private String symb;
private final int IMAGE_SIZE = 256;
public StringText(){
lower = "abcdefghijklmnopqrstuvwxyz";
upper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
symb = ".,?:*!+-() ";
fontChar = new ArrayList<TextChar>();
readInFont("font");
populateCheck();
}
public void drawText(String ts, int x, int y, float red, float green, float blue){
for(int a = 0; a < ts.length(); a++){
for(int b = 0; b < fontChar.size(); b++)
if(fontChar.get(b).getChar == ts.charAt(a))
render(fontChar.get(b),a, x, y, red, green, blue);
}
}
private void populateCheck(){
int charX = 0;
int charY = 0;
for(int x = 0; x < lower.length(); x++){
TextChar t = new TextChar(lower.charAt(x), charX, -8);
fontChar.add(t);
charX += CHAR_SIZE;
}
charX = 0;
for(int x = 0; x < upper.length(); x++){
TextChar t = new TextChar (upper.charAt(x), charX, -16);
fontChar.add(t);
charX += CHAR_SIZE;
}
charX = 0;
for(int x = 0; x < symb.length(); x++){
TextChar t = new TextChar(symb.charAt(x), charX, -24);
fontChar.add(t);
charX += CHAR_SIZE;
}
}
private void render(TextChar textChar, int current, int x, int y, float red, float green, float blue){
int inc = current * (CHAR_SIZE + MARGINE);
glLoadIdentity();
glTranslatef(x + inc,y,0f);
glColor3f(red, green, blue);
glScalef(1.0f, -1.0f, 1.0f);
fontTexture.bind();
System.out.println("Letter Position: " + (x+inc) + ", " + y + " Character: " + textChar.getChar);
float x0 = textChar.x/IMAGE_SIZE;
float y0 = textChar.y/IMAGE_SIZE;
float x1 = (textChar.x + CHAR_SIZE)/IMAGE_SIZE;
float y1 = (textChar.y - CHAR_SIZE)/IMAGE_SIZE;
glBegin(GL_QUADS);
glTexCoord2f( x0, -y0); glVertex2f(0,0);
glTexCoord2f(x1,-y0); glVertex2f(IMAGE_SIZE,0);
glTexCoord2f(x1,-y1); glVertex2f(IMAGE_SIZE, IMAGE_SIZE);
glTexCoord2f(x0,-y1); glVertex2f(0,IMAGE_SIZE);
glEnd();
}
private void readInFont(String s){
try {
fontTexture = TextureLoader.getTexture("PNG", new FileInputStream(new File("res/font/"+ s +".png")));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private class TextChar{
int x,y;
char getChar;
public TextChar(char s, int xa, int ya){
x = xa;
y = ya;
getChar = s;
}
}
}
I figured out a better way to do it than this by using an ASCII bitmap.