A part of my application is working as a tutorial. For that purpose I got JPanels that display a JLabel that has an image as content. Although when the image is larger than what fits the screen it will just cut what doesn't fit. What I need to do is resize the image so that I will fit the space the JLabel is given. Also tried using JScrollPanes but didn't make any difference(although I prefer to resize image).
I tried getting a scaled instance of the image using
Image scaledImg = myPicture.getScaledInstance(width, height, Image.SCALE_SMOOTH);
but it didn't make any difference.
This is the current code :
private JLabel getTutorialLabel(int type) {
String path = "/Images/Tutorial/test" + type + ".png";
try {
BufferedImage tutorialPic = ImageIO.read(getClass().getResource(path));
// int height = (int) (screenDim.height * 0.9);
// int width = (int) (screenDim.width * 0.9);
// Image scaledImg = myPicture.getScaledInstance(width, height, Image.SCALE_SMOOTH);
JLabel picLabel = new JLabel(new ImageIcon(tutorialPic));
return picLabel;
} catch (IOException ex) {
Logger.getLogger(Tutorial.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
How I get the image to display properly?
Please note that there are other components displayed along with the JLabel(JMenuBar and some buttons on bottom of screen) so I need to image to fill the space JLabel is given.
Edit: updated the method
You could try this method in the Java Helper Library.
Or, here's the method itself:
/**
* This method resizes the given image using Image.SCALE_SMOOTH.
*
* #param image the image to be resized
* #param width the desired width of the new image. Negative values force the only constraint to be height.
* #param height the desired height of the new image. Negative values force the only constraint to be width.
* #param max if true, sets the width and height as maximum heights and widths, if false, they are minimums.
* #return the resized image.
*/
public static Image resizeImage(Image image, int width, int height, boolean max) {
if (width < 0 && height > 0) {
return resizeImageBy(image, height, false);
} else if (width > 0 && height < 0) {
return resizeImageBy(image, width, true);
} else if (width < 0 && height < 0) {
PrinterHelper.printErr("Setting the image size to (width, height) of: ("
+ width + ", " + height + ") effectively means \"do nothing\"... Returning original image");
return image;
//alternatively you can use System.err.println("");
//or you could just ignore this case
}
int currentHeight = image.getHeight(null);
int currentWidth = image.getWidth(null);
int expectedWidth = (height * currentWidth) / currentHeight;
//Size will be set to the height
//unless the expectedWidth is greater than the width and the constraint is maximum
//or the expectedWidth is less than the width and the constraint is minimum
int size = height;
if (max && expectedWidth > width) {
size = width;
} else if (!max && expectedWidth < width) {
size = width;
}
return resizeImageBy(image, size, (size == width));
}
/**
* Resizes the given image using Image.SCALE_SMOOTH.
*
* #param image the image to be resized
* #param size the size to resize the width/height by (see setWidth)
* #param setWidth whether the size applies to the height or to the width
* #return the resized image
*/
public static Image resizeImageBy(Image image, int size, boolean setWidth) {
if (setWidth) {
return image.getScaledInstance(size, -1, Image.SCALE_SMOOTH);
} else {
return image.getScaledInstance(-1, size, Image.SCALE_SMOOTH);
}
}
Related
I am working a method to resize featured image and I have finished it, but after testing seems something isn't right cause the image is deformed and don't look nice at all, I am looking very carefully, but I don't get to see what's the problem, I would really appreciate if you have any idea what is wrong or a better way to solve this. Have a look please, I would really need some better advice and thanks in advance!!
private byte[] resizeFeatureImage(MultipartFile featureImage)
{
try
{
BufferedImage originalImage = ImageIO.read(featureImage.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
double featImageWidth = originalImage.getWidth();
double featImageHeight = originalImage.getHeight();
if (featImageHeight > MAX_FEAT_IMAGE_HEIGHT || featImageWidth > MAX_FEAT_IMAGE_WIDTH)
{
// Sanity check on the input (division by zero, infinity):
if (featImageWidth <= 1 || featImageHeight <= 1)
{
throw new IllegalArgumentException("Cannot do image resize for " + featureImage);
}
// The scaling factors to reach to max on WIDTH and HEIGHT:
double xScale = MAX_FEAT_IMAGE_WIDTH / featImageWidth;
double yScale = MAX_FEAT_IMAGE_HEIGHT / featImageHeight;
// Proportional (scale WIDTH and HEIGHT by the same factor):
double scale = Math.min(xScale, yScale);
// (Possibly) Do not enlarge:
scale = Math.min(1.0, scale);
int finalWidth = Math.min((int) Math.round(scale * featImageWidth), MAX_FEAT_IMAGE_WIDTH);
int finalHeight = Math.min((int) Math.round(scale * featImageHeight), MAX_FEAT_IMAGE_HEIGHT);
double ratio = featImageWidth / featImageHeight;
// WIDTH is bigger then HEIGHT
if (ratio > 1)
{
finalWidth = MAX_FEAT_IMAGE_WIDTH;
finalHeight = (int) Math.round(MAX_FEAT_IMAGE_HEIGHT / ratio);
}
// HEIGHT is bigger then WIDTH
else if (ratio < 1)
{
finalWidth = (int) Math.round(MAX_FEAT_IMAGE_WIDTH / ratio);
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
}
// WIDTH and HEIGHT are equal
else
{
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
finalWidth = MAX_FEAT_IMAGE_WIDTH;
}
logger.info("[resizeFeatureImage] [FEATURE IMAGE RESIZE] Starting to resize feature Image");
Graphics2D g2d;
BufferedImage resizedImage;
if (featureImage.getContentType().contains("png"))
{
resizedImage = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_INT_ARGB);
}
else
{
resizedImage = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_3BYTE_BGR);
}
g2d = resizedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.drawImage(ImageIO.read(featureImage.getInputStream()), 0, 0, finalWidth, finalHeight,
null);
g2d.dispose();
ImageIO.write(resizedImage, featureImage.getContentType().split("/")[1], baos);
logger.info("[resizeFeatureImage] [FEATURE IMAGE RESIZE] Feature image resized!");
return baos.toByteArray();
}
else
{
ImageIO.write(originalImage, featureImage.getContentType().split("/")[1], baos);
return baos.toByteArray();
}
}
catch (Exception e)
{
logger.warn("[resizeFeatureImage] [STATUS] - ERROR ");
logger.warn("[resizeFeatureImage] [EXCEPTION] " + e.getMessage(), e);
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"The file you uploaded can be damaged or has incorrect encoding.");
}
}
When you scale, your image needs to retain the same ratio.
double featImageWidth = originalImage.getWidth();
double featImageHeight = originalImage.getHeight();
double ratio = featImageWidth/featImageHeight;
//this width meets your constraints
int finalWidth = MAX_FEAT_IMAGE_WIDTH;
//this final height is what the height would be to keep the same ratio.
int finalHeight = (int)(finalWidth/ratio);
if(finalHeight > MAX_FEAT_IMAGE_HEIGHT){
//the height constrains the image.
finalHeight = MAX_FEAT_IMAGE_HEIGHT;
finalWidth = (int)(finalHeight*ratio)
}
I'm implementing Camera 2 API in my project. I'm using TextureView and these line of codes to set the camera fullscreen preview size:
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mPreviewSize = map.getOutputSizes(SurfaceTexture.class)[0];
This seems to be the largest preview size that device support. I'm not sure if this size works with all devices and fit its device's aspect ratio without being stretched. Does anyone know?
If your Camera resolutions , texture view and your device's display dimensions are not same then you have to adjust the dimensions. For that you have to put your TextureView inside of FrameLayout. Below Code is applicable to all the devices with various Display resolutions.
Take your Display Dimetions if you are previewing in full screen.Take int DSI_height, int DSI_width global variable.
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
DSI_height = displayMetrics.heightPixels;
DSI_width = displayMetrics.widthPixels;
select your required resolutions from Camera2 API and assign to Size imageDimension, Take private Size imageDimension globally and use
setAspectRatioTextureView(imageDimension.getHeight(),imageDimension.getWidth());
and use below logic
private void setAspectRatioTextureView(int ResolutionWidth , int ResolutionHeight )
{
if(ResolutionWidth > ResolutionHeight){
int newWidth = DSI_width;
int newHeight = ((DSI_width * ResolutionWidth)/ResolutionHeight);
updateTextureViewSize(newWidth,newHeight);
}else {
int newWidth = DSI_width;
int newHeight = ((DSI_width * ResolutionHeight)/ResolutionWidth);
updateTextureViewSize(newWidth,newHeight);
}
}
private void updateTextureViewSize(int viewWidth, int viewHeight) {
Log.d(TAG, "TextureView Width : " + viewWidth + " TextureView Height : " + viewHeight);
textureView.setLayoutParams(new FrameLayout.LayoutParams(viewWidth, viewHeight));
}
There might be edge cases where that approach would fail, but I don't have a perfect answer to your question why.
In contrast, I have a proper approach on how to implement a version that will most certainly work:
Looking at the Google API demos for the Camera 2, I found some sample code that should be helpful to you to make sure it will fit all screen sized correctly:
/**
* Given {#code choices} of {#code Size}s supported by a camera, choose the smallest one that
* is at least as large as the respective texture view size, and that is at most as large as the
* respective max size, and whose aspect ratio matches with the specified value. If such size
* doesn't exist, choose the largest one that is at most as large as the respective max size,
* and whose aspect ratio matches with the specified value.
*
* #param choices The list of sizes that the camera supports for the intended output
* class
* #param textureViewWidth The width of the texture view relative to sensor coordinate
* #param textureViewHeight The height of the texture view relative to sensor coordinate
* #param maxWidth The maximum width that can be chosen
* #param maxHeight The maximum height that can be chosen
* #param aspectRatio The aspect ratio
* #return The optimal {#code Size}, or an arbitrary one if none were big enough
*/
private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
// Collect the supported resolutions that are smaller than the preview Surface
List<Size> notBigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
option.getHeight() == option.getWidth() * h / w) {
if (option.getWidth() >= textureViewWidth &&
option.getHeight() >= textureViewHeight) {
bigEnough.add(option);
} else {
notBigEnough.add(option);
}
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CompareSizesByArea());
} else {
Log.e(TAG, "Couldn't find any suitable preview size");
return choices[0];
}
}
Source
Also you should take a look at the whole Camera2BasicFragment.java and AutoFitTextureView.java classes for proper implementation.
I solved this problem via a different approach. I get the screen width and height and calculate how much wider or higher the preview would have to be to fill the whole screen and keep aspect ratio. It works pretty well for me without any distortions.
Add a class member variable:
public DisplayMetrics mMetrics = new DisplayMetrics();
Use the following as onMeasure:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
WindowManager windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(mMetrics);
double ratio = (double)mRatioWidth / (double)mRatioHeight;
double invertedRatio = (double)mRatioHeight / (double)mRatioWidth;
double portraitHeight = width * invertedRatio;
double portraitWidth = width * (mMetrics.heightPixels / portraitHeight);
double landscapeWidth = height * ratio;
double landscapeHeight = (mMetrics.widthPixels / landscapeWidth) * height;
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension((int)portraitWidth, mMetrics.heightPixels);
} else {
setMeasuredDimension(mMetrics.widthPixels, (int)landscapeHeight);
}
}
}
Any feedback is greatly appreciated ;)
Best M
Change the AutoFitTextureView.java file and set value like below:
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, height);
Log.d("rlijeolid1",String.valueOf(width)+"\t"+String.valueOf(height));
} else {
setMeasuredDimension(width , height);
Log.d("rlijeolid2",String.valueOf(height * mRatioWidth / mRatioHeight)+"\t"+String.valueOf(height));
}
}
}
I am trying to draw characters from Hindi on a plain white background and store the resulting image as a jpeg. I need to size the image dynamically so that it fits the text. Currently, image height is fixed by assuming a size of 35 pixels (font size has been set to 22). How do I fix image width?
So far, I have tried to set the image width to 35 pixels times the maximum length of the different lines of text. That has not worked and the images saved are very wide. I am using drawString method of the graphics class in Java.
The function that creates images:
public static void printImages_temp(List<String> list) {
/* Function to print translations contained in list to images.
* Steps:
* 1. Take plain white image.
* 2. Write English word on top.
* 3. Take each translation and print one to each line.
*/
String dest = tgtDir + "\\" + list.get(0) + ".jpg"; //destination file image.
int imgWidth_max = 410;
int imgHeight_max = 230;
int fontSize = 22;
Font f = new Font("SERIF", Font.BOLD, fontSize);
//compute height and width of image.
int img_height = list.size() * 35 + 20;
int img_width = 0;
int max_length = 0;
for(int i = 0; i < list.size(); i++) {
if(list.get(i).length() > max_length) {
max_length = list.get(i).length();
}
}
img_width = max_length * 20;
System.out.println("New dimensions of image = " + img_width + " " + img_height);
BufferedImage img = new BufferedImage(img_width, img_height, BufferedImage.TYPE_INT_RGB);
Graphics g = img.getGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, img_width, img_height);
//image has to be written to another file.
for(int i = 0; i < list.size(); i++) {
g.setColor(Color.BLACK);
g.setFont(f);
g.drawString(list.get(i), 10, (i + 1) * 35);
}
//g.drawString(translation, 10, fontWidth); //a 22pt font is approx. 35 pixels long.
g.dispose();
try {
ImageIO.write(img, "jpeg", new File(dest));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("File written successfully to " + dest);
}
My questions:
How do I get width of a non-Latin type character, given that the fonts used to render the text are generic?
Is there a way to get the width that applies to all UTF-8 characters?
How do I create an in-memory fully transparent SWT image and draw a black line on it with antialias enabled?
I expect the result to include only black color and alpha values ranging from 0 to 255 due to antialias...
I googled and tried everything that I could... is this possible at all?
This is how I did and it works:
Image src = new Image(null, 16, 16);
ImageData imageData = src.getImageData();
imageData.transparentPixel = imageData.getPixel(0, 0);
src.dispose();
Image icon = new Image(null, imageData);
//draw on the icon with gc
I was able to make this work, although it feels a bit hacky:
Display display = Display.getDefault();
int width = 10;
int height = 10;
Image canvas = new Image(display, width, height);
GC gc = new GC(canvas);
gc.setAntialias(SWT.ON);
// This sets the alpha on the entire canvas to transparent
gc.setAlpha(0);
gc.fillRectangle(0, 0, width, height);
// Reset our alpha and draw a line
gc.setAlpha(255);
gc.setForeground(display.getSystemColor(SWT.COLOR_BLACK));
gc.drawLine(0, 0, width, height);
// We're done with the GC, so dispose of it
gc.dispose();
ImageData canvasData = canvas.getImageData();
canvasData.alphaData = new byte[width * height];
// This is the hacky bit that is making assumptions about
// the underlying ImageData. In my case it is 32 bit data
// so every 4th byte in the data array is the alpha for that
// pixel...
for (int idx = 0; idx < (width * height); idx++) {
int coord = (idx * 4) + 3;
canvasData.alphaData[idx] = canvasData.data[coord];
}
// Now that we've set the alphaData, we can create our
// final image
Image finalImage = new Image(canvasData);
// And get rid of the canvas
canvas.dispose();
After this, finalImage can be drawn into a GC with drawImage and the transparent parts will be respected.
I made it by allocating an ImageData, making it transparent then creating the Image from the data :
static Image createTransparentImage(Display display, int width, int height) {
// allocate an image data
ImageData imData = new ImageData(width, height, 24, new PaletteData(0xff0000,0x00ff00, 0x0000ff));
imData.setAlpha(0, 0, 0); // just to force alpha array allocation with the right size
Arrays.fill(imData.alphaData, (byte) 0); // set whole image as transparent
// Initialize image from transparent image data
return new Image(display, imData);
}
To scale with transparency, I've found that I have to manually set the alpha byte array as shown below. So the alpha ends up with nearest-neighbor anti aliasing.
public static Image scaleImage(Device device, Image orig, int scaledWidth, int scaledHeight) {
Rectangle origBounds = orig.getBounds();
if (origBounds.width == scaledWidth && origBounds.height == scaledHeight) {
return orig;
}
ImageData origData = orig.getImageData();
ImageData imData = new ImageData(scaledWidth, scaledHeight, origData.depth, origData.palette);
if (origData.alphaData != null) {
imData.alphaData = new byte[imData.width * imData.height];
for (int row = 0; row < imData.height; row++) {
for (int col = 0; col < imData.width; col++) {
int origRow = row * origData.height / imData.height;
int origCol = col * origData.width / imData.width;
byte origAlpha = origData.alphaData[origRow * origData.width + origCol];
imData.alphaData[row * imData.width + col] = origAlpha;
}
}
}
final Image scaled = new Image(device, imData);
GC gc = new GC(scaled);
gc.setAntialias(SWT.ON);
gc.setInterpolation(SWT.HIGH);
gc.setBackground(device.getSystemColor(SWT.COLOR_WHITE));
gc.fillRectangle(0, 0, scaledWidth, scaledHeight);
gc.drawImage(orig, 0, 0, origBounds.width, origBounds.height, 0, 0, scaledWidth, scaledHeight);
gc.dispose();
return scaled;
}
If I have an image of which I know the height and the width, how can I fit it in a rectangle with the biggest possible size without stretching the image.
Pseudo code is enough (but I'm going to use this in Java).
Thanks.
So, based on the answer, I wrote this: but it doesn't work. What do I do wrong?
double imageRatio = bi.getHeight() / bi.getWidth();
double rectRatio = getHeight() / getWidth();
if (imageRatio < rectRatio)
{
// based on the widths
double scale = getWidth() / bi.getWidth();
g.drawImage(bi, 0, 0, (int) (bi.getWidth() * scale), (int) (bi.getHeight() * scale), this);
}
if (rectRatio < imageRatio)
{
// based on the height
double scale = getHeight() / bi.getHeight();
g.drawImage(bi, 0, 0 , (int) (bi.getWidth() * scale), (int) (bi.getHeight() * scale), this);
}
Determine the aspect ratio of both (height divided by width, say, so tall, skinny rectangles have an aspect ratio > 1).
If your rectangle's aspect ratio is greater than that of your image, then scale the image uniformly based on the widths (rectangle width / image width).
If your rectangle's aspect ratio is less than that of your image, then scale the image uniformly based on the heights (rectangle height / image height).
Here is my two cents:
/**
* Calculate the bounds of an image to fit inside a view after scaling and keeping the aspect ratio.
* #param vw container view width
* #param vh container view height
* #param iw image width
* #param ih image height
* #param neverScaleUp if <code>true</code> then it will scale images down but never up when fiting
* #param out Rect that is provided to receive the result. If <code>null</code> then a new rect will be created
* #return Same rect object that was provided to the method or a new one if <code>out</code> was <code>null</code>
*/
private static Rect calcCenter (int vw, int vh, int iw, int ih, boolean neverScaleUp, Rect out) {
double scale = Math.min( (double)vw/(double)iw, (double)vh/(double)ih );
int h = (int)(!neverScaleUp || scale<1.0 ? scale * ih : ih);
int w = (int)(!neverScaleUp || scale<1.0 ? scale * iw : iw);
int x = ((vw - w)>>1);
int y = ((vh - h)>>1);
if (out == null)
out = new Rect( x, y, x + w, y + h );
else
out.set( x, y, x + w, y + h );
return out;
}
This will not affect your aspect ration and will fit exactly on one side and not overshoot on the other side.
public static Rect getScaled(int imgWidth, int imgHeight, int boundaryWidth, int boundaryHeight) {
int original_width = imgWidth;
int original_height = imgHeight;
int bound_width = boundaryWidth;
int bound_height = boundaryHeight;
int new_width = original_width;
int new_height = original_height;
// first check if we need to scale width
if (original_width > bound_width) {
//scale width to fit
new_width = bound_width;
//scale height to maintain aspect ratio
new_height = (new_width * original_height) / original_width;
}
// then check if we need to scale even with the new height
if (new_height > bound_height) {
//scale height to fit instead
new_height = bound_height;
//scale width to maintain aspect ratio
new_width = (new_height * original_width) / original_height;
}
return new Rect(0,0,new_width, new_height);
}