Files
smartDriveEE/src/main/java/com/rj/utils/ImageConversionUtil.java
2025-10-17 21:27:27 +08:00

758 lines
28 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.rj.utils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.URL;
import java.util.List;
import java.util.ArrayList;
/**
* 图像转换工具类
*
* 提供图像格式转换、背景移除、透明度处理等功能
* 专门用于满足阿里云背景生成API的图片要求
* - 主体图像必须为带透明背景的RGBA四通道图像
* - PNG格式长边不超过2048像素
* - 公网可访问的URL
*
* @author 成
* @date 2025/1/30
* @version 1.0
*/
@Slf4j
@Component
public class ImageConversionUtil {
/**
* 高级图片转换方法,专门处理透明背景需求
* 确保生成符合阿里云要求的RGBA四通道PNG图像
*
* @param imageUrl 原始图片URL
* @return 转换后的图片字节数组
*/
public byte[] convertImageToAliyunFormatAdvanced(String imageUrl) {
try {
log.info("开始高级图片转换为阿里云格式");
// 1. 下载原始图片
BufferedImage originalImage = downloadImage(imageUrl);
if (originalImage == null) {
log.error("无法下载原始图片");
return null;
}
int width = originalImage.getWidth();
int height = originalImage.getHeight();
log.info("原始图片尺寸: {}x{}, 类型: {}", width, height, getImageTypeName(originalImage.getType()));
// 2. 检查尺寸如果超过2048则缩放
if (Math.max(width, height) > 2048) {
double scale = 2048.0 / Math.max(width, height);
int newWidth = (int) (width * scale);
int newHeight = (int) (height * scale);
log.info("图片尺寸过大,缩放至: {}x{}", newWidth, newHeight);
BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = scaledImage.createGraphics();
// 设置最高质量渲染
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
// 绘制原始图片到缩放图片上
g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
g2d.dispose();
originalImage = scaledImage;
width = newWidth;
height = newHeight;
}
// 3. 创建真正的RGBA四通道图像强制透明背景
BufferedImage rgbaImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rgbaImage.createGraphics();
// 设置最高质量渲染
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
// 强制清空背景为完全透明
g2d.setComposite(AlphaComposite.Clear);
g2d.fillRect(0, 0, width, height);
// 设置正常合成模式
g2d.setComposite(AlphaComposite.SrcOver);
// 如果原始图片没有透明背景,使用智能背景移除
if (!originalImage.getColorModel().hasAlpha() || !hasTransparentPixels(originalImage)) {
log.info("原始图片没有透明背景,使用智能背景移除");
// 使用智能背景移除算法
BufferedImage transparentImage = createTransparentBackgroundImage(originalImage);
// 将处理后的图片绘制到RGBA图像上
g2d.drawImage(transparentImage, 0, 0, null);
// 验证透明像素数量
int transparentCount = 0;
for (int y = 0; y < height; y += Math.max(1, height / 50)) {
for (int x = 0; x < width; x += Math.max(1, width / 50)) {
int rgb = rgbaImage.getRGB(x, y);
int alpha = (rgb >> 24) & 0xFF;
if (alpha == 0) {
transparentCount++;
}
}
}
log.info("智能背景移除完成,采样透明像素比例: {:.2f}%",
(double) transparentCount / ((width / Math.max(1, width / 50)) * (height / Math.max(1, height / 50))) * 100);
} else {
// 原始图片已有透明度,直接绘制
g2d.drawImage(originalImage, 0, 0, null);
}
g2d.dispose();
// 4. 深度验证RGBA图像
boolean hasTransparency = rgbaImage.getColorModel().hasAlpha();
log.info("RGBA图像是否支持透明度: {}", hasTransparency);
// 详细分析透明度
analyzeTransparency(rgbaImage);
analyzeColorMode(rgbaImage);
// 5. 转换为PNG格式字节数组
ByteArrayOutputStream baos = new ByteArrayOutputStream();
boolean success = ImageIO.write(rgbaImage, "PNG", baos);
if (success) {
byte[] imageBytes = baos.toByteArray();
log.info("高级图片转换成功,大小: {} bytes", imageBytes.length);
// 验证PNG文件头
if (imageBytes.length >= 8) {
boolean isPng = (imageBytes[0] == (byte) 0x89 && imageBytes[1] == 0x50 &&
imageBytes[2] == 0x4E && imageBytes[3] == 0x47);
log.info("输出文件是否为PNG格式: {}", isPng);
// 检查PNG是否包含透明度信息
if (imageBytes.length > 25) {
// 检查IHDR chunk中的颜色类型
boolean hasAlphaChannel = false;
for (int i = 0; i < imageBytes.length - 25; i++) {
if (imageBytes[i] == 'I' && imageBytes[i+1] == 'H' &&
imageBytes[i+2] == 'D' && imageBytes[i+3] == 'R') {
// 颜色类型在第25个字节
int colorType = imageBytes[i + 25] & 0xFF;
hasAlphaChannel = (colorType == 4 || colorType == 6);
log.info("PNG颜色类型: {}, 是否包含Alpha通道: {}", colorType, hasAlphaChannel);
break;
}
}
}
}
return imageBytes;
} else {
log.error("高级图片转换失败");
return null;
}
} catch (Exception e) {
log.error("高级图片转换失败", e);
return null;
}
}
/**
* 下载图片
*
* @param imageUrl 图片URL
* @return BufferedImage对象
*/
private BufferedImage downloadImage(String imageUrl) {
try {
URL url = new URL(imageUrl);
return ImageIO.read(url);
} catch (Exception e) {
log.error("下载图片失败: {}", imageUrl, e);
return null;
}
}
/**
* 获取图片类型名称
*
* @param type 图片类型
* @return 类型名称
*/
private String getImageTypeName(int type) {
switch (type) {
case BufferedImage.TYPE_INT_RGB:
return "RGB";
case BufferedImage.TYPE_INT_ARGB:
return "ARGB";
case BufferedImage.TYPE_INT_ARGB_PRE:
return "ARGB_PRE";
case BufferedImage.TYPE_4BYTE_ABGR:
return "ABGR";
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
return "ABGR_PRE";
case BufferedImage.TYPE_3BYTE_BGR:
return "BGR";
case BufferedImage.TYPE_BYTE_GRAY:
return "GRAY";
case BufferedImage.TYPE_USHORT_555_RGB:
return "RGB_555";
case BufferedImage.TYPE_USHORT_565_RGB:
return "RGB_565";
default:
return "UNKNOWN";
}
}
/**
* 分析透明度
*
* @param image 图片对象
*/
private void analyzeTransparency(BufferedImage image) {
try {
int width = image.getWidth();
int height = image.getHeight();
boolean hasTransparency = image.getColorModel().hasAlpha();
log.info("是否支持透明度: {}", hasTransparency);
// 检查是否有透明像素
boolean hasTransparentPixels = false;
for (int y = 0; y < height; y += 10) { // 采样检查
for (int x = 0; x < width; x += 10) {
int rgb = image.getRGB(x, y);
int alpha = (rgb >> 24) & 0xFF;
if (alpha < 255) {
hasTransparentPixels = true;
break;
}
}
if (hasTransparentPixels) break;
}
log.info("是否包含透明像素: {}", hasTransparentPixels);
} catch (Exception e) {
log.error("分析透明度失败", e);
}
}
/**
* 分析颜色模式
*
* @param image 图片对象
*/
private void analyzeColorMode(BufferedImage image) {
try {
int width = image.getWidth();
int height = image.getHeight();
// 采样分析颜色
int sampleSize = Math.min(100, Math.min(width, height));
int stepX = width / sampleSize;
int stepY = height / sampleSize;
int transparentPixels = 0;
int opaquePixels = 0;
int totalPixels = 0;
for (int y = 0; y < height; y += stepY) {
for (int x = 0; x < width; x += stepX) {
int rgb = image.getRGB(x, y);
int alpha = (rgb >> 24) & 0xFF;
if (alpha == 0) {
transparentPixels++;
} else if (alpha == 255) {
opaquePixels++;
}
totalPixels++;
}
}
double transparentRatio = (double) transparentPixels / totalPixels;
double opaqueRatio = (double) opaquePixels / totalPixels;
log.info("透明度分析:");
log.info(" 透明像素比例: {:.2f}%", transparentRatio * 100);
log.info(" 不透明像素比例: {:.2f}%", opaqueRatio * 100);
log.info(" 半透明像素比例: {:.2f}%", (1 - transparentRatio - opaqueRatio) * 100);
} catch (Exception e) {
log.error("分析颜色模式失败", e);
}
}
/**
* 检查图片是否包含透明像素
*
* @param image 图片对象
* @return 是否包含透明像素
*/
private boolean hasTransparentPixels(BufferedImage image) {
if (!image.getColorModel().hasAlpha()) {
return false;
}
int width = image.getWidth();
int height = image.getHeight();
int sampleSize = Math.min(1000, width * height);
for (int i = 0; i < sampleSize; i++) {
int x = (i * width) / sampleSize;
int y = (i * height) / sampleSize;
int rgb = image.getRGB(x, y);
int alpha = (rgb >> 24) & 0xFF;
if (alpha < 255) {
return true;
}
}
return false;
}
/**
* 创建带透明背景的图片(专门处理无透明背景的图片)
* 使用高级背景移除算法,确保彻底移除背景
*
* @param originalImage 原始图片
* @return 带透明背景的图片
*/
private BufferedImage createTransparentBackgroundImage(BufferedImage originalImage) {
int width = originalImage.getWidth();
int height = originalImage.getHeight();
log.info("开始高级背景移除处理,图片尺寸: {}x{}", width, height);
// 创建RGBA图像
BufferedImage transparentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// 第一步:分析图片,找到主体区域
int[] edgePixels = findEdgePixels(originalImage);
int leftEdge = edgePixels[0];
int rightEdge = edgePixels[1];
int topEdge = edgePixels[2];
int bottomEdge = edgePixels[3];
log.info("检测到主体区域: 左={}, 右={}, 上={}, 下={}", leftEdge, rightEdge, topEdge, bottomEdge);
// 第二步:分析背景颜色特征
Color[] backgroundColors = analyzeBackgroundColors(originalImage, edgePixels);
log.info("分析到背景颜色数量: {}", backgroundColors.length);
// 第三步:高级背景移除处理
int transparentCount = 0;
int subjectCount = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = originalImage.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
// 多重判断是否为主体像素
boolean isSubject = isSubjectPixel(x, y, r, g, b, edgePixels, backgroundColors, originalImage);
if (isSubject) {
// 主体区域:保持原色,但可能进行边缘羽化
int alpha = calculateEdgeAlpha(x, y, edgePixels, width, height);
transparentImage.setRGB(x, y, (alpha << 24) | (rgb & 0xFFFFFF));
subjectCount++;
} else {
// 背景区域:设为透明
transparentImage.setRGB(x, y, 0x00000000);
transparentCount++;
}
}
}
log.info("背景移除完成 - 主体像素: {}, 透明像素: {}, 主体比例: {:.2f}%",
subjectCount, transparentCount, (double) subjectCount / (width * height) * 100);
// 第四步:后处理优化
BufferedImage optimizedImage = postProcessTransparentImage(transparentImage, originalImage);
return optimizedImage;
}
/**
* 分析背景颜色特征
*
* @param image 图片对象
* @param edgePixels 主体边缘坐标
* @return 背景颜色数组
*/
private Color[] analyzeBackgroundColors(BufferedImage image, int[] edgePixels) {
int width = image.getWidth();
int height = image.getHeight();
int leftEdge = edgePixels[0];
int rightEdge = edgePixels[1];
int topEdge = edgePixels[2];
int bottomEdge = edgePixels[3];
List<Color> backgroundColors = new ArrayList<>();
// 采样边缘区域的背景颜色
int sampleSize = 100;
for (int i = 0; i < sampleSize; i++) {
int x, y;
// 随机选择边缘区域外的像素
if (i % 4 == 0) {
// 左侧边缘
x = (int) (Math.random() * Math.max(1, leftEdge));
y = (int) (Math.random() * height);
} else if (i % 4 == 1) {
// 右侧边缘
x = rightEdge + (int) (Math.random() * Math.max(1, width - rightEdge));
y = (int) (Math.random() * height);
} else if (i % 4 == 2) {
// 上侧边缘
x = (int) (Math.random() * width);
y = (int) (Math.random() * Math.max(1, topEdge));
} else {
// 下侧边缘
x = (int) (Math.random() * width);
y = bottomEdge + (int) (Math.random() * Math.max(1, height - bottomEdge));
}
if (x >= 0 && x < width && y >= 0 && y < height) {
int rgb = image.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
backgroundColors.add(new Color(r, g, b));
}
}
return backgroundColors.toArray(new Color[0]);
}
/**
* 判断像素是否为主体像素(多重条件判断)
*
* @param x 像素X坐标
* @param y 像素Y坐标
* @param r 红色值
* @param g 绿色值
* @param b 蓝色值
* @param edgePixels 主体边缘坐标
* @param backgroundColors 背景颜色数组
* @param image 原始图片
* @return 是否为主体像素
*/
private boolean isSubjectPixel(int x, int y, int r, int g, int b, int[] edgePixels,
Color[] backgroundColors, BufferedImage image) {
int leftEdge = edgePixels[0];
int rightEdge = edgePixels[1];
int topEdge = edgePixels[2];
int bottomEdge = edgePixels[3];
// 条件1位置判断 - 必须在主体区域内
boolean inSubjectArea = (x >= leftEdge && x <= rightEdge && y >= topEdge && y <= bottomEdge);
if (!inSubjectArea) {
return false;
}
// 条件2颜色判断 - 不能是明显的背景色
boolean isBackgroundColor = false;
for (Color bgColor : backgroundColors) {
int colorDiff = Math.abs(r - bgColor.getRed()) +
Math.abs(g - bgColor.getGreen()) +
Math.abs(b - bgColor.getBlue());
if (colorDiff < 30) { // 颜色相似度阈值
isBackgroundColor = true;
break;
}
}
// 条件3亮度判断 - 不能是过亮或过暗的背景色
int brightness = (r + g + b) / 3;
boolean isExtremeBrightness = brightness > 240 || brightness < 20;
// 条件4颜色均匀性判断 - 不能是过于均匀的颜色
boolean isUniformColor = Math.abs(r - g) < 10 && Math.abs(g - b) < 10 && Math.abs(r - b) < 10;
// 条件5边缘检测 - 检查周围像素的变化
boolean hasColorVariation = hasSignificantColorVariation(image, x, y);
// 综合判断:必须满足主体区域条件,且不满足背景特征
return inSubjectArea && !isBackgroundColor && !isExtremeBrightness &&
(!isUniformColor || hasColorVariation);
}
/**
* 检查像素周围是否有显著的颜色变化
*
* @param image 图片对象
* @param x 像素X坐标
* @param y 像素Y坐标
* @return 是否有显著颜色变化
*/
private boolean hasSignificantColorVariation(BufferedImage image, int x, int y) {
int width = image.getWidth();
int height = image.getHeight();
int centerRgb = image.getRGB(x, y);
int centerR = (centerRgb >> 16) & 0xFF;
int centerG = (centerRgb >> 8) & 0xFF;
int centerB = centerRgb & 0xFF;
int variationCount = 0;
int totalSamples = 0;
// 检查周围8个像素
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (dx == 0 && dy == 0) continue;
int nx = x + dx;
int ny = y + dy;
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
int neighborRgb = image.getRGB(nx, ny);
int neighborR = (neighborRgb >> 16) & 0xFF;
int neighborG = (neighborRgb >> 8) & 0xFF;
int neighborB = neighborRgb & 0xFF;
int colorDiff = Math.abs(centerR - neighborR) +
Math.abs(centerG - neighborG) +
Math.abs(centerB - neighborB);
if (colorDiff > 30) { // 颜色差异阈值
variationCount++;
}
totalSamples++;
}
}
}
return totalSamples > 0 && (double) variationCount / totalSamples > 0.3;
}
/**
* 计算边缘像素的Alpha值羽化效果
*
* @param x 像素X坐标
* @param y 像素Y坐标
* @param edgePixels 主体边缘坐标
* @param width 图片宽度
* @param height 图片高度
* @return Alpha值
*/
private int calculateEdgeAlpha(int x, int y, int[] edgePixels, int width, int height) {
int leftEdge = edgePixels[0];
int rightEdge = edgePixels[1];
int topEdge = edgePixels[2];
int bottomEdge = edgePixels[3];
// 计算到边缘的距离
int distToLeft = x - leftEdge;
int distToRight = rightEdge - x;
int distToTop = y - topEdge;
int distToBottom = bottomEdge - y;
int minDist = Math.min(Math.min(distToLeft, distToRight), Math.min(distToTop, distToBottom));
// 如果距离边缘很近,进行羽化处理
if (minDist <= 3) {
return Math.max(128, 255 - (3 - minDist) * 40); // 边缘羽化
}
return 255; // 完全不透明
}
/**
* 后处理透明图片,进一步优化背景移除效果
*
* @param transparentImage 初步处理的透明图片
* @param originalImage 原始图片
* @return 优化后的透明图片
*/
private BufferedImage postProcessTransparentImage(BufferedImage transparentImage, BufferedImage originalImage) {
int width = transparentImage.getWidth();
int height = transparentImage.getHeight();
log.info("开始后处理优化,进一步清理残留背景");
BufferedImage optimizedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
// 复制透明图片
Graphics2D g2d = optimizedImage.createGraphics();
g2d.drawImage(transparentImage, 0, 0, null);
g2d.dispose();
// 多轮清理残留背景
for (int round = 1; round <= 3; round++) {
log.info("执行第{}轮背景清理", round);
int cleanedPixels = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = optimizedImage.getRGB(x, y);
int alpha = (rgb >> 24) & 0xFF;
// 只处理非透明像素
if (alpha > 0) {
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
// 检查是否应该被清理
if (shouldCleanPixel(x, y, r, g, b, optimizedImage, round)) {
optimizedImage.setRGB(x, y, 0x00000000); // 设为透明
cleanedPixels++;
}
}
}
}
log.info("第{}轮清理完成,清理了{}个像素", round, cleanedPixels);
// 如果清理的像素很少,说明已经比较干净了
if (cleanedPixels < 100) {
break;
}
}
// 最终验证透明像素比例
int finalTransparentCount = 0;
for (int y = 0; y < height; y += Math.max(1, height / 50)) {
for (int x = 0; x < width; x += Math.max(1, width / 50)) {
int rgb = optimizedImage.getRGB(x, y);
int alpha = (rgb >> 24) & 0xFF;
if (alpha == 0) {
finalTransparentCount++;
}
}
}
log.info("后处理完成,最终透明像素比例: {:.2f}%",
(double) finalTransparentCount / ((width / Math.max(1, width / 50)) * (height / Math.max(1, height / 50))) * 100);
return optimizedImage;
}
/**
* 判断像素是否应该被清理
*
* @param x 像素X坐标
* @param y 像素Y坐标
* @param r 红色值
* @param g 绿色值
* @param b 蓝色值
* @param image 当前图片
* @param round 清理轮次
* @return 是否应该清理
*/
private boolean shouldCleanPixel(int x, int y, int r, int g, int b, BufferedImage image, int round) {
int width = image.getWidth();
int height = image.getHeight();
// 检查周围透明像素的比例
int transparentNeighbors = 0;
int totalNeighbors = 0;
for (int dy = -2; dy <= 2; dy++) {
for (int dx = -2; dx <= 2; dx++) {
if (dx == 0 && dy == 0) continue;
int nx = x + dx;
int ny = y + dy;
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
int neighborRgb = image.getRGB(nx, ny);
int neighborAlpha = (neighborRgb >> 24) & 0xFF;
if (neighborAlpha == 0) {
transparentNeighbors++;
}
totalNeighbors++;
}
}
}
double transparentRatio = totalNeighbors > 0 ? (double) transparentNeighbors / totalNeighbors : 0;
// 根据轮次调整清理策略
double threshold = 0.6 - (round - 1) * 0.1; // 逐渐降低阈值
// 如果周围透明像素比例很高,且当前像素颜色特征像背景,则清理
if (transparentRatio > threshold) {
// 检查颜色特征
int brightness = (r + g + b) / 3;
boolean isUniformColor = Math.abs(r - g) < 15 && Math.abs(g - b) < 15 && Math.abs(r - b) < 15;
boolean isExtremeBrightness = brightness > 220 || brightness < 30;
return isUniformColor || isExtremeBrightness;
}
return false;
}
/**
* 找到图片的主体边缘
*
* @param image 图片对象
* @return [left, right, top, bottom] 边缘坐标
*/
private int[] findEdgePixels(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
int leftEdge = width;
int rightEdge = 0;
int topEdge = height;
int bottomEdge = 0;
// 扫描图片找到非背景像素的边界
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int rgb = image.getRGB(x, y);
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
// 判断是否为主体像素(非白色/浅色背景)
boolean isSubject = !(r > 240 && g > 240 && b > 240);
if (isSubject) {
if (x < leftEdge) leftEdge = x;
if (x > rightEdge) rightEdge = x;
if (y < topEdge) topEdge = y;
if (y > bottomEdge) bottomEdge = y;
}
}
}
// 确保边界有效
leftEdge = Math.max(0, leftEdge - 5); // 留一些边距
rightEdge = Math.min(width - 1, rightEdge + 5);
topEdge = Math.max(0, topEdge - 5);
bottomEdge = Math.min(height - 1, bottomEdge + 5);
return new int[]{leftEdge, rightEdge, topEdge, bottomEdge};
}
}