8309621: [XWayland][Screencast] screen capture failure with sun.java2d.uiScale other than 1

Reviewed-by: prr, honkar
This commit is contained in:
Alexander Zvegintsev 2023-10-11 22:14:23 +00:00
parent 8d2ad2b1ae
commit 387896fb34
8 changed files with 188 additions and 68 deletions

View File

@ -26,11 +26,14 @@
package sun.awt.screencast;
import sun.awt.UNIXToolkit;
import sun.java2d.pipe.Region;
import sun.security.action.GetPropertyAction;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.geom.AffineTransform;
import java.security.AccessController;
import java.util.Arrays;
import java.util.List;
@ -109,9 +112,20 @@ public class ScreencastHelper {
.stream(GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getScreenDevices())
.map(graphicsDevice ->
graphicsDevice.getDefaultConfiguration().getBounds()
).toList();
.map(graphicsDevice -> {
GraphicsConfiguration gc =
graphicsDevice.getDefaultConfiguration();
Rectangle screen = gc.getBounds();
AffineTransform tx = gc.getDefaultTransform();
return new Rectangle(
Region.clipRound(screen.x * tx.getScaleX()),
Region.clipRound(screen.y * tx.getScaleY()),
Region.clipRound(screen.width * tx.getScaleX()),
Region.clipRound(screen.height * tx.getScaleY())
);
})
.toList();
}
private static synchronized native void closeSession();

View File

@ -369,6 +369,17 @@ final class TokenStorage {
System.out.println("// getTokens same sizes 2. " + result);
}
// 3. add tokens with the same or greater number of screens
// This is useful if we once received a token with one screen resolution
// and the same screen was later scaled in the system.
// In that case, the token is still valid.
allTokenItems
.stream()
.filter(t ->
t.allowedScreensBounds.size() >= affectedScreenBounds.size())
.forEach(result::add);
return result;
}

View File

@ -319,6 +319,10 @@ GtkApi* gtk3_load(JNIEnv *env, const char* lib_name)
/* Pixbuf */
fp_gdk_pixbuf_new = dl_symbol("gdk_pixbuf_new");
fp_gdk_pixbuf_new_from_data = dl_symbol("gdk_pixbuf_new_from_data");
fp_gdk_pixbuf_scale_simple = dl_symbol("gdk_pixbuf_scale_simple");
fp_gdk_pixbuf_copy_area = dl_symbol("gdk_pixbuf_copy_area");
fp_gdk_pixbuf_new_from_file =
dl_symbol("gdk_pixbuf_new_from_file");
fp_gdk_pixbuf_get_from_drawable =
@ -3123,4 +3127,10 @@ static void gtk3_init(GtkApi* gtk) {
gtk->g_main_context_iteration = fp_g_main_context_iteration;
gtk->g_error_free = fp_g_error_free;
gtk->g_unix_fd_list_get = fp_g_unix_fd_list_get;
gtk->gdk_pixbuf_new = fp_gdk_pixbuf_new;
gtk->gdk_pixbuf_new_from_data = fp_gdk_pixbuf_new_from_data;
gtk->gdk_pixbuf_scale_simple = fp_gdk_pixbuf_scale_simple;
gtk->gdk_pixbuf_get_pixels = fp_gdk_pixbuf_get_pixels;
gtk->gdk_pixbuf_copy_area = fp_gdk_pixbuf_copy_area;
}

View File

@ -528,6 +528,30 @@ static void (*fp_gdk_draw_rectangle)(GdkDrawable*, GdkGC*, gboolean,
gint, gint, gint, gint);
static GdkPixbuf *(*fp_gdk_pixbuf_new)(GdkColorspace colorspace,
gboolean has_alpha, int bits_per_sample, int width, int height);
static GdkPixbuf *(*fp_gdk_pixbuf_new_from_data)(
const guchar *data,
GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height,
int rowstride,
GdkPixbufDestroyNotify destroy_fn,
gpointer destroy_fn_data
);
static void (*fp_gdk_pixbuf_copy_area) (
const GdkPixbuf* src_pixbuf,
int src_x,
int src_y,
int width,
int height,
GdkPixbuf* dest_pixbuf,
int dest_x,
int dest_y
);
static void (*fp_gdk_drawable_get_size)(GdkDrawable *drawable,
gint* width, gint* height);
static gboolean (*fp_gtk_init_check)(int* argc, char** argv);

View File

@ -532,6 +532,8 @@ typedef void (*GClosureNotify)(gpointer data, GClosure *closure);
typedef void (*GDestroyNotify)(gpointer data);
typedef void (*GCallback)(void);
typedef void GdkPixbuf;
typedef void (* GdkPixbufDestroyNotify) (guchar *pixels, gpointer data);
typedef struct GtkApi {
int version;
@ -797,6 +799,46 @@ typedef struct GtkApi {
gint index_,
GError **error);
GdkPixbuf *(*gdk_pixbuf_new)(GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height);
GdkPixbuf *(*gdk_pixbuf_new_from_data)(
const guchar *data,
GdkColorspace colorspace,
gboolean has_alpha,
int bits_per_sample,
int width,
int height,
int rowstride,
GdkPixbufDestroyNotify destroy_fn,
gpointer destroy_fn_data
);
GdkPixbuf *(*gdk_pixbuf_scale_simple)(GdkPixbuf *src,
int dest_width,
int dest_heigh,
GdkInterpType interp_type
);
guchar* (*gdk_pixbuf_get_pixels) (const GdkPixbuf* pixbuf);
void (*gdk_pixbuf_copy_area) (
const GdkPixbuf* src_pixbuf,
int src_x,
int src_y,
int width,
int height,
GdkPixbuf* dest_pixbuf,
int dest_x,
int dest_y
);
/* </for screencast, used only with GTK3> */
} GtkApi;

View File

@ -175,49 +175,6 @@ static gboolean initScreencast(const gchar *token,
return TRUE;
}
static inline void convertRGBxToBGRx(int* in) {
char* o = (char*) in;
char tmp = o[0];
o[0] = o[2];
o[2] = tmp;
}
static gchar * cropTo(
struct spa_data data,
struct spa_video_info_raw raw,
guint32 x,
guint32 y,
guint32 width,
guint32 height
) {
int srcW = raw.size.width;
if (data.chunk->stride / 4 != srcW) {
fprintf(stderr, "%s:%i Unexpected stride / 4: %i srcW: %i\n",
__func__, __LINE__, data.chunk->stride / 4, srcW);
}
int* d = data.data;
int *outData = calloc(width * height, sizeof(int));
if (!outData) {
ERR("failed to allocate memory\n");
return NULL;
}
gboolean needConversion = raw.format != SPA_VIDEO_FORMAT_BGRx;
for (guint32 j = y; j < y + height; ++j) {
for (guint32 i = x; i < x + width; ++i) {
int color = *(d + (j * srcW) + i);
if (needConversion) {
convertRGBxToBGRx(&color);
}
*(outData + ((j - y) * width) + (i - x)) = color;
}
}
return (gchar*) outData;
}
static void onStreamParamChanged(
void *userdata,
uint32_t id,
@ -301,24 +258,81 @@ static void onStreamProcess(void *userdata) {
struct spa_data spaData = spaBuffer->datas[0];
gint streamWidth = data->rawFormat.size.width;
gint streamHeight = data->rawFormat.size.height;
DEBUG_SCREEN(screen);
DEBUG_SCREEN_PREFIX(screen,
"got a frame of size %d offset %d stride %d "
"flags %d FD %li captureDataReady %i\n",
"flags %d FD %li captureDataReady %i of stream %dx%d\n",
spaBuffer->datas[0].chunk->size,
spaData.chunk->offset,
spaData.chunk->stride,
spaData.chunk->flags,
spaData.fd,
screen->captureDataReady
screen->captureDataReady,
streamWidth,
streamHeight
);
data->screenProps->captureData = cropTo(
spaData,
data->rawFormat,
screen->captureArea.x, screen->captureArea.y,
screen->captureArea.width, screen->captureArea.height
);
GdkRectangle captureArea = screen->captureArea;
GdkRectangle screenBounds = screen->bounds;
GdkPixbuf *pixbuf = gtk->gdk_pixbuf_new_from_data(spaData.data,
GDK_COLORSPACE_RGB,
TRUE,
8,
streamWidth,
streamHeight,
spaData.chunk->stride,
NULL,
NULL);
if (screen->bounds.width != streamWidth
|| screen->bounds.height != streamHeight) {
DEBUG_SCREEN_PREFIX(screen, "scaling stream data %dx%d -> %dx%d\n",
streamWidth, streamHeight,
screen->bounds.width, screen->bounds.height
);
GdkPixbuf *scaled = gtk->gdk_pixbuf_scale_simple(pixbuf,
screen->bounds.width,
screen->bounds.height,
GDK_INTERP_BILINEAR);
gtk->g_object_unref(pixbuf);
pixbuf = scaled;
}
GdkPixbuf *cropped = NULL;
if (captureArea.width != screenBounds.width
|| captureArea.height != screenBounds.height) {
cropped = gtk->gdk_pixbuf_new(GDK_COLORSPACE_RGB,
TRUE,
8,
captureArea.width,
captureArea.height);
if (cropped) {
gtk->gdk_pixbuf_copy_area(pixbuf,
captureArea.x,
captureArea.y,
captureArea.width,
captureArea.height,
cropped,
0, 0);
} else {
ERR("Cannot create a new pixbuf.\n");
}
gtk->g_object_unref(pixbuf);
pixbuf = NULL;
data->screenProps->captureDataPixbuf = cropped;
} else {
data->screenProps->captureDataPixbuf = pixbuf;
}
screen->captureDataReady = TRUE;
@ -366,11 +380,7 @@ static bool startStream(
SPA_FORMAT_mediaSubtype,
SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
SPA_FORMAT_VIDEO_format,
SPA_POD_CHOICE_ENUM_Id(
2,
SPA_VIDEO_FORMAT_RGBx,
SPA_VIDEO_FORMAT_BGRx
),
SPA_POD_Id(SPA_VIDEO_FORMAT_BGRx),
SPA_FORMAT_VIDEO_size,
SPA_POD_CHOICE_RANGE_Rectangle(
&SPA_RECTANGLE(320, 240),
@ -910,7 +920,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
"\t||\tx %5i y %5i w %5i h %5i %s\n"
"\t||\tx %5i y %5i w %5i h %5i %s\n"
"\t||\tx %5i y %5i w %5i h %5i %s\n\n",
i, screenProps->captureData,
i, screenProps->captureDataPixbuf,
requestedArea.x, requestedArea.y,
requestedArea.width, requestedArea.height,
"requested area",
@ -924,7 +934,7 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
"in-screen coords capture area"
);
if (screenProps->captureData) {
if (screenProps->captureDataPixbuf) {
for (int y = 0; y < captureArea.height; y++) {
jsize preY = (requestedArea.y > screenProps->bounds.y)
? 0
@ -939,14 +949,18 @@ JNIEXPORT jint JNICALL Java_sun_awt_screencast_ScreencastHelper_getRGBPixelsImpl
(*env)->SetIntArrayRegion(
env, pixelArray,
start, len,
((jint *) screenProps->captureData)
+ (captureArea.width * y)
((jint *) gtk->gdk_pixbuf_get_pixels(
screenProps->captureDataPixbuf
))
+ (captureArea.width * y)
);
}
}
free(screenProps->captureData);
screenProps->captureData = NULL;
if (screenProps->captureDataPixbuf) {
gtk->g_object_unref(screenProps->captureDataPixbuf);
screenProps->captureDataPixbuf = NULL;
}
screenProps->shouldCapture = FALSE;
fp_pw_thread_loop_lock(pw.loop);

View File

@ -48,7 +48,7 @@ struct ScreenProps {
GdkRectangle captureArea;
struct PwStreamData *data;
gchar *captureData;
GdkPixbuf *captureDataPixbuf;
volatile gboolean shouldCapture;
volatile gboolean captureDataReady;
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2022, JetBrains s.r.o.. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
@ -32,7 +32,6 @@ import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import javax.swing.UIManager;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
@ -54,6 +53,12 @@ public class ScreenCaptureGtkTest {
Color.GREEN, Color.BLUE, Color.ORANGE, Color.RED};
public static void main(String[] args) throws Exception {
if ("2".equals(System.getProperty("jdk.gtk.version"))
&& System.getenv("WAYLAND_DISPLAY") != null) {
// screen capture is not supported with gtk2 on Wayland
return;
}
final int topOffset = 50;
final int leftOffset = 50;