Everybody has seen the cute pictures floating around showing a mini world! Some examples:


But how is it done? And can it be done fast?
Answers: Easy and Yes!
How does it work?
For a digital Tilt-Shift we use some kind of gradient blur. What I mean is a blur that is very light where the focus lens is and gets blurrier the further something is in the picture. Some ‘faster’ systems only use one blur and then put the focused part back into the picture but this creates an ugly edge between the sharp and blurred part of the picture. And we do not want this!
To get his effect we could do several blurs where we enlarge the focus box in the loop so we get some gradience. Eg. If we do it 8 times we get a gradient of 8 layers. Not bad but…we want more!
Doing a blur in languages like Realstudio and Basic4Android (or Java) is not that fast. This is the first issue we’ll have to tackle because we want to be close to doing it real-time.
Second, a lot of focusses can only be a rectangle or a circle. This has a side effect: some objects in the picture will be in focus while we do not want that because it spoils the illusion. So our focus needs to be able to be irregular. In this tutorial the example uses also a rectangle but I’ll show you how to change this to anything you want.
In some cases changing the saturation of the picture can even more give the illusion of a miniature.
So in short we want:
1. few blurs, but still have 255 gradient levels
2. the blur must be fast
3. an irregular focus
4. some kind of saturation
5. did I mention fast?
How will Basic4Android (or java) and Realbasic be able to do this?
Well, we’re gonna cheat!
Instead of creating a gradient blur, we are going to write one heavy blur and then resharpen it. In fact you’ll see we can do the 255 levels of sharpness with only two blurs!
In Realbasic we can take advantage of the great mask property in a picture we’ve used a lot in the past. In Basic4Android I’m going to simulate this so the code can be very similar.
For our example let’s say we want to make this:

Let me show you in a schema what the steps are we’re going to use in our Tilt-Shift (click the picture to see full size):

1. our orignal picture. Make sure it is a sharp one
2. a blurred version of (1)
3. our focus area.
4. a blurred version of (3)
5. we use our blurred (4) as a mask on our original (1). As you can see in the schema, (5) fades out to its background (the alpha channel increases), in this case white.
6. so if we draw (5) on our blurred (2) the blurred pixels will re-sharpen! Do you see the magic happening here ![]()
7. the saturation is optional but may make the effect more real.
And the clever part is step 3: you can draw any shape you want here! Some examples for Realbasic:

And remember, in Basic4Android (java) it needs to be inverted:

And now for the code.
(For B4A you’ll need my ABExtDrawing library version 1.7 here)
First we’ll need a fast blur. Again we’ll cheat as we’re not doing a real gaussian blur. We are ging to exploit our mask feature again and do a diagonal blur.
We’ll need some help functions:
Resize()
Realbasic:
Function Resize(pic as picture, Percentage as integer) As Picture #pragma BackgroundTasks false #pragma BoundsChecking false #pragma NilObjectChecking false #pragma StackOverflowChecking false dim p as Picture dim w,h as Integer dim by as Double = Percentage / 100 w = pic.Width * by h = pic.Height * by p = NewPicture(w,h,32) p.Graphics.UseOldRenderer=true p.Graphics.DrawPicture pic,0,0,w,h, 0,0,pic.Width, pic.Height Return p End Function
Java (B4A library):
public Bitmap Resize(Bitmap bmp, float Percentage)
{
float by = (float) (Percentage/100.0);
int w = (int) (bmp.getWidth()*by);
int h = (int) (bmp.getHeight()*by);
if (Percentage>100) {
return Bitmap.createScaledBitmap(bmp, w, h, true);
} else {
return Bitmap.createScaledBitmap(bmp, w, h, false);
}
}
Innerblur()
We create a mask on our picture and draw it a number of times on each other, just shifting bits to the left, top, right and bottom.
Realbasic:
Function InnerBlur(Pic as picture, Level as double) As Picture
#pragma BackgroundTasks false
#pragma BoundsChecking false
#pragma NilObjectChecking false
#pragma StackOverflowChecking false
dim p,t as Picture
dim L,w,h as Integer
w=pic.Width
h=pic.Height
p=NewPicture(w,h,32)
p.Graphics.UseOldRenderer=true
p.Graphics.DrawPicture pic,0,0
t=NewPicture(w,h,32)
t.Graphics.UseOldRenderer=true
t.Mask.Graphics.UseOldRenderer=true
t.Mask.Graphics.ForeColor=&c7F7F7F
t.Mask.Graphics.FillRect 0,0,t.Width,t.Height
for L=abs(Level) DownTo 1
t.Graphics.DrawPicture p,0,0
p.Graphics.DrawPicture t,-L,-L 'upper left
t.Graphics.DrawPicture p,0,0
p.Graphics.DrawPicture t,-L,L 'lower left
t.Graphics.DrawPicture p,0,0
p.Graphics.DrawPicture t,L,L 'lower right
t.Graphics.DrawPicture p,0,0
p.Graphics.DrawPicture t,L,-L 'upper right
next
Return p
End Function
Java (B4A library):
private Bitmap InnerBlur(Bitmap bmp, float Level) {
int w = bmp.getWidth();
int h = bmp.getHeight();
Paint paint = new Paint();
paint.setAlpha(0x7F);
Bitmap t = Bitmap.createBitmap(w, h, Config.ARGB_8888);
Canvas tc = new Canvas(t);
Bitmap p = Bitmap.createBitmap(w, h, Config.ARGB_8888);
Canvas pc = new Canvas(p);
pc.drawBitmap(bmp, 0, 0, null);
int Lev = (int) Level;
for (int L=Lev;L>0;L--) {
tc.drawBitmap(p, 0, 0, paint);
pc.drawBitmap(t, -L, -L, null);
tc.drawBitmap(p, 0, 0, paint);
pc.drawBitmap(t, -L, L, null);
tc.drawBitmap(p, 0, 0, paint);
pc.drawBitmap(t, L, L, null);
tc.drawBitmap(p, 0, 0, paint);
pc.drawBitmap(t, L, -L, null);
}
t.recycle();
return p;
}
Blur()
The actual blur function is a mix of Resizes and Innerblurs.
1. Resize to smaller
2. Innerblur to level
3. Resize back to original size
4. Innerblur with level 1 to make it smooth
Realbasic:
Function Blur(Pic as picture, Level as double, Speed as Integer) As Picture
#pragma BackgroundTasks false
#pragma BoundsChecking false
#pragma NilObjectChecking false
#pragma StackOverflowChecking false
dim p as Picture
dim L,w,h as Integer
w=pic.Width
h=pic.Height
p=NewPicture(w,h,32)
p.Graphics.UseOldRenderer=true
p.Graphics.DrawPicture pic,0,0
L=Level-round(Level*.4)
select case speed
case 0
// shrink the image, blur it
p=Resize(p,50)
p=InnerBlur(p, L )
// back to normal, smooth it
p=Resize(p, 200)
p=InnerBlur(p, 1)
case 1
// shrink the image, blur it
p=Resize(p,25)
p=InnerBlur(p, L )
// back to normal, smooth it
p=Resize(p, 400)
p=InnerBlur(p, 1)
end select
Return p
End Function
Java (B4A library):
/**
* blur and image.
* Speed: 0 = normal, 1 = fast
*/
public Bitmap Blur(Bitmap bmp, float Level, int Speed)
{
int L = (int) (Level - Math.round(Level*.4));
Bitmap p = null;
switch (Speed) {
case 0:
p = Resize(bmp, 50);
p = InnerBlur(p, L);
p = Resize(p,200);
p = InnerBlur(p, 1);
break;
case 1:
p = Resize(bmp, 25);
p = InnerBlur(p, L);
p = Resize(p,400);
p = InnerBlur(p, 1);
break;
}
return p;
}
This is, as far as I know, the fastest way to do a blur in Realbasic and Java. If someone knows of a faster one, please contact me, I’ll be very interested.
Before we continue, I’m going to make a function in B4A to merge a Mask layer with a picture so we can do the tilt Shift in a way similar to Realbasic.
As mentioned in a previous article, you have to make sure your images are in a certain format on Android otherwise you can strange colors or resizes.
Here is a function that makes sure the picture is in ARGB_8888 format:
Basic4Android:
' exDraw was dimmed before as Dim ExDraw As ABExtDrawing '(version 1.70 or higher required) Sub Convert_RGB565_To_ARGB8888(iFolder As String, iFile As String) As Bitmap Dim iBmp As Bitmap iBmp.Initialize(iFolder, iFile) ' only convert it if it is a RGB565 If ExDraw.GetConfig(iBmp) = ExDraw.RGB_565 Then Dim w, h As Int w = iBmp.Width h = iBmp.Height Dim tmpBmp As Bitmap tmpBmp.InitializeMutable(w,h) Dim cnvRect As Rect cnvRect.Initialize(0,0,w, h) Dim cnvCanv As Canvas cnvCanv.Initialize2(tmpBmp) Dim aRect As Rect aRect.Initialize(0,0,w,h) cnvCanv.DrawBitmap(iBmp,Null, aRect) Return tmpBmp Else Return iBmp End If End Sub
And here is our merge function:
Basic4Android:
Sub MergeWithAlphaLayer(iBmp As Bitmap, iLayerBmp As Bitmap) As Bitmap
Dim MergedBmp As Bitmap
Dim MergedBmpCanv As Canvas
Dim w, h As Int
w = iBmp.Width
h = iBmp.Height
Dim aRect As Rect
aRect.Initialize(0,0,w,h)
MergedBmp.InitializeMutable(w,h)
MergedBmpCanv.Initialize2(MergedBmp)
Dim Alpha As Bitmap
Alpha.InitializeMutable(w,h)
' pic
MergedBmpCanv.DrawBitmap(iBmp, Null, aRect)
' mask
Dim Pixels(w*h) As Int
ExDraw.getPixels(iLayerBmp,Pixels , 0, w, 0, 0, w, h)
Dim count As Int
count = (w*h) - 1
For i = 0 To count
Pixels(i) = Bit.ShiftLeft(Pixels(i),8) 'move the red pixel value to the alpha channel
Next
ExDraw.setPixels(Alpha, Pixels, 0, w, 0, 0, w, h)
Dim AlphaP As ABPaint
AlphaP.Initialize
AlphaP.SetAntiAlias(True)
AlphaP.SetPorterDuffXfermode(ExDraw.PorterDuffMode_DST_IN) 'DST_IN only takes over the alpha values
ExDraw.drawBitmap2(MergedBmpCanv, Alpha, 0, 0, AlphaP)
ExDraw.Recycle(Alpha)
Return MergedBmp
End Sub
Ok, back on track! The saturation function. I admit the one in Java has some room for improvement.
Realbasic:
Sub Saturation(Pic as picture, Ammount as integer)
#pragma BackgroundTasks false
#pragma BoundsChecking false
#pragma NilObjectChecking false
#pragma StackOverflowChecking false
dim c as color
dim d as Double
dim a,x,y,w,h,m() as Integer
dim s as RGBSurface
dim p as Picture
dim ir,ig,ib as Integer
s=pic.RGBSurface
d=Ammount/100
w=pic.Width-1
h=pic.Height-1
Redim m(510)
for x=0 to 510
m(x)=(x-255)*d
Next
for y=0 to h
for x=0 to w
c=s.Pixel(x,y)
ir=c.red
ig=c.green
ib=c.blue
a=( ir + ig + ib ) \ 3
s.Pixel(x,y)=rgb( ir+m(ir-a+255) , ig+m(ig-a+255) , ib+m(ib-a+255) )
next
next
End Sub
Java (B4A library):
public Bitmap Saturate(Bitmap bmp,float Ammount) {
//Initialize the ColorMatrix object
ColorMatrix colorMatrix = new ColorMatrix();
//Initialize the ColorMatrixColorFilter object
ColorMatrixColorFilter cmFilter = new ColorMatrixColorFilter(colorMatrix);
//Initialize the cmPaint
Paint cmPaint = new Paint();
//Set 'cmFilter' as the color filter of this paint
cmPaint.setColorFilter(cmFilter);
colorMatrix.setSaturation(Ammount/(float)100);
//Create a new ColorMatrixColorFilter with the recently altered colorMatrix
cmFilter = new ColorMatrixColorFilter(colorMatrix);
//Assign the ColorMatrix to the paint object again
cmPaint.setColorFilter(cmFilter);
//Draw the Bitmap into the mutable Bitmap using the canvas. Don't forget to pass the Paint as the last parameter
Bitmap alteredBitmap = Bitmap.createBitmap(bmp.getWidth(), bmp.getHeight(), bmp.getConfig());
Canvas canvas = new Canvas(alteredBitmap);
canvas.drawBitmap(bmp, 0, 0, cmPaint);
return alteredBitmap;
}
The actual Tilt Shift. This is nothing more that going over the steps I’ve shown in the schema.
Realbasic:
Sub DoTiltShiftRect(bmp as Picture, BlurRadius as integer, focusX as integer, focusY as integer, focusW as integer, focusH as integer, DoSaturtion as boolean)
mBuffer = new Picture(640,480, 32)
tmpPic = NewPicture(640,480, 32)
tmpMask = NewPicture(640,480, 32)
mBuffer = Blur(bmp,blurRadius,0)
tmpPic.Graphics.DrawPicture bmp,0,0
tmpMask.Graphics.FillRect(focusX,focusY - focusH/2, focusW,focusH)
tmpMask = Blur(tmpMask, 7,1)
tmpPic.Mask.Graphics.DrawPicture tmpMask,0,0
mBuffer.Graphics.DrawPicture tmpPic,0,0
if DoSaturtion then
Saturation mBuffer,80
end if
Canvas1.Refresh(false)
End Sub
Basic4Android:
Sub DoTiltShiftRect(iBmp As Bitmap, BlurRadius As Int, focusR As Rect, DoSaturation As Boolean) As Bitmap Dim tgtCanv As Canvas Dim w, h As Int w = iBmp.Width h = iBmp.Height Dim tgtBmp As Bitmap tgtBmp.InitializeMutable(w, h) tgtCanv.Initialize2(tgtBmp) ' blur the picture Dim srcBlurred As Bitmap srcBlurred = ExDraw.Blur(iBmp, BlurRadius, 0) ' create the sharp box Dim Alpha As Bitmap Alpha.InitializeMutable(w, h) Dim AlphaCanv As Canvas AlphaCanv.Initialize2(Alpha) ' draw a full white rect on a black background Dim tmpR As Rect tmpR.Initialize(0,0,w, h) AlphaCanv.DrawRect(tmpR, Colors.black, True, 0dip) AlphaCanv.DrawRect(focusR, Colors.white, True, 0dip) ' blur the white box to emulate a gradient blur, may be fast and ugly Dim AlphaBlurred As Bitmap AlphaBlurred = ExDraw.Blur(Alpha, 7, 1) ' merge the blurred alpha layer with the bitmap into the overlay Dim Overlay As Bitmap Overlay = MergeWithAlphaLayer(iBmp, AlphaBlurred) ' saturate the picture to make it more life like Overlay = ExDraw.Saturate(Overlay, 80) Dim aRect As Rect aRect.Initialize(0,0,w,h) ' draw the blurred image tgtCanv.DrawBitmap(srcBlurred, aRect, aRect) ' draw the overlay tgtCanv.DrawBitmap(Overlay, aRect, aRect) ' saturate If DoSaturation Then tgtBmp = ExDraw.Saturate(tgtBmp, 90) End If ExDraw.Recycle(srcBlurred) ExDraw.Recycle(Alpha) ExDraw.Recycle(AlphaBlurred) ExDraw.Recycle(Overlay) Return tgtBmp End Sub
And that’s it! Remember I’ll only did it for a rectangle here in the tutorial, but you can use anything.
Notes about the programs:
1. you can point anywhere on the y-axe in the picture to re-position the focusbox. 2. in the Realbasic version you can change the picture in the main.open() event: CurrentPicture = beach2 3. for the compiled RB version you can use the command line: Syntax: TiltShift.exe /PIC=picturename.jpg;focusleft;focustop;focuswidth;focusheight;dosaturation focus variables = the focus box DoSaturation: 0 = false, 1 = true Example: TiltShift.exe /PIC=beach2.jpg;30;300;580;240;1 4. in the Basic4Android version you can change the picture in the Activity_Create() sub: srcBmp = ABBitmapTools.Convert_RGB565_To_ARGB8888(File.DirAssets, "beach2.jpg") 5. the TiltShift.apk can be found in the B4A version in the folder \Objects\.
It would be nice if you give me credit if you use this code in your own program.
The full source code can be downloaded from:
Realbasic: download here
Basic4Android: download here
And some more eye candy!


if you like my work















