aboutsummaryrefslogtreecommitdiff
path: root/Emby.Drawing.Skia/SkiaEncoder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Emby.Drawing.Skia/SkiaEncoder.cs')
-rw-r--r--Emby.Drawing.Skia/SkiaEncoder.cs244
1 files changed, 165 insertions, 79 deletions
diff --git a/Emby.Drawing.Skia/SkiaEncoder.cs b/Emby.Drawing.Skia/SkiaEncoder.cs
index 071c40c29..0eaa96609 100644
--- a/Emby.Drawing.Skia/SkiaEncoder.cs
+++ b/Emby.Drawing.Skia/SkiaEncoder.cs
@@ -74,8 +74,9 @@ namespace Emby.Drawing.Skia
return typeof(SKBitmap).GetTypeInfo().Assembly.GetName().Version.ToString();
}
- private static bool IsWhiteSpace(SKColor color)
+ private static bool IsTransparent(SKColor color)
{
+
return (color.Red == 255 && color.Green == 255 && color.Blue == 255) || color.Alpha == 0;
}
@@ -96,11 +97,11 @@ namespace Emby.Drawing.Skia
}
}
- private static bool IsAllWhiteRow(SKBitmap bmp, int row)
+ private static bool IsTransparentRow(SKBitmap bmp, int row)
{
for (var i = 0; i < bmp.Width; ++i)
{
- if (!IsWhiteSpace(bmp.GetPixel(i, row)))
+ if (!IsTransparent(bmp.GetPixel(i, row)))
{
return false;
}
@@ -108,11 +109,11 @@ namespace Emby.Drawing.Skia
return true;
}
- private static bool IsAllWhiteColumn(SKBitmap bmp, int col)
+ private static bool IsTransparentColumn(SKBitmap bmp, int col)
{
for (var i = 0; i < bmp.Height; ++i)
{
- if (!IsWhiteSpace(bmp.GetPixel(col, i)))
+ if (!IsTransparent(bmp.GetPixel(col, i)))
{
return false;
}
@@ -125,7 +126,7 @@ namespace Emby.Drawing.Skia
var topmost = 0;
for (int row = 0; row < bitmap.Height; ++row)
{
- if (IsAllWhiteRow(bitmap, row))
+ if (IsTransparentRow(bitmap, row))
topmost = row + 1;
else break;
}
@@ -133,7 +134,7 @@ namespace Emby.Drawing.Skia
int bottommost = bitmap.Height;
for (int row = bitmap.Height - 1; row >= 0; --row)
{
- if (IsAllWhiteRow(bitmap, row))
+ if (IsTransparentRow(bitmap, row))
bottommost = row;
else break;
}
@@ -141,7 +142,7 @@ namespace Emby.Drawing.Skia
int leftmost = 0, rightmost = bitmap.Width;
for (int col = 0; col < bitmap.Width; ++col)
{
- if (IsAllWhiteColumn(bitmap, col))
+ if (IsTransparentColumn(bitmap, col))
leftmost = col + 1;
else
break;
@@ -149,7 +150,7 @@ namespace Emby.Drawing.Skia
for (int col = bitmap.Width - 1; col >= 0; --col)
{
- if (IsAllWhiteColumn(bitmap, col))
+ if (IsTransparentColumn(bitmap, col))
rightmost = col;
else
break;
@@ -192,16 +193,31 @@ namespace Emby.Drawing.Skia
{
using (var stream = new SKFileStream(path))
{
- var codec = SKCodec.Create(stream);
+ using (var codec = SKCodec.Create(stream))
+ {
+ if (codec == null)
+ {
+ origin = SKCodecOrigin.TopLeft;
+ return null;
+ }
- // create the bitmap
- var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
- // decode
- codec.GetPixels(bitmap.Info, bitmap.GetPixels());
+ // create the bitmap
+ var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
- origin = codec.Origin;
+ if (bitmap != null)
+ {
+ // decode
+ codec.GetPixels(bitmap.Info, bitmap.GetPixels());
- return bitmap;
+ origin = codec.Origin;
+ }
+ else
+ {
+ origin = SKCodecOrigin.TopLeft;
+ }
+
+ return bitmap;
+ }
}
}
@@ -238,7 +254,7 @@ namespace Emby.Drawing.Skia
return Decode(path, forceAnalyzeBitmap, out origin);
}
- private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient, ImageOrientation? orientation)
+ private SKBitmap GetBitmap(string path, bool cropWhitespace, bool autoOrient)
{
SKCodecOrigin origin;
@@ -246,11 +262,14 @@ namespace Emby.Drawing.Skia
{
var bitmap = GetBitmap(path, cropWhitespace, true, out origin);
- if (origin != SKCodecOrigin.TopLeft)
+ if (bitmap != null)
{
- using (bitmap)
+ if (origin != SKCodecOrigin.TopLeft)
{
- return RotateAndFlip(bitmap, origin);
+ using (bitmap)
+ {
+ return OrientImage(bitmap, origin);
+ }
}
}
@@ -260,82 +279,153 @@ namespace Emby.Drawing.Skia
return GetBitmap(path, cropWhitespace, false, out origin);
}
- private SKBitmap RotateAndFlip(SKBitmap original, SKCodecOrigin origin)
+ private SKBitmap OrientImage(SKBitmap bitmap, SKCodecOrigin origin)
{
- // these are the origins that represent a 90 degree turn in some fashion
- var differentOrientations = new SKCodecOrigin[]
+ //var transformations = {
+ // 2: { rotate: 0, flip: true},
+ // 3: { rotate: 180, flip: false},
+ // 4: { rotate: 180, flip: true},
+ // 5: { rotate: 90, flip: true},
+ // 6: { rotate: 90, flip: false},
+ // 7: { rotate: 270, flip: true},
+ // 8: { rotate: 270, flip: false},
+ //}
+
+ switch (origin)
{
- SKCodecOrigin.LeftBottom,
- SKCodecOrigin.LeftTop,
- SKCodecOrigin.RightBottom,
- SKCodecOrigin.RightTop
- };
- // check if we need to turn the image
- bool isDifferentOrientation = differentOrientations.Any(o => o == origin);
+ case SKCodecOrigin.TopRight:
+ {
+ var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
+ using (var surface = new SKCanvas(rotated))
+ {
+ surface.Translate(rotated.Width, 0);
+ surface.Scale(-1, 1);
+ surface.DrawBitmap(bitmap, 0, 0);
+ }
+
+ return rotated;
+ }
- // define new width/height
- var width = isDifferentOrientation ? original.Height : original.Width;
- var height = isDifferentOrientation ? original.Width : original.Height;
+ case SKCodecOrigin.BottomRight:
+ {
+ var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
+ using (var surface = new SKCanvas(rotated))
+ {
+ float px = bitmap.Width;
+ px /= 2;
- var bitmap = new SKBitmap(width, height, true);
+ float py = bitmap.Height;
+ py /= 2;
- // todo: the stuff in this switch statement should be rewritten to use pointers
- switch (origin)
- {
- case SKCodecOrigin.LeftBottom:
+ surface.RotateDegrees(180, px, py);
+ surface.DrawBitmap(bitmap, 0, 0);
+ }
- for (var x = 0; x < original.Width; x++)
- for (var y = 0; y < original.Height; y++)
- bitmap.SetPixel(y, original.Width - 1 - x, original.GetPixel(x, y));
- break;
+ return rotated;
+ }
- case SKCodecOrigin.RightTop:
+ case SKCodecOrigin.BottomLeft:
+ {
+ var rotated = new SKBitmap(bitmap.Width, bitmap.Height);
+ using (var surface = new SKCanvas(rotated))
+ {
+ float px = bitmap.Width;
+ px /= 2;
- for (var x = 0; x < original.Width; x++)
- for (var y = 0; y < original.Height; y++)
- bitmap.SetPixel(original.Height - 1 - y, x, original.GetPixel(x, y));
- break;
+ float py = bitmap.Height;
+ py /= 2;
- case SKCodecOrigin.RightBottom:
+ surface.Translate(rotated.Width, 0);
+ surface.Scale(-1, 1);
- for (var x = 0; x < original.Width; x++)
- for (var y = 0; y < original.Height; y++)
- bitmap.SetPixel(original.Height - 1 - y, original.Width - 1 - x, original.GetPixel(x, y));
+ surface.RotateDegrees(180, px, py);
+ surface.DrawBitmap(bitmap, 0, 0);
+ }
- break;
+ return rotated;
+ }
case SKCodecOrigin.LeftTop:
+ {
+ // TODO: Remove dual canvases, had trouble with flipping
+ using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width))
+ {
+ using (var surface = new SKCanvas(rotated))
+ {
+ surface.Translate(rotated.Width, 0);
- for (var x = 0; x < original.Width; x++)
- for (var y = 0; y < original.Height; y++)
- bitmap.SetPixel(y, x, original.GetPixel(x, y));
- break;
+ surface.RotateDegrees(90);
- case SKCodecOrigin.BottomLeft:
+ surface.DrawBitmap(bitmap, 0, 0);
- for (var x = 0; x < original.Width; x++)
- for (var y = 0; y < original.Height; y++)
- bitmap.SetPixel(x, original.Height - 1 - y, original.GetPixel(x, y));
- break;
+ }
- case SKCodecOrigin.BottomRight:
+ var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height);
+ using (var flippedCanvas = new SKCanvas(flippedBitmap))
+ {
+ flippedCanvas.Translate(flippedBitmap.Width, 0);
+ flippedCanvas.Scale(-1, 1);
+ flippedCanvas.DrawBitmap(rotated, 0, 0);
+ }
- for (var x = 0; x < original.Width; x++)
- for (var y = 0; y < original.Height; y++)
- bitmap.SetPixel(original.Width - 1 - x, original.Height - 1 - y, original.GetPixel(x, y));
- break;
+ return flippedBitmap;
+ }
+ }
- case SKCodecOrigin.TopRight:
+ case SKCodecOrigin.RightTop:
+ {
+ var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
+ using (var surface = new SKCanvas(rotated))
+ {
+ surface.Translate(rotated.Width, 0);
+ surface.RotateDegrees(90);
+ surface.DrawBitmap(bitmap, 0, 0);
+ }
- for (var x = 0; x < original.Width; x++)
- for (var y = 0; y < original.Height; y++)
- bitmap.SetPixel(original.Width - 1 - x, y, original.GetPixel(x, y));
- break;
+ return rotated;
+ }
- }
+ case SKCodecOrigin.RightBottom:
+ {
+ // TODO: Remove dual canvases, had trouble with flipping
+ using (var rotated = new SKBitmap(bitmap.Height, bitmap.Width))
+ {
+ using (var surface = new SKCanvas(rotated))
+ {
+ surface.Translate(0, rotated.Height);
+ surface.RotateDegrees(270);
+ surface.DrawBitmap(bitmap, 0, 0);
+ }
+
+ var flippedBitmap = new SKBitmap(rotated.Width, rotated.Height);
+ using (var flippedCanvas = new SKCanvas(flippedBitmap))
+ {
+ flippedCanvas.Translate(flippedBitmap.Width, 0);
+ flippedCanvas.Scale(-1, 1);
+ flippedCanvas.DrawBitmap(rotated, 0, 0);
+ }
- return bitmap;
+ return flippedBitmap;
+ }
+ }
+
+ case SKCodecOrigin.LeftBottom:
+ {
+ var rotated = new SKBitmap(bitmap.Height, bitmap.Width);
+ using (var surface = new SKCanvas(rotated))
+ {
+ surface.Translate(0, rotated.Height);
+ surface.RotateDegrees(270);
+ surface.DrawBitmap(bitmap, 0, 0);
+ }
+
+ return rotated;
+ }
+
+ default:
+ return bitmap;
+ }
}
public string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat selectedOutputFormat)
@@ -356,11 +446,11 @@ namespace Emby.Drawing.Skia
var blur = options.Blur ?? 0;
var hasIndicator = options.AddPlayedIndicator || options.UnplayedCount.HasValue || !options.PercentPlayed.Equals(0);
- using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient, orientation))
+ using (var bitmap = GetBitmap(inputPath, options.CropWhiteSpace, autoOrient))
{
if (bitmap == null)
{
- throw new Exception(string.Format("Skia unable to read image {0}", inputPath));
+ throw new ArgumentOutOfRangeException(string.Format("Skia unable to read image {0}", inputPath));
}
//_logger.Info("Color type {0}", bitmap.Info.ColorType);
@@ -504,10 +594,6 @@ namespace Emby.Drawing.Skia
get { return "Skia"; }
}
- public void Dispose()
- {
- }
-
public bool SupportsImageCollageCreation
{
get { return true; }