1 /*
   2  * Copyright (c) 2009, 2015, 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 com.sun.prism.impl;
  27 
  28 import java.nio.ByteBuffer;
  29 import java.util.HashMap;
  30 import java.util.Iterator;
  31 import java.util.Map;
  32 import com.sun.glass.ui.Screen;
  33 import com.sun.javafx.font.FontResource;
  34 import com.sun.javafx.font.FontStrike;
  35 import com.sun.javafx.geom.RectBounds;
  36 import com.sun.javafx.geom.transform.BaseTransform;
  37 import com.sun.javafx.image.ByteToBytePixelConverter;
  38 import com.sun.javafx.image.impl.ByteGray;
  39 import com.sun.javafx.sg.prism.NGCamera;
  40 import com.sun.prism.PixelFormat;
  41 import com.sun.prism.RTTexture;
  42 import com.sun.prism.RenderTarget;
  43 import com.sun.prism.ResourceFactory;
  44 import com.sun.prism.Texture;
  45 import com.sun.prism.Texture.WrapMode;
  46 import com.sun.prism.impl.paint.PaintUtil;
  47 import com.sun.prism.impl.shape.MaskData;
  48 import com.sun.prism.paint.Gradient;
  49 
  50 public abstract class BaseContext {
  51 
  52     private final Screen screen;
  53     private final ResourceFactory factory;
  54     private final VertexBuffer vertexBuffer;
  55 
  56     private static final int MIN_MASK_DIM = 1024;
  57     private Texture maskTex;
  58     private ByteBuffer maskBuffer;
  59     private ByteBuffer clearBuffer;
  60     private int curMaskRow;
  61     private int nextMaskRow;
  62     private int curMaskCol;
  63     private int highMaskCol;
  64     private Texture paintTex;
  65     private int[] paintPixels;
  66     private ByteBuffer paintBuffer;
  67 
  68     private Texture rectTex;
  69     private int rectTexMax;
  70     private Texture wrapRectTex;
  71     private Texture ovalTex;
  72 
  73     // TODO: need to dispose these when the context is disposed... (RT-27421)
  74     private final Map<FontStrike, GlyphCache>
  75         greyGlyphCaches = new HashMap<FontStrike, GlyphCache>();
  76     private final Map<FontStrike, GlyphCache>
  77         lcdGlyphCaches = new HashMap<FontStrike, GlyphCache>();
  78 
  79     protected BaseContext(Screen screen, ResourceFactory factory, VertexBuffer vb) {
  80         this.screen = screen;
  81         this.factory = factory;
  82         this.vertexBuffer = vb;
  83     }
  84 
  85     protected void setDeviceParametersFor2D() {}
  86     protected void setDeviceParametersFor3D() {}
  87 
  88     public Screen getAssociatedScreen() {
  89         return screen;
  90     }
  91 
  92     public ResourceFactory getResourceFactory() {
  93         return factory;
  94     }
  95 
  96     public VertexBuffer getVertexBuffer() {
  97         return vertexBuffer;
  98     }
  99 
 100     public void flushVertexBuffer() {
 101         if (curMaskRow > 0 || curMaskCol > 0) {
 102             maskTex.lock();
 103             // assert !maskTex.isSurfaceLost();
 104             // since it was bound and unflushed...
 105             maskTex.update(maskBuffer, maskTex.getPixelFormat(),
 106                            0, 0, 0, 0, highMaskCol, nextMaskRow,
 107                            maskTex.getPhysicalWidth(), true);
 108             maskTex.unlock();
 109             curMaskRow = curMaskCol = nextMaskRow = highMaskCol = 0;
 110         }
 111         vertexBuffer.flush();
 112     }
 113 
 114     /**
 115      *
 116      * This method will call releaseRenderTarget method to reset last
 117      * renderTarget and textures if g is null
 118      */
 119     public void setRenderTarget(BaseGraphics g) {
 120         if (g != null) {
 121             setRenderTarget(g.getRenderTarget(), g.getCameraNoClone(),
 122                     g.isDepthTest() && g.isDepthBuffer(), g.isState3D());
 123         } else {
 124             releaseRenderTarget();
 125         }
 126     }
 127 
 128     protected void releaseRenderTarget() {
 129         // Default implementation is a no-op. A pipeline may override if needed.
 130     }
 131 
 132     protected abstract void setRenderTarget(RenderTarget target, NGCamera camera,
 133                                             boolean depthTest, boolean state3D);
 134 
 135     public abstract void validateClearOp(BaseGraphics g);
 136 
 137     public abstract void validatePaintOp(BaseGraphics g, BaseTransform xform,
 138                                          Texture maskTex,
 139                                          float bx, float by, float bw, float bh);
 140 
 141     public abstract void validateTextureOp(BaseGraphics g, BaseTransform xform,
 142                                            Texture src, PixelFormat format);
 143 
 144     public void clearGlyphCaches() {
 145         clearCaches(greyGlyphCaches);
 146         clearCaches(lcdGlyphCaches);
 147     }
 148 
 149     private void clearCaches(Map<FontStrike, GlyphCache> glyphCaches) {
 150         for (Iterator<FontStrike> iter = glyphCaches.keySet().iterator(); iter.hasNext();) {
 151             iter.next().clearDesc();
 152         }
 153 
 154         for (GlyphCache cache : glyphCaches.values()) {
 155             if (cache != null) {
 156                 cache.clear();
 157             }
 158         }
 159         glyphCaches.clear();
 160     }
 161 
 162     abstract public RTTexture getLCDBuffer();
 163 
 164     public GlyphCache getGlyphCache(FontStrike strike) {
 165         Map<FontStrike, GlyphCache> glyphCaches =
 166             (strike.getAAMode() ==FontResource.AA_LCD)
 167             ? lcdGlyphCaches : greyGlyphCaches;
 168         return getGlyphCache(strike, glyphCaches);
 169     }
 170 
 171     public boolean isSuperShaderEnabled() {
 172         return false;
 173     }
 174 
 175     private GlyphCache getGlyphCache(FontStrike strike,
 176                                      Map<FontStrike, GlyphCache> glyphCaches) {
 177         GlyphCache glyphCache = glyphCaches.get(strike);
 178         if (glyphCache == null) {
 179             glyphCache = new GlyphCache(this, strike);
 180             glyphCaches.put(strike, glyphCache);
 181         }
 182         return glyphCache;
 183     }
 184 
 185     public Texture validateMaskTexture(MaskData maskData, boolean canScale) {
 186         int pad = canScale ? 1 : 0;
 187         int needW = maskData.getWidth() + pad + pad;
 188         int needH = maskData.getHeight() + pad + pad;
 189         int texW = 0, texH = 0;
 190 
 191         if (maskTex != null) {
 192             maskTex.lock();
 193             if (maskTex.isSurfaceLost()) {
 194                 maskTex = null;
 195             } else {
 196                 texW = maskTex.getContentWidth();
 197                 texH = maskTex.getContentHeight();
 198             }
 199         }
 200 
 201         if (maskTex == null || texW < needW || texH < needH) {




 202             if (maskTex != null) {
 203                 flushVertexBuffer();
 204                 maskTex.dispose();
 205                 maskTex = null;
 206             }
 207             maskBuffer = null;
 208 
 209             // grow the mask texture so that the new one is always
 210             // at least as large as the previous one; this avoids
 211             // lots of creation/disposal when the shapes alternate
 212             // between narrow/tall and wide/short
 213             int newTexW = Math.max(MIN_MASK_DIM, Math.max(needW, texW));
 214             int newTexH = Math.max(MIN_MASK_DIM, Math.max(needH, texH));
 215 

 216             maskTex = getResourceFactory().
 217                 createMaskTexture(newTexW, newTexH, WrapMode.CLAMP_NOT_NEEDED);
 218             maskBuffer = ByteBuffer.allocate(newTexW * newTexH);
 219             if (clearBuffer == null || clearBuffer.capacity() < newTexW) {
 220                 clearBuffer = null;
 221                 clearBuffer = ByteBuffer.allocate(newTexW);
 222             }
 223             curMaskRow = curMaskCol = nextMaskRow = highMaskCol = 0;
 224         }


 225 
 226         return maskTex;
 227     }
 228 
 229     public void updateMaskTexture(MaskData maskData, RectBounds maskBounds, boolean canScale) {
 230         // assert maskTex bound as texture 1...
 231         maskTex.assertLocked();
 232         int maskW = maskData.getWidth();
 233         int maskH = maskData.getHeight();
 234         int texW = maskTex.getContentWidth();
 235         int texH = maskTex.getContentHeight();
 236         int pad = canScale ? 1 : 0;
 237         int needW = maskW + pad + pad;
 238         int needH = maskH + pad + pad;
 239         if (curMaskCol + needW > texW) {
 240             curMaskCol = 0;
 241             curMaskRow = nextMaskRow;
 242         }
 243         if (curMaskRow + needH > texH) {
 244             flushVertexBuffer(); // side effect clears mask params
 245 //            curMaskRow = curMaskCol = nextMaskRow = 0;
 246         }
 247 
 248         int offset = curMaskRow * texW + curMaskCol;
 249         ByteToBytePixelConverter b2bpc = ByteGray.ToByteGrayConverter();
 250         if (canScale) {
 251             // [UL => UR)
 252             int off = offset;
 253             b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, maskW + 1, 1);
 254             // [UR => LR)
 255             off = offset + maskW + 1;
 256             b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, 1, maskH + 1);
 257             // (UL => LL]
 258             off = offset + texW;  // UL corner + 1 row
 259             b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, 1, maskH + 1);
 260             // (LL => LR]
 261             off = offset + (maskH + 1) * texW + 1; // LL corner + 1 col
 262             b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, maskW + 1, 1);
 263             offset += texW + 1;
 264         }
 265         b2bpc.convert(maskData.getMaskBuffer(), 0, maskW,
 266                       maskBuffer, offset, texW,
 267                       maskW, maskH);
 268 
 269         float physW = maskTex.getPhysicalWidth();
 270         float physH = maskTex.getPhysicalHeight();
 271         maskBounds.setMinX((curMaskCol + pad        ) / physW);
 272         maskBounds.setMinY((curMaskRow + pad        ) / physH);
 273         maskBounds.setMaxX((curMaskCol + pad + maskW) / physW);
 274         maskBounds.setMaxY((curMaskRow + pad + maskH) / physH);
 275 
 276         curMaskCol = curMaskCol + needW;
 277         if (highMaskCol < curMaskCol) highMaskCol = curMaskCol;
 278         if (nextMaskRow < curMaskRow + needH) nextMaskRow = curMaskRow + needH;
 279     }
 280 
 281     public int getRectTextureMaxSize() {
 282         if (rectTex == null) {
 283             createRectTexture();
 284         }
 285         return rectTexMax;
 286     }
 287 
 288     public Texture getRectTexture() {
 289         if (rectTex == null) {
 290             createRectTexture();
 291         }
 292 
 293         // rectTex is left permanent and locked so it never
 294         // goes away or needs to be checked for isSurfaceLost(), but we
 295         // add a lock here so that the caller can unlock without knowing
 296         // our inner implementation details
 297         rectTex.lock();
 298         return rectTex;
 299     }
 300 
 301     private void createRectTexture() {
 302         int texMax = PrismSettings.primTextureSize;
 303         if (texMax < 0) texMax = getResourceFactory().getMaximumTextureSize();
 304         int texDim = 3;
 305         int nextCellSize = 2;
 306         while (texDim + nextCellSize + 1 <= texMax) {
 307             rectTexMax = nextCellSize;
 308             texDim += ++nextCellSize;
 309         }
 310         byte mask[] = new byte[texDim * texDim];
 311         int cellY = 1;
 312         for (int cellH = 1; cellH <= rectTexMax; cellH++) {
 313             int cellX = 1;
 314             for (int cellW = 1; cellW <= rectTexMax; cellW++) {
 315                 int index = cellY * texDim + cellX;
 316                 for (int y = 0; y < cellH; y++) {
 317                     for (int x = 0; x < cellW; x++) {
 318                         mask[index + x] = (byte) 0xff;
 319                     }
 320                     index += texDim;
 321                 }
 322                 cellX += cellW + 1;
 323             }
 324             cellY += cellH + 1;
 325         }
 326         if (PrismSettings.verbose) {
 327             System.out.println("max rectangle texture cell size = "+rectTexMax);
 328         }
 329         Texture tex =
 330             getResourceFactory().createMaskTexture(texDim, texDim,
 331                                                    WrapMode.CLAMP_NOT_NEEDED);
 332         // rectTex remains permanently locked, useful, and permanent
 333         // an additional lock is added when a caller calls getWrapGreientTeture for
 334         // them to unlock
 335         tex.contentsUseful();
 336         tex.makePermanent();
 337         PixelFormat pf = tex.getPixelFormat();
 338         int scan = texDim * pf.getBytesPerPixelUnit();
 339         tex.update(ByteBuffer.wrap(mask), pf,
 340                    0, 0, 0, 0, texDim, texDim,
 341                    scan, false);
 342         rectTex = tex;
 343     }
 344 
 345     public Texture getWrapRectTexture() {
 346         if (wrapRectTex == null) {
 347             Texture tex =
 348                 getResourceFactory().createMaskTexture(2, 2, WrapMode.CLAMP_TO_EDGE);
 349             // wrapRectTex remains permanently locked, useful, and permanent
 350             // an additional lock is added when a caller calls getWrapGreientTeture for
 351             // them to unlock
 352             tex.contentsUseful();
 353             tex.makePermanent();
 354             int w = tex.getPhysicalWidth();
 355             int h = tex.getPhysicalHeight();
 356             if (PrismSettings.verbose) {
 357                 System.out.println("wrap rectangle texture = "+w+" x "+h);
 358             }
 359             // assert w == 2 && h == 2?
 360             byte mask[] = new byte[w * h];
 361             int off = w;
 362             for (int y = 1; y < h; y++) {
 363                 for (int x = 1; x < h; x++) {
 364                     mask[off + x] = (byte) 0xff;
 365                 }
 366                 off += w;
 367             }
 368             PixelFormat pf = tex.getPixelFormat();
 369             int scan = w * pf.getBytesPerPixelUnit();
 370             tex.update(ByteBuffer.wrap(mask), pf,
 371                        0, 0, 0, 0, w, h,
 372                        scan, false);
 373             wrapRectTex = tex;
 374         }
 375 
 376         // wrapRectTex is left permanent and locked so it never
 377         // goes away or needs to be checked for isSurfaceLost(), but we
 378         // add a lock here so that the caller can unlock without knowing
 379         // our inner implementation details
 380         wrapRectTex.lock();
 381         return wrapRectTex;
 382     }
 383 
 384     public Texture getOvalTexture() {
 385         if (ovalTex == null) {
 386             int cellMax = getRectTextureMaxSize();
 387             int texDim = (cellMax * (cellMax + 1)) / 2;
 388             // size now points at the start of the max-sized cell
 389             texDim += cellMax + 1;
 390             // size now points just past the empty row on the far side of the
 391             // max-sized cell - which is the dimension we want the texture...
 392             byte mask[] = new byte[texDim * texDim];
 393             int cellY = 1;
 394             for (int cellH = 1; cellH <= cellMax; cellH++) {
 395                 int cellX = 1;
 396                 for (int cellW = 1; cellW <= cellMax; cellW++) {
 397                     int index = cellY * texDim + cellX;
 398 //                    System.out.println("rasterizing "+cell_w+" x "+cell_h);
 399                     for (int y = 0; y < cellH; y++) {
 400                         if (y * 2 >= cellH) {
 401                             int reflecty = cellH - 1 - y;
 402                             // handle bottom half of ellipse via reflection
 403                             int rindex = index + (reflecty - y) * texDim;
 404                             for (int x = 0; x < cellW; x++) {
 405                                 mask[index + x] = mask[rindex + x];
 406                             }
 407                         } else {
 408                             // Use 8 sub-row samples
 409                             float ovalY = y + 0.0625f;  // 1/16
 410                             for (int i = 0; i < 8; i++) {
 411                                 float ovalX = (ovalY / cellH) - 0.5f;
 412                                 ovalX = (float) Math.sqrt(0.25f - ovalX * ovalX);
 413                                 int oxi = Math.round(cellW * 4.0f * (1.0f - ovalX * 2.0f));
 414                                 int edgeX = oxi >> 3;
 415                                 int subX = oxi & 0x7;
 416 //                                System.out.println("y = "+oy+", mask["+rx+"] += "+(8-subx)+", mask["+(rx+1)+"] += "+subx);
 417                                 mask[index + edgeX] += 8 - subX;
 418                                 mask[index + edgeX + 1] += subX;
 419                                 ovalY += 0.125f;  // 1/8
 420                             }
 421                             int accum = 0;
 422                             for (int x = 0; x < cellW; x++) {
 423                                 if (x * 2 >= cellW) {
 424                                     // handle right half of ellipse via reflection
 425                                     mask[index + x] = mask[index + cellW - 1 - x];
 426                                 } else {
 427                                     accum += mask[index + x];
 428 //                                    System.out.println("accum["+rx+"] = "+accum);
 429                                     mask[index + x] = (byte) ((accum * 255 + 32) / 64);
 430                                 }
 431                             }
 432                             // Sometimes for smaller ovals we leave some
 433                             // accumulation dirt just past the last cell
 434                             mask[index + cellW] = 0;
 435                         }
 436                         index += texDim;
 437                     }
 438                     cellX += cellW + 1;
 439                 }
 440                 cellY += cellH + 1;
 441             }
 442             if (false) {
 443                 int index = 0;
 444                 for (int y = 0; y < texDim; y++) {
 445                     for (int x = 0; x < texDim; x++) {
 446                         String s = Integer.toHexString((mask[index++] & 0xff) | 0x100);
 447                         System.out.print(s.substring(1)+" ");
 448                     }
 449                     System.out.println();
 450                 }
 451             }
 452             Texture tex =
 453                 getResourceFactory().createMaskTexture(texDim, texDim,
 454                                                        WrapMode.CLAMP_NOT_NEEDED);
 455             tex.contentsUseful();
 456             tex.makePermanent();
 457             PixelFormat pf = tex.getPixelFormat();
 458             int scan = texDim * pf.getBytesPerPixelUnit();
 459             tex.update(ByteBuffer.wrap(mask), pf,
 460                        0, 0, 0, 0, texDim, texDim,
 461                        scan, false);
 462             ovalTex = tex;
 463         }
 464 
 465         // ovalTex is left permanent and locked so it never
 466         // goes away or needs to be checked for isSurfaceLost(), but we
 467         // add a lock here so that the caller can unlock without knowing
 468         // our inner implementation details
 469         ovalTex.lock();
 470         return ovalTex;
 471     }
 472 
 473     public Texture getGradientTexture(Gradient grad, BaseTransform xform,
 474                                       int paintW, int paintH,
 475                                       MaskData maskData,
 476                                       float bx, float by, float bw, float bh)
 477     {
 478         int sizeInPixels = paintW * paintH;
 479         int sizeInBytes = sizeInPixels * 4;
 480         if (paintBuffer == null || paintBuffer.capacity() < sizeInBytes) {
 481             paintPixels = new int[sizeInPixels];
 482             paintBuffer = ByteBuffer.wrap(new byte[sizeInBytes]);
 483         }
 484 
 485         if (paintTex != null) {
 486             paintTex.lock();
 487             if (paintTex.isSurfaceLost()) {
 488                 paintTex = null;
 489             }
 490         }
 491 
 492         if (paintTex == null ||
 493             paintTex.getContentWidth()  < paintW ||
 494             paintTex.getContentHeight() < paintH)
 495         {
 496             int newTexW = paintW;
 497             int newTexH = paintH;
 498             if (paintTex != null) {
 499                 // grow the paint texture so that the new one is always
 500                 // at least as large as the previous one; this avoids
 501                 // lots of creation/disposal when the shapes alternate
 502                 // between narrow/tall and wide/short
 503                 newTexW = Math.max(paintW, paintTex.getContentWidth());
 504                 newTexH = Math.max(paintH, paintTex.getContentHeight());
 505                 paintTex.dispose();
 506             }
 507             paintTex = getResourceFactory().
 508                 createTexture(PixelFormat.BYTE_BGRA_PRE,
 509                               Texture.Usage.DEFAULT,
 510                               Texture.WrapMode.CLAMP_NOT_NEEDED,
 511                               newTexW, newTexH);
 512         }
 513 
 514         // note that the gradient will be tightly packed into paintImg
 515         // (i.e., no space at the end of each logical row) since there
 516         // is no way to control scanline stride for texture uploads in ES1
 517         PaintUtil.fillImageWithGradient(paintPixels, grad, xform,
 518                                         0, 0, paintW, paintH,
 519                                         bx, by, bw, bh);
 520 
 521         // RT-27421
 522         // TODO: could save some work here if we converted the *GradientContext
 523         // classes to produce ByteRgbaPre data instead of IntArgbPre data...
 524         byte[] bytePixels = paintBuffer.array();
 525         if (maskData != null) {
 526             // modulate with the mask pixels while we convert from
 527             // IntArgbPre to ByteRgbaPre
 528             byte[] maskPixels = maskData.getMaskBuffer().array();
 529             int j = 0;
 530             for (int i = 0; i < sizeInPixels; i++) {
 531                 int pixel = paintPixels[i];
 532                 int maskA = maskPixels[i] & 0xff;
 533                 bytePixels[j++] = (byte)((((pixel       ) & 0xff) * maskA) / 255);
 534                 bytePixels[j++] = (byte)((((pixel >>   8) & 0xff) * maskA) / 255);
 535                 bytePixels[j++] = (byte)((((pixel >>  16) & 0xff) * maskA) / 255);
 536                 bytePixels[j++] = (byte)((((pixel >>> 24)       ) * maskA) / 255);
 537             }
 538         } else {
 539             // just convert from IntArgbPre to ByteRgbaPre
 540             int j = 0;
 541             for (int i = 0; i < sizeInPixels; i++) {
 542                 int pixel = paintPixels[i];
 543                 bytePixels[j++] = (byte)((pixel       ) & 0xff);
 544                 bytePixels[j++] = (byte)((pixel >>   8) & 0xff);
 545                 bytePixels[j++] = (byte)((pixel >>  16) & 0xff);
 546                 bytePixels[j++] = (byte)((pixel >>> 24)       );
 547             }
 548         }
 549 
 550         paintTex.update(paintBuffer, PixelFormat.BYTE_BGRA_PRE,
 551                         0, 0, 0, 0, paintW, paintH, paintW*4, false);
 552 
 553         return paintTex;
 554     }
 555 }
--- EOF ---