package com.sun.prism.impl;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import com.sun.glass.ui.Screen;
import com.sun.javafx.font.FontResource;
import com.sun.javafx.font.FontStrike;
import com.sun.javafx.geom.RectBounds;
import com.sun.javafx.geom.transform.BaseTransform;
import com.sun.javafx.image.ByteToBytePixelConverter;
import com.sun.javafx.image.impl.ByteGray;
import com.sun.javafx.sg.prism.NGCamera;
import com.sun.prism.PixelFormat;
import com.sun.prism.RTTexture;
import com.sun.prism.RenderTarget;
import com.sun.prism.ResourceFactory;
import com.sun.prism.Texture;
import com.sun.prism.Texture.WrapMode;
import com.sun.prism.impl.paint.PaintUtil;
import com.sun.prism.impl.shape.MaskData;
import com.sun.prism.paint.Gradient;
public abstract class BaseContext {
private final Screen screen;
private final ResourceFactory factory;
private final VertexBuffer vertexBuffer;
private static final int MIN_MASK_DIM = 1024;
private Texture maskTex;
private ByteBuffer maskBuffer;
private ByteBuffer clearBuffer;
private int curMaskRow;
private int nextMaskRow;
private int curMaskCol;
private int highMaskCol;
private Texture paintTex;
private int[] paintPixels;
private ByteBuffer paintBuffer;
private Texture rectTex;
private int rectTexMax;
private Texture wrapRectTex;
private Texture ovalTex;
private final Map<FontStrike, GlyphCache>
greyGlyphCaches = new HashMap<FontStrike, GlyphCache>();
private final Map<FontStrike, GlyphCache>
lcdGlyphCaches = new HashMap<FontStrike, GlyphCache>();
protected BaseContext(Screen screen, ResourceFactory factory, int vbQuads) {
this.screen = screen;
this.factory = factory;
this.vertexBuffer = new VertexBuffer(this, vbQuads);
}
protected void setDeviceParametersFor2D() {}
protected void setDeviceParametersFor3D() {}
public Screen getAssociatedScreen() {
return screen;
}
public ResourceFactory getResourceFactory() {
return factory;
}
public VertexBuffer getVertexBuffer() {
return vertexBuffer;
}
public void flushVertexBuffer() {
vertexBuffer.flush();
}
protected final void flushMask() {
if (curMaskRow > 0 || curMaskCol > 0) {
maskTex.lock();
maskTex.update(maskBuffer, maskTex.getPixelFormat(),
0, 0, 0, 0, highMaskCol, nextMaskRow,
maskTex.getContentWidth(), true);
maskTex.unlock();
curMaskRow = curMaskCol = nextMaskRow = highMaskCol = 0;
}
}
public void drawQuads(float coordArray[], byte colorArray[], int numVertices) {
flushMask();
renderQuads(coordArray, colorArray, numVertices);
}
protected abstract void renderQuads(float coordArray[], byte colorArray[], int numVertices);
public void setRenderTarget(BaseGraphics g) {
if (g != null) {
setRenderTarget(g.getRenderTarget(), g.getCameraNoClone(),
g.isDepthTest() && g.isDepthBuffer(), g.isState3D());
} else {
releaseRenderTarget();
}
}
protected void releaseRenderTarget() {
}
protected abstract void setRenderTarget(RenderTarget target, NGCamera camera,
boolean depthTest, boolean state3D);
public abstract void validateClearOp(BaseGraphics g);
public abstract void validatePaintOp(BaseGraphics g, BaseTransform xform,
Texture maskTex,
float bx, float by, float bw, float bh);
public abstract void validateTextureOp(BaseGraphics g, BaseTransform xform,
Texture src, PixelFormat format);
public void clearGlyphCaches() {
clearCaches(greyGlyphCaches);
clearCaches(lcdGlyphCaches);
}
private void clearCaches(Map<FontStrike, GlyphCache> glyphCaches) {
for (Iterator<FontStrike> iter = glyphCaches.keySet().iterator(); iter.hasNext();) {
iter.next().clearDesc();
}
for (GlyphCache cache : glyphCaches.values()) {
if (cache != null) {
cache.clear();
}
}
glyphCaches.clear();
}
abstract public RTTexture getLCDBuffer();
public GlyphCache getGlyphCache(FontStrike strike) {
Map<FontStrike, GlyphCache> glyphCaches =
(strike.getAAMode() ==FontResource.AA_LCD)
? lcdGlyphCaches : greyGlyphCaches;
return getGlyphCache(strike, glyphCaches);
}
public boolean isSuperShaderEnabled() {
return false;
}
private GlyphCache getGlyphCache(FontStrike strike,
Map<FontStrike, GlyphCache> glyphCaches) {
GlyphCache glyphCache = glyphCaches.get(strike);
if (glyphCache == null) {
glyphCache = new GlyphCache(this, strike);
glyphCaches.put(strike, glyphCache);
}
return glyphCache;
}
public Texture validateMaskTexture(MaskData maskData, boolean canScale) {
int pad = canScale ? 1 : 0;
int needW = maskData.getWidth() + pad + pad;
int needH = maskData.getHeight() + pad + pad;
int texW = 0, texH = 0;
if (maskTex != null) {
maskTex.lock();
if (maskTex.isSurfaceLost()) {
maskTex = null;
} else {
texW = maskTex.getContentWidth();
texH = maskTex.getContentHeight();
}
}
if (maskTex == null || texW < needW || texH < needH) {
if (maskTex != null) {
flushVertexBuffer();
maskTex.dispose();
maskTex = null;
}
maskBuffer = null;
int newTexW = Math.max(MIN_MASK_DIM, Math.max(needW, texW));
int newTexH = Math.max(MIN_MASK_DIM, Math.max(needH, texH));
maskTex = getResourceFactory().
createMaskTexture(newTexW, newTexH, WrapMode.CLAMP_NOT_NEEDED);
maskBuffer = ByteBuffer.allocate(newTexW * newTexH);
if (clearBuffer == null || clearBuffer.capacity() < newTexW) {
clearBuffer = null;
clearBuffer = ByteBuffer.allocate(newTexW);
}
curMaskRow = curMaskCol = nextMaskRow = highMaskCol = 0;
}
return maskTex;
}
public void updateMaskTexture(MaskData maskData, RectBounds maskBounds, boolean canScale) {
maskTex.assertLocked();
int maskW = maskData.getWidth();
int maskH = maskData.getHeight();
int texW = maskTex.getContentWidth();
int texH = maskTex.getContentHeight();
int pad = canScale ? 1 : 0;
int needW = maskW + pad + pad;
int needH = maskH + pad + pad;
if (curMaskCol + needW > texW) {
curMaskCol = 0;
curMaskRow = nextMaskRow;
}
if (curMaskRow + needH > texH) {
flushVertexBuffer();
}
int offset = curMaskRow * texW + curMaskCol;
ByteToBytePixelConverter b2bpc = ByteGray.ToByteGrayConverter();
if (canScale) {
int off = offset;
b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, maskW + 1, 1);
off = offset + maskW + 1;
b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, 1, maskH + 1);
off = offset + texW;
b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, 1, maskH + 1);
off = offset + (maskH + 1) * texW + 1;
b2bpc.convert(clearBuffer, 0, 0, maskBuffer, off, texW, maskW + 1, 1);
offset += texW + 1;
}
b2bpc.convert(maskData.getMaskBuffer(), 0, maskW,
maskBuffer, offset, texW,
maskW, maskH);
float physW = maskTex.getPhysicalWidth();
float physH = maskTex.getPhysicalHeight();
maskBounds.setMinX((curMaskCol + pad ) / physW);
maskBounds.setMinY((curMaskRow + pad ) / physH);
maskBounds.setMaxX((curMaskCol + pad + maskW) / physW);
maskBounds.setMaxY((curMaskRow + pad + maskH) / physH);
curMaskCol = curMaskCol + needW;
if (highMaskCol < curMaskCol) highMaskCol = curMaskCol;
if (nextMaskRow < curMaskRow + needH) nextMaskRow = curMaskRow + needH;
}
public int getRectTextureMaxSize() {
if (rectTex == null) {
createRectTexture();
}
return rectTexMax;
}
public Texture getRectTexture() {
if (rectTex == null) {
createRectTexture();
}
rectTex.lock();
return rectTex;
}
private void createRectTexture() {
int texMax = PrismSettings.primTextureSize;
if (texMax < 0) texMax = getResourceFactory().getMaximumTextureSize();
int texDim = 3;
int nextCellSize = 2;
while (texDim + nextCellSize + 1 <= texMax) {
rectTexMax = nextCellSize;
texDim += ++nextCellSize;
}
byte mask[] = new byte[texDim * texDim];
int cellY = 1;
for (int cellH = 1; cellH <= rectTexMax; cellH++) {
int cellX = 1;
for (int cellW = 1; cellW <= rectTexMax; cellW++) {
int index = cellY * texDim + cellX;
for (int y = 0; y < cellH; y++) {
for (int x = 0; x < cellW; x++) {
mask[index + x] = (byte) 0xff;
}
index += texDim;
}
cellX += cellW + 1;
}
cellY += cellH + 1;
}
if (PrismSettings.verbose) {
System.out.println("max rectangle texture cell size = "+rectTexMax);
}
Texture tex =
getResourceFactory().createMaskTexture(texDim, texDim,
WrapMode.CLAMP_NOT_NEEDED);
tex.contentsUseful();
tex.makePermanent();
PixelFormat pf = tex.getPixelFormat();
int scan = texDim * pf.getBytesPerPixelUnit();
tex.update(ByteBuffer.wrap(mask), pf,
0, 0, 0, 0, texDim, texDim,
scan, false);
rectTex = tex;
}
public Texture getWrapRectTexture() {
if (wrapRectTex == null) {
Texture tex =
getResourceFactory().createMaskTexture(2, 2, WrapMode.CLAMP_TO_EDGE);
tex.contentsUseful();
tex.makePermanent();
int w = tex.getPhysicalWidth();
int h = tex.getPhysicalHeight();
if (PrismSettings.verbose) {
System.out.println("wrap rectangle texture = "+w+" x "+h);
}
byte mask[] = new byte[w * h];
int off = w;
for (int y = 1; y < h; y++) {
for (int x = 1; x < h; x++) {
mask[off + x] = (byte) 0xff;
}
off += w;
}
PixelFormat pf = tex.getPixelFormat();
int scan = w * pf.getBytesPerPixelUnit();
tex.update(ByteBuffer.wrap(mask), pf,
0, 0, 0, 0, w, h,
scan, false);
wrapRectTex = tex;
}
wrapRectTex.lock();
return wrapRectTex;
}
public Texture getOvalTexture() {
if (ovalTex == null) {
int cellMax = getRectTextureMaxSize();
int texDim = (cellMax * (cellMax + 1)) / 2;
texDim += cellMax + 1;
byte mask[] = new byte[texDim * texDim];
int cellY = 1;
for (int cellH = 1; cellH <= cellMax; cellH++) {
int cellX = 1;
for (int cellW = 1; cellW <= cellMax; cellW++) {
int index = cellY * texDim + cellX;
for (int y = 0; y < cellH; y++) {
if (y * 2 >= cellH) {
int reflecty = cellH - 1 - y;
int rindex = index + (reflecty - y) * texDim;
for (int x = 0; x < cellW; x++) {
mask[index + x] = mask[rindex + x];
}
} else {
float ovalY = y + 0.0625f;
for (int i = 0; i < 8; i++) {
float ovalX = (ovalY / cellH) - 0.5f;
ovalX = (float) Math.sqrt(0.25f - ovalX * ovalX);
int oxi = Math.round(cellW * 4.0f * (1.0f - ovalX * 2.0f));
int edgeX = oxi >> 3;
int subX = oxi & 0x7;
mask[index + edgeX] += 8 - subX;
mask[index + edgeX + 1] += subX;
ovalY += 0.125f;
}
int accum = 0;
for (int x = 0; x < cellW; x++) {
if (x * 2 >= cellW) {
mask[index + x] = mask[index + cellW - 1 - x];
} else {
accum += mask[index + x];
mask[index + x] = (byte) ((accum * 255 + 32) / 64);
}
}
mask[index + cellW] = 0;
}
index += texDim;
}
cellX += cellW + 1;
}
cellY += cellH + 1;
}
if (false) {
int index = 0;
for (int y = 0; y < texDim; y++) {
for (int x = 0; x < texDim; x++) {
String s = Integer.toHexString((mask[index++] & 0xff) | 0x100);
System.out.print(s.substring(1)+" ");
}
System.out.println();
}
}
Texture tex =
getResourceFactory().createMaskTexture(texDim, texDim,
WrapMode.CLAMP_NOT_NEEDED);
tex.contentsUseful();
tex.makePermanent();
PixelFormat pf = tex.getPixelFormat();
int scan = texDim * pf.getBytesPerPixelUnit();
tex.update(ByteBuffer.wrap(mask), pf,
0, 0, 0, 0, texDim, texDim,
scan, false);
ovalTex = tex;
}
ovalTex.lock();
return ovalTex;
}
public Texture getGradientTexture(Gradient grad, BaseTransform xform,
int paintW, int paintH,
MaskData maskData,
float bx, float by, float bw, float bh)
{
int sizeInPixels = paintW * paintH;
int sizeInBytes = sizeInPixels * 4;
if (paintBuffer == null || paintBuffer.capacity() < sizeInBytes) {
paintPixels = new int[sizeInPixels];
paintBuffer = ByteBuffer.wrap(new byte[sizeInBytes]);
}
if (paintTex != null) {
paintTex.lock();
if (paintTex.isSurfaceLost()) {
paintTex = null;
}
}
if (paintTex == null ||
paintTex.getContentWidth() < paintW ||
paintTex.getContentHeight() < paintH)
{
int newTexW = paintW;
int newTexH = paintH;
if (paintTex != null) {
newTexW = Math.max(paintW, paintTex.getContentWidth());
newTexH = Math.max(paintH, paintTex.getContentHeight());
paintTex.dispose();
}
paintTex = getResourceFactory().
createTexture(PixelFormat.BYTE_BGRA_PRE,
Texture.Usage.DEFAULT,
Texture.WrapMode.CLAMP_NOT_NEEDED,
newTexW, newTexH);
}
PaintUtil.fillImageWithGradient(paintPixels, grad, xform,
0, 0, paintW, paintH,
bx, by, bw, bh);
byte[] bytePixels = paintBuffer.array();
if (maskData != null) {
byte[] maskPixels = maskData.getMaskBuffer().array();
int j = 0;
for (int i = 0; i < sizeInPixels; i++) {
int pixel = paintPixels[i];
int maskA = maskPixels[i] & 0xff;
bytePixels[j++] = (byte)((((pixel ) & 0xff) * maskA) / 255);
bytePixels[j++] = (byte)((((pixel >> 8) & 0xff) * maskA) / 255);
bytePixels[j++] = (byte)((((pixel >> 16) & 0xff) * maskA) / 255);
bytePixels[j++] = (byte)((((pixel >>> 24) ) * maskA) / 255);
}
} else {
int j = 0;
for (int i = 0; i < sizeInPixels; i++) {
int pixel = paintPixels[i];
bytePixels[j++] = (byte)((pixel ) & 0xff);
bytePixels[j++] = (byte)((pixel >> 8) & 0xff);
bytePixels[j++] = (byte)((pixel >> 16) & 0xff);
bytePixels[j++] = (byte)((pixel >>> 24) );
}
}
paintTex.update(paintBuffer, PixelFormat.BYTE_BGRA_PRE,
0, 0, 0, 0, paintW, paintH, paintW*4, false);
return paintTex;
}
}