diff --git a/apps/files/css/detailsView.css b/apps/files/css/detailsView.css
index ffead92312..8eded7acda 100644
--- a/apps/files/css/detailsView.css
+++ b/apps/files/css/detailsView.css
@@ -19,6 +19,27 @@
float: left;
}
+#app-sidebar .thumbnailContainer.image {
+ margin-left: -15px;
+ margin-right: -35px; /* 15 + 20 for the close button */
+ margin-top: -15px;
+}
+
+#app-sidebar .image .thumbnail {
+ width:100%;
+ display:block;
+ height: 250px;
+ background-repeat: no-repeat;
+ background-position: 50% top;
+ background-size: 100%;
+ float: none;
+ margin: 0;
+}
+
+#app-sidebar .image.portrait .thumbnail {
+ background-size: contain;
+}
+
#app-sidebar .thumbnail {
width: 75px;
height: 75px;
diff --git a/apps/files/js/fileinfomodel.js b/apps/files/js/fileinfomodel.js
index 05060854fb..1c850239cd 100644
--- a/apps/files/js/fileinfomodel.js
+++ b/apps/files/js/fileinfomodel.js
@@ -52,6 +52,15 @@
return this.get('mimetype') === 'httpd/unix-directory';
},
+ /**
+ * Returns whether this file is an image
+ *
+ * @return {boolean} true if this is an image, false otherwise
+ */
+ isImage: function() {
+ return this.get('mimetype').substr(0, 6) === 'image/';
+ },
+
/**
* Returns the full path to this file
*
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index ac96d58701..9593ee79e6 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -1359,6 +1359,12 @@
if (options.y) {
urlSpec.y = options.y;
}
+ if (options.a) {
+ urlSpec.a = options.a;
+ }
+ if (options.mode) {
+ urlSpec.mode = options.mode;
+ }
if (etag){
// use etag as cache buster
@@ -1377,9 +1383,14 @@
img.onload = function(){
// if loading the preview image failed (no preview for the mimetype) then img.width will < 5
if (img.width > 5) {
- ready(previewURL);
+ ready(previewURL, img);
+ } else if (options.error) {
+ options.error();
}
};
+ if (options.error) {
+ img.onerror = options.error;
+ }
img.src = previewURL;
},
diff --git a/apps/files/js/mainfileinfodetailview.js b/apps/files/js/mainfileinfodetailview.js
index f647a0de2a..785eed8d71 100644
--- a/apps/files/js/mainfileinfodetailview.js
+++ b/apps/files/js/mainfileinfodetailview.js
@@ -10,7 +10,7 @@
(function() {
var TEMPLATE =
- '' +
+ '
' +
'
{{name}}
' +
'
' +
@@ -106,6 +106,7 @@
if (this.model) {
var isFavorite = (this.model.get('tags') || []).indexOf(OC.TAG_FAVORITE) >= 0;
this.$el.html(this.template({
+ type: this.model.isImage()? 'image': '',
nameLabel: t('files', 'Name'),
name: this.model.get('displayName') || this.model.get('name'),
pathLabel: t('files', 'Path'),
@@ -123,16 +124,51 @@
// TODO: we really need OC.Previews
var $iconDiv = this.$el.find('.thumbnail');
+ $iconDiv.addClass('icon-loading');
+ $container = this.$el.find('.thumbnailContainer');
if (!this.model.isDirectory()) {
this._fileList.lazyLoadPreview({
path: this.model.getFullPath(),
mime: this.model.get('mimetype'),
etag: this.model.get('etag'),
- x: 75,
- y: 75,
- callback: function(previewUrl) {
- $iconDiv.css('background-image', 'url("' + previewUrl + '")');
- }
+ y: this.model.isImage() ? 250: 75,
+ x: this.model.isImage() ? 99999 /* only limit on y */ : 75,
+ a: this.model.isImage() ? 1 : null,
+ callback: function(previewUrl, img) {
+ $iconDiv.previewImg = previewUrl;
+ if (img) {
+ $iconDiv.removeClass('icon-loading');
+ if(img.height > img.width) {
+ $container.addClass('portrait');
+ }
+ }
+ if (this.model.isImage() && img) {
+ $iconDiv.parent().addClass('image');
+ var targetHeight = img.height / window.devicePixelRatio;
+ if (targetHeight <= 75) {
+ $container.removeClass('image'); // small enough to fit in normaly
+ targetHeight = 75;
+ }
+ } else {
+ targetHeight = 75;
+ }
+
+ // only set background when we have an actual preview
+ // when we dont have a preview we show the mime icon in the error handler
+ if (img) {
+ $iconDiv.css({
+ 'background-image': 'url("' + previewUrl + '")',
+ 'height': targetHeight
+ });
+ }
+ }.bind(this),
+ error: function() {
+ $iconDiv.removeClass('icon-loading');
+ this.$el.find('.thumbnailContainer').removeClass('image'); //fall back to regular view
+ $iconDiv.css({
+ 'background-image': 'url("' + $iconDiv.previewImg + '")'
+ });
+ }.bind(this)
});
} else {
// TODO: special icons / shared / external
diff --git a/core/ajax/preview.php b/core/ajax/preview.php
index fc98d80eb0..baa0ed4ec6 100644
--- a/core/ajax/preview.php
+++ b/core/ajax/preview.php
@@ -31,6 +31,7 @@ $maxY = array_key_exists('y', $_GET) ? (int)$_GET['y'] : '36';
$scalingUp = array_key_exists('scalingup', $_GET) ? (bool)$_GET['scalingup'] : true;
$keepAspect = array_key_exists('a', $_GET) ? true : false;
$always = array_key_exists('forceIcon', $_GET) ? (bool)$_GET['forceIcon'] : true;
+$mode = array_key_exists('mode', $_GET) ? $_GET['mode'] : 'fill';
if ($file === '') {
//400 Bad Request
@@ -56,6 +57,7 @@ if (!$info instanceof OCP\Files\FileInfo || !$always && !\OC::$server->getPrevie
$preview->setMaxX($maxX);
$preview->setMaxY($maxY);
$preview->setScalingUp($scalingUp);
+ $preview->setMode($mode);
$preview->setKeepAspect($keepAspect);
$preview->showPreview();
}
diff --git a/lib/private/preview.php b/lib/private/preview.php
index 5dcab476a4..978da1161c 100644
--- a/lib/private/preview.php
+++ b/lib/private/preview.php
@@ -38,6 +38,9 @@ class Preview {
//the thumbnail folder
const THUMBNAILS_FOLDER = 'thumbnails';
+ const MODE_FILL = 'fill';
+ const MODE_COVER = 'cover';
+
//config
private $maxScaleFactor;
/** @var int maximum width allowed for a preview */
@@ -56,6 +59,7 @@ class Preview {
private $scalingUp;
private $mimeType;
private $keepAspect = false;
+ private $mode = self::MODE_FILL;
//used to calculate the size of the preview to generate
/** @var int $maxPreviewWidth max width a preview can have */
@@ -331,6 +335,19 @@ class Preview {
return $this;
}
+ /**
+ * Set whether to cover or fill the specified dimensions
+ *
+ * @param string $mode
+ *
+ * @return \OC\Preview
+ */
+ public function setMode($mode) {
+ $this->mode = $mode;
+
+ return $this;
+ }
+
/**
* Sets whether we need to generate a preview which keeps the aspect ratio of the original file
*
@@ -531,14 +548,22 @@ class Preview {
* @param int $askedWidth
* @param int $askedHeight
*
+ * @param int $originalWidth
+ * @param int $originalHeight
* @return \int[]
*/
- private function applyAspectRatio($askedWidth, $askedHeight) {
- $originalRatio = $this->maxPreviewWidth / $this->maxPreviewHeight;
+ private function applyAspectRatio($askedWidth, $askedHeight, $originalWidth = 0, $originalHeight = 0) {
+ if(!$originalWidth){
+ $originalWidth= $this->maxPreviewWidth;
+ }
+ if (!$originalHeight) {
+ $originalHeight = $this->maxPreviewHeight;
+ }
+ $originalRatio = $originalWidth / $originalHeight;
// Defines the box in which the preview has to fit
$scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
- $askedWidth = min($askedWidth, $this->maxPreviewWidth * $scaleFactor);
- $askedHeight = min($askedHeight, $this->maxPreviewHeight * $scaleFactor);
+ $askedWidth = min($askedWidth, $originalWidth * $scaleFactor);
+ $askedHeight = min($askedHeight, $originalHeight * $scaleFactor);
if ($askedWidth / $originalRatio < $askedHeight) {
// width restricted
@@ -550,6 +575,32 @@ class Preview {
return [(int)$askedWidth, (int)$askedHeight];
}
+ /**
+ * Resizes the boundaries to cover the area
+ *
+ * @param int $askedWidth
+ * @param int $askedHeight
+ * @param int $previewWidth
+ * @param int $previewHeight
+ * @return \int[]
+ */
+ private function applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight) {
+ $originalRatio = $previewWidth / $previewHeight;
+ // Defines the box in which the preview has to fit
+ $scaleFactor = $this->scalingUp ? $this->maxScaleFactor : 1;
+ $askedWidth = min($askedWidth, $previewWidth * $scaleFactor);
+ $askedHeight = min($askedHeight, $previewHeight * $scaleFactor);
+
+ if ($askedWidth / $originalRatio > $askedHeight) {
+ // height restricted
+ $askedHeight = round($askedWidth / $originalRatio);
+ } else {
+ $askedWidth = round($askedHeight * $originalRatio);
+ }
+
+ return [(int)$askedWidth, (int)$askedHeight];
+ }
+
/**
* Makes sure an upscaled preview doesn't end up larger than the max dimensions defined in the
* config
@@ -791,7 +842,15 @@ class Preview {
*/
if ($this->keepAspect) {
list($askedWidth, $askedHeight) =
- $this->applyAspectRatio($askedWidth, $askedHeight);
+ $this->applyAspectRatio($askedWidth, $askedHeight, $previewWidth, $previewHeight);
+ }
+
+ if ($this->mode === self::MODE_COVER) {
+ list($scaleWidth, $scaleHeight) =
+ $this->applyCover($askedWidth, $askedHeight, $previewWidth, $previewHeight);
+ } else {
+ $scaleWidth = $askedWidth;
+ $scaleHeight = $askedHeight;
}
/**
@@ -799,7 +858,7 @@ class Preview {
* Takes the scaling ratio into consideration
*/
list($newPreviewWidth, $newPreviewHeight) = $this->scale(
- $image, $askedWidth, $askedHeight, $previewWidth, $previewHeight
+ $image, $scaleWidth, $scaleHeight, $previewWidth, $previewHeight
);
// The preview has been resized and should now have the asked dimensions
@@ -1000,6 +1059,9 @@ class Preview {
if ($this->keepAspect && !$isMaxPreview) {
$previewPath .= '-with-aspect';
}
+ if ($this->mode === self::MODE_COVER) {
+ $previewPath .= '-cover';
+ }
$previewPath .= '.png';
return $previewPath;