/*
* **** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is part of dcm4che, an implementation of DICOM(TM) in
* Java(TM), hosted at https://github.com/dcm4che.
*
* The Initial Developer of the Original Code is Agfa Healthcare.
* Portions created by the Initial Developer are Copyright (C) 2011-2015
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* See @authors listed below
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* **** END LICENSE BLOCK *****
*/
package org.dcm4che3.imageio.codec;
import java.awt.*;
import java.awt.color.ColorSpace;
import java.awt.image.*;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author Gunter Zeilinger <gunterze@gmail.com>
* @since Feb 2015.
*/
class BufferedImageUtils {
public static BufferedImage createBufferedImage(ImageParams imageParams, TransferSyntaxType tsType) {
int dataType = imageParams.getBitsAllocated() > 8
? (imageParams.isSigned() && (tsType == null || tsType.canEncodeSigned())
? DataBuffer.TYPE_SHORT
: DataBuffer.TYPE_USHORT)
: DataBuffer.TYPE_BYTE;
int samples = imageParams.getSamples();
int bitsStored = tsType == null
? imageParams.getBitsStored()
: Math.min(imageParams.getBitsStored(), tsType.getMaxBitsStored());
ComponentColorModel cm = samples == 1
? new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_GRAY),
new int[] { bitsStored },
false, // hasAlpha
false, // isAlphaPremultiplied,
Transparency.OPAQUE,
dataType)
: new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_sRGB),
new int[] { bitsStored, bitsStored, bitsStored },
false, // hasAlpha
false, // isAlphaPremultiplied,
Transparency.OPAQUE,
dataType);
int rows = imageParams.getRows();
int columns = imageParams.getColumns();
SampleModel sm = imageParams.isBanded()
? new BandedSampleModel(dataType, columns, rows, samples)
: new PixelInterleavedSampleModel(dataType, columns, rows,
samples, columns * samples, bandOffsets(samples));
WritableRaster raster = Raster.createWritableRaster(sm, null);
return new BufferedImage(cm, raster, false, null);
}
private static int[] bandOffsets(int samples) {
int[] offsets = new int[samples];
for (int i = 0; i < samples; i++)
offsets[i] = i;
return offsets;
}
public static int sizeOf(BufferedImage bi) {
WritableRaster raster = bi.getRaster();
DataBuffer db = raster.getDataBuffer();
return db.getSize() * db.getNumBanks()
* (DataBuffer.getDataTypeSize(db.getDataType()) >>> 3);
}
public static void writeTo(BufferedImage bi, OutputStream out) throws IOException {
WritableRaster raster = bi.getRaster();
SampleModel sm = raster.getSampleModel();
DataBuffer db = raster.getDataBuffer();
switch (db.getDataType()) {
case DataBuffer.TYPE_BYTE:
writeTo(sm, ((DataBufferByte) db).getBankData(), out);
break;
case DataBuffer.TYPE_USHORT:
writeTo(sm, ((DataBufferUShort) db).getData(), out);
break;
case DataBuffer.TYPE_SHORT:
writeTo(sm, ((DataBufferShort) db).getData(), out);
break;
case DataBuffer.TYPE_INT:
writeTo(sm, ((DataBufferInt) db).getData(), out);
break;
default:
throw new UnsupportedOperationException(
"Unsupported Datatype: " + db.getDataType());
}
}
private static void writeTo(SampleModel sm, byte[][] bankData, OutputStream out)
throws IOException {
int h = sm.getHeight();
int w = sm.getWidth();
ComponentSampleModel csm = (ComponentSampleModel) sm;
int len = w * csm.getPixelStride();
int stride = csm.getScanlineStride();
if (csm.getBandOffsets()[0] != 0)
bgr2rgb(bankData[0]);
for (byte[] b : bankData)
for (int y = 0, off = 0; y < h; ++y, off += stride)
out.write(b, off, len);
}
private static void bgr2rgb(byte[] bs) {
for (int i = 0, j = 2; j < bs.length; i += 3, j += 3) {
byte b = bs[i];
bs[i] = bs[j];
bs[j] = b;
}
}
private static void writeTo(SampleModel sm, short[] data, OutputStream out)
throws IOException {
int h = sm.getHeight();
int w = sm.getWidth();
int stride = ((ComponentSampleModel) sm).getScanlineStride();
byte[] b = new byte[w * 2];
for (int y = 0; y < h; ++y) {
for (int i = 0, j = y * stride; i < b.length;) {
short s = data[j++];
b[i++] = (byte) s;
b[i++] = (byte) (s >> 8);
}
out.write(b);
}
}
private static void writeTo(SampleModel sm, int[] data, OutputStream out)
throws IOException {
int h = sm.getHeight();
int w = sm.getWidth();
int stride = ((SinglePixelPackedSampleModel) sm).getScanlineStride();
byte[] b = new byte[w * 3];
for (int y = 0; y < h; ++y) {
for (int i = 0, j = y * stride; i < b.length;) {
int s = data[j++];
b[i++] = (byte) (s >> 16);
b[i++] = (byte) (s >> 8);
b[i++] = (byte) s;
}
out.write(b);
}
}
public static void nullifyUnusedBits(int bitsStored, DataBuffer db) {
if (bitsStored >= 16)
return;
short[] data;
switch (db.getDataType()) {
case DataBuffer.TYPE_USHORT:
data = ((DataBufferUShort) db).getData();
break;
case DataBuffer.TYPE_SHORT:
data = ((DataBufferShort) db).getData();
break;
default:
throw new IllegalArgumentException("Unsupported Datatype: " + db.getDataType());
}
int mask = (1 << bitsStored) - 1;
for (int i = 0; i < data.length; i++)
data[i] &= mask;
}
public static int maxDiff(WritableRaster raster, WritableRaster raster2) {
ComponentSampleModel csm =
(ComponentSampleModel) raster.getSampleModel();
ComponentSampleModel csm2 =
(ComponentSampleModel) raster2.getSampleModel();
DataBuffer db = raster.getDataBuffer();
DataBuffer db2 = raster2.getDataBuffer();
switch (db.getDataType()) {
case DataBuffer.TYPE_BYTE:
return maxDiff(csm, ((DataBufferByte) db).getBankData(),
csm2, ((DataBufferByte) db2).getBankData());
case DataBuffer.TYPE_USHORT:
case DataBuffer.TYPE_SHORT:
return maxDiff(csm, getShortData(db),csm2, getShortData(db2));
default:
throw new UnsupportedOperationException(
"Unsupported Datatype: " + db.getDataType());
}
}
private static short[] getShortData (DataBuffer db) {
if (db instanceof DataBufferShort)
return ((DataBufferShort)db).getData();
if (db instanceof DataBufferUShort)
return ((DataBufferUShort)db).getData();
throw new UnsupportedOperationException(
"Unsupported Datatype: " + db.getDataType());
}
public static int maxDiff(WritableRaster raster, WritableRaster raster2, int blockSize) {
if (blockSize <= 1)
return maxDiff(raster, raster2);
ComponentSampleModel csm =
(ComponentSampleModel) raster.getSampleModel();
ComponentSampleModel csm2 =
(ComponentSampleModel) raster2.getSampleModel();
DataBuffer db = raster.getDataBuffer();
DataBuffer db2 = raster2.getDataBuffer();
int w = csm.getWidth();
int h = csm.getHeight();
int maxY = (h / blockSize - 1) * blockSize;
int maxX = (w / blockSize - 1) * blockSize;
int[] samples = new int[blockSize * blockSize];
int diff, maxDiff = 0;
for (int b = 0; b < csm.getNumBands(); b++)
for (int y = 0; y < maxY; y += blockSize) {
for (int x = 0; x < maxX; x += blockSize) {
if (maxDiff < (diff = Math.abs(
sum(csm.getSamples(
x, y, blockSize, blockSize, b, samples, db))
- sum(csm2.getSamples(
x, y, blockSize, blockSize, b, samples, db2)))))
maxDiff = diff;
}
}
return maxDiff / samples.length;
}
private static int sum(int[] samples) {
int sum = 0;
for (int sample : samples)
sum += sample;
return sum;
}
private static int maxDiff(ComponentSampleModel csm, short[] data,
ComponentSampleModel csm2, short[] data2) {
int w = csm.getWidth() * csm.getPixelStride();
int h = csm.getHeight();
int stride = csm.getScanlineStride();
int stride2 = csm2.getScanlineStride();
int diff, maxDiff = 0;
for (int y = 0; y < h; y++) {
for (int j = w, i = y * stride, i2 = y * stride2; j-- > 0; i++, i2++) {
if (maxDiff < (diff = Math.abs(data[i] - data2[i2])))
maxDiff = diff;
}
}
return maxDiff;
}
private static int maxDiff(ComponentSampleModel csm, byte[][] banks,
ComponentSampleModel csm2, byte[][] banks2) {
int w = csm.getWidth();
int h = csm.getHeight();
int bands = csm.getNumBands();
int stride = csm.getScanlineStride();
int pixelStride = csm.getPixelStride();
int[] bankIndices = csm.getBankIndices();
int[] bandOffsets = csm.getBandOffsets();
int stride2 = csm2.getScanlineStride();
int pixelStride2 = csm2.getPixelStride();
int[] bankIndices2 = csm2.getBankIndices();
int[] bandOffsets2 = csm2.getBandOffsets();
int diff, maxDiff = 0;
for (int b = 0; b < bands; b++) {
byte[] bank = banks[bankIndices[b]];
byte[] bank2 = banks2[bankIndices2[b]];
int off = bandOffsets[b];
int off2 = bandOffsets2[b];
for (int y = 0; y < h; y++) {
for (int x = w, i = y * stride + off, i2 = y * stride2 + off2;
x-- > 0; i += pixelStride, i2 += pixelStride2) {
if (maxDiff < (diff = Math.abs(bank[i] - bank2[i2])))
maxDiff = diff;
}
}
}
return maxDiff;
}
}