/* * JaamSim Discrete Event Simulation * Copyright (C) 2013 Ausenco Engineering Canada Inc. * * 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 com.jaamsim.DisplayModels; import java.net.URI; import java.util.ArrayList; import javax.swing.filechooser.FileNameExtensionFilter; import com.jaamsim.Graphics.DisplayEntity; import com.jaamsim.Graphics.OverlayImage; import com.jaamsim.basicsim.Entity; import com.jaamsim.basicsim.ErrorException; import com.jaamsim.datatypes.IntegerVector; import com.jaamsim.input.BooleanInput; import com.jaamsim.input.FileInput; import com.jaamsim.input.Keyword; import com.jaamsim.math.Transform; import com.jaamsim.math.Vec3d; import com.jaamsim.render.DisplayModelBinding; import com.jaamsim.render.ImageProxy; import com.jaamsim.render.OverlayTextureProxy; import com.jaamsim.render.RenderProxy; import com.jaamsim.render.VisibilityInfo; public class ImageModel extends DisplayModel { @Keyword(description = "The file containing the image to show, valid formats are: BMP, JPG, PNG, PCX, GIF.", example = "Ship3DModel ImageFile { ..\\images\\CompanyIcon.png }") private final FileInput imageFile; @Keyword(description = "Indicates the loaded image has an alpha channel (transparency information) that should be used", example = "CompanyLogo Transparent { TRUE }") private final BooleanInput transparent; @Keyword(description = "Indicates the loaded image should use texture compression in video memory", example = "WorldMap CompressedTexture { TRUE }") private final BooleanInput compressedTexture; private static final String[] validFileExtensions; private static final String[] validFileDescriptions; static { validFileExtensions = new String[5]; validFileDescriptions = new String[5]; validFileExtensions[0] = "JPG"; validFileExtensions[1] = "PNG"; validFileExtensions[2] = "GIF"; validFileExtensions[3] = "BMP"; validFileExtensions[4] = "PCX"; validFileDescriptions[0] = "JPEG Image (*.jpg)"; validFileDescriptions[1] = "Portable Network Graphics (*.png)"; validFileDescriptions[2] = "Graphics Interchange Format (*.gif)"; validFileDescriptions[3] = "Windows Bitmap (*.bmp)"; validFileDescriptions[4] = "Personal Computer Exchange (*.pcx)"; } { imageFile = new FileInput( "ImageFile", "Key Inputs", null ); imageFile.setFileType("Image"); imageFile.setValidFileExtensions(validFileExtensions); imageFile.setValidFileDescriptions(validFileDescriptions); this.addInput( imageFile); transparent = new BooleanInput("Transparent", "Key Inputs", false); this.addInput(transparent); compressedTexture = new BooleanInput("CompressedTexture", "Key Inputs", false); this.addInput(compressedTexture); } public ImageModel() {} @Override public DisplayModelBinding getBinding(Entity ent) { if (ent instanceof OverlayImage) { return new OverlayBinding(ent, this); } return new Binding(ent, this); } @Override public boolean canDisplayEntity(Entity ent) { return ent instanceof DisplayEntity; } public URI getImageFile() { return imageFile.getValue(); } /** * Compares the specified file extension to the list of valid extensions. * * @param str - the file extension to be tested. * @return TRUE if the extension is valid. */ public static boolean isValidExtension(String str) { for (String ext : validFileExtensions) { if (str.equalsIgnoreCase(ext)) return true; } return false; } /** * Returns a file name extension filter for each of the supported file types. * * @return an array of file name extension filters. */ public static FileNameExtensionFilter[] getFileNameExtensionFilters() { return FileInput.getFileNameExtensionFilters("Image", validFileExtensions, validFileDescriptions); } private class Binding extends DisplayModelBinding { private ArrayList<RenderProxy> cachedProxies; private DisplayEntity dispEnt; private Transform transCache; private Vec3d scaleCache; private URI imageCache; private boolean compressedCache; private boolean transparentCache; private VisibilityInfo viCache; public Binding(Entity ent, DisplayModel dm) { super(ent, dm); dispEnt = (DisplayEntity)observee; } private void updateCache(double simTime) { Transform trans; Vec3d scale; long pickingID; if (dispEnt == null) { trans = Transform.ident; scale = DisplayModel.ONES; pickingID = 0; } else { trans = dispEnt.getGlobalTrans(); scale = dispEnt.getSize(); scale.mul3(getModelScale()); pickingID = dispEnt.getEntityNumber(); } URI imageName = imageFile.getValue(); Boolean transp = transparent.getValue(); Boolean compressed = compressedTexture.getValue(); VisibilityInfo vi = getVisibilityInfo(); boolean dirty = false; dirty = dirty || !compare(transCache, trans); dirty = dirty || dirty_vec3d(scaleCache, scale); dirty = dirty || !compare(imageCache, imageName); dirty = dirty || transparentCache != transp; dirty = dirty || compressedCache != compressed; dirty = dirty || !compare(viCache, vi); transCache = trans; scaleCache = scale; imageCache = imageName; transparentCache = transp; compressedCache = compressed; viCache = vi; if (cachedProxies != null && !dirty) { // Nothing changed registerCacheHit("ImageModel"); return; } registerCacheMiss("ImageModel"); // Gather some inputs cachedProxies = new ArrayList<>(); cachedProxies.add(new ImageProxy(imageName, trans, scale, transp, compressed, vi, pickingID)); } @Override public void collectProxies(double simTime, ArrayList<RenderProxy> out) { // This is slightly quirky behaviour, as a null entity will be shown because we use that for previews if (dispEnt == null || !dispEnt.getShow()) { return; } updateCache(simTime); out.addAll(cachedProxies); } } private class OverlayBinding extends DisplayModelBinding { private OverlayImage imageObservee; private OverlayTextureProxy cachedProxy = null; private URI filenameCache; private IntegerVector posCache; private IntegerVector sizeCache; private boolean alignBottomCache; private boolean alignRightCache; private boolean transpCache; private VisibilityInfo viCache; public OverlayBinding(Entity ent, DisplayModel dm) { super(ent, dm); try { imageObservee = (OverlayImage)observee; } catch (ClassCastException e) { // The observee is not a display entity imageObservee = null; } } @Override public void collectProxies(double simTime, ArrayList<RenderProxy> out) { if (imageObservee == null || !imageObservee.getShow()) { return; } URI filename = imageFile.getValue(); IntegerVector pos = imageObservee.getScreenPosition(); IntegerVector size = imageObservee.getImageSize(); boolean alignRight = imageObservee.getAlignRight(); boolean alignBottom = imageObservee.getAlignBottom(); boolean transp = transparent.getValue(); VisibilityInfo vi = getVisibilityInfo(); boolean dirty = false; dirty = dirty || !compare(filenameCache, filename); dirty = dirty || !compare(posCache, pos); dirty = dirty || !compare(sizeCache, size); dirty = dirty || alignRightCache != alignRight; dirty = dirty || alignBottomCache != alignBottom; dirty = dirty || transpCache != transp; dirty = dirty || !compare(viCache, vi); filenameCache = filename; posCache = pos; sizeCache = size; alignRightCache = alignRight; alignBottomCache = alignBottom; transpCache = transp; viCache = vi; if (cachedProxy != null && !dirty) { // Nothing changed out.add(cachedProxy); registerCacheHit("OverlayImage"); return; } registerCacheMiss("OverlayImage"); try { cachedProxy = new OverlayTextureProxy(pos.get(0), pos.get(1), size.get(0), size.get(1), filename, transp, false, alignRight, alignBottom, vi); out.add(cachedProxy); } catch (ErrorException ex) { cachedProxy = null; } } @Override protected void collectSelectionBox(double simTime, ArrayList<RenderProxy> out) { // No selection widgets for now } } }