Autopsy  4.5.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
ImageUtils.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-17 Basis Technology Corp.
5  *
6  * Copyright 2012 42six Solutions.
7  * Contact: aebadirad <at> 42six <dot> com
8  * Project Contact/Architect: carrier <at> sleuthkit <dot> org
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  */
22 package org.sleuthkit.autopsy.coreutils;
23 
24 import com.google.common.collect.ImmutableSortedSet;
25 import com.google.common.io.Files;
26 import java.awt.Image;
27 import java.awt.image.BufferedImage;
28 import java.io.BufferedInputStream;
29 import java.io.File;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.nio.file.Paths;
33 import java.text.MessageFormat;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.EnumSet;
39 import java.util.Iterator;
40 import java.util.List;
41 import static java.util.Objects.nonNull;
42 import java.util.SortedSet;
43 import java.util.TreeSet;
44 import java.util.concurrent.ConcurrentHashMap;
45 import java.util.concurrent.ExecutionException;
46 import java.util.concurrent.Executor;
47 import java.util.concurrent.Executors;
48 import java.util.logging.Level;
49 import javafx.concurrent.Task;
50 import javafx.embed.swing.SwingFXUtils;
51 import javax.annotation.Nonnull;
52 import javax.annotation.Nullable;
53 import javax.imageio.IIOException;
54 import javax.imageio.ImageIO;
55 import javax.imageio.ImageReadParam;
56 import javax.imageio.ImageReader;
57 import javax.imageio.event.IIOReadProgressListener;
58 import javax.imageio.stream.ImageInputStream;
59 import org.apache.commons.lang3.ObjectUtils;
60 import org.apache.commons.lang3.StringUtils;
61 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
62 import org.opencv.core.Core;
63 import org.openide.util.NbBundle;
68 import org.sleuthkit.datamodel.AbstractFile;
69 import org.sleuthkit.datamodel.Content;
70 import org.sleuthkit.datamodel.ReadContentInputStream;
71 import org.sleuthkit.datamodel.TskCoreException;
72 
77 public class ImageUtils {
78 
79  private static final Logger LOGGER = Logger.getLogger(ImageUtils.class.getName());
80 
84  private static final String FORMAT = "png"; //NON-NLS
85 
86  public static final int ICON_SIZE_SMALL = 50;
87  public static final int ICON_SIZE_MEDIUM = 100;
88  public static final int ICON_SIZE_LARGE = 200;
89 
90  private static final BufferedImage DEFAULT_THUMBNAIL;
91 
92  private static final List<String> GIF_EXTENSION_LIST = Arrays.asList("gif");
93  private static final SortedSet<String> GIF_MIME_SET = ImmutableSortedSet.copyOf(new String[]{"image/gif"});
94 
95  private static final List<String> SUPPORTED_IMAGE_EXTENSIONS = new ArrayList<>();
96  private static final SortedSet<String> SUPPORTED_IMAGE_MIME_TYPES;
97 
98  private static final boolean OPEN_CV_LOADED;
99 
107  private static final ConcurrentHashMap<Long, File> cacheFileMap = new ConcurrentHashMap<>();
108 
109  static {
110  ImageIO.scanForPlugins();
111  BufferedImage tempImage;
112  try {
113  tempImage = ImageIO.read(ImageUtils.class.getResourceAsStream("/org/sleuthkit/autopsy/images/file-icon.png"));//NON-NLS
114  } catch (IOException ex) {
115  LOGGER.log(Level.SEVERE, "Failed to load default icon.", ex); //NON-NLS
116  tempImage = null;
117  }
118  DEFAULT_THUMBNAIL = tempImage;
119 
120  //load opencv libraries
121  boolean openCVLoadedTemp;
122  try {
123  System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
124  if (System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64")) { //NON-NLS
125  System.loadLibrary("opencv_ffmpeg248_64"); //NON-NLS
126  } else {
127  System.loadLibrary("opencv_ffmpeg248"); //NON-NLS
128  }
129 
130  openCVLoadedTemp = true;
131  } catch (UnsatisfiedLinkError e) {
132  openCVLoadedTemp = false;
133  LOGGER.log(Level.SEVERE, "OpenCV Native code library failed to load", e); //NON-NLS
134  //TODO: show warning bubble
135 
136  }
137 
138  OPEN_CV_LOADED = openCVLoadedTemp;
139  SUPPORTED_IMAGE_EXTENSIONS.addAll(Arrays.asList(ImageIO.getReaderFileSuffixes()));
140  SUPPORTED_IMAGE_EXTENSIONS.add("tec"); // Add JFIF .tec files
141  SUPPORTED_IMAGE_EXTENSIONS.removeIf("db"::equals); // remove db files
142 
143  SUPPORTED_IMAGE_MIME_TYPES = new TreeSet<>(Arrays.asList(ImageIO.getReaderMIMETypes()));
144  /*
145  * special cases and variants that we support, but don't get registered
146  * with ImageIO automatically
147  */
148  SUPPORTED_IMAGE_MIME_TYPES.addAll(Arrays.asList(
149  "image/x-rgb", //NON-NLS
150  "image/x-ms-bmp", //NON-NLS
151  "image/x-portable-graymap", //NON-NLS
152  "image/x-portable-bitmap", //NON-NLS
153  "application/x-123")); //TODO: is this correct? -jm //NON-NLS
154  SUPPORTED_IMAGE_MIME_TYPES.removeIf("application/octet-stream"::equals); //NON-NLS
155 
156  //Clear the file map when the case changes, so we don't accidentaly get images from the old case.
157  Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> cacheFileMap.clear());
158  }
159 
164 
168  private static final Executor imageSaver =
169  Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
170  .namingPattern("thumbnail-saver-%d").build()); //NON-NLS
171 
172  public static List<String> getSupportedImageExtensions() {
173  return Collections.unmodifiableList(SUPPORTED_IMAGE_EXTENSIONS);
174  }
175 
176  public static SortedSet<String> getSupportedImageMimeTypes() {
177  return Collections.unmodifiableSortedSet(SUPPORTED_IMAGE_MIME_TYPES);
178  }
179 
186  public static Image getDefaultThumbnail() {
187  return DEFAULT_THUMBNAIL;
188  }
189 
200  public static boolean thumbnailSupported(Content content) {
201 
202  if (!(content instanceof AbstractFile)) {
203  return false;
204  }
205  AbstractFile file = (AbstractFile) content;
206 
208  || isImageThumbnailSupported(file);
209  }
210 
219  public static boolean isImageThumbnailSupported(AbstractFile file) {
220  return isMediaThumbnailSupported(file, "image/", SUPPORTED_IMAGE_MIME_TYPES, SUPPORTED_IMAGE_EXTENSIONS) || hasImageFileHeader(file);//NON-NLS
221  }
222 
231  public static boolean isGIF(AbstractFile file) {
232  return isMediaThumbnailSupported(file, null, GIF_MIME_SET, GIF_EXTENSION_LIST);
233  }
234 
255  static boolean isMediaThumbnailSupported(AbstractFile file, String mimeTypePrefix, final Collection<String> supportedMimeTypes, final List<String> supportedExtension) {
256  if (false == file.isFile() || file.getSize() <= 0) {
257  return false;
258  }
259 
260  String extension = file.getNameExtension();
261 
262  if (StringUtils.isNotBlank(extension) && supportedExtension.contains(extension)) {
263  return true;
264  } else {
265  try {
266  String mimeType = getFileTypeDetector().getMIMEType(file);
267  if (StringUtils.isNotBlank(mimeTypePrefix) && mimeType.startsWith(mimeTypePrefix)) {
268  return true;
269  }
270  return supportedMimeTypes.contains(mimeType);
271  } catch (FileTypeDetectorInitException ex) {
272  LOGGER.log(Level.SEVERE, "Error determining MIME type of " + getContentPathSafe(file), ex);//NON-NLS
273  return false;
274  }
275  }
276  }
277 
290  if (fileTypeDetector == null) {
291  fileTypeDetector = new FileTypeDetector();
292  }
293  return fileTypeDetector;
294  }
295 
306  public static BufferedImage getThumbnail(Content content, int iconSize) {
307  if (content instanceof AbstractFile) {
308  AbstractFile file = (AbstractFile) content;
309  if (ImageUtils.isGIF(file)) {
310  /*
311  * Intercepting the image reading code for GIFs here allows us
312  * to rescale easily, but we lose animations.
313  */
314  try (BufferedInputStream bufferedReadContentStream = getBufferedReadContentStream(file);) {
315  if (Thread.interrupted()) {
316  return DEFAULT_THUMBNAIL;
317  }
318  final BufferedImage image = ImageIO.read(bufferedReadContentStream);
319  if (image != null) {
320  if (Thread.interrupted()) {
321  return DEFAULT_THUMBNAIL;
322  }
323  return ScalrWrapper.resizeHighQuality(image, iconSize, iconSize);
324  }
325  } catch (IOException iOException) {
326  LOGGER.log(Level.WARNING, "Failed to get thumbnail for " + getContentPathSafe(content), iOException); //NON-NLS
327  }
328  return DEFAULT_THUMBNAIL;
329  }
330 
331  Task<javafx.scene.image.Image> thumbnailTask = newGetThumbnailTask(file, iconSize, true);
332  if (Thread.interrupted()) {
333  return DEFAULT_THUMBNAIL;
334  }
335  thumbnailTask.run();
336  try {
337  return SwingFXUtils.fromFXImage(thumbnailTask.get(), null);
338  } catch (InterruptedException | ExecutionException ex) {
339  LOGGER.log(Level.WARNING, "Failed to get thumbnail for " + getContentPathSafe(content), ex); //NON-NLS
340  }
341  }
342  return DEFAULT_THUMBNAIL;
343  }
344 
354  private static BufferedInputStream getBufferedReadContentStream(AbstractFile file) {
355  return new BufferedInputStream(new ReadContentInputStream(file));
356  }
357 
368  @Nullable
369  public static File getCachedThumbnailFile(Content content, int iconSize) {
370  getThumbnail(content, iconSize);
371  return getCachedThumbnailLocation(content.getId());
372  }
373 
384  private static File getCachedThumbnailLocation(long fileID) {
385  return cacheFileMap.computeIfAbsent(fileID, id -> {
386  try {
387  String cacheDirectory = Case.getCurrentCase().getCacheDirectory();
388  return Paths.get(cacheDirectory, "thumbnails", fileID + ".png").toFile(); //NON-NLS
389  } catch (IllegalStateException e) {
390  LOGGER.log(Level.WARNING, "Could not get cached thumbnail location. No case is open."); //NON-NLS
391  return null;
392  }
393  });
394  }
395 
404  public static boolean hasImageFileHeader(AbstractFile file) {
405  return isJpegFileHeader(file) || isPngFileHeader(file);
406  }
407 
415  public static boolean isJpegFileHeader(AbstractFile file) {
416  if (file.getSize() < 100) {
417  return false;
418  }
419 
420  try {
421  byte[] fileHeaderBuffer = readHeader(file, 2);
422  /*
423  * Check for the JPEG header. Since Java bytes are signed, we cast
424  * them to an int first.
425  */
426  return (((fileHeaderBuffer[0] & 0xff) == 0xff) && ((fileHeaderBuffer[1] & 0xff) == 0xd8));
427  } catch (TskCoreException ex) {
428  //ignore if can't read the first few bytes, not a JPEG
429  return false;
430  }
431  }
432 
442  private static long getJfifStartOfImageOffset(AbstractFile file) {
443  byte[] fileHeaderBuffer;
444  long length;
445  try {
446  length = file.getSize();
447  if (length % 2 != 0) {
448  length -= 1; // Make it an even number so we can parse two bytes at a time
449  }
450  if (length >= 1024) {
451  length = 1024;
452  }
453  fileHeaderBuffer = readHeader(file, (int) length); // read up to first 1024 bytes
454  } catch (TskCoreException ex) {
455  // Couldn't read header. Let ImageIO try it.
456  return 0;
457  }
458 
459  if (fileHeaderBuffer != null) {
460  for (int index = 0; index < length; index += 2) {
461  // Look for Start Of Image marker and return the index when it's found
462  if ((fileHeaderBuffer[index] == (byte) 0xFF) && (fileHeaderBuffer[index + 1] == (byte) 0xD8)) {
463  return index;
464  }
465  }
466  }
467 
468  // Didn't match JFIF. Let ImageIO try to open it from offset 0.
469  return 0;
470  }
471 
479  public static boolean isPngFileHeader(AbstractFile file) {
480  if (file.getSize() < 10) {
481  return false;
482  }
483 
484  try {
485  byte[] fileHeaderBuffer = readHeader(file, 8);
486  /*
487  * Check for the png header. Since Java bytes are signed, we cast
488  * them to an int first.
489  */
490  return (((fileHeaderBuffer[1] & 0xff) == 0x50) && ((fileHeaderBuffer[2] & 0xff) == 0x4E)
491  && ((fileHeaderBuffer[3] & 0xff) == 0x47) && ((fileHeaderBuffer[4] & 0xff) == 0x0D)
492  && ((fileHeaderBuffer[5] & 0xff) == 0x0A) && ((fileHeaderBuffer[6] & 0xff) == 0x1A)
493  && ((fileHeaderBuffer[7] & 0xff) == 0x0A));
494 
495  } catch (TskCoreException ex) {
496  //ignore if can't read the first few bytes, not an png
497  return false;
498  }
499  }
500 
501  private static byte[] readHeader(AbstractFile file, int buffLength) throws TskCoreException {
502  byte[] fileHeaderBuffer = new byte[buffLength];
503  int bytesRead = file.read(fileHeaderBuffer, 0, buffLength);
504 
505  if (bytesRead != buffLength) {
506  //ignore if can't read the first few bytes, not an image
507  throw new TskCoreException("Could not read " + buffLength + " bytes from " + file.getName());//NON-NLS
508  }
509  return fileHeaderBuffer;
510  }
511 
522  static public int getImageWidth(AbstractFile file) throws IOException {
523  return getImageProperty(file,
524  "ImageIO could not determine width of {0}: ", //NON-NLS
525  imageReader -> imageReader.getWidth(0)
526  );
527  }
528 
539  static public int getImageHeight(AbstractFile file) throws IOException {
540  return getImageProperty(file,
541  "ImageIO could not determine height of {0}: ", //NON-NLS
542  imageReader -> imageReader.getHeight(0)
543  );
544 
545  }
546 
555  @FunctionalInterface
556  private static interface PropertyExtractor<T> {
557 
558  public T extract(ImageReader reader) throws IOException;
559  }
560 
582  private static <T> T getImageProperty(AbstractFile file, final String errorTemplate, PropertyExtractor<T> propertyExtractor) throws IOException {
583  try (InputStream inputStream = getBufferedReadContentStream(file);
584  ImageInputStream input = ImageIO.createImageInputStream(inputStream)) {
585  if (input == null) {
586  IIOException iioException = new IIOException("Could not create ImageInputStream.");
587  LOGGER.log(Level.WARNING, errorTemplate + iioException.toString(), getContentPathSafe(file));
588  throw iioException;
589  }
590  Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
591 
592  if (readers.hasNext()) {
593  ImageReader reader = readers.next();
594  reader.setInput(input);
595  try {
596  return propertyExtractor.extract(reader);
597  } catch (IOException ex) {
598  LOGGER.log(Level.WARNING, errorTemplate + ex.toString(), getContentPathSafe(file));
599  throw ex;
600  } finally {
601  reader.dispose();
602  }
603  } else {
604  IIOException iioException = new IIOException("No ImageReader found.");
605  LOGGER.log(Level.WARNING, errorTemplate + iioException.toString(), getContentPathSafe(file));
606  throw iioException;
607  }
608  }
609  }
610 
627  public static Task<javafx.scene.image.Image> newGetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure) {
628  return new GetThumbnailTask(file, iconSize, defaultOnFailure);
629 
630  }
631 
635  static private class GetThumbnailTask extends ReadImageTaskBase {
636 
637  private static final String FAILED_TO_READ_IMAGE_FOR_THUMBNAIL_GENERATION = "Failed to read {0} for thumbnail generation."; //NON-NLS
638 
639  private final int iconSize;
640  private final File cacheFile;
641  private final boolean defaultOnFailure;
642 
643  @NbBundle.Messages({"# {0} - file name",
644  "GetOrGenerateThumbnailTask.loadingThumbnailFor=Loading thumbnail for {0}",
645  "# {0} - file name",
646  "GetOrGenerateThumbnailTask.generatingPreviewFor=Generating preview for {0}"})
647  private GetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure) {
648  super(file);
649  updateMessage(Bundle.GetOrGenerateThumbnailTask_loadingThumbnailFor(file.getName()));
650  this.iconSize = iconSize;
651  this.defaultOnFailure = defaultOnFailure;
652  this.cacheFile = getCachedThumbnailLocation(file.getId());
653  }
654 
655  @Override
656  protected javafx.scene.image.Image call() throws Exception {
657  if (isCancelled()) {
658  return null;
659  }
660  if (isGIF(file)) {
661  return readImage();
662  }
663 
664  // If a thumbnail file is already saved locally, just read that.
665  if (cacheFile != null) {
666  synchronized (cacheFile) {
667  if (cacheFile.exists()) {
668  try {
669  if (isCancelled()) {
670  return null;
671  }
672  BufferedImage cachedThumbnail = ImageIO.read(cacheFile);
673  if (isCancelled()) {
674  return null;
675  }
676  if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
677  return SwingFXUtils.toFXImage(cachedThumbnail, null);
678  }
679  } catch (Exception ex) {
680  LOGGER.log(Level.WARNING, "ImageIO had a problem reading the cached thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
681  cacheFile.delete(); //since we can't read the file we might as well delete it.
682  }
683  }
684  }
685  }
686 
687  //There was no correctly-sized cached thumbnail so make one.
688  BufferedImage thumbnail = null;
690  if (OPEN_CV_LOADED) {
691  updateMessage(Bundle.GetOrGenerateThumbnailTask_generatingPreviewFor(file.getName()));
692  if (isCancelled()) {
693  return null;
694  }
695  thumbnail = VideoUtils.generateVideoThumbnail(file, iconSize);
696  }
697  if (null == thumbnail) {
698  if (defaultOnFailure) {
699  thumbnail = DEFAULT_THUMBNAIL;
700  } else {
701  throw new IIOException("Failed to generate a thumbnail for " + getContentPathSafe(file));//NON-NLS
702  }
703  }
704 
705  } else {
706  if (isCancelled()) {
707  return null;
708  }
709  //read the image into a buffered image.
710  //TODO: I don't like this, we just converted it from BufferedIamge to fx Image -jm
711  BufferedImage bufferedImage = SwingFXUtils.fromFXImage(readImage(), null);
712  if (null == bufferedImage) {
713  String msg = MessageFormat.format(FAILED_TO_READ_IMAGE_FOR_THUMBNAIL_GENERATION, getContentPathSafe(file));
714  LOGGER.log(Level.WARNING, msg);
715  throw new IIOException(msg);
716  }
717  updateProgress(-1, 1);
718  if (isCancelled()) {
719  return null;
720  }
721  //resize, or if that fails, crop it
722  try {
723  if (isCancelled()) {
724  return null;
725  }
726  thumbnail = ScalrWrapper.resizeFast(bufferedImage, iconSize);
727  } catch (IllegalArgumentException | OutOfMemoryError e) {
728  // if resizing does not work due to extreme aspect ratio or oom, crop the image instead.
729  LOGGER.log(Level.WARNING, "Cropping {0}, because it could not be scaled: " + e.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
730 
731  final int height = bufferedImage.getHeight();
732  final int width = bufferedImage.getWidth();
733  if (iconSize < height || iconSize < width) {
734  final int cropHeight = Math.min(iconSize, height);
735  final int cropWidth = Math.min(iconSize, width);
736  try {
737  if (isCancelled()) {
738  return null;
739  }
740  if (isCancelled()) {
741  return null;
742  }
743  thumbnail = ScalrWrapper.cropImage(bufferedImage, cropWidth, cropHeight);
744  } catch (Exception cropException) {
745  LOGGER.log(Level.WARNING, "Could not crop {0}: " + cropException.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
746  }
747  }
748  } catch (Exception e) {
749  LOGGER.log(Level.WARNING, "Could not scale {0}: " + e.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
750  throw e;
751  }
752  }
753 
754  updateProgress(-1, 1);
755 
756  //if we got a valid thumbnail save it
757  if ((cacheFile != null) && thumbnail != null && DEFAULT_THUMBNAIL != thumbnail) {
758  saveThumbnail(thumbnail);
759  }
760  if (isCancelled()) {
761  return null;
762  }
763  return SwingFXUtils.toFXImage(thumbnail, null);
764  }
765 
771  private void saveThumbnail(BufferedImage thumbnail) {
772  imageSaver.execute(() -> {
773  try {
774  synchronized (cacheFile) {
775  Files.createParentDirs(cacheFile);
776  if (cacheFile.exists()) {
777  cacheFile.delete();
778  }
779  ImageIO.write(thumbnail, FORMAT, cacheFile);
780  }
781  } catch (Exception ex) {
782  LOGGER.log(Level.WARNING, "Could not write thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
783  }
784  });
785  }
786  }
787 
801  public static Task<javafx.scene.image.Image> newReadImageTask(AbstractFile file) {
802  return new ReadImageTask(file);
803 
804  }
805 
809  @NbBundle.Messages({
810  "# {0} - file name",
811  "ReadImageTask.mesageText=Reading image: {0}"})
812  static private class ReadImageTask extends ReadImageTaskBase {
813 
814  ReadImageTask(AbstractFile file) {
815  super(file);
816  updateMessage(Bundle.ReadImageTask_mesageText(file.getName()));
817  }
818 
819  @Override
820  protected javafx.scene.image.Image call() throws Exception {
821  return readImage();
822  }
823  }
824 
828  static private abstract class ReadImageTaskBase extends Task<javafx.scene.image.Image> implements IIOReadProgressListener {
829 
830  private static final String IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT = "ImageIO could not read {0}. It may be unsupported or corrupt"; //NON-NLS
831  final AbstractFile file;
832 
833  ReadImageTaskBase(AbstractFile file) {
834  this.file = file;
835  }
836 
837  protected javafx.scene.image.Image readImage() throws IOException {
838  if (isCancelled()) {
839  return null;
840  }
841  if (ImageUtils.isGIF(file)) {
842  //use JavaFX to directly read GIF to preserve potential animation
843  javafx.scene.image.Image image = new javafx.scene.image.Image(getBufferedReadContentStream(file));
844  if (image.isError() == false) {
845  return image;
846  }
847  } else if (file.getNameExtension().equalsIgnoreCase("tec")) { //NON-NLS
848  ReadContentInputStream readContentInputStream = new ReadContentInputStream(file);
849  // Find first Start Of Image marker
850  readContentInputStream.seek(getJfifStartOfImageOffset(file));
851  //use JavaFX to directly read .tec files
852  javafx.scene.image.Image image = new javafx.scene.image.Image(new BufferedInputStream(readContentInputStream));
853  if (image.isError() == false) {
854  return image;
855  }
856  }
857  //fall through to default image reading code if there was an error
858  return getImageProperty(file, "ImageIO could not read {0}: ",
859  imageReader -> {
860  imageReader.addIIOReadProgressListener(ReadImageTaskBase.this);
861  /*
862  * This is the important part, get or create a
863  * ImageReadParam, create a destination image to hold
864  * the decoded result, then pass that image with the
865  * param.
866  */
867  ImageReadParam param = imageReader.getDefaultReadParam();
868  BufferedImage bufferedImage = imageReader.getImageTypes(0).next().createBufferedImage(imageReader.getWidth(0), imageReader.getHeight(0));
869  param.setDestination(bufferedImage);
870  try {
871  if (isCancelled()) {
872  return null;
873  }
874  bufferedImage = imageReader.read(0, param); //should always be same bufferedImage object
875  } catch (IOException iOException) {
876  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + iOException.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
877  } finally {
878  imageReader.removeIIOReadProgressListener(ReadImageTaskBase.this);
879  }
880  if (isCancelled()) {
881  return null;
882  }
883  return SwingFXUtils.toFXImage(bufferedImage, null);
884  }
885  );
886  }
887 
888  @Override
889  public void imageProgress(ImageReader reader, float percentageDone) {
890  //update this task with the progress reported by ImageReader.read
891  updateProgress(percentageDone, 100);
892  if (isCancelled()) {
893  reader.removeIIOReadProgressListener(this);
894  reader.abort();
895  reader.dispose();
896  }
897  }
898 
899  @Override
900  public boolean isCancelled() {
901  if (Thread.interrupted()) {
902  this.cancel(true);
903  return true;
904  }
905  return super.isCancelled();
906  }
907 
908  @Override
909  protected void succeeded() {
910  super.succeeded();
911  try {
912  javafx.scene.image.Image fxImage = get();
913  if (fxImage == null) {
914  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT, ImageUtils.getContentPathSafe(file));
915  } else if (fxImage.isError()) {
916  //if there was somekind of error, log it
917  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(fxImage.getException()), ImageUtils.getContentPathSafe(file));
918  }
919  } catch (InterruptedException | ExecutionException ex) {
920  failed();
921  }
922  }
923 
924  @Override
925  protected void failed() {
926  super.failed();
927  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(getException()), ImageUtils.getContentPathSafe(file));
928  }
929 
930  @Override
931  public void imageComplete(ImageReader source) {
932  updateProgress(100, 100);
933  }
934 
935  @Override
936  public void imageStarted(ImageReader source, int imageIndex) {
937  }
938 
939  @Override
940  public void sequenceStarted(ImageReader source, int minIndex) {
941  }
942 
943  @Override
944  public void sequenceComplete(ImageReader source) {
945  }
946 
947  @Override
948  public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) {
949  }
950 
951  @Override
952  public void thumbnailProgress(ImageReader source, float percentageDone) {
953  }
954 
955  @Override
956  public void thumbnailComplete(ImageReader source) {
957  }
958 
959  @Override
960  public void readAborted(ImageReader source) {
961  }
962  }
963 
972  static String getContentPathSafe(Content content) {
973  try {
974  return content.getUniquePath();
975  } catch (TskCoreException tskCoreException) {
976  String contentName = content.getName();
977  LOGGER.log(Level.SEVERE, "Failed to get unique path for " + contentName, tskCoreException); //NON-NLS
978  return contentName;
979  }
980  }
981 
990  @Deprecated
991  public static Image getDefaultIcon() {
992  return getDefaultThumbnail();
993  }
994 
1005  @Deprecated
1006 
1007  public static File getFile(long id) {
1008  return getCachedThumbnailLocation(id);
1009  }
1010 
1024  @Nonnull
1025  @Deprecated
1026  public static BufferedImage getIcon(Content content, int iconSize) {
1027  return getThumbnail(content, iconSize);
1028  }
1029 
1044  @Nullable
1045  @Deprecated
1046  public static File getIconFile(Content content, int iconSize) {
1047  return getCachedThumbnailFile(content, iconSize);
1048 
1049  }
1050 }
static File getIconFile(Content content, int iconSize)
static boolean isGIF(AbstractFile file)
static final List< String > SUPPORTED_IMAGE_EXTENSIONS
Definition: ImageUtils.java:95
static boolean isPngFileHeader(AbstractFile file)
static boolean thumbnailSupported(Content content)
void imageProgress(ImageReader reader, float percentageDone)
static Task< javafx.scene.image.Image > newGetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure)
void imageStarted(ImageReader source, int imageIndex)
static synchronized BufferedImage resizeFast(BufferedImage input, int size)
static FileTypeDetector fileTypeDetector
static final SortedSet< String > GIF_MIME_SET
Definition: ImageUtils.java:93
static Task< javafx.scene.image.Image > newReadImageTask(AbstractFile file)
void sequenceStarted(ImageReader source, int minIndex)
static synchronized BufferedImage resizeHighQuality(BufferedImage input, int width, int height)
static boolean isJpegFileHeader(AbstractFile file)
void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex)
GetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure)
static int getImageHeight(AbstractFile file)
void thumbnailProgress(ImageReader source, float percentageDone)
static List< String > getSupportedImageExtensions()
static final BufferedImage DEFAULT_THUMBNAIL
Definition: ImageUtils.java:90
static long getJfifStartOfImageOffset(AbstractFile file)
static File getCachedThumbnailFile(Content content, int iconSize)
static boolean hasImageFileHeader(AbstractFile file)
static byte[] readHeader(AbstractFile file, int buffLength)
static File getCachedThumbnailLocation(long fileID)
static final ConcurrentHashMap< Long, File > cacheFileMap
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static int getImageWidth(AbstractFile file)
static< T > T getImageProperty(AbstractFile file, final String errorTemplate, PropertyExtractor< T > propertyExtractor)
static void addEventTypeSubscriber(Set< Events > eventTypes, PropertyChangeListener subscriber)
Definition: Case.java:419
static BufferedInputStream getBufferedReadContentStream(AbstractFile file)
static boolean isImageThumbnailSupported(AbstractFile file)
static BufferedImage getThumbnail(Content content, int iconSize)
static final List< String > GIF_EXTENSION_LIST
Definition: ImageUtils.java:92
synchronized static FileTypeDetector getFileTypeDetector()
static BufferedImage getIcon(Content content, int iconSize)
static boolean isVideoThumbnailSupported(AbstractFile file)
Definition: VideoUtils.java:97
static final SortedSet< String > SUPPORTED_IMAGE_MIME_TYPES
Definition: ImageUtils.java:96
static synchronized BufferedImage cropImage(BufferedImage input, int width, int height)
static SortedSet< String > getSupportedImageMimeTypes()

Copyright © 2012-2016 Basis Technology. Generated on: Tue Feb 20 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.