Thursday, November 6, 2008

Fractal compression. Comparision of two blocks.

Here I want to write implementation of function which gets minimum distance between two blocks. Every block has N (block_size * block_size) elements. In my case block_size = 8. And every element of block is byte. So the function gets two such blocks and find minimum distance between them using different values for Factor (F) an Shift(S) of one block. Here some mathematical equations for solving this problem.



Comments for the code:
  • Block - is a class of one block (8x8 bytes) element.
  • _data - array of elements of this block (b)
  • bl - array of elements of bl block (a)
  • diri - is a Sum(ai*bi)
  • d - is a Sum(ai)
  • r - is a Sum(bi)
  • d2 - is a Sum(ai*ai)
  • r2 - is a Sum(bi*bi)

[C++ code] :

BlockDistance Block::Distance( Block *bl )
{
BlockDistance bd;
int diri = std::inner_product(_data, _data + block_size * block_size, &((*bl)[0]), 0);
int d=getR(), r=bl->getR(), d2 = getR2(), r2 = bl->getR2();
int N = block_size*block_size;
if (N*d2-d*d != 0.0)
bd.Factor = (double)(N*diri-d*r)/(double)(N*d2-d*d);
else bd.Factor = 0.0;
bd.Shift = (r-bd.Factor*d)/(double)N;
double ddd = d2 + (bd.Factor * bd.Factor * r2) + N * (bd.Shift * bd.Shift) - 2 * (bd.Factor * diri) - 2* (d * bd.Shift) + 2 * (bd.Factor * bd.Shift * r);
bd.Distance = ddd / N;
return bd;
}

This method also used the following functions. Them calculates Sum(ai) and Sum(ai*ai) and saves calculated value into some cache variable, because these values are used many times.

[C++ code]:

inline int getR()
{
if (!(_calculated_flags & 1))
{
_r = std::accumulate(_data, _data + block_size * block_size, 0);
_calculated_flags ^= 1;
}
return _r;
}
inline int getR2()
{
if (!(_calculated_flags & 2))
{
_r2 = std::inner_product(_data, _data + block_size * block_size, _data, 0);
_calculated_flags ^= 2;
}
return _r2;
}

Fractal compression. Introduction.

I have no time to introduce you to fractal compression. So I just give you a link to wikipedia.

http://en.wikipedia.org/wiki/Fractal_compression

In my following posts I want to show implementation of some functions used by fractal compressor and decompressor.

Thursday, March 20, 2008

Active Desktop or Image Gallery for your desktop.

At this post I will tell you how to create your own Image Gallery or Photo Gallery at your desktop.

Explanation (why?).

First of all I wanted to have dinamically changed images on my desktop. But it was only my dream. Once upon a time I downloaded from the Internet about 500 art works by some well known artists :) Then I understood that I want to see it one by one (for example 1 per day) at my desktop. That is why I created this solution.

How it works?

I have created Html page with black backround and with just one image. This image fit browser window. All gallery images are called the same way: ***.jpg where *** - is a number with leading zeroes. These images are placed into "Images" folder, so Html page can load them by the number. All manipulations are organized using JavaScript. I added small setup window for this page, which you can see just clicking at + button at the top left corner of this page. And, at the end, the state of the page I save using Cookies.

And:
1. Active Desktop in Windows allows us to set html page as desktop image.
2. This html page was developed for IE8 browser, so, at other browsers it may fails. But we actually need just IE.

How to use:

1. Copy the following Html code to some html file. (To do it open notepad and save the following as file named index.html)


<html>
<head>
<script>

// global variables
var screenBorder = 50;
var screenX;
var screenY;
var $get;

var minImage = 1;
var maxImage = 5;
var imageIndex = 1;
var loadMode = "Random";

var image = new Image();

function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}

function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}

function eraseCookie(name) {
createCookie(name,"",-1);
}

// save state to cookies
function saveState()
{
createCookie("minImage", minImage, 3650);
createCookie("maxImage", maxImage, 3650);
createCookie("imageIndex", imageIndex, 3650);
createCookie("loadMode", loadMode, 3650);
}

// load state from cookies
function loadState()
{
var tmp = readCookie("minImage");
if (tmp) minImage = parseInt(tmp);
tmp = readCookie("maxImage");
if (tmp) maxImage = parseInt(tmp);
tmp = readCookie("imageIndex");
if (tmp) imageIndex = parseInt(tmp);
tmp = readCookie("loadMode");
if (tmp) loadMode = tmp;
}

function showSetupWindow()
{
loadState();
$get('firstImg').value = minImage;
$get('lastImg').value = maxImage;
if (loadMode == "Random")
$get('randomButton').checked = true;
else
$get('currentButton').checked = true;
$get("setupWindow").style.visibility = "visible";
}

function closeSetupWindow()
{
// TODO: save state from window to variables.
//
minImage = parseInt($get('firstImg').value);
maxImage = parseInt($get('lastImg').value);
if ($get('randomButton').checked === true)
loadMode = "Random";
else
loadMode = "Current";
saveState();
$get("setupWindow").style.visibility = "hidden";
}

function prev()
{
imageIndex--;
if (imageIndex < minImage)
imageIndex = maxImage;
loadImage(imageIndex);
}

function next()
{
imageIndex++;
if (imageIndex > maxImage)
imageIndex = minImage;
loadImage(imageIndex);
}

function bodyLoad()
{
$get = document.getElementById;
screenX = document.body.clientWidth;
screenY = document.body.clientHeight;
loadState();
if (loadMode == "Random")
imageIndex = Math.round(Math.random()*(maxImage-minImage))+minImage;
saveState();
loadImage(imageIndex);
}

function loadImage(imageIndex)
{
var screenX = document.body.clientWidth - 60;
var screenY = document.body.clientHeight - 40;
var num = imageIndex;
if (imageIndex < 10) num = "0" + num;
if (imageIndex < 100) num = "0" + num;
image.onload = function(){
image.style.position = "absolute";
var imageWidth = image.width;
var imageHeight = image.height;
var img = document.getElementById('image1');
img.onload = function()
{
if ((screenX/imageWidth)*imageHeight <= screenY)
{
img.width = screenX;
img.height = (screenX/imageWidth)*imageHeight;
img.style.top = Math.round((screenY - (screenX/imageWidth)*imageHeight)/2 + 20) + "px";
img.style.left = "30px";
}
else
{
img.width = (screenY/imageHeight)*imageWidth;
img.height = screenY;
img.style.top = "40px";
img.style.left = Math.round((screenX - (screenY/imageHeight)*imageWidth) / 2 + 30) + "px";
}
}
img.src = image.src;
}
var desc = document.getElementById('desc1');
image.src = "images/" + num + ".jpg";
}


</script>
</head>
<body bgcolor="#000000" onload="bodyLoad();">
<img id="image1" style="position: absolute; top: 100px; left: 100px;" />
<a style="position: absolute; top: 10px; right: 10px; color: White;" href="http://serpblog.blogspot.com/2008/03/active-desktop-or-image-gallery-for.html" target="_blank">Home (serp)</a>
<input type="button" value="+" onclick="showSetupWindow();" />
<div id="setupWindow" style="visibility: hidden; padding: 10px; overflow: hidden; position: absolute; top: 0px; left: 0px; width: 295px; height: 170px; background: black; border: 2px solid green; color: #FFFFFF;">
<div style="position: absolute; top: 10px; left: 10px;">First image:</div>
<input id="firstImg" style="position: absolute; top: 10px; left: 100px;" id="firstImage" size="5" style="float: right;" type="text" />
<div style="position: absolute; top: 30px; left: 10px;">Last image:</div>
<input id="lastImg" style="position: absolute; top: 30px; left: 100px;" id="lastImage" size="5" type="text" />
<input style="position: absolute; top: 60px; left: 10px; width: 150px;" type="button" name="prev" onclick="prev();" value="Previous image" />
<input style="position: absolute; top: 90px; left: 10px; width: 150px;" type="button" name="next" onclick="next();"value="Next image" />
<div style="position: absolute; top: 10px; left: 190px;">Load mode:</div>
<div style="position: absolute; top: 40px; left: 210px;">Random</div>
<input id="randomButton" style="position: absolute; top: 40px; left: 180px;" type="radio" name="loadMode" value="Random"/>
<div style="position: absolute; top: 60px; left: 210px;">Current</div>
<input id="currentButton" style="position: absolute; top: 60px; left: 180px;" type="radio" name="loadMode" value="Current" />
<input style="position: absolute; top: 130px; left: 10px; width: 270px;" type="button" onclick="closeSetupWindow();" value="Save and close" />
</div>

</body>
</html>

2. At the folder where you saved index.html create Images folder and create there five files 001.jpg 002.jpg 003.jpg 004.jpg and 005.jpg

To do it I prepared some images for you:



3. Right click on your desktop, choose options - desktop and change your current image to index.html page, you have created.

4. Have fun :)

What else?

Using setup window (just clicking on + button at top left corner of your desktop) you may change amount of images into your collection increasing Last Image property.
Also you may divide your collection to some parts and load one of them using First Image and Last Image properties.
You may navigate forward and backward into current collection of images using "Next image" and "Previous image" buttons.
You may freeze current displayed image using "Current" radio button.

Future.

I see the future... :) A lot of options like - background color.
For every image we aslo shows description file. This may be interesting for some art works.

Or may be new site with a number of large collections of desktop images. So user just chooses collection, but all images are stored on the internet, and user just download one by one, for example one per day =)

Or something else, who knows?....

P.S. If you have some problems or ideas please fell free to describe it here.

Sunday, October 28, 2007

This is just an example of using "canvas" tag into html.

In this post I want to show some sample on drawing vector objects on HTML Canvas element. Canvas element is new HTML element which allows us to write some graphics objects into the browser through the JavaScript code. This element is supported by FireFox, Safary and some other browsers. Internet Explorer does not support this element directly, but you can use excanvas library which implements Canvas functionality through VML. To read more about Canvas you may visit Mozilla Developer Center.

In the following sample I want to draw an image of boy (I call this image “The boy from South Park:)). I try to do it using just some parts of such figures as circle, rectangle and some base transformations of canvas: translation, rotation and scale.

Let’s define some auxiliary functions:

drawCircle function draws filled circle.
ctx
is Canvas context
x
is a horizontal position of circle center
y
is a vertical position of circle center
r
is a radius of circle
color
is a color of circle

Example:

drawCircle(ctx, 100, 135, 50, "#00aa00");

drawEllipse function draws ellipse with center in point (x,y), the smallest radius equal w and proportion between radiuses equal to scale. Ellipse is rotated with angle and filled with color.

Example:

drawEllipse(ctx, 100, 135, 50, 1.5, Math.PI/4, "#00aa00");

drawArc function draws part of circle with center in point (x,y) radius w started at angle1 and finished at angle2. Boolean parameter fill describes should this part of circle be filled or striked. lineWidth means the width of line in the case of striking.

Example:

drawArc(ctx, 100, 135, 50, (3/4)*Math.PI, (1/4)*Math.PI, "#00aa00", false, 10);


Next functions just draw some parts of the boy.


drawTrousers(ctx, 100, 200, 80, 20);

Just draws a blue rectangle. J

drawShoes(ctx, 85, 305, 115, 305);

Just draws two small parts of circles.

drawJacket(ctx, 100, 180, 50, 60, "#7C4940");

This function is more complex. It draws half part of filled ellipse. Bottom of the jacket is a part of arc with lineWidth equal 10. And, of cause, here we can see 3 lines.

drawHands(ctx, 100, 180, 45, "#CC0000");

Every hand is just two red circles. One for hand and one for thumb.

drawCollar(ctx, 100, 120, 30, "#CC0000");

Collar is an ellipse which will be covered with head.

drawFace(ctx, 100, 100, 50, 45);

Face is a combination of filled ellipses.

drawPompon(ctx, 100, 55, 10);

To draw it I just write line and rotate it several times.

drawPartOfCap(ctx, 100, 300, 215, 10, "#CC0000");

You can see just part of an arc.

drawMouth(ctx, 98, 155);

And one more part of an arc.

So, finally, applying this drawings one by one we will get the image of boy and be happy.



Here you can see the source HTML file with JavaScript code which draws an image of the boy.

<html>
<head>
</head>
<body>
<canvas id="Canvas1" width=200 height=270 style="border:1px solid red;">
</canvas>
<script>
window.onload = function() {
var canvas = document.getElementById("Canvas1");
var ctx = canvas.getContext('2d');

drawTrousers(ctx, 100, 200, 80, 20);
drawShoes(ctx, 85, 305, 115, 305);
drawJacket(ctx, 100, 180, 50, 60, "#7C4940");
drawHands(ctx, 100, 180, 45, "#CC0000");
drawCollar(ctx, 100, 120, 30, "#CC0000");
drawFace(ctx, 100, 100, 50, 45);
drawPompon(ctx, 100, 55, 10);
drawPartOfCap(ctx, 100, 300, 215, 10, "#CC0000");
drawMouth(ctx, 98, 155);


function drawHands(ctx, x, y, r, color) {
ctx.save();
drawCircle(ctx, x + r, y, 10, color);
drawCircle(ctx, x + r - 6, y - 4, 5, color);
drawCircle(ctx, x - r, y, 10, color);
drawCircle(ctx, x - r + 6, y - 4, 5, color);
ctx.restore();
}

function drawJacket(ctx, x, y, w, h, color) {
ctx.save();
ctx.scale(w/h, 1);
drawArc(ctx, x * (h/w), y, h, 0, Math.PI, color, true);
ctx.restore();

drawArc(ctx, x, y - h*2 + 8, h*2, Math.PI*(10/16), Math.PI*(6/16), color, false, w/3);

drawLine(ctx, x,y + 15,x,y-h, "#000000", 2);
drawLine(ctx, x - w*(0.6),y - h*(0.4),x - w*(0.7),y - h*(0.1),"#000000", 1);
drawLine(ctx, x + w*(0.6),y - h*(0.4),x + w*(0.7),y - h*(0.1),"#000000", 1);
}

function drawCollar(ctx, x, y, r, color) {
drawEllipse(ctx,x,y,r,1.3,0,color);
}

function drawTrousers(ctx, x, y, w, h) {
ctx.save();
ctx.translate(x,y);
ctx.fillStyle = "#000099";
ctx.fillRect(-w/2, -h/2, w, h);
ctx.restore();
}

function drawShoes(ctx, x, y, x1, y1) {
drawArc(ctx, x, y, 100, -Math.PI*(6/16), -Math.PI*(10/16), "#000000", true);
drawArc(ctx, x1, y1, 100, -Math.PI*(6/16), -Math.PI*(10/16), "#000000", true);
}

function drawMouth(ctx, x, y) {
drawArc(ctx, x, y, 25, -Math.PI*(6/16), -Math.PI*(9/16), "#000000", false, 1);
}

function drawLine(ctx, x1, y1, x2, y2, color, lineWidth) {
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = lineWidth;
ctx.beginPath();
ctx.moveTo(x1,y1);
ctx.lineTo(x2,y2);
ctx.stroke();
ctx.restore();
}

function drawFace(ctx, x, y, w, h) {
ctx.save();
ctx.translate(x,y);
ctx.scale(w/h, 1);
drawCircle(ctx, 0, 0, h, "#FEC6A5");
drawArc(ctx, 0, 0, h, - Math.PI/12, - (11/12)*Math.PI, "#000099", true)

// draw left eye
drawEllipse(ctx, w/4, w/10, 12, 1.2, Math.PI/4, "#FFFFFF");
drawCircle(ctx, w/6, w/10, 2, "#000000");

// draw right eye
drawEllipse(ctx, -w/4, w/10, 12, 1.2, -Math.PI/4, "#FFFFFF");
drawCircle(ctx, -w/6, w/10, 2, "#000000");

ctx.restore();
}

function drawPartOfCap(ctx, x, y, r, height, color) {
ctx.save();
ctx.lineCap = "round";
drawArc(ctx,x, y, r, -(15/35)*Math.PI, -Math.PI*(20/35), color, false, height);
ctx.restore();
}

function drawPompon(ctx, x, y, r) {
ctx.save();
ctx.translate(x,y);
ctx.lineWidth = r / 6;
ctx.lineCap = "round";
ctx.strokeStyle = "#CC0000";
for (var i = 0; i < 8; i++) {
ctx.rotate(Math.PI/8);
ctx.beginPath();
ctx.moveTo(-r, 0);
ctx.lineTo(r, 0);
ctx.stroke();
}
ctx.restore();
}

function drawArc(ctx, x, y, w, angle1, angle2, color, fill, lineWidth) {
ctx.save();
if (fill)
ctx.fillStyle = color;
else {
ctx.strokeStyle = color;
if (lineWidth) ctx.lineWidth = lineWidth;
}
ctx.translate(x,y);
ctx.beginPath();
ctx.arc(0, 0, w, angle1, angle2, true);
if (fill)
ctx.fill();
else
ctx.stroke();
ctx.restore();
}

function drawEllipse(ctx, x, y, w, scale, angle, color) {
ctx.save();
ctx.translate(x,y);
ctx.rotate(angle);
ctx.scale(scale, 1);
drawCircle(ctx, 0, 0, w, color);
ctx.restore();
}

function drawCircle(ctx, x, y, r, color) {
ctx.save();
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, r, 0, Math.PI*2, true);
ctx.fill();
ctx.restore();
}
}
</script>
</body>
</html>

Saturday, October 27, 2007

Hello World!

Hello everybody. I'm glad to see you here.
This post is just about "hello world".

Something like:

printf("Hello World!");
or:
write("Hello World");
or may be:
echo "Hello World"

etc. etc. etc.