001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas/
009 * 
010 * All Rights Reserved
011 * 
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.  
014 * 
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 * 
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
023 * GNU Lesser Public License for more details.
024 * 
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program.  If not, see https://www.gnu.org/licenses/.
027 */
028
029package edu.wisc.ssec.mcidasv.data;
030
031import edu.wisc.ssec.mcidas.AREAnav;
032
033import java.io.*;
034import java.util.ArrayList;
035import java.util.List;
036
037import java.awt.Image;
038import java.awt.Toolkit;
039
040import ucar.unidata.util.Misc;
041
042public class McIdasXFrameInfo {
043
044    private int myFrameNumber;
045    private McIdasXInfo myXInfo;
046
047    private int width = 0;
048    private int height = 0;
049    private int[] stretchTable = new int[256];
050    private int[] colorTable = new int[256];
051    private int[] graphicsTable = new int[256];
052
053    private byte myImage[];
054    
055    private boolean isLoaded = false;
056    
057    /**
058     * Constructor
059     */
060    public McIdasXFrameInfo() {
061        this.myFrameNumber = 0;
062        this.myXInfo = new McIdasXInfo();
063        this.isLoaded = false;
064    }
065
066    /**
067     * Copy constructor
068     *
069     * @param that The McIdasXFrameInfo to copy
070     *
071     */
072    public McIdasXFrameInfo(McIdasXFrameInfo that) {
073        this.myFrameNumber = that.myFrameNumber;
074        this.myXInfo = that.myXInfo;
075        this.isLoaded = false;
076    }
077    
078    /**
079     * Create a new McIDAS-X frame using the given frame number and
080     * {@literal "info"} object.
081     *
082     * @param frameNumber Frame number.
083     * @param xInfo McIDAS-X {@literal "info"} object. Cannot be {@code null}.
084     */
085    public McIdasXFrameInfo(int frameNumber, McIdasXInfo xInfo) {
086//      System.out.println("McIdasXFrameInfo constructor: frame " + frameNumber + ", xInfo " + xInfo.toString());
087        this.myFrameNumber = frameNumber;
088        this.myXInfo = xInfo;
089        this.isLoaded = false;
090    }
091    
092    public void setRefreshData(boolean refresh) {
093        if (refresh) this.isLoaded = false;
094    }
095    
096    private int setFrameData() {
097//      System.out.println(" <=> setFrameData frame " + this.myFrameNumber);
098        int ret = -1;
099        DataInputStream inputStream = myXInfo.getDataInputStream(this.myFrameNumber);
100        try {
101            this.width = inputStream.readInt();
102            this.height = inputStream.readInt();
103        } catch (Exception e) {
104            System.out.println("getFrameData exception: " + e);
105            return ret;
106        }
107
108        if (!getTable(inputStream, stretchTable)) return ret;
109        if (!getTable(inputStream, colorTable)) return ret;
110        if (!getTable(inputStream, graphicsTable)) return ret;
111
112        int havebytes = 0;
113        int needbytes = this.width * this.height;
114        byte[] image = new byte[needbytes];
115        int count = 0;
116        int num = 0;
117        int indx = 0;
118        try {
119            while (needbytes > 0) {
120                num = inputStream.read(image, indx, needbytes);
121                indx += num;
122                havebytes += num;
123                needbytes -= num;
124                count++;
125                Misc.sleep(10);
126                if (count > 100) return ret;
127            }
128        } catch (Exception e) {
129            System.out.println("getFrameData exception: " + e);
130            return ret;
131        }
132        this.isLoaded = true;
133        this.myImage = image;
134        ret = 0;
135        return ret;
136    }
137
138    public List getGraphics() {
139//      System.out.println(" <=> getGraphics frame " + this.myFrameNumber);
140        DataInputStream inputStream = myXInfo.getGraphicsInputStream(this.myFrameNumber);
141        List graphics = new ArrayList();
142        int pixels = 0;
143        try {
144                BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
145                String lineOut = br.readLine();
146                while (lineOut != null) {
147//                      System.out.println("getGraphics adding pixel: " + lineOut);
148                graphics.add(lineOut);
149                        pixels++;
150                        lineOut = br.readLine();
151                }
152            } catch (Exception e) {
153                System.out.println("getGraphics exception: " + e);
154            try { inputStream.close(); }
155            catch (Exception ee) {}
156                return graphics;
157            }
158//        System.out.println("getGraphics: " + pixels);
159            return graphics;
160    }
161    
162    public int[] getFrameDirectory() {
163//      System.out.println(" <=> getFrameDirectory frame " + this.myFrameNumber);
164        String filename = "Frame" + this.myFrameNumber + ".0";
165        DataInputStream inputStream = myXInfo.getFileInputStream(filename);
166
167        int dirLength = 64;
168        int[] dir = getInts(inputStream, dirLength);
169        
170        // The next byte tells us what type of nav we have...
171        // it also needs to be the first byte of the nav array
172        int navLength;
173        int[] navType = getInts(inputStream, 1);
174        if (navType[0] == AREAnav.LALO) navLength = 128;
175        else navLength = 640;
176        int[] navRest = getInts(inputStream, navLength - 1);
177        int[] nav = new int[navLength];
178        System.arraycopy(navType, 0, nav, 0, 1);
179        System.arraycopy(navRest, 0, nav, 1, navLength - 1);
180
181        int auxLength = 0;
182        int rows = 0;
183        int cols = 0;
184        int begLat= 0;
185        int begLon =0;
186        if (navLength == 128) {
187            rows = nav[65];
188            cols = nav[66];
189            begLat = nav[78]/4;
190            begLon = nav[79]/4;
191            auxLength = begLon + (rows*cols);
192        }
193        int[] aux = getInts(inputStream, auxLength);
194
195        int[] frmdir = new int[dirLength + navLength + auxLength];
196        System.arraycopy(dir, 0, frmdir, 0, dirLength);
197        System.arraycopy(nav, 0, frmdir, dirLength, navLength);
198        if (auxLength > 0)
199            System.arraycopy(aux, 0, frmdir, dirLength+navLength, auxLength);
200      
201        try { inputStream.close(); }
202        catch (Exception ee) {}
203 
204        return frmdir;
205    }
206
207    private boolean getTable(DataInputStream inputStream, int[] tbl) {
208        try {
209            for (int i=0; i<256; i++) {
210                tbl[i] = inputStream.readInt();
211            }
212        } catch (Exception e) {
213            System.out.println("getTable exception: " + e);
214            return false;
215        }
216        return true;
217    }
218    
219    private int[] getInts(DataInputStream stream, int count) {
220        int[] buf = new int[count];
221        if (count < 1) return buf;
222
223        int havebytes=0;
224        int needbytes=count;
225
226        try {
227            while (havebytes<needbytes) {
228                buf[havebytes] = stream.readInt();
229                havebytes++;
230            }
231        } catch (Exception e) {
232            System.out.println("getInts exception: " + e);
233            return buf;
234        }
235
236        return buf;
237    }
238    
239    public int getFrameNumber() {
240        return this.myFrameNumber;
241    }
242
243    public int getLineSize() {
244        if (!this.isLoaded) {
245           if (setFrameData() < 0) this.height = -1;
246        }
247        return this.height;
248    }
249
250    public int getElementSize() {
251        if (!this.isLoaded) {
252            if (setFrameData() < 0) this.width = -1;
253        }
254        return this.width;
255    }
256
257    public int[] getStretchTable() {
258        if (!this.isLoaded) {
259                if (setFrameData() < 0) this.stretchTable = new int[256];
260        }
261        return this.stretchTable;
262    }
263    
264    public int[] getColorTable() {
265        if (!this.isLoaded) {
266                if (setFrameData() < 0) this.colorTable = new int[256];
267        }
268        return this.colorTable;
269    }
270    
271    public int[] getGraphicsTable() {
272        if (!this.isLoaded) {
273                if (setFrameData() < 0) this.graphicsTable = new int[256];
274        }
275        return this.graphicsTable;
276    }
277    
278    public float[][] getEnhancementTable() {
279        float[][] enhancementTable = new float[3][256];
280        if (!this.isLoaded) {
281                if (setFrameData() < 0) return enhancementTable;
282        }
283        for (int i=1; i<18; i++) {
284                enhancementTable[0][i] = (float)((this.colorTable[i]/0x10000)&0xff);
285                enhancementTable[1][i] = (float)((this.colorTable[i]/0x100)&0xff);
286                enhancementTable[2][i] = (float)(this.colorTable[i]&0xff);
287        }
288        for (int i=18; i<256; i++) {
289                enhancementTable[0][i] = (float)((this.colorTable[this.stretchTable[i]]/0x10000)&0xff);
290                enhancementTable[1][i] = (float)((this.colorTable[this.stretchTable[i]]/0x100)&0xff);
291                enhancementTable[2][i] = (float)(this.colorTable[this.stretchTable[i]]&0xff);
292        }
293        for (int i=0; i<256; i++) {
294                enhancementTable[0][i] /= 0xff;
295                enhancementTable[1][i] /= 0xff;
296                enhancementTable[2][i] /= 0xff;
297        }
298        return enhancementTable;
299    }
300
301    public byte[] getImage() {
302        if (!this.isLoaded) {
303                if (setFrameData() < 0) this.myImage = new byte[0];
304        }
305        return this.myImage;
306    }
307
308    public Image getGIF() {
309        int MAX_BYTES = 1048576;
310        byte[] imagebytes = new byte[MAX_BYTES];
311        DataInputStream inputStream = this.myXInfo.getGIFInputStream(this.myFrameNumber);
312        int n = 0;
313        int i = 0;
314        for (n=0; n<MAX_BYTES; n++) {
315                try {
316                        i = inputStream.read();
317                } catch (Exception ee) { }
318                if (i < 0) break;
319                imagebytes[n] = (byte)i;
320        }
321        byte[] gifbytes = new byte[n];
322        System.arraycopy(imagebytes, 0, gifbytes, 0, n);
323        Image imageGIF = Toolkit.getDefaultToolkit().createImage(gifbytes);
324        imagebytes = null;
325        try { inputStream.close(); }
326        catch (Exception ee) {}
327        return imageGIF;
328    }
329
330}