/******************************************************************************* * Copyright (c) 2016 Weasis Team and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nicolas Roduit - initial API and implementation *******************************************************************************/ package org.weasis.image.jni; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.RandomAccessFile; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import javax.imageio.stream.FileCacheImageInputStream; import javax.imageio.stream.FileImageInputStream; import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.MemoryCacheImageInputStream; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.sun.media.imageio.stream.SegmentedImageInputStream; public abstract class StreamSegment { private static final Logger LOGGER = LoggerFactory.getLogger(StreamSegment.class); protected final long[] segPosition; protected final int[] segLength; StreamSegment(long[] startPos, int[] length) { this.segPosition = startPos; this.segLength = length; } public static void adaptParametersFromStream(ImageInputStream iis, NativeImage mlImage) throws IOException { if (mlImage == null) { return; } // Not a good practice (should be instanceof) but necessary to remove the dependency with dcm4che3 lib if ("org.dcm4che3.imageio.stream.SegmentedInputImageStream".equals(iis.getClass().getName())) { try { Class<? extends ImageInputStream> clazz = iis.getClass(); Field fStream = clazz.getDeclaredField("stream"); Field fCurSegment = clazz.getDeclaredField("curSegment"); if (fCurSegment != null && fStream != null) { fCurSegment.setAccessible(true); fStream.setAccessible(true); FileImageInputStream fstream = (FileImageInputStream) fStream.get(iis); Field fRaf = FileImageInputStream.class.getDeclaredField("raf"); if (fRaf != null) { fRaf.setAccessible(true); Integer curSegment = (Integer) fCurSegment.get(iis); if (curSegment != null && curSegment >= 0) { Field fSegmentPositionsList = clazz.getDeclaredField("segmentPositionsList"); Field fSegmentLengths = clazz.getDeclaredField("segmentLengths"); if (fSegmentPositionsList != null && fSegmentLengths != null) { fSegmentPositionsList.setAccessible(true); fSegmentLengths.setAccessible(true); long[] segmentPositionsList = (long[]) fSegmentPositionsList.get(iis); int[] segmentLengths = (int[]) fSegmentLengths.get(iis); RandomAccessFile raf = (RandomAccessFile) fRaf.get(fstream); /* * PS 3.5.8.2 Though a fragment may not contain encoded data from more than one frame, * the encoded data from one frame may span multiple fragments. See note in Section 8.2. */ mlImage.setStreamSegment(new FileStreamSegment(raf, FileStreamSegment.getFileIDfromFileDescriptor(raf.getFD()), Arrays.copyOfRange(segmentPositionsList, curSegment, segmentPositionsList.length), Arrays.copyOfRange(segmentLengths, curSegment, segmentLengths.length))); } } } } } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.error("getFileDescriptor from SegmentedInputImageStream", e); //$NON-NLS-1$ } else { LOGGER.error(e.getMessage()); } } } else if (iis instanceof FileImageInputStream) { RandomAccessFile raf = FileStreamSegment.getRandomAccessFile((FileImageInputStream) iis); if (raf != null) { mlImage.setStreamSegment( new FileStreamSegment(raf, FileStreamSegment.getFileIDfromFileDescriptor(raf.getFD()), new long[] { 0 }, new int[] { (int) raf.length() })); } } else if (iis instanceof SegmentedImageInputStream) { try { Field fStream = SegmentedImageInputStream.class.getDeclaredField("stream"); Field fMapper = SegmentedImageInputStream.class.getDeclaredField("mapper"); if (fMapper != null && fStream != null) { fMapper.setAccessible(true); fStream.setAccessible(true); Object mapperObject = fMapper.get(iis); // Not a good practice (should be instanceof) but necessary to remove the dependency with dcm4che14 lib if ("org.dcm4cheri.image.ItemParser".equals(mapperObject.getClass().getName())) { FileImageInputStream fstream = (FileImageInputStream) fStream.get(iis); Field fRaf = FileImageInputStream.class.getDeclaredField("raf"); if (fRaf != null) { fRaf.setAccessible(true); Field fItems = mapperObject.getClass().getDeclaredField("items"); Method mNumberOfDataFragments = mapperObject.getClass().getMethod("getNumberOfDataFragments"); //Needs to be called so that all segments are added to List "items" int nbrOfDataFragments = (int) mNumberOfDataFragments.invoke(mapperObject, new Object[]{}); if (fItems != null) { fItems.setAccessible(true); ArrayList items = (ArrayList) fItems.get(mapperObject); if (!items.isEmpty()) { Field fStartPos = items.get(0).getClass().getDeclaredField("startPos"); Field fLength = items.get(0).getClass().getDeclaredField("length"); Field fOffset = items.get(0).getClass().getDeclaredField("offset"); if (fStartPos != null && fLength != null) { fStartPos.setAccessible(true); fLength.setAccessible(true); long[] segmentStartPositions = new long[items.size()]; int[] segmentLengths = new int[items.size()]; int startIndex = -1; for (int i = 0; i < items.size(); i++) { if(startIndex == -1 && (long) fOffset.get(items.get(i)) == iis.getStreamPosition()) { startIndex = i; } segmentStartPositions[i] = (long) fStartPos.get(items.get(i)); segmentLengths[i] = (int) fLength.get(items.get(i)); } if(startIndex != -1) { segmentStartPositions = Arrays.copyOfRange(segmentStartPositions, startIndex, segmentStartPositions.length); segmentLengths = Arrays.copyOfRange(segmentLengths, startIndex, segmentLengths.length); RandomAccessFile raf = (RandomAccessFile) fRaf.get(fstream); /* * PS 3.5.8.2 Though a fragment may not contain encoded data from more than one frame, * the encoded data from one frame may span multiple fragments. See note in Section 8.2. */ FileStreamSegment segment = new FileStreamSegment(raf, FileStreamSegment.getFileIDfromFileDescriptor(raf.getFD()), segmentStartPositions, segmentLengths); mlImage.setStreamSegment(segment); return; } } } } } } } throw new IllegalArgumentException("No adaptor implemented for this type of SegmentedImageInputStream"); } catch (Exception e) { if (LOGGER.isDebugEnabled()) { LOGGER.error("getFileDescriptor from SegmentedInputImageStream", e); } else { LOGGER.error(e.getMessage()); } } } else if (iis instanceof FileCacheImageInputStream) { throw new IllegalArgumentException("No adaptor implemented yet for FileCacheImageInputStream"); } else if (iis instanceof MemoryCacheImageInputStream) { ByteArrayInputStream stream = MemoryStreamSegment.getByteArrayInputStream((MemoryCacheImageInputStream) iis); if (stream != null) { byte[] b = getByte(stream); if (b != null) { mlImage.setStreamSegment(new MemoryStreamSegment(b)); } return; } throw new IllegalArgumentException("No adaptor implemented for this type of MemoryCacheImageInputStream"); } else { throw new IllegalArgumentException("No stream adaptor found for " + iis.getClass().getName() + "!"); } } public long[] getSegPosition() { return segPosition; } public int[] getSegLength() { return segLength; } public static byte[] getByte(ByteArrayInputStream inputStream) { if (inputStream != null) { try { Field fid = ByteArrayInputStream.class.getDeclaredField("buf"); if (fid != null) { fid.setAccessible(true); return (byte[]) fid.get(inputStream); } } catch (Exception e) { LOGGER.error("Cannot get bytes from inputstream", e); } } return null; } public abstract ByteBuffer getDirectByteBuffer(int segment) throws IOException; public abstract ByteBuffer getDirectByteBuffer(int startSeg, int endSeg) throws IOException; }