Mapsforge themes compatibility improvements #388 #100

This commit is contained in:
Emux 2017-09-02 18:36:49 +03:00
parent b695d43fee
commit f4f8eb8d1c
20 changed files with 276 additions and 229 deletions

View File

@ -2,6 +2,7 @@
## New since 0.8.0 ## New since 0.8.0
- Mapsforge themes compatibility [#100](https://github.com/mapsforge/vtm/issues/100)
- vtm-theme-comparator module [#387](https://github.com/mapsforge/vtm/issues/387) - vtm-theme-comparator module [#387](https://github.com/mapsforge/vtm/issues/387)
- Many other minor improvements and bug fixes - Many other minor improvements and bug fixes
- [Solved issues](https://github.com/mapsforge/vtm/issues?q=is%3Aclosed+milestone%3A0.9.0) - [Solved issues](https://github.com/mapsforge/vtm/issues?q=is%3Aclosed+milestone%3A0.9.0)

View File

@ -21,8 +21,8 @@
android:id="@+id/theme_newtron" android:id="@+id/theme_newtron"
android:title="@string/theme_newtron" /> android:title="@string/theme_newtron" />
<item <item
android:id="@+id/theme_load" android:id="@+id/theme_external"
android:title="@string/theme_load" /> android:title="@string/theme_external" />
</group> </group>
<item <item

View File

@ -6,6 +6,7 @@
<string name="theme_osmagray">Osmagray</string> <string name="theme_osmagray">Osmagray</string>
<string name="theme_tubes">Tubes</string> <string name="theme_tubes">Tubes</string>
<string name="theme_newtron">NewTron</string> <string name="theme_newtron">NewTron</string>
<string name="theme_external">External theme</string>
<string name="ok">OK</string> <string name="ok">OK</string>
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="error">Error</string> <string name="error">Error</string>
@ -17,6 +18,5 @@
<string name="style_1">Show nature</string> <string name="style_1">Show nature</string>
<string name="style_2">Hide nature</string> <string name="style_2">Hide nature</string>
<string name="menu_gridlayer">Grid</string> <string name="menu_gridlayer">Grid</string>
<string name="theme_load">load theme extern</string>
</resources> </resources>

View File

@ -43,11 +43,10 @@ public final class ValidRenderTheme implements ValidFileFilter {
try { try {
ThemeFile theme = new ExternalRenderTheme(file.getAbsolutePath()); ThemeFile theme = new ExternalRenderTheme(file.getAbsolutePath());
DefaultHandler renderThemeHandler; DefaultHandler renderThemeHandler;
if(ThemeUtils.isMapsforgeTheme(new FileInputStream(file))) { if (ThemeUtils.isMapsforgeTheme(new FileInputStream(file)))
renderThemeHandler = new XmlMapsforgeThemeBuilder(theme); renderThemeHandler = new XmlMapsforgeThemeBuilder(theme);
}else{ else
renderThemeHandler = new XmlThemeBuilder(theme); renderThemeHandler = new XmlThemeBuilder(theme);
}
XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader(); XMLReader xmlReader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
xmlReader.setContentHandler(renderThemeHandler); xmlReader.setContentHandler(renderThemeHandler);
xmlReader.parse(new InputSource(theme.getRenderThemeAsStream())); xmlReader.parse(new InputSource(theme.getRenderThemeAsStream()));

View File

@ -51,6 +51,7 @@ public class MapsforgeMapActivity extends MapActivity {
private TileGridLayer mGridLayer; private TileGridLayer mGridLayer;
private DefaultMapScaleBar mMapScaleBar; private DefaultMapScaleBar mMapScaleBar;
private Menu mMenu;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -85,6 +86,7 @@ public class MapsforgeMapActivity extends MapActivity {
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.theme_menu, menu); getMenuInflater().inflate(R.menu.theme_menu, menu);
mMenu = menu;
return true; return true;
} }
@ -117,8 +119,8 @@ public class MapsforgeMapActivity extends MapActivity {
item.setChecked(true); item.setChecked(true);
return true; return true;
case R.id.theme_load: case R.id.theme_external:
startActivityForResult(new Intent(MapsforgeMapActivity.this, ThemeFilePicker.class), startActivityForResult(new Intent(this, ThemeFilePicker.class),
SELECT_THEME_FILE); SELECT_THEME_FILE);
return true; return true;
@ -181,18 +183,13 @@ public class MapsforgeMapActivity extends MapActivity {
} }
} else if (requestCode == SELECT_THEME_FILE) { } else if (requestCode == SELECT_THEME_FILE) {
if (resultCode != RESULT_OK || intent == null || intent.getStringExtra(FilePicker.SELECTED_FILE) == null) { if (resultCode != RESULT_OK || intent == null || intent.getStringExtra(FilePicker.SELECTED_FILE) == null) {
finish();
return; return;
} }
String themePath = intent.getStringExtra(FilePicker.SELECTED_FILE); String file = intent.getStringExtra(FilePicker.SELECTED_FILE);
ExternalRenderTheme externalRenderTheme = new ExternalRenderTheme(file);
ExternalRenderTheme externalRenderTheme = new ExternalRenderTheme(themePath); mMap.setTheme(externalRenderTheme);
try { mMenu.findItem(R.id.theme_external).setChecked(true);
mMap.setTheme(externalRenderTheme, true);
} catch (Exception e) {
e.printStackTrace();
}
} }
} }

View File

@ -88,6 +88,14 @@ public class AndroidCanvas implements Canvas {
canvas.drawColor(color, color == Color.TRANSPARENT ? PorterDuff.Mode.CLEAR : PorterDuff.Mode.SRC_OVER); canvas.drawColor(color, color == Color.TRANSPARENT ? PorterDuff.Mode.CLEAR : PorterDuff.Mode.SRC_OVER);
} }
@Override
public void fillRectangle(float x, float y, float width, float height, int color) {
RectF rect = new RectF(x, y, x + width, y + height);
android.graphics.Paint paint = new android.graphics.Paint();
paint.setColor(color);
canvas.drawRect(rect, paint);
}
@Override @Override
public int getHeight() { public int getHeight() {
return canvas.getHeight(); return canvas.getHeight();
@ -97,12 +105,4 @@ public class AndroidCanvas implements Canvas {
public int getWidth() { public int getWidth() {
return canvas.getWidth(); return canvas.getWidth();
} }
@Override
public void fillRectangle(int x, int y, int width, int height, int color) {
RectF rec = new RectF(x, y, x + width, y + height);
android.graphics.Paint paint = new android.graphics.Paint();
paint.setColor(color);
canvas.drawRect(rec, paint);
}
} }

View File

@ -186,6 +186,16 @@ public class AwtCanvas implements Canvas {
fillRectangle(0, 0, getWidth(), getHeight(), color); fillRectangle(0, 0, getWidth(), getHeight(), color);
} }
@Override
public void fillRectangle(float x, float y, float width, float height, int color) {
java.awt.Color awtColor = color == Color.TRANSPARENT ? TRANSPARENT : new java.awt.Color(color);
Composite originalComposite = this.canvas.getComposite();
this.canvas.setComposite(AlphaComposite.getInstance(color == Color.TRANSPARENT ? AlphaComposite.CLEAR : AlphaComposite.SRC_OVER));
this.canvas.setColor(awtColor);
this.canvas.fillRect((int) x, (int) y, (int) width, (int) height);
this.canvas.setComposite(originalComposite);
}
@Override @Override
public int getHeight() { public int getHeight() {
return this.bitmap != null ? this.bitmap.getHeight() : 0; return this.bitmap != null ? this.bitmap.getHeight() : 0;
@ -195,14 +205,4 @@ public class AwtCanvas implements Canvas {
public int getWidth() { public int getWidth() {
return this.bitmap != null ? this.bitmap.getWidth() : 0; return this.bitmap != null ? this.bitmap.getWidth() : 0;
} }
@Override
public void fillRectangle(int x, int y, int width, int height, int color) {
java.awt.Color awtColor = color == Color.TRANSPARENT ? TRANSPARENT : new java.awt.Color(color);
Composite originalComposite = this.canvas.getComposite();
this.canvas.setComposite(AlphaComposite.getInstance(color == Color.TRANSPARENT ? AlphaComposite.CLEAR : AlphaComposite.SRC_OVER));
this.canvas.setColor(awtColor);
this.canvas.fillRect(x, y, width, height);
this.canvas.setComposite(originalComposite);
}
} }

View File

@ -150,6 +150,14 @@ public class IosCanvas implements Canvas {
this.cgBitmapContext.fillRect(rect); this.cgBitmapContext.fillRect(rect);
} }
@Override
public void fillRectangle(float x, float y, float width, float height, int color) {
CGRect rect = new CGRect(x, y, width, height);
setFillColor(this.cgBitmapContext, (color));
this.cgBitmapContext.setBlendMode(CGBlendMode.Normal);
this.cgBitmapContext.fillRect(rect);
}
@Override @Override
public int getHeight() { public int getHeight() {
return this.cgBitmapContext != null ? (int) this.cgBitmapContext.getHeight() : 0; return this.cgBitmapContext != null ? (int) this.cgBitmapContext.getHeight() : 0;
@ -159,12 +167,4 @@ public class IosCanvas implements Canvas {
public int getWidth() { public int getWidth() {
return this.cgBitmapContext != null ? (int) this.cgBitmapContext.getWidth() : 0; return this.cgBitmapContext != null ? (int) this.cgBitmapContext.getWidth() : 0;
} }
@Override
public void fillRectangle(int x, int y, int width, int height, int color) {
CGRect rect = new CGRect(x, y, width, height);
setFillColor(this.cgBitmapContext, (color));
this.cgBitmapContext.setBlendMode(CGBlendMode.Normal);
this.cgBitmapContext.fillRect(rect);
}
} }

View File

@ -72,9 +72,9 @@ public class LineRenderTest extends GdxMap {
line2 = new LineStyle(Color.GREEN, 1); line2 = new LineStyle(Color.GREEN, 1);
line4 = new LineStyle(Color.LTGRAY, 3); line4 = new LineStyle(Color.LTGRAY, 3);
} else { } else {
line1 = new LineStyle(0, null, Color.fade(Color.RED, 0.5f), 4.0f, Cap.BUTT, false, 0, 0, 0, 0, 1f, false, null, true); line1 = new LineStyle(0, null, Color.fade(Color.RED, 0.5f), 4.0f, Cap.BUTT, false, 0, 0, 0, 0, 1f, false, null, true, null);
line2 = new LineStyle(0, null, Color.GREEN, 6.0f, Cap.BUTT, false, 0, 0, 0, 0, 1f, false, null, true); line2 = new LineStyle(0, null, Color.GREEN, 6.0f, Cap.BUTT, false, 0, 0, 0, 0, 1f, false, null, true, null);
line4 = new LineStyle(0, null, Color.LTGRAY, 2.0f, Cap.ROUND, false, 0, 0, 0, 0, 1f, false, null, true); line4 = new LineStyle(0, null, Color.LTGRAY, 2.0f, Cap.ROUND, false, 0, 0, 0, 0, 1f, false, null, true, null);
} }
TextureItem tex = new TextureItem(CanvasAdapter.getBitmapAsset("", "patterns/dot.png")); TextureItem tex = new TextureItem(CanvasAdapter.getBitmapAsset("", "patterns/dot.png"));
@ -90,8 +90,8 @@ public class LineRenderTest extends GdxMap {
.randomOffset(true) .randomOffset(true)
.build(); .build();
LineStyle outline = new LineStyle(0, null, Color.BLUE, 2.0f, Cap.ROUND, false, 0, 0, 0, 0, 1f, true, null, true); LineStyle outline = new LineStyle(0, null, Color.BLUE, 2.0f, Cap.ROUND, false, 0, 0, 0, 0, 1f, true, null, true, null);
LineStyle outline2 = new LineStyle(0, null, Color.RED, 2.0f, Cap.ROUND, false, 0, 0, 0, 0, 0, true, null, true); LineStyle outline2 = new LineStyle(0, null, Color.RED, 2.0f, Cap.ROUND, false, 0, 0, 0, 0, 0, true, null, true, null);
LineBucket ol = l.buckets.addLineBucket(0, outline); LineBucket ol = l.buckets.addLineBucket(0, outline);
LineBucket ol2 = l.buckets.addLineBucket(5, outline2); LineBucket ol2 = l.buckets.addLineBucket(5, outline2);

View File

@ -116,6 +116,11 @@ public class GwtCanvas implements org.oscim.backend.canvas.Canvas {
// TODO // TODO
} }
@Override
public void fillRectangle(float x, float y, float width, float height, int color) {
// TODO
}
@Override @Override
public int getHeight() { public int getHeight() {
return this.bitmap != null ? this.bitmap.getHeight() : 0; return this.bitmap != null ? this.bitmap.getHeight() : 0;
@ -125,9 +130,4 @@ public class GwtCanvas implements org.oscim.backend.canvas.Canvas {
public int getWidth() { public int getWidth() {
return this.bitmap != null ? this.bitmap.getWidth() : 0; return this.bitmap != null ? this.bitmap.getWidth() : 0;
} }
@Override
public void fillRectangle(int x, int y, int width, int height, int color) {
// TODO
}
} }

View File

@ -0,0 +1,35 @@
/*
* Copyright 2017 Longri
* Copyright 2017 devemux86
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.theme;
import java.io.InputStream;
/**
* A utility class with theme specific helper methods.
*/
public final class ThemeUtils {
/**
* Check if the given InputStream is a Mapsforge render theme.
*/
public static boolean isMapsforgeTheme(InputStream is) {
// TODO
return false;
}
private ThemeUtils() {
}
}

View File

@ -44,10 +44,9 @@ uniform float u_mode;
void void
main(){ main(){
if (u_mode >= 1.0) { if (u_mode >= 1.0) {
float step = 2.0;
float step= 2.0; if (u_mode == 2.0) { // dashed texture
if (u_mode == 3.0){// dashed texture step = 1.0;
step =1.0;
} }
vec4 c=texture2D(tex,vec2(abs(mod(v_st.s+1.0,step)),(v_st.t+1.0)*0.5)); vec4 c=texture2D(tex,vec2(abs(mod(v_st.s+1.0,step)),(v_st.t+1.0)*0.5));
float fuzz=fwidth(c.a); float fuzz=fwidth(c.a);

View File

@ -55,9 +55,9 @@ public interface Canvas {
void fillColor(int color); void fillColor(int color);
void fillRectangle(float x, float y, float width, float height, int color);
int getHeight(); int getHeight();
int getWidth(); int getWidth();
void fillRectangle(int x, int y, int width, int height, int color);
} }

View File

@ -1,6 +1,6 @@
/* /*
* Copyright 2013 Hannes Janetzek * Copyright 2013 Hannes Janetzek
* Copyright 2016 devemux86 * Copyright 2016-2017 devemux86
* Copyright 2017 Longri * Copyright 2017 Longri
* *
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
@ -359,7 +359,7 @@ public final class LineTexBucket extends LineBucket {
LineTexBucket lb = (LineTexBucket) b; LineTexBucket lb = (LineTexBucket) b;
LineStyle line = lb.line.current(); LineStyle line = lb.line.current();
gl.uniform1f(shader.uMode, line.dashTexture? 3 : line.texture != null ? 1 : 0); gl.uniform1f(shader.uMode, line.dashArray != null ? 2 : (line.texture != null ? 1 : 0));
if (line.texture != null) if (line.texture != null)
line.texture.bind(); line.texture.bind();

View File

@ -0,0 +1,23 @@
/*
* Copyright 2017 Longri
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.theme;
import org.xml.sax.SAXException;
public class SAXTerminationException extends SAXException {
public SAXTerminationException() {
super();
}
}

View File

@ -1,6 +1,6 @@
/* /*
* Copyright 2013 Hannes Janetzek * Copyright 2013 Hannes Janetzek
* Copyright 2016 devemux86 * Copyright 2016-2017 devemux86
* Copyright 2017 Longri * Copyright 2017 Longri
* *
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
@ -18,21 +18,11 @@
*/ */
package org.oscim.theme; package org.oscim.theme;
import org.oscim.backend.CanvasAdapter; import org.oscim.backend.CanvasAdapter;
import org.oscim.theme.IRenderTheme.ThemeException; import org.oscim.theme.IRenderTheme.ThemeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import java.io.IOException;
import javax.xml.parsers.ParserConfigurationException;
public class ThemeLoader { public class ThemeLoader {
private static final Logger log = LoggerFactory.getLogger(ThemeLoader.class);
public static boolean USE_ATLAS; public static boolean USE_ATLAS;
public static boolean POT_TEXTURES; public static boolean POT_TEXTURES;
@ -56,21 +46,12 @@ public class ThemeLoader {
return load(theme, null); return load(theme, null);
} }
public static IRenderTheme load(ThemeFile theme, ThemeCallback themeCallback) throws ThemeException { public static IRenderTheme load(ThemeFile theme, ThemeCallback themeCallback) throws ThemeException {
IRenderTheme t = null; IRenderTheme t;
if (ThemeUtils.isMapsforgeTheme(theme.getRenderThemeAsStream()))
try { t = USE_ATLAS ? XmlMapsforgeAtlasThemeBuilder.read(theme, themeCallback) : XmlMapsforgeThemeBuilder.read(theme, themeCallback);
if(ThemeUtils.isMapsforgeTheme(theme.getRenderThemeAsStream())){ else
t = USE_ATLAS ? XmlMapsforgeAtlasThemeBuilder.read(theme, themeCallback) : XmlMapsforgeThemeBuilder.read(theme, themeCallback); t = USE_ATLAS ? XmlAtlasThemeBuilder.read(theme, themeCallback) : XmlThemeBuilder.read(theme, themeCallback);
}else{
t = USE_ATLAS ? XmlAtlasThemeBuilder.read(theme, themeCallback) : XmlThemeBuilder.read(theme, themeCallback);
}
} catch (IOException | ParserConfigurationException | SAXException e) {
e.printStackTrace();
}
if (t != null) if (t != null)
t.scaleTextSize(CanvasAdapter.textScale + (CanvasAdapter.dpi / CanvasAdapter.DEFAULT_DPI - 1)); t.scaleTextSize(CanvasAdapter.textScale + (CanvasAdapter.dpi / CanvasAdapter.DEFAULT_DPI - 1));
return t; return t;

View File

@ -1,7 +1,6 @@
/* /*
* Copyright 2017 Longri * Copyright 2017 Longri
* * Copyright 2017 devemux86
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
* *
* This program is free software: you can redistribute it and/or modify it under the * This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software * terms of the GNU Lesser General Public License as published by the Free Software
@ -16,63 +15,59 @@
*/ */
package org.oscim.theme; package org.oscim.theme;
import org.oscim.utils.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes; import org.xml.sax.Attributes;
import org.xml.sax.InputSource; import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler; import org.xml.sax.helpers.DefaultHandler;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory; import javax.xml.parsers.SAXParserFactory;
/** /**
* Created by Longri on 30.08.2017. * A utility class with theme specific helper methods.
*/ */
public final class ThemeUtils {
public class ThemeUtils { private static final Logger log = LoggerFactory.getLogger(ThemeUtils.class);
public static class SAXTerminatorException extends SAXException {
public SAXTerminatorException() {
super();
}
}
/** /**
* Return true, if the given InputStream a Mapsforge render theme! * Check if the given InputStream is a Mapsforge render theme.
*
* @param stream
* @return TRUE or FALSE
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/ */
public static boolean isMapsforgeTheme(InputStream stream) throws IOException, SAXException, ParserConfigurationException { public static boolean isMapsforgeTheme(InputStream is) {
final AtomicBoolean isMapsforgeTheme = new AtomicBoolean(false);
SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true);
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
xmlReader.setContentHandler(new DefaultHandler() {
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (localName.equals("rendertheme")) {
isMapsforgeTheme.set(uri.equals("http://mapsforge.org/renderTheme"));
//we have all info's, break parsing
throw new SAXTerminatorException();
}
}
});
try { try {
xmlReader.parse(new InputSource(stream)); final AtomicBoolean isMapsforgeTheme = new AtomicBoolean(false);
} catch (SAXTerminatorException e) { SAXParserFactory factory = SAXParserFactory.newInstance();
// do nothing factory.setNamespaceAware(true);
XMLReader xmlReader = factory.newSAXParser().getXMLReader();
xmlReader.setContentHandler(new DefaultHandler() {
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (localName.equals("rendertheme")) {
isMapsforgeTheme.set(uri.equals("http://mapsforge.org/renderTheme"));
// We have all info, break parsing
throw new SAXTerminationException();
}
}
});
try {
xmlReader.parse(new InputSource(is));
} catch (SAXTerminationException e) {
// Do nothing
}
return isMapsforgeTheme.get();
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
} finally {
IOUtils.closeQuietly(is);
} }
stream.close();
return isMapsforgeTheme.get();
} }
private ThemeUtils() {
}
} }

View File

@ -25,7 +25,6 @@ import org.oscim.backend.XMLReaderAdapter;
import org.oscim.backend.canvas.Bitmap; import org.oscim.backend.canvas.Bitmap;
import org.oscim.backend.canvas.Canvas; import org.oscim.backend.canvas.Canvas;
import org.oscim.backend.canvas.Color; import org.oscim.backend.canvas.Color;
import org.oscim.backend.canvas.Paint;
import org.oscim.backend.canvas.Paint.Cap; import org.oscim.backend.canvas.Paint.Cap;
import org.oscim.backend.canvas.Paint.FontFamily; import org.oscim.backend.canvas.Paint.FontFamily;
import org.oscim.backend.canvas.Paint.FontStyle; import org.oscim.backend.canvas.Paint.FontStyle;
@ -65,7 +64,6 @@ import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Set; import java.util.Set;
import java.util.Stack; import java.util.Stack;
import java.util.regex.Pattern;
import static java.lang.Boolean.parseBoolean; import static java.lang.Boolean.parseBoolean;
import static java.lang.Float.parseFloat; import static java.lang.Float.parseFloat;
@ -74,11 +72,7 @@ import static java.lang.Integer.parseInt;
public class XmlMapsforgeThemeBuilder extends DefaultHandler { public class XmlMapsforgeThemeBuilder extends DefaultHandler {
private static final Logger log = LoggerFactory.getLogger(XmlMapsforgeThemeBuilder.class); private static final Logger log = LoggerFactory.getLogger(XmlMapsforgeThemeBuilder.class);
private static final int RENDER_THEME_VERSION = 4; private static final int RENDER_THEME_VERSION = 6;
private static final Pattern SPLIT_PATTERN = Pattern.compile(",");
private static final float REPEAT_GAP_DEFAULT = 200f;
private static final float REPEAT_START_DEFAULT = 30f;
private enum Element { private enum Element {
RENDER_THEME, RENDERING_INSTRUCTION, RULE, STYLE, ATLAS, RENDERING_STYLE RENDER_THEME, RENDERING_INSTRUCTION, RULE, STYLE, ATLAS, RENDERING_STYLE
@ -93,6 +87,9 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
private static final String OUTLINE_STYLE = "O"; private static final String OUTLINE_STYLE = "O";
private static final String AREA_STYLE = "A"; private static final String AREA_STYLE = "A";
private static final float REPEAT_GAP_DEFAULT = 200f;
private static final float REPEAT_START_DEFAULT = 30f;
/** /**
* @param theme an input theme containing valid render theme XML data. * @param theme an input theme containing valid render theme XML data.
* @return a new RenderTheme which is created by parsing the XML data from the input theme. * @return a new RenderTheme which is created by parsing the XML data from the input theme.
@ -139,7 +136,7 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
private final Stack<RuleBuilder> mRuleStack = new Stack<>(); private final Stack<RuleBuilder> mRuleStack = new Stack<>();
private final HashMap<String, RenderStyle> mStyles = new HashMap<>(10); private final HashMap<String, RenderStyle> mStyles = new HashMap<>(10);
private final HashMap<String, TextBuilder<?>> mTextStyles = new HashMap<>(10); private final HashMap<String, TextStyle.TextBuilder<?>> mTextStyles = new HashMap<>(10);
private final AreaBuilder<?> mAreaBuilder = AreaStyle.builder(); private final AreaBuilder<?> mAreaBuilder = AreaStyle.builder();
private final CircleBuilder<?> mCircleBuilder = CircleStyle.builder(); private final CircleBuilder<?> mCircleBuilder = CircleStyle.builder();
@ -415,7 +412,7 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
if ("when-matched".equals(value)) if ("when-matched".equals(value))
selector |= Selector.WHEN_MATCHED; selector |= Selector.WHEN_MATCHED;
} else { } else {
XmlMapsforgeThemeBuilder.logUnknownAttribute(localName, name, value, i); logUnknownAttribute(localName, name, value, i);
} }
} }
@ -424,8 +421,8 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
else if (closed == Closed.NO) else if (closed == Closed.NO)
element = Rule.Element.LINE; element = Rule.Element.LINE;
XmlMapsforgeThemeBuilder.validateNonNegative("zoom-min", zoomMin); validateNonNegative("zoom-min", zoomMin);
XmlMapsforgeThemeBuilder.validateNonNegative("zoom-max", zoomMax); validateNonNegative("zoom-max", zoomMax);
if (zoomMin > zoomMax) if (zoomMin > zoomMax)
throw new ThemeException("zoom-min must be less or equal zoom-max: " + zoomMin); throw new ThemeException("zoom-min must be less or equal zoom-max: " + zoomMin);
@ -449,7 +446,7 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
return texture; return texture;
} }
private void handleLineElement(String localName, Attributes attributes, boolean isStyle, boolean symbolLine) private void handleLineElement(String localName, Attributes attributes, boolean isStyle, boolean hasSymbol)
throws SAXException { throws SAXException {
String use = attributes.getValue("use"); String use = attributes.getValue("use");
@ -463,7 +460,7 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
} }
} }
LineStyle line = createLine(style, localName, attributes, mLevels++, false, symbolLine); LineStyle line = createLine(style, localName, attributes, mLevels++, false, hasSymbol);
if (isStyle) { if (isStyle) {
mStyles.put(LINE_STYLE + line.style, line); mStyles.put(LINE_STYLE + line.style, line);
@ -489,7 +486,7 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
* @return a new Line with the given rendering attributes. * @return a new Line with the given rendering attributes.
*/ */
private LineStyle createLine(LineStyle line, String elementName, Attributes attributes, private LineStyle createLine(LineStyle line, String elementName, Attributes attributes,
int level, boolean isOutline, boolean symbolLine) { int level, boolean isOutline, boolean hasSymbol) {
LineBuilder<?> b = mLineBuilder.set(line); LineBuilder<?> b = mLineBuilder.set(line);
b.isOutline(isOutline); b.isOutline(isOutline);
b.level(level); b.level(level);
@ -556,10 +553,13 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
else if ("style".equals(name)) else if ("style".equals(name))
; // ignore ; // ignore
else if ("dasharray".equals(name)) else if ("stroke-dasharray".equals(name)) {
; // TBD b.dashArray = parseFloatArray(value);
for (int j = 0; j < b.dashArray.length; ++j) {
b.dashArray[j] = b.dashArray[j] * mScale;
}
else if ("symbol-width".equals(name)) } else if ("symbol-width".equals(name))
b.symbolWidth = (int) (Integer.parseInt(value) * mScale); b.symbolWidth = (int) (Integer.parseInt(value) * mScale);
else if ("symbol-height".equals(name)) else if ("symbol-height".equals(name))
@ -568,31 +568,22 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("stroke-dasharray".equals(name)) { else if ("symbol-scaling".equals(name))
b.strokeDasharray = parseFloatArray(value); ; // no-op
for (int j = 0; j < b.strokeDasharray.length; ++j) {
b.strokeDasharray[j] = b.strokeDasharray[j] * mScale; else
}
} else if ("dy".equals(name)) {
// NB: minus..
//TODO b.dy = -Float.parseFloat(value) * mScale;
} else if ("align-center".equals(name)) {
//TODO handle align-center
} else if ("repeat".equals(name)) {
//TODO handle repeat
} else if ("display".equals(name)) {
//TODO handle display
} else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
if (b.dashArray != null) {
if (b.strokeDasharray != null) {//create a dashed texture // Create a dashed texture
int bmpWidth = 0; int bmpWidth = 0;
int bmpHeight = (int) (b.strokeWidth); int bmpHeight = (int) (b.strokeWidth);
if (bmpHeight < 1) bmpHeight = 2; if (bmpHeight < 1)
for (float f : b.strokeDasharray) { bmpHeight = 2;
if (f < 1) f = 1; for (float f : b.dashArray) {
if (f < 1)
f = 1;
bmpWidth += f; bmpWidth += f;
} }
@ -603,9 +594,10 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
boolean bw = false; boolean bw = false;
int x = 0; int x = 0;
for (float f : b.strokeDasharray) { for (float f : b.dashArray) {
if (f < 1) f = 1; if (f < 1)
canvas.fillRectangle(x * factor, 0, (int) f * factor, bmpHeight * factor, (bw ? Color.TRANSPARENT : Color.WHITE)); f = 1;
canvas.fillRectangle(x * factor, 0, f * factor, bmpHeight * factor, (bw ? Color.TRANSPARENT : Color.WHITE));
x += f; x += f;
bw = !bw; bw = !bw;
} }
@ -619,13 +611,12 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
b.stippleColor = b.fillColor; b.stippleColor = b.fillColor;
b.fillColor = Color.TRANSPARENT; b.fillColor = Color.TRANSPARENT;
b.strokeColor = Color.TRANSPARENT; b.strokeColor = Color.TRANSPARENT;
} else { } else {
b.texture = Utils.loadTexture(mTheme.getRelativePathPrefix(), src, b.symbolWidth, b.symbolHeight, b.symbolPercent); b.texture = Utils.loadTexture(mTheme.getRelativePathPrefix(), src, b.symbolWidth, b.symbolHeight, b.symbolPercent);
if (symbolLine) {
// we have no way to set a repeatGap for the renderer, if (hasSymbol) {
// so we create a texture that already contains this repeatGap. // We have no way to set a repeat gap for the renderer,
// so we create a texture that already contains this repeat gap.
float repeatGap = REPEAT_GAP_DEFAULT * mScale; float repeatGap = REPEAT_GAP_DEFAULT * mScale;
float repeatStart = REPEAT_START_DEFAULT * mScale; float repeatStart = REPEAT_START_DEFAULT * mScale;
int width = (int) (b.texture.width + repeatGap); int width = (int) (b.texture.width + repeatGap);
@ -636,13 +627,13 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
canvas.drawBitmap(b.texture.bitmap, repeatStart, 0); canvas.drawBitmap(b.texture.bitmap, repeatStart, 0);
b.texture = new TextureItem(bmp); b.texture = new TextureItem(bmp);
// we must set stipple values // We must set stipple values
// The multipliers are determined empirically to // The multipliers are determined empirically to
// correspond to the representation at Mapsforge! // correspond to the representation at Mapsforge.
b.stipple = b.texture.width * 3; b.stipple = b.texture.width * 3;
b.strokeWidth *= 2 * mScale; b.strokeWidth *= 2 * mScale;
// use texture color // Use texture color
b.stippleColor = Color.WHITE; b.stippleColor = Color.WHITE;
b.fillColor = Color.TRANSPARENT; b.fillColor = Color.TRANSPARENT;
b.strokeColor = Color.TRANSPARENT; b.strokeColor = Color.TRANSPARENT;
@ -736,9 +727,10 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("symbol-scaling".equals(name)) { else if ("symbol-scaling".equals(name))
// no-op ; // no-op
} else
else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
@ -781,7 +773,7 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
if ("img".equals(name)) { if ("img".equals(name)) {
img = value; img = value;
} else { } else {
XmlMapsforgeThemeBuilder.logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
} }
validateExists("img", img, elementName); validateExists("img", img, elementName);
@ -813,7 +805,7 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
Integer.parseInt(pos[3])); Integer.parseInt(pos[3]));
} }
} else { } else {
XmlMapsforgeThemeBuilder.logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
} }
validateExists("id", regionName, elementName); validateExists("id", regionName, elementName);
@ -899,18 +891,15 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
else if ("base-text-scale".equals(name)) else if ("base-text-scale".equals(name))
baseTextScale = Float.parseFloat(value); baseTextScale = Float.parseFloat(value);
else if ("map-background-outside".equals(name)) { else
//TODO handle map-background-outside logUnknownAttribute(elementName, name, value, i);
} else
XmlMapsforgeThemeBuilder.logUnknownAttribute(elementName, name, value, i);
} }
validateExists("version", version, elementName); validateExists("version", version, elementName);
if (version != RENDER_THEME_VERSION) if (version > RENDER_THEME_VERSION)
throw new ThemeException("invalid render theme version:" throw new ThemeException("invalid render theme version:" + version);
+ version);
validateNonNegative("base-stroke-width", baseStrokeWidth); validateNonNegative("base-stroke-width", baseStrokeWidth);
validateNonNegative("base-text-scale", baseTextScale); validateNonNegative("base-text-scale", baseTextScale);
@ -1018,12 +1007,17 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("display".equals(name)) { else if ("symbol-scaling".equals(name))
//TODO Handle display attribute NEVER, ALWAYS, IFSPACE; ; // no-op
} else if ("symbol-id".equals(name)) {
//TODO Handle symbol-id; else if ("position".equals(name)) {
} else if ("position".equals(name)) { // Until implement position..
//TODO Handle position: AUTO, CENTER, BELOW, BELOW_LEFT, BELOW_RIGHT, ABOVE, ABOVE_LEFT, ABOVE_RIGHT, LEFT, RIGHT if (b.dy == 0) {
value = "below".equals(value) ? "-20" : "20";
// NB: minus..
b.dy = -Float.parseFloat(value) * mScale;
}
} else } else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
@ -1115,15 +1109,10 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("symbol-scaling".equals(name)) { else if ("symbol-scaling".equals(name))
// no-op ; // no-op
} else if ("display".equals(name)) {
//TODO Handle display attribute NEVER, ALWAYS, IFSPACE; else
} else if ("id".equals(name)) {
//TODO Handle 'id'
} else if ("priority".equals(name)) {
//TODO Handle 'priority'
} else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
@ -1203,6 +1192,15 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
return mCategories == null || rule.cat == null || mCategories.contains(rule.cat); return mCategories == null || rule.cat == null || mCategories.contains(rule.cat);
} }
private static float[] parseFloatArray(String dashString) {
String[] dashEntries = dashString.split(",");
float[] dashIntervals = new float[dashEntries.length];
for (int i = 0; i < dashEntries.length; ++i) {
dashIntervals[i] = Float.parseFloat(dashEntries[i]);
}
return dashIntervals;
}
private static void validateNonNegative(String name, float value) { private static void validateNonNegative(String name, float value) {
if (value < 0) if (value < 0)
throw new ThemeException(name + " must not be negative: " throw new ThemeException(name + " must not be negative: "
@ -1214,13 +1212,4 @@ public class XmlMapsforgeThemeBuilder extends DefaultHandler {
throw new ThemeException("missing attribute " + name throw new ThemeException("missing attribute " + name
+ " for element: " + elementName); + " for element: " + elementName);
} }
private static float[] parseFloatArray(String dashString) {
String[] dashEntries = SPLIT_PATTERN.split(dashString);
float[] dashIntervals = new float[dashEntries.length];
for (int i = 0; i < dashEntries.length; ++i) {
dashIntervals[i] = Float.parseFloat(dashEntries[i]);
}
return dashIntervals;
}
} }

View File

@ -405,7 +405,7 @@ public class XmlThemeBuilder extends DefaultHandler {
if ("when-matched".equals(value)) if ("when-matched".equals(value))
selector |= Selector.WHEN_MATCHED; selector |= Selector.WHEN_MATCHED;
} else { } else {
XmlThemeBuilder.logUnknownAttribute(localName, name, value, i); logUnknownAttribute(localName, name, value, i);
} }
} }
@ -414,8 +414,8 @@ public class XmlThemeBuilder extends DefaultHandler {
else if (closed == Closed.NO) else if (closed == Closed.NO)
element = Rule.Element.LINE; element = Rule.Element.LINE;
XmlThemeBuilder.validateNonNegative("zoom-min", zoomMin); validateNonNegative("zoom-min", zoomMin);
XmlThemeBuilder.validateNonNegative("zoom-max", zoomMax); validateNonNegative("zoom-max", zoomMax);
if (zoomMin > zoomMax) if (zoomMin > zoomMax)
throw new ThemeException("zoom-min must be less or equal zoom-max: " + zoomMin); throw new ThemeException("zoom-min must be less or equal zoom-max: " + zoomMin);
@ -462,9 +462,12 @@ public class XmlThemeBuilder extends DefaultHandler {
mCurrentRule.addStyle(line); mCurrentRule.addStyle(line);
/* Note 'outline' will not be inherited, it's just a /* Note 'outline' will not be inherited, it's just a
* shortcut to add the outline RenderInstruction. */ * shortcut to add the outline RenderInstruction. */
LineStyle outline = createOutline(attributes.getValue("outline"), attributes); String outlineValue = attributes.getValue("outline");
if (outline != null) if (outlineValue != null) {
mCurrentRule.addStyle(outline); LineStyle outline = createOutline(outlineValue, attributes);
if (outline != null)
mCurrentRule.addStyle(outline);
}
} }
} }
} }
@ -555,6 +558,9 @@ public class XmlThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("symbol-scaling".equals(name))
; // no-op
else else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
@ -648,6 +654,9 @@ public class XmlThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("symbol-scaling".equals(name))
; // no-op
else else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
@ -691,7 +700,7 @@ public class XmlThemeBuilder extends DefaultHandler {
if ("img".equals(name)) { if ("img".equals(name)) {
img = value; img = value;
} else { } else {
XmlThemeBuilder.logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
} }
validateExists("img", img, elementName); validateExists("img", img, elementName);
@ -723,7 +732,7 @@ public class XmlThemeBuilder extends DefaultHandler {
Integer.parseInt(pos[3])); Integer.parseInt(pos[3]));
} }
} else { } else {
XmlThemeBuilder.logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
} }
validateExists("id", regionName, elementName); validateExists("id", regionName, elementName);
@ -810,15 +819,14 @@ public class XmlThemeBuilder extends DefaultHandler {
baseTextScale = Float.parseFloat(value); baseTextScale = Float.parseFloat(value);
else else
XmlThemeBuilder.logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
validateExists("version", version, elementName); validateExists("version", version, elementName);
if (version != RENDER_THEME_VERSION) if (version > RENDER_THEME_VERSION)
throw new ThemeException("invalid render theme version:" throw new ThemeException("invalid render theme version:" + version);
+ version);
validateNonNegative("base-stroke-width", baseStrokeWidth); validateNonNegative("base-stroke-width", baseStrokeWidth);
validateNonNegative("base-text-scale", baseTextScale); validateNonNegative("base-text-scale", baseTextScale);
@ -926,6 +934,9 @@ public class XmlThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("symbol-scaling".equals(name))
; // no-op
else else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }
@ -1017,6 +1028,9 @@ public class XmlThemeBuilder extends DefaultHandler {
else if ("symbol-percent".equals(name)) else if ("symbol-percent".equals(name))
b.symbolPercent = Integer.parseInt(value); b.symbolPercent = Integer.parseInt(value);
else if ("symbol-scaling".equals(name))
; // no-op
else else
logUnknownAttribute(elementName, name, value, i); logUnknownAttribute(elementName, name, value, i);
} }

View File

@ -48,25 +48,26 @@ public final class LineStyle extends RenderStyle<LineStyle> {
public final int symbolWidth; public final int symbolWidth;
public final int symbolHeight; public final int symbolHeight;
public final int symbolPercent; public final int symbolPercent;
public boolean dashTexture;
public final float[] dashArray;
public LineStyle(int stroke, float width) { public LineStyle(int stroke, float width) {
this(0, "", stroke, width, Cap.BUTT, true, 0, 0, 0, -1, 0, false, null, true); this(0, "", stroke, width, Cap.BUTT, true, 0, 0, 0, -1, 0, false, null, true, null);
} }
public LineStyle(int level, int stroke, float width) { public LineStyle(int level, int stroke, float width) {
this(level, "", stroke, width, Cap.BUTT, true, 0, 0, 0, -1, 0, false, null, true); this(level, "", stroke, width, Cap.BUTT, true, 0, 0, 0, -1, 0, false, null, true, null);
} }
public LineStyle(int stroke, float width, Cap cap) { public LineStyle(int stroke, float width, Cap cap) {
this(0, "", stroke, width, cap, true, 0, 0, 0, -1, 0, false, null, true); this(0, "", stroke, width, cap, true, 0, 0, 0, -1, 0, false, null, true, null);
} }
public LineStyle(int level, String style, int color, float width, public LineStyle(int level, String style, int color, float width,
Cap cap, boolean fixed, Cap cap, boolean fixed,
int stipple, int stippleColor, float stippleWidth, int stipple, int stippleColor, float stippleWidth,
int fadeScale, float blur, boolean isOutline, TextureItem texture, int fadeScale, float blur, boolean isOutline, TextureItem texture,
boolean randomOffset) { boolean randomOffset, float[] dashArray) {
this.level = level; this.level = level;
this.style = style; this.style = style;
@ -91,6 +92,8 @@ public final class LineStyle extends RenderStyle<LineStyle> {
this.symbolWidth = 0; this.symbolWidth = 0;
this.symbolHeight = 0; this.symbolHeight = 0;
this.symbolPercent = 100; this.symbolPercent = 100;
this.dashArray = dashArray;
} }
private LineStyle(LineBuilder<?> b) { private LineStyle(LineBuilder<?> b) {
@ -114,7 +117,8 @@ public final class LineStyle extends RenderStyle<LineStyle> {
this.symbolWidth = b.symbolWidth; this.symbolWidth = b.symbolWidth;
this.symbolHeight = b.symbolHeight; this.symbolHeight = b.symbolHeight;
this.symbolPercent = b.symbolPercent; this.symbolPercent = b.symbolPercent;
this.dashTexture = b.strokeDasharray != null;
this.dashArray = b.dashArray;
} }
@Override @Override
@ -146,7 +150,8 @@ public final class LineStyle extends RenderStyle<LineStyle> {
public int symbolWidth; public int symbolWidth;
public int symbolHeight; public int symbolHeight;
public int symbolPercent; public int symbolPercent;
public float[] strokeDasharray;
public float[] dashArray;
public LineBuilder() { public LineBuilder() {
} }
@ -176,6 +181,8 @@ public final class LineStyle extends RenderStyle<LineStyle> {
this.symbolHeight = line.symbolHeight; this.symbolHeight = line.symbolHeight;
this.symbolPercent = line.symbolPercent; this.symbolPercent = line.symbolPercent;
this.dashArray = line.dashArray;
return self(); return self();
} }
@ -254,6 +261,11 @@ public final class LineStyle extends RenderStyle<LineStyle> {
return self(); return self();
} }
public T dashArray(float[] dashArray) {
this.dashArray = dashArray;
return self();
}
public T reset() { public T reset() {
level = -1; level = -1;
style = null; style = null;
@ -277,7 +289,9 @@ public final class LineStyle extends RenderStyle<LineStyle> {
symbolWidth = 0; symbolWidth = 0;
symbolHeight = 0; symbolHeight = 0;
symbolPercent = 100; symbolPercent = 100;
strokeDasharray = null;
dashArray = null;
return self(); return self();
} }