Autopsy  4.4
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.Iterator;
39 import java.util.List;
40 import static java.util.Objects.nonNull;
41 import java.util.SortedSet;
42 import java.util.TreeSet;
43 import java.util.concurrent.ConcurrentHashMap;
44 import java.util.concurrent.ExecutionException;
45 import java.util.concurrent.Executor;
46 import java.util.concurrent.Executors;
47 import java.util.logging.Level;
48 import javafx.concurrent.Task;
49 import javafx.embed.swing.SwingFXUtils;
50 import javax.annotation.Nonnull;
51 import javax.annotation.Nullable;
52 import javax.imageio.IIOException;
53 import javax.imageio.ImageIO;
54 import javax.imageio.ImageReadParam;
55 import javax.imageio.ImageReader;
56 import javax.imageio.event.IIOReadProgressListener;
57 import javax.imageio.stream.ImageInputStream;
58 import org.apache.commons.lang3.ObjectUtils;
59 import org.apache.commons.lang3.StringUtils;
60 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
61 import org.opencv.core.Core;
62 import org.openide.util.NbBundle;
67 import org.sleuthkit.datamodel.AbstractFile;
68 import org.sleuthkit.datamodel.Content;
69 import org.sleuthkit.datamodel.ReadContentInputStream;
70 import org.sleuthkit.datamodel.TskCoreException;
71 
76 public class ImageUtils {
77 
78  private static final Logger LOGGER = Logger.getLogger(ImageUtils.class.getName());
79 
83  private static final String FORMAT = "png"; //NON-NLS
84 
85  public static final int ICON_SIZE_SMALL = 50;
86  public static final int ICON_SIZE_MEDIUM = 100;
87  public static final int ICON_SIZE_LARGE = 200;
88 
89  private static final BufferedImage DEFAULT_THUMBNAIL;
90 
91  private static final List<String> GIF_EXTENSION_LIST = Arrays.asList("gif");
92  private static final SortedSet<String> GIF_MIME_SET = ImmutableSortedSet.copyOf(new String[]{"image/gif"});
93 
94  private static final List<String> SUPPORTED_IMAGE_EXTENSIONS = new ArrayList<>();
95  private static final SortedSet<String> SUPPORTED_IMAGE_MIME_TYPES;
96 
97  private static final boolean OPEN_CV_LOADED;
98 
106  private static final ConcurrentHashMap<Long, File> cacheFileMap = new ConcurrentHashMap<>();
107 
108  static {
109  ImageIO.scanForPlugins();
110  BufferedImage tempImage;
111  try {
112  tempImage = ImageIO.read(ImageUtils.class.getResourceAsStream("/org/sleuthkit/autopsy/images/file-icon.png"));//NON-NLS
113  } catch (IOException ex) {
114  LOGGER.log(Level.SEVERE, "Failed to load default icon.", ex); //NON-NLS
115  tempImage = null;
116  }
117  DEFAULT_THUMBNAIL = tempImage;
118 
119  //load opencv libraries
120  boolean openCVLoadedTemp;
121  try {
122  System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
123  if (System.getProperty("os.arch").equals("amd64") || System.getProperty("os.arch").equals("x86_64")) { //NON-NLS
124  System.loadLibrary("opencv_ffmpeg248_64"); //NON-NLS
125  } else {
126  System.loadLibrary("opencv_ffmpeg248"); //NON-NLS
127  }
128 
129  openCVLoadedTemp = true;
130  } catch (UnsatisfiedLinkError e) {
131  openCVLoadedTemp = false;
132  LOGGER.log(Level.SEVERE, "OpenCV Native code library failed to load", e); //NON-NLS
133  //TODO: show warning bubble
134 
135  }
136 
137  OPEN_CV_LOADED = openCVLoadedTemp;
138  SUPPORTED_IMAGE_EXTENSIONS.addAll(Arrays.asList(ImageIO.getReaderFileSuffixes()));
139  SUPPORTED_IMAGE_EXTENSIONS.add("tec"); // Add JFIF .tec files
140  SUPPORTED_IMAGE_EXTENSIONS.removeIf("db"::equals); // remove db files
141 
142  SUPPORTED_IMAGE_MIME_TYPES = new TreeSet<>(Arrays.asList(ImageIO.getReaderMIMETypes()));
143  /*
144  * special cases and variants that we support, but don't get registered
145  * with ImageIO automatically
146  */
147  SUPPORTED_IMAGE_MIME_TYPES.addAll(Arrays.asList(
148  "image/x-rgb", //NON-NLS
149  "image/x-ms-bmp", //NON-NLS
150  "image/x-portable-graymap", //NON-NLS
151  "image/x-portable-bitmap", //NON-NLS
152  "application/x-123")); //TODO: is this correct? -jm //NON-NLS
153  SUPPORTED_IMAGE_MIME_TYPES.removeIf("application/octet-stream"::equals); //NON-NLS
154 
155  //Clear the file map when the case changes, so we don't accidentaly get images from the old case.
156  Case.addEventSubscriber(Case.Events.CURRENT_CASE.toString(), evt -> cacheFileMap.clear());
157  }
158 
163 
167  private static final Executor imageSaver
168  = Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
169  .namingPattern("thumbnail-saver-%d").build()); //NON-NLS
170 
171  public static List<String> getSupportedImageExtensions() {
172  return Collections.unmodifiableList(SUPPORTED_IMAGE_EXTENSIONS);
173  }
174 
175  public static SortedSet<String> getSupportedImageMimeTypes() {
176  return Collections.unmodifiableSortedSet(SUPPORTED_IMAGE_MIME_TYPES);
177  }
178 
185  public static Image getDefaultThumbnail() {
186  return DEFAULT_THUMBNAIL;
187  }
188 
199  public static boolean thumbnailSupported(Content content) {
200 
201  if (!(content instanceof AbstractFile)) {
202  return false;
203  }
204  AbstractFile file = (AbstractFile) content;
205 
207  || isImageThumbnailSupported(file);
208  }
209 
218  public static boolean isImageThumbnailSupported(AbstractFile file) {
219  return isMediaThumbnailSupported(file, "image/", SUPPORTED_IMAGE_MIME_TYPES, SUPPORTED_IMAGE_EXTENSIONS) || hasImageFileHeader(file);//NON-NLS
220  }
221 
230  public static boolean isGIF(AbstractFile file) {
231  return isMediaThumbnailSupported(file, null, GIF_MIME_SET, GIF_EXTENSION_LIST);
232  }
233 
253  static boolean isMediaThumbnailSupported(AbstractFile file, String mimeTypePrefix, final Collection<String> supportedMimeTypes, final List<String> supportedExtension) {
254  if (false == file.isFile() || file.getSize() <= 0) {
255  return false;
256  }
257 
258  String extension = file.getNameExtension();
259 
260  if (StringUtils.isNotBlank(extension) && supportedExtension.contains(extension)) {
261  return true;
262  } else {
263  try {
264  String mimeType = getFileTypeDetector().detect(file);
265  if (StringUtils.isNotBlank(mimeTypePrefix) && mimeType.startsWith(mimeTypePrefix)) {
266  return true;
267  }
268  return supportedMimeTypes.contains(mimeType);
269  } catch (FileTypeDetectorInitException | TskCoreException ex) {
270  LOGGER.log(Level.SEVERE, "Error determining MIME type of " + getContentPathSafe(file), ex);//NON-NLS
271  return false;
272  }
273  }
274  }
275 
288  if (fileTypeDetector == null) {
289  fileTypeDetector = new FileTypeDetector();
290  }
291  return fileTypeDetector;
292  }
293 
304  public static BufferedImage getThumbnail(Content content, int iconSize) {
305  if (content instanceof AbstractFile) {
306  AbstractFile file = (AbstractFile) content;
307  if (ImageUtils.isGIF(file)) {
308  /*
309  * Intercepting the image reading code for GIFs here allows us
310  * to rescale easily, but we lose animations.
311  */
312  try (BufferedInputStream bufferedReadContentStream = getBufferedReadContentStream(file);) {
313  final BufferedImage image = ImageIO.read(bufferedReadContentStream);
314  if (image != null) {
315  return ScalrWrapper.resizeHighQuality(image, iconSize, iconSize);
316  }
317  } catch (IOException iOException) {
318  LOGGER.log(Level.WARNING, "Failed to get thumbnail for " + getContentPathSafe(content), iOException); //NON-NLS
319  }
320  return DEFAULT_THUMBNAIL;
321  }
322 
323  Task<javafx.scene.image.Image> thumbnailTask = newGetThumbnailTask(file, iconSize, true);
324  thumbnailTask.run();
325  try {
326  return SwingFXUtils.fromFXImage(thumbnailTask.get(), null);
327  } catch (InterruptedException | ExecutionException ex) {
328  LOGGER.log(Level.WARNING, "Failed to get thumbnail for " + getContentPathSafe(content), ex); //NON-NLS
329  }
330  }
331  return DEFAULT_THUMBNAIL;
332  }
333 
343  private static BufferedInputStream getBufferedReadContentStream(AbstractFile file) {
344  return new BufferedInputStream(new ReadContentInputStream(file));
345  }
346 
357  @Nullable
358  public static File getCachedThumbnailFile(Content content, int iconSize) {
359  getThumbnail(content, iconSize);
360  return getCachedThumbnailLocation(content.getId());
361  }
362 
373  private static File getCachedThumbnailLocation(long fileID) {
374  return cacheFileMap.computeIfAbsent(fileID, id -> {
375  try {
376  String cacheDirectory = Case.getCurrentCase().getCacheDirectory();
377  return Paths.get(cacheDirectory, "thumbnails", fileID + ".png").toFile(); //NON-NLS
378  } catch (IllegalStateException e) {
379  LOGGER.log(Level.WARNING, "Could not get cached thumbnail location. No case is open."); //NON-NLS
380  return null;
381  }
382  });
383  }
384 
393  public static boolean hasImageFileHeader(AbstractFile file) {
394  return isJpegFileHeader(file) || isPngFileHeader(file);
395  }
396 
404  public static boolean isJpegFileHeader(AbstractFile file) {
405  if (file.getSize() < 100) {
406  return false;
407  }
408 
409  try {
410  byte[] fileHeaderBuffer = readHeader(file, 2);
411  /*
412  * Check for the JPEG header. Since Java bytes are signed, we cast
413  * them to an int first.
414  */
415  return (((fileHeaderBuffer[0] & 0xff) == 0xff) && ((fileHeaderBuffer[1] & 0xff) == 0xd8));
416  } catch (TskCoreException ex) {
417  //ignore if can't read the first few bytes, not a JPEG
418  return false;
419  }
420  }
421 
431  private static long getJfifStartOfImageOffset(AbstractFile file) {
432  byte[] fileHeaderBuffer;
433  long length;
434  try {
435  length = file.getSize();
436  if (length % 2 != 0) {
437  length -= 1; // Make it an even number so we can parse two bytes at a time
438  }
439  if (length >= 1024) {
440  length = 1024;
441  }
442  fileHeaderBuffer = readHeader(file, (int) length); // read up to first 1024 bytes
443  } catch (TskCoreException ex) {
444  // Couldn't read header. Let ImageIO try it.
445  return 0;
446  }
447 
448  if (fileHeaderBuffer != null) {
449  for (int index = 0; index < length; index += 2) {
450  // Look for Start Of Image marker and return the index when it's found
451  if ((fileHeaderBuffer[index] == (byte) 0xFF) && (fileHeaderBuffer[index + 1] == (byte) 0xD8)) {
452  return index;
453  }
454  }
455  }
456 
457  // Didn't match JFIF. Let ImageIO try to open it from offset 0.
458  return 0;
459  }
460 
468  public static boolean isPngFileHeader(AbstractFile file) {
469  if (file.getSize() < 10) {
470  return false;
471  }
472 
473  try {
474  byte[] fileHeaderBuffer = readHeader(file, 8);
475  /*
476  * Check for the png header. Since Java bytes are signed, we cast
477  * them to an int first.
478  */
479  return (((fileHeaderBuffer[1] & 0xff) == 0x50) && ((fileHeaderBuffer[2] & 0xff) == 0x4E)
480  && ((fileHeaderBuffer[3] & 0xff) == 0x47) && ((fileHeaderBuffer[4] & 0xff) == 0x0D)
481  && ((fileHeaderBuffer[5] & 0xff) == 0x0A) && ((fileHeaderBuffer[6] & 0xff) == 0x1A)
482  && ((fileHeaderBuffer[7] & 0xff) == 0x0A));
483 
484  } catch (TskCoreException ex) {
485  //ignore if can't read the first few bytes, not an png
486  return false;
487  }
488  }
489 
490  private static byte[] readHeader(AbstractFile file, int buffLength) throws TskCoreException {
491  byte[] fileHeaderBuffer = new byte[buffLength];
492  int bytesRead = file.read(fileHeaderBuffer, 0, buffLength);
493 
494  if (bytesRead != buffLength) {
495  //ignore if can't read the first few bytes, not an image
496  throw new TskCoreException("Could not read " + buffLength + " bytes from " + file.getName());//NON-NLS
497  }
498  return fileHeaderBuffer;
499  }
500 
511  static public int getImageWidth(AbstractFile file) throws IOException {
512  return getImageProperty(file,
513  "ImageIO could not determine width of {0}: ", //NON-NLS
514  imageReader -> imageReader.getWidth(0)
515  );
516  }
517 
528  static public int getImageHeight(AbstractFile file) throws IOException {
529  return getImageProperty(file,
530  "ImageIO could not determine height of {0}: ", //NON-NLS
531  imageReader -> imageReader.getHeight(0)
532  );
533 
534  }
535 
544  @FunctionalInterface
545  private static interface PropertyExtractor<T> {
546 
547  public T extract(ImageReader reader) throws IOException;
548  }
549 
570  private static <T> T getImageProperty(AbstractFile file, final String errorTemplate, PropertyExtractor<T> propertyExtractor) throws IOException {
571  try (InputStream inputStream = getBufferedReadContentStream(file);
572  ImageInputStream input = ImageIO.createImageInputStream(inputStream)) {
573  if (input == null) {
574  IIOException iioException = new IIOException("Could not create ImageInputStream.");
575  LOGGER.log(Level.WARNING, errorTemplate + iioException.toString(), getContentPathSafe(file));
576  throw iioException;
577  }
578  Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
579 
580  if (readers.hasNext()) {
581  ImageReader reader = readers.next();
582  reader.setInput(input);
583  try {
584  return propertyExtractor.extract(reader);
585  } catch (IOException ex) {
586  LOGGER.log(Level.WARNING, errorTemplate + ex.toString(), getContentPathSafe(file));
587  throw ex;
588  } finally {
589  reader.dispose();
590  }
591  } else {
592  IIOException iioException = new IIOException("No ImageReader found.");
593  LOGGER.log(Level.WARNING, errorTemplate + iioException.toString(), getContentPathSafe(file));
594  throw iioException;
595  }
596  }
597  }
598 
615  public static Task<javafx.scene.image.Image> newGetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure) {
616  return new GetThumbnailTask(file, iconSize, defaultOnFailure);
617 
618  }
619 
623  static private class GetThumbnailTask extends ReadImageTaskBase {
624 
625  private static final String FAILED_TO_READ_IMAGE_FOR_THUMBNAIL_GENERATION = "Failed to read {0} for thumbnail generation."; //NON-NLS
626 
627  private final int iconSize;
628  private final File cacheFile;
629  private final boolean defaultOnFailure;
630 
631  @NbBundle.Messages({"# {0} - file name",
632  "GetOrGenerateThumbnailTask.loadingThumbnailFor=Loading thumbnail for {0}",
633  "# {0} - file name",
634  "GetOrGenerateThumbnailTask.generatingPreviewFor=Generating preview for {0}"})
635  private GetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure) {
636  super(file);
637  updateMessage(Bundle.GetOrGenerateThumbnailTask_loadingThumbnailFor(file.getName()));
638  this.iconSize = iconSize;
639  this.defaultOnFailure = defaultOnFailure;
640  this.cacheFile = getCachedThumbnailLocation(file.getId());
641  }
642 
643  @Override
644  protected javafx.scene.image.Image call() throws Exception {
645  if (isGIF(file)) {
646  return readImage();
647  }
648  if (isCancelled()) {
649  return null;
650  }
651 
652  // If a thumbnail file is already saved locally, just read that.
653  if (cacheFile != null) {
654  synchronized (cacheFile) {
655  if (cacheFile.exists()) {
656  try {
657  BufferedImage cachedThumbnail = ImageIO.read(cacheFile);
658  if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
659  return SwingFXUtils.toFXImage(cachedThumbnail, null);
660  }
661  } catch (Exception ex) {
662  LOGGER.log(Level.WARNING, "ImageIO had a problem reading the cached thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
663  cacheFile.delete(); //since we can't read the file we might as well delete it.
664  }
665  }
666  }
667  }
668 
669  if (isCancelled()) {
670  return null;
671  }
672 
673  //There was no correctly-sized cached thumbnail so make one.
674  BufferedImage thumbnail = null;
676  if (OPEN_CV_LOADED) {
677  updateMessage(Bundle.GetOrGenerateThumbnailTask_generatingPreviewFor(file.getName()));
678  thumbnail = VideoUtils.generateVideoThumbnail(file, iconSize);
679  }
680  if (null == thumbnail) {
681  if (defaultOnFailure) {
682  thumbnail = DEFAULT_THUMBNAIL;
683  } else {
684  throw new IIOException("Failed to generate a thumbnail for " + getContentPathSafe(file));//NON-NLS
685  }
686  }
687 
688  } else {
689  //read the image into a buffered image.
690  //TODO: I don't like this, we just converted it from BufferedIamge to fx Image -jm
691  BufferedImage bufferedImage = SwingFXUtils.fromFXImage(readImage(), null);
692  if (null == bufferedImage) {
693  String msg = MessageFormat.format(FAILED_TO_READ_IMAGE_FOR_THUMBNAIL_GENERATION, getContentPathSafe(file));
694  LOGGER.log(Level.WARNING, msg);
695  throw new IIOException(msg);
696  }
697  updateProgress(-1, 1);
698 
699  //resize, or if that fails, crop it
700  try {
701  thumbnail = ScalrWrapper.resizeFast(bufferedImage, iconSize);
702  } catch (IllegalArgumentException | OutOfMemoryError e) {
703  // if resizing does not work due to extreme aspect ratio or oom, crop the image instead.
704  LOGGER.log(Level.WARNING, "Cropping {0}, because it could not be scaled: " + e.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
705 
706  final int height = bufferedImage.getHeight();
707  final int width = bufferedImage.getWidth();
708  if (iconSize < height || iconSize < width) {
709  final int cropHeight = Math.min(iconSize, height);
710  final int cropWidth = Math.min(iconSize, width);
711  try {
712  thumbnail = ScalrWrapper.cropImage(bufferedImage, cropWidth, cropHeight);
713  } catch (Exception cropException) {
714  LOGGER.log(Level.WARNING, "Could not crop {0}: " + cropException.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
715  }
716  }
717  } catch (Exception e) {
718  LOGGER.log(Level.WARNING, "Could not scale {0}: " + e.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
719  throw e;
720  }
721  }
722 
723  if (isCancelled()) {
724  return null;
725  }
726 
727  updateProgress(-1, 1);
728 
729  //if we got a valid thumbnail save it
730  if ((cacheFile != null) && thumbnail != null && DEFAULT_THUMBNAIL != thumbnail) {
731  saveThumbnail(thumbnail);
732  }
733 
734  return SwingFXUtils.toFXImage(thumbnail, null);
735  }
736 
742  private void saveThumbnail(BufferedImage thumbnail) {
743  imageSaver.execute(() -> {
744  try {
745  synchronized (cacheFile) {
746  Files.createParentDirs(cacheFile);
747  if (cacheFile.exists()) {
748  cacheFile.delete();
749  }
750  ImageIO.write(thumbnail, FORMAT, cacheFile);
751  }
752  } catch (Exception ex) {
753  LOGGER.log(Level.WARNING, "Could not write thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
754  }
755  });
756  }
757  }
758 
772  public static Task<javafx.scene.image.Image> newReadImageTask(AbstractFile file) {
773  return new ReadImageTask(file);
774 
775  }
776 
780  @NbBundle.Messages({
781  "# {0} - file name",
782  "ReadImageTask.mesageText=Reading image: {0}"})
783  static private class ReadImageTask extends ReadImageTaskBase {
784 
785  ReadImageTask(AbstractFile file) {
786  super(file);
787  updateMessage(Bundle.ReadImageTask_mesageText(file.getName()));
788  }
789 
790  @Override
791  protected javafx.scene.image.Image call() throws Exception {
792  return readImage();
793  }
794  }
795 
799  static private abstract class ReadImageTaskBase extends Task<javafx.scene.image.Image> implements IIOReadProgressListener {
800 
801  private static final String IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT = "ImageIO could not read {0}. It may be unsupported or corrupt"; //NON-NLS
802  final AbstractFile file;
803 
804  ReadImageTaskBase(AbstractFile file) {
805  this.file = file;
806  }
807 
808  protected javafx.scene.image.Image readImage() throws IOException {
809  if (ImageUtils.isGIF(file)) {
810  //use JavaFX to directly read GIF to preserve potential animation
811  javafx.scene.image.Image image = new javafx.scene.image.Image(getBufferedReadContentStream(file));
812  if (image.isError() == false) {
813  return image;
814  }
815  } else if (file.getNameExtension().equalsIgnoreCase("tec")) { //NON-NLS
816  ReadContentInputStream readContentInputStream = new ReadContentInputStream(file);
817  // Find first Start Of Image marker
818  readContentInputStream.seek(getJfifStartOfImageOffset(file));
819  //use JavaFX to directly read .tec files
820  javafx.scene.image.Image image = new javafx.scene.image.Image(new BufferedInputStream(readContentInputStream));
821  if (image.isError() == false) {
822  return image;
823  }
824  }
825  //fall through to default image reading code if there was an error
826  if (isCancelled()) {
827  return null;
828  }
829 
830  return getImageProperty(file, "ImageIO could not read {0}: ",
831  imageReader -> {
832  imageReader.addIIOReadProgressListener(ReadImageTaskBase.this);
833  /*
834  * This is the important part, get or create a
835  * ImageReadParam, create a destination image to hold
836  * the decoded result, then pass that image with the
837  * param.
838  */
839  ImageReadParam param = imageReader.getDefaultReadParam();
840  BufferedImage bufferedImage = imageReader.getImageTypes(0).next().createBufferedImage(imageReader.getWidth(0), imageReader.getHeight(0));
841  param.setDestination(bufferedImage);
842  try {
843  bufferedImage = imageReader.read(0, param); //should always be same bufferedImage object
844  } catch (IOException iOException) {
845  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + iOException.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
846  } finally {
847  imageReader.removeIIOReadProgressListener(ReadImageTaskBase.this);
848  }
849  if (isCancelled()) {
850  return null;
851  }
852  return SwingFXUtils.toFXImage(bufferedImage, null);
853  }
854  );
855  }
856 
857  @Override
858  public void imageProgress(ImageReader reader, float percentageDone) {
859  //update this task with the progress reported by ImageReader.read
860  updateProgress(percentageDone, 100);
861  if (isCancelled()) {
862  reader.removeIIOReadProgressListener(this);
863  reader.abort();
864  reader.dispose();
865  }
866  }
867 
868  @Override
869  protected void succeeded() {
870  super.succeeded();
871  try {
872  javafx.scene.image.Image fxImage = get();
873  if (fxImage == null) {
874  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT, ImageUtils.getContentPathSafe(file));
875  } else if (fxImage.isError()) {
876  //if there was somekind of error, log it
877  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(fxImage.getException()), ImageUtils.getContentPathSafe(file));
878  }
879  } catch (InterruptedException | ExecutionException ex) {
880  failed();
881  }
882  }
883 
884  @Override
885  protected void failed() {
886  super.failed();
887  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(getException()), ImageUtils.getContentPathSafe(file));
888  }
889 
890  @Override
891  public void imageComplete(ImageReader source) {
892  updateProgress(100, 100);
893  }
894 
895  @Override
896  public void imageStarted(ImageReader source, int imageIndex) {
897  }
898 
899  @Override
900  public void sequenceStarted(ImageReader source, int minIndex) {
901  }
902 
903  @Override
904  public void sequenceComplete(ImageReader source) {
905  }
906 
907  @Override
908  public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) {
909  }
910 
911  @Override
912  public void thumbnailProgress(ImageReader source, float percentageDone) {
913  }
914 
915  @Override
916  public void thumbnailComplete(ImageReader source) {
917  }
918 
919  @Override
920  public void readAborted(ImageReader source) {
921  }
922  }
923 
932  static String getContentPathSafe(Content content) {
933  try {
934  return content.getUniquePath();
935  } catch (TskCoreException tskCoreException) {
936  String contentName = content.getName();
937  LOGGER.log(Level.SEVERE, "Failed to get unique path for " + contentName, tskCoreException); //NON-NLS
938  return contentName;
939  }
940  }
941 
950  @Deprecated
951  public static Image getDefaultIcon() {
952  return getDefaultThumbnail();
953  }
954 
965  @Deprecated
966 
967  public static File getFile(long id) {
968  return getCachedThumbnailLocation(id);
969  }
970 
984  @Nonnull
985  @Deprecated
986  public static BufferedImage getIcon(Content content, int iconSize) {
987  return getThumbnail(content, iconSize);
988  }
989 
1004  @Nullable
1005  @Deprecated
1006  public static File getIconFile(Content content, int iconSize) {
1007  return getCachedThumbnailFile(content, iconSize);
1008 
1009  }
1010 
1011 }
static File getIconFile(Content content, int iconSize)
static boolean isGIF(AbstractFile file)
static final List< String > SUPPORTED_IMAGE_EXTENSIONS
Definition: ImageUtils.java:94
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:92
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:89
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:161
static int getImageWidth(AbstractFile file)
static< T > T getImageProperty(AbstractFile file, final String errorTemplate, PropertyExtractor< T > propertyExtractor)
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:91
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:95
static void addEventSubscriber(Set< String > eventNames, PropertyChangeListener subscriber)
Definition: Case.java:381
static synchronized BufferedImage cropImage(BufferedImage input, int width, int height)
static SortedSet< String > getSupportedImageMimeTypes()

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