1. /*
  2. * @(#)ImageUtil.java 1.2 03/12/19 16:54:01
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.imageio.plugins.common;
  8. import java.awt.Point;
  9. import java.awt.Rectangle;
  10. import java.awt.Transparency;
  11. import java.awt.color.ColorSpace;
  12. import java.awt.image.BufferedImage;
  13. import java.awt.image.ColorModel;
  14. import java.awt.image.ComponentColorModel;
  15. import java.awt.image.ComponentSampleModel;
  16. import java.awt.image.DataBuffer;
  17. import java.awt.image.DataBufferByte;
  18. import java.awt.image.DataBufferInt;
  19. import java.awt.image.DataBufferShort;
  20. import java.awt.image.DataBufferUShort;
  21. import java.awt.image.DirectColorModel;
  22. import java.awt.image.IndexColorModel;
  23. import java.awt.image.MultiPixelPackedSampleModel;
  24. import java.awt.image.Raster;
  25. import java.awt.image.RenderedImage;
  26. import java.awt.image.SampleModel;
  27. import java.awt.image.SinglePixelPackedSampleModel;
  28. import java.awt.image.WritableRaster;
  29. import java.util.Arrays;
  30. //import javax.imageio.ImageTypeSpecifier;
  31. import javax.imageio.IIOException;
  32. import javax.imageio.IIOImage;
  33. import javax.imageio.ImageTypeSpecifier;
  34. import javax.imageio.ImageWriter;
  35. import javax.imageio.spi.ImageWriterSpi;
  36. public class ImageUtil {
  37. /* XXX testing only
  38. public static void main(String[] args) {
  39. ImageTypeSpecifier bilevel =
  40. ImageTypeSpecifier.createIndexed(new byte[] {(byte)0, (byte)255},
  41. new byte[] {(byte)0, (byte)255},
  42. new byte[] {(byte)0, (byte)255},
  43. null, 1,
  44. DataBuffer.TYPE_BYTE);
  45. ImageTypeSpecifier gray =
  46. ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false);
  47. ImageTypeSpecifier grayAlpha =
  48. ImageTypeSpecifier.createGrayscale(8, DataBuffer.TYPE_BYTE, false,
  49. false);
  50. ImageTypeSpecifier rgb =
  51. ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
  52. new int[] {0, 1, 2},
  53. DataBuffer.TYPE_BYTE,
  54. false,
  55. false);
  56. ImageTypeSpecifier rgba =
  57. ImageTypeSpecifier.createInterleaved(ColorSpace.getInstance(ColorSpace.CS_sRGB),
  58. new int[] {0, 1, 2, 3},
  59. DataBuffer.TYPE_BYTE,
  60. true,
  61. false);
  62. ImageTypeSpecifier packed =
  63. ImageTypeSpecifier.createPacked(ColorSpace.getInstance(ColorSpace.CS_sRGB),
  64. 0xff000000,
  65. 0x00ff0000,
  66. 0x0000ff00,
  67. 0x000000ff,
  68. DataBuffer.TYPE_BYTE,
  69. false);
  70. SampleModel bandedSM =
  71. new java.awt.image.BandedSampleModel(DataBuffer.TYPE_BYTE,
  72. 1, 1, 15);
  73. System.out.println(createColorModel(bilevel.getSampleModel()));
  74. System.out.println(createColorModel(gray.getSampleModel()));
  75. System.out.println(createColorModel(grayAlpha.getSampleModel()));
  76. System.out.println(createColorModel(rgb.getSampleModel()));
  77. System.out.println(createColorModel(rgba.getSampleModel()));
  78. System.out.println(createColorModel(packed.getSampleModel()));
  79. System.out.println(createColorModel(bandedSM));
  80. }
  81. */
  82. /**
  83. * Creates a <code>ColorModel</code> that may be used with the
  84. * specified <code>SampleModel</code>. If a suitable
  85. * <code>ColorModel</code> cannot be found, this method returns
  86. * <code>null</code>.
  87. *
  88. * <p> Suitable <code>ColorModel</code>s are guaranteed to exist
  89. * for all instances of <code>ComponentSampleModel</code>.
  90. * For 1- and 3- banded <code>SampleModel</code>s, the returned
  91. * <code>ColorModel</code> will be opaque. For 2- and 4-banded
  92. * <code>SampleModel</code>s, the output will use alpha transparency
  93. * which is not premultiplied. 1- and 2-banded data will use a
  94. * grayscale <code>ColorSpace</code>, and 3- and 4-banded data a sRGB
  95. * <code>ColorSpace</code>. Data with 5 or more bands will have a
  96. * <code>BogusColorSpace</code>.</p>
  97. *
  98. * <p>An instance of <code>DirectColorModel</code> will be created for
  99. * instances of <code>SinglePixelPackedSampleModel</code> with no more
  100. * than 4 bands.</p>
  101. *
  102. * <p>An instance of <code>IndexColorModel</code> will be created for
  103. * instances of <code>MultiPixelPackedSampleModel</code>. The colormap
  104. * will be a grayscale ramp with <code>1 << numberOfBits</code>
  105. * entries ranging from zero to at most 255.</p>
  106. *
  107. * @return An instance of <code>ColorModel</code> that is suitable for
  108. * the supplied <code>SampleModel</code>, or <code>null</code>.
  109. *
  110. * @throws IllegalArgumentException If <code>sampleModel</code> is
  111. * <code>null</code>.
  112. */
  113. public static final ColorModel createColorModel(SampleModel sampleModel) {
  114. // Check the parameter.
  115. if(sampleModel == null) {
  116. throw new IllegalArgumentException("sampleModel == null!");
  117. }
  118. // Get the data type.
  119. int dataType = sampleModel.getDataType();
  120. // Check the data type
  121. switch(dataType) {
  122. case DataBuffer.TYPE_BYTE:
  123. case DataBuffer.TYPE_USHORT:
  124. case DataBuffer.TYPE_SHORT:
  125. case DataBuffer.TYPE_INT:
  126. case DataBuffer.TYPE_FLOAT:
  127. case DataBuffer.TYPE_DOUBLE:
  128. break;
  129. default:
  130. // Return null for other types.
  131. return null;
  132. }
  133. // The return variable.
  134. ColorModel colorModel = null;
  135. // Get the sample size.
  136. int[] sampleSize = sampleModel.getSampleSize();
  137. // Create a Component ColorModel.
  138. if(sampleModel instanceof ComponentSampleModel) {
  139. // Get the number of bands.
  140. int numBands = sampleModel.getNumBands();
  141. // Determine the color space.
  142. ColorSpace colorSpace = null;
  143. if(numBands <= 2) {
  144. colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY);
  145. } else if(numBands <= 4) {
  146. colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  147. } else {
  148. colorSpace = new BogusColorSpace(numBands);
  149. }
  150. boolean hasAlpha = (numBands == 2) || (numBands == 4);
  151. boolean isAlphaPremultiplied = false;
  152. int transparency = hasAlpha ?
  153. Transparency.TRANSLUCENT : Transparency.OPAQUE;
  154. colorModel = new ComponentColorModel(colorSpace,
  155. sampleSize,
  156. hasAlpha,
  157. isAlphaPremultiplied,
  158. transparency,
  159. dataType);
  160. } else if (sampleModel.getNumBands() <= 4 &&
  161. sampleModel instanceof SinglePixelPackedSampleModel) {
  162. SinglePixelPackedSampleModel sppsm =
  163. (SinglePixelPackedSampleModel)sampleModel;
  164. int[] bitMasks = sppsm.getBitMasks();
  165. int rmask = 0;
  166. int gmask = 0;
  167. int bmask = 0;
  168. int amask = 0;
  169. int numBands = bitMasks.length;
  170. if (numBands <= 2) {
  171. rmask = gmask = bmask = bitMasks[0];
  172. if (numBands == 2) {
  173. amask = bitMasks[1];
  174. }
  175. } else {
  176. rmask = bitMasks[0];
  177. gmask = bitMasks[1];
  178. bmask = bitMasks[2];
  179. if (numBands == 4) {
  180. amask = bitMasks[3];
  181. }
  182. }
  183. int bits = 0;
  184. for (int i = 0; i < sampleSize.length; i++) {
  185. bits += sampleSize[i];
  186. }
  187. return new DirectColorModel(bits, rmask, gmask, bmask, amask);
  188. } else if(sampleModel instanceof MultiPixelPackedSampleModel) {
  189. // Load the colormap with a ramp.
  190. int bitsPerSample = sampleSize[0];
  191. int numEntries = 1 << bitsPerSample;
  192. byte[] map = new byte[numEntries];
  193. for (int i = 0; i < numEntries; i++) {
  194. map[i] = (byte)(i*255/(numEntries - 1));
  195. }
  196. colorModel = new IndexColorModel(bitsPerSample, numEntries,
  197. map, map, map);
  198. }
  199. return colorModel;
  200. }
  201. /**
  202. * For the case of binary data (<code>isBinary()</code> returns
  203. * <code>true</code>), return the binary data as a packed byte array.
  204. * The data will be packed as eight bits per byte with no bit offset,
  205. * i.e., the first bit in each image line will be the left-most of the
  206. * first byte of the line. The line stride in bytes will be
  207. * <code>(int)((getWidth()+7)/8)</code>. The length of the returned
  208. * array will be the line stride multiplied by <code>getHeight()</code>
  209. *
  210. * @return the binary data as a packed array of bytes with zero offset
  211. * of <code>null</code> if the data are not binary.
  212. * @throws IllegalArgumentException if <code>isBinary()</code> returns
  213. * <code>false</code> with the <code>SampleModel</code> of the
  214. * supplied <code>Raster</code> as argument.
  215. */
  216. public static byte[] getPackedBinaryData(Raster raster,
  217. Rectangle rect) {
  218. SampleModel sm = raster.getSampleModel();
  219. if(!isBinary(sm)) {
  220. throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
  221. }
  222. int rectX = rect.x;
  223. int rectY = rect.y;
  224. int rectWidth = rect.width;
  225. int rectHeight = rect.height;
  226. DataBuffer dataBuffer = raster.getDataBuffer();
  227. int dx = rectX - raster.getSampleModelTranslateX();
  228. int dy = rectY - raster.getSampleModelTranslateY();
  229. MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
  230. int lineStride = mpp.getScanlineStride();
  231. int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
  232. int bitOffset = mpp.getBitOffset(dx);
  233. int numBytesPerRow = (rectWidth + 7)/8;
  234. if(dataBuffer instanceof DataBufferByte &&
  235. eltOffset == 0 && bitOffset == 0 &&
  236. numBytesPerRow == lineStride &&
  237. ((DataBufferByte)dataBuffer).getData().length ==
  238. numBytesPerRow*rectHeight) {
  239. return ((DataBufferByte)dataBuffer).getData();
  240. }
  241. byte[] binaryDataArray = new byte[numBytesPerRow*rectHeight];
  242. int b = 0;
  243. if(bitOffset == 0) {
  244. if(dataBuffer instanceof DataBufferByte) {
  245. byte[] data = ((DataBufferByte)dataBuffer).getData();
  246. int stride = numBytesPerRow;
  247. int offset = 0;
  248. for(int y = 0; y < rectHeight; y++) {
  249. System.arraycopy(data, eltOffset,
  250. binaryDataArray, offset,
  251. stride);
  252. offset += stride;
  253. eltOffset += lineStride;
  254. }
  255. } else if(dataBuffer instanceof DataBufferShort ||
  256. dataBuffer instanceof DataBufferUShort) {
  257. short[] data = dataBuffer instanceof DataBufferShort ?
  258. ((DataBufferShort)dataBuffer).getData() :
  259. ((DataBufferUShort)dataBuffer).getData();
  260. for(int y = 0; y < rectHeight; y++) {
  261. int xRemaining = rectWidth;
  262. int i = eltOffset;
  263. while(xRemaining > 8) {
  264. short datum = data[i++];
  265. binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF);
  266. binaryDataArray[b++] = (byte)(datum & 0xFF);
  267. xRemaining -= 16;
  268. }
  269. if(xRemaining > 0) {
  270. binaryDataArray[b++] = (byte)((data[i] >>> 8) & 0XFF);
  271. }
  272. eltOffset += lineStride;
  273. }
  274. } else if(dataBuffer instanceof DataBufferInt) {
  275. int[] data = ((DataBufferInt)dataBuffer).getData();
  276. for(int y = 0; y < rectHeight; y++) {
  277. int xRemaining = rectWidth;
  278. int i = eltOffset;
  279. while(xRemaining > 24) {
  280. int datum = data[i++];
  281. binaryDataArray[b++] = (byte)((datum >>> 24) & 0xFF);
  282. binaryDataArray[b++] = (byte)((datum >>> 16) & 0xFF);
  283. binaryDataArray[b++] = (byte)((datum >>> 8) & 0xFF);
  284. binaryDataArray[b++] = (byte)(datum & 0xFF);
  285. xRemaining -= 32;
  286. }
  287. int shift = 24;
  288. while(xRemaining > 0) {
  289. binaryDataArray[b++] =
  290. (byte)((data[i] >>> shift) & 0xFF);
  291. shift -= 8;
  292. xRemaining -= 8;
  293. }
  294. eltOffset += lineStride;
  295. }
  296. }
  297. } else { // bitOffset != 0
  298. if(dataBuffer instanceof DataBufferByte) {
  299. byte[] data = ((DataBufferByte)dataBuffer).getData();
  300. if((bitOffset & 7) == 0) {
  301. int stride = numBytesPerRow;
  302. int offset = 0;
  303. for(int y = 0; y < rectHeight; y++) {
  304. System.arraycopy(data, eltOffset,
  305. binaryDataArray, offset,
  306. stride);
  307. offset += stride;
  308. eltOffset += lineStride;
  309. }
  310. } else { // bitOffset % 8 != 0
  311. int leftShift = bitOffset & 7;
  312. int rightShift = 8 - leftShift;
  313. for(int y = 0; y < rectHeight; y++) {
  314. int i = eltOffset;
  315. int xRemaining = rectWidth;
  316. while(xRemaining > 0) {
  317. if(xRemaining > rightShift) {
  318. binaryDataArray[b++] =
  319. (byte)(((data[i++]&0xFF) << leftShift) |
  320. ((data[i]&0xFF) >>> rightShift));
  321. } else {
  322. binaryDataArray[b++] =
  323. (byte)((data[i]&0xFF) << leftShift);
  324. }
  325. xRemaining -= 8;
  326. }
  327. eltOffset += lineStride;
  328. }
  329. }
  330. } else if(dataBuffer instanceof DataBufferShort ||
  331. dataBuffer instanceof DataBufferUShort) {
  332. short[] data = dataBuffer instanceof DataBufferShort ?
  333. ((DataBufferShort)dataBuffer).getData() :
  334. ((DataBufferUShort)dataBuffer).getData();
  335. for(int y = 0; y < rectHeight; y++) {
  336. int bOffset = bitOffset;
  337. for(int x = 0; x < rectWidth; x += 8, bOffset += 8) {
  338. int i = eltOffset + bOffset16;
  339. int mod = bOffset % 16;
  340. int left = data[i] & 0xFFFF;
  341. if(mod <= 8) {
  342. binaryDataArray[b++] = (byte)(left >>> (8 - mod));
  343. } else {
  344. int delta = mod - 8;
  345. int right = data[i+1] & 0xFFFF;
  346. binaryDataArray[b++] =
  347. (byte)((left << delta) |
  348. (right >>> (16 - delta)));
  349. }
  350. }
  351. eltOffset += lineStride;
  352. }
  353. } else if(dataBuffer instanceof DataBufferInt) {
  354. int[] data = ((DataBufferInt)dataBuffer).getData();
  355. for(int y = 0; y < rectHeight; y++) {
  356. int bOffset = bitOffset;
  357. for(int x = 0; x < rectWidth; x += 8, bOffset += 8) {
  358. int i = eltOffset + bOffset32;
  359. int mod = bOffset % 32;
  360. int left = data[i];
  361. if(mod <= 24) {
  362. binaryDataArray[b++] =
  363. (byte)(left >>> (24 - mod));
  364. } else {
  365. int delta = mod - 24;
  366. int right = data[i+1];
  367. binaryDataArray[b++] =
  368. (byte)((left << delta) |
  369. (right >>> (32 - delta)));
  370. }
  371. }
  372. eltOffset += lineStride;
  373. }
  374. }
  375. }
  376. return binaryDataArray;
  377. }
  378. /**
  379. * Returns the binary data unpacked into an array of bytes.
  380. * The line stride will be the width of the <code>Raster</code>.
  381. *
  382. * @throws IllegalArgumentException if <code>isBinary()</code> returns
  383. * <code>false</code> with the <code>SampleModel</code> of the
  384. * supplied <code>Raster</code> as argument.
  385. */
  386. public static byte[] getUnpackedBinaryData(Raster raster,
  387. Rectangle rect) {
  388. SampleModel sm = raster.getSampleModel();
  389. if(!isBinary(sm)) {
  390. throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
  391. }
  392. int rectX = rect.x;
  393. int rectY = rect.y;
  394. int rectWidth = rect.width;
  395. int rectHeight = rect.height;
  396. DataBuffer dataBuffer = raster.getDataBuffer();
  397. int dx = rectX - raster.getSampleModelTranslateX();
  398. int dy = rectY - raster.getSampleModelTranslateY();
  399. MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
  400. int lineStride = mpp.getScanlineStride();
  401. int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
  402. int bitOffset = mpp.getBitOffset(dx);
  403. byte[] bdata = new byte[rectWidth*rectHeight];
  404. int maxY = rectY + rectHeight;
  405. int maxX = rectX + rectWidth;
  406. int k = 0;
  407. if(dataBuffer instanceof DataBufferByte) {
  408. byte[] data = ((DataBufferByte)dataBuffer).getData();
  409. for(int y = rectY; y < maxY; y++) {
  410. int bOffset = eltOffset*8 + bitOffset;
  411. for(int x = rectX; x < maxX; x++) {
  412. byte b = data[bOffset8];
  413. bdata[k++] =
  414. (byte)((b >>> (7 - bOffset & 7)) & 0x0000001);
  415. bOffset++;
  416. }
  417. eltOffset += lineStride;
  418. }
  419. } else if(dataBuffer instanceof DataBufferShort ||
  420. dataBuffer instanceof DataBufferUShort) {
  421. short[] data = dataBuffer instanceof DataBufferShort ?
  422. ((DataBufferShort)dataBuffer).getData() :
  423. ((DataBufferUShort)dataBuffer).getData();
  424. for(int y = rectY; y < maxY; y++) {
  425. int bOffset = eltOffset*16 + bitOffset;
  426. for(int x = rectX; x < maxX; x++) {
  427. short s = data[bOffset16];
  428. bdata[k++] =
  429. (byte)((s >>> (15 - bOffset % 16)) &
  430. 0x0000001);
  431. bOffset++;
  432. }
  433. eltOffset += lineStride;
  434. }
  435. } else if(dataBuffer instanceof DataBufferInt) {
  436. int[] data = ((DataBufferInt)dataBuffer).getData();
  437. for(int y = rectY; y < maxY; y++) {
  438. int bOffset = eltOffset*32 + bitOffset;
  439. for(int x = rectX; x < maxX; x++) {
  440. int i = data[bOffset32];
  441. bdata[k++] =
  442. (byte)((i >>> (31 - bOffset % 32)) &
  443. 0x0000001);
  444. bOffset++;
  445. }
  446. eltOffset += lineStride;
  447. }
  448. }
  449. return bdata;
  450. }
  451. /**
  452. * Sets the supplied <code>Raster</code>'s data from an array
  453. * of packed binary data of the form returned by
  454. * <code>getPackedBinaryData()</code>.
  455. *
  456. * @throws IllegalArgumentException if <code>isBinary()</code> returns
  457. * <code>false</code> with the <code>SampleModel</code> of the
  458. * supplied <code>Raster</code> as argument.
  459. */
  460. public static void setPackedBinaryData(byte[] binaryDataArray,
  461. WritableRaster raster,
  462. Rectangle rect) {
  463. SampleModel sm = raster.getSampleModel();
  464. if(!isBinary(sm)) {
  465. throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
  466. }
  467. int rectX = rect.x;
  468. int rectY = rect.y;
  469. int rectWidth = rect.width;
  470. int rectHeight = rect.height;
  471. DataBuffer dataBuffer = raster.getDataBuffer();
  472. int dx = rectX - raster.getSampleModelTranslateX();
  473. int dy = rectY - raster.getSampleModelTranslateY();
  474. MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
  475. int lineStride = mpp.getScanlineStride();
  476. int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
  477. int bitOffset = mpp.getBitOffset(dx);
  478. int b = 0;
  479. if(bitOffset == 0) {
  480. if(dataBuffer instanceof DataBufferByte) {
  481. byte[] data = ((DataBufferByte)dataBuffer).getData();
  482. if(data == binaryDataArray) {
  483. // Optimal case: simply return.
  484. return;
  485. }
  486. int stride = (rectWidth + 7)/8;
  487. int offset = 0;
  488. for(int y = 0; y < rectHeight; y++) {
  489. System.arraycopy(binaryDataArray, offset,
  490. data, eltOffset,
  491. stride);
  492. offset += stride;
  493. eltOffset += lineStride;
  494. }
  495. } else if(dataBuffer instanceof DataBufferShort ||
  496. dataBuffer instanceof DataBufferUShort) {
  497. short[] data = dataBuffer instanceof DataBufferShort ?
  498. ((DataBufferShort)dataBuffer).getData() :
  499. ((DataBufferUShort)dataBuffer).getData();
  500. for(int y = 0; y < rectHeight; y++) {
  501. int xRemaining = rectWidth;
  502. int i = eltOffset;
  503. while(xRemaining > 8) {
  504. data[i++] =
  505. (short)(((binaryDataArray[b++] & 0xFF) << 8) |
  506. (binaryDataArray[b++] & 0xFF));
  507. xRemaining -= 16;
  508. }
  509. if(xRemaining > 0) {
  510. data[i++] =
  511. (short)((binaryDataArray[b++] & 0xFF) << 8);
  512. }
  513. eltOffset += lineStride;
  514. }
  515. } else if(dataBuffer instanceof DataBufferInt) {
  516. int[] data = ((DataBufferInt)dataBuffer).getData();
  517. for(int y = 0; y < rectHeight; y++) {
  518. int xRemaining = rectWidth;
  519. int i = eltOffset;
  520. while(xRemaining > 24) {
  521. data[i++] =
  522. (int)(((binaryDataArray[b++] & 0xFF) << 24) |
  523. ((binaryDataArray[b++] & 0xFF) << 16) |
  524. ((binaryDataArray[b++] & 0xFF) << 8) |
  525. (binaryDataArray[b++] & 0xFF));
  526. xRemaining -= 32;
  527. }
  528. int shift = 24;
  529. while(xRemaining > 0) {
  530. data[i] |=
  531. (int)((binaryDataArray[b++] & 0xFF) << shift);
  532. shift -= 8;
  533. xRemaining -= 8;
  534. }
  535. eltOffset += lineStride;
  536. }
  537. }
  538. } else { // bitOffset != 0
  539. int stride = (rectWidth + 7)/8;
  540. int offset = 0;
  541. if(dataBuffer instanceof DataBufferByte) {
  542. byte[] data = ((DataBufferByte)dataBuffer).getData();
  543. if((bitOffset & 7) == 0) {
  544. for(int y = 0; y < rectHeight; y++) {
  545. System.arraycopy(binaryDataArray, offset,
  546. data, eltOffset,
  547. stride);
  548. offset += stride;
  549. eltOffset += lineStride;
  550. }
  551. } else { // bitOffset % 8 != 0
  552. int rightShift = bitOffset & 7;
  553. int leftShift = 8 - rightShift;
  554. int leftShift8 = 8 + leftShift;
  555. int mask = (byte)(255<<leftShift);
  556. int mask1 = (byte)~mask;
  557. for(int y = 0; y < rectHeight; y++) {
  558. int i = eltOffset;
  559. int xRemaining = rectWidth;
  560. while(xRemaining > 0) {
  561. byte datum = binaryDataArray[b++];
  562. if (xRemaining > leftShift8) {
  563. // when all the bits in this BYTE will be set
  564. // into the data buffer.
  565. data[i] = (byte)((data[i] & mask ) |
  566. ((datum&0xFF) >>> rightShift));
  567. data[++i] = (byte)((datum & 0xFF) << leftShift);
  568. } else if (xRemaining > leftShift) {
  569. // All the "leftShift" high bits will be set
  570. // into the data buffer. But not all the
  571. // "rightShift" low bits will be set.
  572. data[i] = (byte)((data[i] & mask ) |
  573. ((datum&0xFF) >>> rightShift));
  574. i++;
  575. data[i] =
  576. (byte)((data[i] & mask1) | ((datum & 0xFF) << leftShift));
  577. }
  578. else {
  579. // Less than "leftShift" high bits will be set.
  580. int remainMask = (1 << leftShift - xRemaining) - 1;
  581. data[i] =
  582. (byte)((data[i] & (mask | remainMask)) |
  583. (datum&0xFF) >>> rightShift & ~remainMask);
  584. }
  585. xRemaining -= 8;
  586. }
  587. eltOffset += lineStride;
  588. }
  589. }
  590. } else if(dataBuffer instanceof DataBufferShort ||
  591. dataBuffer instanceof DataBufferUShort) {
  592. short[] data = dataBuffer instanceof DataBufferShort ?
  593. ((DataBufferShort)dataBuffer).getData() :
  594. ((DataBufferUShort)dataBuffer).getData();
  595. int rightShift = bitOffset & 7;
  596. int leftShift = 8 - rightShift;
  597. int leftShift16 = 16 + leftShift;
  598. int mask = (short)(~(255 << leftShift));
  599. int mask1 = (short)(65535 << leftShift);
  600. int mask2 = (short)~mask1;
  601. for(int y = 0; y < rectHeight; y++) {
  602. int bOffset = bitOffset;
  603. int xRemaining = rectWidth;
  604. for(int x = 0; x < rectWidth;
  605. x += 8, bOffset += 8, xRemaining -= 8) {
  606. int i = eltOffset + (bOffset >> 4);
  607. int mod = bOffset & 15;
  608. int datum = binaryDataArray[b++] & 0xFF;
  609. if(mod <= 8) {
  610. // This BYTE is set into one SHORT
  611. if (xRemaining < 8) {
  612. // Mask the bits to be set.
  613. datum &= 255 << 8 - xRemaining;
  614. }
  615. data[i] = (short)((data[i] & mask) | (datum << leftShift));
  616. } else if (xRemaining > leftShift16) {
  617. // This BYTE will be set into two SHORTs
  618. data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF));
  619. data[++i] =
  620. (short)((datum << leftShift)&0xFFFF);
  621. } else if (xRemaining > leftShift) {
  622. // This BYTE will be set into two SHORTs;
  623. // But not all the low bits will be set into SHORT
  624. data[i] = (short)((data[i] & mask1) | ((datum >>> rightShift)&0xFFFF));
  625. i++;
  626. data[i] =
  627. (short)((data[i] & mask2) | ((datum << leftShift)&0xFFFF));
  628. } else {
  629. // Only some of the high bits will be set into
  630. // SHORTs
  631. int remainMask = (1 << leftShift - xRemaining) - 1;
  632. data[i] = (short)((data[i] & (mask1 | remainMask)) |
  633. ((datum >>> rightShift)&0xFFFF & ~remainMask));
  634. }
  635. }
  636. eltOffset += lineStride;
  637. }
  638. } else if(dataBuffer instanceof DataBufferInt) {
  639. int[] data = ((DataBufferInt)dataBuffer).getData();
  640. int rightShift = bitOffset & 7;
  641. int leftShift = 8 - rightShift;
  642. int leftShift32 = 32 + leftShift;
  643. int mask = 0xFFFFFFFF << leftShift;
  644. int mask1 = ~mask;
  645. for(int y = 0; y < rectHeight; y++) {
  646. int bOffset = bitOffset;
  647. int xRemaining = rectWidth;
  648. for(int x = 0; x < rectWidth;
  649. x += 8, bOffset += 8, xRemaining -= 8) {
  650. int i = eltOffset + (bOffset >> 5);
  651. int mod = bOffset & 31;
  652. int datum = binaryDataArray[b++] & 0xFF;
  653. if(mod <= 24) {
  654. // This BYTE is set into one INT
  655. int shift = 24 - mod;
  656. if (xRemaining < 8) {
  657. // Mask the bits to be set.
  658. datum &= 255 << 8 - xRemaining;
  659. }
  660. data[i] = (data[i] & (~(255 << shift))) | (datum << shift);
  661. } else if (xRemaining > leftShift32) {
  662. // All the bits of this BYTE will be set into two INTs
  663. data[i] = (data[i] & mask) | (datum >>> rightShift);
  664. data[++i] = datum << leftShift;
  665. } else if (xRemaining > leftShift) {
  666. // This BYTE will be set into two INTs;
  667. // But not all the low bits will be set into INT
  668. data[i] = (data[i] & mask) | (datum >>> rightShift);
  669. i++;
  670. data[i] = (data[i] & mask1) | (datum << leftShift);
  671. } else {
  672. // Only some of the high bits will be set into INT
  673. int remainMask = (1 << leftShift - xRemaining) - 1;
  674. data[i] = (data[i] & (mask | remainMask)) |
  675. (datum >>> rightShift & ~remainMask);
  676. }
  677. }
  678. eltOffset += lineStride;
  679. }
  680. }
  681. }
  682. }
  683. /**
  684. * Copies data into the packed array of the <code>Raster</code>
  685. * from an array of unpacked data of the form returned by
  686. * <code>getUnpackedBinaryData()</code>.
  687. *
  688. * <p> If the data are binary, then the target bit will be set if
  689. * and only if the corresponding byte is non-zero.
  690. *
  691. * @throws IllegalArgumentException if <code>isBinary()</code> returns
  692. * <code>false</code> with the <code>SampleModel</code> of the
  693. * supplied <code>Raster</code> as argument.
  694. */
  695. public static void setUnpackedBinaryData(byte[] bdata,
  696. WritableRaster raster,
  697. Rectangle rect) {
  698. SampleModel sm = raster.getSampleModel();
  699. if(!isBinary(sm)) {
  700. throw new IllegalArgumentException(I18N.getString("ImageUtil0"));
  701. }
  702. int rectX = rect.x;
  703. int rectY = rect.y;
  704. int rectWidth = rect.width;
  705. int rectHeight = rect.height;
  706. DataBuffer dataBuffer = raster.getDataBuffer();
  707. int dx = rectX - raster.getSampleModelTranslateX();
  708. int dy = rectY - raster.getSampleModelTranslateY();
  709. MultiPixelPackedSampleModel mpp = (MultiPixelPackedSampleModel)sm;
  710. int lineStride = mpp.getScanlineStride();
  711. int eltOffset = dataBuffer.getOffset() + mpp.getOffset(dx, dy);
  712. int bitOffset = mpp.getBitOffset(dx);
  713. int k = 0;
  714. if(dataBuffer instanceof DataBufferByte) {
  715. byte[] data = ((DataBufferByte)dataBuffer).getData();
  716. for(int y = 0; y < rectHeight; y++) {
  717. int bOffset = eltOffset*8 + bitOffset;
  718. for(int x = 0; x < rectWidth; x++) {
  719. if(bdata[k++] != (byte)0) {
  720. data[bOffset8] |=
  721. (byte)(0x00000001 << (7 - bOffset & 7));
  722. }
  723. bOffset++;
  724. }
  725. eltOffset += lineStride;
  726. }
  727. } else if(dataBuffer instanceof DataBufferShort ||
  728. dataBuffer instanceof DataBufferUShort) {
  729. short[] data = dataBuffer instanceof DataBufferShort ?
  730. ((DataBufferShort)dataBuffer).getData() :
  731. ((DataBufferUShort)dataBuffer).getData();
  732. for(int y = 0; y < rectHeight; y++) {
  733. int bOffset = eltOffset*16 + bitOffset;
  734. for(int x = 0; x < rectWidth; x++) {
  735. if(bdata[k++] != (byte)0) {
  736. data[bOffset16] |=
  737. (short)(0x00000001 <<
  738. (15 - bOffset % 16));
  739. }
  740. bOffset++;
  741. }
  742. eltOffset += lineStride;
  743. }
  744. } else if(dataBuffer instanceof DataBufferInt) {
  745. int[] data = ((DataBufferInt)dataBuffer).getData();
  746. for(int y = 0; y < rectHeight; y++) {
  747. int bOffset = eltOffset*32 + bitOffset;
  748. for(int x = 0; x < rectWidth; x++) {
  749. if(bdata[k++] != (byte)0) {
  750. data[bOffset32] |=
  751. (int)(0x00000001 <<
  752. (31 - bOffset % 32));
  753. }
  754. bOffset++;
  755. }
  756. eltOffset += lineStride;
  757. }
  758. }
  759. }
  760. public static boolean isBinary(SampleModel sm) {
  761. return sm instanceof MultiPixelPackedSampleModel &&
  762. ((MultiPixelPackedSampleModel)sm).getPixelBitStride() == 1 &&
  763. sm.getNumBands() == 1;
  764. }
  765. public static ColorModel createColorModel(ColorSpace colorSpace,
  766. SampleModel sampleModel) {
  767. ColorModel colorModel = null;
  768. if(sampleModel == null) {
  769. throw new IllegalArgumentException(I18N.getString("ImageUtil1"));
  770. }
  771. int numBands = sampleModel.getNumBands();
  772. if (numBands < 1 || numBands > 4) {
  773. return null;
  774. }
  775. int dataType = sampleModel.getDataType();
  776. if (sampleModel instanceof ComponentSampleModel) {
  777. if (dataType < DataBuffer.TYPE_BYTE ||
  778. //dataType == DataBuffer.TYPE_SHORT ||
  779. dataType > DataBuffer.TYPE_DOUBLE) {
  780. return null;
  781. }
  782. if (colorSpace == null)
  783. colorSpace =
  784. numBands <= 2 ?
  785. ColorSpace.getInstance(ColorSpace.CS_GRAY) :
  786. ColorSpace.getInstance(ColorSpace.CS_sRGB);
  787. boolean useAlpha = (numBands == 2) || (numBands == 4);
  788. int transparency = useAlpha ?
  789. Transparency.TRANSLUCENT : Transparency.OPAQUE;
  790. boolean premultiplied = false;
  791. int dataTypeSize = DataBuffer.getDataTypeSize(dataType);
  792. int[] bits = new int[numBands];
  793. for (int i = 0; i < numBands; i++) {
  794. bits[i] = dataTypeSize;
  795. }
  796. colorModel = new ComponentColorModel(colorSpace,
  797. bits,
  798. useAlpha,
  799. premultiplied,
  800. transparency,
  801. dataType);
  802. } else if (sampleModel instanceof SinglePixelPackedSampleModel) {
  803. SinglePixelPackedSampleModel sppsm =
  804. (SinglePixelPackedSampleModel)sampleModel;
  805. int[] bitMasks = sppsm.getBitMasks();
  806. int rmask = 0;
  807. int gmask = 0;
  808. int bmask = 0;
  809. int amask = 0;
  810. numBands = bitMasks.length;
  811. if (numBands <= 2) {
  812. rmask = gmask = bmask = bitMasks[0];
  813. if (numBands == 2) {
  814. amask = bitMasks[1];
  815. }
  816. } else {
  817. rmask = bitMasks[0];
  818. gmask = bitMasks[1];
  819. bmask = bitMasks[2];
  820. if (numBands == 4) {
  821. amask = bitMasks[3];
  822. }
  823. }
  824. int[] sampleSize = sppsm.getSampleSize();
  825. int bits = 0;
  826. for (int i = 0; i < sampleSize.length; i++) {
  827. bits += sampleSize[i];
  828. }
  829. if (colorSpace == null)
  830. colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
  831. colorModel =
  832. new DirectColorModel(colorSpace,
  833. bits, rmask, gmask, bmask, amask,
  834. false,
  835. sampleModel.getDataType());
  836. } else if (sampleModel instanceof MultiPixelPackedSampleModel) {
  837. int bits =
  838. ((MultiPixelPackedSampleModel)sampleModel).getPixelBitStride();
  839. int size = 1 << bits;
  840. byte[] comp = new byte[size];
  841. for (int i = 0; i < size; i++)
  842. comp[i] = (byte)(255 * i / (size - 1));
  843. colorModel = new IndexColorModel(bits, size, comp, comp, comp);
  844. }
  845. return colorModel;
  846. }
  847. public static int getElementSize(SampleModel sm) {
  848. int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
  849. if (sm instanceof MultiPixelPackedSampleModel) {
  850. MultiPixelPackedSampleModel mppsm =
  851. (MultiPixelPackedSampleModel)sm;
  852. return mppsm.getSampleSize(0) * mppsm.getNumBands();
  853. } else if (sm instanceof ComponentSampleModel) {
  854. return sm.getNumBands() * elementSize;
  855. } else if (sm instanceof SinglePixelPackedSampleModel) {
  856. return elementSize;
  857. }
  858. return elementSize * sm.getNumBands();
  859. }
  860. public static long getTileSize(SampleModel sm) {
  861. int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
  862. if (sm instanceof MultiPixelPackedSampleModel) {
  863. MultiPixelPackedSampleModel mppsm =
  864. (MultiPixelPackedSampleModel)sm;
  865. return (mppsm.getScanlineStride() * mppsm.getHeight() +
  866. (mppsm.getDataBitOffset() + elementSize -1) / elementSize) *
  867. ((elementSize + 7) / 8);
  868. } else if (sm instanceof ComponentSampleModel) {
  869. ComponentSampleModel csm = (ComponentSampleModel)sm;
  870. int[] bandOffsets = csm.getBandOffsets();
  871. int maxBandOff = bandOffsets[0];
  872. for (int i=1; i<bandOffsets.length; i++)
  873. maxBandOff = Math.max(maxBandOff, bandOffsets[i]);
  874. long size = 0;
  875. int pixelStride = csm.getPixelStride();
  876. int scanlineStride = csm.getScanlineStride();
  877. if (maxBandOff >= 0)
  878. size += maxBandOff + 1;
  879. if (pixelStride > 0)
  880. size += pixelStride * (sm.getWidth() - 1);
  881. if (scanlineStride > 0)
  882. size += scanlineStride * (sm.getHeight() - 1);
  883. int[] bankIndices = csm.getBankIndices();
  884. maxBandOff = bankIndices[0];
  885. for (int i=1; i<bankIndices.length; i++)
  886. maxBandOff = Math.max(maxBandOff, bankIndices[i]);
  887. return size * (maxBandOff + 1) * ((elementSize + 7) / 8);
  888. } else if (sm instanceof SinglePixelPackedSampleModel) {
  889. SinglePixelPackedSampleModel sppsm =
  890. (SinglePixelPackedSampleModel)sm;
  891. long size = sppsm.getScanlineStride() * (sppsm.getHeight() - 1) +
  892. sppsm.getWidth();
  893. return size * ((elementSize + 7) / 8);
  894. }
  895. return 0;
  896. }
  897. public static long getBandSize(SampleModel sm) {
  898. int elementSize = DataBuffer.getDataTypeSize(sm.getDataType());
  899. if (sm instanceof ComponentSampleModel) {
  900. ComponentSampleModel csm = (ComponentSampleModel)sm;
  901. int pixelStride = csm.getPixelStride();
  902. int scanlineStride = csm.getScanlineStride();
  903. long size = Math.min(pixelStride, scanlineStride);
  904. if (pixelStride > 0)
  905. size += pixelStride * (sm.getWidth() - 1);
  906. if (scanlineStride > 0)
  907. size += scanlineStride * (sm.getHeight() - 1);
  908. return size * ((elementSize + 7) / 8);
  909. } else
  910. return getTileSize(sm);
  911. }
  912. /**
  913. * Tests whether the color indices represent a gray-scale image.
  914. *
  915. * @param r The red channel color indices.
  916. * @param g The green channel color indices.
  917. * @param b The blue channel color indices.
  918. * @return If all the indices have 256 entries, and are identical mappings,
  919. * return <code>true</code> otherwise, return <code>false</code>.
  920. */
  921. public static boolean isIndicesForGrayscale(byte[] r, byte[] g, byte[] b) {
  922. if (r.length != g.length || r.length != b.length)
  923. return false;
  924. int size = r.length;
  925. if (size != 256)
  926. return false;
  927. for (int i = 0; i < size; i++) {
  928. byte temp = (byte) i;
  929. if (r[i] != temp || g[i] != temp || b[i] != temp)
  930. return false;
  931. }
  932. return true;
  933. }
  934. /** Converts the provided object to <code>String</code> */
  935. public static String convertObjectToString(Object obj) {
  936. if (obj == null)
  937. return "";
  938. String s = "";
  939. if (obj instanceof byte[]) {
  940. byte[] bArray = (byte[])obj;
  941. for (int i = 0; i < bArray.length; i++)
  942. s += bArray[i] + " ";
  943. return s;
  944. }
  945. if (obj instanceof int[]) {
  946. int[] iArray = (int[])obj;
  947. for (int i = 0; i < iArray.length; i++)
  948. s += iArray[i] + " " ;
  949. return s;
  950. }
  951. if (obj instanceof short[]) {
  952. short[] sArray = (short[])obj;
  953. for (int i = 0; i < sArray.length; i++)
  954. s += sArray[i] + " " ;
  955. return s;
  956. }
  957. return obj.toString();
  958. }
  959. /** Checks that the provided <code>ImageWriter</code> can encode
  960. * the provided <code>ImageTypeSpecifier</code> or not. If not, an
  961. * <code>IIOException</code> will be thrown.
  962. * @param writer The provided <code>ImageWriter</code>.
  963. * @param type The image to be tested.
  964. * @throws IIOException If the writer cannot encoded the provided image.
  965. */
  966. public static final void canEncodeImage(ImageWriter writer,
  967. ImageTypeSpecifier type)
  968. throws IIOException {
  969. ImageWriterSpi spi = writer.getOriginatingProvider();
  970. if(type != null && spi != null && !spi.canEncodeImage(type)) {
  971. throw new IIOException(I18N.getString("ImageUtil2")+" "+
  972. writer.getClass().getName());
  973. }
  974. }
  975. /** Checks that the provided <code>ImageWriter</code> can encode
  976. * the provided <code>ColorModel</code> and <code>SampleModel</code>.
  977. * If not, an <code>IIOException</code> will be thrown.
  978. * @param writer The provided <code>ImageWriter</code>.
  979. * @param colorModel The provided <code>ColorModel</code>.
  980. * @param sampleModel The provided <code>SampleModel</code>.
  981. * @throws IIOException If the writer cannot encoded the provided image.
  982. */
  983. public static final void canEncodeImage(ImageWriter writer,
  984. ColorModel colorModel,
  985. SampleModel sampleModel)
  986. throws IIOException {
  987. ImageTypeSpecifier type = null;
  988. if (colorModel != null && sampleModel != null)
  989. type = new ImageTypeSpecifier(colorModel, sampleModel);
  990. canEncodeImage(writer, type);
  991. }
  992. /**
  993. * Returns whether the image has contiguous data across rows.
  994. */
  995. public static final boolean imageIsContiguous(RenderedImage image) {
  996. SampleModel sm;
  997. if(image instanceof BufferedImage) {
  998. WritableRaster ras = ((BufferedImage)image).getRaster();
  999. sm = ras.getSampleModel();
  1000. } else {
  1001. sm = image.getSampleModel();
  1002. }
  1003. if (sm instanceof ComponentSampleModel) {
  1004. // Ensure image rows samples are stored contiguously
  1005. // in a single bank.
  1006. ComponentSampleModel csm = (ComponentSampleModel)sm;
  1007. if (csm.getPixelStride() != csm.getNumBands()) {
  1008. return false;
  1009. }
  1010. int[] bandOffsets = csm.getBandOffsets();
  1011. for (int i = 0; i < bandOffsets.length; i++) {
  1012. if (bandOffsets[i] != i) {
  1013. return false;
  1014. }
  1015. }
  1016. int[] bankIndices = csm.getBankIndices();
  1017. for (int i = 0; i < bandOffsets.length; i++) {
  1018. if (bankIndices[i] != 0) {
  1019. return false;
  1020. }
  1021. }
  1022. return true;
  1023. }
  1024. // Otherwise true if and only if it's a bilevel image with
  1025. // a MultiPixelPackedSampleModel, 1 bit per pixel, and 1 bit
  1026. // pixel stride.
  1027. return ImageUtil.isBinary(sm);
  1028. }
  1029. }