Drawing on image and saving - image

I guess I don't really understand how Graphics objects work in Visual C++ 2010 Express.
I am grabbing a frame from a webcam, and drawing a circle on it. It works great on the screen. I simply create a Graphics object, draw the image, and draw the ellipse.
In the pictureBox_paint function, I have
Graphics^ g = e->Graphics; // from the camera
System::Drawing::Rectangle destRect = System::Drawing::Rectangle(0,0,pbCameraMonitor->Size.Width,pbCameraMonitor->Size.Height);
double slitHeightToWidth = 3;
g->DrawImage(this->currentCamImage,destRect);
int circleX, circleY;
circleX = (int) (pbCameraMonitor->Size.Width - radius/slitHeightToWidth)/2;
circleY = (int) (pbCameraMonitor->Size.Height - radius)/2;
g->DrawEllipse(Pens::Red, circleX, circleY, (int) radius/slitHeightToWidth, (int) radius);
So far so good, my ellipse gets drawn on there nicely. The destRect bit makes sure it is scaled to the pictureBox size. I simply invalidate the pictureBox every time the camera reports a new image, and I have video.
Now, on a button click I want to save this image, with the red ellipse on it. However, I don't want the rescaled version shown on the screen, I want the full res version. So, I'll grab another frame into a Bitmap^ called "grabbedFrame" and do this:
String ^photofile = "Image_" + expRecord.timestamp.ToString("s") + ".jpg"; // get a unique filename
photofile = photofile->Replace(':', '_');
Graphics^ g = Graphics::FromImage(grabbedFrame);
g->DrawEllipse(Pens::Red, 20, 20, 20, 20); // circle size fixed just for demo
grabbedFrame->Save(photofile, System::Drawing::Imaging::ImageFormat::Jpeg);
When I do that, I get a save of the image without the red circle.
Does g->DrawEllipse actually modify the Bitmap? Or just contain the Bitmap + instructions to draw? If the latter, how does the pictureBox know the Bitmap has been modified? If the former, why doesn't my save contain the modification?
How can I save the modified Bitmap?

You need to draw the loaded image into a new Bitmap, make your modifications to that bitmap, and then save it.
Something like (pseudo-code'ish):
// create bitmap and get its graphics
Bitmap^ pBmp = gcnew Bitmap(grabbedFrame->Width, grabbedFrame->Height);
Graphics^ g = Graphics::FromImage(pBmp);
// draw grabbed frame into bitmap
g->DrawImage(grabbedFrame, 0, 0, grabbedFrame->Width, grabbedFrame->Height);
// draw other stuff
g->DrawEllipse(Pens::Red, 20, 20, 20, 20);
// save the result
pBmp->Save(photofile, System::Drawing::Imaging::ImageFormat::Jpeg);

Related

iText 7 - How to fill a canvas rectangle with a transparent color

In iText 7.1.9 I am taking a pdf created programmatically (not via iText) and need to apply a transparent rectangle along the left side and bottom to ensure the no content exists within a predefined clear zone (for print).
The below code places the yellow rectangles correctly but the desired result is the for the yellow fill to be semi-transparent or not 100% opaque so that visual inspection will show the content that that intersects with the rectangle instead of the rectangle clipping the content.
var page = pdf.GetPage(1);
PdfCanvas canvas = new PdfCanvas(page);
canvas.SaveState();
canvas.SetFillColor(iText.Kernel.Colors.ColorConstants.YELLOW);
var pageHeight = page.GetPageSize().GetHeight();
var pageWidth = page.GetPageSize().GetWidth();
// left side
canvas.Rectangle(0, 0, 15, pageHeight);
// bottom
canvas.Rectangle(0, 0, pageWidth, 15);
canvas.Fill();
canvas.RestoreState();
I attempted to use a TransparentColor but canvas.SetFillColor won't accept a TransparentColor, are there any other options?
When we speak about low-level content stream instructions, color itself and transparency levels are specified separately in PDF syntax. The TransparentColor class that you speak about was designed to simplify lives of users who are less familiar with nuances of PDF syntax, but it it a higher-level class that you can use e.g. in layout module, and in your case you operate with the document on quite low level.
Long story short, to set color transparency you only need one additional line next to setting the color itself:
canvas.SetExtGState(new PdfExtGState().SetFillOpacity(0.5f));
So the code becomes:
var page = pdf.GetPage(1);
PdfCanvas canvas = new PdfCanvas(page);
canvas.SaveState();
canvas.SetFillColor(iText.Kernel.Colors.ColorConstants.YELLOW);
canvas.SetExtGState(new PdfExtGState().SetFillOpacity(0.5f));
var pageHeight = page.GetPageSize().GetHeight();
var pageWidth = page.GetPageSize().GetWidth();
// left side
canvas.Rectangle(0, 0, 15, pageHeight);
// bottom
canvas.Rectangle(0, 0, pageWidth, 15);
canvas.Fill();
canvas.RestoreState();

How can I get rid of artifacts in ImageSource created with SkiaSharp

I created an app in which I want to display text on top of google maps. I chose to use custom markers, but they can only be images, so I decided to create an image from my text utilizing SkiaSharp.
private static ImageSource CreateImageSource(string text)
{
int numberSize = 20;
int margin = 5;
SKBitmap bitmap = new SKBitmap(30, numberSize + margin * 2, SKImageInfo.PlatformColorType, SKAlphaType.Premul);
SKCanvas canvas = new SKCanvas(bitmap);
SKPaint paint = new SKPaint
{
Style = SKPaintStyle.StrokeAndFill,
TextSize = numberSize,
Color = SKColors.Red,
StrokeWidth = 1,
};
canvas.DrawText(text.ToString(), 0, numberSize, paint);
SKImage skImage = SKImage.FromBitmap(bitmap);
SKData data = skImage.Encode(SKEncodedImageFormat.Png, 100);
return ImageSource.FromStream(data.AsStream);
}
The images I create however have ugly artifacts on the top of the resulting image and my feeling is that they get worse if I create multiple images.
I built an example app, that shows the artifacts and the code I used to draw the text. It can be found here:
https://github.com/hot33331/SkiaSharpExample
How can I get rid of those artifacts. Am I using skia wrong?
I got the following answer from Matthew Leibowitz on the SkiaSharp GitHub:
The chances are you are not clearing the canvas/bitmap first.
You can either do bitmap.Erase(SKColors.Transparent) or canvas.Clear(SKColors.Transparent) (you can use any color).
The reason for this is performance. When creating a new bitmap, the computer has no way of knowing what background color you want. So, if it was to go transparent and you wanted white, then there would be two draw operations to clear the pixels (and this may be very expensive for large images).
During the allocation of the bitmap, the memory is provided, but the actual data is untouched. If there was anything there previously (which there will be), this data appears as colored pixels.
When I've seen that before, it's been because the memory passed to SkiaSharp was not zeroed. As an optimization, though, Skia assumes that the memory block passed to it is pre zeroed. Resultingly, if your first operation is a clear, it will ignore that operation, because it thinks that the state is already clean. To resolve this issue, you can manually zero the memory passed to SkiaSharp.
public static SKSurface CreateSurface(int width, int height)
{
// create a block of unmanaged native memory for use as the Skia bitmap buffer.
// unfortunately, this may not be zeroed in some circumstances.
IntPtr buff = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(width * height * 4);
byte[] empty = new byte[width * height * 4];
// copy in zeroed memory.
// maybe there's a more sanctioned way to do this.
System.Runtime.InteropServices.Marshal.Copy(empty, 0, buff, width * height * 4);
// create the actual SkiaSharp surface.
var colorSpace = CGColorSpace.CreateDeviceRGB();
var bContext = new CGBitmapContext(buff, width, height, 8, width * 4, colorSpace, (CGImageAlphaInfo)bitmapInfo);
var surface = SKSurface.Create(width, height, SKColorType.Rgba8888, SKAlphaType.Premul, bitmap.Data, width * 4);
return surface;
}
Edit: btw, I assume this is a bug in SkiaSharp. The samples/apis that create the buffer for you should probably be zeroing it out. Depending on the platform it can be hard to repro as the memory alloc behaves differently. More or less likely to provide you untouched memory.

Is it possible to save a generated image in Codename One?

My question is related to this previous question. What I want to achieve is to stack images (they have transparency), write a string on top, and save the photomontage / photocollage with full resolution.
#Override
protected void beforeMain(Form f) {
Image photoBase = fetchResourceFile().getImage("Voiture_4_3.jpg");
Image watermark = fetchResourceFile().getImage("Watermark.png");
f.setLayout(new LayeredLayout());
final Label drawing = new Label();
f.addComponent(drawing);
// Image mutable dans laquelle on va dessiner (fond blanc)
Image mutableImage = Image.createImage(photoBase.getWidth(), photoBase.getHeight());
drawing.getUnselectedStyle().setBgImage(mutableImage);
drawing.getUnselectedStyle().setBackgroundType(Style.BACKGROUND_IMAGE_SCALED_FIT);
// Paint all the stuff
paints(mutableImage.getGraphics(), photoBase, watermark, photoBase.getWidth(), photoBase.getHeight());
// Save the collage
Image screenshot = Image.createImage(photoBase.getWidth(), photoBase.getHeight());
f.revalidate();
f.setVisible(true);
drawing.paintComponent(screenshot.getGraphics(), true);
String imageFile = FileSystemStorage.getInstance().getAppHomePath() + "screenshot.png";
try(OutputStream os = FileSystemStorage.getInstance().openOutputStream(imageFile)) {
ImageIO.getImageIO().save(screenshot, os, ImageIO.FORMAT_PNG, 1);
} catch(IOException err) {
err.printStackTrace();
}
}
public void paints(Graphics g, Image background, Image watermark, int width, int height) {
g.drawImage(background, 0, 0);
g.drawImage(watermark, 0, 0);
g.setColor(0xFF0000);
// Upper left corner
g.fillRect(0, 0, 10, 10);
// Lower right corner
g.setColor(0x00FF00);
g.fillRect(width - 10, height - 10, 10, 10);
g.setColor(0xFF0000);
Font f = Font.createTrueTypeFont("Geometos", "Geometos.ttf").derive(220, Font.STYLE_BOLD);
g.setFont(f);
// Draw a string right below the M from Mercedes on the car windscreen (measured in Gimp)
g.drawString("HelloWorld",
(int) (848 ),
(int) (610)
);
}
This is the saved screenshot I get if I use the Iphone6 skin (the payload image is smaller than the original one and is centered). If I use the Xoom skin this is what I get (the payload image is still smaller than the original image but it has moved to the left).
So to sum it all up : why is the saved screenshot with Xoom skin different from the one I get with Iphone skin ? Is there anyway to directly save the graphics on which I paint in the paints method so that the saved image would have the original dimensions ?
Thanks a lot to anyone that could help me :-)!
Cheers,
You can save an image in Codename one using the ImageIO class. Notice that you can draw a container hierarchy into a mutable image using the paintComponent(Graphics) method.
You can do both approaches with draw image on mutable or via layouts. Personally I always prefer layouts as I like the abstraction but I wouldn't say the mutable image approach is right/wrong.
Notice that if you change/repaint a lot then mutable images are slower (this will not be noticeable for regular code or on the simulator) as they are forced to use the software renderer and can't use the GPU fully.
In the previous question it seems you placed the image with a "FIT" style which naturally drew it smaller than the containing container and then drew the image on top of it manually... This is problematic.
One solution is to draw everything manually but then you will need to do the "fit" aspect of drawing yourself. If you use layouts you should position everything based on the layouts including your drawing/text.

can't get the image to rotate in center in Qt

i am trying to rotate a 89x89 image inside the QLabel Widge.
#include "header.h"
disc::disc(QWidget *Parent) : QWidget(Parent)
{
orig_pixmap = new QPixmap();
rotate = new QLabel(this);
showDegrees = new QLabel(this);
orig_pixmap->load("pic.png");
degrees = 0;
rotate->resize(89,89);
rotate->move(100,10);
rotate->setStyleSheet("QLabel{background-color:red;}");
showDegrees->resize(100,100);
showDegrees->move(400,0);
}
void disc::rotate_disc()
{
QString string;
degrees++;
if(degrees == 360) degrees = 0;
QTransform rotate_disc;
rotate_disc.translate( (orig_pixmap->width()-rotate->width())/2 , (orig_pixmap->width()-rotate->height())/2);
rotate_disc.rotate(degrees,Qt::ZAxis);
QPixmap pixmap;
//pixmap = orig_disc->scaled(89,89,Qt::IgnoreAspectRatio,Qt::SmoothTransformation);
pixmap = orig_pixmap->transformed(rotate_disc,Qt::SmoothTransformation);
rotate->setPixmap(pixmap);
string.append("Degrees: " + QString::number(degrees) + "*");
showDegrees->setText(string);
}
Even though it rotates. The image's half gets rolled outside the QLabel and hence that side is not visible.How can i make it center rotate at origin(0,0) of the image center.
Here is the file
http://www65.zippyshare.com/v/85775455/file.html
if you look at it you can see that image is like bouncing to the left.how can i make it rotate inside the black area.
i setup a signal timeout at every 10ms to the rotate_disc() function. I am using this to learn Qt indepth.
Thank You!
I've done it like this...
//Move windows's coordinate system to center.
QTransform transform_centerOfWindow( 1, 0, 0, 1, width()/2, height()/2 );
//Rotate coordinate system.
transform_centerOfWindow.rotate( m_LoadingDisk_degrees );
//Draw image with offset to center of image.
QPainter painter( this );
//Transform coordinate system.
painter.setTransform( transform_centerOfWindow );
//Load image.
//Notice: As coordinate system is set at center of window, we have to center the image also... so the minus offset to come to center of image.
painter.drawPixmap( -loadingDisk.width()/2, -loadingDisk.height()/2, loadingDisk );
I think all you're really missing is the initial translation to do the rotation around the centre of the pixmap.
Move it to the origin, rotate, then move it back. And remember, transformations are applied in reverse order from how you'd expect, given how you specify them in the code.
QTransform rotate_disc;
rotate_disc.translate(orig_pixmap->width()/2.0 , orig_pixmap->height()/2.0);
rotate_disc.rotate(degrees);
rotate_disc.translate(-orig_pixmap->width()/2.0 , -orig_pixmap->height()/2.0);
If you are making a loading indicator, animated gif would be much easier. See GIF animation in Qt

Resizing and saving an image in WinMobile and .NET CF throws OutOfMemoryException

I have a WinMobile app which allows the user the snap a photo with the camera, and then use for for various things. The photo can be snapped at 1600x1200, 800x600 or 640x480, but it must always be resized to 400px for the longest size (the other is proportional of course). Here's the code:
private void LoadImage(string path)
{
Image tmpPhoto = new Bitmap(path);
// calculate new bitmap size...
double width = ...
double height = ...
// draw new bitmap
Image photo = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(photo))
{
g.FillRectangle(new SolidBrush(Color.White), new Rectangle(0, 0, photo.Width, photo.Height));
int srcX = (int)((double)(tmpPhoto.Width - width) / 2d);
int srcY = (int)((double)(tmpPhoto.Height - height) / 2d);
g.DrawImage(tmpPhoto, new Rectangle(0, 0, photo.Width, photo.Height), new Rectangle(srcX, srcY, photo.Width, photo.Height), GraphicsUnit.Pixel);
}
tmpPhoto.Dispose();
// save new image and dispose
photo.Save(Path.Combine(config.TempPath, config.TempPhotoFileName), System.Drawing.Imaging.ImageFormat.Jpeg);
photo.Dispose();
}
Now the problem is that the app breaks in the photo.Save call, with an OutOfMemoryException. And I don't know why, since I dispose the tempPhoto (with the original photo from the camera) as soon as I can, and I also dispose the Graphics obj. Why does this happen? It seems impossible to me that one can't take a photo with the camera and resize/save it without making it crash :( Should I restor t C++ for such a simple thing?
Thanks.
Have you looked at memory usage with each step to see exactly where you're using the most? You omitted your calculations for width and height, but assuming they are right you would end up with photo requiring 400x300x3 (24bits) == 360k for the bitmap data itself, which is not inordinately large.
My guess is that even though you're calling Dispose, the resources aren't getting rleased, especially if you're calling this method multiple times. The CF behaves in an unexpected way with Bitmaps. I call it a bug. The CF team doesn't.

Resources