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                //Set dash array depending on affiliation and status
748                siIcon = RendererUtilities.setAffiliationDashArray(symbolID, siIcon);
749
750                //update line and fill color of frame SVG
751                if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null)) {
752                    if (drawCustomOutline) {
753                        // create outline with larger stroke-width first (if selected)
754                        strSVGIcon = RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), RendererUtilities.getIdealOutlineColor(lineColor), fillColor, true);
755                    }
756
757                    // append normal symbol SVG to be layered on top of outline
758                    strSVGIcon += RendererUtilities.setSVGSPCMColors(symbolID, siIcon.getSVG(), lineColor, fillColor, false);
759                }
760                else//weather symbol (don't change color of weather graphics)
761                    strSVGIcon = siIcon.getSVG();
762
763                //If symbol is Static Depiction, add internal mine graphic based on sector modifier 1
764                if(SymbolID.getEntityCode(symbolID) == 270701 && siMod1 != null)
765                {
766                    if (drawCustomOutline) {
767                        // create outline with larger stroke-width first (if selected)
768                        strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), RendererUtilities.getIdealOutlineColor(RendererUtilities.getColorFromHexString("#00A651")), RendererUtilities.getColorFromHexString("#00A651"), true);
769                    }
770                    //strSVGIcon += siMod1.getSVG();
771                    strSVGIcon += RendererUtilities.setSVGSPCMColors(mod1ID, siMod1.getSVG(), lineColor, fillColor, false);
772                }
773
774                if (pixelSize > 0)
775                {
776                    symbolBounds = RectUtilities.makeRect(left,top,width,height);
777                    rect = new Rect(symbolBounds);
778
779                    //adjust size
780                    float p = pixelSize;
781                    float h = rect.height();
782                    float w = rect.width();
783
784                    ratio = Math.min((p / h), (p / w));
785
786                    symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio);
787
788                    //make sure border padding isn't excessive.
789                    w = symbolBounds.width();
790                    h = symbolBounds.height();
791
792                    if(borderPadding > 0) {
793                        if (h / (h + borderPadding) > 0.10) {
794                            borderPadding = (float) (h * 0.1);
795                        } else if (w / (w + borderPadding) > 0.10) {
796                            borderPadding = (float) (w * 0.1);
797                        }
798                    }
799                }
800
801                //Draw glyphs to bitmap
802                Bitmap bmp = Bitmap.createBitmap((symbolBounds.width() + Math.round(borderPadding)), (symbolBounds.height() + Math.round(borderPadding)), Config.ARGB_8888);
803                Canvas canvas = new Canvas(bmp);
804
805                symbolBounds = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
806
807                //grow size SVG to accommodate the outline we added
808                int offset = 0;
809                if(drawCustomOutline)
810                {
811                    RectUtilities.grow(rect, 2);
812                    offset = 4;
813
814                    //RectUtilities.grow(rect, Math.round(borderPadding / ratio));
815                    //offset = (int)borderPadding;
816                }//*/
817
818
819                if(msi.getSymbolSet()==SymbolID.SymbolSet_ControlMeasure && msi.getDrawRule()==DrawRules.POINT1)//smooth out action points
820                    strSVGIcon = "/n<g stroke-linejoin=\"round\" >/n" + strSVGIcon + "/n</g>";
821
822                strSVG = svgStart + strSVGIcon + "</svg>";
823                mySVG = SVG.getFromString(strSVG);
824                //mySVG.setDocumentViewBox(left,top,width,height);
825                mySVG.setDocumentViewBox(rect.left,rect.top,rect.width(),rect.height());
826                mySVG.renderToCanvas(canvas);
827
828                Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,new RectF(offset, offset, symbolBounds.right-offset, symbolBounds.bottom-offset));
829                if(offset > 0)
830                    centerPoint.offset(offset,offset);
831
832                ii = new ImageInfo(bmp, centerPoint, symbolBounds);
833
834                if(cacheEnabled && drawAsIcon == false && bmp.getAllocationByteCount() <= maxCachedEntrySize)
835                {
836                    synchronized (_SinglePointCacheMutex)
837                    {
838                        if(_tgCache.get(key) == null)
839                            _tgCache.put(key, ii);
840                    }
841                }
842                /*if (drawAsIcon == false && pixelSize <= 100)
843
844                    _tgCache.put(key, ii);
845                }//*/
846            }
847
848            //Process Modifiers
849            ImageInfo iiNew = null;
850            if (drawAsIcon == false && (hasTextModifiers || hasDisplayModifiers))
851            {
852                SymbolDimensionInfo sdiTemp = null;
853                if (SymbolUtilities.isSPWithSpecialModifierLayout(symbolID))//(SymbolUtilitiesD.isTGSPWithSpecialModifierLayout(symbolID))
854                {
855                    sdiTemp = ModifierRenderer.ProcessTGSPWithSpecialModifierLayout(ii, symbolID, modifiers, attributes, lineColor);
856                }
857                else
858                {
859                    sdiTemp = ModifierRenderer.ProcessTGSPModifiers(ii, symbolID, modifiers, attributes, lineColor);
860                }
861                iiNew = (sdiTemp instanceof ImageInfo ? (ImageInfo)sdiTemp : null);
862            }
863
864            if (iiNew != null)
865            {
866                ii = iiNew;
867            }
868
869            //cleanup
870            //bmp.recycle();
871            symbolBounds = null;
872            fullBMP = null;
873            fullBounds = null;
874            mySVG = null;
875
876
877            if (drawAsIcon)
878            {
879                return ii.getSquareImageInfo();
880            }
881            else
882            {
883                return ii;
884            }
885
886        }
887        catch (Exception exc)
888        {
889            ErrorLogger.LogException("SinglePointRenderer", "RenderSP", exc);
890        }
891        return null;
892    }
893
894
895    /**
896     *
897     * @param symbolID
898     * @return
899     */
900    @SuppressWarnings("unused")
901    public ImageInfo RenderModifier(String symbolID, Map<String,String> attributes)
902    {
903        ImageInfo temp = null;
904        String basicSymbolID = null;
905
906        Color lineColor = null;
907        Color fillColor = null;//SymbolUtilities.getFillColorOfAffiliation(symbolID);
908
909        int alpha = -1;
910
911
912        //SVG rendering variables
913        MSInfo msi = null;
914        String iconID = null;
915        SVGInfo siIcon = null;
916        int top = 0;
917        int left = 0;
918        int width = 0;
919        int height = 0;
920        String svgStart = null;
921        String strSVG = null;
922        SVG mySVG = null;
923
924        float ratio = 0;
925
926        Rect symbolBounds = null;
927        RectF fullBounds = null;
928        Bitmap fullBMP = null;
929
930        boolean drawAsIcon = false;
931        int pixelSize = -1;
932        boolean keepUnitRatio = true;
933        boolean hasDisplayModifiers = false;
934        boolean hasTextModifiers = false;
935        int symbolOutlineWidth = RendererSettings.getInstance().getSinglePointSymbolOutlineWidth();
936        boolean drawCustomOutline = false;
937
938        try
939        {
940
941            msi = MSLookup.getInstance().getMSLInfo(symbolID);
942            if (attributes != null)
943            {
944                if (attributes.containsKey(MilStdAttributes.KeepUnitRatio))
945                {
946                    keepUnitRatio = Boolean.parseBoolean(attributes.get(MilStdAttributes.KeepUnitRatio));
947                }
948
949                if (attributes.containsKey(MilStdAttributes.LineColor))
950                {
951                    lineColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.LineColor));
952                }
953
954                if (attributes.containsKey(MilStdAttributes.FillColor))
955                {
956                    fillColor = RendererUtilities.getColorFromHexString(attributes.get(MilStdAttributes.FillColor));
957                }
958
959                if (attributes.containsKey(MilStdAttributes.Alpha))
960                {
961                    alpha = Integer.parseInt(attributes.get(MilStdAttributes.Alpha));
962                }
963
964                if (attributes.containsKey(MilStdAttributes.DrawAsIcon))
965                {
966                    drawAsIcon = Boolean.parseBoolean(attributes.get(MilStdAttributes.DrawAsIcon));
967                }
968
969                if (attributes.containsKey(MilStdAttributes.PixelSize))
970                {
971                    pixelSize = Integer.parseInt(attributes.get(MilStdAttributes.PixelSize));
972                    if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure)
973                    {
974                        if(SymbolID.getEntityCode(symbolID)==270701)//static depiction
975                            pixelSize = (int)(pixelSize * 0.9);//try to scale to be somewhat in line with units
976                    }
977                }
978
979                if(drawAsIcon==false)//don't outline icons because they're not going on the map
980                {
981                    if(attributes.containsKey(MilStdAttributes.OutlineSymbol))
982                        drawCustomOutline = Boolean.parseBoolean(attributes.get(MilStdAttributes.OutlineSymbol));
983                    else
984                        drawCustomOutline = RendererSettings.getInstance().getOutlineSPControlMeasures();
985                }
986
987                if(SymbolUtilities.isMultiPoint(symbolID))
988                    drawCustomOutline=false;//icon previews for multipoints do not need outlines since they shouldn't be on the map
989
990                /*if (attributes.containsKey(MilStdAttributes.OutlineWidth)>=0)
991                 symbolOutlineWidth = Integer.parseInt(attributes.get(MilStdAttributes.OutlineWidth));//*/
992            }
993
994            int outlineOffset = symbolOutlineWidth;
995            if (drawCustomOutline && outlineOffset > 2)
996            {
997                outlineOffset = (outlineOffset - 1) / 2;
998            }
999            else
1000            {
1001                outlineOffset = 0;
1002            }
1003
1004        }
1005        catch (Exception excModifiers)
1006        {
1007            ErrorLogger.LogException("SinglePointRenderer", "RenderModifier", excModifiers);
1008        }
1009
1010        try
1011        {
1012            ImageInfo ii = null;
1013            int intFill = -1;
1014            if (fillColor != null)
1015            {
1016                intFill = fillColor.toInt();
1017            }
1018
1019
1020            if(msi.getSymbolSet() != SymbolID.SymbolSet_ControlMeasure)
1021                lineColor = Color.BLACK;//color isn't black but should be fine for weather since colors can't be user defined.
1022
1023
1024            //if not, generate symbol
1025            if (ii == null)//*/
1026            {
1027                int version = SymbolID.getVersion(symbolID);
1028                //check symbol size////////////////////////////////////////////
1029                Rect rect = null;
1030
1031                iconID = SVGLookup.getMod1ID(symbolID);
1032                siIcon = SVGLookup.getInstance().getSVGLInfo(iconID, version);
1033                top = Math.round(siIcon.getBbox().top);
1034                left = Math.round(siIcon.getBbox().left);
1035                width = Math.round(siIcon.getBbox().width());
1036                height = Math.round(siIcon.getBbox().height());
1037                if(siIcon.getBbox().bottom > 400)
1038                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 612 792\">";
1039                else
1040                    svgStart = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"0 0 400 400\">";
1041
1042                String strSVGIcon = null;
1043                String strSVGOutline = null;
1044
1045                //update line and fill color of frame SVG
1046                if(msi.getSymbolSet() == SymbolID.SymbolSet_ControlMeasure && (lineColor != null || fillColor != null))
1047                    strSVGIcon = RendererUtilities.setSVGFrameColors(symbolID,siIcon.getSVG(),lineColor,fillColor);
1048                else
1049                    strSVGIcon = siIcon.getSVG();
1050
1051                if (pixelSize > 0)
1052                {
1053                    symbolBounds = RectUtilities.makeRect(left,top,width,height);
1054                    rect = new Rect(symbolBounds);
1055
1056                    //adjust size
1057                    float p = pixelSize;
1058                    float h = rect.height();
1059                    float w = rect.width();
1060
1061                    ratio = Math.min((p / h), (p / w));
1062
1063                    symbolBounds = RectUtilities.makeRect(0f, 0f, w * ratio, h * ratio);
1064
1065                }
1066
1067
1068                //TODO: figure out how to draw an outline and adjust the symbol bounds accordingly
1069
1070                //Draw glyphs to bitmap
1071                Bitmap bmp = Bitmap.createBitmap((symbolBounds.width()), (symbolBounds.height()), Config.ARGB_8888);
1072                Canvas canvas = new Canvas(bmp);
1073
1074                symbolBounds = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
1075
1076                strSVG = svgStart + strSVGIcon + "</svg>";
1077                mySVG = SVG.getFromString(strSVG);
1078                mySVG.setDocumentViewBox(left,top,width,height);
1079                mySVG.renderToCanvas(canvas);
1080
1081                Point centerPoint = SymbolUtilities.getCMSymbolAnchorPoint(symbolID,new RectF(0, 0, symbolBounds.right, symbolBounds.bottom));
1082
1083                ii = new ImageInfo(bmp, centerPoint, symbolBounds);
1084
1085
1086                /*if (drawAsIcon == false && pixelSize <= 100)
1087                {
1088                    _tgCache.put(key, ii);
1089                }//*/
1090            }
1091
1092
1093            //cleanup
1094            //bmp.recycle();
1095            symbolBounds = null;
1096            fullBMP = null;
1097            fullBounds = null;
1098            mySVG = null;
1099
1100
1101            if (drawAsIcon)
1102            {
1103                return ii.getSquareImageInfo();
1104            }
1105            else
1106            {
1107                return ii;
1108            }
1109
1110        }
1111        catch (Exception exc)
1112        {
1113            ErrorLogger.LogException("SinglePointRenderer", "RenderModifier", exc);
1114        }
1115        return null;
1116    }
1117
1118
1119
1120    /**
1121     *
1122     * @param symbolID
1123     * @param lineColor
1124     * @param fillColor
1125     * @param size
1126     * @param keepUnitRatio
1127     * @param drawOutline (only for single-point Control Measures)
1128     * @return
1129     */
1130    private static String makeCacheKey(String symbolID, int lineColor, int fillColor, int size, boolean keepUnitRatio, boolean drawOutline)
1131    {
1132        return makeCacheKey(symbolID, lineColor, fillColor, "null",size, keepUnitRatio, false);
1133    }
1134
1135    private static String makeCacheKey(String symbolID, int lineColor, int fillColor, String iconColor, int size, boolean keepUnitRatio, boolean drawOutline)
1136    {
1137        //String key = symbolID.substring(0, 20) + String.valueOf(lineColor) + String.valueOf(fillColor) + String.valueOf(size) + String.valueOf(keepUnitRatio);
1138        String key = symbolID.substring(0, 7) + symbolID.substring(10, 20) + SymbolID.getFrameShape(symbolID) + lineColor + fillColor + iconColor + size + keepUnitRatio + drawOutline;
1139        return key;
1140    }
1141
1142    public void logError(String tag, Throwable thrown)
1143    {
1144        if (tag == null || tag.equals(""))
1145        {
1146            tag = "singlePointRenderer";
1147        }
1148
1149        String message = thrown.getMessage();
1150        String stack = getStackTrace(thrown);
1151        if (message != null)
1152        {
1153            Log.e(tag, message);
1154        }
1155        if (stack != null)
1156        {
1157            Log.e(tag, stack);
1158        }
1159    }
1160
1161    public String getStackTrace(Throwable thrown)
1162    {
1163        try
1164        {
1165            if (thrown != null)
1166            {
1167                if (thrown.getStackTrace() != null)
1168                {
1169                    String eol = System.getProperty("line.separator");
1170                    StringBuilder sb = new StringBuilder();
1171                    sb.append(thrown.toString());
1172                    sb.append(eol);
1173                    for (StackTraceElement element : thrown.getStackTrace())
1174                    {
1175                        sb.append("        at ");
1176                        sb.append(element);
1177                        sb.append(eol);
1178                    }
1179                    return sb.toString();
1180                }
1181                else
1182                {
1183                    return thrown.getMessage() + "- no stack trace";
1184                }
1185            }
1186            else
1187            {
1188                return "no stack trace";
1189            }
1190        }
1191        catch (Exception exc)
1192        {
1193            Log.e("getStackTrace", exc.getMessage());
1194        }
1195        return thrown.getMessage();
1196    }//
1197
1198    /*
1199     private static String PrintList(ArrayList list)
1200     {
1201     String message = "";
1202     for(Object item : list)
1203     {
1204
1205     message += item.toString() + "\n";
1206     }
1207     return message;
1208     }//*/
1209    /*
1210     private static String PrintObjectMap(Map<String, Object> map)
1211     {
1212     Iterator<Object> itr = map.values().iterator();
1213     String message = "";
1214     String temp = null;
1215     while(itr.hasNext())
1216     {
1217     temp = String.valueOf(itr.next());
1218     if(temp != null)
1219     message += temp + "\n";
1220     }
1221     //ErrorLogger.LogMessage(message);
1222     return message;
1223     }//*/
1224    @Override
1225    public void onSettingsChanged(SettingsChangedEvent sce)
1226    {
1227
1228        if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_FontChanged))
1229        {
1230            synchronized (_modifierFontMutex)
1231            {
1232                _modifierFont = RendererSettings.getInstance().getModiferFont();
1233                _modifierOutlineFont = RendererSettings.getInstance().getModiferFont();
1234                FontMetrics fm = new FontMetrics();
1235                fm = _modifierFont.getFontMetrics();
1236                _modifierDescent = fm.descent;
1237                //_modifierFontHeight = fm.top + fm.bottom;
1238                _modifierFontHeight = fm.bottom - fm.top;
1239
1240                _modifierFont.setStrokeWidth(RendererSettings.getInstance().getTextOutlineWidth());
1241                _modifierOutlineFont.setColor(Color.white.toInt());
1242                _deviceDPI = RendererSettings.getInstance().getDeviceDPI();
1243
1244                ModifierRenderer.setModifierFont(_modifierFont, _modifierFontHeight, _modifierDescent);
1245
1246            }
1247        }
1248
1249        if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_CacheSizeChanged))
1250        {
1251
1252            int cSize = RendererSettings.getInstance().getCacheSize()/2;
1253            //adjust unit cache
1254            if(cSize != cacheSize) {
1255                cacheSize = cSize;
1256                if (cacheSize >= 5)
1257                    maxCachedEntrySize = cacheSize / 5;
1258                else
1259                    maxCachedEntrySize = 1;
1260
1261                if(cacheEnabled) //if cache enabled, update cache
1262                {
1263
1264                    synchronized (_UnitCacheMutex) {
1265                        if(_unitCache != null)
1266                            _unitCache.evictAll();
1267                        _unitCache = new LruCache<String, ImageInfo>(cSize) {
1268                            @Override
1269                            protected int sizeOf(String key, ImageInfo ii) {
1270                                return ii.getByteCount();// / 1024;
1271                            }
1272                        };
1273                    }
1274                    //adjust tg cache
1275                    synchronized (_SinglePointCacheMutex) {
1276                        if(_tgCache != null)
1277                            _tgCache.evictAll();
1278                        _tgCache = new LruCache<String, ImageInfo>(cSize) {
1279                            @Override
1280                            protected int sizeOf(String key, ImageInfo ii) {
1281                                return ii.getByteCount();// / 1024;
1282                            }
1283                        };
1284                    }
1285                }
1286            }
1287        }
1288        if(sce != null && sce.getEventType().equals(SettingsChangedEvent.EventType_CacheToggled))
1289        {
1290            if(cacheEnabled != RendererSettings.getInstance().getCacheEnabled())
1291            {
1292                cacheEnabled = RendererSettings.getInstance().getCacheEnabled();
1293
1294                if (cacheEnabled == false)
1295                {
1296                    synchronized (_SinglePointCacheMutex)
1297                    {
1298                        if (_tgCache != null)
1299                            _tgCache.evictAll();
1300                        _tgCache = null;
1301                    }
1302                    synchronized (_UnitCacheMutex)
1303                    {
1304                        if (_unitCache != null)
1305                            _unitCache.evictAll();
1306                        _unitCache = null;
1307                    }
1308                }
1309                else
1310                {
1311                    int cSize = RendererSettings.getInstance().getCacheSize() / 2;
1312                    synchronized (_SinglePointCacheMutex)
1313                    {
1314                        if(_tgCache != null)
1315                            _tgCache.evictAll();
1316                        _tgCache = new LruCache<String, ImageInfo>(cSize) {
1317                            @Override
1318                            protected int sizeOf(String key, ImageInfo ii) {
1319                                return ii.getByteCount();// / 1024;
1320                            }
1321                        };
1322                    }
1323                    synchronized (_UnitCacheMutex)
1324                    {
1325                        if(_unitCache != null)
1326                            _unitCache.evictAll();
1327                        _unitCache = new LruCache<String, ImageInfo>(cSize) {
1328                            @Override
1329                            protected int sizeOf(String key, ImageInfo ii) {
1330                                return ii.getByteCount();// / 1024;
1331                            }
1332                        };
1333                    }
1334                }
1335            }
1336        }
1337    }
1338}