/* * 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.commands; import org.icepdf.core.pobjects.Page; import org.icepdf.core.pobjects.graphics.ImageReference; import org.icepdf.core.pobjects.graphics.OptionalContentState; import org.icepdf.core.pobjects.graphics.PaintTimer; import org.icepdf.core.util.Defs; import java.awt.*; import java.awt.geom.AffineTransform; /** * The ImageDrawCmd class when executed will draw the image associated * with this DrawCmd. * * @since 5.0 */ public class ImageDrawCmd extends AbstractDrawCmd { // enable disable scaled paint. private static boolean isScaledPaint; // narrow image scaling max dimension size to kick of the use of the lookup // table public static int MIN_DIMENSION; static { isScaledPaint = Defs.booleanProperty("org.icepdf.core.imageDrawCmd.scale.enabled", false); MIN_DIMENSION = Defs.intProperty("org.icepdf.core.imageDrawCmd.maxDimension", 5); } private ImageReference image; // paint scale factor of original image. private int xScale = 1; private int yScale = 1; private boolean xIsScale = false; private boolean yIsScale = false; // narrow image scaling lookup table for 1xh or wx1 images. Soft values // but keeps the images from not painting a low zoom levels. // first column is the zoom level and the second is the total number of // pixels that must be present for the image to be painted. private static final double[][] SCALE_LOOKUP = { {1.50, 2}, {0.70, 3}, {0.40, 4}, {0.30, 6}, {0.20, 8}, {0.10, 10}, {0.05, 12} }; public ImageDrawCmd(ImageReference image) { this.image = image; // check image dimensions to see if we should do some work for // Xxh or wxX images sizes, as they tend not to be painted by Java2d // at zoom levels < 144%. if (isScaledPaint) { if (image.getHeight() <= MIN_DIMENSION) { yIsScale = true; } if (image.getWidth() <= MIN_DIMENSION) { xIsScale = true; } } } public Image getImage() throws InterruptedException { return image.getImage(); } @Override public Shape paintOperand(Graphics2D g, Page parentPage, Shape currentShape, Shape clip, AffineTransform base, OptionalContentState optionalContentState, boolean paintAlpha, PaintTimer paintTimer) throws InterruptedException { if (optionalContentState.isVisible()) { if (isScaledPaint && (xIsScale || yIsScale)) { calculateThinScale(base.getScaleX()); } image.drawImage(g, 0, 0, xScale, yScale); if (parentPage != null && paintTimer.shouldTriggerRepaint()) { parentPage.notifyPaintPageListeners(); } } return currentShape; } /** * Alter the width or height value of 1px or less then MIN_DIMENSION. * * @param scale scale factor of current view. */ private void calculateThinScale(double scale) { if (xIsScale) { xScale = commonScaling(scale, image.getWidth()); } // horizon scale needs to be applied for an Wx1px image. if (yIsScale) { yScale = commonScaling(scale, image.getHeight()); } } /** * Fetches a scale value from lookup table and returns the appropriate * scale so the image will be visible. * * @param scale page level scale being applied to page. * @param size original size, width or height of the image to sale. * @return scale value applied to g.drawImage(). */ private int commonScaling(double scale, int size) { // find the appropriate range and final minimal for (int i = SCALE_LOOKUP.length - 1; i >= 0; i--) { if (scale < SCALE_LOOKUP[i][0]) { double neededSize = SCALE_LOOKUP[i][1]; double scaleFactor = neededSize / size; return (int) Math.ceil(scaleFactor); } } return 1; } }