I'd like to keep rotating a icon from FontAwesome in Processing java. (the fa-spin icon to be precisely.) (\f110)
Link to effect: Example
My create-function for an icon
public void drawIcon(int size, String icon, float custom_height) {
font = createFont("fontawesome-webfont.ttf",size);
textFont(font);
if(icon != null && !icon.trim().isEmpty()) {
text(icon, width / 2, height / custom_height);
}
}
Initialization object + called method
To create my icon, I initialized an object and called my function in the draw()-method :
Content content = new Content(); // content object
PFont font; // font object
public void draw() {
content.drawIcon(46, "\uf110", 7);
}
I found the rotate/translate methods from the documentation, but can't figure out the correct parameters for rotating this icon 360 degrees continiously.
Attempt
public void draw() {
rotate(degrees(360));
content.drawIcon(46, "\uf110", 7);
}
I recommended loading the font once in setup() rather than potentially multiple times a second in drawIcon().
You can simply use text() after you've loaded the font and called textFont().
e.g.
PFont font; // font object
public void setup() {
font = loadFont("fontawesome-webfont.ttf",46);
textFont(font);
}
public void drawIcon(String icon, float custom_height) {
if(icon != null && !icon.trim().isEmpty()) {
text(icon, width / 2, height / custom_height);
}
}
In terms of rotating, at the moment you're specifying the same angle continuously. What you probably want to do is create a variable to keep track of the current angle, increment the angle and pass that to rotate() in draw().
PFont font; // font object
int angle = 0;
public void setup() {
font = loadFont("fontawesome-webfont.ttf",46);
textFont(font);
}
public void drawIcon(String icon, float custom_height) {
if(icon != null && !icon.trim().isEmpty()) {
text(icon, width / 2, height / custom_height);
}
}
public void draw() {
//increment angle (angle++; would to the same, but hopefully expanded version is easier to read)
angle = angle + 1;
rotate(degrees(angle));
content.drawIcon("\uf110", 7);
}
Be sure to checkout the Rotate Processing example as well.
You may notice that the symbol might not rotate from the centre.
This will require you to use multiple coordinate spaces using pushMatrix();/popMatrix(); calls. Do read the 2D transformations tutorial for more info on how to do that.
Related
Based on the answer for the question Get the exact Stringposition in PDF I can now get all the strings in a PDF file. Please have a look at the code:
PdfReader reader = new PdfReader("file.pdf");
RenderListener listener = new MyTextRenderListener();
PdfReaderContentParser parser = new PdfReaderContentParser(reader);
parser.processContent(1, listener);
static class MyTextRenderListener implements RenderListener {
#Override
public void renderText(TextRenderInfo renderInfo) {
String text = renderInfo.getText(); // line with text
}
#Override
public void beginTextBlock() { }
#Override
public void endTextBlock() { }
#Override
public void renderImage(ImageRenderInfo renderInfo) { }
}
mkl in his answer wrote:
if yourRenderListenerin addition to inspecting the text
withgetText()also considersgetBaseline()or
evengetAscentLine()andgetDescentLine().you have all the
coordinates you will likely need.
In fact, TextRenderInfo has a few instances of LineSegment class which give some sort of coordinates. How do I use those coordinate (by transforming or extracting appropriate values from) to prepare a Rectangle object so the text that is found could be removed? A rectangle object has four coordinates that describe the position of the given text.
An example of removing strings (i.e. redacting) by using a Rectangle object can be found at SO (Remove text occurrences contained in a specified area with iText
)
UPDATE
I managed to do what I wanted by trial-and-error but I consider this a workaround and not a proper solution.
#Override
public void renderText(TextRenderInfo renderInfo) {
LineSegment baseline = renderInfo.getBaseline();
float x = baseline.getStartPoint().get(Vector.I1);
float y = baseline.getStartPoint().get(Vector.I2);
float xx = baseline.getEndPoint().get(Vector.I1);
float yy = baseline.getEndPoint().get(Vector.I2);
rectangle = new Rectangle(x, yy, xx, y + 5);
}
Now I have a Rectangle object (note that I add 5 to one of its coordinates by playing with coordinate so that they cover all the string) and I can now redact the text. It works fine for unitary colours (e.g. white) when there is no image. When the text is on image or the page colour is in different colour than black, it will fail. That's why I describe my solution as a workaround. To me, it would be better to blank the text (replace it with empty string). How this could be done?
Response to mkl's comment
Not sure, if I've done it right:
LineSegment descentLine = renderInfo.getDescentLine();
float x = descentLine.getStartPoint().get(Vector.I1);
float y = descentLine.getStartPoint().get(Vector.I2);
float xx = descentLine.getEndPoint().get(Vector.I1);
float yy = descentLine.getEndPoint().get(Vector.I2);
rectangle = new Rectangle(xx, yy, x, y);
I've used also the ascentLIne the same way. Unfortunetly, none of this have worked.
In all your attempts you tried to construct the rectangle from a single line, originally the base line, later the descent line. With such an approach you obviously don't have the height of the rectangle and can only guess.
Instead of that you should make use of both the descent and ascent lines!
E.g. assuming the simplified case of text drawn upright:
LineSegment ascentLine = renderInfo.getAscentLine();
LineSegment descentLine = renderInfo.getDescentLine();
float llx = descentLine.getStartPoint().get(Vector.I1);
float lly = descentLine.getStartPoint().get(Vector.I2);
float urx = ascentLine.getEndPoint().get(Vector.I1);
float ury = ascentLine.getEndPoint().get(Vector.I2);
rectangle = new Rectangle(llx, lly, urx, ury);
I want to learn to create partially colored text/String in processing(java) like below example. It can be 10% white 90% black. or any ratio for any two color. I would like to learn how to do it in processing.
Option 1: In Processing
Render your text into a PGraphics object and then iterate over the pixels[] of the object to change the colour of a proportion of the text.
In the example below, the colour ratio will not be correct unless you create a PGraphics object of the exact dimensions of the text - I'm not sure whether there is a programmatic way to determine the required dimensions. (As a result, I'm using a ratio of 0.6 to recolour the top ~50% of the text in the example).
Furthermore, noSmooth() has been called on the object as anti-aliasing will create pixels that aren't quite the original text colour, which makes replacing them more complicated than simply checking for equality (==).
import processing.core.PApplet;
import processing.core.PGraphics;
public class Text extends PApplet {
PGraphics text;
public static void main(String[] args) {
PApplet.main(Text.class);
}
#Override
public void settings() {
size(400, 400);
}
#Override
public void setup() {
makeText();
}
#Override
public void draw() {
background(55);
image(text, 100, 100);
}
public void makeText() {
final int orange = color(255,165,0);
final int yellow = color(255,255,0);
final float ratio = 0.6f;
text = createGraphics(150, 60);
text.noSmooth();
text.beginDraw();
text.fill(orange);
text.textSize(60);
text.textAlign(LEFT, TOP);
text.loadPixels();
text.text("TEXT", 0, 0);
text.endDraw();
for (int pixel = 0; pixel < (text.pixels.length * ratio); pixel++) {
if (text.pixels[pixel] == orange) {
text.pixels[pixel] = yellow;
}
}
}
}
Result:
Option 2: With JavaFX
This is a hacky method, but it gives better results since the text is anti-aliased. This method requires that your sketch be in FX2D rendering mode, which can be prescribed in the size() call.
Expose the stackPane belonging to the PApplet (this is where the JavaFX text object will be added):
Canvas canvas = (Canvas) ((PSurfaceFX) getSurface()).getNative();
StackPane p = (StackPane) canvas.getParent();
Create a JavaFX text object. I am using a CSS style (a linear gradient with immediate cutoff) for the partially-coloured effect.
javafx.scene.text.Text t = new javafx.scene.text.Text("TEXT");
t.setCache(true);
t.setFont(Font.font(null, FontWeight.NORMAL, 60));
t.setStyle("-fx-fill:linear-gradient( from 100.0% 100.0% to 100.0% 0.0%, rgb(255, 165, 0) 0.5," + "rgb(255, 255, 0)" +" 0.5);");
Finally, add the text to the PApplet's stage stackpane (which was exposed earlier):
p.getChildren().add(t);
Result (note the anti-aliasing):
Also note that you'lll need to use t.setVisible() to toggle visibility since this text element is entirely separate from Processing's drawing canvas.
I want to get the color for specific coordinates inside a Canvas. I already tried getting a snapshot using this code:
WritableImage snap = gc.getCanvas().snapshot(null, null);
snap.getPixelReader().getArgb(x, y); //This just gets the color without assigning it.
But it just takes too much time for my application. I was wondering if there is any other way to access the color of a pixel for which I know the coordinates.
A Canvas buffers the drawing instructions prescribed by invoking the methods of a GraphicsContext. There are no pixels to read until the Canvas is rendered in a later pulse, and the internal format of the instruction buffer is not exposed in the API.
If a snapshot() of the Canvas is feasible, a rendered pixel may be examined using the resulting image's PixelReader.
int aRGB = image.getPixelReader().getArgb(x, y);
This example focuses on a single pixel. This example displays the ARGB BlendMode result in a TextField and a Tooltip as the mouse moves on the Canvas. More examples may be found here.
As an alternative, consider drawing into a BufferedImage, illustrated here, which allows access to the image's pixels directly and via its WritableRaster. Adding the following line to this complete example outputs the expected value for opaque red in ARGB order: ffff0000.
System.out.println(Integer.toHexString(bi.getRGB(50, 550)));
public class Pixel
{
private static final SnapshotParameters SP = new SnapshotParameters();
private static final WritableImage WI = new WritableImage(1, 1);
private static final PixelReader PR = WI.getPixelReader();
private Pixel()
{
}
public static int getArgb(Node n, double x, double y)
{
synchronized (WI)
{
Rectangle2D r = new Rectangle2D(x, y, 1, 1);
SP.setViewport(r);
n.snapshot(SP, WI);
return PR.getArgb(0, 0);
}
}
public static Color getColor(Node n, double x, double y)
{
synchronized (WI)
{
Rectangle2D r = new Rectangle2D(x, y, 1, 1);
SP.setViewport(r);
n.snapshot(SP, WI);
return PR.getColor(0, 0);
}
}
}
We are building an Android application that involves image editing. A few of the features include rotating an image and erasing part of the image.
We are using the following library: https://github.com/nimengbo/StickerView
We have successfully created a function to rotate and erase the image. However, when we tried to perform the following actions:
Rotating an image at a certain degree.
Then, erasing the image.
We found the following bug:
When we tried to erase the rotated image, the erased path did not reflect the path which our finger traced on the screen.
From the above image, the yellow line is the actual movement of the finger (straight down vertical across the sticker). But, the resulting erased path was found to be diagonal.
This problem only exists when the image is rotated. It does not exist when the image is not rotated.
After further debugging, we have a few assumptions from the above problems:
Due to the rotated image, the x and y absolution position is changed. Thus, the path does not reflect the correct path by the touch routes.
How can we ensure that the path is still referencing the right path on what the finger is touching even after being rotated?
Here is the code we have in our StickerView.java class that extends ImageView class.
onTouchEvent
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = MotionEventCompat.getActionMasked(event);
float[] pointXY = new float[2];
pointXY = getAbsolutePosition(event.getX(0),event.getY(0));
float xPoint = pointXY[0];
float yPoint = pointXY[1];
switch (action) {
case MotionEvent.ACTION_DOWN:
// first touch
// if it is inside the image
if (isInBitmap(event)) {
// set isInSide to true
isInSide = true;
// if it is a scratch
if(doScratch){
// start creating the scratch path
mScratchPath = new Path();
mScratchPath.moveTo(xPoint, yPoint);
mScratchPath.lineTo(xPoint, yPoint);
paths.add(new Pair<Path, Paint>(mScratchPath, mScratchCurrentPaint));
}
}
break;
case MotionEvent.ACTION_MOVE:
// if two fingers touch and is not a scratch,
// then it means we can rotate / resize / pan
if (isPointerDown && !doScratch) {
// reset matrix
matrix.reset();
// get the center point
scaledImageCenterX = (mImageWidth * mScaleFactor) / 2 ;
scaledImageCenterY = (mImageHeight * mScaleFactor) / 2;
// ROTATE THE IMAGE !!!
matrix.postRotate(lastRotateDegree, scaledImageCenterX, scaledImageCenterY);
// done to call onDraw
invalidate();
}
break;
}
if (operationListener != null) {
operationListener.onEdit(this);
}
// if it is a scratch
if(doScratch){
// then for every point, create a scratch path
mScratchPath.lineTo(xPoint, yPoint);
invalidate();
}else{
mScaleDetector.onTouchEvent(event);
mRotateDetector.onTouchEvent(event);
mMoveDetector.onTouchEvent(event);
mShoveDetector.onTouchEvent(event);
}
return handled;
}
onDraw
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// if the image exists
if (mBitmap != null) {
// save canvas
canvas.save();
// if it is a scratch
if(doScratch){
// scratch the image
mFillCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
// Draw our surface, nice an pristine
final Drawable surface = mScratchSurface;
if(surface != null) {
surface.draw(mFillCanvas);
}
//Scratch the surface
if(paths != null) {
for (Pair<Path, Paint> p : paths) {
mFillCanvas.drawPath(p.first,p.second);
}
}
mBitmap = mFillCache;
}
canvas.drawBitmap(mBitmap, matrix, bitmapPaint);
canvas.restore();
}
}
getAbsolutePosition function
public float[] getAbsolutePosition(float Ax, float Ay) {
float[] mMatrixValues = new float[9];
matrix.getValues(mMatrixValues);
float x = mImageWidth - ((mMatrixValues[Matrix.MTRANS_X] - Ax) / mMatrixValues[Matrix.MSCALE_X]) - (mImageWidth - getTranslationX());
float y = mImageHeight - ((mMatrixValues[Matrix.MTRANS_Y] - Ay) / mMatrixValues[Matrix.MSCALE_X]) - (mImageHeight - getTranslationY());
return new float[] { x, y};
}
You need to apply the rotation matrics on your coordinates. Here theta is the angle by which the image is rotated.
Image source wikipedia
In code it will be something like this
int getAbsoluteX(int x, int y, double theta)
{
int x_new = (int)(x*Math.cos(theta) - y*Math.sin(theta));
return x_new;
}
int getAbsoluteY(int x, int y, double theta)
{
int y_new = (int)(x*Math.sin(theta) + y*Math.cos(theta));
return y_new;
}
You are rotating the entire view in the onDraw method. Not only this affects the performance (you are repeating the same transformation every time), it affects the outcome as you noticed since it rotates everything in the view, including the paths.
If for some reason you must do it this way (not recommended), then you need to rotate the paths in the opposite direction by the same amount. I strongly suggest you don't do that. Try to find a way around rotating the background inside the onDraw, instead. For example, whenever the background is rotated, you can create a new background (outside the onDraw method) and use that background in the onDraw method.
There are many questions of the converse, inserting a JTextPane into a JPanel. This is not my question. I need to be able to insert a custom JPanel (with drag and drop, drag, and mouse click listeners) into a JTextPane, which is then put into a JScrollPane, and finally put into a JFrame for displaying. The reason is because I need to have an image with support for resizing by dragging it within a chat client, which is itself primarily text.
Conveniently enough, there is a relevant method in JTextPane: insertComponent(Component c), but whenever I use it, my components end up being squished to exactly one line of text worth of space (even though they report having a larger size). This is perfect for plain buttons, but if I need anything larger, I'm out of luck. I can insert images by themselves just fine, with ImageIcons, but images wrapped inside a JPanel don't work at all (plus I can't add any listeners to ImageIcons, since they're not GUI elements; overriding one isn't an option).
Whenever a user drags an image into the chat client, this bit of code inserts the custom JPanel:
private void sendImage(BufferedImage im, int cl) throws IOException {
if(output == null) return;
//Send the image itself over to your friend
byte[] toSend = toBytes(im, cl);
sendString(nickname.hashCode() + "image"); //Header for image
output.writeInt(toSend.length); //Tells how many bytes to read.
output.write(toSend);
//Let the user know that the image was sent
float linmb = (float)(toSend.length / 1048576.0); //Size of file sent
addText("\n" + nickname + " sent an image! (" + linmb + " MB)\n", Color.RED.darker());
//Show the image itself
DraggerPanel d = new DraggerPanel(im, true);
text.insertComponent(d);
d.repaint();
//Spacer
addText("\n");
}
This is the source for DraggerPanel, the custom JPanel that holds an image:
public class DraggerPanel extends JPanel {
private BufferedImage image; //The image we're drawing
private Point startingPoint = null; //Starting point for resizing
private boolean first = true; //Is this the first drag?
private boolean lockedDrag; //If true, then lock x and y to be proportionally dragged.
public DraggerPanel(BufferedImage image, boolean lockedDrag) {
super();
this.image = image;
this.lockedDrag = lockedDrag;
//The listener for dragging events.
addMouseMotionListener(new MouseMotionListener() {
private int inWidth = 0, inHeight = 0; //Initial height and width values
private double ratio = 0; //Ratio of height to width for locked drag.
public void mouseDragged(MouseEvent m) {
if (first) { //If we're first, record initial position.
startingPoint = m.getPoint();
first = false;
inWidth = getWidth();
inHeight = getHeight();
ratio = (double)inHeight / inWidth;
} else { //Otherwise, change the size of the window.
if (!lockedDrag) {
int w = (int)startingPoint.getX() - m.getX();
int h = (int)startingPoint.getY() - m.getY();
setSize(Math.abs(inWidth - w), Math.abs(inHeight - h));
} else {
int w = (int)startingPoint.getX() - m.getX();
int h = (int)((double)ratio * w);
setSize(Math.abs(inWidth - w), Math.abs(inHeight - h));
}
}
repaint();
}
public void mouseMoved(MouseEvent m){
}
});
//Lets us know when you're not dragging anymore.
addMouseListener(new MouseAdapter(){public void mouseReleased(MouseEvent m){first = true;}});
//Set appropriate size.
if(image != null) setSize(image.getWidth(), image.getHeight());
else setSize(200,200);
//We're live, baby.
setVisible(true);
}
public void paint(Graphics g) {
if (image == null) super.paint(g);
else g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
}
}
Update 1: I followed #camickr 's advice, and updated the DraggerPanel to use setPreferredSize instead of setSize, as well as overrode paintComponent() instead of paint(). Now, the image has the proper height, but is stretched to the width of the JTextPane (which seems like what it was doing before). Furthermore, resizing doesn't seem to matter- the image doesn't change its size at all. Mouse events are definitely going through, but not affecting the size. It seems as though the original problem isn't fully resolved, since the JPanel's size isn't what I need it to be, and the solution to that will also lead to a solution to the resizing issue.
Update 2: I did it! I finally did it. To the future time travelers who have this issue, I basically yelled at the JTextPane by not only using setSize() in my overridden JPanel, but also setPreferredSize() and setMaximumSize(). The preferred one works well with height, and the maximum sets the width (God knows why). Thanks for your tips, #camickr!
my components end up being squished to exactly one line of text worth of space (even though they report having a larger size).
I would guess the size is not important.
I would think you need to override the getPreferredSize() method of your DraggerPanel to return the preferred size of the panel so the text pane can display the panel.
Also, custom painting is done by overriding the paintComponent(...) method NOT the paint() method.