Add support for 9-patch images
This commit is contained in:
Родитель
0c3884d56d
Коммит
5b1f32a80e
|
@ -86,7 +86,7 @@ namespace Samples
|
||||||
AddSample (listView, "Editable checkboxes", typeof(ListView2));
|
AddSample (listView, "Editable checkboxes", typeof(ListView2));
|
||||||
AddSample (w, "Markdown", typeof (MarkDownSample));
|
AddSample (w, "Markdown", typeof (MarkDownSample));
|
||||||
AddSample (w, "Menu", typeof(MenuSamples));
|
AddSample (w, "Menu", typeof(MenuSamples));
|
||||||
var mn = AddSample (w, "Mnemonics", typeof (Mnemonics));
|
AddSample (w, "Mnemonics", typeof (Mnemonics));
|
||||||
AddSample (w, "Notebook", typeof(NotebookSample));
|
AddSample (w, "Notebook", typeof(NotebookSample));
|
||||||
AddSample (w, "Paneds", typeof(PanedViews));
|
AddSample (w, "Paneds", typeof(PanedViews));
|
||||||
AddSample (w, "Popover", typeof(PopoverSample));
|
AddSample (w, "Popover", typeof(PopoverSample));
|
||||||
|
@ -111,6 +111,7 @@ namespace Samples
|
||||||
AddSample (n, "Text", typeof(DrawingText));
|
AddSample (n, "Text", typeof(DrawingText));
|
||||||
AddSample (n, "Partial Images", typeof (PartialImages));
|
AddSample (n, "Partial Images", typeof (PartialImages));
|
||||||
AddSample (n, "Custom Drawn Image", typeof (ImageScaling));
|
AddSample (n, "Custom Drawn Image", typeof (ImageScaling));
|
||||||
|
AddSample (n, "9-patch Image", typeof (Image9Patch));
|
||||||
AddSample (n, "Widget Rendering", typeof (WidgetRendering));
|
AddSample (n, "Widget Rendering", typeof (WidgetRendering));
|
||||||
|
|
||||||
var wf = AddSample (null, "Widget Features", null);
|
var wf = AddSample (null, "Widget Features", null);
|
||||||
|
|
|
@ -101,6 +101,7 @@
|
||||||
<Compile Include="Samples\ListView2.cs" />
|
<Compile Include="Samples\ListView2.cs" />
|
||||||
<Compile Include="Samples\OpacitySample.cs" />
|
<Compile Include="Samples\OpacitySample.cs" />
|
||||||
<Compile Include="Samples\PasswordEntries.cs" />
|
<Compile Include="Samples\PasswordEntries.cs" />
|
||||||
|
<Compile Include="Samples\Image9Patch.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -126,6 +127,28 @@
|
||||||
<EmbeddedResource Include="document-generic%402x.png">
|
<EmbeddedResource Include="document-generic%402x.png">
|
||||||
<LogicalName>document-generic@2x.png</LogicalName>
|
<LogicalName>document-generic@2x.png</LogicalName>
|
||||||
</EmbeddedResource>
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="zoom-panel-dark.9.png">
|
||||||
|
<LogicalName>zoom-panel-dark.9.png</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="zoom-panel-dark%402x.9.png">
|
||||||
|
<LogicalName>zoom-panel-dark@2x.9.png</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="..\Testing\Tests\ninep-ss.9.png">
|
||||||
|
<Link>Samples\ninep-ss.9.png</Link>
|
||||||
|
<LogicalName>ninep-ss.9.png</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="..\Testing\Tests\ninep-st.9.png">
|
||||||
|
<Link>Samples\ninep-st.9.png</Link>
|
||||||
|
<LogicalName>ninep-st.9.png</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="..\Testing\Tests\ninep-ts.9.png">
|
||||||
|
<Link>Samples\ninep-ts.9.png</Link>
|
||||||
|
<LogicalName>ninep-ts.9.png</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<EmbeddedResource Include="..\Testing\Tests\ninep-tt.9.png">
|
||||||
|
<Link>Samples\ninep-tt.9.png</Link>
|
||||||
|
<LogicalName>ninep-tt.9.png</LogicalName>
|
||||||
|
</EmbeddedResource>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ProjectExtensions>
|
<ProjectExtensions>
|
||||||
<MonoDevelop>
|
<MonoDevelop>
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// Image9Patch.cs
|
||||||
|
//
|
||||||
|
// Author:
|
||||||
|
// lluis <>
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 lluis
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
using System;
|
||||||
|
using Xwt;
|
||||||
|
using Xwt.Drawing;
|
||||||
|
|
||||||
|
namespace Samples
|
||||||
|
{
|
||||||
|
public class Image9Patch: Canvas
|
||||||
|
{
|
||||||
|
Image img_ss, img_tt, img_st, img_ts;
|
||||||
|
|
||||||
|
public Image9Patch ()
|
||||||
|
{
|
||||||
|
img_ss = Image.FromResource ("ninep-ss.9.png");
|
||||||
|
img_tt = Image.FromResource ("ninep-tt.9.png");
|
||||||
|
img_st = Image.FromResource ("ninep-st.9.png");
|
||||||
|
img_ts = Image.FromResource ("ninep-ts.9.png");
|
||||||
|
Margin = 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnDraw (Context ctx, Rectangle dirtyRect)
|
||||||
|
{
|
||||||
|
var w = Math.Truncate (Bounds.Width / 2);
|
||||||
|
var h = Math.Truncate (Bounds.Height / 2);
|
||||||
|
ctx.DrawImage (img_ss, new Rectangle (0, 0, w, h).Inflate (-10, -10));
|
||||||
|
ctx.DrawImage (img_tt, new Rectangle (w, 0, w, h).Inflate (-10, -10));
|
||||||
|
ctx.DrawImage (img_st, new Rectangle (0, h, w, h).Inflate (-10, -10));
|
||||||
|
ctx.DrawImage (img_ts, new Rectangle (w, h, w, h).Inflate (-10, -10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.0 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 4.0 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.7 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.7 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.7 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 1.7 KiB |
|
@ -91,6 +91,12 @@ namespace Xwt.CairoBackend
|
||||||
|
|
||||||
#region IContextBackendHandler implementation
|
#region IContextBackendHandler implementation
|
||||||
|
|
||||||
|
public override double GetScaleFactor (object backend)
|
||||||
|
{
|
||||||
|
CairoContextBackend gc = (CairoContextBackend)backend;
|
||||||
|
return gc.ScaleFactor;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Save (object backend)
|
public override void Save (object backend)
|
||||||
{
|
{
|
||||||
CairoContextBackend gc = (CairoContextBackend)backend;
|
CairoContextBackend gc = (CairoContextBackend)backend;
|
||||||
|
@ -312,9 +318,9 @@ namespace Xwt.CairoBackend
|
||||||
ctx.Context.NewPath();
|
ctx.Context.NewPath();
|
||||||
ctx.Context.Rectangle (destRect.X, destRect.Y, destRect.Width, destRect.Height);
|
ctx.Context.Rectangle (destRect.X, destRect.Y, destRect.Width, destRect.Height);
|
||||||
ctx.Context.Clip ();
|
ctx.Context.Clip ();
|
||||||
ctx.Context.Translate (destRect.X-srcRect.X, destRect.Y-srcRect.Y);
|
|
||||||
double sx = destRect.Width / srcRect.Width;
|
double sx = destRect.Width / srcRect.Width;
|
||||||
double sy = destRect.Height / srcRect.Height;
|
double sy = destRect.Height / srcRect.Height;
|
||||||
|
ctx.Context.Translate (destRect.X-srcRect.X*sx, destRect.Y-srcRect.Y*sy);
|
||||||
ctx.Context.Scale (sx, sy);
|
ctx.Context.Scale (sx, sy);
|
||||||
img.Alpha *= ctx.GlobalAlpha;
|
img.Alpha *= ctx.GlobalAlpha;
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,11 @@ namespace Xwt.Mac
|
||||||
{
|
{
|
||||||
const double degrees = System.Math.PI / 180d;
|
const double degrees = System.Math.PI / 180d;
|
||||||
|
|
||||||
|
public override double GetScaleFactor (object backend)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Save (object backend)
|
public override void Save (object backend)
|
||||||
{
|
{
|
||||||
var ct = (CGContextBackend) backend;
|
var ct = (CGContextBackend) backend;
|
||||||
|
|
|
@ -207,7 +207,17 @@ namespace Xwt.Mac
|
||||||
|
|
||||||
public override object CropBitmap (object backend, int srcX, int srcY, int width, int height)
|
public override object CropBitmap (object backend, int srcX, int srcY, int width, int height)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException ();
|
NSImage img = (NSImage)backend;
|
||||||
|
NSBitmapImageRep bitmap = img.Representations ().OfType<NSBitmapImageRep> ().FirstOrDefault ();
|
||||||
|
if (bitmap != null) {
|
||||||
|
RectangleF empty = RectangleF.Empty;
|
||||||
|
var cgi = bitmap.AsCGImage (ref empty, null, null).WithImageInRect (new RectangleF (srcX, srcY, width, height));
|
||||||
|
NSImage res = new NSImage (cgi, new SizeF (width, height));
|
||||||
|
cgi.Dispose ();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw new InvalidOperationException ("Not a bitmnap image");
|
||||||
}
|
}
|
||||||
|
|
||||||
static NSImage FromResource (string res)
|
static NSImage FromResource (string res)
|
||||||
|
|
|
@ -81,6 +81,8 @@ namespace Xwt.Backends
|
||||||
/// It doesn't affect colors that have already been set.
|
/// It doesn't affect colors that have already been set.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract void SetGlobalAlpha (object backend, double globalAlpha);
|
public abstract void SetGlobalAlpha (object backend, double globalAlpha);
|
||||||
|
|
||||||
|
public abstract double GetScaleFactor (object backend);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,6 +92,20 @@ namespace Xwt.Drawing
|
||||||
return new BitmapImage (ToolkitEngine.ImageBackendHandler.CropBitmap (Backend, x, y, pixelWidth, pixelHeight), new Size (pixelWidth / scaleX, pixelHeight / scaleY));
|
return new BitmapImage (ToolkitEngine.ImageBackendHandler.CropBitmap (Backend, x, y, pixelWidth, pixelHeight), new Size (pixelWidth / scaleX, pixelHeight / scaleY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a crop of the image
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="x">X coordinate, in physical pixels</param>
|
||||||
|
/// <param name="srcY">Y coordinate, in physical pixels</param>
|
||||||
|
/// <param name="pixelWidth">Width, in physical pixels</param>
|
||||||
|
/// <param name="pixelHeight">Height, in physical pixels</param>
|
||||||
|
public BitmapImage Crop (Rectangle pixelRect)
|
||||||
|
{
|
||||||
|
var scaleX = Math.Truncate (PixelWidth / Width);
|
||||||
|
var scaleY = Math.Truncate (PixelHeight / Height);
|
||||||
|
return new BitmapImage (ToolkitEngine.ImageBackendHandler.CropBitmap (Backend, (int)pixelRect.X, (int)pixelRect.Y, (int)pixelRect.Width, (int)pixelRect.Height), new Size (pixelRect.Width / scaleX, pixelRect.Height / scaleY));
|
||||||
|
}
|
||||||
|
|
||||||
public Size PixelSize {
|
public Size PixelSize {
|
||||||
get { return pixelSize; }
|
get { return pixelSize; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -358,6 +358,10 @@ namespace Xwt.Drawing
|
||||||
{
|
{
|
||||||
handler.SetLineDash (Backend, offset, pattern);
|
handler.SetLineDash (Backend, offset, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal double ScaleFactor {
|
||||||
|
get { return handler.GetScaleFactor (Backend); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,8 @@ namespace Xwt.Drawing
|
||||||
internal NativeImageRef NativeRef;
|
internal NativeImageRef NativeRef;
|
||||||
internal double requestedAlpha = 1;
|
internal double requestedAlpha = 1;
|
||||||
|
|
||||||
|
static int[] supportedScales = { 2 };
|
||||||
|
|
||||||
internal Image ()
|
internal Image ()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -173,7 +175,9 @@ namespace Xwt.Drawing
|
||||||
|
|
||||||
var reqSize = toolkit.ImageBackendHandler.GetSize (img);
|
var reqSize = toolkit.ImageBackendHandler.GetSize (img);
|
||||||
|
|
||||||
List<object> altImages = new List<object> ();
|
var ext = GetExtension (resource);
|
||||||
|
var altImages = new List<Tuple<string,object>> ();
|
||||||
|
|
||||||
foreach (var r in assembly.GetManifestResourceNames ()) {
|
foreach (var r in assembly.GetManifestResourceNames ()) {
|
||||||
int i = r.LastIndexOf ('@');
|
int i = r.LastIndexOf ('@');
|
||||||
if (i != -1) {
|
if (i != -1) {
|
||||||
|
@ -181,17 +185,22 @@ namespace Xwt.Drawing
|
||||||
if (rname == resource || rname == name) {
|
if (rname == resource || rname == name) {
|
||||||
var rim = toolkit.ImageBackendHandler.LoadFromResource (assembly, r);
|
var rim = toolkit.ImageBackendHandler.LoadFromResource (assembly, r);
|
||||||
if (rim != null)
|
if (rim != null)
|
||||||
altImages.Add (rim);
|
altImages.Add (new Tuple<string, object> (r, rim));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (altImages.Count > 0) {
|
if (altImages.Count > 0) {
|
||||||
altImages.Insert (0, img);
|
altImages.Insert (0, new Tuple<string, object> (resource, img));
|
||||||
img = toolkit.ImageBackendHandler.CreateMultiResolutionImage (altImages);
|
if (ext == ".9.png")
|
||||||
|
return CreateComposedNinePatch (toolkit, altImages);
|
||||||
|
img = toolkit.ImageBackendHandler.CreateMultiResolutionImage (altImages.Select (i => i.Item2));
|
||||||
}
|
}
|
||||||
return new Image (img, toolkit) {
|
var res = new Image (img, toolkit) {
|
||||||
requestedSize = reqSize
|
requestedSize = reqSize
|
||||||
};
|
};
|
||||||
|
if (ext == ".9.png")
|
||||||
|
res = new NinePatchImage (res.ToBitmap ());
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Image CreateMultiSizeIcon (IEnumerable<Image> images)
|
public static Image CreateMultiSizeIcon (IEnumerable<Image> images)
|
||||||
|
@ -201,66 +210,57 @@ namespace Xwt.Drawing
|
||||||
return new Image (Toolkit.CurrentEngine.ImageBackendHandler.CreateMultiSizeIcon (images.Select (i => i.GetBackend ())));
|
return new Image (Toolkit.CurrentEngine.ImageBackendHandler.CreateMultiSizeIcon (images.Select (i => i.GetBackend ())));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static bool ParseImageName (string resourceId, string fileName, out int size, out int scale)
|
|
||||||
{
|
|
||||||
if (!fileName.StartsWith (resourceId)) {
|
|
||||||
size = -1;
|
|
||||||
scale = -1;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size = -1;
|
|
||||||
int i = fileName.LastIndexOf ('.');
|
|
||||||
if (i < 0)
|
|
||||||
i = fileName.Length - 1;
|
|
||||||
|
|
||||||
scale = ParseScale (fileName, ref i);
|
|
||||||
size = ParseSize (fileName, ref i);
|
|
||||||
|
|
||||||
return i == resourceId.Length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ParseScale (string s, ref int i)
|
|
||||||
{
|
|
||||||
if (i > 1 && s [i] >= '0' && s [i] <= '9' && s [i - 1] == '@') {
|
|
||||||
var scale = s [i] - '0';
|
|
||||||
i = i - 2;
|
|
||||||
return scale;
|
|
||||||
} else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ParseSize (string s, ref int i)
|
|
||||||
{
|
|
||||||
int end = i;
|
|
||||||
int n = i;
|
|
||||||
while (n >= 0 && char.IsDigit (s[n]))
|
|
||||||
n--;
|
|
||||||
if (end == n || n < 0 || s [n] != 'x')
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
var x = n;
|
|
||||||
var n2 = end;
|
|
||||||
n--;
|
|
||||||
|
|
||||||
while (n >= 0 && n2 > x && s[n] == s[n2]) {
|
|
||||||
n--;
|
|
||||||
n2--;
|
|
||||||
}
|
|
||||||
if (n2 == x && n >= 0 && s[n] == '_') {
|
|
||||||
i = n - 1;
|
|
||||||
return int.Parse (s.Substring (x + 1, end - x - 1));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
public static Image FromFile (string file)
|
public static Image FromFile (string file)
|
||||||
{
|
{
|
||||||
var toolkit = Toolkit.CurrentEngine;
|
var toolkit = Toolkit.CurrentEngine;
|
||||||
if (toolkit == null)
|
if (toolkit == null)
|
||||||
throw new ToolkitNotInitializedException ();
|
throw new ToolkitNotInitializedException ();
|
||||||
return new Image (toolkit.ImageBackendHandler.LoadFromFile (file), toolkit);
|
|
||||||
|
var ext = GetExtension (file);
|
||||||
|
var img = toolkit.ImageBackendHandler.LoadFromFile (file);
|
||||||
|
|
||||||
|
List<Tuple<string,object>> altImages = null;
|
||||||
|
foreach (var s in supportedScales) {
|
||||||
|
var fn = file.Substring (0, file.Length - ext.Length) + "@" + s + ext;
|
||||||
|
if (File.Exists (fn)) {
|
||||||
|
if (altImages == null) {
|
||||||
|
altImages = new List<Tuple<string, object>> ();
|
||||||
|
altImages.Add (new Tuple<string, object> (file, img));
|
||||||
|
}
|
||||||
|
altImages.Add (new Tuple<string, object> (fn, toolkit.ImageBackendHandler.LoadFromFile (fn)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (altImages != null) {
|
||||||
|
if (ext == ".9.png")
|
||||||
|
return CreateComposedNinePatch (toolkit, altImages);
|
||||||
|
img = toolkit.ImageBackendHandler.CreateMultiResolutionImage (altImages.Select (i => i.Item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = new Image (img, toolkit);
|
||||||
|
if (ext == ".9.png")
|
||||||
|
res = new NinePatchImage (res.ToBitmap ());
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Image CreateComposedNinePatch (Toolkit toolkit, List<Tuple<string,object>> altImages)
|
||||||
|
{
|
||||||
|
var npImage = new NinePatchImage ();
|
||||||
|
foreach (var fi in altImages) {
|
||||||
|
int i = fi.Item1.LastIndexOf ('@');
|
||||||
|
double scaleFactor;
|
||||||
|
if (i == -1)
|
||||||
|
scaleFactor = 1;
|
||||||
|
else {
|
||||||
|
int j = fi.Item1.IndexOf ('.', ++i);
|
||||||
|
if (!double.TryParse (fi.Item1.Substring (i + 1, j - i), out scaleFactor)) {
|
||||||
|
toolkit.ImageBackendHandler.Dispose (fi.Item2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
npImage.AddFrame (new Image (fi.Item2, toolkit).ToBitmap (), scaleFactor);
|
||||||
|
}
|
||||||
|
return npImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Image FromStream (Stream stream)
|
public static Image FromStream (Stream stream)
|
||||||
|
@ -271,6 +271,14 @@ namespace Xwt.Drawing
|
||||||
return new Image (toolkit.ImageBackendHandler.LoadFromStream (stream), toolkit);
|
return new Image (toolkit.ImageBackendHandler.LoadFromStream (stream), toolkit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string GetExtension (string fileName)
|
||||||
|
{
|
||||||
|
if (fileName.EndsWith (".9.png", StringComparison.Ordinal))
|
||||||
|
return ".9.png";
|
||||||
|
else
|
||||||
|
return Path.GetExtension (fileName);
|
||||||
|
}
|
||||||
|
|
||||||
public void Save (string file, ImageFileType fileType)
|
public void Save (string file, ImageFileType fileType)
|
||||||
{
|
{
|
||||||
using (var f = File.OpenWrite (file))
|
using (var f = File.OpenWrite (file))
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
//
|
||||||
|
// NinePatchImage.cs
|
||||||
|
//
|
||||||
|
// Author:
|
||||||
|
// Lluis Sanchez <lluis@xamarin.com>
|
||||||
|
//
|
||||||
|
// Copyright (c) 2013 Xamarin Inc.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
|
// in the Software without restriction, including without limitation the rights
|
||||||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
|
// furnished to do so, subject to the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be included in
|
||||||
|
// all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
// THE SOFTWARE.
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Xwt.Drawing
|
||||||
|
{
|
||||||
|
public class NinePatchImage: DrawingImage
|
||||||
|
{
|
||||||
|
List<ImageFrame> frames = new List<ImageFrame> ();
|
||||||
|
|
||||||
|
class ImageFrame {
|
||||||
|
public BitmapImage Bitmap;
|
||||||
|
public double ScaleFactor;
|
||||||
|
public List<ImageSection> HorizontalSections;
|
||||||
|
public List<ImageSection> VerticalSections;
|
||||||
|
public double StretchableWidth;
|
||||||
|
public double StretchableHeight;
|
||||||
|
public BitmapImage[] TileCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImageSection {
|
||||||
|
public int Start;
|
||||||
|
public double Size;
|
||||||
|
public RenderMode Mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum RenderMode {
|
||||||
|
Fixed,
|
||||||
|
Stretch,
|
||||||
|
Tile
|
||||||
|
}
|
||||||
|
|
||||||
|
public NinePatchImage ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public NinePatchImage (BitmapImage bitmap)
|
||||||
|
{
|
||||||
|
AddFrame (bitmap, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AddFrame (BitmapImage bitmap, double scaleFactor)
|
||||||
|
{
|
||||||
|
ImageFrame frame = new ImageFrame {
|
||||||
|
Bitmap = bitmap,
|
||||||
|
ScaleFactor = scaleFactor
|
||||||
|
};
|
||||||
|
frames.Add (frame);
|
||||||
|
|
||||||
|
frame.HorizontalSections = CreateSections (frame, Enumerable.Range (1, (int)bitmap.Width - 2).Select (n => bitmap.GetPixel (n, 0)));
|
||||||
|
frame.VerticalSections = CreateSections (frame, Enumerable.Range (1, (int)bitmap.Height - 2).Select (n => bitmap.GetPixel (0, n)));
|
||||||
|
|
||||||
|
double padLeft = 0, padTop = 0, padRight = 0, padBottom = 0;
|
||||||
|
var hbox = CreateSections (frame, Enumerable.Range (1, (int)bitmap.Width - 1).Select (n => bitmap.GetPixel (n, (int)bitmap.Height - 1)));
|
||||||
|
var sec = hbox.FirstOrDefault (s => s.Mode != RenderMode.Fixed);
|
||||||
|
if (sec != null) {
|
||||||
|
padLeft = sec.Start;
|
||||||
|
padRight = bitmap.Width - 2 - padLeft - sec.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
var vbox = CreateSections (frame, Enumerable.Range (1, (int)bitmap.Height - 1).Select (n => bitmap.GetPixel ((int)bitmap.Width - 1, n)));
|
||||||
|
sec = vbox.FirstOrDefault (s => s.Mode != RenderMode.Fixed);
|
||||||
|
if (sec != null) {
|
||||||
|
padTop = sec.Start;
|
||||||
|
padBottom = bitmap.Height - 2 - padTop - sec.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
Padding = new WidgetSpacing (padLeft, padTop, padRight, padBottom);
|
||||||
|
|
||||||
|
frame.StretchableWidth = frame.HorizontalSections.Where (s => s.Mode != RenderMode.Fixed).Sum (s => s.Size);
|
||||||
|
frame.StretchableHeight = frame.VerticalSections.Where (s => s.Mode != RenderMode.Fixed).Sum (s => s.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ImageSection> CreateSections (ImageFrame frame, IEnumerable<Color> pixels)
|
||||||
|
{
|
||||||
|
List<ImageSection> sections = new List<ImageSection> ();
|
||||||
|
|
||||||
|
ImageSection section = null;
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
foreach (var p in pixels) {
|
||||||
|
RenderMode mode;
|
||||||
|
if (p == Colors.Red)
|
||||||
|
mode = RenderMode.Tile;
|
||||||
|
else if (p == Colors.Black)
|
||||||
|
mode = RenderMode.Stretch;
|
||||||
|
else
|
||||||
|
mode = RenderMode.Fixed;
|
||||||
|
|
||||||
|
if (section == null || mode != section.Mode) {
|
||||||
|
section = new ImageSection {
|
||||||
|
Start = n,
|
||||||
|
Size = 1,
|
||||||
|
Mode = mode
|
||||||
|
};
|
||||||
|
sections.Add (section);
|
||||||
|
} else
|
||||||
|
section.Size++;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
return sections;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageFrame GetFrame (double scaleFactor)
|
||||||
|
{
|
||||||
|
return frames.FirstOrDefault (i => Math.Abs (i.ScaleFactor - scaleFactor) < 0.1) ?? frames[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed override void OnDraw (Context ctx, Rectangle bounds)
|
||||||
|
{
|
||||||
|
var frame = GetFrame (ctx.ScaleFactor);
|
||||||
|
var fixedWidth = frame.Bitmap.Width - frame.StretchableWidth;
|
||||||
|
var fixedHeight = frame.Bitmap.Height - frame.StretchableHeight;
|
||||||
|
double totalVariableWidth = bounds.Width - fixedWidth;
|
||||||
|
double totalVariableHeight = bounds.Height - fixedHeight;
|
||||||
|
double remainingVariableHeight = totalVariableHeight;
|
||||||
|
|
||||||
|
double y = bounds.Y, yb = 1;
|
||||||
|
int tileIndex = 0;
|
||||||
|
|
||||||
|
ctx.Save ();
|
||||||
|
if (totalVariableWidth < 0) {
|
||||||
|
if (fixedWidth > 0)
|
||||||
|
ctx.Scale (bounds.Width / fixedWidth, 1);
|
||||||
|
totalVariableWidth = 0;
|
||||||
|
}
|
||||||
|
if (totalVariableHeight < 0) {
|
||||||
|
if (fixedHeight > 0)
|
||||||
|
ctx.Scale (1, bounds.Height / fixedHeight);
|
||||||
|
totalVariableHeight = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var vs in frame.VerticalSections) {
|
||||||
|
|
||||||
|
double sh = CalcSectionSize (frame, vs, totalVariableHeight, frame.StretchableHeight, ref remainingVariableHeight);
|
||||||
|
|
||||||
|
double x = bounds.X, xb = 1;
|
||||||
|
double remainingVariableWidth = totalVariableWidth;
|
||||||
|
|
||||||
|
foreach (var hs in frame.HorizontalSections) {
|
||||||
|
var sourceRegion = new Rectangle (xb, yb, hs.Size, vs.Size);
|
||||||
|
double sw = CalcSectionSize (frame, hs, totalVariableWidth, frame.StretchableWidth, ref remainingVariableWidth);
|
||||||
|
var targetRegion = new Rectangle (x, y, sw, sh);
|
||||||
|
|
||||||
|
if (vs.Mode != RenderMode.Tile && hs.Mode != RenderMode.Tile) {
|
||||||
|
var t = GetTile (frame, tileIndex, sourceRegion);
|
||||||
|
ctx.DrawImage (t, targetRegion);
|
||||||
|
} else {
|
||||||
|
double scaleX = 1;
|
||||||
|
double scaleY = 1;
|
||||||
|
if (hs.Mode == RenderMode.Stretch) {
|
||||||
|
scaleX = sw / hs.Size;
|
||||||
|
targetRegion.Width = hs.Size;
|
||||||
|
}
|
||||||
|
if (vs.Mode == RenderMode.Stretch) {
|
||||||
|
scaleY = sh / vs.Size;
|
||||||
|
targetRegion.Height = vs.Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Save ();
|
||||||
|
ctx.Translate (targetRegion.Location);
|
||||||
|
if (scaleX != 1 || scaleY != 1)
|
||||||
|
ctx.Scale (scaleX, scaleY);
|
||||||
|
targetRegion.Location = Point.Zero;
|
||||||
|
ctx.Pattern = new ImagePattern (GetTile (frame, tileIndex, sourceRegion));
|
||||||
|
ctx.NewPath ();
|
||||||
|
ctx.Rectangle (targetRegion);
|
||||||
|
ctx.Fill ();
|
||||||
|
ctx.Restore ();
|
||||||
|
}
|
||||||
|
x += sw / frame.ScaleFactor;
|
||||||
|
xb += hs.Size;
|
||||||
|
tileIndex++;
|
||||||
|
}
|
||||||
|
yb += vs.Size;
|
||||||
|
y += sh / frame.ScaleFactor;
|
||||||
|
}
|
||||||
|
ctx.Restore ();
|
||||||
|
}
|
||||||
|
|
||||||
|
double CalcSectionSize (ImageFrame frame, ImageSection sec, double totalVariable, double stretchableSize, ref double remainingVariable)
|
||||||
|
{
|
||||||
|
if (sec.Mode != RenderMode.Fixed) {
|
||||||
|
double sw = Math.Round (totalVariable * (sec.Size / stretchableSize));
|
||||||
|
if (sw > remainingVariable)
|
||||||
|
sw = remainingVariable;
|
||||||
|
remainingVariable -= sw;
|
||||||
|
return sw;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return sec.Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BitmapImage GetTile (ImageFrame frame, int tileIndex, Rectangle sourceRegion)
|
||||||
|
{
|
||||||
|
if (frame.TileCache == null)
|
||||||
|
frame.TileCache = new BitmapImage [frame.HorizontalSections.Count * frame.VerticalSections.Count];
|
||||||
|
|
||||||
|
var img = frame.TileCache [tileIndex];
|
||||||
|
if (img != null)
|
||||||
|
return img;
|
||||||
|
|
||||||
|
img = frame.Bitmap.Crop (sourceRegion);
|
||||||
|
return frame.TileCache [tileIndex] = img;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected sealed override Size GetDefaultSize ()
|
||||||
|
{
|
||||||
|
var frame = frames [0];
|
||||||
|
return new Size (frame.Bitmap.Width - 2, frame.Bitmap.Height - 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WidgetSpacing Padding { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -583,6 +583,13 @@ namespace Xwt.Drawing
|
||||||
return ctx.NativePathHandler.IsPointInFill (ctx.NativeBackend, x, y);
|
return ctx.NativePathHandler.IsPointInFill (ctx.NativeBackend, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override double GetScaleFactor (object backend)
|
||||||
|
{
|
||||||
|
var ctx = (VectorBackend)backend;
|
||||||
|
CreateNativePathBackend (ctx);
|
||||||
|
return ctx.NativeContextHandler.GetScaleFactor (ctx.NativeBackend);
|
||||||
|
}
|
||||||
|
|
||||||
public override void Dispose (object backend)
|
public override void Dispose (object backend)
|
||||||
{
|
{
|
||||||
var ctx = (VectorBackend)backend;
|
var ctx = (VectorBackend)backend;
|
||||||
|
|
|
@ -316,6 +316,7 @@
|
||||||
<Compile Include="Xwt.Backends\KeyboardHandler.cs" />
|
<Compile Include="Xwt.Backends\KeyboardHandler.cs" />
|
||||||
<Compile Include="Xwt\FrameBox.cs" />
|
<Compile Include="Xwt\FrameBox.cs" />
|
||||||
<Compile Include="Xwt\ToolkitNotInitializedException.cs" />
|
<Compile Include="Xwt\ToolkitNotInitializedException.cs" />
|
||||||
|
<Compile Include="Xwt.Drawing\NinePatchImage.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||||
<ItemGroup />
|
<ItemGroup />
|
||||||
|
|
Загрузка…
Ссылка в новой задаче