blend2d / blend2d

2D Vector Graphics Engine Powered by a JIT Compiler

Home Page:https://blend2d.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Rendering bug with BLContext for certain image sizes

judicaelclair opened this issue · comments

With latest master, for certain image sizes, rendering with BLContext distorts the image.
Pseudocode:

void render_via_data(BLImage& img) {
  BLImageData data;
  img.makeMutable(&data);
  uint32_t* pxs = static_cast<uint32_t*>(data.pixelData);
  for (const int y : range(height)) {
    for (const int x : range(width)) {
      const BLRgba32 v = /* implementation details */;
      pxs[y * width + x] = v.value;
    }
  }
}

void render_via_ctx(BLImage& img) {
  BLContext ctx(img);
  ctx.setCompOp(BL_COMP_OP_SRC_OVER);
  for (const int y : range(height)) {
    for (const int x : range(width)) {
      const BLRgba32 v = /* implementation details */;
      ctx.setFillStyle(v);
      ctx.fillRect(BLRectI{x, y, 1, 1});
    }
  }
}

Here is the correct image by using render_via_data():
render_via_data_correct

Here is the incorrect image by using render_via_ctx():
render_via_ctx_incorrect

As you can see, the image is skewed and wrapped, and there is a weird diagonal line that shouldn't be there. This is for an image with width=75 and height=50. If the image is instead width=300 and height=200 for example, render_via_ctx() produces the correct results.

The following code is not correct:

pxs[y * width + x] = v.value;

To calculate a valid pixel address, use stride:

uint8_t* pxs = static_cast<uint8_t*>(data.pixelData);

for (const int y : range(height)) {
  uint32_t* scanline = reinterpret_cast<uint32_t*>(pxs);
  for (const int x : range(width)) {
    const BLRgba32 v = /* implementation details */;
    scanline[x] = v.value;
  }
  pxs += data.stride;
}

Blend2D would align large images to 4 pixels to maintain 16-byte alignment for each scanline. It would not align small images to not waste memory. Also, you can create subimages that have much larger stride than width, etc... So the only correct calculation is to use stride.

Oh okay that makes sense, thank you! I was making the same mistake when generating an OpenGL texture from a BLImage, which is why the image rendered via BLContext looked weird. All works as expected now when using stride.