View Javadoc
1   /******************************************************************************
2    * SecondRibbon.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  
22  /** Rework FirstMosaic so that we can minimize the number of file reads.
23   * This engine walks through each row of a Pic and places the best tile there.
24   * "Best" is determined by subtracting the tile 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/22
28   * @see <a href="http://cvs.buckosoft.com/Projects/java/PicMan/PicMan/src/main/java/com/buckosoft/PicMan/business/mosaic/engine/SecondRibbon.java">SecondRibbon.java</a>
29   */
30  public class SecondRibbon extends MosaicEngine {
31  	private static final boolean DEBUG = false;
32  	private final Log logger = LogFactory.getLog(getClass());
33  	
34  	private	int		curRow = 0;
35  	private	int[]	curCols;
36  	private	int		curCol;
37  	private	int		maxRow;
38  	private	int		maxCol;
39  	private	int		p;
40  	private	List<Pic>	picList;
41  	private	String	info = "N/A";
42  	private	int[]	used;
43  	private	int		tileRows;
44  	private	String	buildStep = "Idle";
45  	private	int		pass = -1;
46  	
47  	/** Return our current build status
48  	 * @return The status
49  	 */
50  	public	String	getStatus() {
51  		StringBuilder sb = new StringBuilder();
52  		sb.append(buildStep);
53  		sb.append(": pass=");
54  		sb.append(pass);
55  		if (!buildStep.equals("Calc")) {
56  			sb.append(" curRow = ");
57  			sb.append(curRow);
58  			sb.append(" curCol = ");
59  			sb.append(curCol);
60  		}
61  		sb.append(" p=");
62  		sb.append(p);
63  		return(sb.toString());
64  	}
65  
66  	/** Return a semi-static info string
67  	 * @return "tileRows= y maxCol= x pics=n
68  	 */
69  	public String getInfo() {
70  		return(info);
71  	}
72  
73  	protected boolean _build() {
74  		Graphics2D	gd = bi.createGraphics();
75  		
76  		// 75, pull from this size set even though the size calc should be able to deal with, say, a 50 and figure it out.
77  		picList = dbf.getPics(this.mosaicSet, tileHeight);
78  		if (DEBUG)
79  			logger.info("Working from " + picList.size() + " pics");
80  		maxRow = this.masterPic.getHeight();
81  		maxCol = this.masterPic.getWidth();
82  		tileRows = maxRow / tileHeight;
83  		if (tileRows * tileHeight < maxRow)
84  			tileRows++;
85  		curCols = new int[tileRows];
86  		
87  		long[][]	rate = new long[tileRows][picList.size()];		// rate each pic for this location
88  				used = new int[picList.size()];		// number of times each pic is used
89  		info = "tileRows=" + tileRows + " maxCol=" + maxCol + " pics=" + picList.size();
90  		if (DEBUG)
91  			logger.info("tileRows=" + tileRows + " rowHeight=" + tileHeight);
92  		if (!mosaic.isBatch())
93  			restoreBuild(gd);
94  		boolean workDone = true;
95  		while (workDone) {
96  			pass++;
97  			workDone = false;
98  			BufferedImage mbi;
99  			int	x,y;
100 			buildStep = "Calc";
101 			for (p=0; p<picList.size(); p++) {
102 				mbi = pmf.getMosaicThumbNail(picList.get(p), tileHeight).getImage();
103 				for (curRow=0; curRow<tileRows; curRow++) {
104 					curCol = curCols[curRow];
105 					int	cr = curRow * tileHeight;
106 //					if (DEBUG) {
107 //						if (curRow == 28 && pass == 4)
108 //							logger.info("pass 4 row 28");
109 //					}
110 					if (curCol < maxCol) {
111 						workDone = true;
112 //						rate[curRow][p] = used[p]*500;
113 						rate[curRow][p] = used[p]*0;
114 						for (x=0; x<mbi.getWidth(); x++) {
115 							for (y=0; y<mbi.getHeight(); y++) {
116 								int mpx = mbi.getRGB(x, y);
117 								int	ppx;
118 								if (x+curCol >= bi.getWidth() || y+cr >= bi.getHeight())
119 									ppx = mpx;
120 								else
121 									ppx = bi.getRGB(x+curCol, y+cr);
122 //								rate[curRow][p] += Math.abs(((mpx)&0xFF)     - ((ppx)&0xFF))     * 30;
123 //								rate[curRow][p] += Math.abs(((mpx>>8)&0xFF)  - ((ppx>>8)&0xFF))  * 59;
124 //								rate[curRow][p] += Math.abs(((mpx>>16)&0xFF) - ((ppx>>16)&0xFF)) * 11;
125 								rate[curRow][p] += Math.abs(((mpx)&0xFF)     - ((ppx)&0xFF))     * 1;
126 								rate[curRow][p] += Math.abs(((mpx>>8)&0xFF)  - ((ppx>>8)&0xFF))  * 1;
127 								rate[curRow][p] += Math.abs(((mpx>>16)&0xFF) - ((ppx>>16)&0xFF)) * 1;
128 							}
129 						}
130 						// Portrait always wins over landscape because there is less pixels to go wrong.
131 						// Give equal weight per pixel column
132 						rate[curRow][p] /= mbi.getWidth();
133 						//logger.info("rate[" + p + "]=" + rate[p] + " ("+ picList.get(p).getName() + ")");
134 					}
135 				}
136 				if (!workDone)
137 					break;
138 				if (!pmf.isEngineOn())
139 					return(true);
140 			}
141 			buildStep = "Draw";
142 			for (curRow=0; curRow<tileRows; curRow++) {
143 				curCol = curCols[curRow];
144 				int cr = curRow * tileHeight;
145 				if (curCol >= maxCol)
146 					continue;
147 				long best = Long.MAX_VALUE;
148 				int	besti = 0;
149 				int	besti2 = 0;
150 				for (x=0; x<rate[curRow].length; x++) {
151 					if (rate[curRow][x] < best) {
152 						besti2 = besti;
153 						best = rate[curRow][x];
154 						besti = x;
155 					}
156 				}
157 				//p = besti;
158 				if (DEBUG) {
159 					if (besti == -1)
160 						logger.info("row=" + curRow + " besti == -1");
161 					else
162 						logger.info("besti = " + besti + " (" + picList.get(besti).getName() + ") = " + rate[curRow][besti]);
163 					if (besti2 == -1)
164 						logger.info("row=" + curRow + " besti2 == -1");
165 					else
166 						logger.info("besti2 = " + besti2 + " (" + picList.get(besti2).getName() + ") = " + rate[curRow][besti2]);
167 				}
168 				used[besti]++;
169 				mbi = pmf.getMosaicThumbNail(picList.get(besti), tileHeight).getImage();
170 				if (!mosaic.isBatch()) {
171 					MosaicTile	tile = new MosaicTile(this.mid, picList.get(besti).getPid(), curCol, cr, mbi.getWidth(), mbi.getHeight());
172 					dbf.storeMosaicTile(tile);
173 				}
174 				gd.drawImage(mbi, null, curCol, cr);
175 				curCols[curRow] += mbi.getWidth();
176 				setLastMosaicUpdate();
177 			}
178 			if (!mosaic.isBatch())
179 				dbf.storeMosaic(mosaic);
180 		}
181 		buildStep = "Done";
182 		return(false);
183 	}
184 
185 	private	void restoreBuild(Graphics2D gd) {
186 		List<MosaicTile> lmt = dbf.getMosaicTiles(this.mid);
187 		if (DEBUG)
188 			logger.info("Restoring " + lmt.size() + " tiles");
189 		boolean workDone = true;
190 		buildStep = "Restore";
191 		while (workDone) {
192 			workDone = false;
193 			pass++;
194 			updateRestoreStatus();
195 			for (curRow=0; curRow<tileRows; curRow++) {
196 				curCol = curCols[curRow];
197 				int	cr = curRow * tileHeight;
198 				Iterator<MosaicTile> iter = lmt.iterator();
199 				while (iter.hasNext()) {
200 					MosaicTile mt = iter.next();
201 					if (mt.getX() == curCol && mt.getY() == cr) {
202 						int besti = getPicListIndex(mt.getPid());
203 						if (DEBUG)
204 							logger.info("restore: besti = " + besti + " (" + picList.get(besti).getName() + ")");
205 						used[besti]++;
206 						BufferedImage mbi = pmf.getThumbNail(picList.get(besti), tileHeight).getImage();
207 						gd.drawImage(mbi, null, curCol, cr);
208 						curCols[curRow] += mbi.getWidth();
209 						setLastMosaicUpdate();
210 						workDone = true;
211 						break;
212 					}
213 				}
214 			}
215 		}
216 	}
217 	
218 	private	void updateRestoreStatus() {
219 		buildStep = "Restore";
220 	}
221 
222 	private	int	getPicListIndex(int pid) {
223 		for (int i=0; i<picList.size(); i++) {
224 			Pic p = picList.get(i);
225 			if (p.getPid() == pid)
226 				return(i);
227 		}
228 		return(-1);
229 	}
230 }