org.eclipse.birt.chart.device.g2d.G2dRendererBase.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.birt.chart.device.g2d.G2dRendererBase.java

Source

/*******************************************************************************
 * Copyright (c) 2009 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/

package org.eclipse.birt.chart.device.g2d;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.TexturePaint;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.WritableRaster;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.imageio.ImageIO;

import org.apache.commons.codec.binary.Base64;
import org.eclipse.birt.chart.computation.IConstants;
import org.eclipse.birt.chart.device.DeviceAdapter;
import org.eclipse.birt.chart.device.FontUtil;
import org.eclipse.birt.chart.device.IDeviceRenderer;
import org.eclipse.birt.chart.device.IDisplayServer;
import org.eclipse.birt.chart.device.ITextRenderer;
import org.eclipse.birt.chart.device.extension.i18n.Messages;
import org.eclipse.birt.chart.device.plugin.ChartDeviceExtensionPlugin;
import org.eclipse.birt.chart.event.ArcRenderEvent;
import org.eclipse.birt.chart.event.AreaRenderEvent;
import org.eclipse.birt.chart.event.ClipRenderEvent;
import org.eclipse.birt.chart.event.ImageRenderEvent;
import org.eclipse.birt.chart.event.LineRenderEvent;
import org.eclipse.birt.chart.event.OvalRenderEvent;
import org.eclipse.birt.chart.event.PolygonRenderEvent;
import org.eclipse.birt.chart.event.PrimitiveRenderEvent;
import org.eclipse.birt.chart.event.RectangleRenderEvent;
import org.eclipse.birt.chart.event.TextRenderEvent;
import org.eclipse.birt.chart.event.TransformationEvent;
import org.eclipse.birt.chart.exception.ChartException;
import org.eclipse.birt.chart.log.ILogger;
import org.eclipse.birt.chart.log.Logger;
import org.eclipse.birt.chart.model.attribute.Bounds;
import org.eclipse.birt.chart.model.attribute.ColorDefinition;
import org.eclipse.birt.chart.model.attribute.EmbeddedImage;
import org.eclipse.birt.chart.model.attribute.Fill;
import org.eclipse.birt.chart.model.attribute.Gradient;
import org.eclipse.birt.chart.model.attribute.ImageSourceType;
import org.eclipse.birt.chart.model.attribute.LineAttributes;
import org.eclipse.birt.chart.model.attribute.LineStyle;
import org.eclipse.birt.chart.model.attribute.Location;
import org.eclipse.birt.chart.model.attribute.PatternImage;
import org.eclipse.birt.chart.model.attribute.Position;
import org.eclipse.birt.chart.model.attribute.Size;
import org.eclipse.birt.chart.model.attribute.impl.BoundsImpl;
import org.eclipse.birt.chart.render.BaseRenderer;
import org.eclipse.birt.chart.render.InteractiveRenderer;
import org.eclipse.birt.chart.util.PatternImageUtil;
import org.eclipse.birt.chart.util.PatternImageUtil.ByteColorModel;
import org.eclipse.birt.chart.util.SecurityUtil;

/**
 * The base class of all renderers which bases on java.awt.Graphics2D.
 */

public class G2dRendererBase extends DeviceAdapter {
    private static ILogger logger = Logger.getLogger("org.eclipse.birt.chart.device.extension/g2d"); //$NON-NLS-1$

    protected IDisplayServer _ids;
    protected Graphics2D _g2d;
    protected InteractiveRenderer iv;
    protected ITextRenderer _tr = null;

    private final Map<LineAttributes, Stroke> _htLineStyles = new HashMap<LineAttributes, Stroke>();

    /**
     * Make bounds height/width always positive.
     * 
     * @param bo
     * @return
     */
    protected static final Bounds normalizeBounds(Bounds bo) {
        if (bo.getHeight() < 0) {
            bo.setTop(bo.getTop() + bo.getHeight());
            bo.setHeight(-bo.getHeight());
        }

        if (bo.getWidth() < 0) {
            bo.setLeft(bo.getLeft() + bo.getWidth());
            bo.setWidth(-bo.getWidth());
        }

        return bo;
    }

    /**
     * In SWING, polygons are defined with 'int' co-ordinates. There is no
     * concept of a Polygon2D. As a result, we downgrade high-res 'double'
     * co-ordinates to 'int' co-ordinates.
     * 
     * @param la
     * @return array of coordinates
     */
    public static final int[][] getCoordinatesAsInts(Location[] la) {
        final int n = la.length;
        final int[] iaX = new int[n];
        final int[] iaY = new int[n];

        for (int i = 0; i < n; i++) {
            iaX[i] = (int) la[i].getX();
            iaY[i] = (int) la[i].getY();
        }

        return new int[][] { iaX, iaY };
    }

    public static final double[][] getCoordinates(Location[] la) {
        final int n = la.length;
        final double[] iaX = new double[n];
        final double[] iaY = new double[n];

        for (int i = 0; i < n; i++) {
            iaX[i] = la[i].getX();
            iaY[i] = la[i].getY();
        }

        return new double[][] { iaX, iaY };
    }

    public static Shape getPolygon2D(Location[] loa) {
        Path2D path = new Path2D.Double();
        path.moveTo(loa[0].getX(), loa[0].getY());
        for (int i = 1; i < loa.length; ++i) {
            path.lineTo(loa[i].getX(), loa[i].getY());
        }
        path.closePath();
        return path;
    }

    protected Shape getPolygon(Location loa[]) {
        final int[][] i2a = getCoordinatesAsInts(loa);
        return new Polygon(i2a[0], i2a[1], loa.length);
    }

    /**
     * 
     * @param iArcStyle
     * @return
     */
    protected static final int toG2dArcType(int iArcStyle) {
        switch (iArcStyle) {
        case ArcRenderEvent.OPEN:
            return Arc2D.OPEN;
        case ArcRenderEvent.CLOSED:
            return Arc2D.CHORD;
        case ArcRenderEvent.SECTOR:
            return Arc2D.PIE;
        }
        return -1;
    }

    /**
     * Reusable 'strokes' for rendering lines may be obtained from here
     * 
     * @param ls
     * @return stroke
     */
    public final Stroke getCachedStroke(LineAttributes lia) {
        if (lia == null)
            return null;

        Stroke s = _htLineStyles.get(lia);
        if (s == null) {
            BasicStroke bs = null;
            int thickness = lia.getThickness();
            if (thickness == 0) {
                // Thickness can be zero, but dashed pattern can not be.
                thickness = 1;
            }
            if (lia.getStyle().getValue() == LineStyle.DASHED) {
                float[] faStyle = new float[] { 6 * thickness, 4 * thickness };
                bs = new BasicStroke(lia.getThickness(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, faStyle,
                        0);
            } else if (lia.getStyle().getValue() == LineStyle.DOTTED) {
                float[] faStyle = new float[] { thickness, 4 * thickness };
                bs = new BasicStroke(lia.getThickness(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, faStyle,
                        0);
            } else if (lia.getStyle().getValue() == LineStyle.DASH_DOTTED) {
                float[] faStyle = new float[] { 6 * thickness, 4 * thickness, thickness, 4 * thickness };
                bs = new BasicStroke(lia.getThickness(), BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0, faStyle,
                        0);
            } else if (lia.getStyle().getValue() == LineStyle.SOLID) {
                bs = new BasicStroke(lia.getThickness(), BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND);
            }
            if (bs != null) {
                _htLineStyles.put(lia, bs);
            }
            return bs;
        }
        return s;
    }

    private static Set<String> sLocalFontFamilyNamesSet = new HashSet<String>();

    static {
        String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
        sLocalFontFamilyNamesSet = new HashSet<String>(Arrays.asList(fonts));
    }

    private static Map<String, String> sLogicFontNameMap = new HashMap<String, String>();

    static {
        sLogicFontNameMap.put(FontUtil.LOGIC_FONT_FAMILY_SERIF, "Serif"); //$NON-NLS-1$
        sLogicFontNameMap.put(FontUtil.LOGIC_FONT_FAMILY_SANS_SERIF, "SansSerif"); //$NON-NLS-1$
        sLogicFontNameMap.put(FontUtil.LOGIC_FONT_FAMILY_MONOSPACE, "Monospaced"); //$NON-NLS-1$
    }

    @Override
    protected String convertFont(String fontFamily) {
        String localFont = sLogicFontNameMap.get(fontFamily);
        if (localFont == null) {
            localFont = fontFamily;
        }
        if (sLocalFontFamilyNamesSet.contains(localFont)) {
            return localFont.toLowerCase();
        }

        return FontUtil.getFontFamily(fontFamily);
    }

    protected void prepareGraphicsContext() {
        _g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        _g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

        getDisplayServer().setGraphicsContext(_g2d);
    }

    @Override
    public void setProperty(String sProperty, Object oValue) {
        if (sProperty.equals(IDeviceRenderer.GRAPHICS_CONTEXT)) {
            _g2d = (Graphics2D) oValue;
            prepareGraphicsContext();
        } else if (sProperty.equals(IDeviceRenderer.DPI_RESOLUTION)) {
            getDisplayServer().setDpiResolution(((Integer) oValue).intValue());
        }
    }

    @Override
    public Object getGraphicsContext() {
        return _g2d;
    }

    @Override
    public IDisplayServer getDisplayServer() {
        return _ids;
    }

    @Override
    public void setClip(ClipRenderEvent cre) {
        final Location[] loa = cre.getVertices();

        if (loa == null) {
            _g2d.setClip(null);
        } else {
            _g2d.setClip(getPolygon(loa));
        }
    }

    @Override
    public void drawImage(ImageRenderEvent ire) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(ire);
        }
        if (ire.getImage() == null || ire.getLocation() == null) {
            return;
        }

        java.awt.Image img = null;

        if (ire.getImage() instanceof EmbeddedImage) {
            try {
                byte[] data = Base64.decodeBase64(((EmbeddedImage) ire.getImage()).getData().getBytes());

                img = createImage(data);
            } catch (Exception ilex) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING, ilex);
            }
        } else if (ire.getImage().getSource() != ImageSourceType.FILE
                && ire.getImage().getSource() != ImageSourceType.REPORT) {
            try {
                final String sUrl = ire.getImage().getURL();
                img = (java.awt.Image) _ids.loadImage(SecurityUtil.newURL(sUrl));
            } catch (ChartException ilex) {
                // Ignore the invalid path, and log it only
                logger.log(new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING, ilex));
            } catch (MalformedURLException muex) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING, muex);
            }
        }

        if (img == null) {
            return;
        }

        Location loc = ire.getLocation();
        Position pos = ire.getPosition();
        if (pos == null) {
            pos = Position.INSIDE_LITERAL;
        }

        ImageObserver io = (ImageObserver) _ids.getObserver();

        final boolean bSizeSet = ire.getWidth() * ire.getHeight() > 0;
        int width = bSizeSet ? ire.getWidth() : img.getWidth(io);
        int height = bSizeSet ? ire.getHeight() : img.getHeight(io);
        int x = (int) loc.getX();
        int y = (int) loc.getY();

        switch (pos.getValue()) {
        case Position.INSIDE:
        case Position.OUTSIDE:
            x -= width / 2;
            y -= height / 2;
            break;
        case Position.LEFT:
            x -= width;
            y -= height / 2;
            break;
        case Position.RIGHT:
            y -= height / 2;
            break;
        case Position.ABOVE:
            x -= width / 2;
            y -= height;
            break;
        case Position.BELOW:
            x -= width / 2;
            break;
        }

        _g2d.drawImage(img, x, y, width, height, io);
    }

    @Override
    public void drawLine(LineRenderEvent lre) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(lre);
        }
        // CHECK IF THE LINE ATTRIBUTES ARE CORRECTLY DEFINED
        final LineAttributes lia = lre.getLineAttributes();
        if (!validateLineAttributes(lre.getSource(), lia) || lia.getColor() == null) {
            return;
        }

        if (lia.getColor().isSetTransparency() && lia.getColor().getTransparency() == 0) {
            return;
        }

        // DRAW THE LINE
        final Location loStart = lre.getStart();
        final Location loEnd = lre.getEnd();
        // Location may be beyond integer range and need long time to
        // render
        if (!checkValidLocation(loStart) || !checkValidLocation(loEnd)) {
            return;
        }
        Stroke sPrevious = null, sCurrent = getCachedStroke(lia);
        if (sCurrent != null) // SOME STROKE DEFINED?
        {
            sPrevious = _g2d.getStroke();
            _g2d.setStroke(sCurrent);
        }

        _g2d.setColor((Color) _ids.getColor(lia.getColor()));
        _g2d.draw(new Line2D.Double(loStart.getX(), loStart.getY(), loEnd.getX(), loEnd.getY()));

        if (sPrevious != null) // RESTORE PREVIOUS STROKE
        {
            _g2d.setStroke(sPrevious);
        }
    }

    protected boolean checkValidLocation(Location lo) {
        // If the location is out of integer range, it needs endless time to
        // render
        return lo.getX() < Integer.MAX_VALUE && lo.getX() > Integer.MIN_VALUE && lo.getY() < Integer.MAX_VALUE
                && lo.getY() > Integer.MIN_VALUE;
    }

    @Override
    public void drawRectangle(RectangleRenderEvent rre) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(rre);
        }
        // CHECK IF THE LINE ATTRIBUTES ARE CORRECTLY DEFINED
        final LineAttributes lia = rre.getOutline();
        if (!validateLineAttributes(rre.getSource(), lia)) {
            return;
        }

        // SETUP THE FOREGROUND COLOR (DARKER BACKGROUND IF DEFINED AS NULL)
        final Color cFG = (Color) validateEdgeColor(lia.getColor(), rre.getBackground(), _ids);
        if (cFG == null || cFG.getAlpha() == 0) {
            return;
        }

        // RENDER THE RECTANGLE WITH THE APPROPRIATE LINE STYLE
        final Bounds bo = normalizeBounds(rre.getBounds());
        Stroke sPrevious = null;
        Stroke sCurrent = getCachedStroke(lia);
        if (sCurrent != null) // SOME STROKE DEFINED?
        {
            sPrevious = _g2d.getStroke();
            _g2d.setStroke(sCurrent);
        }
        _g2d.setColor(cFG);
        _g2d.draw(new Rectangle2D.Double(bo.getLeft(), bo.getTop(), bo.getWidth() - 1, bo.getHeight() - 1));
        if (sPrevious != null) // RESTORE PREVIOUS STROKE
        {
            _g2d.setStroke(sPrevious);
        }
    }

    @Override
    public void fillRectangle(RectangleRenderEvent rre) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(rre);
        }
        final Fill flBackground = validateMultipleFill(rre.getBackground());

        if (isFullTransparent(flBackground)) {
            return;
        }

        final Bounds bo = normalizeBounds(rre.getBounds());
        final Rectangle2D.Double r2d = new Rectangle2D.Double(bo.getLeft(), bo.getTop(), bo.getWidth(),
                bo.getHeight());
        if (flBackground instanceof ColorDefinition) {
            final ColorDefinition cd = (ColorDefinition) flBackground;
            _g2d.setColor((Color) _ids.getColor(cd));
            _g2d.fill(r2d);
        } else if (flBackground instanceof Gradient) {
            final Gradient g = (Gradient) flBackground;
            final ColorDefinition cdStart = g.getStartColor();
            final ColorDefinition cdEnd = g.getEndColor();
            // boolean bCyclic = g.isCyclic();
            double dAngleInDegrees = g.getDirection();
            final double dAngleInRadians = ((-dAngleInDegrees * Math.PI) / 180.0);
            // int iAlpha = g.getTransparency();

            if (dAngleInDegrees < -90 || dAngleInDegrees > 90) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING,
                        "SwingRendererImpl.exception.gradient.angle", //$NON-NLS-1$
                        new Object[] { new Double(dAngleInDegrees) }, Messages.getResourceBundle(getULocale()));
            }

            Point2D.Double p2dStart, p2dEnd;
            if (dAngleInDegrees == 90) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees == -90) {
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees > 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getHeight() - bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else if (dAngleInDegrees < 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(), bo.getTop());
            }
            _g2d.setPaint(new GradientPaint(p2dStart, (Color) _ids.getColor(cdStart), p2dEnd,
                    (Color) _ids.getColor(cdEnd)));
            _g2d.fill(r2d);
        } else if (flBackground instanceof org.eclipse.birt.chart.model.attribute.Image) {
            if (flBackground instanceof PatternImage) {
                fillWithPatternImage(new Area(r2d), flBackground);
                return;
            }
            java.awt.Image img = createImageFromModel(flBackground);

            if (img != null) {
                final Shape shClip = _g2d.getClip();
                Area ar2 = new Area(r2d);
                if (shClip != null) {
                    Area ar1 = new Area(shClip);
                    ar2.intersect(ar1);
                }
                _g2d.setClip(ar2);

                img = scaleImage(img);
                final Size szImage = _ids.getSize(img);

                int iXRepeat = (int) (Math.ceil(r2d.width / szImage.getWidth()));
                int iYRepeat = (int) (Math.ceil(r2d.height / szImage.getHeight()));
                ImageObserver io = (ImageObserver) _ids.getObserver();
                for (int i = 0; i < iXRepeat; i++) {
                    for (int j = 0; j < iYRepeat; j++) {
                        _g2d.drawImage(img, (int) (r2d.x + i * szImage.getWidth()),
                                (int) (r2d.y + j * szImage.getHeight()), io);
                    }
                }

                _g2d.setClip(shClip); // RESTORE
            }
        }
    }

    @Override
    public void drawPolygon(PolygonRenderEvent pre) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(pre);
        }
        // CHECK IF THE LINE ATTRIBUTES ARE CORRECTLY DEFINED
        final LineAttributes lia = pre.getOutline();
        if (!validateLineAttributes(pre.getSource(), lia)) {
            return;
        }

        // SETUP THE FOREGROUND COLOR (DARKER BACKGROUND IF DEFINED AS NULL)
        final Color cFG = (Color) validateEdgeColor(lia.getColor(), pre.getBackground(), _ids);
        if (cFG == null || cFG.getAlpha() == 0) {
            return;
        }

        // DRAW THE POLYGON
        final Location[] la = pre.getPoints();
        Stroke sPrevious = null;
        final Stroke sCurrent = getCachedStroke(lia);
        if (sCurrent != null) // SOME STROKE DEFINED?
        {
            sPrevious = _g2d.getStroke();
            _g2d.setStroke(sCurrent);
        }

        _g2d.setColor(cFG);
        _g2d.draw(getPolygon(la));
        if (sPrevious != null) // RESTORE PREVIOUS STROKE
        {
            _g2d.setStroke(sPrevious);
        }
    }

    @Override
    public void fillPolygon(PolygonRenderEvent pre) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(pre);
        }
        final Fill flBackground = validateMultipleFill(pre.getBackground());

        if (isFullTransparent(flBackground)) {
            return;
        }

        final Location[] loa = pre.getPoints();
        final int[][] i2a = getCoordinatesAsInts(loa);

        if (flBackground instanceof ColorDefinition) {
            final ColorDefinition cd = (ColorDefinition) flBackground;
            _g2d.setColor((Color) _ids.getColor(cd));
            _g2d.fill(getPolygon(loa));
        } else if (flBackground instanceof Gradient) {
            final Gradient g = (Gradient) flBackground;
            final ColorDefinition cdStart = g.getStartColor();
            final ColorDefinition cdEnd = g.getEndColor();
            // final boolean bRadial = g.isCyclic();
            final double dAngleInDegrees = g.getDirection();
            final double dAngleInRadians = ((-dAngleInDegrees * Math.PI) / 180.0);
            // final int iAlpha = g.getTransparency();

            final double dMinX = BaseRenderer.getX(loa, IConstants.MIN);
            final double dMaxX = BaseRenderer.getX(loa, IConstants.MAX);
            final double dMinY = BaseRenderer.getY(loa, IConstants.MIN);
            final double dMaxY = BaseRenderer.getY(loa, IConstants.MAX);

            if (dAngleInDegrees < -90 || dAngleInDegrees > 90) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING,
                        "SwingRendererImpl.exception.gradient.angle", //$NON-NLS-1$
                        new Object[] { new Double(dAngleInDegrees) }, Messages.getResourceBundle(getULocale()));
            }

            Point2D.Double p2dStart, p2dEnd;
            if (dAngleInDegrees == 90) {
                p2dStart = new Point2D.Double(dMinX, dMaxY);
                p2dEnd = new Point2D.Double(dMinX, dMinY);
            } else if (dAngleInDegrees == -90) {
                p2dStart = new Point2D.Double(dMinX, dMinY);
                p2dEnd = new Point2D.Double(dMinX, dMaxY);
            } else if (dAngleInDegrees > 0) {
                p2dStart = new Point2D.Double(dMinX, dMaxY);
                p2dEnd = new Point2D.Double(dMaxX, dMaxY - (dMaxX - dMinX) * Math.abs(Math.tan(dAngleInRadians)));
            } else if (dAngleInDegrees < 0) {
                p2dStart = new Point2D.Double(dMinX, dMinY);
                p2dEnd = new Point2D.Double(dMaxX, dMinY + (dMaxX - dMinX) * Math.abs(Math.tan(dAngleInRadians)));
            } else {
                p2dStart = new Point2D.Double(dMinX, dMinY);
                p2dEnd = new Point2D.Double(dMaxX, dMinY);
            }
            _g2d.setPaint(new GradientPaint(p2dStart, (Color) _ids.getColor(cdStart), p2dEnd,
                    (Color) _ids.getColor(cdEnd)));
            _g2d.fill(getPolygon(loa));
        } else if (flBackground instanceof org.eclipse.birt.chart.model.attribute.Image) {
            Area ar2 = new Area(new Polygon(i2a[0], i2a[1], loa.length));
            if (flBackground instanceof PatternImage) {
                fillWithPatternImage(ar2, flBackground);
                return;
            }
            java.awt.Image img = createImageFromModel(flBackground);

            if (img != null) {
                final Shape shClip = _g2d.getClip();
                if (shClip != null) {
                    Area ar1 = new Area(shClip);
                    ar2.intersect(ar1);
                }
                _g2d.setClip(ar2);

                final double dMinX = BaseRenderer.getX(loa, IConstants.MIN);
                final double dMaxX = BaseRenderer.getX(loa, IConstants.MAX);
                final double dMinY = BaseRenderer.getY(loa, IConstants.MIN);
                final double dMaxY = BaseRenderer.getY(loa, IConstants.MAX);
                final Size szImage = _ids.getSize(img);

                final int iXRepeat = (int) (Math.ceil((dMaxX - dMinX) / szImage.getWidth()));
                final int iYRepeat = (int) (Math.ceil((dMaxY - dMinY) / szImage.getHeight()));
                final ImageObserver io = (ImageObserver) _ids.getObserver();
                for (int i = 0; i < iXRepeat; i++) {
                    for (int j = 0; j < iYRepeat; j++) {
                        _g2d.drawImage(img, (int) (dMinX + i * szImage.getWidth()),
                                (int) (dMinY + j * szImage.getHeight()), io);
                    }
                }

                _g2d.setClip(shClip); // RESTORE
            }
        }
    }

    @Override
    public void drawArc(ArcRenderEvent are) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(are);
        }

        // CHECK IF THE LINE ATTRIBUTES ARE CORRECTLY DEFINED
        final LineAttributes lia = are.getOutline();
        if (!validateLineAttributes(are.getSource(), lia)) {
            return;
        }

        // SETUP THE FOREGROUND COLOR (DARKER BACKGROUND IF DEFINED AS NULL)
        final Color cFG = (Color) validateEdgeColor(lia.getColor(), are.getBackground(), _ids);
        if (cFG == null || cFG.getAlpha() == 0) {
            return;
        }

        // DRAW THE ARC
        Stroke sPrevious = null;
        Stroke sCurrent = getCachedStroke(lia);
        if (sCurrent != null) // SOME STROKE DEFINED?
        {
            sPrevious = _g2d.getStroke();
            _g2d.setStroke(sCurrent);
        }
        _g2d.setColor(cFG);

        if ((are.getInnerRadius() >= 0 && are.getOuterRadius() > 0 && are.getInnerRadius() < are.getOuterRadius())
                || (are.getInnerRadius() > 0 && are.getOuterRadius() <= 0)) {
            Bounds rctOuter = getOuterRectangle(are);
            Bounds rctInner = getInnerRectangle(are);

            Shape outerArc = new Arc2D.Double(rctOuter.getLeft(), rctOuter.getTop(), rctOuter.getWidth(),
                    rctOuter.getHeight(), are.getStartAngle(), are.getAngleExtent(), Arc2D.OPEN);
            Shape innerArc = new Arc2D.Double(rctInner.getLeft(), rctInner.getTop(), rctInner.getWidth(),
                    rctInner.getHeight(), are.getStartAngle() + are.getAngleExtent(), -are.getAngleExtent(),
                    Arc2D.OPEN);

            double startAngle = Math.toRadians(-are.getStartAngle());
            double stopAngle = Math.toRadians(-are.getStartAngle() - are.getAngleExtent());

            double xsOuter = (rctOuter.getLeft() + (Math.cos(startAngle) * 0.5 + 0.5) * rctOuter.getWidth());
            double ysOuter = (rctOuter.getTop() + (Math.sin(startAngle) * 0.5 + 0.5) * rctOuter.getHeight());

            double xeInner = (rctInner.getLeft() + (Math.cos(stopAngle) * 0.5 + 0.5) * rctInner.getWidth());
            double yeInner = (rctInner.getTop() + (Math.sin(stopAngle) * 0.5 + 0.5) * rctInner.getHeight());

            GeneralPath gp = new GeneralPath();
            gp.append(outerArc, false);
            gp.lineTo((float) xeInner, (float) yeInner);
            gp.append(innerArc, false);
            gp.lineTo((float) xsOuter, (float) ysOuter);

            Area area = new Area(gp);
            Shape prevClip = _g2d.getClip();
            Area ar2 = new Area(area);
            if (prevClip != null) {
                Area ar1 = new Area(prevClip);
                ar2.intersect(ar1);
            }
            _g2d.setClip(ar2);
            _g2d.draw(area);
            _g2d.setClip(prevClip);

        } else {
            _g2d.draw(new Arc2D.Double(are.getTopLeft().getX(), are.getTopLeft().getY(), are.getWidth(),
                    are.getHeight(), are.getStartAngle(), are.getAngleExtent(), toG2dArcType(are.getStyle())));
        }

        if (sPrevious != null) // RESTORE PREVIOUS STROKE
        {
            _g2d.setStroke(sPrevious);
        }
    }

    protected Bounds getOuterRectangle(ArcRenderEvent are) {
        Bounds rctOuter;
        if (are.getOuterRadius() > 0) {
            double radio = are.getHeight() / are.getWidth();
            rctOuter = BoundsImpl.create(are.getTopLeft().getX() + (are.getWidth() - 2 * are.getOuterRadius()) / 2,
                    are.getTopLeft().getY() + (are.getHeight() - 2 * are.getOuterRadius() * radio) / 2,
                    2 * are.getOuterRadius(), 2 * are.getOuterRadius() * radio);
        } else {
            rctOuter = BoundsImpl.create(are.getTopLeft().getX(), are.getTopLeft().getY(), are.getWidth(),
                    are.getHeight());
        }
        return rctOuter;
    }

    protected Bounds getInnerRectangle(ArcRenderEvent are) {
        Bounds rctInner;
        if (are.getInnerRadius() > 0) {
            double radio = are.getHeight() / are.getWidth();
            rctInner = BoundsImpl.create(are.getTopLeft().getX() + (are.getWidth() - 2 * are.getInnerRadius()) / 2,
                    are.getTopLeft().getY() + (are.getHeight() - 2 * are.getInnerRadius() * radio) / 2,
                    2 * are.getInnerRadius(), 2 * are.getInnerRadius() * radio);
        } else {
            rctInner = BoundsImpl.create(are.getTopLeft().getX() + (are.getWidth() - 2 * are.getInnerRadius()) / 2,
                    are.getTopLeft().getY() + (are.getHeight() - 2 * are.getInnerRadius()) / 2,
                    2 * are.getInnerRadius(), 2 * are.getInnerRadius());
        }
        return rctInner;
    }

    @Override
    public void fillArc(ArcRenderEvent are) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(are);
        }

        final Fill flBackground = validateMultipleFill(are.getBackground());

        if (isFullTransparent(flBackground)) {
            return;
        }

        if (flBackground instanceof ColorDefinition) {
            final ColorDefinition cl = (ColorDefinition) flBackground;
            final Color clrPrevious = _g2d.getColor();
            final Color currentColor = (Color) _ids.getColor(cl);
            _g2d.setColor(currentColor);

            if ((are.getInnerRadius() >= 0 && are.getOuterRadius() > 0
                    && are.getInnerRadius() < are.getOuterRadius())
                    || (are.getInnerRadius() > 0 && are.getOuterRadius() <= 0)) {
                Bounds rctOuter = getOuterRectangle(are);
                Bounds rctInner = getInnerRectangle(are);

                Shape outerArc = new Arc2D.Double(rctOuter.getLeft(), rctOuter.getTop(), rctOuter.getWidth(),
                        rctOuter.getHeight(), are.getStartAngle(), are.getAngleExtent(), Arc2D.PIE);

                Shape innerArc = new Arc2D.Double(rctInner.getLeft(), rctInner.getTop(), rctInner.getWidth(),
                        rctInner.getHeight(), are.getStartAngle(), are.getAngleExtent(), Arc2D.PIE);

                Area fArea = new Area(outerArc);
                fArea.exclusiveOr(new Area(innerArc));

                Shape prevClip = _g2d.getClip();
                Area ar2 = new Area(fArea);
                if (prevClip != null) {
                    Area ar1 = new Area(prevClip);
                    ar2.intersect(ar1);
                }
                _g2d.setClip(ar2);
                _g2d.fill(fArea);
                _g2d.setClip(prevClip);
            } else {
                _g2d.fill(new Arc2D.Double(are.getTopLeft().getX(), are.getTopLeft().getY(), are.getWidth(),
                        are.getHeight(), are.getStartAngle(), are.getAngleExtent(), toG2dArcType(are.getStyle())));
            }

            _g2d.setColor(clrPrevious); // RESTORE
        } else if (flBackground instanceof Gradient) {
            final Gradient g = (Gradient) flBackground;
            final ColorDefinition cdStart = g.getStartColor();
            final ColorDefinition cdEnd = g.getEndColor();
            double dAngleInDegrees = g.getDirection();
            final double dAngleInRadians = ((-dAngleInDegrees * Math.PI) / 180.0);
            Bounds bo = are.getBounds();

            if (dAngleInDegrees < -90 || dAngleInDegrees > 90) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING,
                        "SwingRendererImpl.exception.gradient.angle", //$NON-NLS-1$
                        new Object[] { new Double(dAngleInDegrees) }, Messages.getResourceBundle(getULocale()));
            }

            Point2D.Double p2dStart, p2dEnd;
            if (dAngleInDegrees == 90) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees == -90) {
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees > 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getHeight() - bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else if (dAngleInDegrees < 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(), bo.getTop());
            }

            final Paint pPrevious = _g2d.getPaint();
            _g2d.setPaint(new GradientPaint(p2dStart, (Color) _ids.getColor(cdStart), p2dEnd,
                    (Color) _ids.getColor(cdEnd)));

            if ((are.getInnerRadius() >= 0 && are.getOuterRadius() > 0
                    && are.getInnerRadius() < are.getOuterRadius())
                    || (are.getInnerRadius() > 0 && are.getOuterRadius() <= 0)) {
                Bounds rctOuter = getOuterRectangle(are);
                Bounds rctInner = getInnerRectangle(are);

                Shape outerArc = new Arc2D.Double(rctOuter.getLeft(), rctOuter.getTop(), rctOuter.getWidth(),
                        rctOuter.getHeight(), are.getStartAngle(), are.getAngleExtent(), Arc2D.PIE);

                Shape innerArc = new Arc2D.Double(rctInner.getLeft(), rctInner.getTop(), rctInner.getWidth(),
                        rctInner.getHeight(), are.getStartAngle(), are.getAngleExtent(), Arc2D.PIE);

                Area fArea = new Area(outerArc);
                fArea.exclusiveOr(new Area(innerArc));

                Shape prevClip = _g2d.getClip();
                Area ar2 = new Area(fArea);
                if (prevClip != null) {
                    Area ar1 = new Area(prevClip);
                    ar2.intersect(ar1);
                }
                _g2d.setClip(ar2);
                _g2d.fill(fArea);
                _g2d.setClip(prevClip);
            } else {
                _g2d.fill(new Arc2D.Double(are.getTopLeft().getX(), are.getTopLeft().getY(), are.getWidth(),
                        are.getHeight(), are.getStartAngle(), are.getAngleExtent(), toG2dArcType(are.getStyle())));
            }

            _g2d.setPaint(pPrevious); // RESTORE
        } else if (flBackground instanceof org.eclipse.birt.chart.model.attribute.Image) {
            final Bounds bo = are.getBounds();
            final Rectangle2D.Double r2d = new Rectangle2D.Double(bo.getLeft(), bo.getTop(), bo.getWidth(),
                    bo.getHeight());

            Shape shPreviousClip = _g2d.getClip();
            Area ar = null;

            if ((are.getInnerRadius() >= 0 && are.getOuterRadius() > 0
                    && are.getInnerRadius() < are.getOuterRadius())
                    || (are.getInnerRadius() > 0 && are.getOuterRadius() <= 0)) {
                Bounds rctOuter = getOuterRectangle(are);
                Bounds rctInner = getInnerRectangle(are);

                Shape outerArc = new Arc2D.Double(rctOuter.getLeft(), rctOuter.getTop(), rctOuter.getWidth(),
                        rctOuter.getHeight(), are.getStartAngle(), are.getAngleExtent(), Arc2D.PIE);

                Shape innerArc = new Arc2D.Double(rctInner.getLeft(), rctInner.getTop(), rctInner.getWidth(),
                        rctInner.getHeight(), are.getStartAngle(), are.getAngleExtent(), Arc2D.PIE);

                Area fArea = new Area(outerArc);
                fArea.exclusiveOr(new Area(innerArc));

                if (shPreviousClip != null) {
                    Area ar1 = new Area(shPreviousClip);
                    fArea.intersect(ar1);
                }
                // _g2d.setClip( fArea );
                ar = fArea;
            } else {
                // SETUP THE CLIPPING AREA
                final Shape shArc = new Arc2D.Double(are.getTopLeft().getX(), are.getTopLeft().getY(),
                        are.getWidth(), are.getHeight(), are.getStartAngle(), are.getAngleExtent(),
                        toG2dArcType(are.getStyle()));

                Area ar2 = new Area(shArc);
                if (shPreviousClip != null) {
                    Area ar1 = new Area(shPreviousClip);
                    ar2.intersect(ar1);
                }
                // _g2d.setClip( ar2 );
                ar = ar2;
            }

            if (flBackground instanceof PatternImage) {
                fillWithPatternImage(new Area(ar), flBackground);
                return;
            }

            _g2d.setClip(ar);

            // LOAD THE IMAGE
            java.awt.Image img = createImageFromModel(flBackground);
            if (img != null) {
                // REPLICATE THE IMAGE AS NEEDED
                final Size szImage = _ids.getSize(img);
                int iXRepeat = (int) (Math.ceil(r2d.width / szImage.getWidth()));
                int iYRepeat = (int) (Math.ceil(r2d.height / szImage.getHeight()));
                ImageObserver io = (ImageObserver) _ids.getObserver();
                for (int i = 0; i < iXRepeat; i++) {
                    for (int j = 0; j < iYRepeat; j++) {
                        _g2d.drawImage(img, (int) (r2d.x + i * szImage.getWidth()),
                                (int) (r2d.y + j * szImage.getHeight()), io);
                    }
                }
            }

            _g2d.setClip(shPreviousClip); // RESTORE
        }
    }

    protected Image createImage(byte[] data) {
        try {
            return ImageIO.read(new ByteArrayInputStream(data));
        } catch (IOException e) {
            logger.log(e);
        }
        return null;
    }

    protected java.awt.Image createImageFromModel(Fill imageModel) throws ChartException {

        java.awt.Image img = null;
        if (imageModel instanceof EmbeddedImage) {
            try {
                byte[] data = Base64.decodeBase64(((EmbeddedImage) imageModel).getData().getBytes());

                img = createImage(data);
            } catch (Exception ilex) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING, ilex);
            }
        } else if (imageModel instanceof PatternImage) {
            PatternImage pi = (PatternImage) imageModel;
            byte[] data = PatternImageUtil.createImageData(pi, ByteColorModel.RGBA);
            BufferedImage bimg = new BufferedImage(8, 8, BufferedImage.TYPE_4BYTE_ABGR);
            img = bimg;
            WritableRaster raster = bimg.getRaster();
            raster.setDataElements(0, 0, 8, 8, data);
            bimg.flush();
        } else if (imageModel instanceof org.eclipse.birt.chart.model.attribute.Image) {
            if (((org.eclipse.birt.chart.model.attribute.Image) imageModel).getSource() == ImageSourceType.STATIC) {
                try {
                    final String sUrl = ((org.eclipse.birt.chart.model.attribute.Image) imageModel).getURL();
                    img = (java.awt.Image) _ids.loadImage(SecurityUtil.newURL(sUrl));
                } catch (ChartException ilex) {
                    throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING, ilex);
                } catch (MalformedURLException muex) {
                    throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING, muex);
                }
            }
        }

        return img;

    }

    /**
     * Scales image according to output DPI. If 96, do not need to scale
     * 
     * @param img
     * @return
     */
    private java.awt.Image scaleImage(java.awt.Image img) {
        if (this._ids.getDpiResolution() == 96) {
            // Do not scale in normal dpi
            return img;
        }
        double scale = this._ids.getDpiResolution() / 96d;
        int newWidth = (int) (img.getWidth((ImageObserver) getDisplayServer().getObserver()) * scale);
        int newHeight = (int) (img.getHeight((ImageObserver) getDisplayServer().getObserver()) * scale);
        return img.getScaledInstance(newWidth, newHeight, Image.SCALE_DEFAULT);
    }

    protected BufferedImage convertPatternImage(java.awt.Image img) {
        if (img instanceof BufferedImage) {
            return (BufferedImage) img;
        }
        return null;
    }

    protected void fillWithPatternImage(Shape shape, Fill fill) throws ChartException {
        java.awt.Image img = createImageFromModel(fill);
        BufferedImage bimg = convertPatternImage(img);

        if (bimg != null) {
            _g2d.setPaint(new TexturePaint(bimg, new Rectangle(0, 0, bimg.getWidth(), bimg.getHeight())));
            _g2d.fill(shape);
        }
    }

    @Override
    public void drawArea(AreaRenderEvent are) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(are);
        }

        // CHECK IF THE LINE ATTRIBUTES ARE CORRECTLY DEFINED
        final LineAttributes lia = are.getOutline();
        if (!validateLineAttributes(are.getSource(), lia)) {
            return;
        }

        // SETUP THE FOREGROUND COLOR (DARKER BACKGROUND IF DEFINED AS NULL)
        final Color cFG = (Color) validateEdgeColor(lia.getColor(), are.getBackground(), _ids);
        // IF UNDEFINED OR TOTALLY TRANSPARENT, EXIT
        if (cFG == null || cFG.getAlpha() == 0) {
            return;
        }

        // BUILD THE GENERAL PATH STRUCTURE
        final GeneralPath gp = new GeneralPath();
        PrimitiveRenderEvent pre;
        for (int i = 0; i < are.getElementCount(); i++) {
            pre = are.getElement(i);
            if (pre instanceof ArcRenderEvent) {
                final ArcRenderEvent acre = (ArcRenderEvent) pre;
                final Arc2D.Double a2d = new Arc2D.Double(acre.getTopLeft().getX(), acre.getTopLeft().getY(),
                        acre.getWidth(), acre.getHeight(), acre.getStartAngle(), acre.getAngleExtent(),
                        toG2dArcType(acre.getStyle()));
                gp.append(a2d, true);
            } else if (pre instanceof LineRenderEvent) {
                final LineRenderEvent lre = (LineRenderEvent) pre;
                final Line2D.Double l2d = new Line2D.Double(lre.getStart().getX(), lre.getStart().getY(),
                        lre.getEnd().getX(), lre.getEnd().getY());
                gp.append(l2d, true);
            }
        }

        // DRAW THE GENERAL PATH
        Stroke sPrevious = null;
        Stroke sCurrent = getCachedStroke(lia);
        if (sCurrent != null) // SOME STROKE DEFINED?
        {
            sPrevious = _g2d.getStroke();
            _g2d.setStroke(sCurrent);
        }

        _g2d.setColor(cFG);
        _g2d.draw(gp);

        if (sPrevious != null) // RESTORE PREVIOUS STROKE
        {
            _g2d.setStroke(sPrevious);
        }
    }

    @Override
    public void fillArea(AreaRenderEvent are) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(are);
        }

        final Fill flBackground = validateMultipleFill(are.getBackground());

        if (isFullTransparent(flBackground)) {
            return;
        }

        // SETUP SWING DATA STRUCTURES
        final GeneralPath gp = new GeneralPath();
        PrimitiveRenderEvent pre;
        for (int i = 0; i < are.getElementCount(); i++) {
            pre = are.getElement(i);
            if (pre instanceof ArcRenderEvent) {
                final ArcRenderEvent acre = (ArcRenderEvent) pre;
                final Arc2D.Double a2d = new Arc2D.Double(acre.getTopLeft().getX(), acre.getTopLeft().getY(),
                        acre.getWidth(), acre.getHeight(), acre.getStartAngle(), acre.getAngleExtent(),
                        toG2dArcType(acre.getStyle()));
                gp.append(a2d, true);
            } else if (pre instanceof LineRenderEvent) {
                final LineRenderEvent lre = (LineRenderEvent) pre;
                final Line2D.Double l2d = new Line2D.Double(lre.getStart().getX(), lre.getStart().getY(),
                        lre.getEnd().getX(), lre.getEnd().getY());
                gp.append(l2d, true);
            }
        }

        // BEGIN FILLING
        if (flBackground instanceof ColorDefinition) {
            _g2d.setColor((Color) _ids.getColor((ColorDefinition) flBackground));
        } else if (flBackground instanceof Gradient) {
            final Gradient g = (Gradient) flBackground;
            final ColorDefinition cdStart = g.getStartColor();
            final ColorDefinition cdEnd = g.getEndColor();
            // boolean bCyclic = g.isCyclic();
            double dAngleInDegrees = g.getDirection();
            final double dAngleInRadians = ((-dAngleInDegrees * Math.PI) / 180.0);
            // int iAlpha = g.getTransparency();
            Bounds bo = are.getBounds();

            /*
             * if (bCyclic) { }
             */

            if (dAngleInDegrees < -90 || dAngleInDegrees > 90) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING,
                        "SwingRendererImpl.exception.gradient.angle", //$NON-NLS-1$
                        new Object[] { new Double(dAngleInDegrees) }, Messages.getResourceBundle(getULocale()));
            }

            Point2D.Double p2dStart, p2dEnd;
            if (dAngleInDegrees == 90) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees == -90) {
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees > 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getHeight() - bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else if (dAngleInDegrees < 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(), bo.getTop());
            }
            _g2d.setPaint(new GradientPaint(p2dStart, (Color) _ids.getColor(cdStart), p2dEnd,
                    (Color) _ids.getColor(cdEnd)));
        } else if (flBackground instanceof org.eclipse.birt.chart.model.attribute.Image) {
            // TODO TBD
        }
        _g2d.fill(gp);
    }

    @Override
    public void drawOval(OvalRenderEvent ore) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(ore);
        }

        // CHECK IF THE LINE ATTRIBUTES ARE CORRECTLY DEFINED
        final LineAttributes lia = ore.getOutline();
        if (!validateLineAttributes(ore.getSource(), lia)) {
            return;
        }

        // SETUP THE FOREGROUND COLOR (DARKER BACKGROUND IF DEFINED AS NULL)
        final Color cFG = (Color) validateEdgeColor(lia.getColor(), ore.getBackground(), _ids);
        if (cFG == null || cFG.getAlpha() == 0) {
            return;
        }

        // RENDER THE ELLIPSE WITH THE APPROPRIATE LINE STYLE
        final Bounds bo = ore.getBounds();
        final Ellipse2D.Double e2d = new Ellipse2D.Double(bo.getLeft(), bo.getTop(), bo.getWidth(), bo.getHeight());

        Stroke sPrevious = null;
        Stroke sCurrent = getCachedStroke(lia);
        if (sCurrent != null) // SOME STROKE DEFINED?
        {
            sPrevious = _g2d.getStroke();
            _g2d.setStroke(sCurrent);
        }

        _g2d.setColor(cFG);
        _g2d.draw(e2d);

        if (sPrevious != null) // RESTORE PREVIOUS STROKE
        {
            _g2d.setStroke(sPrevious);
        }
    }

    @Override
    public void fillOval(OvalRenderEvent ore) throws ChartException {
        if (iv != null) {
            iv.modifyEvent(ore);
        }

        final Fill flBackground = validateMultipleFill(ore.getBackground());

        if (isFullTransparent(flBackground)) {
            return;
        }

        final Bounds bo = ore.getBounds();
        final Ellipse2D.Double e2d = new Ellipse2D.Double(bo.getLeft(), bo.getTop(), bo.getWidth(), bo.getHeight());
        if (flBackground instanceof ColorDefinition) {
            final ColorDefinition cd = (ColorDefinition) flBackground;
            _g2d.setColor((Color) _ids.getColor(cd));
            _g2d.fill(e2d);
        } else if (flBackground instanceof Gradient) {
            final Gradient g = (Gradient) flBackground;
            final ColorDefinition cdStart = g.getStartColor();
            final ColorDefinition cdEnd = g.getEndColor();
            // boolean bCyclic = g.isCyclic();
            double dAngleInDegrees = g.getDirection();
            final double dAngleInRadians = ((-dAngleInDegrees * Math.PI) / 180.0);

            if (dAngleInDegrees < -90 || dAngleInDegrees > 90) {
                throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING,
                        "SwingRendererImpl.exception.gradient.angle", //$NON-NLS-1$
                        new Object[] { new Double(dAngleInDegrees) }, Messages.getResourceBundle(getULocale()));
            }

            Point2D.Double p2dStart, p2dEnd;
            if (dAngleInDegrees == 90) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees == -90) {
                p2dEnd = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
            } else if (dAngleInDegrees > 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop() + bo.getHeight());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getHeight() - bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else if (dAngleInDegrees < 0) {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(),
                        bo.getTop() + bo.getWidth() * Math.abs(Math.tan(dAngleInRadians)));
            } else {
                p2dStart = new Point2D.Double(bo.getLeft(), bo.getTop());
                p2dEnd = new Point2D.Double(bo.getLeft() + bo.getWidth(), bo.getTop());
            }
            _g2d.setPaint(new GradientPaint(p2dStart, (Color) _ids.getColor(cdStart), p2dEnd,
                    (Color) _ids.getColor(cdEnd)));
            _g2d.fill(e2d);
        } else if (flBackground instanceof org.eclipse.birt.chart.model.attribute.Image) {
            Area ar2 = new Area(e2d);
            if (flBackground instanceof PatternImage) {
                fillWithPatternImage(ar2, flBackground);
                return;
            }
            java.awt.Image img = createImageFromModel(flBackground);

            if (img != null) {

                final Shape shClip = _g2d.getClip();
                if (shClip != null) {
                    Area ar1 = new Area(shClip);
                    ar2.intersect(ar1);
                }
                _g2d.setClip(ar2);

                final Size szImage = _ids.getSize(img);

                int iXRepeat = (int) (Math.ceil(e2d.width / szImage.getWidth()));
                int iYRepeat = (int) (Math.ceil(e2d.height / szImage.getHeight()));
                ImageObserver io = (ImageObserver) _ids.getObserver();
                for (int i = 0; i < iXRepeat; i++) {
                    for (int j = 0; j < iYRepeat; j++) {
                        _g2d.drawImage(img, (int) (e2d.x + i * szImage.getWidth()),
                                (int) (e2d.y + j * szImage.getHeight()), io);
                    }
                }

                _g2d.setClip(shClip); // RESTORE
            }
        }
    }

    @Override
    public void drawText(TextRenderEvent tre) throws ChartException {
        String fontName = convertFont(tre.getLabel().getCaption().getFont().getName());
        if (fontName != null) {
            tre.getLabel().getCaption().getFont().setName(fontName);
        }

        if (iv != null) {
            iv.modifyEvent(tre);
        }

        if (!tre.getLabel().isVisible())
            return;

        switch (tre.getAction()) {
        case TextRenderEvent.UNDEFINED:
            throw new ChartException(ChartDeviceExtensionPlugin.ID, ChartException.RENDERING,
                    "SwingRendererImpl.exception.missing.text.render.action", //$NON-NLS-1$
                    Messages.getResourceBundle(getULocale()));

        case TextRenderEvent.RENDER_SHADOW_AT_LOCATION:
            _tr.renderShadowAtLocation(this, tre.getTextPosition(), tre.getLocation(), tre.getLabel());
            break;

        case TextRenderEvent.RENDER_TEXT_AT_LOCATION:
            _tr.renderTextAtLocation(this, tre.getTextPosition(), tre.getLocation(), tre.getLabel());
            break;

        case TextRenderEvent.RENDER_TEXT_IN_BLOCK:
            _tr.renderTextInBlock(this, tre.getBlockBounds(), tre.getBlockAlignment(), tre.getLabel());
            break;
        }
    }

    @Override
    public void applyTransformation(TransformationEvent tev) throws ChartException {
        switch (tev.getTransform()) {
        case TransformationEvent.TRANSLATE:
            _g2d.translate(tev.getTranslateX(), tev.getTranslateY());
            break;

        case TransformationEvent.ROTATE:
            _g2d.rotate((tev.getRotation() * Math.PI) / 180d);
            break;

        case TransformationEvent.SCALE:
            _g2d.scale(tev.getScale(), tev.getScale());
            break;
        }
    }

    @Override
    public void dispose() {
        if (_ids != null) {
            _ids.dispose();
            _ids = null;
        }

        if (_g2d != null) {
            _g2d.dispose();
            _g2d = null;
        }
    }

}