Initial checkin
7
.classpath
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||||
|
<classpathentry kind="var" path="MAVEN_REPO/servletapi/jars/servletapi-2.4.jar"/>
|
||||||
|
<classpathentry kind="output" path="target"/>
|
||||||
|
</classpath>
|
||||||
17
.project
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>album</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
40
Makefile
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#
|
||||||
|
# Java interpreter and compiler
|
||||||
|
#
|
||||||
|
JAVA = java
|
||||||
|
JAVAC = javac
|
||||||
|
|
||||||
|
#
|
||||||
|
# Various tools
|
||||||
|
#
|
||||||
|
RM = rm -f
|
||||||
|
|
||||||
|
#
|
||||||
|
# Which libraries do we use?
|
||||||
|
#
|
||||||
|
CLASSPATH=$(HOME)/src:$(HOME)/src/album/freeJSX1.0.7.4.jar
|
||||||
|
|
||||||
|
CLASSES = Photo.class Album.class Item.class Transform.class
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# The file types we deal with
|
||||||
|
#
|
||||||
|
.SUFFIXES: .java .class
|
||||||
|
|
||||||
|
|
||||||
|
all: $(CLASSES)
|
||||||
|
@echo Done
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) *.class
|
||||||
|
$(RM) *~
|
||||||
|
|
||||||
|
.java.class:
|
||||||
|
$(JAVAC) -classpath $(CLASSPATH) $<
|
||||||
|
|
||||||
|
|
||||||
|
test: $(CLASSES) Test.class
|
||||||
|
$(JAVA) -cp $(CLASSPATH) album.config.Test /home/knut/src/album/photos
|
||||||
|
|
||||||
|
#eof
|
||||||
BIN
album.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
60
design.txt
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
A (hidden) file exists in each album directory, holding information
|
||||||
|
about each image and each sub-album. For both images and albums the key is the
|
||||||
|
filename and the order in which each is listed determines the ordering
|
||||||
|
in the album. Common to both we have the following attributes:
|
||||||
|
|
||||||
|
- Caption, a text fragment explaining what's in the picture/album.
|
||||||
|
|
||||||
|
Special to images additional attributes stored are:
|
||||||
|
|
||||||
|
- Rotation (floating point number)
|
||||||
|
|
||||||
|
- Mask, represented by the four border sizes; top, left, bottom,
|
||||||
|
right. All floating point.
|
||||||
|
|
||||||
|
- Thumbnail mask. Same as above, but applies to thumbnail images.
|
||||||
|
As these are very small there may be a need to focus on a detail
|
||||||
|
of the original image to have a visual cue about the subject of
|
||||||
|
the photo.
|
||||||
|
|
||||||
|
For albums the additional attributes are:
|
||||||
|
|
||||||
|
- Cover picture, the picture (or album) representing the album as a
|
||||||
|
whole for the purpose of a thumbnail.
|
||||||
|
|
||||||
|
|
||||||
|
Web Interface
|
||||||
|
=============
|
||||||
|
|
||||||
|
The theory of operation is that the directory holding all the
|
||||||
|
subdirectories with albums maps to a webapp. The URLs accepted by the
|
||||||
|
webapp reflect the directory structure of the photo directories, but
|
||||||
|
adds some twists to make it possible to represent thumbnails, medium
|
||||||
|
sized images and large images in addition to the album pages
|
||||||
|
themselves. URLs for the albums are:
|
||||||
|
|
||||||
|
/album/index-num.html
|
||||||
|
|
||||||
|
There is a configuretion switch indicating how many thumbnails to
|
||||||
|
show per album page. This makes "num" in index-num.html represent the
|
||||||
|
associated subset of images. There are also one picture per page
|
||||||
|
pages. These have the pattern:
|
||||||
|
|
||||||
|
/album/filename.html
|
||||||
|
|
||||||
|
The individual pictures are accessed with URLs like the following:
|
||||||
|
|
||||||
|
/album/.../size/filename.ext
|
||||||
|
|
||||||
|
In this URL size has to be one of the following: "thumb", "medium",
|
||||||
|
"large". The (global?) configuration determines the actual size
|
||||||
|
constraints for each of the sizes. Only certain file extentions are
|
||||||
|
supported. Notably "jpeg" and "png". "html" can not be allowed as it
|
||||||
|
would destroy the page per picture setup.
|
||||||
|
|
||||||
|
|
||||||
BIN
photos/forkalsrud-ca-1951.jpg
Executable file
|
After Width: | Height: | Size: 90 KiB |
BIN
photos/fra_jakt.jpg
Executable file
|
After Width: | Height: | Size: 73 KiB |
BIN
photos/l1000729.jpg
Normal file
|
After Width: | Height: | Size: 327 KiB |
BIN
photos/l1000802.jpg
Normal file
|
After Width: | Height: | Size: 814 KiB |
BIN
photos/portraits/anton-rudi.jpg
Executable file
|
After Width: | Height: | Size: 5 KiB |
BIN
photos/portraits/otto-kaurstad.jpg
Executable file
|
After Width: | Height: | Size: 6 KiB |
BIN
photos/portraits/valdemar-dahl.jpg
Executable file
|
After Width: | Height: | Size: 6.4 KiB |
BIN
src/album/config/Album$1.class
Normal file
BIN
src/album/config/Album$2.class
Normal file
BIN
src/album/config/Album.class
Normal file
49
src/album/config/Album.java
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package album.config;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class Album
|
||||||
|
extends Item
|
||||||
|
{
|
||||||
|
String frontPic;
|
||||||
|
|
||||||
|
Entry[] contents;
|
||||||
|
|
||||||
|
public Album(String name) {
|
||||||
|
super(name);
|
||||||
|
frontPic = null;
|
||||||
|
contents = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void scan(File dir)
|
||||||
|
{
|
||||||
|
File[] dirs = dir.listFiles(new FileFilter() {
|
||||||
|
public boolean accept(File f) {
|
||||||
|
return f.isDirectory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
File[] files = dir.listFiles(new FilenameFilter() {
|
||||||
|
public boolean accept(File dir, String name) {
|
||||||
|
return name.endsWith(".JPEG")
|
||||||
|
|| name.endsWith(".jpeg")
|
||||||
|
|| name.endsWith(".JPG")
|
||||||
|
|| name.endsWith(".jpg");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
contents = new Entry[dirs.length + files.length];
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
for (File d : dirs) {
|
||||||
|
contents[idx++] = new Reference(d.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (File f : files) {
|
||||||
|
contents[idx++] = new Photo(f.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
BIN
src/album/config/Entry.class
Normal file
11
src/album/config/Entry.java
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
package album.config;
|
||||||
|
|
||||||
|
public abstract class Entry {
|
||||||
|
String name;
|
||||||
|
|
||||||
|
public Entry(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
BIN
src/album/config/Item.class
Normal file
19
src/album/config/Item.java
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package album.config;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class Item
|
||||||
|
extends Entry
|
||||||
|
{
|
||||||
|
String caption;
|
||||||
|
boolean hidden;
|
||||||
|
|
||||||
|
public Item(String name) {
|
||||||
|
super(name);
|
||||||
|
caption = null;
|
||||||
|
hidden = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
BIN
src/album/config/Photo.class
Normal file
21
src/album/config/Photo.java
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package album.config;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import javax.imageio.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.awt.geom.*;
|
||||||
|
|
||||||
|
public class Photo
|
||||||
|
extends Item
|
||||||
|
{
|
||||||
|
Transform xform;
|
||||||
|
|
||||||
|
public Photo(String name) {
|
||||||
|
super(name);
|
||||||
|
xform = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
BIN
src/album/config/Reference.class
Normal file
11
src/album/config/Reference.java
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
package album.config;
|
||||||
|
|
||||||
|
public class Reference
|
||||||
|
extends Entry
|
||||||
|
{
|
||||||
|
public Reference(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
BIN
src/album/config/Test.class
Normal file
20
src/album/config/Test.java
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package album.config;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public class Test
|
||||||
|
{
|
||||||
|
|
||||||
|
public static void main(String args[])
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
File dir = new File(args[0]);
|
||||||
|
|
||||||
|
Album a = new Album(dir.getName());
|
||||||
|
|
||||||
|
a.scan(dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
BIN
src/album/config/Transform.class
Normal file
12
src/album/config/Transform.java
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package album.config;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.geom.*;
|
||||||
|
|
||||||
|
public class Transform {
|
||||||
|
Dimension size;
|
||||||
|
float rotation;
|
||||||
|
Rectangle region;
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
BIN
src/album/editor/Editor$1.class
Normal file
BIN
src/album/editor/Editor$2.class
Normal file
BIN
src/album/editor/Editor$CropFrame$CropSpinnerModel.class
Normal file
BIN
src/album/editor/Editor$CropFrame.class
Normal file
BIN
src/album/editor/Editor$Renderer$1.class
Normal file
BIN
src/album/editor/Editor$Renderer.class
Normal file
BIN
src/album/editor/Editor.class
Normal file
473
src/album/editor/Editor.java
Normal file
|
|
@ -0,0 +1,473 @@
|
||||||
|
package album.editor;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
import java.awt.geom.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
import javax.imageio.*;
|
||||||
|
import javax.imageio.stream.*;
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.*;
|
||||||
|
import javax.swing.border.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Editor
|
||||||
|
*
|
||||||
|
* @author knut@forkalsrud.org
|
||||||
|
*/
|
||||||
|
public class Editor
|
||||||
|
extends JComponent
|
||||||
|
{
|
||||||
|
BufferedImage img;
|
||||||
|
double rotation;
|
||||||
|
double scale;
|
||||||
|
Rectangle region;
|
||||||
|
Dimension displaySize;
|
||||||
|
AffineTransform t;
|
||||||
|
|
||||||
|
CropFrame mask;
|
||||||
|
|
||||||
|
Rectangle currentRect = null;
|
||||||
|
|
||||||
|
Rectangle rectToDraw = null;
|
||||||
|
|
||||||
|
Rectangle previousRectDrawn = new Rectangle();
|
||||||
|
|
||||||
|
public Editor(BufferedImage img) {
|
||||||
|
this.img = img;
|
||||||
|
|
||||||
|
rotation = 0; // 135.0;
|
||||||
|
displaySize = new Dimension(480, 300);
|
||||||
|
int w = img.getWidth();
|
||||||
|
int h = img.getHeight();
|
||||||
|
|
||||||
|
scale = Math.min(displaySize.width, displaySize.height)
|
||||||
|
/ Math.sqrt(w * w + h * h);
|
||||||
|
|
||||||
|
t = new AffineTransform();
|
||||||
|
t.translate(displaySize.width/2.0, displaySize.height/2.0);
|
||||||
|
t.scale(scale, scale);
|
||||||
|
t.translate(-w/2.0, -h/2.0);
|
||||||
|
Point topLeft = new Point(0, 0);
|
||||||
|
Point topLeft1 = new Point();
|
||||||
|
Point bottomRight = new Point(w, h);
|
||||||
|
Point bottomRight2 = new Point();
|
||||||
|
t.transform(topLeft, topLeft1);
|
||||||
|
t.transform(bottomRight, bottomRight2);
|
||||||
|
Dimension d = new Dimension((int)Math.round(bottomRight2.getX() - topLeft1.getX() + 1),
|
||||||
|
(int)Math.round(bottomRight2.getY() - topLeft1.getY() + 1));
|
||||||
|
Rectangle imgRec = new Rectangle(topLeft1, d);
|
||||||
|
|
||||||
|
log("rect: " + imgRec);
|
||||||
|
|
||||||
|
mask = new CropFrame();
|
||||||
|
add(mask);
|
||||||
|
mask.setBounds(imgRec);
|
||||||
|
mask.setVisible(true);
|
||||||
|
|
||||||
|
setPreferredSize(displaySize);
|
||||||
|
MyListener ml = new MyListener();
|
||||||
|
addMouseListener(ml);
|
||||||
|
addMouseMotionListener(ml);
|
||||||
|
|
||||||
|
|
||||||
|
mask.addMouseListener(mask);
|
||||||
|
mask.addMouseMotionListener(mask);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
AffineTransform render() {
|
||||||
|
int w = img.getWidth();
|
||||||
|
int h = img.getHeight();
|
||||||
|
|
||||||
|
scale = Math.min(displaySize.width, displaySize.height)
|
||||||
|
/ Math.sqrt(w * w + h * h);
|
||||||
|
|
||||||
|
t = new AffineTransform();
|
||||||
|
|
||||||
|
t.translate(displaySize.width/2.0, displaySize.height/2.0);
|
||||||
|
t.scale(scale, scale);
|
||||||
|
t.rotate(rotation * Math.PI / 180);
|
||||||
|
t.translate(-w/2.0, -h/2.0);
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VolatileImage dst;
|
||||||
|
|
||||||
|
protected void paintComponent(Graphics g1) {
|
||||||
|
super.paintComponent(g1);
|
||||||
|
|
||||||
|
if (getWidth() != displaySize.width
|
||||||
|
|| getHeight() != displaySize.height
|
||||||
|
|| t == null || dst == null)
|
||||||
|
{
|
||||||
|
displaySize.width = getWidth();
|
||||||
|
displaySize.height = getHeight();
|
||||||
|
AffineTransform dxf = render();
|
||||||
|
|
||||||
|
|
||||||
|
dst = createVolatileImage(getWidth(), getHeight());
|
||||||
|
Graphics2D g = dst.createGraphics();
|
||||||
|
g.drawRenderedImage(img, dxf);
|
||||||
|
}
|
||||||
|
g1.drawImage(dst, 0, 0, dst.getWidth(), dst.getHeight(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void drawWireframe(Graphics2D g, int w, int h) {
|
||||||
|
double delta = Math.round(Math.sqrt(w * w + h * h) / 20);
|
||||||
|
//
|
||||||
|
// 0------------4------------1
|
||||||
|
// | /|\ |
|
||||||
|
// | 6 | 7 |
|
||||||
|
// | | |
|
||||||
|
// | 5 |
|
||||||
|
// | 8 |
|
||||||
|
// | | |
|
||||||
|
// | 10-+-11 |
|
||||||
|
// | | |
|
||||||
|
// | 9 |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// 3-------------------------2
|
||||||
|
double[] wireFramePoints = new double[] {
|
||||||
|
0, 0,
|
||||||
|
w, 0,
|
||||||
|
w, h,
|
||||||
|
0, h,
|
||||||
|
w/2, 0,
|
||||||
|
w/2, 3 * delta,
|
||||||
|
w/2 - delta, delta,
|
||||||
|
w/2 + delta, delta,
|
||||||
|
w/2, h/2 - delta,
|
||||||
|
w/2, h/2 + delta,
|
||||||
|
w/2 - delta, h/2,
|
||||||
|
w/2 + delta, h/2
|
||||||
|
};
|
||||||
|
|
||||||
|
float[] screen = new float[wireFramePoints.length];
|
||||||
|
int[] x = new int[wireFramePoints.length / 2];
|
||||||
|
int[] y = new int[wireFramePoints.length / 2];
|
||||||
|
|
||||||
|
t.transform(wireFramePoints, 0, screen, 0, x.length);
|
||||||
|
|
||||||
|
for (int i = 0; i < x.length; ++i) {
|
||||||
|
x[i] = Math.round(screen[2*i]);
|
||||||
|
y[i] = Math.round(screen[2*i + 1]);
|
||||||
|
}
|
||||||
|
g.setColor(Color.BLUE);
|
||||||
|
|
||||||
|
g.drawPolygon(x, y, 4);
|
||||||
|
g.drawLine(x[4], y[4], x[5], y[5]);
|
||||||
|
g.drawLine(x[4], y[4], x[6], y[6]);
|
||||||
|
g.drawLine(x[4], y[4], x[7], y[7]);
|
||||||
|
g.drawLine(x[8], y[8], x[9], y[9]);
|
||||||
|
g.drawLine(x[10], y[10], x[11], y[11]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class MyListener extends MouseInputAdapter {
|
||||||
|
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
int x = e.getX();
|
||||||
|
int y = e.getY();
|
||||||
|
mask.setLocation(x, y);
|
||||||
|
mask.setSize(1, 1);
|
||||||
|
mask.repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
mask.updateFrame(e.getX(), e.getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
mask.updateFrame(e.getX(), e.getY());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
JComponent createControlPanel() {
|
||||||
|
BevelBorder border = new BevelBorder(BevelBorder.LOWERED);
|
||||||
|
JPanel hp = new JPanel();
|
||||||
|
hp.setLayout(new BoxLayout(hp, BoxLayout.Y_AXIS));
|
||||||
|
hp.setBorder(
|
||||||
|
new TitledBorder(border, "Controls", TitledBorder.LEFT,
|
||||||
|
TitledBorder.ABOVE_TOP));
|
||||||
|
hp.add(new JLabel("Rotation"));
|
||||||
|
final JSlider rotationSlider = new JSlider(-180, 180, 0);
|
||||||
|
rotationSlider.setPaintTicks(true);
|
||||||
|
rotationSlider.setMajorTickSpacing(15);
|
||||||
|
rotationSlider.addChangeListener(new ChangeListener() {
|
||||||
|
int previousValue = 0;
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
int value = rotationSlider.getValue();
|
||||||
|
if (value != previousValue) {
|
||||||
|
previousValue = value;
|
||||||
|
rotation = value;
|
||||||
|
t = null;
|
||||||
|
// render();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hp.add(rotationSlider);
|
||||||
|
|
||||||
|
|
||||||
|
final JCheckBox renderQualityControl = new JCheckBox("Render Quality", false);
|
||||||
|
renderQualityControl.addChangeListener(new ChangeListener() {
|
||||||
|
boolean previousValue = false;
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
boolean value = renderQualityControl.isSelected();
|
||||||
|
if (value != previousValue) {
|
||||||
|
previousValue = value;
|
||||||
|
render();
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hp.add(renderQualityControl);
|
||||||
|
|
||||||
|
|
||||||
|
hp.add(new JPanel());
|
||||||
|
|
||||||
|
return hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static void main(String args[])
|
||||||
|
throws Exception
|
||||||
|
{
|
||||||
|
Iterator readers = ImageIO.getImageReadersByFormatName("jpg");
|
||||||
|
ImageReader reader = (ImageReader)readers.next();
|
||||||
|
ImageInputStream iis = ImageIO.createImageInputStream(
|
||||||
|
new File(args[0]));
|
||||||
|
reader.setInput(iis, true);
|
||||||
|
ImageReadParam param = reader.getDefaultReadParam();
|
||||||
|
// param.setSourceSubsampling(8, 8, 0, 0);
|
||||||
|
BufferedImage raw = reader.read(0, param);
|
||||||
|
|
||||||
|
Editor x = new Editor(raw);
|
||||||
|
|
||||||
|
JFrame frame = new JFrame("Editor");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
|
Container c = frame.getContentPane();
|
||||||
|
c.setLayout(new BorderLayout());
|
||||||
|
c.add(x.createControlPanel(), BorderLayout.EAST);
|
||||||
|
c.add(x, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static class CropFrame extends JComponent implements MouseListener, MouseMotionListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private int resizeBand = 6;
|
||||||
|
|
||||||
|
Stroke stroke = new BasicStroke(1f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 1f, new float[] { 5f, 5f }, 0);
|
||||||
|
|
||||||
|
public CropFrame()
|
||||||
|
{
|
||||||
|
setOpaque(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// 1------(5)-------2
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// (8) (6)
|
||||||
|
// | |
|
||||||
|
// | |
|
||||||
|
// 4------(7)-------3
|
||||||
|
//
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g); //paints the background and image
|
||||||
|
|
||||||
|
// Draw a rectangle on top of the image.
|
||||||
|
// g.setColor(Color.WHITE);
|
||||||
|
g.setXORMode(Color.white); //Color of line varies
|
||||||
|
((Graphics2D)g).setStroke(stroke);
|
||||||
|
//depending on image colors
|
||||||
|
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void mouseEntered(MouseEvent e) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void mouseExited(MouseEvent e) {
|
||||||
|
// TODO Auto-generated method stub
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
int r = findRegion(e.getX(), e.getY());
|
||||||
|
if (r < 8) {
|
||||||
|
edge = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
updateFrame(e.getX() + getX(), e.getY() + getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
updateFrame(e.getX() + getX(), e.getY() + getY());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
boolean is(int who, int alt0, int alt1, int alt2) {
|
||||||
|
return who == alt0 || who == alt1 || who == alt2;
|
||||||
|
}
|
||||||
|
int[] oppositeX = new int[] { NE, N, NW, W, SW, S, SE, E };
|
||||||
|
int[] oppositeY = new int[] { SW, S, SE, E, NE, N, NW, W };
|
||||||
|
Cursor[] cursors = new Cursor[] {
|
||||||
|
Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR),
|
||||||
|
Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR),
|
||||||
|
};
|
||||||
|
String[] edgeName = new String[] { "NW", "N", "NE", "E", "SE", "S", "SW", "W" };
|
||||||
|
static final int NW = 0;
|
||||||
|
static final int N = 1;
|
||||||
|
static final int NE = 2;
|
||||||
|
static final int E = 3;
|
||||||
|
static final int SE = 4;
|
||||||
|
static final int S = 5;
|
||||||
|
static final int SW = 6;
|
||||||
|
static final int W = 7;
|
||||||
|
int edge = SE;
|
||||||
|
Rectangle frame = new Rectangle();
|
||||||
|
|
||||||
|
void updateFrame(int x, int y) {
|
||||||
|
getBounds(frame);
|
||||||
|
if (is(edge, NW, W, SW)) {
|
||||||
|
frame.width += frame.x - x;
|
||||||
|
frame.x = x;
|
||||||
|
}
|
||||||
|
if (is(edge, NE, E, SE)) {
|
||||||
|
frame.width = x - frame.x;
|
||||||
|
}
|
||||||
|
if (is(edge, NW, N, NE)) {
|
||||||
|
frame.height += frame.y - y;
|
||||||
|
frame.y = y;
|
||||||
|
}
|
||||||
|
if (is(edge, SW, S, SE)) {
|
||||||
|
frame.height = y - frame.y;
|
||||||
|
}
|
||||||
|
if (frame.width < 0) {
|
||||||
|
frame.x += frame.width;
|
||||||
|
frame.width = -frame.width;
|
||||||
|
edge = oppositeX[edge];
|
||||||
|
}
|
||||||
|
if (frame.height < 0) {
|
||||||
|
frame.y += frame.height;
|
||||||
|
frame.height = -frame.height;
|
||||||
|
edge = oppositeY[edge];
|
||||||
|
}
|
||||||
|
setBounds(frame);
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int findRegion(int ex, int ey) {
|
||||||
|
int w = getWidth();
|
||||||
|
int h = getHeight();
|
||||||
|
int c = 8;
|
||||||
|
|
||||||
|
if (ex > (w - resizeBand)) {
|
||||||
|
if (ey > (h - resizeBand)) {
|
||||||
|
c = SE;
|
||||||
|
} else if (ey < resizeBand) {
|
||||||
|
c = NE;
|
||||||
|
} else {
|
||||||
|
c = E;
|
||||||
|
}
|
||||||
|
} else if (ex < resizeBand) {
|
||||||
|
if (ey > (h - resizeBand)) {
|
||||||
|
c = SW;
|
||||||
|
} else if (ey < resizeBand) {
|
||||||
|
c = NW;
|
||||||
|
} else {
|
||||||
|
c = W;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ey > (h - resizeBand)) {
|
||||||
|
c = S;
|
||||||
|
} else if (ey < resizeBand) {
|
||||||
|
c = N;
|
||||||
|
} else {
|
||||||
|
c = 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseMoved(MouseEvent e) {
|
||||||
|
setCursor(cursors[findRegion(e.getX(), e.getY())]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void log(String x) {
|
||||||
|
System.out.println(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
44
src/album/editor/Makefile
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#
|
||||||
|
# Java interpreter and compiler
|
||||||
|
#
|
||||||
|
JAVA = java
|
||||||
|
JAVAC = javac
|
||||||
|
|
||||||
|
#
|
||||||
|
# Various tools
|
||||||
|
#
|
||||||
|
RM = rm -f
|
||||||
|
|
||||||
|
#
|
||||||
|
# Which libraries do we use?
|
||||||
|
#
|
||||||
|
CLASSPATH=$(HOME)/src/album
|
||||||
|
|
||||||
|
CLASSES = Editor.class
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# The file types we deal with
|
||||||
|
#
|
||||||
|
.SUFFIXES: .java .class
|
||||||
|
|
||||||
|
|
||||||
|
all: $(CLASSES)
|
||||||
|
@echo Done
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) *.class
|
||||||
|
$(RM) *~
|
||||||
|
|
||||||
|
.java.class:
|
||||||
|
$(JAVAC) -classpath $(CLASSPATH) $<
|
||||||
|
|
||||||
|
|
||||||
|
editor: Editor.class
|
||||||
|
$(JAVA) -cp $(CLASSPATH) editor.Editor /home/knut/src/album/photos/l1000802.jpg
|
||||||
|
|
||||||
|
reader: ReaderHelper.class
|
||||||
|
$(JAVA) -cp $(CLASSPATH) editor.ReaderHelper /home/knut/src/album/photos
|
||||||
|
|
||||||
|
|
||||||
|
#eof
|
||||||
BIN
src/album/editor/ReaderHelper$1.class
Normal file
BIN
src/album/editor/ReaderHelper.class
Normal file
BIN
src/album/editor/SelectionDemo$1.class
Normal file
BIN
src/album/editor/SelectionDemo$SelectionArea$MyListener.class
Normal file
BIN
src/album/editor/SelectionDemo$SelectionArea.class
Normal file
BIN
src/album/editor/SelectionDemo.class
Normal file
209
src/album/editor/SelectionDemo.java
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
/* From http://java.sun.com/docs/books/tutorial/index.html */
|
||||||
|
/*
|
||||||
|
* SelectionDemo.java is a 1.4 application that requires one other file:
|
||||||
|
* images/starfield.gif
|
||||||
|
*/
|
||||||
|
|
||||||
|
package album.editor;
|
||||||
|
|
||||||
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.ImageIcon;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.event.MouseInputAdapter;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This displays an image. When the user drags within the image, this program
|
||||||
|
* displays a rectangle and a string indicating the bounds of the rectangle.
|
||||||
|
*/
|
||||||
|
public class SelectionDemo {
|
||||||
|
JLabel label;
|
||||||
|
|
||||||
|
static String starFile = "/home/knut/src/album/photos/forkalsrud-ca-1951.jpg";
|
||||||
|
|
||||||
|
private void buildUI(Container container, ImageIcon image) {
|
||||||
|
container.setLayout(new BoxLayout(container, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
SelectionArea area = new SelectionArea(image, this);
|
||||||
|
container.add(area);
|
||||||
|
|
||||||
|
label = new JLabel("Drag within the image.");
|
||||||
|
label.setLabelFor(area);
|
||||||
|
container.add(label);
|
||||||
|
|
||||||
|
//Align the left edges of the components.
|
||||||
|
area.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
|
label.setAlignmentX(Component.LEFT_ALIGNMENT); //redundant
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateLabel(Rectangle rect) {
|
||||||
|
int width = rect.width;
|
||||||
|
int height = rect.height;
|
||||||
|
|
||||||
|
//Make the coordinates look OK if a dimension is 0.
|
||||||
|
if (width == 0) {
|
||||||
|
width = 1;
|
||||||
|
}
|
||||||
|
if (height == 0) {
|
||||||
|
height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
label.setText("Rectangle goes from (" + rect.x + ", " + rect.y
|
||||||
|
+ ") to (" + (rect.x + width - 1) + ", "
|
||||||
|
+ (rect.y + height - 1) + ").");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns an ImageIcon, or null if the path was invalid. */
|
||||||
|
protected static ImageIcon createImageIcon(String path) {
|
||||||
|
return new ImageIcon(path, "description");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the GUI and show it. For thread safety, this method should be
|
||||||
|
* invoked from the event-dispatching thread.
|
||||||
|
*/
|
||||||
|
private static void createAndShowGUI() {
|
||||||
|
//Make sure we have nice window decorations.
|
||||||
|
JFrame.setDefaultLookAndFeelDecorated(true);
|
||||||
|
|
||||||
|
//Create and set up the window.
|
||||||
|
JFrame frame = new JFrame("SelectionDemo");
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
|
||||||
|
//Set up the content pane.
|
||||||
|
SelectionDemo controller = new SelectionDemo();
|
||||||
|
controller.buildUI(frame.getContentPane(), createImageIcon(starFile));
|
||||||
|
|
||||||
|
//Display the window.
|
||||||
|
frame.pack();
|
||||||
|
frame.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
//Schedule a job for the event-dispatching thread:
|
||||||
|
//creating and showing this application's GUI.
|
||||||
|
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
createAndShowGUI();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SelectionArea extends JLabel {
|
||||||
|
Rectangle currentRect = null;
|
||||||
|
|
||||||
|
Rectangle rectToDraw = null;
|
||||||
|
|
||||||
|
Rectangle previousRectDrawn = new Rectangle();
|
||||||
|
|
||||||
|
SelectionDemo controller;
|
||||||
|
|
||||||
|
public SelectionArea(ImageIcon image, SelectionDemo controller) {
|
||||||
|
super(image); //This component displays an image.
|
||||||
|
this.controller = controller;
|
||||||
|
setOpaque(true);
|
||||||
|
setMinimumSize(new Dimension(10, 10)); //don't hog space
|
||||||
|
|
||||||
|
MyListener myListener = new MyListener();
|
||||||
|
addMouseListener(myListener);
|
||||||
|
addMouseMotionListener(myListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class MyListener extends MouseInputAdapter {
|
||||||
|
public void mousePressed(MouseEvent e) {
|
||||||
|
int x = e.getX();
|
||||||
|
int y = e.getY();
|
||||||
|
currentRect = new Rectangle(x, y, 0, 0);
|
||||||
|
updateDrawableRect(getWidth(), getHeight());
|
||||||
|
repaint();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseDragged(MouseEvent e) {
|
||||||
|
updateSize(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void mouseReleased(MouseEvent e) {
|
||||||
|
updateSize(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the size of the current rectangle and call repaint.
|
||||||
|
* Because currentRect always has the same origin, translate it if
|
||||||
|
* the width or height is negative.
|
||||||
|
*
|
||||||
|
* For efficiency (though that isn't an issue for this program),
|
||||||
|
* specify the painting region using arguments to the repaint()
|
||||||
|
* call.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void updateSize(MouseEvent e) {
|
||||||
|
int x = e.getX();
|
||||||
|
int y = e.getY();
|
||||||
|
currentRect.setSize(x - currentRect.x, y - currentRect.y);
|
||||||
|
updateDrawableRect(getWidth(), getHeight());
|
||||||
|
Rectangle totalRepaint = rectToDraw.union(previousRectDrawn);
|
||||||
|
repaint(totalRepaint.x, totalRepaint.y, totalRepaint.width,
|
||||||
|
totalRepaint.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g); //paints the background and image
|
||||||
|
|
||||||
|
//If currentRect exists, paint a box on top.
|
||||||
|
if (currentRect != null) {
|
||||||
|
//Draw a rectangle on top of the image.
|
||||||
|
g.setXORMode(Color.white); //Color of line varies
|
||||||
|
//depending on image colors
|
||||||
|
g.drawRect(rectToDraw.x, rectToDraw.y, rectToDraw.width - 1,
|
||||||
|
rectToDraw.height - 1);
|
||||||
|
|
||||||
|
controller.updateLabel(rectToDraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateDrawableRect(int compWidth, int compHeight) {
|
||||||
|
int x = currentRect.x;
|
||||||
|
int y = currentRect.y;
|
||||||
|
int width = currentRect.width;
|
||||||
|
int height = currentRect.height;
|
||||||
|
|
||||||
|
//Make the width and height positive, if necessary.
|
||||||
|
if (width < 0) {
|
||||||
|
width = 0 - width;
|
||||||
|
x = x - width + 1;
|
||||||
|
if (x < 0) {
|
||||||
|
width += x;
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (height < 0) {
|
||||||
|
height = 0 - height;
|
||||||
|
y = y - height + 1;
|
||||||
|
if (y < 0) {
|
||||||
|
height += y;
|
||||||
|
y = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//The rectangle shouldn't extend past the drawing area.
|
||||||
|
if ((x + width) > compWidth) {
|
||||||
|
width = compWidth - x;
|
||||||
|
}
|
||||||
|
if ((y + height) > compHeight) {
|
||||||
|
height = compHeight - y;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update rectToDraw after saving old value.
|
||||||
|
if (rectToDraw != null) {
|
||||||
|
previousRectDrawn.setBounds(rectToDraw.x, rectToDraw.y,
|
||||||
|
rectToDraw.width, rectToDraw.height);
|
||||||
|
rectToDraw.setBounds(x, y, width, height);
|
||||||
|
} else {
|
||||||
|
rectToDraw = new Rectangle(x, y, width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/album/web/AlbumServlet.java
Normal file
|
|
@ -0,0 +1,122 @@
|
||||||
|
package album.web;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.*;
|
||||||
|
|
||||||
|
public class AlbumServlet
|
||||||
|
extends HttpServlet
|
||||||
|
{
|
||||||
|
File baseDir = new File("/home/knut");
|
||||||
|
|
||||||
|
public void init()
|
||||||
|
throws ServletException
|
||||||
|
{
|
||||||
|
System.out.println("in init of Album");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||||
|
throws ServletException, IOException
|
||||||
|
{
|
||||||
|
/* System.out.println("pathInfo: " + req.getPathInfo());
|
||||||
|
System.out.println("pathXlat: " + req.getPathTranslated());
|
||||||
|
System.out.println("queryString: " + req.getQueryString());
|
||||||
|
System.out.println("requestUri: " + req.getRequestURI());
|
||||||
|
System.out.println("servletPath: " + req.getServletPath());
|
||||||
|
*/
|
||||||
|
res.setContentType("text/html");
|
||||||
|
// Determine which directory to list
|
||||||
|
String directory = req.getServletPath();
|
||||||
|
directory = directory.substring(
|
||||||
|
"/".length(),
|
||||||
|
directory.length() - ".album".length());
|
||||||
|
|
||||||
|
System.out.println("directory: " + directory);
|
||||||
|
|
||||||
|
File dir = new File(baseDir, directory);
|
||||||
|
if (dir == null) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir.isDirectory()) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dir.canRead()) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine sort order
|
||||||
|
File[] files = dir.listFiles(new FileFilter() {
|
||||||
|
public boolean accept(File candidate) {
|
||||||
|
return candidate.isDirectory()
|
||||||
|
|| candidate.getName().endsWith(".gif")
|
||||||
|
|| candidate.getName().endsWith(".GIF")
|
||||||
|
|| candidate.getName().endsWith(".png")
|
||||||
|
|| candidate.getName().endsWith(".PNG")
|
||||||
|
|| candidate.getName().endsWith(".jpg")
|
||||||
|
|| candidate.getName().endsWith(".JPG")
|
||||||
|
|| candidate.getName().endsWith(".jpeg")
|
||||||
|
|| candidate.getName().endsWith(".JPEG");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Arrays.sort(files, new Comparator<File>() {
|
||||||
|
public int compare(File f1, File f2) {
|
||||||
|
|
||||||
|
// Directories first
|
||||||
|
if (f1.isDirectory() && !f2.isDirectory())
|
||||||
|
return -1;
|
||||||
|
if (f2.isDirectory() && !f1.isDirectory())
|
||||||
|
return 1;
|
||||||
|
return f1.getName().compareTo(f2.getName());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Produce <img> tags for a thumbnail of each image or other
|
||||||
|
// album
|
||||||
|
PrintWriter out = res.getWriter();
|
||||||
|
out.println("<html>");
|
||||||
|
out.println("<head>");
|
||||||
|
out.println("<title>" + directory + "</title>");
|
||||||
|
out.println("</head>");
|
||||||
|
out.println("<body>");
|
||||||
|
|
||||||
|
String baseUrl = req.getContextPath()
|
||||||
|
+ "/" + directory + "/";
|
||||||
|
|
||||||
|
for (int i = 0; i < files.length; ++i) {
|
||||||
|
File file = files[i];
|
||||||
|
String anchorUrl = "";
|
||||||
|
String iconUrl = "";
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
anchorUrl = baseUrl + file.getName() + ".album";
|
||||||
|
iconUrl = req.getContextPath() + "/album.png";
|
||||||
|
} else if (file.isFile()) {
|
||||||
|
anchorUrl = baseUrl + file.getName() + ".photo";
|
||||||
|
iconUrl = baseUrl + file.getName() + ".bitmap?size=100";
|
||||||
|
} else {
|
||||||
|
throw new ServletException("Unknown file: " + file);
|
||||||
|
}
|
||||||
|
|
||||||
|
out.print("<a href=\"" + anchorUrl + "\">");
|
||||||
|
out.print("<img hspace=5 vspace=5 src=\"" + iconUrl + "\">");
|
||||||
|
out.println("</a>");
|
||||||
|
}
|
||||||
|
out.println("</body>");
|
||||||
|
out.println("</html>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getServletInfo() {
|
||||||
|
return "Display a directory as an album";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
223
src/album/web/BitmapServlet.java
Normal file
|
|
@ -0,0 +1,223 @@
|
||||||
|
package album.web;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
import javax.imageio.*;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.*;
|
||||||
|
|
||||||
|
public class BitmapServlet
|
||||||
|
extends HttpServlet
|
||||||
|
implements ImageObserver
|
||||||
|
{
|
||||||
|
File baseDir = new File("/home/knut");
|
||||||
|
File cacheDir = new File("/var/album");
|
||||||
|
|
||||||
|
final static int BUF_SIZE = 1024;
|
||||||
|
protected volatile boolean complete = false;
|
||||||
|
protected int size;
|
||||||
|
|
||||||
|
|
||||||
|
public void init()
|
||||||
|
throws ServletException
|
||||||
|
{
|
||||||
|
System.out.println("in init of Bitmap");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
File cacheFile(String filename, int size) {
|
||||||
|
return new File(cacheDir, filename + "-" + size + ".png");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||||
|
throws ServletException, IOException
|
||||||
|
{
|
||||||
|
res.setContentType("image/png");
|
||||||
|
|
||||||
|
// Determine which file to show
|
||||||
|
String filename = req.getServletPath();
|
||||||
|
filename = filename.substring(
|
||||||
|
"/".length(),
|
||||||
|
filename.length() - ".bitmap".length());
|
||||||
|
|
||||||
|
System.out.println("file: " + filename);
|
||||||
|
|
||||||
|
File file = new File(baseDir, filename);
|
||||||
|
if (file == null) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.isFile()) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.canRead()) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size = Integer.parseInt(req.getParameter("size"));
|
||||||
|
|
||||||
|
File cacheFile = cacheFile(filename, size);
|
||||||
|
|
||||||
|
if (!cacheFile.exists()) {
|
||||||
|
writeJpegImage(file, size, cacheFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
OutputStream out = res.getOutputStream();
|
||||||
|
FileInputStream infile = new FileInputStream(cacheFile);
|
||||||
|
byte[] buf = new byte[BUF_SIZE];
|
||||||
|
int chunksize;
|
||||||
|
do {
|
||||||
|
chunksize = infile.read(buf);
|
||||||
|
if (chunksize > 0)
|
||||||
|
out.write(buf, 0, chunksize);
|
||||||
|
} while (!(chunksize < 0));
|
||||||
|
infile.close();
|
||||||
|
} catch (FileNotFoundException e1) {
|
||||||
|
System.err.println("File creation error! " + e1.getMessage());
|
||||||
|
} catch (IOException e2) {
|
||||||
|
System.err.println("IO error! " + e2.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DONE = (ABORT | ALLBITS | ERROR | FRAMEBITS);
|
||||||
|
|
||||||
|
int previousInfoFlags = 0;
|
||||||
|
// HEIGHT PROPERTIES SOMEBITS WIDTH
|
||||||
|
public boolean imageUpdate(Image img, int infoflags,
|
||||||
|
int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
if (infoflags != previousInfoFlags) {
|
||||||
|
/* StringBuffer b = new StringBuffer("Flags:");
|
||||||
|
if ((infoflags & ABORT) != 0)
|
||||||
|
b.append(" ABORT");
|
||||||
|
if ((infoflags & ALLBITS) != 0)
|
||||||
|
b.append(" ALLBITS");
|
||||||
|
if ((infoflags & ERROR) != 0)
|
||||||
|
b.append(" ERROR");
|
||||||
|
if ((infoflags & FRAMEBITS) != 0)
|
||||||
|
b.append(" FRAMEBITS");
|
||||||
|
if ((infoflags & HEIGHT) != 0)
|
||||||
|
b.append(" HEIGHT");
|
||||||
|
if ((infoflags & WIDTH) != 0)
|
||||||
|
b.append(" WIDTH");
|
||||||
|
if ((infoflags & PROPERTIES) != 0)
|
||||||
|
b.append(" PROPERTIES");
|
||||||
|
if ((infoflags & SOMEBITS) != 0)
|
||||||
|
b.append(" SOMEBITS");
|
||||||
|
System.err.println(b.toString());
|
||||||
|
*/
|
||||||
|
previousInfoFlags = infoflags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((infoflags & DONE) != 0) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.complete = true;
|
||||||
|
// new Throwable().printStackTrace(System.err);
|
||||||
|
notify();
|
||||||
|
// System.err.println("Wakeup! " + this.complete + " " + this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Dimension scaleMaxExtent(int extent, Dimension in)
|
||||||
|
{
|
||||||
|
Dimension original = new Dimension(in);
|
||||||
|
if (extent > 0) {
|
||||||
|
if (original.width > original.height && original.width > extent) {
|
||||||
|
original.height *= extent;
|
||||||
|
original.height /= original.width;
|
||||||
|
original.width = extent;
|
||||||
|
} else if (original.height > extent) {
|
||||||
|
original.width *= extent;
|
||||||
|
original.width /= original.height;
|
||||||
|
original.height = extent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void writeJpegImage(File source, int size, File destination)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
BufferedImage img = ImageIO.read(source);
|
||||||
|
Dimension dim = scaleMaxExtent(size, new Dimension(
|
||||||
|
img.getWidth(), img.getHeight()));
|
||||||
|
|
||||||
|
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
complete = false;
|
||||||
|
boolean alreadyDone = tk.prepareImage(img, -1, -1, this);
|
||||||
|
complete = complete || alreadyDone;
|
||||||
|
|
||||||
|
// Suspend until the image is rendered completely
|
||||||
|
for (int i = 0; !this.complete && i < 10; ++i)
|
||||||
|
try {
|
||||||
|
// System.err.println("Waiting 1: " + this.complete + " " + this);
|
||||||
|
wait(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BufferedImage buff = new BufferedImage(
|
||||||
|
dim.width, dim.height, BufferedImage.TYPE_INT_RGB);
|
||||||
|
Graphics g = buff.createGraphics();
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
complete = false;
|
||||||
|
|
||||||
|
boolean alreadyDone = g.drawImage(img, 0, 0, dim.width, dim.height, this);
|
||||||
|
|
||||||
|
complete = complete || alreadyDone;
|
||||||
|
|
||||||
|
// Suspend until the image is rendered completely
|
||||||
|
for (int i = 0; !complete && i < 10; ++i)
|
||||||
|
try {
|
||||||
|
// System.err.println("Waiting 2: " + this.complete);
|
||||||
|
wait(2000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > 120 || size == 0) {
|
||||||
|
g.setColor(Color.cyan);
|
||||||
|
int lineHeight = g.getFontMetrics().getHeight();
|
||||||
|
int year = Calendar.getInstance().get(Calendar.YEAR);
|
||||||
|
g.drawString("<EFBFBD> " + year + " forkalsrud.org", 5, 5 + lineHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
destination.getParentFile().mkdirs();
|
||||||
|
FileOutputStream out = new FileOutputStream(destination);
|
||||||
|
ImageIO.write(buff, "png", out);
|
||||||
|
out.close();
|
||||||
|
/* System.err.println("OK: " + ok
|
||||||
|
+ " Writers : "
|
||||||
|
+ Arrays.asList(ImageIO.getWriterFormatNames()));
|
||||||
|
*/
|
||||||
|
} catch (FileNotFoundException e1) {
|
||||||
|
System.err.println("File creation error! " + e1.getMessage());
|
||||||
|
} catch (IOException e2) {
|
||||||
|
System.err.println("IO error! " + e2.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
171
src/album/web/PhotoServlet.java
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
package album.web;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
import javax.imageio.*;
|
||||||
|
|
||||||
|
import javax.servlet.*;
|
||||||
|
import javax.servlet.http.*;
|
||||||
|
|
||||||
|
public class PhotoServlet
|
||||||
|
extends HttpServlet
|
||||||
|
implements ImageObserver
|
||||||
|
{
|
||||||
|
File baseDir = new File("/home/knut");
|
||||||
|
File cacheDir = new File("/var/album");
|
||||||
|
|
||||||
|
final static int BUF_SIZE = 1024;
|
||||||
|
protected volatile boolean complete = false;
|
||||||
|
protected int size;
|
||||||
|
|
||||||
|
|
||||||
|
public void init()
|
||||||
|
throws ServletException
|
||||||
|
{
|
||||||
|
System.out.println("in init of Bitmap");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
File cacheFile(String filename, int size) {
|
||||||
|
return new File(cacheDir, filename + "-" + size + ".jpeg");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void doGet(HttpServletRequest req, HttpServletResponse res)
|
||||||
|
throws ServletException, IOException
|
||||||
|
{
|
||||||
|
res.setContentType("text/plain");
|
||||||
|
|
||||||
|
// Determine which file to show
|
||||||
|
String filename = req.getServletPath();
|
||||||
|
filename = filename.substring(
|
||||||
|
"/".length(),
|
||||||
|
filename.length() - ".photo".length());
|
||||||
|
|
||||||
|
System.out.println("file: " + filename);
|
||||||
|
|
||||||
|
File file = new File(baseDir, filename);
|
||||||
|
if (file == null) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.isFile()) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_NOT_FOUND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.canRead()) {
|
||||||
|
res.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintWriter out = res.getWriter();
|
||||||
|
|
||||||
|
|
||||||
|
String[] formats = ImageIO.getReaderFormatNames();
|
||||||
|
out.println(Arrays.asList(formats).toString());
|
||||||
|
|
||||||
|
Iterator ii = ImageIO.getImageReadersByFormatName("jpeg");
|
||||||
|
while (ii.hasNext()) {
|
||||||
|
ImageReader ir = (ImageReader)ii.next();
|
||||||
|
|
||||||
|
Class[] inputs = ir.getOriginatingProvider().getInputTypes();
|
||||||
|
out.println(" inputs: " + Arrays.asList(inputs).toString());
|
||||||
|
|
||||||
|
ir.setInput(ImageIO.createImageInputStream(file));
|
||||||
|
|
||||||
|
int cnt = ir.getNumImages(true);
|
||||||
|
|
||||||
|
int thCnt = ir.getNumThumbnails(0);
|
||||||
|
|
||||||
|
out.println("Num images: " + cnt + " thumbnails: " + thCnt);
|
||||||
|
|
||||||
|
// BufferedImage th = ir.readThumbnail(0, 0);
|
||||||
|
|
||||||
|
// out.println(" Thumbnail size: " + th.getWidth() + "x" + th.getWidth());
|
||||||
|
ir.dispose();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
EXIFInfo info = new EXIFInfo(file);
|
||||||
|
Map props = info.getEXIFMetaData();
|
||||||
|
|
||||||
|
Set s = props.entrySet();
|
||||||
|
Iterator i = s.iterator();
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
Map.Entry e = (Map.Entry)i.next();
|
||||||
|
out.println(e.getKey() + ": " + e.getValue());
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int DONE = (ABORT | ALLBITS | ERROR | FRAMEBITS);
|
||||||
|
|
||||||
|
int previousInfoFlags = 0;
|
||||||
|
// HEIGHT PROPERTIES SOMEBITS WIDTH
|
||||||
|
public boolean imageUpdate(Image img, int infoflags,
|
||||||
|
int x, int y, int width, int height)
|
||||||
|
{
|
||||||
|
if (infoflags != previousInfoFlags) {
|
||||||
|
/* StringBuffer b = new StringBuffer("Flags:");
|
||||||
|
if ((infoflags & ABORT) != 0)
|
||||||
|
b.append(" ABORT");
|
||||||
|
if ((infoflags & ALLBITS) != 0)
|
||||||
|
b.append(" ALLBITS");
|
||||||
|
if ((infoflags & ERROR) != 0)
|
||||||
|
b.append(" ERROR");
|
||||||
|
if ((infoflags & FRAMEBITS) != 0)
|
||||||
|
b.append(" FRAMEBITS");
|
||||||
|
if ((infoflags & HEIGHT) != 0)
|
||||||
|
b.append(" HEIGHT");
|
||||||
|
if ((infoflags & WIDTH) != 0)
|
||||||
|
b.append(" WIDTH");
|
||||||
|
if ((infoflags & PROPERTIES) != 0)
|
||||||
|
b.append(" PROPERTIES");
|
||||||
|
if ((infoflags & SOMEBITS) != 0)
|
||||||
|
b.append(" SOMEBITS");
|
||||||
|
System.err.println(b.toString());
|
||||||
|
*/
|
||||||
|
previousInfoFlags = infoflags;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((infoflags & DONE) != 0) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.complete = true;
|
||||||
|
// new Throwable().printStackTrace(System.err);
|
||||||
|
notify();
|
||||||
|
// System.err.println("Wakeup! " + this.complete + " " + this);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Dimension scaleMaxExtent(int extent, Dimension in)
|
||||||
|
{
|
||||||
|
Dimension original = new Dimension(in);
|
||||||
|
if (extent > 0) {
|
||||||
|
if (original.width > original.height && original.width > extent) {
|
||||||
|
original.height *= extent;
|
||||||
|
original.height /= original.width;
|
||||||
|
original.width = extent;
|
||||||
|
} else if (original.height > extent) {
|
||||||
|
original.width *= extent;
|
||||||
|
original.width /= original.height;
|
||||||
|
original.height = extent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// eof
|
||||||
10
webapp/WEB-INF/web.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<web-app>
|
||||||
|
|
||||||
|
<servlet-mapping url-pattern="*.album"
|
||||||
|
servlet-name="album.AlbumServlet" />
|
||||||
|
<servlet-mapping url-pattern="*.photo"
|
||||||
|
servlet-name="album.PhotoServlet" />
|
||||||
|
<servlet-mapping url-pattern="*.bitmap"
|
||||||
|
servlet-name="album.BitmapServlet" />
|
||||||
|
|
||||||
|
</web-app>
|
||||||