I want to create a screen magnifier in RealBasic but don't see any classes or APIs for reading areas of the screen that I can then render to my window.
Anything?
Side-question: If I can't read entire areas, can I at least do per-pixel reading to simulate an eye-dropper tool that reads the pixel's color under the cursor?
There are several ways that both a magnifier and a eye-dropper can be made with Realbasic (shameless plug: I wrote an eyedropper in RealBasic a while ago.) It's very simple, just call the System.Pixel function using System.MouseX and System.MouseY as its parameters. System.Pixel returns a Color corresponding to the color of the pixel at the screen coordinates you specify.
With this color information you can (obviously) show the color at a larger scale by drawing to a Picture object or a Canvas control (as with an eyedropper.)
This method can be used for something like a magnifier, but probably shouldn't be. Drawing pixel-by-pixel in RealBasic can be quite slow, which with realtime tasks like a magnifier will lead to performance issues and flickering.
Under Windows, and likely under Mac OS X and GTK+, there are API functions which allow you to capture an area of the screen, which is useful for screenshots, and for manipulating bitmap images using a number of standard algorithms.
Here is a simple function which calls the Windows API to capture an 800x600 portion of the screen, magnify it by 3, and copy it into a Picture object:
Function GetZoomedPic() As Picture
Declare Function GetDesktopWindow Lib "User32" () As Integer
Declare Function GetDC Lib "User32" (HWND As Integer) As Integer
Declare Function StretchBlt Lib "GDI32" (destDC As Integer, destX As Integer, destY As Integer, destWidth As Integer, destHeight As Integer, _
sourceDC As Integer, sourceX As Integer, sourceY As Integer, sourceWidth As Integer, sourceHeight As Integer, rasterOp As Integer) As Boolean
Declare Function ReleaseDC Lib "User32" (HWND As Integer, DC As Integer) As Integer
Const CAPTUREBLT = &h40000000
Const SRCCOPY = &HCC0020
Dim coordx, coordy As Integer
Dim magnifyLvl As Integer = 3
Dim screenCap As New Picture(800, 600, 32)
coordx = System.MouseX - (screenCap.Width \ (magnifyLvl * 2))
coordy = System.Mousey - (screenCap.Height \ (magnifyLvl * 2))
Dim rectWidth, rectHeight As Integer
rectWidth = screenCap.Width \ magnifyLvl
rectHeight = screenCap.Height \ magnifyLvl
Dim deskHWND As Integer = GetDesktopWindow()
Dim deskHDC As Integer = GetDC(deskHWND)
Call StretchBlt(screenCap.Graphics.Handle(1), 0, 0, screenCap.Width, screenCap.Height, DeskHDC, coordx, coordy, rectWidth, _
rectHeight, SRCCOPY Or CAPTUREBLT)
Call ReleaseDC(DeskHWND, deskHDC)
Return screenCap
End Function
Around the same time I wrote that Eyedropper, I also wrote a basic magnifier project. You can download the project file here. In addition to demonstrating the above function, it can also serve as a basic demonstration of drawing to a Canvas without flickering, of using a RealBasic Picture object with Windows GDI Device Contexts and the use of threads to offload work from the main thread.
Take a look at this thread on the Real Software forums on this very topic. Looks like there are a number of solutions you can try.
http://forums.realsoftware.com/viewtopic.php?f=10&t=7818&hilit=screen+magnifier
Related
I am trying to find an example, or documentation, on creating a picture (floating shape object) inside the Excel sheet. The source is supposed to be a numeric bitmap data, stored in a VBA array acquired using external I/O libraries. Using Excel cells as an intermediary storage is possible, but not desired, since the RGB bitmap data is expected to be huge.
The task itself seems to be extremely simple in matlab-like environments, or python. But I just have no Idea how to make it in Excel and VBA without importing an independent image file from the file system.
In terms of storing the file, how huge is 'huge'? If you convert the image into Base64, it'll be a fairly trivial task to split it up amongst the cells and then reconstitute it when converting it into an image.
Alternatively, you can store the Base64 string in a standard module - I'm currently doing much the same thing, but my image only clocks in at 100kb (better to save it as a PNG rather than BMP).
In terms of converting the Base64 string to an image, the Windows Image Acquisition COM object will convert a byte array into a stdPicture image type (and further to my point above, it will also accept PNG files...]. The following function accepts a Base64 string, converts it into a byte array, and returns an stdPicture object:
Function Base64toStdPicture(ByVal Base64Code As String) As StdPicture
Dim ImgVector As Object
Dim Node As Object
Set ImgVector = CreateObject("WIA.Vector")
Set Node = CreateObject("Msxml2.DOMDocument.3.0").createElement("base64")
Node.DataType = "bin.base64"
Node.Text = Base64Code
ImgVector.BinaryData = Node.nodeTypedValue
Set Base64toStdPicture = ImgVector.BinaryData.Picture
Set Node = Nothing
Set ImgVector = Nothing
End Function
From that point, you can out it in an image control, or copy it to / from the clipboard, etc.
I created an app to calculate the sizes of a multi-image, in order to use them in the Codename One designer.
As you can see in the screenshot below, my app has a slider and I get the width (in millimeters) of it. The problem is that the value of the selected width is incorrect. I tested the app on two Android devices and the measured lengths are different from the ones reported by the app.
You can see the full source code, however the relevant code is the following:
Label value = new Label("Move the cursor on the slider...");
Style thumbStyle = new Style();
thumbStyle.setFont(Font.createSystemFont(Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_LARGE), true);
Slider slider = new Slider();
slider.setMaxValue(1000);
slider.setMinValue(0);
slider.setProgress(20); // Set the starting value
slider.setThumbImage(FontImage.create("|", thumbStyle));
slider.setEditable(true); // to it works as a slider instead of a progress bar
slider.addActionListener(e -> {
Integer valueSelected = slider.getProgress();
Integer sliderWidth = slider.getWidth();
Double inch = sliderWidth.doubleValue() / (slider.getMaxValue() - slider.getMinValue()) * valueSelected / 100.0;
Integer millimeters = Double.valueOf(inch * 25.4).intValue();
value.setText("Value selected: " + millimeters.toString() + " mm");
});
Thank you very much for any help
Millimeter measures aren't accurate. A device can return different values or ratios for the convert method than the value it returns for the density flag.
Unfortunately, Googles test suite to certify a device as "good" doesn't actually cover these things. There isn't much we can do about that.
Do my tiles need to adhere to any particular specs?
I have a large image file which I'd like to turn into a map with LeafletJS. I am going to be using the Python Imaging Library to cut it up into all the various tiles I need.
However, I can't find any information about using custom maps in Leaflet. Do I provide Leaflet with the range of X,Y,Z info somehow? Do I give it the pixel size of each tile? Does it figure this out on its own?
To put my question into one concise question: What do I need to do in order to have image files that can double as map tiles with LeafletJS, and what, if anything, do I need to do in my front-end script? (beyond the obvious specifying of my custom URL)
You are looking for a TileLayer. In this TileLayer, you give the URL for the to-be-fetched images to leaflet with a template like this:
http://{s}.somedomain.com/blabla/{z}/{x}/{y}.png
When you are at the specified zoom, x and y level, Leaflet will automatically fetch the tiles on the URL you gave.
Depending on the image you want to show, the bigger part of the work will however be in the tile generation. Tiles by default have a 256x256px size (can be changed in the TileLayer options), and if you are using geodata the used projection is Mercator projection. It may take some time to get the tile ids right. Here is an example on how the tile ids work.
You can even serve tiles directly from a database.
The format leaflet specifies is very flexible.
Leaflet just uses the z,x,y place holders to request specific tiles.
For example:
L.tileLayer('http://localhost/tileserver/tile.aspx?z={z}&x={x}&y={y}', {
minZoom: 7, maxZoom: 16,
attribution: 'My Tile Server'
}).addTo(map);
where Tiles.aspx
Option Strict On
Partial Class tile
Inherits System.Web.UI.Page
Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
Dim z, x, y As Integer
z = CInt(Request.QueryString("z"))
x = CInt(Request.QueryString("x"))
y = CInt(Request.QueryString("y"))
Dim b() As Byte = DB.GetTile(z, x, y)
Response.Buffer = True
Response.Charset = ""
'Response.Cache.SetCacheability(HttpCacheability.NoCache)
Response.ContentType = "image/png"
Response.AddHeader("content-disposition", "attachment;filename=" & y & ".png")
Response.BinaryWrite(b)
Response.Flush()
Response.End()
End Sub
I attempted the following in a call to XCreateWindow():
unsigned long ctt_attribute_mask = CWWinGravity | CWCursor;
ctt_attributes->win_gravity = NorthEastGravity;
ctt_attributes->cursor = XC_arrow;
ctt_window = XCreateWindow(dpy, parent, ctt_xpos, ctt_ypos,
ctt_xy_size, ctt_xy_size, ctt_border,
ctt_depth, ctt_class, ctt_visual,
ctt_attribute_mask, ctt_attributes);
This creates the window, but it doesn't affect the pointer when it rolls over the window.
I want to use the user's desktop environment's standard pointer cursor when the mouse appears over my window.
Xlib is required, because this is a toolkit-agnostic program.
ETA: Additional context is available; see create_ctt_window starting on line 35 in the source file.
ctt_attributes->cursor = XCreateFontCursor(dpy, XC_arrow);
This is not the desktop environment's standard pointer cursor, this is the X11 rather ugly bitmapped cursor. If you want themed cursors, use libXcursor. I have no experience with it.
Here's the example from The Xlib Programming Manual, vol 1, p 182.
#include <X11/cursorfont.h>
int cursor_shape = XC_arrow;
Window window;
Cursor cursor;
cursor = XCreateFontCursor(display, cursor_shape);
XDefineCursor(display, window, cursor);
/* Now cursor will appear when pointer is in window */
So it looks like n.m. is spot-on. You need to call XCreateFontCursor to translate XC_arrow (which is just an integer that designates the cursor's location in the font's encoding vector) into a Cursor resource. I think the Cursor resource is just an integer, too. That's why you get no compile error. But you do indeed have a type mismatch.
What's the best way to convert a WPF (resolution-independent) width and height to physical screen pixels?
I'm showing WPF content in a WinForms Form (via ElementHost) and trying to work out some sizing logic. I've got it working fine when the OS is running at the default 96 dpi. But it won't work when the OS is set to 120 dpi or some other resolution, because then a WPF element that reports its Width as 96 will actually be 120 pixels wide as far as WinForms is concerned.
I couldn't find any "pixels per inch" settings on System.Windows.SystemParameters. I'm sure I could use the WinForms equivalent (System.Windows.Forms.SystemInformation), but is there a better way to do this (read: a way using WPF APIs, rather than using WinForms APIs and manually doing the math)? What's the "best way" to convert WPF "pixels" to real screen pixels?
EDIT: I'm also looking to do this before the WPF control is shown on the screen. It looks like Visual.PointToScreen could be made to give me the right answer, but I can't use it, because the control isn't parented yet and I get InvalidOperationException "This Visual is not connected to a PresentationSource".
Transforming a known size to device pixels
If your visual element is already attached to a PresentationSource (for example, it is part of a window that is visible on screen), the transform is found this way:
var source = PresentationSource.FromVisual(element);
Matrix transformToDevice = source.CompositionTarget.TransformToDevice;
If not, use HwndSource to create a temporary hWnd:
Matrix transformToDevice;
using(var source = new HwndSource(new HwndSourceParameters()))
transformToDevice = source.CompositionTarget.TransformToDevice;
Note that this is less efficient than constructing using a hWnd of IntPtr.Zero but I consider it more reliable because the hWnd created by HwndSource will be attached to the same display device as an actual newly-created Window would. That way, if different display devices have different DPIs you are sure to get the right DPI value.
Once you have the transform, you can convert any size from a WPF size to a pixel size:
var pixelSize = (Size)transformToDevice.Transform((Vector)wpfSize);
Converting the pixel size to integers
If you want to convert the pixel size to integers, you can simply do:
int pixelWidth = (int)pixelSize.Width;
int pixelHeight = (int)pixelSize.Height;
but a more robust solution would be the one used by ElementHost:
int pixelWidth = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Width));
int pixelHeight = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Height));
Getting the desired size of a UIElement
To get the desired size of a UIElement you need to make sure it is measured. In some circumstances it will already be measured, either because:
You measured it already
You measured one of its ancestors, or
It is part of a PresentationSource (eg it is in a visible Window) and you are executing below DispatcherPriority.Render so you know measurement has already happened automatically.
If your visual element has not been measured yet, you should call Measure on the control or one of its ancestors as appropriate, passing in the available size (or new Size(double.PositivieInfinity, double.PositiveInfinity) if you want to size to content:
element.Measure(availableSize);
Once the measuring is done, all that is necessary is to use the matrix to transform the DesiredSize:
var pixelSize = (Size)transformToDevice.Transform((Vector)element.DesiredSize);
Putting it all together
Here is a simple method that shows how to get the pixel size of an element:
public Size GetElementPixelSize(UIElement element)
{
Matrix transformToDevice;
var source = PresentationSource.FromVisual(element);
if(source!=null)
transformToDevice = source.CompositionTarget.TransformToDevice;
else
using(var source = new HwndSource(new HwndSourceParameters()))
transformToDevice = source.CompositionTarget.TransformToDevice;
if(element.DesiredSize == new Size())
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
return (Size)transformToDevice.Transform((Vector)element.DesiredSize);
}
Note that in this code I call Measure only if no DesiredSize is present. This provides a convenient method to do everything but has several deficiencies:
It may be that the element's parent would have passed in a smaller availableSize
It is inefficient if the actual DesiredSize is zero (it is remeasured repeatedly)
It may mask bugs in a way that causes the application to fail due to unexpected timing (eg. the code being called at or above DispatchPriority.Render)
Because of these reasons, I would be inclined to omit the Measure call in GetElementPixelSize and just let the client do it.
Simple proportion between Screen.WorkingArea and SystemParameters.WorkArea:
private double PointsToPixels (double wpfPoints, LengthDirection direction)
{
if (direction == LengthDirection.Horizontal)
{
return wpfPoints * Screen.PrimaryScreen.WorkingArea.Width / SystemParameters.WorkArea.Width;
}
else
{
return wpfPoints * Screen.PrimaryScreen.WorkingArea.Height / SystemParameters.WorkArea.Height;
}
}
private double PixelsToPoints(int pixels, LengthDirection direction)
{
if (direction == LengthDirection.Horizontal)
{
return pixels * SystemParameters.WorkArea.Width / Screen.PrimaryScreen.WorkingArea.Width;
}
else
{
return pixels * SystemParameters.WorkArea.Height / Screen.PrimaryScreen.WorkingArea.Height;
}
}
public enum LengthDirection
{
Vertical, // |
Horizontal // ——
}
This works fine with multiple monitors as well.
I found a way to do it, but I don't like it much:
using (var graphics = Graphics.FromHwnd(IntPtr.Zero))
{
var pixelWidth = (int) (element.DesiredSize.Width * graphics.DpiX / 96.0);
var pixelHeight = (int) (element.DesiredSize.Height * graphics.DpiY / 96.0);
// ...
}
I don't like it because (a) it requires a reference to System.Drawing, rather than using WPF APIs; and (b) I have to do the math myself, which means I'm duplicating WPF's implementation details. In .NET 3.5, I have to truncate the result of the calculation to match what ElementHost does with AutoSize=true, but I don't know whether this will still be accurate in future versions of .NET.
This does seem to work, so I'm posting it in case it helps others. But if anyone has a better answer, please, post away.
Just did a quick lookup in the ObjectBrowser and found something quite interesting, you might want to check it out.
System.Windows.Form.AutoScaleMode, it has a property called DPI. Here's the docs, it might be what you are looking for :
public const
System.Windows.Forms.AutoScaleMode Dpi
= 2
Member of System.Windows.Forms.AutoScaleMode
Summary: Controls scale relative to
the display resolution. Common
resolutions are 96 and 120 DPI.
Apply that to your form, it should do the trick.
{enjoy}