BitBLT and Jpg (Please Help)
|
|
Thread rating:  |
Paul - 23 Sep 2004 08:16 GMT Hi
I have just about reached the end of my research on this one, so as a last resort I was hoping I could get a definative answer from you guys.
Using my flat bed scanner , I have scanned in a CD. The result shows the picture of the CD but shows a square outline around it.
All i want to do is Remove the outline and end up with a picture of the Round CD. I was sure BitBlt was the function to use, but I dont think this applies to jpg files. Can anyone please help on this one
Regards
Mike Williams - 23 Sep 2004 11:39 GMT > All i want to do is Remove the outline and end up with a picture > of the Round CD. I was sure BitBlt was the function to use, but > I dont think this applies to jpg files. Can anyone please help I see that you've already received answers regarding the use of BitBlt and StretchBlt so I won't go there again. As for the jpg stuff, there are many different ways of doing it, but one of the simplest is to load the jpeg into an Autoredraw Picture Box. For this simple method you really need to be running your machine at full colour depth. You will end up with a bitmap in the picture box's "Autoredraw" Image property. Your BitBlt (and other APIs) can access this bitmap using the hDC property of the picture box as the hSrcDC parameter in your BitBlt function. Also, the Width and Height properties of the Picture Box will give you the pixel width and height of the bitmap:
Me.ScaleMode = vbPixels With Picture1 .BorderStyle = vbBSNone .ScaleMode = vbPixels .AutoSize = True .AutoRedraw = True .Picture = LoadPicture("c:\boats.jpg") End With
Mike
Paul - 23 Sep 2004 13:22 GMT Hi Mike
Thank you for the post.
Although I have had replies regarding BitBLT, I am still confused about it. From what I can gather, its all down to the Raster options (probably wrong).
I am not quite sure how to get to the Bitmap info in the jpg file as you described. Do you have any samples I could play with so I can try to understand this more.
The printer examples you previously did were excellent by the way
Paul
>> All i want to do is Remove the outline and end up with a picture >> of the Round CD. I was sure BitBlt was the function to use, but [quoted text clipped - 21 lines] > > Mike Mike Williams - 23 Sep 2004 18:45 GMT > Hi Mike <snip> The printer examples you previously > did were excellent by the way Well that's definitely got you off to a good start. Flattery will get you everywhere ;-)
> Thank you for the post. Although I have had replies regarding > BitBLT, I am still confused about it. From what I can gather, > its all down to the Raster options (probably wrong). I am not > quite sure how to get to the Bitmap info in the jpg file as you > described. Do you have any samples I could play with so I > can try to understand this more. I haven't got any "ready written" examples, but I could certainly write you one. However, rather than write a complete example that you could just drop into your own code I would much prefer to write and post "little bits at a time", providing code extracts complete with explanations, so that instead of ending up with just a "block of working code" you will end also up with a good understanding of the principles involved. If you are prepared to accept that then post again and let me know.
In the meantime, what *exactly* are you starting off with? For example, you say you have scanned a CD and that your scan shows a picture of the CD with a square outline around it. Does that mean you have already cropped the scanned image and that the "square outline" is just some "noise" around it (or perhaps almost black, depending on the colour of the underside of the scan lid)? Or is your scanned image an image of the full scan bed, with the CD picture somewhere within it? Also, is there any specific reason why you have scanned the CD as a jpg instead of a bitmap, other than the obvious "smaller file size"? And what is the scan resolution? Post *full details* of what you are starting with. Also, when you come to eventually print the "round CD label image" onto your page, will your code have already printed anything else on that page that either wholly or partially sits in the "unwanted corners" of the round CD image? In other words, do you really need to print the round CD picture so that its "unwanted corners" are printed transparently, or will it be okay to simply print the "unwanted corners" white (which would obliterate anything else that was already sent to the page at those positions)? Full details please, Paul.
Mike
Paul - 23 Sep 2004 20:12 GMT Hi Mike
A quick rundown on how my app is going to work.
Using a commondialog control i browse for a cd label which is mostly in jpg format as these are sometimes downloaded from the internet. The image is then loaded into an image box (although it looks like this has to be a picture box because it has a .hdc property) The image is then shown on the form (minus the square edges).
To make it easier I have done an example which you can view here http://www.ukirlp.co.uk/cd.html
From what I have read about BitBlt, I need 2 seperate images (a mask and the original), although I am not sure.
Anyway, this is as far as I have got. if it makes any sense.
Paul
Option explicit
Declare Function BitBlt Lib "gdi32" Alias _ "BitBlt" (ByVal hDestDC As Long, _ ByVal x As Long, ByVal y As Long, _ ByVal nWidth As Long, ByVal nHeight _ As Long, ByVal hSrcDC As Long, ByVal _ xSrc As Long, ByVal ySrc As Long, _ ByVal dwRop As Long) As Long
'Const for BitBLT Public Const SRCPAINT = &HEE0086 'Source OR Destination Public Const SRCAND = &H8800C6 'Source AND Destination
Private Sub Form_Load()
Dim strFilename As String
On Error GoTo error
With commondialog .CancelError = True .ShowOpen strFilename = .FileName PicSrc.Picture = LoadPicture(strFilename) End With
Exit Sub
error: MsgBox Err.Description End Sub
Private Sub Form_Paint()
BitBlt picSrc.hDC, 20, 20, 320, 320, picCD.hDC, 0, 0, SRCAND
End Sub
>> Hi Mike <snip> The printer examples you previously >> did were excellent by the way [quoted text clipped - 37 lines] > > Mike Mike Williams - 23 Sep 2004 21:25 GMT > To make it easier I have done an example which you > can view here http://www.ukirlp.co.uk/cd.html From what I can see, the jpegs you are using are not exactly the same size as the diameter of the actual image (the picture rectangle seems to be slightly larger than the image of the "disk label" it contains). Is this the case? If so, you will have a bit of a problem determining the actual size of the "disk label portion" of the jpeg, especially if the amount by which the width of the image exceeds the diameter of the label portion of it is different in different images, and if the "extra bit" is not always exactly the same colour. It would be much easier if you could use images that have all been cropped accurately.
Some more detail of the images you are using would be nice.
Mike
Paul - 23 Sep 2004 22:07 GMT Hi Mike
The image on the left was the original image
I am not that concerned about the hole in the middle or the actual size of the image at this early learning stage what I am trying to achieve is to remove the square outline and just end up with the circular shape to start with
Paul
>> To make it easier I have done an example which you >> can view here http://www.ukirlp.co.uk/cd.html [quoted text clipped - 12 lines] > > Mike Mike Williams - 23 Sep 2004 22:35 GMT > The image on the left was the original image. I am not > that concerned about the hole in the middle or the actual > size of the image at this early learning stage. What I am > trying to achieve is to remove the square outline and just > end up with the circular shape to start with. I know that, Paul. But I think you misunderstood what I said in my previous post. I am asking you about the size of the "circular part" of the picture *relative to* the size of the rectangular picture as a whole. In other words, if you scan a CD and then crop the resultant picture *exactly* so that the circular part exactly fits into the cropped rectangle then the width and height of the rectangular picture will both be exactly the same size as the diameter of the "circular" part of the picture (the CD label itself). Ideally, that is what we would want to start with. However, looking at your original rectangular picture, it is slightly larger than the circular part. The circular portion is slightly smaller than the rectangle and it sits roughly in the middle of it. And that is just one picture you have shown me. There may be other original pictures where the size (width and height) of the rectangular picture is very much larger than the part of it that represents the circular CD label, and there may also be other originals where the circular portion is not even in the middle. Now do you see what I mean? Tell me all about the various originals you are using. If possible, post a number of full size originals to that link you gave me in your earlier message, so that I can look at a number of them in their exact original form.
Mike
Paul - 23 Sep 2004 23:01 GMT Hi Mike
I have put 3 scans on the webpage , they are quite large in size.
Sorry to be a nuisance, does this give you a clearer picture
http://www.ukirlp.co.uk/cd.html
Paul
>> The image on the left was the original image. I am not >> that concerned about the hole in the middle or the actual [quoted text clipped - 23 lines] > > Mike Mike Williams - 24 Sep 2004 10:18 GMT > Hi Mike. I have put 3 scans on the webpage , they are > quite large in size. I've had a look at the three jpg pictures on the link you posted. Two of them have been cropped more or less correctly (although one is slighly out), so that the circular image of the CD sits more or less exactly inside a rectangle of similar size (within a few pixels or so). The third one ("The Corrs") is slightly different because it has been cropped a bit too large (in one dimension) so that the width of the rectangle is a bit larger than the diameter of the circular image. However, for simplicity it might be best for the time being to assume that most of your images will have been cropped more or less correctly (as is approximately the case with the other two pictures). If you want to write code to deal reliably with incorrectly cropped images that might contain the circular portion anywhere within the rectangle then I'm afraid you will have a lot of work on your hands writing some reliable "edge detection" code. It's possible to do of course, but it will be much simpler (at least for now) to assume that all of your images will have been correctly cropped.
Before I start, I'd like to say that (as with most things) there is more than one way of tackling this job, but for convenience I will use (eventually!) the StretchBlt API to output the result to the printer. Not all printers support StretchBlt, but I think most modern printers do. We can easily use the GetDeviceCaps API to find out if the attached printer supports StretchBlt, but I think I'll leave that for the time being. Besides, as far as I know (but I'm not absolutely certain), the Windows operating system will attempt to perform the "stretch" using some other available lower level capability if confronted with a printer that does not support it. Anyway, we'll leave complications of that nature until later (much later!). Besides, if you just want the "unwanted pixels that surround the circular image" to be printed white on the page (rather than transparent) then you will be able to forget StretchBlt and use the more simple VB PaintPicture method, which will look after all that stuff for you. In fact (although I haven't thought too much about it yet) you will probably be able to use VB PaintPicture method to perform a transparent "blit" as well. But let's leave that for later.
The very first thing you need to do (whatever printer output method you end up using) is to take your original jpg picture (any jpg picture will do for test purposes) and turn it into a full colour bitmap (very easy to do if your system is running at full colour depth, or even at 16 bit colour depth, which I imagine applies to almost all computers these days). You can do this simply by loading the jpg into a borderless Picture Box with its Autoredraw property and its Autosize property both set to True. That will result in the picture ending up as a bitmap in the Image property of the Picture Box. As far as APIs are concerned, you can then access this bitmap using the hDC property of the Picture Box. If you set the Form's ScaleMode to vbPixels then the Width and Height properties of the Picture Box will tell you the pixel size of the bitmap.
Then you need to create another Autoredraw borderless Picture Box of exactly the same size as the first (after the picture has been loaded into it). In this second Picture Box you need to draw a "mask" image. This "mask" needs to contain black pixels wherever you want the original picture to remain in their original state and white pixels where you want the original picture to be turned white. In other words, to transform the rectangular bitmap into a bitmap where the centre circle is "picture" and the surrounding area is white you need to draw a mask that is a black circle on a white background. If you also want to mask out the small central circular area then you need to also draw a small white circle inside the large black circle.
Once you have done this, you need to combine every pixel in the bitmap with the corresponding pixel in the mask using a logical OR operation. BitBlt will do this for you if you set the dwRop parameter to vbSrcPaint. This will result in the bitmap turning to white wherever there are white pixels in the mask, and remaining its original colour wherever there are black pixels in the mask. (I'm not sure how much you already know about bitwise logic, but if you would like me to explain in detail why this happens then post again).
After doing all this you will end up with a cicular picture in the centre of the Picture Box, with all surrounding pixels being white and with a small white circle in the centre. If all you want to do is print this onto a blank page then you can do it straight away, using either StretchBlt or PaintPicture. However, if you do this, any "already drawn" stuff on the same printer page will be overwritten by the entire rectangular picture (including the white portions of it). If that's all you want to do then that's fine. Otherwise, if you want to print it onto the page so that "the white bits" are transparent (rather than white) and do not overwrite stuff you have already drawn into that page then we have a little bit more work to do (but I think I'll leave that for later).
Have a look at the following code. Obviously, you will need to substitute the hard coded jpg picture path and filename with one that you have on your own machine. It will of course work with quite large jpg pictures, but you can more easily see what is happening if you use a small size jpg for test purposes (perhaps about 400 pixels wide). The code automatically turns the picture into an exact square, but it is obviously best if you start off by using a jpg picture that is approximately square in the first place. All the code does at the moment is load a jpg picture into a Picture Box, draw it exactly square if it is not already so, draw an appropriate "circular" mask and then combine the picture and ther mask as described above. It then draws tyhe result to the printer at the correct size for a CD label. As I've already said, if you want to take it one stage further than this and draw it so that thw white portions are tran sparent (rather than white) so that they don't interfere with anything else you have already sent to the printer on the same page then there is a little more work to do (but not much though). Post again if you would like help with that.
Paste the following code into a standard VB Form containing two Picture Boxes and two Command Buttons.
Mike
Option Explicit Private Declare Function BitBlt Lib "gdi32" _ (ByVal hDestDC As Long, _ ByVal x As Long, ByVal y As Long, _ ByVal nWidth As Long, ByVal nHeight As Long, _ ByVal hSrcDC As Long, _ ByVal xSrc As Long, ByVal ySrc As Long, _ ByVal dwRop As Long) As Long Private picWide As Long, picHigh As Long
Private Sub Form_Load() Me.WindowState = vbMaximized Me.ScaleMode = vbPixels With Picture1 ' the picture .AutoSize = True .BorderStyle = vbBSNone .ScaleMode = vbPixels .AutoRedraw = True End With With Picture2 ' the mask .BorderStyle = vbBSNone .ScaleMode = vbPixels .AutoRedraw = True .FillStyle = vbFSSolid .BackColor = vbWhite End With Command2.Enabled = False End Sub
Private Sub Command1_Click() Picture1.Picture = LoadPicture("c:\cd2.jpg") DoEvents picWide = Picture1.Width picHigh = Picture1.Height If picWide > picHigh Then Picture1.Height = picWide picHigh = picWide Else Picture1.Width = picHigh picWide = picHigh End If Caption = picWide & " pixels by " & Format(picHigh) & " pixels." Picture1.PaintPicture Picture1.Picture, 0, 0, picWide, picHigh Picture1.Left = 0: Picture1.Top = 0 Picture2.Move picWide + 4, 0, picWide, picHigh DoEvents Picture2.FillColor = vbBlack Picture2.Circle (picWide / 2, picHigh / 2), picWide \ 2 - 1, vbBlack Picture2.FillColor = vbWhite Picture2.Circle (picWide / 2, picHigh / 2), picWide \ 17, vbWhite DoEvents Command1.Enabled = False Command2.Enabled = True End Sub
Private Sub Command2_Click() ' This Blit combines the existing image in Picture1's ' Image property (the main picture) with the image data ' in Picture2's Image property (the mask image) using ' the vbSrcPaint raster opcode (a logical OR operation). BitBlt Picture1.hDC, 0, 0, picWide, picHigh, _ Picture2.hDC, 0, 0, vbSrcPaint Picture1.Refresh ' copy the Autoredraw Image to the display ' Now print the resultant image at a diameter of 120 mm Printer.ScaleMode = vbMillimeters Printer.PaintPicture Picture1.Image, 0, 0, 120, 120 Printer.EndDoc Command2.Enabled = False Command1.Enabled = True End Sub
Martin Trump - 24 Sep 2004 17:48 GMT >I've had a look at the three jpg pictures on the link you posted. Two >of them have been cropped Not sure I've understood the OP's requirements properly. However, I've tried a different approach:-
1) Picture.Point in an area near the TLH corner and get the average r, g, b components of the background. 2) Scan the entire picture and see (with a tolerance) where the picture's RGB values change to/from the b/g and note the x, y and colour values there. That gives the edge points of the CD. (All that that works OK :-) 3) To prevent coggy edges, at each edge point of the CD outline .Pset the original pixel with the average of a 9X9 array around it, should avoid cogginess and allow any b/g colour to be used. (Not working yet
:-( All that is, of course, is *desperately* slow but using the .bmp in binary might make it usable.
Comments would be appreciated.
Regards
 Signature Martin Trump
Mike Williams - 24 Sep 2004 18:51 GMT > Not sure I've understood the OP's requirements properly. > However, I've tried a different approach:- Actually the OP's original requirement was to print the jpg of the CD in such a way that the "unwanted" area of the image rectangle that lies outside the circular area of the label itself would not be printed at all. That was the purpose of the code I posted.
> 1) Picture.Point in an area near the TLH corner and get the > average r, g, b components of the background . . . etc Yep. That is a secondary requirement that is brought about by the fact that the jpg images supplied by the OP were not properly cropped, so that in some cases the circular image was smaller than the rectangle and did not even sit in the middle of it. I haven't done any code for that yet, but your own suggestions seem okay.
> 2) Scan the entire picture and see (with a tolerance) where > the picture's RGB values change to/from the b/g and note > the x, y and colour values there. That gives the edge points > of the CD. (All that that works OK :-) Yes. We know that the actual label part of the jpg image is circular, so all we need to do is find the left, right, top and botton edges. That will tell us the exact size and position of the circle within the rectangle, and will enable us to draw the Mask for the label with good accuracy. Even so, that may not be such an easy job with some images, because the "unwanted" areas can be almost any colour at all, and so can the edge pixels of the circular portion. Some sort of good line detection technique would be helpful, but I;ve never done stuff like that. Maybe you (or someone else) can come up with some useful code for that?
> 3) To prevent coggy edges, at each edge point of the CD outline > .Pset the original pixel with the average of a 9X9 array around it, > should avoid cogginess and allow any b/g colour to be used. > (Not working yet) :-( Actually, the "coggy" or "stepped" edges of the Mask image that we draw isn't really apparent in the printed output. This is because with the scan resolution used by the OP a single pixel is about 1/800 of the diameter of the circular label on the screen, whereas on the printer (which has a much higher resolution) a single pixel is typically only about 1/3000 of the diameter, and in some cases much less than that. It looks okay.
> Comments would be appreciated. Have a look at the following modification to the code that I previously posted. It draws the label transparently over the top of anything else already drawn into the same printer page. I've used the VB PaintPicture method instead of the StretchBlt method because PaintPicture is more likely to behave properly on a much wider range of printers. In fact I'm pretty sure that if PaintPicture discovers that a specific printer does not support StretchBlt it will jump though a few hoops that enable it to use StretchDIBits instead, which many more printers support, and if a printer doesn't support either of those I think that PaintPicture will jump through some more hoops to enable it to instead use the much more widely accepted BitBlt. Paste the code into a VB Form containing two picture boxes and two command buttons. Use any jpg image you like to check it out.
Mike
Option Explicit Private Declare Function BitBlt Lib "gdi32" _ (ByVal hDestDC As Long, _ ByVal x As Long, ByVal y As Long, _ ByVal nWidth As Long, ByVal nHeight As Long, _ ByVal hSrcDC As Long, _ ByVal xSrc As Long, ByVal ySrc As Long, _ ByVal dwRop As Long) As Long Private picWide As Long, picHigh As Long
Private Sub Form_Load() Me.WindowState = vbMaximized Me.ScaleMode = vbPixels With Picture1 ' the picture .AutoSize = True .BorderStyle = vbBSNone .ScaleMode = vbPixels .AutoRedraw = True End With With Picture2 ' the mask .BorderStyle = vbBSNone .ScaleMode = vbPixels .AutoRedraw = True .FillStyle = vbFSSolid .BackColor = vbWhite End With Command2.Enabled = False End Sub
Private Sub Command1_Click() Picture1.Picture = LoadPicture("c:\cd2.jpg") DoEvents picWide = Picture1.Width picHigh = Picture1.Height If picWide > picHigh Then Picture1.Height = picWide picHigh = picWide Else Picture1.Width = picHigh picWide = picHigh End If Caption = picWide & " pixels by " & Format(picHigh) & " pixels." Picture1.PaintPicture Picture1.Picture, 0, 0, picWide, picHigh Picture1.Left = 0: Picture1.Top = 0 Picture2.Move picWide + 4, 0, picWide, picHigh DoEvents Picture2.FillColor = vbBlack Picture2.Circle (picWide / 2, picHigh / 2), picWide \ 2 - 1, vbBlack Picture2.FillColor = vbWhite Picture2.Circle (picWide / 2, picHigh / 2), picWide \ 17, vbWhite DoEvents Command1.Enabled = False Command2.Enabled = True End Sub
Private Sub Command2_Click() ' This Blit combines the existing image in Picture1's ' Image property (the main picture) with the image data ' in Picture2's Image property (the mask image) using ' the vbSrcPaint raster opcode (a logical OR operation). BitBlt Picture1.hDC, 0, 0, picWide, picHigh, _ Picture2.hDC, 0, 0, vbSrcPaint Picture1.Refresh ' copy the Autoredraw Image to the display ' Now print the resultant image at a diameter of 120 mm Printer.ScaleMode = vbMillimeters Printer.Print ' always start print jobs with this Printer.CurrentX = 0: Printer.CurrentY = 0 ' first print some background text just to show that ' the picture that is printed on top of it is ' printed using "transparency" Printer.Font.Name = "Times New Roman" Printer.Font.Size = 10 Dim n As Long, s1 As String s1 = "This is some background stuff just so that you can" s1 = s1 & " see the picture is printed transparently." For n = 1 To 32 Printer.Print s1 Next n ' Now print the image "transparently" on top in two stages ' First print the MASK using vbMergePaint ' which is the bitwise logical operation ' Destination = (NOT Mask) OR Destination Printer.PaintPicture Picture2.Image, _ 0, 0, 120, 120, , , , , vbMergePaint ' Then print the image on top of it using vbSrcAnd ' which is the bitwise logical operation ' Destination = Destination AND Image Printer.PaintPicture Picture1.Image, _ 0, 0, 120, 120, , , , , vbSrcAnd Printer.EndDoc Command2.Enabled = False Command1.Enabled = True End Sub
Martin Trump - 27 Sep 2004 17:39 GMT >Actually the OP's original requirement was to print the jpg of the CD >in such a way that the "unwanted" area of the image rectangle that lies >outside the circular area of the label itself would not be printed at >all. That was the purpose of the code I posted. Hi Mike and All
Browsing through my "might be useful someday" files I found some code you posted around Dec '03 which used CreateEllipticRgn and SetWindowRgn.
Could that be applicable here? Just curious.
Regards
 Signature Martin Trump
Mike Williams - 27 Sep 2004 19:50 GMT > Hi Mike and All. Browsing through my "might be useful someday" > files I found some code you posted around Dec '03 which used >CreateEllipticRgn and SetWindowRgn. Could that be applicable > here? Just curious. Not really Martin. Paul wants to output his stuff to the printer, not to a window. The code which I posted in this thread on 24 Sep will definitely do it for him though.
> Browsing through my "might be useful someday" files I found > some code . . . . . While you've got your "might be useful someday" folder open Martin, here's something relating to window regions that very definitely might be useful to you one day. It is an answer that I intended to post in response to Matthew's question on Saturday night (UK time) in the thread "Transparent Background" about creating a countdown clock centered on the monitor with a transparent background (so only the numbers show). But by the time I had written the code Matthew had disappeared into Google and hasn't been seen since!
Anyway, the code deals with window regions in order to make a Form that is shaped to a specific region, but more importantly it shows a simple method of using the very clever API Path functions (of which there are about a dozen) which can be used for various purposes but which are particularly useful for creating window regions from a combination of the multitude of API text and graphic drawing methods. Start a new VB project and place a Timer on the Form. Set the Form's BorderStyle property to zero (None) in the IDE at design time. Then paste in the following code. Run it as a compiled exe, because the "always on top" stuff doesn't usually work when run from within the VB IDE. In particular, have a look at the TextToRegion subroutine, which turns some printed text into a winow region in just a few lines.
Mike
Option Explicit Private Declare Function BeginPath Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Function EndPath Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Function PathToRegion Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Function SetWindowRgn Lib "user32" _ (ByVal hwnd As Long, ByVal hRgn As Long, _ ByVal bRedraw As Boolean) As Long Private Declare Function SetWindowPos Lib "user32" _ (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, _ ByVal x As Long, ByVal y As Long, ByVal cx As Long, _ ByVal cy As Long, ByVal wFlags As Long) As Long Private Declare Function DeleteObject Lib "gdi32" _ (ByVal hObject As Long) As Long Private Declare Function TextOut Lib "gdi32" Alias _ "TextOutA" (ByVal hdc As Long, ByVal x As Long, _ ByVal y As Long, ByVal lpString As String, _ ByVal nCount As Long) As Long Private Const SWP_NOMOVE = &H2 Private Const SWP_NOSIZE = &H1 Private Const HWND_TOPMOST = -1 Private counter As Long
Private Sub Form_Load() ' Paste this code into a standard VB Form containing ' a Timer Control. Set the BorderStyle property of ' the Form to None at design time. Dim s1 As String With Me .BackColor = vbBlue .Font.Name = "Arial" .Font.Bold = True .Font.Size = 48 .Width = .TextWidth("999") .Height = .TextHeight("999") .Left = (Screen.Width - .Width) / 2 .Top = (Screen.Height - .Height) / 2 End With counter = 120 s1 = Format(counter) ' The following sets the Form to be always on top. Note ' that this doesn't usually work when run in the VB IDE ' but it works fine when run as a compiled exe. SetWindowPos hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE Or SWP_NOMOVE ' The following sets the Form to the shape of a string ' representing the current value of counter. TextToRegion Format(counter) Timer1.Interval = 1000 Timer1.Enabled = True End Sub
Private Sub TextToRegion(s1 As String) Dim NewRgn As Long BeginPath Me.hdc TextOut Me.hdc, 0, 0, s1, Len(s1) EndPath Me.hdc NewRgn = PathToRegion(Me.hdc) SetWindowRgn Me.hwnd, NewRgn, True DeleteObject NewRgn End Sub
Private Sub Timer1_Timer() ' Note: this is not intended to be an accurate one ' second countdown timer, but is simply an approximate ' countdown timer solely intended to demonstrate the ' window region stuff. counter = counter - 1 If counter < 0 Then Timer1.Enabled = False Unload Me End If TextToRegion Format(counter) End Sub
Private Sub Form_Unload(Cancel As Integer) End ' So shout at me then! I don't care ;-) End Sub
Mike Williams - 27 Sep 2004 20:36 GMT > Anyway, the code deals with window regions in order to > make a Form that is shaped to a specific region . . . . . I suppose that I'd better explain that "End" statement in the Form Unload event in the code I posted, before somebody shouts at me for it!
I put it in because I was having a little trouble with the app "hanging on in there" when it was supposed to terminate. For some reason I couldn't see it before, but my mistake was to leave some code executing in the Timer event *after* I had unloaded the Form. Anyway, here is the fix. Get rid of the "End" statement (and the Unload event code itself) and change the Timer event code to:
Private Sub Timer1_Timer() counter = counter - 1 If counter < 0 Then Timer1.Enabled = False Unload Me Else TextToRegion Format(counter) End If End Sub
Mike
Mike Williams - 27 Sep 2004 22:07 GMT You're probably getting fed up of this now, Martin (and the rest of you!), but I've been playing a little more with the various API Path functions while sipping (or is that gulping!) this Lamb's® rum and Coke®. I've known about the API Paths for a long time, but they have always been in the "check this stuff out later" folder in my head and I never actually got around to doing it until the other day. Paths are brilliant, Martin. You can even stroke them! (They can't touch you for it!). I've only just scraped the surface so far, but here is another little useful routine that uses them to print "outline" text.
Mike
Option Explicit Private Declare Function BeginPath Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Function EndPath Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Function StrokePath Lib "gdi32" _ (ByVal hdc As Long) As Long Private Declare Function TextOut Lib "gdi32" Alias _ "TextOutA" (ByVal hdc As Long, ByVal x As Long, _ ByVal y As Long, ByVal lpString As String, _ ByVal nCount As Long) As Long
Private Sub Command1_Click() Dim s1 As String Me.Font.Name = "Times New Roman" Me.Font.Bold = True Me.Font.Size = 48 s1 = "Whisky and Coke" BeginPath Me.hdc TextOut Me.hdc, 0, 0, s1, Len(s1) EndPath Me.hdc StrokePath Me.hdc End Sub
Michael B. Johnson - 27 Sep 2004 23:20 GMT >> Anyway, the code deals with window regions in order to >> make a Form that is shaped to a specific region . . . . . > >I suppose that I'd better explain that "End" statement in the Form Unload >event in the code I posted, before somebody shouts at me for it! We weren't quite sure you weren't having a hangover, so we didn't shout. But I'm sure at least some of us were contemplating it. ;-) _______________________ Michael B. Johnson
Paul - 24 Sep 2004 19:23 GMT Hi Mike
Just got in from work..
This is excellent work.
I have had a quick read through but not applied the code as yet.
Let me grab a cuppa and stoke up the Pipe :-)
Your explanations are absolutely excellent and precise.
Let me work through it all and make sure I understand it fully before I ask any questions that may already be in your documentation
Paul
PS. Do you live in the UK ?
>> Hi Mike. I have put 3 scans on the webpage , they are >> quite large in size. [quoted text clipped - 172 lines] > Command1.Enabled = True > End Sub Mike Williams - 24 Sep 2004 20:59 GMT > Hi Mike <snip> Let me work through it all and make sure > I understand it fully before I ask any questions that may > already be in your documentation Fine. Just fire away when you're ready Paul. By the way, have you also looked at the code I recently posted in this same thread in response to Martin Trump's post? It is a slight modification of the code I posted to you earlier, and it prints the picture transparently on top of some other stuff on the same page. Should be just what you want.
> PS. Do you live in the UK ? Yes. I live in Ormskirk, Lancashire (not far from Liverpool).
Mike
Paul - 24 Sep 2004 21:23 GMT Hi Mike
I am other side on Pennines in Doncaster
Just a quick question
this line is for the inner circle Picture2.Circle (picWide / 2, picHigh / 2), picWide \ 17, vbWhite
I presume the "17" is the radious of the inner circle in pixels because you set the picture scale mode to pixels in the form load
How did you determine it to be 17
Paul
>> Hi Mike <snip> Let me work through it all and make sure >> I understand it fully before I ask any questions that may [quoted text clipped - 11 lines] > > Mike Mike Williams - 24 Sep 2004 22:30 GMT > Just a quick question . . . this line is for the inner circle > Picture2.Circle (picWide / 2, picHigh / 2), picWide \ 17, vbWhite > I presume the "17" is the radious of the inner circle in pixels > because you set the picture scale mode to pixels in the form > load. How did you determine it to be 17 The scalemode is really irrelevant in that respect Paul. The picwide \ 17 value for the radius of the inner circle is the equivalent of "one seventeenth of the CD label diameter" (since the label diameter is the same as the width of the picture). In other words the *diameter* of the inner circle is set to be two seventeenths of the diameter of the complete label. Nothing magic was going on there. I could of course have actually measured the diameter of the inner circle "hole" of a real CD and compared it to its full diameter, but I didn't do that. I simply tried tried picwide\10, then picwide\12, then picwide\15 etc until by trial and error I ended up with the value picwide\17, which looked about right.
In fact you might like to measure the diameter of a real CD (120 mm I believe) and then measure the diameter of the central hole and divide one by the other to come up with a more accurate figure. My figure was just guesswork :-(
Mike
Paul - 25 Sep 2004 08:55 GMT Morning Mike
Raining here in Doncaster so its another computer day :-)
I have gone through all the code (including the transparent one) and I would like to ask you a question re stretchBlt You mentioned that we could use the stretchblt instead of paintpicture What advantage would that give me Mike
Paul
>> Just a quick question . . . this line is for the inner circle >> Picture2.Circle (picWide / 2, picHigh / 2), picWide \ 17, vbWhite [quoted text clipped - 19 lines] > > Mike Mike Williams - 25 Sep 2004 10:02 GMT > Morning Mike Raining here in Doncaster so its another computer day :-) Yep. Raining here in Ormskirk too. But then I'm a bit of an odd ball, I actually like the rain :-)
> I have gone through all the code (including the transparent one) > and I would like to ask you a question re stretchBlt > You mentioned that we could use the stretchblt instead of > paintpicture. What advantage would that give me Mike None really, a least not in your current app. StretchBlt (and BitBlt and the other API graphic routines) are usually a lot faster than the equivalent VB PaintPicture method when drawing to the display or to a Picture Box, but shaving a few milliseconds off a job doesn't matter much when drawing to very slow external hardware, such as a printer. Besides, the VB PaintPicture method is very "intelligent", and it is usually more reliable when outputting stuff to a printer. If you use API graphic methods on a printer then you have to write code to first of all examine the specific attached printer to see what capabilities it has, and if it doesn't support a specific method (StretchBlt for example) then you have to write more code to perform the job differently, using one or more different functions that the printer is capable of handling. You don't have this problem with PaintPicture, because it looks after all this stuff for you.
If you ever write stuff to draw to the display though (or to picture boxes or whatever), I would definitely recommend using the API methods instead of their VB equivalents, because in such cases the speed increase is significant. Oh. One other thing. Drawing stuff into Autoredraw Picture Boxes is a commonly required task, and any such drawings (whether using VB or API methods) are actually drawn into the "off screen" Image of the picture box in memory. If you draw into an Autoredraw picture box using PaintPicture (or any of the VB graphic methods, such as Line, Circle, Pset etc) then VB will immediately trigger a "refresh" on that Picture Box, such that it's "on screen" content will be updated at the next screen update period. However, drawing into an Autoredraw picture box using the various API methods (StretchBlt, BitBlt, SetPixel etc) does NOT trigger such a refresh, so that you can take as long as you want building up a complex drawing and it will never actually be displayed in the picture box "on screen" until you specifically want it to be (using a Refresh statement). This can be very important in many cases.
Mike
Paul - 30 Sep 2004 09:41 GMT Morning Mike
Im back again with more questions please :)
Sorry ive been quiet for a few days but i have been studying that code you did, I am one of these very slow leatners but when people write me example code, i like to try and understand how it works rather than CTRL C / CTRL V.
Anyway back to my next question..
I have got the printing of the labels off to a fine art now (Thanks to Mike) with more understanding of the BitBlt than I had a few weeks ago.
To take my project a little further, I wanted to implement a print preview so that what you saw on the screen was a scaled down version on how it would apperar on the printer. Having searched Google, I see lots of links to commercial ocx but I wanted to learn the code aspect if its not to difficult to do.
Mike: Using your SetprinterOrigin example , lets asume that you have printed this.
printer.scalemode vbcentimeters
SetprinterOrigin 10,20 '10mm from the left and 20mm from the top
Printer.Print Printer.Print "This is 100mm from the left of the page" Printer.EndDoc
How would I print that to either a form or picture box which would represent an acurate print preview please
Paul
>> Morning Mike Raining here in Doncaster so its another computer day :-) > [quoted text clipped - 39 lines] > > Mike Mike Williams - 30 Sep 2004 22:28 GMT > Morning Mike. Im back again with more questions please :) > ,<snip> Using your SetprinterOrigin example, lets asume that [quoted text clipped - 3 lines] > that to either a form or picture box which > would represent an acurate print preview please The SetPrinterOrigin code will work just the same on a Picture Box as it does on the Printer, if you changed it about a little so that it needed three parameters instead of two, with the extra third parameter being the Object (Printer or Picture Box). However, it isn't very useful to do that, because the routine was really designed to alleviate the problems you might otherwise have due to the fact that the standard (0, 0) origin is the top left corner of the "printable area" of the page, which is *not* the same as the top left corner of the physical page. Picture Boxes don't have this problem. If I were you I would simply use SetPrinterOrigin 0, 0 to set the origin to the top left corner of the physical page and then use CurrentX and CurrentY to set the actual position of your printed stuff. The coordinates (0, 0) are already at the top left corner of a Picture Box, and so you don't need to mess about with it.
> To take my project a little further, I wanted to implement a print > preview so that what you saw on the screen was a scaled down > version on how it would apperar on the printer. That's fairly easy to do, but a lot depends on what "view" you wish to show the user. For the simple case of a "full page" view you can simply place a Picture Box on your Form so that it is approximately the same shape as an A4 page and so that the user can see all of it at once. Then use the following code:
Dim wide As Single, high As Single With Printer .ScaleMode = vbCentimeters wide = .ScaleX(.Width, vbTwips, .ScaleMode) high = .ScaleY(.Height, vbTwips, .ScaleMode) End With With Picture1 .Height = .Width * (high / wide) .ScaleWidth = wide .ScaleHeight = high End With
The above code will set the Picture Box up so that it looks pretty much the same shape as an A4 page (or whatever size page your printer is currently using) and you can print stuff to the Picture Box in exactly the same way that you would print it to the printer, using exactly the same x and y coordinates and exactly the same sizes. The Picture Box will then contain a more or less exact copy of what the printed page would contain, but of course the whole things would be much smaller and you can see it all on screen at once. Make sure all of your coordinates and sizes are set using floating point variables (Singles, for example) in both case (printer and picture box).
Post again if you need more help.
Mike
|
|
|