Skip to content

Instantly share code, notes, and snippets.

@nrrb
Created May 29, 2011 03:30
Show Gist options
  • Save nrrb/997439 to your computer and use it in GitHub Desktop.
Save nrrb/997439 to your computer and use it in GitHub Desktop.
Subtiling a rectangle with an image, preserving image ratio (language: Processing)
/***********************************************************************************
***********************************************************************************
**
** Author: Nick Bennett
** Email: nick@tothebe.at
** Github: https://github.com/tothebeat
**
** Image Tile - Arbitrary File
** Date Created: October 16, 2009
**
** Load any BMP/JPG/PNG/GIF file on your computer to be tiled in the
** drawing area.
**
** Keys:
** l/L - Load a new image file.
** s/S - Save the tiled image to a file on your computer.
** q/Q - Quit (Useless with web applet version).
**
**
***********************************************************************************
***********************************************************************************/
boolean DEBUG = false;
TiledImage tile;
TiledImage tile2, tile3, tile4;
int drawing_mode = 0;
void setup() {
size(512, 512, P2D);
// size(screen.width, screen.height, P2D);
background(0);
LoadTiledImage();
}
void draw() {
}
void keyTyped() {
switch(key) {
case 'l' :
case 'L' :
LoadTiledImage();
break;
case 's' :
case 'S' :
SaveTiledImage();
break;
case 'q' :
case 'Q' :
exit();
break;
}
}
void mouseClicked() {
drawing_mode = (drawing_mode + 1) % 2;
}
void mouseMoved() {
if(tile != null) {
tile.SetDisplaySize(mouseX, mouseY);
// 2 - Lower Left Quadrant
tile2.SetDisplaySize(mouseX, height - mouseY);
// 3 - Upper Right Quadrant
tile3.SetDisplaySize(width - mouseX, mouseY);
// 4 - Lower Right Quadrant
tile4.SetDisplaySize(width - mouseX, height - mouseY);
if(drawing_mode == 0) {
tile.Draw(0, 0);
tile2.Draw(0, mouseY);
tile3.Draw(mouseX, 0);
tile4.Draw(mouseX, mouseY);
}
else {
tile.DrawTileBoxes(0, 0);
tile2.DrawTileBoxes(0, mouseY);
tile3.DrawTileBoxes(mouseX, 0);
tile4.DrawTileBoxes(mouseX, mouseY);
}
}
}
void SaveTiledImage() {
String savePath;
savePath = selectOutput();
if(savePath == null) {
if(DEBUG) println("No save location selected!");
return;
}
if(!IsImageFilename(savePath)) {
// Append a .PNG to the end of the filename, what are you going to do
// with the default image format of TIFF?
savePath = savePath + ".PNG";
}
saveFrame(savePath);
// saveFrame("TiledImage-" + day() + hour() + minute() + second() + ".jpg");
}
void LoadTiledImage() {
PImage img;
img = LoadImage();
if(img != null) {
tile = new TiledImage(img, width, height);
tile2 = new TiledImage(img);
tile3 = new TiledImage(img);
tile4 = new TiledImage(img);
// tile.Draw(0, 0);
tile.DrawTileBoxes(0, 0);
}
}
PImage LoadImage() {
PImage img;
String loadPath;
loadPath = selectInput();
if(loadPath == null) {
if(DEBUG) println("No path loaded!");
return null;
}
if(DEBUG) println("Selected " + loadPath);
if(!IsImageFilename(loadPath)) {
if(DEBUG) println("This doesn't look like a JPG file!");
return null;
}
img = loadImage(loadPath);
if(img == null) {
if(DEBUG) println("Unable to load image!");
return null;
}
return img;
}
boolean IsImageFilename(String filePath) {
filePath = filePath.toLowerCase();
if(filePath.indexOf(".jpg") >= 0) {
return true;
}
if(filePath.indexOf(".png") >= 0) {
return true;
}
if(filePath.indexOf(".gif") >= 0) {
return true;
}
if(filePath.indexOf(".bmp") >= 0) {
return true;
}
return false;
}
void DisplayImageCentered(PImage img) {
// background(AverageColor(img));
background(0);
float imgAspect = (float)img.width / (float)img.height;
int dispWidth, dispHeight, x, y;
if(imgAspect > 1) {
dispWidth = width;
dispHeight = int((float)dispWidth / imgAspect);
x = 0;
y = (height - dispHeight) / 2;
}
else {
dispHeight = height;
dispWidth = int((float)dispHeight * imgAspect);
x = (width - dispWidth) / 2;
y = 0;
}
image(img, x, y, dispWidth, dispHeight);
}
color AverageColor(PImage img) {
int redAccumulate = 0;
int greenAccumulate = 0;
int blueAccumulate = 0;
img.loadPixels();
int pixelBufferSize = img.width * img.height;
for(int i = 0; i < pixelBufferSize; i++) {
color c = img.pixels[i];
redAccumulate += red(c);
greenAccumulate += green(c);
blueAccumulate += blue(c);
}
redAccumulate /= pixelBufferSize;
greenAccumulate /= pixelBufferSize;
blueAccumulate /= pixelBufferSize;
color avgColor = color(redAccumulate, greenAccumulate, blueAccumulate);
return avgColor;
}
class TiledImage {
PImage img;
float image_width;
float image_height;
private float display_width = 0;
private float display_height = 0;
float min_tile_size = 5;
// This number tells how big the tile
// coordinate arrays start out and how
// to increment them when they need more space
private int array_increment = 40;
private float[] tile_x;
private float[] tile_y;
private float[] tile_w;
private float[] tile_h;
private int numTiles = 0;
TiledImage(PImage img_obj) {
img = img_obj;
image_width = img.width;
image_height = img.height;
}
TiledImage(PImage img_obj, float disp_width, float disp_height) {
img = img_obj;
image_width = img.width;
image_height = img.height;
display_width = disp_width;
display_height = disp_height;
this.GenerateTileCoordinates();
}
// Based on the image dimensions, the display dimensions, and
// the minimum tile size, this fills in the img_tile_coordinates
// array with the coordinates of the image tiles. All coordinates
// are relative to (0, 0) with the maximum extent being
// (display_width, display_height).
void GenerateTileCoordinates() {
numTiles = 0;
tile_x = new float[array_increment];
tile_y = new float[array_increment];
tile_w = new float[array_increment];
tile_h = new float[array_increment];
float rectCornerX = 0;
float rectCornerY = 0;
float rectWidth = display_width;
float rectHeight = display_height;
float tileWidth;
float tileHeight;
float rectAspect = rectWidth / rectHeight;
float tileAspect = image_width / image_height;
boolean widthConstrained = false;
boolean heightConstrained = false;
do{
if(tileAspect == rectAspect) {
// Trivial case
tileWidth = rectWidth;
tileHeight = rectHeight;
this.AddTileCoordinate(rectCornerX, rectCornerY, tileWidth, tileHeight);
rectWidth -= tileWidth;
rectHeight -= tileHeight;
rectAspect = rectWidth / rectHeight;
break;
}
else if(tileAspect > rectAspect) {
widthConstrained = true;
heightConstrained = false;
}
else if(tileAspect < rectAspect) {
widthConstrained = false;
heightConstrained = true;
}
if(widthConstrained) {
tileWidth = rectWidth;
tileHeight = tileWidth * (1.0 / tileAspect);
this.AddTileCoordinate(rectCornerX, rectCornerY, tileWidth, tileHeight);
rectCornerX = rectCornerX;
rectCornerY = rectCornerY + tileHeight;
rectWidth = rectWidth;
rectHeight = rectHeight - tileHeight;
rectAspect = rectWidth / rectHeight;
}
if(heightConstrained) {
tileHeight = rectHeight;
tileWidth = tileHeight * tileAspect;
this.AddTileCoordinate(rectCornerX, rectCornerY, tileWidth, tileHeight);
rectCornerX = rectCornerX + tileWidth;
rectCornerY = rectCornerY;
rectWidth = rectWidth - tileWidth;
rectHeight = rectHeight;
rectAspect = rectWidth / rectHeight;
}
}while((rectWidth >= min_tile_size) && (rectHeight >= min_tile_size));
}
void SetDisplaySize(float disp_width, float disp_height) {
display_width = disp_width;
display_height = disp_height;
this.GenerateTileCoordinates();
}
private void AddTileCoordinate(float x, float y, float w, float h) {
// All of the arrays have the same size; using the size of x is arbitrary
if(numTiles == tile_x.length) {
tile_x = expand(tile_x, tile_x.length + array_increment);
tile_y = expand(tile_y, tile_y.length + array_increment);
tile_w = expand(tile_w, tile_w.length + array_increment);
tile_h = expand(tile_h, tile_h.length + array_increment);
}
tile_x[numTiles] = x;
tile_y[numTiles] = y;
tile_w[numTiles] = w;
tile_h[numTiles] = h;
numTiles++;
}
float[] GetTileCoordinates(int numTile) {
if((numTile >= numTiles) || (numTile < 0)) {
return null;
}
float[] tile_coords = {tile_x[numTile], tile_y[numTile], tile_w[numTile], tile_h[numTile]};
return tile_coords;
}
// We don't want anything but internal functions touching the numTiles variable,
// we'll just give a get() accessor for this variable to outside peeps
int NumTiles() {
return numTiles;
}
void Draw(float x, float y) {
// Check first that the tile coordinates have been generated
if(tile_x == null) {
return;
}
for(int i = 0; i < this.numTiles; i++) {
image(this.img, tile_x[i] + x, tile_y[i] + y,
tile_w[i], tile_h[i]);
}
}
// Draws the tiled image to the window, with the upper left corner of the image
// at (x, y)
void DrawEvenly(float x, float y) {
// Check first that the tile coordinates have been generated
if(tile_x == null) {
return;
}
color average_color = this.AverageColor();
background(average_color);
for(int i = 0; i < this.numTiles; i++) {
image(this.img, tile_x[i] + x, tile_y[i] + y,
tile_w[i], tile_h[i]);
}
}
void DrawTileBoxes(float x, float y) {
if(tile_x == null) {
return;
}
// background(0);
fill(0);
stroke(255);
for(int i = 0; i < this.numTiles; i++) {
rect(tile_x[i] + x, tile_y[i] + y,
tile_w[i], tile_h[i]);
}
}
/*
// This returns a PImage object that contains the same as what is produced in
// Draw() but in an object rather than directly to the screen.
PImage Image() {
if(tile_x == null) {
return null;
}
PImage return_image = createImage((int)display_width, (int)display_height, RGB);
// Need to fill this part out, not sure how to draw images to other images
// rather than to the screen
}
*/
private color AverageColor() {
int redAccumulate = 0;
int greenAccumulate = 0;
int blueAccumulate = 0;
img.loadPixels();
int pixelBufferSize = img.width * img.height;
for(int i = 0; i < pixelBufferSize; i++) {
color c = img.pixels[i];
redAccumulate += red(c);
greenAccumulate += green(c);
blueAccumulate += blue(c);
}
redAccumulate /= pixelBufferSize;
greenAccumulate /= pixelBufferSize;
blueAccumulate /= pixelBufferSize;
color avgColor = color(redAccumulate, greenAccumulate, blueAccumulate);
return avgColor;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment