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

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