You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pdfbox.apache.org by le...@apache.org on 2014/02/02 16:20:53 UTC

svn commit: r1563633 - in /pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics: pattern/PDShadingPatternResources.java shading/AxialShadingContext.java shading/PDShadingType2.java shading/RadialShadingContext.java

Author: lehmi
Date: Sun Feb  2 15:20:53 2014
New Revision: 1563633

URL: http://svn.apache.org/r1563633
Log:
PDFBOX-615: take the shading matrix into account based on a proposal by Luis Bernado, add background support

Modified:
    pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/pattern/PDShadingPatternResources.java
    pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/AxialShadingContext.java
    pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShadingType2.java
    pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/RadialShadingContext.java

Modified: pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/pattern/PDShadingPatternResources.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/pattern/PDShadingPatternResources.java?rev=1563633&r1=1563632&r2=1563633&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/pattern/PDShadingPatternResources.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/pattern/PDShadingPatternResources.java Sun Feb  2 15:20:53 2014
@@ -211,10 +211,10 @@ public class PDShadingPatternResources e
                 paint = new Type1ShadingPaint((PDShadingType1)getShading(), getMatrix(), pageHeight);
                 break;
             case PDShadingResources.SHADING_TYPE2:
-                paint = new AxialShadingPaint((PDShadingType2)getShading(), null, pageHeight);
+                paint = new AxialShadingPaint((PDShadingType2)getShading(), getMatrix(), pageHeight);
                 break;
             case PDShadingResources.SHADING_TYPE3:
-                paint = new RadialShadingPaint((PDShadingType3)getShading(), null, pageHeight);
+                paint = new RadialShadingPaint((PDShadingType3)getShading(), getMatrix(), pageHeight);
                 break;
             case PDShadingResources.SHADING_TYPE4:
             case PDShadingResources.SHADING_TYPE5:

Modified: pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/AxialShadingContext.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/AxialShadingContext.java?rev=1563633&r1=1563632&r2=1563633&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/AxialShadingContext.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/AxialShadingContext.java Sun Feb  2 15:20:53 2014
@@ -17,9 +17,12 @@
 package org.apache.pdfbox.pdmodel.graphics.shading;
 
 import java.awt.PaintContext;
+import java.awt.Transparency;
 import java.awt.color.ColorSpace;
 import java.awt.geom.AffineTransform;
 import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
 import java.awt.image.Raster;
 import java.awt.image.WritableRaster;
 import java.io.IOException;
@@ -44,15 +47,14 @@ import org.apache.pdfbox.util.Matrix;
 public class AxialShadingContext implements PaintContext 
 {
 
-    private ColorModel colorModel;
-    private PDFunction function;
+    private ColorModel outputColorModel;
     private ColorSpace shadingColorSpace;
     private PDFunction shadingTinttransform;
+    private PDShadingType2 shadingType;
 
     private float[] coords;
     private float[] domain;
-    private int[] extend0Values;
-    private int[] extend1Values;
+    private float[] background;
     private boolean[] extend;
     private double x1x0; 
     private double y1y0;
@@ -77,6 +79,7 @@ public class AxialShadingContext impleme
     public AxialShadingContext(PDShadingType2 shadingType2, ColorModel colorModelValue, 
             AffineTransform xform, Matrix ctm, int pageHeight) 
     {
+        shadingType = shadingType2;
         coords = shadingType2.getCoords().toFloatArray();
         if (ctm != null)
         {
@@ -99,94 +102,78 @@ public class AxialShadingContext impleme
             coords[1] = pageHeight + translateY - coords[1];
             coords[3] = pageHeight + translateY - coords[3];
         }
-        // colorSpace 
-        try 
+        // get the shading colorSpace
+        try
         {
-            PDColorSpace cs = shadingType2.getColorSpace();
+            PDColorSpace cs = shadingType.getColorSpace();
             if (!(cs instanceof PDDeviceRGB))
             {
                 // we have to create an instance of the shading colorspace if it isn't RGB
                 shadingColorSpace = cs.getJavaColorSpace();
+                // get the tint transformation function if the colorspace has one
                 if (cs instanceof PDDeviceN)
                 {
-                    shadingTinttransform = ((PDDeviceN)cs).getTintTransform();
+                    shadingTinttransform = ((PDDeviceN) cs).getTintTransform();
                 }
                 else if (cs instanceof PDSeparation)
                 {
-                    shadingTinttransform = ((PDSeparation)cs).getTintTransform();
+                    shadingTinttransform = ((PDSeparation) cs).getTintTransform();
                 }
             }
-        } 
-        catch (IOException exception) 
-        {
-            LOG.error("error while creating colorSpace", exception);
-        }
-        // colorModel
-        if (colorModelValue != null)
-        {
-            colorModel = colorModelValue;
-        }
-        else
-        {
-            try
-            {
-                // TODO bpc != 8 ??  
-                colorModel = shadingType2.getColorSpace().createColorModel(8);
-            }
-            catch(IOException exception)
-            {
-                LOG.error("error while creating colorModel", exception);
-            }
-        }
-        // shading function
-        try
-        {
-            function = shadingType2.getFunction();
         }
-        catch(IOException exception)
+        catch (IOException exception)
         {
-            LOG.error("error while creating a function", exception);
+            LOG.error("error while creating colorSpace", exception);
         }
+        // create the output colormodel using RGB+alpha as colorspace
+        ColorSpace outputCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+        outputColorModel = new ComponentColorModel(outputCS, true, false, Transparency.TRANSLUCENT,
+                DataBuffer.TYPE_BYTE);
         // domain values
-        if (shadingType2.getDomain() != null)
+        if (shadingType.getDomain() != null)
         {
-            domain = shadingType2.getDomain().toFloatArray();
+            domain = shadingType.getDomain().toFloatArray();
         }
-        else 
+        else
         {
             // set default values
-            domain = new float[]{0,1};
+            domain = new float[] { 0, 1 };
         }
         // extend values
-        COSArray extendValues = shadingType2.getExtend();
-        if (shadingType2.getExtend() != null)
+        COSArray extendValues = shadingType.getExtend();
+        if (shadingType.getExtend() != null)
         {
             extend = new boolean[2];
-            extend[0] = ((COSBoolean)extendValues.get(0)).getValue();
-            extend[1] = ((COSBoolean)extendValues.get(1)).getValue();
+            extend[0] = ((COSBoolean) extendValues.get(0)).getValue();
+            extend[1] = ((COSBoolean) extendValues.get(1)).getValue();
         }
         else
         {
             // set default values
-            extend = new boolean[]{false,false};
+            extend = new boolean[] { false, false };
         }
         // calculate some constants to be used in getRaster
-        x1x0 = coords[2] - coords[0]; 
+        x1x0 = coords[2] - coords[0];
         y1y0 = coords[3] - coords[1];
-        d1d0 = domain[1]-domain[0];
-        denom = Math.pow(x1x0,2) + Math.pow(y1y0, 2);
-        // TODO take a possible Background value into account
-        
+        d1d0 = domain[1] - domain[0];
+        denom = Math.pow(x1x0, 2) + Math.pow(y1y0, 2);
+
+        // get background values if available
+        COSArray bg = shadingType2.getBackground();
+        if (bg != null)
+        {
+            background = bg.toFloatArray();
+        }
     }
     /**
      * {@inheritDoc}
      */
     public void dispose() 
     {
-        colorModel = null;
-        function = null;
+    	outputColorModel = null;
         shadingColorSpace = null;
         shadingTinttransform = null;
+        shadingType = null;
     }
 
     /**
@@ -194,7 +181,7 @@ public class AxialShadingContext impleme
      */
     public ColorModel getColorModel() 
     {
-        return colorModel;
+        return outputColorModel;
     }
 
     /**
@@ -204,40 +191,50 @@ public class AxialShadingContext impleme
     {
         // create writable raster
         WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h);
-        float[] input = new float[1];
-        int[] data = new int[w * h * 3];
-        boolean saveExtend0 = false;
-        boolean saveExtend1 = false;
-        for (int j = 0; j < h; j++) 
+        boolean useBackground = false;
+        int[] data = new int[w * h * 4];
+        for (int j = 0; j < h; j++)
         {
-            for (int i = 0; i < w; i++) 
+            for (int i = 0; i < w; i++)
             {
-                int index = (j * w + i) * 3;
-                double inputValue = x1x0 * (x + i - coords[0]); 
+                useBackground = false;
+                double inputValue = x1x0 * (x + i - coords[0]);
                 inputValue += y1y0 * (y + j - coords[1]);
-                inputValue /= denom;
+                // TODO this happens if start == end, see PDFBOX-1442
+                if (denom == 0)
+                {
+                    if (background != null)
+                    {
+                        useBackground = true;
+                    }
+                    else
+                    {
+                        continue;
+                    }
+                }
+                else
+                {
+                    inputValue /= denom;
+                }
                 // input value is out of range
                 if (inputValue < domain[0])
                 {
                     // the shading has to be extended if extend[0] == true
                     if (extend[0])
                     {
-                        if (extend0Values == null)
+                        inputValue = domain[0];
+                    }
+                    else
+                    {
+                        if (background != null)
                         {
-                            inputValue = domain[0];
-                            saveExtend0 = true;
+                            useBackground = true;
                         }
                         else
                         {
-                            // use the chached values
-                            System.arraycopy(extend0Values, 0, data, index, 3);
                             continue;
                         }
                     }
-                    else 
-                    {
-                        continue;
-                    }
                 }
                 // input value is out of range
                 else if (inputValue > domain[1])
@@ -245,61 +242,103 @@ public class AxialShadingContext impleme
                     // the shading has to be extended if extend[1] == true
                     if (extend[1])
                     {
-                        if (extend1Values == null)
+                        inputValue = domain[1];
+                    }
+                    else
+                    {
+                        if (background != null)
                         {
-                            inputValue = domain[1];
-                            saveExtend1 = true;
+                            useBackground = true;
                         }
                         else
                         {
-                            // use the chached values
-                            System.arraycopy(extend1Values, 0, data, index, 3);
                             continue;
                         }
                     }
-                    else 
+                }
+                float[] values = null;
+                int index = (j * w + i) * 4;
+                if (useBackground)
+                {
+                    // use the given backgound color values
+                    values = background;
+                }
+                else
+                {
+                    try
                     {
-                        continue;
+                        float input = (float) (domain[0] + (d1d0 * inputValue));
+                        values = shadingType.evalFunction(input);
+                    }
+                    catch (IOException exception)
+                    {
+                        LOG.error("error while processing a function", exception);
                     }
                 }
-                input[0] = (float)(domain[0] + (d1d0*inputValue));
-                float[] values = null;
-                try 
+                // convert color values from shading colorspace to RGB if necessary
+                if (shadingColorSpace != null)
                 {
-                    values = function.eval(input);
-                    // convert color values from shading colorspace to RGB 
-                    if (shadingColorSpace != null)
+                    if (shadingTinttransform != null)
                     {
-                        if (shadingTinttransform != null)
+                        try
                         {
                             values = shadingTinttransform.eval(values);
                         }
-                        values = shadingColorSpace.toRGB(values);
+                        catch (IOException exception)
+                        {
+                            LOG.error("error while processing a function", exception);
+                        }
                     }
-                } 
-                catch (IOException exception) 
-                {
-                    LOG.error("error while processing a function", exception);
-                }
-                data[index] = (int)(values[0]*255);
-                data[index+1] = (int)(values[1]*255);
-                data[index+2] = (int)(values[2]*255);
-                if (saveExtend0)
-                {
-                    // chache values
-                    extend0Values = new int[3];
-                    System.arraycopy(data, index, extend0Values, 0, 3);
-                }
-                if (saveExtend1)
-                {
-                    // chache values
-                    extend1Values = new int[3];
-                    System.arraycopy(data, index, extend1Values, 0, 3);
+                    values = shadingColorSpace.toRGB(values);
                 }
+                data[index] = (int) (values[0] * 255);
+                data[index + 1] = (int) (values[1] * 255);
+                data[index + 2] = (int) (values[2] * 255);
+                data[index + 3] = 255;
             }
         }
         raster.setPixels(0, 0, w, h, data);
         return raster;
     }
 
+    /**
+     * Returns the coords values.
+     * 
+     * @return the coords values as array
+     */
+    public float[] getCoords() 
+    {
+        return coords;
+    }
+        
+    /**
+     * Returns the domain values.
+     * 
+     * @return the domain values as array
+     */
+    public float[] getDomain() 
+    {
+        return domain;
+    }
+        
+    /**
+     * Returns the extend values.
+     * 
+     * @return the extend values as array
+     */
+    public boolean[] getExtend() 
+    {
+        return extend;
+    }
+    
+    /**
+     * Returns the function used for the shading tint transformation.
+     * 
+     * @return the shading tint transformation function
+     */
+    public PDFunction getShadingTintTransform() 
+    {
+        return shadingTinttransform;
+    }
+
 }

Modified: pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShadingType2.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShadingType2.java?rev=1563633&r1=1563632&r2=1563633&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShadingType2.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/PDShadingType2.java Sun Feb  2 15:20:53 2014
@@ -21,6 +21,7 @@ package org.apache.pdfbox.pdmodel.graphi
 import java.io.IOException;
 
 import org.apache.pdfbox.cos.COSArray;
+import org.apache.pdfbox.cos.COSBase;
 import org.apache.pdfbox.cos.COSDictionary;
 import org.apache.pdfbox.cos.COSName;
 import org.apache.pdfbox.pdmodel.common.function.PDFunction;
@@ -37,6 +38,8 @@ public class PDShadingType2 extends PDSh
     private COSArray domain = null;
     private COSArray extend = null;
     private PDFunction function = null;
+    private PDFunction[] functionArray = null;
+
     
     /**
      * Constructor using the given shading dictionary.
@@ -177,6 +180,8 @@ public class PDShadingType2 extends PDSh
      * 
      * @exception IOException If we are unable to create the PDFunction object. 
      * 
+     * @deprecated
+     * 
      */
     public PDFunction getFunction() throws IOException
     {
@@ -187,4 +192,63 @@ public class PDShadingType2 extends PDSh
         return function;
     }
 
+    /**
+     * Provide the function(s) of the shading dictionary as array.
+     * 
+     * @return an array containing the function(s) 
+     * @throws IOException throw if something went wrong
+     */
+    private PDFunction[] getFunctionsArray() throws IOException
+    {
+        if (functionArray == null)
+        {
+            COSBase functionObject = getCOSDictionary().getDictionaryObject(COSName.FUNCTION);
+            if (functionObject instanceof COSDictionary)
+            {
+                functionArray = new PDFunction[1];
+                functionArray[0] = PDFunction.create(functionObject);
+            }
+            else
+            {
+                COSArray functionCOSArray = (COSArray)functionObject;
+                int numberOfFunctions = functionCOSArray.size();
+                functionArray = new PDFunction[numberOfFunctions];
+                for (int i=0; i<numberOfFunctions; i++)
+                {
+                    functionArray[i] = PDFunction.create(functionCOSArray.get(i));
+                }
+            }
+        }
+        return functionArray;
+    }
+    
+    /**
+     * Convert the input value using the functions of the shading dictionary.
+     * 
+     * @param inputValue the input value
+     * @return the output values
+     * @throws IOException thrown if something went wrong
+     */
+    public float[] evalFunction(float inputValue) throws IOException
+    {
+        float[] input = new float[] {inputValue};
+        PDFunction[] functions = getFunctionsArray();
+        int numberOfFunctions = functions.length;
+        float[] returnValues = null;
+        if (numberOfFunctions == 1)
+        {
+            returnValues = functions[0].eval(input);
+        }
+        else
+        {
+            returnValues = new float[numberOfFunctions];
+            for (int i=0; i<numberOfFunctions;i++)
+            {
+                float[] newValue = functions[i].eval(input);
+                returnValues[i] = newValue[0];
+            }
+        }
+        return returnValues;
+    }
+
 }

Modified: pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/RadialShadingContext.java
URL: http://svn.apache.org/viewvc/pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/RadialShadingContext.java?rev=1563633&r1=1563632&r2=1563633&view=diff
==============================================================================
--- pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/RadialShadingContext.java (original)
+++ pdfbox/branches/1.8/pdfbox/src/main/java/org/apache/pdfbox/pdmodel/graphics/shading/RadialShadingContext.java Sun Feb  2 15:20:53 2014
@@ -17,9 +17,12 @@
 package org.apache.pdfbox.pdmodel.graphics.shading;
 
 import java.awt.PaintContext;
+import java.awt.Transparency;
 import java.awt.color.ColorSpace;
 import java.awt.geom.AffineTransform;
 import java.awt.image.ColorModel;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.DataBuffer;
 import java.awt.image.Raster;
 import java.awt.image.WritableRaster;
 import java.io.IOException;
@@ -44,16 +47,16 @@ import org.apache.pdfbox.util.Matrix;
 public class RadialShadingContext implements PaintContext 
 {
 
-    private ColorModel colorModel;
+    private ColorModel outputColorModel;
     private PDFunction function;
     private ColorSpace shadingColorSpace;
     private PDFunction shadingTinttransform;
+    private PDShadingType3 shadingType;
 
     private float[] coords;
     private float[] domain;
-    private int[] extend0Values;
-    private int[] extend1Values;
     private boolean[] extend;
+    private float[] background;
     private double x1x0; 
     private double y1y0;
     private double r1r0;
@@ -82,6 +85,7 @@ public class RadialShadingContext implem
     public RadialShadingContext(PDShadingType3 shadingType3, ColorModel colorModelValue, 
             AffineTransform xform, Matrix ctm, int pageHeight) 
     {
+        shadingType = shadingType3;
         coords = shadingType3.getCoords().toFloatArray();
         if (ctm != null)
         {
@@ -90,12 +94,18 @@ public class RadialShadingContext implem
             // transform the coords from shading to user space
             ctm.createAffineTransform().transform(coords, 0, coordsTemp, 0, 1);
             ctm.createAffineTransform().transform(coords, 3, coordsTemp, 3, 1);
+            // scale radius to user space
+            coords[2] *= ctm.getXScale();
+            coords[5] *= ctm.getXScale();
             // move the 0,0-reference
             coordsTemp[1] = pageHeight - coordsTemp[1];
             coordsTemp[4] = pageHeight - coordsTemp[4];
             // transform the coords from user to device space
             xform.transform(coordsTemp, 0, coords, 0, 1);
             xform.transform(coordsTemp, 3, coords, 3, 1);
+            // scale radius to device space
+            coords[2] *= xform.getScaleX();
+            coords[5] *= xform.getScaleX();
         }
         else
         {
@@ -106,98 +116,82 @@ public class RadialShadingContext implem
             coords[1] = pageHeight + translateY - coords[1];
             coords[4] = pageHeight + translateY - coords[4];
         }
-        // colorSpace 
-        try 
+        // get the shading colorSpace
+        try
         {
-            PDColorSpace cs = shadingType3.getColorSpace();
+            PDColorSpace cs = shadingType.getColorSpace();
             if (!(cs instanceof PDDeviceRGB))
             {
                 // we have to create an instance of the shading colorspace if it isn't RGB
                 shadingColorSpace = cs.getJavaColorSpace();
                 if (cs instanceof PDDeviceN)
                 {
-                    shadingTinttransform = ((PDDeviceN)cs).getTintTransform();
+                    shadingTinttransform = ((PDDeviceN) cs).getTintTransform();
                 }
                 else if (cs instanceof PDSeparation)
                 {
-                    shadingTinttransform = ((PDSeparation)cs).getTintTransform();
+                    shadingTinttransform = ((PDSeparation) cs).getTintTransform();
                 }
             }
-        } 
-        catch (IOException exception) 
-        {
-            LOG.error("error while creating colorSpace", exception);
         }
-        // colorModel
-        if (colorModelValue != null)
-        {
-            colorModel = colorModelValue;
-        }
-        else
+        catch (IOException exception)
         {
-            try
-            {
-                // TODO bpc != 8 ??  
-                colorModel = shadingType3.getColorSpace().createColorModel(8);
-            }
-            catch(IOException exception)
-            {
-                LOG.error("error while creating colorModel", exception);
-            }
-        }
-        // shading function
-        try
-        {
-            function = shadingType3.getFunction();
-        }
-        catch(IOException exception)
-        {
-            LOG.error("error while creating a function", exception);
+            LOG.error("error while creating colorSpace", exception);
         }
+        // create the output colormodel using RGB+alpha as colorspace
+        ColorSpace outputCS = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+        outputColorModel = new ComponentColorModel(outputCS, true, false, Transparency.TRANSLUCENT,
+                DataBuffer.TYPE_BYTE);
         // domain values
-        if (shadingType3.getDomain() != null)
+        if (shadingType.getDomain() != null)
         {
-            domain = shadingType3.getDomain().toFloatArray();
+            domain = shadingType.getDomain().toFloatArray();
         }
-        else 
+        else
         {
             // set default values
-            domain = new float[]{0,1};
+            domain = new float[] { 0, 1 };
         }
         // extend values
-        COSArray extendValues = shadingType3.getExtend();
-        if (shadingType3.getExtend() != null)
+        COSArray extendValues = shadingType.getExtend();
+        if (shadingType.getExtend() != null)
         {
             extend = new boolean[2];
-            extend[0] = ((COSBoolean)extendValues.get(0)).getValue();
-            extend[1] = ((COSBoolean)extendValues.get(1)).getValue();
+            extend[0] = ((COSBoolean) extendValues.get(0)).getValue();
+            extend[1] = ((COSBoolean) extendValues.get(1)).getValue();
         }
         else
         {
             // set default values
-            extend = new boolean[]{false,false};
+            extend = new boolean[] { false, false };
         }
         // calculate some constants to be used in getRaster
-        x1x0 = coords[3] - coords[0]; 
+        x1x0 = coords[3] - coords[0];
         y1y0 = coords[4] - coords[1];
         r1r0 = coords[5] - coords[2];
-        x1x0pow2 = Math.pow(x1x0,2);
-        y1y0pow2 = Math.pow(y1y0,2);
-        r0pow2 = Math.pow(coords[2],2);
+        x1x0pow2 = Math.pow(x1x0, 2);
+        y1y0pow2 = Math.pow(y1y0, 2);
+        r0pow2 = Math.pow(coords[2], 2);
         denom = x1x0pow2 + y1y0pow2 - Math.pow(r1r0, 2);
-        d1d0 = domain[1]-domain[0];
-        // TODO take a possible Background value into account
-        
+        d1d0 = domain[1] - domain[0];
+
+        // get background values if available
+        COSArray bg = shadingType3.getBackground();
+        if (bg != null)
+        {
+            background = bg.toFloatArray();
+        }
     }
     /**
      * {@inheritDoc}
      */
     public void dispose() 
     {
-        colorModel = null;
+    	outputColorModel = null;
         function = null;
         shadingColorSpace = null;
         shadingTinttransform = null;
+        shadingType = null;
     }
 
     /**
@@ -205,7 +199,7 @@ public class RadialShadingContext implem
      */
     public ColorModel getColorModel() 
     {
-        return colorModel;
+        return outputColorModel;
     }
 
     /**
@@ -215,125 +209,146 @@ public class RadialShadingContext implem
     {
         // create writable raster
         WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h);
-        float[] input = new float[1];
-        float inputValue;
-        boolean saveExtend0 = false;
-        boolean saveExtend1 = false;
-        int[] data = new int[w * h * 3];
-        for (int j = 0; j < h; j++) 
+        float inputValue = -1;
+        boolean useBackground;
+        int[] data = new int[w * h * 4];
+        for (int j = 0; j < h; j++)
         {
-            for (int i = 0; i < w; i++) 
+            for (int i = 0; i < w; i++)
             {
-                int index = (j * w + i) * 3;
+                useBackground = false;
                 float[] inputValues = calculateInputValues(x + i, y + j);
-                // choose 1 of the 2 values
-                if (inputValues[0] >= domain[0] && inputValues[0] <= domain[1])
+                if (Float.isNaN(inputValues[0]) && Float.isNaN(inputValues[1]))
                 {
-                    // both values are in the domain -> choose the larger one 
-                    if(inputValues[1] >= domain[0] && inputValues[1] <= domain[1])
+                    if (background != null)
                     {
-                        inputValue = Math.max(inputValues[0], inputValues[1]);
-                    } 
-                    // first value is in the domain, the second not -> choose first value
+                        useBackground = true;
+                    }
                     else
                     {
-                        inputValue = inputValues[0];
+                        continue;
                     }
                 }
                 else
                 {
-                    // first value is not in the domain, but the second -> choose second value
-                    if(inputValues[1] >= domain[0] && inputValues[1] <= domain[1])
+                    // choose 1 of the 2 values
+                    if (inputValues[0] >= domain[0] && inputValues[0] <= domain[1])
                     {
-                        inputValue = inputValues[1];
+                        // both values are in the domain -> choose the larger one
+                        if (inputValues[1] >= domain[0] && inputValues[1] <= domain[1])
+                        {
+                            inputValue = Math.max(inputValues[0], inputValues[1]);
+                        }
+                        // first value is in the domain, the second not -> choose first value
+                        else
+                        {
+                            inputValue = inputValues[0];
+                        }
                     }
-                    // TODO
-                    // both are not in the domain -> choose the first as I don't know it better
                     else
                     {
-                        inputValue = inputValues[0];
-                    }
-                }
-                // input value is out of range
-                if (inputValue < domain[0])
-                {
-                    // the shading has to be extended if extend[0] == true
-                    if (extend[0])
-                    {
-                        if (extend0Values == null)
+                        // first value is not in the domain, but the second -> choose second value
+                        if (inputValues[1] >= domain[0] && inputValues[1] <= domain[1])
                         {
-                            inputValue = domain[0];
-                            saveExtend0 = true;
+                            inputValue = inputValues[1];
                         }
+                        // both are not in the domain (is this correct, or is there some miscalculation??)
                         else
                         {
-                            // use the chached values
-                            System.arraycopy(extend0Values, 0, data, index, 3);
-                            continue;
+                            // TODO don't know what to do in such cases. The following works
+                            // with the given examples
+                            // choose the smaller if extend[0] is set to true
+                            if (extend[0])
+                            {
+                                inputValue = Math.min(inputValues[0], inputValues[1]);
+                            }
+                            // choose the bigger one in all other cases
+                            else
+                            {
+                                inputValue = Math.max(inputValues[0], inputValues[1]);
+                            }
                         }
                     }
-                    else 
+                    // input value is out of range
+                    if (inputValue > domain[1])
                     {
-                        continue;
-                    }
-                }
-                // input value is out of range
-                else if (inputValue > domain[1])
-                {
-                    // the shading has to be extended if extend[1] == true
-                    if (extend[1])
-                    {
-                        if (extend1Values == null)
+                        // the shading has to be extended if extend[1] == true
+                        if (extend[1])
                         {
                             inputValue = domain[1];
-                            saveExtend1 = true;
                         }
                         else
                         {
-                            // use the chached values 
-                            System.arraycopy(extend1Values, 0, data, index, 3);
-                            continue;
+                            if (background != null)
+                            {
+                                useBackground = true;
+                            }
+                            else
+                            {
+                                continue;
+                            }
                         }
                     }
-                    else 
+                    // input value is out of range
+                    else if (inputValue < domain[0])
                     {
-                        continue;
-                    }
-                }
-                input[0] = (float)(domain[0] + (d1d0*inputValue));
-                float[] values = null;
-                try 
-                {
-                    values = function.eval(input);
-                    // convert color values from shading colorspace to RGB 
-                    if (shadingColorSpace != null)
-                    {
-                        if (shadingTinttransform != null)
+                        // the shading has to be extended if extend[0] == true
+                        if (extend[0])
                         {
-                            values = shadingTinttransform.eval(values);
+                            inputValue = domain[0];
+                        }
+                        else
+                        {
+                            if (background != null)
+                            {
+                                useBackground = true;
+                            }
+                            else
+                            {
+                                continue;
+                            }
                         }
-                        values = shadingColorSpace.toRGB(values);
                     }
                 }
-                catch (IOException exception) 
+                float[] values = null;
+                int index = (j * w + i) * 4;
+                if (useBackground)
                 {
-                    LOG.error("error while processing a function", exception);
+                    // use the given backgound color values
+                    values = background;
                 }
-                data[index] = (int)(values[0]*255);
-                data[index+1] = (int)(values[1]*255);
-                data[index+2] = (int)(values[2]*255);
-                if (saveExtend0)
+                else
                 {
-                    // chache values
-                    extend0Values = new int[3];
-                    System.arraycopy(data, index, extend0Values, 0, 3);
+                    try
+                    {
+                        float input = (float) (domain[0] + (d1d0 * inputValue));
+                        values = shadingType.evalFunction(input);
+                    }
+                    catch (IOException exception)
+                    {
+                        LOG.error("error while processing a function", exception);
+                    }
                 }
-                if (saveExtend1)
+                // convert color values from shading colorspace to RGB
+                if (shadingColorSpace != null)
                 {
-                    // chache values
-                    extend1Values = new int[3];
-                    System.arraycopy(data, index, extend1Values, 0, 3);
+                    if (shadingTinttransform != null)
+                    {
+                        try
+                        {
+                            values = shadingTinttransform.eval(values);
+                        }
+                        catch (IOException exception)
+                        {
+                            LOG.error("error while processing a function", exception);
+                        }
+                    }
+                    values = shadingColorSpace.toRGB(values);
                 }
+                data[index] = (int) (values[0] * 255);
+                data[index + 1] = (int) (values[1] * 255);
+                data[index + 2] = (int) (values[2] * 255);
+                data[index + 3] = 255;
             }
         }
         raster.setPixels(0, 0, w, h, data);
@@ -365,13 +380,59 @@ public class RadialShadingContext implem
          *  
          *  The following code calculates the 2 possible values of s
          */
+        double p = -(x - coords[0]) * x1x0 - (y - coords[1]) * y1y0 - coords[2] * r1r0;
+        double q = (Math.pow(x - coords[0], 2) + Math.pow(y - coords[1], 2) - r0pow2);
+        double root = Math.sqrt(p * p - denom * q);
+        float root1 = (float) ((-p + root) / denom);
+        float root2 = (float) ((-p - root) / denom);
+        if (denom < 0)
+        {
+            return new float[] { root1, root2 };
+        }
+        else
+        {
+            return new float[] { root2, root1 };
+        }
+    }
+    
+    /**
+     * Returns the coords values.
+     * 
+     * @return the coords values as array
+     */
+    public float[] getCoords() 
+    {
+        return coords;
+    }
+        
+    /**
+     * Returns the domain values.
+     * 
+     * @return the domain values as array
+     */
+    public float[] getDomain() 
+    {
+        return domain;
+    }
         
-        float[] values = new float[2];
-        double p = (-0.25)*((x - coords[0])*x1x0 + (y - coords[1])*y1y0 - r1r0) / denom;
-        double q = (Math.pow(x - coords[0],2) + Math.pow(y - coords[1],2) - r0pow2) / denom;
-        double root = Math.sqrt(Math.pow(p , 2) - q);
-        values[0] = (float)((-1)*p + root);
-        values[1] = (float)((-1)*p - root);
-        return values;
+    /**
+     * Returns the extend values.
+     * 
+     * @return the extend values as array
+     */
+    public boolean[] getExtend() 
+    {
+        return extend;
     }
+    
+    /**
+     * Returns the function used for the shading tint transformation.
+     * 
+     * @return the shading tint transformation function
+     */
+    public PDFunction getShadingTintTransform() 
+    {
+        return shadingTinttransform;
+    }
+
 }