text: #include "say.h"
  
  static void say_pack_pbo_make_current(GLuint pbo) {
    say_context *context = say_context_current();
  
    if (context->pack_pbo != pbo) {
      glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
      context->pack_pbo = pbo;
    }
  }
  
  static void say_unpack_pbo_make_current(GLuint pbo) {
    say_context *context = say_context_current();
  
    if (context->unpack_pbo != pbo) {
      glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
      context->unpack_pbo = pbo;
    }
  }
  
  static void say_pbo_will_delete(GLuint pbo) {
    mo_array *contexts = say_context_get_all();
    for (size_t i = 0; i < contexts->size; i++) {
      say_context *context = mo_array_get_as(contexts, i, say_context*);
  
      if (context->pack_pbo   == pbo) context->pack_pbo   = 0;
      if (context->unpack_pbo == pbo) context->unpack_pbo = 0;
    }
  }
  
  bool say_pixel_bus_is_available() {
    say_context_ensure();
    return GLEW_ARB_pixel_buffer_object ||
      GLEW_VERSION_2_1;
  }
  
  say_pixel_bus *say_pixel_bus_create(GLenum mode) {
    say_context_ensure();
  
    say_pixel_bus *bus = malloc(sizeof(say_pixel_bus));
  
    glGenBuffers(1, &bus->pbo);
  
    bus->size = 1024;
    bus->mode = mode;
  
    say_pixel_bus_bind_pack(bus);
    glBufferData(GL_PIXEL_PACK_BUFFER, 1024 * sizeof(say_color), NULL,
                 mode);
  
    return bus;
  }
  
  void say_pixel_bus_free(say_pixel_bus *bus) {
    say_context_ensure();
    say_pbo_will_delete(bus->pbo);
    glDeleteBuffers(1, &bus->pbo);
    free(bus);
  }
  
  size_t say_pixel_bus_pull_image(say_pixel_bus *bus,
                                  say_image *img, size_t offset) {
    say_image_bind(img);
    say_pixel_bus_bind_pack(bus);
  
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE,
                  (void*)(offset * sizeof(say_color)));
    return offset + say_image_get_width(img) * say_image_get_height(img);
  }
  
  size_t say_pixel_bus_pull_target(say_pixel_bus *bus,
                                   say_target *target, size_t offset,
                                   size_t x, size_t y, size_t w, size_t h) {
    if (!say_target_make_current(target))
      return offset + w * h;
  
    say_pixel_bus_bind_pack(bus);
  
    glReadPixels(x, target->size.y - y - h, w, h, GL_RGBA,
                 GL_UNSIGNED_BYTE, (void*)(offset * sizeof(say_color)));
    return offset + w * h;
  }
  
  void say_pixel_bus_push_image(say_pixel_bus *bus,
                                  say_image *img, size_t offset,
                                  size_t x, size_t y, size_t w, size_t h) {
    say_image_bind(img);
    say_pixel_bus_bind_unpack(bus);
  
    glTexSubImage2D(GL_TEXTURE_2D, 0,
                    x, y, w, h,
                    GL_RGBA, GL_UNSIGNED_BYTE,
                    (void*)(offset * sizeof(say_color)));
    say_image_mark_out_of_date(img);
  }
  
  void say_pixel_bus_bind_pack(say_pixel_bus *bus) {
    say_context_ensure();
    say_pack_pbo_make_current(bus->pbo);
  }
  
  void say_pixel_bus_bind_unpack(say_pixel_bus *bus) {
    say_context_ensure();
    say_unpack_pbo_make_current(bus->pbo);
  }
  
  void say_pixel_bus_unbind_pack() {
    if (say_pixel_bus_is_available()) say_pack_pbo_make_current(0);
  }
  
  void say_pixel_bus_unbind_unpack() {
    if (say_pixel_bus_is_available()) say_unpack_pbo_make_current(0);
  }
  
  void say_pixel_bus_bind(say_pixel_bus *bus) {
    say_pixel_bus_bind_pack(bus);
    say_pixel_bus_bind_unpack(bus);
  }
  
  void say_pixel_bus_unbind() {
    say_pixel_bus_unbind_pack();
    say_pixel_bus_unbind_unpack();
  }
  
  void say_pixel_bus_resize(say_pixel_bus *bus, size_t new_size) {
    /* This is slow, so better not go through this for no reason. */
    if (bus->size == new_size) return;
  
    say_pixel_bus_bind_pack(bus);
  
    void *buffer = malloc(sizeof(say_color) * new_size);
    size_t copied_size = new_size > bus->size ? bus->size : new_size;
    glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, copied_size * sizeof(say_color),
                       buffer);
  
    glBufferData(GL_PIXEL_PACK_BUFFER, new_size * sizeof(say_color),
                 buffer, bus->mode);
  
    free(buffer);
  
    bus->size = new_size;
  }
  
  void say_pixel_bus_resize_fast(say_pixel_bus *bus, size_t new_size) {
    if (bus->size == new_size) return;
  
    say_pixel_bus_bind_pack(bus);
    glBufferData(GL_PIXEL_PACK_BUFFER, new_size * sizeof(say_color), NULL,
                 bus->mode);
  
    bus->size = new_size;
  }
  
  size_t say_pixel_bus_get_size(say_pixel_bus *bus) {
    return bus->size;
  }
  
  GLuint say_pixel_bus_get_pbo(say_pixel_bus *bus) {
    return bus->pbo;
  }
  