View Javadoc
1   /******************************************************************************
2    * FirstRibbon.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  /** After 8 years, finally make a Mosaic.
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 difference 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/20
28   * @see <a href="http://cvs.buckosoft.com/Projects/java/PicMan/PicMan/src/main/java/com/buckosoft/PicMan/business/mosaic/engine/FirstRibbon.java">FirstRibbon.java</a>
29   */
30  public class FirstRibbon 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  	private	String	info = "N/A";
41  	private	long[]	used;
42  	
43  	/** Return our current build status
44  	 * @return The status
45  	 */
46  	public	String	getStatus() {
47  		StringBuilder sb = new StringBuilder();
48  		sb.append("curRow = ");
49  		sb.append(curRow);
50  		sb.append(" curCol = ");
51  		sb.append(curCol);
52  		sb.append(" p=");
53  		sb.append(p);
54  		return(sb.toString());
55  	}
56  
57  	public String getInfo() {
58  		return(info);
59  	}
60  
61  	protected boolean _build() {
62  		Graphics2D	gd = bi.createGraphics();
63  		
64  		// 75, pull from this size set even though the size calc should be able to deal with, say, a 50 and figure it out.
65  		picList = pmf.getDB().getPics(this.mosaicSet, this.tileHeight);
66  		if (DEBUG)
67  			logger.info("Working from " + picList.size() + " pics");
68  		maxRow = this.masterPic.getHeight();
69  		maxCol = this.masterPic.getWidth();
70  		//int		rowHeight = maxRow / tileHeight;
71  		long[]	rate = new long[picList.size()];		// rate each pic for this location
72  				used = new long[picList.size()];		// number of times each pic is used
73  		info = "maxRow=" + maxRow + " maxCol=" + maxCol + " pics=" + picList.size();
74  		if (DEBUG)
75  			logger.info("maxRow=" + maxRow + " rowHeight=" + tileHeight);
76  		restoreBuild(gd);
77  		for (; curRow<maxRow; curRow += tileHeight, curCol=0) {
78  			for (; curCol<maxCol; ) {
79  				int	x,y;
80  				for (p=0; p<picList.size(); p++) {
81  					BufferedImage mbi = pmf.getMosaicThumbNail(picList.get(p), tileHeight).getImage();
82  					//BufferedImage mbi = pmf.getThumbNail(null, tileHeight).getImage();
83  					rate[p] = used[p]*1;
84  					for (x=0; x<mbi.getWidth(); x++) {
85  						for (y=0; y<mbi.getHeight(); y++) {
86  							int mpx = mbi.getRGB(x, y);
87  							int	ppx;
88  							if (x+curCol >= bi.getWidth() || y+curRow >= bi.getHeight())
89  								ppx = mpx;
90  							else
91  								ppx = bi.getRGB(x+curCol, y+ curRow);
92  //							rate[p] += Math.abs(((mpx)&0xFF)     - ((ppx)&0xFF))     * 30;
93  //							rate[p] += Math.abs(((mpx>>8)&0xFF)  - ((ppx>>8)&0xFF))  * 59;
94  //							rate[p] += Math.abs(((mpx>>16)&0xFF) - ((ppx>>16)&0xFF)) * 11;
95  							rate[p] += Math.abs(((mpx)&0xFF)     - ((ppx)&0xFF))     * 1;
96  							rate[p] += Math.abs(((mpx>>8)&0xFF)  - ((ppx>>8)&0xFF))  * 1;
97  							rate[p] += Math.abs(((mpx>>16)&0xFF) - ((ppx>>16)&0xFF)) * 1;
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 = 0;
107 				int	besti2 = 0;
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 				if (!pmf.getDB().getSystem().isEngineOn())
129 					return(true);
130 			}
131 		}
132 		return(false);
133 	}
134 
135 	private	void restoreBuild(Graphics2D gd) {
136 		List<MosaicTile> lmt = pmf.getDB().getMosaicTiles(this.mid);
137 		if (DEBUG)
138 			logger.info("Restoring " + lmt.size() + " tiles");
139 		for (; curRow<maxRow; curRow += tileHeight, curCol=0) {
140 			for (; curCol<maxCol; ) {
141 				boolean found = false;
142 				Iterator<MosaicTile> iter = lmt.iterator();
143 				while (iter.hasNext()) {
144 					MosaicTile mt = iter.next();
145 					if (mt.getX() == curCol && mt.getY() == curRow) {
146 						int besti = getPicListIndex(mt.getPid());
147 						if (DEBUG)
148 							logger.info("restore: besti = " + besti + " (" + picList.get(besti).getName() + ")");
149 						used[besti]++;
150 						BufferedImage mbi = pmf.getThumbNail(picList.get(besti), tileHeight).getImage();
151 						gd.drawImage(mbi, null, curCol, curRow);
152 						curCol += mbi.getWidth();
153 						setLastMosaicUpdate();
154 						found = true;
155 						break;
156 					}
157 				}
158 				if (!found)
159 					return;
160 			}
161 		}
162 	}
163 	
164 	private	int	getPicListIndex(int pid) {
165 		for (int i=0; i<picList.size(); i++) {
166 			Pic p = picList.get(i);
167 			if (p.getPid() == pid)
168 				return(i);
169 		}
170 		return(-1);
171 	}
172 }