📜 ⬆️ ⬇️

Java 6 & 7 ed Irregular Windows

Simpotic?

In the wake of the musicians of Bremen


As probably many people remember, in the past century (even in the days of Windows 2000 ), it was fashionable to create all kinds of splash screens and mini-appliqués in non-rectangular windows (as well as unusual controls).
These ponies were written in C / C ++ using WinAPI using so-called. regions. It was not so simple, because you had to not only stumble over the jambs and Windows and the language, but also the miscalculation of the polygons for drawing was also scaring. Therefore, “drawing” one or two rounded windows, I put this topic down indefinitely.
And this Monday, the article “Windows of the" wrong "form" flashed, again drawing my attention to this topic. Expecting to know that the WinAPI wrapper functions were implemented in .NET , was disappointed to see descriptions of external functions. And here I, as a programmer mostly in Java , remembered that, then still Sun , promised to introduce functions for drawing a window of arbitrary shape.



Cup of coffee


Well, what opportunities did Sun / Oracle provide us?
  1. JNI / JNA :
    • platform dependent
    • write additional native code for each system
  2. Transparency emulation by drawing the background:
    • the window is still clickable
    • slow drawing
    • noticeable delays when moving the window
    • does not respond quickly to changes in the background (especially trouble with animation in the background)
  3. Class com.sun.awt.AWTUtilities :
    • Available from version 1.6_10 only in Sun VM
    • inner class whose support is not guaranteed
  4. Public methods of java.awt.Window , java.awt.GraphicsEnvironment , java.awt.GraphicsConfiguration classes:
    • Available from version 1.7.0, cheers!
    • which is annoying: what about OpenJDK ?

At the moment, the penultimate method is the most acceptable. So, the task we have is this: Draw a translucent window of complex non-rectangular shape (based on the image), with a background image, with the ability to move the window, resize the window, place any controls (both standard and also of arbitrary shape)

Regions, regions, territories


Having smoked, having smoked manuals and tutorials, it became clear that the classes that implement the java.awt.Shape interface are responsible for the shape of the window, as well as any geometric shapes. That is, if we manage to generate a closed Shape from our image, similarly to the region in Windows, the problem will be solved.
However, according to the prevailing cross-platform tradition, you need to make sure that the OS itself is able to draw such windows. Using AWTUtilities , we write, for example, the following code:
if((com.sun.awt.AWTUtilities.isTranslucencySupported (com.sun.awt.AWTUtilities.Translucency.PERPIXEL_TRANSLUCENT)) && (com.sun.awt.AWTUtilities.isTranslucencyCapable (GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()))) { //   } 

PERPIXEL_TRANSLUCENT means that the graphics system is capable of displaying not only fully transparent windows, but also, roughly speaking, “have holes” in the windows, and also that the current device is able to work with transparency (alas, Windows 98 in the span). and do not check, but then when you try to use these functions, the corresponding exception will crash.
Also, it may be that the old Java is installed in the system and there are no AWTUtilities at all - then you can use the auxiliary class AWTUtilitiesWrapper from this article that works with AWTUtilities through reflection. I did not begin to use it, because the probability is sufficient that everyone has updated Java, and the code becomes more incomprehensible. We will work directly with AWTUtilities :
  1. Before showing the window, remove the title bar (disable the standard OS decorations):
     this.setUndecorated(true); 
  2. Ask for the ability to make the window translucent:
     com.sun.awt.AWTUtilities.setWindowOpaque(this, false); 
  3. Set the window transparency level:
     com.sun.awt.AWTUtilities.setWindowOpacity(this, .75); 

    The value of the second argument must be between 0 - 1 (in our
    case 75%).
    You can even make the window completely transparent, but it will respond to
    pressing
  4. construct the desired Shape , set the shape we need for the window:
     com.sun.awt.AWTUtilities.setWindowShape(this, s); 

    Now, in a magical way, everything that is outside of our figure is not
    It will not only not be drawn, but will also not respond to pressing (recall
    approach from the book " Swing Hacks ")
  5. Once we lost the standard header strip, but we want to move the window,
    add the ability to move the window by clicking anywhere in the window (for this I
    used the MoveMouseListener class from the same book)
  6. Add the “Close” button to the window, also non-standard

Here are the forms!


The next task: from any picture with a transparent background, well separated from the foreground, make up its closed contour. For my perverted torture, I googled this lovely young lady:
The young lady is not mine, this is only an example
At first, I was thinking of implementing the convex hull method, but later refused - our young lady is not only very convex, but in some places convex. On the other, my stupid brains were not enough in the evening, except for using the solution "head-on": run through the pixels, removing the transparent pixels from the initial rectangle . Such code turned out:
 static Shape contour(final BufferedImage i) { final int w = i.getWidth(); final int h = i.getHeight(); final Area s = new Area(new Rectangle(w, h)); final Rectangle r = new Rectangle(0, 0, 1, 1); for (ry = 0; ry < h; r.y++) { System.out.println(ry + "/" + h); for (rx = 0; rx < w; r.x++) { if ((i.getRGB(rx, ry) & 0xFF000000) != 0xFF000000) { s.subtract(new Area( r )); } } } return s; } 

Transparent pixels are also considered to be translucent, too (ie, with alpha <255) - of course, if necessary, you can specify a certain threshold value, or even assign any color, say, white: 0xFFFFFF). But it doesn’t matter, something else turned out to be important - speed, because no one will wait 10 minutes at a load of 100% while our Intro deigns to seem. Yes, and generate the form every time unnecessarily, it is better to save somewhere, which would then quickly load and show.

R Tape loading error


Java has built-in tools for saving and loading any primitives and objects that implement the ava.io.Serializable interface, as well as recursively serializable. But the trouble is: neither Shape, nor any of its implementing classes are serializable! After quite a while, I managed to get the java.awt.geom.PathIterator interface, which allows you to run around the contour to save it, and the java.awt.geom.GeneralPath class, in which you can write the previously saved contour. And that's what happened:
 static void save(final Shape s, final DataOutput os) throws IOException { final PathIterator pi = s.getPathIterator(null); os.writeInt(pi.getWindingRule()); System.out.println(pi.getWindingRule()); while (!pi.isDone()) { final double[] coords = new double[6]; final int type = pi.currentSegment(coords); os.writeInt(type); System.out.println(type); for (final double coord : coords) { os.writeDouble(coord); System.out.println(coord); } System.out.println(""); pi.next(); } } 

Using the java.io.DataOutput interface allows you to save data anywhere - I used the rare java.io.RandomAccessFile class that implements it, but you can write it in java.io.ObjectOutputStream . It is possible (and even better) to save the file next to the class files, so that later it can be obtained even from the archive with the applet.
 static Shape load(final DataInput is) throws IOException { final GeneralPath gp = new GeneralPath(is.readInt()); final double[] data = new double[6]; CYC: while (true) { final int type = is.readInt(); for (int i = 0; i < data.length; i++) { data[i] = is.readDouble(); } switch (type) { case PathIterator.SEG_MOVETO: gp.moveTo(data[0], data[1]); break; case PathIterator.SEG_LINETO: gp.lineTo(data[0], data[1]); break; case PathIterator.SEG_QUADTO: gp.quadTo(data[0], data[1], data[2], data[3]); break; case PathIterator.SEG_CUBICTO: gp.curveTo(data[0], data[1], data[2], data[3], data[4], data[5]); break; case PathIterator.SEG_CLOSE: break CYC; } } return gp.createTransformedShape(null); } 

Of course, the correct file is necessary for correct recovery - for simplicity, I do not conduct any checks, and no exceptions are processed (but in general it would be necessary!). The file format is:
  1. Contour traversal direction: clockwise or counterclockwise (integer)
  2. Any number of blocks:
    1. Curve type (integer)
    2. Curve anchor points (array of 6 fractional)

In addition, we draw a background image in a completely standard way - by overloading the paint (Graphics g) method, or rather paintComponent (Graphics g)
')

All or nothing


Well, if we ourselves draw a window, then for completeness, we also need to draw our own controls. For now, I’ll confine myself to the “Close” button.

To demonstrate, I went the second way:


Finita la comedia


Everything! Making the finishing touches: In a standard way, add a button to the window and hang a handler on it. Run and admire:
Simpotic?
Like many of my projects, I keep on xp-dev.com : project page , svn repository
There are two projects for Eclipse Helios : Shaped for Java6 and Shaped7 for Java7. To reduce the difference in versions, specific queries were made to class methods, and auxiliary functions to a separate utility class.



Pulp fiction:


Source: https://habr.com/ru/post/127240/


All Articles