OpenGl записывает неверные данные в изображение - PullRequest
1 голос
/ 16 февраля 2020

я пытаюсь написать wpf как пользовательский интерфейс. Я использую Sharpfont, FreeImage c# привязки и OpenGL. Моя проблема заключается в обработке шрифтов и создании текстур для моих шрифтов. Это то, что я получаю ...

Это мой код шрифта

    int IGlyphAtlas.Width => SIZE;
    int IGlyphAtlas.Height => SIZE;

    public int ID { get; } = id++;
    public float Size { get; }
    public string Family => fontface.Family;
    public bool IsLoaded => fontface != null;
    public FontWeight Weight => fontface.Weight;
    public FontStyle Style => fontface.Style;
    public FontStretch Stretch => fontface.Stretch;
    public Texture Texture => internaltexture;

    private bool isdisposed;
    private readonly FontFace fontface;
    private readonly TextAnalyzer analyzer;
    private readonly Texture2D internaltexture;
    private readonly List<PixelData> pixeldata = new List<PixelData>();
    public Font(FontFace face, float size)
    {
        TextureAttributes attributes = new TextureAttributes()
        {
            filter = OpenGL.GlFilter.LINEAR,
            format = OpenGL.GLPixelFormat.RED,
            type = OpenGL.GLPixelDataType.UNSIGNED_BYTE,
            wrap = OpenGL.GLTextureWrapMode.CLAMP_TO_BORDER,
            internalformat = OpenGL.GLInternalFormat.R8,
        };

        Size = size;
        fontface = face;
        analyzer = new TextAnalyzer(this);
        internaltexture = new Texture2D(attributes, SIZE, SIZE);
    }
    public TextLayout Layout(string text, TextFormat format, Rect rect, bool direct)
    {
        TextLayout layout = new TextLayout();

        format.Font = fontface;
        format.Size = Size;

        analyzer.Clear();
        analyzer.AppendText(text, format);
        analyzer.PerformLayout((float)rect.x, (float)rect.y, (float)rect.width, (float)rect.height, layout);

        return layout;
    }
    public Texture CopyInternalTexture()
    {
        TextureAttributes attributes = new TextureAttributes()
        {
            filter = OpenGL.GlFilter.LINEAR,
            format = OpenGL.GLPixelFormat.RED,
            type = OpenGL.GLPixelDataType.UNSIGNED_BYTE,
            wrap = OpenGL.GLTextureWrapMode.CLAMP_TO_BORDER,
            internalformat = OpenGL.GLInternalFormat.R8,
        };
        return new Texture2D(attributes, SIZE, SIZE, internaltexture.Read());
    }

    public void Dispose() => Dispose(true);
    private void Dispose(bool dispose)
    {
        if (isdisposed) return;
        if (dispose)
        {
            isdisposed = true;
        }
    }

    void IGlyphAtlas.Insert(char glyph, int page, int x, int y, int width, int height, IntPtr data)
    {
        if (isdisposed) throw new ObjectDisposedException(nameof(Font));
        if (page > 0) throw new InvalidOperationException("the texture of the font is to small for more glyphs");

        //byte[] pixels = new byte[width * height];
        //Marshal.Copy(data, pixels, 0, pixels.Length);
        internaltexture.Write(0, x, y, width, height, data);

        //pixeldata.Add(new PixelData(glyph, x, y, width, height, pixels));
    }

это мой код текстуры

    public int Width { get; }
    public int Height { get; }

    public GLTextureTarget Target { get; }
    public GLPixelDataType Type => textureattributes.type;
    public GLPixelFormat Format => textureattributes.format;
    public GLInternalFormat InternalFormat => textureattributes.internalformat;
    public GLTextureWrapMode Wrap => textureattributes.wrap;
    public GlFilter Filter => textureattributes.filter;

    private bool isdisposed;
    private readonly uint textureindex;
    private readonly TextureAttributes textureattributes;

    public Texture(GLTextureTarget target, TextureAttributes attributes, int width, int height)
    {
        Target = target;
        Width = width;
        Height = height;

        textureattributes = attributes;
        textureindex = GL.GenTexture();

        GL.BindTexture(Target, textureindex);
        Initialize(IntPtr.Zero);
        GL.BindTexture(Target, 0);
    }
    public Texture(GLTextureTarget target, TextureAttributes attributes, int width, int height, IntPtr dataptr)
    {
        Target = target;
        Width = width;
        Height = height;

        textureattributes = attributes;
        textureindex = GL.GenTexture();
        GL.BindTexture(Target, textureindex);
        Initialize(dataptr);
        GL.BindTexture(Target, 0);
    }
    public Texture(GLTextureTarget target, TextureAttributes attributes, int width, int height, byte[] dataarray)
    {
        Target = target;
        Width = width;
        Height = height;

        textureattributes = attributes;
        textureindex = GL.GenTexture();

        GCHandle pinnedArray = GCHandle.Alloc(dataarray, GCHandleType.Pinned);
        IntPtr pointer = pinnedArray.AddrOfPinnedObject();

        GL.BindTexture(Target, textureindex);
        Initialize(pointer);
        GL.BindTexture(Target, 0);

        pinnedArray.Free();
    }

    internal void Bind()
    {
        if (isdisposed) throw new ObjectDisposedException(nameof(Texture));
        GL.BindTexture(Target, textureindex);
    }
    internal void Release() => GL.BindTexture(Target, 0);

    public void Write(int level, int xoffset, int yoffset, int width, int height, IntPtr data)
    {
        if (isdisposed) throw new ObjectDisposedException(nameof(Texture));

        GL.BindTexture(Target, textureindex);
        WriteOverride(level, xoffset, yoffset, width, height, data);
        GL.BindTexture(Target, 0);
    }
    public void Write(int level, int xoffset, int yoffset, int width, int height, byte[] data)
    {
        if (isdisposed) throw new ObjectDisposedException(nameof(Texture));

        GL.BindTexture(Target, textureindex);
        WriteOverride(level, xoffset, yoffset, width, height, data);
        GL.BindTexture(Target, 0);
    }
    public byte[] Read()
    {
        if (isdisposed) throw new ObjectDisposedException(nameof(Texture));

        int size = Width * Height * GetBPP();
        GL.BindTexture(Target, textureindex);

        byte[] dataarray = new byte[size];
        GL.GetTextureImage(textureindex, 0, Format, Type, size, dataarray);

        GL.BindTexture(Target, 0);
        return dataarray;
    }
    public byte[] Read(int level, int xoffset, int yoffset, int width, int height)
    {
        if (isdisposed) throw new ObjectDisposedException(nameof(Texture));

        int size = width * height * GetBPP();
        byte[] dataarray = new byte[size];

        GL.BindTexture(Target, textureindex);
        GL.GetTextureSubImage(textureindex, 0, xoffset, yoffset, 0, width, height, 0, Format, Type, size, dataarray);
        GL.BindTexture(Target, 0);
        return dataarray;
    }
    public void Dispose() => Dispose(true);

    protected abstract void Initialize(IntPtr dataptr);
    protected abstract void WriteOverride(int level, int x, int y, int width, int height, byte[] data);
    protected abstract void WriteOverride(int level, int x, int y, int width, int height, IntPtr data);

    private void Dispose(bool dispose)
    {
        if (isdisposed) return;
        if (dispose)
        {
            isdisposed = true;
            GL.DeleteTextures(1, new[] { textureindex });
        }
    }
    private int GetBPP()
    {
        switch (Format)
        {
            case GLPixelFormat.STENCIL_INDEX:
            case GLPixelFormat.DEPTH_COMPONENT:
            case GLPixelFormat.RED:
            case GLPixelFormat.GREEN:
            case GLPixelFormat.BLUE: return 1;
            case GLPixelFormat.RG: return 2;
            case GLPixelFormat.BGR:
            case GLPixelFormat.RGB: return 3;
            case GLPixelFormat.BGRA:
            case GLPixelFormat.RGBA: return 4;
            case GLPixelFormat.RED_INTEGER:
            case GLPixelFormat.GREEN_INTEGER:
            case GLPixelFormat.BLUE_INTEGER: return 4;
            case GLPixelFormat.RG_INTEGER: return 8;
            case GLPixelFormat.BGR_INTEGER:
            case GLPixelFormat.RGB_INTEGER: return 12;
            case GLPixelFormat.BGRA_INTEGER:
            case GLPixelFormat.RGBA_INTEGER: return 16;
            case GLPixelFormat.NONE:
            default: return 0;
        }
    }

и texture2D

    public Texture2D(TextureAttributes attributes, int width, int height) : 
        base(GLTextureTarget.TEXTURE_2D, attributes, width, height)
    {
    }
    public Texture2D(TextureAttributes attributes, int width, int height, IntPtr dataptr) :
        base(GLTextureTarget.TEXTURE_2D, attributes, width, height, dataptr)
    {
    }
    public Texture2D(TextureAttributes attributes, int width, int height, byte[] dataarray) :
        base(GLTextureTarget.TEXTURE_2D, attributes, width, height, dataarray)
    {
    }

    protected override void Initialize(IntPtr dataptr)
    {
        GL.TexParameteri(Target, GLTextureParameter.TEXTURE_MIN_FILTER, Filter);
        GL.TexParameteri(Target, GLTextureParameter.TEXTURE_MAG_FILTER, Filter);
        GL.TexParameteri(Target, GLTextureParameter.TEXTURE_WRAP_S, Wrap);
        GL.TexParameteri(Target, GLTextureParameter.TEXTURE_WRAP_T, Wrap);
        GL.GenerateMipmap(Target);

        GL.TexImage2D(GLTexture2DProxyTarget.TEXTURE_2D, 0, InternalFormat, Width, Height, 0, Format, Type, dataptr);
    }

    protected override void WriteOverride(int level, int x, int y, int width, int height, byte[] data) 
        => GL.TexSubImage2D(GLTexture2DTarget.TEXTURE_2D, level, x, y, width, height, Format, Type, data);

    protected override void WriteOverride(int level, int x, int y, int width, int height, IntPtr data) 
        => GL.TexSubImage2D(GLTexture2DTarget.TEXTURE_2D, level, x, y, width, height, Format, Type, data);

im с использованием одного канала (красный) для данных текстуры. sharpfont работает правильно (подтверждено). я что-то пропустил, написав текстуру?

1 Ответ

0 голосов
/ 16 февраля 2020

По умолчанию OpenGL предполагает, что начало каждой строки изображения выровнено на 4 байта. Это связано с тем, что параметр GL_UNPACK_ALIGNMENT по умолчанию равен 4.
Поскольку изображение имеет 1 (КРАСНЫЙ) цветной канал и плотно упаковано, загрузка глифа с шириной, кратной 4 работам , но происходит сбой, если ширина не делится на 4.

Измените параметр GL_UNPACK_ALIGNMENT на 1, прежде чем указывать двухмерное изображение текстуры (glTexImage2D):

Gl.PixelStorei(GL.UNPACK_ALIGNMENT, 1);
GL.TexImage2D(GLTexture2DProxyTarget.TEXTURE_2D, 0, InternalFormat, Width, Height, 0, Format, Type, dataptr);
Gl.PixelStorei(GL.UNPACK_ALIGNMENT, 4);
...