1 /*
   2  * Copyright (c) 2010, 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.xr;
  27 
  28 import java.awt.*;
  29 import java.util.*;
  30 
  31 /**
  32  * We render non-antialiased geometry (consisting of rectangles) into a mask,
  33  * which is later used in a composition step.
  34  * To avoid mask-allocations of large size, MaskTileManager splits
  35  * geometry larger than MASK_SIZE into several tiles,
  36  * and stores the geometry in instances of MaskTile.
  37  *
  38  * @author Clemens Eisserer
  39  */
  40 
  41 public class MaskTileManager {
  42 
  43     public static final int MASK_SIZE = 256;
  44 
  45     MaskTile mainTile = new MaskTile();
  46 
  47     ArrayList<MaskTile> tileList;
  48     int allocatedTiles = 0;
  49     int xTiles, yTiles;
  50 
  51     XRCompositeManager xrMgr;
  52     XRBackend con;
  53 
  54     int maskPixmap;
  55     int maskPicture;
  56     long maskGC;
  57     int lineMaskPixmap;
  58     int lineMaskPicture;
  59     long drawLineGC;
  60     long clearLineGC;
  61 
  62     public MaskTileManager(XRCompositeManager xrMgr, int parentXid) {
  63         tileList = new ArrayList<MaskTile>();
  64         this.xrMgr = xrMgr;
  65         this.con = xrMgr.getBackend();
  66 
  67         maskPixmap = con.createPixmap(parentXid, 8, MASK_SIZE, MASK_SIZE);
  68         maskPicture = con.createPicture(maskPixmap, XRUtils.PictStandardA8);
  69         con.renderRectangle(maskPicture, XRUtils.PictOpClear,
  70                             new XRColor(Color.black),
  71                             0, 0, MASK_SIZE, MASK_SIZE);
  72         maskGC = con.createGC(maskPixmap);
  73         con.setGCExposures(maskGC, false);
  74 
  75         lineMaskPixmap = con.createPixmap(parentXid, 8, MASK_SIZE, MASK_SIZE);
  76         lineMaskPicture =
  77              con.createPicture(lineMaskPixmap, XRUtils.PictStandardA8);
  78         con.renderRectangle(lineMaskPicture, XRUtils.PictOpClear,
  79                           new XRColor(Color.black), 0, 0, MASK_SIZE, MASK_SIZE);
  80 
  81         drawLineGC = con.createGC(lineMaskPixmap);
  82         con.setGCExposures(drawLineGC, false);
  83         con.setGCForeground(drawLineGC, 255);
  84 
  85         clearLineGC = con.createGC(lineMaskPixmap);
  86         con.setGCExposures(clearLineGC, false);
  87         con.setGCForeground(clearLineGC, 0);
  88     }
  89 
  90     /**
  91      * Adds a rectangle to the mask.
  92      */
  93     public void addRect(int x, int y, int width, int height) {
  94         mainTile.addRect(x, y, width, height);
  95     }
  96 
  97     /**
  98      * Adds a line to the mask.
  99      */
 100     public void addLine(int x1, int y1, int x2, int y2) {
 101         mainTile.addLine(x1, y1, x2, y2);
 102     }
 103 
 104     /**
 105      * Transfers the geometry stored (rectangles, lines) to one or more masks,
 106      * and renders the result to the destination surface.
 107      */
 108     public void fillMask(XRSurfaceData dst) {
 109 
 110         boolean maskRequired = xrMgr.maskRequired();
 111 
 112         if (maskRequired) {
 113             mainTile.calculateDirtyAreas();
 114             DirtyRegion dirtyArea = mainTile.getDirtyArea().cloneRegion();
 115             mainTile.translate(-dirtyArea.x, -dirtyArea.y);
 116 
 117             XRColor maskColor = xrMgr.getMaskColor();
 118 
 119             // We don't need tiling if all geometry fits in a single tile
 120             if (dirtyArea.getWidth() <= MASK_SIZE &&
 121                 dirtyArea.getHeight() <= MASK_SIZE)
 122             {
 123                 compositeSingleTile(dst, mainTile, dirtyArea,
 124                                      maskRequired, 0, 0, maskColor);
 125             } else {
 126                 allocTiles(dirtyArea);
 127                 tileRects();
 128 
 129                 for (int i = 0; i < yTiles; i++) {
 130                     for (int m = 0; m < xTiles; m++) {
 131                         MaskTile tile = tileList.get(i * xTiles + m);
 132 
 133                         int tileStartX = m * MASK_SIZE;
 134                         int tileStartY = i * MASK_SIZE;
 135                         compositeSingleTile(dst, tile, dirtyArea, maskRequired,
 136                                             tileStartX, tileStartY, maskColor);
 137                     }
 138                 }
 139             }
 140         } else {
 141             xrMgr.XRRenderRectangles(dst, mainTile.getRects());
 142         }
 143 
 144         mainTile.reset();
 145     }
 146 
 147     /**
 148      * Uploads aa geometry generated for maskblit/fill into the mask pixmap.
 149      */
 150     public int uploadMask(int w, int h, int maskscan, int maskoff, byte[] mask) {
 151         int maskPic = XRUtils.None;
 152 
 153         if (mask != null) {
 154             float maskAlpha =
 155                  xrMgr.isTexturePaintActive() ? xrMgr.getExtraAlpha() : 1.0f;
 156             con.putMaskImage(maskPixmap, maskGC, mask, 0, 0, 0, 0,
 157                              w, h, maskoff, maskscan, maskAlpha);
 158             maskPic = maskPicture;
 159         } else if (xrMgr.isTexturePaintActive()) {
 160             maskPic = xrMgr.getExtraAlphaMask();
 161          }
 162 
 163         return maskPic;
 164     }
 165 
 166     /**
 167      * Clears the area of the mask-pixmap used for uploading aa coverage values.
 168      */
 169     public void clearUploadMask(int mask, int w, int h) {
 170         if (mask == maskPicture) {
 171             con.renderRectangle(maskPicture, XRUtils.PictOpClear,
 172                                 XRColor.NO_ALPHA, 0, 0, w, h);
 173         }
 174     }
 175 
 176 
 177     /**
 178      * Renders the rectangles provided to the mask, and does a composition
 179      * operation with the properties set inXRCompositeManager.
 180      */
 181     protected void compositeSingleTile(XRSurfaceData dst, MaskTile tile,
 182                                        DirtyRegion dirtyArea,
 183                                        boolean maskRequired,
 184                                        int tileStartX, int tileStartY,
 185                                        XRColor maskColor) {
 186         if (tile.rects.getSize() > 0) {
 187             DirtyRegion tileDirtyArea = tile.getDirtyArea();
 188 
 189             int x = tileDirtyArea.x + tileStartX + dirtyArea.x;
 190             int y = tileDirtyArea.y + tileStartY + dirtyArea.y;
 191             int width = tileDirtyArea.x2 - tileDirtyArea.x;
 192             int height = tileDirtyArea.y2 - tileDirtyArea.y;
 193             width = Math.min(width, MASK_SIZE);
 194             height = Math.min(height, MASK_SIZE);
 195 
 196             int rectCnt = tile.rects.getSize();
 197 
 198             if (maskRequired) {
 199                 int mask = XRUtils.None;
 200 
 201                 /*
 202                  * Optimization: When the tile only contains one rectangle, the
 203                  * composite-operation boundaries can be used as geometry
 204                  */
 205                 if (rectCnt > 1) {
 206                     con.renderRectangles(maskPicture, XRUtils.PictOpSrc,
 207                                          maskColor, tile.rects);
 208                     mask = maskPicture;
 209                 } else {
 210                     if (xrMgr.isTexturePaintActive()) {
 211                         mask = xrMgr.getExtraAlphaMask();
 212                     }
 213                 }
 214 
 215                 xrMgr.XRComposite(XRUtils.None, mask, dst.getPicture(),
 216                                   x, y, tileDirtyArea.x, tileDirtyArea.y,
 217                                   x, y, width, height);
 218 
 219                 /* Clear dirty rectangle of the rect-mask */
 220                 if (rectCnt > 1) {
 221                     con.renderRectangle(maskPicture, XRUtils.PictOpClear,
 222                                         XRColor.NO_ALPHA,
 223                                         tileDirtyArea.x, tileDirtyArea.y,
 224                                         width, height);
 225                 }
 226 
 227                 tile.reset();
 228             } else if (rectCnt > 0) {
 229                 tile.rects.translateRects(tileStartX + dirtyArea.x,
 230                                           tileStartY + dirtyArea.y);
 231                 xrMgr.XRRenderRectangles(dst, tile.rects);
 232             }
 233         }
 234     }
 235 
 236 
 237     /**
 238      * Allocates enough MaskTile instances, to cover the whole
 239      * mask area, or resets existing ones.
 240      */
 241     protected void allocTiles(DirtyRegion maskArea) {
 242         xTiles = (maskArea.getWidth() / MASK_SIZE) + 1;
 243         yTiles = (maskArea.getHeight() / MASK_SIZE) + 1;
 244         int tileCnt = xTiles * yTiles;
 245 
 246         if (tileCnt > allocatedTiles) {
 247             for (int i = 0; i < tileCnt; i++) {
 248                 if (i < allocatedTiles) {
 249                     tileList.get(i).reset();
 250                 } else {
 251                     tileList.add(new MaskTile());
 252                 }
 253             }
 254 
 255             allocatedTiles = tileCnt;
 256         }
 257     }
 258 
 259     /**
 260      * Tiles the stored rectangles, if they are larger than the MASK_SIZE
 261      */
 262     protected void tileRects() {
 263         GrowableRectArray rects = mainTile.rects;
 264 
 265         for (int i = 0; i < rects.getSize(); i++) {
 266             int tileXStartIndex = rects.getX(i) / MASK_SIZE;
 267             int tileYStartIndex = rects.getY(i) / MASK_SIZE;
 268             int tileXLength =
 269                 ((rects.getX(i) + rects.getWidth(i)) / MASK_SIZE + 1) -
 270                  tileXStartIndex;
 271             int tileYLength =
 272                  ((rects.getY(i) + rects.getHeight(i)) / MASK_SIZE + 1) -
 273                  tileYStartIndex;
 274 
 275             for (int n = 0; n < tileYLength; n++) {
 276                 for (int m = 0; m < tileXLength; m++) {
 277 
 278                     int tileIndex =
 279                          xTiles * (tileYStartIndex + n) + tileXStartIndex + m;
 280                     MaskTile tile = tileList.get(tileIndex);
 281 
 282                     GrowableRectArray rectTileList = tile.getRects();
 283                     int tileArrayIndex = rectTileList.getNextIndex();
 284 
 285                     int tileStartPosX = (tileXStartIndex + m) * MASK_SIZE;
 286                     int tileStartPosY = (tileYStartIndex + n) * MASK_SIZE;
 287 
 288                     rectTileList.setX(tileArrayIndex, rects.getX(i) - tileStartPosX);
 289                     rectTileList.setY(tileArrayIndex, rects.getY(i) - tileStartPosY);
 290                     rectTileList.setWidth(tileArrayIndex, rects.getWidth(i));
 291                     rectTileList.setHeight(tileArrayIndex, rects.getHeight(i));
 292 
 293                     limitRectCoords(rectTileList, tileArrayIndex);
 294 
 295                     tile.getDirtyArea().growDirtyRegion
 296                        (rectTileList.getX(tileArrayIndex),
 297                         rectTileList.getY(tileArrayIndex),
 298                         rectTileList.getWidth(tileArrayIndex) +
 299                              rectTileList.getX(tileArrayIndex),
 300                         rectTileList.getHeight(tileArrayIndex) +
 301                             rectTileList.getY(tileArrayIndex));
 302                 }
 303             }
 304         }
 305     }
 306 
 307     /**
 308      * Limits the rect's coordinates to the mask coordinates. The result is used
 309      * by growDirtyRegion.
 310      */
 311     private void limitRectCoords(GrowableRectArray rects, int index) {
 312         if ((rects.getX(index) + rects.getWidth(index)) > MASK_SIZE) {
 313             rects.setWidth(index, MASK_SIZE - rects.getX(index));
 314         }
 315         if ((rects.getY(index) + rects.getHeight(index)) > MASK_SIZE) {
 316             rects.setHeight(index, MASK_SIZE - rects.getY(index));
 317         }
 318         if (rects.getX(index) < 0) {
 319             rects.setWidth(index, rects.getWidth(index) + rects.getX(index));
 320             rects.setX(index, 0);
 321         }
 322         if (rects.getY(index) < 0) {
 323             rects.setHeight(index, rects.getHeight(index) + rects.getY(index));
 324             rects.setY(index, 0);
 325         }
 326     }
 327 }