Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

553
LINES

< > BotCompany Repo | #1004553 // ImageSurface

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (19144L/119K).

1  
sclass ImageSurface extends Surface {
2  
  BufferedImage image;
3  
  double zoomX = 1, zoomY = 1, zoomFactor = 1.5;
4  
  private Rectangle selection;
5  
  new L<AutoCloseable> tools;
6  
  
7  
  // use overlays now
8  
  O overlay; // voidfunc(Graphics2D)
9  
  L<G2Drawable> overlays = syncL();
10  
  
11  
  Runnable onSelectionChange;
12  
  settable bool verbose;
13  
  bool noMinimumSize = true;
14  
  S titleForUpload;
15  
  O onZoom;
16  
  bool specialPurposed; // true = don't show image changing commands in popup menu
17  
  settable bool allowPaste;
18  
  settable bool zoomable = true;
19  
  bool noAlpha; // set to true to speed up drawing if you don't use alpha
20  
  O interpolationMode = RenderingHints.VALUE_INTERPOLATION_BILINEAR;
21  
  O onNewImage; // use imageChanged event instead
22  
  BufferedImage imageToDraw; // if you want to draw a different image
23  
  File file; // where image was loaded from
24  
  bool autoZoomToDisplay; // only works 100% when not in scrollpane
25  
  settable bool repaintInThread; // after setImage, repaint in same thread
26  
  BoolVar showingVar;
27  
  
28  
  Pt mousePosition;
29  
  event mousePositionChanged;
30  
  event imageChanged;
31  
  event userModifiedImage(BufferedImage image);
32  
 
33  
  public ImageSurface() {
34  
    this(dummyImage());
35  
  }
36  
  
37  
  static BufferedImage dummyImage() {
38  
    ret whiteImage(1);
39  
  }
40  
  
41  
  *(File file) {
42  
    setImage(file);
43  
  }
44  
45  
  *(MakesBufferedImage image) {
46  
    this(image != null ? image.getBufferedImage() : dummyImage());
47  
  }
48  
  
49  
  *(BufferedImage image) {
50  
    setImage(image);
51  
    clearSurface = false;
52  
53  
    // perform auto-zoom when shown, resized or when parent resized
54  
    bindToComponent(this, l0 performAutoZoom, null);
55  
    onResize(this, l0 performAutoZoom);
56  
    onEnclosingScrollPaneResize(this, l0 performAutoZoom);
57  
    
58  
    componentPopupMenu2(this, ImageSurface_popupMenuMaker());
59  
    new ImageSurfaceSelector(this);
60  
    
61  
    jHandleFileDrop(this, voidfunc(File f) { setImage(loadBufferedImage(f)) });
62  
    
63  
    imageSurfaceOnHover(this, p -> {
64  
      mousePosition = p;
65  
      mousePositionChanged();
66  
    });
67  
  }
68  
69  
  public ImageSurface(RGBImage image, double zoom) {
70  
    this(image);
71  
    setZoom(zoom);
72  
  }
73  
74  
  // point is already in image coordinates
75  
  protected void fillPopupMenu(JPopupMenu menu, final Point point) {
76  
    if (zoomable) {
77  
      JMenuItem miZoomReset = new JMenuItem("Zoom 100%");
78  
      miZoomReset.addActionListener(new ActionListener() {
79  
        public void actionPerformed(ActionEvent evt) {
80  
          setZoom(1.0);
81  
          centerPoint(point);
82  
        }
83  
      });
84  
      menu.add(miZoomReset);
85  
  
86  
      JMenuItem miZoomIn = new JMenuItem("Zoom in");
87  
      miZoomIn.addActionListener(new ActionListener() {
88  
        public void actionPerformed(ActionEvent evt) {
89  
          zoomIn(zoomFactor);
90  
          centerPoint(point);
91  
        }
92  
      });
93  
      menu.add(miZoomIn);
94  
  
95  
      JMenuItem miZoomOut = new JMenuItem("Zoom out");
96  
      miZoomOut.addActionListener(new ActionListener() {
97  
        public void actionPerformed(ActionEvent evt) {
98  
          zoomOut(zoomFactor);
99  
          centerPoint(point);
100  
        }
101  
      });
102  
      menu.add(miZoomOut);
103  
  
104  
      menu.add(jMenuItemStayCheckedOnClick("Zoom to window",
105  
        -> autoZoomToDisplay,
106  
        -> setAutoZoomToDisplay(true)));
107  
      
108  
      addMenuItem(menu, "Show full screen", r { showFullScreen() });
109  
      
110  
      addMenuItem(menu, "Point: " + point.x + "," + point.y + " (image: " + w() + "*" + h() + ")", null);
111  
  
112  
      menu.addSeparator();
113  
    }
114  
115  
    if (!specialPurposed)
116  
      addMenuItem(menu, "Load image...", r { selectFile("Load image",
117  
        voidfunc(File f) { setImage(loadImage2(f)) }) });
118  
    addMenuItem(menu, "Save image...", r { saveImage() });
119  
    ifdef ImageSurface_AllowUpload
120  
      addMenuItem(menu, "Upload image...", r { uploadTheImage() });
121  
    endifdef
122  
    addMenuItem(menu, "Copy image to clipboard", r { copyImageToClipboard(getImage()) });
123  
    if (!specialPurposed || allowPaste)
124  
      addMenuItem(menu, "Paste image from clipboard", r { loadFromClipboard() });
125  
      
126  
    if (!specialPurposed)
127  
      addMenuItem(menu, "Load image snippet...", r {
128  
        selectImageSnippet(voidfunc(S imageID) {
129  
          setImage(loadImage2(imageID))
130  
        });
131  
      });
132  
    if (selection != null)
133  
      addMenuItem(menu, "Crop", r { crop() });
134  
    if (!specialPurposed)
135  
      addMenuItem(menu, "No image", r { noImage() });
136  
  }
137  
  
138  
  void noImage() { setImage((BufferedImage) null); }
139  
  
140  
  void crop() {
141  
    if (selection == null) ret;
142  
    BufferedImage img = cloneClipBufferedImage(getImage(), selection);
143  
    selection = null;
144  
    setUserModifiedImage(img);
145  
  }
146  
  
147  
  void setUserModifiedImage(BufferedImage img) {
148  
    setImage(img);
149  
    userModifiedImage(img);
150  
  }
151  
  
152  
  void loadFromClipboard() {
153  
    BufferedImage img = getImageFromClipboard();
154  
    if (img != null)
155  
      setUserModifiedImage(img);
156  
  }
157  
  
158  
  swappable File defaultImageDir() { ret getProgramDir(); }
159  
160  
  void saveImage() {
161  
    var image = getImage();
162  
    JFileChooser fileChooser = new JFileChooser(defaultImageDir());
163  
    if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
164  
      try {
165  
        main saveImage(image, file = fileChooser.getSelectedFile());
166  
      } catch e {
167  
        popup(e);
168  
      }
169  
    }
170  
  }
171  
  
172  
  void drawImageItself(int w, int h, Graphics2D g) {
173  
    int iw = getZoomedWidth(), ih = getZoomedHeight();
174  
    if (interpolationMode == RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR || zoomX >= 1 || zoomY >= 1) {
175  
      // faster
176  
      g.drawImage(image, 0, 0, iw, ih, null);
177  
    } else
178  
      g.drawImage(resizeImage(image, iw, ih), 0, 0, null); // smoother
179  
  }
180  
181  
  swappable void drawBackground(int w, int h, Graphics2D g) {
182  
    g.setColor(or(getBackground(), Color.white));
183  
    g.fillRect(0, 0, w, h);
184  
  }
185  
186  
  public void render(int w, int h, Graphics2D g) {
187  
    if (verbose) _print("render");
188  
    g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolationMode);
189  
    drawBackground(w, h, g);
190  
    BufferedImage image = or(imageToDraw, this.image);
191  
    if (!hasImage())
192  
      drawBackground(w, h, g);
193  
    else {
194  
      bool alpha = !noAlpha && hasTransparency(image);
195  
      if (alpha)
196  
        drawBackground(w, h, g);
197  
    
198  
      drawImageItself(w, h, g);
199  
      
200  
      int iw = getZoomedWidth(), ih = getZoomedHeight();
201  
      if (!alpha) {
202  
        g.fillRect(iw, 0, w-iw, h);
203  
        g.fillRect(0, ih, iw, h-ih);
204  
      }
205  
    }
206  
207  
    if (overlay != null) {
208  
      if (verbose) _print("render overlay");
209  
      pcallF(overlay, g);
210  
    }
211  
    
212  
    for (var overlay : cloneList(overlays)) pcall {
213  
      overlay.drawOn(cloneGraphics(g));
214  
    }
215  
216  
    if (selection != null) {
217  
      if (verbose) _print("render selection");
218  
      // drawRect is inclusive, selection is exclusive, so... whatever, tests show it's cool.
219  
      drawSelectionRect(g, selection, Color.green, Color.white);
220  
    }
221  
  }
222  
223  
  public void drawSelectionRect(Graphics2D g, Rectangle selection, Color green, Color white) {
224  
    drawSelectionRect(g, selection, green, white, zoomX, zoomY);
225  
  }
226  
  
227  
  public void drawSelectionRect(Graphics2D g, Rectangle selection, Color green, Color white, double zoomX, double zoomY) {
228  
    g.setColor(green);
229  
    int top = (int) (selection.y * zoomY);
230  
    int bottom = (int) ((selection.y+selection.height) * zoomY);
231  
    int left = (int) (selection.x * zoomX);
232  
    int right = (int) ((selection.x+selection.width) * zoomX);
233  
    g.drawRect(left-1, top-1, right-left+1, bottom-top+1);
234  
    g.setColor(white);
235  
    g.drawRect(left - 2, top - 2, right - left + 3, bottom - top + 3);
236  
  }
237  
238  
  public ImageSurface setZoom(double zoom) {
239  
    setZoom(zoom, zoom);
240  
    this;
241  
  }
242  
243  
  public void setZoom(double zoomX, double zoomY) {
244  
    autoZoomToDisplay = false;
245  
    setZoom_dontChangeAutoZoom(zoomX, zoomY);
246  
  }
247  
  
248  
  public void setZoom_dontChangeAutoZoom(double zoomX, double zoomY default zoomX) {
249  
    if (this.zoomX == zoomX && this.zoomY == zoomY) ret;
250  
    if (verbose) _print("Setting zoom");
251  
    this.zoomX = zoomX;
252  
    this.zoomY = zoomY;
253  
    revalidateMe();
254  
    repaint();
255  
    centerPoint(new Point(getImage().getWidth()/2, getImage().getHeight()/2));
256  
257  
    pcallF(onZoom);
258  
  }
259  
260  
  public scaffolded Dimension getMinimumSize() {
261  
    if (noMinimumSize) ret new Dimension(1, 1);
262  
    int w = getZoomedWidth();
263  
    int h = getZoomedHeight();
264  
    Dimension min = super.getMinimumSize();
265  
    ret printIfScaffoldingEnabled(this, new Dimension(Math.max(w, min.width), Math.max(h, min.height)));
266  
  }
267  
268  
  int getZoomedHeight() {
269  
    return (int) (h() * zoomY);
270  
  }
271  
272  
  int getZoomedWidth() {
273  
    return (int) (w() * zoomX);
274  
  }
275  
276  
  bool isShowing_quick() {
277  
    if (showingVar == null) swing {
278  
      showingVar if null = componentShowingVar(ImageSurface.this);
279  
    }
280  
    
281  
    ret showingVar!;
282  
  }
283  
  
284  
  public void setImageIfShowing_thisThread(BufferedImage etc image) {
285  
    if (isShowing_quick())
286  
      setImage_thisThread(image);
287  
  }
288  
  
289  
  void setImage(File file) {
290  
    setFile(file);
291  
    setImage(loadImage2(file));
292  
  }
293  
294  
  public void setImage(MakesBufferedImage image) swing {
295  
    setImage_thisThread(image);
296  
  }
297  
  
298  
  public void setImage(BufferedImage img) swing {
299  
    setImage_thisThread(img);
300  
  }
301  
    
302  
  public void setImage_thisThread(BufferedImage etc img) {
303  
    BufferedImage newImage = img != null ? img : dummyImage();
304  
    BufferedImage oldImage = image;
305  
    image = newImage;
306  
    if (verbose) print("Old image size:" + imageSize(oldImage) + ", new image size: " + imageSize(newImage));
307  
    bool sameSize = imagesHaveSameSize(oldImage, newImage);
308  
    if (!sameSize) {
309  
      if (verbose) _print("New image size");
310  
      revalidateMe();
311  
    }
312  
    quickRepaint();
313  
    pcallF(onNewImage);
314  
    if (!sameSize && autoZoomToDisplay) zoomToDisplaySize();
315  
    imageChanged();
316  
  }
317  
  
318  
  void setImageAndZoomToDisplay(BufferedImage img) {
319  
    setImage(img);
320  
    zoomToDisplaySize();
321  
  }
322  
323  
  public BufferedImage getImage() {
324  
    return image;
325  
  }
326  
327  
  public double getZoomX() {
328  
    return zoomX;
329  
  }
330  
331  
  public double getZoomY() {
332  
    return zoomY;
333  
  }
334  
335  
  public scaffolded Dimension getPreferredSize() {
336  
    ret printIfScaffoldingEnabled(this, new Dimension(getZoomedWidth(), getZoomedHeight()));
337  
  }
338  
339  
  /** returns a scrollpane with the scroll-mode prevent-garbage-drawing fix applied */
340  
  public JScrollPane makeScrollPane() {
341  
    JScrollPane scrollPane = new JScrollPane(this);
342  
    scrollPane.getViewport().setScrollMode(JViewport.BACKINGSTORE_SCROLL_MODE);
343  
    return scrollPane;
344  
  }
345  
346  
  public void zoomToWindow() { zoomToDisplaySize(); }
347  
  public void zoomToDisplaySize() swing {
348  
    if (!hasImage()) return;
349  
    Dimension display = getDisplaySize();
350  
    if (display.width == 0 || display.height == 0) ret;
351  
    int w = w(), h = h();
352  
    double xRatio = (display.width-5)/(double) w;
353  
    double yRatio = (display.height-5)/(double) h;
354  
    if (scaffoldingEnabled(this))
355  
      printVars zoomToDisplaySize(+display, +w, +h, +xRatio, +yRatio);
356  
    setZoom_dontChangeAutoZoom(min(xRatio, yRatio));
357  
    revalidateMe();
358  
  }
359  
360  
  /** tricky magic to get parent scroll pane */
361  
  private scaffolded Dimension getDisplaySize() {
362  
    Container c = getParent();
363  
    while (c != null) {
364  
      if (c instanceof JScrollPane)
365  
        return c.getSize();
366  
      c = c.getParent();
367  
    }
368  
    return getSize();
369  
  }
370  
  
371  
  public void setSelection(Rect r) {
372  
    setSelection(toRectangle(r));
373  
  }
374  
375  
  public void setSelection(Rectangle r) {
376  
    if (neq(selection, r)) {
377  
      selection = r;
378  
      pcallF(onSelectionChange);
379  
      quickRepaint();
380  
    }
381  
  }
382  
383  
  public Rectangle getSelection() {
384  
    return selection;
385  
  }
386  
387  
  public RGBImage getRGBImage() {
388  
    return new RGBImage(getImage());
389  
  }
390  
  
391  
  // p is in image coordinates
392  
  void centerPoint(Point p) {
393  
    JScrollPane sp = enclosingScrollPane(this);
394  
    if (sp == null) ret;
395  
      
396  
    p = new Point((int) (p.x*getZoomX()), (int) (p.y*getZoomY()));
397  
    final JViewport viewport = sp.getViewport();
398  
    Dimension viewSize = viewport.getExtentSize();
399  
    
400  
    //_print("centerPoint " + p);
401  
    int x = max(0, p.x-viewSize.width/2);
402  
    int y = max(0, p.y-viewSize.height/2);
403  
    
404  
    //_print("centerPoint " + p + " => " + x + "/" + y);
405  
    p = new Point(x,y);
406  
    //_print("centerPoint " + p);
407  
    final Point _p = p;
408  
    awtLater(r {
409  
      viewport.setViewPosition(_p);
410  
    });
411  
  }
412  
  
413  
  Pt pointFromEvent(MouseEvent e) {
414  
    ret pointFromComponentCoordinates(new Pt(e.getX(), e.getY()));
415  
  }
416  
  
417  
  Pt pointFromComponentCoordinates(Pt p) {
418  
    ret new Pt((int) (p.x/zoomX), (int) (p.y/zoomY));
419  
  }
420  
  
421  
  Pt pointToComponentCoordinates(double x, double y) {
422  
    ret new Pt((int) (x*zoomX), (int) (y*zoomY));
423  
  }
424  
  
425  
  void uploadTheImage {
426  
    //call(hotwire(/*#1007313*/#1016427), "go", getImage(), titleForUpload);
427  
    ifdef ImageSurface_AllowUpload
428  
      var img = getImage();
429  
      JTextField tf = jTextField(titleForUpload);
430  
      showFormTitled("Upload Image (PNG)",
431  
        "Image title (optional)", tf, func {
432  
          disableSubmitButton(getFrame(tf));
433  
          thread "Upload Image" {
434  
            try {
435  
              messageBox("Image uploaded as " + uploadPNGToImageServer(img, titleForUpload = getTextTrim(tf)));
436  
              disposeFrame(tf);
437  
            } catch e {
438  
              enableSubmitButton(getFrame(tf));
439  
              messageBox(e);
440  
            }
441  
          }
442  
          false;
443  
        });
444  
    endifdef
445  
  }
446  
  
447  
  void showFullScreen() {
448  
    showFullScreenImageSurface(getImage());
449  
  }
450  
  
451  
  void zoomIn(double f) { setZoom(getZoomX()*f, getZoomY()*f); }
452  
  void zoomOut(double f) { setZoom(getZoomX()/f, getZoomY()/f); }
453  
  
454  
  selfType setFile(File f) { file = f; this; }
455  
  
456  
  void setOverlay(IVF1<Graphics2D> overlay) {
457  
    this.overlay = overlay;
458  
  }
459  
  
460  
  bool hasImage() { ret image != null; }
461  
  public int w() { ret image.getWidth(); }
462  
  public int h() { ret image.getHeight(); }
463  
  
464  
  void setPixelated aka pixelate(bool b) {
465  
    assertTrue(b);
466  
    imageSurface_pixelated(this);
467  
  }
468  
  
469  
  selfType setAutoZoomToDisplay aka autoZoomToDisplay(bool b) {
470  
    if (autoZoomToDisplay = b)
471  
      zoomToDisplaySize();
472  
    this;
473  
  }
474  
  
475  
  void quickRepaint() {
476  
    if (repaintInThread)
477  
      paintImmediately(0, 0, getWidth(), getHeight());
478  
    else
479  
      repaint();
480  
  }
481  
  
482  
  void setTool(ImageSurfaceMouseHandler tool) swing {
483  
    removeAllTools();
484  
    addTool(tool);
485  
  }
486  
  
487  
  bool hasTool(AutoCloseable tool) {
488  
    ret swing(-> tools.contains(tool));
489  
  }
490  
  
491  
  void addTool(ImageSurfaceMouseHandler tool) swing {
492  
    if (!tools.contains(tool))
493  
      tool.register(this);
494  
  }
495  
  
496  
  void removeTool(AutoCloseable tool) swing {
497  
    if (tools.contains(tool)) {
498  
      close(tool);
499  
      tools.remove(tool);
500  
    }
501  
  }
502  
  
503  
  void removeAllTools aka clearTools() {
504  
    closeAllAndClear(tools);
505  
  }
506  
  
507  
  void performAutoZoom {
508  
    if (autoZoomToDisplay) zoomToDisplaySize();
509  
  }
510  
  
511  
  void revalidateMe() {
512  
    revalidateIncludingFullCenterContainer(this);
513  
  }
514  
  
515  
  void addOverlay(G2Drawable overlay) {
516  
    overlays.add(overlay);
517  
    repaint();
518  
  }
519  
  
520  
  void clearOverlays() {
521  
    if (nempty(overlays)) {
522  
      overlays.clear();
523  
      repaint();
524  
    }
525  
  }
526  
  
527  
  void setOverlay(G2Drawable overlay) {
528  
    clearOverlays();
529  
    if (overlay != null) addOverlay(overlay);
530  
  }
531  
  
532  
  void loadImage(File f) {
533  
    setImage(loadImage2(f));
534  
  }
535  
  
536  
  JComponent visualize() {
537  
    ret jscroll_center_borderless(this);
538  
  }
539  
  
540  
  void standardZoom() {
541  
    setZoom(1.0);
542  
  }
543  
  
544  
  !include #1034645 // print
545  
} // end of ImageSurface
546  
547  
// static function allows garbage collection 
548  
static VF2<ImageSurface, JPopupMenu> ImageSurface_popupMenuMaker() {
549  
  ret voidfunc(ImageSurface is, JPopupMenu menu) {
550  
    Point p = is.pointFromEvent(componentPopupMenu_mouseEvent.get()).getPoint();
551  
    is.fillPopupMenu(menu, p);
552  
  };
553  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 20 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, jtubtzbbkimh, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, ppjhyzlbdabe, pyentgdyhuwx, pzhvpgtvlbxg, sawdedvomwva, tslmcundralx, tvejysmllsmz, vouqrxazstgt, xrpafgyirdlv

No comments. add comment

Snippet ID: #1004553
Snippet name: ImageSurface
Eternal ID of this version: #1004553/163
Text MD5: d1e8e48ee0a64fb49de058409c417d27
Transpilation MD5: 68579e37079dfbc7cb009eb4284f2520
Author: stefan
Category: javax / gui
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-08-23 18:20:57
Source code size: 16129 bytes / 553 lines
Pitched / IR pitched: No / No
Views / Downloads: 1343 / 7446
Version history: 162 change(s)
Referenced in: [show references]