Fixed rendering of 9-patch images in retina resolution

This commit is contained in:
Lluis Sanchez 2013-12-02 10:16:17 +01:00
Родитель 2b7a78e58e
Коммит 31083e058e
14 изменённых файлов: 229 добавлений и 36 удалений

Просмотреть файл

@ -143,5 +143,17 @@
<EmbeddedResource Include="Tests\ninep-tt.9.png"> <EmbeddedResource Include="Tests\ninep-tt.9.png">
<LogicalName>ninep-tt.9.png</LogicalName> <LogicalName>ninep-tt.9.png</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-ss.9%402x.png">
<LogicalName>ninep-ss.9@2x.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-st.9%402x.png">
<LogicalName>ninep-st.9@2x.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-ts.9%402x.png">
<LogicalName>ninep-ts.9@2x.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-tt.9%402x.png">
<LogicalName>ninep-tt.9@2x.png</LogicalName>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

Просмотреть файл

@ -270,5 +270,17 @@
<EmbeddedResource Include="Tests\ninep-tt.9.png"> <EmbeddedResource Include="Tests\ninep-tt.9.png">
<LogicalName>ninep-tt.9.png</LogicalName> <LogicalName>ninep-tt.9.png</LogicalName>
</EmbeddedResource> </EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-ss.9%402x.png">
<LogicalName>ninep-ss.9@2x.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-st.9%402x.png">
<LogicalName>ninep-st.9@2x.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-ts.9%402x.png">
<LogicalName>ninep-ts.9@2x.png</LogicalName>
</EmbeddedResource>
<EmbeddedResource Include="Tests\ninep-tt.9%402x.png">
<LogicalName>ninep-tt.9@2x.png</LogicalName>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
</Project> </Project>

Просмотреть файл

@ -52,11 +52,11 @@ namespace Xwt
context.SetLineWidth (1); context.SetLineWidth (1);
} }
protected void CheckImage (string refImageName) protected void CheckImage (string refImageName, double scaleFactor = 1)
{ {
if (builder == null) if (builder == null)
return; return;
var img = builder.ToBitmap (); var img = builder.ToBitmap (scaleFactor);
builder.Dispose (); builder.Dispose ();
builder = null; builder = null;

Просмотреть файл

@ -189,6 +189,167 @@ namespace Xwt
context.DrawImage (np, 0, 0); context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileTileHigher.png"); CheckImage ("NinePatchTileTileHigher.png");
} }
// 2x scale factor
[Test]
public void NinePatchStretchStretchDefaultSize2x ()
{
var np = Image.FromResource ("ninep-ss.9.png");
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchStretchDefaultSize2x.png", 2);
}
[Test]
public void NinePatchStretchStretchWiderHigher2x ()
{
var np = Image.FromResource ("ninep-ss.9.png");
np = np.WithSize (np.Width * 3, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchStretchWiderHigher2x.png", 2);
}
[Test]
public void NinePatchStretchStretchWider2x ()
{
var np = Image.FromResource ("ninep-ss.9.png");
np = np.WithSize (np.Width * 3, np.Height);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchStretchWider2x.png", 2);
}
[Test]
public void NinePatchStretchStretchHigher2x ()
{
var np = Image.FromResource ("ninep-ss.9.png");
np = np.WithSize (np.Width, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchStretchHigher2x.png", 2);
}
[Test]
public void NinePatchStretchTileDefaultSize2x ()
{
var np = Image.FromResource ("ninep-st.9.png");
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchTileDefaultSize2x.png", 2);
}
[Test]
public void NinePatchStretchTileWiderHigher2x ()
{
var np = Image.FromResource ("ninep-st.9.png");
np = np.WithSize (np.Width * 3, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchTileWiderHigher2x.png", 2);
}
[Test]
public void NinePatchStretchTileWider2x ()
{
var np = Image.FromResource ("ninep-st.9.png");
np = np.WithSize (np.Width * 3, np.Height);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchTileWider2x.png", 2);
}
[Test]
public void NinePatchStretchTileHigher2x ()
{
var np = Image.FromResource ("ninep-st.9.png");
np = np.WithSize (np.Width, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchStretchTileHigher2x.png", 2);
}
[Test]
public void NinePatchTileStretchDefaultSize2x ()
{
var np = Image.FromResource ("ninep-ts.9.png");
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileStretchDefaultSize2x.png", 2);
}
[Test]
public void NinePatchTileStretchWiderHigher2x ()
{
var np = Image.FromResource ("ninep-ts.9.png");
np = np.WithSize (np.Width * 3, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileStretchWiderHigher2x.png", 2);
}
[Test]
public void NinePatchTileStretchWider2x ()
{
var np = Image.FromResource ("ninep-ts.9.png");
np = np.WithSize (np.Width * 3, np.Height);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileStretchWider2x.png", 2);
}
[Test]
public void NinePatchTileStretchHigher2x ()
{
var np = Image.FromResource ("ninep-ts.9.png");
np = np.WithSize (np.Width, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileStretchHigher2x.png", 2);
}
[Test]
public void NinePatchTileTileDefaultSize2x ()
{
var np = Image.FromResource ("ninep-tt.9.png");
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileTileDefaultSize2x.png", 2);
}
[Test]
public void NinePatchTileTileWiderHigher2x ()
{
var np = Image.FromResource ("ninep-tt.9.png");
np = np.WithSize (np.Width * 3, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileTileWiderHigher2x.png", 2);
}
[Test]
public void NinePatchTileTileWider2x ()
{
var np = Image.FromResource ("ninep-tt.9.png");
np = np.WithSize (np.Width * 3, np.Height);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileTileWider2x.png", 2);
}
[Test]
public void NinePatchTileTileHigher2x ()
{
var np = Image.FromResource ("ninep-tt.9.png");
np = np.WithSize (np.Width, np.Height * 3);
InitBlank ((int)np.Width, (int)np.Height);
context.DrawImage (np, 0, 0);
CheckImage ("NinePatchTileTileHigher2x.png", 2);
}
} }
} }

Просмотреть файл

@ -99,8 +99,9 @@ namespace Xwt
return null; return null;
} }
public static void CheckImage (string refImageName, Image img) public static void CheckImage (string refImageName, Image im)
{ {
BitmapImage img = im as BitmapImage ?? im.ToBitmap ();
Image coreRefImage = LoadReferenceImage (refImageName); Image coreRefImage = LoadReferenceImage (refImageName);
Image refImage = !RecheckAll ? LoadCustomReferenceImage (refImageName) : null; Image refImage = !RecheckAll ? LoadCustomReferenceImage (refImageName) : null;
@ -109,8 +110,8 @@ namespace Xwt
if (refImage == null) { if (refImage == null) {
ImageFailures.Add (new FailedImageInfo () { ImageFailures.Add (new FailedImageInfo () {
TestImage = img, TestImage = img.WithSize (img.PixelWidth, img.PixelHeight),
ReferenceImage = img, ReferenceImage = img.WithSize (img.PixelWidth, img.PixelHeight),
Name = refImageName, Name = refImageName,
TargetDir = ProjectReferenceImageDir TargetDir = ProjectReferenceImageDir
}); });
@ -135,8 +136,8 @@ namespace Xwt
if (!knownFailure) { if (!knownFailure) {
ImageFailures.Add (new FailedImageInfo () { ImageFailures.Add (new FailedImageInfo () {
TestImage = img, TestImage = img.WithSize (img.PixelWidth, img.PixelHeight),
ReferenceImage = refImage, ReferenceImage = refImage.WithSize (img.PixelWidth, img.PixelHeight),
DiffImage = diff, DiffImage = diff,
Name = refImageName, Name = refImageName,
TargetDir = ProjectCustomReferenceImageDir TargetDir = ProjectCustomReferenceImageDir
@ -149,13 +150,13 @@ namespace Xwt
public static Image DiffImages (Image img1, Image img2) public static Image DiffImages (Image img1, Image img2)
{ {
bool foundDifference = false; bool foundDifference = false;
var bmp1 = img1.ToBitmap (); var bmp1 = (img1 as BitmapImage) ?? img1.ToBitmap ();
var bmp2 = img2.ToBitmap (); var bmp2 = (img2 as BitmapImage) ?? img2.ToBitmap ();
var res = new ImageBuilder ((int)Math.Min (bmp1.Size.Width, bmp2.Size.Width), (int) Math.Min (bmp1.Size.Height, bmp2.Size.Height)); var res = new ImageBuilder ((int)Math.Min (bmp1.PixelWidth, bmp2.PixelWidth), (int) Math.Min (bmp1.PixelHeight, bmp2.PixelHeight));
var bmpr = res.ToBitmap (); var bmpr = res.ToBitmap ();
res.Dispose (); res.Dispose ();
for (int y=0; y<bmp1.Size.Height && y < bmp2.Size.Height; y++) { for (int y=0; y<bmp1.PixelHeight && y < bmp2.PixelHeight; y++) {
for (int x=0; x<bmp1.Size.Width && x<bmp2.Size.Width; x++) { for (int x=0; x<bmp1.PixelWidth && x<bmp2.PixelWidth; x++) {
var p1 = bmp1.GetPixel (x, y); var p1 = bmp1.GetPixel (x, y);
var p2 = bmp2.GetPixel (x, y); var p2 = bmp2.GetPixel (x, y);
var col = Colors.White; var col = Colors.White;

Двоичные данные
Testing/Tests/ninep-ss.9@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Двоичные данные
Testing/Tests/ninep-st.9@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Двоичные данные
Testing/Tests/ninep-ts.9@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Двоичные данные
Testing/Tests/ninep-tt.9@2x.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 2.2 KiB

Просмотреть файл

@ -344,8 +344,9 @@ namespace Xwt.GtkBackend
using (var ctx = new Cairo.Context (sf)) { using (var ctx = new Cairo.Context (sf)) {
ImageDescription idesc = new ImageDescription () { ImageDescription idesc = new ImageDescription () {
Alpha = 1, Alpha = 1,
Size = new Size (width * scaleFactor, height * scaleFactor) Size = new Size (width, height)
}; };
ctx.Scale (scaleFactor, scaleFactor);
Draw (actx, ctx, scaleFactor, 0, 0, idesc); Draw (actx, ctx, scaleFactor, 0, 0, idesc);
var f = new ImageFrame (ImageBuilderBackend.CreatePixbuf (sf), Math.Max((int)width,1), Math.Max((int)height,1), true); var f = new ImageFrame (ImageBuilderBackend.CreatePixbuf (sf), Math.Max((int)width,1), Math.Max((int)height,1), true);
AddFrame (f); AddFrame (f);

Просмотреть файл

@ -43,6 +43,7 @@ namespace Xwt.Mac
public CGAffineTransform? InverseViewTransform; public CGAffineTransform? InverseViewTransform;
public Stack<ContextStatus> StatusStack = new Stack<ContextStatus> (); public Stack<ContextStatus> StatusStack = new Stack<ContextStatus> ();
public ContextStatus CurrentStatus = new ContextStatus (); public ContextStatus CurrentStatus = new ContextStatus ();
public double ScaleFactor = 1;
} }
class ContextStatus class ContextStatus
@ -56,7 +57,8 @@ namespace Xwt.Mac
public override double GetScaleFactor (object backend) public override double GetScaleFactor (object backend)
{ {
return 1; var ct = (CGContextBackend) backend;
return ct.ScaleFactor;
} }
public override void Save (object backend) public override void Save (object backend)

Просмотреть файл

@ -129,12 +129,13 @@ namespace Xwt.Mac
var bmp = new CGBitmapContext (IntPtr.Zero, pixelWidth, pixelHeight, 8, bytesPerRow, Util.DeviceRGBColorSpace, flags); var bmp = new CGBitmapContext (IntPtr.Zero, pixelWidth, pixelHeight, 8, bytesPerRow, Util.DeviceRGBColorSpace, flags);
bmp.TranslateCTM (0, pixelHeight); bmp.TranslateCTM (0, pixelHeight);
bmp.ScaleCTM (1, -1); bmp.ScaleCTM ((float)scaleFactor, (float)-scaleFactor);
var ctx = new CGContextBackend { var ctx = new CGContextBackend {
Context = bmp, Context = bmp,
Size = new SizeF (pixelWidth, pixelHeight), Size = new SizeF ((float)width, (float)height),
InverseViewTransform = bmp.GetCTM ().Invert () InverseViewTransform = bmp.GetCTM ().Invert (),
ScaleFactor = scaleFactor
}; };
var ci = (CustomImage)handle; var ci = (CustomImage)handle;
@ -146,6 +147,7 @@ namespace Xwt.Mac
var im = new NSImage (); var im = new NSImage ();
im.AddRepresentation (imageRep); im.AddRepresentation (imageRep);
im.Size = new SizeF ((float)width, (float)height); im.Size = new SizeF ((float)width, (float)height);
bmp.Dispose ();
return im; return im;
} }
else { else {
@ -192,7 +194,11 @@ namespace Xwt.Mac
public override Size GetSize (object handle) public override Size GetSize (object handle)
{ {
NSImage img = (NSImage)handle; NSImage img = (NSImage)handle;
return new Size ((int)img.Size.Width, (int)img.Size.Height); NSBitmapImageRep bitmap = img.Representations ().OfType<NSBitmapImageRep> ().FirstOrDefault ();
if (bitmap != null)
return new Size (bitmap.PixelsWide, bitmap.PixelsHigh);
else
return new Size ((int)img.Size.Width, (int)img.Size.Height);
} }
public override object CopyBitmap (object handle) public override object CopyBitmap (object handle)

Просмотреть файл

@ -252,8 +252,8 @@ namespace Xwt.Drawing
if (i == -1) if (i == -1)
scaleFactor = 1; scaleFactor = 1;
else { else {
int j = fi.Item1.IndexOf ('.', ++i); int j = fi.Item1.IndexOf ('x', ++i);
if (!double.TryParse (fi.Item1.Substring (i + 1, j - i), out scaleFactor)) { if (!double.TryParse (fi.Item1.Substring (i, j - i), out scaleFactor)) {
toolkit.ImageBackendHandler.Dispose (fi.Item2); toolkit.ImageBackendHandler.Dispose (fi.Item2);
continue; continue;
} }

Просмотреть файл

@ -138,8 +138,8 @@ namespace Xwt.Drawing
var frame = GetFrame (ctx.ScaleFactor); var frame = GetFrame (ctx.ScaleFactor);
var fixedWidth = frame.Bitmap.Width - 2 - frame.StretchableWidth; var fixedWidth = frame.Bitmap.Width - 2 - frame.StretchableWidth;
var fixedHeight = frame.Bitmap.Height - 2 - frame.StretchableHeight; var fixedHeight = frame.Bitmap.Height - 2 - frame.StretchableHeight;
double totalVariableWidth = bounds.Width - fixedWidth; double totalVariableWidth = bounds.Width - fixedWidth / frame.ScaleFactor;
double totalVariableHeight = bounds.Height - fixedHeight; double totalVariableHeight = bounds.Height - fixedHeight / frame.ScaleFactor;
double remainingVariableHeight = totalVariableHeight; double remainingVariableHeight = totalVariableHeight;
double y = bounds.Y, yb = 1; double y = bounds.Y, yb = 1;
@ -173,34 +173,30 @@ namespace Xwt.Drawing
var t = GetTile (frame, tileIndex, sourceRegion); var t = GetTile (frame, tileIndex, sourceRegion);
ctx.DrawImage (t, targetRegion); ctx.DrawImage (t, targetRegion);
} else { } else {
double scaleX = 1; double pw = hs.Size / frame.ScaleFactor;
double scaleY = 1; double ph = vs.Size / frame.ScaleFactor;
if (hs.Mode == RenderMode.Stretch) { if (hs.Mode == RenderMode.Stretch) {
scaleX = sw / hs.Size; pw = targetRegion.Width;
targetRegion.Width = hs.Size;
} }
if (vs.Mode == RenderMode.Stretch) { if (vs.Mode == RenderMode.Stretch) {
scaleY = sh / vs.Size; ph = targetRegion.Height;
targetRegion.Height = vs.Size;
} }
ctx.Save (); ctx.Save ();
ctx.Translate (targetRegion.Location); ctx.Translate (targetRegion.Location);
if (scaleX != 1 || scaleY != 1)
ctx.Scale (scaleX, scaleY);
targetRegion.Location = Point.Zero; targetRegion.Location = Point.Zero;
ctx.Pattern = new ImagePattern (GetTile (frame, tileIndex, sourceRegion)); ctx.Pattern = new ImagePattern (GetTile (frame, tileIndex, sourceRegion).WithSize (pw, ph));
ctx.NewPath (); ctx.NewPath ();
ctx.Rectangle (targetRegion); ctx.Rectangle (targetRegion);
ctx.Fill (); ctx.Fill ();
ctx.Restore (); ctx.Restore ();
} }
x += sw / frame.ScaleFactor; x += sw;
xb += hs.Size; xb += hs.Size;
tileIndex++; tileIndex++;
} }
yb += vs.Size; yb += vs.Size;
y += sh / frame.ScaleFactor; y += sh;
} }
ctx.Restore (); ctx.Restore ();
} }
@ -215,7 +211,7 @@ namespace Xwt.Drawing
return sw; return sw;
} }
else { else {
return sec.Size; return sec.Size / frame.ScaleFactor;
} }
} }
@ -234,8 +230,10 @@ namespace Xwt.Drawing
protected sealed override Size GetDefaultSize () protected sealed override Size GetDefaultSize ()
{ {
var frame = frames [0]; var frame = GetFrame (1);
return new Size (frame.Bitmap.Width - 2, frame.Bitmap.Height - 2); if (frame == null)
frame = frames [0];
return new Size ((frame.Bitmap.Width - 2) / frame.ScaleFactor, (frame.Bitmap.Height - 2) / frame.ScaleFactor);
} }
public WidgetSpacing Padding { get; private set; } public WidgetSpacing Padding { get; private set; }