public static AppearsToBeJpeg ( SIL.Windows.Forms.ImageToolbox.PalasoImage imageInfo ) : bool | ||
imageInfo | SIL.Windows.Forms.ImageToolbox.PalasoImage | |
return | bool |
// Make a thumbnail of the input image. newWidth and newHeight are both limits; the image will not // be larger than original, but if necessary will be shrunk to fit within the indicated rectangle. // If parameter 'backColor' is not Empty, we fill the background of the thumbnail with that color. public static bool GenerateThumbnail(string originalPath, string pathToProcessedImage, int newWidth, Color backColor) { using (var originalImage = PalasoImage.FromFileRobustly(originalPath)) { // check if it needs resized if (originalImage.Image.Width <= newWidth) { return(false); } // calculate dimensions var newW = (originalImage.Image.Width > newWidth) ? newWidth : originalImage.Image.Width; // allow for proper rounding from the division var newH = (newW * originalImage.Image.Height + (originalImage.Image.Width / 2)) / originalImage.Image.Width; var thumbnail = new Bitmap(newW, newH); var g = Graphics.FromImage(thumbnail); if (backColor != Color.Empty) { using (var brush = new SolidBrush(backColor)) { g.FillRectangle(brush, new Rectangle(0, 0, newW, newH)); } } Image imageToDraw = originalImage.Image; bool useOriginalImage = ImageUtils.AppearsToBeJpeg(originalImage); if (!useOriginalImage) { imageToDraw = MakePngBackgroundTransparent(originalImage); } var destRect = new Rectangle(0, 0, newW, newH); // Note the image size may change when the background is made transparent. // See https://silbloom.myjetbrains.com/youtrack/issue/BL-5632. g.DrawImage(imageToDraw, destRect, new Rectangle(0, 0, imageToDraw.Width, imageToDraw.Height), GraphicsUnit.Pixel); if (!useOriginalImage) { imageToDraw.Dispose(); } RobustImageIO.SaveImage(thumbnail, pathToProcessedImage); } return(true); }
public string GetPathToResizedImage(string originalPath) { //don't mess with Bloom UI images if (new[] { "/img/", "placeHolder", "Button" }.Any(s => originalPath.Contains(s))) { return(originalPath); } string resizedPath; if (_paths.TryGetValue(originalPath, out resizedPath)) { if (File.Exists(resizedPath) && new FileInfo(originalPath).LastWriteTimeUtc <= new FileInfo(resizedPath).LastWriteTimeUtc) { return(resizedPath); } else { _paths.Remove(originalPath); } } using (var originalImage = PalasoImage.FromFile(originalPath)) { if (!originalPath.Contains("Button") && !ImageUtils.AppearsToBeJpeg(originalImage)) { ((Bitmap)originalImage.Image).MakeTransparent(Color.White); //instead of white, show the background color } if (originalImage.Image.Width > TargetDimension || originalImage.Image.Height > TargetDimension) { var maxDimension = Math.Max(originalImage.Image.Width, originalImage.Image.Height); double shrinkFactor = (TargetDimension / (double)maxDimension); var destWidth = (int)(shrinkFactor * originalImage.Image.Width); var destHeight = (int)(shrinkFactor * originalImage.Image.Height); using (var b = new Bitmap(destWidth, destHeight)) { using (Graphics g = Graphics.FromImage((Image)b)) { //in version 1.0, we used .NearestNeighbor. But if there is a border line down the right size (as is common for thumbnails that, //are, for example, re-inserted into Teacher's Guides), then the line gets cut off. So I switched it to HighQualityBicubic g.InterpolationMode = InterpolationMode.HighQualityBicubic; //.NearestNeighbor;//or smooth it: HighQualityBicubic g.DrawImage(originalImage.Image, 0, 0, destWidth, destHeight); } var temp = Path.Combine(_cacheFolder, Path.GetRandomFileName() + Path.GetExtension(originalPath)); //Hatton July 2012: //Once or twice I saw a GDI+ error on the Save below, when the app 1st launched. //I verified that if there is an IO error, that's what it you get (a GDI+ error). //I looked once, and the %temp%/Bloom directory wasn't there, so that's what I think caused the error. //It's not clear why the temp/bloom directory isn't there... possibly it was there a moment ago //but then some startup thread cleared and deleted it? (we are now running on a thread responding to the http request) Exception error = null; for (int i = 0; i < 5; i++) //try up to five times, a second apart { try { error = null; if (!Directory.Exists(Path.GetDirectoryName(temp))) { Directory.CreateDirectory(Path.GetDirectoryName(temp)); } b.Save(temp, originalImage.Image.RawFormat); break; } catch (Exception e) { Logger.WriteEvent("Error in LowResImage while trying to write image."); Logger.WriteEvent(e.Message); error = e; Thread.Sleep(1000); //wait a second before trying again } } if (error != null) { //NB: this will be on a non-UI thread, so it probably won't work well! ErrorReport.NotifyUserOfProblem(error, "Bloom is having problem saving a low-res version to your temp directory, at " + temp + "\r\n\r\nYou might want to quit and restart Bloom. In the meantime, Bloom will try to use the full-res images."); return(originalPath); } try { _paths.Add(originalPath, temp); //remember it so we can reuse if they show it again, and later delete } catch (Exception) { // it happens sometimes that though it wasn't in the _paths when we entered, it is now // I haven't tracked it down... possibly we get a new request for the image while we're busy compressing it? } return(temp); } } else { return(originalPath); } } }
private bool MakePngBackgroundTransparent(string originalPath, string pathToProcessedImage) { try { using (var originalImage = PalasoImage.FromFileRobustly(originalPath)) { //if it's a jpeg, we don't resize, we don't mess with transparency, nothing. These things //are scary in .net. Just send the original back and wash our hands of it. if (ImageUtils.AppearsToBeJpeg(originalImage)) { return(false); } using (var processedBitmap = MakePngBackgroundTransparent(originalImage)) { //Hatton July 2012: //Once or twice I saw a GDI+ error on the Save below, when the app 1st launched. //I verified that if there is an IO error, that's what you get (a GDI+ error). //I looked once, and the %temp%/Bloom directory wasn't there, so that's what I think caused the error. //It's not clear why the temp/bloom directory isn't there... possibly it was there a moment ago //but then some startup thread cleared and deleted it? (we are now running on a thread responding to the http request) Exception error = null; for (var i = 0; i < 3; i++) //try three times { try { error = null; RobustImageIO.SaveImage(processedBitmap, pathToProcessedImage, originalImage.Image.RawFormat); break; } catch (Exception e) { Logger.WriteEvent("***Error in RuntimeImageProcessor while trying to write image."); Logger.WriteEvent(e.Message); error = e; //in setting the sleep time, keep in mind that this may be one of 20 images //so if the problem happens to all of them, then you're looking 20*retries*sleep-time, //which will look like hung program. //Meanwhile, this transparency thing is actually just a nice-to-have. If we give //up, it's ok. Thread.Sleep(100); //wait a 1/5 second before trying again } } if (error != null) { throw error; //will be caught below } } } return(true); } //we want to gracefully degrade if this fails (as it did once, see comment in bl-2871) catch (TagLib.CorruptFileException e) { NonFatalProblem.Report(ModalIf.Beta, PassiveIf.All, "Problem with image metadata", originalPath, e); return(false); } catch (Exception e) { //while beta might make sense, this is actually //a common failure at the moment, with the license.png //so I'm setting to alpha. NonFatalProblem.Report(ModalIf.Alpha, PassiveIf.All, "Problem making image transparent.", originalPath, e); return(false); } }
// Make a thumbnail of the input image. newWidth and newHeight are both limits; the image will not be larger than original, // but if necessary will be shrunk to fit within the indicated rectangle. public static bool GenerateEBookThumbnail(string coverImagePath, string pathToProcessedImage, int thumbnailWidth, int thumbnailHeight, Color backColor) { using (var coverImage = PalasoImage.FromFileRobustly(coverImagePath)) { var coverImageWidth = coverImage.Image.Width; var coverImageHeight = coverImage.Image.Height; // We want to see a small border of background color, even if the image is a photo. const int kborder = 1; var availableThumbnailWidth = thumbnailWidth - (2 * kborder); var availableThumbnailHeight = thumbnailHeight - (2 * kborder); // Calculate how big the image can be while keeping its original proportions. // First assume the width is the limiting factor var targetImageWidth = (coverImageWidth > availableThumbnailWidth) ? availableThumbnailWidth : coverImage.Image.Width; var targetImageHeight = targetImageWidth * coverImageHeight / coverImageWidth; // if actually the height is the limiting factor, maximize height and re-compute the width if (targetImageHeight > availableThumbnailHeight) { targetImageHeight = availableThumbnailHeight; targetImageWidth = targetImageHeight * coverImageWidth / coverImageHeight; } // pad to center the cover image var horizontalPadding = (availableThumbnailWidth - targetImageWidth) / 2; var verticalPadding = (availableThumbnailHeight - targetImageHeight) / 2; var destRect = new Rectangle(kborder + horizontalPadding, kborder + verticalPadding, targetImageWidth, targetImageHeight); // the decision here is just a heuristic based on the observation that line-drawings seem to look better in nice square block of color, // while full-color (usually jpeg) books look better with a thin (or no) border. We could put this under user control eventually. Rectangle backgroundAndBorderRect; var appearsToBeJpeg = ImageUtils.AppearsToBeJpeg(coverImage); if (appearsToBeJpeg) { backgroundAndBorderRect = destRect; backgroundAndBorderRect.Inflate(kborder * 2, kborder * 2); } else { // or, if we decide to always deliver the full thing: backgroundAndBorderRect = new Rectangle(0, 0, thumbnailWidth, thumbnailHeight); } using (var thumbnail = new Bitmap(thumbnailWidth, thumbnailHeight)) using (var g = Graphics.FromImage(thumbnail)) using (var brush = new SolidBrush(backColor)) { g.FillRectangle(brush, backgroundAndBorderRect); lock (ConvertWhiteToTransparent) { var imageAttributes = appearsToBeJpeg ? null : ConvertWhiteToTransparent; g.DrawImage( coverImage.Image, // finally, draw the cover image destRect, // with a scaled and centered destination 0, 0, coverImageWidth, coverImageHeight, // from the entire cover image, GraphicsUnit.Pixel, imageAttributes); // changing white to transparent if a png } RobustImageIO.SaveImage(thumbnail, pathToProcessedImage); // PNG thumbnails created from jpeg files seem to often be way too big, so try to save them as jpeg // files instead if it saves space. See https://silbloom.myjetbrains.com/youtrack/issue/BL-5605. if (appearsToBeJpeg && Path.GetFileName(pathToProcessedImage) == "thumbnail.png") { var jpgPath = Path.ChangeExtension(pathToProcessedImage, "jpg"); RobustImageIO.SaveImage(thumbnail, jpgPath, ImageFormat.Jpeg); var infoPng = new FileInfo(pathToProcessedImage); var infoJpg = new FileInfo(jpgPath); //Debug.WriteLine(String.Format("thumbnail.png size={0}; thumbnail.jpg size={1} (using smaller)", infoPng.Length, infoJpg.Length)); if (infoJpg.Length < infoPng.Length) { File.Delete(pathToProcessedImage); } else { File.Delete(jpgPath); } } } } return(true); }