/** * Copyright (C) 2009-2014 Cars and Tracks Development Project (CTDP). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.ctdp.rfdynhud.widgets.base.widget; import java.awt.Color; import net.ctdp.rfdynhud.editor.__EDPrivilegedAccess; import net.ctdp.rfdynhud.gamedata.LiveGameData; import net.ctdp.rfdynhud.properties.BackgroundProperty; import net.ctdp.rfdynhud.properties.BackgroundProperty.BackgroundType; import net.ctdp.rfdynhud.render.ImageTemplate; import net.ctdp.rfdynhud.render.TextureImage2D; import net.ctdp.rfdynhud.util.TextureManager; import org.jagatoo.logging.Log; import org.jagatoo.util.Tools; /** * This class encapsulates a {@link Widget}'s effective background. * This can be a simple color or a scaled image. * * @author Marvin Froehlich (CTDP) */ public class WidgetBackground { private static final boolean isEditorMode = ( __EDPrivilegedAccess.editorClassLoader != null ); private final Widget widget; private final BackgroundProperty property; private int changeCount = 0; private float backgroundScaleX = 1.0f; private float backgroundScaleY = 1.0f; private TextureImage2D backgroundTexture = null; private boolean bgTexDirty = true; private TextureImage2D mergedBackgroundTexture = null; private boolean mergedBgTexDirty = true; /** * Gets the current type of this background. * * @return the current type of this background. */ public final BackgroundType getType() { return ( property.getBackgroundType() ); } /** * Gets the {@link Color} from this {@link WidgetBackground}. * The result is only valid, if the {@link BackgroundType} ({@link #getType()}) is {@link BackgroundType#COLOR}. * * @return the {@link Color} from this {@link WidgetBackground}. */ public final Color getColor() { if ( !property.getBackgroundType().isColor() ) return ( null ); return ( property.getColorValue() ); } private void loadBackgroundImage( ImageTemplate image, int width, int height ) { /* if ( backgroundImageName.isNoImage() ) { backgroundTexture = null; backgroundScaleX = 1.0f; backgroundScaleY = 1.0f; } else */ { boolean looksLikeEditorMode = ( changeCount > 1 ); boolean reloadBackground = bgTexDirty; if ( !bgTexDirty && looksLikeEditorMode && ( ( backgroundTexture == null ) || ( backgroundTexture.getWidth() != width ) || ( backgroundTexture.getHeight() != height ) ) ) reloadBackground = true; if ( reloadBackground ) { try { backgroundTexture = image.getScaledTextureImage( width, height, backgroundTexture, isEditorMode ); bgTexDirty = false; } catch ( Throwable t ) { Log.exception( Widget.LOG_CHANNEL, t ); } backgroundScaleX = (float)width / (float)image.getBaseWidth(); backgroundScaleY = (float)height / (float)image.getBaseHeight(); } } } public final int getWidth() { return ( widget.getEffectiveWidth() - widget.getBorder().getInnerLeftWidthWOPadding() - widget.getBorder().getInnerRightWidthWOPadding() ); } public final int getHeight() { return ( widget.getEffectiveHeight() - widget.getBorder().getInnerTopHeightWOPadding() - widget.getBorder().getInnerBottomHeightWOPadding() ); } void onPropertyValueChanged( Widget widget, BackgroundType oldBGType, BackgroundType newBGType, String oldValue, String newValue ) { if ( oldBGType == null ) return; changeCount++; int width = getWidth(); int height = getHeight(); float deltaScaleX = -1.0f; float deltaScaleY = -1.0f; boolean imageChanged = false; if ( newBGType == BackgroundType.COLOR ) { if ( oldBGType == BackgroundType.IMAGE ) { ImageTemplate it = TextureManager.getImage( oldValue ); deltaScaleX = (float)width / (float)it.getBaseWidth(); deltaScaleY = (float)height / (float)it.getBaseHeight(); imageChanged = true; } backgroundTexture = null; bgTexDirty = true; mergedBgTexDirty = true; } else if ( newBGType == BackgroundType.IMAGE ) { if ( oldBGType == BackgroundType.COLOR ) { ImageTemplate it = TextureManager.getImage( newValue ); deltaScaleX = (float)it.getBaseWidth() / (float)width; deltaScaleY = (float)it.getBaseHeight() / (float)height; imageChanged = true; } else if ( oldBGType == BackgroundType.IMAGE ) { ImageTemplate it = TextureManager.getImage( oldValue ); float oldBgScaleX = (float)width / (float)it.getBaseWidth(); float oldBgScaleY = (float)height / (float)it.getBaseHeight(); it = TextureManager.getImage( newValue ); float newScaleX = (float)width / (float)it.getBaseWidth(); float newScaleY = (float)height / (float)it.getBaseHeight(); deltaScaleX = oldBgScaleX / newScaleX; deltaScaleY = oldBgScaleY / newScaleY; imageChanged = !Tools.objectsEqual( oldValue, newValue ); } backgroundTexture = null; bgTexDirty = true; mergedBgTexDirty = true; } widget.onBackgroundChanged( imageChanged, deltaScaleX, deltaScaleY ); widget.forceAndSetDirty( true ); } void onWidgetSizeChanged() { bgTexDirty = true; mergedBgTexDirty = true; if ( widget instanceof AbstractAssembledWidget ) { for ( int i = 0; i < ( (AbstractAssembledWidget)widget ).getNumParts(); i++ ) { if ( ( (AbstractAssembledWidget)widget ).getPart( i ).getBackground() != null ) { ( (AbstractAssembledWidget)widget ).getPart( i ).getBackground().onWidgetSizeChanged(); } } } } /** * Gets the {@link TextureImage2D} from this {@link BackgroundProperty}. * The result is only valid, if the {@link BackgroundType} ({@link #getType()}) is {@link BackgroundType#IMAGE}. * * @return the {@link TextureImage2D} from this {@link BackgroundProperty}. */ public final TextureImage2D getTexture() { if ( !property.getBackgroundType().isImage() ) return ( null ); if ( bgTexDirty ) { loadBackgroundImage( property.getImageValue(), getWidth(), getHeight() ); } return ( backgroundTexture ); } void setMergedBGDirty() { this.mergedBgTexDirty = true; } private static boolean needsTexture( AbstractAssembledWidget widget, Color backgroundColor ) { for ( int i = 0; i < widget.getNumParts(); i++ ) { Widget part = widget.getPart( i ); WidgetBackground bg = part.getBackground(); if ( ( ( bg != null ) && bg.getType().isImage() ) || part.overridesDrawBackground ) return ( true ); if ( ( bg != null ) && bg.getType().isColor() && ( bg.getColor().getAlpha() > 0 ) && !bg.getColor().equals( backgroundColor ) ) return ( true ); if ( part instanceof AbstractAssembledWidget ) { if ( needsTexture( (AbstractAssembledWidget)part, backgroundColor ) ) return ( true ); } } return ( false ); } private void createAndUpdateMergedBackgroundTexture( LiveGameData gameData, boolean isEditorMode ) { int width = getWidth(); int height = getHeight(); mergedBackgroundTexture = TextureImage2D.getOrCreateDrawTexture( width, height, true, mergedBackgroundTexture, isEditorMode ); widget._drawBackground( gameData, isEditorMode, mergedBackgroundTexture, 0, 0, width, height, true ); mergedBgTexDirty = false; } /** * * @param gameData * @param isEditorMode */ void updateMergedBackground( LiveGameData gameData, boolean isEditorMode ) { if ( mergedBgTexDirty ) { if ( property.getBackgroundType().isColor() ) { if ( widget.overridesDrawBackground ) { createAndUpdateMergedBackgroundTexture( gameData, isEditorMode ); } else if ( ( widget instanceof AbstractAssembledWidget ) && needsTexture( (AbstractAssembledWidget)widget, getColor() ) ) { createAndUpdateMergedBackgroundTexture( gameData, isEditorMode ); } else { mergedBackgroundTexture = null; mergedBgTexDirty = false; } } else //if ( property.getBackgroundType().isImage() ) { if ( widget.overridesDrawBackground ) { createAndUpdateMergedBackgroundTexture( gameData, isEditorMode ); } else if ( ( widget instanceof AbstractAssembledWidget ) && needsTexture( (AbstractAssembledWidget)widget, null ) ) { createAndUpdateMergedBackgroundTexture( gameData, isEditorMode ); } else { mergedBackgroundTexture = null; mergedBgTexDirty = false; } } } } /** * Gets the merged background, composed of the backgrounds of this (possibly assembled) {@link Widget} and the parts of this Widget. * If this {@link Widget} is not an {@link AbstractAssembledWidget} and has a background color (no image) * and doesn't override the {@link Widget#drawBackground(net.ctdp.rfdynhud.gamedata.LiveGameData, boolean, TextureImage2D, int, int, int, int, boolean)} method, * this method returns <code>null</code>. * * @return the marged background texture or <code>null</code>. */ public final TextureImage2D getMergedTexture() { return ( mergedBackgroundTexture ); } /** * Gets the factor, by which the background image has been scaled to fit the area. * Returns 1.0, if a background color is used. * * @return the factor, by which the background image has been scaled to fit the area. */ public final float getScaleX() { if ( property.getBackgroundType().isColor() ) return ( 1.0f ); if ( bgTexDirty ) { loadBackgroundImage( property.getImageValue(), getWidth(), getHeight() ); } return ( backgroundScaleX ); } /** * Gets the factor, by which the background image has been scaled to fit the area. * Returns 1.0, if a background color is used. * * @return the factor, by which the background image has been scaled to fit the area. */ public final float getScaleY() { if ( property.getBackgroundType().isColor() ) return ( 1.0f ); if ( bgTexDirty ) { loadBackgroundImage( property.getImageValue(), getWidth(), getHeight() ); } return ( backgroundScaleY ); } public final boolean valueEquals( Color color ) { if ( !property.getBackgroundType().isColor() ) return ( false ); return ( getColor().equals( color ) ); } public WidgetBackground( final Widget widget, BackgroundProperty property ) { this.widget = widget; this.property = property; } }