/* * Copyright 2017 Laszlo Balazs-Csiki * * This file is part of Pixelitor. Pixelitor is free software: you * can redistribute it and/or modify it under the terms of the GNU * General Public License, version 3 as published by the Free * Software Foundation. * * Pixelitor is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Pixelitor. If not, see <http://www.gnu.org/licenses/>. */ package pixelitor; import pixelitor.gui.ImageComponents; import pixelitor.history.FadeableEdit; import pixelitor.history.History; import pixelitor.layers.DeleteActiveLayerAction; import pixelitor.layers.Drawable; import pixelitor.selection.SelectionActions; import pixelitor.utils.Utils; import javax.swing.*; import java.awt.image.BufferedImage; import java.util.Optional; /** * Consistency checks that run only in developer mode. * They are enabled by the Build setting or by the assertions */ public final class ConsistencyChecks { private ConsistencyChecks() { // do not instantiate } public static void checkAll(Composition comp, boolean checkImageCoversCanvas) { assert comp != null; selectionCheck(comp); assert fadeCheck(comp); if (checkImageCoversCanvas) { assert imageCoversCanvasCheck(comp); } assert layerDeleteActionEnabledCheck(); } public static boolean fadeCheck(Composition comp) { Drawable dr = comp.getActiveDrawableOrNull(); if (dr == null) { // nothing to check return true; } return fadeCheck(dr); } /** * Checks whether Fade would work now */ @SuppressWarnings("SameReturnValue") public static boolean fadeCheck(Drawable dr) { assert dr != null; if (!History.canFade(dr)) { return true; } Optional<FadeableEdit> edit = History.getPreviousEditForFade(dr); if (edit.isPresent()) { BufferedImage current = dr.getImageOrSubImageIfSelected(false, true); FadeableEdit fadeableEdit = edit.get(); BufferedImage previous = fadeableEdit.getBackupImage(); if (previous == null) { // soft reference expired return true; } boolean differentWidth = current.getWidth() != previous.getWidth(); boolean differentHeight = current.getHeight() != previous.getHeight(); if (differentWidth || differentHeight) { Utils.debugImage(current, "current"); Utils.debugImage(previous, "previous"); String lastFadeableOp = History.getLastEditName(); Composition comp = dr.getComp(); String historyCompName = fadeableEdit.getComp().getName(); String activeCompName = ImageComponents.getActiveCompOrNull().getName(); throw new IllegalStateException("'Fade " + lastFadeableOp + "' would not work now:" + "\nFadeableEdit class = " + fadeableEdit.getClass().getName() + ", and name = " + fadeableEdit.getName() + "\n current selected dimensions: " + current.getWidth() + "x" + current.getHeight() + ", " + "history dimensions: " + previous.getWidth() + "x" + previous.getHeight() + "\nchecked composition = " + comp.getName() + "(hasSelection = " + comp.hasSelection() + (comp.hasSelection() ? ", selection bounds = " + comp.getSelection().getShapeBounds() : "") + ")" + "\nchecked composition canvas = " + comp.getCanvas().getBounds() + "\nhistory composition = " + historyCompName + "\nactive composition = " + activeCompName + "\n" ); } } return true; } private static void selectionCheck(Composition comp) { if (!SwingUtilities.isEventDispatchThread()) { return; } if (comp.hasSelection()) { if (!SelectionActions.areEnabled()) { throw new IllegalStateException(comp.getName() + " has selection, but selection actions are disabled, thread is " + Thread.currentThread().getName()); } } } public static boolean imageCoversCanvasCheck(Composition comp) { comp.forEachDrawable(ConsistencyChecks::imageCoversCanvasCheck); return true; } public static boolean imageCoversCanvasCheck(Drawable dr) { Composition comp = dr.getComp(); BufferedImage image = dr.getImage(); Canvas canvas = comp.getCanvas(); if (canvas == null) { // can happen during the loading of pxc files return true; } int canvasWidth = canvas.getWidth(); int canvasHeight = canvas.getHeight(); int txAbs = -dr.getTX(); int imageWidth = image.getWidth(); if (txAbs + canvasWidth > imageWidth) { return throwImageDoesNotCoverCanvasException(dr); } int tyAbs = -dr.getTY(); int imageHeight = image.getHeight(); if (tyAbs + canvasHeight > imageHeight) { return throwImageDoesNotCoverCanvasException(dr); } return true; } private static boolean throwImageDoesNotCoverCanvasException(Drawable dr) { Composition comp = dr.getComp(); BufferedImage bufferedImage = dr.getImage(); int canvasWidth = comp.getCanvasWidth(); int canvasHeight = comp.getCanvasHeight(); int imageWidth = bufferedImage.getWidth(); int imageHeight = bufferedImage.getHeight(); int tx = dr.getTX(); int ty = dr.getTY(); String className = dr.getClass().getSimpleName(); String msg = String.format("canvasWidth = %d, canvasHeight = %d, " + "imageWidth = %d, imageHeight = %d, tx = %d, ty = %d, class = %s", canvasWidth, canvasHeight, imageWidth, imageHeight, tx, ty, className); throw new IllegalStateException(msg); } @SuppressWarnings("SameReturnValue") public static boolean layerDeleteActionEnabledCheck() { DeleteActiveLayerAction action = DeleteActiveLayerAction.INSTANCE; if (action == null) { return true; } Composition comp = ImageComponents.getActiveCompOrNull(); if (comp == null) { return true; } boolean enabled = action.isEnabled(); int numLayers = comp.getNumLayers(); if (enabled) { if (numLayers <= 1) { throw new IllegalStateException("delete layer enabled for " + comp.getName() + ", but numLayers = " + numLayers); } } else { // disabled if (numLayers >= 2) { throw new IllegalStateException("delete layer disabled for " + comp.getName() + ", but numLayers = " + numLayers); } } return true; } }