~exec64/imv

9ae3ee1da14dcd88bbdb47ec0eac8ba3f2191d7e — Tuomas Siipola 2 years ago 1bb925e
Show chequered pattern on image instead of viewport

OpenGL is used for rendering because `cairo_rectangle` and
`cairo_pattern_t` had performance problems on zoomed and rotated images.

The size and contrast of the chequered pattern is also reduces to match
other image viewers and editors.

Closes #253
3 files changed, 66 insertions(+), 14 deletions(-)

M src/canvas.c
M src/canvas.h
M src/imv.c
M src/canvas.c => src/canvas.c +58 -8
@@ 17,6 17,11 @@
#include <librsvg/rsvg.h>
#endif

// 16x16 chequerboard texture data
#define REPEAT8(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__, __VA_ARGS__
unsigned char checkers_data[] = { REPEAT8(REPEAT8(0xCC, 0xCC, 0xCC, 0xFF), REPEAT8(0x80, 0x80, 0x80, 0xFF)),
                                  REPEAT8(REPEAT8(0x80, 0x80, 0x80, 0xFF), REPEAT8(0xCC, 0xCC, 0xCC, 0xFF)) };

struct imv_canvas {
  cairo_surface_t *surface;
  cairo_t *cairo;


@@ 28,6 33,7 @@ struct imv_canvas {
    struct imv_bitmap *bitmap;
    GLuint texture;
  } cache;
  GLuint checkers_texture;
};

struct imv_canvas *imv_canvas_create(int width, int height)


@@ 45,6 51,20 @@ struct imv_canvas *imv_canvas_create(int width, int height)
  glGenTextures(1, &canvas->texture);
  assert(canvas->texture);

  glGenTextures(1, &canvas->checkers_texture);
  assert(canvas->checkers_texture);

  glBindTexture(GL_TEXTURE_2D, canvas->checkers_texture);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
  glPixelStorei(GL_UNPACK_ROW_LENGTH, 16);
  glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
  glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 16, 16, 0, GL_RGBA,
               GL_UNSIGNED_INT_8_8_8_8_REV, checkers_data);

  canvas->width = width;
  canvas->height = height;



@@ 66,6 86,7 @@ void imv_canvas_free(struct imv_canvas *canvas)
  if (canvas->cache.texture) {
    glDeleteTextures(1, &canvas->cache.texture);
  }
  glDeleteTextures(1, &canvas->checkers_texture);
  free(canvas);
}



@@ 111,16 132,45 @@ void imv_canvas_fill(struct imv_canvas *canvas)
  cairo_fill(canvas->cairo);
}

void imv_canvas_fill_checkers(struct imv_canvas *canvas, int size)
void imv_canvas_fill_checkers(struct imv_canvas *canvas, struct imv_image *image,
                              int bx, int by, double scale,
                              double rotation, bool mirrored)
{
  for (int x = 0; x < canvas->width; x += size) {
    for (int y = 0; y < canvas->height; y += size) {
      float color = ((x/size + y/size) % 2 == 0) ? 0.25 : 0.75;
      cairo_set_source_rgba(canvas->cairo, color, color, color, 1);
      cairo_rectangle(canvas->cairo, x, y, size, size);
      cairo_fill(canvas->cairo);
    }
  GLint viewport[4];
  glGetIntegerv(GL_VIEWPORT, viewport);

  glPushMatrix();
  glOrtho(0.0, viewport[2], viewport[3], 0.0, 0.0, 10.0);

  glBindTexture(GL_TEXTURE_2D, canvas->checkers_texture);
  glEnable(GL_TEXTURE_2D);

  const int left = bx;
  const int top = by;
  const int right = left + imv_image_width(image) * scale;
  const int bottom = top + imv_image_height(image) * scale;
  const int center_x = left + imv_image_width(image) * scale / 2;
  const int center_y = top + imv_image_height(image) * scale / 2;
  const float s = (right - left) / 16.0;
  const float t = s * imv_image_height(image) / imv_image_width(image);

  glTranslated(center_x, center_y, 0);
  if (mirrored) {
    glScaled(-1, 1, 1);
  }
  glRotated(rotation, 0, 0, 1);
  glTranslated(-center_x, -center_y, 0);

  glBegin(GL_TRIANGLE_FAN);
  glTexCoord2f(0, 0); glVertex2i(left, top);
  glTexCoord2f(s, 0); glVertex2i(right, top);
  glTexCoord2f(s, t); glVertex2i(right, bottom);
  glTexCoord2f(0, t); glVertex2i(left, bottom);
  glEnd();

  glBindTexture(GL_TEXTURE_2D, 0);
  glDisable(GL_TEXTURE_2D);
  glPopMatrix();
}

void imv_canvas_font(struct imv_canvas *canvas, const char *name, int size)

M src/canvas.h => src/canvas.h +4 -2
@@ 33,8 33,10 @@ void imv_canvas_fill_rectangle(struct imv_canvas *canvas, int x, int y, int widt
/* Fill the whole canvas with the current color */
void imv_canvas_fill(struct imv_canvas *canvas);

/* Fill the whole canvas with a chequerboard pattern */
void imv_canvas_fill_checkers(struct imv_canvas *canvas, int size);
/* Blit the given image area with a chequerboard pattern to the current OpenGL framebuffer */
void imv_canvas_fill_checkers(struct imv_canvas *canvas, struct imv_image *image,
                              int x, int y, double scale,
                              double rotation, bool mirrored);

/* Select the font to draw text with */
void imv_canvas_font(struct imv_canvas *canvas, const char *name, int size);

M src/imv.c => src/imv.c +4 -4
@@ 1319,10 1319,6 @@ static void render_window(struct imv *imv)
        1.0);
    imv_canvas_fill(imv->canvas);
    imv_canvas_draw(imv->canvas);
  } else {
    /* chequered background */
    imv_canvas_fill_checkers(imv->canvas, 16);
    imv_canvas_draw(imv->canvas);
  }

  /* draw our actual image */


@@ 1334,6 1330,10 @@ static void render_window(struct imv *imv)
    imv_viewport_get_scale(imv->view, &scale);
    imv_viewport_get_rotation(imv->view, &rotation);
    imv_viewport_get_mirrored(imv->view, &mirrored);
    if (imv->background.type == BACKGROUND_CHEQUERED) {
      imv_canvas_fill_checkers(imv->canvas, imv->current_image,
                               x, y, scale, rotation, mirrored);
    }
    imv_canvas_draw_image(imv->canvas, imv->current_image,
                          x, y, scale, rotation, mirrored,
                          imv->upscaling_method, imv->cache_invalidated);