Ok, I think I’ve tried to reproduce what you’re doing, except I generated the pixel source texture on the fly instead of having it a content image. You’ll still have to make the debug font (see my other post) but I now have this…
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using System;
using System.Collections.Generic;
namespace Game1
{
public class Game1 : Game
{
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch;
private Texture2D _pixel;
private RenderTarget2D _pixelSource;
private SpriteFont _debugFont;
private Vector2 _pixelPos = new Vector2(2, 80);
private LineTestManager _manager;
private List<ILineTestManagerRenderer> _renderers = new List<ILineTestManagerRenderer>();
public Game1()
{
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
this.IsMouseVisible = true;
}
protected override void Initialize()
{
_graphics.PreferredBackBufferWidth = 1280;
_graphics.PreferredBackBufferHeight = 720;
_graphics.ApplyChanges();
base.Initialize();
}
protected override void LoadContent()
{
_spriteBatch = new SpriteBatch(GraphicsDevice);
_pixel = new Texture2D(_graphics.GraphicsDevice, 1, 1);
_pixel.SetData<Color>(new Color[] { Color.White });
_pixelSource = new RenderTarget2D(_graphics.GraphicsDevice, 1001, 1001);
this.GraphicsDevice.SetRenderTarget(_pixelSource);
this.GraphicsDevice.Clear(Color.Transparent);
_spriteBatch.Begin();
_spriteBatch.Draw(_pixel, _pixelPos, Color.White);
_spriteBatch.End();
this.GraphicsDevice.SetRenderTarget(null);
_debugFont = Content.Load<SpriteFont>("Debug");
_manager = new LineTestManager();
_manager.AddTest(100);
_manager.AddTest(200);
_manager.AddTest(300);
_manager.AddTest(400);
_manager.AddTest(795);
_renderers.Add(new LineTestManagerRendererBasic(_spriteBatch, _debugFont, _pixel, _manager));
_renderers.Add(new LineTestManagerRendererOther(_spriteBatch, _debugFont, _pixelSource, _pixelPos, _manager));
}
protected override void Update(GameTime gameTime)
{
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
this.GraphicsDevice.Clear(Color.Black);
_spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp);
foreach (var renderer in _renderers)
renderer.Draw(gameTime);
_spriteBatch.End();
base.Draw(gameTime);
}
}
public class LineTest
{
public uint Length { get; private set; }
public Color Colour { get; private set; }
public LineTest(uint length, Color colour)
{
this.Length = length;
this.Colour = colour;
}
}
public class LineTestManager
{
private List<LineTest> _lines = new List<LineTest>();
public IReadOnlyList<LineTest> Lines { get { return _lines; } }
public uint MaxLength { get; private set; } = 0;
public void AddTest(uint length)
{
_lines.Add(new LineTest(length, Color.Red));
this.MaxLength = Math.Max(this.MaxLength, length);
}
}
public interface ILineTestManagerRenderer
{
Vector2 StartPosition { get; set; }
void Draw(GameTime gameTime);
}
public class LineTestManagerRendererBasic : ILineTestManagerRenderer
{
private SpriteBatch _spriteBatch;
private SpriteFont _font;
private Texture2D _pixel;
private LineTestManager _manager;
public Vector2 StartPosition { get; set; } = new Vector2(10, 10);
public LineTestManagerRendererBasic(SpriteBatch spriteBatch, SpriteFont spriteFont, Texture2D pixel, LineTestManager manager)
{
_spriteBatch = spriteBatch ?? throw new ArgumentNullException();
_font = spriteFont ?? throw new ArgumentNullException();
_pixel = pixel ?? throw new ArgumentNullException();
_manager = manager ?? throw new ArgumentNullException();
}
public void Draw(GameTime gameTime)
{
Vector2 padding = new Vector2(_font.MeasureString("".PadLeft(_manager.MaxLength.ToString().Length + 2, '0')).X, _font.LineSpacing);
for (int i = 0; i < _manager.Lines.Count; i++)
{
LineTest line = _manager.Lines[i];
Vector2 position = this.StartPosition + new Vector2(0, i * padding.Y);
_spriteBatch.DrawString(_font, line.Length.ToString() + ":", position, line.Colour);
position.X += padding.X;
position.Y += _font.LineSpacing / 2f;
_spriteBatch.Draw(_pixel, new Rectangle((int)position.X, (int)position.Y, (int)line.Length, 1), line.Colour);
}
}
}
public class LineTestManagerRendererOther : ILineTestManagerRenderer
{
private SpriteBatch _spriteBatch;
private SpriteFont _font;
private Texture2D _pixelSource;
private Vector2 _pixelPos;
private LineTestManager _manager;
public Vector2 StartPosition { get; set; } = new Vector2(10, 150);
public LineTestManagerRendererOther(SpriteBatch spriteBatch, SpriteFont spriteFont, Texture2D pixelSource, Vector2 pixelPos, LineTestManager manager)
{
_spriteBatch = spriteBatch ?? throw new ArgumentNullException();
_font = spriteFont ?? throw new ArgumentNullException();
_pixelSource = pixelSource ?? throw new ArgumentNullException();
_pixelPos = pixelPos;
_manager = manager ?? throw new ArgumentNullException();
}
public void Draw(GameTime gameTime)
{
Vector2 padding = new Vector2(_font.MeasureString("".PadLeft(_manager.MaxLength.ToString().Length + 2, '0')).X, _font.LineSpacing);
for (int i = 0; i < _manager.Lines.Count; i++)
{
LineTest line = _manager.Lines[i];
Vector2 position = this.StartPosition + new Vector2(0, i * padding.Y);
_spriteBatch.DrawString(_font, line.Length.ToString() + ":", position, line.Colour);
position.X += padding.X;
position.Y += _font.LineSpacing / 2f;
Vector2 pixelScale = new Vector2(line.Length, 1);
_spriteBatch.Draw(
_pixelSource,
new Vector2((int)position.X, (int)position.Y),
new Rectangle((int)_pixelPos.X, (int)_pixelPos.Y, 1, 1),
Color.Red,
0f,
Vector2.Zero,
pixelScale,
SpriteEffects.None,
0
);
}
}
}
}
It’s now generating a 1001x1001 texture and sticking a pixel in it, then using that full size texture as the source and a sourceRectangle at the location of the pixel to get the pixel. I think I’m scaling in the same way you are and it’s actually seemingly rendering the lines the right size.
Do you maybe wanna give this a try on your end and see if you get a different result?
I’ve gotta step away for a while but I’ll try to remember to check this later.