/*******************************************************************************
* Copyright 2017 Ivan Shubin http://galenframework.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.galenframework.rainbow4j;
import com.galenframework.rainbow4j.filters.ImageFilter;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
public class ImageHandler {
private ByteBuffer bytes;
private int width;
private int height;
public final static int BLOCK_SIZE = 4;
public ImageHandler(BufferedImage image) {
this.bytes = readRgbModelFrom(image);
this.width = image.getWidth();
this.height = image.getHeight();
}
public ImageHandler(int width, int height) {
this.width = width;
this.height = height;
this.bytes = ByteBuffer.allocateDirect(width * height * BLOCK_SIZE);
}
private static ByteBuffer readRgbModelFrom(BufferedImage image) {
int w = image.getWidth();
int h = image.getHeight();
int[] pixels = new int[w * h];
image.getRGB(0, 0, w, h, pixels, 0, w);
// use direct byte buffer to resolve heap errors here
ByteBuffer rgbBytes = ByteBuffer.allocateDirect(w * h * BLOCK_SIZE);
for (int r = 0; r < h; r++) {
for (int c = 0; c < w; c++) {
int index = r * w + c;
int indexRgb = r * w * BLOCK_SIZE + c * BLOCK_SIZE;
rgbBytes.put(indexRgb, (byte) ((pixels[index] >> 16) & 0xff));
rgbBytes.put(indexRgb + 1, (byte) ((pixels[index] >> 8) & 0xff));
rgbBytes.put(indexRgb + 2, (byte) (pixels[index] & 0xff));
rgbBytes.put(indexRgb + 3, (byte) ((pixels[index] >> 24) & 0xff));
}
}
return rgbBytes;
}
public Color pickColor(int x, int y) {
if (x < width && y < height && x >= 0 && y >= 0) {
int k = y * width * BLOCK_SIZE + x * BLOCK_SIZE;
return new Color(bytes.get(k) & 0xff,
bytes.get(k + 1) & 0xff,
bytes.get(k + 2) & 0xff,
bytes.get(k + 3) & 0xff
);
} else {
return new Color(0, 0, 0);
}
}
public static long colorDiff(Color left, Color right) {
if (left.getAlpha() > 128 && right.getAlpha() > 128) {
return Math.abs(left.getRed() - right.getRed())
+ Math.abs(left.getGreen() - right.getGreen())
+ Math.abs(left.getBlue() - right.getBlue());
} else {
return 0L;
}
}
public BufferedImage getImage() {
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
for (int r = 0; r < height; r++) {
for (int c = 0; c < width; c++) {
int index = r * width * BLOCK_SIZE + c * BLOCK_SIZE;
int red = bytes.get(index) & 0xFF;
int green = bytes.get(index + 1) & 0xFF;
int blue = bytes.get(index + 2) & 0xFF;
int alpha = bytes.get(index + 3) & 0xFF;
int rgb = (alpha << 24) | (red << 16) | (green << 8) | blue;
image.setRGB(c, r, rgb);
}
}
return image;
}
public void applyFilter(ImageFilter filter, Rectangle area) {
filter.apply(this.bytes, width, height, area);
}
public void setRGBA(int x, int y, int r, int g, int b, int a) {
int k = y * width * BLOCK_SIZE + x * BLOCK_SIZE;
bytes.put(k, (byte) (r & 0xff));
bytes.put(k + 1, (byte) (g & 0xff));
bytes.put(k + 2, (byte) (b & 0xff));
bytes.put(k + 3, (byte) (a & 0xff));
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
public ByteBuffer getBytes() {
return bytes;
}
public void applyFilter(ImageFilter filter) {
this.applyFilter(filter, new Rectangle(0, 0, width, height));
}
}