public static async Task GenerateMosaicFromTilesAsync(
            Stream sourceImage,
            CloudBlobContainer tileContainer, string tileDirectory,
            Stream outputStream,
            int tilePixels,
            TraceWriter log)
        {
            using (var tileProvider = new QuadrantMatchingTileProvider()) {
                MosaicBuilder.TileHeight = int.Parse(Environment.GetEnvironmentVariable("MosaicTileWidth"));
                MosaicBuilder.TileWidth  = int.Parse(Environment.GetEnvironmentVariable("MosaicTileHeight"));

                // override default tile width and height if specified
                if (tilePixels != 0)
                {
                    MosaicBuilder.TileWidth = MosaicBuilder.TileHeight = tilePixels;
                }

                MosaicBuilder.DitheringRadius = -1;
                MosaicBuilder.ScaleMultiplier = 1;
                List <byte[]> tileImages = await GetTileImagesAsync(tileContainer, tileDirectory, log);

                tileProvider.SetSourceStream(sourceImage);
                tileProvider.ProcessInputImageColors(MosaicBuilder.TileWidth, MosaicBuilder.TileHeight);
                tileProvider.ProcessTileColors(tileImages);

                log.Info("Generating mosaic...");
                var start = DateTime.Now;

                GenerateMosaic(tileProvider, sourceImage, tileImages, outputStream);

                log.Info($"Time to generate mosaic: {(DateTime.Now - start).TotalMilliseconds}");
            }
        }
Exemple #2
0
        public static void GenerateMosaicFromTiles(
            Stream sourceImage, CloudBlobContainer tileContainer, string tileDirectory, Stream outputStream)
        {
            using (var tileProvider = new QuadrantMatchingTileProvider()) {
                MosaicBuilder.TileHeight      = int.Parse(Environment.GetEnvironmentVariable("MosaicTileWidth"));
                MosaicBuilder.TileWidth       = int.Parse(Environment.GetEnvironmentVariable("MosaicTileHeight"));
                MosaicBuilder.DitheringRadius = -1;
                MosaicBuilder.ScaleMultiplier = 1;

                var directory  = tileContainer.GetDirectoryReference(tileDirectory);
                var blobs      = directory.ListBlobs(true);
                var tileImages = new List <byte[]>();

                foreach (var b in blobs)
                {
                    if (b.GetType() == typeof(CloudBlockBlob))
                    {
                        var blob = (CloudBlockBlob)b;
                        blob.FetchAttributes();

                        var bytes = new byte[blob.Properties.Length];
                        blob.DownloadToByteArray(bytes, 0);

                        tileImages.Add(bytes);
                    }
                }

                tileProvider.SetSourceStream(sourceImage);
                tileProvider.ProcessInputImageColors(MosaicBuilder.TileWidth, MosaicBuilder.TileHeight);
                tileProvider.ProcessTileColors(tileImages);

                GenerateMosaic(tileProvider, sourceImage, tileImages, outputStream);
            }
        }
        private static void GenerateMosaic(QuadrantMatchingTileProvider tileProvider, Stream inputStream, List <byte[]> tileImages, Stream outputStream)
        {
            SKBitmap[,] mosaicTileGrid;

            inputStream.Seek(0, SeekOrigin.Begin);

            using (var skStream = new SKManagedStream(inputStream))
                using (var bitmap = SKBitmap.Decode(skStream)) {
                    // use transparency for the source image overlay
                    var srcImagePaint = new SKPaint()
                    {
                        Color = SKColors.White.WithAlpha(200)
                    };

                    int xTileCount = bitmap.Width / MosaicBuilder.TileWidth;
                    int yTileCount = bitmap.Height / MosaicBuilder.TileHeight;

                    int tileCount = xTileCount * yTileCount;

                    mosaicTileGrid = new SKBitmap[xTileCount, yTileCount];

                    int finalTileWidth  = MosaicBuilder.TileWidth * MosaicBuilder.ScaleMultiplier;
                    int finalTileHeight = MosaicBuilder.TileHeight * MosaicBuilder.ScaleMultiplier;
                    int targetWidth     = xTileCount * finalTileWidth;
                    int targetHeight    = yTileCount * finalTileHeight;

                    var tileList = new List <(int, int)>();

                    // add coordinates for the left corner of each tile
                    for (int x = 0; x < xTileCount; x++)
                    {
                        for (int y = 0; y < yTileCount; y++)
                        {
                            tileList.Add((x, y));
                        }
                    }

                    // create output surface
                    var surface = SKSurface.Create(targetWidth, targetHeight, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
                    surface.Canvas.DrawColor(SKColors.White); // clear the canvas / fill with white
                    surface.Canvas.DrawBitmap(bitmap, 0, 0, srcImagePaint);

                    // using the Darken blend mode causes colors from the source image to come through
                    var tilePaint = new SKPaint()
                    {
                        BlendMode = SKBlendMode.Darken
                    };
                    surface.Canvas.SaveLayer(tilePaint); // save layer so blend mode is applied

                    var random = new Random();

                    while (tileList.Count > 0)
                    {
                        // choose a new tile at random
                        int nextIndex = random.Next(tileList.Count);
                        var tileInfo  = tileList[nextIndex];
                        tileList.RemoveAt(nextIndex);

                        // get the tile image for this point
                        //var exclusionList = GetExclusionList(mosaicTileGrid, tileInfo.Item1, tileInfo.Item2);
                        var tileBitmap = tileProvider.GetImageForTile(tileInfo.Item1, tileInfo.Item2);
                        mosaicTileGrid[tileInfo.Item1, tileInfo.Item2] = tileBitmap;

                        // draw the tile on the surface at the coordinates
                        SKRect tileRect = SKRect.Create(tileInfo.Item1 * TileWidth, tileInfo.Item2 * TileHeight, finalTileWidth, finalTileHeight);
                        surface.Canvas.DrawBitmap(tileBitmap, tileRect);
                    }

                    surface.Canvas.Restore(); // merge layers
                    surface.Canvas.Flush();

                    var imageBytes = surface.Snapshot().Encode(SKEncodedImageFormat.Jpeg, 80);
                    imageBytes.SaveTo(outputStream);
                }
        }