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