diff --git a/src/libply-splash-core/ply-pixel-buffer.c b/src/libply-splash-core/ply-pixel-buffer.c index 61ee91c4..52a3f86e 100644 --- a/src/libply-splash-core/ply-pixel-buffer.c +++ b/src/libply-splash-core/ply-pixel-buffer.c @@ -43,11 +43,13 @@ struct _ply_pixel_buffer { uint32_t *bytes; - ply_rectangle_t area; - ply_list_t *clip_areas; + ply_rectangle_t area; /* in device pixels */ + ply_rectangle_t logical_area; /* in logical pixels */ + ply_list_t *clip_areas; /* in device pixels */ - ply_region_t *updated_areas; + ply_region_t *updated_areas; /* in device pixels */ uint32_t is_opaque : 1; + int device_scale; }; static inline void ply_pixel_buffer_blend_value_at_pixel (ply_pixel_buffer_t *buffer, @@ -168,6 +170,34 @@ ply_pixel_buffer_blend_value_at_pixel (ply_pixel_buffer_t *buffer, buffer->bytes[y * buffer->area.width + x] = pixel_value; } +static void +ply_rectangle_upscale (ply_rectangle_t *area, + int scale) +{ + area->x *= scale; + area->y *= scale; + area->width *= scale; + area->height *= scale; +} + +static void +ply_rectangle_downscale (ply_rectangle_t *area, + int scale) +{ + area->x /= scale; + area->y /= scale; + area->width /= scale; + area->height /= scale; +} + +static void +ply_pixel_buffer_adjust_area_for_device_scale (ply_pixel_buffer_t *buffer, + ply_rectangle_t *area) +{ + ply_rectangle_upscale (area, buffer->device_scale); +} + +/* this function will also convert logical pixels to device pixels */ static void ply_pixel_buffer_crop_area_to_clip_area (ply_pixel_buffer_t *buffer, ply_rectangle_t *area, @@ -176,6 +206,7 @@ ply_pixel_buffer_crop_area_to_clip_area (ply_pixel_buffer_t *buffer, ply_list_node_t *node; *cropped_area = *area; + ply_pixel_buffer_adjust_area_for_device_scale (buffer, cropped_area); node = ply_list_get_first_node (buffer->clip_areas); while (node != NULL) { @@ -199,6 +230,9 @@ ply_pixel_buffer_fill_area_with_pixel_value (ply_pixel_buffer_t *buffer, unsigned long row, column; ply_rectangle_t cropped_area; + if (fill_area == NULL) + fill_area = &buffer->logical_area; + ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area); /* If we're filling the entire buffer with a fully opaque color, @@ -216,6 +250,8 @@ ply_pixel_buffer_fill_area_with_pixel_value (ply_pixel_buffer_t *buffer, pixel_value); } } + + ply_region_add_rectangle (buffer->updated_areas, &cropped_area); } void @@ -227,6 +263,8 @@ ply_pixel_buffer_push_clip_area (ply_pixel_buffer_t *buffer, new_clip_area = malloc (sizeof(*new_clip_area)); *new_clip_area = *clip_area; + ply_pixel_buffer_adjust_area_for_device_scale (buffer, new_clip_area); + ply_list_append_data (buffer->clip_areas, new_clip_area); } @@ -252,6 +290,8 @@ ply_pixel_buffer_new (unsigned long width, buffer->bytes = (uint32_t *) calloc (height, width * sizeof(uint32_t)); buffer->area.width = width; buffer->area.height = height; + buffer->logical_area = buffer->area; + buffer->device_scale = 1; buffer->clip_areas = ply_list_new (); ply_pixel_buffer_push_clip_area (buffer, &buffer->area); @@ -290,21 +330,21 @@ ply_pixel_buffer_get_size (ply_pixel_buffer_t *buffer, assert (buffer != NULL); assert (size != NULL); - *size = buffer->area; + *size = buffer->logical_area; } unsigned long ply_pixel_buffer_get_width (ply_pixel_buffer_t *buffer) { assert (buffer != NULL); - return buffer->area.width; + return buffer->logical_area.width; } unsigned long ply_pixel_buffer_get_height (ply_pixel_buffer_t *buffer) { assert (buffer != NULL); - return buffer->area.height; + return buffer->logical_area.height; } bool @@ -386,7 +426,7 @@ ply_pixel_buffer_fill_with_gradient (ply_pixel_buffer_t *buffer, ply_rectangle_t cropped_area; if (fill_area == NULL) - fill_area = &buffer->area; + fill_area = &buffer->logical_area; ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area); @@ -457,24 +497,16 @@ ply_pixel_buffer_fill_with_color (ply_pixel_buffer_t *buffer, double alpha) { uint32_t pixel_value; - ply_rectangle_t cropped_area; assert (buffer != NULL); - if (fill_area == NULL) - fill_area = &buffer->area; - - ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area); - red *= alpha; green *= alpha; blue *= alpha; pixel_value = PLY_PIXEL_BUFFER_COLOR_TO_PIXEL_VALUE (red, green, blue, alpha); - ply_pixel_buffer_fill_area_with_pixel_value (buffer, &cropped_area, pixel_value); - - ply_region_add_rectangle (buffer->updated_areas, &cropped_area); + ply_pixel_buffer_fill_area_with_pixel_value (buffer, fill_area, pixel_value); } void @@ -483,7 +515,6 @@ ply_pixel_buffer_fill_with_hex_color_at_opacity (ply_pixel_buffer_t *buffer, uint32_t hex_color, double opacity) { - ply_rectangle_t cropped_area; uint32_t pixel_value; double red; double green; @@ -492,11 +523,6 @@ ply_pixel_buffer_fill_with_hex_color_at_opacity (ply_pixel_buffer_t *buffer, assert (buffer != NULL); - if (fill_area == NULL) - fill_area = &buffer->area; - - ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area); - /* if they only gave an rgb hex number, assume an alpha of 0xff */ if ((hex_color & 0xff000000) == 0) @@ -515,9 +541,7 @@ ply_pixel_buffer_fill_with_hex_color_at_opacity (ply_pixel_buffer_t *buffer, pixel_value = PLY_PIXEL_BUFFER_COLOR_TO_PIXEL_VALUE (red, green, blue, alpha); - ply_pixel_buffer_fill_area_with_pixel_value (buffer, &cropped_area, pixel_value); - - ply_region_add_rectangle (buffer->updated_areas, &cropped_area); + ply_pixel_buffer_fill_area_with_pixel_value (buffer, fill_area, pixel_value); } void @@ -529,48 +553,120 @@ ply_pixel_buffer_fill_with_hex_color (ply_pixel_buffer_t *buffer, hex_color, 1.0); } +static inline uint32_t +ply_pixels_interpolate (uint32_t *bytes, + int width, + int height, + double x, + double y) +{ + int ix; + int iy; + int i; + int offset_x; + int offset_y; + uint32_t pixels[2][2]; + uint32_t reply = 0; + + for (offset_y = 0; offset_y < 2; offset_y++) { + for (offset_x = 0; offset_x < 2; offset_x++) { + ix = x + offset_x; + iy = y + offset_y; + + if (ix < 0 || ix >= width || iy < 0 || iy >= height) + pixels[offset_y][offset_x] = 0x00000000; + else + pixels[offset_y][offset_x] = bytes[ix + iy * width]; + } + } + if (!pixels[0][0] && !pixels[0][1] && !pixels[1][0] && !pixels[1][1]) return 0; + + ix = x; + iy = y; + x -= ix; + y -= iy; + for (i = 0; i < 4; i++) { + uint32_t value = 0; + uint32_t mask = 0xFF << (i * 8); + value += ((pixels[0][0]) & mask) * (1 - x) * (1 - y); + value += ((pixels[0][1]) & mask) * x * (1 - y); + value += ((pixels[1][0]) & mask) * (1 - x) * y; + value += ((pixels[1][1]) & mask) * x * y; + reply |= value & mask; + } + return reply; +} + void -ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buffer_t *buffer, - ply_rectangle_t *fill_area, - ply_rectangle_t *clip_area, - uint32_t *data, - double opacity) +ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (ply_pixel_buffer_t *buffer, + ply_rectangle_t *fill_area, + ply_rectangle_t *clip_area, + uint32_t *data, + double opacity, + int scale) { unsigned long row, column; uint8_t opacity_as_byte; + ply_rectangle_t logical_fill_area; ply_rectangle_t cropped_area; unsigned long x; unsigned long y; + double scale_factor; assert (buffer != NULL); - if (fill_area == NULL) - fill_area = &buffer->area; + if (fill_area == NULL) { + fill_area = &buffer->logical_area; + logical_fill_area = buffer->logical_area; + } else { + logical_fill_area = *fill_area; + ply_rectangle_downscale (&logical_fill_area, scale); + } - ply_pixel_buffer_crop_area_to_clip_area (buffer, fill_area, &cropped_area); + ply_pixel_buffer_crop_area_to_clip_area (buffer, &logical_fill_area, &cropped_area); - if (clip_area) - ply_rectangle_intersect (&cropped_area, clip_area, &cropped_area); + if (clip_area) { + ply_rectangle_t device_clip_area; + + device_clip_area = *clip_area; + ply_rectangle_downscale (&device_clip_area, scale); + ply_pixel_buffer_adjust_area_for_device_scale (buffer, &device_clip_area); + ply_rectangle_intersect (&cropped_area, &device_clip_area, &cropped_area); + } if (cropped_area.width == 0 || cropped_area.height == 0) return; - x = cropped_area.x - fill_area->x; - y = cropped_area.y - fill_area->y; opacity_as_byte = (uint8_t) (opacity * 255.0); + scale_factor = (double)scale / buffer->device_scale; + x = cropped_area.x; + y = cropped_area.y; + /* column, row are the point we want to write into, in + pixel_buffer coordinate space (device pixels) + + scale_factor * (column - fill_area->x), scale_factor * (row - fill_area->y) + is the point we want to source from, in the data coordinate + space */ for (row = y; row < y + cropped_area.height; row++) { for (column = x; column < x + cropped_area.width; column++) { uint32_t pixel_value; - pixel_value = data[fill_area->width * row + column]; + if (buffer->device_scale == scale) + pixel_value = data[fill_area->width * (row - fill_area->y) + + column - fill_area->x]; + else + pixel_value = ply_pixels_interpolate (data, + fill_area->width, + fill_area->height, + scale_factor * column - fill_area->x, + scale_factor * row - fill_area->y); if ((pixel_value >> 24) == 0x00) continue; pixel_value = make_pixel_value_translucent (pixel_value, opacity_as_byte); ply_pixel_buffer_blend_value_at_pixel (buffer, - cropped_area.x + (column - x), - cropped_area.y + (row - y), + column, row, pixel_value); } } @@ -578,16 +674,31 @@ ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buffer_t ply_region_add_rectangle (buffer->updated_areas, &cropped_area); } +void +ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buffer_t *buffer, + ply_rectangle_t *fill_area, + ply_rectangle_t *clip_area, + uint32_t *data, + double opacity) +{ + ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer, + fill_area, + clip_area, + data, + opacity, + 1); +} + void ply_pixel_buffer_fill_with_argb32_data_at_opacity (ply_pixel_buffer_t *buffer, ply_rectangle_t *fill_area, uint32_t *data, double opacity) { - ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (buffer, - fill_area, - NULL, - data, opacity); + ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer, + fill_area, + NULL, + data, opacity, 1); } void @@ -595,10 +706,10 @@ ply_pixel_buffer_fill_with_argb32_data (ply_pixel_buffer_t *buffer, ply_rectangle_t *fill_area, uint32_t *data) { - ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (buffer, - fill_area, - NULL, - data, 1.0); + ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer, + fill_area, + NULL, + data, 1.0, 1); } void @@ -607,10 +718,10 @@ ply_pixel_buffer_fill_with_argb32_data_with_clip (ply_pixel_buffer_t *buffer, ply_rectangle_t *clip_area, uint32_t *data) { - ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (buffer, - fill_area, - clip_area, - data, 1.0); + ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (buffer, + fill_area, + clip_area, + data, 1.0, 1); } static void @@ -628,35 +739,6 @@ ply_pixel_buffer_copy_area (ply_pixel_buffer_t *canvas, } } -static void -ply_pixel_buffer_blend_area (ply_pixel_buffer_t *canvas, - ply_pixel_buffer_t *source, - int x, int y, - ply_rectangle_t *cropped_area, - double opacity) -{ - unsigned long row, column; - uint8_t opacity_as_byte = (uint8_t) (opacity * 255.0); - - for (row = y; row < y + cropped_area->height; row++) { - for (column = x; column < x + cropped_area->width; column++) { - uint32_t pixel_value; - - pixel_value = source->bytes[row * source->area.width + column]; - - pixel_value = make_pixel_value_translucent (pixel_value, opacity_as_byte); - - if ((pixel_value >> 24) == 0x00) - continue; - - ply_pixel_buffer_blend_value_at_pixel (canvas, - cropped_area->x + (column - x), - cropped_area->y + (row - y), - pixel_value); - } - } -} - void ply_pixel_buffer_fill_with_buffer_at_opacity_with_clip (ply_pixel_buffer_t *canvas, ply_pixel_buffer_t *source, @@ -665,36 +747,51 @@ ply_pixel_buffer_fill_with_buffer_at_opacity_with_clip (ply_pixel_buffer_t *canv ply_rectangle_t *clip_area, float opacity) { - ply_rectangle_t cropped_area; + ply_rectangle_t fill_area; unsigned long x; unsigned long y; assert (canvas != NULL); assert (source != NULL); - cropped_area.x = x_offset; - cropped_area.y = y_offset; - cropped_area.width = source->area.width; - cropped_area.height = source->area.height; + /* Fast path to memcpy if we need no blending or scaling */ + if (opacity == 1.0 && ply_pixel_buffer_is_opaque (source) && + canvas->device_scale == source->device_scale) { + ply_rectangle_t cropped_area; - ply_pixel_buffer_crop_area_to_clip_area (canvas, &cropped_area, &cropped_area); + cropped_area.x = x_offset; + cropped_area.y = y_offset; + cropped_area.width = source->logical_area.width; + cropped_area.height = source->logical_area.height; - if (clip_area) - ply_rectangle_intersect (&cropped_area, clip_area, &cropped_area); + ply_pixel_buffer_crop_area_to_clip_area (canvas, &cropped_area, &cropped_area); - if (cropped_area.width == 0 || cropped_area.height == 0) - return; + /* clip_area is in source device pixels, which are also canvas device pixels */ + if (clip_area) + ply_rectangle_intersect (&cropped_area, clip_area, &cropped_area); - x = cropped_area.x - x_offset; - y = cropped_area.y - y_offset; + if (cropped_area.width == 0 || cropped_area.height == 0) + return; + + x = cropped_area.x - x_offset; + y = cropped_area.y - y_offset; - if (opacity == 1.0 && ply_pixel_buffer_is_opaque (source)) ply_pixel_buffer_copy_area (canvas, source, x, y, &cropped_area); - else - ply_pixel_buffer_blend_area (canvas, source, x, y, &cropped_area, - opacity); - ply_region_add_rectangle (canvas->updated_areas, &cropped_area); + ply_region_add_rectangle (canvas->updated_areas, &cropped_area); + } else { + fill_area.x = x_offset * source->device_scale; + fill_area.y = y_offset * source->device_scale; + fill_area.width = source->area.width; + fill_area.height = source->area.height; + + ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (canvas, + &fill_area, + clip_area, + source->bytes, + opacity, + source->device_scale); + } } void @@ -752,52 +849,15 @@ ply_pixel_buffer_interpolate (ply_pixel_buffer_t *buffer, double x, double y) { - int ix; - int iy; int width; int height; - - int i; - - int offset_x; - int offset_y; - uint32_t pixels[2][2]; - uint32_t reply = 0; uint32_t *bytes; width = buffer->area.width; height = buffer->area.height; - - bytes = ply_pixel_buffer_get_argb32_data (buffer); - for (offset_y = 0; offset_y < 2; offset_y++) { - for (offset_x = 0; offset_x < 2; offset_x++) { - ix = x + offset_x; - iy = y + offset_y; - - if (ix < 0 || ix >= width || iy < 0 || iy >= height) - pixels[offset_y][offset_x] = 0x00000000; - else - pixels[offset_y][offset_x] = bytes[ix + iy * width]; - } - } - if (!pixels[0][0] && !pixels[0][1] && !pixels[1][0] && !pixels[1][1]) return 0; - - ix = x; - iy = y; - x -= ix; - y -= iy; - for (i = 0; i < 4; i++) { - uint32_t value = 0; - uint32_t mask = 0xFF << (i * 8); - value += ((pixels[0][0]) & mask) * (1 - x) * (1 - y); - value += ((pixels[0][1]) & mask) * x * (1 - y); - value += ((pixels[1][0]) & mask) * (1 - x) * y; - value += ((pixels[1][1]) & mask) * x * y; - reply |= value & mask; - } - return reply; + return ply_pixels_interpolate (bytes, width, height, x, y); } ply_pixel_buffer_t * @@ -908,4 +968,20 @@ ply_pixel_buffer_tile (ply_pixel_buffer_t *old_buffer, return buffer; } +int +ply_pixel_buffer_get_device_scale (ply_pixel_buffer_t *buffer) +{ + return buffer->device_scale; +} + +void +ply_pixel_buffer_set_device_scale (ply_pixel_buffer_t *buffer, + int scale) +{ + buffer->device_scale = scale; + + buffer->logical_area.width = buffer->area.width / scale; + buffer->logical_area.height = buffer->area.height / scale; +} + /* vim: set ts=4 sw=4 et ai ci cino={.5s,^-2,+.5s,t0,g0,e-2,n-2,p2s,(0,=.5s,:.5s */ diff --git a/src/libply-splash-core/ply-pixel-buffer.h b/src/libply-splash-core/ply-pixel-buffer.h index a374c6c5..595e9bd0 100644 --- a/src/libply-splash-core/ply-pixel-buffer.h +++ b/src/libply-splash-core/ply-pixel-buffer.h @@ -43,6 +43,9 @@ ply_pixel_buffer_t *ply_pixel_buffer_new (unsigned long width, void ply_pixel_buffer_free (ply_pixel_buffer_t *buffer); void ply_pixel_buffer_get_size (ply_pixel_buffer_t *buffer, ply_rectangle_t *size); +int ply_pixel_buffer_get_device_scale (ply_pixel_buffer_t *buffer); +void ply_pixel_buffer_set_device_scale (ply_pixel_buffer_t *buffer, + int scale); unsigned long ply_pixel_buffer_get_width (ply_pixel_buffer_t *buffer); unsigned long ply_pixel_buffer_get_height (ply_pixel_buffer_t *buffer); @@ -90,6 +93,12 @@ void ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip (ply_pixel_buff ply_rectangle_t *clip_area, uint32_t *data, double opacity); +void ply_pixel_buffer_fill_with_argb32_data_at_opacity_with_clip_and_scale (ply_pixel_buffer_t *buffer, + ply_rectangle_t *fill_area, + ply_rectangle_t *clip_area, + uint32_t *data, + double opacity, + int scale); void ply_pixel_buffer_fill_with_buffer_at_opacity_with_clip (ply_pixel_buffer_t *canvas, ply_pixel_buffer_t *source,