View Javadoc
1   package nom.tam.fits.compression.algorithm.plio;
2   
3   import java.nio.ByteBuffer;
4   import java.nio.IntBuffer;
5   import java.nio.ShortBuffer;
6   
7   import nom.tam.fits.compression.algorithm.api.ICompressor;
8   
9   /*
10   * #%L
11   * nom.tam FITS library
12   * %%
13   * Copyright (C) 1996 - 2024 nom-tam-fits
14   * %%
15   * This is free and unencumbered software released into the public domain.
16   *
17   * Anyone is free to copy, modify, publish, use, compile, sell, or
18   * distribute this software, either in source code form or as a compiled
19   * binary, for any purpose, commercial or non-commercial, and by any
20   * means.
21   *
22   * In jurisdictions that recognize copyright laws, the author or authors
23   * of this software dedicate any and all copyright interest in the
24   * software to the public domain. We make this dedication for the benefit
25   * of the public at large and to the detriment of our heirs and
26   * successors. We intend this dedication to be an overt act of
27   * relinquishment in perpetuity of all present and future rights to this
28   * software under copyright law.
29   *
30   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
31   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
33   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
34   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
35   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
36   * OTHER DEALINGS IN THE SOFTWARE.
37   * #L%
38   */
39  
40  /**
41   * (<i>for internal use</i>) The PLIO compression algorithm. The original decompression code was written by Doug Tody,
42   * NRAO and included (ported to c and adapted) in cfitsio by William Pence, NASA/GSFC. That code was then ported to Java
43   * by R. van Nieuwenhoven. Later it was massively refactored to harmonize the different compression algorithms and
44   * reduce the duplicate code pieces without obscuring the algorithm itself as good as possible.
45   *
46   * @author Doug Tody
47   * @author William Pence
48   * @author Richard van Nieuwenhoven
49   */
50  @SuppressWarnings("javadoc")
51  public abstract class PLIOCompress {
52  
53      public static class BytePLIOCompressor extends PLIOCompress implements ICompressor<ByteBuffer> {
54  
55          private ByteBuffer pixelData;
56  
57          @Override
58          public boolean compress(ByteBuffer buffer, ByteBuffer compressed) {
59              pixelData = buffer;
60              compress(compressed.asShortBuffer(), pixelData.limit());
61              return true;
62          }
63  
64          @Override
65          public void decompress(ByteBuffer compressed, ByteBuffer buffer) {
66              pixelData = buffer;
67              decompress(compressed.asShortBuffer(), pixelData.limit());
68          }
69  
70          @Override
71          protected int nextPixel() {
72              return pixelData.get();
73          }
74  
75          @Override
76          protected void put(int index, int pixel) {
77              pixelData.put(index, (byte) pixel);
78          }
79      }
80  
81      public static class ShortPLIOCompressor extends PLIOCompress implements ICompressor<ShortBuffer> {
82  
83          private ShortBuffer pixelData;
84  
85          @Override
86          public boolean compress(ShortBuffer buffer, ByteBuffer compressed) {
87              pixelData = buffer;
88              super.compress(compressed.asShortBuffer(), pixelData.limit());
89              return true;
90          }
91  
92          @Override
93          public void decompress(ByteBuffer compressed, ShortBuffer buffer) {
94              pixelData = buffer;
95              decompress(compressed.asShortBuffer(), pixelData.limit());
96          }
97  
98          @Override
99          protected int nextPixel() {
100             return pixelData.get();
101         }
102 
103         @Override
104         protected void put(int index, int pixel) {
105             pixelData.put(index, (short) pixel);
106         }
107     }
108 
109     /**
110      * Attention int values are limited to 24 bits!
111      */
112     public static class IntPLIOCompressor extends PLIOCompress implements ICompressor<IntBuffer> {
113 
114         private IntBuffer pixelData;
115 
116         @Override
117         public boolean compress(IntBuffer buffer, ByteBuffer compressed) {
118             pixelData = buffer;
119             super.compress(compressed.asShortBuffer(), pixelData.limit());
120             return true;
121         }
122 
123         @Override
124         public void decompress(ByteBuffer compressed, IntBuffer buffer) {
125             pixelData = buffer;
126             decompress(compressed.asShortBuffer(), pixelData.limit());
127         }
128 
129         @Override
130         protected int nextPixel() {
131             return pixelData.get();
132         }
133 
134         @Override
135         protected void put(int index, int pixel) {
136             pixelData.put(index, (short) pixel);
137         }
138     }
139 
140     private static final int FIRST_VALUE_WITH_13_BIT = 4096;
141 
142     private static final int FIRST_VALUE_WITH_14_BIT = 8192;
143 
144     private static final int FIRST_VALUE_WITH_15_BIT = 16384;
145 
146     private static final int FIRST_VALUE_WITH_16_BIT = 32768;
147 
148     private static final int HEADER_SIZE_FIELD1 = 3;
149 
150     private static final int HEADER_SIZE_FIELD2 = 4;
151 
152     private static final int LAST_VALUE_FITTING_IN_12_BIT = FIRST_VALUE_WITH_13_BIT - 1;
153 
154     private static final int MINI_HEADER_SIZE = 3;
155 
156     private static final int MINI_HEADER_SIZE_FIELD = 2;
157 
158     /**
159      * The exact meaning of this var is not clear at the moment of porting the algorithm to Java.
160      */
161     private static final int N20481 = 20481;
162 
163     private static final int OPCODE_1 = 1;
164 
165     private static final int OPCODE_2 = 2;
166 
167     private static final int OPCODE_3 = 3;
168 
169     private static final int OPCODE_4 = 4;
170 
171     private static final int OPCODE_5 = 5;
172 
173     private static final int OPCODE_6 = 6;
174 
175     private static final int OPCODE_7 = 7;
176 
177     private static final int OPCODE_8 = 8;
178 
179     private static final short[] PLIO_HEADER = {(short) 0, (short) 7, (short) -100, (short) 0, (short) 0, (short) 0,
180             (short) 0};
181 
182     private static final int SHIFT_12_BITS = 12;
183 
184     private static final int SHIFT_15_BITS = 15;
185 
186     private static final int VALUE_OF_BIT_13_AND14_ON = 12288;
187 
188     /**
189      * PL_P2L -- Convert a pixel tiledImageOperation to a line list. The length of the list is returned as the function
190      * value.
191      *
192      * @param compressedData encoded line list
193      * @param npix           number of pixels to convert
194      */
195     protected void compress(ShortBuffer compressedData, int npix) {
196         compressedData.put(PLIO_HEADER);
197         final int xe = npix - 1;
198         int op = PLIO_HEADER.length;
199         /* Computing MAX */
200         int pv = Math.max(0, nextPixel());
201         int x1 = 0;
202         int iz = 0;
203         int hi = 1;
204         int nv = 0;
205         for (int ip = 0; ip <= xe; ++ip) {
206             if (ip < xe) {
207                 /* Computing MAX */
208                 nv = Math.max(0, nextPixel());
209                 if (nv == pv) {
210                     continue;
211                 }
212                 if (pv == 0) {
213                     pv = nv;
214                     x1 = ip + 1;
215                     continue;
216                 }
217             } else if (pv == 0) {
218                 x1 = xe + 1;
219             }
220 
221             int np = ip - x1 + 1;
222             int nz = x1 - iz;
223             boolean skip = false;
224             if (pv > 0) {
225                 int dv = pv - hi;
226                 if (dv != 0) {
227                     hi = pv;
228                     if (Math.abs(dv) > LAST_VALUE_FITTING_IN_12_BIT) {
229                         compressedData.put(op, (short) ((pv & LAST_VALUE_FITTING_IN_12_BIT) + FIRST_VALUE_WITH_13_BIT));
230                         ++op;
231                         compressedData.put(op, (short) (pv / FIRST_VALUE_WITH_13_BIT));
232                         ++op;
233                     } else {
234                         if (dv < 0) {
235                             compressedData.put(op, (short) (-dv + VALUE_OF_BIT_13_AND14_ON));
236                         } else {
237                             compressedData.put(op, (short) (dv + FIRST_VALUE_WITH_14_BIT));
238                         }
239                         ++op;
240                         if (np == 1 && nz == 0) {
241                             int v = compressedData.get(op - 1);
242                             compressedData.put(op - 1, (short) (v | FIRST_VALUE_WITH_15_BIT));
243                             skip = true;
244                         }
245                     }
246                 }
247             }
248             if (!skip) {
249                 if (nz > 0) {
250                     while (nz > 0) {
251                         compressedData.put(op, (short) Math.min(LAST_VALUE_FITTING_IN_12_BIT, nz));
252                         ++op;
253                         nz += -LAST_VALUE_FITTING_IN_12_BIT;
254                     }
255                     if (np == 1 && pv > 0) {
256                         compressedData.put(op - 1, (short) (compressedData.get(op - 1) + N20481));
257                         skip = true;
258                     }
259                 }
260             }
261             if (!skip) {
262                 while (np > 0) {
263                     compressedData.put(op, (short) (Math.min(LAST_VALUE_FITTING_IN_12_BIT, np) + FIRST_VALUE_WITH_15_BIT));
264                     ++op;
265                     np += -LAST_VALUE_FITTING_IN_12_BIT;
266                 }
267             }
268             x1 = ip + 1;
269             iz = x1;
270             pv = nv;
271         }
272         compressedData.put(HEADER_SIZE_FIELD1, (short) (op % FIRST_VALUE_WITH_16_BIT));
273         compressedData.put(HEADER_SIZE_FIELD2, (short) (op / FIRST_VALUE_WITH_16_BIT));
274         compressedData.position(op);
275     }
276 
277     /**
278      * PL_L2PI -- Translate a PLIO line list into an integer pixel tiledImageOperation. The number of pixels output
279      * (always npix) is returned as the function value.
280      *
281      * @param  compressedData encoded line list
282      * @param  npix           number of pixels to convert
283      *
284      * @return                number of pixels converted
285      */
286     protected int decompress(ShortBuffer compressedData, int npix) {
287         int llfirt;
288         int lllen;
289         if (!(compressedData.get(2) > 0)) {
290             lllen = (compressedData.get(HEADER_SIZE_FIELD2) << SHIFT_15_BITS) + compressedData.get(HEADER_SIZE_FIELD1);
291             llfirt = compressedData.get(1);
292         } else {
293             lllen = compressedData.get(MINI_HEADER_SIZE_FIELD);
294             llfirt = MINI_HEADER_SIZE;
295         }
296         final int xe = npix;
297         int op = 0;
298         int x1 = 1;
299         int pv = 1;
300         for (int ip = llfirt; ip <= lllen; ++ip) {
301             final int opcode = compressedData.get(ip) / FIRST_VALUE_WITH_13_BIT;
302             final int data = compressedData.get(ip) & LAST_VALUE_FITTING_IN_12_BIT;
303             final int sw0001 = opcode + 1;
304             if (sw0001 == OPCODE_1 || sw0001 == OPCODE_5 || sw0001 == OPCODE_6) {
305                 final int x2 = x1 + data - 1;
306                 final int i2 = Math.min(x2, xe);
307                 final int np = i2 - Math.max(x1, 0) + 1;
308                 if (np > 0) {
309                     final int otop = op + np - 1;
310                     if (!(opcode == OPCODE_4)) {
311                         for (int index = op; index <= otop; ++index) {
312                             put(index, 0);
313                         }
314                         if (opcode == OPCODE_5 && i2 == x2) {
315                             put(otop, pv);
316                         }
317                     } else {
318                         for (int index = op; index <= otop; ++index) {
319                             put(index, pv);
320                         }
321                     }
322                     op = otop + 1;
323                 }
324                 x1 = x2 + 1;
325             } else if (sw0001 == OPCODE_2) {
326                 pv = (compressedData.get(ip + 1) << SHIFT_12_BITS) + data;
327                 ++ip;
328             } else if (sw0001 == OPCODE_3) {
329                 pv += data;
330             } else if (sw0001 == OPCODE_4) {
331                 pv -= data;
332             } else if (sw0001 == OPCODE_7) {
333                 pv += data;
334                 if (x1 >= 0 && x1 <= xe) {
335                     put(op, pv);
336                     ++op;
337                 }
338                 ++x1;
339             } else if (sw0001 == OPCODE_8) {
340                 pv -= data;
341                 if (x1 >= 0 && x1 <= xe) {
342                     put(op, pv);
343                     ++op;
344                 }
345                 ++x1;
346             }
347             if (x1 > xe) {
348                 break;
349             }
350         }
351         for (int index = op; index < npix; ++index) {
352             put(index, 0);
353         }
354         return npix;
355     }
356 
357     protected abstract int nextPixel();
358 
359     protected abstract void put(int index, int pixel);
360 
361 }