How can I draw a circle to the screen with PlayN? - playn

This is about as basic as it gets. I don't want to use an image file. Rather, I want to programmatically draw a circle and blit it to a surface (as they say in pygame).
I tried to follow the "Using CanvasLayers" example here:
https://developers.google.com/playn/devguide/rendering
From my game class:
// Surface
SurfaceLayer surface;
// Background
int width = 640;
int height = 480;
//ImageLayer bgLayer;
CanvasImage bgImage;
Canvas canvas;
// Circle
CanvasImage circleImage;
//ImageLayer circleLayer;
int circleRadius = 20;
int circleX = 0;
int circleY = 0;
#Override
public void init() {
// create a surface
surface = graphics().createSurfaceLayer(width, height);
graphics().rootLayer().add(surface);
// create a solid background
// http://code.google.com/p/playn101/source/browse/core/src/main/java/playn101/core/J.java#81
bgImage = graphics().createImage(width, height);
canvas = bgImage.canvas();
canvas.setFillColor(0xff87ceeb);
canvas.fillRect(0, 0, width, height);
//bgLayer = graphics().createImageLayer(bgImage);
//graphics().rootLayer().add(bgLayer);
// create a circle
circleImage = graphics().createImage(circleRadius, circleRadius);
canvas = circleImage.canvas();
canvas.setFillColor(0xff0000eb);
canvas.fillCircle(circleX, circleY, circleRadius);
//circleLayer = graphics().createImageLayer(circleImage);
//graphics().rootLayer().add(circleLayer);
}
#Override
public void paint(float alpha) {
// the background automatically paints itself, so no need to do anything
// here!
surface.clear(0);
surface.drawImage(bgImage, 0, 0);
surface.drawImage(circleImage, 100, 100);
}
But I get a blank window in Java and Eclipse complains:
The method drawImage(CanvasImage, int, int) is undefined for the type
SurfaceLayer
That, however, is the way it is used in the example at the link.

If the code you provided even compiles, then you are using some very old version of PlayN.
Update to PlayN 1.1.1 and fix the compilation errors that result, and your code will work fine.
The following is your code updated to work with PlayN 1.1.1:
private SurfaceLayer surface;
private CanvasImage bgImage;
private CanvasImage circleImage;
#Override
public void init() {
// create a surface
int width = graphics().width(), height = graphics().height();
surface = graphics().createSurfaceLayer(width, height);
graphics().rootLayer().add(surface);
// create a solid background
bgImage = graphics().createImage(width, height);
Canvas canvas = bgImage.canvas();
canvas.setFillColor(0xff87ceeb);
canvas.fillRect(0, 0, width, height);
// create a circle
int circleRadius = 20;
int circleX = 0;
int circleY = 0;
circleImage = graphics().createImage(circleRadius, circleRadius);
canvas = circleImage.canvas();
canvas.setFillColor(0xff0000eb);
canvas.fillCircle(circleX, circleY, circleRadius);
}
#Override
public void paint(float alpha) {
Surface s = surface.surface();
s.clear();
s.drawImage(bgImage, 0, 0);
s.drawImage(circleImage, 100, 100);
}
If you really intend to make a game using this approach, you should use ImmediateLayer not SurfaceLayer. ImmediateLayer will issue your drawImage, etc. calls directly against the framebuffer. SurfaceLayer will make an off-screen framebuffer and render everything into that and then copy that off-screen framebuffer to the main framebuffer every frame (in addition to the double buffering naturally performed by OpenGL, etc.), resulting in a needless copy of your entire screen.

Your code looks fine to me...
set the canvasTransform,
canvas.setTransform(1, 0, 0, 1, 0, 0);
and please for testing purposes make the bg bigger
graphics().createImage(circleRadius, circleRadius); (400,400)

Related

Scaling start screen with fullscreen JavaFX

I'm stuck on full scaling for my JavaFX application. I'm in the process of making a full screen feature for the application and I'm running into issues on trying to get the aspect ratio and positioning right without manually editing the values.
With the way I've been trying, the values butcher the game's start screen making the positioning change making the designs of the game offset from the center of the application. I can understand the reasoning behind it with the way I set it up. My problem is wondering how to scale the start screen and keep it's original position without having to manually edit the values.
What I thought of was trying to input the value and having it scale according to that value then putting the result in the position of objects X and Y.
if (fullscreen) {
WIDTH = (Enter aspect ratio here) * 1.5;
HEIGHT = (Enter aspect ratio here) * 1.5;
} else {
WIDTH = 990;
HEIGHT = 525;
}
with Obvious flaws this butchers the start screen.
My solution was to make a double() that you just enter the value of the application WIDTH/HEIGHT then entering the amount you want to divide by (since I couldn't come up with exact cords, I grabbed the WIDTH and divided by specific value for it to align in the center) following with a boolean to state whether it's full screened or not. Though my only issue with this theory is that it'll only work with 1920x1080 monitors so I'd assume I would have to manually enter all types of aspect ratios to make it fit otherwise the start screen would be butchered.
I've seen a way of scaling here:
JavaFX fullscreen - resizing elements based upon screen size
Though I'm not sure how to correctly implement it.
public static boolean fullscreen = false;
public static double WIDTH = 990;
public static double HEIGHT = 525;
public static Pane pane = new Pane();
public static void StartScreen() {
pane.setPrefSize(WIDTH, (HEIGHT - 25)); // the 25 is for the text field/input.
pane.setStyle("-fx-background-color: transparent;");
Group sGroup = new Group();
Image i = new Image("file:start/So7AA.png");
ImageView outer = new ImageView(i);
// outer.setX(Ce.WIDTH/4.75); //4.75 // The functioning code for the snippit
// outer.setY(-10); //-10
outer.setX(Ce.WIDTH/position(3.60, fullscreen)); //4.75 // The non functioning code.
outer.setY(position(-1, Ce.fullscreen)); //-10
outer.setFitWidth(550);
outer.setFitHeight(550);
outer.setOpacity(.3);
GaussianBlur gBlur = new GaussianBlur();
gBlur.setRadius(50);
ImageView seal = new ImageView(i);
// seal.setX(Ce.WIDTH/3.83); //247.5 - 3.83
// seal.setY(39); //39
seal.setX(Ce.WIDTH/position(3.83, fullscreen)); //247.5 - 3.83
seal.setY(position(32, Ce.fullscreen)); //39
seal.setFitWidth(450);
seal.setFitHeight(450);
ImageView sealBlur = new ImageView(i);
// sealBlur.setX(Ce.WIDTH/3.83); //247.5 - 3.83
// sealBlur.setY(39); //39
sealBlur.setX(Ce.WIDTH/position(3.83, fullscreen)); //247.5 - 3.83
sealBlur.setY(position(32, Ce.fullscreen));
sealBlur.setFitWidth(450);
sealBlur.setFitHeight(450);
sealBlur.setEffect(gBlur);
}
For getting the values of the WIDTH and HEIGHT:
public static double getWidth(double W, boolean fs) {
if (fs) {
return WIDTH = Screen.getPrimary().getBounds().getMaxX();
} else {
return WIDTH = W;
}
}
public static double getHeight(double H, boolean fs) {
if (fs) {
return HEIGHT = Screen.getPrimary().getBounds().getMaxY();
} else {
return HEIGHT = H;
}
}
I know there's a way around this, I'm just not sure how to pull it off.
I'm not sure exactly what the requirements are here, but it looks like you have three images, which you want centered, and you want them all scaled by the same amount so that one of the images fills the available space in its container. (Then, you just need to make sure its container grows to fill all the space, and you can call stage.setFullScreen(true) or stage.setMaximized(true) as needed.)
You can do this with a pretty simple custom pane that manages the layout in the layoutChildren() method:
public class ImagePane extends Region {
private final Image image1;
private final ImageView imageView1;
private final Image image2;
private final ImageView imageView2;
private final Image image3;
private final ImageView imageView3;
public ImagePane(Image image1, Image image2, Image image3) {
this.image1 = image1;
this.image2 = image2;
this.image3 = image3;
imageView1 = new ImageView(image1);
imageView2 = new ImageView(image2);
imageView3 = new ImageView(image3);
getChildren().addAll(imageView1, imageView2, imageView3);
}
#Override
protected void layoutChildren() {
double xScale = getWidth() / image1.getWidth();
double yScale = getHeight() / image1.getHeight();
double scale = Math.min(xScale, yScale);
for (ImageView view : List.of(imageView1, imageView2, imageView3) {
scaleAndCenter(view, scale);
}
}
private void scaleAndCenter(ImageView view, scale) {
double w = scale * view.getImage().getWidth();
double h = scale * view.getImage().getHeight();
view.setFitWidth(w);
view.setFitHeight(h);
view.relocate((getWidth()-w) / 2, (getHeight()-h) / 2);
}
}
The rest of your layout looks something like:
Label label = new Label("Type in 'start'.\nType in 'options' for options.\n(Demo)");
TextField textField = new TextField();
ImagePane imagePane = new ImagePane(new Image(...), new Image(...), new Image(...));
AnchorPane anchor = new AnchorPane(imagePane, label);
AnchorPane.setTopAnchor(imagePane, 0.0);
AnchorPane.setRightAnchor(imagePane, 0.0);
AnchorPane.setBottomAnchor(imagePane, 0.0);
AnchorPane.setLeftAnchor(imagePane, 0.0);
AnchorPane.setTopAnchor(label, 5.0);
AnchorPane.setLeftAnchor(label, 5.0);
BorderPane root = new BorderPane();
root.setCenter(anchor);
root.setBottom(textField);
Now everything should just respond to whatever size is assigned to the root pane, so setting full screen mode should "just work".

Loading BMP into byte array using JavaFX

I am trying to create a simple Bitmap class using JavaFX which allows me to load, use, modify and save a bitmap to file (for a scientific simulator). I have problem loading the image into a byte array.
1- Am I correct that each pixel will need 3 bytes space in the buffer?
2- getPixels function receives the following exception:
java.lang.ClassCastException: class javafx.scene.image.PixelFormat$ByteRgb cannot be cast to class javafx.scene.image.WritablePixelFormat (javafx.scene.image.PixelFormat$ByteRgb and javafx.scene.image.WritablePixelFormat are in unnamed module of loader 'app')
The cast to (WritablePixelFormat) was suggested by Intellij. What am I doing wrong?
3- How can I use getPixels function to load the whole image into a 2D array of RGB integers? (to make working with pixels easier)
Thanks.
public class BMP
{
byte[] buffer;
int width;
int height;
public BMP()
{
}
public void load(String filename) throws FileNotFoundException
{
//Creating an image
Image image = new Image(new FileInputStream(filename));
this.width = (int)image.getWidth();
this.height = (int)image.getHeight();
this.buffer = new byte[width * height * 3];
//Reading color from the loaded image
PixelReader pixelReader = image.getPixelReader();
//Reading pixels of the image
/*
for(int y = 0; y < height; y++) {
for(int x = 0; x < width; x++) {
//Retrieving the color of the pixel of the loaded image
Color color = pixelReader.getColor(x, y);
System.out.println(color.toString());
}
}*/
pixelReader.getPixels(
0,
0,
width,
height,
(WritablePixelFormat<ByteBuffer>) PixelFormat.getByteRgbInstance(),
buffer,
0,
width * 3
);
}
public static void main(String[] args)
{
BMP bmp1 = new BMP();
try
{
bmp1.load("e:/1.bmp");
System.out.println("Width:"+ bmp1.width + " length:" + bmp1.height);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
}
}
With your loaded image and its PixelReader you can construct a WritableImage which will provide you with a PixelWriter. That should be sufficient to work with the image. I would not extract that into an array.

NullPointerException when trying to use ImageView

I grabbed the following code from another site - I can't find a problem with it but it keeps giving me a nullpointerexception. The issue I think is right in the first part before the setContentView but including the whole thing just in case. Thanks in advance for the help!
public class TileSet extends Activity {
Bitmap originalImage = BitmapFactory.decodeResource(getResources(),
R.drawable.announcementbc);
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
//Create an Image view and add our bitmap with reflection to it
imageView.setImageBitmap(getRefelection(originalImage));
//imageView.setImageBitmap(image);
//Add the image to a linear layout and display it
LinearLayout linLayout = new LinearLayout(this);
linLayout.addView(imageView, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// set LinearLayout as ContentView
setContentView(linLayout);
}
// public static Bitmap getRefelection(Bitmap image)
public static Bitmap getRefelection(Bitmap image)
{
//The gap we want between the reflection and the original image
final int reflectionGap = 4;
//Get you bit map from drawable folder
Bitmap originalImage = image;
int width = originalImage.getWidth();
int height = originalImage.getHeight();
//This will not scale but will flip on the Y axis
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
//Create a Bitmap with the flip matix applied to it.
//We only want the bottom half of the image
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height/2, width, height/2, matrix, false);
//Create a new bitmap with same width but taller to fit reflection
Bitmap bitmapWithReflection = Bitmap.createBitmap(width
, (height + height/2), Config.ARGB_8888);
//Create a new Canvas with the bitmap that's big enough for
//the image plus gap plus reflection
Canvas canvas = new Canvas(bitmapWithReflection);
//Draw in the original image
canvas.drawBitmap(originalImage, 0, 0, null);
//Draw in the gap
Paint deafaultPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap, deafaultPaint);
//Draw in the reflection
canvas.drawBitmap(reflectionImage,0, height + reflectionGap, null);
//Create a shader that is a linear gradient that covers the reflection
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff,
TileMode.CLAMP);
//Set the paint to use this shader (linear gradient)
paint.setShader(shader);
//Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
//Draw a rectangle using the paint with our linear gradient
canvas.drawRect(0, height, width,
bitmapWithReflection.getHeight() + reflectionGap, paint);
reflectionImage.recycle();
return bitmapWithReflection;
}
}
imageView.setImageBitmap(getRefelection(originalImage));
This is the line it is crashing on, and the reason is that imageView is null.
When you create a member variable such as the one you listed, you need to either create an object for it to point to, or set it to an already-created object.
In your case, I'm guessing you want to set it to one already created by your layout. Your layout XML should have some ImageView declared, but in order to get it in the Java code, you'll need to call findViewById()
If your XML is declared like this, with an id:
<ImageView
android:id="#+id/my_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Then you'll need to write the following before you can call setImageBitmap():
imageView = (ImageView) findViewById(R.id.my_image_view);
Additionally, you cannot make the findViewById call until after you make the call to setContentView(), because that's the call that actually creates your ImageView.
Although, looking at your code a bit further, you probably don't need to do the reflection bit, or even the setImageBitmap() call, because you can specify the drawable directly in the XML (unless you want to change it when the program is running):
<ImageView
android:id="#+id/my_image_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/announcementbc"
/>
By adding the source line, and pointing it to your drawable, the setContentView call will automatically set the correct Bitmap on the ImageView.
Here is the latest failed code as per the comment above
public class Tile2 extends Activity{
// Bitmap originalImage = BitmapFactory.decodeResource(getResources(), R.drawable.announcementbc);
ImageView imageView;
Bitmap originalImage;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView (R.layout.tile);
imageView = (ImageView) findViewById(R.id.ivTile);
originalImage = BitmapFactory.decodeResource(getResources(), R.drawable.announcementbc);
// ImageView imageView = new ImageView(this);
imageView.setImageBitmap(getReflection(originalImage));
//Add the image to a linear layout and display it
LinearLayout linLayout = new LinearLayout(this);
linLayout.addView(imageView, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
// set LinearLayout as ContentView
setContentView(linLayout);
}
public static Bitmap getReflection(Bitmap image)
{
//The gap we want between the reflection and the original image
final int reflectionGap = 4;
//Get you bit map from drawable folder
Bitmap originalImage = image;
int width = originalImage.getWidth();
int height = originalImage.getHeight();
//This will not scale but will flip on the Y axis
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
//Create a Bitmap with the flip matix applied to it.
//We only want the bottom half of the image
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height/2, width, height/2, matrix, false);
//Create a new bitmap with same width but taller to fit reflection
Bitmap bitmapWithReflection = Bitmap.createBitmap(width
, (height + height/2), Config.ARGB_8888);
//Create a new Canvas with the bitmap that's big enough for
//the image plus gap plus reflection
Canvas canvas = new Canvas(bitmapWithReflection);
//Draw in the original image
canvas.drawBitmap(originalImage, 0, 0, null);
//Draw in the gap
Paint deafaultPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap, deafaultPaint);
//Draw in the reflection
canvas.drawBitmap(reflectionImage,0, height + reflectionGap, null);
//Create a shader that is a linear gradient that covers the reflection
Paint paint = new Paint();
LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff,
TileMode.CLAMP);
//Set the paint to use this shader (linear gradient)
paint.setShader(shader);
//Set the Transfer mode to be porter duff and destination in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
//Draw a rectangle using the paint with our linear gradient
canvas.drawRect(0, height, width,
bitmapWithReflection.getHeight() + reflectionGap, paint);
reflectionImage.recycle();
return bitmapWithReflection;
}
}

Custom button field blackberry: onfocus, onUnfocus

I want to change the colors of background button and the color of text onfocus
How can I make it?
class RoundedRectField extends Field {
// Layout values
private static final int CURVE_X = 12; // X-axis inset of curve
private static final int CURVE_Y = 12; // Y-axis inset of curve
private static final int MARGIN = 2; // Space within component boundary
// Static colors
private static final int TEXT_COLOR = 0xFFFFFF; // White
private static final int BORDER_COLOR = 0xFF8000; // dark gray
private static final int BACKGROUND_COLOR = 0xFFFFFF; // White
private static final int TEXT_COLOR_selected = 0xFF6DB6;
private static final int BORDER_COLOR_selected = 0xFF8000;
private static final int BACKGROUND_COLOR_selected = 0xCCCCCC;
boolean _focus = false;
private static String text_button;
// Point types array for rounded rectangle. Each point type
// corresponds to one of the colors in the colors array. The
// space marks the division between points on the top half of
// the rectangle and those on the bottom.
private static final byte[] PATH_POINT_TYPES = {
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, Graphics.CURVEDPATH_END_POINT,
Graphics.CURVEDPATH_QUADRATIC_BEZIER_CONTROL_POINT,
Graphics.CURVEDPATH_END_POINT, };
// Colors array for rounded rectangle gradient. Each color corresponds
// to one of the points in the point types array. Top light, bottom black.
private static final int[] PATH_GRADIENT = { 0xFF8000, 0xFF8000, 0xFF8000,
0xFF8000, 0xFF8000, 0xFF8000,
0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500, 0xFC0500 };
// Center our readonly field in the space we're given.
public RoundedRectField(String text_button) {
super(FIELD_HCENTER | FIELD_VCENTER | READONLY);
this.text_button = text_button;
}
// This field in this demo has a fixed height.
public int getPreferredHeight() {
return 70;
}
// This field in this demo has a fixed width.
public int getPreferredWidth() {
return 240;
}
// When layout is requested, return our height and width.
protected void layout(int width, int height) {
setExtent(getPreferredWidth(), getPreferredHeight());
}
// When painting is requested, do it ourselves.
protected void paint(Graphics g) {
// Clear this area to white background, fully opaque.
g.clear();
g.setGlobalAlpha(255);
g.setBackgroundColor(BACKGROUND_COLOR);
// Drawing within our margin.
int width = getPreferredWidth() - (MARGIN * 2);
int height = getPreferredHeight() - (MARGIN * 2);
// Compute paths for the rounded rectangle. The 1st point (0) is on
// the left
// side, right where the curve in the top left corner starts. So the
// top left
// corner is point 1. These points correspond to our static arrays.
int[] xPts = { 0, 0, CURVE_X, width - CURVE_X, width, width, width,
width, width - CURVE_X, CURVE_X, 0, 0 };
int[] yPts = { CURVE_Y, 0, 0, 0, 0, CURVE_Y, height - CURVE_Y,
height, height, height, height, height - CURVE_Y };
// Draw the gradient fill.
g.drawShadedFilledPath(xPts, yPts, PATH_POINT_TYPES, PATH_GRADIENT,
null);
// Draw a rounded rectangle for the outline.
// I think that drawRoundRect looks better than drawPathOutline.
g.setColor(BORDER_COLOR);
g.drawRoundRect(0, 0, width, height, CURVE_X * 2, CURVE_Y * 2);
// Place some text in the center.
Font font = Font.getDefault().derive(Font.PLAIN, 9, Ui.UNITS_pt);
int textWidth = font.getAdvance(text_button);
int textHeight = font.getHeight();
g.setColor(TEXT_COLOR);
g.setFont(font);
g.drawText(text_button, (width / 2) - (textWidth / 2) - MARGIN,
(height / 2) - (textHeight / 2) - MARGIN);
}
protected void onFocus(int direction) {
_focus = true;
Dialog.alert("dcd");
invalidate();
super.onFocus(direction);
}
protected void onUnfocus() {
_focus = false;
invalidate();
super.onUnfocus();
}
}
You can do it several ways. One popular way is to provide custom focus drawing in the paint() method, which you already override.
You should be able to do this (I'm assuming you declared the _selected colors for the focused state):
if (isFocus()) {
g.setBackgroundColor(BACKGROUND_COLOR_selected);
else {
g.setBackgroundColor(BACKGROUND_COLOR);
}
...
if (isFocus()) {
g.setColor(TEXT_COLOR_selected);
} else {
g.setColor(TEXT_COLOR);
}
Those lines go in paint(), right where you are currently calling g.setBackgroundColor and g.setColor(TEXT_COLOR).
Then, you would override drawFocus() and do nothing, since your focus drawing is handled in paint():
protected void drawFocus(Graphics graphics, boolean on) {
// override superclass implementation and do nothing
}
Finally, you need to make your Field focusable, in order to ever receive focus. You can do so like this:
public RoundedRectField(String text_button) {
super(FIELD_HCENTER | FIELD_VCENTER | FOCUSABLE);
this.text_button = text_button;
}
If you need the field to be dynamically focusable (sometimes focusable, or sometimes not focusable), then you could implement this method:
public boolean isFocusable() {
But, if the field is always focusable, then using the FOCUSABLE flag in your constructor will work. I tested this out, and I saw the text color change with focus (on a OS 5.0 9550).

Qt painted content goes lost

I am writing an info-screen program. I created a full-screen widget and draw contents onto it.
In order to extend the life cycle of the TFT-display device, I want to implement a pixel-shifting feature. With other words, in every X minutes, I shift the screen to left/right/top/down for Y pixels.
My approach is as follows:
I use two layers (two QWidget).
I paint contents on the top layer.
When a pixel-shifting is performed, I just move the top layer for specified offset.
And then fill a background color to the bottom layer.
However, I found a problem:
If I move up the top layer for 10 pixels, the 10-pixel-content goes out of the screen. But when I move this layer down for 10 pixels. The 10-pixel-content will not be updated, it is gone.
How can I keep these 10-pixel-content? Is there any magic widget flag to solve this problem?
UPDATE 1:
The code is written in language D, but it is easy to understand:
class Canvas: QWidget
{
private QPixmap content;
this(QWidget parent)
{
super(parent);
setAttribute(Qt.WA_OpaquePaintEvent, true);
}
public void requestForPaint(QPixmap content, QRegion region)
{
this.content = content;
update(region);
}
protected override void paintEvent(QPaintEvent event)
{
if (this.content !is null)
{
QPainter painter = new QPainter(this);
painter.setClipping(event.region);
painter.fillRect(event.region.boundingRect, new QColor(0, 0, 0));
painter.drawPixmap(event.region.rect, this.content);
this.content = null;
painter.setClipping(false);
}
}
}
class Screen: QWidget
{
private Canvas canvas;
this()
{
super(); // Top-Level widget
setAutoFillBackground(True);
this.canvas = new Canvas(this);
showFullScreen();
}
public void requestForPaint(QPixmap content, QRegion region)
{
this.canvas.requestForPaint(content, region);
}
private updateBackgroundColor(QColor backgroundColor)
{
QPalette newPalette = palette();
newPalette.setColor(backgroundRole(), backgroundColor);
setPalette(newPalette);
}
public shiftPixels(int dx, int dy)
{
this.canvas.move(dx, dy);
updateBackgroundColor(new QColor(0, 0, 0)); // Just a demo background color
}
}
Screen screen = new Screen;
screen.requestForPaint(some_content, some_region);
screen.shiftPixels(0, -10);
screen.shiftPixels(0, 10);
Looking at the code, my first guess is that your region might be wrong. Try repainting the whole widget each time, and see if that solves the missing 10 pixel problem. If it does, then try working out why your region isn't covering the newly exposed portion.
One possibility along those lines: I notice in your Screen::requestForPaint method that you directly call the Canvas::requestForPaint without doing anything with the region. In Qt, the coordinates for anything like that are often assumed to be local, so if you don't account for the current position of the canvas widget, you might get an incorrect region.
Why not setting the position of the widget directly...? Another options might be using QPainter::translate(-1,-1) or something similar.

Resources