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:
parent
1bb925e1d5
commit
9ae3ee1da1
66
src/canvas.c
66
src/canvas.c
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue