Cool stuff

BlitzMax Forums/BlitzMax Module Tweaks/Cool stuff

xacto(Posted 2004) [#1]
I've completed a round of refactorings on the BRL modules as well as some nice additions.

The broad strokes: I've extended the TXXXLoader factory pattern into a TXXXSaver factory for audio samples and pixmaps. I've also made the interfaces on TAudioSample and TPixmap more OO-esq, for folks like me who don't use the global functions much. For code clarity and maintenance I tried to make all of the global functions simple wrappers to the Type itself where applicable.

I noticed the loader factories were implemented with a simple linked list and not TList. I assumed this was a timing issue where BRL implement the loaders before TList was done/usable. Since TList is now available I refactored the custom linked lists to use TList, which made the code much more readable and smaller.

Some of the pub modules had to change, which I detail below.

Downloads:
http://www.workerbee.com/downloads/brl.mod-1.0.0.zip
http://www.workerbee.com/downloads/pub.mod-1.0.0.zip

NOTE:
- Make a BACKUP of your existing mods directory before trying to use these. I've tested them but you never know what might happen.

- If you don't know much about C or dealing with possible C compiler errors/warnings then don't try to use these.

- I've not tested these on MacOS or Linux. If someone can give them a shot I'd like to know if there are any problems.

Here is a detailed list of changes:

Changed Modules

BRL.AudioSample
- Added Load class method to TAudioSample. Redefined global LoadAudioSample in terms of TAudioSample.Load.
- Added Save method to TAudioSample.
- Added TAudioSampleSaver factory and global list of registered TAudioSampleSavers called sample_savers.
- Changed sample_loaders to use TList instead of the custom linked list.
- Changed Load to use EachIn operator on TList instead of the custom linked list walk.
- Removed unneeded _succ Field from TAudioSampleLoader as TList replaces custom linked list functionality.

BRL.Font
- Changed Global _loaders to font_loaders to be consistent with other modules following similar pattern.
- Changed font_loaders to use TList instead of the custom linked list implementation.
- Added Load method to TFont and redefined global LoadFont function in terms of TFont.Load.
- Removed unneeded _succ Field from TFontLoader as TList replaces custom linked list functionality.

BRL.FreeTypeFont
- Removed Load from TFreeTypeFont subclass and moved implementation to the TFreeTypeFontLoader subclass. This allowed the base TFont to define Load in generic terms of TFontLoaders, returning a TFont. This implementation is consistent with the way TAudioSampleLoader/TAudioSampleSaver and TPixmapLoader/TPixmapSaver are implemented.

BRL.Pixmap
- Added XFlip method to TPixmap and redefined global XFlipPixmap in terms of the pixmap instance's XFlip method.
- Added YFlip method to TPixmap and redefined global YFlipPixmap in terms of the pixmap instance's YFlip method.
- Added Mask method to TPixmap and redefined global MaskPixmap in terms of the pixmap's Mask method.
- Added Resize method to TPixmap and redefined global ResizePixmap in terms of the pixmap's Resize method.
- Added Load class method to TPixmap and redefined global LoadPixmap in terms of the class method Load.
- Added Save method to TPixmap to use the new TPixmapSaver factory.
- Added Global pixmap_savers as a TList of registered TPixmapSavers.
- Changed Global pixmap_loaders to use TList instead of the custom linked list implementation.
- Changed TPixmap.Load to use EachIn operator on TList of providers instead of the custom linked list walk.
- Remove _succ Field from TPixmapLoader as TList provides similar functionality.

BRL.TGALoader
- Removed TGAHeader and constants and moved to BRL.TGACommon for reuse in TGA.TGASaver.

PUB.LibJPG
- Added savejpg function to loadjpg.c. Probably should rename the C source file but I left it as-is for now.
- Added Extern function prototype to .bmx file.

PUB.LibPNG
- Added required Extern function prototypes to .bmx file.

New Modules

BRL.BMPSaver
- Added TPixmapSaverBMP subclass. When this module is included in your code you'll be able to save any TPixmap as a BMP file.

BRL.PNGSaver
- Added TPixmapSaverPNG subclass. When this module is included in your code you'll be able to save any TPixmap as a PNG file.

BRL.JPGSaver
- Added TPixmapSaverJPG subclass. When this module is included in your code you'll be able to save any TPixmap as a JPG file.

BRL.TGASaver
- Added TPixmapSaverTGA subclass. When this module is included in your code you'll be able to save any TPixmap as a TGA file.
- NOTE: The TGA saving support is currently weak. TGA's are written in pure PF_RGB888 format without any RLE compression. 8, 15, and 16-bit formats aren't currently supported either.

BRL.WAVSaver
- Added TAudioSampleSaverWAV subclass. When this module is included in your code you'll be able to save any TAudioSample as a WAV file.

BRL.TGACommon
- Common module for TGAHeader and constant defintions. BRL.TGALoader and BRL.TGASaver use this module.

Examples
Here's an example of loading and saving a TPixmap in a variety of formats:



Here's an example of loading and saving a WAV audio sample:



If anyone finds issues, etc., let me know.


skn3(Posted 2004) [#2]
Sounds nice, I wonder how these are going to be added (if they are)


Bot Builder(Posted 2004) [#3]
I agree, looks like some good changes. I'd prefer to wait for BRL to incorperate these changes into their modules and then syncmods. This way I can still run sample code :) I might try out some of the save commands though.


marksibly(Posted 2004) [#4]
Nice work!

You're dead right about the list/factory stuff - much of the module development work has been a real 'chicken and egg' drama.

One thing I don't agree with is adding a bunch of methods to Pixmap. I like my types as 'light' and to the point as possible, and since there are likely to be many more pixmap transformation style functions coming, Pixmap would end up with a very fat interface.

In fact, I think the pixmap utility functions like MaskPixmap, ScalePixmap etc should probably have been moved further away from pixmap (into perhaps a PixmapUtil module) not nearer.


skn3(Posted 2004) [#5]
Yeah, perhaps you could have Tpixmap and TpixmapEx (ala windows api)


xacto(Posted 2004) [#6]
@Mark:

On TPixmap:

If it's the bulk, i.e. lines of code, that adding additional methods creates then I'd suggest looking into partial types. This is a compiler detail but, from what I can see, should be fairly easy to implement. The basic jist is that you can define the same type in many files and the compiler merges them together before compiling. This allow the programmer to spread code across multiple files for easier reading.

If your concern is having the methods on TPixmap, I'd wonder why you even bothered with OO in the first place. The beauty of OO is that you have a "thing" (noun) and you can ask it to do stuff for you. The messages (verbs) you can send to an object should relate to what that object (noun) is. It seems very natural to me that I'd ask a TPixmap to YFlip, Resize, Mask, or Save itself.


Curtastic(Posted 2004) [#7]
dude, edit that post. I agree with you, but mark knows what OO is


marksibly(Posted 2004) [#8]
Hey, no problems - OO coding style is fun to debate!

As for 'why I bothered with OO in the first place', the stream system, pixmap loaders and (unused as yet) 'virtual' Max2D design would have been a *lot* harder to implement without OO. And of course, OO is also useful for application developers.

My main problem with bunging everything 'inside' TPixmap is that its the first step down a road that leads to a huge, unmanageable beast of a type with what's called a 'fat interface' (see http://www.codeproject.com/gen/design/nfOORules.asp under 'Interface pollution').

I have seen such monster types arise in several projects - frequently in 'swiss army knife' style string classes - and try hard to avoid them in my own work.

Of course, just what methods TPixmap should have (ie: its responsibilities) can be debated all night long! However, it was designed mainly to deal with 'offline' pixel buffers, and wasn't really intended to be an 'uber image' thing.


Kanati(Posted 2004) [#9]
My main problem with bunging everything 'inside' TPixmap is that its the first step down a road that leads to a huge, unmanageable beast of a type with what's called a 'fat interface' (see www.codeproject.com/gen/design/nfOORules.asp under 'Interface pollution').


For some nice examples of this... check out the microsoft foundation classes. :)


Curtastic(Posted 2004) [#10]
ok cool

but it also says

No Global Variables. Because misbehaving modules may write erroneous data to such global variables whose effect can be felt in many places throughout the program. Sometimes Global variables are useful e.g. cout and cin in c++. If they do not violate the open close principle then sometimes they are worth the style violation

so ScalePixmap() would be global and pixmap.scale() would be private


What if you could add methods to a type without actually changing the code in between Type and EndType ?

Wouldnt this solve everything?
'PixmapUtilmodule.bmx

Method TPixmap.Scale(x,y)
...
EndMethod



[edit: I guess that is kind of unstructured]


marksibly(Posted 2004) [#11]

so ScalePixmap() would be global and pixmap.scale() would be private



ScalePixmap() is a function, not a variable - nothing's gonna accidentally 'write' to it!


What if you could add methods to a type without actually changing the code in between Type and EndType ?



This is an awesome feature, and some languages support it fully, eg: 'Nice' - don't know if that's still around though. Objective C also has support for something like this. Java, C++ and C# don't.

The main problem is that it's a complex feature to implement compiler-wise, and the runtime performance would probably be less than stellar.


Curtastic(Posted 2004) [#12]
okay I thought "variable" could mean anything in a class.
only because it says cout is a variable
Sometimes Global variables are useful e.g. cout and cin in c++.


[edit: I am confused now]


dynaman(Posted 2004) [#13]
>>For some nice examples of this... check out the microsoft foundation classes. :)

They can get away with it, same as Java, because they load the foundation classes once and then all the programs made with them link to the one instance. BlitzMax on the other hand will be loading the classes with each new compiled exe. Neither method is "better" then the other, just more appropriate in different circumstances. (although for games I prefer the blitzmax way - everything can be wrapped in one exe)


xacto(Posted 2004) [#14]
First, I meant no disrespect in my response to Mark. I apologise if my tone implied any. It appears Mark wasn't offended, so I'll rest easy. :-> BlitzMax is an awesome achievement and I have a great deal of respect for Mark and crew.

@Coorrae:
Your suggestion is essentially what partial types boil down to.

http://www.developer.com/net/net/article.php/2232061

@Mark:


I have seen such monster types arise in several projects - frequently in 'swiss army knife' style string classes - and try hard to avoid them in my own work.



I understand better now. See, my OOP background is in Smalltalk so I suppose I'm used to the "swiss army knife" style classes.


Of course, just what methods TPixmap should have (ie: its responsibilities) can be debated all night long! However, it was designed mainly to deal with 'offline' pixel buffers, and wasn't really intended to be an 'uber image' thing.



Fair enough. The intention of the original designers is very important in molding any class and I defer to you for those decisions. However, since I'd really like to use BlitzMax in our upcoming projects I'd like to be able to mold the interfaces to meet our internal style.

With that said, the only issue that I haven't seen addressed is some kind of branching/override system for modules. Let me explain. Say I want to change BRL.pixmap, adding some things in that BRL doesn't want to include. I don't want to extend TPixmap because I need the base class to provide a certain interface. With the way bmk and the mods directory is structured today I'd have to:

1. Backup my changes to the BRL.pixmap mod.
2. Run syncmods. We'll assume an update to BRL.pixmap is downloaded.
3. Now I have to merge my changes back into BRL.pixmap from the backup.

We need a mods search directory feature on bmk. This would accept a colon separated list of paths for mods. Once bmk finds a particular mod along the search path it stops looking. This way I could copy BRL.pixmap into a custom-mods directory, make our changes and be safe when I run syncmods, which would always update the main mods directory.

When we're ready we could merge in updates from BRL into our custom-mods. The difference here is that we wouldn't be forced to accept the changes every time.

In effect this is a "poor mans branch". I only offer this as one possible solution; there are others.


Robert(Posted 2004) [#15]
@xacto

What are your reasons for not wanting to extend TPixmap? Every function that accepts a TPixmap will accept your derived class as well - so I don't quite see the problem.


skidracer(Posted 2004) [#16]
You may want to consider a pixmaputil module instead.

If you modify existing pixmaps you will impact on other parts of BlitzMax like the collision system and the texture / mipmap management stuff which won't magically inherit your modified pixmap data (which is why I presume Mark made it Final).

For instance a software scale algorithm will need to allocate a new array instead so the fact it is in the form that creates a new pixmap from a source pixmap is a natural design rather than extending the behavior of Pixmap.


xacto(Posted 2004) [#17]
@Robert

It's true that if I extended from TPixmap, say TPixmapEx, that every function/method that expected a TPixmap would accept a TPixmapEx. However, those receivers would only see a TPixmap; not a TPixmapEx. Any interfaces specific to TPixmapEx would not be available to the receiver.

For a majority of situations a TPixmapEx will probably be just fine but there may be cases where we want to add/change the base class interface. My previous post was using TPixmap as an example of keeping customer changes and BRL changes distinct from each other.


Robert(Posted 2004) [#18]
However, those receivers would only see a TPixmap; not a TPixmapEx. Any interfaces specific to TPixmapEx would not be available to the receiver.


That actually isn't a problem since BlitzMAX supports run-time type identification. You can check to see if a passed TPixmap object is actually a TPixmapEx (or type derived from TPixmapEx), cast the object to a TPixmapEx object, and then call TPixmapEx methods on it.

Type Obj
	
End Type 

Type ObjDrv Extends Obj
	Method update()
		fld :+ 2
	End Method
	
	Field fld
End Type

Global a:Obj=New Obj
Global b:ObjDrv=New ObjDrv

updateObj(a)
updateObj(b)

Print b.fld


Function updateObj(instObj:Obj)
	If ObjDrv(instObj)
		Print "You passed an ObjDrv!"
		(ObjDrv(instObj)).update()
	End If
End Function



ImaginaryHuman(Posted 2004) [#19]
Umm. .. .are there any options for those picture savers, such as jpeg quality? png interlacing and compression techniques?


xacto(Posted 2004) [#20]
I'm in the process of writing a function that will set the global JPEG picture quality. It'll be an integer from 0 (awful) to 100 (nearly perfect). It's currently hard coded at 50, which yields a decent image.

LibPNG only supports one compression algorithm at the moment so it's set to that default. As for PNG interlacing I'd have to do some investigation on how to best support this. Interlacing isn't something I need so I might not spend too much time on it.

Note: There are several minor bug fixes on the way as well.


Kanati(Posted 2004) [#21]
I would love to use these... But this just illustrates the problem I pointed out in another thread. We're changing official modules that will be broken again should BRL update the modules on their end and shove them down in a synch.


RexRhino(Posted 2004) [#22]
Yeah, I would like to use the Audio saver methods, big time... But unless the changes come down the official pipeline with a synch, I am not really going to mess with them.


xacto(Posted 2004) [#23]
I plan on manually merging updates from BRL into my mods directory. I know this will take extra work on my part but having the source code is just too much a temptation to ignore.

I've added the beta mods directory to Vault (http://www.sourcegear.com). I'll do a syncmods whenever necessary, and use Vault to help merge the changes into my existing custom mods.

Short of BRL adding a quasi-SCM into BMX I don't see any other way to do it.


Russell(Posted 2004) [#24]
One thing that OO can promote if Classes\Types are overstuffed, unintentionally, is bloat. Ever wonder how a program such as Stuff-It for Windows, which is simply a compressor\decompressor, can possibly be 12.5 megabytes in size when other programs (say for DOS...) are in the <100k size range? Bloat is your answer.

I support any company or compiling method that avoids this.

.Net, MFC, OWL, etc can make programming easier in some respects, but there is a price to pay...

Too bad BMax can't (yet) strip the BARE essential functions from the modules to compile a given source to its absolute minimum size. This would be a nice feature, eh? ;)

Russell

p.s. I, too, would like to know what the standard procedure is for modules to be added\modified and becoming part of the 'official' package. I seem to remember that this was a little bit of a problem with the Amiga version of Blitz 2 (functions being added by users unofficially, etc). Maybe we could take a vote? ;)


Warren(Posted 2004) [#25]
Ugh, please, no more voting. BRL needs to make a decision and tell us to live with it.


Kanati(Posted 2004) [#26]
Easiest way would be to just make the BRL mods untouchable. You mess with them you are on your own. Don't distribute anything you do to them.

That will promote people making their own mods in PUB instead of mucking about in the "official" mods.


Hotcakes(Posted 2004) [#27]
Wasn't that the whole idea to begin with?!? =] Hence why syncmods works the way it does. Hence why people are asking these questions in this thread.

Don't make hardcore adjustments to the official mods and you won't lose anything! Simple as that. Now live with it ;]


Dreamora(Posted 2004) [#28]
yepp that is the use of inheritance ... extend stuff of whichs implementation you don't need to know anything ( -> brl modules ) beside the fact that they exist and specific functions/methods exist.

But stuff like this here might help to extend the official modules :)


Bot Builder(Posted 2004) [#29]
.Net, MFC, OWL, etc can make programming easier in some respects, but there is a price to pay...
Actually, .net makes your exes really small since most of the code is in the .net framework which is required.

Too bad BMax can't (yet) strip the BARE essential functions from the modules to compile a given source to its absolute minimum size. This would be a nice feature, eh? ;)
I agree. It'd be cool if when it compiles modules it would also take all the functions, types, and variables, compile into .a files, with each one importing all the ones they rely on. Then, if you run the compiler in "Ultimate Release" mode it will figure out which variables, types, and functions its using, and then import those specific files. You could also have "Super Ultimate Mode", which would take it even further, and take out all the unused methods and fields in classes, generate the source file, and compile that. Would be pretty awesome ;0


Russell(Posted 2004) [#30]
@BotBuilder:
I agree that the .net framework makes the main exe smaller, but there are many PCs that don't have this framework installed, so therefore this has to be downloaded or installed from disk. The overall application size is greatly increased.

That being said, I suppose it's something we have to live with on the Windows platform, since this seems to be the way they are going (making PC programs even less portable across other platforms). Object Oriented programming can still encourage bloat and does seem to slow programs down (Just have a look at anything written by Adobe - sloooooow <at least on Windows>). One just has to be aware of that possibility when writing their OO programs, unless they don't mind ;)

I like the "Super Ultimate Edition" idea, and feel that this would not be all that difficult to employ. A really smart module parser would be an interesting programming challenge... ;)

Russell


Kanati(Posted 2004) [#31]
since this seems to be the way they are going (making PC programs even less portable across other platforms).


That's incorrect. .NET is tons more portable than previous windows software since .net frameworks and runtimes are being or have been created for other operating systems. Unless you write unmanaged code that plays about in the OS specific API, you should be able to compile with very few changes your C# program on any platform that has a framework.

Object Oriented programming can still encourage bloat and does seem to slow programs down


It can... In much the same way that GOTO and GOSUB encourages poor programming. It's something the programmer has to overcome though. Not a fault of the language itself. I'm not afraid of a little bloat here and there though. I personally could care less if a program is 100k or 100meg as long as it works and works well for the purpose intended.

Kanati


Warren(Posted 2004) [#32]
Seriously. Hard drive space is so cheap these days, I really don't care about a few extra meg here and there. Whatever. Just make my programming life easier...


Russell(Posted 2004) [#33]
@Kanati
OMG! I am genuinely surprised if Microsoft has actually open-sourced the .net framework as this would be a huge departure from their usual "our way or the highway" mentality.

Programs in general have, I suspect, been steadily running slower and slower (including subsequent versions of Windows), but we've not really noticed because hardware has more than kept up with the change. Windows would be completely unbareable if Intel\AMD had not increased their efficiency and speed at almost exponential rates. Run Windows XP on a 90mhz Pentium with 64MB to see what I mean... (Windows 95\98 run at a respectable speed on this same system).

@WarrenM:
That's no excuse to write overbloated, inefficient and slow software. But, I'll admit we are a lazy bunch. We will accept anything, as you say, that makes our lives easier.
If you intend on distributing software you've created over the internet, you can not assume everyone has a broadband connection (yet), and therefore distribution size is a factor to be considered. And when your program is compared to a competitor's which is half the size and 30% faster with the same features...

Russell


Kanati(Posted 2004) [#34]
In the US at least it was reported recently that more homes now have broadband than dialup. Broadband is finally overtaking those nasty evil 56k modems. :)