View Javadoc
1   /******************************************************************************
2    * VectorRibbon.java - Make a Mosaic
3    * 
4    * PicMan - The BuckoSoft Picture Manager in Java
5    * Copyright(c) 2008 - Dick Balaska
6    * 
7    */
8   package com.buckosoft.PicMan.business.mosaic.engine;
9   
10  import java.awt.Graphics2D;
11  import java.awt.image.BufferedImage;
12  import java.util.Iterator;
13  import java.util.List;
14  
15  import org.apache.commons.logging.Log;
16  import org.apache.commons.logging.LogFactory;
17  
18  import com.buckosoft.PicMan.business.mosaic.MosaicEngine;
19  import com.buckosoft.PicMan.domain.MosaicTile;
20  import com.buckosoft.PicMan.domain.Pic;
21  import com.buckosoft.PicMan.domain.mosaic.MosaicVector;
22  
23  /** This engine walks through each row of a Pic and places the best tile there.
24   * "Best" is determined by subtracting the precalculated MosaicVectors from the Pic and summing the different in each pixel.
25   * This is performed from each tile in the set, and the one with the least difference is chosen.  
26   * @author Dick Balaska
27   * @since 2008/01/25
28   * @see <a href="http://cvs.buckosoft.com/Projects/java/PicMan/PicMan/src/main/java/com/buckosoft/PicMan/business/mosaic/engine/VectorRibbon.java">VectorRibbon.java</a>
29   */
30  public class VectorRibbon extends MosaicEngine {
31  	private static final boolean DEBUG = true;
32  	private final Log logger = LogFactory.getLog(getClass());
33  	
34  	private	int		curRow = 0;
35  	private	int		curCol = 0;
36  	private	int		maxRow;
37  	private	int		maxCol;
38  	private	int		p;
39  	private	List<Pic>			picList;
40  	@SuppressWarnings("unused")
41  	private	List<MosaicVector>	vectorList;
42  	private	String	info = "N/A";
43  	private	long[]	used;
44  	
45  	/** Return our current build status
46  	 * @return The status
47  	 */
48  	public	String	getStatus() {
49  		StringBuilder sb = new StringBuilder();
50  		sb.append("curRow = ");
51  		sb.append(curRow);
52  		sb.append(" curCol = ");
53  		sb.append(curCol);
54  		sb.append(" p=");
55  		sb.append(p);
56  		return(sb.toString());
57  	}
58  
59  	public String getInfo() {
60  		return(info);
61  	}
62  
63  	protected boolean _build() {
64  		Graphics2D	gd = bi.createGraphics();
65  		
66  		// 75, pull from this size set even though the size calc should be able to deal with, say, a 50 and figure it out.
67  		picList = pmf.getDB().getPics(this.mosaicSet, 75);
68  		vectorList = pmf.getDB().getMosaicVectors(picList);
69  		if (DEBUG)
70  			logger.info("Working from " + picList.size() + " pics");
71  		maxRow = this.masterPic.getHeight();
72  		maxCol = this.masterPic.getWidth();
73  		//int		rowHeight = maxRow / tileHeight;
74  		long[]	rate = new long[picList.size()];		// rate each pic for this location
75  				used = new long[picList.size()];		// number of times each pic is used
76  		info = "maxRow=" + maxRow + " maxCol=" + maxCol + " pics=" + picList.size();
77  		if (DEBUG)
78  			logger.info("maxRow=" + maxRow + " rowHeight=" + tileHeight);
79  		//restoreBuild(gd);
80  		for (; curRow<maxRow; curRow += tileHeight, curCol=0) {
81  			for (; curCol<maxCol; ) {
82  				int	x,y;
83  				for (p=0; p<picList.size(); p++) {
84  					BufferedImage mbi = pmf.getMosaicThumbNail(picList.get(p), tileHeight).getImage();
85  					//BufferedImage mbi = pmf.getThumbNail(null, tileHeight).getImage();
86  					rate[p] = used[p];
87  					for (x=0; x<mbi.getWidth(); x++) {
88  						for (y=0; y<mbi.getHeight(); y++) {
89  							int mpx = mbi.getRGB(x, y);
90  							int	ppx;
91  							if (x+curCol >= bi.getWidth() || y+curRow >= bi.getHeight())
92  								ppx = mpx;
93  							else
94  								ppx = bi.getRGB(x+curCol, y+ curRow);
95  							rate[p] += Math.abs((mpx - ppx) & 0xFF);
96  							rate[p] += Math.abs(((mpx-ppx) >> 8) & 0xFF);
97  							rate[p] += Math.abs(((mpx-ppx) >> 16) & 0xFF);
98  						}
99  					}
100 					// Portrait always wins over landscape because there is less pixels to go wrong.
101 					// Give equal weight per pixel column
102 					rate[p] /= mbi.getWidth();
103 					//logger.info("rate[" + p + "]=" + rate[p] + " ("+ picList.get(p).getName() + ")");
104 				}
105 				long best = Long.MAX_VALUE;
106 				int	besti = -1;
107 				int	besti2 = -1;
108 				for (x=0; x<rate.length; x++) {
109 					if (rate[x] < best) {
110 						besti2 = besti;
111 						best = rate[x];
112 						besti = x;
113 					}
114 				}
115 				if (DEBUG) {
116 					logger.info("besti  = " + besti + " (" + picList.get(besti).getName() + ") = " + rate[besti]);
117 					logger.info("besti2 = " + besti2 + " (" + picList.get(besti2).getName() + ") = " + rate[besti2]);
118 				}
119 				used[besti]++;
120 				BufferedImage mbi = pmf.getThumbNail(picList.get(besti), tileHeight).getImage();
121 				if (!mosaic.isBatch()) {
122 					MosaicTile	tile = new MosaicTile(this.mid, picList.get(besti).getPid(), curCol, curRow, mbi.getWidth(), mbi.getHeight());
123 					pmf.getDB().storeMosaicTile(tile);
124 				}
125 				gd.drawImage(mbi, null, curCol, curRow);
126 				curCol += mbi.getWidth();
127 				setLastMosaicUpdate();
128 			}
129 		}
130 		return(false);
131 	}
132 
133 	@SuppressWarnings("unused")
134 	private	void restoreBuild(Graphics2D gd) {
135 		List<MosaicTile> lmt = pmf.getDB().getMosaicTiles(this.mid);
136 		if (DEBUG)
137 			logger.info("Restoring " + lmt.size() + " tiles");
138 		for (; curRow<maxRow; curRow += tileHeight, curCol=0) {
139 			for (; curCol<maxCol; ) {
140 				boolean found = false;
141 				Iterator<MosaicTile> iter = lmt.iterator();
142 				while (iter.hasNext()) {
143 					MosaicTile mt = iter.next();
144 					if (mt.getX() == curCol && mt.getY() == curRow) {
145 						int besti = getPicListIndex(mt.getPid());
146 						if (DEBUG)
147 							logger.info("restore: besti = " + besti + " (" + picList.get(besti).getName() + ")");
148 						used[besti]++;
149 						BufferedImage mbi = pmf.getThumbNail(picList.get(besti), tileHeight).getImage();
150 						gd.drawImage(mbi, null, curCol, curRow);
151 						curCol += mbi.getWidth();
152 						setLastMosaicUpdate();
153 						found = true;
154 						break;
155 					}
156 				}
157 				if (!found)
158 					return;
159 			}
160 		}
161 	}
162 	
163 	private	int	getPicListIndex(int pid) {
164 		for (int i=0; i<picList.size(); i++) {
165 			Pic p = picList.get(i);
166 			if (p.getPid() == pid)
167 				return(i);
168 		}
169 		return(-1);
170 	}
171 }