1. /*
  2. * @(#)DQTMarkerSegment.java 1.4 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.imageio.plugins.jpeg;
  8. import javax.imageio.IIOException;
  9. import javax.imageio.metadata.IIOInvalidTreeException;
  10. import javax.imageio.metadata.IIOMetadataNode;
  11. import javax.imageio.stream.ImageOutputStream;
  12. import javax.imageio.plugins.jpeg.JPEGQTable;
  13. import java.io.IOException;
  14. import java.util.List;
  15. import java.util.ArrayList;
  16. import java.util.Iterator;
  17. import org.w3c.dom.Node;
  18. import org.w3c.dom.NodeList;
  19. import org.w3c.dom.NamedNodeMap;
  20. /**
  21. * A DQT (Define Quantization Table) marker segment.
  22. */
  23. class DQTMarkerSegment extends MarkerSegment {
  24. List tables = new ArrayList(); // Could be 1 to 4
  25. DQTMarkerSegment(float quality, boolean needTwo) {
  26. super(JPEG.DQT);
  27. tables.add(new Qtable(true, quality));
  28. if (needTwo) {
  29. tables.add(new Qtable(false, quality));
  30. }
  31. }
  32. DQTMarkerSegment(JPEGBuffer buffer) throws IOException {
  33. super(buffer);
  34. int count = length;
  35. while (count > 0) {
  36. Qtable newGuy = new Qtable(buffer);
  37. tables.add(newGuy);
  38. count -= newGuy.data.length+1;
  39. }
  40. buffer.bufAvail -= length;
  41. }
  42. DQTMarkerSegment(JPEGQTable[] qtables) {
  43. super(JPEG.DQT);
  44. for (int i = 0; i < qtables.length; i++) {
  45. tables.add(new Qtable(qtables[i], i));
  46. }
  47. }
  48. DQTMarkerSegment(Node node) throws IIOInvalidTreeException {
  49. super(JPEG.DQT);
  50. NodeList children = node.getChildNodes();
  51. int size = children.getLength();
  52. if ((size < 1) || (size > 4)) {
  53. throw new IIOInvalidTreeException("Invalid DQT node", node);
  54. }
  55. for (int i = 0; i < size; i++) {
  56. tables.add(new Qtable(children.item(i)));
  57. }
  58. }
  59. protected Object clone() {
  60. DQTMarkerSegment newGuy = (DQTMarkerSegment) super.clone();
  61. newGuy.tables = new ArrayList(tables.size());
  62. Iterator iter = tables.iterator();
  63. while (iter.hasNext()) {
  64. Qtable table = (Qtable) iter.next();
  65. newGuy.tables.add(table.clone());
  66. }
  67. return newGuy;
  68. }
  69. IIOMetadataNode getNativeNode() {
  70. IIOMetadataNode node = new IIOMetadataNode("dqt");
  71. for (int i= 0; i<tables.size(); i++) {
  72. Qtable table = (Qtable) tables.get(i);
  73. node.appendChild(table.getNativeNode());
  74. }
  75. return node;
  76. }
  77. /**
  78. * Writes the data for this segment to the stream in
  79. * valid JPEG format.
  80. */
  81. void write(ImageOutputStream ios) throws IOException {
  82. // We don't write DQT segments; the IJG library does.
  83. }
  84. void print() {
  85. printTag("DQT");
  86. System.out.println("Num tables: "
  87. + Integer.toString(tables.size()));
  88. for (int i= 0; i<tables.size(); i++) {
  89. Qtable table = (Qtable) tables.get(i);
  90. table.print();
  91. }
  92. System.out.println();
  93. }
  94. /**
  95. * Assuming the given table was generated by scaling the "standard"
  96. * visually lossless luminance table, extract the scale factor that
  97. * was used.
  98. */
  99. Qtable getChromaForLuma(Qtable luma) {
  100. Qtable newGuy = null;
  101. // Determine if the table is all the same values
  102. // if so, use the same table
  103. boolean allSame = true;
  104. for (int i = 1; i < luma.QTABLE_SIZE; i++) {
  105. if (luma.data[i] != luma.data[i-1]) {
  106. allSame = false;
  107. break;
  108. }
  109. }
  110. if (allSame) {
  111. newGuy = (Qtable) luma.clone();
  112. newGuy.tableID = 1;
  113. } else {
  114. // Otherwise, find the largest coefficient less than 255. This is
  115. // the largest value that we know did not clamp on scaling.
  116. int largestPos = 0;
  117. for (int i = 1; i < luma.QTABLE_SIZE; i++) {
  118. if (luma.data[i] > luma.data[largestPos]) {
  119. largestPos = i;
  120. }
  121. }
  122. // Compute the scale factor by dividing it by the value in the
  123. // same position from the "standard" table.
  124. // If the given table was not generated by scaling the standard,
  125. // the resulting table will still be reasonable, as it will reflect
  126. // a comparable scaling of chrominance frequency response of the
  127. // eye.
  128. float scaleFactor = ((float)(luma.data[largestPos]))
  129. / ((float)(JPEGQTable.K1Div2Luminance.getTable()[largestPos]));
  130. // generate a new table
  131. JPEGQTable jpegTable =
  132. JPEGQTable.K2Div2Chrominance.getScaledInstance(scaleFactor,
  133. true);
  134. newGuy = new Qtable(jpegTable, 1);
  135. }
  136. return newGuy;
  137. }
  138. Qtable getQtableFromNode(Node node) throws IIOInvalidTreeException {
  139. return new Qtable(node);
  140. }
  141. /**
  142. * A quantization table within a DQT marker segment.
  143. */
  144. class Qtable implements Cloneable {
  145. int elementPrecision;
  146. int tableID;
  147. final int QTABLE_SIZE = 64;
  148. int [] data; // 64 elements, in natural order
  149. /**
  150. * The zigzag-order position of the i'th element
  151. * of a DCT block read in natural order.
  152. */
  153. private final int [] zigzag = {
  154. 0, 1, 5, 6, 14, 15, 27, 28,
  155. 2, 4, 7, 13, 16, 26, 29, 42,
  156. 3, 8, 12, 17, 25, 30, 41, 43,
  157. 9, 11, 18, 24, 31, 40, 44, 53,
  158. 10, 19, 23, 32, 39, 45, 52, 54,
  159. 20, 22, 33, 38, 46, 51, 55, 60,
  160. 21, 34, 37, 47, 50, 56, 59, 61,
  161. 35, 36, 48, 49, 57, 58, 62, 63
  162. };
  163. Qtable(boolean wantLuma, float quality) {
  164. elementPrecision = 0;
  165. JPEGQTable base = null;
  166. if (wantLuma) {
  167. tableID = 0;
  168. base = JPEGQTable.K1Div2Luminance;
  169. } else {
  170. tableID = 1;
  171. base = JPEGQTable.K2Div2Chrominance;
  172. }
  173. if (quality != JPEG.DEFAULT_QUALITY) {
  174. quality = JPEG.convertToLinearQuality(quality);
  175. if (wantLuma) {
  176. base = JPEGQTable.K1Luminance.getScaledInstance
  177. (quality, true);
  178. } else {
  179. base = JPEGQTable.K2Div2Chrominance.getScaledInstance
  180. (quality, true);
  181. }
  182. }
  183. data = base.getTable();
  184. }
  185. Qtable(JPEGBuffer buffer) throws IIOException {
  186. elementPrecision = buffer.buf[buffer.bufPtr] >>> 4;
  187. tableID = buffer.buf[buffer.bufPtr++] & 0xf;
  188. if (elementPrecision != 0) {
  189. // IJG is compiled for 8-bits, so this shouldn't happen
  190. throw new IIOException ("Unsupported element precision");
  191. }
  192. data = new int [QTABLE_SIZE];
  193. // Read from zig-zag order to natural order
  194. for (int i = 0; i < QTABLE_SIZE; i++) {
  195. data[i] = buffer.buf[buffer.bufPtr+zigzag[i]] & 0xff;
  196. }
  197. buffer.bufPtr += QTABLE_SIZE;
  198. }
  199. Qtable(JPEGQTable table, int id) {
  200. elementPrecision = 0;
  201. tableID = id;
  202. data = table.getTable();
  203. }
  204. Qtable(Node node) throws IIOInvalidTreeException {
  205. if (node.getNodeName().equals("dqtable")) {
  206. NamedNodeMap attrs = node.getAttributes();
  207. int count = attrs.getLength();
  208. if ((count < 1) || (count > 2)) {
  209. throw new IIOInvalidTreeException
  210. ("dqtable node must have 1 or 2 attributes", node);
  211. }
  212. elementPrecision = 0;
  213. tableID = getAttributeValue(node, attrs, "qtableId", 0, 3, true);
  214. if (node instanceof IIOMetadataNode) {
  215. IIOMetadataNode ourNode = (IIOMetadataNode) node;
  216. JPEGQTable table = (JPEGQTable) ourNode.getUserObject();
  217. if (table == null) {
  218. throw new IIOInvalidTreeException
  219. ("dqtable node must have user object", node);
  220. }
  221. data = table.getTable();
  222. } else {
  223. throw new IIOInvalidTreeException
  224. ("dqtable node must have user object", node);
  225. }
  226. } else {
  227. throw new IIOInvalidTreeException
  228. ("Invalid node, expected dqtable", node);
  229. }
  230. }
  231. protected Object clone() {
  232. Qtable newGuy = null;
  233. try {
  234. newGuy = (Qtable) super.clone();
  235. } catch (CloneNotSupportedException e) {} // won't happen
  236. if (data != null) {
  237. newGuy.data = (int []) data.clone();
  238. }
  239. return newGuy;
  240. }
  241. IIOMetadataNode getNativeNode() {
  242. IIOMetadataNode node = new IIOMetadataNode("dqtable");
  243. node.setAttribute("elementPrecision",
  244. Integer.toString(elementPrecision));
  245. node.setAttribute("qtableId",
  246. Integer.toString(tableID));
  247. node.setUserObject(new JPEGQTable(data));
  248. return node;
  249. }
  250. void print() {
  251. System.out.println("Table id: " + Integer.toString(tableID));
  252. System.out.println("Element precision: "
  253. + Integer.toString(elementPrecision));
  254. (new JPEGQTable(data)).toString();
  255. /*
  256. for (int i = 0; i < 64; i++) {
  257. if (i % 8 == 0) {
  258. System.out.println();
  259. }
  260. System.out.print(" " + Integer.toString(data[i]));
  261. }
  262. System.out.println();
  263. */
  264. }
  265. }
  266. }