1 /*
   2  * Copyright (c) 2007, 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         bbox[0] = cache.bboxX0;
  80         bbox[1] = cache.bboxY0;
  81         bbox[2] = cache.bboxX1;
  82         bbox[3] = cache.bboxY1;
  83         //System.out.println("bbox["+bbox[0]+", "+bbox[1]+" => "+bbox[2]+", "+bbox[3]+"]");
  84     }
  85 
  86     /**
  87      * Gets the width of the tiles that the generator batches output into.
  88      * @return the width of the standard alpha tile
  89      */
  90     public int getTileWidth() {
  91         return TILE_SIZE;
  92     }
  93 
  94     /**
  95      * Gets the height of the tiles that the generator batches output into.
  96      * @return the height of the standard alpha tile
  97      */
  98     public int getTileHeight() {
  99         return TILE_SIZE;
 100     }
 101 
 102     /**
 103      * Gets the typical alpha value that will characterize the current
 104      * tile.
 105      * The answer may be 0x00 to indicate that the current tile has
 106      * no coverage in any of its pixels, or it may be 0xff to indicate
 107      * that the current tile is completely covered by the path, or any
 108      * other value to indicate non-trivial coverage cases.
 109      * @return 0x00 for no coverage, 0xff for total coverage, or any other
 110      *         value for partial coverage of the tile
 111      */
 112     public int getTypicalAlpha() {
 113         int al = cache.alphaSumInTile(x, y);
 114         // Note: if we have a filled rectangle that doesn't end on a tile
 115         // border, we could still return 0xff, even though al!=maxTileAlphaSum
 116         // This is because if we return 0xff, our users will fill a rectangle
 117         // starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
 118         // and height min(TILE_SIZE,bboxY1-y), which is what should happen.
 119         // However, to support this, we would have to use 2 Math.min's
 120         // and 2 multiplications per tile, instead of just 2 multiplications
 121         // to compute maxTileAlphaSum. The savings offered would probably
 122         // not be worth it, considering how rare this case is.
 123         // Note: I have not tested this, so in the future if it is determined
 124         // that it is worth it, it should be implemented. Perhaps this method's
 125         // interface should be changed to take arguments the width and height
 126         // of the current tile. This would eliminate the 2 Math.min calls that
 127         // would be needed here, since our caller needs to compute these 2
 128         // values anyway.
 129         return (al == 0x00 ? 0x00 :
 130             (al == maxTileAlphaSum ? 0xff : 0x80));
 131     }
 132 
 133     /**
 134      * Skips the current tile and moves on to the next tile.
 135      * Either this method, or the getAlpha() method should be called
 136      * once per tile, but not both.
 137      */
 138     public void nextTile() {
 139         if ((x += TILE_SIZE) >= cache.bboxX1) {
 140             x = cache.bboxX0;
 141             y += TILE_SIZE;
 142         }
 143     }
 144 
 145     /**
 146      * Gets the alpha coverage values for the current tile.
 147      * Either this method, or the nextTile() method should be called
 148      * once per tile, but not both.
 149      */
 150     public void getAlpha(byte tile[], int offset, int rowstride) {
 151         // Decode run-length encoded alpha mask data
 152         // The data for row j begins at cache.rowOffsetsRLE[j]
 153         // and is encoded as a set of 2-byte pairs (val, runLen)
 154         // terminated by a (0, 0) pair.
 155 
 156         int x0 = this.x;
 157         int x1 = x0 + TILE_SIZE;
 158         int y0 = this.y;
 159         int y1 = y0 + TILE_SIZE;
 160         if (x1 > cache.bboxX1) x1 = cache.bboxX1;
 161         if (y1 > cache.bboxY1) y1 = cache.bboxY1;
 162         y0 -= cache.bboxY0;
 163         y1 -= cache.bboxY0;
 164 
 165         int idx = offset;
 166         for (int cy = y0; cy < y1; cy++) {
 167             int[] row = cache.rowAARLE[cy];
 168             assert row != null;
 169             int cx = cache.minTouched(cy);
 170             if (cx > x1) cx = x1;
 171 
 172             for (int i = x0; i < cx; i++) {
 173                 tile[idx++] = 0x00;
 174             }
 175 
 176             int pos = 2;
 177             while (cx < x1 && pos < row[1]) {
 178                 byte val;
 179                 int runLen = 0;
 180                 assert row[1] > 2;
 181                 try {
 182                     val = alphaMap[row[pos]];
 183                     runLen = row[pos + 1];
 184                     assert runLen > 0;
 185                 } catch (RuntimeException e0) {
 186                     System.out.println("maxalpha = "+maxalpha);
 187                     System.out.println("tile["+x0+", "+y0+
 188                                        " => "+x1+", "+y1+"]");
 189                     System.out.println("cx = "+cx+", cy = "+cy);
 190                     System.out.println("idx = "+idx+", pos = "+pos);
 191                     System.out.println("len = "+runLen);
 192                     System.out.print(cache.toString());
 193                     e0.printStackTrace();
 194                     throw e0;
 195                 }
 196 
 197                 int rx0 = cx;
 198                 cx += runLen;
 199                 int rx1 = cx;
 200                 if (rx0 < x0) rx0 = x0;
 201                 if (rx1 > x1) rx1 = x1;
 202                 runLen = rx1 - rx0;
 203                 //System.out.println("M["+runLen+"]");
 204                 while (--runLen >= 0) {
 205                     try {
 206                         tile[idx++] = val;
 207                     } catch (RuntimeException e) {
 208                         System.out.println("maxalpha = "+maxalpha);
 209                         System.out.println("tile["+x0+", "+y0+
 210                                            " => "+x1+", "+y1+"]");
 211                         System.out.println("cx = "+cx+", cy = "+cy);
 212                         System.out.println("idx = "+idx+", pos = "+pos);
 213                         System.out.println("rx0 = "+rx0+", rx1 = "+rx1);
 214                         System.out.println("len = "+runLen);
 215                         System.out.print(cache.toString());
 216                         e.printStackTrace();
 217                         throw e;
 218                     }
 219                 }
 220                 pos += 2;
 221             }
 222             if (cx < x0) { cx = x0; }
 223             while (cx < x1) {
 224                 tile[idx++] = 0x00;
 225                 cx++;
 226             }
 227             /*
 228             for (int i = idx - (x1-x0); i < idx; i++) {
 229                 System.out.print(hex(tile[i], 2));
 230             }
 231             System.out.println();
 232             */
 233             idx += (rowstride - (x1-x0));
 234         }
 235         nextTile();
 236     }
 237 
 238     static String hex(int v, int d) {
 239         String s = Integer.toHexString(v);
 240         while (s.length() < d) {
 241             s = "0"+s;
 242         }
 243         return s.substring(0, d);
 244     }
 245 
 246     /**
 247      * Disposes this tile generator.
 248      * No further calls will be made on this instance.
 249      */
 250     public void dispose() {}
 251 }
 252