bitbank2 / PNGdec

Arduino PNG image decoder library
Apache License 2.0
162 stars 25 forks source link

Use std::function for callbacks in C++ mode. #7

Open quentinmit opened 2 years ago

quentinmit commented 2 years ago

This allows lambda functions to be used (while continuing to support bare function pointers).

This also adds a variant of decode that accepts a draw callback directly, so the same image can be drawn in multiple places without re-parsing the header.

bitbank2 commented 2 years ago

I appreciate you making an effort to improve the PNGdec library. A couple of things -

1) I don't see any specific advantage to what you changed. Who needs to use lambda functions with this code and why is important to skip header parsing (which takes almost no time)? 2) This change is incomplete without fixing the example code

quentinmit commented 2 years ago

I appreciate you making an effort to improve the PNGdec library. A couple of things -

  1. I don't see any specific advantage to what you changed. Who needs to use lambda functions with this code and why is important to skip header parsing (which takes almost no time)?

The main reason to support lambda functions is that they can capture scope. In my usecase, I'm using PNGdec inside ESPHome, which is a C++ framework that leans heavily on object-oriented programming. Here's some example code showing how I used this PR:

  void MQTTImage::draw(esphome::display::DisplayBuffer* it, int x1, int y1, int width, int height) {
    png_.decode([it, x1, y1](PNGDRAW *pDraw) {
      if (pDraw->iBpp == 1) {
        for (int x = 0; x < pDraw->iWidth; x++) {
          it->draw_pixel_at(x1+x, y1+pDraw->y, (pDraw->pPixels[x/8] & (0x80 >> (x%8))) ? COLOR_ON : COLOR_OFF);
        };
      } else {
        for (int x = 0; x < pDraw->iWidth; x++) {
          it->draw_pixel_at(x1+x, y1+pDraw->y, (pDraw->pPixels[x] != 0) ? COLOR_ON : COLOR_OFF);
        };
      }
    }, 0);
  }

Notice how the lambda captures it, width, and height. That's impossible to achieve with bare function pointers.

It's not strictly necessary to add the second variant of the draw method; in my case it just makes my code significantly shorter to not have to re-construct the PNG object on each redraw loop.

  1. This change is incomplete without fixing the example code

I believe the example code is unaffected (though of course I can add another example showing how to use a std::function?) since a bare function pointer can be used as an std::function.

MR-XieXuan commented 1 year ago

English : Yes, I found this problem too. I was just about to submit this question, and I'm grateful to see that someone has already asked. I think this is a good place to use anonymous functions. If the callback function is inside the object, it will throw the handler function that should not be thrown into the global. In order to call functions inside my object I had to create a static object manager somewhere else to allow it to manipulate different events inside my object. Thanks a lot to @quentinmit, I'll get std::function<> code from you to simplify my code. 以上翻译结果来自有道神经网络翻译(YNMT)· 计算机 The above translation results are from Youdao Neural Network Translation (YNMT) · computer

Chinese : 是的,我也发现了这个问题。 我正准备提交这个问题,看到已经有人提了我很感激。 我认为这个地方使用匿名函数是一个很好的选择。 如果回调函数是在对象里的话会把不应该把处理函数丢到全局去。 为了做到调用对象内函数我不得不在别的地方做静态的对象管理器以允许他操控我对象内的不同事件。 非常感谢老哥 @quentinmit 做的贡献,我会在你这里获取到使用 std::function<> 的代码简化我的代码。