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
This commit is contained in:
Tuomas Siipola 2020-06-28 16:01:01 +03:00 committed by Harry Jeffery
parent 1bb925e1d5
commit 9ae3ee1da1
3 changed files with 66 additions and 14 deletions

View file

@ -17,6 +17,11 @@
#include <librsvg/rsvg.h> #include <librsvg/rsvg.h>
#endif #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 { struct imv_canvas {
cairo_surface_t *surface; cairo_surface_t *surface;
cairo_t *cairo; cairo_t *cairo;
@ -28,6 +33,7 @@ struct imv_canvas {
struct imv_bitmap *bitmap; struct imv_bitmap *bitmap;
GLuint texture; GLuint texture;
} cache; } cache;
GLuint checkers_texture;
}; };
struct imv_canvas *imv_canvas_create(int width, int height) 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); glGenTextures(1, &canvas->texture);
assert(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->width = width;
canvas->height = height; canvas->height = height;
@ -66,6 +86,7 @@ void imv_canvas_free(struct imv_canvas *canvas)
if (canvas->cache.texture) { if (canvas->cache.texture) {
glDeleteTextures(1, &canvas->cache.texture); glDeleteTextures(1, &canvas->cache.texture);
} }
glDeleteTextures(1, &canvas->checkers_texture);
free(canvas); free(canvas);
} }
@ -111,16 +132,45 @@ void imv_canvas_fill(struct imv_canvas *canvas)
cairo_fill(canvas->cairo); 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) { GLint viewport[4];
for (int y = 0; y < canvas->height; y += size) { glGetIntegerv(GL_VIEWPORT, viewport);
float color = ((x/size + y/size) % 2 == 0) ? 0.25 : 0.75;
cairo_set_source_rgba(canvas->cairo, color, color, color, 1); glPushMatrix();
cairo_rectangle(canvas->cairo, x, y, size, size); glOrtho(0.0, viewport[2], viewport[3], 0.0, 0.0, 10.0);
cairo_fill(canvas->cairo);
} 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) void imv_canvas_font(struct imv_canvas *canvas, const char *name, int size)

View file

@ -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 */ /* Fill the whole canvas with the current color */
void imv_canvas_fill(struct imv_canvas *canvas); void imv_canvas_fill(struct imv_canvas *canvas);
/* Fill the whole canvas with a chequerboard pattern */ /* Blit the given image area with a chequerboard pattern to the current OpenGL framebuffer */
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 x, int y, double scale,
double rotation, bool mirrored);
/* Select the font to draw text with */ /* Select the font to draw text with */
void imv_canvas_font(struct imv_canvas *canvas, const char *name, int size); void imv_canvas_font(struct imv_canvas *canvas, const char *name, int size);

View file

@ -1319,10 +1319,6 @@ static void render_window(struct imv *imv)
1.0); 1.0);
imv_canvas_fill(imv->canvas); imv_canvas_fill(imv->canvas);
imv_canvas_draw(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 */ /* 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_scale(imv->view, &scale);
imv_viewport_get_rotation(imv->view, &rotation); imv_viewport_get_rotation(imv->view, &rotation);
imv_viewport_get_mirrored(imv->view, &mirrored); 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, imv_canvas_draw_image(imv->canvas, imv->current_image,
x, y, scale, rotation, mirrored, x, y, scale, rotation, mirrored,
imv->upscaling_method, imv->cache_invalidated); imv->upscaling_method, imv->cache_invalidated);