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