diff --git a/src/org/openstreetmap/josm/gui/MapView.java b/src/org/openstreetmap/josm/gui/MapView.java
index 5f4df08..d4d5e9c 100644
--- a/src/org/openstreetmap/josm/gui/MapView.java
+++ b/src/org/openstreetmap/josm/gui/MapView.java
@@ -1182,6 +1182,9 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
         }
     };
 
+    /**
+     * Destroy this map view panel. Should be called once when it is not needed any more.
+     */
     public void destroy() {
         layerManager.removeLayerChangeListener(this);
         layerManager.removeActiveLayerChangeListener(this);
@@ -1195,6 +1198,7 @@ LayerManager.LayerChangeListener, MainLayerManager.ActiveLayerChangeListener {
         synchronized (temporaryLayers) {
             temporaryLayers.clear();
         }
+        nonChangedLayersBuffer = null;
     }
 
     /**
diff --git a/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java b/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java
index 02843eb..971fe66 100644
--- a/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java
+++ b/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java
@@ -21,14 +21,30 @@ import org.openstreetmap.josm.tools.CheckParameterUtil;
  */
 public class BoxTextElement extends StyleElement {
 
+    /**
+     * MapCSS text-anchor-horizontal
+     */
     public enum HorizontalTextAlignment { LEFT, CENTER, RIGHT }
 
+    /**
+     * MapCSS text-anchor-vertical
+     */
     public enum VerticalTextAlignment { ABOVE, TOP, CENTER, BOTTOM, BELOW }
 
+    /**
+     * Something that provides us with a {@link BoxProviderResult}
+     */
     public interface BoxProvider {
+        /**
+         * Compute and get the {@link BoxProviderResult}. The temporary flag is set if the result of the computation may change in the future.
+         * @return The result of the computation.
+         */
         BoxProviderResult get();
     }
 
+    /**
+     * A box rectangle with a flag if it is temporary.
+     */
     public static class BoxProviderResult {
         private final Rectangle box;
         private final boolean temporary;
@@ -55,6 +71,9 @@ public class BoxTextElement extends StyleElement {
         }
     }
 
+    /**
+     * A {@link BoxProvider} that always returns the same non-temporary rectangle
+     */
     public static class SimpleBoxProvider implements BoxProvider {
         private final Rectangle box;
 
@@ -85,17 +104,60 @@ public class BoxTextElement extends StyleElement {
         }
     }
 
+    /**
+     * A rectangle with size 0x0
+     */
     public static final Rectangle ZERO_BOX = new Rectangle(0, 0, 0, 0);
 
+    /**
+     * The default style a simple node should use for it's text
+     */
+    public static final BoxTextElement SIMPLE_NODE_TEXT_ELEMSTYLE;
+    static {
+        MultiCascade mc = new MultiCascade();
+        Cascade c = mc.getOrCreateCascade("default");
+        c.put(TEXT, Keyword.AUTO);
+        Node n = new Node();
+        n.put("name", "dummy");
+        SIMPLE_NODE_TEXT_ELEMSTYLE = create(new Environment(n, mc, "default", null), NodeElement.SIMPLE_NODE_ELEMSTYLE.getBoxProvider());
+        if (SIMPLE_NODE_TEXT_ELEMSTYLE == null) throw new AssertionError();
+    }
+
+    /**
+     * Caches the default text color from the preferences.
+     *
+     * FIXME: the cache isn't updated if the user changes the preference during a JOSM
+     * session. There should be preference listener updating this cache.
+     */
+    private static volatile Color defaultTextColorCache;
+
+    /**
+     * The text this element should display.
+     */
     public TextLabel text;
     // Either boxProvider or box is not null. If boxProvider is different from
     // null, this means, that the box can still change in future, otherwise
     // it is fixed.
     protected BoxProvider boxProvider;
     protected Rectangle box;
+    /**
+     * The {@link HorizontalTextAlignment} for this text.
+     */
     public HorizontalTextAlignment hAlign;
+    /**
+     * The {@link VerticalTextAlignment} for this text.
+     */
     public VerticalTextAlignment vAlign;
 
+    /**
+     * Create a new {@link BoxTextElement}
+     * @param c The current cascade
+     * @param text The text to display
+     * @param boxProvider The box provider to use
+     * @param box The initial box to use.
+     * @param hAlign The {@link HorizontalTextAlignment}
+     * @param vAlign The {@link VerticalTextAlignment}
+     */
     public BoxTextElement(Cascade c, TextLabel text, BoxProvider boxProvider, Rectangle box,
             HorizontalTextAlignment hAlign, VerticalTextAlignment vAlign) {
         super(c, 5f);
@@ -109,18 +171,37 @@ public class BoxTextElement extends StyleElement {
         this.vAlign = vAlign;
     }
 
+    /**
+     * Create a new {@link BoxTextElement} with a dynamic box
+     * @param env The MapCSS environment
+     * @param boxProvider The box provider that computes the box.
+     * @return A new {@link BoxTextElement} or <code>null</code> if the creation failed.
+     */
     public static BoxTextElement create(Environment env, BoxProvider boxProvider) {
         return create(env, boxProvider, null);
     }
 
+    /**
+     * Create a new {@link BoxTextElement} with a fixed box
+     * @param env The MapCSS environment
+     * @param box The box
+     * @return A new {@link BoxTextElement} or <code>null</code> if the creation failed.
+     */
     public static BoxTextElement create(Environment env, Rectangle box) {
         return create(env, null, box);
     }
 
+    /**
+     * Create a new {@link BoxTextElement} with a boxprovider and a box.
+     * @param env The MapCSS environment
+     * @param boxProvider The box provider.
+     * @param box The box. Only considered if boxProvider is null.
+     * @return A new {@link BoxTextElement} or <code>null</code> if the creation failed.
+     */
     public static BoxTextElement create(Environment env, BoxProvider boxProvider, Rectangle box) {
         initDefaultParameters();
 
-        TextLabel text = TextLabel.create(env, DEFAULT_TEXT_COLOR, false);
+        TextLabel text = TextLabel.create(env, defaultTextColorCache, false);
         if (text == null) return null;
         // Skip any primitives that don't have text to draw. (Styles are recreated for any tag change.)
         // The concrete text to render is not cached in this object, but computed for each
@@ -163,6 +244,10 @@ public class BoxTextElement extends StyleElement {
         return new BoxTextElement(c, text, boxProvider, box, hAlign, vAlign);
     }
 
+    /**
+     * Get the box in which the content should be drawn.
+     * @return The box.
+     */
     public Rectangle getBox() {
         if (boxProvider != null) {
             BoxProviderResult result = boxProvider.get();
@@ -175,28 +260,10 @@ public class BoxTextElement extends StyleElement {
         return box;
     }
 
-    public static final BoxTextElement SIMPLE_NODE_TEXT_ELEMSTYLE;
-    static {
-        MultiCascade mc = new MultiCascade();
-        Cascade c = mc.getOrCreateCascade("default");
-        c.put(TEXT, Keyword.AUTO);
-        Node n = new Node();
-        n.put("name", "dummy");
-        SIMPLE_NODE_TEXT_ELEMSTYLE = create(new Environment(n, mc, "default", null), NodeElement.SIMPLE_NODE_ELEMSTYLE.getBoxProvider());
-        if (SIMPLE_NODE_TEXT_ELEMSTYLE == null) throw new AssertionError();
-    }
-
-    /*
-     * Caches the default text color from the preferences.
-     *
-     * FIXME: the cache isn't updated if the user changes the preference during a JOSM
-     * session. There should be preference listener updating this cache.
-     */
-    private static volatile Color DEFAULT_TEXT_COLOR;
 
     private static void initDefaultParameters() {
-        if (DEFAULT_TEXT_COLOR != null) return;
-        DEFAULT_TEXT_COLOR = PaintColors.TEXT.get();
+        if (defaultTextColorCache != null) return;
+        defaultTextColorCache = PaintColors.TEXT.get();
     }
 
     @Override
diff --git a/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java b/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java
index 350ae7f..c50b971 100644
--- a/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java
+++ b/src/org/openstreetmap/josm/gui/mappaint/styleelement/LineElement.java
@@ -20,21 +20,13 @@ import org.openstreetmap.josm.gui.mappaint.MultiCascade;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat;
 import org.openstreetmap.josm.tools.Utils;
 
+/**
+ * This is the style definition for a simple line.
+ */
 public class LineElement extends StyleElement {
-
-    public static LineElement createSimpleLineStyle(Color color, boolean isAreaEdge) {
-        MultiCascade mc = new MultiCascade();
-        Cascade c = mc.getOrCreateCascade("default");
-        c.put(WIDTH, Keyword.DEFAULT);
-        c.put(COLOR, color != null ? color : PaintColors.UNTAGGED.get());
-        c.put(OPACITY, 1f);
-        if (isAreaEdge) {
-            c.put(Z_INDEX, -3f);
-        }
-        Way w = new Way();
-        return createLine(new Environment(w, mc, "default", null));
-    }
-
+    /**
+     * The default style for any untagged way.
+     */
     public static final LineElement UNTAGGED_WAY = createSimpleLineStyle(null, false);
 
     private BasicStroke line;
@@ -72,6 +64,144 @@ public class LineElement extends StyleElement {
         this.realWidth = realWidth;
         this.wayDirectionArrows = wayDirectionArrows;
     }
+    @Override
+    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
+            boolean selected, boolean outermember, boolean member) {
+        Way w = (Way) primitive;
+        /* show direction arrows, if draw.segment.relevant_directions_only is not set,
+        the way is tagged with a direction key
+        (even if the tag is negated as in oneway=false) or the way is selected */
+        boolean showOrientation;
+        if (defaultSelectedHandling) {
+            showOrientation = !isModifier && (selected || paintSettings.isShowDirectionArrow()) && !paintSettings.isUseRealWidth();
+        } else {
+            showOrientation = wayDirectionArrows;
+        }
+        boolean showOneway = !isModifier && !selected &&
+                !paintSettings.isUseRealWidth() &&
+                paintSettings.isShowOnewayArrow() && w.hasDirectionKeys();
+        boolean onewayReversed = w.reversedDirection();
+        /* head only takes over control if the option is true,
+        the direction should be shown at all and not only because it's selected */
+        boolean showOnlyHeadArrowOnly = showOrientation && !selected && paintSettings.isShowHeadArrowOnly();
+        Node lastN;
+
+        Color myDashedColor = dashesBackground;
+        BasicStroke myLine = line, myDashLine = dashesLine;
+        if (realWidth > 0 && paintSettings.isUseRealWidth() && !showOrientation) {
+            float myWidth = (int) (100 /  (float) (painter.getCircum() / realWidth));
+            if (myWidth < line.getLineWidth()) {
+                myWidth = line.getLineWidth();
+            }
+            myLine = new BasicStroke(myWidth, line.getEndCap(), line.getLineJoin(),
+                    line.getMiterLimit(), line.getDashArray(), line.getDashPhase());
+            if (dashesLine != null) {
+                myDashLine = new BasicStroke(myWidth, dashesLine.getEndCap(), dashesLine.getLineJoin(),
+                        dashesLine.getMiterLimit(), dashesLine.getDashArray(), dashesLine.getDashPhase());
+            }
+        }
+
+        Color myColor = color;
+        if (defaultSelectedHandling && selected) {
+            myColor = paintSettings.getSelectedColor(color.getAlpha());
+        } else if (member || outermember) {
+            myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
+        } else if (w.isDisabled()) {
+            myColor = paintSettings.getInactiveColor();
+            myDashedColor = paintSettings.getInactiveColor();
+        }
+
+        painter.drawWay(w, myColor, myLine, myDashLine, myDashedColor, offset, showOrientation,
+                showOnlyHeadArrowOnly, showOneway, onewayReversed);
+
+        if (paintSettings.isShowOrderNumber() && !painter.isInactiveMode()) {
+            int orderNumber = 0;
+            lastN = null;
+            for (Node n : w.getNodes()) {
+                if (lastN != null) {
+                    orderNumber++;
+                    painter.drawOrderNumber(lastN, n, orderNumber, myColor);
+                }
+                lastN = n;
+            }
+        }
+    }
+
+    @Override
+    public boolean isProperLineStyle() {
+        return !isModifier;
+    }
+
+    public String linejoinToString(int linejoin) {
+        switch (linejoin) {
+            case BasicStroke.JOIN_BEVEL: return "bevel";
+            case BasicStroke.JOIN_ROUND: return "round";
+            case BasicStroke.JOIN_MITER: return "miter";
+            default: return null;
+        }
+    }
+
+    public String linecapToString(int linecap) {
+        switch (linecap) {
+            case BasicStroke.CAP_BUTT: return "none";
+            case BasicStroke.CAP_ROUND: return "round";
+            case BasicStroke.CAP_SQUARE: return "square";
+            default: return null;
+        }
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null || getClass() != obj.getClass())
+            return false;
+        if (!super.equals(obj))
+            return false;
+        final LineElement other = (LineElement) obj;
+        return Objects.equals(line, other.line) &&
+            Objects.equals(color, other.color) &&
+            Objects.equals(dashesLine, other.dashesLine) &&
+            Objects.equals(dashesBackground, other.dashesBackground) &&
+            offset == other.offset &&
+            realWidth == other.realWidth &&
+            wayDirectionArrows == other.wayDirectionArrows;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), line, color, dashesBackground, offset, realWidth, wayDirectionArrows, dashesLine);
+    }
+
+    @Override
+    public String toString() {
+        return "LineElemStyle{" + super.toString() + "width=" + line.getLineWidth() +
+            " realWidth=" + realWidth + " color=" + Utils.toString(color) +
+            " dashed=" + Arrays.toString(line.getDashArray()) +
+            (line.getDashPhase() == 0 ? "" : " dashesOffses=" + line.getDashPhase()) +
+            " dashedColor=" + Utils.toString(dashesBackground) +
+            " linejoin=" + linejoinToString(line.getLineJoin()) +
+            " linecap=" + linecapToString(line.getEndCap()) +
+            (offset == 0 ? "" : " offset=" + offset) +
+            '}';
+    }
+
+    /**
+     * Creates a simple line with default widt.
+     * @param color The color to use
+     * @param isAreaEdge If this is an edge for an area. Edges are drawn at lower Z-Index.
+     * @return The line style.
+     */
+    public static LineElement createSimpleLineStyle(Color color, boolean isAreaEdge) {
+        MultiCascade mc = new MultiCascade();
+        Cascade c = mc.getOrCreateCascade("default");
+        c.put(WIDTH, Keyword.DEFAULT);
+        c.put(COLOR, color != null ? color : PaintColors.UNTAGGED.get());
+        c.put(OPACITY, 1f);
+        if (isAreaEdge) {
+            c.put(Z_INDEX, -3f);
+        }
+        Way w = new Way();
+        return createLine(new Environment(w, mc, "default", null));
+    }
 
     public static LineElement createLine(Environment env) {
         return createImpl(env, LineType.NORMAL);
@@ -104,77 +234,13 @@ public class LineElement extends StyleElement {
     private static LineElement createImpl(Environment env, LineType type) {
         Cascade c = env.mc.getCascade(env.layer);
         Cascade cDef = env.mc.getCascade("default");
-        Float width;
-        switch (type) {
-            case NORMAL:
-                width = getWidth(c, WIDTH, getWidth(cDef, WIDTH, null));
-                break;
-            case CASING:
-                Float casingWidth = c.get(type.prefix + WIDTH, null, Float.class, true);
-                if (casingWidth == null) {
-                    RelativeFloat relCasingWidth = c.get(type.prefix + WIDTH, null, RelativeFloat.class, true);
-                    if (relCasingWidth != null) {
-                        casingWidth = relCasingWidth.val / 2;
-                    }
-                }
-                if (casingWidth == null)
-                    return null;
-                width = getWidth(c, WIDTH, getWidth(cDef, WIDTH, null));
-                if (width == null) {
-                    width = 0f;
-                }
-                width += 2 * casingWidth;
-                break;
-            case LEFT_CASING:
-            case RIGHT_CASING:
-                width = getWidth(c, type.prefix + WIDTH, null);
-                break;
-            default:
-                throw new AssertionError();
-        }
+        Float width = computeWidth(type, c, cDef);
         if (width == null)
             return null;
 
-        float realWidth = c.get(type.prefix + REAL_WIDTH, 0f, Float.class);
-        if (realWidth > 0 && MapPaintSettings.INSTANCE.isUseRealWidth()) {
+        float realWidth = computeRealWidth(env, type, c);
 
-            /* if we have a "width" tag, try use it */
-            String widthTag = env.osm.get("width");
-            if (widthTag == null) {
-                widthTag = env.osm.get("est_width");
-            }
-            if (widthTag != null) {
-                try {
-                    realWidth = Float.parseFloat(widthTag);
-                } catch (NumberFormatException nfe) {
-                    Main.warn(nfe);
-                }
-            }
-        }
-
-        Float offset = c.get(OFFSET, 0f, Float.class);
-        switch (type) {
-            case NORMAL:
-                break;
-            case CASING:
-                offset += c.get(type.prefix + OFFSET, 0f, Float.class);
-                break;
-            case LEFT_CASING:
-            case RIGHT_CASING:
-                Float baseWidthOnDefault = getWidth(cDef, WIDTH, null);
-                Float baseWidth = getWidth(c, WIDTH, baseWidthOnDefault);
-                if (baseWidth == null || baseWidth < 2f) {
-                    baseWidth = 2f;
-                }
-                float casingOffset = c.get(type.prefix + OFFSET, 0f, Float.class);
-                casingOffset += baseWidth / 2 + width / 2;
-                /* flip sign for the right-casing-offset */
-                if (type == LineType.RIGHT_CASING) {
-                    casingOffset *= -1f;
-                }
-                offset += casingOffset;
-                break;
-        }
+        Float offset = computeOffset(type, c, cDef, width);
 
         int alpha = 255;
         Color color = c.get(type.prefix + COLOR, null, Color.class);
@@ -272,123 +338,83 @@ public class LineElement extends StyleElement {
                 offset, realWidth, wayDirectionArrows);
     }
 
-    @Override
-    public void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, StyledMapRenderer painter,
-            boolean selected, boolean outermember, boolean member) {
-        Way w = (Way) primitive;
-        /* show direction arrows, if draw.segment.relevant_directions_only is not set,
-        the way is tagged with a direction key
-        (even if the tag is negated as in oneway=false) or the way is selected */
-        boolean showOrientation;
-        if (defaultSelectedHandling) {
-            showOrientation = !isModifier && (selected || paintSettings.isShowDirectionArrow()) && !paintSettings.isUseRealWidth();
-        } else {
-            showOrientation = wayDirectionArrows;
-        }
-        boolean showOneway = !isModifier && !selected &&
-                !paintSettings.isUseRealWidth() &&
-                paintSettings.isShowOnewayArrow() && w.hasDirectionKeys();
-        boolean onewayReversed = w.reversedDirection();
-        /* head only takes over control if the option is true,
-        the direction should be shown at all and not only because it's selected */
-        boolean showOnlyHeadArrowOnly = showOrientation && !selected && paintSettings.isShowHeadArrowOnly();
-        Node lastN;
-
-        Color myDashedColor = dashesBackground;
-        BasicStroke myLine = line, myDashLine = dashesLine;
-        if (realWidth > 0 && paintSettings.isUseRealWidth() && !showOrientation) {
-            float myWidth = (int) (100 /  (float) (painter.getCircum() / realWidth));
-            if (myWidth < line.getLineWidth()) {
-                myWidth = line.getLineWidth();
-            }
-            myLine = new BasicStroke(myWidth, line.getEndCap(), line.getLineJoin(),
-                    line.getMiterLimit(), line.getDashArray(), line.getDashPhase());
-            if (dashesLine != null) {
-                myDashLine = new BasicStroke(myWidth, dashesLine.getEndCap(), dashesLine.getLineJoin(),
-                        dashesLine.getMiterLimit(), dashesLine.getDashArray(), dashesLine.getDashPhase());
-            }
-        }
-
-        Color myColor = color;
-        if (defaultSelectedHandling && selected) {
-            myColor = paintSettings.getSelectedColor(color.getAlpha());
-        } else if (member || outermember) {
-            myColor = paintSettings.getRelationSelectedColor(color.getAlpha());
-        } else if (w.isDisabled()) {
-            myColor = paintSettings.getInactiveColor();
-            myDashedColor = paintSettings.getInactiveColor();
+    private static Float computeWidth(LineType type, Cascade c, Cascade cDef) {
+        Float width;
+        switch (type) {
+            case NORMAL:
+                width = getWidth(c, WIDTH, getWidth(cDef, WIDTH, null));
+                break;
+            case CASING:
+                Float casingWidth = c.get(type.prefix + WIDTH, null, Float.class, true);
+                if (casingWidth == null) {
+                    RelativeFloat relCasingWidth = c.get(type.prefix + WIDTH, null, RelativeFloat.class, true);
+                    if (relCasingWidth != null) {
+                        casingWidth = relCasingWidth.val / 2;
+                    }
+                }
+                if (casingWidth == null)
+                    return null;
+                width = getWidth(c, WIDTH, getWidth(cDef, WIDTH, null));
+                if (width == null) {
+                    width = 0f;
+                }
+                width += 2 * casingWidth;
+                break;
+            case LEFT_CASING:
+            case RIGHT_CASING:
+                width = getWidth(c, type.prefix + WIDTH, null);
+                break;
+            default:
+                throw new AssertionError();
         }
+        return width;
+    }
 
-        painter.drawWay(w, myColor, myLine, myDashLine, myDashedColor, offset, showOrientation,
-                showOnlyHeadArrowOnly, showOneway, onewayReversed);
+    private static float computeRealWidth(Environment env, LineType type, Cascade c) {
+        float realWidth = c.get(type.prefix + REAL_WIDTH, 0f, Float.class);
+        if (realWidth > 0 && MapPaintSettings.INSTANCE.isUseRealWidth()) {
 
-        if (paintSettings.isShowOrderNumber() && !painter.isInactiveMode()) {
-            int orderNumber = 0;
-            lastN = null;
-            for (Node n : w.getNodes()) {
-                if (lastN != null) {
-                    orderNumber++;
-                    painter.drawOrderNumber(lastN, n, orderNumber, myColor);
+            /* if we have a "width" tag, try use it */
+            String widthTag = env.osm.get("width");
+            if (widthTag == null) {
+                widthTag = env.osm.get("est_width");
+            }
+            if (widthTag != null) {
+                try {
+                    realWidth = Float.parseFloat(widthTag);
+                } catch (NumberFormatException nfe) {
+                    Main.warn(nfe);
                 }
-                lastN = n;
             }
         }
+        return realWidth;
     }
 
-    @Override
-    public boolean isProperLineStyle() {
-        return !isModifier;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (obj == null || getClass() != obj.getClass())
-            return false;
-        if (!super.equals(obj))
-            return false;
-        final LineElement other = (LineElement) obj;
-        return Objects.equals(line, other.line) &&
-            Objects.equals(color, other.color) &&
-            Objects.equals(dashesLine, other.dashesLine) &&
-            Objects.equals(dashesBackground, other.dashesBackground) &&
-            offset == other.offset &&
-            realWidth == other.realWidth &&
-            wayDirectionArrows == other.wayDirectionArrows;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(super.hashCode(), line, color, dashesBackground, offset, realWidth, wayDirectionArrows, dashesLine);
-    }
-
-    @Override
-    public String toString() {
-        return "LineElemStyle{" + super.toString() + "width=" + line.getLineWidth() +
-            " realWidth=" + realWidth + " color=" + Utils.toString(color) +
-            " dashed=" + Arrays.toString(line.getDashArray()) +
-            (line.getDashPhase() == 0 ? "" : " dashesOffses=" + line.getDashPhase()) +
-            " dashedColor=" + Utils.toString(dashesBackground) +
-            " linejoin=" + linejoinToString(line.getLineJoin()) +
-            " linecap=" + linecapToString(line.getEndCap()) +
-            (offset == 0 ? "" : " offset=" + offset) +
-            '}';
-    }
-
-    public String linejoinToString(int linejoin) {
-        switch (linejoin) {
-            case BasicStroke.JOIN_BEVEL: return "bevel";
-            case BasicStroke.JOIN_ROUND: return "round";
-            case BasicStroke.JOIN_MITER: return "miter";
-            default: return null;
+    private static Float computeOffset(LineType type, Cascade c, Cascade cDef, Float width) {
+        Float offset = c.get(OFFSET, 0f, Float.class);
+        switch (type) {
+            case NORMAL:
+                break;
+            case CASING:
+                offset += c.get(type.prefix + OFFSET, 0f, Float.class);
+                break;
+            case LEFT_CASING:
+            case RIGHT_CASING:
+                Float baseWidthOnDefault = getWidth(cDef, WIDTH, null);
+                Float baseWidth = getWidth(c, WIDTH, baseWidthOnDefault);
+                if (baseWidth == null || baseWidth < 2f) {
+                    baseWidth = 2f;
+                }
+                float casingOffset = c.get(type.prefix + OFFSET, 0f, Float.class);
+                casingOffset += baseWidth / 2 + width / 2;
+                /* flip sign for the right-casing-offset */
+                if (type == LineType.RIGHT_CASING) {
+                    casingOffset *= -1f;
+                }
+                offset += casingOffset;
+                break;
         }
+        return offset;
     }
 
-    public String linecapToString(int linecap) {
-        switch (linecap) {
-            case BasicStroke.CAP_BUTT: return "none";
-            case BasicStroke.CAP_ROUND: return "round";
-            case BasicStroke.CAP_SQUARE: return "square";
-            default: return null;
-        }
-    }
 }
