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:
- 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.
- 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.
- 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.
- 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.
- 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.
- I used constrains to limit variables for convenience/correct functioning.
- 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.
- 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; } }