[C++] opzet bitmap-class

Pagina: 1
Acties:

  • JeromeB
  • Registratie: September 2003
  • Laatst online: 19-03 22:07
Momenteel ben ik bezig met een bitmap-class. Dat lukt best aardig, maar ik twijfel over mijn opzet. Mijn huidige opzet is alsvolgt:

opzet 1:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class bitmap
{
    protected:
        unsigned long int _width;
        unsigned long int _height;
        rgba** _pixelgrid;

    public:
        bitmap();
        bitmap(unsigned long int width, unsigned long int height);

        ~bitmap();

        // Device Independent Bitmaps (Microsofts Paint file format):
        bool loadDIB(char* filename);
        bool saveDIB(char* filename);

        // PiCture eXchange (ZSofts PC Paintbrush file format):
        bool loadPCX(char* filename);
        bool savePCX(char* filename);

        unsigned long int getWidth();
        unsigned long int setWidth(unsigned long int width);
        
        unsigned long int getHeight();
        unsigned long int setHeight(unsigned long int height);

        rgba getPixel(unsigned long int x, unsigned long int y);
        bool setPixel(unsigned long int x, unsigned long int y, rgba color);
};

Voorlopig wil ik ondersteuning voor het BMP/DIB-formaat en het PCX-formaat. Later wil ik dit uitbreiden met andere formaten. Dan zal ik dus meer member-functies moeten toevoegen aan de class. Dat bevalt mij niet.

Ik zat te denken om de volgende opzet te gebruiken. Ik kan de load en save functies nu in een apart bestand zetten. Dat bestand hoef ik alleen toetevoegen als ik die functies ook daadwerkelijk nodig heb. Als ik bijvoorbeeld alleen met DIBs wil werken dan heb ik niet de overhead van de PCX-functies.

opzet 2:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// bitmap.h
#ifndef __BITMAPJE
#define __BITMAPJE
class bitmap
{
    protected:
        unsigned long int _width;
        unsigned long int _height;
        rgba** _pixelgrid;

    public:
        bitmap();
        bitmap(unsigned long int width, unsigned long int height);
        
        ~bitmap();

        unsigned long int getWidth();
        unsigned long int setWidth(unsigned long int width);
        
        unsigned long int getHeight();
        unsigned long int setHeight(unsigned long int height);

        rgba getPixel(unsigned long int x, unsigned long int y);
        bool setPixel(unsigned long int x, unsigned long int y, rgba color);
};
#endif

C++:
1
2
3
4
#include <bitmap.h>
// Device Independent Bitmaps (Microsofts Paint file format):
bool loadDIB(char* filename, bitmap& bmp);
bool saveDIB(char* filename, const bitmap& bmp);

C++:
1
2
3
4
#include <bitmap.h>
// PiCture eXchange (ZSofts PC Paintbrush file format):
bool loadPCX(char* filename, bitmap& bmp);
bool savePCX(char* filename, const bitmap& bmp);


Een andere mogelijkheid zou het volgende zijn:

opzet 3:
C++:
1
2
3
4
5
6
7
#include <bitmap.h>
// Device Independent Bitmaps (Microsofts Paint file format):
struct DIB : public bitmap
{
    bool load(char* filename);
    bool save(char* filename);
};

C++:
1
2
3
4
5
6
7
#include <bitmap.h>
// PiCture eXchange (ZSofts PC Paintbrush file format):
struct PCX : public bitmap
{
    bool load(char* filename);
    bool save(char* filename);
};

Het probleem met deze opzet is dat het bijvoorbeeld moeilijker is om een DIB te laden en vervolgens als PCX opteslaan.

mijn vraag: Welke opzet vinden jullie het best of kiezen jullie liever een andere opzet?

PC load letter? What the fuck does that mean?


  • MisterData
  • Registratie: September 2001
  • Laatst online: 09-04 12:07
Ik zou zeggen: kijk eens naar hoe andere libraries, zoals GDI+ (zoeken op MSDN naar Gdiplus) het oplossen? :)

Ik geloof dat in GDI+ er in de Image-class (of Bitmap) een methode LoadFile(const wchar_t* filename) zit ofzo. Die zoekt dan aan de hand van de extensie en/of andere info uit welke 'decoder' er moet worden gebruikt. Dat kun je ook nog maken: een factory die uitzoekt welke decoder bij een file hoort.. Het voordeel is dat je zelfs at runtime decoders kunt toevoegen (bijvoorbeeld met een DLL) (vooropgesteld dat je onder Windows werkt).

[ Voor 67% gewijzigd door MisterData op 13-11-2005 13:33 ]


  • writser
  • Registratie: Mei 2000
  • Laatst online: 16-04 20:35
Ik zou de load-functies zo maken dat hij at runtime bepaalt wat voor type plaatje wordt geladen. Eventueel kun je nog het plugin systeem van mrData overnemen, maar dat lijkt me erg veel werk. Kijk voor een aardig voorbeeld van een image class hier: http://doc.trolltech.com/4.0/qimage.html. Ik neem aan dat je voor een spelletje o.i.d. wat aan het experimenteren bent, anders zou je wel gebruik maken van een standaardpakket. misschien heb je wat aan dit project: http://www.libsdl.org/projects/SDL_image?

Je tweede opzet vind ik niet zo handig. Je zet specifieke functies in aparte headers, waardoor iemand die jouw 'library' gaat gebruiken zich van te voren al moet afvragen welke bestandsformaten hij gaat gebruiken, en welke headers hij daarvoor moet includen. Dat is juist functionaliteit die je in je klasse wilt hebben!

Over het saven van plaatjes: Op de een of andere manier moet de gebruiker toch kunnen specificeren hoe hij zijn plaatje wil opslaan. Bovendien zijn er voor elk bestandsformaat weer verschillende opties, denk aan het al dan niet gebruiken van RLE voor bitmaps, het specificeren van metadata voor PNG's etc. Je ontkomt er niet aan om hier allemaal verschillende functies voor te maken. Vraag jezelf af, heb je dit allemaal wel nodig? Ik ben benieuwd wat je precies aan het maken bent.

Oke, dit was een chaotische post op een brakke zondagochtend. Hopelijk heb je er wat aan :P

ps: Als je werkt met c++, gebruik dan const std::string & en geen const char *

Onvoorstelbaar!


  • MisterData
  • Registratie: September 2001
  • Laatst online: 09-04 12:07
[offtopic]
Sowieso zou dit:
C++:
1
bool loadPCX(char* filename); 

moeten zijn:
C++:
1
bool loadPCX(const char* filename); 


vooropgesteld dat je filename (de inhoud van de filename-string) niet wijzigt :)

  • whoami
  • Registratie: December 2000
  • Laatst online: 00:06
Jouw eerste opzet vind ik zo geen goed idee.
Je hebt nu één class, met daarin verschillende methods die elk een ander type afbeelding behandelen.
Dat wil dus zeggen dat je op den duur één grote complexe class krijgt, die moeilijk uit te breiden, en moeilijk te onderhouden is.

De best opzet is imho gewoon een abstracte class 'Image' (oid), die een aantal methods implementeert die voor ieder type afbeelding gewoon hetzelfde zijn.
Daarnaast heb je dan nog een aantal abstracte members die specifiek zijn die je in je concrete subclasses implementeert.

Wat je echter ook kunt doen, is het 'laden v/d image', en de afbeelding en z'n functionaliteit gescheiden houden.
Je hebt dan bv één 'image' class, en verschillende 'image-loader' classes: voor ieder formaat (pcx, jpg, ...) één, die allemaal een image returnen.
(Of dat mogelijk is, en of het handig is in deze context weet ik wel niet zo goed)

https://fgheysels.github.io/


  • JeromeB
  • Registratie: September 2003
  • Laatst online: 19-03 22:07
MisterData schreef op zondag 13 november 2005 @ 13:31:
Ik zou zeggen: kijk eens naar hoe andere libraries, zoals GDI+ (zoeken op MSDN naar Gdiplus) het oplossen? :)
Ik heb niet veel ervaring met GDI/GDI+, maar volgensmij is de bitmap-class gebasseerd op de DIB. Dat is voor mij niet helemaal wenselijk. Ik wil namenlijk ook kunnen werken met plaatjes die een andere opzet hebben.
MisterData schreef op zondag 13 november 2005 @ 13:31:
Ik geloof dat in GDI+ er in de Image-class (of Bitmap) een methode LoadFile(const wchar_t* filename) zit ofzo. Die zoekt dan aan de hand van de extensie en/of andere info uit welke 'decoder' er moet worden gebruikt. Dat kun je ook nog maken: een factory die uitzoekt welke decoder bij een file hoort.. Het voordeel is dat je zelfs at runtime decoders kunt toevoegen (bijvoorbeeld met een DLL) (vooropgesteld dat je onder Windows werkt).
writser schreef op zondag 13 november 2005 @ 14:04:
Ik zou de load-functies zo maken dat hij at runtime bepaalt wat voor type plaatje wordt geladen. Eventueel kun je nog het plugin systeem van mrData overnemen, maar dat lijkt me erg veel werk. Kijk voor een aardig voorbeeld van een image class hier: http://doc.trolltech.com/4.0/qimage.html. Ik neem aan dat je voor een spelletje o.i.d. wat aan het experimenteren bent, anders zou je wel gebruik maken van een standaardpakket. misschien heb je wat aan dit project: http://www.libsdl.org/projects/SDL_image?
Het is inderdaad wel handig dat at runtime het bestandsformaat wordt bepaalt. Dit kan ik bij mijn 1e opzet heel makkelijk implementeren, maar mijn 1e opzet is niet flexibel. Er ontstaat dan 1 grote class waar ik steeds weer member-functies aantoe moet voegen.

Kunnen jullie mij uitleggen hoe je zo'n plugin-systeem zou opzetten. Ik heb totaal geen idee hoe ik zoiets zou moeten doen. Overigens wil ik mijn bitmap-class het liefst platform-onafhankelijk maken. DLL's zijn dus den boze.
writser schreef op zondag 13 november 2005 @ 14:04:
Je tweede opzet vind ik niet zo handig. Je zet specifieke functies in aparte headers, waardoor iemand die jouw 'library' gaat gebruiken zich van te voren al moet afvragen welke bestandsformaten hij gaat gebruiken, en welke headers hij daarvoor moet includen. Dat is juist functionaliteit die je in je klasse wilt hebben!
Hier twijfel ik nog over. Het is waar dat iemand vantevoren al moet weten welke bestandsformaten hij gaat gebruiken, maar wat is daar mis mee? Ik denk vaak nog niet zo Object Oriented, misschien twijfel ik daardoor.
writser schreef op zondag 13 november 2005 @ 14:04:
Over het saven van plaatjes: Op de een of andere manier moet de gebruiker toch kunnen specificeren hoe hij zijn plaatje wil opslaan. Bovendien zijn er voor elk bestandsformaat weer verschillende opties, denk aan het al dan niet gebruiken van RLE voor bitmaps, het specificeren van metadata voor PNG's etc. Je ontkomt er niet aan om hier allemaal verschillende functies voor te maken. Vraag jezelf af, heb je dit allemaal wel nodig? Ik ben benieuwd wat je precies aan het maken bent.
Ik wilde een extra argument (char flags[] ofzo) toevoegen aan aan mijn save-functies. Hier moet ik nog eens goed over nadenken.

Ik heb wel een paar leuke ideeën, maar niet een concreet project waaraan ik werk. Mijn bitmap-class is gewoon een experiment. Ik wil mij graag verdiepen in 2D graphics (bestandsformaten, compressie-algoritmes, figuurtjes plotten, vul-algoritmes, transformaties en als mijn wiskunde wat verder gevordert is misschien ook filter-technieken, lightning- en schaduw-effecten).
writser schreef op zondag 13 november 2005 @ 14:04:
ps: Als je werkt met c++, gebruik dan const std::string & en geen const char *
MisterData schreef op zondag 13 november 2005 @ 14:25:
[offtopic]
Sowieso zou dit:
C++:
1
bool loadPCX(char* filename); 

moeten zijn:
C++:
1
bool loadPCX(const char* filename); 


vooropgesteld dat je filename (de inhoud van de filename-string) niet wijzigt :)
Bedankt voor de tips. Ik heb nog wel eens moeite met dat soort dingetjes.
whoami schreef op zondag 13 november 2005 @ 14:28:
Jouw eerste opzet vind ik zo geen goed idee.
Je hebt nu één class, met daarin verschillende methods die elk een ander type afbeelding behandelen.
Dat wil dus zeggen dat je op den duur één grote complexe class krijgt, die moeilijk uit te breiden, en moeilijk te onderhouden is.

De best opzet is imho gewoon een abstracte class 'Image' (oid), die een aantal methods implementeert die voor ieder type afbeelding gewoon hetzelfde zijn.
Daarnaast heb je dan nog een aantal abstracte members die specifiek zijn die je in je concrete subclasses implementeert.
Dat lijkt een beetje op mijn 3e opzet (hoewel mijn bitmap dan geen abstracte class is). Het probleem daarbij is dat ik moeilijk een load functie kan maken die at runtime het bestandsformaat bepaalt. Een ander probleem is het laden van het ene formaat en het vervolgens opslaan als een ander formaat.
whoami schreef op zondag 13 november 2005 @ 14:28:
Wat je echter ook kunt doen, is het 'laden v/d image', en de afbeelding en z'n functionaliteit gescheiden houden.
Je hebt dan bv één 'image' class, en verschillende 'image-loader' classes: voor ieder formaat (pcx, jpg, ...) één, die allemaal een image returnen.
(Of dat mogelijk is, en of het handig is in deze context weet ik wel niet zo goed)
Dat volg ik niet helemaal. Wil je dat iets uitgebreider uitleggen?

PC load letter? What the fuck does that mean?


  • writser
  • Registratie: Mei 2000
  • Laatst online: 16-04 20:35
Dat volg ik niet helemaal. Wil je dat iets uitgebreider uitleggen?
Ik wilde een extra argument (char flags[] ofzo) toevoegen aan aan mijn save-functies. Hier moet ik nog eens goed over nadenken.
Kijk, je zit nu met het volgende probleem. Stel je hebt een mooie klasse 'image' gemaakt die prima werkt met BMP en PCX. Deze compileer je en gebruik je in je projecten. Werkt allemaal prima!
Maar een jaar later besluit je dat je ook PNG wil kunnen lezen en opslaan. Je moet je nu weer gaan verdiepen in de code van je image class. Voor het opslaan moet je de char flags[] aanpassen, want voor PNG zijn er bepaalde speciale opties die je wil gebruiken. Kortom, je moet je hele klasse herschrijven. Wat whoami bedoelt is een constructie als:
code:
1
2
3
4
5
6
class Image;
class BmpImage : Image;

Image * image = BmpImage::load("foo.bmp");
// doe dingen met image.
BmpImage::save("bar.bmp");

Wil je nu ook PNG kunnen laden, dan hoef je niet de hele image class te veranderen, maar alleen een nieuwe klasse PngImage te maken, die alleen de functies verandert die nuttig zijn voor PNG. Dus:
code:
1
2
3
4
5
6
7
class Image;
class BmpImage : Image;
class PngImage : Image;

Image * image = BmpImage::load("foo.bmp");
// doe dingen met image.
PngImage::save("bar.png", specialePngOptie = 69);

Je code wordt op die manier veel beter herbruikbaar.

Onvoorstelbaar!

Pagina: 1