PImage a, b;
int offset = 0;
int imageSize = 400;
boolean running = false;
Subtitles s;
int playTime;
float imageY;
int totalLength = 10000;
void setup()
{
size(500,600);
//size(screenWidth,screenHeight,OPENGL);
//orientation(PORTRAIT);
a = loadImage("nightingale.jpg");
if (imageSize>width) imageSize=width;
a.resize(imageSize, imageSize);
background(0);
imageY = max((height-width)/3,0);
s = new Subtitles(totalLength);
b = createImage(imageSize, imageSize, RGB);
a.loadPixels();
b.loadPixels();
arrayCopy(a.pixels, b.pixels);
smooth();
playTime = millis();
}
void draw()
{
background(0);
if (running)
{
b.loadPixels();
float br = 0;
for (int i=0; i<b.pixels.length; i++)
{
if (b.pixels[i]==#FFFFFF) b.pixels[i]=#000000;
else if (b.pixels[i]==#000000) b.pixels[i]=#FFFFFF;
int pixelColor = b.pixels[i];
int b_r = (pixelColor >> 16) & 0xff;
int b_g = (pixelColor >> 8) & 0xff;
int b_b = pixelColor & 0xff;
br += 0.3*b_r+0.59*b_g+0.11*b_b;
}
float avg = br/b.pixels.length;
if (avg<150) b.filter(DILATE);
else b.filter(ERODE);
fastBlur(b, 1);
int p = (int)constrain(30-(frameCount-offset)/30, 6, 255);
b.filter(POSTERIZE, p);
image(b, 0, imageY, width, width);
}
else image(a, 0, imageY, width, width);
s.write(playTime,running);
if (((millis()-playTime)/1000)>totalLength)
{
running = false;
}
}
// Super Fast Blur v1.1 by Mario Klingemann
void fastBlur(PImage img, int radius) {
if (radius<1) {
return;
}
int w=img.width;
int h=img.height;
int wm=w-1;
int hm=h-1;
int wh=w*h;
int div=radius+radius+1;
int r[]=new int[wh];
int g[]=new int[wh];
int b[]=new int[wh];
int rsum, gsum, bsum, fx, fy, i, p, p1, p2, yp, yi, yw;
int vmin[] = new int[max(w, h)];
int vmax[] = new int[max(w, h)];
int[] pix=img.pixels;
int dv[]=new int[256*div];
for (i=0;i<256*div;i++) {
dv[i]=(i/div);
}
yw=yi=0;
for (fy=0;fy<h;fy++) {
rsum=gsum=bsum=0;
for (i=-radius;i<=radius;i++) {
p=pix[yi+min(wm, max(i, 0))];
rsum+=(p & 0xff0000)>>16;
gsum+=(p & 0x00ff00)>>8;
bsum+= p & 0x0000ff;
}
for (fx=0;fx<w;fx++) {
r[yi]=dv[rsum];
g[yi]=dv[gsum];
b[yi]=dv[bsum];
if (fy==0) {
vmin[fx]=min(fx+radius+1, wm);
vmax[fx]=max(fx-radius, 0);
}
p1=pix[yw+vmin[fx]];
p2=pix[yw+vmax[fx]];
rsum+=((p1 & 0xff0000)-(p2 & 0xff0000))>>16;
gsum+=((p1 & 0x00ff00)-(p2 & 0x00ff00))>>8;
bsum+= (p1 & 0x0000ff)-(p2 & 0x0000ff);
yi++;
}
yw+=w;
}
for (fx=0;fx<w;fx++) {
rsum=gsum=bsum=0;
yp=-radius*w;
for (i=-radius;i<=radius;i++) {
yi=max(0, yp)+fx;
rsum+=r[yi];
gsum+=g[yi];
bsum+=b[yi];
yp+=w;
}
yi=fx;
for (fy=0;fy<h;fy++) {
pix[yi]=0xff000000 | (dv[rsum]<<16) | (dv[gsum]<<8) | dv[bsum];
if (fx==0) {
vmin[fy]=min(fy+radius+1, hm)*w;
vmax[fy]=max(fy-radius, 0)*w;
}
p1=fx+vmin[fy];
p2=fx+vmax[fy];
rsum+=r[p1]-r[p2];
gsum+=g[p1]-g[p2];
bsum+=b[p1]-b[p2];
yi+=w;
}
}
}
void mousePressed()
{
if (mouseY>=s.boxY&&mouseY<=(s.boxY+s.boxHeight))
{
running = !running;
if (running)
{
a.loadPixels();
b.loadPixels();
arrayCopy(a.pixels, b.pixels);
b.updatePixels();
offset=frameCount;
playTime = millis();
background(0);
image(a, 0, imageY, width, width);
}
}
else if(mouseY>=imageY&&mouseY<=imageY+width) b.filter(INVERT);
}
class Subtitles
{
float boxX; // left edge of box; depends on screen size
float boxY;
float boxWidth;
float boxHeight;
PFont font;
int fontSize;
String title = "The Nightingale - tap to start";
String[] theText = {
"The Nightingale",
"And let those whose eyesight is dim",
"catch a nightingale",
"before the break of day",
"and take its gallbladder",
"and empty it,",
"and to it add one drop of dew",
"found on clean grass,",
"and then often anoint the eyelids",
"and lashes that are around the eye,",
"on retiring to bed",
"--and if it touches the eye inside a little",
"it does no harm--",
"and the dimness",
"will be marvelously removed from the eyes."
};
int[] timings = {
0, 2, 4, 7, 9, 11, 13, 15, 17, 20, 22, 25, 27, 29, 30
};
int totalLength;
int repLength = 33;
Subtitles(int t)
{
totalLength = t;
font=createFont("sans_serif", 72, true);
for (int i=0; i<36; i++)
{
textFont(font);
textSize(i);
float testSize = 0;
for (int j=0; j<theText.length;j++)
{
float thisSize = textWidth(theText[j]);
if (thisSize > testSize) testSize = thisSize;
}
if (testSize*1.1 > width)
{
textSize(i-1);
break;
}
}
float testSize = 0;
for (int j=0; j<theText.length;j++)
{
float thisSize = textWidth(theText[j]);
if (thisSize > testSize) testSize = thisSize;
}
boxWidth = testSize*1.1;
boxHeight = (textAscent()+textDescent())*1.5;
boxX = (width-boxWidth)/2;
boxY = min(height*.95,height-(1.5*boxHeight));
}
void write(int ptime, boolean playing)
{
smooth();
stroke(255);
fill(0);
roundedRect(boxX, boxY, boxWidth, boxHeight, 15, 15);
fill(255);
textAlign(CENTER);
int time = (int)(millis()-ptime)/1000;
int whichLine = 0;
String thisText;
if (! (playing && time<=totalLength))
{
thisText = title;
}
else
{
for (int i=0; i<timings.length; i++)
{
if (time%repLength >= timings[i]) whichLine = i;
}
thisText = theText[whichLine%theText.length];
}
text(thisText, width/2, boxY+boxHeight*3/4.0);
}
void roundedRect(float x, float y, float w, float h, float rx, float ry)
{
beginShape();
vertex(x, y+ry); //top of left side
bezierVertex(x, y, x, y, x+rx, y); //top left corner
vertex(x+w-rx, y); //right of top side
bezierVertex(x+w, y, x+w, y, x+w, y+ry); //top right corner
vertex(x+w, y+h-ry); //bottom of right side
bezierVertex(x+w, y+h, x+w, y+h, x+w-rx, y+h); //bottom right corner
vertex(x+rx, y+h); //left of bottom side
bezierVertex(x, y+h, x, y+h, x, y+h-ry); //bottom left corner
endShape(CLOSE);
}
}
Part of my series of Hildegard von Bingen texts.
Uses dotlassie's Superfast Blur (http://www.openprocessing.org/visuals/?visualID=5383)