From: <aki...@us...> - 2014-11-28 14:31:51
|
Revision: 9712 http://sourceforge.net/p/gridarta/code/9712 Author: akirschbaum Date: 2014-11-28 14:31:46 +0000 (Fri, 28 Nov 2014) Log Message: ----------- Add support for tile stretching. [Atrinik] Modified Paths: -------------- streams/tilestretching/src/atrinik/ChangeLog streams/tilestretching/src/atrinik/src/main/java/net/sf/gridarta/var/atrinik/model/gameobject/GameObject.java streams/tilestretching/src/atrinik/src/test/java/net/sf/gridarta/var/atrinik/gui/map/renderer/MapRendererTest.java streams/tilestretching/src/daimonin/src/main/java/net/sf/gridarta/var/daimonin/model/gameobject/GameObject.java streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/AbstractIsoMapRenderer.java streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/IsoMapRenderer.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/AbstractFaceProvider.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/ArchFaceProvider.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/CollectedFaceProvider.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/EmptyFaceProvider.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceObjectProviders.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceProvider.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FilterFaceProvider.java streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/gameobject/DefaultIsoGameObject.java streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/GameObjectFactoryTest.java streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/TestGameObject.java Added Paths: ----------- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/StretchedImageFilter.java Modified: streams/tilestretching/src/atrinik/ChangeLog =================================================================== --- streams/tilestretching/src/atrinik/ChangeLog 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/atrinik/ChangeLog 2014-11-28 14:31:46 UTC (rev 9712) @@ -1,3 +1,7 @@ +2014-11-28 Alex Tokar + + * Add support for tile stretching. + 2014-11-16 Alex Tokar * Remove support for 'no_priest' map attribute; add support for Modified: streams/tilestretching/src/atrinik/src/main/java/net/sf/gridarta/var/atrinik/model/gameobject/GameObject.java =================================================================== --- streams/tilestretching/src/atrinik/src/main/java/net/sf/gridarta/var/atrinik/model/gameobject/GameObject.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/atrinik/src/main/java/net/sf/gridarta/var/atrinik/model/gameobject/GameObject.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -112,6 +112,14 @@ /** * {@inheritDoc} */ + @Override + public boolean isStretched() { + return getAttributeInt(LAYER) == 1 || getAttributeInt(LAYER) == 2; + } + + /** + * {@inheritDoc} + */ @NotNull @Override public GameObject clone() { Modified: streams/tilestretching/src/atrinik/src/test/java/net/sf/gridarta/var/atrinik/gui/map/renderer/MapRendererTest.java =================================================================== --- streams/tilestretching/src/atrinik/src/test/java/net/sf/gridarta/var/atrinik/gui/map/renderer/MapRendererTest.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/atrinik/src/test/java/net/sf/gridarta/var/atrinik/gui/map/renderer/MapRendererTest.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -131,6 +131,7 @@ mapModel.beginTransaction("TEST"); try { final TestGameObject gameObject = mapModelCreator.newGameObject("arch", "name"); + gameObject.setAttributeInt(DefaultIsoGameObject.LAYER, 3); gameObject.setAttributeInt(DefaultIsoGameObject.Z, 5); mapModel.addGameObjectToMap(gameObject, new Point(0, 0), mapModelCreator.getTopmostInsertionMode()); } finally { @@ -150,6 +151,7 @@ mapModel.beginTransaction("TEST"); try { final TestGameObject gameObject = mapModelCreator.newGameObject("arch", "name"); + gameObject.setAttributeInt(DefaultIsoGameObject.LAYER, 3); gameObject.setAttributeInt(DefaultIsoGameObject.Z, -5); mapModel.addGameObjectToMap(gameObject, new Point(0, 0), mapModelCreator.getTopmostInsertionMode()); } finally { @@ -245,6 +247,7 @@ mapModel.beginTransaction("TEST"); try { final TestGameObject gameObject = mapModelCreator.newGameObject("arch", "name"); + gameObject.setAttributeInt(DefaultIsoGameObject.LAYER, 3); gameObject.setAttributeInt(DefaultIsoGameObject.ALIGN, 5); gameObject.setAttributeInt(DefaultIsoGameObject.ROTATE, 20); gameObject.setAttributeInt(DefaultIsoGameObject.Z, 15); Modified: streams/tilestretching/src/daimonin/src/main/java/net/sf/gridarta/var/daimonin/model/gameobject/GameObject.java =================================================================== --- streams/tilestretching/src/daimonin/src/main/java/net/sf/gridarta/var/daimonin/model/gameobject/GameObject.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/daimonin/src/main/java/net/sf/gridarta/var/daimonin/model/gameobject/GameObject.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -114,6 +114,14 @@ /** * {@inheritDoc} */ + @Override + public boolean isStretched() { + return false; + } + + /** + * {@inheritDoc} + */ @NotNull @Override public GameObject clone() { Modified: streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/AbstractIsoMapRenderer.java =================================================================== --- streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/AbstractIsoMapRenderer.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/AbstractIsoMapRenderer.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -501,6 +501,7 @@ icon = unknownSquareIcon; } else { final boolean drawDouble = head.isDrawDouble(mapViewSettings.isDoubleFaces()); + final boolean isStretched = head.isStretched(); if (mapViewSettings.isAlphaType(head.getEditType())) { if (drawDouble) { icon = head.getTransparentDoubleImage(); @@ -508,7 +509,10 @@ icon = head.getTransparentImage(); } } else { - if (drawDouble) { + final long stretchFactor; + if (isStretched && (stretchFactor = head.getStretchFactor()) != 0) { + icon = head.getStretchedImage(stretchFactor); + } else if (drawDouble) { icon = head.getDoubleImage(); } else { icon = head.getNormalImage(); @@ -516,6 +520,7 @@ } } final int xOffset = head.getAttributeInt(DefaultIsoGameObject.ALIGN); + final int yOffset = head.getYOffset(); final int zoom = head.getAttributeInt(DefaultIsoGameObject.ZOOM); final double rotate = getRotate(head); final int alpha = head.getAttributeInt(DefaultIsoGameObject.ALPHA); @@ -544,7 +549,7 @@ final int multiPartNr = archetype.getMultiPartNr(); final int x = xStart - multiPositionData.getXOffset(headMultiShapeID, multiPartNr) + multiPositionData.getWidth(headMultiShapeID) / 2 - iconWidth / 2; final int y = yStart - multiPositionData.getYOffset(headMultiShapeID, multiPartNr) + isoMapSquareInfo.getYLen() - iconHeight; - paintScaledIcon(g, icon, x + xOffset, y, zoom, alpha, rotate, tmpIconWidth, tmpIconHeight, iconWidth, iconHeight); + paintScaledIcon(g, icon, x + xOffset, y - yOffset, zoom, alpha, rotate, tmpIconWidth, tmpIconHeight, iconWidth, iconHeight); } } else { final int x; @@ -554,15 +559,14 @@ x = xStart; } final int y = yStart + isoMapSquareInfo.getYLen() - iconHeight; - paintScaledIcon(g, icon, x + xOffset, y, zoom, alpha, rotate, tmpIconWidth, tmpIconHeight, iconWidth, iconHeight); + paintScaledIcon(g, icon, x + xOffset, y - yOffset, zoom, alpha, rotate, tmpIconWidth, tmpIconHeight, iconWidth, iconHeight); } // Paint first object (most likely a mob) in spawn points. if (!inSpawnPoint && isSpawnPoint(head)) { final G mob = head.getFirst(); if (mob != null) { - final int yOffset = mob.getAttributeInt(DefaultIsoGameObject.Z); - paintGameObject(g, xStart, yStart - yOffset, mob, true); + paintGameObject(g, xStart, yStart, mob, true); } } } Modified: streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/IsoMapRenderer.java =================================================================== --- streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/IsoMapRenderer.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/gridarta/src/main/java/net/sf/gridarta/gui/map/renderer/IsoMapRenderer.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -162,19 +162,11 @@ if (square.isEmpty()) { emptySquareIcon.paintIcon(this, g, x, y); } else { - boolean foundFloor = false; for (final G node : square) { filterControl.objectInSquare(filterState, node); if (filterControl.canShow(node)) { final G head = node.getHead(); - final int yOffset; - if (!foundFloor && head.getAttributeInt(DefaultIsoGameObject.LAYER) == 1) { - foundFloor = true; - yOffset = 0; - } else { - yOffset = head.getAttributeInt(DefaultIsoGameObject.Z); - } - paintGameObjectIfVisible(g, x, y - yOffset, node); + paintGameObjectIfVisible(g, x, y, node); } } } Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/AbstractFaceProvider.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/AbstractFaceProvider.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/AbstractFaceProvider.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -53,16 +53,17 @@ */ @Nullable @Override - public ImageIcon getImageIconForFacename(@NotNull final String faceName) { + public ImageIcon getImageIconForFacename(@NotNull final String faceName, final long stretch) { ImageIcon icon = null; - final SoftReference<ImageIcon> ref = cache.get(faceName); + final String realFaceName = String.format("%s-%d", faceName, stretch); + final SoftReference<ImageIcon> ref = cache.get(realFaceName); if (ref != null) { icon = ref.get(); } if (icon == null) { - icon = createImage(faceName); + icon = createImage(faceName, stretch); if (icon != null) { - cache.put(faceName, new SoftReference<ImageIcon>(icon)); + cache.put(realFaceName, new SoftReference<ImageIcon>(icon)); } } return icon; @@ -82,9 +83,10 @@ /** * Create an image not found in the cache. * @param faceName face name to get image for, excluding path and ending + * @param stretch stretch factor * @return icon for faceName */ @Nullable - protected abstract ImageIcon createImage(@NotNull String faceName); + protected abstract ImageIcon createImage(@NotNull String faceName, final long stretch); } Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/ArchFaceProvider.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/ArchFaceProvider.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/ArchFaceProvider.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -75,7 +75,7 @@ */ @Nullable @Override - protected ImageIcon createImage(@NotNull final String faceName) { + protected ImageIcon createImage(@NotNull final String faceName, final long stretch) { final String filename = files.get(faceName); if (filename == null) { return null; Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/CollectedFaceProvider.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/CollectedFaceProvider.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/CollectedFaceProvider.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -87,7 +87,7 @@ */ @Nullable @Override - protected ImageIcon createImage(@NotNull final String faceName) { + protected ImageIcon createImage(@NotNull final String faceName, final long stretch) { final Long position = positions.get(faceName); if (position == null) { return null; Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/EmptyFaceProvider.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/EmptyFaceProvider.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/EmptyFaceProvider.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -34,7 +34,7 @@ */ @Nullable @Override - public ImageIcon getImageIconForFacename(@NotNull final String faceName) { + public ImageIcon getImageIconForFacename(@NotNull final String faceName, final long stretch) { return null; } Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceObjectProviders.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceObjectProviders.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceObjectProviders.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -151,6 +151,12 @@ private final FilterFaceProvider doubleAlphaFaceProvider; /** + * The face provider for stretched floor faces. + */ + @NotNull + private final FilterFaceProvider stretchedFaceProvider; + + /** * The {@link FaceObjects} instance for looking up face names. */ @NotNull @@ -180,6 +186,7 @@ this.resourceIcons = resourceIcons; doubleFaceProvider = new FilterFaceProvider(new DoubleImageFilter(doubleFaceOffset)); doubleAlphaFaceProvider = new FilterFaceProvider(new DoubleImageFilter(doubleFaceOffset)); + stretchedFaceProvider = new FilterFaceProvider(new StretchedImageFilter()); } /** @@ -206,7 +213,7 @@ if (normalFaceProvider != null) { normalFaceProvider.reload(); } - for (final FaceProvider faceProvider : new FaceProvider[] { GRAY, RED, GREEN, BLUE, ALPHA, GRID, doubleFaceProvider, doubleAlphaFaceProvider }) { + for (final FaceProvider faceProvider : new FaceProvider[] { GRAY, RED, GREEN, BLUE, ALPHA, GRID, doubleFaceProvider, doubleAlphaFaceProvider, stretchedFaceProvider }) { faceProvider.reload(); } @@ -230,6 +237,7 @@ GRID.setParent(normalFaceProvider); doubleFaceProvider.setParent(normalFaceProvider); doubleAlphaFaceProvider.setParent(normalFaceProvider); + stretchedFaceProvider.setParent(normalFaceProvider); } /** @@ -259,7 +267,7 @@ }); //noinspection ConstantConditions - return getFace(baseObject.getFaceObjName(), undefinedArchetype[0], normalFaceProvider, normalFaceProvider); + return getFace(baseObject.getFaceObjName(), undefinedArchetype[0], normalFaceProvider, normalFaceProvider, 0); } /** @@ -270,7 +278,7 @@ */ @NotNull public ImageIcon getTrans(@NotNull final GameObject<?, ?, ?> gameObject) { - return getFace(gameObject.getFaceObjName(), gameObject.hasUndefinedArchetype(), ALPHA, ALPHA); + return getFace(gameObject.getFaceObjName(), gameObject.hasUndefinedArchetype(), ALPHA, ALPHA, 0); } /** @@ -285,7 +293,7 @@ throw new IllegalStateException(); } //noinspection ConstantConditions - return getFace(gameObject.getFaceObjName(), gameObject.hasUndefinedArchetype(), normalFaceProvider, doubleFaceProvider); + return getFace(gameObject.getFaceObjName(), gameObject.hasUndefinedArchetype(), normalFaceProvider, doubleFaceProvider, 0); } /** @@ -296,20 +304,32 @@ */ @NotNull public ImageIcon getTransDouble(@NotNull final GameObject<?, ?, ?> gameObject) { - return getFace(gameObject.getFaceObjName(), gameObject.hasUndefinedArchetype(), ALPHA, doubleAlphaFaceProvider); + return getFace(gameObject.getFaceObjName(), gameObject.hasUndefinedArchetype(), ALPHA, doubleAlphaFaceProvider, 0); } /** + * Returns the stretched face for a {@link GameObject} as an {@link + * ImageIcon}. + * @param gameObject the game object + * @return the image icon + */ + @NotNull + public ImageIcon getStretched(@NotNull final GameObject<?, ?, ?> gameObject, final long stretch) { + return getFace(gameObject.getFaceObjName(), gameObject.hasUndefinedArchetype(), stretchedFaceProvider, ALPHA, stretch); + } + + /** * Returns the {@link ImageIcon} of a face with a certain face name. * @param faceName the face name of face * @param hasUndefinedArchetype if set, return the face for an game object * referencing an undefined archetype * @param singleFaceProvider the face provider to use for single faces * @param doubleFaceProvider the face provider to use for double faces + * @param stretch stretch factor * @return face for face {@code faceName} */ @NotNull - private ImageIcon getFace(@Nullable final String faceName, final boolean hasUndefinedArchetype, @NotNull final FaceProvider singleFaceProvider, @NotNull final FaceProvider doubleFaceProvider) { + private ImageIcon getFace(@Nullable final String faceName, final boolean hasUndefinedArchetype, @NotNull final FaceProvider singleFaceProvider, @NotNull final FaceProvider doubleFaceProvider, final long stretch) { if (hasUndefinedArchetype) { return resourceIcons.getResourceIcon(ResourceIcons.SQUARE_NO_ARCH); } @@ -323,10 +343,19 @@ return resourceIcons.getResourceIcon(ResourceIcons.SQUARE_NO_FACE); } - final FaceProvider faceProvider = faceObject.isDouble() ? doubleFaceProvider : singleFaceProvider; + final FaceProvider faceProvider; + + if (stretch != 0) { + faceProvider = stretchedFaceProvider; + } else if (faceObject.isDouble()) { + faceProvider = doubleFaceProvider; + } else { + faceProvider = singleFaceProvider; + } + final String alternativeFaceName = faceObject.getAlternativeFaceName(); final String effectiveFaceName = alternativeFaceName != null && singleFaceProvider == doubleFaceProvider ? alternativeFaceName : faceName; - final ImageIcon face = faceProvider.getImageIconForFacename(effectiveFaceName); + final ImageIcon face = faceProvider.getImageIconForFacename(effectiveFaceName, stretch); if (face == null) { return resourceIcons.getResourceIcon(ResourceIcons.SQUARE_UNKNOWN); } @@ -341,7 +370,7 @@ */ @Nullable public ImageIcon getImageIconForFacename(@NotNull final String faceObjName) { - return normalFaceProvider == null ? null : normalFaceProvider.getImageIconForFacename(faceObjName); + return normalFaceProvider == null ? null : normalFaceProvider.getImageIconForFacename(faceObjName, 0); } /** Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceProvider.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceProvider.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FaceProvider.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -35,7 +35,7 @@ * @return icon for faceName */ @Nullable - ImageIcon getImageIconForFacename(@NotNull String faceName); + ImageIcon getImageIconForFacename(@NotNull String faceName, final long stretch); /** * Reload faces. This method does not really immediately reload all faces, Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FilterFaceProvider.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FilterFaceProvider.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/FilterFaceProvider.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -48,8 +48,8 @@ /** * Create a new instance. The parent is unset. The parent must be set with * {@link #setParent(FaceProvider)} before invoking {@link - * #getImageIconForFacename(String)} or {@link #createImage(String)} or - * getting a face resp. creating an image will throw an {@link + * #getImageIconForFacename(String, long)} or {@link #createImage(String, + * long)} or getting a face resp. creating an image will throw an {@link * IllegalStateException}. * @param filter ImageFilter to apply for creating the images */ @@ -80,15 +80,20 @@ */ @Nullable @Override - protected ImageIcon createImage(@NotNull final String faceName) { + protected ImageIcon createImage(@NotNull final String faceName, final long stretch) { if (parent == null) { throw new IllegalStateException("FilterFaceProvider in use but parent not set."); } - final ImageIcon imageIcon = parent.getImageIconForFacename(faceName); + final ImageIcon imageIcon = parent.getImageIconForFacename(faceName, stretch); if (imageIcon == null) { return null; } try { + if (stretch != 0) { + final StretchedImageFilter filter2 = (StretchedImageFilter) filter; + filter2.setStretchedFactor(stretch); + } + return new ImageIcon(Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(imageIcon.getImage().getSource(), filter))); } catch (final Exception ignored) { return null; Added: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/StretchedImageFilter.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/StretchedImageFilter.java (rev 0) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/StretchedImageFilter.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -0,0 +1,322 @@ +/* + * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games. + * Copyright (C) 2000-2010 The Gridarta Developers. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +package net.sf.gridarta.model.face; + +import java.awt.Point; +import java.awt.image.ColorModel; +import java.awt.image.ImageFilter; +import org.jetbrains.annotations.NotNull; + +/** + * An {@link ImageFilter} that produces stretched faces. + * @author Alex Tokar (based on C implementation by James Little) + */ +public class StretchedImageFilter extends ImageFilter { + + /** + * The default RGB color model. + */ + private static final ColorModel RGB_DEFAULT_COLOR_MODEL = ColorModel.getRGBdefault(); + + /** + * The stretch factor. + */ + private long stretchFactor; + + /** + * The image width. + */ + private int width; + + private int height; + + /** + * The destination image's pixels. + */ + @NotNull + private int[] raster; + + @NotNull + private int[] pixels; + + @NotNull + private final Point[] point = { new Point(), new Point(), new Point(), new Point() }; + + @NotNull + private final Point[] point2 = { new Point(), new Point(), new Point(), new Point() }; + + @NotNull + private final double[] slope = { 0.0, 0.0, 0.0, 0.0 }; + + @NotNull + private final int[] stdTileHalfLen = { 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 11, 10, 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5, 4, 4, 3, 3, 2, 2, 1, 1, 0, 0 }; + + /** + * Set the stretch factor for this instance. + * @param stretchFactor the stretch factor + */ + public void setStretchedFactor(final long stretchFactor) { + this.stretchFactor = stretchFactor; + } + + /** + * {@inheritDoc} + */ + @Override + public void setDimensions(final int width, final int height) { + this.width = width; + this.height = height; + final int newHeight = height + (int) ((stretchFactor >> 24) & 0xff); + pixels = new int[width * height]; + raster = new int[width * newHeight]; + super.setDimensions(width, newHeight); + } + + /** + * {@inheritDoc} + */ + @Override + public void setHints(final int hints) { + super.setHints(COMPLETESCANLINES | TOPDOWNLEFTRIGHT); + } + + /** + * {@inheritDoc} + */ + @Override + public void setPixels(final int x, final int y, final int w, final int h, @NotNull final ColorModel model, @NotNull final byte[] pixels, final int off, final int scansize) { + int srcOff = off; + int dstOff = y * width + x; + for (int yc = 0; yc < h; yc++) { + for (int xc = 0; xc < w; xc++) { + this.pixels[dstOff++] = model.getRGB(pixels[srcOff++] & 0xff); + } + srcOff += scansize - w; + dstOff += width - w; + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setPixels(final int x, final int y, final int w, final int h, @NotNull final ColorModel model, @NotNull final int[] pixels, final int off, final int scansize) { + int srcOff = off; + int dstOff = y * width + x; + for (int yc = 0; yc < h; yc++) { + for (int xc = 0; xc < w; xc++) { + this.pixels[dstOff++] = model.getRGB(pixels[srcOff++]); + } + srcOff += scansize - w; + dstOff += width - w; + } + } + + private void determineLine(final int idx, final int sx, final int sy, final int ex, final int ey) { + point[idx].x = sx; + point[idx].y = sy; + point2[idx].x = ex; + point2[idx].y = ey; + final double xDiff = Math.abs(sx - ex); + final double yDiff = Math.abs(sy - ey); + slope[idx] = xDiff == 0 ? 0.0 : yDiff / xDiff; + } + + private void copyPixelToPixel(final int x, final int y, final int x2, final int y2, final double brightness, final int wd, final int ht, final int wd2, final int ht2, @NotNull final ColorModel model) { + if (x < 0 || y < 0 || x2 < 0 || y2 < 0) { + return; + } + + if (x >= wd || x2 >= wd2 || y >= ht || y2 >= ht2) { + return; + } + + final int color = model.getRGB(pixels[y * wd + x]); + + final int a = (color >> 24) & 0xff; + if (a == 0) { + return; + } + final int r1 = (color >> 16) & 0xff; + final int g1 = (color >> 8) & 0xff; + final int b1 = (color >> 0) & 0xff; + + final int r = Math.min(255, (int) (r1 * brightness)); + final int g = Math.min(255, (int) (g1 * brightness)); + final int b = Math.min(255, (int) (b1 * brightness)); + raster[y2 * wd2 + x2] = (a << 24) | (r << 16) | (g << 8) | (b << 0); + } + + private void copyVerticalLine(final int srcX, final int srcSy, final int srcEy, final int destX, final int destSy, final int destEy, final double brightness, final boolean extra, final int wd, final int ht, final int wd2, final int ht2, @NotNull final ColorModel model) { + final int minSrcY = Math.min(srcSy, srcEy); + final int maxSrcY = Math.max(srcSy, srcEy); + final int minDestY = Math.min(destSy, destEy); + final int maxDestY = Math.max(destSy, destEy); + final int srcH = maxSrcY - minSrcY; + final int destH = maxDestY - minDestY; + + if (destH == 0) { + if (srcH == 0) { + copyPixelToPixel(srcX, minSrcY, destX, minDestY, brightness, wd, ht, wd2, ht2, model); + return; + } else { + copyPixelToPixel(srcX, (maxSrcY - minSrcY) / 2, destX, minDestY, brightness, wd, ht, wd2, ht2, model); + return; + } + } + + if (srcH == 0) { + final int pixel = model.getRGB(pixels[minSrcY * wd + srcX]); + for (int y = minDestY; y <= maxDestY; y++) { + raster[y * wd2 + destX] = pixel; + } + return; + } + + /* The stretching */ + final double ratio = (double) srcH / (double) destH; + + for (int y = 0; y <= destH; y++) { + final int goY = minDestY + y; + final int getY = (int) (minSrcY + (y * ratio)); + copyPixelToPixel(srcX, getY, destX, goY, brightness, wd, ht, wd2, ht2, model); + } + + if (extra) { + if (maxDestY + 1 < ht2) { + copyPixelToPixel(srcX, maxSrcY, destX, maxDestY + 1, brightness, wd, ht, wd2, ht2, model); + } + } + } + + private void stretch(final int wd, final int ht, @NotNull final ColorModel model) { + final int n = (int) (stretchFactor >> 24) & 0xff; + final int e = (int) (stretchFactor >> 16) & 0xff; + final int w = (int) (stretchFactor >> 8) & 0xff; + final int s = (int) stretchFactor & 0xff; + + final int wd2 = wd; + final int ht2 = ht + n; + + final boolean flat = n != 0 || e != 0 || w != 0 || s != 0; + + double eDark = 1.0; + double wDark = 1.0; + if (w > e) { + wDark = 1.0 - ((w - e) / 25.0); + + if (n > 0 || s > 0) { + eDark = wDark; + } + } + + if (e > w) { + eDark = 1.0 + ((e - w) / 25.0); + + if (s > 0 || n > 0) { + wDark = eDark; + } + } + + determineLine(0, 2, 10 - w + n, 22, 0); + determineLine(1, 2, 12 - w + n, 22, 22 + n - s); + determineLine(2, 45, 10 - e + n, 25, 0); + determineLine(3, 45, 12 - e + n, 25, 22 + n - s); + + for (int lnNum = 0; lnNum < 4; lnNum += 2) { + final int destSx = point[lnNum].x; + final int destSy = point[lnNum].y; + final int destEx = point2[lnNum].x; + final int destEy = point2[lnNum].y; + final double destSlope = slope[lnNum]; + + final int destSy2 = point[lnNum + 1].y; + final int destEy2 = point2[lnNum + 1].y; + final double destSlope2 = slope[lnNum + 1]; + + final int destYInc = destSy > destEy ? -1 : 1; + final int destXInc = destSx > destEx ? -1 : 1; + final int destYInc2 = destSy2 > destEy2 ? -1 : 1; + + int dx = destSx; + int dy = destSy; + double kicker = 0.0; + int y2 = destSy2; + double kicker2 = 0.0; + int atLeastOne = 0; + + while ((destSlope != 0.0 && dx != destEx && dy != destEy) || (atLeastOne == 0 && destSlope == 0.0)) { + atLeastOne = 1; + + if (kicker >= 1.0) { + kicker -= 1.0; + dy += destYInc; + } + + if (kicker2 >= 1.0) { + kicker2 -= 1.0; + y2 += destYInc2; + } + + final int srcLen = stdTileHalfLen[dx]; + + if (lnNum < 2) { + copyVerticalLine(dx, 11 + srcLen, 11 - srcLen, dx, dy, y2, wDark, flat, wd, ht, wd2, ht2, model); + } else { + copyVerticalLine(dx, 11 + srcLen, 11 - srcLen, dx, dy, y2, eDark, flat, wd, ht, wd2, ht2, model); + } + + dx += destXInc; + + kicker += destSlope; + kicker2 += destSlope2; + } + } + + for (int dx = 22; dx < 22 + 2; dx++) { + copyVerticalLine(dx, 0, 23, dx, 0, 23 + n - s, wDark, flat, wd, ht, wd2, ht2, model); + } + + for (int dx = 24; dx < 24 + 2; dx++) { + copyVerticalLine(dx, 0, 23, dx, 0, 23 + n - s, eDark, flat, wd, ht, wd2, ht2, model); + } + + for (int dx = 0; dx < 2; dx++) { + copyPixelToPixel(dx, 11, dx, 11 + n - w, wDark, wd, ht, wd2, ht2, model); + } + + for (int dx = 46; dx < 48; dx++) { + copyPixelToPixel(dx, 11, dx, 11 + n - e, eDark, wd, ht, wd2, ht2, model); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void imageComplete(final int status) { + if (status != IMAGEERROR && status != IMAGEABORTED) { + stretch(width, height, RGB_DEFAULT_COLOR_MODEL); + super.setPixels(0, 0, width, height + (int) ((stretchFactor >> 24) & 0xff), RGB_DEFAULT_COLOR_MODEL, raster, 0, width); + } + super.imageComplete(status); + } + +} Property changes on: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/face/StretchedImageFilter.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Modified: streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/gameobject/DefaultIsoGameObject.java =================================================================== --- streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/gameobject/DefaultIsoGameObject.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/main/java/net/sf/gridarta/model/gameobject/DefaultIsoGameObject.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -19,12 +19,14 @@ package net.sf.gridarta.model.gameobject; +import java.awt.Point; import javax.swing.ImageIcon; import net.sf.gridarta.model.anim.AnimationObjects; import net.sf.gridarta.model.archetype.Archetype; import net.sf.gridarta.model.baseobject.BaseObject; import net.sf.gridarta.model.face.FaceObjectProviders; import net.sf.gridarta.model.maparchobject.MapArchObject; +import net.sf.gridarta.model.mapmodel.MapSquare; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,6 +41,10 @@ */ private static final long serialVersionUID = 1L; + private static final int MAX_STRETCH = 8; + + private static final int MAX_STRETCH_DIAGONAL = 12; + /** * The name of the "z" attribute. */ @@ -52,6 +58,12 @@ public static final String LAYER = "layer"; /** + * The name of the "sub_layer" attribute. + */ + @NotNull + public static final String SUB_LAYER = "sub_layer"; + + /** * The name of the "sys_object" attribute. */ @NotNull @@ -127,6 +139,16 @@ private ImageIcon transDoubleFace; /** + * The stretched floor face. + * @serial + */ + @Nullable + private ImageIcon stretchedFace; + + @NotNull + private final Point point = new Point(); + + /** * Creates a new instance. * @param archetype the base archetype * @param faceObjectProviders the face object providers for looking up @@ -154,6 +176,7 @@ transFace = null; doubleFace = null; transDoubleFace = null; + stretchedFace = null; super.setObjectFace(); } @@ -193,7 +216,149 @@ return transDoubleFace; } + public final int getYOffset() { + final int layer = getAttributeInt(LAYER); + + int height; + if (layer == 0 || layer == 6 || layer == 7 || layer == 3 || layer == 4) { + height = getTopYFloorOffset(); + } else { + height = getYFloorOffset(0, 0); + } + + if (layer > 1) { + height += getAttributeInt(Z); + } + + return height; + } + + public final int getTopYFloorOffset() { + int topHeight = 0; + + for (final G gameObject : getMapSquare().recursive()) { + if (gameObject.getAttributeInt(LAYER) == 1) { + final int height = gameObject.getAttributeInt(Z); + + if (height > topHeight) { + topHeight = height; + } + } + } + + return topHeight; + } + + private int getYFloorOffset(final int dx, final int dy) { + final MapSquare<G, A, R> mapSquare = getMapSquare(); + point.x = mapSquare.getMapX() + dx; + point.y = mapSquare.getMapY() + dy; + + try { + final MapSquare<G, A, R> mapSquare2 = mapSquare.getMapModel().getMapSquare(point); + + for (final G gameObject : mapSquare2.recursive()) { + if (gameObject.getAttributeInt(LAYER) == 1 && gameObject.getAttributeInt(SUB_LAYER) == getAttributeInt(SUB_LAYER)) { + return gameObject.getAttributeInt(Z); + } + } + } catch (final IndexOutOfBoundsException ignored) { + // skip points outside map bounds + } + + return 0; + } + + public final long getStretchFactor() { + int nwHeight = getYFloorOffset(-1, -1); + int nHeight = getYFloorOffset(0, -1); + int neHeight = getYFloorOffset(1, -1); + int swHeight = getYFloorOffset(-1, 1); + int sHeight = getYFloorOffset(0, 1); + int seHeight = getYFloorOffset(1, 1); + int wHeight = getYFloorOffset(-1, 0); + int eHeight = getYFloorOffset(1, 0); + final int myHeight = getYFloorOffset(0, 0); + + if (Math.abs(myHeight - eHeight) > MAX_STRETCH) { + eHeight = myHeight; + } + + if (Math.abs(myHeight - seHeight) > MAX_STRETCH_DIAGONAL) { + seHeight = myHeight; + } + + if (Math.abs(myHeight - sHeight) > MAX_STRETCH) { + sHeight = myHeight; + } + + if (Math.abs(myHeight - swHeight) > MAX_STRETCH_DIAGONAL) { + swHeight = myHeight; + } + + if (Math.abs(myHeight - wHeight) > MAX_STRETCH) { + wHeight = myHeight; + } + + if (Math.abs(myHeight - nwHeight) > MAX_STRETCH_DIAGONAL) { + nwHeight = myHeight; + } + + if (Math.abs(myHeight - nHeight) > MAX_STRETCH) { + nHeight = myHeight; + } + + if (Math.abs(myHeight - neHeight) > MAX_STRETCH_DIAGONAL) { + neHeight = myHeight; + } + + int top = Math.max(wHeight, nwHeight); + top = Math.max(top, nHeight); + top = Math.max(top, myHeight); + + int bottom = Math.max(sHeight, seHeight); + bottom = Math.max(bottom, eHeight); + bottom = Math.max(bottom, myHeight); + + int right = Math.max(nHeight, neHeight); + right = Math.max(right, eHeight); + right = Math.max(right, myHeight); + + int left = Math.max(wHeight, swHeight); + left = Math.max(left, sHeight); + left = Math.max(left, myHeight); + + int minHt = Math.min(top, bottom); + minHt = Math.min(minHt, left); + minHt = Math.min(minHt, right); + minHt = Math.min(minHt, myHeight); + + top -= minHt; + bottom -= minHt; + left -= minHt; + right -= minHt; + + top = Math.max(0, Math.min(255, top)); + bottom = Math.max(0, Math.min(255, bottom)); + left = Math.max(0, Math.min(255, left)); + right = Math.max(0, Math.min(255, right)); + + return bottom + (left << 8) + (right << 16) + (top << 24); + } + /** + * Returns a stretched variant of the face for this floor-type GameObject. + * @return the stretched face for this game object + */ + @NotNull + public ImageIcon getStretchedImage(final long stretchFactor) { + if (stretchedFace == null) { + stretchedFace = faceObjectProviders.getStretched(this, stretchFactor); + } + return stretchedFace; + } + + /** * Returns whether to draw this game object with double height. * @param drawDoubleFaces whether the "draw double faces" map view setting * is enabled @@ -202,6 +367,12 @@ public abstract boolean isDrawDouble(final boolean drawDoubleFaces); /** + * Returns whether to draw this game object with stretching transformation. + * @return whether to draw this game object with stretching transformation + */ + public abstract boolean isStretched(); + + /** * {@inheritDoc} */ @NotNull @@ -231,6 +402,7 @@ transFace = null; doubleFace = null; transDoubleFace = null; + stretchedFace = null; super.facesReloaded(); } Modified: streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/GameObjectFactoryTest.java =================================================================== --- streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/GameObjectFactoryTest.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/GameObjectFactoryTest.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -97,7 +97,7 @@ final FaceProvider faceProvider = new FaceProvider() { @Override - public ImageIcon getImageIconForFacename(@NotNull final String faceName) { + public ImageIcon getImageIconForFacename(@NotNull final String faceName, final long stretch) { if (faceName.equals("face")) { return new ImageIcon(); } Modified: streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/TestGameObject.java =================================================================== --- streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/TestGameObject.java 2014-11-28 13:48:04 UTC (rev 9711) +++ streams/tilestretching/src/model/src/test/java/net/sf/gridarta/model/gameobject/TestGameObject.java 2014-11-28 14:31:46 UTC (rev 9712) @@ -68,6 +68,14 @@ * {@inheritDoc} */ @Override + public boolean isStretched() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override public int getLightRadius() { return 0; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |