1 /*
   2  * Copyright (c) 2009, 2013, 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.transform.BaseTransform;
  36 import com.sun.javafx.sg.prism.NGCamera;
  37 import com.sun.prism.PixelFormat;
  38 import com.sun.prism.RTTexture;
  39 import com.sun.prism.RenderTarget;
  40 import com.sun.prism.ResourceFactory;
  41 import com.sun.prism.Texture;
  42 import com.sun.prism.Texture.WrapMode;
  43 import com.sun.prism.impl.paint.PaintUtil;
  44 import com.sun.prism.impl.shape.MaskData;
  45 import com.sun.prism.paint.Gradient;
  46 
  47 public abstract class BaseContext {
  48 
  49     private final Screen screen;
  50     private final ResourceFactory factory;
  51     private final VertexBuffer vertexBuffer;
  52 
  53     private Texture maskTex;
  54     private Texture paintTex;
  55     private int[] paintPixels;
  56     private ByteBuffer paintBuffer;
  57 
  58     private Texture rectTex;
  59     private int rectTexMax;
  60     private Texture wrapRectTex;
  61     private Texture ovalTex;
  62 
  63     // TODO: need to dispose these when the context is disposed... (RT-27421)
  64     private final Map<FontStrike, GlyphCache>
  65         greyGlyphCaches = new HashMap<FontStrike, GlyphCache>();
  66     private final Map<FontStrike, GlyphCache>
  67         lcdGlyphCaches = new HashMap<FontStrike, GlyphCache>();
  68 
  69     protected BaseContext(Screen screen, ResourceFactory factory, VertexBuffer vb) {
  70         this.screen = screen;
  71         this.factory = factory;
  72         this.vertexBuffer = vb;
  73     }
  74 
  75     protected void setDeviceParametersFor2D() {}
  76     protected void setDeviceParametersFor3D() {}
  77 
  78     public Screen getAssociatedScreen() {
  79         return screen;
  80     }
  81 
  82     public ResourceFactory getResourceFactory() {
  83         return factory;
  84     }
  85 
  86     public VertexBuffer getVertexBuffer() {
  87         return vertexBuffer;
  88     }
  89 
  90     public void flushVertexBuffer() {
  91         vertexBuffer.flush();
  92     }
  93 
  94     /**
  95      *
  96      * This method will call releaseRenderTarget method to reset last
  97      * renderTarget and textures if g is null
  98      */
  99     public void setRenderTarget(BaseGraphics g) {
 100         if (g != null) {
 101             setRenderTarget(g.getRenderTarget(), g.getCameraNoClone(),
 102                     g.isDepthTest() && g.isDepthBuffer(), g.isState3D());
 103         } else {
 104             releaseRenderTarget();
 105         }
 106     }
 107 
 108     protected void releaseRenderTarget() {
 109         // Default implementation is a no-op. A pipeline may override if needed.
 110     }
 111 
 112     protected abstract void setRenderTarget(RenderTarget target, NGCamera camera,
 113                                             boolean depthTest, boolean state3D);
 114 
 115     public abstract void validateClearOp(BaseGraphics g);
 116 
 117     public abstract void validatePaintOp(BaseGraphics g, BaseTransform xform,
 118                                          Texture maskTex,
 119                                          float bx, float by, float bw, float bh);
 120 
 121     public abstract void validateTextureOp(BaseGraphics g, BaseTransform xform,
 122                                            Texture src, PixelFormat format);
 123 
 124     public void clearGlyphCaches() {
 125         clearCaches(greyGlyphCaches);
 126         clearCaches(lcdGlyphCaches);
 127     }
 128 
 129     private void clearCaches(Map<FontStrike, GlyphCache> glyphCaches) {
 130         for (Iterator<FontStrike> iter = glyphCaches.keySet().iterator(); iter.hasNext();) {
 131             iter.next().clearDesc();
 132         }
 133 
 134         for (GlyphCache cache : glyphCaches.values()) {
 135             if (cache != null) {
 136                 cache.clear();
 137             }
 138         }
 139         glyphCaches.clear();
 140     }
 141 
 142     abstract public RTTexture getLCDBuffer();
 143 
 144     public GlyphCache getGlyphCache(FontStrike strike) {
 145         Map<FontStrike, GlyphCache> glyphCaches =
 146             (strike.getAAMode() ==FontResource.AA_LCD)
 147             ? lcdGlyphCaches : greyGlyphCaches;
 148         return getGlyphCache(strike, glyphCaches);
 149     }
 150 
 151     public boolean isSuperShaderEnabled() {
 152         return false;
 153     }
 154 
 155     private GlyphCache getGlyphCache(FontStrike strike,
 156                                      Map<FontStrike, GlyphCache> glyphCaches) {
 157         GlyphCache glyphCache = glyphCaches.get(strike);
 158         if (glyphCache == null) {
 159             glyphCache = new GlyphCache(this, strike);
 160             glyphCaches.put(strike, glyphCache);
 161         }
 162         return glyphCache;
 163     }
 164 
 165     public Texture getMaskTexture(MaskData maskData, boolean canScale) {
 166         int maskW = maskData.getWidth();
 167         int maskH = maskData.getHeight();
 168         if (maskTex != null) {
 169             maskTex.lock();
 170             if (maskTex.isSurfaceLost()) {
 171                 maskTex = null;
 172             }
 173         }
 174         if (maskTex == null ||
 175             maskTex.getContentWidth()  < maskW ||
 176             maskTex.getContentHeight() < maskH)
 177         {
 178             int newTexW = maskW;
 179             int newTexH = maskH;
 180             if (maskTex != null) {
 181                 // grow the mask texture so that the new one is always
 182                 // at least as large as the previous one; this avoids
 183                 // lots of creation/disposal when the shapes alternate
 184                 // between narrow/tall and wide/short
 185                 newTexW = Math.max(maskW, maskTex.getContentWidth());
 186                 newTexH = Math.max(maskH, maskTex.getContentHeight());
 187                 maskTex.dispose();
 188             }
 189             maskTex = getResourceFactory().
 190                 createMaskTexture(newTexW, newTexH,
 191                                   canScale
 192                                       ? WrapMode.CLAMP_TO_ZERO
 193                                       : WrapMode.CLAMP_NOT_NEEDED);
 194         }
 195 
 196         maskData.uploadToTexture(maskTex, 0, 0, false);
 197 
 198         return maskTex;
 199     }
 200 
 201     public int getRectTextureMaxSize() {
 202         if (rectTex == null) {
 203             createRectTexture();
 204         }
 205         return rectTexMax;
 206     }
 207 
 208     public Texture getRectTexture() {
 209         if (rectTex == null) {
 210             createRectTexture();
 211         }
 212 
 213         // rectTex is left permanent and locked so it never
 214         // goes away or needs to be checked for isSurfaceLost(), but we
 215         // add a lock here so that the caller can unlock without knowing
 216         // our inner implementation details
 217         rectTex.lock();
 218         return rectTex;
 219     }
 220 
 221     private void createRectTexture() {
 222         int texMax = PrismSettings.primTextureSize;
 223         if (texMax < 0) texMax = getResourceFactory().getMaximumTextureSize();
 224         int texDim = 3;
 225         int nextCellSize = 2;
 226         while (texDim + nextCellSize + 1 <= texMax) {
 227             rectTexMax = nextCellSize;
 228             texDim += ++nextCellSize;
 229         }
 230         byte mask[] = new byte[texDim * texDim];
 231         int cellY = 1;
 232         for (int cellH = 1; cellH <= rectTexMax; cellH++) {
 233             int cellX = 1;
 234             for (int cellW = 1; cellW <= rectTexMax; cellW++) {
 235                 int index = cellY * texDim + cellX;
 236                 for (int y = 0; y < cellH; y++) {
 237                     for (int x = 0; x < cellW; x++) {
 238                         mask[index + x] = (byte) 0xff;
 239                     }
 240                     index += texDim;
 241                 }
 242                 cellX += cellW + 1;
 243             }
 244             cellY += cellH + 1;
 245         }
 246         if (PrismSettings.verbose) {
 247             System.out.println("max rectangle texture cell size = "+rectTexMax);
 248         }
 249         Texture tex =
 250             getResourceFactory().createMaskTexture(texDim, texDim,
 251                                                    WrapMode.CLAMP_NOT_NEEDED);
 252         // rectTex remains permanently locked, useful, and permanent
 253         // an additional lock is added when a caller calls getWrapGreientTeture for
 254         // them to unlock
 255         tex.contentsUseful();
 256         tex.makePermanent();
 257         PixelFormat pf = tex.getPixelFormat();
 258         int scan = texDim * pf.getBytesPerPixelUnit();
 259         tex.update(ByteBuffer.wrap(mask), pf,
 260                    0, 0, 0, 0, texDim, texDim,
 261                    scan, false);
 262         rectTex = tex;
 263     }
 264 
 265     public Texture getWrapRectTexture() {
 266         if (wrapRectTex == null) {
 267             Texture tex =
 268                 getResourceFactory().createMaskTexture(2, 2, WrapMode.CLAMP_TO_EDGE);
 269             // wrapRectTex remains permanently locked, useful, and permanent
 270             // an additional lock is added when a caller calls getWrapGreientTeture for
 271             // them to unlock
 272             tex.contentsUseful();
 273             tex.makePermanent();
 274             int w = tex.getPhysicalWidth();
 275             int h = tex.getPhysicalHeight();
 276             if (PrismSettings.verbose) {
 277                 System.out.println("wrap rectangle texture = "+w+" x "+h);
 278             }
 279             // assert w == 2 && h == 2?
 280             byte mask[] = new byte[w * h];
 281             int off = w;
 282             for (int y = 1; y < h; y++) {
 283                 for (int x = 1; x < h; x++) {
 284                     mask[off + x] = (byte) 0xff;
 285                 }
 286                 off += w;
 287             }
 288             PixelFormat pf = tex.getPixelFormat();
 289             int scan = w * pf.getBytesPerPixelUnit();
 290             tex.update(ByteBuffer.wrap(mask), pf,
 291                        0, 0, 0, 0, w, h,
 292                        scan, false);
 293             wrapRectTex = tex;
 294         }
 295 
 296         // wrapRectTex is left permanent and locked so it never
 297         // goes away or needs to be checked for isSurfaceLost(), but we
 298         // add a lock here so that the caller can unlock without knowing
 299         // our inner implementation details
 300         wrapRectTex.lock();
 301         return wrapRectTex;
 302     }
 303 
 304     public Texture getOvalTexture() {
 305         if (ovalTex == null) {
 306             int cellMax = getRectTextureMaxSize();
 307             int texDim = (cellMax * (cellMax + 1)) / 2;
 308             // size now points at the start of the max-sized cell
 309             texDim += cellMax + 1;
 310             // size now points just past the empty row on the far side of the
 311             // max-sized cell - which is the dimension we want the texture...
 312             byte mask[] = new byte[texDim * texDim];
 313             int cellY = 1;
 314             for (int cellH = 1; cellH <= cellMax; cellH++) {
 315                 int cellX = 1;
 316                 for (int cellW = 1; cellW <= cellMax; cellW++) {
 317                     int index = cellY * texDim + cellX;
 318 //                    System.out.println("rasterizing "+cell_w+" x "+cell_h);
 319                     for (int y = 0; y < cellH; y++) {
 320                         if (y * 2 >= cellH) {
 321                             int reflecty = cellH - 1 - y;
 322                             // handle bottom half of ellipse via reflection
 323                             int rindex = index + (reflecty - y) * texDim;
 324                             for (int x = 0; x < cellW; x++) {
 325                                 mask[index + x] = mask[rindex + x];
 326                             }
 327                         } else {
 328                             // Use 8 sub-row samples
 329                             float ovalY = y + 0.0625f;  // 1/16
 330                             for (int i = 0; i < 8; i++) {
 331                                 float ovalX = (ovalY / cellH) - 0.5f;
 332                                 ovalX = (float) Math.sqrt(0.25f - ovalX * ovalX);
 333                                 int oxi = Math.round(cellW * 4.0f * (1.0f - ovalX * 2.0f));
 334                                 int edgeX = oxi >> 3;
 335                                 int subX = oxi & 0x7;
 336 //                                System.out.println("y = "+oy+", mask["+rx+"] += "+(8-subx)+", mask["+(rx+1)+"] += "+subx);
 337                                 mask[index + edgeX] += 8 - subX;
 338                                 mask[index + edgeX + 1] += subX;
 339                                 ovalY += 0.125f;  // 1/8
 340                             }
 341                             int accum = 0;
 342                             for (int x = 0; x < cellW; x++) {
 343                                 if (x * 2 >= cellW) {
 344                                     // handle right half of ellipse via reflection
 345                                     mask[index + x] = mask[index + cellW - 1 - x];
 346                                 } else {
 347                                     accum += mask[index + x];
 348 //                                    System.out.println("accum["+rx+"] = "+accum);
 349                                     mask[index + x] = (byte) ((accum * 255 + 32) / 64);
 350                                 }
 351                             }
 352                             // Sometimes for smaller ovals we leave some
 353                             // accumulation dirt just past the last cell
 354                             mask[index + cellW] = 0;
 355                         }
 356                         index += texDim;
 357                     }
 358                     cellX += cellW + 1;
 359                 }
 360                 cellY += cellH + 1;
 361             }
 362             if (false) {
 363                 int index = 0;
 364                 for (int y = 0; y < texDim; y++) {
 365                     for (int x = 0; x < texDim; x++) {
 366                         String s = Integer.toHexString((mask[index++] & 0xff) | 0x100);
 367                         System.out.print(s.substring(1)+" ");
 368                     }
 369                     System.out.println();
 370                 }
 371             }
 372             Texture tex =
 373                 getResourceFactory().createMaskTexture(texDim, texDim,
 374                                                        WrapMode.CLAMP_NOT_NEEDED);
 375             tex.contentsUseful();
 376             tex.makePermanent();
 377             PixelFormat pf = tex.getPixelFormat();
 378             int scan = texDim * pf.getBytesPerPixelUnit();
 379             tex.update(ByteBuffer.wrap(mask), pf,
 380                        0, 0, 0, 0, texDim, texDim,
 381                        scan, false);
 382             ovalTex = tex;
 383         }
 384 
 385         // ovalTex is left permanent and locked so it never
 386         // goes away or needs to be checked for isSurfaceLost(), but we
 387         // add a lock here so that the caller can unlock without knowing
 388         // our inner implementation details
 389         ovalTex.lock();
 390         return ovalTex;
 391     }
 392 
 393     public Texture getGradientTexture(Gradient grad, BaseTransform xform,
 394                                       int paintW, int paintH,
 395                                       MaskData maskData,
 396                                       float bx, float by, float bw, float bh)
 397     {
 398         int sizeInPixels = paintW * paintH;
 399         int sizeInBytes = sizeInPixels * 4;
 400         if (paintBuffer == null || paintBuffer.capacity() < sizeInBytes) {
 401             paintPixels = new int[sizeInPixels];
 402             paintBuffer = ByteBuffer.wrap(new byte[sizeInBytes]);
 403         }
 404 
 405         if (paintTex != null) {
 406             paintTex.lock();
 407             if (paintTex.isSurfaceLost()) {
 408                 paintTex = null;
 409             }
 410         }
 411 
 412         if (paintTex == null ||
 413             paintTex.getContentWidth()  < paintW ||
 414             paintTex.getContentHeight() < paintH)
 415         {
 416             int newTexW = paintW;
 417             int newTexH = paintH;
 418             if (paintTex != null) {
 419                 // grow the paint texture so that the new one is always
 420                 // at least as large as the previous one; this avoids
 421                 // lots of creation/disposal when the shapes alternate
 422                 // between narrow/tall and wide/short
 423                 newTexW = Math.max(paintW, paintTex.getContentWidth());
 424                 newTexH = Math.max(paintH, paintTex.getContentHeight());
 425                 paintTex.dispose();
 426             }
 427             paintTex = getResourceFactory().
 428                 createTexture(PixelFormat.BYTE_BGRA_PRE,
 429                               Texture.Usage.DEFAULT,
 430                               Texture.WrapMode.CLAMP_NOT_NEEDED,
 431                               newTexW, newTexH);
 432         }
 433 
 434         // note that the gradient will be tightly packed into paintImg
 435         // (i.e., no space at the end of each logical row) since there
 436         // is no way to control scanline stride for texture uploads in ES1
 437         PaintUtil.fillImageWithGradient(paintPixels, grad, xform,
 438                                         0, 0, paintW, paintH,
 439                                         bx, by, bw, bh);
 440 
 441         // RT-27421
 442         // TODO: could save some work here if we converted the *GradientContext
 443         // classes to produce ByteRgbaPre data instead of IntArgbPre data...
 444         byte[] bytePixels = paintBuffer.array();
 445         if (maskData != null) {
 446             // modulate with the mask pixels while we convert from
 447             // IntArgbPre to ByteRgbaPre
 448             byte[] maskPixels = maskData.getMaskBuffer().array();
 449             int j = 0;
 450             for (int i = 0; i < sizeInPixels; i++) {
 451                 int pixel = paintPixels[i];
 452                 int maskA = maskPixels[i] & 0xff;
 453                 bytePixels[j++] = (byte)((((pixel       ) & 0xff) * maskA) / 255);
 454                 bytePixels[j++] = (byte)((((pixel >>   8) & 0xff) * maskA) / 255);
 455                 bytePixels[j++] = (byte)((((pixel >>  16) & 0xff) * maskA) / 255);
 456                 bytePixels[j++] = (byte)((((pixel >>> 24)       ) * maskA) / 255);
 457             }
 458         } else {
 459             // just convert from IntArgbPre to ByteRgbaPre
 460             int j = 0;
 461             for (int i = 0; i < sizeInPixels; i++) {
 462                 int pixel = paintPixels[i];
 463                 bytePixels[j++] = (byte)((pixel       ) & 0xff);
 464                 bytePixels[j++] = (byte)((pixel >>   8) & 0xff);
 465                 bytePixels[j++] = (byte)((pixel >>  16) & 0xff);
 466                 bytePixels[j++] = (byte)((pixel >>> 24)       );
 467             }
 468         }
 469 
 470         paintTex.update(paintBuffer, PixelFormat.BYTE_BGRA_PRE,
 471                         0, 0, 0, 0, paintW, paintH, paintW*4, false);
 472 
 473         return paintTex;
 474     }
 475 }