1
2
3
4
5
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.HashMap;
13 import java.util.List;
14 import java.util.concurrent.CountDownLatch;
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 import com.buckosoft.PicMan.domain.Thumbnail;
23
24
25
26
27
28
29
30 public class FourthRibbon extends MosaicEngine {
31 private static final boolean DEBUG = false;
32 private static final boolean DEBUGDRAW = false;
33 private final Log logger = LogFactory.getLog(getClass());
34
35 private int curRow = 0;
36 private int[] curColsL;
37 private int[] curColsR;
38 private int[] pixels;
39 private int curCol;
40 private int maxRow;
41 private int maxCol;
42 private int startRow;
43 private int p;
44 private String curPicName;
45 private List<Pic> picList;
46 private HashMap<String, Double> rateMap;
47 private String info = "N/A";
48 private int[] used;
49 private int tileRows;
50 private String buildStep = "Idle";
51 private int pass = -1;
52 private long[][] rateL;
53 private long[][] rateR;
54 private Graphics2D gd;
55 private int baseX;
56 private int baseY;
57 private double rateWeight = 1.0;
58 private double usedWeight = 0.0;
59 private boolean workDoneL = false;
60 private boolean workDoneR = false;
61 private int reduceStyle = 0;
62
63
64 public FourthRibbon() {
65 addConfig("baseX", "X offset of the base point", ConfigItem.TYPE_INT, 0);
66 addConfig("baseY", "Y offset of the base point", ConfigItem.TYPE_INT, 0);
67 addConfig("usedWeight", "Multiplier to reduce used pics", ConfigItem.TYPE_DOUBLE, 1.0);
68 addConfig("reduceStyle", "Reduce: 0=calc, 1=draw", ConfigItem.TYPE_INT, 1);
69 addConfig("threads", "Threads: (" + Runtime.getRuntime().availableProcessors() + ") max", ConfigItem.TYPE_INT, 1);
70 }
71
72
73
74
75 public String getStatus() {
76 StringBuilder sb = new StringBuilder();
77 sb.append(buildStep);
78 sb.append(": pass=");
79 sb.append(pass);
80 if (workDoneL || workDoneR)
81 sb.append(" ");
82 if (workDoneL)
83 sb.append("L");
84 if (workDoneR)
85 sb.append("R");
86 if (!buildStep.equals("Calc")) {
87 sb.append(" curRow = ");
88 sb.append(curRow);
89 sb.append(" curCol = ");
90 sb.append(curCol);
91 }
92 sb.append(" p=");
93 sb.append(p);
94 return(sb.toString());
95 }
96
97
98
99
100 public String getInfo() {
101 return(info);
102 }
103
104 protected boolean _build() {
105 jobLogEntry.setNote("Setup");
106 gd = bi.createGraphics();
107 baseX = (Integer)buildParameters.get("baseX");
108 baseY = (Integer)buildParameters.get("baseY");
109 rateWeight = (Double)buildParameters.get("rateWeight");
110 usedWeight = (Double)buildParameters.get("usedWeight");
111 reduceStyle = (Integer)buildParameters.get("reduceStyle");
112
113 picList = dbf.getPics(this.mosaicSet, tileHeight);
114 rateMap = new HashMap<String, Double>();
115 for (Pic pic : picList) {
116 double d = dbf.getPicRate(pic.getName(), this.mosaicSet, tileHeight);
117 rateMap.put(pic.getName(), new Double(d));
118
119
120 }
121 if (DEBUG)
122 logger.info("Working from " + picList.size() + " pics");
123 startRow = 0 - (baseY % tileHeight);
124 startRow = baseY % tileHeight;
125 if (startRow > 0)
126 startRow -= tileHeight;
127 maxRow = this.masterPic.getHeight();
128 maxCol = this.masterPic.getWidth();
129 pixels = bi.getRGB(0, 0, maxCol, maxRow, null, 0, maxCol);
130 tileRows = maxRow / tileHeight;
131 if (tileRows * tileHeight + startRow < maxRow)
132 tileRows++;
133 if (tileRows * tileHeight + startRow < maxRow)
134 tileRows++;
135 curColsL = new int[tileRows];
136 curColsR = new int[tileRows];
137 for (int i=0; i<tileRows; i++) {
138 curColsL[i] = baseX;
139 curColsR[i] = baseX;
140 }
141 rateL = new long[tileRows][picList.size()];
142 rateR = new long[tileRows][picList.size()];
143 used = new int[picList.size()];
144 info = "tileRows=" + tileRows + " startRow=" + startRow + " pics=" + picList.size();
145 if (DEBUG)
146 logger.info("tileRows=" + tileRows + " rowHeight=" + tileHeight);
147 if (baseX > maxCol)
148 baseX = maxCol;
149 if (baseY > maxRow)
150 baseY = maxRow;
151
152
153 BufferedImage mbi;
154 workDoneL = true;
155 workDoneR = true;
156 while (workDoneL || workDoneR) {
157 pass++;
158 jobLogEntry.setNote("pass: " + pass);
159 if (!pmf.isEngineOn())
160 return(false);
161 buildStep = "Calc";
162 for (p=0; p<picList.size(); p++) {
163 curPicName = picList.get(p).getName();
164
165
166 Pic xp = picList.get(p);
167 Thumbnail xtn = pmf.getMosaicThumbNail(xp, tileHeight);
168 mbi = xtn.getImage();
169
170 CountDownLatch latch = new CountDownLatch(2);
171 if (workDoneL)
172 new Thread(new CalcLeft(latch, mbi)).start();
173 else
174 latch.countDown();
175 if (workDoneR)
176 new Thread(new CalcRight(latch, mbi)).start();
177 else
178 latch.countDown();
179 try {
180 latch.await();
181 } catch (InterruptedException e) {
182 logger.warn(e);
183 }
184 if (!workDoneL && !workDoneR)
185 break;
186 }
187 buildStep = "Draw";
188 if (workDoneL)
189 workDoneL = _drawLeft();
190 if (workDoneR)
191 workDoneR = _drawRight();
192 if (!mosaic.isBatch())
193 dbf.storeMosaic(mosaic);
194 }
195
196 buildStep = "Done";
197 return(false);
198 }
199
200 class CalcLeft implements Runnable {
201 CountDownLatch latch;
202 BufferedImage mbi;
203
204 CalcLeft(CountDownLatch latch, BufferedImage mbi) {
205 this.latch = latch;
206 this.mbi = mbi;
207 }
208 @Override
209 public void run() {
210 _calcLeft();
211 latch.countDown();
212 }
213
214 private boolean _calcLeft() {
215 boolean workDone = false;
216 int x,y;
217 int mw = mbi.getWidth();
218 int mh = mbi.getHeight();
219 for (int curRow=0; curRow<tileRows; curRow++) {
220 int curCol = curColsL[curRow];
221 int cr = curRow * tileHeight + startRow;
222 if (curCol > 0) {
223 workDone = true;
224 if (reduceStyle == 0)
225 rateL[curRow][p] = -(long)(used[p]*usedWeight);
226 else
227 rateL[curRow][p] = 0;
228 for (x=0; x<mw; x++) {
229 y=0;
230 if (cr<0)
231 y = -startRow;
232 for (; y<mh; y++) {
233 int mpx = mbi.getRGB(x, y);
234 int ppx;
235 if (curCol+x-mw < 0 || y+cr >= maxRow)
236 ppx = mpx;
237 else
238
239 ppx = pixels[x+curCol-mw+((y+cr)*maxCol)];
240
241
242
243 rateL[curRow][p] += Math.abs(((mpx)&0xFF) - ((ppx)&0xFF)) * 1;
244 rateL[curRow][p] += Math.abs(((mpx>>8)&0xFF) - ((ppx>>8)&0xFF)) * 1;
245 rateL[curRow][p] += Math.abs(((mpx>>16)&0xFF) - ((ppx>>16)&0xFF)) * 1;
246 }
247 }
248
249
250 rateL[curRow][p] /= mw;
251
252 rateL[curRow][p] *= (1+(rateWeight * (9-rateMap.get(curPicName))));
253
254 }
255 }
256 workDoneL = workDone;
257 return(workDone);
258 }
259
260 }
261
262 class CalcRight implements Runnable {
263 CountDownLatch latch;
264 BufferedImage mbi;
265
266 CalcRight(CountDownLatch latch, BufferedImage mbi) {
267 this.latch = latch;
268 this.mbi = mbi;
269 }
270 @Override
271 public void run() {
272 _calcRight();
273 latch.countDown();
274 }
275
276 private boolean _calcRight() {
277 boolean workDone = false;
278 int mw = mbi.getWidth();
279 int mh = mbi.getHeight();
280 int x,y;
281 for (int curRow=0; curRow<tileRows; curRow++) {
282 int curCol = curColsR[curRow];
283 int cr = curRow * tileHeight + startRow;
284 if (curCol < maxCol) {
285 workDone = true;
286 if (reduceStyle == 0)
287 rateR[curRow][p] = -(long)(used[p]*usedWeight);
288 else
289 rateR[curRow][p] = 0;
290 for (x=0; x<mw; x++) {
291 y=0;
292 if (cr<0)
293 y = -startRow;
294 for (; y<mh; y++) {
295 int mpx = mbi.getRGB(x, y);
296 int ppx;
297 if (x+curCol >= maxCol || y+cr >= maxRow)
298 ppx = mpx;
299 else
300
301 ppx = pixels[x+curCol+((y+cr)*maxCol)];
302
303
304
305 rateR[curRow][p] += Math.abs(((mpx)&0xFF) - ((ppx)&0xFF)) * 1;
306 rateR[curRow][p] += Math.abs(((mpx>>8)&0xFF) - ((ppx>>8)&0xFF)) * 1;
307 rateR[curRow][p] += Math.abs(((mpx>>16)&0xFF) - ((ppx>>16)&0xFF)) * 1;
308 }
309 }
310
311
312 rateR[curRow][p] /= mw;
313
314 rateR[curRow][p] *= (1+(rateWeight * (9-rateMap.get(curPicName))));
315 }
316 }
317 workDoneR = workDone;
318 return(workDone);
319 }
320 }
321
322 private boolean _drawLeft() {
323 boolean workDone = false;
324 BufferedImage bi;
325 int x;
326 for (curRow=0; curRow<tileRows; curRow++) {
327 curCol = curColsL[curRow];
328 int cr = curRow * tileHeight + startRow;
329 if (curCol < 0)
330 continue;
331 long best = Long.MAX_VALUE;
332 int besti = 0;
333 int besti2 = 0;
334 for (x=0; x<rateL[curRow].length; x++) {
335 if (rateL[curRow][x] < best) {
336 besti2 = besti;
337 best = rateL[curRow][x];
338 besti = x;
339 }
340 }
341
342 if (DEBUGDRAW) {
343 if (besti == -1)
344 logger.info("row=" + curRow + " besti == -1");
345 else
346 logger.info("besti = " + besti + " (" + picList.get(besti).getName() + ") = " + rateL[curRow][besti]);
347 if (besti2 == -1)
348 logger.info("row=" + curRow + " besti2 == -1");
349 else
350 logger.info("besti2 = " + besti2 + " (" + picList.get(besti2).getName() + ") = " + rateL[curRow][besti2]);
351 }
352 used[besti]++;
353 bi = pmf.getMosaicThumbNail(picList.get(besti), tileHeight).getImage();
354 if (!mosaic.isBatch()) {
355 MosaicTile tile = new MosaicTile(this.mid, picList.get(besti).getPid(), curCol-maxCol, cr, maxCol, maxRow);
356 dbf.storeMosaicTile(tile);
357 }
358 gd.drawImage(bi, null, curCol-maxCol, cr);
359 workDone = true;
360 curColsL[curRow] -= maxCol;
361 if (reduceStyle == 1) {
362 String name = picList.get(besti).getName();
363 double dd = rateMap.get(name) - (rateMap.get(name) * this.usedWeight);
364 rateMap.put(name, dd);
365 }
366 setLastMosaicUpdate();
367 }
368 return(workDone);
369 }
370
371 private boolean _drawRight() {
372 boolean workDone = false;
373 BufferedImage bi;
374 int x;
375 for (curRow=0; curRow<tileRows; curRow++) {
376 curCol = curColsR[curRow];
377 int cr = curRow * tileHeight + startRow;
378 if (curCol >= maxCol)
379 continue;
380 long best = Long.MAX_VALUE;
381 int besti = 0;
382 int besti2 = 0;
383 for (x=0; x<rateR[curRow].length; x++) {
384 if (rateR[curRow][x] < best) {
385 besti2 = besti;
386 best = rateR[curRow][x];
387 besti = x;
388 }
389 }
390
391 if (DEBUGDRAW) {
392 if (besti == -1)
393 logger.info("row=" + curRow + " besti == -1");
394 else
395 logger.info("besti = " + besti + " (" + picList.get(besti).getName() + ") = " + rateR[curRow][besti]);
396 if (besti2 == -1)
397 logger.info("row=" + curRow + " besti2 == -1");
398 else
399 logger.info("besti2 = " + besti2 + " (" + picList.get(besti2).getName() + ") = " + rateR[curRow][besti2]);
400 }
401 used[besti]++;
402 bi = pmf.getMosaicThumbNail(picList.get(besti), tileHeight).getImage();
403 if (!mosaic.isBatch()) {
404 MosaicTile tile = new MosaicTile(this.mid, picList.get(besti).getPid(), curCol, cr, maxCol, maxRow);
405 dbf.storeMosaicTile(tile);
406 }
407 gd.drawImage(bi, null, curCol, cr);
408 workDone = true;
409 curColsR[curRow] += maxCol;
410 if (reduceStyle == 1) {
411 String name = picList.get(besti).getName();
412 double dd = rateMap.get(name) - (rateMap.get(name) * this.usedWeight);
413 rateMap.put(name, dd);
414 }
415 setLastMosaicUpdate();
416 }
417 return(workDone);
418 }
419 }