/* * **** 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 org.dcm4che3.data.*; import org.dcm4che3.imageio.codec.jpeg.PatchJPEGLS; import org.dcm4che3.imageio.codec.jpeg.PatchJPEGLSImageInputStream; import org.dcm4che3.imageio.stream.SegmentedImageInputStream; import org.dcm4che3.io.DicomInputHandler; import org.dcm4che3.io.DicomInputStream; import org.dcm4che3.io.DicomOutputStream; import org.dcm4che3.util.ByteUtils; import org.dcm4che3.util.StreamUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageReadParam; import javax.imageio.ImageReader; import javax.imageio.stream.MemoryCacheImageInputStream; import java.awt.image.*; import java.io.IOException; /** * @author Gunter Zeilinger <gunterze@gmail.com> * @since Jan 2015. * * @deprecated This is prototype code. StreamDecompressor will be replaced by a Transcoder that supports both stream * compression and decompression. For now you can continue using {@link Decompressor} for non-stream decompression. */ @Deprecated public class StreamDecompressor implements CoerceAttributes { private static final Logger LOG = LoggerFactory.getLogger(StreamDecompressor.class); protected final DicomInputStream in; protected final DicomOutputStream out; protected final String tsuid; protected final TransferSyntaxType tsType; protected final Attributes dataset; protected ImageReader decompressor; protected PatchJPEGLS patchJPEGLS; protected boolean pixeldataProcessed; protected CoerceAttributes coerceAttributes = this; public StreamDecompressor(DicomInputStream in, String tsuid, DicomOutputStream out) { this.in = in; this.out = out; this.tsuid = tsuid; this.tsType = TransferSyntaxType.forUID(tsuid); if (tsType == null) throw new IllegalArgumentException("Unknown Transfer Syntax: " + tsuid); if (tsType.isPixeldataEncapsulated()) { ImageReaderFactory.ImageReaderParam param = ImageReaderFactory.getImageReaderParam(tsuid); if (param == null) throw new IllegalArgumentException("Unsupported Transfer Syntax: " + tsuid); this.decompressor = ImageReaderFactory.getImageReader(param); LOG.debug("Decompressor: {}", decompressor.getClass().getName()); this.patchJPEGLS = param.getPatchJPEGLS(); } this.dataset = new Attributes(in.bigEndian(), 64); } public CoerceAttributes getCoerceAttributes() { return coerceAttributes; } public void setCoerceAttributes(CoerceAttributes coerceAttributes) { if (coerceAttributes == null) throw new NullPointerException(); this.coerceAttributes = coerceAttributes; } @Override public Attributes coerce(Attributes attrs) { return attrs; } public boolean decompress() throws IOException { in.setDicomInputHandler(handler); in.readAttributes(dataset, -1, -1); (pixeldataProcessed ? dataset : coerceAttributes.coerce(dataset)).writeTo(out); return pixeldataProcessed && decompressor != null; } public void dispose() { if (decompressor != null) decompressor.dispose(); } protected void onPixelData(DicomInputStream dis, Attributes attrs) throws IOException { int tag = dis.tag(); VR vr = dis.vr(); int len = dis.length(); if (len == -1) { if (!tsType.isPixeldataEncapsulated()) { throw new IOException("Unexpected encapsulated Pixel Data"); } BufferedImage bi = null; ImageParams imageParams = new ImageParams(dataset); imageParams.decompress(attrs, tsType); if (tsType == TransferSyntaxType.RLE) bi = BufferedImageUtils.createBufferedImage(imageParams, null); coerceAttributes.coerce(attrs).writeTo(out); attrs.clear(); out.writeHeader(Tag.PixelData, VR.OW, imageParams.getEncodedLength()); decompressFrames(dis, imageParams, bi); if (imageParams.paddingNull()) out.write(0); } else { if (tsType.isPixeldataEncapsulated()) throw new IOException("Pixel Data not encapsulated"); coerceAttributes.coerce(attrs).writeTo(out); attrs.clear(); out.writeHeader(tag, vr, len); StreamUtils.copy(dis, out, len); } pixeldataProcessed = true; } protected void decompressFrames(DicomInputStream dis, ImageParams imageParams, BufferedImage bi) throws IOException { dis.readHeader(); dis.skipFully(dis.length()); long pos = dis.getPosition(); MemoryCacheImageInputStream iis = new MemoryCacheImageInputStream(dis); byte[] header = new byte[8]; boolean singleFrame = imageParams.getFrames() == 1; for (int i = 0; i < imageParams.getFrames(); i++) { iis.readFully(header); SegmentedImageInputStream siis = new SegmentedImageInputStream( iis, iis.getStreamPosition(), ByteUtils.bytesToIntLE(header, 4), singleFrame); decompressor.setInput(patchJPEGLS != null ? new PatchJPEGLSImageInputStream(siis, patchJPEGLS) : siis); ImageReadParam readParam = decompressor.getDefaultReadParam(); readParam.setDestination(bi); long start = System.currentTimeMillis(); bi = decompressor.read(0, readParam); long lastSegmentEnd = siis.getLastSegmentEnd(); iis.seek(lastSegmentEnd); iis.flushBefore(lastSegmentEnd); long end = System.currentTimeMillis(); if (LOG.isDebugEnabled()) LOG.debug("Decompressed frame #{} 1:{} in {} ms", i + 1, (float) BufferedImageUtils.sizeOf(bi) / siis.getStreamPosition(), end - start ); writeFrame(bi); } iis.readFully(header); dis.setPosition(pos + iis.getStreamPosition()); } protected void writeFrame(BufferedImage bi) throws IOException { BufferedImageUtils.writeTo(bi, out); } private final DicomInputHandler handler = new DicomInputHandler() { @Override public void readValue(DicomInputStream dis, Attributes attrs) throws IOException { if (dis.tag() == Tag.PixelData && dis.level() == 0) onPixelData(dis, attrs); else dis.readValue(dis, attrs); } @Override public void readValue(DicomInputStream dis, Sequence seq) throws IOException { dis.readValue(dis, seq); } @Override public void readValue(DicomInputStream dis, Fragments frags) throws IOException { throw new UnsupportedOperationException(); } @Override public void startDataset(DicomInputStream dis) throws IOException {} @Override public void endDataset(DicomInputStream dis) throws IOException {} }; }