/*******************************************************************************
* 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.imageio.codec;
import java.awt.RenderingHints;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Consumer;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.FileImageInputStream;
import javax.imageio.stream.ImageInputStream;
import javax.media.jai.JAI;
import javax.media.jai.ParameterBlockJAI;
import javax.media.jai.PlanarImage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.core.api.explorer.ObservableEvent;
import org.weasis.core.api.explorer.model.AbstractFileModel;
import org.weasis.core.api.explorer.model.DataExplorerModel;
import org.weasis.core.api.gui.util.AppProperties;
import org.weasis.core.api.image.util.ImageFiler;
import org.weasis.core.api.image.util.LayoutUtil;
import org.weasis.core.api.media.MimeInspector;
import org.weasis.core.api.media.data.Codec;
import org.weasis.core.api.media.data.FileCache;
import org.weasis.core.api.media.data.ImageElement;
import org.weasis.core.api.media.data.MediaElement;
import org.weasis.core.api.media.data.MediaReader;
import org.weasis.core.api.media.data.MediaSeries;
import org.weasis.core.api.media.data.Series;
import org.weasis.core.api.media.data.SeriesEvent;
import org.weasis.core.api.media.data.TagW;
import org.weasis.core.api.util.StringUtil;
public class ImageElementIO implements MediaReader {
private static final Logger LOGGER = LoggerFactory.getLogger(ImageElementIO.class);
public static final File CACHE_UNCOMPRESSED_DIR =
AppProperties.buildAccessibleTempDirectory(AppProperties.FILE_CACHE_DIR.getName(), "uncompressed"); //$NON-NLS-1$
protected URI uri;
private final FileCache fileCache;
protected String mimeType;
private ImageElement image = null;
private final Codec codec;
public ImageElementIO(URI media, String mimeType, Codec codec) {
this.uri = Objects.requireNonNull(media);
this.fileCache = new FileCache(this);
if (mimeType == null) {
this.mimeType = MimeInspector.UNKNOWN_MIME_TYPE;
} else if ("image/x-ms-bmp".equals(mimeType)) { //$NON-NLS-1$
this.mimeType = "image/bmp"; //$NON-NLS-1$
} else {
this.mimeType = mimeType;
}
this.codec = codec;
}
@Override
public PlanarImage getImageFragment(MediaElement media) throws Exception {
Objects.requireNonNull(media);
FileCache cache = media.getFileCache();
Path imgCachePath = null;
File file;
if (cache.isRequireTransformation()) {
file = cache.getTransformedFile();
if (file == null) {
String filename = StringUtil.bytesToMD5(media.getMediaURI().toString().getBytes());
imgCachePath = CACHE_UNCOMPRESSED_DIR.toPath().resolve(filename + ".tif"); //$NON-NLS-1$
if (Files.isReadable(imgCachePath)) {
file = imgCachePath.toFile();
cache.setTransformedFile(file);
this.mimeType = "image/tiff"; //$NON-NLS-1$
imgCachePath = null;
} else {
file = cache.getOriginalFile().get();
}
}
} else {
file = cache.getOriginalFile().get();
}
if (file != null) {
PlanarImage img = readImage(file, imgCachePath == null);
if (imgCachePath != null) {
File rawFile = uncompress(imgCachePath, img);
if( rawFile != null){
file = rawFile;
}
cache.setTransformedFile(file);
img = readImage(file, true);
}
return img;
}
return null;
}
private PlanarImage readImage(File file, boolean createTiledLayout) throws Exception {
ImageReader reader = getDefaultReader(mimeType);
if (reader == null) {
LOGGER.info("Cannot find a reader for the mime type: {}", mimeType); //$NON-NLS-1$
return null;
}
PlanarImage img;
RenderingHints hints = createTiledLayout ? LayoutUtil.createTiledLayoutHints() : null;
ImageInputStream in = new FileImageInputStream(new RandomAccessFile(file, "r")); //$NON-NLS-1$
ParameterBlockJAI pb = new ParameterBlockJAI("ImageRead"); //$NON-NLS-1$
pb.setParameter("Input", in); //$NON-NLS-1$
pb.setParameter("Reader", reader); //$NON-NLS-1$
img = JAI.create("ImageRead", pb, hints); //$NON-NLS-1$
// to avoid problem with alpha channel and png encoded in 24 and 32 bits
img = PlanarImage.wrapRenderedImage(ImageFiler.getReadableImage(img));
image.setTag(TagW.ImageWidth, img.getWidth());
image.setTag(TagW.ImageHeight, img.getHeight());
return img;
}
@Override
public URI getUri() {
return uri;
}
@Override
public void reset() {
}
@Override
public MediaElement getPreview() {
return getSingleImage();
}
@Override
public boolean delegate(DataExplorerModel explorerModel) {
return false;
}
@Override
public MediaElement[] getMediaElement() {
MediaElement element = getSingleImage();
if (element != null) {
return new MediaElement[] { element };
}
return null;
}
@Override
public MediaSeries<MediaElement> getMediaSeries() {
String sUID = null;
MediaElement element = getSingleImage();
if (element != null) {
sUID = (String) element.getTagValue(TagW.get("SeriesInstanceUID")); //$NON-NLS-1$
}
if (sUID == null) {
sUID = uri == null ? "unknown" : uri.toString(); //$NON-NLS-1$
}
MediaSeries<MediaElement> series =
new Series<MediaElement>(TagW.SubseriesInstanceUID, sUID, AbstractFileModel.series.getTagView()) { // $NON-NLS-1$
@Override
public String getMimeType() {
synchronized (this) {
for (MediaElement img : medias) {
return img.getMimeType();
}
}
return null;
}
@Override
public void addMedia(MediaElement media) {
if (media instanceof ImageElement) {
this.add(media);
DataExplorerModel model = (DataExplorerModel) getTagValue(TagW.ExplorerModel);
if (model != null) {
model.firePropertyChange(new ObservableEvent(ObservableEvent.BasicAction.ADD, model, null,
new SeriesEvent(SeriesEvent.Action.ADD_IMAGE, this, media)));
}
}
}
};
ImageElement img = getSingleImage();
if (img != null) {
series.add(getSingleImage());
series.setTag(TagW.FileName, img.getName());
}
return series;
}
@Override
public int getMediaElementNumber() {
return 1;
}
private ImageElement getSingleImage() {
if (image == null) {
image = new ImageElement(this, 0);
}
return image;
}
@Override
public String getMediaFragmentMimeType() {
return mimeType;
}
@Override
public Map<TagW, Object> getMediaFragmentTags(Object key) {
return new HashMap<>();
}
@Override
public void close() {
// TODO Auto-generated method stub
}
@Override
public Codec getCodec() {
return codec;
}
@Override
public String[] getReaderDescription() {
return new String[] { "Image Codec: " + codec.getCodecName() }; //$NON-NLS-1$
}
public ImageReader getDefaultReader(String mimeType) {
if (mimeType != null) {
Iterator readers = ImageIO.getImageReadersByMIMEType(mimeType);
if (readers.hasNext()) {
return (ImageReader) readers.next();
}
}
return null;
}
@Override
public Object getTagValue(TagW tag) {
MediaElement element = getSingleImage();
if (tag != null && element != null) {
return element.getTagValue(tag);
}
return null;
}
@Override
public void replaceURI(URI uri) {
throw new UnsupportedOperationException();
}
@Override
public void setTag(TagW tag, Object value) {
throw new UnsupportedOperationException();
}
@Override
public boolean containTagKey(TagW tag) {
return false;
}
@Override
public void setTagNoNull(TagW tag, Object value) {
throw new UnsupportedOperationException();
}
@Override
public Iterator<Entry<TagW, Object>> getTagEntrySetIterator() {
throw new UnsupportedOperationException();
}
@Override
public FileCache getFileCache() {
return fileCache;
}
private File uncompress(Path imgCachePath, RenderedImage img) {
/*
* Make an image cache with its thumbnail when the image size is larger than a tile size and if not DICOM file
*/
if (img != null && (img.getWidth() > ImageFiler.TILESIZE || img.getHeight() > ImageFiler.TILESIZE)
&& !mimeType.contains("dicom")) { //$NON-NLS-1$
File outFile = imgCachePath.toFile();
if (ImageFiler.writeTIFF(outFile, img, true, true, false)) {
this.mimeType = "image/tiff"; //$NON-NLS-1$
return outFile;
} else {
try {
Files.deleteIfExists(outFile.toPath());
} catch (IOException e) {
LOGGER.error("Deleting temp tiff file", e); //$NON-NLS-1$
}
}
}
return null;
}
@Override
public boolean buildFile(File ouptut) {
return false;
}
}