mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2025-01-08 22:24:45 +01:00
I think I fixed it.. The image scaling issue
Added prototype for media descriptions
This commit is contained in:
parent
e7aeee7ba9
commit
39cb81d758
@ -130,7 +130,9 @@ ComposeWindow extends JFrame {
|
|||||||
if (a.id != null) continue;
|
if (a.id != null) continue;
|
||||||
// Assume it had already been uploaded.
|
// Assume it had already been uploaded.
|
||||||
|
|
||||||
api.uploadFile(a.uploadee, new RequestListener() {
|
api.uploadFile(
|
||||||
|
a.uploadee, a.description,
|
||||||
|
new RequestListener() {
|
||||||
|
|
||||||
public void
|
public void
|
||||||
connectionFailed(IOException eIo)
|
connectionFailed(IOException eIo)
|
||||||
@ -561,9 +563,6 @@ implements ActionListener {
|
|||||||
private JPanel
|
private JPanel
|
||||||
selections;
|
selections;
|
||||||
|
|
||||||
private ButtonGroup
|
|
||||||
selectionsGroup;
|
|
||||||
|
|
||||||
private JToggleButton
|
private JToggleButton
|
||||||
attachment1,
|
attachment1,
|
||||||
attachment2,
|
attachment2,
|
||||||
@ -621,29 +620,29 @@ implements ActionListener {
|
|||||||
|
|
||||||
if (working.size() > 3)
|
if (working.size() > 3)
|
||||||
{
|
{
|
||||||
attachment4.doClick();
|
|
||||||
Image i = working.get(3).image;
|
Image i = working.get(3).image;
|
||||||
attachment4.setIcon(new ImageIcon(i));
|
attachment4.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
else if (working.size() > 2)
|
else if (working.size() > 2)
|
||||||
{
|
{
|
||||||
attachment3.doClick();
|
|
||||||
Image i = working.get(2).image;
|
Image i = working.get(2).image;
|
||||||
attachment3.setIcon(new ImageIcon(i));
|
attachment3.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
else if (working.size() > 1)
|
else if (working.size() > 1)
|
||||||
{
|
{
|
||||||
attachment2.doClick();
|
|
||||||
Image i = working.get(1).image;
|
Image i = working.get(1).image;
|
||||||
attachment2.setIcon(new ImageIcon(i));
|
attachment2.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
else if (working.size() > 0)
|
else if (working.size() > 0)
|
||||||
{
|
{
|
||||||
attachment1.doClick();
|
|
||||||
Image i = working.get(0).image;
|
Image i = working.get(0).image;
|
||||||
attachment1.setIcon(new ImageIcon(i));
|
attachment1.setIcon(new ImageIcon(i));
|
||||||
}
|
}
|
||||||
else selectionsGroup.clearSelection();
|
|
||||||
|
attachment4.setSelected(working.size() > 3);
|
||||||
|
attachment3.setSelected(working.size() > 2);
|
||||||
|
attachment2.setSelected(working.size() > 1);
|
||||||
|
attachment1.setSelected(working.size() > 0);
|
||||||
|
|
||||||
int bw = sz.width;
|
int bw = sz.width;
|
||||||
int hgap = 4;
|
int hgap = 4;
|
||||||
@ -661,11 +660,6 @@ implements ActionListener {
|
|||||||
{
|
{
|
||||||
Object src = eA.getSource();
|
Object src = eA.getSource();
|
||||||
|
|
||||||
if (false)
|
|
||||||
{
|
|
||||||
// Clicked on filled attachment button.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src == add)
|
if (src == add)
|
||||||
{
|
{
|
||||||
int r = chooser.showOpenDialog(this);
|
int r = chooser.showOpenDialog(this);
|
||||||
@ -697,40 +691,93 @@ implements ActionListener {
|
|||||||
|
|
||||||
if (src == delete)
|
if (src == delete)
|
||||||
{
|
{
|
||||||
Object sm = selectionsGroup.getSelection();
|
if (attachment1.isSelected())
|
||||||
if (sm == attachment1.getModel())
|
|
||||||
{
|
{
|
||||||
assert working.size() > 0;
|
assert working.size() > 0;
|
||||||
working.remove(0);
|
working.remove(0);
|
||||||
updateButtons();
|
|
||||||
}
|
}
|
||||||
if (sm == attachment2.getModel())
|
if (attachment2.isSelected())
|
||||||
{
|
{
|
||||||
assert working.size() > 1;
|
assert working.size() > 1;
|
||||||
working.remove(1);
|
working.remove(1);
|
||||||
updateButtons();
|
|
||||||
}
|
}
|
||||||
if (sm == attachment3.getModel())
|
if (attachment3.isSelected())
|
||||||
{
|
{
|
||||||
assert working.size() > 2;
|
assert working.size() > 2;
|
||||||
working.remove(2);
|
working.remove(2);
|
||||||
updateButtons();
|
|
||||||
}
|
}
|
||||||
if (sm == attachment4.getModel())
|
if (attachment4.isSelected())
|
||||||
{
|
{
|
||||||
assert working.size() > 3;
|
assert working.size() > 3;
|
||||||
working.remove(3);
|
working.remove(3);
|
||||||
updateButtons();
|
|
||||||
}
|
}
|
||||||
|
updateButtons();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src != add && selections.isAncestorOf((Component)src))
|
||||||
|
{
|
||||||
|
int iCurr = getSelectedIndex();
|
||||||
|
int iNext = getIndex(src);
|
||||||
|
System.err.println(iCurr + ":" + iNext);
|
||||||
|
assert iNext != 0;
|
||||||
|
if (iCurr == iNext) {
|
||||||
|
save();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (iCurr != 0) save();
|
||||||
|
load(iNext);
|
||||||
|
}
|
||||||
|
|
||||||
if (src == revert)
|
if (src == revert)
|
||||||
{
|
{
|
||||||
return;
|
assert getSelectedIndex() != 0;
|
||||||
|
// Should the controls be disabled until
|
||||||
|
// there is an attachment?
|
||||||
|
load(getSelectedIndex());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int
|
||||||
|
getIndex(Object src)
|
||||||
|
{
|
||||||
|
if (src == attachment4) return 4;
|
||||||
|
if (src == attachment3) return 3;
|
||||||
|
if (src == attachment2) return 2;
|
||||||
|
if (src == attachment1) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int
|
||||||
|
getSelectedIndex()
|
||||||
|
{
|
||||||
|
if (attachment4.isSelected()) return 4;
|
||||||
|
if (attachment3.isSelected()) return 3;
|
||||||
|
if (attachment2.isSelected()) return 2;
|
||||||
|
if (attachment1.isSelected()) return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
save()
|
||||||
|
{
|
||||||
|
int index = getSelectedIndex();
|
||||||
|
assert index != 0;
|
||||||
|
|
||||||
|
Attachment a = working.get(index - 1);
|
||||||
|
a.description = this.description.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void
|
||||||
|
load(int index)
|
||||||
|
{
|
||||||
|
assert index > 0;
|
||||||
|
assert working.size() >= index;
|
||||||
|
|
||||||
|
Attachment a = working.get(index - 1);
|
||||||
|
this.description.setText(a.description);
|
||||||
|
}
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
AttachmentsComponent(ComposeWindow primaire)
|
AttachmentsComponent(ComposeWindow primaire)
|
||||||
@ -762,17 +809,20 @@ implements ActionListener {
|
|||||||
attachment2.setMargin(add.getMargin());
|
attachment2.setMargin(add.getMargin());
|
||||||
attachment3.setMargin(add.getMargin());
|
attachment3.setMargin(add.getMargin());
|
||||||
attachment4.setMargin(add.getMargin());
|
attachment4.setMargin(add.getMargin());
|
||||||
|
attachment1.addActionListener(this);
|
||||||
|
attachment2.addActionListener(this);
|
||||||
|
attachment3.addActionListener(this);
|
||||||
|
attachment4.addActionListener(this);
|
||||||
|
|
||||||
selections = new JPanel();
|
selections = new JPanel();
|
||||||
selections.setOpaque(false);
|
selections.setOpaque(false);
|
||||||
selections.setLayout(new GridLayout(1, 0, 4, 0));
|
selections.setLayout(new GridLayout(1, 0, 4, 0));
|
||||||
working = new ArrayList<Attachment>();
|
working = new ArrayList<Attachment>();
|
||||||
selectionsGroup = new ButtonGroup();
|
ButtonGroup selectionsGroup = new ButtonGroup();
|
||||||
selectionsGroup.add(attachment1);
|
selectionsGroup.add(attachment1);
|
||||||
selectionsGroup.add(attachment2);
|
selectionsGroup.add(attachment2);
|
||||||
selectionsGroup.add(attachment3);
|
selectionsGroup.add(attachment3);
|
||||||
selectionsGroup.add(attachment4);
|
selectionsGroup.add(attachment4);
|
||||||
// Have to add selection listener to button group
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
|
|
||||||
JButton del = new JButton("D");
|
JButton del = new JButton("D");
|
||||||
|
@ -2,6 +2,11 @@
|
|||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
|
import java.awt.MediaTracker;
|
||||||
|
import java.awt.Graphics;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.image.ImageObserver;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
@ -435,9 +435,10 @@ MastodonApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void
|
public void
|
||||||
uploadFile(File file, RequestListener handler)
|
uploadFile(File file, String alt, RequestListener handler)
|
||||||
{
|
{
|
||||||
assert file != null;
|
assert file != null;
|
||||||
|
assert alt != null;
|
||||||
assert file.canRead();
|
assert file.canRead();
|
||||||
|
|
||||||
String bct =
|
String bct =
|
||||||
@ -496,10 +497,12 @@ MastodonApi {
|
|||||||
String url = instanceUrl + "/api/v1/media/";
|
String url = instanceUrl + "/api/v1/media/";
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
URL endpoint = new URL(url);
|
String s1 = "?description=" + encode(alt);
|
||||||
|
|
||||||
|
URL endpoint = new URL(url + s1);
|
||||||
HttpURLConnection conn = cast(endpoint.openConnection());
|
HttpURLConnection conn = cast(endpoint.openConnection());
|
||||||
String s1 = "Bearer " + token;
|
String s2 = "Bearer " + token;
|
||||||
conn.setRequestProperty("Authorization", s1);
|
conn.setRequestProperty("Authorization", s2);
|
||||||
conn.setDoOutput(true);
|
conn.setDoOutput(true);
|
||||||
conn.setRequestMethod("POST");
|
conn.setRequestMethod("POST");
|
||||||
conn.setFixedLengthStreamingMode(contentLength);
|
conn.setFixedLengthStreamingMode(contentLength);
|
||||||
|
@ -429,54 +429,7 @@ implements ActionListener {
|
|||||||
setAuthorId(String n) { authorId.setText(n); }
|
setAuthorId(String n) { authorId.setText(n); }
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setAuthorAvatar(Image n)
|
setAuthorAvatar(Image n) { profile.setImage(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.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* (悪) It doesn't work. I might be forced to use
|
|
||||||
* BufferedImage with ImageIO. Or have
|
|
||||||
* ImageApi#getScaledInstance spam until it works.
|
|
||||||
*/
|
|
||||||
|
|
||||||
profile.setImage(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setDate(String n) { date.setText(n); }
|
setDate(String n) { date.setText(n); }
|
||||||
|
@ -8,6 +8,8 @@ import java.awt.Graphics;
|
|||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.awt.Shape;
|
import java.awt.Shape;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.geom.Ellipse2D;
|
import java.awt.geom.Ellipse2D;
|
||||||
import java.awt.event.MouseListener;
|
import java.awt.event.MouseListener;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
@ -255,6 +257,7 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
private Image
|
private Image
|
||||||
|
copy,
|
||||||
scaled;
|
scaled;
|
||||||
|
|
||||||
private int
|
private int
|
||||||
@ -267,22 +270,78 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
selectedOverlay,
|
selectedOverlay,
|
||||||
disabledOverlay;
|
disabledOverlay;
|
||||||
|
|
||||||
|
private static Shape
|
||||||
|
roundClip;
|
||||||
|
|
||||||
// ---%-@-%---
|
// ---%-@-%---
|
||||||
|
|
||||||
public void
|
public void
|
||||||
setImage(Image n)
|
setImage(Image n)
|
||||||
{
|
{
|
||||||
image = n;
|
image = n;
|
||||||
|
copy = null;
|
||||||
scaled = null;
|
scaled = null;
|
||||||
|
|
||||||
|
if (image != null)
|
||||||
|
{
|
||||||
|
image.flush();
|
||||||
|
prepareImage(image, this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// - -%- -
|
// - -%- -
|
||||||
|
|
||||||
public boolean
|
public boolean
|
||||||
imageUpdate(Image i, int f, int x, int y, int w, int h)
|
imageUpdate(Image img, int f, int x, int y, int w, int h)
|
||||||
{
|
{
|
||||||
if ((f | WIDTH) != 0) repaint();
|
// AbstractButton overrode this to refuse updates for
|
||||||
return super.imageUpdate(i, f, x, y, w, h);
|
// images that aren't the button's icon. We don't use
|
||||||
|
// the icon, so we're overriding it back. Also, we have
|
||||||
|
// some async work to do regarding the images.
|
||||||
|
|
||||||
|
if ((f & (ABORT|ERROR)) != 0) return false;
|
||||||
|
|
||||||
|
boolean all = (f & ALLBITS) != 0;
|
||||||
|
boolean frame = (f & FRAMEBITS) != 0;
|
||||||
|
boolean some = (f & SOMEBITS) != 0;
|
||||||
|
|
||||||
|
if (frame && img != this.image) return false;
|
||||||
|
|
||||||
|
if (img == this.image && (all || frame))
|
||||||
|
{
|
||||||
|
int ow = img.getWidth(null);
|
||||||
|
int oh = img.getHeight(null);
|
||||||
|
|
||||||
|
if (copy == null)
|
||||||
|
{
|
||||||
|
int type = BufferedImage.TYPE_INT_ARGB;
|
||||||
|
copy = new BufferedImage(ow, oh, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics g = copy.getGraphics();
|
||||||
|
g.drawImage(img, 0, 0, null);
|
||||||
|
g.dispose();
|
||||||
|
|
||||||
|
int algo = Image.SCALE_SMOOTH;
|
||||||
|
Rectangle b = roundClip.getBounds();
|
||||||
|
int sw = ow > oh ? -1 : b.width;
|
||||||
|
int sh = oh > ow ? -1 : b.height;
|
||||||
|
scaled = copy.getScaledInstance(sw, sh, algo);
|
||||||
|
/*
|
||||||
|
* We create a scaled instance from a BufferedImage copy
|
||||||
|
* rather than this.image directly, to avoid a ClassCast
|
||||||
|
* Exception bug in the JDK, where ColorModel was
|
||||||
|
* incorrectly casting an int array of input data into
|
||||||
|
* a byte array. I'm not sure why that bug exists nor
|
||||||
|
* why they haven't noticed it, but.
|
||||||
|
*/
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
if (img == scaled && (some || all))
|
||||||
|
{
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
return all ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void
|
protected void
|
||||||
@ -294,34 +353,13 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
if (isFocusOwner())
|
if (isFocusOwner())
|
||||||
g.drawImage(selectedOverlay, 0, 0, this);
|
g.drawImage(selectedOverlay, 0, 0, this);
|
||||||
|
|
||||||
if (image == null) return;
|
if (scaled == null) return;
|
||||||
|
|
||||||
int ow = image.getWidth(this);
|
|
||||||
int oh = image.getHeight(this);
|
|
||||||
int nx = 6;
|
|
||||||
int ny = 6;
|
|
||||||
int nw = button.getWidth(this) - 12;
|
|
||||||
int nh = button.getHeight(this) - 12;
|
|
||||||
Shape defaultClip, roundClip;
|
|
||||||
defaultClip = g.getClip();
|
|
||||||
roundClip = new Ellipse2D.Float(nx, ny, nw, nh);
|
|
||||||
|
|
||||||
if (scaled == null)
|
|
||||||
{
|
|
||||||
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
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// (知) Be careful to be idempotent when calling
|
|
||||||
// async methods like #drawImage.
|
|
||||||
|
|
||||||
|
Rectangle b = roundClip.getBounds();
|
||||||
|
Shape defaultClip = g.getClip();
|
||||||
g.setClip(roundClip);
|
g.setClip(roundClip);
|
||||||
g.drawImage(scaled, nx, ny, getParent());
|
g.drawImage(scaled, b.x, b.y, this);
|
||||||
|
getParent().repaint();
|
||||||
// I don't know why, but when we repaint ourselves, our
|
// I don't know why, but when we repaint ourselves, our
|
||||||
// parent doesn't repaint, so nothing seems to happen.
|
// parent doesn't repaint, so nothing seems to happen.
|
||||||
g.setClip(defaultClip);
|
g.setClip(defaultClip);
|
||||||
@ -383,8 +421,8 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
setFocusable(true);
|
setFocusable(true);
|
||||||
setOpaque(false);
|
setOpaque(false);
|
||||||
|
|
||||||
int w = button.getWidth(this);
|
int w = button.getWidth(null);
|
||||||
int h = button.getHeight(this);
|
int h = button.getHeight(null);
|
||||||
setPreferredSize(new Dimension(w, h));
|
setPreferredSize(new Dimension(w, h));
|
||||||
|
|
||||||
this.addKeyListener(this);
|
this.addKeyListener(this);
|
||||||
@ -405,6 +443,14 @@ implements KeyListener, MouseListener, FocusListener {
|
|||||||
button = new ImageIcon(u1).getImage();
|
button = new ImageIcon(u1).getImage();
|
||||||
disabledOverlay = new ImageIcon(u2).getImage();
|
disabledOverlay = new ImageIcon(u2).getImage();
|
||||||
selectedOverlay = new ImageIcon(u3).getImage();
|
selectedOverlay = new ImageIcon(u3).getImage();
|
||||||
|
|
||||||
|
int radius = 6;
|
||||||
|
roundClip = new Ellipse2D.Float(
|
||||||
|
radius,
|
||||||
|
radius,
|
||||||
|
button.getWidth(null) - (2 * radius),
|
||||||
|
button.getHeight(null) - (2 * radius)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Binary file not shown.
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Loading…
Reference in New Issue
Block a user