Class DefaultPortrait
- java.lang.Object
-
- ca.cgjennings.apps.arkham.component.AbstractPortrait
-
- ca.cgjennings.apps.arkham.component.DefaultPortrait
-
- All Implemented Interfaces:
Portrait,java.io.Serializable
public class DefaultPortrait extends AbstractPortrait implements java.io.Serializable
A default implementation of thePortraitinterface that creates a key-based portrait similar to that provided by theDIYsystem. This implementation can be used with components that have up to 32 faces.Note: Compiled (non-DIY) game components that use this class must take care to implement cloning correctly.
DefaultPortraitcannot implementCloneablebecause of the complexities of cloning linked portraits. Instead, the game component must implement aclonemethod that correctly creates deep copies of its portraits. Otherwise, the portrait will end up shared between the original and the clone. Adjustments in the original will affect the clone when it is redrawn. A simple solution is to implement the game component'sclonemethod to returnca.cgjennings.util.SerialClone.clone(this).- Since:
- 3.0
- Author:
- Chris Jennings
- See Also:
- Serialized Form
-
-
Nested Class Summary
-
Nested classes/interfaces inherited from interface ca.cgjennings.apps.arkham.component.Portrait
Portrait.Feature
-
-
Field Summary
Fields Modifier and Type Field Description static intMAXIMUM_SHEET_INDEXThe maximum index of a component's sheets that a default portrait can be drawn on.-
Fields inherited from interface ca.cgjennings.apps.arkham.component.Portrait
ROTATABLE_PORTRAIT_FEATURES, STANDARD_PORTRAIT_FEATURES
-
-
Constructor Summary
Constructors Constructor Description DefaultPortrait(DefaultPortrait parent, java.lang.String key)Creates a default portrait instance that is a linked to another portrait, called its parent.DefaultPortrait(GameComponent gc, java.lang.String key)Creates a default portrait instance that does not support rotation.DefaultPortrait(GameComponent gc, java.lang.String key, boolean allowRotation)Creates a default portrait instance for a game component.DefaultPortrait(GameComponent gc, java.lang.String key, java.util.EnumSet<Portrait.Feature> portraitFeatures)Creates a default portrait instance for a game component.DefaultPortrait(java.lang.String newKey, DefaultPortrait original)Creates a default portrait that replaces an existing portrait by switching to a new base key.
-
Method Summary
All Methods Instance Methods Concrete Methods Deprecated Methods Modifier and Type Method Description java.awt.geom.Point2DcomputeDefaultImagePan(java.awt.image.BufferedImage image)Returns a default pan value for an image.doublecomputeDefaultImageRotation(java.awt.image.BufferedImage image)Returns a default rotation value for an image.doublecomputeDefaultImageScale(java.awt.image.BufferedImage image)Returns a default scale value for an image.voidcopyStateTo(DefaultPortrait target)Copies the basic state of this portrait to another portrait: the scale, pan, and rotation values, and the source image.protected voidfireChildrenModified()Notifies any children that their attached sheets must be redrawn.protected voidfirePortraitModified()Notifies the sheets indicated bygetFacesToUpdate()that they must be redrawn because the portrait settings have been modified.java.lang.StringgetBaseKey()Returns the base setting key that this portrait uses.java.util.List<DefaultPortrait>getChildren()Returns any portraits that are linked to this portrait as a list.java.awt.DimensiongetClipDimensions()Returns the size of the bounding rectangle of the area that the portrait is drawn in on the component sheet, in the coordinate system of the sheet's template.booleangetClipping()Returnstrueif the portrait will be clipped to the clip region.java.awt.image.BufferedImagegetClipStencil()Optionally returns an image that describes how the portrait will "show through" surrounding card features.int[]getFacesToUpdate()Returns an array of the indices of the faces on which this portrait will appear.java.util.EnumSet<Portrait.Feature>getFeatures()Returns the set of portrait features supported by the portrait.GameComponentgetGameComponent()Returns the game component that this portrait is associated with.java.awt.image.BufferedImagegetImage()Returns the image being used by this portrait.java.awt.geom.Point2DgetPan(java.awt.geom.Point2D dest)Returns the pan value as aPoint2D.doublegetPanX()Returns the horizontal pan value.doublegetPanY()Returns the vertical pan value.DefaultPortraitgetParent()Returns the portrait that this portrait is linked to, ornull.doublegetRotation()Returns the rotation angle for the portrait image.doublegetScale()Returns the scale of the portrait.booleangetScaleUsesMinimum()Returns whether the default portrait scale will cover the entire clip region or fit the portrait within the clip region.java.lang.StringgetSource()Returns the identifier for the image being used by this portrait.protected java.awt.image.BufferedImagegetSyntheticEdgeImage()Returns an image that can be used to paint the portrait with any synthetic edges applied.doublegetSyntheticEdgeLimit()Returns the current synthetic edge limit ratio.voidinstallDefault()Installs the default portrait, if it is not already set.booleanisBackgroundFilled()Returnstrueif portrait areas will be filled with solid white before painting the portrait.voidpaint(java.awt.Graphics2D g, RenderTarget target)Paints the portrait image on a graphics context provided by aSheet.voidsetBackgroundFilled(boolean fill)Sets whether the portrait clip region will be filled with solid white before painting the portrait.voidsetClipping(boolean clipping)Sets whether the portrait is clipped to the clip region.voidsetClipStencil(java.awt.image.BufferedImage clipStencil)Sets an explicit clip stencil for this portrait.voidsetFacesToUpdate(int bitmap)Deprecated.It is preferable, and easier, to usesetFacesToUpdate(int[]).voidsetFacesToUpdate(int[] faces)Sets the sheets that need to be redrawn when the portrait settings change.protected voidsetFeatures(java.util.EnumSet<Portrait.Feature> features)Sets the feature set supported by the portrait.voidsetImage(java.lang.String resource, java.awt.image.BufferedImage image)Explicitly sets the image used by this portrait.voidsetPan(java.awt.geom.Point2D pan)Sets the pan value from a reference point.voidsetPanX(double x)Sets the horizontal pan value.voidsetPanY(double y)Sets the vertical pan value.voidsetRotation(double angleInDegrees)Sets the rotation angle for the portrait image.voidsetScale(double scale)Sets the scale factor applied to the default size chosen by the component.voidsetScaleUsesMinimum(boolean useMinimum)Sets whether this portrait should use the minimum fit scale when computing a default scale value for an image.voidsetSource(java.lang.String resource)Sets the image used by the portrait.The value ofresourceis an identifier such as a local path to an image file or a URL.voidsetSyntheticEdgeLimit(double limitRatio)Sets the synthetic edge limit ratio for the portrait.-
Methods inherited from class ca.cgjennings.apps.arkham.component.AbstractPortrait
createStencil, createStencil, getImageFromIdentifier
-
-
-
-
Field Detail
-
MAXIMUM_SHEET_INDEX
public static final int MAXIMUM_SHEET_INDEX
The maximum index of a component's sheets that a default portrait can be drawn on.- See Also:
- Constant Field Values
-
-
Constructor Detail
-
DefaultPortrait
public DefaultPortrait(GameComponent gc, java.lang.String key)
Creates a default portrait instance that does not support rotation. This is a cover forDefaultPortrait( gc, key, false ).- Parameters:
gc- the game component that the portrait is used withkey- the key used to determine the basic properties of the portrait; the various-portrait-*suffixes are appended automatically
-
DefaultPortrait
public DefaultPortrait(GameComponent gc, java.lang.String key, boolean allowRotation)
Creates a default portrait instance for a game component.- Parameters:
gc- the game component that the portrait is used withkey- the key used to determine the basic properties of the portrait; the various-portrait-*suffixes are appended automaticallyallowRotation- iftrue, rotating the portrait is allowed
-
DefaultPortrait
public DefaultPortrait(GameComponent gc, java.lang.String key, java.util.EnumSet<Portrait.Feature> portraitFeatures)
Creates a default portrait instance for a game component.- Parameters:
gc- the game component that the portrait is used withkey- the key used to determine the basic properties of the portrait; the various-portrait-*suffixes are appended automaticallyportraitFeatures- set of portrait features that the portrait will report supporting- See Also:
setFeatures(java.util.EnumSet)
-
DefaultPortrait
public DefaultPortrait(DefaultPortrait parent, java.lang.String key)
Creates a default portrait instance that is a linked to another portrait, called its parent. The portrait will start with the same features as its parent except for theSOURCEfeature.Note: Be careful not to confuse this with
DefaultPortrait(java.lang.String, ca.cgjennings.apps.arkham.component.DefaultPortrait)which takes arguments of the same type, but in the opposite order.- Parameters:
parent- the portrait that this portrait's source will come fromkey- the key used to determine the basic properties of the portrait
-
DefaultPortrait
public DefaultPortrait(java.lang.String newKey, DefaultPortrait original)Creates a default portrait that replaces an existing portrait by switching to a new base key. This can be used to convert old component files when a component's base key name changes. It is normally used in a component'sonReadfunction (for DIY components) orreadObjectmethod (for compiled code).Note: Be careful not to confuse this with
DefaultPortrait(ca.cgjennings.apps.arkham.component.DefaultPortrait, java.lang.String)which takes arguments of the same type, but in the opposite order.- Parameters:
newKey- the new base key that will replace the old keyoriginal- the original portrait (typically, it has just be read from a save file)
-
-
Method Detail
-
getParent
public final DefaultPortrait getParent()
Returns the portrait that this portrait is linked to, ornull.- Returns:
- the parent portrait, or
nullif this portrait is not linked to another portrait - See Also:
DefaultPortrait(ca.cgjennings.apps.arkham.component.DefaultPortrait, java.lang.String),getChildren()
-
getChildren
public final java.util.List<DefaultPortrait> getChildren()
Returns any portraits that are linked to this portrait as a list.- Returns:
- a list of portraits linked to this portrait (possibly empty)
- See Also:
DefaultPortrait(ca.cgjennings.apps.arkham.component.DefaultPortrait, java.lang.String),getParent()
-
getGameComponent
public final GameComponent getGameComponent()
Returns the game component that this portrait is associated with.- Returns:
- the component that was provided when this portrait instance was created
-
getBaseKey
public final java.lang.String getBaseKey()
Returns the base setting key that this portrait uses.- Returns:
- the base key name that was provided when this portrait instance was created
-
setFacesToUpdate
@Deprecated public void setFacesToUpdate(int bitmap)
Deprecated.It is preferable, and easier, to usesetFacesToUpdate(int[]).Sets the sheets to be updated when the portrait is modified. This is a convenience method that sets the faces from a bit mask instead of an array. Setting bit n of the bitmap indicates that face n of the component should be updated when the portrait changes. (That is, that the portrait appears on that face.)Example
bitmapvalues:
0 : do not redraw any faces automatically
1 : front face only (this is the value of1 << 0)
2 : back face only
3 : front and back faces (this is the value of(1 << 0)|(1 << 1))
4 : update only the third face (this is the value of1 << 2)
-1 : update all faces- Parameters:
bitmap- a bitmap indicating the indices of the faces to update automatically
-
setFacesToUpdate
public final void setFacesToUpdate(int[] faces)
Sets the sheets that need to be redrawn when the portrait settings change. When the portrait is adjusted, the faces that use the portrait must be redrawn to reflect the adjustment. By default, every face of the component will be updated. Unless every face actually displays the portrait in question, this slows down the application needlessly. This method allows you specify which faces need to be redrawn. To use it, pass an array of integers in which each integer is the index of one of the component's faces (0 for the first face, 1 for the second, and so on).- Parameters:
faces- an array, each element of which is the index of a face that shows this portrait- Throws:
java.lang.NullPointerException- iffacesisnulljava.lang.IllegalArgumentException- if any value in thefacesarray is outside the supported range of 32 faces
-
getFacesToUpdate
public int[] getFacesToUpdate()
Returns an array of the indices of the faces on which this portrait will appear. This allows the portrait instance to avoid redrawing faces that the portrait does not appear on.- Returns:
- a bitmap indicating the indices of the faces to update automatically
- See Also:
setFacesToUpdate(int[])
-
setFeatures
protected final void setFeatures(java.util.EnumSet<Portrait.Feature> features)
Sets the feature set supported by the portrait. This method allows subclasses to modify portrait features. It should be called only from a constructor.- Parameters:
features- the set of features to support
-
getFeatures
public final java.util.EnumSet<Portrait.Feature> getFeatures()
Returns the set of portrait features supported by the portrait.- Specified by:
getFeaturesin interfacePortrait- Overrides:
getFeaturesin classAbstractPortrait- Returns:
- the portrait's capabilities
-
installDefault
public void installDefault()
Installs the default portrait, if it is not already set. The default portrait is determined by looking up the base key with the suffix-portrait-templateappended. The value of this key must be a path relative to the resources folder that identifies an image file. If the key is not defined, then the valueportraits/misc-portrait.jp2will be used, but this should only be used as a placeholder during development. By default, the pan position will be set to (0,0) and the scale and rotation will be set as if for any other portrait installed by callingsetSource(java.lang.String). The default panning and scale values (for the default image only) can be overridden by keys with the suffix-portrait-panx,-portrait-pany, and-portrait-scale. The default rotation (if enabled, and for both the default image and images set bysetSource(java.lang.String)), can be set via the key suffix-portrait-rotation. (SeecomputeDefaultImageRotation(java.awt.image.BufferedImage).)- Specified by:
installDefaultin interfacePortrait
-
setSource
public void setSource(java.lang.String resource)
Description copied from interface:PortraitSets the image used by the portrait.The value ofresourceis an identifier such as a local path to an image file or a URL. Eithernullor an empty string is a special identifier that requests a default image that depends on the component and portrait number.When a new portrait is set, the scale, pan, and rotation values will be changed to suit the new image. If you wish to keep these fixed, you must store these values in temporary variables before setting them image, and then restore them once this method returns.
If an image cannot be obtained from
resource(for example, if the file it names does not exist or is invalid), then a special error image is substituted.
-
setImage
public void setImage(java.lang.String resource, java.awt.image.BufferedImage image)Description copied from interface:PortraitExplicitly sets the image used by this portrait. The source for the portrait will be set to reported source, but no attempt will be made to load the portrait from this source. Instead, the given image will be used as if it was the image loaded from the reported source. This is sometimes useful when you want to copy an image to a new component but you do not know if the source location is actually available on this system.
-
getSource
public java.lang.String getSource()
Description copied from interface:PortraitReturns the identifier for the image being used by this portrait. See the description ofPortrait.setSource(java.lang.String)for details.
-
getImage
public java.awt.image.BufferedImage getImage()
Description copied from interface:PortraitReturns the image being used by this portrait. The original image is returned, rather than a copy, in order to conserve resources. However, it is important that this image not be modified in any way as it may be shared between several objects.
-
setSyntheticEdgeLimit
public void setSyntheticEdgeLimit(double limitRatio)
Sets the synthetic edge limit ratio for the portrait. When set to a non-zero value, the portrait image will be extended in every direction using mirrored copies of the original image. The amount that each edge is extended depends on the limit ratio. A value of 1 extends the image by one full image copy on each side, while values between 0 and 1 extend the image proportionally. For example, a value of 0.1 would extend the image by one tenth of an image on each side. The default edge limit is 0, meaning that no edges will be synthesized.The effect of a non-zero limit is similar to creating a
synthetic bleed marginon a sheet.This feature may be particularly useful for designs that:
- include a designed (not synthetic) bleed margin;
- have a portrait area that overlaps this bleed margin; and
- want to provide a way for users to easily extend a portrait into the bleed margin without sacrificing composition.
- Parameters:
limitRatio- the new limit ratio to set- Throws:
java.lang.IllegalArgumentException- if the limit is not between 0 and 1 inclusive
-
getSyntheticEdgeLimit
public double getSyntheticEdgeLimit()
Returns the current synthetic edge limit ratio.- Returns:
- the current limit, from 0 to 1
- See Also:
setSyntheticEdgeLimit(double)
-
getSyntheticEdgeImage
protected java.awt.image.BufferedImage getSyntheticEdgeImage()
Returns an image that can be used to paint the portrait with any synthetic edges applied.- Returns:
- an image, with edges extended according to the synthetic edge limit, or null
-
getPan
public final java.awt.geom.Point2D getPan(java.awt.geom.Point2D dest)
Description copied from class:AbstractPortraitReturns the pan value as aPoint2D. The pan values will be stored indest; ifnull, a new point object will be allocated and returned.The base class calls
Portrait.getPanX()andPortrait.getPanY()to obtain x and y values to store in the destination point.- Specified by:
getPanin interfacePortrait- Overrides:
getPanin classAbstractPortrait- Parameters:
dest- a point object to store the pan value in- Returns:
- the point object that contains the pan value
-
setPan
public final void setPan(java.awt.geom.Point2D pan)
Description copied from class:AbstractPortraitSets the pan value from a reference point.The base class calls
Portrait.setPanX(double)andPortrait.setPanY(double)using the x and y values stored inpan.- Specified by:
setPanin interfacePortrait- Overrides:
setPanin classAbstractPortrait- Parameters:
pan- a point set to the new X and Y pan values
-
getPanX
public double getPanX()
Description copied from interface:PortraitReturns the horizontal pan value. A value of 0 places the image at its default location within the portrait area. The units are pixels in the component's template image.
-
getPanY
public double getPanY()
Description copied from interface:PortraitReturns the vertical pan value. A value of 0 places the image at its default location within the portrait area. The units are pixels in the component's template image.
-
setPanX
public void setPanX(double x)
Description copied from interface:PortraitSets the horizontal pan value.
-
setPanY
public void setPanY(double y)
Description copied from interface:PortraitSets the vertical pan value.
-
getScale
public double getScale()
Description copied from interface:PortraitReturns the scale of the portrait. A scale of 1 means that the portrait is being used at the default size chosen by the component.
-
setScale
public void setScale(double scale)
Description copied from interface:PortraitSets the scale factor applied to the default size chosen by the component. Note that for many component types, the default size just fits in the available space, so that a value less than 1 will show blank space at one or more edges of the portrait area.
-
setRotation
public void setRotation(double angleInDegrees)
Description copied from class:AbstractPortraitSets the rotation angle for the portrait image.The base class does nothing since the abstract portrait does not support rotation by default.
- Specified by:
setRotationin interfacePortrait- Overrides:
setRotationin classAbstractPortrait- Parameters:
angleInDegrees- the rotation angle, in degrees
-
getRotation
public double getRotation()
Description copied from class:AbstractPortraitReturns the rotation angle for the portrait image. The default rotation is usually 0, but it differs by component. Positive angles turn in the anti-clockwise direction, while negative angles turn in the clockwise direction.The base class always returns 0 since the abstract portrait does not support rotation by default.
- Specified by:
getRotationin interfacePortrait- Overrides:
getRotationin classAbstractPortrait- Returns:
- the rotation angle, in degrees
-
copyStateTo
public void copyStateTo(DefaultPortrait target)
Copies the basic state of this portrait to another portrait: the scale, pan, and rotation values, and the source image.- Parameters:
target- the target to copy to
-
setScaleUsesMinimum
public final void setScaleUsesMinimum(boolean useMinimum)
Sets whether this portrait should use the minimum fit scale when computing a default scale value for an image. Whenfalse, the default scale value is selected so that the entire portrait clip region is covered, even if that means part of the portrait won't be visible. Whentrue, the default scale value is selected so that the entire image just fits within the clip region, even if that means that part of the clip region will not be covered by the portrait. The default setting isfalse(cover the entire clip region).Note: This is normally called at most once, just after the portrait is first created.
- Parameters:
useMinimum- iftrue, the minimum scale that fits the portrait within the clip region will be the default scale value
-
getScaleUsesMinimum
public final boolean getScaleUsesMinimum()
Returns whether the default portrait scale will cover the entire clip region or fit the portrait within the clip region. SeesetScaleUsesMinimum(boolean)for details.- Returns:
trueif the minimal scaling method is enabled
-
setClipping
public final void setClipping(boolean clipping)
Sets whether the portrait is clipped to the clip region. The default istrue. Whenfalsethe clip region is only used to determine a portrait image's default size.Note: This is normally called at most once, just after the portrait is first created.
- Parameters:
clipping- iftrue, clip the portrait to the clip region when it is drawn withpaint(java.awt.Graphics2D, ca.cgjennings.apps.arkham.sheet.RenderTarget).
-
getClipping
public final boolean getClipping()
Returnstrueif the portrait will be clipped to the clip region. SeesetClipping(boolean)for details.- Returns:
trueif clipping is enabled.
-
getClipDimensions
public java.awt.Dimension getClipDimensions()
Returns the size of the bounding rectangle of the area that the portrait is drawn in on the component sheet, in the coordinate system of the sheet's template. This may returnnullif this value is unknown or inapplicable, in which case some features of the portrait panel will not be available.- Specified by:
getClipDimensionsin interfacePortrait- Returns:
- the dimensions of the portrait's clipping rectangle
-
getClipStencil
public java.awt.image.BufferedImage getClipStencil()
Description copied from class:AbstractPortraitOptionally returns an image that describes how the portrait will "show through" surrounding card features. The image's alpha channel is taken to represent a mask for the features that appear over the portrait area. For example, pixels with an alpha of 0 will show the portrait image only, and pixels with an alpha of 255 will show part of a sheet feature that obscures the underlying portrait. In other words, the alpha channel describes the shape of the portrait. If the portrait is simply an unobscured rectangle (that is, nothing is drawn overtop of it), this method can simply returnnull.Note: The value returned by this method is not guaranteed to be accurate and should not be relied on.
The base class returns
null, indicating that the portrait area is unobscured.- Specified by:
getClipStencilin interfacePortrait- Overrides:
getClipStencilin classAbstractPortrait- Returns:
- an image whose alpha channel describes the obscured areas of the
portrait, or
null
-
setClipStencil
public void setClipStencil(java.awt.image.BufferedImage clipStencil)
Sets an explicit clip stencil for this portrait. Setting an explicit clip stencil permanently overrides the default clip stencil mechanism. Once an explicit stencil is set,getClipStencil()will return the most recently set stencil. The stencil can be removed by explicitly setting it tonull.Note: The explicit clip stencil is not serialized when the portrait is written to a save file. You will need to ensure that the stencil is set both when the component is first created and when it is read form a save file. In DIY components, a convenient way to do this is to set the stencil in the appropriate painter creation function.
- Parameters:
clipStencil- the clip stencil to use in the portrait'sPortraitPanelcomponent
-
setBackgroundFilled
public final void setBackgroundFilled(boolean fill)
Sets whether the portrait clip region will be filled with solid white before painting the portrait. If set, the portrait clip region will be filled in with solid white before painting the portrait. This is usually turned off when the user is expected to use portraits that have transparency because the portrait is painted over a background illustration.Note: This is normally called at most once, just after the portrait is first created.
- Parameters:
fill- iftrue, the portrait background will be filled in when it is drawn withpaint(java.awt.Graphics2D, ca.cgjennings.apps.arkham.sheet.RenderTarget).
-
isBackgroundFilled
public final boolean isBackgroundFilled()
Returnstrueif portrait areas will be filled with solid white before painting the portrait. SeesetBackgroundFilled(boolean)for details.- Returns:
trueif the portrait clip region is filled before drawing the portrait
-
paint
public void paint(java.awt.Graphics2D g, RenderTarget target)Paints the portrait image on a graphics context provided by aSheet.- Parameters:
g- the graphics context for paintingtarget- the rendering target
-
firePortraitModified
protected void firePortraitModified()
Notifies the sheets indicated bygetFacesToUpdate()that they must be redrawn because the portrait settings have been modified. There is normally no need to call this directly, as updates are taken care of automatically.
-
fireChildrenModified
protected void fireChildrenModified()
Notifies any children that their attached sheets must be redrawn. This is called when the parent's source image changes. There is normally no need to call this directly, as updates are taken care of automatically.
-
computeDefaultImagePan
public java.awt.geom.Point2D computeDefaultImagePan(java.awt.image.BufferedImage image)
Returns a default pan value for an image. This is the initial pan value used when the image source changes. The base class always returns (0,0).- Parameters:
image- the image to compute a default pan for- Returns:
- the default pan value
-
computeDefaultImageScale
public double computeDefaultImageScale(java.awt.image.BufferedImage image)
Returns a default scale value for an image. This is the initial scale value used when the image source changes. The scale is determined based on theportrait-clip-regionfor the provided portrait key.- Parameters:
image- the image to compute a default scale for- Returns:
- the default scale value for
image
-
computeDefaultImageRotation
public double computeDefaultImageRotation(java.awt.image.BufferedImage image)
Returns a default rotation value for an image. This is the initial rotation used when the image source changes.The base class implementation uses the following procedure: If the portrait does not include the rotation feature, 0 is always returned. Otherwise, if a key equal to the base key with the suffix
-default-rotationis defined and can be parsed as a double value, then that value is returned. If the key does not exist or cannot be parsed, 0 is returned.- Parameters:
image- the image to determine the default rotation for- Returns:
- the default rotation value for
image
-
-