Edit C:\Program Files\Java\jdk1.8.0_121\com\sun\prism\impl\ps\PaintHelper.java
/* * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * */ package com.sun.prism.impl.ps; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.util.List; import com.sun.javafx.geom.PickRay; import com.sun.javafx.geom.Point2D; import com.sun.javafx.geom.Vec3d; import com.sun.javafx.geom.transform.Affine2D; import com.sun.javafx.geom.transform.Affine3D; import com.sun.javafx.geom.transform.AffineBase; import com.sun.javafx.geom.transform.BaseTransform; import com.sun.javafx.geom.transform.NoninvertibleTransformException; import com.sun.javafx.sg.prism.NGCamera; import com.sun.javafx.sg.prism.NGPerspectiveCamera; import com.sun.prism.Image; import com.sun.prism.PixelFormat; import com.sun.prism.ResourceFactory; import com.sun.prism.Texture; import com.sun.prism.Texture.Usage; import com.sun.prism.Texture.WrapMode; import com.sun.prism.impl.BufferUtil; import com.sun.prism.paint.Color; import com.sun.prism.paint.Gradient; import com.sun.prism.paint.ImagePattern; import com.sun.prism.paint.LinearGradient; import com.sun.prism.paint.RadialGradient; import com.sun.prism.paint.Stop; import com.sun.prism.ps.Shader; import com.sun.prism.ps.ShaderGraphics; class PaintHelper { /****************** Shared MultipleGradientPaint support ********************/ /** * The maximum number of gradient "stops" supported by our native * fragment shader implementations. * * This value has been empirically determined and capped to allow * our native shaders to run on all shader-level graphics hardware, * even on the older, more limited GPUs. Even the oldest Nvidia * hardware could handle 16, or even 32 fractions without any problem. * But the first-generation boards from ATI would fall back into * software mode (which is unusably slow) for values larger than 12; * it appears that those boards do not have enough native registers * to support the number of array accesses required by our gradient * shaders. So for now we will cap this value at 12, but we can * re-evaluate this in the future as hardware becomes more capable. */ static final int MULTI_MAX_FRACTIONS = 12; /** * Make the texture width a power of two value larger * than MULTI_MAX_FRACTIONS. */ private static final int MULTI_TEXTURE_SIZE = 16; private static final int MULTI_CACHE_SIZE = 256; private static final int GTEX_CLR_TABLE_SIZE = 101; // for every % from 0% to 100% private static final int GTEX_CLR_TABLE_MIRRORED_SIZE = GTEX_CLR_TABLE_SIZE * 2 - 1; private static final float FULL_TEXEL_Y = 1.0f / MULTI_CACHE_SIZE; private static final float HALF_TEXEL_Y = FULL_TEXEL_Y / 2.0f; private static final FloatBuffer stopVals = BufferUtil.newFloatBuffer(MULTI_MAX_FRACTIONS * 4); private static final ByteBuffer bgraColors = BufferUtil.newByteBuffer(MULTI_TEXTURE_SIZE*4); private static final Image colorsImg = Image.fromByteBgraPreData(bgraColors, MULTI_TEXTURE_SIZE, 1); private static final int[] previousColors = new int[MULTI_TEXTURE_SIZE]; private static final byte gtexColors[] = new byte[GTEX_CLR_TABLE_MIRRORED_SIZE * 4]; private static final Image gtexImg = Image.fromByteBgraPreData(ByteBuffer.wrap(gtexColors), GTEX_CLR_TABLE_MIRRORED_SIZE, 1); private static long cacheOffset = -1; private static Texture gradientCacheTexture = null; private static Texture gtexCacheTexture = null; private static final Affine2D scratchXform2D = new Affine2D(); private static final Affine3D scratchXform3D = new Affine3D(); private static float len(float dx, float dy) { return ((dx == 0f) ? Math.abs(dy) : ((dy == 0f) ? Math.abs(dx) : (float)Math.sqrt(dx * dx + dy * dy))); } static void initGradientTextures(ShaderGraphics g) { gradientCacheTexture = g.getResourceFactory().createTexture( PixelFormat.BYTE_BGRA_PRE, Usage.DEFAULT, WrapMode.CLAMP_TO_EDGE, MULTI_TEXTURE_SIZE, MULTI_CACHE_SIZE); gradientCacheTexture.setLinearFiltering(true); // gradientCacheTexture remains permanently locked, useful, and permanent // an additional lock is added when a caller calls getGreientTeture for // them to unlock gradientCacheTexture.contentsUseful(); gradientCacheTexture.makePermanent(); gtexCacheTexture = g.getResourceFactory().createTexture( PixelFormat.BYTE_BGRA_PRE, Usage.DEFAULT, WrapMode.CLAMP_NOT_NEEDED, GTEX_CLR_TABLE_MIRRORED_SIZE, MULTI_CACHE_SIZE); gtexCacheTexture.setLinearFiltering(true); // gtexCacheTexture remains permanently locked, useful, and permanent // an additional lock is added when a caller calls getWrapGreientTeture for // them to unlock gtexCacheTexture.contentsUseful(); gtexCacheTexture.makePermanent(); } static Texture getGradientTexture(ShaderGraphics g, Gradient paint) { if (gradientCacheTexture == null) { initGradientTextures(g); } // gradientCacheTexture is left permanent and locked so it never // goes away or needs to be checked for isSurfaceLost(), but we // add a lock here so that the caller can unlock without knowing // our inner implementation details gradientCacheTexture.lock(); return gradientCacheTexture; } static Texture getWrapGradientTexture(ShaderGraphics g) { if (gtexCacheTexture == null) { initGradientTextures(g); } // gtexCacheTexture is left permanent and locked so it never // goes away or needs to be checked for isSurfaceLost(), but we // add a lock here so that the caller can unlock without knowing // our inner implementation details gtexCacheTexture.lock(); return gtexCacheTexture; } private static void stopsToImage(List<Stop> stops, int numStops) { if (numStops > MULTI_MAX_FRACTIONS) { throw new RuntimeException( "Maximum number of gradient stops exceeded " + "(paint uses " + numStops + " stops, but max is " + MULTI_MAX_FRACTIONS + ")"); } bgraColors.clear(); Color lastColor = null; for (int i = 0; i < MULTI_TEXTURE_SIZE; i++) { Color c; if (i < numStops) { c = stops.get(i).getColor(); lastColor = c; } else { // repeat the last color for the remaining slots so that // we can simply reference the last pixel of the texture when // dealing with edge conditions c = lastColor; } c.putBgraPreBytes(bgraColors); // optimization: keep track of colors used each time so that // we can skip updating the texture if the colors are same as // the last time int argb = c.getIntArgbPre(); if (argb != previousColors[i]) { previousColors[i] = argb; } } bgraColors.rewind(); } private static void insertInterpColor(byte colors[], int index, Color c0, Color c1, float t) { t *= 255.0f; float u = 255.0f - t; index *= 4; colors[index + 0] = (byte) (c0.getBluePremult() * u + c1.getBluePremult() * t + 0.5f); colors[index + 1] = (byte) (c0.getGreenPremult() * u + c1.getGreenPremult() * t + 0.5f); colors[index + 2] = (byte) (c0.getRedPremult() * u + c1.getRedPremult() * t + 0.5f); colors[index + 3] = (byte) (c0.getAlpha() * u + c1.getAlpha() * t + 0.5f); } private static Color PINK = new Color(1.0f, 0.078431375f, 0.5764706f, 1.0f); private static void stopsToGtexImage(List<Stop> stops, int numStops) { Color lastColor = stops.get(0).getColor(); float offset = stops.get(0).getOffset(); int lastIndex = (int) (offset * (GTEX_CLR_TABLE_SIZE - 1) + 0.5f); insertInterpColor(gtexColors, 0, lastColor, lastColor, 0.0f); for (int i = 1; i < numStops; i++) { Color color = stops.get(i).getColor(); offset = stops.get(i).getOffset(); int index = (int) (offset * (GTEX_CLR_TABLE_SIZE - 1) + 0.5f); if (index == lastIndex) { insertInterpColor(gtexColors, index, lastColor, color, 0.5f); } else { for (int j = lastIndex+1; j <= index; j++) { float t = j - lastIndex; t /= (index - lastIndex); insertInterpColor(gtexColors, j, lastColor, color, t); } } lastIndex = index; lastColor = color; } // assert (lastIndex = GTEX_CLR_TABLE_SIZE); // now mirror the list for fast REFLECT calculations // mirroring is around index = (GTEX_CLR_TABLE_SIZE-1) which is // where the last color for fract=1.0 should have been stored for (int i = 1; i < GTEX_CLR_TABLE_SIZE; i++) { int j = (GTEX_CLR_TABLE_SIZE - 1 + i) * 4; int k = (GTEX_CLR_TABLE_SIZE - 1 - i) * 4; gtexColors[j + 0] = gtexColors[k + 0]; gtexColors[j + 1] = gtexColors[k + 1]; gtexColors[j + 2] = gtexColors[k + 2]; gtexColors[j + 3] = gtexColors[k + 3]; } } // Uses a least recently allocated algorithm for caching Gradient colors. // This could be optimized so that we never use the same color twice. // We always increment the cacheOffset (long) and keep the gradients stored // the cache in the range [cacheOffset - cacheSize + 1, cacheOffset].. public static int initGradient(Gradient paint) { long offset = paint.getGradientOffset(); if (offset >= 0 && (offset > cacheOffset - MULTI_CACHE_SIZE)) { return (int) (offset % MULTI_CACHE_SIZE); } else { List<Stop> stops = paint.getStops(); int numStops = paint.getNumStops(); stopsToImage(stops,numStops); stopsToGtexImage(stops, numStops); long nextOffset = ++cacheOffset; paint.setGradientOffset(nextOffset); int cacheIdx = (int)(nextOffset % MULTI_CACHE_SIZE); // both gradientCacheTexture and gtexCacheTexture should be // left permanent and locked so we can always call update on // either or both of them here. gradientCacheTexture.update(colorsImg, 0, cacheIdx); gtexCacheTexture.update(gtexImg, 0, cacheIdx); return cacheIdx; } } private static void setMultiGradient(Shader shader, Gradient paint) { List<Stop> stops = paint.getStops(); int numStops = paint.getNumStops(); stopVals.clear(); for (int i = 0; i < MULTI_MAX_FRACTIONS; i++) { // TODO: optimize this... (RT-27377) stopVals.put((i < numStops) ? stops.get(i).getOffset() : 0f); stopVals.put((i < numStops-1) ? 1f / (stops.get(i+1).getOffset() - stops.get(i).getOffset()) : 0f); stopVals.put(0f); // unused stopVals.put(0f); // unused } stopVals.rewind(); shader.setConstants("fractions", stopVals, 0, MULTI_MAX_FRACTIONS); float index_y = initGradient(paint); shader.setConstant("offset", index_y / (float)MULTI_CACHE_SIZE + HALF_TEXEL_Y); // Note that the colors image/texture has already been updated // in BaseShaderContext.validatePaintOp()... } private static void setTextureGradient(Shader shader, Gradient paint) { float cy = initGradient(paint) + 0.5f; float cx = 0.5f; float fractmul = 0.0f, clampmul = 0.0f; switch (paint.getSpreadMethod()) { case Gradient.PAD: // distance from 0.5 texels to TABLE_SIZE - 0.5 texels clampmul = GTEX_CLR_TABLE_SIZE - 1.0f; break; case Gradient.REPEAT: // distance from 0.5 texels to TABLE_SIZE - 0.5 texels fractmul = GTEX_CLR_TABLE_SIZE - 1.0f; break; case Gradient.REFLECT: // distance from 0.5 texels to MIRROR_TABLE_SIZE - 0.5 texels fractmul = GTEX_CLR_TABLE_MIRRORED_SIZE - 1.0f; break; } float xscale = 1.0f / gtexCacheTexture.getPhysicalWidth(); float yscale = 1.0f / gtexCacheTexture.getPhysicalHeight(); cx *= xscale; cy *= yscale; fractmul *= xscale; clampmul *= xscale; shader.setConstant("content", cx, cy, fractmul, clampmul); // Note that the colors image/texture has already been updated // in BaseShaderContext.validatePaintOp()... } /********************** LinearGradientPaint support *************************/ /** * This method uses techniques that are nearly identical to those * employed in setGradientPaint() above. The primary difference * is that at the native level we use a fragment shader to manually * apply the plane equation constants to the current fragment position * to calculate the gradient position in the range [0,1] (the native * code for GradientPaint does the same, except that it uses OpenGL's * automatic texture coordinate generation facilities). We Also, project * in the 3D case to create a perspective vector which is used in the * fragment shader. * * One other minor difference worth mentioning is that * setGradientPaint() calculates the plane equation constants * such that the gradient end points are positioned at 0.25 and 0.75 * (for reasons discussed in the comments for that method). In * contrast, for LinearGradientPaint we setup the equation constants * such that the gradient end points fall at 0.0 and 1.0. The * reason for this difference is that in the fragment shader we * have more control over how the gradient values are interpreted * (depending on the paint's CycleMethod). */ static void setLinearGradient(ShaderGraphics g, Shader shader, LinearGradient paint, float rx, float ry, float rw, float rh) { BaseTransform paintXform = paint.getGradientTransformNoClone(); Affine3D at = scratchXform3D; g.getPaintShaderTransform(at); if (paintXform != null) { at.concatenate(paintXform); } float x1 = rx + (paint.getX1() * rw); float y1 = ry + (paint.getY1() * rh); float x2 = rx + (paint.getX2() * rw); float y2 = ry + (paint.getY2() * rh); // calculate plane equation constants float x = x1; float y = y1; at.translate(x, y); // now gradient point 1 is at the origin x = x2 - x; y = y2 - y; double len = len(x, y); at.rotate(Math.atan2(y, x)); // now gradient point 2 is on the positive x-axis at.scale(len, 1); // now gradient point 1 is at (0.0, 0), point 2 is at (1.0, 0) double p0, p1, p2; if (!at.is2D()) { BaseTransform inv; try { inv = at.createInverse(); } catch (NoninvertibleTransformException e) { at.setToScale(0, 0, 0); inv = at; } NGCamera cam = g.getCameraNoClone(); Vec3d tmpVec = new Vec3d(); PickRay tmpvec = new PickRay(); PickRay ray00 = project(0,0,cam,inv,tmpvec,tmpVec,null); PickRay ray10 = project(1,0,cam,inv,tmpvec,tmpVec,null); PickRay ray01 = project(0,1,cam,inv,tmpvec,tmpVec,null); p0 = ray10.getDirectionNoClone().x - ray00.getDirectionNoClone().x; p1 = ray01.getDirectionNoClone().x - ray00.getDirectionNoClone().x; p2 = ray00.getDirectionNoClone().x; p0 *= -ray00.getOriginNoClone().z; p1 *= -ray00.getOriginNoClone().z; p2 *= -ray00.getOriginNoClone().z; double wv0 = ray10.getDirectionNoClone().z - ray00.getDirectionNoClone().z; double wv1 = ray01.getDirectionNoClone().z - ray00.getDirectionNoClone().z; double wv2 = ray00.getDirectionNoClone().z; shader.setConstant("gradParams", (float)p0, (float)p1, (float)p2, (float)ray00.getOriginNoClone().x); shader.setConstant("perspVec", (float)wv0, (float)wv1, (float)wv2); } else { try { at.invert(); } catch (NoninvertibleTransformException ex) { at.setToScale(0, 0, 0); } p0 = (float)at.getMxx(); p1 = (float)at.getMxy(); p2 = (float)at.getMxt(); shader.setConstant("gradParams", (float)p0, (float)p1, (float)p2, 0.0f); shader.setConstant("perspVec", 0.0f, 0.0f, 1.0f); } setMultiGradient(shader, paint); } static AffineBase getLinearGradientTx(LinearGradient paint, Shader shader, BaseTransform renderTx, float rx, float ry, float rw, float rh) { AffineBase ret; float x1 = paint.getX1(); float y1 = paint.getY1(); float x2 = paint.getX2(); float y2 = paint.getY2(); if (paint.isProportional()) { x1 = rx + x1 * rw; y1 = ry + y1 * rh; x2 = rx + x2 * rw; y2 = ry + y2 * rh; } float dx = x2 - x1; float dy = y2 - y1; float len = len(dx, dy); if (paint.getSpreadMethod() == Gradient.REFLECT) { len *= 2.0f; } BaseTransform paintXform = paint.getGradientTransformNoClone(); if (paintXform.isIdentity() && renderTx.isIdentity()) { Affine2D at = scratchXform2D; // calculate plane equation constants at.setToTranslation(x1, y1); // now gradient point 1 is at the origin at.rotate(dx, dy); // now gradient point 2 is on the positive x-axis at.scale(len, 1); // now 0,0 maps to gradient point 1 and 1,0 maps to point 2 ret = at; } else { Affine3D at = scratchXform3D; at.setTransform(renderTx); at.concatenate(paintXform); // calculate plane equation constants at.translate(x1, y1); // now gradient point 1 is at the origin at.rotate(Math.atan2(dy, dx)); // now gradient point 2 is on the positive x-axis at.scale(len, 1); // now 0,0 maps to gradient point 1 and 1,0 maps to point 2 ret = at; } try { ret.invert(); } catch (NoninvertibleTransformException e) { scratchXform2D.setToScale(0, 0); ret = scratchXform2D; } setTextureGradient(shader, paint); return ret; } /********************** RadialGradientPaint support *************************/ /** * This method calculates six m** values and a focus adjustment value that * are used by the native fragment shader. (See LinearGradient Comment for * the 3D case.) These techniques are based on a whitepaper by Daniel Rice * on radial gradient performance (attached to the bug report for 6521533). * One can refer to that document for the complete set of formulas and * calculations, but the basic goal is to compose a transform that will * convert an (x,y) position in device space into a "u" value that represents * the relative distance to the gradient focus point. The resulting * value can be used to look up the appropriate color by linearly * interpolating between the two nearest colors in the gradient. */ static void setRadialGradient(ShaderGraphics g, Shader shader, RadialGradient paint, float rx, float ry, float rw, float rh) { Affine3D at = scratchXform3D; g.getPaintShaderTransform(at); // save original (untransformed) center and focus points and // adjust to account for proportional attribute if necessary float radius = paint.getRadius(); float cx = paint.getCenterX(); float cy = paint.getCenterY(); float fa = paint.getFocusAngle(); float fd = paint.getFocusDistance(); if (fd < 0) { fd = -fd; fa = fa+180; } fa = (float) Math.toRadians(fa); if (paint.isProportional()) { float bcx = rx + (rw / 2f); float bcy = ry + (rh / 2f); float scale = Math.min(rw, rh); cx = (cx - 0.5f) * scale + bcx; cy = (cy - 0.5f) * scale + bcy; if (rw != rh && rw != 0f && rh != 0f) { at.translate(bcx, bcy); at.scale(rw / scale, rh / scale); at.translate(-bcx, -bcy); } radius = radius * scale; } // transform from gradient coords to device coords BaseTransform paintXform = paint.getGradientTransformNoClone(); if (paintXform != null) { at.concatenate(paintXform); } // transform unit circle to gradient coords; we start with the // unit circle (center=(0,0), focus on positive x-axis, radius=1) // and then transform into gradient space at.translate(cx, cy); at.rotate(fa); at.scale(radius, radius); // invert to get mapping from device coords to unit circle try { at.invert(); } catch (Exception e) { at.setToScale(0.0, 0.0, 0.0); } if (!at.is2D()) { NGCamera cam = g.getCameraNoClone(); Vec3d tmpVec = new Vec3d(); PickRay tmpvec = new PickRay(); PickRay ray00 = project(0, 0, cam, at, tmpvec, tmpVec, null); PickRay ray10 = project(1, 0, cam, at, tmpvec, tmpVec, null); PickRay ray01 = project(0, 1, cam, at, tmpvec, tmpVec, null); double p0 = ray10.getDirectionNoClone().x - ray00.getDirectionNoClone().x; double p1 = ray01.getDirectionNoClone().x - ray00.getDirectionNoClone().x; double p2 = ray00.getDirectionNoClone().x; double py0 = ray10.getDirectionNoClone().y - ray00.getDirectionNoClone().y; double py1 = ray01.getDirectionNoClone().y - ray00.getDirectionNoClone().y; double py2 = ray00.getDirectionNoClone().y; p0 *= -ray00.getOriginNoClone().z; p1 *= -ray00.getOriginNoClone().z; p2 *= -ray00.getOriginNoClone().z; py0 *= -ray00.getOriginNoClone().z; py1 *= -ray00.getOriginNoClone().z; py2 *= -ray00.getOriginNoClone().z; double wv0 = ray10.getDirectionNoClone().z - ray00.getDirectionNoClone().z; double wv1 = ray01.getDirectionNoClone().z - ray00.getDirectionNoClone().z; double wv2 = ray00.getDirectionNoClone().z; shader.setConstant("perspVec", (float) wv0, (float) wv1, (float) wv2); shader.setConstant("m0", (float) p0, (float) p1, (float) p2, (float) ray00.getOriginNoClone().x); shader.setConstant("m1", (float) py0, (float) py1, (float) py2, (float) ray00.getOriginNoClone().y); } else { float m00 = (float) at.getMxx(); float m01 = (float) at.getMxy(); float m02 = (float) at.getMxt(); shader.setConstant("m0", m00, m01, m02, 0.0f); float m10 = (float) at.getMyx(); float m11 = (float) at.getMyy(); float m12 = (float) at.getMyt(); shader.setConstant("m1", m10, m11, m12, 0.0f); shader.setConstant("perspVec", 0.0f, 0.0f, 1.0f); } // clamp the focus point so that it does not rest on, or outside // of, the circumference of the gradient circle fd = (float) Math.min(fd, 0.99f); // pack a few unrelated, precalculated values into a single float4 float denom = 1.0f - (fd * fd); float inv_denom = 1.0f / denom; shader.setConstant("precalc", fd, denom, inv_denom); setMultiGradient(shader, paint); } static AffineBase getRadialGradientTx(RadialGradient paint, Shader shader, BaseTransform renderTx, float rx, float ry, float rw, float rh) { Affine3D at = scratchXform3D; at.setTransform(renderTx); // save original (untransformed) center and focus points and // adjust to account for proportional attribute if necessary float radius = paint.getRadius(); float cx = paint.getCenterX(); float cy = paint.getCenterY(); float fa = paint.getFocusAngle(); float fd = paint.getFocusDistance(); if (fd < 0) { fd = -fd; fa = fa+180; } fa = (float) Math.toRadians(fa); if (paint.isProportional()) { float bcx = rx + (rw / 2f); float bcy = ry + (rh / 2f); float scale = Math.min(rw, rh); cx = (cx - 0.5f) * scale + bcx; cy = (cy - 0.5f) * scale + bcy; if (rw != rh && rw != 0f && rh != 0f) { at.translate(bcx, bcy); at.scale(rw / scale, rh / scale); at.translate(-bcx, -bcy); } radius = radius * scale; } if (paint.getSpreadMethod() == Gradient.REFLECT) { radius *= 2.0f; } // transform from gradient coords to device coords BaseTransform paintXform = paint.getGradientTransformNoClone(); if (paintXform != null) { at.concatenate(paintXform); } // transform unit circle to gradient coords; we start with the // unit circle (center=(0,0), focus on positive x-axis, radius=1) // and then transform into gradient space at.translate(cx, cy); at.rotate(fa); at.scale(radius, radius); // invert to get mapping from device coords to unit circle try { at.invert(); } catch (Exception e) { at.setToScale(0.0, 0.0, 0.0); } // clamp the focus point so that it does not rest on, or outside // of, the circumference of the gradient circle fd = (float) Math.min(fd, 0.99f); // pack a few unrelated, precalculated values into a single float4 float denom = 1.0f - (fd * fd); float inv_denom = 1.0f / denom; shader.setConstant("precalc", fd, denom, inv_denom); setTextureGradient(shader, paint); return at; } /************************** ImagePattern support ****************************/ /** * We use the plane equation to automatically * map the ImagePattern image to the geometry being rendered. The * shader uses two separate plane equations that take the (x,y) * location (in device space) of the fragment being rendered to * calculate (u,v) texture coordinates for that fragment: * u = Ax + By + Cz + Dw * v = Ex + Fy + Gz + Hw * * For the 3D case we calculate a perspective vector by projecting rays * at various points, can finding the deltas. So we need to calculate appropriate * values for the plane equation constants (A,B,D) and (E,F,H) such * that {u,v}=0 for the top-left of the ImagePattern's anchor * rectangle and {u,v}=1 for the bottom-right of the anchor rectangle. * We can easily make the texture image repeat for {u,v} values * outside the range [0,1] by using the fract() instruction within * the shader. * * Calculating the plane equation constants is surprisingly simple. * We can think of it as an inverse matrix operation that takes * device space coordinates and transforms them into user space * coordinates that correspond to a location relative to the anchor * rectangle. First, we translate and scale the current user space * transform by applying the anchor rectangle bounds. We then take * the inverse of this affine transform. The rows of the resulting * inverse matrix correlate nicely to the plane equation constants * we were seeking. */ static void setImagePattern(ShaderGraphics g, Shader shader, ImagePattern paint, float rx, float ry, float rw, float rh) { float x1 = rx + (paint.getX() * rw); float y1 = ry + (paint.getY() * rh); float x2 = x1 + (paint.getWidth() * rw); float y2 = y1 + (paint.getHeight() * rh); ResourceFactory rf = g.getResourceFactory(); Image img = paint.getImage(); Texture paintTex = rf.getCachedTexture(img, Texture.WrapMode.REPEAT); float cx = paintTex.getContentX(); float cy = paintTex.getContentY(); float cw = paintTex.getContentWidth(); float ch = paintTex.getContentHeight(); float texw = paintTex.getPhysicalWidth(); float texh = paintTex.getPhysicalHeight(); paintTex.unlock(); // calculate plane equation constants Affine3D at = scratchXform3D; g.getPaintShaderTransform(at); at.translate(x1, y1); at.scale(x2 - x1, y2 - y1); // Adjustment for case when WrapMode.REPEAT is simulated if (cw < texw) { at.translate(0.5/cw, 0.0); cx += 0.5f; } if (ch < texh) { at.translate(0.0, 0.5/ch); cy += 0.5f; } try { at.invert(); } catch (Exception e) { at.setToScale(0.0, 0.0, 0.0); } if (!at.is2D()) { NGCamera cam = g.getCameraNoClone(); Vec3d tmpVec = new Vec3d(); PickRay tmpvec = new PickRay(); PickRay ray00 = project(0,0,cam,at,tmpvec,tmpVec,null); PickRay ray10 = project(1,0,cam,at,tmpvec,tmpVec,null); PickRay ray01 = project(0,1,cam,at,tmpvec,tmpVec,null); double p0 = ray10.getDirectionNoClone().x - ray00.getDirectionNoClone().x; double p1 = ray01.getDirectionNoClone().x - ray00.getDirectionNoClone().x; double p2 = ray00.getDirectionNoClone().x; double py0 = ray10.getDirectionNoClone().y - ray00.getDirectionNoClone().y; double py1 = ray01.getDirectionNoClone().y - ray00.getDirectionNoClone().y; double py2 = ray00.getDirectionNoClone().y; p0 *= -ray00.getOriginNoClone().z; p1 *= -ray00.getOriginNoClone().z; p2 *= -ray00.getOriginNoClone().z; py0 *= -ray00.getOriginNoClone().z; py1 *= -ray00.getOriginNoClone().z; py2 *= -ray00.getOriginNoClone().z; double wv0 = ray10.getDirectionNoClone().z - ray00.getDirectionNoClone().z; double wv1 = ray01.getDirectionNoClone().z - ray00.getDirectionNoClone().z; double wv2 = ray00.getDirectionNoClone().z; shader.setConstant("perspVec", (float)wv0, (float)wv1, (float)wv2); shader.setConstant("xParams", (float)p0, (float)p1, (float)p2, (float)ray00.getOriginNoClone().x); shader.setConstant("yParams", (float)py0, (float)py1, (float)py2, (float)ray00.getOriginNoClone().y); } else { float m00 = (float)at.getMxx(); float m01 = (float)at.getMxy(); float m02 = (float)at.getMxt(); shader.setConstant("xParams", m00, m01, m02, 0.0f); float m10 = (float)at.getMyx(); float m11 = (float)at.getMyy(); float m12 = (float)at.getMyt(); shader.setConstant("yParams", m10, m11, m12, 0.0f); shader.setConstant("perspVec", 0.0f, 0.0f, 1.0f); } cx /= texw; cy /= texh; cw /= texw; ch /= texh; shader.setConstant("content", cx, cy, cw, ch); } static AffineBase getImagePatternTx(ShaderGraphics g, ImagePattern paint, Shader shader, BaseTransform renderTx, float rx, float ry, float rw, float rh) { float px = paint.getX(); float py = paint.getY(); float pw = paint.getWidth(); float ph = paint.getHeight(); if (paint.isProportional()) { px = rx + px * rw; py = ry + py * rh; pw = pw * rw; ph = ph * rh; } ResourceFactory rf = g.getResourceFactory(); Image img = paint.getImage(); Texture paintTex = rf.getCachedTexture(img, Texture.WrapMode.REPEAT); float cx = paintTex.getContentX(); float cy = paintTex.getContentY(); float cw = paintTex.getContentWidth(); float ch = paintTex.getContentHeight(); float texw = paintTex.getPhysicalWidth(); float texh = paintTex.getPhysicalHeight(); paintTex.unlock(); // calculate plane equation constants AffineBase ret; if (renderTx.isIdentity()) { Affine2D at = scratchXform2D; at.setToTranslation(px, py); at.scale(pw, ph); ret = at; } else { Affine3D at = scratchXform3D; at.setTransform(renderTx); at.translate(px, py); at.scale(pw, ph); ret = at; } // Adjustment for case when WrapMode.REPEAT is simulated if (cw < texw) { ret.translate(0.5/cw, 0.0); cx += 0.5f; } if (ch < texh) { ret.translate(0.0, 0.5/ch); cy += 0.5f; } try { ret.invert(); } catch (Exception e) { ret = scratchXform2D; scratchXform2D.setToScale(0.0, 0.0); } cx /= texw; cy /= texh; cw /= texw; ch /= texh; shader.setConstant("content", cx, cy, cw, ch); return ret; } static PickRay project(float x, float y, NGCamera cam, BaseTransform inv, PickRay tmpray, Vec3d tmpvec, Point2D ret) { tmpray = cam.computePickRay(x, y, tmpray); return tmpray.project(inv, cam instanceof NGPerspectiveCamera, tmpvec, ret); } }
Ms-Dos/Windows
Unix
Write backup
jsp File Browser version 1.2 by
www.vonloesch.de