On my server, I have three files per image.
A thumbnail file, which is cropped to 128 by 128.
A small file, which I aspect fit to a max of 160 by 240.
A large file, which I aspect fit to a max of 960 by 540.
My method for returning these URLs to three20's gallery looks like this:
- (NSString*)URLForVersion:(TTPhotoVersion)version {
switch (version) {
case TTPhotoVersionLarge:
return _urlLarge;
case TTPhotoVersionMedium:
return _urlSmall;
case TTPhotoVersionSmall:
return _urlSmall;
case TTPhotoVersionThumbnail:
return _urlThumb;
default:
return nil;
}
}
After having logged when these various values are called, the following happens:
When the thumbnail page loads, only thumbnails are called (as expected)
When an image is tapped, the thumbnail appears, and not the small image.
After that thumbnail appears, the large image is loaded directly (without the small image being displayed).
What I desire to happen is the following
This is the same (thumbnails load as expected on the main page)
When the image is tapped, the small image is loaded first
Then after that, the large image is loaded.
Or, the following
Thumbnails
Straight to large image.
The problem with the thumb, is that I crop it so it is a square.
This means that when a thumbnail image is displayed in the main viewer (after thumb was tapped), it is oversized, and when the large image loads, it immediately scales down to fit.
That looks really bad, and to me, it would make far more sense if it loaded the thumbs in the thumbnail view, and then the small image followed by the large image in the detail view.
Does anyone have any suggestions on how to fix this?
Is the best way simply to make the thumbs the same aspect ratio?
I would appreciate any advice on this issue
Looking at the three20 source I can see that TTPhotoView loads the preview image using the following logic:
- (BOOL)loadPreview:(BOOL)fromNetwork {
if (![self loadVersion:TTPhotoVersionLarge fromNetwork:NO]) {
if (![self loadVersion:TTPhotoVersionSmall fromNetwork:NO]) {
if (![self loadVersion:TTPhotoVersionThumbnail fromNetwork:fromNetwork]) {
return NO;
}
}
}
return YES;
}
The problem is that as your small image is on the server and not locally the code skips the image and uses the Thumbnail for the preview.
I would suggest that your best solution would be to edit the thumbnails so that they have the same aspect ratio as the large images. This is what the developer of this class seems to have expected!
I think you have three ways to go here:
modify the actual loadPreview implementation from TTPhotoView so that it implements the logic you want (i.e., allowing loading the small version from the network);
subclass TTPhotoView and override loadPreview to the same effect as above;
pre-cache the small versions of your photos; i.e, modify/subclass TTThumbView so that when TTPhotoVersionThumbnail is set, it pre-caches the TTPhotoVersionSmall version; in this case, being the image already present locally, loadPreview will find it without needing to go out for the network; as an aside, you might do the pre-caching at any time that you see fit for your app; to pre-cache the image you would create a TTButton with the proper URL (this will both deal with the TTURLRequest and the cache for you);
otherwise, you could do the crop on-the-fly from the small version to the thumbnail version by using this UIImage category; in this case you should also tweak the way your TTThumbView is drawn by overriding its imageForCurrentState method so that the cropping is applied when necessary. Again, either you modify directly TTThumbView or you subclass it; alternatively, you can define layoutSubviews in your photo view controller and modify there each of the TTThumbViews you have:
- (void)layoutSubviews {
[super layoutSubviews];
for (NSInteger i = 0; i < _thumbViews.count; ++i) {
TTThumbView* tv = [_thumbViews objectAtIndex:i];
[tv contentForCurrentState].image = <cropped image>;
If you prefer not using the private method contentForCurrentState, you could simply do:
[tv addSubview:<cropped image>];
As you can see, each option will have its pros and cons; 1 and 2 are the easiest to implement, but the small version will be loaded from the network so it could add some delay; the same holds true for 4, although the approach is different; 3 gives you the most responsive implementation (no additional delay from the network, since you pre-cache), but it is possibly the most complex solution to implement (either you download the image and cache it yourself, or use TTButton to do that for you, which is kind of not very "clean").
Anyway, hope it helps.
Related
Could anyone tell me please which class/method in Magento should be overridden (in a plugin) in order to automatically resize images at upload please ?
The aim is to set a max width/height in Magento Settings and then use these settings in this class/method.
This question has been asked at least 2 or 3 times, but I don't consider "Magento automatically make different versions" as an acceptable answer for multiples reasons:
backend users sometimes upload HD images like 10Mpixels or so
10Mpixels images will NEVER (at least not until a few years) be necessary in a simple web shop
it takes hard disk, backup disk and can fill disk quota quite quickly, locking up Magento
it does not make sense to discuss "why" when the question is just "how"
Thank you :)
We have created a simple Magento plugin to enable image resize at upload. You can configure a max width and height or keep it disabled : https://github.com/ircf/magento-resize-at-upload
This plugin will be added in magento connect soon.
Anyone can choose to use it or not, this is another question.
We have done something similar without Magento but directly PHP, maybe you can do the same, I don't know what your mileage is.
On Linux install ImageMagick and read about the command-line tools on that site here: Magick commands.
Once you know the command syntax you need, follow these steps:
upload binary to temp directory with unique name (use PHP's uniqueid());
fire the conversion with PHP's exec() statement;
move the file to the definitive destination folder (use PHP's rename() statement);
unklink (remove) the temporary file (use PHP's unlink() statement).
This was much work but once done, it works very well now on many systems we have.
Magento's product image resizing is already very powerful and under no circumstances should you be thinking about replacing it. It makes no sense.
Any backend user uploading 10Mega-pixel image without optimisation should not be a web master. Any product photography should always be optimised for the web and even more optimised to fit your stores template.
Lets look at the how from a different angle - You upload a 1200px X 1200px image to magento admin (optimised should be less than 150kb) and while on the frontend your responsive template shows a maximum product image of around 600px the retina version would still be 1200px.
Using the picture element or webkits accepted srcset you can quickly define a new resolution at different screen widths and density all from a single high resolution image since this creates these sizes on the fly and stores in cache.
If storage is an issue you can always flush your cache to save storage and clean up old product images however if storage is a problem I recommend upgrading your hosting as it makes no sense to look for hacks in the system to accommodate poor hardware infrastructure.
Consider using CDN's and other cloud based systems as a means for backups and just ignore the media folder in your own local backups.
I have written code for resizing any products images in Magento 2 according to your desired width and height. I will describe how to resize images In Magento2, Update product image in Magento2, Remove white frame in Magento 2, change product image dimensions Magento 2 such as needing to resize a custom image and display it on the site in special sized areas.
For Magento 2, you can use the following code to resize your images in Magento 2
Step 1: You need to create helper class file Image.php at Vender\Module\Helper\Image.php and the past below code.
< ?php
namespace Vender\Module\Helper;
use Magento\Framework\Filesystem;
use Magento\Framework\Url;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Filesystem\DirectoryList;
class Image extends \Magento\Framework\App\Helper\AbstractHelper {
protected $scopeConfig;
protected $storeManager;
protected $messageManager;
protected $_response;
protected $_resourceConfig;
protected $_responseFactory;
protected $_url;
protected $_filesystem;
protected $_directory;
protected $_imageFactory;
public function __construct(
\Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Framework\Message\ManagerInterface $messageManager,
\Magento\Framework\App\ResponseInterface $response,
\Magento\Framework\App\Config\Storage\WriterInterface $resourceConfig,
\Magento\Framework\App\ResponseFactory $responseFactory,
\Magento\Framework\UrlInterface $url,
\Magento\Framework\Filesystem $filesystem,
\Magento\Framework\Image\AdapterFactory $imageFactory
)
{
$this->scopeConfig = $scopeConfig;
$this->_storeManager=$storeManager;
$this->messageManager=$messageManager;
$this->_response=$response;
$this->_resourceConfig=$resourceConfig;
$this->_responseFactory = $responseFactory;
$this->_url = $url;
$this->_filesystem = $filesystem;
$this->_directory = $filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$this->_imageFactory = $imageFactory;
}
public function imageResize(
$src,
$width=35,
$height=35,
$dir='resized/'
){
$absPath = $this->_filesystem
->getDirectoryRead(DirectoryList::MEDIA)
->getAbsolutePath().$src;
$imageResized = $this->_filesystem
->getDirectoryRead(DirectoryList::MEDIA)
->getAbsolutePath($dir).
$this->getNewDirectoryImage($src);
$imageResize = $this->_imageFactory->create();
$imageResize->open($absPath);
$imageResize->backgroundColor([255, 255, 255]);
$imageResize->constrainOnly(TRUE);
$imageResize->keepTransparency(TRUE);
$imageResize->keepFrame(true);
$imageResize->keepAspectRatio(true);
$imageResize->resize($width,$height);
$dest = $imageResized ;
$imageResize->save($dest);
$resizedURL= $this->_storeManager->getStore()
->getBaseUrl(\Magento\Framework\UrlInterface::URL_TYPE_MEDIA).
$dir.$this->getNewDirectoryImage($src);
return $resizedURL;
}
public function getNewDirectoryImage($src){
$segments = array_reverse(explode('/',$src));
$first_dir = substr($segments[0],0,1);
$second_dir = substr($segments[0],1,1);
return 'cache/'.$first_dir.'/'.$second_dir.'/'.$segments[0];
}
}
Step 2: Using below code you can call above imageResize() method from any class, block or template.
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$imgpath = $objectManager->create('Vender\Module\Helper\Image')->imageResize('IMAGE_PATH','50','50','YOUR_DIR_NAME/');
Now I am going to explain the methods I have used
1. getDirectoryRead()
2. getAbsolutePath()
3. backgroundColor()
4. constrainOnly()
5. keepTransparency()
6. keepFrame()
7. keepAspectRatio()
Magento 2 – Properly remove white image from frame
< ?php foreach ($this->getGalleryImages() as $_image): ?>
•
helper('catalog/image')->init($this->getProduct(), 'thumbnail', $_image->getFile())->resize(56); ?>" width="56" height="56" alt="< ?php echo $this->htmlEscape($_image->getLabel()) ?>" />
< ?php endforeach; ?>
If you want resizing image in magento 2 and you want further information about this then read our blog : HOW TO RESIZE IMAGES IN MAGENTO 2?
I currently have a function in PHP called img(), that takes at least two arguments, as such:
print img("foo.jpg", 100);
That would return something like this:
<img src='/cache/foo_XXXXX.jpg' width='100' height='76' />
So, the function takes the arguments, resize the original file and save it as a cached file on the server and return the image tag. Obviously, if the cached version already exists, it just returns it and saves the processing for the next time.
Now, this works perfectly in 99 cases out of 100, but for some sites there are pages which are quite image-heavy, and since the cached versions of the image is expired after X number of days, the img() functions needs to recreate 50+ high resolution images to small thumbnails, and loading such a page takes several seconds and that's not acceptable.
So my idea is to separate this. So doing this:
print img("foo.jpg", 100);
would return this:
<img src='img.php?ARGUMENTS' width='?' height='?' />
Which means that the img() function doesn't actually handle the file at all, but rather output HTML that tells the browser to get the image from a PHP script rather than a static image file on the server.
Now, the "ARGUMENT" part would of course contain all the information that img.php needs to deal with the file in the same way that img() has dealt with it - perhaps a serialized string version of all the arguments to the function?
But the problem is the width/height values of the image tag! Since processing is moved outside the scope of the current script, it has no idea how large the resulting image would be, right? I obviously can't wait for img.php to complete and return it. I am using imagemagick for the processing (so the second argument can be 100, "50%", "<40" and whatnot) and perhaps there is a way to "dryrun" it with imagemagick - i.e. having imagemagick return the resulting size of a specific file using a specific command?
How would you solve this?
You have the list of all images somewhere in database, right? You can add the fields for width and height and calculate scaled width and height while generating HTML. You could also add thumbnails width and height into database as well.
I'm building a site that depends on bookmarklets. These bookmarklets pull the URL and a couple of other elements. However, I need to select 1 image from the page the user bookmarks. Currently I'm trying to use the PHP Simple HTML DOM Parser http://simplehtmldom.sourceforge.net/
It pulls the HTML as expected, and returns the tags as expected. However, I want to take this a step further and only return images with a min width of 40px. I know about the function getimagesize() but from what I understand, this is resource heavy. Is there a better method available to pre-process the image and achieve the results I'm looking for?
Thanks!
First check if the image HTML tag has a width attribute. If it's above 40, skip over it. As Matthew mentioned, it will get false positives where people sized down a large image to 40px wide, but that's no big deal; the point of this step is to quickly weed out the first dozen or so images that are obviously too big.
Once the script catches an image that SAYS it's under 40px wide, check the header information to deduce a general width based on the size of the file. This is faster than getimagesize because you don't have to download the image to get the info.
function get_image_kb($path) {
$headers = get_headers($path);
$len = explode(" ",$headers[6]);
return $len[1];
}
$imageKb = get_image_kb('test1.jpg');
// I'm going to gander 40x80 is about 2000kb
$cutoffSize = 2000;
if ($imageKb < $cutoffSize) {
// this is the one!
}
else {
// it was a phoney, keep scraping
}
Setting it at 2000kb will also let through images that are 100x30, which isn't good.
However, at this point, you've weeded out most of the huge 800kb files that would really slow you down, and because we know it's under 2kb, it's not too taxing to test this one with getimagesize() to get an accurate width.
You can tweak the process depending on how picky you are for the 40px mark, as usual higher accuracy takes more time, and vice versa.
I am looking to build an app similar to santayourself (facebook application) a screenshot below
The application will accept a photo and then the users can move it with controls for zoom and rotate and moving image right, left, up and down. Then once the face is as required then the user can click save to save the image on the server (php, apache, linux).
Any recommendations on how to go about this project? I guess a javascript solution will be better. Any suggestions welcome.
javascript AND php GD-library would do it - most of the things described above can be done w javascript alone. The fastest way to do this would be to have the santa mask done w a transparent png absolutely placed over a simalarly placed client photo that is however placed in a div the same size as the mask with overflow set to hidden. Since the client phot is absolute within the div it can be moved around and its size can be manipulated by the user through some mechanism as shown above. However - rotation will be a bitch and here you will have to use php gd-library or image majik (personally i would dump rotation). This is a simple-ish job but time consuming - the user-interface to image manipulation is tricky tho. If the output for this is for print-from-screen i would not bother w further server-side manipulation, but rather just store the image to mask positional relationship (1/2 kb) of data...
yep. javascript is the way to go about interactive things like this. I can see this easily being done with a simple script and some PNGs (though you might have to do something creative for the rotation). PHP would only be needed for saving.
EDIT: Actually, now that I think of it, a HTML 5 canvas approach would be best. It's got lots of transformation and pixel-manipulation methods, and can even save the image client-side! Remember, though that HTML 5 is not supported in all browsers (basically everything except IE).
(HTML 5 Canvas Spec)
The drawImage method is what you're looking for:
(I quote from spec)
void drawImage(in HTMLImageElement image, in float dx, in float dy, in optional float dw, in float dh);
So, your HTML would have a canvas element that draws the user's picture:
<canvas id="canvasElement" width="xx px" height="xx px">
<!-- What to display in browsers that don't support canvas -->
<p>Your browser doesn't support canvas</p>
</canvas>
Then, your javascript:
var view;
var context;
var userPhoto=new Image;
userPhoto.src="uploaded.jpg";
// Update these with UI settings
var position = {x:x, y:y};
var scale;
var rotation;
function init() {
// Run this once at the loading of your page
view = document.getElementById("canvasElement");
context = view.getContext("2d");
}
function update() {
// Run this every time you want the picture size, position, rotation updated
context.clearRect(0, 0, view.width, view.height);
// Scale X and Y
context.scale( scale, scale );
// Rotate (convert degrees to radians)
context.rotate( rotation / 3.14159 * 180 )
// Draw the image at X and Y
context.drawImage( userPhoto, position.x, position.y )
}
HTML 5 Canvas is very powerful, so there's tons of other things you can do to your image if you go this direction. However, another viable solution would be to use flash, which is supported everywhere — but I recommend HTML 5 as it is the way of the future (Steve Jobs: Thoughts on Flash).
Have a look at the jCrop library (jQuery), you may be able to tweak it enough to do what you want to do.
http://deepliquid.com/content/Jcrop.html (they obviously supply a few demos)
I have a site where users can upload images. I process these images directly and resize them into 5 additional formats using the CodeIgniter Image Manipulation class. I do this quite efficiently as follow:
I always resize from the previous format, instead of from the original
I resize using an image quality of 90% which about halves the file size of jpegs
The above way of doing things I implemented after advise I got from another question I asked. My test case is a 1.6MB JPEG in RGB mode with a high resolution of 3872 x 2592. For that image, which is kind of borderline case, the resize process in total takes about 2 secs, which is acceptable to me.
Now, only one challenge remains. I want the original file to be compressed using that 90% quality but without resizing it. The idea being that that file too will take half the file size. I figured I could simply resize it to its' current dimensions, but that doesn't seem to do anything to the file or its size. Here's my code, somewhat simplified:
$sourceimage = "test.jpg";
$resize_settings['image_library'] = 'gd2';
$resize_settings['source_image'] = $sourceimage;
$resize_settings['maintain_ratio'] = false;
$resize_settings['quality'] = '90%';
$this->load->library('image_lib', $resize_settings);
$resize_settings['width'] = $imagefile['width'];
$resize_settings['height'] = $imagefile['height'];
$resize_settings['new_image'] = $filename;
$this->image_lib->initialize($resize_settings);
$this->image_lib->resize();
The above code works fine for all formats except the original. I tried debugging into the CI class to see why nothing happens and I noticed that the script detects that the dimensions did not change. Next, it simply makes a copy of that file without processing it at all. I commented that piece of code to force it to resize but now still nothing happens.
Does anybody know how to compress an image (any image, not just jpegs) to 90% using the CI class without changing the dimensions?
I guess you could do something like this:
$original_size = getimagesize('/path/to/original.jpg');
And then set the following options like this:
$resize_settings['width'] = $original_size[0];
$resize_settings['height'] = $original_size[1];
Ok, so that doesn't work due to CI trying to be smart, the way I see it you've three possible options:
Rotate the Image by 360º
Watermark the Image (with a 1x1 Transparent Image)
Do It Yourself
The DIY approach is really simple, I know you don't want to use "custom" functions but take a look:
ImageJPEG(ImageCreateFromString(file_get_contents('/path/to/original.jpg')), '/where/to/save/optimized.jpg', 90);
As you can see, it's even more simpler than using CI.
PS: The snippet above can open any type of image (GIF, PNG and JPEG) and it always saves the image as JPEG with 90% of quality, I believe this is what you're trying to archive.