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