IM-Shop 2015

Forget about Photoshop. The newest, bestest drawing software out there is none other than IM-Shop 2015! Create your own masterpieces  in this OPEN SOURCE SOFTWARE!!!1!! Key features include:

  • RGB color customization to create just the right color you have been dreaming about!
  • Ability to adjust alpha transparency for realistic translucent effects!
  • Wide range of brush sizes to allow for limitless creativity!
  • A total of TWO (!) brush shapes because we don’t discriminate people who love squares!
  • Adjustable brush speed to account for pro artists!
  • Never lose your work with the amazing “save :)” button!
  • A secret snake game built in for those gamer artists!
  • AND MORE…

I wanted to paint
Scott taught me Processing skills
Now I’m limitless

So you are still reading. How did this amazing piece of software come to be, you ask? The answer is PROCESSING! (AKA many variables many functions some arrays and almost 300 lines of code)

In a nutshell:

  1. Variables (like colors, alpha, brush size and speed) are stored and later controlled using if() and case() switch statements. The if() asks whether a key has been pressed or not. Then, the switch() case chooses the case that corresponds to the key being pressed. Each case would increase or decrease a certain variable by a small amount every frame, allowing the user to control them. Note that this is case sensitive: ‘r’ is not the same input as ‘R’. That is how pressing shift or not changes whether a color is added or substracted.
  2. Text. Text. Ugh. Creating a font looks straightforward until you try it. It shouldn’t be a problem if you don’t try renaming anything (because who cares about convenient names anyways). Just create a PFont with a name to store your font, and assign it an actual font using createFont(font name, size). Then just write stuff using testSize(), textFont() and text(string, x, y) to write stuff in the screen.
  3. The HUD. Just four rectangles. And text on top. For the color level displays, I just used rectangles whose R, G or B levels depended on the global variables chosen by the user (same for alpha). For the numerical displays, I just used the text() function but using the variables themselves instead of strings.
  4. Mouse buttons. Those were made, again, using basic shapes. To make them work, I used the mousePressed() function. Within it, I called several if() statements which would check the mouse coordinates at the moment of the click. If they all fell within the button’s range, the code would run.
  5. The round/square brush was a bit trickier than expected. Again, I used a switch() case within an if() (mainly in case I wanted to add more brush shapes, but a nested if() else would have been enough). However, I had to make sure the code would only run once (that is, switch brush type once instead of quickly alternating between both brushes while the spacebar was pressed). To do so, I called a mouseRelease() function and used it to control a variable previousBrush that checks if the brush has already changed or not.
  6. I used constrains to limit variables for convenience/correct functioning.
  7. To save masterpieces, I used the saveFrame() function. However, the tricky part was ensuring a different filename every save. To do so, I created a string that would be the file name using Processing’s day(), month(), year(), hour() and minute() functions.
  8. The last interesting tidbit was getting the snake to “die” once it touched itself or the edge od the screen. The latter was simple enough, but for the former I had to make use of arrays. I created two arrays: one to store the snake’s past x-coordinates and one to store its y-coordinates. That way, every frame I could check if the snake’s current coordinates matched any previous coordinates by iterating through the array.

Thank you for choosing Adobo Software. 😀

To see IM-Shop 2015 in action, please click here.

To download IM-Shop 2015, just copy the code below into Processing 3.0

int r = 0; //Red level
int g = 0; //Green level
int b = 0; //Blue level
int dead = 0; //For flashy screen :D
int alpha = 255; //Transparency
int step = 5; //Increment or decrement for RGBA levels
int brush = 0; //To store the brush shape
int prevBrush = 0; //To ensure brush switches happily
float sizeStep = 0.5; //Same as step but for brush size
float xPos = 800/2; 
float yPos = 600/2; //Brush x,y position
int speed = 2; //Brush speed duh
float size = 10; //Brush size
float timerStart = 0;
float timer = 0;
int snakeStart = 0;
int snakeXSpeed = 0;
int snakeYSpeed = -5;
int[] snakeX = {};
int[] snakeY = {};

PFont ComicSans;
String title = "IM-Shop 2015";
String filename = "Artwork_" + str(day()) + "." + str(month()) + "." + str(year()) + "_" + str(hour()) + "." + str(minute()) + "_######.png";
String snakename = "Snakework_" + str(day()) + "." + str(month()) + "." + str(year()) + "_" + str(hour()) + "." + str(minute()) + "_######.png";

void setup() {
  size(800, 600);
  frameRate(60);
  background(random(0, 255), random(0, 255), random(0, 255));
  ComicSans = createFont("Comic Sans MS", 24);
  textSize(24);
  textFont(ComicSans);
}

void draw() {
  if (dead == 0) {
    rectMode(CORNER);
    stroke(0);
    strokeWeight(3);
    fill(200);
    rect(0, 549, 800, 50);
    rect(0, 0, 100, 600);
    rect(699, 0, 100, 600);
    rect(0, 0, 800, 50); //Frames of the HUD
    noStroke();
    fill(0);
    textSize(24);
    text(title, 320, 35); //Title text
    //HUD text
    textSize(20);
    text("red (r)", 15, 100);
    text("green (g)", 8, 175);
    text("blue (b)", 15, 250);
    text("alpha (z)", 12, 325);
    text("size (q,e)", 708, 100);
    text("brush ( )", 708, 170);
    text("spd (1,2)", 708, 220);
    text("Move", 15, 420);
    text("with", 15, 445);
    text("WASD", 15, 470);
    text("(C) Adobo Corporation. Plz don't use/sue if you are epileptic.", 110, 580);
    text(size, 700, 135);
    text(r, 55, 135);
    text(g, 55, 210);
    text(b, 55, 285);
    text(alpha, 55, 360);
    text(speed, 720, 250);
    //RGB and alpha indicators
    stroke(0);
    strokeWeight(3);
    fill(r, 0, 0);
    rect(15, 110, 25, 25);
    fill(0, g, 0);
    rect(15, 185, 25, 25);
    fill(0, 0, b);
    rect(15, 260, 25, 25);
    fill(0, 0, 0, alpha);
    rect(15, 335, 25, 25);
    //Exit button
    fill(175, 0, 0);
    rect(758, 7, 35, 35);
    line(758, 7, 793, 42);
    line(758, 42, 793, 7);
    //Clear and save buttons
    fill(255);
    rect(7, 550, 75, 35);
    rect(707, 465, 75, 25);
    //Don't click button
    fill(0, 200, 0);
    rect(707, 500, 75, 75);
    fill(0);
    text("clear", 17, 575);
    text("save :)", 715, 484);
    text("Don't", 715, 525);
    text("click!!!", 715, 550);
    //Brush mode: circle or square. brush variable is controlled with spacebar
    switch (brush) {
    case 0:
      ellipseMode(CENTER);
      noStroke();
      fill(r, g, b, alpha);
      ellipse(xPos, yPos, size, size);
      fill(0);
      ellipse(735, 185, 20, 20);
      break;
    case 1:
      rectMode(CENTER);
      noStroke();
      fill(r, g, b, alpha);
      rect(xPos, yPos, size, size);
      fill(0);
      rect(735, 185, 20, 20);
      break;
    }
  }
  //Reading keyboard inputs
  if (keyPressed == true) {
    switch (key) {
    case 'w':
      if (dead == 0) {
        yPos -= speed;
      } else if (dead == 2 && snakeYSpeed != 5) {
        snakeXSpeed = 0;
        snakeYSpeed = -5;
      }
      break;
    case 's':
      if (dead == 0) {
        yPos += speed;
      } else if (dead == 2 && snakeYSpeed != -5) {
        snakeXSpeed = 0;
        snakeYSpeed = 5;
      }
      break;
    case 'a':
      if (dead == 0) {
        xPos -= speed;
      } else if (dead == 2 && snakeXSpeed != 5) {
        snakeXSpeed = -5;
        snakeYSpeed = 0;
      }
      break;
    case 'd':  
      if (dead == 0) {
        xPos += speed;
      } else if (dead == 2 && snakeXSpeed != -5) {
        snakeXSpeed = 5;
        snakeYSpeed = 0;
      }
      break;
    case 'R':
      r += step;
      break;
    case 'r':
      r -= step;
      break;
    case 'G':
      g += step;
      break;
    case 'g':
      g -= step;
      break;
    case 'B':
      b += step;
      break;
    case 'b':
      b -= step;
      break;
    case 'Z':
      alpha += step;
      break;
    case 'z':
      alpha -= step;
      break;
    case 'q':
      size -= sizeStep;
      break;
    case 'e':
      size += sizeStep;
      break;
    case '1':
      speed --;
      break;
    case '2':
      speed ++;
      break;
    case ' ': //Switching brush modes. prevBrush is important to make it switch once per press
      if (brush == 0 && prevBrush == brush) {
        brush = 1;
      } else if (brush == 1 && prevBrush == brush) {
        brush = 0;
      }
      break;
    }
    //Constrain variables: RGBA 0-255, size 1-75, speed 1-25 and x,yPos to the screen
    r = constrain(r, 0, 255);
    g = constrain(g, 0, 255);
    b = constrain(b, 0, 255);
    alpha = constrain(alpha, 0, 255);
    size = constrain(size, 1, 75);
    speed = constrain(speed, 1, 25);
    xPos = constrain(xPos, 0, 800);
    yPos = constrain(yPos, 0, 600);
  }
  //Code for flashy dead screen lol
  if (dead == 1) {
    timer = frameCount;
    background(random(0, 255), random(0, 255), random(0, 255)); //Random colors
    //Random text yay
    fill(0);
    textSize(random(10, 42));
    text("Y U DO DIS?", random (0, 650), random (50, 550));
    text("ded", random (0, 650), random (50, 550));
    text("ur a disgrace", random (0, 650), random (50, 550));
    text("IM rocks", random (0, 650), random (50, 550));
    text(":(", random (0, 650), random (50, 550));
    text("SPLAT SPLAT", random (0, 650), random (50, 550));
    text("do a barrel roll!", random (0, 650), random (50, 550));
    text("I'M DISAPPOINT", random (0, 650), random (50, 550));
    text("sanic speed", random (0, 650), random (50, 550));
    textSize(300);
    text(")':", 250, 400);
    if (timerStart + 900 <= timer) {
      dead = 2;
    }
  }
  if (dead == 2) {
    if (snakeStart == 0) {
      background(255);
      xPos = 400;
      yPos = 300;
      snakeStart = 1;
    }
    noStroke();
    fill(0);
    ellipse(xPos, yPos, 10, 10);
    xPos += snakeXSpeed;
    yPos += snakeYSpeed;
    for (int i = 0; i < snakeX.length; i++) {
      if ((xPos == snakeX[i] && yPos == snakeY[i]) || xPos == 0 || xPos == 799 || yPos == 0 || yPos == 599) {
        saveFrame(snakename);
        dead = 3;
      }
    }
    snakeX = append(snakeX, int(xPos));
    snakeY = append(snakeY, int(yPos));
  }
  if (dead == 3) {
    background(0);
    noStroke();
    fill(255);
    textSize(72);
    text("ded :(", 200, 200);
  }
}

//mousePressed() to read screen button inputs
void mousePressed() {
  if (mouseButton == LEFT) {
    if ((mouseX > 758) && (mouseX < 793) && (mouseY > 7) && (mouseY < 42) && (dead == 0)) {
      exit();
    }
    if ((mouseX > 7) && (mouseX < 82) && (mouseY > 550) && (mouseY < 585)) {
      clear();
      background(random(0, 255), random(0, 255), random(0, 255));
    }
    if ((mouseX > 707) && (mouseX < 782) && (mouseY > 500) && (mouseY < 575)) {
      dead = 1;
      timerStart = frameCount;
    }
    if ((mouseX > 707) && (mouseX < 782) && (mouseY > 465) && (mouseY < 490) && (dead == 0)) {
      print(filename);
      saveFrame(filename);
    }
  }
}

//keyReleased() for brush control
void keyReleased() {
  if (key == ' ') {
    prevBrush = brush;
  }
}

 

Leave a Reply