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