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 }