Exemplo n.º 1
0
        /// <summary>
        /// pic.twitter.com アップロード時に JPEG への変換を回避するための加工を行う
        /// </summary>
        /// <remarks>
        /// pic.twitter.com へのアップロード時に、アルファチャンネルを持たない PNG 画像が
        /// JPEG 形式に変換され画質が低下する問題を回避します。
        /// PNG 以外の画像や、すでにアルファチャンネルを持つ PNG 画像に対しては何もしません。
        /// </remarks>
        /// <returns>加工が行われた場合は true、そうでない場合は false</returns>
        private bool AddAlphaChannelIfNeeded(Image origImage, out MemoryImage newImage)
        {
            newImage = null;

            // PNG 画像以外に対しては何もしない
            if (origImage.RawFormat.Guid != ImageFormat.Png.Guid)
            {
                return(false);
            }

            using (var bitmap = new Bitmap(origImage))
            {
                // アルファ値が 255 以外のピクセルが含まれていた場合は何もしない
                foreach (var x in Enumerable.Range(0, bitmap.Width))
                {
                    foreach (var y in Enumerable.Range(0, bitmap.Height))
                    {
                        if (bitmap.GetPixel(x, y).A != 255)
                        {
                            return(false);
                        }
                    }
                }

                // 左上の 1px だけアルファ値を 254 にする
                var pixel    = bitmap.GetPixel(0, 0);
                var newPixel = Color.FromArgb(pixel.A - 1, pixel.R, pixel.G, pixel.B);
                bitmap.SetPixel(0, 0, newPixel);

                // MemoryImage 作成時に画像はコピーされるため、この後 bitmap は破棄しても問題ない
                newImage = MemoryImage.CopyFromImage(bitmap);

                return(true);
            }
        }
Exemplo n.º 2
0
        public override async Task <MemoryImage> LoadThumbnailImageAsync(HttpClient http, CancellationToken cancellationToken)
        {
            // 画像中央に描画されるタイル (ピクセル単位ではなくタイル番号を表す)
            // タイル番号に小数部が含まれているが、これはタイル内の相対的な位置を表すためこのまま保持する
            var centerTileNum = this.WorldToTilePos(this.Longitude, this.Latitude, this.Zoom);

            // 画像左上に描画されるタイル
            var topLeftTileNum = PointF.Add(centerTileNum, new SizeF(-this.ThumbnailSize.Width / 2.0f / TileSize.Width, -this.ThumbnailSize.Height / 2.0f / TileSize.Height));

            // タイル番号の小数部をもとに、タイル画像を描画する際のピクセル単位のオフセットを算出する
            var tileOffset = Size.Round(new SizeF(-TileSize.Width * (topLeftTileNum.X - (int)topLeftTileNum.X), -TileSize.Height * (topLeftTileNum.Y - (int)topLeftTileNum.Y)));

            // 縦横のタイル枚数
            var tileCountX = (int)Math.Ceiling((double)(this.ThumbnailSize.Width + Math.Abs(tileOffset.Width)) / TileSize.Width);
            var tileCountY = (int)Math.Ceiling((double)(this.ThumbnailSize.Height + Math.Abs(tileOffset.Height)) / TileSize.Height);

            // 読み込む対象となるタイル画像が 10 枚を越えていたら中断
            // ex. 一辺が 512px 以内のサムネイル画像を生成する場合、必要なタイル画像は最大で 9 枚
            if (tileCountX * tileCountY > 10)
            {
                throw new OperationCanceledException();
            }

            // タイル画像を読み込む
            var tilesTask = new Task <MemoryImage> [tileCountX, tileCountY];

            foreach (var x in Enumerable.Range(0, tileCountX))
            {
                foreach (var y in Enumerable.Range(0, tileCountY))
                {
                    var tilePos = Point.Add(Point.Truncate(topLeftTileNum), new Size(x, y));
                    tilesTask[x, y] = this.LoadTileImageAsync(http, tilePos);
                }
            }

            await Task.WhenAll(tilesTask.Cast <Task <MemoryImage> >())
            .ConfigureAwait(false);

            using (var bitmap = new Bitmap(this.ThumbnailSize.Width, this.ThumbnailSize.Height))
            {
                using (var g = Graphics.FromImage(bitmap))
                {
                    g.TranslateTransform(tileOffset.Width, tileOffset.Height);

                    foreach (var x in Enumerable.Range(0, tileCountX))
                    {
                        foreach (var y in Enumerable.Range(0, tileCountY))
                        {
                            using (var image = tilesTask[x, y].Result)
                            {
                                g.DrawImage(image.Image, TileSize.Width * x, TileSize.Height * y);
                            }
                        }
                    }
                }

                MemoryImage result = null;
                try
                {
                    result = MemoryImage.CopyFromImage(bitmap);
                    return(result);
                }
                catch { result?.Dispose(); throw; }
            }
        }