1 /*
   2  * Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.java2d.pisces;
  27 
  28 import java.util.Map;
  29 import java.util.concurrent.ConcurrentHashMap;
  30 
  31 import sun.java2d.pipe.AATileGenerator;
  32 
  33 final class PiscesTileGenerator implements AATileGenerator {
  34     public static final int TILE_SIZE = PiscesCache.TILE_SIZE;
  35 
  36     // perhaps we should be using weak references here, but right now
  37     // that's not necessary. The way the renderer is, this map will
  38     // never contain more than one element - the one with key 64, since
  39     // we only do 8x8 supersampling.
  40     private static final Map<Integer, byte[]> alphaMapsCache = new
  41                    ConcurrentHashMap<Integer, byte[]>();
  42 
  43     PiscesCache cache;
  44     int x, y;
  45     final int maxalpha;
  46     private final int maxTileAlphaSum;
  47 
  48     // The alpha map used by this object (taken out of our map cache) to convert
  49     // pixel coverage counts gotten from PiscesCache (which are in the range
  50     // [0, maxalpha]) into alpha values, which are in [0,256).
  51     byte alphaMap[];
  52 
  53     public PiscesTileGenerator(Renderer r, int maxalpha) {
  54         this.cache = r.getCache();
  55         this.x = cache.bboxX0;
  56         this.y = cache.bboxY0;
  57         this.alphaMap = getAlphaMap(maxalpha);
  58         this.maxalpha = maxalpha;
  59         this.maxTileAlphaSum = TILE_SIZE*TILE_SIZE*maxalpha;
  60     }
  61 
  62     private static byte[] buildAlphaMap(int maxalpha) {
  63         byte[] alMap = new byte[maxalpha+1];
  64         int halfmaxalpha = maxalpha>>2;
  65         for (int i = 0; i <= maxalpha; i++) {
  66             alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
  67         }
  68         return alMap;
  69     }
  70 
  71     public static byte[] getAlphaMap(int maxalpha) {
  72         if (!alphaMapsCache.containsKey(maxalpha)) {
  73             alphaMapsCache.put(maxalpha, buildAlphaMap(maxalpha));
  74         }
  75         return alphaMapsCache.get(maxalpha);
  76     }
  77 
  78     public void getBbox(int bbox[]) {
  79         cache.getBBox(bbox);
  80         //System.out.println("bbox["+bbox[0]+", "+bbox[1]+" => "+bbox[2]+", "+bbox[3]+"]");
  81     }
  82 
  83     /**
  84      * Gets the width of the tiles that the generator batches output into.
  85      * @return the width of the standard alpha tile
  86      */
  87     public int getTileWidth() {
  88         return TILE_SIZE;
  89     }
  90 
  91     /**
  92      * Gets the height of the tiles that the generator batches output into.
  93      * @return the height of the standard alpha tile
  94      */
  95     public int getTileHeight() {
  96         return TILE_SIZE;
  97     }
  98 
  99     /**
 100      * Gets the typical alpha value that will characterize the current
 101      * tile.
 102      * The answer may be 0x00 to indicate that the current tile has
 103      * no coverage in any of its pixels, or it may be 0xff to indicate
 104      * that the current tile is completely covered by the path, or any
 105      * other value to indicate non-trivial coverage cases.
 106      * @return 0x00 for no coverage, 0xff for total coverage, or any other
 107      *         value for partial coverage of the tile
 108      */
 109     public int getTypicalAlpha() {
 110         int al = cache.alphaSumInTile(x, y);
 111         // Note: if we have a filled rectangle that doesn't end on a tile
 112         // border, we could still return 0xff, even though al!=maxTileAlphaSum
 113         // This is because if we return 0xff, our users will fill a rectangle
 114         // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
 115         // and height min(TILE_SIZE,bboxY1-y), which is what should happen.
 116         // However, to support this, we would have to use 2 Math.min's
 117         // and 2 multiplications per tile, instead of just 2 multiplications
 118         // to compute maxTileAlphaSum. The savings offered would probably
 119         // not be worth it, considering how rare this case is.
 120         // Note: I have not tested this, so in the future if it is determined
 121         // that it is worth it, it should be implemented. Perhaps this method's
 122         // interface should be changed to take arguments the width and height
 123         // of the current tile. This would eliminate the 2 Math.min calls that
 124         // would be needed here, since our caller needs to compute these 2
 125         // values anyway.
 126         return (al == 0x00 ? 0x00 :
 127             (al == maxTileAlphaSum ? 0xff : 0x80));
 128     }
 129 
 130     /**
 131      * Skips the current tile and moves on to the next tile.
 132      * Either this method, or the getAlpha() method should be called
 133      * once per tile, but not both.
 134      */
 135     public void nextTile() {
 136         if ((x += TILE_SIZE) >= cache.bboxX1) {
 137             x = cache.bboxX0;
 138             y += TILE_SIZE;
 139         }
 140     }
 141 
 142     /**
 143      * Gets the alpha coverage values for the current tile.
 144      * Either this method, or the nextTile() method should be called
 145      * once per tile, but not both.
 146      */
 147     public void getAlpha(byte tile[], int offset, int rowstride) {
 148         // Decode run-length encoded alpha mask data
 149         // The data for row j begins at cache.rowOffsetsRLE[j]
 150         // and is encoded as a set of 2-byte pairs (val, runLen)
 151         // terminated by a (0, 0) pair.
 152 
 153         int x0 = this.x;
 154         int x1 = x0 + TILE_SIZE;
 155         int y0 = this.y;
 156         int y1 = y0 + TILE_SIZE;
 157         if (x1 > cache.bboxX1) x1 = cache.bboxX1;
 158         if (y1 > cache.bboxY1) y1 = cache.bboxY1;
 159         y0 -= cache.bboxY0;
 160         y1 -= cache.bboxY0;
 161 
 162         int idx = offset;
 163         for (int cy = y0; cy < y1; cy++) {
 164             int[] row = cache.rowAARLE[cy];
 165             assert row != null;
 166             int cx = cache.minTouched(cy);
 167             if (cx > x1) cx = x1;
 168 
 169             for (int i = x0; i < cx; i++) {
 170                 tile[idx++] = 0x00;
 171             }
 172 
 173             int pos = 2;
 174             while (cx < x1 && pos < row[1]) {
 175                 byte val;
 176                 int runLen = 0;
 177                 assert row[1] > 2;
 178                 try {
 179                     val = alphaMap[row[pos]];
 180                     runLen = row[pos + 1];
 181                     assert runLen > 0;
 182                 } catch (RuntimeException e0) {
 183                     System.out.println("maxalpha = "+maxalpha);
 184                     System.out.println("tile["+x0+", "+y0+
 185                                        " => "+x1+", "+y1+"]");
 186                     System.out.println("cx = "+cx+", cy = "+cy);
 187                     System.out.println("idx = "+idx+", pos = "+pos);
 188                     System.out.println("len = "+runLen);
 189                     System.out.print(cache.toString());
 190                     e0.printStackTrace();
 191                     throw e0;
 192                 }
 193 
 194                 int rx0 = cx;
 195                 cx += runLen;
 196                 int rx1 = cx;
 197                 if (rx0 < x0) rx0 = x0;
 198                 if (rx1 > x1) rx1 = x1;
 199                 runLen = rx1 - rx0;
 200                 //System.out.println("M["+runLen+"]");
 201                 while (--runLen >= 0) {
 202                     try {
 203                         tile[idx++] = val;
 204                     } catch (RuntimeException e) {
 205                         System.out.println("maxalpha = "+maxalpha);
 206                         System.out.println("tile["+x0+", "+y0+
 207                                            " => "+x1+", "+y1+"]");
 208                         System.out.println("cx = "+cx+", cy = "+cy);
 209                         System.out.println("idx = "+idx+", pos = "+pos);
 210                         System.out.println("rx0 = "+rx0+", rx1 = "+rx1);
 211                         System.out.println("len = "+runLen);
 212                         System.out.print(cache.toString());
 213                         e.printStackTrace();
 214                         throw e;
 215                     }
 216                 }
 217                 pos += 2;
 218             }
 219             if (cx < x0) { cx = x0; }
 220             while (cx < x1) {
 221                 tile[idx++] = 0x00;
 222                 cx++;
 223             }
 224             /*
 225             for (int i = idx - (x1-x0); i < idx; i++) {
 226                 System.out.print(hex(tile[i], 2));
 227             }
 228             System.out.println();
 229             */
 230             idx += (rowstride - (x1-x0));
 231         }
 232         nextTile();
 233     }
 234 
 235     static String hex(int v, int d) {
 236         String s = Integer.toHexString(v);
 237         while (s.length() < d) {
 238             s = "0"+s;
 239         }
 240         return s.substring(0, d);
 241     }
 242 
 243     /**
 244      * Disposes this tile generator.
 245      * No further calls will be made on this instance.
 246      */
 247     public void dispose() {}
 248 }
 249