diff --git a/apps/gallery/ajax/thumbnail.php b/apps/gallery/ajax/thumbnail.php
index ff0cb44022ceedd403790df7c32ef6fe4c8e3ce2..4fc9eba992d9eb25f12f98c976f19a34e751fb4e 100644
--- a/apps/gallery/ajax/thumbnail.php
+++ b/apps/gallery/ajax/thumbnail.php
@@ -20,14 +20,15 @@
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */
-
  
 OCP\JSON::checkLoggedIn();
 OCP\JSON::checkAppEnabled('gallery');
+require_once('apps/gallery/lib/managers.php');
+
 
-$img = $_GET['img'];
+$img = $_GET['filepath'];
 
-$image = OC_Gallery_Photo::getThumbnail($img);
+$image = \OC\Pictures\ThumbnailsManager::getInstance()->getThumbnail($img);
 if ($image) {
 	OCP\Response::enableCaching(3600 * 24); // 24 hour
 	$image->show();
diff --git a/apps/gallery/lib/managers.php b/apps/gallery/lib/managers.php
new file mode 100644
index 0000000000000000000000000000000000000000..96669da27278dced7e26a75b989f0d1bb94996a3
--- /dev/null
+++ b/apps/gallery/lib/managers.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace OC\Pictures;
+
+require_once('lib/base.php');
+
+\OCP\JSON::checkLoggedIn();
+\OCP\JSON::checkAppEnabled('gallery');
+
+class DatabaseManager {
+	private static $instance = null;
+	const TAG = 'DatabaseManager';
+	
+	public static function getInstance() {
+		if (self::$instance === null)
+			self::$instance = new DatabaseManager();
+		return self::$instance;
+	}
+	
+	public function getFileData($path) {
+		$gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery';
+		$path = $gallery_path.$path;
+		$stmt = \OCP\DB::prepare('SELECT * FROM *PREFIX*pictures_images_cache WHERE uid_owner LIKE ? AND path = ?');
+		$result = $stmt->execute(array(\OCP\USER::getUser(), $path));
+		if (($row = $result->fetchRow()) != false) {
+			return $row;
+		}
+		$image = new \OC_Image();
+		if (!$image->loadFromFile($path)) {
+			return false;
+		}
+		$stmt = \OCP\DB::prepare('INSERT INTO *PREFIX*pictures_images_cache (uid_owner, path, width, height) VALUES (?, ?, ?, ?)');
+		$stmt->execute(array(\OCP\USER::getUser(), $path, $image->width(), $image->height()));
+		unset($image);
+		return $this->getFileData($path);
+	}
+	
+	private function __construct() {}
+}
+
+class ThumbnailsManager {
+	
+	private static $instance = null;
+	const TAG = 'ThumbnailManager';
+	
+	public static function getInstance() {
+		if (self::$instance === null)
+			self::$instance = new ThumbnailsManager();
+		return self::$instance;
+	}
+
+	public function getThumbnail($path) {
+		$gallery_path = \OCP\Config::getSystemValue( 'datadirectory' ).'/'.\OC_User::getUser().'/gallery';
+		if (file_exists($gallery_path.$path)) {
+			return new \OC_Image($gallery_path.$path);
+		}
+		if (!\OC_Filesystem::file_exists($path)) {
+			\OC_Log::write(self::TAG, 'File '.$path.' don\'t exists', \OC_Log::WARN);
+			return false;
+		}
+		$image = new \OC_Image();
+		$image->loadFromFile(\OC_Filesystem::getLocalFile($path));
+		if (!$image->valid()) return false;
+
+		$image->fixOrientation();
+
+		$ret = $image->preciseResize(floor((200*$image->width())/$image->height()), 200);
+		
+		if (!$ret) {
+			\OC_Log::write(self::TAG, 'Couldn\'t resize image', \OC_Log::ERROR);
+			unset($image);
+			return false;
+		}
+
+		$image->save($gallery_path.'/'.$path);
+		return $image;
+	}
+	
+	public function getThumbnailInfo($path) {
+		$arr = DatabaseManager::getInstance()->getFileData($path);
+		$ret = array('filepath' => $arr['path'],
+		             'width' => $arr['width'],
+		             'height' => $arr['height']);
+		return $ret;
+	}
+	
+	public function delete($path) {
+		unlink(\OC::$CONFIG_DATADIRECTORY_ROOT.'/'.\OC_User::getUser()."/gallery".$path);
+	}
+	
+	private function __construct() {}
+
+}
+?>
\ No newline at end of file
diff --git a/apps/gallery/lib/testimg.jpg b/apps/gallery/lib/testimg.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..0d9a2bd8812f7b1d0b492821054eb9a4a3b7ada9
Binary files /dev/null and b/apps/gallery/lib/testimg.jpg differ
diff --git a/apps/gallery/lib/tiles.php b/apps/gallery/lib/tiles.php
new file mode 100644
index 0000000000000000000000000000000000000000..d7f5208ff83b4d3c6a7b93ab610052bd35b243ec
--- /dev/null
+++ b/apps/gallery/lib/tiles.php
@@ -0,0 +1,183 @@
+<?php
+
+namespace OC\Pictures;
+
+require_once('lib/base.php');
+require_once('managers.php');
+
+const TILE_PROPORTION_HORIZONTAL = 0;
+const TILE_PROPORTION_VERTICAL = 0;
+const GET_THUMBNAIL_PATH = '?app=gallery&getfile=ajax/thumbnail.php&filepath=';
+const TAG = 'Pictures';
+
+class TileBase {
+  public function getWidth() { return false; }
+
+  public function getHeight() { return 200; }
+
+  public function getOnHoverAction() { return false; }
+  
+  public function getOnOutAction() { return false; }
+  
+  public function getOnClickAction() { return false; }
+
+  public function getDisplayedLayer() { return false; }
+
+  public function getTileProportion() { return false; }
+  
+  public function get() { return false; }
+}
+
+class TilesLine {
+
+  public function __construct() {
+    $this->tiles_array = array();
+  }
+
+  public function setAvailableSpace($space) {
+    $available_space = $space;
+  }
+
+  public function getTilesCount() {
+    return count($this->tiles_array);
+  }
+
+  public function addTile($tile) {
+    array_push($this->tiles_array, $tile);
+  }
+
+  public function getLeftSpace() {
+    $occupied_space = 0;
+    for ($i = 0; $i < count($this->tiles_array); $i++) {
+      $occupied_space += $this->tiles_array[$i]->getWidth();
+    }
+    return $this->available_space - $occupied_space;
+  }
+
+  public function tileWillFit($tile) {
+    return $this->getLeftSpace() > $tile->getWidth();
+  }
+  
+  public function get() {
+	  $r = '<div class="line gallery_div">';
+	  
+	  for ($i = 0; $i < count($this->tiles_array); $i++) {
+	  	  $img_w = $this->tiles_array[$i]->getWidth();
+	  	  $extra = '';
+	  	  if ($img_w != 200) $extra = ' style="width:'.$img_w.'px"';
+		  $r .= '<div class="gallery_div" '.$extra.' onmouseover="'.$this->tiles_array[$i]->getOnHoverAction().'" onmouseout="'.$this->tiles_array[$i]->getOnOutAction().'">'.$this->tiles_array[$i]->get().'</div>';
+	  }
+	  
+	  $r .= '</div>';
+	  return $r;
+  }
+
+  private $tiles_array;
+  private $available_space;
+}
+
+class TileSingle extends TileBase {
+
+  public function __construct($path) {
+    \OC_Log::write(TAG, 'Loading file from path '.$path, \OC_Log::DEBUG);
+    $this->file_path = $path;
+/*    $this->image = new \OC_Image();
+    if (!$this->image->loadFromFile($this->file_path)) {
+      \OC_Log::write(TAG, 'Loading file filed', \OC_Log::ERROR);
+      return;
+    }
+    $this->image->fixOrientation();*/
+  }
+
+  public function getWidth() {
+    $a = ThumbnailsManager::getInstance()->getThumbnailInfo($this->file_path);
+    return $a['width'];
+  }
+
+  public function forceSize($width_must_fit=false) {
+      $current_height = $this->image->height();
+      $current_width = $this->image->width();
+      
+      // we need height of 250px but not for tiles stack
+      if ($current_width > $current_height && !$width_must_fit) {
+	      $this->image->resize(floor((250*$current_width)/$current_height));
+      } else {
+		  $this->image->resize(200);
+	  }
+  }
+  
+  public function get($extra = '') {
+	  return '<img src="'.GET_THUMBNAIL_PATH.urlencode($this->getPath()).'" '.$extra.'>';
+  }
+  
+  public function getMiniatureSrc() {
+	  return GET_THUMBNAIL_PATH.urlencode($this->getPath());
+  }
+
+  public function getPath() {
+	  return $this->file_path;
+  }
+
+  private $file_path;
+  private $image;
+}
+
+class TileStack extends TileBase {
+
+  const STACK_REPRESENTATIVES = 3;
+
+  public function __construct($path_array, $stack_name) {
+    $this->tiles_array = array();
+    $this->stack_name = $stack_name;
+    for ($i = 0; $i < count($path_array) && $i < self::STACK_REPRESENTATIVES; $i++) {
+	  $tile = new TileSingle($path_array[$i]);
+      array_push($this->tiles_array, $tile);
+    }
+  }
+  
+  public function forceSize($width_must_fit=false) {
+	  for ($i = 0; $i < count($this->tiles_array); $i++)
+	    $this->tiles_array[$i]->forceSize(true);
+  }
+
+  public function getWidth() {
+    $max = 0;
+    for ($i = 0; $i < count($this->tiles_array); $i++) {
+	    $max = max($max, $this->tiles_array[$i]->getWidth());
+    }
+    return min(200, $max);
+  }
+
+  public function get() {
+    $r = '<div class="title gallery_div">'.$this->stack_name.'</div>';
+    for ($i = 0; $i < count($this->tiles_array); $i++) {
+      $top = rand(-5, 5);
+      $left = rand(-5, 5);
+      $img_w = $this->tiles_array[$i]->getWidth();
+      $extra = '';
+      if ($img_w < 200) {
+	      $extra = 'width:'.$img_w.'px;';
+      }
+      $r .= '<div class="miniature_border gallery_div" style="background-image:url(\''.$this->tiles_array[$i]->getMiniatureSrc().'\');margin-top:'.$top.'px; margin-left:'.$left.'px;'.$extra.'"></div>';
+//      $r .= $this->tiles_array[$i]->get(' style="margin-top:'.$top.'px; margin-left:'.$left.'px; "');
+    }
+    return $r;
+  }
+
+  public function getOnHoverAction() {
+	  return 'javascript:t(this);return false;';
+  }
+  
+  public function getOnOutAction() {
+	  return 'javascript:o(this);return false;';
+  }
+
+  public function getCount() {
+	  return count($this->tiles_array);
+  }
+
+  private $tiles_array;
+  private $stack_name;
+}
+
+?>
diff --git a/apps/gallery/lib/tiles_test.php b/apps/gallery/lib/tiles_test.php
new file mode 100644
index 0000000000000000000000000000000000000000..022a88f75cc2095ce03705ce0c9b778c39d68544
--- /dev/null
+++ b/apps/gallery/lib/tiles_test.php
@@ -0,0 +1,87 @@
+<?php
+$l = OC_L10N::get('gallery');
+?>
+<style>
+div.gallery_div {position:relative; display: inline-block; height: 202px; width: 200px; margin: 5px;}
+div.miniature_border {position:absolute; height: 200px; -webkit-transition-duration: .2s; background-position: 50%;}
+div.line {display:inline-block; border: 0; width: auto; height: 210px}
+div.gallery_div img{position:absolute; top: 1; left: 0; -webkit-transition-duration: 0.3s; height:200px; width: auto;}
+div.gallery_div img.shrinker {width:80px !important;}
+div.title { opacity: 0; text-align: center; vertical-align: middle; font-family: Arial; font-size: 12px; border: 0; position: absolute; text-overflow: ellipsis; bottom: 20px; left:10px; height:auto; padding: 5px; width: 170px; background-color: black; color: white; -webkit-transition: opacity 0.5s;  z-index:1000; border-radius: 7px}
+div.visible { opacity: 0.8;}
+</style>
+<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
+<script type="text/javascript">
+function t(element) {
+	$('div', element).each(function(index, elem) {
+	 	if ($(elem).hasClass('title')) {
+		 	$(elem).addClass('visible');
+	 	} else {
+			$(elem).css('margin-top', Math.floor(30-(Math.random()*60)) + 'px')
+			       .css('margin-left', Math.floor(30-(Math.random()*60))+ 'px')
+			       .css('z-index', '999');
+		}
+	});
+}
+
+function o(element) {
+	$('div', element).each(function(index, elem) {
+	 	if ($(elem).hasClass('title')) {
+		 	$(elem).removeClass('visible');
+	 	} else {
+			$(elem).css('margin-top', Math.floor(5-(Math.random()*10)) + 'px')
+		    	   .css('margin-left', Math.floor(5-(Math.random()*10))+ 'px')
+		    	   .css('z-index', '3');
+		}
+	});
+}
+
+</script>
+
+<?php
+
+include('apps/gallery/lib/tiles.php');
+$root = empty($_GET['root'])?'/':$_GET['root'];
+
+$images = \OC_FileCache::searchByMime('image', null, '/bartek/files'.$root);
+sort($images);
+
+$arr = array();
+$tl = new \OC\Pictures\TilesLine();
+$ts = new \OC\Pictures\TileStack(array(), '');
+$previous_element = $images[0];
+for($i = 0; $i < count($images); $i++) {
+    error_log($images[$i]);
+	$prev_dir_arr = explode('/', $previous_element);
+	$dir_arr = explode('/', $images[$i]);
+
+	if (count($dir_arr)==1) {
+		$tl->addTile(new \OC\Pictures\TileSingle($images[$i]));
+		continue;
+	}
+	if (strcmp($prev_dir_arr[0], $dir_arr[0])!=0) {
+		$tl->addTile(new \OC\Pictures\TileStack($arr, $prev_dir_arr[0]));
+		$arr = array();
+	}
+	$arr[] = $root.$images[$i];
+	$previous_element = $images[$i];
+}
+
+$dir_arr = explode('/', $previous_element);
+
+if (count($dir_arr)==0) {
+	$tl->addTile(new \OC\Pictures\TileSingle($previous_element));
+} else if (count($dir_arr) && $ts->getCount() == 0){
+    $ts = new \OC\Pictures\TileStack(array($previous_element), $dir_arr[0]);
+} else {
+	$arr[] = $previous_element;
+	$ts->addTile($arr);
+}
+
+if ($ts->getCount() != 0) {
+	$tl->addTile($ts);
+}
+
+echo $tl->get();
+
+?>
diff --git a/apps/gallery/templates/index.php b/apps/gallery/templates/index.php
index 99af3bda0a3a00978669c29f70b029b8cf999921..55710038c0a684b1a997e58aae64fe0b054c80b7 100644
--- a/apps/gallery/templates/index.php
+++ b/apps/gallery/templates/index.php
@@ -1,31 +1,86 @@
 <?php
-OCP\Util::addStyle('gallery', 'styles');
-OCP\Util::addscript('gallery', 'albums');
-OCP\Util::addscript('gallery', 'scanner');
-OCP\Util::addscript('gallery', 'album_cover');
-OCP\Util::addStyle('files', 'files');
-OCP\Util::addscript('files_imageviewer', 'jquery.mousewheel-3.0.4.pack');
-OCP\Util::addscript('files_imageviewer', 'jquery.fancybox-1.3.4.pack');
-OCP\Util::addStyle( 'files_imageviewer', 'jquery.fancybox-1.3.4' );
 $l = OC_L10N::get('gallery');
 ?>
-<script type="text/javascript">var gallery_scanning_root='<?php echo OCP\Config::getUserValue(OCP\USER::getUser(), 'gallery', 'root', '/'); ?>'; var gallery_default_order = '<?php echo OCP\Config::getUserValue(OCP\USER::getUser(), 'gallery', 'order', 'ASC'); ?>';</script>
-<div id="controls">
-	<div id="scan">
-		<div id="scanprogressbar"></div>
-		<input type="button" class="start" value="<?php echo $l->t('Rescan');?>" onclick="javascript:scanForAlbums();" />
-    <input type="button" class="stop" style="display:none" value="<?php echo $l->t('Stop');?>" onclick="javascript:Scanner.stop();" />
-    <input type="button" id="g-share-button" value="<?php echo $l->t('Share'); ?>" onclick="javascript:shareGallery();" />
-		<input type="button" id="g-settings-button" value="<?php echo $l->t('Settings');?>" onclick="javascript:settings();"/>
-	</div>
-	<div id="g-album-navigation">
-		<div class="crumb last" style="background-image:url('<?php echo OC::$WEBROOT;?>/core/img/breadcrumb.png')">
-			<a href="javascript:returnToElement(0);">main</a>
-		</div>
-	</div>
-	<div id="g-album-loading" class="crumb" style="display:none">
-		<img src="<?php echo OCP\Util::linkTo('gallery', 'img/loading.gif'); ?>">
-	</div>
-</div>
-<div id="gallery_list">
-</div>
+<style>
+div.gallery_div {position:relative; display: inline-block; height: 202px; width: 200px; margin: 5px;}
+div.miniature_border {position:absolute; height: 200px; -webkit-transition-duration: .2s; background-position: 50%;}
+div.line {display:inline-block; border: 0; width: auto; height: 210px}
+div.gallery_div img{position:absolute; top: 1; left: 0; -webkit-transition-duration: 0.3s; height:200px; width: auto;}
+div.gallery_div img.shrinker {width:80px !important;}
+div.title { opacity: 0; text-align: center; vertical-align: middle; font-family: Arial; font-size: 12px; border: 0; position: absolute; text-overflow: ellipsis; bottom: 20px; left:10px; height:auto; padding: 5px; width: 170px; background-color: black; color: white; -webkit-transition: opacity 0.5s;  z-index:1000; border-radius: 7px}
+div.visible { opacity: 0.8;}
+</style>
+<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
+<script type="text/javascript">
+function t(element) {
+	$('div', element).each(function(index, elem) {
+	 	if ($(elem).hasClass('title')) {
+		 	$(elem).addClass('visible');
+	 	} else {
+			$(elem).css('margin-top', Math.floor(30-(Math.random()*60)) + 'px')
+			       .css('margin-left', Math.floor(30-(Math.random()*60))+ 'px')
+			       .css('z-index', '999');
+		}
+	});
+}
+
+function o(element) {
+	$('div', element).each(function(index, elem) {
+	 	if ($(elem).hasClass('title')) {
+		 	$(elem).removeClass('visible');
+	 	} else {
+			$(elem).css('margin-top', Math.floor(5-(Math.random()*10)) + 'px')
+		    	   .css('margin-left', Math.floor(5-(Math.random()*10))+ 'px')
+		    	   .css('z-index', '3');
+		}
+	});
+}
+
+</script>
+
+<?php
+
+include('apps/gallery/lib/tiles.php');
+$root = empty($_GET['root'])?'/':$_GET['root'];
+
+$images = \OC_FileCache::searchByMime('image', null, '/'.\OCP\USER\getUser().'/files'.$root);
+sort($images);
+
+$arr = array();
+$tl = new \OC\Pictures\TilesLine();
+$ts = new \OC\Pictures\TileStack(array(), '');
+$previous_element = $images[0];
+for($i = 0; $i < count($images); $i++) {
+	$prev_dir_arr = explode('/', $previous_element);
+	$dir_arr = explode('/', $images[$i]);
+
+	if (count($dir_arr)==1) {
+		$tl->addTile(new \OC\Pictures\TileSingle($root.$images[$i]));
+		continue;
+	}
+	if (strcmp($prev_dir_arr[0], $dir_arr[0])!=0) {
+		$tl->addTile(new \OC\Pictures\TileStack($arr, $prev_dir_arr[0]));
+		$arr = array();
+	}
+	$arr[] = $root.$images[$i];
+	$previous_element = $images[$i];
+}
+
+$dir_arr = explode('/', $previous_element);
+
+if (count($dir_arr)==0) {
+	$tl->addTile(new \OC\Pictures\TileSingle($previous_element));
+} else if (count($dir_arr) && $ts->getCount() == 0){
+    $ts = new \OC\Pictures\TileStack(array($root.$previous_element), $dir_arr[0]);
+} else {
+	$arr[] = $previous_element;
+	$ts->addTile($arr);
+}
+
+if ($ts->getCount() != 0) {
+	$tl->addTile($ts);
+}
+
+echo $tl->get();
+
+?>
diff --git a/lib/image.php b/lib/image.php
index 4c53dc32f582bf80de9a83fb21ca703109ed0e39..3150631cc348c7733b7a69a961d28ce6ed790d0c 100644
--- a/lib/image.php
+++ b/lib/image.php
@@ -136,6 +136,8 @@ class OC_Image {
 	*/
 	private function _output($filepath=null) {
 		if($filepath) {
+			if (!file_exists(dirname($filepath)))
+				mkdir(dirname($filepath), 0777, true);
 			if(!is_writable(dirname($filepath))) {
 				OC_Log::write('core',__METHOD__.'(): Directory \''.dirname($filepath).'\' is not writable.', OC_Log::ERROR);
 				return false;
@@ -419,7 +421,7 @@ class OC_Image {
 		if(is_resource($str)) {
 			return false;
 		}
-		$this->resource = imagecreatefromstring($str);
+		$this->resource = @imagecreatefromstring($str);
 		if(!$this->resource) {
 			OC_Log::write('core','OC_Image->loadFromData, couldn\'t load', OC_Log::DEBUG);
 			return false;
@@ -438,7 +440,7 @@ class OC_Image {
 		}
 		$data = base64_decode($str);
 		if($data) { // try to load from string data
-			$this->resource = imagecreatefromstring($data);
+			$this->resource = @imagecreatefromstring($data);
 			if(!$this->resource) {
 				OC_Log::write('core','OC_Image->loadFromBase64, couldn\'t load', OC_Log::DEBUG);
 				return false;
@@ -489,6 +491,32 @@ class OC_Image {
 		return true;
 	}
 
+	public function preciseResize($width, $height) {
+		if (!$this->valid()) {
+			OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR);
+			return false;			
+		}
+		$width_orig=imageSX($this->resource);
+		$height_orig=imageSY($this->resource);
+		$process = imagecreatetruecolor($width, $height);
+
+		if ($process == false) {
+			OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR);
+			imagedestroy($process);
+			return false;
+		}
+
+		imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);
+		if ($process == false) {
+			OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$width.'x'.$height,OC_Log::ERROR);
+			imagedestroy($process);
+			return false;
+		}
+		imagedestroy($this->resource);
+		$this->resource = $process;
+		return true;
+	}
+
 	/**
 	* @brief Crops the image to the middle square. If the image is already square it just returns.
 	* @param int maximum size for the result (optional)