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>
#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)

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 */
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);

View file

@ -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);