[dw-free] cleaning up userpics code
[commit: http://hg.dwscoalition.org/dw-free/rev/b29d6cfc5a83]
http://bugs.dwscoalition.org/show_bug.cgi?id=513
Move icon resizing code to the LJ::Userpic package.
Patch by
kareila.
Files modified:
http://bugs.dwscoalition.org/show_bug.cgi?id=513
Move icon resizing code to the LJ::Userpic package.
Patch by
![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Files modified:
- bin/worker/userpic-resize-gm
- cgi-bin/LJ/Userpic.pm
- cgi-bin/ljuserpics.pl
- htdocs/editicons.bml
- htdocs/misc/mogupic.bml
- htdocs/tools/userpicfactory.bml
-------------------------------------------------------------------------------- diff -r 62e8e4716e81 -r b29d6cfc5a83 bin/worker/userpic-resize-gm --- a/bin/worker/userpic-resize-gm Wed Sep 08 14:21:07 2010 +0800 +++ b/bin/worker/userpic-resize-gm Wed Sep 08 14:41:32 2010 +0800 @@ -26,6 +26,6 @@ sub lj_upf_resize { sub lj_upf_resize { my $job = shift; my $args = eval { Storable::thaw($job->arg) } || []; - return Storable::nfreeze(LJ::_get_upf_scaled(@$args)); + return Storable::nfreeze( LJ::Userpic->_get_upf_scaled( @$args ) ); } diff -r 62e8e4716e81 -r b29d6cfc5a83 cgi-bin/LJ/Userpic.pm --- a/cgi-bin/LJ/Userpic.pm Wed Sep 08 14:21:07 2010 +0800 +++ b/cgi-bin/LJ/Userpic.pm Wed Sep 08 14:41:32 2010 +0800 @@ -711,6 +711,184 @@ sub create { return $upic; } +# this will return a user's userpicfactory image stored in mogile scaled down. +# if only $size is passed, will return image scaled so the largest dimension will +# not be greater than $size. If $x1, $y1... are set then it will return the image +# scaled so the largest dimension will not be greater than 100 +# all parameters are optional, default size is 640. +# +# if maxfilesize option is passed, get_upf_scaled will decrease the image quality +# until it reaches maxfilesize, in kilobytes. (only applies to the 100x100 userpic) +# +# returns [imageref, mime, width, height] on success, undef on failure. +# +# note: this will always keep the image's original aspect ratio and not distort it. +sub get_upf_scaled { + my ( $class, @args ) = @_; + + my $gc = LJ::gearman_client(); + + # no gearman, do this in-process + return $class->_get_upf_scaled( @args ) unless $gc; + + # invoke gearman + my $u = LJ::get_remote() or die "No remote user"; + unshift @args, "userid" => $u->id; + + my $result; + my $arg = Storable::nfreeze(\@args); + my $task = Gearman::Task->new('lj_upf_resize', \$arg, + { + uniq => '-', + on_complete => sub { + my $res = shift; + return unless $res; + $result = Storable::thaw($$res); + } + }); + + my $ts = $gc->new_task_set(); + $ts->add_task($task); + $ts->wait(timeout => 30); # 30 sec timeout; + + # job failed ... error reporting? + die "Could not resize image down\n" unless $result; + + return $result; +} + +# actual method +sub _get_upf_scaled +{ + my ( $class, %opts ) = @_; + my $size = delete $opts{size} || 640; + my $x1 = delete $opts{x1}; + my $y1 = delete $opts{y1}; + my $x2 = delete $opts{x2}; + my $y2 = delete $opts{y2}; + my $border = delete $opts{border} || 0; + my $maxfilesize = delete $opts{maxfilesize} || 38; + my $u = LJ::want_user(delete $opts{userid} || delete $opts{u}) || LJ::get_remote(); + my $mogkey = delete $opts{mogkey}; + my $downsize_only = delete $opts{downsize_only}; + croak "No userid or remote" unless $u || $mogkey; + + $maxfilesize *= 1024; + + croak "Invalid parameters to get_upf_scaled\n" if scalar keys %opts; + + my $mode = ($x1 || $y1 || $x2 || $y2) ? "crop" : "scale"; + + eval "use Image::Magick (); 1;" + or return undef; + + eval "use Image::Size (); 1;" + or return undef; + + $mogkey ||= 'upf:' . $u->{userid}; + my $dataref = LJ::mogclient()->get_file_data($mogkey) or return undef; + + # original width/height + my ($ow, $oh) = Image::Size::imgsize($dataref); + return undef unless $ow && $oh; + + # converts an ImageMagick object to the form returned to our callers + my $imageParams = sub { + my $im = shift; + my $blob = $im->ImageToBlob; + return [\$blob, $im->Get('MIME'), $im->Get('width'), $im->Get('height')]; + }; + + # compute new width and height while keeping aspect ratio + my $getSizedCoords = sub { + my $newsize = shift; + + my $fromw = $ow; + my $fromh = $oh; + + my $img = shift; + if ($img) { + $fromw = $img->Get('width'); + $fromh = $img->Get('height'); + } + + return (int($newsize * $fromw/$fromh), $newsize) if $fromh > $fromw; + return ($newsize, int($newsize * $fromh/$fromw)); + }; + + # get the "medium sized" width/height. this is the size which + # the user selects from + my ($medw, $medh) = $getSizedCoords->($size); + return undef unless $medw && $medh; + + # simple scaling mode + if ($mode eq "scale") { + my $image = Image::Magick->new(size => "${medw}x${medh}") + or return undef; + $image->BlobToImage($$dataref); + unless ($downsize_only && ($medw > $ow || $medh > $oh)) { + $image->Resize(width => $medw, height => $medh); + } + return $imageParams->($image); + } + + # else, we're in 100x100 cropping mode + + # scale user coordinates up from the medium pixelspace to full pixelspace + $x1 *= ($ow/$medw); + $x2 *= ($ow/$medw); + $y1 *= ($oh/$medh); + $y2 *= ($oh/$medh); + + # cropping dimensions from the full pixelspace + my $tw = $x2 - $x1; + my $th = $y2 - $y1; + + # but if their selected region in full pixelspace is 800x800 or something + # ridiculous, no point decoding the JPEG to its full size... we can + # decode to a smaller size so we get 100px when we crop + my $min_dim = $tw < $th ? $tw : $th; + my ($decodew, $decodeh) = ($ow, $oh); + my $wanted_size = 100; + if ($min_dim > $wanted_size) { + # then let's not decode the full JPEG down from its huge size + my $de_scale = $wanted_size / $min_dim; + $decodew = int($de_scale * $decodew); + $decodeh = int($de_scale * $decodeh); + $_ *= $de_scale foreach ($x1, $x2, $y1, $y2); + } + + $_ = int($_) foreach ($x1, $x2, $y1, $y2, $tw, $th); + + # make the pristine (uncompressed) 100x100 image + my $timage = Image::Magick->new(size => "${decodew}x${decodeh}") + or return undef; + $timage->BlobToImage($$dataref); + $timage->Scale(width => $decodew, height => $decodeh); + + my $w = ($x2 - $x1); + my $h = ($y2 - $y1); + my $foo = $timage->Mogrify(crop => "${w}x${h}+$x1+$y1"); + + my $targetSize = $border ? 98 : 100; + + my ($nw, $nh) = $getSizedCoords->($targetSize, $timage); + $timage->Scale(width => $nw, height => $nh); + + # add border if desired + $timage->Border(geometry => "1x1", color => 'black') if $border; + + foreach my $qual (qw(100 90 85 75)) { + # work off a copy of the image so we aren't recompressing it + my $piccopy = $timage->Clone(); + $piccopy->Set('quality' => $qual); + my $ret = $imageParams->($piccopy); + return $ret if length(${ $ret->[0] }) < $maxfilesize; + } + + return undef; +} + # make this picture the default sub make_default { my $self = shift; diff -r 62e8e4716e81 -r b29d6cfc5a83 cgi-bin/ljuserpics.pl --- a/cgi-bin/ljuserpics.pl Wed Sep 08 14:21:07 2010 +0800 +++ b/cgi-bin/ljuserpics.pl Wed Sep 08 14:41:32 2010 +0800 @@ -354,186 +354,5 @@ sub get_picid_from_keyword return $default; } -# this will return a user's userpicfactory image stored in mogile scaled down. -# if only $size is passed, will return image scaled so the largest dimension will -# not be greater than $size. If $x1, $y1... are set then it will return the image -# scaled so the largest dimension will not be greater than 100 -# all parameters are optional, default size is 640. -# -# if maxfilesize option is passed, get_upf_scaled will decrease the image quality -# until it reaches maxfilesize, in kilobytes. (only applies to the 100x100 userpic) -# -# returns [imageref, mime, width, height] on success, undef on failure. -# -# note: this will always keep the image's original aspect ratio and not distort it. -sub get_upf_scaled { - my @args = @_; - - my $gc = LJ::gearman_client(); - - # no gearman, do this in-process - return LJ::_get_upf_scaled(@args) - unless $gc; - - # invoke gearman - my $u = LJ::get_remote() - or die "No remote user"; - unshift @args, "userid" => $u->id; - - my $result; - my $arg = Storable::nfreeze(\@args); - my $task = Gearman::Task->new('lj_upf_resize', \$arg, - { - uniq => '-', - on_complete => sub { - my $res = shift; - return unless $res; - $result = Storable::thaw($$res); - } - }); - - my $ts = $gc->new_task_set(); - $ts->add_task($task); - $ts->wait(timeout => 30); # 30 sec timeout; - - # job failed ... error reporting? - die "Could not resize image down\n" unless $result; - - return $result; -} - -# actual method -sub _get_upf_scaled -{ - my %opts = @_; - my $size = delete $opts{size} || 640; - my $x1 = delete $opts{x1}; - my $y1 = delete $opts{y1}; - my $x2 = delete $opts{x2}; - my $y2 = delete $opts{y2}; - my $border = delete $opts{border} || 0; - my $maxfilesize = delete $opts{maxfilesize} || 38; - my $u = LJ::want_user(delete $opts{userid} || delete $opts{u}) || LJ::get_remote(); - my $mogkey = delete $opts{mogkey}; - my $downsize_only = delete $opts{downsize_only}; - croak "No userid or remote" unless $u || $mogkey; - - $maxfilesize *= 1024; - - croak "Invalid parameters to get_upf_scaled\n" if scalar keys %opts; - - my $mode = ($x1 || $y1 || $x2 || $y2) ? "crop" : "scale"; - - eval "use Image::Magick (); 1;" - or return undef; - - eval "use Image::Size (); 1;" - or return undef; - - $mogkey ||= 'upf:' . $u->{userid}; - my $dataref = LJ::mogclient()->get_file_data($mogkey) or return undef; - - # original width/height - my ($ow, $oh) = Image::Size::imgsize($dataref); - return undef unless $ow && $oh; - - # converts an ImageMagick object to the form returned to our callers - my $imageParams = sub { - my $im = shift; - my $blob = $im->ImageToBlob; - return [\$blob, $im->Get('MIME'), $im->Get('width'), $im->Get('height')]; - }; - - # compute new width and height while keeping aspect ratio - my $getSizedCoords = sub { - my $newsize = shift; - - my $fromw = $ow; - my $fromh = $oh; - - my $img = shift; - if ($img) { - $fromw = $img->Get('width'); - $fromh = $img->Get('height'); - } - - return (int($newsize * $fromw/$fromh), $newsize) if $fromh > $fromw; - return ($newsize, int($newsize * $fromh/$fromw)); - }; - - # get the "medium sized" width/height. this is the size which - # the user selects from - my ($medw, $medh) = $getSizedCoords->($size); - return undef unless $medw && $medh; - - # simple scaling mode - if ($mode eq "scale") { - my $image = Image::Magick->new(size => "${medw}x${medh}") - or return undef; - $image->BlobToImage($$dataref); - unless ($downsize_only && ($medw > $ow || $medh > $oh)) { - $image->Resize(width => $medw, height => $medh); - } - return $imageParams->($image); - } - - # else, we're in 100x100 cropping mode - - # scale user coordinates up from the medium pixelspace to full pixelspace - $x1 *= ($ow/$medw); - $x2 *= ($ow/$medw); - $y1 *= ($oh/$medh); - $y2 *= ($oh/$medh); - - # cropping dimensions from the full pixelspace - my $tw = $x2 - $x1; - my $th = $y2 - $y1; - - # but if their selected region in full pixelspace is 800x800 or something - # ridiculous, no point decoding the JPEG to its full size... we can - # decode to a smaller size so we get 100px when we crop - my $min_dim = $tw < $th ? $tw : $th; - my ($decodew, $decodeh) = ($ow, $oh); - my $wanted_size = 100; - if ($min_dim > $wanted_size) { - # then let's not decode the full JPEG down from its huge size - my $de_scale = $wanted_size / $min_dim; - $decodew = int($de_scale * $decodew); - $decodeh = int($de_scale * $decodeh); - $_ *= $de_scale foreach ($x1, $x2, $y1, $y2); - } - - $_ = int($_) foreach ($x1, $x2, $y1, $y2, $tw, $th); - - # make the pristine (uncompressed) 100x100 image - my $timage = Image::Magick->new(size => "${decodew}x${decodeh}") - or return undef; - $timage->BlobToImage($$dataref); - $timage->Scale(width => $decodew, height => $decodeh); - - my $w = ($x2 - $x1); - my $h = ($y2 - $y1); - my $foo = $timage->Mogrify(crop => "${w}x${h}+$x1+$y1"); - - my $targetSize = $border ? 98 : 100; - - my ($nw, $nh) = $getSizedCoords->($targetSize, $timage); - $timage->Scale(width => $nw, height => $nh); - - # add border if desired - $timage->Border(geometry => "1x1", color => 'black') if $border; - - foreach my $qual (qw(100 90 85 75)) { - # work off a copy of the image so we aren't recompressing it - my $piccopy = $timage->Clone(); - $piccopy->Set('quality' => $qual); - my $ret = $imageParams->($piccopy); - return $ret if length(${ $ret->[0] }) < $maxfilesize; - } - - return undef; -} - - 1; diff -r 62e8e4716e81 -r b29d6cfc5a83 htdocs/editicons.bml --- a/htdocs/editicons.bml Wed Sep 08 14:21:07 2010 +0800 +++ b/htdocs/editicons.bml Wed Sep 08 14:41:32 2010 +0800 @@ -200,7 +200,7 @@ use strict; return $err->("Invalid userpic creation parameters.") if (!$scaledsizemax || !$x2); my $picinfo = eval { - LJ::get_upf_scaled( + LJ::Userpic->get_upf_scaled( x1 => $x1, y1 => $y1, x2 => $x2, @@ -802,7 +802,7 @@ sub parse_post_uploads } eval { - my $picinfo = LJ::get_upf_scaled( + my $picinfo = LJ::Userpic->get_upf_scaled( size => 100, u => $u, mogkey => "upf_${counter}:$u->{userid}" diff -r 62e8e4716e81 -r b29d6cfc5a83 htdocs/misc/mogupic.bml --- a/htdocs/misc/mogupic.bml Wed Sep 08 14:21:07 2010 +0800 +++ b/htdocs/misc/mogupic.bml Wed Sep 08 14:41:32 2010 +0800 @@ -28,7 +28,7 @@ _c?><?_code my $size = int($GET{'size'}); $size = 640 if $size <= 0 || $size > 640; - my $upf = LJ::get_upf_scaled(size => $size, userid => $u->id, mogkey => "upf_$GET{index}:$u->{userid}"); + my $upf = LJ::Userpic->get_upf_scaled( size => $size, userid => $u->id, mogkey => "upf_$GET{index}:$u->{userid}" ); return $ML{'.error.image'} unless $upf; diff -r 62e8e4716e81 -r b29d6cfc5a83 htdocs/tools/userpicfactory.bml --- a/htdocs/tools/userpicfactory.bml Wed Sep 08 14:21:07 2010 +0800 +++ b/htdocs/tools/userpicfactory.bml Wed Sep 08 14:41:32 2010 +0800 @@ -42,7 +42,7 @@ _c?> if (!$w || !$h) { # we do not have the width and height passed in, we have to compute it ourselves - my $upf = LJ::get_upf_scaled(size => $scaledSizeMax, userid => $remote->{userid}, mogkey => "upf_$GET{index}:$remote->{userid}"); + my $upf = LJ::Userpic->get_upf_scaled( size => $scaledSizeMax, userid => $remote->userid, mogkey => "upf_$GET{index}:$remote->{userid}" ); ($w, $h) = ($upf->[2], $upf->[3]) if ($upf && $upf->[2]); } --------------------------------------------------------------------------------