My app features an InfiniteScrollAdapter populated with images through URLImage and URLImage.ImageAdapter.
In the simulator (Iphone3GS or Xoom or GoogleNexus7), and NPE is shown the first time the InfiniteScrollAdapter appears, although the file does exist on the server.
Please note : In this test there was only one entry in the database. So on the image below what you should see is the same row (image + text) duplicated 3 times.
Please note that the order in the undisplayed icon can differ
The code I used to download the image is :
Image tempPlaceholder = Image.createImage(
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.accentColor);
Graphics gr = tempPlaceholder.getGraphics();
gr.setAntiAliased(true);
gr.setColor(ParametresGeneraux.accentColor);
gr.fillArc(0, 0, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 0, 360);
EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true);
final Image reportImage = URLImage.createToStorage(
roundPlaceholder,
photoFilenameInStorage,
currentReport.getPhotoPath(),
ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK
);
And here is the overridden imageAdapter method :
public final static URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() {
#Override
public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) {
final Image[] tmp = new Image[1];
if (!Display.getInstance().isEdt()) {
// The image scaling has to be called from EDT
Display.getInstance().callSeriallyAndWait(() -> {
tmp[0] = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight());
if (tmp[0].getWidth() > placeholderImage.getWidth()) {
int diff = tmp[0].getWidth() - placeholderImage.getWidth();
int x = diff / 2;
tmp[0] = tmp[0].subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true);
} else if (tmp[0].getHeight() > placeholderImage.getHeight()) {
int diff = tmp[0].getHeight() - placeholderImage.getHeight();
int y = diff / 2;
tmp[0] = tmp[0].subImage(0, y, Math.min(placeholderImage.getWidth(), tmp[0].getWidth()),
Math.min(placeholderImage.getHeight(), tmp[0].getHeight()), true);
}
});
} else {
tmp[0] = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight());
if (tmp[0].getWidth() > placeholderImage.getWidth()) {
int diff = tmp[0].getWidth() - placeholderImage.getWidth();
int x = diff / 2;
tmp[0] = tmp[0].subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true);
} else if (tmp[0].getHeight() > placeholderImage.getHeight()) {
int diff = tmp[0].getHeight() - placeholderImage.getHeight();
int y = diff / 2;
tmp[0] = tmp[0].subImage(0, y, Math.min(placeholderImage.getWidth(), tmp[0].getWidth()),
Math.min(placeholderImage.getHeight(), tmp[0].getHeight()), true);
}
}
EncodedImage[] image2Return = new EncodedImage[1];
if (!Display.getInstance().isEdt()) {
// The image scaling has to be called from EDT
Display.getInstance().callSeriallyAndWait(() -> {
Image roundMask = Image.createImage(tmp[0].getWidth(), tmp[0].getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, tmp[0].getWidth(), tmp[0].getHeight(), 0, 360);
Object mask = roundMask.createMask();
tmp[0] = tmp[0].applyMask(mask);
image2Return[0] = EncodedImage.createFromImage(tmp[0], false);
});
} else {
Image roundMask = Image.createImage(tmp[0].getWidth(), tmp[0].getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, tmp[0].getWidth(), tmp[0].getHeight(), 0, 360);
Object mask = roundMask.createMask();
tmp[0] = tmp[0].applyMask(mask);
image2Return[0] = EncodedImage.createFromImage(tmp[0], false);
}
return image2Return[0];
}
In the stacktrace, the NPE seems to stem from the overridden URLImage.ImageAdapter :
java.lang.IllegalArgumentException: create image failed for the given
image data of length: 0 at
com.codename1.ui.Image.createImage(Image.java:654) at
com.codename1.ui.EncodedImage.getInternal(EncodedImage.java:365) at
com.codename1.ui.EncodedImage.getInternalImpl(EncodedImage.java:340)
at com.codename1.ui.EncodedImage.getHeight(EncodedImage.java:522) at
com.codename1.ui.Image.scaledLargerRatio(Image.java:899) at
com.my.application.ParametresGeneraux$1.lambda$adaptImage$0(ParametresGeneraux.java:564)
at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:95) at
com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1207) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1244) at
com.codename1.ui.URLImage$DownloadCompleted.actionPerformed(URLImage.java:233)
at com.codename1.ui.URLImage$4.onSucess(URLImage.java:301) at
com.codename1.ui.URLImage$4.onSucess(URLImage.java:297) at
com.codename1.util.CallbackDispatcher.run(CallbackDispatcher.java:53)
at com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.mainEDTLoop(Display.java:999) at
com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120) at
com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
[EDT] 0:0:0,1 - Codename One revisions:
e5c43877074c18b4b5c7748d000e5cfac75ab749 2318
[EDT] 0:0:0,1 - Exception: java.lang.NullPointerException - null
java.lang.NullPointerException at
com.codename1.impl.javase.JavaSEPort.scale(JavaSEPort.java:3996) at
com.codename1.ui.Image.scale(Image.java:1007) at
com.codename1.ui.Image.scaledImpl(Image.java:953) at
com.codename1.ui.Image.scaled(Image.java:918) at
com.codename1.impl.javase.JavaSEPort$71.save(JavaSEPort.java:7659) at
com.codename1.ui.EncodedImage.scaledEncoded(EncodedImage.java:626) at
com.codename1.ui.EncodedImage.scaled(EncodedImage.java:653) at
com.codename1.ui.Image.scaledLargerRatio(Image.java:904) at
com.my.application.ParametresGeneraux$1.lambda$adaptImage$0(ParametresGeneraux.java:564)
at com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:95) at
com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1207) at
com.codename1.ui.Display.invokeAndBlock(Display.java:1244) at
com.codename1.ui.URLImage$DownloadCompleted.actionPerformed(URLImage.java:233)
at com.codename1.ui.URLImage$4.onSucess(URLImage.java:301) at
com.codename1.ui.URLImage$4.onSucess(URLImage.java:297) at
com.codename1.util.CallbackDispatcher.run(CallbackDispatcher.java:53)
at com.codename1.ui.Display.processSerialCalls(Display.java:1154) at
com.codename1.ui.Display.edtLoopImpl(Display.java:1098) at
com.codename1.ui.Display.mainEDTLoop(Display.java:999) at
com.codename1.ui.RunnableWrapper.run(RunnableWrapper.java:120) at
com.codename1.impl.CodenameOneThread.run(CodenameOneThread.java:176)
Moreover, a glance in the .cn1 directory shows the URLImage storage file name with the suffix "ImageURLTMP" which does not appear when everything works without NPE.
Finally, if I come back to this form later, everything works as expected (images were shown, no NPE). I tried to test for downloadedImage nullness in imageAdapter but the EncodedImage is not null.
How can I avoid this NPE?
Edit March 1st 2017
Following the answers from #Diamond and #Shai, I believe the NPE occurs because the InfiniteScrollAdapter wants to fill in the screen with rows and consequently launches the download of the same image simultaneously (because it is not in cache). So a solution could be to prevent the InfiniteScrollAdapter to loop (so it becomes finite). How can I do that ?
Please also note that there is not 404 error, the Network monitor shows response code 200 as depicted below. However the image should not be downloaded 3 times, should it ?
Change your ImageAdapter to the following:
public static final URLImage.ImageAdapter RESIZE_SCALE_WITH_ROUND_MASK = new URLImage.ImageAdapter() {
#Override
public EncodedImage adaptImage(EncodedImage downloadedImage, EncodedImage placeholderImage) {
Image tmp = downloadedImage.scaledLargerRatio(placeholderImage.getWidth(), placeholderImage.getHeight());
if (tmp.getWidth() > placeholderImage.getWidth()) {
int diff = tmp.getWidth() - placeholderImage.getWidth();
int x = diff / 2;
tmp = tmp.subImage(x, 0, placeholderImage.getWidth(), placeholderImage.getHeight(), true);
} else if (tmp.getHeight() > placeholderImage.getHeight()) {
int diff = tmp.getHeight() - placeholderImage.getHeight();
int y = diff / 2;
tmp = tmp.subImage(0, y, Math.min(placeholderImage.getWidth(), tmp.getWidth()),
Math.min(placeholderImage.getHeight(), tmp.getHeight()), true);
}
Image roundMask = Image.createImage(tmp.getWidth(), tmp.getHeight(), 0xff000000);
Graphics gr = roundMask.getGraphics();
gr.setColor(0xffffff);
gr.fillArc(0, 0, tmp.getWidth(), tmp.getHeight(), 0, 360);
Object mask = roundMask.createMask();
tmp = tmp.applyMask(mask);
return EncodedImage.createFromImage(tmp, false);
}
#Override
public boolean isAsyncAdapter() {
return true;
}
};
No need to check EDT.
Make sure your tempPlaceholder image is applied to your component first and at the end of your logic, call your URLImage in a callSerially() method:
Image tempPlaceholder = Image.createImage(
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX,
ParametresGeneraux.accentColor);
Graphics gr = tempPlaceholder.getGraphics();
gr.setAntiAliased(true);
gr.setColor(ParametresGeneraux.accentColor);
gr.fillArc(0, 0, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, ParametresGeneraux.SIZE_OF_REPORT_PIC_IN_PX, 0, 360);
myComponent.setIcon(tempPlaceholder);
...
//Then call this at the end of everything
Display.getInstance().callSerially(() -> {
EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true);
final Image reportImage = URLImage.createToStorage(
roundPlaceholder,
photoFilenameInStorage,
currentReport.getPhotoPath(),
ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK
);
myComponent.setIcon(reportImage);
myComponent.getComponentForm().repaint();
});
Edit:
Based on #Shai's answer, you could check if you are currently downloading the same image and prevent another one from being pulled. Because this usually causes a conflict:
//Declare this at the top of your class
final static private Map<String, Image> LOADED_URLS = new HashMap<>();
//Then change the URLImage image method to this
Display.getInstance().callSerially(() -> {
EncodedImage roundPlaceholder = EncodedImage.createFromImage(tempPlaceholder, true);
final Image reportImage = LOADED_URLS.containsKey(photoFilenameInStorage) ? LOADED_URLS.get(photoFilenameInStorage)
: URLImage.createToStorage(
roundPlaceholder,
photoFilenameInStorage,
currentReport.getPhotoPath(),
ParametresGeneraux.RESIZE_SCALE_WITH_ROUND_MASK
);
LOADED_URLS.put(photoFilenameInStorage, reportImage);
myComponent.setIcon(reportImage);
myComponent.getComponentForm().repaint();
});
In your adapter check if downloadedImage.getData() is null. I assume it's not and it's a 404 error page or something similar to that.
In that case your adapter can catch the exception and just return a fallback that matches what you expect to see when no image exists.
This works the second time around since the system sees the tmp file and assumes a download is in progress so it doesn't invoke the download code again. The tmp file is later renamed to the final downloadable file.
Related
I am trying to set the registration point of an image to the bottom centre so it can rotate around that point. So far my code looks like this:
var img = new createjs.Bitmap("img.png");
img.x = 200;
img.y = 180;
img.scaleX = 0.35;
img.scaleY = 0.35;
img.regX = img.width/2;
img.regY = 0;
img.rotation = 15;
canvas.addChild(img);
I tried changing the numbers of img.regY but I can't seem to get it correct.
What is redBalloon.width?
The best approach is to wait until the image is loaded, and then use its natural width.
img.image.onload = function() {
img.regX = img.image.naturalWidth;
img.regY = img.image.naturalHeight;
}
Hope that helps.
If I set leadcomponent method, all the btns returns same name but I set different name for each button. If I comment out leadcomponent line, it works fine. What could have happened? I setName to all the buttons so that I can apply action listener and open different form according to the name returned.
My code:
BorderLayout gl1 = new BorderLayout();
Container middleContainer = new Container(gl1);
parentContiner.addComponent(middleContainer);
GridLayout gl2 = new GridLayout(counter / 3 + 1, 3);
gl2.setAutoFit(true);
Container gridContainer = new Container(gl2);
parentContiner.addComponent(gridContainer);
for (HashMap<String, Object> entry : homeStorage) {
if (!"".equals(entry.get("img").toString())) {
counter++;
Button homeButton = new Button();
id = entry.get("id").toString();
imageUrl = entry.get("img").toString();
title = entry.get("name").toString();
homePlaceholder = homePlaceholder.scaled(screenWidth / 3 - 17, screenWidth / 3 - 17);
encodedHomePlaceholder = EncodedImage.createFromImage(homePlaceholder, true);
Image btnIcon = URLImage.createToStorage(encodedHomePlaceholder, "home_" + title + imageUrl, allUrl.globalHomeImageUrl + imageUrl, URLImage.RESIZE_SCALE_TO_FILL);
homeButton.setIcon(btnIcon);
***//set the name of button to id***
homeButton.setName(id);
TextArea buttonTitle = new TextArea(properCase(title));
buttonTitle.setEditable(false);
buttonTitle.setGrowByContent(true);
buttonTitle.setGrowLimit(2);
buttonTitle.setScrollVisible(false);
Container containerBtnTitle = new Container(new FlowLayout(Label.RIGHT, Label.BOTTOM));
containerBtnTitle.add(buttonTitle);
gridContainer.add(LayeredLayout.encloseIn(homeButton, containerBtnTitle));
**//if i remove this line(setLeadcomponent) it works (ie returns different name as set above, otherwise all the button returns the same name (ie last id)**
gridContainer.setLeadComponent(homeButton);
gridContainer.revalidate();
}
homeButton.addActionListener((e) -> {
if (homeButton.getName().equals("3")) {
showForm("YoutubeVideos", null);
} else if (homeButton.getName().equals("17")) {
showForm("k2Gallery", null);
} else if (homeButton.getName().equals("15")) {
showForm("EmergencyCategory", null);
} else if (homeButton.getName().equals("2")) {
showForm("PartyClicks", null);
} else if (homeButton.getName().equals("1")) {
showForm("WhereTheParty", null);
} else if (homeButton.getName().equals("5")) {
showForm("CocktailRecipies", null);
}
});
}
Each container can have only 1 lead component.
You are creating a few homeButton but you have 1 gridContainer.
I am trying to render three points using array buffers, but I can't get anything rendered.
This is my code:
import java.nio.*;
import javax.media.opengl.*;
public class SimpleScene implements GLEventListener
{
private GL3 gl;
int myIdentityShader;
#Override
public void display(GLAutoDrawable drawable) {
gl.glClear(GL3.GL_COLOR_BUFFER_BIT);
gl.glUseProgram(myIdentityShader);
gl.glDrawArrays(GL3.GL_POINTS, 0, 3);
}
#Override
public void dispose(GLAutoDrawable drawable) {
gl.glDeleteProgram(myIdentityShader);
gl.glDeleteVertexArrays(1, vertexArrayObject, 0);
}
#Override
public void init(GLAutoDrawable drawable) {
gl = drawable.getGL().getGL3();
try {
myIdentityShader = createShaderProgram();
} catch (Exception e) {
e.printStackTrace();
return;
}
gl.glPointSize(30);
gl.glClearColor(0.7f, 0, 0, 1);
float[] floatData = {
0.25f, -0.25f, 0.5f, 1.0f,
-0.25f, -0.25f, 0.5f, 1.0f,
0.25f, 0.25f, 0.5f, 1.0f
};
FloatBuffer data = FloatBuffer.allocate(3 * 4);
for (int i=0;i<12;i++)
data.put(floatData[i]);
gl.glGenVertexArrays(1, vertexArrayObject, 0);
gl.glBindVertexArray(vertexArrayObject[0]);
int[] buffers = new int[1];
gl.glGenBuffers(1, buffers, 0);
gl.glBindBuffer(GL3.GL_ARRAY_BUFFER, buffers[0]);
gl.glBufferData(GL3.GL_ARRAY_BUFFER, data.capacity(), data, GL3.GL_STATIC_DRAW);
gl.glVertexAttribPointer(2, 4, GL3.GL_FLOAT, false, 0, 0);
gl.glEnableVertexAttribArray(2);
}
#Override
public void reshape(GLAutoDrawable drawable, int arg1, int arg2, int arg3,
int arg4) {
// TODO Add reshape code
}
private String vertexShader = "#version 130 \n" +
"in vec4 position; \n" +
"void main(void) \n" +
"{ \n" +
" gl_Position = position; \n" +
"} \n";
private String fragmentShader = "#version 130 \n" +
"out vec4 vFragColor; \n" +
"void main(void) \n" +
"{ \n" +
" vFragColor = vec4(0.0, 0.8, 1.0, 1.0); \n" +
"} \n";
private int[] vertexArrayObject = new int[1];
private int createShaderProgram() throws Exception
{
int hVertexShader, hFragmentShader, hShaderProgram;
int[] successTest = new int[] {1};
hVertexShader = gl.glCreateShader(GL3.GL_VERTEX_SHADER);
hFragmentShader = gl.glCreateShader(GL3.GL_FRAGMENT_SHADER);
gl.glShaderSource(hVertexShader, 1, new String[] {vertexShader}, null);
gl.glShaderSource(hFragmentShader, 1, new String[] {fragmentShader}, null);
gl.glCompileShader(hVertexShader);
gl.glCompileShader(hFragmentShader);
gl.glGetShaderiv(hVertexShader, GL3.GL_COMPILE_STATUS, successTest, 0);
if (successTest[0] == 0)
{
byte[] infoLog = new byte[1024];
gl.glGetShaderInfoLog(hVertexShader, 1024, null, 0, infoLog, 0);
gl.glDeleteShader(hVertexShader);
gl.glDeleteShader(hFragmentShader);
throw new Exception("Vertex shader compilation failed with: " + new String(infoLog));
}
gl.glGetShaderiv(hFragmentShader, GL3.GL_COMPILE_STATUS, successTest, 0);
if (successTest[0] == 0)
{
byte[] infoLog = new byte[1024];
gl.glGetShaderInfoLog(hFragmentShader, 1024, null, 0, infoLog, 0);
gl.glDeleteShader(hVertexShader);
gl.glDeleteShader(hFragmentShader);
throw new Exception("Fragment shader compilation failed with: " + new String(infoLog));
}
hShaderProgram = gl.glCreateProgram();
gl.glAttachShader(hShaderProgram, hVertexShader);
gl.glAttachShader(hShaderProgram, hFragmentShader);
gl.glBindAttribLocation(hShaderProgram, 2, "position");
gl.glLinkProgram(hShaderProgram);
gl.glGetProgramiv(hShaderProgram, GL3.GL_LINK_STATUS, successTest, 0);
if (successTest[0] == 0)
{
byte[] infoLog = new byte[1024];
gl.glGetProgramInfoLog(hShaderProgram, 1024, null, 0, infoLog, 0);
gl.glDeleteProgram(hShaderProgram);
throw new Exception("Shader linking failed with: " + new String(infoLog);
}
gl.glDeleteShader(hVertexShader);
gl.glDeleteShader(hFragmentShader);
return hShaderProgram;
}
}
I am absolutely sure the shaders and the createShaderProgram method are correct. When I use glVertexAttrib4fv, all three points get rendered in the same location (as expected), but when I use glVertexAttribPointer nothing gets rendered and there’s no error (glGetError returns 0).
I discovered that glVertexAttrib4fv does not send the coordinates if the attribute index is 0, thus resulting in all points being rendered in the center of the screen. This doesn't happen with glVertexAttribPointer, so the coordinates are sent.
You are using Vertex Array Objects inefficiently. The whole point of using a VAO is so that you do not have to set your vertex attrib. pointer and enable / disable the pointers every time you draw something. Set the pointer when you initialize the VAO and since it keeps track of this state, all you have to do is change the bound VAO every time you want to draw something.
However, your actual problem is that you are never binding the vertex attribute position in your vertex shader to generic attribute location 2. Most GLSL implementations will automatically assign that vertex attribute location 0, though this is not required behavior (so do not rely on it). You can either query the location (glGetAttribLocation) of this attribute by name after you link your program, or you can bind the attribute location yourself (glBindAttribLocation). In either case, you must match the attribute location for your pointer to the attribute in the shader.
When I use glVertexAttrib4fv, all three points get rendered in the same location, but when I use glVertexAttribPointer nothing gets rendered and there’s no error (glGetError returns 0).
This is to be expected, glVertexAttrib4fv (...) sets up a constant value to use for EVERY vertex shader invocation that pulls data from that location. While it is true that it takes a pointer (in the C language bindings), that pointer is merely OpenGL's way of passing an array. If you change the value of the data pointed to after the call completes, nothing is going to happen. That is literally why there is a different class of functions to set vertex array pointers.
Update:
After seeing your updated code for this question, the real problem popped up. In Java, when you use FloatBuffer.put (...), it increments the base address of the buffer (which JOGL uses when you call glVertexAttribPointer (...)). You need to flip the buffer by calling FloatBuffer.flip (...), and this will effectively rewind the buffer so that JOGL will read beginning with the first element in the buffer.
I am coding a simple diagram that parses a .tsv file that has 6 columns and 9 rows. I am attempting to put some text on my diagram that is coloured according to the data existing in the third column. I am able to get all the colours on the screen, but for some reason, the text that ends up red is the text corresponding to the row (in the tsv file) BELOW the row whose text I want to be red. For example, while I want the Liberal candidate to have a fill of (200,60,60), the Parti Quebecois candidate appearing in the row below instead becomes red. Following, the New Dem party candidate ends up with the fill of (155,191,219). The code appears as follows:
PImage mapOfCanada; // background map
Premier[] premiers; // premiers data
void setup() {
size(800, 800);
// modified mapOfCanada from http://www.theblog.ca/map-canada
mapOfCanada = loadImage("bigmapofcanada.png");
// from http://en.wikipedia.org/wiki/List_of_current_Canadian_first_ministers
Table table = new Table("premiers.tsv");
int rows = table.getRowCount();
premiers = new Premier[rows];
// read through each row of the source data to populate our premiers array
for (int i=0; i<rows; i++) {
String name = table.getString(i, 0);
String province = table.getString(i, 1);
String party = table.getString(i, 2);
String imgFile = table.getString(i, 3);
PImage img = loadImage(imgFile);
float x = table.getFloat(i,4);
float y = table.getFloat(i,5);
premiers[i] = new Premier(name, province, party, img, x, y);
}
}
void draw() {
background(255);
// draw the background image with a light tint
tint(255, 25);
image(mapOfCanada, 0, 0);
// draw each premier
noTint();
for (Premier premier : premiers) {
image(premier.img, premier.x, premier.y);
}
//drawing lines for those premier images that cannot fit in the alloted province space
line(158,560,145,460); //Alberta
line(300,560,340,500); //Manitoba
line(650,365,670,410); //Newfoundland
line(750,385,710,535); //PEI
line(730,575,720,550); //Nova Scotia
line(670,595,680,560); //New Brunswick
//adding text labels
for (Premier premier : premiers) { //reading through the source data in a loop
textSize(10); //making the text size small yet readable
textAlign(CENTER); //making sure the text is centered above the image
text(premier.name, premier.x+50, premier.y-10); //positioning the text in relation to the x and y coordinates on the source data
{
String string1 = new String("Liberal");
String string2 = new String("Parti Quebecois");
String string3 = new String("New Democratic");
String string4 = new String ("Progressive Conservative");
String string5 = new String ("Saskatchewan Party");
String string6 = new String ("Yukon Party");
if (premier.party.equals("Liberal")) {
fill(200,60,60);
}
else if (premier.party.equals("Parti Quebecois")) {
fill(155,191,219);
}
else if (premier.party.equals("New Democratic")) {
fill(180,151,107);
}
else if(premier.party.equals("Progressive Conservative")) {
fill(96,104,250);
}
else if(premier.party.equals("Saskatchewan Party")) {
fill (107,180,119);
}
else if(premier.party.equals("Yukon Party")) {
fill (47,85,232);
}
else {
fill (0,0,0);
}
}
}
}
class Premier {
String name, province, party;
PImage img; // this is the thumbnail image
float x, y; // these are the coordinates for the thumbnail
Premier(String name, String province, String party, PImage img, float x, float y) {
this.name = name;
this.province = province;
this.party = party;
this.img = img;
this.x = x;
this.y = y;
}
}
Any help re: what I'm doing wrong would be much appreciated! I've edited the post to feature the full code.
Thank you!
Seems like a one off error in the loop that wraps the code that you have posted.
According to the description of an issue you're having, it may be related to indexing error - i.e. using 0-based indexing where it's actually 1-based or vice versa. But without the loop code and fill() code it will be very hard to pinpoint the problem.
In my Swing application, users enter styled text into a JTextPane which uses an RTFEditorKit (HTML is also a possibility).
I then need to render many of these styled notes at specific coordinates in a custom component.
I would think the View.paint method would be helpful here, but I'm not able to create a usable View object.
I have the following method:
public View createView() throws IOException, BadLocationException {
RTFEditorKit kit = new RTFEditorKit();
final Document document = kit.createDefaultDocument();
kit.read(new ByteArrayInputStream(text.getBytes("UTF-8")), document, 0);
return kit.getViewFactory().create(document.getDefaultRootElement());
}
This returns a javax.swing.text.BoxView with the following attributes:
majorAxis = 1
majorSpan = 0
minorSpan = 0
majorReqValid = false
minorReqValid = false
majorRequest = null
minorRequest = null
majorAllocValid = false
majorOffsets = {int[0]#2321}
majorSpans = {int[0]#2322}
minorAllocValid = false
minorOffsets = {int[0]#2323}
minorSpans = {int[0]#2324}
tempRect = {java.awt.Rectangle#2325}"java.awt.Rectangle[x=0,y=0,width=0,height=0]"
children = {javax.swing.text.View[1]#2326}
nchildren = 0
left = 0
right = 0
top = 0
bottom = 0
childAlloc = {java.awt.Rectangle#2327}"java.awt.Rectangle[x=0,y=0,width=0,height=0]"
parent = null
elem = {javax.swing.text.DefaultStyledDocument$SectionElement#2328}"BranchElement(section) 0,35\n"
Note that parent = null and nchildren = 0. This means there's nothing really useful there. I can hack together something by calling JTextPane.getUI().paint, but the text pane needs to be visible, and this feels like the wrong way to do it.
Is there any way to get a visual representation of the RTF content without rendering the actual JTextPane?
This code sort of works, but seems less than ideal. Is there a better way to do it? Also, what's a good way to render the text somewhere other than 0,0 on the graphics?
private static void testRtfRender() {
String s = "{\\rtf1\\ansi\n" +
"{\\fonttbl\\f0\\fnil Monospaced;\\f1\\fnil Lucida Grande;}\n" +
"\n" +
"\\f1\\fs26\\i0\\b0\\cf0 this is a \\b test\\b0\\par\n" +
"}";
JTextPane pane = new JTextPane();
pane.setContentType("text/rtf");
pane.setText(s);
final Dimension preferredSize = pane.getUI().getPreferredSize(pane);
int w = preferredSize.width;
int h = preferredSize.height;
pane.setSize(w, h);
pane.addNotify();
pane.validate();
// would be nice to use this box view instead of instantiating a UI
// however, unless you call setParent() on the view it's useless
// What should the parent of a root element be?
//BoxView view = (BoxView) pane.getEditorKit().getViewFactory().create(pane.getStyledDocument().getDefaultRootElement());
//view.paint(d, new Rectangle(w, h));
BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
final Graphics2D d = img.createGraphics();
d.setClip(0, 0, w, h); // throws a NullPointerException if I leave this out
pane.getUI().paint(d, pane);
d.dispose();
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
}
Check out the ScreenImage class which allows you to create a BufferedImage of any Swing component. It should also work for Swing components that are not visible, but yes you do have to do the rendering first.