mirror of
https://gitlab.com/biskuteri-cafe/JKomasto2.git
synced 2025-01-08 02:44: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;
|
||||
// Assume it had already been uploaded.
|
||||
|
||||
api.uploadFile(a.uploadee, new RequestListener() {
|
||||
api.uploadFile(
|
||||
a.uploadee, a.description,
|
||||
new RequestListener() {
|
||||
|
||||
public void
|
||||
connectionFailed(IOException eIo)
|
||||
@ -561,9 +563,6 @@ implements ActionListener {
|
||||
private JPanel
|
||||
selections;
|
||||
|
||||
private ButtonGroup
|
||||
selectionsGroup;
|
||||
|
||||
private JToggleButton
|
||||
attachment1,
|
||||
attachment2,
|
||||
@ -621,29 +620,29 @@ implements ActionListener {
|
||||
|
||||
if (working.size() > 3)
|
||||
{
|
||||
attachment4.doClick();
|
||||
Image i = working.get(3).image;
|
||||
attachment4.setIcon(new ImageIcon(i));
|
||||
}
|
||||
else if (working.size() > 2)
|
||||
{
|
||||
attachment3.doClick();
|
||||
Image i = working.get(2).image;
|
||||
attachment3.setIcon(new ImageIcon(i));
|
||||
}
|
||||
else if (working.size() > 1)
|
||||
{
|
||||
attachment2.doClick();
|
||||
Image i = working.get(1).image;
|
||||
attachment2.setIcon(new ImageIcon(i));
|
||||
}
|
||||
else if (working.size() > 0)
|
||||
{
|
||||
attachment1.doClick();
|
||||
Image i = working.get(0).image;
|
||||
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 hgap = 4;
|
||||
@ -661,11 +660,6 @@ implements ActionListener {
|
||||
{
|
||||
Object src = eA.getSource();
|
||||
|
||||
if (false)
|
||||
{
|
||||
// Clicked on filled attachment button.
|
||||
}
|
||||
|
||||
if (src == add)
|
||||
{
|
||||
int r = chooser.showOpenDialog(this);
|
||||
@ -697,40 +691,93 @@ implements ActionListener {
|
||||
|
||||
if (src == delete)
|
||||
{
|
||||
Object sm = selectionsGroup.getSelection();
|
||||
if (sm == attachment1.getModel())
|
||||
if (attachment1.isSelected())
|
||||
{
|
||||
assert working.size() > 0;
|
||||
working.remove(0);
|
||||
updateButtons();
|
||||
}
|
||||
if (sm == attachment2.getModel())
|
||||
if (attachment2.isSelected())
|
||||
{
|
||||
assert working.size() > 1;
|
||||
working.remove(1);
|
||||
updateButtons();
|
||||
}
|
||||
if (sm == attachment3.getModel())
|
||||
if (attachment3.isSelected())
|
||||
{
|
||||
assert working.size() > 2;
|
||||
working.remove(2);
|
||||
updateButtons();
|
||||
}
|
||||
if (sm == attachment4.getModel())
|
||||
if (attachment4.isSelected())
|
||||
{
|
||||
assert working.size() > 3;
|
||||
working.remove(3);
|
||||
updateButtons();
|
||||
}
|
||||
updateButtons();
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@ -762,17 +809,20 @@ implements ActionListener {
|
||||
attachment2.setMargin(add.getMargin());
|
||||
attachment3.setMargin(add.getMargin());
|
||||
attachment4.setMargin(add.getMargin());
|
||||
attachment1.addActionListener(this);
|
||||
attachment2.addActionListener(this);
|
||||
attachment3.addActionListener(this);
|
||||
attachment4.addActionListener(this);
|
||||
|
||||
selections = new JPanel();
|
||||
selections.setOpaque(false);
|
||||
selections.setLayout(new GridLayout(1, 0, 4, 0));
|
||||
working = new ArrayList<Attachment>();
|
||||
selectionsGroup = new ButtonGroup();
|
||||
ButtonGroup selectionsGroup = new ButtonGroup();
|
||||
selectionsGroup.add(attachment1);
|
||||
selectionsGroup.add(attachment2);
|
||||
selectionsGroup.add(attachment3);
|
||||
selectionsGroup.add(attachment4);
|
||||
// Have to add selection listener to button group
|
||||
updateButtons();
|
||||
|
||||
JButton del = new JButton("D");
|
||||
|
@ -2,6 +2,11 @@
|
||||
import javax.swing.ImageIcon;
|
||||
import java.awt.Image;
|
||||
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.MalformedURLException;
|
||||
|
||||
|
@ -435,9 +435,10 @@ MastodonApi {
|
||||
}
|
||||
|
||||
public void
|
||||
uploadFile(File file, RequestListener handler)
|
||||
uploadFile(File file, String alt, RequestListener handler)
|
||||
{
|
||||
assert file != null;
|
||||
assert alt != null;
|
||||
assert file.canRead();
|
||||
|
||||
String bct =
|
||||
@ -496,10 +497,12 @@ MastodonApi {
|
||||
String url = instanceUrl + "/api/v1/media/";
|
||||
try
|
||||
{
|
||||
URL endpoint = new URL(url);
|
||||
String s1 = "?description=" + encode(alt);
|
||||
|
||||
URL endpoint = new URL(url + s1);
|
||||
HttpURLConnection conn = cast(endpoint.openConnection());
|
||||
String s1 = "Bearer " + token;
|
||||
conn.setRequestProperty("Authorization", s1);
|
||||
String s2 = "Bearer " + token;
|
||||
conn.setRequestProperty("Authorization", s2);
|
||||
conn.setDoOutput(true);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setFixedLengthStreamingMode(contentLength);
|
||||
|
@ -429,54 +429,7 @@ implements ActionListener {
|
||||
setAuthorId(String n) { authorId.setText(n); }
|
||||
|
||||
public void
|
||||
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.
|
||||
*/
|
||||
/*
|
||||
* (悪) It doesn't work. I might be forced to use
|
||||
* BufferedImage with ImageIO. Or have
|
||||
* ImageApi#getScaledInstance spam until it works.
|
||||
*/
|
||||
|
||||
profile.setImage(n);
|
||||
}
|
||||
setAuthorAvatar(Image n) { profile.setImage(n); }
|
||||
|
||||
public void
|
||||
setDate(String n) { date.setText(n); }
|
||||
|
@ -8,6 +8,8 @@ import java.awt.Graphics;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Shape;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Component;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
@ -255,6 +257,7 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
// - -%- -
|
||||
|
||||
private Image
|
||||
copy,
|
||||
scaled;
|
||||
|
||||
private int
|
||||
@ -267,22 +270,78 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
selectedOverlay,
|
||||
disabledOverlay;
|
||||
|
||||
private static Shape
|
||||
roundClip;
|
||||
|
||||
// ---%-@-%---
|
||||
|
||||
public void
|
||||
setImage(Image n)
|
||||
{
|
||||
image = n;
|
||||
copy = null;
|
||||
scaled = null;
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
image.flush();
|
||||
prepareImage(image, this);
|
||||
}
|
||||
}
|
||||
|
||||
// - -%- -
|
||||
|
||||
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();
|
||||
return super.imageUpdate(i, f, x, y, w, h);
|
||||
// AbstractButton overrode this to refuse updates for
|
||||
// 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
|
||||
@ -294,34 +353,13 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
if (isFocusOwner())
|
||||
g.drawImage(selectedOverlay, 0, 0, this);
|
||||
|
||||
if (image == 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.
|
||||
if (scaled == null) return;
|
||||
|
||||
Rectangle b = roundClip.getBounds();
|
||||
Shape defaultClip = g.getClip();
|
||||
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
|
||||
// parent doesn't repaint, so nothing seems to happen.
|
||||
g.setClip(defaultClip);
|
||||
@ -383,8 +421,8 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
setFocusable(true);
|
||||
setOpaque(false);
|
||||
|
||||
int w = button.getWidth(this);
|
||||
int h = button.getHeight(this);
|
||||
int w = button.getWidth(null);
|
||||
int h = button.getHeight(null);
|
||||
setPreferredSize(new Dimension(w, h));
|
||||
|
||||
this.addKeyListener(this);
|
||||
@ -405,6 +443,14 @@ implements KeyListener, MouseListener, FocusListener {
|
||||
button = new ImageIcon(u1).getImage();
|
||||
disabledOverlay = new ImageIcon(u2).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