1 package jogamp.opengl.util.pngj.chunks; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 import jogamp.opengl.util.pngj.PngHelperInternal; 7 import jogamp.opengl.util.pngj.PngjException; 8 9 /** 10 * We consider "image metadata" every info inside the image except for the most 11 * basic image info (IHDR chunk - ImageInfo class) and the pixels values. 12 * <p> 13 * This includes the palette (if present) and all the ancillary chunks 14 * <p> 15 * This class provides a wrapper over the collection of chunks of a image (read 16 * or to write) and provides some high level methods to access them 17 */ 18 public class PngMetadata { 19 private final ChunksList chunkList; 20 private final boolean readonly; 21 PngMetadata(final ChunksList chunks)22 public PngMetadata(final ChunksList chunks) { 23 this.chunkList = chunks; 24 if (chunks instanceof ChunksListForWrite) { 25 this.readonly = false; 26 } else { 27 this.readonly = true; 28 } 29 } 30 31 /** 32 * Queues the chunk at the writer 33 * <p> 34 * lazyOverwrite: if true, checks if there is a queued "equivalent" chunk 35 * and if so, overwrites it. However if that not check for already written 36 * chunks. 37 */ queueChunk(final PngChunk c, final boolean lazyOverwrite)38 public void queueChunk(final PngChunk c, final boolean lazyOverwrite) { 39 final ChunksListForWrite cl = getChunkListW(); 40 if (readonly) 41 throw new PngjException("cannot set chunk : readonly metadata"); 42 if (lazyOverwrite) { 43 ChunkHelper.trimList(cl.getQueuedChunks(), new ChunkPredicate() { 44 @Override 45 public boolean match(final PngChunk c2) { 46 return ChunkHelper.equivalent(c, c2); 47 } 48 }); 49 } 50 cl.queue(c); 51 } 52 queueChunk(final PngChunk c)53 public void queueChunk(final PngChunk c) { 54 queueChunk(c, true); 55 } 56 getChunkListW()57 private ChunksListForWrite getChunkListW() { 58 return (ChunksListForWrite) chunkList; 59 } 60 61 // ///// high level utility methods follow //////////// 62 63 // //////////// DPI 64 65 /** 66 * returns -1 if not found or dimension unknown 67 */ getDpi()68 public double[] getDpi() { 69 final PngChunk c = chunkList.getById1(ChunkHelper.pHYs, true); 70 if (c == null) 71 return new double[] { -1, -1 }; 72 else 73 return ((PngChunkPHYS) c).getAsDpi2(); 74 } 75 setDpi(final double x)76 public void setDpi(final double x) { 77 setDpi(x, x); 78 } 79 setDpi(final double x, final double y)80 public void setDpi(final double x, final double y) { 81 final PngChunkPHYS c = new PngChunkPHYS(chunkList.imageInfo); 82 c.setAsDpi2(x, y); 83 queueChunk(c); 84 } 85 86 // //////////// TIME 87 88 /** 89 * Creates a time chunk with current time, less secsAgo seconds 90 * <p> 91 * 92 * @return Returns the created-queued chunk, just in case you want to 93 * examine or modify it 94 */ setTimeNow(final int secsAgo)95 public PngChunkTIME setTimeNow(final int secsAgo) { 96 final PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); 97 c.setNow(secsAgo); 98 queueChunk(c); 99 return c; 100 } 101 setTimeNow()102 public PngChunkTIME setTimeNow() { 103 return setTimeNow(0); 104 } 105 106 /** 107 * Creates a time chunk with diven date-time 108 * <p> 109 * 110 * @return Returns the created-queued chunk, just in case you want to 111 * examine or modify it 112 */ setTimeYMDHMS(final int yearx, final int monx, final int dayx, final int hourx, final int minx, final int secx)113 public PngChunkTIME setTimeYMDHMS(final int yearx, final int monx, final int dayx, final int hourx, final int minx, final int secx) { 114 final PngChunkTIME c = new PngChunkTIME(chunkList.imageInfo); 115 c.setYMDHMS(yearx, monx, dayx, hourx, minx, secx); 116 queueChunk(c, true); 117 return c; 118 } 119 120 /** 121 * null if not found 122 */ getTime()123 public PngChunkTIME getTime() { 124 return (PngChunkTIME) chunkList.getById1(ChunkHelper.tIME); 125 } 126 getTimeAsString()127 public String getTimeAsString() { 128 final PngChunkTIME c = getTime(); 129 return c == null ? "" : c.getAsString(); 130 } 131 132 // //////////// TEXT 133 134 /** 135 * Creates a text chunk and queue it. 136 * <p> 137 * 138 * @param k 139 * : key (latin1) 140 * @param val 141 * (arbitrary, should be latin1 if useLatin1) 142 * @param useLatin1 143 * @param compress 144 * @return Returns the created-queued chunks, just in case you want to 145 * examine, touch it 146 */ setText(final String k, final String val, final boolean useLatin1, final boolean compress)147 public PngChunkTextVar setText(final String k, final String val, final boolean useLatin1, final boolean compress) { 148 if (compress && !useLatin1) 149 throw new PngjException("cannot compress non latin text"); 150 PngChunkTextVar c; 151 if (useLatin1) { 152 if (compress) { 153 c = new PngChunkZTXT(chunkList.imageInfo); 154 } else { 155 c = new PngChunkTEXT(chunkList.imageInfo); 156 } 157 } else { 158 c = new PngChunkITXT(chunkList.imageInfo); 159 ((PngChunkITXT) c).setLangtag(k); // we use the same orig tag (this is not quite right) 160 } 161 c.setKeyVal(k, val); 162 queueChunk(c, true); 163 return c; 164 } 165 setText(final String k, final String val)166 public PngChunkTextVar setText(final String k, final String val) { 167 return setText(k, val, false, false); 168 } 169 170 /** 171 * gets all text chunks with a given key 172 * <p> 173 * returns null if not found 174 * <p> 175 * Warning: this does not check the "lang" key of iTxt 176 */ 177 @SuppressWarnings("unchecked") getTxtsForKey(final String k)178 public List<? extends PngChunkTextVar> getTxtsForKey(final String k) { 179 @SuppressWarnings("rawtypes") 180 final 181 List c = new ArrayList(); 182 c.addAll(chunkList.getById(ChunkHelper.tEXt, k)); 183 c.addAll(chunkList.getById(ChunkHelper.zTXt, k)); 184 c.addAll(chunkList.getById(ChunkHelper.iTXt, k)); 185 return c; 186 } 187 188 /** 189 * Returns empty if not found, concatenated (with newlines) if multiple! - 190 * and trimmed 191 * <p> 192 * Use getTxtsForKey() if you don't want this behaviour 193 */ getTxtForKey(final String k)194 public String getTxtForKey(final String k) { 195 final List<? extends PngChunkTextVar> li = getTxtsForKey(k); 196 if (li.isEmpty()) 197 return ""; 198 final StringBuilder t = new StringBuilder(); 199 for (final PngChunkTextVar c : li) 200 t.append(c.getVal()).append("\n"); 201 return t.toString().trim(); 202 } 203 204 /** 205 * Returns the palette chunk, if present 206 * 207 * @return null if not present 208 */ getPLTE()209 public PngChunkPLTE getPLTE() { 210 return (PngChunkPLTE) chunkList.getById1(PngChunkPLTE.ID); 211 } 212 213 /** 214 * Creates a new empty palette chunk, queues it for write and return it to 215 * the caller, who should fill its entries 216 */ createPLTEChunk()217 public PngChunkPLTE createPLTEChunk() { 218 final PngChunkPLTE plte = new PngChunkPLTE(chunkList.imageInfo); 219 queueChunk(plte); 220 return plte; 221 } 222 223 /** 224 * Returns the TRNS chunk, if present 225 * 226 * @return null if not present 227 */ getTRNS()228 public PngChunkTRNS getTRNS() { 229 return (PngChunkTRNS) chunkList.getById1(PngChunkTRNS.ID); 230 } 231 232 /** 233 * Creates a new empty TRNS chunk, queues it for write and return it to the 234 * caller, who should fill its entries 235 */ createTRNSChunk()236 public PngChunkTRNS createTRNSChunk() { 237 final PngChunkTRNS trns = new PngChunkTRNS(chunkList.imageInfo); 238 queueChunk(trns); 239 return trns; 240 } 241 242 } 243