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 }