I want to resize an image with the GDI library so that when I resize it to be larger than before there is no blending. (Like when you zoom in on an image in a paint program)
EG: If my image is 2px wide, and 2px tall
(white, white,
white, black)
, and I resize it to be 100% larger, it is 4px by 4px tall
(white, white, white, white,
white, white, white, white,
white, white, black, black,
white, white, black, black)
What InterpolationMode or Smoothing mode (or other properties) of a graphics object can I use to achieve this? The combinations that I have tried so far all cause grey to appear in the test image.
Here is the code that I'm using
/// <summary>
/// Do the resize using GDI+
/// Credit to the original author
/// http://www.bryanrobson.net/dnn/Code/Imageresizing/tabid/69/Default.aspx
/// </summary>
/// <param name="srcBitmap">The source bitmap to be resized</param>
/// <param name="width">The target width</param>
/// <param name="height">The target height</param>
/// <param name="isHighQuality">Shoule the resize be done at high quality?</param>
/// <returns>The resized Bitmap</returns>
public static Bitmap Resize(Bitmap srcBitmap, int width, int height, bool isHighQuality)
{
// Create the destination Bitmap, and set its resolution
Bitmap destBitmap = new Bitmap((int)Convert.ToInt32(width), (int)Convert.ToInt32(height), PixelFormat.Format24bppRgb);
destBitmap.SetResolution(srcBitmap.HorizontalResolution, srcBitmap.VerticalResolution);
// Create a Graphics object from the destination Bitmap, and set the quality
Graphics grPhoto = Graphics.FromImage(destBitmap);
if (isHighQuality)
{
grPhoto.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;
}
else
{
grPhoto.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; //? this doesn't work
grPhoto.InterpolationMode = InterpolationMode.NearestNeighbor; //? this doesn't work
}
// Do the resize
grPhoto.DrawImage(srcBitmap,
new Rectangle(0, 0, width, height),
new Rectangle(0, 0, srcBitmap.Width, srcBitmap.Height),
GraphicsUnit.Pixel);
grPhoto.Dispose();
return destBitmap;
}
You were on the right track by using InterpolationMode.NearestNeighbor. However, with the default PixelOffsetMode, GDI+ will try and sample at the pixel edges, causing the blending.
To get the scaling without the blending, you also need to use PixelOffsetMode.Half. Change your non-high quality case to:
else
{
grPhoto.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None;
grPhoto.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
}
When you are drawing the image and passing in the source rectangle, pass in only the zoomed-in portion of the original image and draw that to the larger area. When the user zooms in, at some point they will not be able to see the entire image in the viewing area. So, figure out which source region should still be in view and paint only that.
It doesn't look like you're doing anything wrong to me. See the instructions from Microsoft on the InterpolationMode:
http://msdn.microsoft.com/en-us/library/ms533836(VS.85).aspx
Maybe this function is working perfectly, but you're giving it the wrong parameters or displaying the result incorrectly?
Related
Is there a quick way using System.Drawing to quickly enlarge the image canvas of an .png image? (see example below). The caveat is the background might be transparent and I want to keep it transparent.
Edit: Needs to be in ASP .Net CORE
Alternatively, is there a way of putting the image on a white background that is slightly larger than the image?
After a few days of trial and error, I think I found something that works
Image overlayImage = //get your image here from file or url.
xloc = //x coord where to start overlay image.
yloc = //y coord where to start overlay image.
canvasWidth = //width of background canvas
canvasHeight = //height of background canvas
Bitmap baseImage = new Bitmap(canvasWidth, canvasHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics graphics = Graphics.FromImage(baseImage))
{
using (System.Drawing.SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.White))
{
graphics.FillRectangle(myBrush, new Rectangle(0, 0, canvasWidth, canvasHeight)); // white rectangle
}
graphics.CompositingMode = CompositingMode.SourceOver;
graphics.DrawImage(overlayImage, xloc, yloc);
} // graphics will be disposed at this line
I have created an image on a Canvas which is scaled down for display using a transformation. It is also in a ScrollPane which means only a part of the image is visible.
I need to take a snapshot of the entire canvas and save this as a high-resolution image. When I use Canvas.snapshot I get a Writable image of the visible part of the image after scaling down. This results in a low-res partial image being saved.
So how do I go about creating a snapshot which includes the entire canvas (not only the viewport of the scrollpane) and with the resolution before the transformation downwards?
I am not doing anything fancy currently, just this:
public WritableImage getPackageCanvasSnapshot()
{
SnapshotParameters param = new SnapshotParameters();
param.setDepthBuffer(true);
return packageCanvas.snapshot(param, null);
}
I did the following to get a canvas snapshot on a Retina display with a pixelScaleFactor of 2.0. It worked for me.
public static WritableImage pixelScaleAwareCanvasSnapshot(Canvas canvas, double pixelScale) {
WritableImage writableImage = new WritableImage((int)Math.rint(pixelScale*canvas.getWidth()), (int)Math.rint(pixelScale*canvas.getHeight()));
SnapshotParameters spa = new SnapshotParameters();
spa.setTransform(Transform.scale(pixelScale, pixelScale));
return canvas.snapshot(spa, writableImage);
}
Working on allowing the upload of images which can range in a variety of size, then allowing to crop a predefined area of the image for a thumbnail.
The thumbnail size is predefined to 150x150. Using the Jcrop.js tool to select a section of the image.
Problem:
When displaying the uploaded image in a smaller size than the original image by implementing set height/width on the image rendered, then there is a scale factor that comes into play when selecting an area to crop.
You either have to scale down the cropping area proportionately or you have to scale the image in relation to the actual image's size in comparison to its displayed size.
Question:
How do I figure out the scale of the browser displayed image vs. original image? I am currently using the following code to save the image, but how would I take into consideration the scaling?
public static Image CropImage(Image originalImage, int x, int y, int width, int height)
{
var bmp = new Bitmap(width, height);
bmp.SetResolution(originalImage.HorizontalResolution, originalImage.VerticalResolution);
using (var graphic = Graphics.FromImage(bmp))
{
graphic.SmoothingMode = SmoothingMode.AntiAlias;
graphic.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphic.PixelOffsetMode = PixelOffsetMode.HighSpeed;
graphic.DrawImage(originalImage, new Rectangle(0, 0, width, height), x, y, width, height, GraphicsUnit.Pixel);
return bmp;
}
}
Bonus Question:
Another problem I discovered, is that there seems to be no efficient way to transfer the original file's ImageFormat when creating a new Bitmap which creates a ImageFormatMemoryBMP and when you attempt to call Bitmap.Save(memorystream, original rawformat) it will blow up. And bitmap RawFormat has no setter.
So how can you set the format on a new bitmap?
I think perhaps that this problem is solved purely on the front end, no need to use any server side for this.
Jcrop has a built in scale factor handler.
http://deepliquid.com/content/Jcrop_Sizing_Issues.html
Now you can use this in two ways, as I understand it. Either to 'resize' the image for you on the front end using 'box sizing', or you can tell it the 'truesize' of the image and it will work out the scale factor and handle the coordinates for you on it's own.
Box sizing
$('#cropbox').Jcrop({ boxWidth: 450, boxHeight: 400 });
True Size
$.Jcrop('#cropbox',{ trueSize: [500,370] });
Using the true size method you will need to invoke jcrop using the api method:
http://deepliquid.com/content/Jcrop_API.html#API_Invocation_Method
var jcrop_api,
options = { trueSize: [500,370] };
$('#target').Jcrop(options,function(){
jcrop_api = this;
});
Good luck!
I need to do something similar to QPainter::drawImage, but drawing a triangle part of the given picture (into a triangular region of my widget) instead of working with rectangles.
Any idea how I could do that, besides painfully trying to redraw every pixel?
Thanks for your insights!
If it is feasible for you to use a QPixmap instead of a QImage, you can set a bitmap mask for the QPixmap which defines which of the pixels are shown and which are transparent:
myPixmap->setMask(myTriangleMask);
painter->drawPixmap(myPixmap);
Here is another solution based on QImage:
MaskWidget::MaskWidget(QWidget* parent) : QWidget(parent) {
img = QImage("Sample.jpg"); // The image to paint
mask = QImage("Mask.png"); // An indexed 2-bit colormap image
QPainter imgPainter(&img);
imgPainter.drawImage(0, 0, mask); // Paint the mask onto the image
}
void MaskWidget::paintEvent ( QPaintEvent * event ) {
QPainter painter(this);
painter.drawImage(10, 10, img);
}
Mask.png is an image file with the same size as Sample.jpg. It contains an alpha channel to support transparency. You can create this file easily with The GIMP, for example. I added an alpha channel, changed all areas I want to have painted to transparent and all other areas to white. To reduce the size, I finally converted it to an indexed 2-bit image.
You could even create the mask image programmatically with Qt, if you need your triangle be computed based on various parameters.
I'm working on a game using PlayN, and there's a bit where I would like to take an image (which is currently in PNG format, with 8-bit alpha already) and I would like to multiply the image by an additional alpha factor, based on a value from my code.
Specifically, I have a picture of a face that currently lives in an ImageLayer, and the effect that I would like would be to have something like this:
void init() {
faceImage = assetManager().getImage("images/face.png");
graphics().rootLayer().add(faceImage);
}
void update(float deltaMilliseconds) {
// start at fully transparent, fade to fully opaque
float transparency = calcTransparency(deltaMilliseconds);
faceImage.setTransparency(transparency);
}
I expect that there's some way to do some trickiness with GroupLayers and blend modes, perhaps blending the image with a CanvasLayer painted with a solid white rectangle with transparency controlled by my code, but it's not obvious to me if that's the best way to achieve what seems like a pretty common effect.
If you just want to fade the image in from fully-transparent to fully-opaque, then just do the following:
ImageLayer faceLayer;
void init() {
Image faceImage = assetManager().getImage("images/face.png");
faceLayer = graphics().createImageLayer(faceImage);
graphics().rootLayer().add(faceLayer);
}
void update(float delta) {
float alpha = calcAlpha(delta);
faceLayer.setAlpha(alpha);
}
Where alpha ranges from 0 (fully transparent) to 1 (fully opaque).