
com.threerings.media.image.NinePatch Maven / Gradle / Ivy
//
// Nenya library - tools for developing networked games
// Copyright (C) 2002-2012 Three Rings Design, Inc., All Rights Reserved
// https://github.com/threerings/nenya
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.threerings.media.image;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
/**
* The NinePatch class permits drawing a bitmap in nine sections. The four corners are unscaled;
* the four edges are scaled in one axis, and the middle is scaled in both axes. Normally, the
* middle is transparent so that the patch can provide a selection about a rectangle. Essentially,
* it allows the creation of custom graphics that will scale the way that you define, when content
* added within the image exceeds the normal bounds of the graphic.
*
* This is an AWT clone of the NinePatch functionality that exists in Google's Android graphic
* system. See http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch
*/
public class NinePatch
{
/**
* Builds a NinePatch based on the given image.
*
* Figures out the NinePatch bounds by parsing the image: Opaque pixels in the top row
* and left column show the stretchable center. Opaque pixels in the bottom row and right
* column show the content area.
*/
public NinePatch (BufferedImage img)
{
this(img.getSubimage(1, 1, img.getWidth() - 2, img.getHeight() - 2),
getRectangle(img, true), getRectangle(img, false));
}
/**
* Builds a NinePatch based on the given image.
*
* @param center specifies the stretchable center of the image.
* @param content if non-null, specifies the usable content area of the image
*/
public NinePatch (BufferedImage img, Rectangle center, Rectangle content)
{
_img = img;
_left = center.x;
_right = _img.getWidth() - (center.x + center.width);
_top = center.y;
_bottom = _img.getHeight() - (center.y + center.height);
if (content != null) {
_leftPad = content.x;
_rightPad = _img.getWidth() - (content.x + content.width);
_topPad = content.y;
_bottomPad = _img.getHeight() - (content.y + content.height);
} else {
_leftPad = _rightPad = _topPad = _bottomPad = 0;
}
}
public void paint (Graphics2D gfx, Rectangle location)
{
int sx, sy, sw, sh;
int dx, dy, dw, dh;
int width = _img.getWidth();
int height = _img.getHeight();
// Top left
sw = _left;
sh = _top;
sx = 0;
sy = 0;
dw = sw;
dh = sh;
dx = location.x;
dy = location.y;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Top right
sw = _right;
sh = _top;
sx = width - sw;
sy = 0;
dw = sw;
dh = sh;
dx = location.x + location.width - dw;
dy = location.y;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Bottom left
sw = _left;
sh = _bottom;
sx = 0;
sy = height - sh;
dw = sw;
dh = sh;
dx = location.x;
dy = location.y + location.height - dh;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Bottom right
sw = _right;
sh = _bottom;
sx = width - sw;
sy = height - sh;
dw = sw;
dh = sh;
dx = location.x + location.width - dw;
dy = location.y + location.height - dh;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Top center
sw = width - _left - _right;
sh = _top;
sx = _left;
sy = 0;
dw = location.width - _left - _right;
dh = sh;
dx = location.x + _left;
dy = location.y;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Bottom center
sw = width - _left - _right;
sh = _bottom;
sx = _left;
sy = height - _bottom;
dw = location.width - _left - _right;
dh = sh;
dx = location.x + _left;
dy = location.y + location.height - dh;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Left center
sw = _left;
sh = height - _top - _bottom;
sx = 0;
sy = _top;
dw = sw;
dh = location.height - _top - _bottom;
dx = location.x;
dy = location.y + _top;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Right center
sw = _right;
sh = height - _top - _bottom;
sx = width - _right;
sy = _top;
dw = sw;
dh = location.height - _top - _bottom;
dx = location.x + location.width - _right;
dy = location.y + _top;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
// Dead center
sw = width - _left - _right;
sh = height - _top - _bottom;
sx = _left;
sy = _top;
dw = location.width - _left - _right;
dh = location.height - _top - _bottom;
dx = location.x + _left;
dy = location.y + _top;
gfx.drawImage(_img, dx, dy, dx + dw, dy + dh, sx, sy, sx + sw, sy + sh, null);
}
/**
* Returns a rectangle describing the bounds of this NinePatch when drawn such that it frames
* the given content rectangle.
*/
public Rectangle getBoundsSurrounding (Rectangle content)
{
Rectangle bounds = new Rectangle(content);
bounds.width += _leftPad + _rightPad;
bounds.height += _topPad + _bottomPad;
bounds.x -= _leftPad;
bounds.y -= _topPad;
return bounds;
}
/**
* Parses the image to find the bounds of the rectangle defined by pixels on the outside.
*/
protected static Rectangle getRectangle (BufferedImage img, boolean stretch)
{
Rectangle rect = new Rectangle(0, 0, img.getWidth() - 2, img.getHeight() - 2);
for (int xx = 1; xx < rect.width + 1; xx++) {
if (ImageUtil.hitTest(img, xx, stretch ? 0 : img.getHeight() - 1)) {
rect.x = xx - 1;
rect.width -= rect.x;
break;
}
}
for (int xx = img.getWidth() - 1; xx >= rect.x + 1; xx--) {
if (ImageUtil.hitTest(img, xx, stretch ? 0 : img.getHeight() - 1)) {
rect.width = xx - rect.x;
break;
}
}
for (int yy = 1; yy < rect.height + 1; yy++) {
if (ImageUtil.hitTest(img, stretch ? 0 : img.getWidth() - 1, yy)) {
rect.y = yy - 1;
rect.height -= rect.y;
break;
}
}
for (int yy = img.getHeight() - 1; yy >= rect.y + 1; yy--) {
if (ImageUtil.hitTest(img, stretch ? 0 : img.getWidth() - 1, yy)) {
rect.height = yy - rect.y;
break;
}
}
return rect;
}
final protected BufferedImage _img;
/** The size of the non-stretchable regions on each side. */
final protected int _top;
final protected int _bottom;
final protected int _left;
final protected int _right;
/** The amount of padding on each side until we're in the content area. */
final protected int _topPad;
final protected int _bottomPad;
final protected int _leftPad;
final protected int _rightPad;
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy