将C库转换为Java:在结果位图的左上角获取受损的垃圾数据

问题描述 投票:1回答:1

LWJGL3库包含与STB TrueTypeSean Barrett制作的其他库的绑定。

为了修改这个库提供的打包API,将SDF字形渲染到背景纹理而不是普通的位图,我正在从java中的库中再现纹理渲染代码。

我设法让它几乎工作,但我遇到了一块磕磕绊绊的石头,在那里我得到了破碎的垃圾数据 对于纹理的左上角。我有点确信错误必须位于我的stbtt__h_prefilter(...)版本的代码中,因为这是断言失败的地方。

编辑:我在缓冲区上进行读/写操作时忘了考虑当前缓冲区的位置。现在我在位图中仍然有一些垃圾数据,但它的分布更均匀。

实际上,在查看更新的第二张图片时,似乎每个字形的最左边部分都会向下移动一半的字形高度。我无法找到它发生的位置或原因,特别是考虑到位图处理在每个字形被渲染成字体后单独处理,所以根据我的理解,下一行字形应该覆盖这个...?

原始库生成的位图:Correct bitmap

我的版本生成的位图(请参阅偏移的半线切割成一些字母):Mangled bitmap

附录:我的版本生成的位图没有prefilter_...方法:Unfiltered bitmap

下面是从库中找到我的方法版本。原件可以找到here


STB...函数的引用是指generated bindings form lwjgl3

private static boolean packFontRangesRenderIntoRectsSDF(
                  STBTTPackContext context, STBTTFontinfo fontinfo,
                  STBTTPackRange.Buffer ranges, STBRPRect.Buffer rects) {

    int i, j, k;
    boolean returnValue = true;

    int curr_hOversample = context.h_oversample();
    int curr_vOversample = context.v_oversample();

    k = 0;
    for(i = 0 ; i < ranges.remaining() ; i++) {
        float fh = ranges.get(i).font_size();
        float scale = fh > 0.0f ? stbtt_ScaleForPixelHeight(fontinfo, fh) : stbtt_ScaleForMappingEmToPixels(fontinfo, -fh);
        float recip_h, recip_v, sub_x, sub_y;

        curr_hOversample = STBTTPackRange.nh_oversample(ranges.get(i).address()) & 0xFF;
        curr_vOversample = STBTTPackRange.nv_oversample(ranges.get(i).address()) & 0xFF;

        recip_h = 1.0f / (float)curr_hOversample;
        recip_v = 1.0f / (float)curr_vOversample;

        sub_x = __oversample_shift(curr_hOversample);
        sub_y = __oversample_shift(curr_vOversample);

        for(j = 0 ; j < ranges.get(i).num_chars() ; j++) {
            STBRPRect r = rects.get(k);
            if(r.was_packed()) {
                STBTTPackedchar bc = ranges.get(i).chardata_for_range().get(j);

                IntBuffer advance = ByteBuffer.allocateDirect(Integer.BYTES)
                                              .order(ByteOrder.nativeOrder())
                                              .asIntBuffer();
                IntBuffer lsb = ByteBuffer.allocateDirect(Integer.BYTES)
                                          .order(ByteOrder.nativeOrder())
                                          .asIntBuffer();

                IntBuffer x0 = ByteBuffer.allocateDirect(Integer.BYTES)
                                         .order(ByteOrder.nativeOrder())
                                         .asIntBuffer();
                IntBuffer x1 = ByteBuffer.allocateDirect(Integer.BYTES)
                                         .order(ByteOrder.nativeOrder())
                                         .asIntBuffer();
                IntBuffer y0 = ByteBuffer.allocateDirect(Integer.BYTES)
                                         .order(ByteOrder.nativeOrder())
                                         .asIntBuffer();
                IntBuffer y1 = ByteBuffer.allocateDirect(Integer.BYTES)
                                         .order(ByteOrder.nativeOrder())
                                         .asIntBuffer();

                int codepoint = ranges.get(i).array_of_unicode_codepoints() == null ? ranges.get(i).first_unicode_codepoint_in_range() + j : ranges.get(i).array_of_unicode_codepoints().get(j);
                int glyph = stbtt_FindGlyphIndex(fontinfo, codepoint);
                int pad = context.padding();

                r.x((short) (r.x() + pad));
                r.y((short) (r.y() + pad));
                r.w((short) (r.w() - pad));
                r.h((short) (r.h() - pad));
                stbtt_GetGlyphHMetrics(fontinfo, glyph, advance, lsb);
                stbtt_GetGlyphBitmapBox(fontinfo, glyph,
                                        scale * curr_hOversample,
                                        scale * curr_vOversample,
                                        x0, y0, x1, y1);
                //TODO replace below with SDF func
                ByteBuffer buff = context.pixels(context.height() * context.width());
                buff.position(r.x() + r.y() * context.stride_in_bytes());

                stbtt_MakeGlyphBitmapSubpixel(fontinfo, buff,
                                              r.w() - curr_hOversample + 1,
                                              r.h() - curr_vOversample + 1,
                                              context.stride_in_bytes(),
                                              scale * curr_hOversample,
                                              scale * curr_vOversample,
                                              0, 0,
                                              glyph);

                if(curr_hOversample > 1) {
                    //FIXME __h_prefilter(..) function
                    buff.position(r.x() + r.y() * context.stride_in_bytes());
                    __h_prefilter(buff,
                                  r.w(), r.h(), context.stride_in_bytes(),
                                  curr_hOversample);
                }

                if(curr_vOversample > 1) {
                    //FIXME __v_prefilter(..) function
                    buff.position(r.x() + r.y() * context.stride_in_bytes());
                    __v_prefilter(buff,
                                  r.w(), r.h(), context.stride_in_bytes(),
                                  curr_vOversample);
                }

                bc.x0(r.x());
                bc.y0(r.y());
                bc.x1((short) (r.x() + r.w()));
                bc.y1((short) (r.y() + r.h()));
                bc.xadvance(scale * advance.get(0));
                bc.xoff((float) (x0.get(0) * recip_h + sub_x));
                bc.yoff((float) (y0.get(0) * recip_v + sub_y));
                bc.xoff2((x0.get(0) + r.w()) * recip_h + sub_x);
                bc.yoff2((y0.get(0) + r.h()) * recip_v + sub_y);
            } else {
                returnValue = false;
            }

            ++k;
        }
    }

    return returnValue;
}

//copy of stbtt__oversample_shift(..) as it's inaccessible
private static float __oversample_shift(int oversample) {
    if(oversample == 0) {
        return 0.0f;
    }

    return (float)-(oversample - 1) / (2.0f * (float)oversample);
}

private static final int MAX_OVERSAMPLE = 8;
private static final int __OVER_MASK = MAX_OVERSAMPLE - 1;

private static void __h_prefilter(ByteBuffer pixels, int w, int h, int stride_in_bytes, int kernel_width) {
    final int pixels_offset = pixels.position();
    int pixelstride = 0;

    byte[] buffer = new byte[MAX_OVERSAMPLE];
    int safe_w = w - kernel_width;
    int j;

    Arrays.fill(buffer, 0, MAX_OVERSAMPLE, (byte)0);

    for(j = 0 ; j < h ; j++) {
        int i;
        int total;
        Arrays.fill(buffer, 0, kernel_width, (byte)0);

        total = 0;

        for(i = 0 ; i <= safe_w ; i++) {
            total += Byte.toUnsignedInt(pixels.get(pixels_offset + (pixelstride + i))) - Byte.toUnsignedInt(buffer[i & __OVER_MASK]);
            buffer[(i + kernel_width) & __OVER_MASK] = pixels.get(pixels_offset + (pixelstride + i));
            pixels.put(pixels_offset + (pixelstride + i), (byte) Integer.divideUnsigned(total, kernel_width));
        }

        for(; i < w ; ++i) {
//                if(Byte.toUnsignedInt(pixels.get(pixels_offset + (pixelstride + i))) != 0) {
//                    throw new RuntimeException("Expected '0' but was '" + Byte.toUnsignedInt(pixels.get(pixels_offset + (pixelstride + i))) + "'");
//                }

            total -= Byte.toUnsignedInt(buffer[i & __OVER_MASK]);
            pixels.put(pixels_offset + (pixelstride + i), (byte) Integer.divideUnsigned(total, kernel_width));
        }

        pixelstride += stride_in_bytes;
    }
}

private static void __v_prefilter(ByteBuffer pixels, int w, int h, int stride_in_bytes, int kernel_width) {
    final int pixels_offset = pixels.position();
    int pixelstride = 0;

    byte[] buffer = new byte[MAX_OVERSAMPLE];
    int safe_h = h - kernel_width;
    int j;
    Arrays.fill(buffer, 0, MAX_OVERSAMPLE, (byte)0);

    for(j = 0 ; j < w ; j++) {
        int i;
        int total;
        Arrays.fill(buffer, 0, kernel_width, (byte)0);

        total = 0;

        for(i = 0 ; i <= safe_h ; i++) {
            total += Byte.toUnsignedInt(pixels.get(pixels_offset + ((pixelstride + i) * stride_in_bytes))) - Byte.toUnsignedInt(buffer[i & __OVER_MASK]);
            buffer[(i + kernel_width) & __OVER_MASK] = pixels.get(pixels_offset + ((pixelstride + i) * stride_in_bytes));
            pixels.put(pixels_offset + ((pixelstride + i) * stride_in_bytes), (byte) Integer.divideUnsigned(total, kernel_width));
        }

        for(; i < h ; ++i) {
//                if(Byte.toUnsignedInt(pixels.get(pixels_offset + ((pixelstride + i) * stride_in_bytes))) != 0) {
//                    throw new RuntimeException("Expected '0' but was '" + Byte.toUnsignedInt(pixels.get(pixels_offset + ((pixelstride + i) * stride_in_bytes))) + "'");
//                }

            total -= Byte.toUnsignedInt(buffer[i & __OVER_MASK]);
            pixels.put(pixels_offset + ((pixelstride + i) * stride_in_bytes), (byte) Integer.divideUnsigned(total, kernel_width));
        }

        pixelstride += 1;
    }
}
java c lwjgl true-type-fonts
1个回答
0
投票

当我从__v_prefilter(..)方法中删除偏移量时似乎没有问题。

因此将final int pixels_offset = pixels.position();更改为final int pixels_offset = 0;(或从代码中完全删除它)。

我说这似乎是因为我没有对我,现在工作和原始代码之间生成的地图进行任何按位比较。至少在我看来,纹理中没有可辨别的碎片了。

© www.soinside.com 2019 - 2024. All rights reserved.