基於processing實現
**最終效果演示(已把自己臉馬賽克)**
繪製
貓貓
這是一個貓貓臉飾,繪製方法如下,eyes用於繪製眼睛,whisker用於繪製鬍鬚:
void eyes(){
float r1 = dist(mouseX+(0),mouseY+(-0),width/2-(40),height/2+(-0));
float si1 = (mouseX-(width/2-(40)))/r1;
float co1 = (mouseY-height/2+(0))/r1;
float r2 = dist(mouseX+(-0),mouseY+(-0),width/2+(40),height/2+(-0));
float si2 = (mouseX-(width/2+(40)))/r2;
float co2 = (mouseY-height/2+(-0))/r2;
if(abs(r1)<20){
ox1 = mouseX;
oy1 = mouseY;
}else{
ox1 = width/2+37*si1-(-80);
oy1 = height/2+17*co1+(-320);
}
if(abs(r2)<20){
ox2 = mouseX;
oy2 = mouseY;
}else{
ox2 = width/2+37*si2+(265);
oy2 = height/2+17*co2+(-320);
}
fill(254,237,185);
ellipse(width/2-(-80),height/2+(-320),110,80);
ellipse(width/2+(265),height/2+(-320),110,80);
fill(248,251,212);
ellipse(width/2-(-80),height/2+(-320),67,76);
ellipse(width/2+(264),height/2+(-320),67,77);
fill(253,253,250);
ellipse(width/2-(-54),height/2+(-336),36,32);
ellipse(width/2+(242),height/2+(-336),36,32);
fill(27,25,25);
if(pressed){
ellipse(ox1,oy1,9,49);
ellipse(ox2,oy2,9,49);
}
else{
ellipse(ox1,oy1,22,49);
ellipse(ox2,oy2,22,49);
}
}
void whisker(){
fill(254,254,252);
ellipse(width/2-(-19),height/2+(-194),156,7);
fill(254,254,252);
ellipse(width/2-(-19),height/2+(-165),156,7);
fill(254,254,252);
ellipse(width/2-(-19),height/2+(-140),156,7);
fill(254,254,252);
ellipse(width/2-(-315),height/2+(-194),156,7);
fill(254,254,252);
ellipse(width/2-(-315),height/2+(-165),156,7);
fill(254,254,252);
ellipse(width/2-(-315),height/2+(-140),156,7);
}
粒子
(壓縮成gif不知咋的鮮豔度降得這麼猛)
Particle類:
class Particle {
PVector location;
PVector velocity;
PVector acceleration;
float lifespan;
Particle(PVector l) {
// The acceleration
acceleration = new PVector(0, 0.05);
// circel's x and y ==> range
velocity = new PVector(random(-1, 1), random(-2, 0));
// apawn's position
location = l.copy();
// the circle life time
lifespan = 255.0;
}
void run() {
update();
display();
}
void update() {
velocity.add(acceleration);
location.add(velocity);
lifespan-=1.0;
}
boolean isDead() {
if (lifespan <= 0) {
return true;
} else {
return false;
}
}
void display() {
// border
//stroke(0, lifespan);
// border's weight
//strokeWeight(1);
float r = random(0,255);
float g = random(0,255);
float b = random(0,255);
// random the circle's color
fill(r,g,b, lifespan);
// draw circle
ellipse(location.x, location.y, 8, 8);
}
}
ParticleSystem管理類:
// A class to describe a group of Particles
// An ArrayList is used to manage the list of Particles
class ParticleSystem {
ArrayList<Particle> particles;
PVector origin;
ParticleSystem(PVector position) {
origin = position.copy();
particles = new ArrayList<Particle>();
}
void addParticle() {
particles.add(new Particle(origin));
}
void run() {
for (int i = particles.size()-1; i >= 0; i--) {
Particle p = particles.get(i);
p.run();
if (p.isDead()) {
particles.remove(i);
}
}
}
}
交互
鼠標(按鍵與滾輪)
繪製色塊
首先創建三個PImage對象,分別用於存放原圖像,色塊處理後圖像,與原圖像與色塊圖像混合的預覽圖像。
再setup中我們將colorImg與mixImg創建爲與rawImg相同大小的圖片數據進行對應。
PImage rawImg;
PImage colorImg;
PImage mixImg;
void setup(){
size(1000, 980);
pressed = false;
mouseSize = 50;
time =0;
auto = false;
change = false;
cat = false;
rawImg = loadImage("me.png");
pressedX = pressedY = 0;
releasedX = rawImg.width;
releasedY = rawImg.height;
colorImg = createImage(rawImg.width, rawImg.height, RGB);
colorImg.loadPixels();
mixImg = createImage(rawImg.width, rawImg.height, RGB);
mixImg.loadPixels();
noStroke();
}
再進行預覽時我們需要得到rawImg與colorImg的混合圖像,因爲定義update_mixImg,將鼠標所在位置的特定大小區域的原圖像與色塊圖像進行混合:
void update_mixImg(){
mixImg.set(0, 0, colorImg);
mixImg.set(mouseX-mouseSize/2,mouseY-mouseSize/2,rawImg.get(mouseX-mouseSize/2,mouseY-mouseSize/2,mouseSize,mouseSize));
}
在更新colorImg時,要根據選區內顏色的平均值進行色塊處理,因此需定義get_mean_image函數計算選取內的色彩平均值
color get_mean_image(int x,int y,int w,int h){
PImage meanImg;
meanImg = rawImg.get(x,y,w,h);
float meanR = 0.0f;
float meanG = 0.0f;
float meanB = 0.0f;
for(int i=x;i<=x+w;i++){
for(int j=y;j<=y+h;j++){
meanR += red(rawImg.get(i,j));
meanG += green(rawImg.get(i,j));
meanB += blue(rawImg.get(i,j));
}
}
meanR /= w*h;
meanG /= w*h;
meanB /= w*h;
return color(meanR,meanG,meanB);
}
在獲得色彩平均值後要對colorImg進行上色,這裏採用多PImage拼接的方法以實現此目的並封裝入update_colorImg函數:
void update_colorImg(int x,int y,int w,int h){
PImage meanImg;
meanImg = createImage(w, h, RGB);
color mean = get_mean_image(x,y,w,h);
for (int i = 0; i < meanImg.pixels.length; i++) {
meanImg.pixels[i] = mean;
}
colorImg.set(x,y,meanImg);
}
貓眼樣式改變
在鼠標點擊時貓眼的瞳孔會縮小,在鼠標移動時。貓眼睛也會跟着移動。
定義pressed全局變量用於記錄鼠標是否按下,在eyes()中根據是否點下繪製不同半徑橢圓:
boolean pressed;
void mousePressed(){
pressed = true;
}
void mouseReleased(){
pressed = false;
}
void eyes(){
……
fill(27,25,25);
if(pressed){
ellipse(ox1,oy1,9,49);
ellipse(ox2,oy2,9,49);
}
else{
ellipse(ox1,oy1,22,49);
ellipse(ox2,oy2,22,49);
}
}
下方按鈕
此處使用了controlP5,這是一個專門爲Processing提供的GUI庫,方便一些交互操作。
從左往右依次控制:色塊與預覽原圖區域大小,自動填充色快,重新選擇填充區域,貓貓臉飾顯示,粒子顯示。
其GUI實現代碼如下:
import controlP5.*;
ControlP5 cp5;
Slider2D s;
boolean auto,change,cat,particle,pressed;
void setup(){
size(1000, 980);
cp5 = new ControlP5(this);
……
auto = false;
change = false;
cat = false;
……
cp5.addSlider("mouseSize")
.setPosition(40,700)
.setRange(0,255)
.setSize(200,20)
;
cp5.addToggle("auto")
.setPosition(320,700)
.setSize(50,20)
.setMode(ControlP5.SWITCH)
;
cp5.addToggle("change")
.setPosition(400,700)
.setSize(50,20)
.setMode(ControlP5.SWITCH)
;
cp5.addToggle("cat")
.setPosition(480,700)
.setSize(50,20)
.setMode(ControlP5.SWITCH)
;
cp5.addToggle("particle")
.setPosition(560,700)
.setSize(50,20)
.setMode(ControlP5.SWITCH)
;
noStroke();
}
功能
自動填充
對其添加範圍內的隨機數以達到隨即填充目的
void draw(){
……
if(auto){
update_colorImg(random_width()-mouseSize/2,random_height()-mouseSize/2,mouseSize,mouseSize);
}
……
}
更改隨即填充範圍
當change此bool值爲true即下方選項選中時可以更改範圍,達到更改隨即填充位置目的
代碼如下:
void mousePressed(){
pressed = true;
if(change){
pressedX = mouseX;
pressedY = mouseY;
}
}
void mouseReleased(){
pressed = false;
if(change){
releasedX = mouseX;
releasedY = mouseY;
}
}