/* * Copyright 2006-2017 ICEsoft Technologies Canada Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS * IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package org.icepdf.core.pobjects.graphics; import org.icepdf.core.events.PageImageEvent; import org.icepdf.core.events.PageLoadingEvent; import org.icepdf.core.events.PageLoadingListener; import org.icepdf.core.pobjects.ImageStream; import org.icepdf.core.pobjects.Page; import org.icepdf.core.pobjects.Reference; import org.icepdf.core.pobjects.Resources; import org.icepdf.core.util.Defs; import java.awt.*; import java.awt.image.BufferedImage; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.logging.Level; import java.util.logging.Logger; /** * Abstract ImageReference defines the core methods used in ImageStreamReference * MipMappedImageReference and ScaledImageReference. The creation of these * objects is handled by the ImageReferenceFactory. * * @since 5.0 */ public abstract class ImageReference implements Callable<BufferedImage> { private static final Logger logger = Logger.getLogger(ImageReference.class.toString()); protected static boolean useProxy; static { // decide if large images will be scaled useProxy = Defs.booleanProperty("org.icepdf.core.imageProxy", true); } protected FutureTask<BufferedImage> futureTask; protected ImageStream imageStream; protected GraphicsState graphicsState; protected Resources resources; protected BufferedImage image; protected Reference reference; protected int imageIndex; protected Page parentPage; protected ImageReference(ImageStream imageStream, GraphicsState graphicsState, Resources resources, int imageIndex, Page parentPage) { this.imageStream = imageStream; this.graphicsState = graphicsState; this.resources = resources; this.imageIndex = imageIndex; this.parentPage = parentPage; } public abstract int getWidth(); public abstract int getHeight(); public abstract BufferedImage getImage() throws InterruptedException; public void drawImage(Graphics2D aG, int aX, int aY, int aW, int aH) throws InterruptedException { BufferedImage image = getImage(); if (image != null) { try { aG.drawImage(image, aX, aY, aW, aH, null); } catch (Throwable e) { logger.warning("There was a problem painting image, falling back to scaled instance " + imageStream.getPObjectReference() + "(" + imageStream.getWidth() + "x" + imageStream.getHeight() + ")"); int width = image.getWidth(null); Image scaledImage; // do image scaling on larger images. This improves the softness // of some images that contains black and white text. if (width > 1000 && width < 2000) { width = 1000; } else if (width > 2000) { width = 2000; } scaledImage = image.getScaledInstance(width, -1, Image.SCALE_SMOOTH); image.flush(); // try drawing the scaled image one more time. aG.drawImage(scaledImage, aX, aY, aW, aH, null); // store the scaled image for future repaints. this.image = imageStream.getImageUtility().createBufferedImage(scaledImage); } } } /** * Creates a scaled image to match that of the instance vars width/height. * * @return decoded/encoded BufferedImage for the respective ImageStream. */ protected BufferedImage createImage() throws InterruptedException { try { // block until thread comes back. if (futureTask != null) { image = futureTask.get(); } if (image == null) { image = call(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); logger.fine("Image loading interrupted"); throw new InterruptedException(e.getMessage()); } catch (Exception e) { logger.log(Level.FINE, "Image loading execution exception", e); } return image; } public ImageStream getImageStream() { return imageStream; } public boolean isImage() { return image != null; } protected void notifyPageImageLoadedEvent(long duration, boolean interrupted) { if (parentPage != null) { PageImageEvent pageLoadingEvent = new PageImageEvent(parentPage, imageIndex, parentPage.getImageCount(), duration, interrupted); PageLoadingListener client; List<PageLoadingListener> pageLoadingListeners = parentPage.getPageLoadingListeners(); for (int i = pageLoadingListeners.size() - 1; i >= 0; i--) { client = pageLoadingListeners.get(i); client.pageImageLoaded(pageLoadingEvent); } } } protected void notifyImagePageEvents(long duration) { // sound out image loading event. notifyPageImageLoadedEvent(duration, image == null); // check to see if we're done loading and all we were waiting on was // the completion of this image load. if (parentPage != null && imageIndex == parentPage.getImageCount() && parentPage.isPageInitialized() && parentPage.isPagePainted()) { notifyPageLoadingEnded(); } } protected void notifyPageLoadingEnded() { if (parentPage != null) { PageLoadingEvent pageLoadingEvent = new PageLoadingEvent(parentPage, parentPage.isInitiated()); PageLoadingListener client; List<PageLoadingListener> pageLoadingListeners = parentPage.getPageLoadingListeners(); for (int i = pageLoadingListeners.size() - 1; i >= 0; i--) { client = pageLoadingListeners.get(i); client.pageLoadingEnded(pageLoadingEvent); } } } }