Initial checkin

This commit is contained in:
knut 2007-04-01 02:44:17 +00:00
commit e77f61825e
48 changed files with 1519 additions and 0 deletions

7
.classpath Normal file
View 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
View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

60
design.txt Normal file
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
photos/fra_jakt.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
photos/l1000729.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

BIN
photos/l1000802.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 814 KiB

BIN
photos/portraits/anton-rudi.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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

Binary file not shown.

View 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

Binary file not shown.

View 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

Binary file not shown.

View 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

Binary file not shown.

View 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

Binary file not shown.

View 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

Binary file not shown.

View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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
View 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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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);
}
}
}
}

View 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

View 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

View 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
View 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>