OSDN Git Service

pic.twitter.com へのアップロード時にPNG画像がJPEGに変換されるのを回避する機能を追加
authorKimura Youichi <kim.upsilon@bucyou.net>
Wed, 4 Jan 2017 13:55:56 +0000 (22:55 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Mon, 9 Jan 2017 08:51:14 +0000 (17:51 +0900)
アルファチャンネルを持つ PNG 画像に対しては JPEG への変換が行われないことを
利用して、PNG 画像のアップロード時には左上 1px のアルファ値を 254 に書き換える
ことで画質の劣化を回避する

この機能はデフォルトで無効とし、SettingCommon.xml 内の AlphaPNGWorkaround を
true に設定することで使用できる(後で設定画面に項目を追加するかも)

OpenTween/Resources/ChangeLog.txt
OpenTween/Setting/SettingCommon.cs
OpenTween/Twitter.cs

index 2d740c0..8339d03 100644 (file)
@@ -1,6 +1,7 @@
 更新履歴
 
 ==== Ver 1.3.7-dev(2016/xx/xx)
+ * NEW: PNG画像のアップロード時にJPEGへの変換による劣化を回避する機能を追加しました (pic.twitter.com のみ)
  * FIX: OSの設定で小数点にピリオド以外の記号を用いている環境で、Foursquareのサムネイル表示時にエラーが発生する不具合を修正 (thx @Xiatian!)
 
 ==== Ver 1.3.6(2016/11/22)
index 1a38d49..3343199 100644 (file)
@@ -246,6 +246,9 @@ namespace OpenTween
         public bool IsUseNotifyGrowl = false;
         public bool ForceIPv4 = false;
         public bool ErrorReportAnonymous = true;
+
+        /// <summary>pic.twitter.com への画像アップロード時に JPEG への変換を回避する</summary>
+        public bool AlphaPNGWorkaround { get; set; } = false;
     }
 
     public class UserAccount
index b562b7e..f9ff9cd 100644 (file)
@@ -50,6 +50,7 @@ using OpenTween.Api;
 using OpenTween.Api.DataModel;
 using OpenTween.Connection;
 using OpenTween.Models;
+using System.Drawing.Imaging;
 
 namespace OpenTween
 {
@@ -300,12 +301,32 @@ namespace OpenTween
             this.previousStatusId = status.Id;
         }
 
-        public async Task<long> UploadMedia(IMediaItem item)
+        public Task<long> UploadMedia(IMediaItem item)
+            => this.UploadMedia(item, SettingCommon.Instance.AlphaPNGWorkaround);
+
+        public async Task<long> UploadMedia(IMediaItem item, bool alphaPNGWorkaround)
         {
             this.CheckAccountState();
 
-            var response = await this.Api.MediaUpload(item)
-                .ConfigureAwait(false);
+            LazyJson<TwitterUploadMediaResult> response;
+
+            using (var origImage = item.CreateImage())
+            {
+                MemoryImage newImage;
+                if (alphaPNGWorkaround && this.AddAlphaChannelIfNeeded(origImage.Image, out newImage))
+                {
+                    using (var newMediaItem = new MemoryImageMediaItem(newImage))
+                    {
+                        response = await this.Api.MediaUpload(newMediaItem)
+                            .ConfigureAwait(false);
+                    }
+                }
+                else
+                {
+                    response = await this.Api.MediaUpload(item)
+                        .ConfigureAwait(false);
+                }
+            }
 
             var media = await response.LoadJsonAsync()
                 .ConfigureAwait(false);
@@ -313,6 +334,47 @@ namespace OpenTween
             return media.MediaId;
         }
 
+        /// <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;
+            }
+        }
+
         public async Task SendDirectMessage(string postStr)
         {
             this.CheckAccountState();