2010-07-11 4 views
0

У меня есть лист спрайтов, который имеет каждое изображение, центрированное в ячейке 32x32. Фактические изображения не 32x32, но немного меньше. Я бы хотел сделать ячейку и обрезать прозрачные пиксели, чтобы изображение было как можно меньше.Обрезать изображение до наименьшего размера, удалив прозрачные пиксели в java

Как бы это сделать на Java (JDK 6)?

Вот пример того, как я в настоящее время ломки плитки листа в клетки:

BufferedImage tilesheet = ImageIO.read(getClass().getResourceAsStream("/sheet.png"); 
for (int i = 0; i < 15; i++) { 
    Image img = tilesheet.getSubimage(i * 32, 0, 32, 32); 
    // crop here.. 
} 

Моя текущая идея состояла в том, чтобы проверить каждый пиксель от центра работает свой путь, чтобы увидеть, если она прозрачна , но мне было интересно, будет ли ускоренный/более чистый способ сделать это.

ответ

2

Я думаю, что это именно то, что вам нужно сделать, пройти через массив пикселей, проверить альфа, а затем выбросить. Хотя, когда вы, например, имели бы звездную форму, он не будет изменять размер изображения, чтобы быть меньшим, зная об этом.

+0

Да я знаю, о чем-то, как звезда, а не изменение размеров много. Я надеялся, что есть какой-то встроенный фильтр. – Ruggs

0

Если ваш лист уже имеет прозрачные пиксели, то BufferedImage также будет возвращен getSubimage(). По умолчанию Graphics2Dcomposite rule - AlphaComposite.SRC_OVER, что должно быть достаточным для drawImage().

Если суб-изображения имеют особый цвет фона, используйте LookupOp с четырехкомпонентным LookupTable, который устанавливает альфа-компонент в ноль для цветов, соответствующих фону.

Я бы прошел через растровый пиксель только в крайнем случае.

Приложение: дополнительные прозрачные пиксели могут мешать обнаружению столкновений и т. Д. Для их обрезки потребуется непосредственно работать с WritableRaster. Вместо того, чтобы работать из центра, я бы начал с границ, используя пару методов getPixels()/setPixels(), которые могут изменять строку или столбец за раз. Если целая строка или столбец имеет нулевую альфу, отметьте ее для устранения, когда вы позже получите суб-изображение.

+0

У меня нет проблем с настройкой прозрачных пикселей, как вы сказали, что подобъект уже делает это. Я хочу сделать это, чтобы обрезать/обрезать изображение, чтобы обрезать как можно больше окружающих прозрачных пикселей. Мои ячейки 32x32, однако много раз изображение, которое меня интересует, это только что-то вроде 14x28. – Ruggs

3

Этот код работает для меня. Алгоритм прост, он итерации слева/сверху/справа/снизу изображения и находит первый пиксель в столбце/строке, который не является прозрачным. Затем он запоминает новый угол обрезанного изображения и, наконец, возвращает вспомогательное изображение исходного изображения.

Есть вещи, которые можно улучшить.

  1. Алгоритм ожидает, что в данных есть альфа-байт. Он не сработает с индексом из исключения массива, если его нет.

  2. Алгоритм ожидает, что на картинке имеется по крайней мере один непрозрачный пиксель. Если изображение полностью прозрачно, оно не будет выполнено.

    private static BufferedImage trimImage(BufferedImage img) { 
    final byte[] pixels = ((DataBufferByte) img.getRaster().getDataBuffer()).getData(); 
    int width = img.getWidth(); 
    int height = img.getHeight(); 
    int x0, y0, x1, y1;      // the new corners of the trimmed image 
    int i, j;        // i - horizontal iterator; j - vertical iterator 
    leftLoop: 
    for (i = 0; i < width; i++) { 
        for (j = 0; j < height; j++) { 
         if (pixels[(j*width+i)*4] != 0) { // alpha is the very first byte and then every fourth one 
          break leftLoop; 
         } 
        } 
    } 
    x0 = i; 
    topLoop: 
    for (j = 0; j < height; j++) { 
        for (i = 0; i < width; i++) { 
         if (pixels[(j*width+i)*4] != 0) { 
          break topLoop; 
         } 
        } 
    } 
    y0 = j; 
    rightLoop: 
    for (i = width-1; i >= 0; i--) { 
        for (j = 0; j < height; j++) { 
         if (pixels[(j*width+i)*4] != 0) { 
          break rightLoop; 
         } 
        } 
    } 
    x1 = i+1; 
    bottomLoop: 
    for (j = height-1; j >= 0; j--) { 
        for (i = 0; i < width; i++) { 
         if (pixels[(j*width+i)*4] != 0) { 
          break bottomLoop; 
         } 
        } 
    } 
    y1 = j+1; 
    return img.getSubimage(x0, y0, x1-x0, y1-y0); 
    

    }

4

image on transparent background

Там в тривиальное решение - для сканирования каждого пикселя. Алгоритм ниже имеет постоянную производительность O(w•h).

private static BufferedImage trimImage(BufferedImage image) { 
    int width = image.getWidth(); 
    int height = image.getHeight(); 
    int top = height/2; 
    int bottom = top; 
    int left = width/2 ; 
    int right = left; 
    for (int x = 0; x < width; x++) { 
     for (int y = 0; y < height; y++) { 
      if (image.getRGB(x, y) != 0){ 
       top = Math.min(top, x); 
       bottom = Math.max(bottom, x); 
       left = Math.min(left, x); 
       right = Math.max(right, x); 
      } 
     } 
    } 
    return image.getSubimage(left, top, right - left, bottom - top); 
} 

Но это гораздо более эффективным:

private static BufferedImage trimImage(BufferedImage image) { 
    WritableRaster raster = image.getAlphaRaster(); 
    int width = raster.getWidth(); 
    int height = raster.getHeight(); 
    int left = 0; 
    int top = 0; 
    int right = width - 1; 
    int bottom = height - 1; 
    int minRight = width - 1; 
    int minBottom = height - 1; 

    top: 
    for (;top < bottom; top++){ 
     for (int x = 0; x < width; x++){ 
      if (raster.getSample(x, top, 0) != 0){ 
       minRight = x; 
       minBottom = top; 
       break top; 
      } 
     } 
    } 

    left: 
    for (;left < minRight; left++){ 
     for (int y = height - 1; y > top; y--){ 
      if (raster.getSample(left, y, 0) != 0){ 
       minBottom = y; 
       break left; 
      } 
     } 
    } 

    bottom: 
    for (;bottom > minBottom; bottom--){ 
     for (int x = width - 1; x >= left; x--){ 
      if (raster.getSample(x, bottom, 0) != 0){ 
       minRight = x; 
       break bottom; 
      } 
     } 
    } 

    right: 
    for (;right > minRight; right--){ 
     for (int y = bottom; y >= top; y--){ 
      if (raster.getSample(right, y, 0) != 0){ 
       break right; 
      } 
     } 
    } 

    return image.getSubimage(left, top, right - left + 1, bottom - top + 1); 
} 

Этот алгоритм следует идеи из ЭЧАЯ ответа «s (смотри выше) и составляет от 2 до 4 раз эффективнее. Разница в том, что он никогда не сканирует пиксель дважды и пытается заключить диапазон поиска на каждом этапе. производительность

Method в худшем случае O(w•h–a•b)

+0

Это действительно помогло мне. Я портировал его на Android для работы с «Bitmap» на белом фоне. Я бы разместил здесь, если кому-то интересно. – Thomas

+0

@Thomas, пожалуйста, напишите свое решение, я бы хотел его проверить. благодаря – prom85

 Смежные вопросы

  • Нет связанных вопросов^_^