Autopsy  4.1
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;
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 
254  static boolean isMediaThumbnailSupported(AbstractFile file, String mimeTypePrefix, final Collection<String> supportedMimeTypes, final List<String> supportedExtension) {
255  if (false == file.isFile() || file.getSize() <= 0) {
256  return false;
257  }
258 
259  String extension = file.getNameExtension();
260 
261  if (StringUtils.isNotBlank(extension) && supportedExtension.contains(extension)) {
262  return true;
263  } else {
264  try {
265  String mimeType = getFileTypeDetector().detect(file);
266  if (StringUtils.isNotBlank(mimeTypePrefix) && mimeType.startsWith(mimeTypePrefix)) {
267  return true;
268  }
269  return supportedMimeTypes.contains(mimeType);
270  } catch (FileTypeDetectorInitException | TskCoreException ex) {
271  LOGGER.log(Level.SEVERE, "Error determining MIME type of " + getContentPathSafe(file), ex);//NON-NLS
272  return false;
273  }
274  }
275  }
276 
289  if (fileTypeDetector == null) {
290  fileTypeDetector = new FileTypeDetector();
291  }
292  return fileTypeDetector;
293  }
294 
305  public static BufferedImage getThumbnail(Content content, int iconSize) {
306  if (content instanceof AbstractFile) {
307  AbstractFile file = (AbstractFile) content;
308  if (ImageUtils.isGIF(file)) {
309  /*
310  * Intercepting the image reading code for GIFs here allows us
311  * to rescale easily, but we lose animations.
312  */
313  try (BufferedInputStream bufferedReadContentStream = getBufferedReadContentStream(file);) {
314  final BufferedImage image = ImageIO.read(bufferedReadContentStream);
315  if (image != null) {
316  return ScalrWrapper.resizeHighQuality(image, iconSize, iconSize);
317  }
318  } catch (IOException iOException) {
319  LOGGER.log(Level.WARNING, "Failed to get thumbnail for " + getContentPathSafe(content), iOException); //NON-NLS
320  }
321  return DEFAULT_THUMBNAIL;
322  }
323 
324  Task<javafx.scene.image.Image> thumbnailTask = newGetThumbnailTask(file, iconSize, true);
325  thumbnailTask.run();
326  try {
327  return SwingFXUtils.fromFXImage(thumbnailTask.get(), null);
328  } catch (InterruptedException | ExecutionException ex) {
329  LOGGER.log(Level.WARNING, "Failed to get thumbnail for " + getContentPathSafe(content), ex); //NON-NLS
330  }
331  }
332  return DEFAULT_THUMBNAIL;
333  }
334 
344  private static BufferedInputStream getBufferedReadContentStream(AbstractFile file) {
345  return new BufferedInputStream(new ReadContentInputStream(file));
346  }
347 
358  @Nullable
359  public static File getCachedThumbnailFile(Content content, int iconSize) {
360  getThumbnail(content, iconSize);
361  return getCachedThumbnailLocation(content.getId());
362  }
363 
374  private static File getCachedThumbnailLocation(long fileID) {
375  return cacheFileMap.computeIfAbsent(fileID, id -> {
376  try {
377  String cacheDirectory = Case.getCurrentCase().getCacheDirectory();
378  return Paths.get(cacheDirectory, "thumbnails", fileID + ".png").toFile(); //NON-NLS
379  } catch (IllegalStateException e) {
380  LOGGER.log(Level.WARNING, "Could not get cached thumbnail location. No case is open."); //NON-NLS
381  return null;
382  }
383  });
384  }
385 
394  public static boolean hasImageFileHeader(AbstractFile file) {
395  return isJpegFileHeader(file) || isPngFileHeader(file);
396  }
397 
405  public static boolean isJpegFileHeader(AbstractFile file) {
406  if (file.getSize() < 100) {
407  return false;
408  }
409 
410  try {
411  byte[] fileHeaderBuffer = readHeader(file, 2);
412  /*
413  * Check for the JPEG header. Since Java bytes are signed, we cast
414  * them to an int first.
415  */
416  return (((fileHeaderBuffer[0] & 0xff) == 0xff) && ((fileHeaderBuffer[1] & 0xff) == 0xd8));
417  } catch (TskCoreException ex) {
418  //ignore if can't read the first few bytes, not a JPEG
419  return false;
420  }
421  }
422 
432  private static long getJfifStartOfImageOffset(AbstractFile file) {
433  byte[] fileHeaderBuffer;
434  long length;
435  try {
436  length = file.getSize();
437  if (length % 2 != 0) {
438  length -= 1; // Make it an even number so we can parse two bytes at a time
439  }
440  if (length >= 1024) {
441  length = 1024;
442  }
443  fileHeaderBuffer = readHeader(file, (int) length); // read up to first 1024 bytes
444  } catch (TskCoreException ex) {
445  // Couldn't read header. Let ImageIO try it.
446  return 0;
447  }
448 
449  if (fileHeaderBuffer != null) {
450  for (int index = 0; index < length; index += 2) {
451  // Look for Start Of Image marker and return the index when it's found
452  if ((fileHeaderBuffer[index] == (byte) 0xFF) && (fileHeaderBuffer[index + 1] == (byte) 0xD8)) {
453  return index;
454  }
455  }
456  }
457 
458  // Didn't match JFIF. Let ImageIO try to open it from offset 0.
459  return 0;
460  }
461 
469  public static boolean isPngFileHeader(AbstractFile file) {
470  if (file.getSize() < 10) {
471  return false;
472  }
473 
474  try {
475  byte[] fileHeaderBuffer = readHeader(file, 8);
476  /*
477  * Check for the png header. Since Java bytes are signed, we cast
478  * them to an int first.
479  */
480  return (((fileHeaderBuffer[1] & 0xff) == 0x50) && ((fileHeaderBuffer[2] & 0xff) == 0x4E)
481  && ((fileHeaderBuffer[3] & 0xff) == 0x47) && ((fileHeaderBuffer[4] & 0xff) == 0x0D)
482  && ((fileHeaderBuffer[5] & 0xff) == 0x0A) && ((fileHeaderBuffer[6] & 0xff) == 0x1A)
483  && ((fileHeaderBuffer[7] & 0xff) == 0x0A));
484 
485  } catch (TskCoreException ex) {
486  //ignore if can't read the first few bytes, not an png
487  return false;
488  }
489  }
490 
491  private static byte[] readHeader(AbstractFile file, int buffLength) throws TskCoreException {
492  byte[] fileHeaderBuffer = new byte[buffLength];
493  int bytesRead = file.read(fileHeaderBuffer, 0, buffLength);
494 
495  if (bytesRead != buffLength) {
496  //ignore if can't read the first few bytes, not an image
497  throw new TskCoreException("Could not read " + buffLength + " bytes from " + file.getName());//NON-NLS
498  }
499  return fileHeaderBuffer;
500  }
501 
512  static public int getImageWidth(AbstractFile file) throws IOException {
513  return getImageProperty(file,
514  "ImageIO could not determine width of {0}: ", //NON-NLS
515  imageReader -> imageReader.getWidth(0)
516  );
517  }
518 
529  static public int getImageHeight(AbstractFile file) throws IOException {
530  return getImageProperty(file,
531  "ImageIO could not determine height of {0}: ", //NON-NLS
532  imageReader -> imageReader.getHeight(0)
533  );
534 
535  }
536 
545  @FunctionalInterface
546  private static interface PropertyExtractor<T> {
547 
548  public T extract(ImageReader reader) throws IOException;
549  }
550 
572  private static <T> T getImageProperty(AbstractFile file, final String errorTemplate, PropertyExtractor<T> propertyExtractor) throws IOException {
573  try (InputStream inputStream = getBufferedReadContentStream(file);
574  ImageInputStream input = ImageIO.createImageInputStream(inputStream)) {
575  if (input == null) {
576  IIOException iioException = new IIOException("Could not create ImageInputStream.");
577  LOGGER.log(Level.WARNING, errorTemplate + iioException.toString(), getContentPathSafe(file));
578  throw iioException;
579  }
580  Iterator<ImageReader> readers = ImageIO.getImageReaders(input);
581 
582  if (readers.hasNext()) {
583  ImageReader reader = readers.next();
584  reader.setInput(input);
585  try {
586  return propertyExtractor.extract(reader);
587  } catch (IOException ex) {
588  LOGGER.log(Level.WARNING, errorTemplate + ex.toString(), getContentPathSafe(file));
589  throw ex;
590  } finally {
591  reader.dispose();
592  }
593  } else {
594  IIOException iioException = new IIOException("No ImageReader found.");
595  LOGGER.log(Level.WARNING, errorTemplate + iioException.toString(), getContentPathSafe(file));
596  throw iioException;
597  }
598  }
599  }
600 
617  public static Task<javafx.scene.image.Image> newGetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure) {
618  return new GetThumbnailTask(file, iconSize, defaultOnFailure);
619 
620  }
621 
625  static private class GetThumbnailTask extends ReadImageTaskBase {
626 
627  private static final String FAILED_TO_READ_IMAGE_FOR_THUMBNAIL_GENERATION = "Failed to read {0} for thumbnail generation."; //NON-NLS
628 
629  private final int iconSize;
630  private final File cacheFile;
631  private final boolean defaultOnFailure;
632 
633  @NbBundle.Messages({"# {0} - file name",
634  "GetOrGenerateThumbnailTask.loadingThumbnailFor=Loading thumbnail for {0}",
635  "# {0} - file name",
636  "GetOrGenerateThumbnailTask.generatingPreviewFor=Generating preview for {0}"})
637  private GetThumbnailTask(AbstractFile file, int iconSize, boolean defaultOnFailure) {
638  super(file);
639  updateMessage(Bundle.GetOrGenerateThumbnailTask_loadingThumbnailFor(file.getName()));
640  this.iconSize = iconSize;
641  this.defaultOnFailure = defaultOnFailure;
642  this.cacheFile = getCachedThumbnailLocation(file.getId());
643  }
644 
645  @Override
646  protected javafx.scene.image.Image call() throws Exception {
647  if (isGIF(file)) {
648  return readImage();
649  }
650  if (isCancelled()) {
651  return null;
652  }
653 
654  // If a thumbnail file is already saved locally, just read that.
655  if (cacheFile != null) {
656  synchronized (cacheFile) {
657  if (cacheFile.exists()) {
658  try {
659  BufferedImage cachedThumbnail = ImageIO.read(cacheFile);
660  if (nonNull(cachedThumbnail) && cachedThumbnail.getWidth() == iconSize) {
661  return SwingFXUtils.toFXImage(cachedThumbnail, null);
662  }
663  } catch (Exception ex) {
664  LOGGER.log(Level.WARNING, "ImageIO had a problem reading the cached thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
665  cacheFile.delete(); //since we can't read the file we might as well delete it.
666  }
667  }
668  }
669  }
670 
671  if (isCancelled()) {
672  return null;
673  }
674 
675  //There was no correctly-sized cached thumbnail so make one.
676  BufferedImage thumbnail = null;
678  if (OPEN_CV_LOADED) {
679  updateMessage(Bundle.GetOrGenerateThumbnailTask_generatingPreviewFor(file.getName()));
680  thumbnail = VideoUtils.generateVideoThumbnail(file, iconSize);
681  }
682  if (null == thumbnail) {
683  if (defaultOnFailure) {
684  thumbnail = DEFAULT_THUMBNAIL;
685  } else {
686  throw new IIOException("Failed to generate a thumbnail for " + getContentPathSafe(file));//NON-NLS
687  }
688  }
689 
690  } else {
691  //read the image into a buffered image.
692  //TODO: I don't like this, we just converted it from BufferedIamge to fx Image -jm
693  BufferedImage bufferedImage = SwingFXUtils.fromFXImage(readImage(), null);
694  if (null == bufferedImage) {
695  String msg = MessageFormat.format(FAILED_TO_READ_IMAGE_FOR_THUMBNAIL_GENERATION, getContentPathSafe(file));
696  LOGGER.log(Level.WARNING, msg);
697  throw new IIOException(msg);
698  }
699  updateProgress(-1, 1);
700 
701  //resize, or if that fails, crop it
702  try {
703  thumbnail = ScalrWrapper.resizeFast(bufferedImage, iconSize);
704  } catch (IllegalArgumentException | OutOfMemoryError e) {
705  // if resizing does not work due to extreme aspect ratio or oom, crop the image instead.
706  LOGGER.log(Level.WARNING, "Cropping {0}, because it could not be scaled: " + e.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
707 
708  final int height = bufferedImage.getHeight();
709  final int width = bufferedImage.getWidth();
710  if (iconSize < height || iconSize < width) {
711  final int cropHeight = Math.min(iconSize, height);
712  final int cropWidth = Math.min(iconSize, width);
713  try {
714  thumbnail = ScalrWrapper.cropImage(bufferedImage, cropWidth, cropHeight);
715  } catch (Exception cropException) {
716  LOGGER.log(Level.WARNING, "Could not crop {0}: " + cropException.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
717  }
718  }
719  } catch (Exception e) {
720  LOGGER.log(Level.WARNING, "Could not scale {0}: " + e.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
721  throw e;
722  }
723  }
724 
725  if (isCancelled()) {
726  return null;
727  }
728 
729  updateProgress(-1, 1);
730 
731  //if we got a valid thumbnail save it
732  if ((cacheFile != null) && thumbnail != null && DEFAULT_THUMBNAIL != thumbnail) {
733  saveThumbnail(thumbnail);
734  }
735 
736  return SwingFXUtils.toFXImage(thumbnail, null);
737  }
738 
744  private void saveThumbnail(BufferedImage thumbnail) {
745  imageSaver.execute(() -> {
746  try {
747  synchronized (cacheFile) {
748  Files.createParentDirs(cacheFile);
749  if (cacheFile.exists()) {
750  cacheFile.delete();
751  }
752  ImageIO.write(thumbnail, FORMAT, cacheFile);
753  }
754  } catch (Exception ex) {
755  LOGGER.log(Level.WARNING, "Could not write thumbnail for {0}: " + ex.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
756  }
757  });
758  }
759  }
760 
774  public static Task<javafx.scene.image.Image> newReadImageTask(AbstractFile file) {
775  return new ReadImageTask(file);
776 
777  }
778 
782  @NbBundle.Messages({
783  "# {0} - file name",
784  "ReadImageTask.mesageText=Reading image: {0}"})
785  static private class ReadImageTask extends ReadImageTaskBase {
786 
788  super(file);
789  updateMessage(Bundle.ReadImageTask_mesageText(file.getName()));
790  }
791 
792  @Override
793  protected javafx.scene.image.Image call() throws Exception {
794  return readImage();
795  }
796  }
797 
801  static private abstract class ReadImageTaskBase extends Task<javafx.scene.image.Image> implements IIOReadProgressListener {
802 
803  private static final String IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT = "ImageIO could not read {0}. It may be unsupported or corrupt"; //NON-NLS
804  final AbstractFile file;
805 
807  this.file = file;
808  }
809 
810  protected javafx.scene.image.Image readImage() throws IOException {
811  if (ImageUtils.isGIF(file)) {
812  //use JavaFX to directly read GIF to preserve potential animation
813  javafx.scene.image.Image image = new javafx.scene.image.Image(getBufferedReadContentStream(file));
814  if (image.isError() == false) {
815  return image;
816  }
817  } else if (file.getNameExtension().equalsIgnoreCase("tec")) { //NON-NLS
818  ReadContentInputStream readContentInputStream = new ReadContentInputStream(file);
819  // Find first Start Of Image marker
820  readContentInputStream.seek(getJfifStartOfImageOffset(file));
821  //use JavaFX to directly read .tec files
822  javafx.scene.image.Image image = new javafx.scene.image.Image(new BufferedInputStream(readContentInputStream));
823  if (image.isError() == false) {
824  return image;
825  }
826  }
827  //fall through to default image reading code if there was an error
828  if (isCancelled()) {
829  return null;
830  }
831 
832  return getImageProperty(file, "ImageIO could not read {0}: ",
833  imageReader -> {
834  imageReader.addIIOReadProgressListener(ReadImageTaskBase.this);
835  /*
836  * This is the important part, get or create a
837  * ImageReadParam, create a destination image to hold
838  * the decoded result, then pass that image with the
839  * param.
840  */
841  ImageReadParam param = imageReader.getDefaultReadParam();
842  BufferedImage bufferedImage = imageReader.getImageTypes(0).next().createBufferedImage(imageReader.getWidth(0), imageReader.getHeight(0));
843  param.setDestination(bufferedImage);
844  try {
845  bufferedImage = imageReader.read(0, param); //should always be same bufferedImage object
846  } catch (IOException iOException) {
847  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + iOException.toString(), ImageUtils.getContentPathSafe(file)); //NON-NLS
848  } finally {
849  imageReader.removeIIOReadProgressListener(ReadImageTaskBase.this);
850  }
851  if (isCancelled()) {
852  return null;
853  }
854  return SwingFXUtils.toFXImage(bufferedImage, null);
855  }
856  );
857  }
858 
859  @Override
860  public void imageProgress(ImageReader reader, float percentageDone) {
861  //update this task with the progress reported by ImageReader.read
862  updateProgress(percentageDone, 100);
863  if (isCancelled()) {
864  reader.removeIIOReadProgressListener(this);
865  reader.abort();
866  reader.dispose();
867  }
868  }
869 
870  @Override
871  protected void succeeded() {
872  super.succeeded();
873  try {
874  javafx.scene.image.Image fxImage = get();
875  if (fxImage == null) {
876  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT, ImageUtils.getContentPathSafe(file));
877  } else if (fxImage.isError()) {
878  //if there was somekind of error, log it
879  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(fxImage.getException()), ImageUtils.getContentPathSafe(file));
880  }
881  } catch (InterruptedException | ExecutionException ex) {
882  failed();
883  }
884  }
885 
886  @Override
887  protected void failed() {
888  super.failed();
889  LOGGER.log(Level.WARNING, IMAGEIO_COULD_NOT_READ_UNSUPPORTED_OR_CORRUPT + ": " + ObjectUtils.toString(getException()), ImageUtils.getContentPathSafe(file));
890  }
891 
892  @Override
893  public void imageComplete(ImageReader source) {
894  updateProgress(100, 100);
895  }
896 
897  @Override
898  public void imageStarted(ImageReader source, int imageIndex) {
899  }
900 
901  @Override
902  public void sequenceStarted(ImageReader source, int minIndex) {
903  }
904 
905  @Override
906  public void sequenceComplete(ImageReader source) {
907  }
908 
909  @Override
910  public void thumbnailStarted(ImageReader source, int imageIndex, int thumbnailIndex) {
911  }
912 
913  @Override
914  public void thumbnailProgress(ImageReader source, float percentageDone) {
915  }
916 
917  @Override
918  public void thumbnailComplete(ImageReader source) {
919  }
920 
921  @Override
922  public void readAborted(ImageReader source) {
923  }
924  }
925 
934  static String getContentPathSafe(Content content) {
935  try {
936  return content.getUniquePath();
937  } catch (TskCoreException tskCoreException) {
938  String contentName = content.getName();
939  LOGGER.log(Level.SEVERE, "Failed to get unique path for " + contentName, tskCoreException); //NON-NLS
940  return contentName;
941  }
942  }
943 
952  @Deprecated
953  public static Image getDefaultIcon() {
954  return getDefaultThumbnail();
955  }
956 
967  @Deprecated
968 
969  public static File getFile(long id) {
970  return getCachedThumbnailLocation(id);
971  }
972 
986  @Nonnull
987  @Deprecated
988  public static BufferedImage getIcon(Content content, int iconSize) {
989  return getThumbnail(content, iconSize);
990  }
991 
1006  @Nullable
1007  @Deprecated
1008  public static File getIconFile(Content content, int iconSize) {
1009  return getCachedThumbnailFile(content, iconSize);
1010 
1011  }
1012 
1013 }
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 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)
int read(byte[] buf, long offset, long len)
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)
final int read(byte[] buf, long offset, long len)
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:396
static synchronized BufferedImage cropImage(BufferedImage input, int width, int height)
static SortedSet< String > getSupportedImageMimeTypes()

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