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