Screen Capture on OSX using MonoMac? - macos

Can somebody help me with the following code snippet to capture part of or the whole desktop on OSX ? I would like to specify the upper-left corner coordinates (x,y) and the width (w) and height (h) of the rectangle that defines the capture.
It's for a C# MonoMac application on OSX.
This is what I've done:
int windowNumber = 2;
System.Drawing.RectangleF bounds = new RectangleF(0,146,320,157);
CGImage screenImage = MonoMac.CoreGraphics.CGImage.ScreenImage(windowNumber,bounds);
MonoMac.Foundation.NSData bitmapData = screenImage.DataProvider.CopyData();
It looks like I have the bitmap data in 'bitmapData', but I'm not sure how I convert the NSData instance 'bitmapData' to an actual Bitmap; i.e. :
Bitmap screenCapture = ????
The documentation is really sparse and I've googled for examples without luck. So I'm hoping that there's a kind MonoMac expert out there who can point me in the right direction? - An example would be nice :o)
Thank you in advance!

This will give you the bytes of your capture in a .NET byte[], from where you can create a Bitmap or Image or whatever you want. Might not be exactly what you are looking for but should put you in the right direction.
int windowNumber = 2; System.Drawing.RectangleF bounds = new RectangleF(0,146,320,157);
CGImage screenImage = MonoMac.CoreGraphics.CGImage.ScreenImage(windowNumber,bounds);
using(NSBitmapImageRep imageRep = new NSBitmapImageRep(screenImage))
{
NSDictionary properties = NSDictionary.FromObjectAndKey(new NSNumber(1.0), new NSString("NSImageCompressionFactor"));
using(NSData tiffData = imageRep.RepresentationUsingTypeProperties(NSBitmapImageFileType.Png, properties))
{
byte[] imageBytes;
using(var ms = new MemoryStream())
{
tiffData.AsStream().CopyTo(ms);
imageBytes = ms.ToArray();
}
}
}

Related

Save high quality images from Autocad

my problem is that I don´t have much experience with Autocad. Thus I don´t know how to save a project in a good quality image (png?) to insert in a latex document.
Could you give me a hint?
Thank you
Autocad's Publish to Web printers are pretty bad. What I would do is print using DWG to PDF printer or similar (there are a few in autocad's default printer list) then convert that pdf to raster images using a second software like Photoshop, GIMP, etc. There are even small software that convert pdf's to jpgs like TTRPDFToJPG3. If you have a specific idea of what kind of output you're looking for, please feel free to elaborate further. cheers!
If you're looking for a programmatic way to capture the screen, here it is:
using acApp = Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using System.Drawing.Imaging;
using System.Drawing;
namespace ScreenshotTest
{
public class Commands
{
[CommandMethod("CSS")]
static public void CaptureScreenShot()
{
ScreenShotToFile(
acApp.Application.MainWindow,
"c:\\main-window.png",
0, 0, 0, 0
);
ScreenShotToFile(
acApp.Application.DocumentManager.MdiActiveDocument.Window,
"c:\\doc-window.png",
30, 26, 10, 10
);
}
private static void ScreenShotToFile(
Autodesk.AutoCAD.Windows.Window wd,
string filename,
int top, int bottom, int left, int right
)
{
Point pt = wd.Location;
Size sz = wd.Size;
pt.X += left;
pt.Y += top;
sz.Height -= top + bottom;
sz.Width -= left + right;
// Set the bitmap object to the size of the screen
Bitmap bmp =
new Bitmap(
sz.Width,
sz.Height,
PixelFormat.Format32bppArgb
);
using (bmp)
{
// Create a graphics object from the bitmap
using (Graphics gfx = Graphics.FromImage(bmp))
{
// Take a screenshot of our window
gfx.CopyFromScreen(
pt.X, pt.Y, 0,0, sz,
CopyPixelOperation.SourceCopy
);
// Save the screenshot to the specified location
bmp.Save(filename, ImageFormat.Png);
}
}
}
}
}
Source: Taking screenshots of AutoCAD’s main and drawing windows using .NET
Thanks to everyone. I am saving the files in pdf and after I´m using GIMP to convert them in PNG.

NSBitmapImageRep.bitmapImageRepByConvertingToColorSpace() calling deprecated CGContextClear

I'm trying to use
NSBitmapImageRep.bitmapImageRepByConvertingToColorSpace(NSColorSpace.genericRGBColorSpace(), renderingIntent: NSColorRenderingIntent.Perceptual);
to convert an NSImage to a format that can be handled by openGL, and it works (unlike bitmapImageRepByRetaggingWithColorSpace()), but I get an error:
<Error>: The function ‘CGContextClear’ is obsolete and will be removed in an upcoming update. Unfortunately, this application, or a library it uses, is using this obsolete function, and is thereby contributing to an overall degradation of system performance.
which is sort of useless, because it's Apple's own code, and I can't seem to find an alternative to bitmapImageRepByConvertingToColorSpace(), or any note that it too is deprecated.
EDIT:
var image=NSImage(size: frame);
image.lockFocus();
//println("NSImage: \(image.representations)");
string.drawAtPoint(NSMakePoint(0.0,0.0), withAttributes: attribs);
var bitmap2=NSBitmapImageRep(focusedViewRect: NSMakeRect(0.0,0.0,frame.width,frame.height))?;
image.unlockFocus();
bitmap=bitmap2!.bitmapImageRepByConvertingToColorSpace(NSColorSpace.genericRGBColorSpace(), renderingIntent: NSColorRenderingIntent.Perceptual);
For some reason this question interested me. It looks like the solution is to lock focus on your NSImage, then use NSBitmapImageRep(focusedViewRect:) to create the NSBitmapImageRep.
I tried to recreate your situation (as I understand it) and then create an NSBitmapImageRep with the following code:
var img:NSImage = NSImage(size: NSMakeSize(200,200))
img.lockFocus()
let testString:NSString = "test String"
testString.drawAtPoint(NSMakePoint(10,10), withAttributes: nil)
img.unlockFocus()
println("img Description = \(img.description)")
I got the following description:
img Description = NSImage 0x608000067ac0 Size={200, 200} Reps=(
"NSCGImageSnapshotRep:0x61000007cf40 cgImage=CGImage 0x6100001a2bc0")
I then extended this code to:
var img:NSImage = NSImage(size: NSMakeSize(200,200))
img.lockFocus()
let testString:NSString = "test String"
testString.drawAtPoint(NSMakePoint(10,10), withAttributes: nil)
img.unlockFocus()
println("img Description = \(img.description)")
img.lockFocus()
var bitmapRep:NSBitmapImageRep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0.0, 0.0, img.size.width, img.size.height))!
img.unlockFocus()
println("bitmap data planes = \(bitmapRep.bitmapData)")
println("bitmap pixels wide = \(bitmapRep.size.width)")
println("bitmap pixels high = \(bitmapRep.size.height)")
println("bits per sample = \(bitmapRep.bitsPerSample)")
println("samples per pixel = \(bitmapRep.samplesPerPixel)")
println("has alpha = \(bitmapRep.alpha)")
println("is planar = \(bitmapRep.planar)")
println("color space name = \(bitmapRep.colorSpace)")
println("bitmap format = \(bitmapRep.bitmapFormat)")
println("bytes per row = \(bitmapRep.bytesPerRow)")
println("bits per pixel = \(bitmapRep.bitsPerPixel)")
The output for the NSBitmapImageRep parameters was:
bitmap data planes = 0x000000010b6ff100
bitmap pixels wide = 200.0
bitmap pixels high = 200.0
bits per sample = 8
samples per pixel = 4
has alpha = true
is planar = false
color space name = Color LCD colorspace
bitmap format = C.NSBitmapFormat
bytes per row = 800
bits per pixel = 32
I've posted some new code below. I've created two NSBitMapImageRep just like your code (except that I used the method bitmapImageRepByRetaggingWithColorSpace), and I exported both of them to a PNG file. I got the same image in both PNG files.
But I'm wondering a couple of things. First, the difference in your two tests was not only the OS X version, but the hardware produced different color spaces. You should check to be sure bitmap2 is not nil.
Second, I'm wondering if OpenGL cares. If bitmap2 is not nil and you pass OpenGL bitmap2.bitmapData, does it work?
My new code (deleting most of the println):
var img:NSImage = NSImage(size: NSMakeSize(200,200))
img.lockFocus()
let testString:NSString = "test String"
let font:NSFont = NSFont(name: "AppleCasual", size: 18.0)!
let textStyle = NSMutableParagraphStyle.defaultParagraphStyle().mutableCopy() as NSMutableParagraphStyle
textStyle.alignment = NSTextAlignment.LeftTextAlignment
let textColor:NSColor = NSColor(calibratedRed: 1.0, green: 0.0, blue: 1.0, alpha: 1.0)
let attribs:NSDictionary = [NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: textStyle]
testString.drawAtPoint(NSMakePoint(0.0, 0.0), withAttributes: attribs)
img.unlockFocus()
println("img Description = \(img.description)")
img.lockFocus()
var bitmapRep:NSBitmapImageRep = NSBitmapImageRep(focusedViewRect: NSMakeRect(0.0, 0.0, img.size.width, img.size.height))!
img.unlockFocus()
let pngPath:String = "TestStringBeforeChange.png"
let imageProps = [NSImageCompressionFactor: NSNumber(float: 1.0)]
let outputImageData = bitmapRep.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: imageProps)
let fileSavePanel = NSSavePanel()
fileSavePanel.nameFieldStringValue = pngPath
var savePanelReturn = fileSavePanel.runModal()
if savePanelReturn == NSFileHandlingPanelOKButton
{
var theFileURL = fileSavePanel.URL
var saveStatus = outputImageData?.writeToURL(theFileURL!, atomically: true)
}
let bitmapRep2 = bitmapRep.bitmapImageRepByRetaggingWithColorSpace(NSColorSpace.genericRGBColorSpace())
let pngPath2:String = "TestStringAfterChange.png"
let outputImageData2 = bitmapRep2!.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: imageProps)
let fileSavePanel2 = NSSavePanel()
fileSavePanel2.nameFieldStringValue = pngPath2
var savePanel2Return = fileSavePanel2.runModal()
if savePanel2Return == NSFileHandlingPanelOKButton
{
var theFileURL2 = fileSavePanel2.URL
var saveStatus2 = outputImageData2?.writeToURL(theFileURL2!, atomically: true)
}
Sorry for all the posts, but I don't want to let this go, and it's interesting.
I created 3 PNG files: the first using the original NSBitmapImageRep, a second using an NSBitmapImageRep created with bitmapImageRepByRetaggingWithColorSpace and a third using an NSBitmapImageRep created with bitmapImageRepByConvertingToColorSpace (I, of course, got the system warning when I used this function).
Looking at these three PNG files in Preview's Inspector, they were all RGB color models; only the colorSynch profile for the first file was different from that of the second and third file. So I thought maybe there's no difference in the bitmapData among these files.
I then wrote some code to compare the bitmapData:
var firstRepPointer:UnsafeMutablePointer<UInt8> = bitmapRep.bitmapData
var secondRepPointer:UnsafeMutablePointer<UInt8> = bitmapRep2!.bitmapData
var firstByte:UInt8 = 0
var secondByte:UInt8 = 0
for var i:Int = 0; i < 200; i++
{
for var j:Int = 0; j < 200; j++
{
for var k:Int = 0; k < 4; k++
{
memcpy(&firstByte, firstRepPointer, 1)
firstRepPointer += 1
memcpy(&secondByte, secondRepPointer, 1)
secondRepPointer += 1
if firstByte != secondByte {println("firstByte = \(firstByte) secondByte = \(secondByte)")}
}
}
}
As I expected, there were no differences when I compared the bitmapData from the original bitmap rep to the bitmapData from the bitmap rep created with bitmapImageRepByRetaggingWithColorSpace.
It got more interesting, however when I compared the bitmapData from the original bitmap rep to the data from the bitmap rep created with bitmapImageRepByConvertingToColorSpace.
There were lots of differences in the data. Small differences, but lots of them.
Even though there were no differences I could see in the PNG files, the underlying data had been changed by bitmapImageRepByConvertingToColorSpace.
All that said, I think you should be able to pass .bitmapData from any of these bitmap reps to an OpenGL texture. The data from the bitmap rep created with bitmapImageRepByConvertingToColorSpace might create a texture that looks different, but it should be valid data.

How do you resize an image using im4java?

I'm trying to resize an image using im4java. I haven't found any working examples and their JavaDocs are incomplete.
OOTB Java solutions are insufficient and lead to poor quality in the resulting images, despite rendering hint adjustments.
First of all, im4java is an interface for imagemagick and/or graphicsmagick, so you need to install one of them on your computer to get im4java to work.
Here is the code to resize an image:
ConvertCmd cmd = new ConvertCmd();
IMOperation op = new IMOperation();
op.addImage("original_image.jpg");
op.resize(800,600);
op.addImage("resized_image.jpg");
cmd.run(op);
Do you really need this im4java ? I don't know it, but I use to do a pure java transformation :
public BufferedImage scale(final BufferedImage image, final int targetW, final int targetH)
{
final int type = image.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : image.getType();
final BufferedImage scaledImg = new BufferedImage(targetW, targetH, type);
final Graphics2D g = scaledImg.createGraphics();
g.drawImage(image, 0, 0, targetW, targetH, null);
g.dispose();
g.setComposite(AlphaComposite.Src);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
return scaledImg;
}
this code has been working for me for more than 2 years in my project. Let me know if you have any question.

Making a JLabel become another JLabel

So, here is my problem. I have a JPanel, with a JLabel on it, and what I want is once that JLabel is clicked, it should resize, (ideally it will change with it's scale, but for now I am using a constant value). I have returned the image, and I now that I am able to scale it, but I just cant manage to make the original JLabel become the newly sized one.
So this is what should happen ideally, e.getSource should become the newly increased in size JLabel.
I know I'm pretty close, I did a JOptionPane as a debug statement to see if I can increase the size, and I can.
Why cant ((JLabel)me.getSource = a;
where a is my new JLabel?
Anyways, here is my code:
Please help me out.
public void mousePressed(MouseEvent me) {
//GreetingCard.setBackground.findComponentAt(me.getX(), me.getY());
//GreetingCard.setBackground.findComponentAt(me.getX(), me.getY)
JLabel a= (JLabel) me.getSource();
Icon icon = a.getIcon();
int scale = 4;
BufferedImage bi = new BufferedImage(
scale*icon.getIconWidth(),
scale*icon.getIconHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = bi.createGraphics();
g.scale(scale,scale);
icon.paintIcon(null,g,0,0);
g.dispose();
JLabel temp = new JLabel(new ImageIcon(bi));
((JLabel)me.getSource())= temp;
JOptionPane.showMessageDialog(
null,
new JLabel(new ImageIcon(bi)));
System.out.println("The size of the image is" + b.getIconWidth());
initiateEvent = me;
me.consume();
}
me.getSource() returns a JLabel, it doesn´t return a variable you can assign a new value.
Probably the best way is not to create a new JLabel but just assign the new ImageIcon to the old JLabel.
((JLabel)me.getSource()).setIcon(new ImageIcon(bi));
You also need to call label or panel updateUI() to force a "hard" repaint with resizing.

Rendering smallest possible image size with MVC3 vs Webforms Library

I am in the process of moving a webforms app to MVC3. Ironically enough, everything is cool beans except one thing - images are served from a handler, specifically the Microsoft Generated Image Handler. It works really well - on average a 450kb photo gets output at roughly 20kb.
The actual photo on disk weighs in at 417kb, so i am getting a great reduction.
Moving over to MVC3 i would like to drop the handler and use a controller action. However i seem to be unable to achieve the same kind of file size reduction when rendering the image. I walked through the source and took an exact copy of their image transform code yet i am only achieving 230~kb, which is still a lot bigger than what the ms handler is outputting - 16kb.
You can see an example of both the controller and the handler here
I have walked through the handler source code and cannot see anything that is compressing the image further. If you examine both images you can see a difference - the handler rendered image is less clear, more grainy looking, but still what i would consider satisfactory for my needs.
Can anyone give me any pointers here? is output compression somehow being used? or am i overlooking something very obvious?
The code below is used in my home controller to render the image, and is an exact copy of the FitImage method in the Image Transform class that the handler uses ...
public ActionResult MvcImage()
{
var file = Server.MapPath("~/Content/test.jpg");
var img = System.Drawing.Image.FromFile(file);
var sizedImg = MsScale(img);
var newFile = Server.MapPath("~/App_Data/test.jpg");
if (System.IO.File.Exists(newFile))
{
System.IO.File.Delete(newFile);
}
sizedImg.Save(newFile);
return File(newFile, "image/jpeg");
}
private Image MsScale(Image img)
{
var scaled_height = 267;
var scaled_width = 400;
int resizeWidth = 400;
int resizeHeight = 267;
if (img.Height == 0)
{
resizeWidth = img.Width;
resizeHeight = scaled_height;
}
else if (img.Width == 0)
{
resizeWidth = scaled_width;
resizeHeight = img.Height;
}
else
{
if (((float)img.Width / (float)img.Width < img.Height / (float)img.Height))
{
resizeWidth = img.Width;
resizeHeight = scaled_height;
}
else
{
resizeWidth = scaled_width;
resizeHeight = img.Height;
}
}
Bitmap newimage = new Bitmap(resizeWidth, resizeHeight);
Graphics gra = Graphics.FromImage(newimage);
SetupGraphics(gra);
gra.DrawImage(img, 0, 0, resizeWidth, resizeHeight);
return newimage;
}
private void SetupGraphics(Graphics graphics)
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighSpeed;
graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphics.SmoothingMode = SmoothingMode.HighSpeed;
}
If you don't set the quality on the encoder, it uses 100 by default. You'll never get a good size reduction by using 100 due to the way image formats like JPEG work. I've got a VB.net code example of how to set the quality parameter that you should be able to adapt.
80L here is the quality setting. 80 still gives you a fairly high quality image, but at DRASTIC size reduction over 100.
Dim graphic As System.Drawing.Graphics = System.Drawing.Graphics.FromImage(newImage)
graphic.InterpolationMode = Drawing.Drawing2D.InterpolationMode.HighQualityBicubic
graphic.SmoothingMode = Drawing.Drawing2D.SmoothingMode.HighQuality
graphic.PixelOffsetMode = Drawing.Drawing2D.PixelOffsetMode.HighQuality
graphic.CompositingQuality = Drawing.Drawing2D.CompositingQuality.HighQuality
graphic.DrawImage(sourceImage, 0, 0, width, height)
' now encode and send the new image
' This is the important part
Dim info() As Drawing.Imaging.ImageCodecInfo = Drawing.Imaging.ImageCodecInfo.GetImageEncoders()
Dim encoderParameters As New Drawing.Imaging.EncoderParameters(1)
encoderParameters.Param(0) = New Drawing.Imaging.EncoderParameter(Drawing.Imaging.Encoder.Quality, 80L)
ms = New System.IO.MemoryStream
newImage.Save(ms, info(1), encoderParameters)
When you save or otherwise write the image after setting the encoder parameters, it'll output it using the JPEG encoder (in this case) set to quality 80. That will get you the size savings you're looking for.
I believe it's defaulting to PNG format also, although Tridus' solution solves that also.
However, I highly suggest using this MVC-friendly library instead, as it avoids all the image resizing pitfalls and doesn't leak memory. It's very lightweight, free, and fully supported.

Resources