Warning: session_start(): open(/var/lib/php/sessions/sess_nt445rcq13drc5812li8k82ut6, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
sclass ImageSurface extends Surface {
BufferedImage image;
double zoomX = 1, zoomY = 1, zoomFactor = 1.5;
private Rectangle selection;
new L tools;
// use overlays now
O overlay; // voidfunc(Graphics2D)
L overlays = syncL();
Runnable onSelectionChange;
settable bool verbose;
bool noMinimumSize = true;
S titleForUpload;
O onZoom;
bool specialPurposed; // true = don't show image changing commands in popup menu
settable bool allowPaste;
settable bool zoomable = true;
bool noAlpha; // set to true to speed up drawing if you don't use alpha
O interpolationMode = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
O onNewImage; // use imageChanged event instead
BufferedImage imageToDraw; // if you want to draw a different image
File file; // where image was loaded from
bool autoZoomToDisplay; // only works 100% when not in scrollpane
settable bool repaintInThread; // after setImage, repaint in same thread
BoolVar showingVar;
Pt mousePosition;
event mousePositionChanged;
event imageChanged;
event userModifiedImage(BufferedImage image);
public ImageSurface() {
this(dummyImage());
}
static BufferedImage dummyImage() {
ret whiteImage(1);
}
*(File file) {
setImage(file);
}
*(MakesBufferedImage image) {
this(image != null ? image.getBufferedImage() : dummyImage());
}
*(BufferedImage image) {
setImage(image);
clearSurface = false;
// perform auto-zoom when shown, resized or when parent resized
bindToComponent(this, l0 performAutoZoom, null);
onResize(this, l0 performAutoZoom);
onEnclosingScrollPaneResize(this, l0 performAutoZoom);
componentPopupMenu2(this, ImageSurface_popupMenuMaker());
new ImageSurfaceSelector(this);
jHandleFileDrop(this, voidfunc(File f) { setImage(loadBufferedImage(f)) });
imageSurfaceOnHover(this, p -> {
mousePosition = p;
mousePositionChanged();
});
}
public ImageSurface(RGBImage image, double zoom) {
this(image);
setZoom(zoom);
}
// point is already in image coordinates
protected void fillPopupMenu(JPopupMenu menu, final Point point) {
if (zoomable) {
JMenuItem miZoomReset = new JMenuItem("Zoom 100%");
miZoomReset.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
setZoom(1.0);
centerPoint(point);
}
});
menu.add(miZoomReset);
JMenuItem miZoomIn = new JMenuItem("Zoom in");
miZoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
zoomIn(zoomFactor);
centerPoint(point);
}
});
menu.add(miZoomIn);
JMenuItem miZoomOut = new JMenuItem("Zoom out");
miZoomOut.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
zoomOut(zoomFactor);
centerPoint(point);
}
});
menu.add(miZoomOut);
menu.add(jMenuItemStayCheckedOnClick("Zoom to window",
-> autoZoomToDisplay,
-> setAutoZoomToDisplay(true)));
addMenuItem(menu, "Show full screen", r { showFullScreen() });
addMenuItem(menu, "Point: " + point.x + "," + point.y + " (image: " + w() + "*" + h() + ")", null);
menu.addSeparator();
}
if (!specialPurposed)
addMenuItem(menu, "Load image...", r { selectFile("Load image",
voidfunc(File f) { setImage(loadImage2(f)) }) });
addMenuItem(menu, "Save image...", r { saveImage() });
ifdef ImageSurface_AllowUpload
addMenuItem(menu, "Upload image...", r { uploadTheImage() });
endifdef
addMenuItem(menu, "Copy image to clipboard", r { copyImageToClipboard(getImage()) });
if (!specialPurposed || allowPaste)
addMenuItem(menu, "Paste image from clipboard", r { loadFromClipboard() });
if (!specialPurposed)
addMenuItem(menu, "Load image snippet...", r {
selectImageSnippet(voidfunc(S imageID) {
setImage(loadImage2(imageID))
});
});
if (selection != null)
addMenuItem(menu, "Crop", r { crop() });
if (!specialPurposed)
addMenuItem(menu, "No image", r { noImage() });
}
void noImage() { setImage((BufferedImage) null); }
void crop() {
if (selection == null) ret;
BufferedImage img = cloneClipBufferedImage(getImage(), selection);
selection = null;
setUserModifiedImage(img);
}
void setUserModifiedImage(BufferedImage img) {
setImage(img);
userModifiedImage(img);
}
void loadFromClipboard() {
BufferedImage img = getImageFromClipboard();
if (img != null)
setUserModifiedImage(img);
}
swappable File defaultImageDir() { ret getProgramDir(); }
void saveImage() {
var image = getImage();
JFileChooser fileChooser = new JFileChooser(defaultImageDir());
if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
try {
main saveImage(image, file = fileChooser.getSelectedFile());
} catch e {
popup(e);
}
}
}
void drawImageItself(int w, int h, Graphics2D g) {
int iw = getZoomedWidth(), ih = getZoomedHeight();
if (interpolationMode == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR || zoomX >= 1 || zoomY >= 1) {
// faster
g.drawImage(image, 0, 0, iw, ih, null);
} else
g.drawImage(resizeImage(image, iw, ih), 0, 0, null); // smoother
}
swappable void drawBackground(int w, int h, Graphics2D g) {
g.setColor(Color.white);
g.fillRect(0, 0, w, h);
}
public void render(int w, int h, Graphics2D g) {
if (verbose) _print("render");
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolationMode);
drawBackground(w, h);
BufferedImage image = or(imageToDraw, this.image);
if (!hasImage())
drawBackground(w, h, g);
else {
bool alpha = !noAlpha && hasTransparency(image);
if (alpha)
drawBackground(w, h, g);
drawImageItself(w, h, g);
int iw = getZoomedWidth(), ih = getZoomedHeight();
if (!alpha) {
g.fillRect(iw, 0, w-iw, h);
g.fillRect(0, ih, iw, h-ih);
}
}
if (overlay != null) {
if (verbose) _print("render overlay");
pcallF(overlay, g);
}
for (var overlay : cloneList(overlays)) pcall {
overlay.drawOn(cloneGraphics(g));
}
if (selection != null) {
if (verbose) _print("render selection");
// drawRect is inclusive, selection is exclusive, so... whatever, tests show it's cool.
drawSelectionRect(g, selection, Color.green, Color.white);
}
}
public void drawSelectionRect(Graphics2D g, Rectangle selection, Color green, Color white) {
drawSelectionRect(g, selection, green, white, zoomX, zoomY);
}
public void drawSelectionRect(Graphics2D g, Rectangle selection, Color green, Color white, double zoomX, double zoomY) {
g.setColor(green);
int top = (int) (selection.y * zoomY);
int bottom = (int) ((selection.y+selection.height) * zoomY);
int left = (int) (selection.x * zoomX);
int right = (int) ((selection.x+selection.width) * zoomX);
g.drawRect(left-1, top-1, right-left+1, bottom-top+1);
g.setColor(white);
g.drawRect(left - 2, top - 2, right - left + 3, bottom - top + 3);
}
public ImageSurface setZoom(double zoom) {
setZoom(zoom, zoom);
this;
}
public void setZoom(double zoomX, double zoomY) {
autoZoomToDisplay = false;
setZoom_dontChangeAutoZoom(zoomX, zoomY);
}
public void setZoom_dontChangeAutoZoom(double zoomX, double zoomY default zoomX) {
if (this.zoomX == zoomX && this.zoomY == zoomY) ret;
if (verbose) _print("Setting zoom");
this.zoomX = zoomX;
this.zoomY = zoomY;
revalidateMe();
repaint();
centerPoint(new Point(getImage().getWidth()/2, getImage().getHeight()/2));
pcallF(onZoom);
}
public scaffolded Dimension getMinimumSize() {
if (noMinimumSize) ret new Dimension(1, 1);
int w = getZoomedWidth();
int h = getZoomedHeight();
Dimension min = super.getMinimumSize();
ret printIfScaffoldingEnabled(this, new Dimension(Math.max(w, min.width), Math.max(h, min.height)));
}
int getZoomedHeight() {
return (int) (h() * zoomY);
}
int getZoomedWidth() {
return (int) (w() * zoomX);
}
bool isShowing_quick() {
if (showingVar == null) swing {
showingVar if null = componentShowingVar(ImageSurface.this);
}
ret showingVar!;
}
public void setImageIfShowing_thisThread(BufferedImage etc image) {
if (isShowing_quick())
setImage_thisThread(image);
}
void setImage(File file) {
setFile(file);
setImage(loadImage2(file));
}
public void setImage(MakesBufferedImage image) swing {
setImage_thisThread(image);
}
public void setImage(BufferedImage img) swing {
setImage_thisThread(img);
}
public void setImage_thisThread(BufferedImage etc img) {
BufferedImage newImage = img != null ? img : dummyImage();
BufferedImage oldImage = image;
image = newImage;
if (verbose) print("Old image size:" + imageSize(oldImage) + ", new image size: " + imageSize(newImage));
bool sameSize = imagesHaveSameSize(oldImage, newImage);
if (!sameSize) {
if (verbose) _print("New image size");
revalidateMe();
}
quickRepaint();
pcallF(onNewImage);
if (!sameSize && autoZoomToDisplay) zoomToDisplaySize();
imageChanged();
}
void setImageAndZoomToDisplay(BufferedImage img) {
setImage(img);
zoomToDisplaySize();
}
public BufferedImage getImage() {
return image;
}
public double getZoomX() {
return zoomX;
}
public double getZoomY() {
return zoomY;
}
public scaffolded Dimension getPreferredSize() {
ret printIfScaffoldingEnabled(this, new Dimension(getZoomedWidth(), getZoomedHeight()));
}
/** returns a scrollpane with the scroll-mode prevent-garbage-drawing fix applied */
public JScrollPane makeScrollPane() {
JScrollPane scrollPane = new JScrollPane(this);
scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
return scrollPane;
}
public void zoomToWindow() { zoomToDisplaySize(); }
public void zoomToDisplaySize() swing {
if (!hasImage()) return;
Dimension display = getDisplaySize();
if (display.width == 0 || display.height == 0) ret;
int w = w(), h = h();
double xRatio = (display.width-5)/(double) w;
double yRatio = (display.height-5)/(double) h;
if (scaffoldingEnabled(this))
printVars zoomToDisplaySize(+display, +w, +h, +xRatio, +yRatio);
setZoom_dontChangeAutoZoom(min(xRatio, yRatio));
revalidateMe();
}
/** tricky magic to get parent scroll pane */
private scaffolded Dimension getDisplaySize() {
Container c = getParent();
while (c != null) {
if (c instanceof JScrollPane)
return c.getSize();
c = c.getParent();
}
return getSize();
}
public void setSelection(Rect r) {
setSelection(toRectangle(r));
}
public void setSelection(Rectangle r) {
if (neq(selection, r)) {
selection = r;
pcallF(onSelectionChange);
quickRepaint();
}
}
public Rectangle getSelection() {
return selection;
}
public RGBImage getRGBImage() {
return new RGBImage(getImage());
}
// p is in image coordinates
void centerPoint(Point p) {
JScrollPane sp = enclosingScrollPane(this);
if (sp == null) ret;
p = new Point((int) (p.x*getZoomX()), (int) (p.y*getZoomY()));
final JViewport viewport = sp.getViewport();
Dimension viewSize = viewport.getExtentSize();
//_print("centerPoint " + p);
int x = max(0, p.x-viewSize.width/2);
int y = max(0, p.y-viewSize.height/2);
//_print("centerPoint " + p + " => " + x + "/" + y);
p = new Point(x,y);
//_print("centerPoint " + p);
final Point _p = p;
awtLater(r {
viewport.setViewPosition(_p);
});
}
Pt pointFromEvent(MouseEvent e) {
ret pointFromComponentCoordinates(new Pt(e.getX(), e.getY()));
}
Pt pointFromComponentCoordinates(Pt p) {
ret new Pt((int) (p.x/zoomX), (int) (p.y/zoomY));
}
Pt pointToComponentCoordinates(double x, double y) {
ret new Pt((int) (x*zoomX), (int) (y*zoomY));
}
void uploadTheImage {
//call(hotwire(/*#1007313*/#1016427), "go", getImage(), titleForUpload);
ifdef ImageSurface_AllowUpload
var img = getImage();
JTextField tf = jTextField(titleForUpload);
showFormTitled("Upload Image (PNG)",
"Image title (optional)", tf, func {
disableSubmitButton(getFrame(tf));
thread "Upload Image" {
try {
messageBox("Image uploaded as " + uploadPNGToImageServer(img, titleForUpload = getTextTrim(tf)));
disposeFrame(tf);
} catch e {
enableSubmitButton(getFrame(tf));
messageBox(e);
}
}
false;
});
endifdef
}
void showFullScreen() {
showFullScreenImageSurface(getImage());
}
void zoomIn(double f) { setZoom(getZoomX()*f, getZoomY()*f); }
void zoomOut(double f) { setZoom(getZoomX()/f, getZoomY()/f); }
selfType setFile(File f) { file = f; this; }
void setOverlay(IVF1 overlay) {
this.overlay = overlay;
}
bool hasImage() { ret image != null; }
public int w() { ret image.getWidth(); }
public int h() { ret image.getHeight(); }
void setPixelated aka pixelate(bool b) {
assertTrue(b);
imageSurface_pixelated(this);
}
selfType setAutoZoomToDisplay aka autoZoomToDisplay(bool b) {
if (autoZoomToDisplay = b)
zoomToDisplaySize();
this;
}
void quickRepaint() {
if (repaintInThread)
paintImmediately(0, 0, getWidth(), getHeight());
else
repaint();
}
void setTool(ImageSurfaceMouseHandler tool) swing {
removeAllTools();
addTool(tool);
}
bool hasTool(AutoCloseable tool) {
ret swing(-> tools.contains(tool));
}
void addTool(ImageSurfaceMouseHandler tool) swing {
if (!tools.contains(tool))
tool.register(this);
}
void removeTool(AutoCloseable tool) swing {
if (tools.contains(tool)) {
close(tool);
tools.remove(tool);
}
}
void removeAllTools {
closeAllAndClear(tools);
}
void performAutoZoom {
if (autoZoomToDisplay) zoomToDisplaySize();
}
void revalidateMe() {
revalidateIncludingFullCenterContainer(this);
}
void addOverlay(G2Drawable overlay) {
overlays.add(overlay);
repaint();
}
void clearOverlays() {
if (nempty(overlays)) {
overlays.clear();
repaint();
}
}
void setOverlay(G2Drawable overlay) {
clearOverlays();
if (overlay != null) addOverlay(overlay);
}
void loadImage(File f) {
setImage(loadImage2(f));
}
JComponent visualize() {
ret jscroll_center_borderless(this);
}
void standardZoom() {
setZoom(1.0);
}
!include #1034645 // print
} // end of ImageSurface
// static function allows garbage collection
static VF2 ImageSurface_popupMenuMaker() {
ret voidfunc(ImageSurface is, JPopupMenu menu) {
Point p = is.pointFromEvent(componentPopupMenu_mouseEvent.get()).getPoint();
is.fillPopupMenu(menu, p);
};
}