001/* 002 * **** BEGIN LICENSE BLOCK ***** 003 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 004 * 005 * The contents of this file are subject to the Mozilla Public License Version 006 * 1.1 (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * http://www.mozilla.org/MPL/ 009 * 010 * Software distributed under the License is distributed on an "AS IS" basis, 011 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 012 * for the specific language governing rights and limitations under the 013 * License. 014 * 015 * The Original Code is part of dcm4che, an implementation of DICOM(TM) in 016 * Java(TM), hosted at https://github.com/dcm4che. 017 * 018 * The Initial Developer of the Original Code is Agfa Healthcare. 019 * Portions created by the Initial Developer are Copyright (C) 2011-2015 020 * the Initial Developer. All Rights Reserved. 021 * 022 * Contributor(s): 023 * See @authors listed below 024 * 025 * Alternatively, the contents of this file may be used under the terms of 026 * either the GNU General Public License Version 2 or later (the "GPL"), or 027 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 028 * in which case the provisions of the GPL or the LGPL are applicable instead 029 * of those above. If you wish to allow use of your version of this file only 030 * under the terms of either the GPL or the LGPL, and not to allow others to 031 * use your version of this file under the terms of the MPL, indicate your 032 * decision by deleting the provisions above and replace them with the notice 033 * and other provisions required by the GPL or the LGPL. If you do not delete 034 * the provisions above, a recipient may use your version of this file under 035 * the terms of any one of the MPL, the GPL or the LGPL. 036 * 037 * **** END LICENSE BLOCK ***** 038 */ 039 040package org.dcm4che3.imageio.codec; 041 042import java.awt.*; 043import java.awt.color.ColorSpace; 044import java.awt.image.*; 045import java.io.IOException; 046import java.io.OutputStream; 047 048/** 049 * @author Gunter Zeilinger <gunterze@gmail.com> 050 * @since Feb 2015. 051 */ 052class BufferedImageUtils { 053 054 public static BufferedImage createBufferedImage(ImageParams imageParams, TransferSyntaxType tsType) { 055 int dataType = imageParams.getBitsAllocated() > 8 056 ? (imageParams.isSigned() && (tsType == null || tsType.canEncodeSigned()) 057 ? DataBuffer.TYPE_SHORT 058 : DataBuffer.TYPE_USHORT) 059 : DataBuffer.TYPE_BYTE; 060 int samples = imageParams.getSamples(); 061 int bitsStored = tsType == null 062 ? imageParams.getBitsStored() 063 : Math.min(imageParams.getBitsStored(), tsType.getMaxBitsStored()); 064 ComponentColorModel cm = samples == 1 065 ? new ComponentColorModel( 066 ColorSpace.getInstance(ColorSpace.CS_GRAY), 067 new int[] { bitsStored }, 068 false, // hasAlpha 069 false, // isAlphaPremultiplied, 070 Transparency.OPAQUE, 071 dataType) 072 : new ComponentColorModel( 073 ColorSpace.getInstance(ColorSpace.CS_sRGB), 074 new int[] { bitsStored, bitsStored, bitsStored }, 075 false, // hasAlpha 076 false, // isAlphaPremultiplied, 077 Transparency.OPAQUE, 078 dataType); 079 080 int rows = imageParams.getRows(); 081 int columns = imageParams.getColumns(); 082 SampleModel sm = imageParams.isBanded() 083 ? new BandedSampleModel(dataType, columns, rows, samples) 084 : new PixelInterleavedSampleModel(dataType, columns, rows, 085 samples, columns * samples, bandOffsets(samples)); 086 WritableRaster raster = Raster.createWritableRaster(sm, null); 087 return new BufferedImage(cm, raster, false, null); 088 } 089 090 private static int[] bandOffsets(int samples) { 091 int[] offsets = new int[samples]; 092 for (int i = 0; i < samples; i++) 093 offsets[i] = i; 094 return offsets; 095 } 096 097 public static int sizeOf(BufferedImage bi) { 098 WritableRaster raster = bi.getRaster(); 099 DataBuffer db = raster.getDataBuffer(); 100 return db.getSize() * db.getNumBanks() 101 * (DataBuffer.getDataTypeSize(db.getDataType()) >>> 3); 102 } 103 104 public static void writeTo(BufferedImage bi, OutputStream out) throws IOException { 105 WritableRaster raster = bi.getRaster(); 106 SampleModel sm = raster.getSampleModel(); 107 DataBuffer db = raster.getDataBuffer(); 108 switch (db.getDataType()) { 109 case DataBuffer.TYPE_BYTE: 110 writeTo(sm, ((DataBufferByte) db).getBankData(), out); 111 break; 112 case DataBuffer.TYPE_USHORT: 113 writeTo(sm, ((DataBufferUShort) db).getData(), out); 114 break; 115 case DataBuffer.TYPE_SHORT: 116 writeTo(sm, ((DataBufferShort) db).getData(), out); 117 break; 118 case DataBuffer.TYPE_INT: 119 writeTo(sm, ((DataBufferInt) db).getData(), out); 120 break; 121 default: 122 throw new UnsupportedOperationException( 123 "Unsupported Datatype: " + db.getDataType()); 124 } 125 } 126 127 private static void writeTo(SampleModel sm, byte[][] bankData, OutputStream out) 128 throws IOException { 129 int h = sm.getHeight(); 130 int w = sm.getWidth(); 131 ComponentSampleModel csm = (ComponentSampleModel) sm; 132 int len = w * csm.getPixelStride(); 133 int stride = csm.getScanlineStride(); 134 if (csm.getBandOffsets()[0] != 0) 135 bgr2rgb(bankData[0]); 136 for (byte[] b : bankData) 137 for (int y = 0, off = 0; y < h; ++y, off += stride) 138 out.write(b, off, len); 139 } 140 141 private static void bgr2rgb(byte[] bs) { 142 for (int i = 0, j = 2; j < bs.length; i += 3, j += 3) { 143 byte b = bs[i]; 144 bs[i] = bs[j]; 145 bs[j] = b; 146 } 147 } 148 149 private static void writeTo(SampleModel sm, short[] data, OutputStream out) 150 throws IOException { 151 int h = sm.getHeight(); 152 int w = sm.getWidth(); 153 int stride = ((ComponentSampleModel) sm).getScanlineStride(); 154 byte[] b = new byte[w * 2]; 155 for (int y = 0; y < h; ++y) { 156 for (int i = 0, j = y * stride; i < b.length;) { 157 short s = data[j++]; 158 b[i++] = (byte) s; 159 b[i++] = (byte) (s >> 8); 160 } 161 out.write(b); 162 } 163 } 164 165 private static void writeTo(SampleModel sm, int[] data, OutputStream out) 166 throws IOException { 167 int h = sm.getHeight(); 168 int w = sm.getWidth(); 169 int stride = ((SinglePixelPackedSampleModel) sm).getScanlineStride(); 170 byte[] b = new byte[w * 3]; 171 for (int y = 0; y < h; ++y) { 172 for (int i = 0, j = y * stride; i < b.length;) { 173 int s = data[j++]; 174 b[i++] = (byte) (s >> 16); 175 b[i++] = (byte) (s >> 8); 176 b[i++] = (byte) s; 177 } 178 out.write(b); 179 } 180 } 181 182 public static void nullifyUnusedBits(int bitsStored, DataBuffer db) { 183 if (bitsStored >= 16) 184 return; 185 186 short[] data; 187 switch (db.getDataType()) { 188 case DataBuffer.TYPE_USHORT: 189 data = ((DataBufferUShort) db).getData(); 190 break; 191 case DataBuffer.TYPE_SHORT: 192 data = ((DataBufferShort) db).getData(); 193 break; 194 default: 195 throw new IllegalArgumentException("Unsupported Datatype: " + db.getDataType()); 196 } 197 int mask = (1 << bitsStored) - 1; 198 for (int i = 0; i < data.length; i++) 199 data[i] &= mask; 200 } 201 202 public static int maxDiff(WritableRaster raster, WritableRaster raster2) { 203 ComponentSampleModel csm = 204 (ComponentSampleModel) raster.getSampleModel(); 205 ComponentSampleModel csm2 = 206 (ComponentSampleModel) raster2.getSampleModel(); 207 DataBuffer db = raster.getDataBuffer(); 208 DataBuffer db2 = raster2.getDataBuffer(); 209 switch (db.getDataType()) { 210 case DataBuffer.TYPE_BYTE: 211 return maxDiff(csm, ((DataBufferByte) db).getBankData(), 212 csm2, ((DataBufferByte) db2).getBankData()); 213 case DataBuffer.TYPE_USHORT: 214 case DataBuffer.TYPE_SHORT: 215 return maxDiff(csm, getShortData(db),csm2, getShortData(db2)); 216 default: 217 throw new UnsupportedOperationException( 218 "Unsupported Datatype: " + db.getDataType()); 219 } 220 } 221 222 private static short[] getShortData (DataBuffer db) { 223 if (db instanceof DataBufferShort) 224 return ((DataBufferShort)db).getData(); 225 if (db instanceof DataBufferUShort) 226 return ((DataBufferUShort)db).getData(); 227 throw new UnsupportedOperationException( 228 "Unsupported Datatype: " + db.getDataType()); 229 } 230 231 public static int maxDiff(WritableRaster raster, WritableRaster raster2, int blockSize) { 232 if (blockSize <= 1) 233 return maxDiff(raster, raster2); 234 235 ComponentSampleModel csm = 236 (ComponentSampleModel) raster.getSampleModel(); 237 ComponentSampleModel csm2 = 238 (ComponentSampleModel) raster2.getSampleModel(); 239 DataBuffer db = raster.getDataBuffer(); 240 DataBuffer db2 = raster2.getDataBuffer(); 241 int w = csm.getWidth(); 242 int h = csm.getHeight(); 243 int maxY = (h / blockSize - 1) * blockSize; 244 int maxX = (w / blockSize - 1) * blockSize; 245 int[] samples = new int[blockSize * blockSize]; 246 int diff, maxDiff = 0; 247 for (int b = 0; b < csm.getNumBands(); b++) 248 for (int y = 0; y < maxY; y += blockSize) { 249 for (int x = 0; x < maxX; x += blockSize) { 250 if (maxDiff < (diff = Math.abs( 251 sum(csm.getSamples( 252 x, y, blockSize, blockSize, b, samples, db)) 253 - sum(csm2.getSamples( 254 x, y, blockSize, blockSize, b, samples, db2))))) 255 maxDiff = diff; 256 } 257 } 258 return maxDiff / samples.length; 259 } 260 261 private static int sum(int[] samples) { 262 int sum = 0; 263 for (int sample : samples) 264 sum += sample; 265 return sum; 266 } 267 268 private static int maxDiff(ComponentSampleModel csm, short[] data, 269 ComponentSampleModel csm2, short[] data2) { 270 int w = csm.getWidth() * csm.getPixelStride(); 271 int h = csm.getHeight(); 272 int stride = csm.getScanlineStride(); 273 int stride2 = csm2.getScanlineStride(); 274 int diff, maxDiff = 0; 275 for (int y = 0; y < h; y++) { 276 for (int j = w, i = y * stride, i2 = y * stride2; j-- > 0; i++, i2++) { 277 if (maxDiff < (diff = Math.abs(data[i] - data2[i2]))) 278 maxDiff = diff; 279 } 280 } 281 return maxDiff; 282 } 283 284 private static int maxDiff(ComponentSampleModel csm, byte[][] banks, 285 ComponentSampleModel csm2, byte[][] banks2) { 286 int w = csm.getWidth(); 287 int h = csm.getHeight(); 288 int bands = csm.getNumBands(); 289 int stride = csm.getScanlineStride(); 290 int pixelStride = csm.getPixelStride(); 291 int[] bankIndices = csm.getBankIndices(); 292 int[] bandOffsets = csm.getBandOffsets(); 293 int stride2 = csm2.getScanlineStride(); 294 int pixelStride2 = csm2.getPixelStride(); 295 int[] bankIndices2 = csm2.getBankIndices(); 296 int[] bandOffsets2 = csm2.getBandOffsets(); 297 int diff, maxDiff = 0; 298 for (int b = 0; b < bands; b++) { 299 byte[] bank = banks[bankIndices[b]]; 300 byte[] bank2 = banks2[bankIndices2[b]]; 301 int off = bandOffsets[b]; 302 int off2 = bandOffsets2[b]; 303 for (int y = 0; y < h; y++) { 304 for (int x = w, i = y * stride + off, i2 = y * stride2 + off2; 305 x-- > 0; i += pixelStride, i2 += pixelStride2) { 306 if (maxDiff < (diff = Math.abs(bank[i] - bank2[i2]))) 307 maxDiff = diff; 308 } 309 } 310 } 311 return maxDiff; 312 } 313}