I've been working on some Windows Phone 8.1 RT app's lately which require quite alot of icons. For iOS and Android we can use White on Black icons and turn them into the right color(s) through code. But for WP8.1 it seems quite impossible to run it fast.
class ColoredImage
{
public static WriteableBitmap GetColoredImage(WriteableBitmap bitmap, Color color)
{
var result = bitmap;
for (int i = 0; i < (result.PixelWidth); i++)
{
for (int j = 0; j < result.PixelHeight; j++)
{
if (result.GetPixel(i, j) == Colors.Black)
{
result.SetPixel(i, j, Colors.Transparent);
}
else
{
result.SetPixel(i, j, color);
}
}
}
return result;
}
}
I got this class changing the colors of a WriteableBitmap but it takes about 15 seconds to change the colors of a 62x62 image. Is there anything I am doing wrong, and on what can I improve.
Thanks.
Your algo of reading an image finding pixel and changing their colors is a time consuming process. You have got two ways in which you can do it at a faster pace
1) Using Segoe MDL2 Assets You can use these pre installed font family which contains most of the basic images. Benefit is since these are vector images changing foreground would change the color instantly. Search for CharacterMap and then this font in your system and you will find all available fonts
2) If you are not able to find your particular image in charmap then you will have to create path images using blend which is similar to converting images to vector and then you can easily change color.
Related
I want to get access to pixels colors from an image, but as far as I know Image cannot be used in that way. In my code there is an event Modify where I call a method MakeDarker, but I want to know how can I modify pixels there.
I tried to convert an Image to a bitmap or to byte array, but none of this works for me. Visual Studio doesn't even recognize Bitmap, when I'm typing it despite using Xamarin.Forms.Image.
public void Modify(object sender, EventArgs args)
{
ImageModify modify = new ImageModify();
picture = modify.MakeDarker(picture);
}
class ImageModify
{
public Image MakeDarker(Image image)
{
Image output = new Image();
for (int i = 0; i < image.Height; i++)
{
for (int j = 0; j < image.Width; j++)
{
}
}
return output;
}
}
Have you tried with the Color struct from the System.Drawing namespace (don't know if you can use it in Xamarin or if it's specific for Windows based projects) ?
I used it in my own C# projet to collect RGB values of a pixel and it worked well.
I need to read image pixel colors, the image will be only Black and white. Therefore if the pixel is white i want to instantiate white cube and if pixel is black i want to instantiate black cube. Now This is all new to me so i made some digging and i ended up using system.Drawing and bitmaps. However now im stuck. I can't know how to check for the white pixel
For example
private void Pixelreader()
{
Bitmap img = new Bitmap(("ImageName.png");
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
System.Drawing.Color pixel = img.GetPixel(i, j);
if (pixel == *if image is white)
{
// instantiate white color.
}
}
}
}
is there any other way of doing this? Thanks!
If the image is truly black and white only (that is, all pixels are either equal to System.Drawing.Color.Black or System.Drawing.Color.White), then you could compare to these colors directly. Within the code you posted, it will look like this:
if (pixel == System.Drawing.Color.White)
{
//instantiate white color.
}
If the image is part of your Unity assets, a better approach is to read it using Resources. Place the image into Assets/Resources folder; then you can use the following code:
Texture2D image = (Texture2D)Resources.Load("ImageName.png");
If the image is entirely black or entirely white, no need to loop - just check one pixel:
if(image.GetPixel(0,0) == Color.White)
{
//Instantiate white cube
}
else
{
//Instantiate black cube
}
You can actually load an image as a resource into a Texture2D, then use UnityEngine.Texture2D and UnityEngine.Color.GrayScale to check if the color you get out is sufficiently close to white.
It sounds like you are going a bit overboard with it and instead could use features already built in to Unity. Try taking a look in to getting the pixel color during a ray cast.
if (Physics.Raycast (ray, hit)) {
var TextureMap: Texture2D = hit.transform.renderer.material.mainTexture;
var pixelUV = hit.textureCoord;
pixelUV.x *= TextureMap.width;
pixelUV.y *= TextureMap.height;
print ( "x=" + pixelUV.x + ",y=" + pixelUV.y + " " + TextureMap.GetPixel (pixelUV.x,pixelUV.y) );
Taken from here
I'm creating a gannt chart to show hundreds of calendars for individual instances of orders, currently using an algorithm to draw lines and rectangles to create a grid, the problem is I'm the bitmaps are becoming far to large to draw, taking up ram, I've tried multiple different methods including drawing the bitmaps at half size and scaling them up (comes out horribly fuzzy) and still to large.
I want to be able to draw SVGs as I figure for something that draws large simple shapes should reduce the size dramatically compared to bitmaps.
the problem is I cant find anything on msdn that includes any sort of c# library for drawing svgs and I dont want to use external code.
Do I need to create It in XAML or is there a library similar to how bitmaps are drawn ?
Windows Forms = GDI / GDI+
WPF/XAML = DirectX (where possible)
Best bet is to go with WPF/XAML which supports scalable vector graphics (not the same as the .svg file format)
You will need 3rd party code to do SVG in WinForms.
If you are sticking with WinForms, then bitmapping is the only way this can be achieved really. Take a look at PixelFormat - you might be able to reduce the size of your bitmap in memory by using a format which requires fewer bits-per-pixel for example.
There is no need to use external tools or SVGs. With a bit of simple math you can easily just render the necessary parts you want to display. All you need is to know the grid size, the range of dates and the range of line-items that are visible in your view. Let's call them:
DateTime dispStartDate;
DateTime dispEndDate;
int dispStartItem;
int dispEndItem;
int GridSize = 10; //nifty if you'd like a magnification factor
Let's also say you have a class for a Gantt chart item:
class gItem
{
DateTime StartDate{ get; set; }
DateTime EndDate{ get; set; }
int LineNumber{ get; set; }
int Length { get { return EndDate - StartDate; } }
//some other code and stuff you'd like to add
}
Now you need a list containing all of your Gantt chart entries:
List<gItem> GanttItems;
By now you should have assigned values to each of the above variables, now it's time to generate a list of rectangles that would be visible in the view and draw them:
List<Rectangle> EntryRects = new List<Rectangle>();
void UpdateDisplayBounds()
{
foreach(gItem gEntry in GanttItems)
{
if(gEntry.StartDate < dispEndDate && gEntry.EndDate > dispStartDate
&& gEntry.LineNumber >= dispStartItem && gEntry.LineNumber <= dispEndItem)
{
int x = (gEntry.StartDate - dispStartDate) * GridSize;
int y = (gEntry.LineNumber - dispStartItem) * GridSize;
int width = gEntry.Length * GridSize;
int height = GridSize;
EntryRects.Add(new Rectangle(x, y, width, height);
}
}
}
Now you have a list of rectangles only within the display bounds which you can render. So let's draw:
void DrawRectangles(Graphics canvas)//use a picturebox's graphics handler or something for the canvas
{
canvas.Clear(this.BackColor);
using(SolidBrush b = new SolidBrush(Color.Blue)) //Choose your color
{
foreach(Rectangle r in EntryRects)
{
canvas.FillRectangle(b, r);
}
}
}
The above code should get you started. With this you have a list of rectangles that you render on request and the only image taking space in memory is the currently displayed one.
I've faced a strange issue in my monogame (3.1) windows phone 8 app. When app is deactivated and then activated all textures become black. This happened also after the lock screen (UserIdleDetectionMode is enabled).
I've checked GraphicsDevice.IsDisposed, GraphicsDevice.IsContentLost, GraphicsDevice.ResourcesLost but everything looks ok. I've implemented reload of all my textures on Activated and Unobscured events, but full texture reload takes too much time. In the same time on Marketplace I see monogame apps easily handling desactivate-activate. Moreover, the same app for windows phone 7 written on xna, restores very quickly. What do I do wrong with monogame?
My app is based on monogame WP8 template.
Update:
Just have found out that all textures which loaded via Content.Load(...) are restored very quickly. But all my textures are written by a hand: I load a file from TileContainer, unpack it, read its data with ImageTools, create Texture2D and set its pixels with loaded data. Jpeg files also are rendered to RenderTarget2D as BGR565 to consume space.
Moreover I widely use RenderTarget2D for rendering text labels with shadows, sprite runtime compositions and so on. So it looks like that Monogame just don't want to restore images loaded not by Content.Load.
Continue investigating...
I just got a response from Tom Spillman in the Monogame forums and apparently the Content.Load stuff is restored normally and other data needs to be reinitialized by the program. What you can do is hook up to GraphicsDevice.DeviceResetting event to get notified when this reset is taking place.
According to monogame devs lost textures is a normal situation.
I've made full texture reload in GraphicsDevice.DeviceReset event. To make it work fast I've implemented load from xnb uncompressed files. It's pretty simple as long as this format just have pixel values in it. This is the only solution.
Here's how to read from uncompressed xnb:
private static Texture2D TextureFromUncompressedXnbStream(GraphicsDevice graphicsDevice, Stream stream)
{
BinaryReader xnbReader = new BinaryReader(stream);
byte cx = xnbReader.ReadByte();
byte cn = xnbReader.ReadByte();
byte cb = xnbReader.ReadByte();
byte platform = xnbReader.ReadByte();
if (cx != 'X' || cn != 'N' || cb != 'B')
return null;
byte version = xnbReader.ReadByte();
byte flags = xnbReader.ReadByte();
bool compressed = (flags & 0x80) != 0;
if (version != 5 && version != 4)
return null;
int xnbLength = xnbReader.ReadInt32();
xnbReader.ReadBytes(0x9D);//skipping processor string
SurfaceFormat surfaceFormat = (SurfaceFormat)xnbReader.ReadInt32();
int width = (xnbReader.ReadInt32());
int height = (xnbReader.ReadInt32());
int levelCount = (xnbReader.ReadInt32());
int levelCountOutput = levelCount;
Texture2D texture = texture = new Texture2D(graphicsDevice, width, height, false, SurfaceFormat.Color);
for (int level = 0; level < levelCount; level++)
{
int levelDataSizeInBytes = (xnbReader.ReadInt32());
byte[] levelData = xnbReader.ReadBytes(levelDataSizeInBytes);
if (level >= levelCountOutput)
continue;
texture.SetData(level, null, levelData, 0, levelData.Length);
}
xnbReader.Dispose();
return texture;
}
my first question here. And I'm sure it's an easy one, but I can't find a solution anywhere, I've read lots and just can't comprehend how to what i need to do:
I have a *.gif file, that I want to store in System.Drawing.Bitmap, so that I have access to GetPixel() method
I want to have my drawing on some control (currently trying out PictureBox)
So far what I've got:
I create a template, Bitmap from file - and that works (map_t in code)
I create a bitmap that will actually be drawn (map)
I can set my PictureBox image to a bitmap
I can't change this bitmap. Or I can, but the result doesn't show
I thought I'd have to create Graphic from that bitmap, then change it. I have no idea what i'm missing.
Here's the code: MapCanvas is PictureBox on a form, everything is in MouseMove, because I was trying to make some circles around the pointer - didn't work so I ended up with this code, which doesn't work either. I have read every question I could find, nothing helps... Here's the code:
namespace Projekt_innowacje
{
public partial class MapForm : Form
{
Bitmap map_t;
Bitmap map;
public MapForm()
{
InitializeComponent();
map_t = new Bitmap("map.gif", true);
map = new Bitmap(map_t.Width, map_t.Height);
MapCanvas.Image = map;
}
private void MapCanvas_MouseMove(object sender, MouseEventArgs e)
{
Graphics canvas = Graphics.FromImage(map);
for (int i = 0; i <= map.Width; i++)
for (int j = 0; j <= map.Height; j++)
if (map.GetPixel(i, j) == Color.Red)
canvas.DrawLine(new Pen(Color.Blue), i, j, i + 1, j + 1);
// map.SetPixel(i, j, map.GetPixel(i, j)); // also doesn't work
//MapCanvas.Refresh();
MapCanvas.Invalidate();
canvas.Dispose();
}
}
}
The code you are showing will never modify the canvas/map image unless you are making some other changes to the map image that you're not sharing. In your loop you test map.GetPixel, however map is created as a blank image in the constructor, so none of its pixels will ever be Color.Red. Did you intend to test against your template instead?
Also, you may want to have a look at this question which talks about comparing colors. It suggests that for basic color comparisons to use the .ToArgb() method on the color structure since the == and Equals do more than just compare the color value.