コード例 #1
0
        /// <summary>
        /// For electronic books, we want to minimize the actual size of images since they'll
        /// be displayed on small screens anyway.  So before zipping up the file, we replace its
        /// bytes with the bytes of a reduced copy of itself.  If the original image is already
        /// small enough, we return its bytes directly.
        /// We also make png images have transparent backgrounds. This is currently only necessary
        /// for cover pages, but it's an additional complication to detect which those are,
        /// and doesn't seem likely to cost much extra to do.
        /// </summary>
        /// <returns>The bytes of the (possibly) reduced image.</returns>
        internal static byte[] GetBytesOfReducedImage(string filePath)
        {
            using (var originalImage = PalasoImage.FromFileRobustly(filePath))
            {
                var image           = originalImage.Image;
                int originalWidth   = image.Width;
                int originalHeight  = image.Height;
                var appearsToBeJpeg = ImageUtils.AppearsToBeJpeg(originalImage);
                if (originalWidth > kMaxWidth || originalHeight > kMaxHeight || !appearsToBeJpeg)
                {
                    // Preserve the aspect ratio
                    float scaleX = (float)kMaxWidth / (float)originalWidth;
                    float scaleY = (float)kMaxHeight / (float)originalHeight;
                    // no point in ever expanding, even if we're making a new image just for transparency.
                    float scale = Math.Min(1.0f, Math.Min(scaleX, scaleY));

                    // New width and height maintaining the aspect ratio
                    int newWidth         = (int)(originalWidth * scale);
                    int newHeight        = (int)(originalHeight * scale);
                    var imagePixelFormat = image.PixelFormat;
                    switch (imagePixelFormat)
                    {
                    // These three formats are not supported for bitmaps to be drawn on using Graphics.FromImage.
                    // So use the default bitmap format.
                    // Enhance: if these are common it may be worth research to find out whether there are better options.
                    // - possibly the 'reduced' image might not be reduced...even though smaller, the indexed format
                    // might be so much more efficient that it is smaller. However, even if that is true, it doesn't
                    // necessarily follow that it takes less memory to render on the device. So it's not obvious that
                    // we should keep the original just because it's a smaller file.
                    // - possibly we don't need a 32-bit bitmap? Unfortunately the 1bpp/4bpp/8bpp only tells us
                    // that the image uses two, 16, or 256 distinct colors, not what they are or what precision they have.
                    case PixelFormat.Format1bppIndexed:
                    case PixelFormat.Format4bppIndexed:
                    case PixelFormat.Format8bppIndexed:
                        imagePixelFormat = PixelFormat.Format32bppArgb;
                        break;
                    }
                    // OTOH, always using 32-bit format for .png files keeps us from having problems in BloomReader
                    // like BL-5740 (where 24bit format files came out in BR with black backgrounds).
                    if (!appearsToBeJpeg)
                    {
                        imagePixelFormat = PixelFormat.Format32bppArgb;
                    }

                    using (var newImage = new Bitmap(newWidth, newHeight, imagePixelFormat))
                    {
                        // Draws the image in the specified size with quality mode set to HighQuality
                        using (Graphics graphics = Graphics.FromImage(newImage))
                        {
                            graphics.CompositingQuality = CompositingQuality.HighQuality;
                            graphics.InterpolationMode  = InterpolationMode.HighQualityBicubic;
                            graphics.SmoothingMode      = SmoothingMode.HighQuality;
                            using (var imageAttributes = new ImageAttributes())
                            {
                                // See https://stackoverflow.com/a/11850971/7442826
                                // Fixes the 50% gray border issue on bright white or dark images
                                imageAttributes.SetWrapMode(WrapMode.TileFlipXY);

                                // In addition to possibly scaling, we want PNG images to have transparent backgrounds.
                                if (!appearsToBeJpeg)
                                {
                                    // This specifies that all white or very-near-white pixels (all color components at least 253/255)
                                    // will be made transparent.
                                    imageAttributes.SetColorKey(Color.FromArgb(253, 253, 253), Color.White);
                                }
                                var destRect = new Rectangle(0, 0, newWidth, newHeight);
                                graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height,
                                                   GraphicsUnit.Pixel, imageAttributes);
                            }
                        }
                        // Save the file in the same format as the original, and return its bytes.
                        using (var tempFile = TempFile.WithExtension(Path.GetExtension(filePath)))
                        {
                            // This uses default quality settings for jpgs...one site says this is
                            // 75 quality on a scale that runs from 0-100. For most images, this
                            // should give a quality barely distinguishable from uncompressed and still save
                            // about 7/8 of the file size. Lower quality settings rapidly lose quality
                            // while only saving a little space; higher ones rapidly use more space
                            // with only marginal quality improvement.
                            // See  https://photo.stackexchange.com/questions/30243/what-quality-to-choose-when-converting-to-jpg
                            // for more on quality and  https://docs.microsoft.com/en-us/dotnet/framework/winforms/advanced/how-to-set-jpeg-compression-level
                            // for how to control the quality setting if we decide to (RobustImageIO has
                            // suitable overloads).
                            RobustImageIO.SaveImage(newImage, tempFile.Path, image.RawFormat);
                            // Copy the metadata from the original file to the new file.
                            var metadata = SIL.Windows.Forms.ClearShare.Metadata.FromFile(filePath);
                            if (!metadata.IsEmpty)
                            {
                                metadata.Write(tempFile.Path);
                            }
                            return(RobustFile.ReadAllBytes(tempFile.Path));
                        }
                    }
                }
            }
            return(RobustFile.ReadAllBytes(filePath));
        }