001package armyc2.c5isr.renderer;
002
003import android.graphics.Bitmap;
004import android.graphics.Bitmap.Config;
005import android.graphics.Canvas;
006import android.graphics.Paint;
007import android.graphics.Paint.FontMetrics;
008import android.graphics.Point;
009import android.graphics.PointF;
010import android.graphics.Rect;
011import android.graphics.RectF;
012import android.util.Log;
013import android.util.LruCache;
014
015import com.caverock.androidsvg.SVG;
016
017import java.util.HashMap;
018import java.util.Map;
019
020import armyc2.c5isr.renderer.utilities.Color;
021import armyc2.c5isr.renderer.utilities.DrawRules;
022import armyc2.c5isr.renderer.utilities.ErrorLogger;
023import armyc2.c5isr.renderer.utilities.ImageInfo;
024import armyc2.c5isr.renderer.utilities.MSInfo;
025import armyc2.c5isr.renderer.utilities.MSLookup;
026import armyc2.c5isr.renderer.utilities.MilStdAttributes;
027import armyc2.c5isr.renderer.utilities.Modifiers;
028import armyc2.c5isr.renderer.utilities.RectUtilities;
029import armyc2.c5isr.renderer.utilities.RendererSettings;
030import armyc2.c5isr.renderer.utilities.RendererUtilities;
031import armyc2.c5isr.renderer.utilities.SVGInfo;
032import armyc2.c5isr.renderer.utilities.SVGLookup;
033import armyc2.c5isr.renderer.utilities.SVGSymbolInfo;
034import armyc2.c5isr.renderer.utilities.SettingsChangedEvent;
035import armyc2.c5isr.renderer.utilities.SettingsChangedEventListener;
036import armyc2.c5isr.renderer.utilities.SymbolDimensionInfo;
037import armyc2.c5isr.renderer.utilities.SymbolID;
038import armyc2.c5isr.renderer.utilities.SymbolUtilities;
039
040public class SinglePointRenderer implements SettingsChangedEventListener
041{
042
043    private final String TAG = "SinglePointRenderer";
044    private static SinglePointRenderer _instance = null;
045
046    private final Object _SinglePointCacheMutex = new Object();
047    private final Object _UnitCacheMutex = new Object();
048
049    private final Object _modifierFontMutex = new Object();
050    private Paint _modifierFont = new Paint();
051    private Paint _modifierOutlineFont = new Paint();
052    private float _modifierDescent = 2;
053    private float _modifierFontHeight = 10;
054    private int _deviceDPI = 72;
055
056    //private LruCache<String, ImageInfo> _unitCache = new LruCache<String, ImageInfo>(15);
057    //private LruCache<String, ImageInfo> _tgCache = new LruCache<String, ImageInfo>(7);
058    private LruCache<String, ImageInfo> _unitCache = new LruCache<String, ImageInfo>(1024);
059    private LruCache<String, ImageInfo> _tgCache = new LruCache<String, ImageInfo>(1024);
060    private final int maxMemory = (int) (Runtime.getRuntime().maxMemory());// / 1024);
061    private int cacheSize = 5;//RendererSettings.getInstance().getCacheSize() / 2;
062    private int maxCachedEntrySize = cacheSize / 5;
063    private boolean cacheEnabled = RendererSettings.getInstance().getCacheEnabled();
064
065    private SinglePointRenderer()
066    {
067        RendererSettings.getInstance().addEventListener(this);
068        
069        //get modifier font values.
070        onSettingsChanged(new SettingsChangedEvent(SettingsChangedEvent.EventType_FontChanged));
071        //set cache
072        onSettingsChanged(new SettingsChangedEvent(SettingsChangedEvent.EventType_CacheSizeChanged));
073
074    }
075
076    public static synchronized SinglePointRenderer getInstance()
077    {
078        if (_instance == null)
079        {
080            _instance = new SinglePointRenderer();
081        }
082
083        return _instance;
084    }
085
086    /**
087     *
088     * @param symbolID
089     * @param modifiers
090     * @return
091     */
092    public ImageInfo RenderUnit(String symbolID, Map<String,String> modifiers, Map<String,String> attributes)
093    {
094        Color lineColor = SymbolUtilities.getLineColorOfAffiliation(symbolID);
095        Color fillColor = SymbolUtilities.getFillColorOfAffiliation(symbolID);
096        Color iconColor = null;
097
098        int alpha = -1;
099
100
101        //SVG values
102        String frameID = null;
103        String iconID = null;
104        String mod1ID = null;
105        String mod2ID = null;
106        SVGInfo siFrame = null;
107        SVGInfo siIcon = null;
108        SVGInfo siMod1 = null;
109        SVGInfo siMod2 = null;
110        SVG mySVG = null;
111        int top = 0;
112        int left = 0;
113        int width = 0;
114        int height = 0;
115        String svgStart = null;
116        String strSVG = null;
117        String strSVGFrame = null;
118
119
120        Rect symbolBounds = null;
121        Rect fullBounds = null;
122        Bitmap fullBMP = null;
123
124        boolean hasDisplayModifiers = false;
125        boolean hasTextModifiers = false;
126
127        int pixelSize = -1;
128        boolean keepUnitRatio = true;
129        boolean icon = false;
130        boolean noFrame = false;
131
132        int ver = SymbolID.getVersion(symbolID);
133
134        // <editor-fold defaultstate="collapsed" desc="Parse Attributes">
135        try
136        {
137
138            if (attributes.containsKey(MilStdAttributes.PixelSize))
139            {
140                pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize));
141            }
142            else
143            {
144                pixelSize = RendererSettings.getInstance().getDefaultPixelSize();
145            }
146
147            if (attributes.containsKey(MilStdAttributes.KeepUnitRatio))
148            {
149                keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio));
150            }
151
152            if (attributes.containsKey(MilStdAttributes.DrawAsIcon))
153            {
154                icon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon));
155            }
156
157            if (icon)//icon won't show modifiers or display icons
158            {
159                //TODO: symbolID modifications as necessary
160                keepUnitRatio = false;
161                hasDisplayModifiers = false;
162                hasTextModifiers = false;
163                //symbolID = symbolID.substring(0, 10) + "-----";
164            }
165            else
166            {
167                hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers);
168                hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers);
169            }
170
171            if (attributes.containsKey(MilStdAttributes.LineColor))
172            {
173                lineColor = new Color(attributes.get(MilStdAttributes.LineColor));
174            }
175            if (attributes.containsKey(MilStdAttributes.FillColor))
176            {
177                fillColor = new Color(attributes.get(MilStdAttributes.FillColor));
178            }
179            if (attributes.containsKey(MilStdAttributes.IconColor))
180            {
181                iconColor = new Color(attributes.get(MilStdAttributes.IconColor));
182            }//*/
183            if (attributes.containsKey(MilStdAttributes.Alpha))
184            {
185                alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha));
186            }
187
188        }
189        catch (Exception excModifiers)
190        {
191            ErrorLogger.LogException("SinglePointRenderer", "RenderUnit", excModifiers);
192        }
193        // </editor-fold>
194
195        try
196        {
197
198            ImageInfo ii = null;
199            String key = makeCacheKey(symbolID, lineColor.toInt(), fillColor.toInt(), String.valueOf(iconColor),pixelSize, keepUnitRatio, false);
200
201            //see if it's in the cache
202            if(_unitCache != null) {
203                ii = _unitCache.get(key);
204                //safety check in case bitmaps are getting recycled while still in the LRU cache
205                if (ii != null && ii.getImage() != null && ii.getImage().isRecycled()) {
206                    synchronized (_UnitCacheMutex) {
207                        _unitCache.remove(key);
208                        ii = null;
209                    }
210                }
211            }
212            //if not, generate symbol
213            if (ii == null)//*/
214            {
215                int version = SymbolID.getVersion(symbolID);
216                //Get SVG pieces of symbol
217                frameID = SVGLookup.getFrameID(symbolID);
218                iconID = SVGLookup.getMainIconID(symbolID);
219                mod1ID = SVGLookup.getMod1ID(symbolID);
220                mod2ID = SVGLookup.getMod2ID(symbolID);
221                siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version);
222                siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version);
223
224                if(siFrame == null)
225                {
226                    frameID = SVGLookup.getFrameID(SymbolUtilities.reconcileSymbolID(symbolID));
227                    siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version);
228                    if(siFrame == null)//still no match, get unknown frame
229                    {
230                        frameID = SVGLookup.getFrameID(SymbolID.setSymbolSet(symbolID,SymbolID.SymbolSet_Unknown));
231                        siFrame = SVGLookup.getInstance().getSVGLInfo(frameID, version);
232                    }
233                }
234
235                if(siIcon == null)
236                {
237                        if(SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_Unknown)
238                            siIcon = SVGLookup.getInstance().getSVGLInfo("00000000", version);//question mark
239                        /*else if(iconID.substring(2,8).equals("000000")==false && MSLookup.getInstance().getMSLInfo(symbolID) == null)
240                            siIcon = SVGLookup.getInstance().getSVGLInfo("98100000", version);//inverted question mark//*/
241                }
242
243                if(RendererSettings.getInstance().getScaleMainIcon())
244                    siIcon = RendererUtilities.scaleIcon(symbolID,siIcon);
245
246                siMod1 = SVGLookup.getInstance().getSVGLInfo(mod1ID, version);
247                siMod2 = SVGLookup.getInstance().getSVGLInfo(mod2ID, version);
248                top = Math.round(siFrame.getBbox().top);
249                left = Math.round(siFrame.getBbox().left);
250                width = Math.round(siFrame.getBbox().width());
251                height = Math.round(siFrame.getBbox().height());
252                if(siFrame.getBbox().bottom > 400)
253                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">";
254                else
255                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">";
256
257                //update line and fill color of frame SVG
258                if(lineColor != null || fillColor != null)
259                    strSVGFrame = RendererUtilities.setSVGFrameColors(symbolID,siFrame.getSVG(),lineColor,fillColor);
260                else
261                    strSVGFrame = siFrame.getSVG();
262
263                if(frameID.equals("octagon"))//for the 1 unit symbol that doesn't have a frame: 30 + 15000
264                {
265                    noFrame = true;
266                    strSVGFrame = strSVGFrame.replaceFirst("<g id=\"octagon\">", "<g id=\"octagon\" display=\"none\">");
267                }
268
269
270                //get SVG dimensions and target dimensions
271                symbolBounds = RectUtilities.makeRect(left,top,width,height);
272                Rect rect = new Rect(symbolBounds);
273                float ratio = -1;
274
275                if (pixelSize > 0 && keepUnitRatio == true)
276                {
277                    float heightRatio = SymbolUtilities.getUnitRatioHeight(symbolID);
278                    float widthRatio = SymbolUtilities.getUnitRatioWidth(symbolID);
279
280                    if(noFrame == true)//using octagon with display="none" as frame for a 1x1 shape
281                    {
282                        heightRatio = 1.0f;
283                        widthRatio = 1.0f;
284                    }
285
286                    if (heightRatio > widthRatio)
287                    {
288                        pixelSize = (int) ((pixelSize / 1.5f) * heightRatio);
289                    }
290                    else
291                    {
292                        pixelSize = (int) ((pixelSize / 1.5f) * widthRatio);
293                    }
294                }
295                if (pixelSize > 0)
296                {
297                    float p = pixelSize;
298                    float h = rect.height();
299                    float w = rect.width();
300
301                    ratio = Math.min((p / h), (p / w));
302
303                    symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio);
304                }
305
306                //center of octagon is the center of all unit symbols
307                Point centerOctagon = new Point(306, 396);
308                centerOctagon.offset(-left,-top);//offset for the symbol bounds x,y
309                //scale center point by same ratio as the symbol
310                centerOctagon = new Point((int)(centerOctagon.x * ratio), (int)(centerOctagon.y * ratio));
311
312                //set centerpoint of the image
313                Point centerPoint = centerOctagon;
314                Point centerCache = new Point(centerOctagon.x, centerOctagon.y);
315
316                //y offset to get centerpoint so we set back to zero when done.
317                symbolBounds.top = 0;
318
319                //Create destination BMP
320                Bitmap bmp = Bitmap.createBitmap(symbolBounds.width(), symbolBounds.height(), Config.ARGB_8888);
321                Canvas canvas = new Canvas(bmp);
322
323                //draw unit from SVG
324                StringBuilder sb = new StringBuilder();
325                sb.append(svgStart);
326
327                if(strSVGFrame != null)
328                    sb.append(strSVGFrame);
329
330                String color = "";
331                String strokeFill = "";
332                if(iconColor != null)
333                {
334                    //make sure string is properly formatted.
335                    color = RendererUtilities.colorToHexString(iconColor,false);
336                    if(color != null && color != "#000000" && color != "")
337                        strokeFill = " fill=\"" + color + "\" ";
338                    else
339                        color = null;
340                }
341                String unit = "<g" + strokeFill + ">";
342                if (siIcon != null)
343                    unit += (siIcon.getSVG());
344                if (siMod1 != null)
345                    unit += (siMod1.getSVG());
346                if (siMod2 != null)
347                    unit += (siMod2.getSVG());
348                if(iconColor != null && color != null && color != "")
349                    unit = unit.replaceAll("#000000",color);
350                sb.append(unit + "</g>");
351
352                sb.append("</svg>");
353
354                strSVG = sb.toString();
355
356                mySVG = SVG.getFromString(strSVG);
357                mySVG.setDocumentViewBox(left,top,width,height);
358                mySVG.renderToCanvas(canvas);
359
360
361                //adjust centerpoint for HQStaff if present
362                if (SymbolUtilities.isHQ(symbolID))
363                {
364                    PointF point1 = new PointF();
365                    PointF point2 = new PointF();
366                    int affiliation = SymbolID.getAffiliation(symbolID);
367                    int ss = SymbolID.getStandardIdentity(symbolID);
368                    if (affiliation == SymbolID.StandardIdentity_Affiliation_Friend
369                            || affiliation == SymbolID.StandardIdentity_Affiliation_AssumedFriend
370                            || affiliation == SymbolID.StandardIdentity_Affiliation_Neutral
371                            || ss == 15 || ss == 16)//exercise joker or faker
372                    {
373                        point1.x = (symbolBounds.left);
374                        point1.y = symbolBounds.top + (symbolBounds.height());
375                        point2.x = point1.x;
376                        point2.y = point1.y + symbolBounds.height();
377                    }
378                    else
379                    {
380                        point1.x = (symbolBounds.left + 1);
381                        point1.y = symbolBounds.top + (symbolBounds.height() / 2);
382                        point2.x = point1.x;
383                        point2.y = point1.y + symbolBounds.height();
384                    }
385                    centerPoint = new Point((int) point2.x, (int) point2.y);
386                }
387
388                ii = new ImageInfo(bmp, centerPoint, symbolBounds);
389
390                if(cacheEnabled && icon == false && bmp.getAllocationByteCount() <= maxCachedEntrySize)
391                {
392                    synchronized (_UnitCacheMutex)
393                    {
394                        if(_unitCache != null && _unitCache.get(key) == null)
395                            _unitCache.put(key, new ImageInfo(bmp, new Point(centerCache), new Rect(symbolBounds)));
396                    }
397                }
398
399                /*if(icon == false && pixelSize <= 100)
400                {
401                    _unitCache.put(key, new ImageInfo(bmp, new Point(centerCache), new Rect(symbolBounds)));
402                }//*/
403            }
404
405            ImageInfo iiNew = null;
406            SymbolDimensionInfo sdiTemp = null;
407            ////////////////////////////////////////////////////////////////////
408            //process display modifiers
409            if (hasDisplayModifiers)
410            {
411                sdiTemp = ModifierRenderer.processUnitDisplayModifiers( ii, symbolID, modifiers, hasTextModifiers, attributes);
412                iiNew = (sdiTemp instanceof ImageInfo ? (ImageInfo)sdiTemp : null);
413                sdiTemp = null;
414            }
415
416            if (iiNew != null)
417            {
418                ii = iiNew;
419            }
420            iiNew = null;
421
422            //process text modifiers
423            if (hasTextModifiers)
424            {
425                sdiTemp = ModifierRenderer.processSPTextModifiers(ii, symbolID, modifiers, attributes);
426            }
427
428            iiNew = (sdiTemp instanceof ImageInfo ? (ImageInfo)sdiTemp : null);
429            if (iiNew != null)
430            {
431                ii = iiNew;
432            }
433            iiNew = null;
434
435            if(modifiers != null)
436                ii = (ImageInfo) ModifierRenderer.processSpeedLeader(ii,symbolID,modifiers,attributes);
437
438            //cleanup///////////////////////////////////////////////////////////
439            //bmp.recycle();
440            symbolBounds = null;
441            fullBMP = null;
442            fullBounds = null;
443            mySVG = null;
444            ////////////////////////////////////////////////////////////////////
445
446            if (icon == true)
447            {
448                return ii.getSquareImageInfo();
449            }
450            else
451            {
452                return ii;
453            }
454
455        }
456        catch (Exception exc)
457        {
458            ErrorLogger.LogException("SinglePointRenderer", "RenderUnit", exc);
459        }
460        return null;
461    }
462
463    /**
464     *
465     * @param symbolID
466     * @param modifiers
467     * @return
468     */
469    @SuppressWarnings("unused")
470    public ImageInfo RenderSP(String symbolID, Map<String,String> modifiers, Map<String,String> attributes)
471    {
472        ImageInfo temp = null;
473        String basicSymbolID = null;
474
475        Color lineColor = SymbolUtilities.getDefaultLineColor(symbolID);
476        Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID);
477        int outlineWidth = RendererUtilities.calculateOutlineWidth();
478        int alpha = -1;
479
480
481        //SVG rendering variables
482        MSInfo msi = null;
483        String iconID = null;
484        SVGInfo siIcon = null;
485        String mod1ID = null;
486        SVGInfo siMod1 = null;
487        int top = 0;
488        int left = 0;
489        int width = 0;
490        int height = 0;
491        String svgStart = null;
492        String strSVG = null;
493        SVG mySVG = null;
494
495        float ratio = 0;
496
497        RectF symbolBounds = null;
498        RectF imageBounds = null;
499
500        Bitmap fullBMP = null;
501
502        boolean drawAsIcon = false;
503        int pixelSize = -1;
504        boolean keepUnitRatio = true;
505        boolean hasDisplayModifiers = false;
506        boolean hasTextModifiers = false;
507        boolean drawCustomOutline = false;
508
509        msi = MSLookup.getInstance().getMSLInfo(symbolID);
510        int ss = SymbolID.getSymbolSet(symbolID);
511        int ec = SymbolID.getEntityCode(symbolID);
512        int mod1 = 0;
513        int drawRule = 0;
514        if(msi!=null){drawRule = msi.getDrawRule();}
515        boolean hasAPFill = false;
516        if(RendererSettings.getInstance().getActionPointDefaultFill()) {
517            if (SymbolUtilities.isActionPoint(symbolID) || //action points
518                    ec/100 == 2135 || //sonobuoy
519                    ec == 180100 || ec == 180200 || ec == 180400) //ACP, CCP, PUP
520            {
521                if (SymbolID.getSymbolSet(symbolID) == SymbolID.SymbolSet_ControlMeasure) {
522                    lineColor = Color.BLACK;
523                    hasAPFill = true;
524                }
525            }
526        }
527
528        try
529        {
530            if (modifiers == null)
531            {
532                modifiers = new HashMap<>();
533            }
534
535
536            //get symbol info
537
538            msi = MSLookup.getInstance().getMSLInfo(symbolID);
539
540            if (msi == null)//if lookup fails, fix code/use unknown symbol code.
541            {
542                //TODO: change symbolID to Action Point with bad symbolID  in the T or H field
543            }
544
545            /* Fills built into SVG
546            if (SymbolUtilities.hasDefaultFill(symbolID))
547            {
548                fillColor = SymbolUtilities.getFillColorOfAffiliation(symbolID);
549            }
550            if (SymbolUtilities.isTGSPWithFill(symbolID))
551            {
552                fillID = SymbolUtilities.getTGFillSymbolCode(symbolID);
553                if (fillID != null)
554                {
555                    charFillIndex = SinglePointLookup.getInstance().getCharCodeFromSymbol(fillID, symStd);
556                }
557            }
558            else if (SymbolUtilities.isWeatherSPWithFill(symbolID))
559            {
560                charFillIndex = charFrameIndex + 1;
561                fillColor = SymbolUtilities.getFillColorOfWeather(symbolID);
562
563            }//*/
564
565            if (attributes != null)
566            {
567                if (attributes.containsKey(MilStdAttributes.KeepUnitRatio))
568                {
569                    keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio));
570                }
571
572                if (attributes.containsKey(MilStdAttributes.LineColor))
573                {
574                    lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor));
575                }
576
577                if (attributes.containsKey(MilStdAttributes.FillColor))
578                {
579                    fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor));
580                }
581
582                if (attributes.containsKey(MilStdAttributes.Alpha))
583                {
584                    alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha));
585                }
586
587                if (attributes.containsKey(MilStdAttributes.DrawAsIcon))
588                {
589                    drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon));
590                }
591
592                if (attributes.containsKey(MilStdAttributes.PixelSize))
593                {
594                    pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize));
595                }
596                else
597                {
598                    pixelSize = RendererSettings.getInstance().getDefaultPixelSize();
599                }
600                /*if(keepUnitRatio == true && msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && msi.getGeometry().equalsIgnoreCase("point"))
601                {
602                    if(msi.getDrawRule() == DrawRules.POINT1)//Action Points
603                        pixelSize = (int)Math.ceil((pixelSize/1.5f) * 2.0f);
604                    else if(SymbolID.getSymbolSet(symbolID)==SymbolID.SymbolSet_ControlMeasure &&
605                            ec/100 == 2135)//Sonobuoy
606                    {
607                        pixelSize = (int)Math.ceil((pixelSize/1.5f) * 2.0f);
608                    }
609                    else
610                        pixelSize = (int)Math.ceil((pixelSize/1.5f) * 1.2f);
611                }//*/
612
613                if(!(drawAsIcon==true || hasAPFill==true))//don't outline icons because they're not going on the map and icons with fills don't need it
614                {
615                    if (attributes.containsKey(MilStdAttributes.OutlineSymbol))
616                        drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol));
617                    else
618                        drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures();
619
620                    //Protection of Cultural Property doesn't get outlined
621                    if(ss==25 && ec >= 360000 && ec < 360400)
622                        drawCustomOutline = false;
623                }
624
625                if(SymbolUtilities.isMultiPoint(symbolID))
626                    drawCustomOutline=false;//icon previews for multipoints do not need outlines since they shouldn't be on the map
627            }
628
629            if (drawAsIcon)//icon won't show modifiers or display icons
630            {
631                keepUnitRatio = false;
632                hasDisplayModifiers = false;
633                hasTextModifiers = false;
634                drawCustomOutline = false;
635            }
636            else
637            {
638                hasDisplayModifiers = ModifierRenderer.hasDisplayModifiers(symbolID, modifiers);
639                hasTextModifiers = ModifierRenderer.hasTextModifiers(symbolID, modifiers);
640            }
641
642            //Check if we need to set 'N' to "ENY"
643            int aff = SymbolID.getAffiliation(symbolID);
644            //int ss = msi.getSymbolSet();
645            if (ss == SymbolID.SymbolSet_ControlMeasure &&
646                    (aff == SymbolID.StandardIdentity_Affiliation_Hostile_Faker ||
647                 aff == SymbolID.StandardIdentity_Affiliation_Suspect_Joker ) &&
648                    modifiers.containsKey(Modifiers.N_HOSTILE) &&
649                    drawAsIcon == false)
650            {
651                modifiers.put(Modifiers.N_HOSTILE, "ENY");
652            }
653
654        }
655        catch (Exception excModifiers)
656        {
657            ErrorLogger.LogException("SinglePointRenderer", "RenderSP", excModifiers);
658        }
659
660        try
661        {
662            ImageInfo ii = null;
663            int intFill = -1;
664            if (fillColor != null)
665            {
666                intFill = fillColor.toInt();
667            }
668
669
670            if(msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure)
671                lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined.
672
673
674
675            if (SymbolID.getSymbolSet(symbolID)==SymbolID.SymbolSet_ControlMeasure && SymbolID.getEntityCode(symbolID) == 270701)//static depiction
676            {
677                //add mine fill to image
678                mod1 = SymbolID.getModifier1(symbolID);
679                if (!(mod1 >= 13 && mod1 <= 50))
680                    symbolID = SymbolID.setModifier1(symbolID, 13);
681            }
682
683            String key = makeCacheKey(symbolID, lineColor.toInt(), intFill, pixelSize, keepUnitRatio, drawCustomOutline);
684
685            //see if it's in the cache
686            if(_tgCache != null) {
687                ii = _tgCache.get(key);
688                //safety check in case bitmaps are getting recycled while still in the LRU cache
689                if (ii != null && ii.getImage() != null && ii.getImage().isRecycled()) {
690                    synchronized (_SinglePointCacheMutex) {
691                        _tgCache.remove(key);
692                        ii = null;
693                    }
694                }
695            }
696
697            //if not, generate symbol.
698            if (ii == null)//*/
699            {
700                int version = SymbolID.getVersion(symbolID);
701                //check symbol size////////////////////////////////////////////
702                Rect rect = null;
703                iconID = SVGLookup.getMainIconID(symbolID);
704                siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version);
705                if(siIcon==null) {
706                    return null;
707                }
708                mod1ID = SVGLookup.getMod1ID(symbolID);
709                siMod1 = SVGLookup.getInstance().getSVGLInfo(mod1ID, version);
710                float borderPadding = 0;
711                if (drawCustomOutline) {
712                    borderPadding = (int)Math.ceil(outlineWidth/2f);
713                    if(borderPadding % 2 > 0)
714                        borderPadding++;
715                }
716                top = (int)Math.floor(siIcon.getBbox().top);
717                left = (int)Math.floor(siIcon.getBbox().left);
718                width = (int)Math.ceil(siIcon.getBbox().width() + (siIcon.getBbox().left - left));
719                height = (int)Math.ceil(siIcon.getBbox().height() + (siIcon.getBbox().top - top));
720                if(siIcon.getBbox().bottom > 400)
721                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">";
722                else
723                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">";
724
725                String strSVGIcon = null;
726
727                if(keepUnitRatio)
728                {
729                    double scaler = Math.max(width/(float)height, height/(float)width);
730                    if (scaler < 1.2)
731                        scaler = 1.2;
732                    if (scaler > 2)
733                        scaler = 2;
734
735                    if(!SymbolUtilities.isCBRNEvent(symbolID))
736                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * scaler);
737
738                    /*
739                    double min = Math.min(width/(float)height, height/(float)width);
740                    if (min < 0.6)//Rectangle
741                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 2.0f);
742                    else if(min < 0.85)
743                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.8f);
744                    else //more of a square
745                        pixelSize = (int) Math.ceil((pixelSize / 1.5f) * 1.2f);//*/
746                }
747
748                if(hasAPFill) //action points and a few others //Sonobuoy //ACP, CCP, PUP
749                {
750                    String apFill;
751                    if(fillColor != null)
752                        apFill = RendererUtilities.colorToHexString(fillColor,false);
753                    else
754                        apFill = RendererUtilities.colorToHexString(SymbolUtilities.getFillColorOfAffiliation(symbolID),false);
755                    siIcon = new SVGInfo(siIcon.getID(),siIcon.getBbox(), siIcon.getSVG().replaceAll("fill=\"none\"","fill=\"" + apFill + "\""));
756                }
757
758                //Set dash array depending on affiliation and status
759                siIcon = RendererUtilities.setAffiliationDashArray(symbolID, siIcon);
760
761                //Generate Affiliation Planned Circle for version 16
762                SVGSymbolInfo circle = ModifierRenderer.createPlannedCircle(siIcon.getBbox(),symbolID);
763
764                if(circle != null)
765                {
766                    //workaround for bug in androidsvg: dash-array on circle seems to start at 0 degreeds instead of 90 degrees
767                    //submitted ticket here: https://github.com/BigBadaboom/androidsvg/issues/297
768                    String rotation = " rotate(90) scale";
769                    circle = new SVGSymbolInfo(circle.getSVG().replace("scale",rotation),circle.getCenterPointF(),circle.getSymbolBoundsF(),circle.getImageBoundsF());
770
771                    symbolBounds = circle.getSymbolBoundsF();
772                    top = (int)Math.floor(circle.getImageBoundsF().top);
773                    left = (int)Math.floor(circle.getImageBoundsF().left);
774                    width = (int)Math.round(Math.ceil(circle.getImageBoundsF().width() + (circle.getImageBoundsF().left - left)));
775                    height = (int)Math.round(Math.ceil(circle.getImageBoundsF().height() + (circle.getImageBoundsF().top - top)));
776                    if(keepUnitRatio)
777                        pixelSize = (int)(pixelSize * (width / Math.max(siIcon.getBbox().width(),siIcon.getBbox().height())));
778                    String newSVG = siIcon.getSVG().substring(0,siIcon.getSVG().lastIndexOf("</g>"));
779                    newSVG += circle.getSVG() + "</g>";
780                    siIcon = new SVGInfo(siIcon.getID(),circle.getImageBoundsF(), newSVG);
781                }
782
783                //update line and fill color of frame SVG
784                if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null)) {
785                    if (drawCustomOutline) {
786                        // create outline with larger stroke-width first (if selected)
787                        strSVGIcon = RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), RendererUtilities.getIdealOutlineColor(lineColor), fillColor, true,siIcon.getBbox(),pixelSize,outlineWidth);
788                    }
789
790                    // append normal symbol SVG to be layered on top of outline
791                    strSVGIcon += RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), lineColor, fillColor);
792                }
793                else//weather symbol (don't change color of weather graphics)
794                    strSVGIcon = siIcon.getSVG();
795
796                //If symbol is Static Depiction, add internal mine graphic based on sector modifier 1
797                if(SymbolID.getEntityCode(symbolID) == 270701 && siMod1 != null)
798                {
799                    if (drawCustomOutline) {
800                        // create outline with larger stroke-width first (if selected)
801                        strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), RendererUtilities.getIdealOutlineColor(RendererUtilities.getColorFromHexString("#00A651")), RendererUtilities.getColorFromHexString("#00A651"), true,siIcon.getBbox(),pixelSize,outlineWidth);
802                    }
803                    //strSVGIcon += siMod1.getSVG();
804                    strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), lineColor, fillColor);
805                }
806
807                if (pixelSize > 0)
808                {
809                    imageBounds = RectUtilities.makeRectF(left,top,width,height);
810                    if(circle != null)
811                        symbolBounds = circle.getSymbolBoundsF();
812                    else
813                        symbolBounds = new RectF(imageBounds);
814
815                    rect = RectUtilities.makeRectFromRectF(imageBounds);
816
817                    //adjust size
818                    float p = pixelSize;
819                    float h = rect.height();
820                    float w = rect.width();
821
822                    ratio = Math.min((p / h), (p / w));
823
824                    symbolBounds = RectUtilities.makeRectF((symbolBounds.left - imageBounds.left)*ratio, (symbolBounds.top - imageBounds.top)*ratio, symbolBounds.width() * ratio, symbolBounds.height() * ratio);
825                    imageBounds = RectUtilities.makeRectF(0f, 0f, w * ratio, h * ratio);
826
827                    //make sure border padding isn't excessive.
828                    w = symbolBounds.width();
829                    h = symbolBounds.height();
830
831                    /*if(borderPadding > 0) {
832                        if (h / (h + borderPadding) > 0.10) {
833                            borderPadding = (float) (h * 0.1);
834                        } else if (w / (w + borderPadding) > 0.10) {
835                            borderPadding = (float) (w * 0.1);
836                        }
837                    }*/
838                }
839
840
841                //grow size SVG to accommodate the outline we added
842                int offset = 0;
843                if(drawCustomOutline)
844                {
845                    RectUtilities.grow(rect, (int)Math.ceil(borderPadding / ratio));
846                    offset = (int)Math.ceil(borderPadding);
847                }//*/
848
849                //Draw glyphs to bitmap
850                imageBounds = RectUtilities.makeRectF(0f, 0f, (int)(imageBounds.width() + 0.5f + (borderPadding)*2), (int)(imageBounds.height() + 0.5f + (borderPadding)*2));
851                RectUtilities.shift(symbolBounds,offset,offset);
852                Bitmap bmp = Bitmap.createBitmap(Math.round(imageBounds.width()), Math.round(imageBounds.height()), Config.ARGB_8888);
853                Canvas canvas = new Canvas(bmp);
854
855
856                if(SymbolUtilities.isActionPoint(symbolID))//smooth out action points
857                    strSVGIcon = "/n<g stroke-linejoin=\"round\" >/n" + strSVGIcon + "/n</g>";
858
859                strSVG = svgStart + strSVGIcon + "</svg>";
860                mySVG = SVG.getFromString(strSVG);
861                //mySVG.setDocumentViewBox(left,top,width,height);
862                mySVG.setDocumentViewBox(rect.left,rect.top,rect.width(),rect.height());
863                mySVG.renderToCanvas(canvas);
864
865                Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,symbolBounds);
866
867                //now that we're done building symbol and applying outlines if needed,
868                //imageBounds and symbolBounds can be considered to be the same
869                symbolBounds = new RectF(imageBounds);//circle.getSymbolBounds();
870
871                ii = new ImageInfo(bmp, new Point(Math.round(centerPoint.x),Math.round(centerPoint.y)), RectUtilities.makeRectFromRectF(symbolBounds));
872
873                if(cacheEnabled && !drawAsIcon && bmp.getAllocationByteCount() <= maxCachedEntrySize)
874                {
875                    synchronized (_SinglePointCacheMutex)
876                    {
877                        if(_tgCache.get(key) == null)
878                            _tgCache.put(key, ii);
879                    }
880                }
881                /*if (drawAsIcon == false && pixelSize <= 100)
882
883                    _tgCache.put(key, ii);
884                }//*/
885            }
886
887            //Process Modifiers
888            ImageInfo iiNew = null;
889            if (drawAsIcon == false && (hasTextModifiers || hasDisplayModifiers))
890            {
891                SymbolDimensionInfo sdiTemp = null;
892                if (SymbolUtilities.isSPWithSpecialModifierLayout(symbolID))//(SymbolUtilitiesD.isTGSPWithSpecialModifierLayout(symbolID))
893                {
894                    sdiTemp = ModifierRenderer.ProcessTGSPWithSpecialModifierLayout(ii, symbolID, modifiers, attributes, lineColor);
895                }
896                else
897                {
898                    sdiTemp = ModifierRenderer.ProcessTGSPModifiers(ii, symbolID, modifiers, attributes, lineColor);
899                }
900                iiNew = (sdiTemp instanceof ImageInfo ? (ImageInfo)sdiTemp : null);
901            }
902
903            if (iiNew != null)
904            {
905                ii = iiNew;
906            }
907
908            //cleanup
909            //bmp.recycle();
910            symbolBounds = null;
911            fullBMP = null;
912            imageBounds = null;
913            mySVG = null;
914
915
916            if (drawAsIcon)
917            {
918                return ii.getSquareImageInfo();
919            }
920            else
921            {
922                return ii;
923            }
924
925        }
926        catch (Exception exc)
927        {
928            ErrorLogger.LogException("SinglePointRenderer", "RenderSP(" + SymbolUtilities.getBasicSymbolID(symbolID) + ")", exc);
929        }
930        return null;
931    }
932
933
934    /**
935     *
936     * @param symbolID
937     * @return
938     */
939    @SuppressWarnings("unused")
940    public ImageInfo RenderModifier(String symbolID, Map<String,String> attributes)
941    {
942        ImageInfo temp = null;
943        String basicSymbolID = null;
944
945        Color lineColor = null;
946        Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID);
947
948        int outlineWidth = RendererUtilities.calculateOutlineWidth();
949
950        int alpha = -1;
951
952
953        //SVG rendering variables
954        MSInfo msi = null;
955        String iconID = null;
956        SVGInfo siIcon = null;
957        int top = 0;
958        int left = 0;
959        int width = 0;
960        int height = 0;
961        String svgStart = null;
962        String strSVG = null;
963        SVG mySVG = null;
964
965        float ratio = 0;
966
967        Rect symbolBounds = null;
968        RectF fullBounds = null;
969        Bitmap fullBMP = null;
970
971        boolean drawAsIcon = false;
972        int pixelSize = -1;
973        boolean keepUnitRatio = true;
974        boolean hasDisplayModifiers = false;
975        boolean hasTextModifiers = false;
976        int symbolOutlineWidth = RendererSettings.getInstance().getSinglePointSymbolOutlineWidth();
977        boolean drawCustomOutline = false;
978
979        try
980        {
981
982            msi = MSLookup.getInstance().getMSLInfo(symbolID);
983            if (attributes != null)
984            {
985                if (attributes.containsKey(MilStdAttributes.KeepUnitRatio))
986                {
987                    keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio));
988                }
989
990                if (attributes.containsKey(MilStdAttributes.LineColor))
991                {
992                    lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor));
993                }
994
995                if (attributes.containsKey(MilStdAttributes.FillColor))
996                {
997                    fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor));
998                }
999
1000                if (attributes.containsKey(MilStdAttributes.Alpha))
1001                {
1002                    alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha));
1003                }
1004
1005                if (attributes.containsKey(MilStdAttributes.DrawAsIcon))
1006                {
1007                    drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon));
1008                }
1009
1010                if (attributes.containsKey(MilStdAttributes.PixelSize))
1011                {
1012                    pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize));
1013                    if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure)
1014                    {
1015                        if(SymbolID.getEntityCode(symbolID)==270701)//static depiction
1016                            pixelSize = (int)(pixelSize * 0.9);//try to scale to be somewhat in line with units
1017                    }
1018                }
1019
1020                if(drawAsIcon==false)//don't outline icons because they're not going on the map
1021                {
1022                    if(attributes.containsKey(MilStdAttributes.OutlineSymbol))
1023                        drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol));
1024                    else
1025                        drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures();
1026                }
1027
1028                if(SymbolUtilities.isMultiPoint(symbolID))
1029                    drawCustomOutline=false;//icon previews for multipoints do not need outlines since they shouldn't be on the map
1030
1031                /*if (attributes.containsKey(MilStdAttributes.OutlineWidth)>=0)
1032                 symbolOutlineWidth = Integer.parseInt(attributes.get(MilStdAttributes.OutlineWidth));//*/
1033            }
1034
1035            int outlineOffset = symbolOutlineWidth;
1036            if (drawCustomOutline && outlineOffset > 2)
1037            {
1038                outlineOffset = (outlineOffset - 1) / 2;
1039            }
1040            else
1041            {
1042                outlineOffset = 0;
1043            }
1044
1045        }
1046        catch (Exception excModifiers)
1047        {
1048            ErrorLogger.LogException("SinglePointRenderer", "RenderModifier", excModifiers);
1049        }
1050
1051        try
1052        {
1053            ImageInfo ii = null;
1054            int intFill = -1;
1055            if (fillColor != null)
1056            {
1057                intFill = fillColor.toInt();
1058            }
1059
1060
1061            if(msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure)
1062                lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined.
1063
1064
1065            //if not, generate symbol
1066            if (ii == null)//*/
1067            {
1068                int version = SymbolID.getVersion(symbolID);
1069                //check symbol size////////////////////////////////////////////
1070                Rect rect = null;
1071
1072                iconID = SVGLookup.getMod1ID(symbolID);
1073                siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version);
1074                top = Math.round(siIcon.getBbox().top);
1075                left = Math.round(siIcon.getBbox().left);
1076                width = Math.round(siIcon.getBbox().width());
1077                height = Math.round(siIcon.getBbox().height());
1078                if(siIcon.getBbox().bottom > 400)
1079                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">";
1080                else
1081                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">";
1082
1083                String strSVGIcon = null;
1084                String strSVGOutline = null;
1085
1086                //update line and fill color of frame SVG
1087                if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null))
1088                {
1089                    if(drawCustomOutline)
1090                        strSVGIcon += RendererUtilities.setSVGSPCMColors(iconID,siIcon.getSVG(), RendererUtilities.getIdealOutlineColor(lineColor), fillColor,true,siIcon.getBbox(),pixelSize,outlineWidth);
1091                    strSVGIcon += RendererUtilities.setSVGSPCMColors(iconID, siIcon.getSVG(), lineColor, fillColor);
1092                }
1093                else
1094                    strSVGIcon = siIcon.getSVG();
1095
1096                if (pixelSize > 0)
1097                {
1098                    symbolBounds = RectUtilities.makeRect(left,top,width,height);
1099                    rect = new Rect(symbolBounds);
1100
1101                    //adjust size
1102                    float p = pixelSize;
1103                    float h = rect.height();
1104                    float w = rect.width();
1105
1106                    ratio = Math.min((p / h), (p / w));
1107
1108                    symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio);
1109
1110                }
1111
1112
1113                //TODO: figure out how to draw an outline and adjust the symbol bounds accordingly
1114
1115                //Draw glyphs to bitmap
1116                Bitmap bmp = Bitmap.createBitmap((symbolBounds.width()), (symbolBounds.height()), Config.ARGB_8888);
1117                Canvas canvas = new Canvas(bmp);
1118
1119                symbolBounds = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
1120
1121                strSVG = svgStart + strSVGIcon + "</svg>";
1122                mySVG = SVG.getFromString(strSVG);
1123                mySVG.setDocumentViewBox(left,top,width,height);
1124                mySVG.renderToCanvas(canvas);
1125
1126                Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,new RectF(0, 0, symbolBounds.right, symbolBounds.bottom));
1127
1128                ii = new ImageInfo(bmp, centerPoint, symbolBounds);
1129
1130
1131                /*if (drawAsIcon == false && pixelSize <= 100)
1132                {
1133                    _tgCache.put(key, ii);
1134                }//*/
1135            }
1136
1137
1138            //cleanup
1139            //bmp.recycle();
1140            symbolBounds = null;
1141            fullBMP = null;
1142            fullBounds = null;
1143            mySVG = null;
1144
1145
1146            if (drawAsIcon)
1147            {
1148                return ii.getSquareImageInfo();
1149            }
1150            else
1151            {
1152                return ii;
1153            }
1154
1155        }
1156        catch (Exception exc)
1157        {
1158            ErrorLogger.LogException("SinglePointRenderer", "RenderModifier", exc);
1159        }
1160        return null;
1161    }
1162
1163
1164
1165    /**
1166     *
1167     * @param symbolID
1168     * @param lineColor
1169     * @param fillColor
1170     * @param size
1171     * @param keepUnitRatio
1172     * @param drawOutline (only for single-point Control Measures)
1173     * @return
1174     */
1175    private static String makeCacheKey(String symbolID, int lineColor, int fillColor, int size, boolean keepUnitRatio, boolean drawOutline)
1176    {
1177        return makeCacheKey(symbolID, lineColor, fillColor, "null",size, keepUnitRatio, false);
1178    }
1179
1180    private static String makeCacheKey(String symbolID, int lineColor, int fillColor, String iconColor, int size, boolean keepUnitRatio, boolean drawOutline)
1181    {
1182        //String key = symbolID.substring(0, 20) + String.valueOf(lineColor) + String.valueOf(fillColor) + String.valueOf(size) + String.valueOf(keepUnitRatio);
1183        String key = symbolID.substring(0, 7) + symbolID.substring(10, 20) + SymbolID.getFrameShape(symbolID) + lineColor + fillColor + iconColor + size + keepUnitRatio + drawOutline;
1184        return key;
1185    }
1186
1187    public void logError(String tag, Throwable thrown)
1188    {
1189        if (tag == null || tag.equals(""))
1190        {
1191            tag = "singlePointRenderer";
1192        }
1193
1194        String message = thrown.getMessage();
1195        String stack = getStackTrace(thrown);
1196        if (message != null)
1197        {
1198            Log.e(tag, message);
1199        }
1200        if (stack != null)
1201        {
1202            Log.e(tag, stack);
1203        }
1204    }
1205
1206    public String getStackTrace(Throwable thrown)
1207    {
1208        try
1209        {
1210            if (thrown != null)
1211            {
1212                if (thrown.getStackTrace() != null)
1213                {
1214                    String eol = System.getProperty("line.separator");
1215                    StringBuilder sb = new StringBuilder();
1216                    sb.append(thrown.toString());
1217                    sb.append(eol);
1218                    for (StackTraceElement element : thrown.getStackTrace())
1219                    {
1220                        sb.append("        at ");
1221                        sb.append(element);
1222                        sb.append(eol);
1223                    }
1224                    return sb.toString();
1225                }
1226                else
1227                {
1228                    return thrown.getMessage() + "- no stack trace";
1229                }
1230            }
1231            else
1232            {
1233                return "no stack trace";
1234            }
1235        }
1236        catch (Exception exc)
1237        {
1238            Log.e("getStackTrace", exc.getMessage());
1239        }
1240        return thrown.getMessage();
1241    }//
1242
1243    /*
1244     private static String PrintList(ArrayList list)
1245     {
1246     String message = "";
1247     for(Object item : list)
1248     {
1249
1250     message += item.toString() + "\n";
1251     }
1252     return message;
1253     }//*/
1254    /*
1255     private static String PrintObjectMap(Map<String, Object> map)
1256     {
1257     Iterator<Object> itr = map.values().iterator();
1258     String message = "";
1259     String temp = null;
1260     while(itr.hasNext())
1261     {
1262     temp = String.valueOf(itr.next());
1263     if(temp != null)
1264     message += temp + "\n";
1265     }
1266     //ErrorLogger.LogMessage(message);
1267     return message;
1268     }//*/
1269    @Override
1270    public void onSettingsChanged(SettingsChangedEvent sce)
1271    {
1272
1273        if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_FontChanged))
1274        {
1275            synchronized (_modifierFontMutex)
1276            {
1277                _modifierFont = RendererSettings.getInstance().getModiferFont();
1278                _modifierOutlineFont = RendererSettings.getInstance().getModiferFont();
1279                FontMetrics fm = new FontMetrics();
1280                fm = _modifierFont.getFontMetrics();
1281                _modifierDescent = fm.descent;
1282                //_modifierFontHeight = fm.top + fm.bottom;
1283                _modifierFontHeight = fm.bottom - fm.top;
1284
1285                _modifierFont.setStrokeWidth(RendererSettings.getInstance().getTextOutlineWidth());
1286                _modifierOutlineFont.setColor(Color.white.toInt());
1287                _deviceDPI = RendererSettings.getInstance().getDeviceDPI();
1288
1289                ModifierRenderer.setModifierFont(_modifierFont, _modifierFontHeight, _modifierDescent);
1290
1291            }
1292        }
1293
1294        if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_CacheSizeChanged))
1295        {
1296
1297            int cSize = RendererSettings.getInstance().getCacheSize()/2;
1298            //adjust unit cache
1299            if(cSize != cacheSize) {
1300                cacheSize = cSize;
1301                if (cacheSize >= 5)
1302                    maxCachedEntrySize = cacheSize / 5;
1303                else
1304                    maxCachedEntrySize = 1;
1305
1306                if(cacheEnabled) //if cache enabled, update cache
1307                {
1308
1309                    synchronized (_UnitCacheMutex) {
1310                        if(_unitCache != null)
1311                            _unitCache.evictAll();
1312                        _unitCache = new LruCache<String, ImageInfo>(cSize) {
1313                            @Override
1314                            protected int sizeOf(String key, ImageInfo ii) {
1315                                return ii.getByteCount();// / 1024;
1316                            }
1317                        };
1318                    }
1319                    //adjust tg cache
1320                    synchronized (_SinglePointCacheMutex) {
1321                        if(_tgCache != null)
1322                            _tgCache.evictAll();
1323                        _tgCache = new LruCache<String, ImageInfo>(cSize) {
1324                            @Override
1325                            protected int sizeOf(String key, ImageInfo ii) {
1326                                return ii.getByteCount();// / 1024;
1327                            }
1328                        };
1329                    }
1330                }
1331            }
1332        }
1333        if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_CacheToggled))
1334        {
1335            if(cacheEnabled != RendererSettings.getInstance().getCacheEnabled())
1336            {
1337                cacheEnabled = RendererSettings.getInstance().getCacheEnabled();
1338
1339                if (cacheEnabled == false)
1340                {
1341                    synchronized (_SinglePointCacheMutex)
1342                    {
1343                        if (_tgCache != null)
1344                            _tgCache.evictAll();
1345                        _tgCache = null;
1346                    }
1347                    synchronized (_UnitCacheMutex)
1348                    {
1349                        if (_unitCache != null)
1350                            _unitCache.evictAll();
1351                        _unitCache = null;
1352                    }
1353                }
1354                else
1355                {
1356                    int cSize = RendererSettings.getInstance().getCacheSize() / 2;
1357                    synchronized (_SinglePointCacheMutex)
1358                    {
1359                        if(_tgCache != null)
1360                            _tgCache.evictAll();
1361                        _tgCache = new LruCache<String, ImageInfo>(cSize) {
1362                            @Override
1363                            protected int sizeOf(String key, ImageInfo ii) {
1364                                return ii.getByteCount();// / 1024;
1365                            }
1366                        };
1367                    }
1368                    synchronized (_UnitCacheMutex)
1369                    {
1370                        if(_unitCache != null)
1371                            _unitCache.evictAll();
1372                        _unitCache = new LruCache<String, ImageInfo>(cSize) {
1373                            @Override
1374                            protected int sizeOf(String key, ImageInfo ii) {
1375                                return ii.getByteCount();// / 1024;
1376                            }
1377                        };
1378                    }
1379                }
1380            }
1381        }
1382    }
1383}