I'm making a graphics editor for my class project and i want to make so that when, for example a user loads a picture in to the editor or or draw something in the PictureBox, all the alpha parts are shown the chessboard like background.
My idea is that when I create a PictureBox with transparent background set, I create another one behind it, set its BackColor to white and add grey images 50x50, alternately horizontally and vertically. Is that a good approach to the problem? If, not do You have any suggestions?
In Photoshop, for example, I create image 1600x1600. When I zoom to a certain level, it shrinks the boxes and adds more of them to fill the image. If You'we used Photoshop of similar program you know what I mean. Now, how would I go about achieving the same effect?
Creating a Photoshop-like program is a fun project.
There will be many challenges along your way and it is well worth thinking ahead a little..
Here is a short and incomplete list of things to keep in mind:
- Draw- and paint actions
- Undo, redo, edit
- Multiple layers
- Zooming and scrolling
- Saving and printing
So getting a checkerboard background is only the start of a long journey..
Using a PictureBox
as the base canvas is a very good choice, as its several layers will help. Here is a piece of code that will provide you with a flexible checkerboard background, that will keep its size even when you scale the real graphics:
void setBackGround(PictureBox pb, int size, Color col)
{
if (size == 0 && pb.BackgroundImage != null)
{
pb.BackgroundImage.Dispose();
pb.BackgroundImage = null;
return;
}
Bitmap bmp = new Bitmap(size * 2, size * 2);
using (SolidBrush brush = new SolidBrush(col))
using (Graphics G = Graphics.FromImage(bmp) )
{
G.FillRectangle(brush, 0,0,size, size);
G.FillRectangle(brush, size,size, size, size);
}
pb.BackgroundImage = bmp;
pb.BackgroundImageLayout = ImageLayout.Tile;
}
Load an Image for testing and this is what you'll get, left normal, right zoomed in:
Yes, for saving this background should be removed; as you can see in the code, passing in a size = 0 will do that.
What next? Let me give you a few hints on how to approach the various tasks from above:
Scrolling: Picturebox
can't scroll. Instead place it in a Panel
with AutoScroll = true
and make it as large as needed.
Zooming: Playing with its Size
and the SizeMode
will let you zoom in and out the Image
without problems. The BackgroundImage
will stay unscaled, just as it does in Photoshop. You will have to add some more code however to zoom in on the graphics you draw on top of the PB or on the layers. The key here is scaling the Graphics
object using a Graphics.MultiplyTransform(Matrix)
.
Layers: Layers are imo the single most useful feature in PhotoShop (and other quality programs). They can be achieved by nesting transparent drawing canvases. Panels
can be used, I prefer Labels
. If each is sitting inside the one below it and the one at the bottom has the PB as its Parent
, all their contents will be shown combined.
- Don't use the
Label
directly but create a subclass
to hold additional data and know-how!
- Changing their order is not very hard, just keep the nested structure in mind and intact!
- Hiding a layer is done by setting a flag and checking that flag in the painting actions
- Other data can include a Name, Opacity, maybe an overlay color..
- The layers should also be shown in a Layers Palette, best by creating a thumbnail and inserting a layer userobject in a
FlowLayoutPanel
Draw Actions: These are always the key to any drawing in WinForms
. When using the mouse to draw, each such activity creates an object of a DrawAction
class you need to design, which holds all info needed to do the actual drawing, like:
- Type (Rectangle, filledRectangle, Line, FreeHandLine (a series of Points), Text, etc.etc..)
- Colors
- Points
- Widths
- Text
- The layer to draw on
- maybe even a rotation
Along with the LayerCanvas class the DrawAction class will be the most important class in the project, so getting its design right is worth some work!
Only the topmost layer will receive the mouse events. So you need to keep track which layer is the active one and add the action to its actions list. Of course the active layer must also be indicated in the Layers Palette.
Since all drawing is stored in List(s), implementing a unlimited undo and redo is simple. To allow for effective drawing and undo, maybe a common action list and an individual list for each layer is the best design..
- Undo and Redo are just matter of removing the last list element and pushing it onto a redo-stack.
- Editing actions is also possible, including changing the parameters, moving them up or down the actions list or removing one from the middle of the list. It help to show an Actions Palette, like F9 in PhotoShop.
- To flatten two or more layers together all you need is to combine their action lists.
- To flatten all layers into the
Image
you only need to draw them not onto their canvas but into the Image. For the difference of drawing onto a control or into a Bitmap see here! Here we have the PictureBox.Image
as the second level of a PB's structure above the Background.Image
. (The 3rd is the Control
surface, but with the multiple layers on top we don't really need it..)
Saving can be done by either by Image.Save()
after flattening all Layers or after you have switched off the BackgroundImage by telling the PB to draw itself into a Bitmap
( control.DrawToBitmap()
) which you can then save.
Have fun!