diff --git a/PostWindow.java b/PostWindow.java index b59c25e..d9b2ef8 100644 --- a/PostWindow.java +++ b/PostWindow.java @@ -29,6 +29,7 @@ import java.awt.Image; import java.awt.Component; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; +import java.awt.MediaTracker; import java.util.List; import java.util.ArrayList; import java.net.URL; @@ -428,7 +429,49 @@ implements ActionListener { setAuthorId(String n) { authorId.setText(n); } public void - setAuthorAvatar(Image n) { profile.setImage(n); } + setAuthorAvatar(Image n) + { + try { + MediaTracker mt = new MediaTracker(this); + mt.addImage(n, 0); + mt.waitForID(0); + } + catch (InterruptedException eIt) { + assert false; + } + /* + * Blocks until the image loads. We have to do this because + * there is an unfixed JDK bug in Image#getScaledInstance + * (which RoundButton uses) where when you pass it an AWT + * image, it incorrectly sets the ColorModel's transfer type, + * causing a ClassCastException, and returns a valid image + * with blank data. It seems using loaded images does not + * replicate the issue, which would explain why the bug + * hasn't been found and fixed.. + * + * For future readers wondering. JDK 11, up to JDK 15. + * java.awt.image.ColorModel#getAlpha(:845), who was called + * sun.awt.image.ImageRepresentation#convertToRGB, who was + * called by AreaAveragingScaleFilter trying to #setPixels. + * Cause RoundButton was using Image.SCALE_SMOOTH. + * + * I guess it kind of makes sense, why would I try to get a + * scaled instance of an image that hadn't finished loading. + * An intelligent class would render the awt.Image as-is until + * it has completely loaded, then call #getScaledInstance + * instead. I won't have RoundButton do that for now, as + * my other classes like RichTextPane3 might have this too. + * + * If you asked me how I'd generalise the solution. My + * expectation had been that #getScaledInstance waits for + * the image too, returning a null image until it's done. + * So I'd put a #getScaledInstance in ImageApi, which does + * this, using MediaTracker. That assumes what I'm doing + * right now definitely works. + */ + + profile.setImage(n); + } public void setDate(String n) { date.setText(n); } diff --git a/RichTextPane3.java b/RichTextPane3.java index 1ea7af3..9dfcd9a 100644 --- a/RichTextPane3.java +++ b/RichTextPane3.java @@ -39,7 +39,7 @@ implements layoutEnd, selStart, selEnd; private int - startingLine, endingLine; + startingLine, lastLine; // ---%-@-%--- @@ -77,7 +77,7 @@ implements startingLine = 1; layout(html, fm, cursor); layout.put(layoutEnd, cursor.clone()); - endingLine = cursor.line; + lastLine = cursor.line; repaint(); int iy = fm.getAscent(); @@ -108,7 +108,7 @@ implements nextPage() { int advance = getHeightInLines(); - if (endingLine - startingLine < advance) return; + if (lastLine - startingLine < advance) return; else startingLine += advance; repaint(); } @@ -557,7 +557,7 @@ implements FontMetrics fm = getFontMetrics(getFont()); int initial = fm.getAscent(); int advance = fm.getAscent() + fm.getDescent(); - return isnap2(getHeight(), initial, advance); + return isnap2(getHeight(), initial, advance) - 1; } public void diff --git a/TwoToggleButton.java b/TwoToggleButton.java index 923bda8..3906675 100644 --- a/TwoToggleButton.java +++ b/TwoToggleButton.java @@ -298,8 +298,6 @@ implements KeyListener, MouseListener, FocusListener { int ow = image.getWidth(this); int oh = image.getHeight(this); - if (ow == -1) return; - int nx = 6; int ny = 6; int nw = button.getWidth(this) - 12; @@ -312,6 +310,8 @@ implements KeyListener, MouseListener, FocusListener { { int sw = ow > oh ? -1 : nw; int sh = oh > ow ? -1 : nh; + // We do not defend against unloaded images. There is + // a bug on those anyways, see comment in PostWindow. scaled = image.getScaledInstance( sw, sh, Image.SCALE_SMOOTH