//private unsafe byte getColor(int x, int y, FreeImageBitmap src, Channel channel) //{ // int bpp = 4; // if (src.PixelFormat == FreeImageAPI.PixelFormat.Format24bppRgb) // { // bpp = 3; // } // byte* pixel = (byte*)src.Bits.ToPointer(); // pixel += (src.Pitch * (src.Height - y - 1) + x) * bpp; // switch (channel) // { // case Channel.Alpha: // return pixel[3]; // case Channel.Red: // return pixel[2]; // case Channel.Green: // return pixel[1]; // case Channel.Blue: // return pixel[0]; // } // throw new NotSupportedException(); //Won't get here //} private async Task saveUncompressed(String sourceFile, String destFile, bool lossless, FREE_IMAGE_FILTER filter, ImagePageSizeStrategy pageSizeStrategy, Action <FreeImageBitmap> afterResize = null) { await Task.Run(() => { if ((outputFormats & OutputFormats.Uncompressed) != 0) { Log.Info("Creating paged data for {0}", sourceFile); using (FreeImageBitmap source = FreeImageBitmap.FromFile(sourceFile)) { using (var stream = File.Open(String.Format(PagedTextureNameFormat, destFile), FileMode.Create, FileAccess.ReadWrite)) { PagedImage.fromBitmap(source, 128, 1, stream, PagedImage.ImageType.WEBP, maxSize, lossless, filter, pageSizeStrategy, afterResize); } } } }); }
/// <summary> /// Create a paged image from a FreeImageBitmap instance, note that this function is destructive /// to the passed in FreeImageBitmap since it will resize it while creating all the mip levels /// for the paged image. However, you will still need to dispose the image you pass in, this class /// makes a copy while destroying the image, but does not take ownership of the original. /// </summary> /// <param name="image">The image to extract pages from.</param> /// <param name="pageSize">The size of the pages to extract.</param> public static void fromBitmap(FreeImageBitmap image, int pageSize, int padding, Stream stream, ImageType imageType, int maxSize, bool lossless, FREE_IMAGE_FILTER filter, ImagePageSizeStrategy pageSizeStrategy, Action <FreeImageBitmap> afterResize = null) { stream.Seek(HeaderSize, SeekOrigin.Begin); //Leave some space for the header if (image.Width > maxSize) { Logging.Log.Info("Image size {0} was too large, resizing to {1}", image.Width, maxSize); pageSizeStrategy.rescaleImage(image, new Size(maxSize, maxSize), filter); } PagedImage pagedImage = new PagedImage(); int padding2x = padding * 2; FREE_IMAGE_FORMAT outputFormat; FREE_IMAGE_SAVE_FLAGS saveFlags = FREE_IMAGE_SAVE_FLAGS.DEFAULT; switch (imageType) { default: case ImageType.PNG: outputFormat = FREE_IMAGE_FORMAT.FIF_PNG; break; case ImageType.WEBP: outputFormat = FREE_IMAGE_FORMAT.FIF_WEBP; if (lossless) { saveFlags = FREE_IMAGE_SAVE_FLAGS.WEBP_LOSSLESS; } else { saveFlags = (FREE_IMAGE_SAVE_FLAGS)90; } break; } //Kind of weird, ogre and freeimage are backwards from one another in terms of scanline 0 being the top or bottom //This easily fixes the math below by just flipping the image first. Note that pages must be flipped back over because //of this when they are saved. image.RotateFlip(RotateFlipType.RotateNoneFlipY); pagedImage.numImages = 0; pagedImage.imageType = (int)imageType; pagedImage.imageXSize = image.Width; pagedImage.imageYSize = image.Height; pagedImage.pageSize = pageSize; if (pagedImage.imageXSize != pagedImage.imageYSize) { throw new InvalidDataException("Image must be a square"); } int mipLevelCount = 0; int numPages = 0; for (int i = pagedImage.imageXSize; i >= pageSize; i >>= 1) { ++mipLevelCount; int pagesForMip = i / pageSize; numPages += pagesForMip * pagesForMip; } pagedImage.pages = new List <ImageInfo>(numPages); pagedImage.mipIndices = new List <MipIndexInfo>(mipLevelCount); IntSize2 fullPageSize = new IntSize2(pageSize + padding2x, pageSize + padding2x); for (int mip = 0; mip < mipLevelCount; ++mip) { //Setup mip level if (mip != 0) { pageSizeStrategy.rescaleImage(image, new Size(image.Width >> 1, image.Height >> 1), filter); if (afterResize != null) { //Flip so external functions aren't confused image.RotateFlip(RotateFlipType.RotateNoneFlipY); afterResize(image); //Flip back for correct math image.RotateFlip(RotateFlipType.RotateNoneFlipY); } } int size = image.Width / pageSize; pagedImage.mipIndices.Add(new MipIndexInfo(pagedImage.numImages, size)); pageSizeStrategy.extractPage(image, padding, stream, pagedImage, pageSize, fullPageSize, size, outputFormat, saveFlags); } //Write half size mip int halfPageSize = pageSize >> 1; IntSize2 halfSizePadded = new IntSize2(halfPageSize + padding2x, halfPageSize + padding2x); pageSizeStrategy.rescaleImage(image, new Size(image.Width >> 1, image.Height >> 1), filter); pageSizeStrategy.extractPage(image, padding, stream, pagedImage, halfPageSize, halfSizePadded, 1, outputFormat, saveFlags); pagedImage.indexStart = (int)stream.Position; using (BinaryWriter sw = new BinaryWriter(stream, EncodingShim.Default, true)) { foreach (var imageInfo in pagedImage.pages) { imageInfo.write(sw); } //Go back to header reserved space sw.BaseStream.Seek(0, SeekOrigin.Begin); sw.Write(MagicNumber); sw.Write(pagedImage.numImages); sw.Write(pagedImage.imageType); sw.Write(pagedImage.imageXSize); sw.Write(pagedImage.imageYSize); sw.Write(pagedImage.pageSize); sw.Write(pagedImage.indexStart); } }