image: mini image for the profile
CreateFileImageThumbnailSource: Hook to create image thumbnail source from a File
-- $file: MediaFile object with related metadata
+- $file: 'File' object to source the image from
- &$imgPath: Path to image file which can be used as source for our thumbnail algorithm.
- $media: MIME media type ('image', 'video', 'audio' etc.)
{
protected $thumb_w = null; // max width
protected $thumb_h = null; // max height
- protected $thumb_a = null; // "aspect ratio" (more like "square", 1 or 0)
+ protected $thumb_c = null; // crop?
protected function prepare(array $args=array())
{
parent::prepare($args);
- foreach (array('w', 'h', 'a') as $attr) {
- $this->{"thumb_$attr"} = $this->arg($attr);
- }
+ $this->thumb_w = $this->int('w');
+ $this->thumb_h = $this->int('h');
+ $this->thumb_c = $this->boolean('c');
+
return true;
}
function showCore()
{
// Returns a File_thumbnail object or throws exception if not available
- $thumbnail = $this->attachment->getThumbnail($this->thumb_w, $this->thumb_h, $this->thumb_a);
+ $thumbnail = $this->attachment->getThumbnail($this->thumb_w, $this->thumb_h, $this->thumb_c);
$this->element('img', array('src' => $thumbnail->getUrl(), 'alt' => 'Thumbnail'));
}
}
'date' => array('type' => 'int', 'description' => 'date of resource according to http query'),
'protected' => array('type' => 'int', 'description' => 'true when URL is private (needs login)'),
'filename' => array('type' => 'varchar', 'length' => 255, 'description' => 'if a local file, name of the file'),
+ 'width' => array('type' => 'int', 'description' => 'width in pixels, if it can be described as such and data is available'),
+ 'height' => array('type' => 'int', 'description' => 'height in pixels, if it can be described as such and data is available'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
/**
* Get the attachment's thumbnail record, if any.
+ * Make sure you supply proper 'int' typed variables (or null).
*
- * @param $width int Max width of thumbnail in pixels
- * @param $height int Max height of thumbnail in pixels. If null, set to $width
+ * @param $width int Max width of thumbnail in pixels. (if null, use common_config values)
+ * @param $height int Max height of thumbnail in pixels. (if null, square-crop to $width)
+ * @param $crop bool Crop to the max-values' aspect ratio
*
* @return File_thumbnail
*/
- public function getThumbnail($width=null, $height=null)
+ public function getThumbnail($width=null, $height=null, $crop=false)
{
+ if ($this->width < 1 || $this->height < 1) {
+ // Old files may have 0 until migrated with scripts/upgrade.php
+ return null;
+ }
+
if ($width === null) {
- $width = common_config('attachments', 'thumb_width');
- $height = common_config('attachments', 'thumb_height');
- $square = common_config('attachments', 'thumb_square');
- } elseif ($height === null) {
- $square = true;
+ $width = common_config('thumbnail', 'width');
+ $height = common_config('thumbnail', 'height');
+ $crop = common_config('thumbnail', 'crop');
+ }
+
+ if ($height === null) {
+ $height = $width;
+ $crop = true;
+ }
+
+ // Get proper aspect ratio width and height before lookup
+ list($width, $height, $x, $y, $w2, $h2) =
+ ImageFile::getScalingValues($this->width, $this->height, $width, $height, $crop);
+
+ // Doublecheck that parameters are sane and integers.
+ if ($width < 1 || $width > common_config('thumbnail', 'maxsize')
+ || $height < 1 || $height > common_config('thumbnail', 'maxsize')) {
+ // Fail on bad width parameter.
+ throw new ServerException('Bad thumbnail width or height parameter');
}
$params = array('file_id'=> $this->id,
'width' => $width,
- 'height' => $square ? $width : $height);
+ 'height' => $height);
$thumb = File_thumbnail::pkeyGet($params);
if ($thumb === null) {
- // generate a new thumbnail for desired parameters
+ try {
+ $thumb = $this->generateThumbnail($width, $height, $crop);
+ } catch (UnsupportedMediaException $e) {
+ // FIXME: Add "unknown media" icon or something
+ } catch (ServerException $e) {
+ // Probably a remote media file, maybe not available locally
+ }
}
return $thumb;
}
+ /**
+ * Generate and store a thumbnail image for the uploaded file, if applicable.
+ * Call this only if you know what you're doing.
+ *
+ * @param $width int Maximum thumbnail width in pixels
+ * @param $height int Maximum thumbnail height in pixels, if null, crop to $width
+ *
+ * @return File_thumbnail or null
+ */
+ protected function generateThumbnail($width, $height, $crop)
+ {
+ $imgPath = null;
+ $media = common_get_mime_media($this->mimetype);
+ $width = intval($width);
+ if ($height === null) {
+ $height = $width;
+ $crop = true;
+ }
+
+ if (Event::handle('CreateFileImageThumbnailSource', array($this, &$imgPath, $media))) {
+ switch ($media) {
+ case 'image':
+ $imgPath = $this->getPath();
+ break;
+ default:
+ throw new UnsupportedMediaException(_('Unsupported media format.'), $this->getPath());
+ }
+ }
+ if (!file_exists($imgPath)) {
+ throw new ServerException(sprintf('Thumbnail source is not stored locally: %s', $imgPath));
+ }
+
+ try {
+ $image = new ImageFile($this->id, $imgPath);
+ } catch (UnsupportedMediaException $e) {
+ // Avoid deleting the original
+ if ($image->getPath() != $this->getPath()) {
+ $image->unlink();
+ }
+ throw $e;
+ }
+
+ list($width, $height, $x, $y, $w2, $h2) =
+ $image->scaleToFit($width, $height, $crop);
+
+ $outname = "thumb-{$width}x{$height}-" . $this->filename;
+ $outpath = self::path($outname);
+
+ $image->resizeTo($outpath, $width, $height, $x, $y, $w2, $h2);
+
+ // Avoid deleting the original
+ if ($image->getPath() != $this->getPath()) {
+ $image->unlink();
+ }
+ return File_thumbnail::saveThumbnail($this->id,
+ self::url($outname),
+ $width, $height);
+ }
+
public function getPath()
{
return self::path($this->filename);
function _getOembed($url) {
$parameters = array(
- 'maxwidth' => common_config('attachments', 'thumb_width'),
- 'maxheight' => common_config('attachments', 'thumb_height'),
+ 'maxwidth' => common_config('thumbnail', 'width'),
+ 'maxheight' => common_config('thumbnail', 'height'),
);
try {
return oEmbedHelper::getObject($url, $parameters);
class File_thumbnail extends Managed_DataObject
{
- ###START_AUTOCODE
- /* the code below is auto generated do not remove the above tag */
-
public $__table = 'file_thumbnail'; // table name
public $file_id; // int(4) primary_key not_null
public $url; // varchar(255) unique_key
- public $width; // int(4)
- public $height; // int(4)
+ public $width; // int(4) primary_key
+ public $height; // int(4) primary_key
public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP
- /* the code above is auto generated do not remove the tag below */
- ###END_AUTOCODE
-
public static function schemaDef()
{
return array(
'height' => array('type' => 'int', 'description' => 'height of thumbnail'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
- 'primary key' => array('file_id'),
+ 'primary key' => array('file_id', 'width', 'height'),
+ 'indexes' => array(
+ 'file_thumbnail_file_id_idx' => array('file_id'),
+ ),
'foreign keys' => array(
'file_thumbnail_file_id_fkey' => array('file', array('file_id' => 'id')),
)
$tn->width = intval($width);
$tn->height = intval($height);
$tn->insert();
+ return $tn;
}
public function getUrl()
}
function showRepresentation() {
- $thumb = $this->getThumbInfo();
- if ($thumb instanceof File_thumbnail) {
+ try {
+ $thumb = $this->attachment->getThumbnail();
$this->out->element('img', array('alt' => '', 'src' => $thumb->getUrl(), 'width' => $thumb->width, 'height' => $thumb->height));
+ } catch (Exception $e) {
+ // Image representation unavailable
}
}
- /**
- * Pull a thumbnail image reference for the given file, and if necessary
- * resize it to match currently thumbnail size settings.
- *
- * @return File_Thumbnail or false/null
- */
- function getThumbInfo()
- {
- $thumbnail = File_thumbnail::getKV('file_id', $this->attachment->id);
- if ($thumbnail) {
- $maxWidth = common_config('attachments', 'thumb_width');
- $maxHeight = common_config('attachments', 'thumb_height');
- if ($thumbnail->width > $maxWidth) {
- $thumb = clone($thumbnail);
- $thumb->width = $maxWidth;
- $thumb->height = intval($thumbnail->height * $maxWidth / $thumbnail->width);
- return $thumb;
- }
- }
- return $thumbnail;
- }
-
/**
* start a single notice.
*
case 'video/quicktime':
case 'video/webm':
$mediatype = common_get_mime_media($this->attachment->mimetype);
- $thumb = $this->getThumbInfo();
- $poster = ($thumb instanceof File_thumbnail)
- ? $thumb->getUrl()
- : null;
+ try {
+ $thumb = $this->attachment->getThumbnail();
+ $poster = $thumb->getUrl();
+ } catch (Exception $e) {
+ $poster = null;
+ }
$this->out->elementStart($mediatype,
array('class'=>'attachment_player',
'poster'=>$poster,
'monthly_quota' => 15000000,
'uploads' => true,
'show_thumbs' => true, // show thumbnails in notice lists for uploaded images, and photos and videos linked remotely that provide oEmbed info
- 'thumb_width' => 150,
- 'thumb_height' => 150,
- 'thumb_square' => true,
'process_links' => true, // check linked resources for embeddable photos and videos; this will hit referenced external web sites when processing new messages.
),
+ 'thumbnail' =>
+ array('crop' => false, // overridden to true if thumb height === null
+ 'maxsize' => 500, // thumbs bigger than this will not be generated
+ 'width' => 500,
+ 'height' => 250),
'application' =>
array('desclimit' => null),
'group' =>
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* A wrapper on uploaded files
return $num;
}
+
+ public function scaleToFit($maxWidth=null, $maxHeight=null, $crop=null)
+ {
+ return self::getScalingValues($this->width, $this->height,
+ $maxWidth, $maxHeight, $crop);
+ }
+
+ /*
+ * Gets scaling values for images of various types. Cropping can be enabled.
+ *
+ * Values will scale _up_ to fit max values if cropping is enabled!
+ * With cropping disabled, the max value of each axis will be respected.
+ *
+ * @param $width int Original width
+ * @param $height int Original height
+ * @param $maxW int Resulting max width
+ * @param $maxH int Resulting max height
+ * @param $crop int Crop to the size (not preserving aspect ratio)
+ */
+ public static function getScalingValues($width, $height,
+ $maxW=null, $maxH=null,
+ $crop=null)
+ {
+ $maxW = $maxW ?: common_config('thumbnail', 'width');
+ $maxH = $maxH ?: common_config('thumbnail', 'height');
+
+ if ($maxW < 1 || ($maxH !== null && $maxH < 1)) {
+ throw new ServerException('Bad parameters for ImageFile::getScalingValues');
+ } elseif ($maxH === null) {
+ // if maxH is null, we set maxH to equal maxW and enable crop
+ $maxH = $maxW;
+ $crop = true;
+ }
+
+ // Cropping data (for original image size). Default values, 0 and null,
+ // imply no cropping and with preserved aspect ratio (per axis).
+ $cx = 0; // crop x
+ $cy = 0; // crop y
+ $cw = null; // crop area width
+ $ch = null; // crop area height
+
+ if ($crop) {
+ $s_ar = $width / $height;
+ $t_ar = $maxW / $maxH;
+
+ $rw = $maxW;
+ $rh = $maxH;
+
+ // Source aspect ratio differs from target, recalculate crop points!
+ if ($s_ar > $t_ar) {
+ $cx = floor($width / 2 - $height * $t_ar / 2);
+ $cw = ceil($height * $t_ar);
+ } elseif ($s_ar < $t_ar) {
+ $cy = floor($height / 2 - $width / $t_ar / 2);
+ $ch = ceil($width / $t_ar);
+ }
+ } else {
+ $rw = $maxW;
+ $rh = ceil($height * $rw / $width);
+
+ // Scaling caused too large height, decrease to max accepted value
+ if ($rh > $maxH) {
+ $rh = $maxH;
+ $rw = ceil($width * $rh / $height);
+ }
+ }
+ return array(intval($rw), intval($rh),
+ intval($cx), intval($cy),
+ is_null($cw) ? null : intval($cw),
+ is_null($ch) ? null : intval($ch));
+ }
}
//PHP doesn't (as of 2/24/2010) have an imagecreatefrombmp so conditionally define one
// Return image-object
return $image;
}
-}
+} // if(!function_exists('imagecreatefrombmp'))
function show()
{
- $this->thumb = parent::getThumbInfo();
+ $this->thumb = $this->attachment->getThumbnail();
if (!empty($this->thumb)) {
parent::show();
}
var $fileurl = null;
var $short_fileurl = null;
var $mimetype = null;
- var $thumbnailRecord = null;
function __construct(Profile $scoped, $filename = null, $mimetype = null)
{
$this->filename = $filename;
$this->mimetype = $mimetype;
$this->fileRecord = $this->storeFile();
- try {
- $this->thumbnailRecord = $this->storeThumbnail();
- } catch (UnsupportedMediaException $e) {
- // FIXME: Add "unknown media" icon or something
- $this->thumbnailRecord = null;
- }
$this->fileurl = common_local_url('attachment',
array('attachment' => $this->fileRecord->id));
return $file;
}
- /**
- * Generate and store a thumbnail image for the uploaded file, if applicable.
- *
- * @return File_thumbnail or null
- */
- function storeThumbnail($maxWidth=null, $maxHeight=null, $square=true)
- {
- $imgPath = null;
- $media = common_get_mime_media($this->mimetype);
-
- if (Event::handle('CreateFileImageThumbnailSource', array($this, &$imgPath, $media))) {
- switch ($media) {
- case 'image':
- $imgPath = $this->getPath();
- break;
- default:
- throw new UnsupportedMediaException(_('Unsupported media format.'), $this->getPath());
- }
- }
- if (!file_exists($imgPath)) {
- throw new ServerException(sprintf('Thumbnail source is not stored locally: %s', $imgPath));
- }
-
- try {
- $image = new ImageFile($this->fileRecord->id, $imgPath);
- } catch (UnsupportedMediaException $e) {
- // Avoid deleting the original
- if ($image->getPath() != $this->getPath()) {
- $image->unlink();
- }
- throw $e;
- }
-
- $outname = File::filename($this->scoped, 'thumb-' . $this->filename, $this->mimetype);
- $outpath = File::path($outname);
-
- $maxWidth = $maxWidth ?: common_config('attachments', 'thumb_width');
- $maxHeight = $maxHeight ?: common_config('attachments', 'thumb_height');
- list($width, $height, $x, $y, $w2, $h2) =
- $this->scaleToFit($image->width, $image->height,
- $maxWidth, $maxHeight,
- common_config('attachments', 'thumb_square'));
-
- $image->resizeTo($outpath, $width, $height, $x, $y, $w2, $h2);
-
- // Avoid deleting the original
- if ($image->getPath() != $this->getPath()) {
- $image->unlink();
- }
- return File_thumbnail::saveThumbnail($this->fileRecord->id,
- File::url($outname),
- $width,
- $height);
- }
-
- // This will give parameters to scale up if max values are larger than original
- function scaleToFit($width, $height, $maxWidth=null, $maxHeight=null, $square=true)
- {
- if ($width <= 0 || $height <= 0
- || ($maxWidth !== null && $maxWidth <= 0)
- || ($maxHeight !== null && $maxHeight <= 0)) {
- throw new ServerException('Bad scaleToFit parameters for MediaFile');
- } elseif ($maxWidth === null) {
- // maxWidth must be a positive number
- throw new ServerException('MediaFile::scaleToFit maxWidth is null');
- } elseif ($square || $maxHeight === null) {
- // if square thumb ratio or if maxHeight is null,
- // we set maxHeight to equal maxWidth
- $maxHeight = $maxWidth;
- $square = true;
- }
-
- // cropping data
- $cx = 0; // crop x
- $cy = 0; // crop y
- $cw = null; // crop area width
- $ch = null; // crop area height
-
- if ($square) {
- // resulting width and height
- $rw = $maxWidth;
- $rh = $maxHeight;
-
- // minSide will determine the smallest image size
- // and crop-values are determined from this
- $minSide = $width > $height ? $height : $width;
- $cx = $width / 2 - $minSide / 2;
- $cy = $height / 2 - $minSide / 2;
- $cw = $minSide;
- $ch = $minSide;
- } else {
- // resulting sizes
- $rw = $maxWidth;
- $rh = floor($height * $maxWidth / $width);
-
- if ($rh > $maxHeight) {
- $rw = floor($width * $maxHeight / $height);
- $rh = $maxHeight;
- }
- }
- return array($rw, $rh, $cx, $cy, $cw, $ch);
- }
-
function rememberFile($file, $short)
{
$this->maybeAddRedir($file->id, $short);
if (isset($data->thumbnail_url)) {
if (!isset($data->thumbnail_width)) {
// !?!?!
- $data->thumbnail_width = common_config('attachments', 'thumb_width');
- $data->thumbnail_height = common_config('attachments', 'thumb_height');
+ $data->thumbnail_width = common_config('thumbnail', 'width');
+ $data->thumbnail_height = common_config('thumbnail', 'height');
}
}
function scaleImage($width, $height)
{
- $maxwidth = common_config('attachments', 'thumb_width');
- $maxheight = common_config('attachments', 'thumb_height');
+ $maxwidth = common_config('thumbnail', 'width');
+ $maxheight = common_config('thumbnail', 'height');
if ($width > $height && $width > $maxwidth) {
$height = (int) ((((float)$maxwidth)/(float)($width))*(float)$height);
* and disregard any cropping or scaling in the resulting file, as
* that will be handled in the core thumbnail algorithm.
*/
- public function onCreateFileImageThumbnailSource(MediaFile $file, &$imgPath, $media=null)
+ public function onCreateFileImageThumbnailSource(File $file, &$imgPath, $media=null)
{
// The calling function might accidentally pass application/ogg videos.
// If that's a problem, let's fix it in the calling function.
fixupNoticeConversation();
initConversation();
fixupGroupURI();
+ fixupFileGeometry();
initGroupProfileId();
initLocalGroup();
printfnq("DONE.\n");
}
+/*
+ * Added as we now store interpretd width and height in File table.
+ */
+function fixupFileGeometry()
+{
+ printfnq("Ensuring width and height is set for supported local File objects...");
+
+ $file = new File();
+ $file->whereAdd('filename IS NOT NULL'); // local files
+ $file->whereAdd('width IS NULL OR width = 0');
+
+ if ($file->find()) {
+ while ($file->fetch()) {
+ // Add support for video sizes too
+ try {
+ $image = new ImageFile($file->id, $file->getPath());
+ } catch (UnsupportedMediaException $e) {
+ continue;
+ }
+ $orig = clone($file);
+ $file->width = $image->width;
+ $file->height = $image->height;
+ $file->update($orig);
+ }
+ }
+
+ printfnq("DONE.\n");
+}
+
main();