Well, lets say I have the Shape of an O and I want to render it.
Now my current rendering code is this:
public void fill(Shape shape, float xOffset, float yOffset) {
AffineTransform transform = new AffineTransform();
transform.translate(xOffset, yOffset);
PathIterator pi = shape.getPathIterator(transform, 1);
int winding = pi.getWindingRule();
ShapeRenderer r = getInstance();
float[] coords = new float[6];
boolean drawing = false;
while (!pi.isDone()) {
int segment = pi.currentSegment(coords);
switch (segment) {
case PathIterator.SEG_CLOSE:
if (drawing) {
r.endPoly();
}
break;
case PathIterator.SEG_LINETO:
r.lineTo(coords);
break;
case PathIterator.SEG_MOVETO:
if (drawing) {
r.endPoly();
}
drawing = true;
r.beginPoly(winding);
r.moveTo(coords);
break;
default:
throw new IllegalArgumentException("Unexpected value: " + segment);
}
pi.next();
}
}
ShapeRenderer:
private final Deque<Queue<Float>> vertices = new ConcurrentLinkedDeque<>();
#Override
public void beginPoly(int windingRule) {
}
#Override
public void endPoly() {
Queue<Float> q;
GL11.glBegin(GL11.GL_LINE_LOOP);
while ((q = vertices.poll()) != null) {
Float x, y;
while ((x = q.poll()) != null && (y = q.poll()) != null) {
GL11.glVertex2f(x, y);
}
}
GL11.glEnd();
}
#Override
public void moveTo(float[] vertex) {
Queue<Float> q = new ConcurrentLinkedQueue<>();
vertices.offer(q);
lineTo(vertex);
}
#Override
public void lineTo(float[] vertex) {
Queue<Float> q = vertices.peekLast();
q.offer(vertex[0]);
q.offer(vertex[1]);
}
So lets say I want to fill that Shape, just like java awt does, successfully... How would I do that?
(Already tried using GL_POLYGON, but it just fills the entire O and I have a filled circle, not an O. Also tried using parts of jogamp glu but it just rendered nothing, no clue why)
Already tried using GL_POLYGON, but it just fills the entire O and I have a filled circle, "
Yes of course. GL_POLYGON is for Convex shapes and fills the entire area enclosed by the polygon.
You have 2 options:
Use GL_POLYGON twice to draw a white shape and then a red shape inside the white shape.
Use GL_TRIANGLE_STRIP and form a shape that just draws the outline. The primitive can be formed by alternately specifying a point on the outer outline and on the inner outline.
I have a pdf file where-in I am adding a stamp to all it's pages.
But, the problem is, the stamp is added to the upper-left corner of each page. If, the page has text in that part, the stamp appears on the text.
My question is, is there any method by which I can read each page and if there is no text in that part add the stamp else search for nearest available free space, just like what a density scanner does?
I am using IText and Java 1.7.
The free space fider class and the distance calculation function are the same that is there in the accepted answer.
Following is the edited code I am using:
// The resulting PDF file
String RESULT = "K:\\DCIN_TER\\DCIN_EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\" + jtfSONo.getText().trim() + "\\PADR Release\\Final PADR Release 1.pdf";
// Create a reader
PdfReader reader = new PdfReader("K:\\DCIN_TER\\DCIN_EPU2\\CIRCUIT FROM BRANCH\\RAINBOW ORDERS\\" + jtfSONo.getText().trim() + "\\PADR Release\\Final PADR Release.pdf");
// Create a stamper
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(RESULT));
// Loop over the pages and add a footer to each page
int n = reader.getNumberOfPages();
for(int i = 1; i <= n; i++)
{
Collection<Rectangle2D> rectangles = find(reader, 300, 100, n, stamper); // minimum width & height of a rectangle
Iterator itr = rectangles.iterator();
while(itr.hasNext())
{
System.out.println(itr.next());
}
if(!(rectangles.isEmpty()) && (rectangles.size() != 0))
{
Rectangle2D best = null;
double bestDist = Double.MAX_VALUE;
Point2D.Double point = new Point2D.Double(200, 400);
float x = 0, y = 0;
for(Rectangle2D rectangle: rectangles)
{
double distance = distance(rectangle, point);
if(distance < bestDist)
{
best = rectangle;
bestDist = distance;
x = (float) best.getX();
y = (float) best.getY();
int left = (int) best.getMinX();
int right = (int) best.getMaxX();
int top = (int) best.getMaxY();
int bottom = (int) best.getMinY();
System.out.println("x : " + x);
System.out.println("y : " + y);
System.out.println("left : " + left);
System.out.println("right : " + right);
System.out.println("top : " + top);
System.out.println("bottom : " + bottom);
}
}
getFooterTable(i, n).writeSelectedRows(0, -1, x, y, stamper.getOverContent(i)); // 0, -1 indicates 1st row, 1st column upto last row and last column
}
else
getFooterTable(i, n).writeSelectedRows(0, -1, 94, 140, stamper.getOverContent(i)); // bottom left corner
}
// Close the stamper
stamper.close();
// Close the reader
reader.close();
public Collection<Rectangle2D> find(PdfReader reader, float minWidth, float minHeight, int page, PdfStamper stamper) throws IOException
{
Rectangle cropBox = reader.getCropBox(page);
Rectangle2D crop = new Rectangle2D.Float(cropBox.getLeft(), cropBox.getBottom(), cropBox.getWidth(), cropBox.getHeight());
FreeSpaceFinder finder = new FreeSpaceFinder(crop, minWidth, minHeight);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(page, finder);
System.out.println("finder.freeSpaces : " + finder.freeSpaces);
return finder.freeSpaces;
}
// Create a table with page X of Y, #param x the page number, #param y the total number of pages, #return a table that can be used as footer
public static PdfPTable getFooterTable(int x, int y)
{
java.util.Date date = new java.util.Date();
SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy");
String month = sdf.format(date);
System.out.println("Month : " + month);
PdfPTable table = new PdfPTable(1);
table.setTotalWidth(120);
table.setLockedWidth(true);
table.getDefaultCell().setFixedHeight(20);
table.getDefaultCell().setBorder(Rectangle.TOP);
table.getDefaultCell().setBorder(Rectangle.LEFT);
table.getDefaultCell().setBorder(Rectangle.RIGHT);
table.getDefaultCell().setBorderColorTop(BaseColor.BLUE);
table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
table.getDefaultCell().setBorderWidthTop(1f);
table.getDefaultCell().setBorderWidthLeft(1f);
table.getDefaultCell().setBorderWidthRight(1f);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
Font font1 = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.BLUE);
table.addCell(new Phrase("CONTROLLED COPY", font1));
table.getDefaultCell().setFixedHeight(20);
table.getDefaultCell().setBorder(Rectangle.LEFT);
table.getDefaultCell().setBorder(Rectangle.RIGHT);
table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
table.getDefaultCell().setBorderWidthLeft(1f);
table.getDefaultCell().setBorderWidthRight(1f);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
Font font = new Font(FontFamily.HELVETICA, 10, Font.BOLD, BaseColor.RED);
table.addCell(new Phrase(month, font));
table.getDefaultCell().setFixedHeight(20);
table.getDefaultCell().setBorder(Rectangle.LEFT);
table.getDefaultCell().setBorder(Rectangle.RIGHT);
table.getDefaultCell().setBorder(Rectangle.BOTTOM);
table.getDefaultCell().setBorderColorLeft(BaseColor.BLUE);
table.getDefaultCell().setBorderColorRight(BaseColor.BLUE);
table.getDefaultCell().setBorderColorBottom(BaseColor.BLUE);
table.getDefaultCell().setBorderWidthLeft(1f);
table.getDefaultCell().setBorderWidthRight(1f);
table.getDefaultCell().setBorderWidthBottom(1f);
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);
table.addCell(new Phrase("BLR DESIGN DEPT.", font1));
return table;
}
is there any method by which I can read each page and if there is no text in that part add the stamp else search for nearest available free space, just like what a density scanner does?
iText does not offer that functionality out of the box. Depending of what kind of content you want to evade, though, you might consider either rendering the page to an image and looking for white spots in the image or doing text extraction with a strategy that tries to find locations without text.
The first alternative, analyzing a rendered version of the page, would be the focus of a separate question as an image processing library would have to be chosen first.
There are a number of situations, though, in which that first alternative is not the best way to go. E.g. if you only want to evade text but not necessarily graphics (like watermarks), or if you also want to evade invisible text (which usually can be marked in a PDF viewer and, therefore, interfere with your addition).
The second alternative (using text and image extraction abilities of iText) can be the more appropriate approach in such situations.
Here a sample RenderListener for such a task:
public class FreeSpaceFinder implements RenderListener
{
//
// constructors
//
public FreeSpaceFinder(Rectangle2D initialBox, float minWidth, float minHeight)
{
this(Collections.singleton(initialBox), minWidth, minHeight);
}
public FreeSpaceFinder(Collection<Rectangle2D> initialBoxes, float minWidth, float minHeight)
{
this.minWidth = minWidth;
this.minHeight = minHeight;
freeSpaces = initialBoxes;
}
//
// RenderListener implementation
//
#Override
public void renderText(TextRenderInfo renderInfo)
{
Rectangle2D usedSpace = renderInfo.getAscentLine().getBoundingRectange();
usedSpace.add(renderInfo.getDescentLine().getBoundingRectange());
remove(usedSpace);
}
#Override
public void renderImage(ImageRenderInfo renderInfo)
{
Matrix imageMatrix = renderInfo.getImageCTM();
Vector image00 = rect00.cross(imageMatrix);
Vector image01 = rect01.cross(imageMatrix);
Vector image10 = rect10.cross(imageMatrix);
Vector image11 = rect11.cross(imageMatrix);
Rectangle2D usedSpace = new Rectangle2D.Float(image00.get(Vector.I1), image00.get(Vector.I2), 0, 0);
usedSpace.add(image01.get(Vector.I1), image01.get(Vector.I2));
usedSpace.add(image10.get(Vector.I1), image10.get(Vector.I2));
usedSpace.add(image11.get(Vector.I1), image11.get(Vector.I2));
remove(usedSpace);
}
#Override
public void beginTextBlock() { }
#Override
public void endTextBlock() { }
//
// helpers
//
void remove(Rectangle2D usedSpace)
{
final double minX = usedSpace.getMinX();
final double maxX = usedSpace.getMaxX();
final double minY = usedSpace.getMinY();
final double maxY = usedSpace.getMaxY();
final Collection<Rectangle2D> newFreeSpaces = new ArrayList<Rectangle2D>();
for (Rectangle2D freeSpace: freeSpaces)
{
final Collection<Rectangle2D> newFragments = new ArrayList<Rectangle2D>();
if (freeSpace.intersectsLine(minX, minY, maxX, minY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), freeSpace.getWidth(), minY-freeSpace.getMinY()));
if (freeSpace.intersectsLine(minX, maxY, maxX, maxY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), maxY, freeSpace.getWidth(), freeSpace.getMaxY() - maxY));
if (freeSpace.intersectsLine(minX, minY, minX, maxY))
newFragments.add(new Rectangle2D.Double(freeSpace.getMinX(), freeSpace.getMinY(), minX - freeSpace.getMinX(), freeSpace.getHeight()));
if (freeSpace.intersectsLine(maxX, minY, maxX, maxY))
newFragments.add(new Rectangle2D.Double(maxX, freeSpace.getMinY(), freeSpace.getMaxX() - maxX, freeSpace.getHeight()));
if (newFragments.isEmpty())
{
add(newFreeSpaces, freeSpace);
}
else
{
for (Rectangle2D fragment: newFragments)
{
if (fragment.getHeight() >= minHeight && fragment.getWidth() >= minWidth)
{
add(newFreeSpaces, fragment);
}
}
}
}
freeSpaces = newFreeSpaces;
}
void add(Collection<Rectangle2D> rectangles, Rectangle2D addition)
{
final Collection<Rectangle2D> toRemove = new ArrayList<Rectangle2D>();
boolean isContained = false;
for (Rectangle2D rectangle: rectangles)
{
if (rectangle.contains(addition))
{
isContained = true;
break;
}
if (addition.contains(rectangle))
toRemove.add(rectangle);
}
rectangles.removeAll(toRemove);
if (!isContained)
rectangles.add(addition);
}
//
// members
//
public Collection<Rectangle2D> freeSpaces = null;
final float minWidth;
final float minHeight;
final static Vector rect00 = new Vector(0, 0, 1);
final static Vector rect01 = new Vector(0, 1, 1);
final static Vector rect10 = new Vector(1, 0, 1);
final static Vector rect11 = new Vector(1, 1, 1);
}
Using this FreeSpaceFinder you can find empty areas with given minimum dimensions in a method like this:
public Collection<Rectangle2D> find(PdfReader reader, float minWidth, float minHeight, int page) throws IOException
{
Rectangle cropBox = reader.getCropBox(page);
Rectangle2D crop = new Rectangle2D.Float(cropBox.getLeft(), cropBox.getBottom(), cropBox.getWidth(), cropBox.getHeight());
FreeSpaceFinder finder = new FreeSpaceFinder(crop, minWidth, minHeight);
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(page, finder);
return finder.freeSpaces;
}
For your task you now have to choose from the returned rectangles the one which suits you best.
Beware, this code still may have to be tuned to your requirements:
It ignores clip paths, rendering modes, colors, and covering objects. Thus, it considers all text and all bitmap images, whether they are actually visible or not.
It does not consider vector graphics (because the iText parser package does not consider them).
It is not very optimized.
Applied to this PDF page:
with minimum width 200 and height 50, you get these rectangles:
x y w h
000,000 000,000 595,000 056,423
000,000 074,423 595,000 168,681
000,000 267,304 314,508 088,751
000,000 503,933 351,932 068,665
164,296 583,598 430,704 082,800
220,803 583,598 374,197 096,474
220,803 583,598 234,197 107,825
000,000 700,423 455,000 102,396
000,000 700,423 267,632 141,577
361,348 782,372 233,652 059,628
or, more visually, here as rectangles on the page:
The paper plane is a vector graphic and, therefore, ignored.
Of course you could also change the PDF rendering code to not draw stuff you want to ignore and to visibly draw originally invisible stuff which you want to ignore, and then use bitmap image analysis nonetheless...
EDIT
In his comments the OP asked how to find the rectangle in the rectangle collection returned by find which is nearest to a given point.
First of all there not necessarily is the nearest rectangle, there may be multiple.
That been said, one can choose a nearest rectangle as follows:
First one needs to calculate a distance between point and rectangle, e.g.:
double distance(Rectangle2D rectangle, Point2D point)
{
double x = point.getX();
double y = point.getY();
double left = rectangle.getMinX();
double right = rectangle.getMaxX();
double top = rectangle.getMaxY();
double bottom = rectangle.getMinY();
if (x < left) // point left of rect
{
if (y < bottom) // and below
return Point2D.distance(x, y, left, bottom);
if (y > top) // and top
return Point2D.distance(x, y, left, top);
return left - x;
}
if (x > right) // point right of rect
{
if (y < bottom) // and below
return Point2D.distance(x, y, right, bottom);
if (y > top) // and top
return Point2D.distance(x, y, right, top);
return x - right;
}
if (y < bottom) // and below
return bottom - y;
if (y > top) // and top
return y - top;
return 0;
}
Using this distance measurement one can select a nearest rectangle using code like this for a Collection<Rectangle2D> rectangles and a Point2D point:
Rectangle2D best = null;
double bestDist = Double.MAX_VALUE;
for (Rectangle2D rectangle: rectangles)
{
double distance = distance(rectangle, point);
if (distance < bestDist)
{
best = rectangle;
bestDist = distance;
}
}
After this best contains a best rectangle.
For the sample document used above, this method returns the colored rectangles for the page corners and left and right centers:
EDIT TWO
Since iText 5.5.6, the RenderListener interface has been extended as ExtRenderListener to also be signaled about Path construction and path drawing operations. Thus, the FreeSpaceFinder above could also be extended to handle paths:
//
// Additional ExtRenderListener methods
//
#Override
public void modifyPath(PathConstructionRenderInfo renderInfo)
{
List<Vector> points = new ArrayList<Vector>();
if (renderInfo.getOperation() == PathConstructionRenderInfo.RECT)
{
float x = renderInfo.getSegmentData().get(0);
float y = renderInfo.getSegmentData().get(1);
float w = renderInfo.getSegmentData().get(2);
float h = renderInfo.getSegmentData().get(3);
points.add(new Vector(x, y, 1));
points.add(new Vector(x+w, y, 1));
points.add(new Vector(x, y+h, 1));
points.add(new Vector(x+w, y+h, 1));
}
else if (renderInfo.getSegmentData() != null)
{
for (int i = 0; i < renderInfo.getSegmentData().size()-1; i+=2)
{
points.add(new Vector(renderInfo.getSegmentData().get(i), renderInfo.getSegmentData().get(i+1), 1));
}
}
for (Vector point: points)
{
point = point.cross(renderInfo.getCtm());
Rectangle2D.Float pointRectangle = new Rectangle2D.Float(point.get(Vector.I1), point.get(Vector.I2), 0, 0);
if (currentPathRectangle == null)
currentPathRectangle = pointRectangle;
else
currentPathRectangle.add(pointRectangle);
}
}
#Override
public Path renderPath(PathPaintingRenderInfo renderInfo)
{
if (renderInfo.getOperation() != PathPaintingRenderInfo.NO_OP)
remove(currentPathRectangle);
currentPathRectangle = null;
return null;
}
#Override
public void clipPath(int rule)
{
// TODO Auto-generated method stub
}
Rectangle2D.Float currentPathRectangle = null;
(FreeSpaceFinderExt.java)
Using this class the result above is improved to
As you see the paper plane and the table background colorations now also are taken into account.
My other answer focuses on the original question, i.e. how to find free space with given minimum dimensions on a page.
Since that answer had been written, the OP provided code trying to make use of that original answer.
This answer deals with that code.
The code has a number of shortcoming.
The choice of free space on a page depends on the number of pages in the document.
The reason for this is to be found at the start of the loop over the pages:
for(int i = 1; i <= n; i++)
{
Collection<Rectangle2D> rectangles = find(reader, 300, 100, n, stamper);
...
The OP surely meant i, not n there. The code as is always looks for free space on the last document page.
The rectangles are lower than they should be.
The reason for this is to be found in the retrieval and use of the rectangle coordinates:
x = (float) best.getX();
y = (float) best.getY();
...
getFooterTable(i, n).writeSelectedRows(0, -1, x, y, stamper.getOverContent(i));
The Rectangle2D methods getX and getY return the coordinates of the lower left rectangle corner; the PdfPTable methods writeSelectedRows, on the other hand, require the upper left rectangle corner. Thus, getMaxY should be used instead of getY.
This is my second post on Mandelbrot fractal conversion from Java to C#.
As per my assignment, I need to Draw a mandelbrot fractal on a form, and once it is drawn, allow the user to Zoom in using the mouse, while also drawing a rectangle from the initial click point to the point where the click is released. This is the part of code which i believe is responsible for the rectangle.
private static void Swap<T>(ref T t1, ref T t2)
{
T temp = t1;
t1 = t2;
t2 = t1;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
Graphics g1 = e.Graphics;
g1.DrawImage(bitmap, 0, 0, x1, y1);
if (action)
{
//g.setColor(Color.White);
if (xe < xs)
{
Swap(ref xs, ref xe);
}
if (ye < ys)
{
Swap(ref ys, ref ye);
}
g1.DrawRectangle(Pens.White, xs, ys, (xe - xs), (ye - ys));
//g1.Dispose();
}
}
//load method here
private void Form1_Load(object sender, EventArgs e)
//while loading
{
init();
start();
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (action)
{
xe = e.X;
ye = e.Y;
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
action = true;
// e.consume();
if (action)
{
xs = xe = e.X;
ys = ye = e.Y;
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
using (Graphics g = this.CreateGraphics())
{
Pen pen = new Pen(Color.White);
g.DrawRectangle(pen, xs, ys, Math.Abs(xs - xe), Math.Abs(ys - ye));
}
int z, w;
if (xs > xe)
{
z = xs;
xs = xe;
xe = z;
}
if (ys > ye)
{
z = ys;
ys = ye;
ye = z;
}
w = (xe - xs);
z = (ye - ys);
if ((w < 2) && (z < 2)) initvalues();
else
{
if (((float)w > (float)z * xy)) ye = (int)((float)ys + (float)w / xy);
else xe = (int)((float)xs + (float)z * xy);
xende = xstart + xzoom * (double)xe;
yende = ystart + yzoom * (double)ye;
xstart += xzoom * (double)xs;
ystart += yzoom * (double)ys;
}
xzoom = (xende - xstart) / (double)x1;
yzoom = (yende - ystart) / (double)y1;
mandelbrot();
this.Invalidate();
}
What the code does is, draw a rectangle AFTER the dragging is done, and then zoom in with the drawn rectangle still being displayed. What I needed is the rectangle to draw as the mouse is being dragged.
I referred to this question, and solution mentioned there did not help.
Java to C# conversion. How do i draw a rectangle on my bitmap?
Any help would be appreciated.
Drawing the Rectangle
First of all, it appears that the Graphics.DrawRectangle method is unable to draw a rectangle with negative widths or heights. You will therefore have to write a method that will take two points and produce a rectangle meeting the requirements (positive width and height).
private Rectangle CreateRectangle(Point pt1, Point pt2)
{
// we use this method to create the rectangle with positive width and height
int x1 = Math.Min(pt1.X, pt2.X);
int y1 = Math.Min(pt1.Y, pt2.Y);
return new Rectangle(x1, y1, Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
}
Second, in your event handler for the MouseDown event, record the position at which the mouse was held down.
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
this.startPoint = e.Location;// record the start position
}
Next, modify your mouse move method to update the variable that holds current location of the mouse. Additionally, make it invalidate the form so that the image is redrawn (along with the rectangle).
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// record the current position as the end point if the left button is down
this.endPoint = e.Location;
// force a redraw
this.Invalidate();
}
}
In the form's Paint event handler, make your code call the CreateRectangle method with the start and end points of the rectangle in order to draw the rectangle on the form.
private void Form1_Paint(object sender, PaintEventArgs e)
{
// draw the cached Mandelbrot image
e.Graphics.DrawImage(mandelbrotCache, new Point(0, 0));
// draw the current rectangle
e.Graphics.DrawRectangle(rectPen, CreateRectangle(startPoint, endPoint));
}
Finally, in order to remove the rectangle when the mouse button is no longer pressed, set startPoint and endPoint to a value that gets drawn outside the image. This should be done in the MouseUp event handler.
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// setting the point to -1,-1 makes them get drawn off the screen
startPoint = new Point(-1, -1);
endPoint = new Point(-1, -1);
// force an update so that the rectangle disappears
this.Invalidate();
}
}
Addressing the Flickering Issue
In order to stop the form from flickering while you're drawing to it, you will need to enable double buffering on the form. This is done by setting the DoubleBuffered property of the form to true. You can do this anywhere, but I prefer to do it right after the form is created, as below:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// this reduces the flickering
this.DoubleBuffered = true;
}
}
Complete Code:
Here is the complete code for all the steps I detailed above. You can plug in your methods in order to have a working solution.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Point startPoint;
private Point endPoint;
private Image mandelbrotCache;
private Pen rectPen;
public Form1()
{
InitializeComponent();
// this reduces the flickering
this.DoubleBuffered = true;
// initialize a dummy image. Cache a copy of your Mandelbrot fractal here
mandelbrotCache = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
using (var g = Graphics.FromImage(mandelbrotCache))
{
var imgRect = new Rectangle(0, 0,
mandelbrotCache.Width,
mandelbrotCache.Height);
g.FillRectangle(new HatchBrush(HatchStyle.Cross, Color.DarkBlue,
Color.LightBlue), imgRect);
}
// this is the pen to draw the rectangle with
rectPen = new Pen(Color.Red, 3);
}
private Rectangle CreateRectangle(Point pt1, Point pt2)
{
// we use this method to create a rectangle with positive width and height
int x1 = Math.Min(pt1.X, pt2.X);
int y1 = Math.Min(pt1.Y, pt2.Y);
return new Rectangle(x1, y1, Math.Abs(pt1.X - pt2.X), Math.Abs(pt1.Y - pt2.Y));
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
this.startPoint = e.Location;// record the start position
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// record the current position as the end point if the left button is down
this.endPoint = e.Location;
// force a redraw
this.Invalidate();
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
// setting the point to -1,-1 makes them get drawn off the screen
startPoint = new Point(-1, -1);
endPoint = new Point(-1, -1);
// force an update so that the rectangle disappears
this.Invalidate();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// draw the cached Mandelbrot image
e.Graphics.DrawImage(mandelbrotCache, new Point(0, 0));
// draw the current rectangle
e.Graphics.DrawRectangle(rectPen, CreateRectangle(startPoint, endPoint));
}
}
}
Here is a screenshot of a rectangle being drawn.
Note: The left mouse button is still held down. The rectangle disappears immediately the button is released.
I am using Processing in Java to perpetually draw a line graph. This requires a clearing rectangle to draw over drawn lines to make room for the new part of the graph. Everything works fine, but when I call a method, the clearing stops working as it did before. Basically the clearing works by drawing a rectangle in front of where the line is currently at
Below are the two main methods involved. The drawGraph function works fine until I call the redrawGraph which redraws the graph based on the zoom. I think the center variable is the cause of the problem but I cannot figure out why.
public void drawGraph()
{
checkZoom();
int currentValue = seismograph.getCurrentValue();
int lastValue = seismograph.getLastValue();
step = step + zoom;
if(step>offset){
if(restartDraw == true)
{
drawOnGraphics(step-zoom, lastY2, step, currentValue);
image(graphGraphics, 0, 0);
restartDraw = false;
}else{
drawOnGraphics(step-zoom, lastValue, step, currentValue);
image(graphGraphics, 0, 0);
}
} // draw graph (connect last to current point // increase step - x axis
float xClear = step+10; // being clearing area in front of current graph
if (xClear>width - 231) {
xClear = offset - 10; // adjust for far side of the screen
}
graphGraphics.beginDraw();
if (step>graphSizeX+offset) { // draw two clearing rectangles when graph isn't split
// left = bg.get(0, 0, Math.round(step-graphSizeX), height - 200); // capture clearing rectangle from the left side of the background image
// graphGraphics.image(left, 0, 0); // print left clearing rectangle
// if (step+10<width) {
// right = bg.get(Math.round(step+10), 0, width, height - 200); // capture clearing rectangle from the right side of the background image
// // print right clearing rectangle
// }
} else { // draw one clearing rectangle when graph is split
center = bg.get(Math.round(xClear), lastY2, offset, height - 200); // capture clearing rectangle from the center of the background image
graphGraphics.image(center, xClear - 5, 0);// print center clearing rectangle
}
if (step > graphSizeX+offset) { // reset set when graph reaches the end
step = 0+offset;
}
graphGraphics.endDraw();
image(graphGraphics, 0 , 0);
System.out.println("step: " + step + " zoom: " + zoom + " currentValue: "+ currentValue + " lastValue: " + lastValue);
}
private void redrawGraph() //resizes graph when zooming
{
checkZoom();
Object[] data = seismograph.theData.toArray();
clearGraph();
step = offset;
int y2, y1 = 0;
int zoomSize = (int)((width - offset) / zoom);
int tempCount = 0;
graphGraphics.beginDraw();
graphGraphics.strokeWeight(2); // line thickness
graphGraphics.stroke(242,100,66);
graphGraphics.smooth();
while(tempCount < data.length)
{
try
{
y2 = (int)data[tempCount];
step = step + zoom;
if(step > offset && y1 > 0 && step < graphSizeX+offset){
graphGraphics.line(step-zoom, y1, step, y2);
}
y1 = y2;
tempCount++;
lastY2 = y2;
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
graphGraphics.endDraw();
image(graphGraphics, 0, 0);
restartDraw = true;
}
Any help and criticisms are welcome. Thank you for your valuable time.
I'm not sure if that approach is the best. You can try something as simple as this:
// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com
// Example: a graph of random numbers
float[] vals;
void setup() {
size(400,200);
smooth();
// An array of random values
vals = new float[width];
for (int i = 0; i < vals.length; i++) {
vals[i] = random(height);
}
}
void draw() {
background(255);
// Draw lines connecting all points
for (int i = 0; i < vals.length-1; i++) {
stroke(0);
strokeWeight(2);
line(i,vals[i],i+1,vals[i+1]);
}
// Slide everything down in the array
for (int i = 0; i < vals.length-1; i++) {
vals[i] = vals[i+1];
}
// Add a new random value
vals[vals.length-1] = random(height);//use seismograph.getCurrentValue(); here instead
}
You can easily do the same using a PGraphics buffer as your code suggests:
// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com
// Example: a graph of random numbers
float[] vals;
PGraphics graph;
void setup() {
size(400,200);
graph = createGraphics(width,height);
// An array of random values
vals = new float[width];
for (int i = 0; i < vals.length; i++) {
vals[i] = random(height);
}
}
void draw() {
graph.beginDraw();
graph.background(255);
// Draw lines connecting all points
for (int i = 0; i < vals.length-1; i++) {
graph.stroke(0);
graph.strokeWeight(2);
graph.line(i,vals[i],i+1,vals[i+1]);
}
graph.endDraw();
image(graph,0,0);
// Slide everything down in the array
for (int i = 0; i < vals.length-1; i++) {
vals[i] = vals[i+1];
}
// Add a new random value
vals[vals.length-1] = random(height);//use seismograph.getCurrentValue(); here instead
}
The main idea is to cycle the newest data in an array and simply draw the values from this shifting array. As long as you clear the previous frame (background()) the graph should look ok.
I've been stuck with this problem for hours now. I've got a JPanel to which I draw several pictures.
It's a 2D game, I keep printing everything to the screen by simply using repaint();.
At some point, I want to draw a man firing a pistol. Everything works fine so far, but only if the man looks at the north-direction because my animation-pictures are all drawn to the north. Rotating them manually and pasting them into my project after that would make it way too far, so I've decided to rotate the image to make it useable in any direction, but I can't figure out how to do it. Here's some code:
A simple switch in this method checks for the current facing-direction. Note, that the first case (NORTH) is working fine, because the pictures are drawn to the north as default.
switch(CharDirection){
case NORTH:
if(PistolAnim == 0){
try {
Man = ImageIO.read(ManNorthURL);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
try{
if(PistolAnim == 1){
Man = ImageIO.read(Shoot1URL);
PistolAnim = 2;
return;
}
if(PistolAnim == 2){
Man = ImageIO.read(Shoot2URL);
PistolAnim = 3;
return;
}
if(PistolAnim ==3){
Man = ImageIO.read(Shoot3URL);
PistolAnim = 0;
return;
}
} catch (IOException e){
}
}
break;
case EAST:
if(PistolAnim == 0){
try {
Man = ImageIO.read(ManEastURL);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
try{
if(PistolAnim == 1){
Man = ImageIO.read(Shoot1URL);
PistolAnim = 2;
return;
}
if(PistolAnim == 2){
Man = ImageIO.read(Shoot2URL);
PistolAnim = 3;
return;
}
if(PistolAnim ==3){
Man = ImageIO.read(Shoot3URL);
PistolAnim = 0;
return;
}
} catch (IOException e){
}
}
break;
The integer variable "PistolAnim" is for showing the state of the animation. 0 - no animation ongoing
1 - Picture 1
2 - Picture 2
3 - Picture 3
The paint function (well, the important part) looks like this:
g.drawImage(Man, CharX, CharY, Man.getWidth(), Man.getHeight(), null);
I've tried to use the following rotation method:
public void rotate(double Degrees, BufferedImage img1){
ImageIcon icon = new ImageIcon(img1);
BufferedImage BlankCanvas = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = (Graphics2D)BlankCanvas.getGraphics();
g2d.rotate(Math.toRadians(Degrees), icon.getIconWidth()/2, icon.getIconHeight()/2);
g2d.drawImage(img1, 100, 100, null);
img1 = BlankCanvas;
}
I found it on the internet and it worked in some tests of mine, but now it isn't doing what it should do. I inserted the line
rotation(90, Man);
at almost every point of my code, but nothing works. I can't rotate the whole Graphics2D object in paint (it's called g) either, because it draws other pictures as well.
Anybody got an idea?
Thanks!
Graphics2D g2d = (Graphics2D)BlankCanvas.getGraphics();
This code is wrong. You should NOT use the getGraphics() method. You should do your custom painting from within the paintComponent() method of your JPanel and use the Graphics object that is passed to this method. Then when you invoke the "rotate(...)` method you pass this Graphics object to the method and use it to paint the rotated image.
Instead of worrying about the rotation/translation code, you could just use the Rotated Icon. Then you can paint the icon using the paintIcon(...) method:
RotatedIcon rotated = new RotatedIcon(icon, degrees);
rotated.paintIcon(...)
The method below will rotate your BufferedImage to specific angle specified by you. You can use this method to rotate the image.
//method for rotating the image to a specific angle
public BufferedImage rotateImage(BufferedImage image, double angle) {
//calculate the new size of the rotated image
double rad = Math.toRadians(angle);
double sin = Math.abs(Math.sin(rad));
double cos = Math.abs(Math.cos(rad));
int newWidth = (int) Math.floor(image.getWidth() * cos + image.getHeight() * sin);
int newHeight = (int) Math.floor(image.getHeight() * cos + image.getWidth() * sin);
//createding a new bufferedimage with size that will have rotated image
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_BYTE_INDEXED);
//we set the information of how much will the image be
//displaced in x an y axis during the transform operation
AffineTransform at = new AffineTransform();
at.translate((newWidth - image.getWidth()) / 2, (newHeight - image.getHeight()) / 2);
//calculate the center of transformation
int coordX = image.getWidth() / 2;
int coordY = image.getHeight() / 2;
//setting the center and angle information for rotate operation
at.rotate(rad, coordX, coordY);
//executing the rotate operation
AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_BILINEAR);
rotated = scaleOp.filter(image, rotated);
return rotated;
}
After rotating the image to angle you desire you can draw the image.
BufferedImage rotatedImage = rotateImage(image,90);
//draw image