Monday, August 11, 2014

Proportional scaling of controls


With the anchor points of controls, there’s already a lot of possibilities to layout the user interface. The set of lock properties (LockTop, LockLeft, etc) of a RectControl give control over the scaling behavior of control elements when a Window changes size. In nearly all cases this is enough, but sometimes a proportional scaling of elements is best as the Window or other container control changes size.


Proportional scaling can always be done manually in the Resize events of the Window for every RectControl. To make this a bit easier to maintain, two extra methods and a property can be added to a Window or a Panel to do proportional scaling. A method to record all design time controls locations is called from the Open event of the Window or Panel. Then the method to play back the relative locations is called from the resizing events.


The RecordAllpagepanelControls method iterates through all RectControls on the Window and stores the relative location in a String.

  Dim i As Integer
  Dim ctrl As RectControl
  Dim l,t,w,h As Double
 
  Redim ControlsLocations(-1) //should be empty, in case it wasn't.

  For i = 0 To Self.ControlCount-1
   
    If Self.Control(i) IsA RectControl Then
      ctrl = RectControl( Self.Control(i) )
      l=ctrl.Left   / Self.Width
      t=ctrl.Top    / Self.Height
      w=ctrl.Width  / Self.Width
      h=ctrl.Height / Self.Height
     
      ControlsLocations.Append( Str(l) + ";" +Str(t) + ";" +Str(w) + ";" +Str(h) +";" + ctrl.Name)
    End if
   
  Next

The PlaybackPagepanelControls method reads the locations from the String and updates the positions of all stored controls to the new position relative to the current size of the Window.

  Dim i As Integer
  Dim ii As Integer
  Dim s As String
  Dim ctrl As RectControl
  Dim l,t,w,h As Double
 
  ii = 0
 
  For i = 0 To Self.ControlCount-1
   
    If Self.Control(i) IsA RectControl Then
     
      ctrl = RectControl( Self.Control(i) )
     
      s = ControlsLocations(ii)
     
      l=Val(NthField(s, ";", 1))
      t=Val(NthField(s, ";", 2))
      w=Val(NthField(s, ";", 3))
      h=Val(NthField(s, ";", 4))
     
      ctrl.Left = Round(Self.Width * l)
      ctrl.Top = Round(Self.Height * t)
      ctrl.Width = Round(Self.Width * w)
      ctrl.Height = Round(Self.Height * h)
     
      ii = ii+1 //next in ControlsLocations array
    End if
   
  Next

It would probably be even easier to create a component that performs this scaling or a component that adds this scaling to the container control it’s added to. For more detailed behavior, extra tests on the type of control can be added in the play back function of course.

Adding the methods to a simple example Window with a couple of shapes on it, these now scale proportionally.




Tuesday, May 27, 2014

ID3 Viewer (and Writer) application

A small utility program that uses the ID3 classes posted earlier to view the tags and album artwork of an MP3 file.

Complete source for the program can be downloaded here. Should work in most RealStudio or Xojo versions.

Use the menu to open a file or drop an MP3 file on the window and the program opens the file and shows the main tags. A double-click on the artwork area plays the MP3 file. Clicking on the progress bar that then appears allows to scroll to that position in the file.

The text tags can be edited, the artwork can be changed by dropping an image file on the artwork area. Use the save menu items to write an MP3 file with the updated tags.

The compiled program for a few platforms, in case you don't have RealStudio or Xojo or don't want to bother with compiling:

ID3Viewer for Mac OS X (Intel)
ID3Viewer for Mac OS X (PPC)

Free to use.

Saturday, May 10, 2014

Reading and writing ID3 tags in an MP3 file

One of the things that is fairly easy to make in RealBasic (or Xojo) is of course a small media player. The MoviePlayer component allows you to quickly build a small music player to play MP3 files that fits your own tastes and particular needs. What is a bit harder to do though, is to display the artist and title tags of an MP3 file. And slightly harder still to find a way to display any embedded album art in the ID3 tag. There is a way to get some metadata about a file via quicktime, but this is limited to basic artist and title information.

First thing to read ID3 tags is to know about the various versions of the tags. There is the version 1 (ID3V1) of tagging. That appends a fixed size block of text data to the end of the MP3 file. The more capable and now widespread tag is the ID3 version 2 with its variations 2.2, 2.3 and 2.4 (ID3V2). This is a block with text, image or any binary data of flexible size at the beginning of the file before the MP3 music data. There is quite a bit of documentation on these formats on the web, example source in other languages and of course lots of MP3 files with tags to be looked at with a Hex Editor.

There is source code in RealBasic on the web to read ID3 version 1 tags, but no source for reading the ID3V2 tags. Wanting to be able to read album art and be capable of also reading modern tags with their international characters, I made some classes to do just that for both ID3V1 and ID3V2.

These classes open the MP3 file as a BinaryStream and then parse the file byte-by-byte to see what version tag is present and extract the metadata on title, artist, etc and a Picture for the album art.


The classes are a bit basic in that they only read and keep as properties a limited set of metadata, only the items I was interested in. The classes would on the other hand be not that hard to expand to create a dictionary of all tags in a file and read any and all metadata. Another limitation is that the ID3V2Reader class only returns the first image found in the tag as the album art and ignores any additional images.

The ID3V2Reader does however know about and is robust against tags that were written by an older iTunes version that used base 256 encoded tag sizes instead of the correct base 128 encoded size. (Took a few unexpected results on a few MP3 files to clue on to that one though...) The class should also deal correctly with all the possible text encodings, including UTF16 without a BOM.

Wanting to be able to edit and update tags, also an ID3 tag writing class was made. When writing tags this requires a source file as FolderItem and a destination file (as FolderItem) to write the new file with tags into with the MP3 data from the source file. Note that any metadata of a type not supported by the class is left out and not copied from the source.


As the class structure shows, a few helper encoding and decoding functions are used. Numbers in the tag structure are e.g. encoded so that there will never be a series of 4 zero's, because that is a sync tag for the MP3 decoder (or at least something like that).

As a measure to prevent having to re-write the entire file every time a metadata element is changed, e.g. by adding a single character to the title, a padding area is defined at the end of the tag. This allows for small changes in the size of the tag without having to do a full re-write. The size of this area can be set in the ID3Writer class to a value in bytes.

A zipped archive with the source to all three classes can be downloaded here.

These should work and compile on pretty much any version of RealBasic / Real Studio / Xojo.

Thursday, April 17, 2014

Segments and printing in more than 72dpi

Printing is fairly straightforward in Realbasic (or Xojo if you will). If a call of OpenPrinterDialog returns a Graphics pointer that is not nil, then it is a simple drawing onto that Graphics object. It has the size of the available page, but of course in pixels. Probably to enable being truly cross-platform, the Realbasic printer has a resolution of 72dpi, even though the printer will be capable of much higher resolutions.

Luckily, drawing text with DrawString will draw crisp text in the high resolution of the printer. When drawing graphical shapes however the positioning is limited to the integer pixel coordinates. A widely written about solution to this is to draw the graphics on a Picture (Graphics) with a higher resolution and then use DrawPicture to draw it scaled down onto the printer Graphics. Then it will be drawn in the high printer resolution and you still can draw to actual dimensions. I.e. it is still possible to draw a line that is one inch long on paper.

By defining a magnification factor and then drawing a magnified Picture to be drawn scaled down by that same factor again on the printer's Graphics, more precise graphics is possible and the printer's (default) higher resolution is used. With the magnified Picture (BarsImage) already prepared, the menu handler for the print menu becomes simply:


  Dim h As Graphics
  h = OpenPrinterDialog
  If h<> Nil Then
    h.DrawPicture(BarsImage, 0, 0,BarsImage.Width/MagFactor, BarsImage.Height/MagFactor, 0, 0, BarsImage.Width, BarsImage.Height)
  End if
  Return True
 

This is all implemented in the small utility to draw strobe discs. Strobe discs are used to check or adjust the speed of turntables. There is no shortage of strobe disc images on the internet for download of course. This small utility allows fairly easy printing of a strobe disc with the settings you want. Source for the Strobe Maker project is downloadable, so you can tweak the design as desired.
Another item encountered was drawing speed. When drawing this particular graphics, the first obvious method tried was to use the ArcShape object and then draw a series of these rotated. (Drawing a white circle over the centre to make them bars.) This was very crisp and accurate, but really slow. The drawing of the whole shape with ~70 arc segments was slow; drawing 216 arc segments made the program freeze and think about that for almost a minute.

Oddly and fortunately drawing a FigureShape is much faster. Calculating the 4 points of the ArcShape (and accepting that the ends are straight lines) these FigureShape draws the 216 segments in just under a second on the same machine. Then drawing a white bounding circle even adds the rounded segment ends then.

Friday, December 20, 2013

Portable GreyMap

The whole family of portable pixmaps in the Netpbm family of formats have a very extensive set of tools to convert and process them. These tools are however not included as standard on many platforms you'd want to develop in RB for.

The format of the PGM file format, a greyscale bitmap image, is fairly simple and described widely on the net. Apart from being simple, it however also allows for many variations. All these possible variations make it again a bit more interesting to make a robust reader for these files.

One example of the plain-text version (type P2) is this

        P2
        # feep.pgm
        24 7
        15
        0 0  0  0  0  0  0  0  0 0  0  0  0  0  0  0  0 0  0  0  0  0  0  0
        0 3  3  3  3  0  0  7  7 7  7  0  0 11 11 11 11 0  0 15 15 15 15  0
        0 3  0  0  0  0  0  7  0 0  0  0  0 11  0  0  0 0  0 15  0  0 15  0
        0 3  3  3  0  0  0  7  7 7  0  0  0 11 11 11  0 0  0 15 15 15 15  0
        0 3  0  0  0  0  0  7  0 0  0  0  0 11  0  0  0 0  0 15  0  0  0  0
        0 3  0  0  0  0  0  7  7 7  7  0  0 11 11 11 11 0  0 15  0  0  0  0
        0 0  0  0  0  0  0  0  0 0  0  0  0  0  0  0  0 0  0  0  0  0  0  0

A quick and simple LoadFromPGM function reads the file in and stretches the greyscale values of a regular Picture object. (Blurring in the screenshot is from the scaling of the Picture.)
The LoadFromPGM method is written to be fairly forgiving on the file format. It will read one image from a PGM file, either a binary or a plaintext one. There is a lot of repetitive code, it should really be re-factored and it is not the fastest code. But then again, source is provided here so feel free :)

The source for the example PGM viewer application can be downloaded in this archive.

Tuesday, September 3, 2013

Grabbing pixels, magnifying window

With the developer tools of Apple, in the Graphic Tools, there is a small program that magnifies a small area of the screen. This can be handy to see what is happening on the pixel-level with your program as well as for pixel-perfect aligning of any graphics you are drawing in your application.

This little tool 'Pixie' runs of course in OSX. Apart from curiosity - could we do this with RealBasic - we also rather wanted this function on some older systems running OS9.

And indeed we can do this with RealBasic.


The code in the method listed below does this by doggedly asking for the color of a System.Pixel at a coordinate on the screen. With the color of the pixel of the screen then a small rectangle is drawn onto the receiving canvas to create a magnified view.


  Sub UpdateImage(Cnv As Canvas)

  Dim mX, mY As Integer // the mouse position in global coords
  Dim nX, nY As Integer // the size of the sample we are magnifying
  Dim dX, dY As Integer // the mid-point of the magnifying area
  Dim CounterX, CounterY As Integer
  Dim mag As Integer // magnification factor
  
  mag = App.MagFactor  // get from preferences 
  
  mX = System.MouseX
  mY = System.MouseY
  
  // if the pref is to only update when the mouse moved, 
  // check for the condition and bail from Sub if so 
  If App.LiveUpdating = False Then
    If mX = LastX And mY = LastY Then 
      Exit // bail
    End if
  End If
  
  // still here, so need to update our image so store the current coords
  LastX = mX
  LastY = mY
  
  
  // determine the size of the sample we need to take from Screen by
  // dividing the target canvas size by the magnification factor
  nX = Floor( Cnv.Width / mag )
  nY = Floor( Cnv.Height / mag )
  
  // then the midpoint we need to sample around
  dX = Round( nX/2 )
  dY = Round( nY/2)
  
  // then sample the color of every pixel we need to magnify and draw
  // a corresponding square onto the canvas (that was passed as param)
  For CounterX = 0 To nX
    For CounterY = 0 To nY
      magPic.Graphics.ForeColor = System.Pixel(mX+CounterX-dX, mY+CounterY-dY)
      magPic.Graphics.FillRect( CounterX*mag, CounterY*mag,mag,mag)
    Next
  Next
  
  Cnv.Refresh

  End Sub

This method is used to make a magnifying window very much like the Pixie tool. The program in this source is localized into Dutch, but we're sure you will be able to figure out what it all means and be able to change it to the language of your choice. Source is provided right here.

The source also includes handling of preferences and remembering window positions. Because it is not possible to change the type of a Window (document, floating) after it has been created, the program uses two windows and shows/hides these depending on the program state.

Activating the program with a mouse click in the system-wide floater also is a bit of a hack. Have a look at the code :)


Wednesday, June 5, 2013

Graph, just a simple graph

For RealBasic there are several Plot, Graph or graphing components to draw simple and even very complex graphs. Some of the more capable components are commercial software and indeed are on a professional level. Sometimes however all that is needed is a quick chart to show some trend or series of data in an application.

Also there are free and open source components out there with again varying levels of complexity and capability. Having used some for a while, we ended up making our own version to be very simple in use and to introduce only one class. So there is not the Graph that can include several Plots that can include several Series objects. This class or component is a single Graph object that can plot a line-graph.


All the data are copied to arrays in the Graph object, no additional files or classes are needed.

In the GraphExample project the Graph object is included and of course with all source code readable and editable. It only draws line graphs; adding bar graphs or other plot types is of course possible. The handling of the text sizing could be more elegant and the scale-values could be simpler. But it comes as source code and free to use and extend.

To figure out the current capabilities (range limiting, x-grid only etc...) do have a rummage round the example project and the Graph source.

We may even extend it ourselves.

Good luck :)