Kihagyás

Modularizáció: dinamikus linkelés, plug-in rendszer

Adott több képmanipuláló program, modularizálva, statikusan linkelve. Linkeljük dinamikusan, használjuk dinamikusan, majd plug-in szerűen.

A modularizáció témához tartozó összes forrás egyben itt tölthető le.

PNM fájlok

A feladathoz tartozó programok PNM formátumú (pontosabban a PNM formátumcsaládba tartozó) képekkel dolgoznak. Néhány mintakép:

Színes mintaképek (PPM)

Eredeti színes Módosított színes

Szürkeárnyalatos mintaképek (PGM)

Eredeti szürke Módosított szürke

Fekete-fehér mintaképek (PBM)

Eredeti fekete-fehér Módosított fekete-fehér

Dinamikus linkelés

Megnéztük a saját lib-ek statikus linkelését, és a teljesen statikus linkelést is. Most akkor nézzük meg, hogy az eddig statikusan linkelt saját könyvtárainkat hogyan tudjuk dinamikusan linkelni.

Az alábbi Makefile-ok segítségével próbáljuk ki a teljesen dinamikus linkelést. A teljes csomag tömörítve innen tölthető le. A központi Makefile-ban nincs változás.

A dinamikus linkeléshez a lib-et is kicsit másként kell fordítani. Statikus linkelés esetén a linker fordítási időben egymás mellé tudja pakolni a függvényeket, így fordítási időben tudni fogja nem csak azt, hogy melyik függvény milyen memóriacímen lesz elérhető, de azt is, hogy az egyes utasításainak, változóinak mi lesz a címe. Ezért a fordító nyugodtan generálhat olyan kódot, amelyben abszolút címek szerepelnek, ezeket majd a linker beírja a gépi kódú utasítássorozat megfelelő helyeire. A dinamikus linkelésnél viszont csak a betöltésnél derülnek ki ezek a címek, ezért olyan kódot (Position Independent Code) kell generálni, ami bárhová betöltve működik, nincsenek benne abszolút címek.

A dinamikus lib betöltésekor nem lehetne korrigálni a címeket?

Jó kérdés, és részben meg is történik. A dinamikus linkelés viszont nem ennyire egyszerű, a virtuális memória, amit a modern operációs rendszerek használnak, bekavar.

A virtuális memória azt jelenti, hogy minden program úgy látja a memóriát, mintha egyedül használná azt, és az operációs rendszer fogja a fizikai memória darabjait a virtuális memória darabjaihoz hozzárendelni. Így fordulhat elő az, hogy a dinamikusan linkelt függvények fizikailag valóban csak egy példányban vannak a memóriában, de az operációs rendszer ezt a fizikai tartományt az egyik programnak az A virtuális címéhez, a másiknak a B virtuális címéhez rendeli. Mivel mindegyik program a saját virtuális címtartományában dolgozik, és az objektum kódja mindkettőben ugyanaz, ha ide abszolút címek lennének beírva, akkor vagy az egyik program működne jól, vagy a másik (esetleg egyik sem). A függvények címeinél ez meg van oldva, de "tetszőleges" címre megoldani nem lenne praktikus.

A dinamikus library fordítása és elkészítése az alábbi Makefile segítségével történik.

A libpnm alkönyvtár

libpnm/Makefile:

 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
LIB:=libpnm.so
SRCS:=pnm-type.c pnm-manipulation.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=$(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall -fPIC

all: $(LIB)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(LIB): $(OBJS)
    $(CC) -shared -Wl,-soname,$@ -o $@ $^

clean:
    rm -rf $(LIB) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

A programok linkelésénél is jelezni kell, hogy dinamikusan szeretnénk linkelni.

A pnm-info alkönyvtár

pnm-info/Makefile:

 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
PROG:=pnm-info
SRCS:=pnm-info.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
LDFLAGS:=-rdynamic -L../libpnm $(LDFLAGS)
LDLIBS:=-lpnm $(LDLIBS)

all: $(PROG)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@

clean:
    rm -rf $(PROG) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

A pnm-manipulate alkönyvtár

pnm-manipulate/Makefile:

 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
PROG:=pnm-manipulate
SRCS:=pnm-manipulate.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
LDFLAGS:=-rdynamic -L../libpnm $(LDFLAGS)
LDLIBS:=-lpnm $(LDLIBS)

all: $(PROG)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@

clean:
    rm -rf $(PROG) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

A pnm-fuzz alkönyvtár

pnm-fuzz/Makefile:

 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
PROG:=pnm-fuzz
SRCS:=pnm-fuzz.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
LDFLAGS:=-rdynamic -L../libpnm $(LDFLAGS)
LDLIBS:=-lpnm $(LDLIBS)

all: $(PROG)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@

clean:
    rm -rf $(PROG) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

A futtatáskor még egy dolgunk van. Mivel a program nem tartalmazza a library-t, csak hivatkozik rá, azt az operációs rendszernek meg kell keresnie. Hasonlóan ahhoz, ahogy egy programot a PATH környezeti változóban megadott helyeken fog keresni, a dinamikus könyvtárakat az LD_LIBRARY_PATH környezeti változóban megadott helyeken keresi majd. Szóval ezt úgy kell beállítani, hogy a libpnm.so (shared object) fájlt tartalmazó könyvtár benne legyen.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
make
pushd pnm-info
./pnm-monolit-info ../../torony-o.ppm
LD_LIBRARY_PATH=../libpnm:$LD_LIBRARY_PATH ./pnm-monolit-info ../../torony-o.ppm
popd
pushd pnm-monolit-manipulate
LD_LIBRARY_PATH=../libpnm:$LD_LIBRARY_PATH ./pnm-monolit-manipulate ../../torony-o.ppm -PPM inverted.ppm -i
popd
pushd pnm-fuzz
LD_LIBRARY_PATH=../libpnm:$LD_LIBRARY_PATH ./pnm-fuzz ../../torony-o.ppm fuzzed.ppm
popd
make clean

Alternatív lib használata

Miért jobb még (a korábban elhangzottakon túl) a dinamikus linkelés mint a statikus? Készítsünk a libpnm helyett egy alternatív megvalósítást (alterlib), és használjuk ezt (is) anélkül, hogy újrafordítanánk a futtatható programotkat. A teljes csomag tömörítve innen tölthető le.

Valójában csak egy új alterlib könyvtárat kell hozzáadnunk, és csak a .c fájlokat (az implementációt) kell átírni. Mivel a header fájlok ugyanazok, azokat a "régi" libpnm könyvtárból include-oljuk.

A fő Makefile

Makefile:

1
2
3
4
5
6
SUBDIRS:=libpnm alterlib pnm-info pnm-manipulate pnm-fuzz

all clean:
    @for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir $@; done

.PHONY: all clean

Az alterlib alkönyvtár

alterlib/Makefile:

 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
LIB:=libpnm.so
SRCS:=pnm-type.c pnm-manipulation.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall -fPIC

all: $(LIB)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(LIB): $(OBJS)
    $(CC) -shared -Wl,-soname,$@ -o $@ $^

clean:
    rm -rf $(LIB) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

alterlib/pnm-type.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
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#include "pnm-type.h"

#include <stdio.h>
#include <string.h>
#include <malloc.h>

#define UNUSED(X) (void)(X)
#define FORALLPIXELS(P,I) for (unsigned int I = 0, __pxcnt = P->width * P->height; I < __pxcnt; ++I)
#define LOGME printf("Executing function %s\n", __FUNCTION__);

rgbpic_t *rgbpic_load(const char *fname) {
    LOGME
    rgbpic_t *pic = NULL;
    UNUSED((pic = rgbpic_load_PBM(fname)) || (pic = rgbpic_load_PGM(fname)) || (pic = rgbpic_load_PPM(fname)));
    return pic;
}

rgbpic_t *rgbpic_load_PBM(const char *fname) {
    LOGME
    FILE *file;
    rgbpic_t *pic = NULL;
    if (!(file = fopen(fname, "r"))) {
        return NULL;
    }
    if ((fgetc(file) != 'P') || (fgetc(file) != '1')) {
        goto error_handling;
    }
    pic = (rgbpic_t*)malloc(sizeof(rgbpic_t));
    if (!pic) {
        goto error_handling;
    }
    *pic = (rgbpic_t){0, 0, 0, NULL};
    if (fscanf(file, "%hd %hd", &(pic->width), &(pic->height)) != 2) {
        goto error_handling;
    }
    pic->maxpixvalue = 1;
    pic->pixelvalues = (rgbpic_pixel_t*)malloc(pic->width * pic->height * sizeof(rgbpic_pixel_t));
    if (! pic->pixelvalues) {
        goto error_handling;
    }
    rgbpic_monochrome_pixel_t v;
    FORALLPIXELS(pic, i) {
        if (fscanf(file, "%hd", &v) != 1) {
            goto error_handling;
        }
        pic->pixelvalues[i].red   = 1 - v;
        pic->pixelvalues[i].green = 1 - v;
        pic->pixelvalues[i].blue  = 1 - v;
    }
    fclose(file);
    return pic;
  error_handling:
    fclose(file);
    rgbpic_delete(pic);
    return NULL;
}

rgbpic_t *rgbpic_load_PGM(const char *fname) {
    LOGME
    FILE *file;
    rgbpic_t *pic = NULL;
    if (!(file = fopen(fname, "r"))) {
        return NULL;
    }
    if ((fgetc(file) != 'P') || (fgetc(file) != '2')) {
        goto error_handling;
    }
    pic = (rgbpic_t*)malloc(sizeof(rgbpic_t));
    if (!pic) {
        goto error_handling;
    }
    *pic = (rgbpic_t){0, 0, 0, NULL};
    if (fscanf(file, "%hd %hd %hd", &(pic->width), &(pic->height), &(pic->maxpixvalue)) != 3) {
        goto error_handling;
    }
    pic->pixelvalues = (rgbpic_pixel_t*)malloc(pic->width * pic->height * sizeof(rgbpic_pixel_t));
    if (! pic->pixelvalues) {
        goto error_handling;
    }
    rgbpic_monochrome_pixel_t v;
    FORALLPIXELS(pic, i) {
        if (fscanf(file, "%hd", &v) != 1) {
            goto error_handling;
        }
        pic->pixelvalues[i].red   = v;
        pic->pixelvalues[i].green = v;
        pic->pixelvalues[i].blue  = v;
    }
    fclose(file);
    return pic;
  error_handling:
    fclose(file);
    rgbpic_delete(pic);
    return NULL;
}

rgbpic_t *rgbpic_load_PPM(const char *fname) {
    LOGME
    FILE *file;
    rgbpic_t *pic = NULL;
    if (!(file = fopen(fname, "r"))) {
        return NULL;
    }
    if ((fgetc(file) != 'P') || (fgetc(file) != '3')) {
        goto error_handling;
    }
    pic = (rgbpic_t*)malloc(sizeof(rgbpic_t));
    if (!pic) {
        goto error_handling;
    }
    *pic = (rgbpic_t){0, 0, 0, NULL};
    if (fscanf(file, "%hd %hd %hd", &(pic->width), &(pic->height), &(pic->maxpixvalue)) != 3) {
        goto error_handling;
    }
    pic->pixelvalues = (rgbpic_pixel_t*)malloc(pic->width * pic->height * sizeof(rgbpic_pixel_t));
    if (! pic->pixelvalues) {
        goto error_handling;
    }
    FORALLPIXELS(pic, i) {
        if (fscanf(file, "%hd %hd %hd",
              &pic->pixelvalues[i].red,
              &pic->pixelvalues[i].green,
              &pic->pixelvalues[i].blue) != 3) {
            goto error_handling;
        }
    }
    fclose(file);
    return pic;
  error_handling:
    fclose(file);
    rgbpic_delete(pic);
    return NULL;
}

int rgbpic_save_PBM(const char *fname, rgbpic_t *pic) {
    LOGME
    FILE *file;
    if (!(file = fopen(fname, "w"))) {
        return 1;
    }
    fprintf(file, "P1\n%hd %hd\n", pic->width, pic->height);
    FORALLPIXELS(pic, i) {
        rgbpic_monochrome_pixel_t v = rgbpic_rgb_to_gray(pic->pixelvalues[i]) <= pic->maxpixvalue / 2;
        fprintf(file, "%hd%c", v, ((i + 1) % pic->width) ? ' ' : '\n');
    }
    fclose(file);
    return 0;
}

int rgbpic_save_PGM(const char *fname, rgbpic_t *pic) {
    LOGME
    FILE *file;
    if (!(file = fopen(fname, "w"))) {
        return 1;
    }
    fprintf(file, "P2\n%hd %hd\n%hd\n",
            pic->width,
            pic->height,
            pic->maxpixvalue);
    FORALLPIXELS(pic, i) {
        rgbpic_monochrome_pixel_t v = rgbpic_rgb_to_gray(pic->pixelvalues[i]);
        fprintf(file, "%hd%c", v, ((i + 1) % pic->width) ? ' ' : '\n');
    }
    fclose(file);
    return 0;
}

int rgbpic_save_PPM(const char *fname, rgbpic_t *pic) {
    LOGME
    FILE *file;
    if (!(file = fopen(fname, "w"))) {
        return 1;
    }
    fprintf(file, "P3\n%hd %hd\n%hd\n",
            pic->width,
            pic->height,
            pic->maxpixvalue);
    FORALLPIXELS(pic, i) {
        fprintf(file, "%hd %hd %hd%c",
                pic->pixelvalues[i].red,
                pic->pixelvalues[i].green,
                pic->pixelvalues[i].blue,
                ((i + 1) % pic->width) ? ' ' : '\n');
    }
    fclose(file);
    return 0;
}

rgbpic_t *rgbpic_create(rgbpic_picture_size_t width, rgbpic_picture_size_t height, rgbpic_monochrome_pixel_t max) {
    LOGME
    rgbpic_t *pic = (rgbpic_t*)malloc(sizeof(rgbpic_t));
    if (!pic) {
        return NULL;
    }
    *pic = (rgbpic_t){max, width, height, (rgbpic_pixel_t*)malloc(width * height * sizeof(rgbpic_pixel_t))};
    if (! pic->pixelvalues) {
        rgbpic_delete(pic);
        return NULL;
    }
    memset(pic->pixelvalues, 0, width * height * sizeof(rgbpic_pixel_t));
    return pic;
}

void rgbpic_delete(rgbpic_t *pic) {
    LOGME
    if (pic) {
        free(pic->pixelvalues);
        free(pic);
    }
}

alterlib/pnm-manipulation.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
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#include "pnm-manipulation.h"

#include <malloc.h>

#define FORALLPIXELS(P,I) for (unsigned int I = 0, __pxcnt = P->width * P->height; I < __pxcnt; ++I)
#define LOGME printf("Executing function %s\n", __FUNCTION__);

void rgbpic_invert(rgbpic_t *pic) {
    LOGME
}

void rgbpic_invert_R(rgbpic_t *pic) {
    LOGME
}

void rgbpic_invert_G(rgbpic_t *pic) {
    LOGME
}

void rgbpic_invert_B(rgbpic_t *pic) {
    LOGME
}

void rgbpic_mirror_h(rgbpic_t *pic) {
    LOGME
}

void rgbpic_mirror_v(rgbpic_t *pic) {
    LOGME
}

void rgbpic_rotate_l(rgbpic_t *pic) {
    LOGME
}

void rgbpic_mirror_c(rgbpic_t *pic) {
    LOGME
}

void rgbpic_rotate_r(rgbpic_t *pic) {
    LOGME
}

void rgbpic_color_RG(rgbpic_t *pic) {
    LOGME
}

void rgbpic_color_GB(rgbpic_t *pic) {
    LOGME
}

void rgbpic_color_BR(rgbpic_t *pic) {
    LOGME
}

void rgbpic_delete_R(rgbpic_t *pic) {
    LOGME
}

void rgbpic_delete_G(rgbpic_t *pic) {
    LOGME
}

void rgbpic_delete_B(rgbpic_t *pic) {
    LOGME
}

void rgbpic_convert_to_grey_avg(rgbpic_t *pic) {
    LOGME
}

void rgbpic_convert_to_grey_lum(rgbpic_t *pic) {
    LOGME
}

void rgbpic_convert_R_to_grey(rgbpic_t *pic) {
    LOGME
}

void rgbpic_convert_G_to_grey(rgbpic_t *pic) {
    LOGME
}

void rgbpic_convert_B_to_grey(rgbpic_t *pic) {
    LOGME
}

void rgbpic_convert_to_bw_avg(rgbpic_t *pic) {
    LOGME
}

void rgbpic_convert_to_bw_lum(rgbpic_t *pic) {
    LOGME
}

Az alternatív lib használatához mindössze annyi szükséges, hogy az LD_LIBRARY_PATH környezeti változó alapján a rendszer hamarabb találja meg az alternatív megvalósítást tartalmazó libpnm.so fájlt.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
make
pushd pnm-info
./pnm-monolit-info ../../torony-o.ppm
LD_LIBRARY_PATH=../libpnm:$LD_LIBRARY_PATH ./pnm-monolit-info ../../torony-o.ppm
LD_LIBRARY_PATH=../alterlib:$LD_LIBRARY_PATH ./pnm-monolit-info ../../torony-o.ppm
popd
pushd pnm-monolit-manipulate
LD_LIBRARY_PATH=../libpnm:$LD_LIBRARY_PATH ./pnm-monolit-manipulate ../../torony-o.ppm -PPM inverted.ppm -i
LD_LIBRARY_PATH=../alterlib:$LD_LIBRARY_PATH ./pnm-monolit-manipulate ../../torony-o.ppm -PPM alternative.ppm -i
popd
make clean

Dinamikus libhasználat

Most pedig lépjünk még egyet, és ne a betöltésnél mondjuk meg, hogy melyik megvalósítást kell használni, hanem hagyjuk, hogy a program futás közben döntse el. Néhány alapvetőbb funkciót továbbra is a megszokott módon kell hozzászerkeszteni a programhoz (ezeket a libpnmtype.so-ba szerveztük ki), de a képeket manipuláló többi függvényt futásidőben betöltve fogjuk használni. Ehhez két külön .so fájlt készítünk, és a programban fogjuk eldönteni, hogy melyik is kell nekünk. A teljes csomag tömörítve innen tölthető le.

Az alterlib-re nem lesz szükségünk, így visszatérhetünk a korábbi fő Makefile-hoz. Az eddigi egy lib-et viszont kettébontottuk, így ezt jelezni kell a Makefile-okban.

A Makefile-ok

Makefile:

1
2
3
4
5
6
SUBDIRS:=libpnm pnm-info pnm-manipulate pnm-fuzz

all clean:
    @for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir $@; done

.PHONY: all clean

libpnm/Makefile:

 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
LIBS:=libpnmtype.so libpnmmanip.so libpnmdummy.so
SRCS:=pnm-type.c pnm-manipulation.c pnm-dummy.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=$(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall -fPIC

all: $(LIBS)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(LIBS):
    $(CC) -shared -Wl,-soname,$@ -o $@ $^

clean:
    rm -rf $(LIBS) $(OBJS) $(DEPS)

.PHONY: all clean

libpnmtype.so: pnm-type.o
libpnmmanip.so: pnm-manipulation.o
libpnmdummy.so: pnm-dummy.o

include $(DEPS)

pnm-fuzz/Makefile:

 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
PROG:=pnm-fuzz
SRCS:=pnm-fuzz.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
LDFLAGS:=-rdynamic -L../libpnm $(LDFLAGS)
LDLIBS:=-lpnmtype $(LDLIBS)

all: $(PROG)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@

clean:
    rm -rf $(PROG) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

pnm-info/Makefile:

 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
PROG:=pnm-info
SRCS:=pnm-info.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
LDFLAGS:=-rdynamic -L../libpnm $(LDFLAGS)
LDLIBS:=-lpnmtype $(LDLIBS)

all: $(PROG)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@

clean:
    rm -rf $(PROG) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

pnm-manipulate/Makefile:

 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
PROG:=pnm-manipulate
SRCS:=pnm-manipulate.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
LDFLAGS:=-rdynamic -L../libpnm $(LDFLAGS)
LDLIBS:=-ldl -lpnmtype $(LDLIBS)

all: $(PROG)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@

clean:
    rm -rf $(PROG) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

A lib alternatív megvalósításában (pnm-dummy.so) csak kevés függvényt valósítunk meg.

Az alternatív lib

libpnm/pnm-dummy.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#include "pnm-manipulation.h"

#define FORALLPIXELS(P,I) for (unsigned int I = 0, __pxcnt = P->width * P->height; I < __pxcnt; ++I)

void rgbpic_invert(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        pic->pixelvalues[i].red   = pic->maxpixvalue - pic->pixelvalues[i].red;
        pic->pixelvalues[i].green = pic->maxpixvalue - pic->pixelvalues[i].green;
        pic->pixelvalues[i].blue  = pic->maxpixvalue - pic->pixelvalues[i].blue;
    }
}

void rgbpic_mirror_c(rgbpic_t *pic) {
}

void rgbpic_delete_R(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        pic->pixelvalues[i].red = 0;
    }
}

void rgbpic_delete_G(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        pic->pixelvalues[i].green = 0;
    }
}

void rgbpic_delete_B(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        pic->pixelvalues[i].blue = 0;
    }
}

void rgbpic_convert_to_grey_lum(rgbpic_t *pic) {
    rgbpic_monochrome_pixel_t v;
    FORALLPIXELS(pic, i) {
        v = rgbpic_rgb_to_gray(pic->pixelvalues[i]);
        pic->pixelvalues[i].red   = v;
        pic->pixelvalues[i].green = v;
        pic->pixelvalues[i].blue  = v;
    }
}

A pnm-info és pnm-fuzz programok nem használják a manipulációs függvényeket, így rajtuk nem kell változtatni. A pnm-manipulate viszont maga fogja dinamikusan használni a megadott lib-eket.

A pnm-manipulate kódja

pnm-manipulate/pnm-manipulate.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
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#include "pnm-manipulation.h"
#include "pnm-type.h"

#include <dlfcn.h>
#include <stdio.h>
#include <string.h>

typedef struct modparam {
    char *option;
    char *name;
} modparam;

modparam otable[] = {
    {"-i",   "rgbpic_invert"},
    {"-iR",  "rgbpic_invert_R"},
    {"-iG",  "rgbpic_invert_G"},
    {"-iB",  "rgbpic_invert_B"},
    {"-v",   "rgbpic_mirror_v"},
    {"-h",   "rgbpic_mirror_h"},
    {"-c",   "rgbpic_mirror_c"},
    {"-l",   "rgbpic_rotate_l"},
    {"-r",   "rgbpic_rotate_r"},
    {"-xRG", "rgbpic_color_RG"},
    {"-xGB", "rgbpic_color_GB"},
    {"-xBR", "rgbpic_color_BR"},
    {"-dR",  "rgbpic_delete_R"},
    {"-dG",  "rgbpic_delete_G"},
    {"-dB",  "rgbpic_delete_B"},
    {"-g",   "rgbpic_convert_to_grey_lum"},
    {"-gR",  "rgbpic_convert_R_to_grey"},
    {"-gG",  "rgbpic_convert_G_to_grey"},
    {"-gB",  "rgbpic_convert_B_to_grey"},
    {"-b",   "rgbpic_convert_to_bw_lum"},
    {NULL, NULL}
};

typedef int (*savf)(const char*, rgbpic_t*);
typedef struct savparam {
    char *option;
    savf function;
} savparam;

savparam stable[] = {
    {"-PBM", rgbpic_save_PBM},
    {"-PGM", rgbpic_save_PGM},
    {"-PPM", rgbpic_save_PPM},
    {NULL, NULL}
};

/*
 * A main függvény.
 */
int main(int argc, char *argv[]) {
    int argi;
    rgbpic_t *kep;                              /* A kép (illetve a képre mutató pointer) */
    void *lib_handle;
    char *error_msg;

    if (argc < 5) {                                            /* Ha nincs elég paraméter */
        printf("Nincs eleg parameter!\n");
        printf("Használat: %s <dynamic-library> <inputfile>", argv[0]);
        if (stable->option) {
            savparam *sp = stable;
            printf(" %s", sp->option);
            while ((++sp)->option) {
                printf("|%s", sp->option);
            }
        }
        printf(" <outputfile>");
        if (otable->option) {
            modparam *mp = otable;
            printf(" [%s", mp->option);
            while ((++mp)->option) {
                printf(", %s", mp->option);
            }
        }
        printf("]\n");
        return 1;
    }

    lib_handle = dlopen(argv[1], RTLD_LAZY);
    if (!lib_handle) {
        fprintf(stderr, "Nem sikerült megnyitni a %s lib-et!\n%s\n", argv[1], dlerror());
        return 1;
    }

    if (!(kep = rgbpic_load(argv[2]))) {  /* Az első paramétert input képként értelmezzük */
        printf("Nem sikerult megnyitni: %s\n", argv[2]);
        dlclose(lib_handle);
        return 1;
    }

    /*
     * A 4. paramétertől kezdve az elvégzendő műveletek kapcsolói
     * szerepelnek.
     */
    for (argi = 5; argi < argc; ++argi) {
        /*
         * Megkeressük, hogy a következő kapcsoló benne van-e az
         * előbb összeállított táblázatunkban.
         */
        for (modparam *p = otable; p->option; ++p) {
            /*
             * Ha megtaláltuk, a hozzá tartozó módosító
             * függvényt végrehajtjuk a képre, és nem
             * keressük tovább, hanem nézzük a következő
             * paramétert.
             */
            if (!strcmp(p->option, argv[argi])) {
                rgbpic_manipulation_fnc_t function;
                function = dlsym(lib_handle, p->name);
                if ((error_msg = dlerror()) != NULL) {
                    fprintf(stderr, "%s\n", error_msg);
                    break;
                }
                function(kep);
                break;
            }
        }
    }

    dlclose(lib_handle);
    /*
     * A 2. paraméter a kiíratás formátumát adja meg, ezt is
     * megkeressük az előre megadott másik tömbünkben.
     */
    for (savparam *sp = stable; sp->option; ++sp) {
        if (!strcmp(sp->option, argv[3])) {
            /*
             * Ha megtaláltuk, akkor a hozzá tartozó
             * függvény segítségével elmentjük a képet a
             * megadott néven (3. paraméter).
             */
            if (sp->function(argv[4], kep)) {
                /* Ha a mentés nem sikerült, a program
                 * szövegesen is jelez és hibakóddal tér
                 * vissza.
                 */
                printf("Nem sikerult elmenteni: %s\n", argv[3]);
                rgbpic_delete(kep);
                return 1;
            }
            break;
        }
    }
    /* Töröljük a képet. */
    rgbpic_delete(kep);
    return 0;
}

Így a programnak paraméterben tudjuk megadni, hogy melyik .so fájlban keresse a függvényeket.

1
2
3
4
5
6
make
pushd pnm-manipulate
LD_LIBRARY_PATH=../libpnm:$LD_LIBRARY_PATH ./pnm-manipulation libpnmmanip.so ../../torony-o.ppm -PPM outm.ppm -i -c -b
LD_LIBRARY_PATH=../libpnm:$LD_LIBRARY_PATH ./pnm-manipulation libpnmdummy.so ../../torony-o.ppm -PPM outd.ppm -i -c -b
popd
make clean

Plug-in

Ha megnézzük, az előbb a pnm-manipulation.h-ból a függvénydeklarációkat nem is használtuk ki. A lib-ben ezen előre deklarált függvényeken túl bármilyen (persze a rgbpic_manipulation_fnc_t típusnak megfelelő) függvényt megvalósíthatnánk, és azt a neve alapján ugyanúgy tudnánk használni. Jelenleg egyetlen dolog köt bennünket: a programunk egy előre definiált függvénynév-listából dolgozik (nem a libpnm/pnm-manipulation.h-ban deklarált lista, hanem a pnm-manipulate/pnm-manipulate.c otable listája). Szüntessük meg ezt a kötöttséget, és csináljunk egy plug-in rendszert, amivel utólag is szinte tetszőlegesen bővíthetjük a képet manipuláló függvények listáját. A teljes csomag tömörítve innen tölthető le.

Először készítünk egy interfészt a plug-in-jeinknek. Ez után bárki implementálja ezt az interfészt, azt a programunk már be tudja tölteni (akkor is, ha előtte nem is hallott róla), vagyis a programunk bármely részének újrafordítása nélkül bővíthető lesz. Mindegyik plug-in külön .so lesz: a csinald funkciót a libcsinald.so fáljban fogja keresni a programunk, így ennek elérhetőnek kell lennie az LD_LIBRARY_PATH-ben megadott útvonalakon. De bármi, ami elérhető az LD_LIBRARY_PATH-en, használható lesz (mindaddig, amíg a megadott interfészt implementálja).

A struktúránk jelentősen átalakul. Lesz egy libpnm lib-ünk, ami továbbra is az alapvető típusdefiníciókért és fájlkezelésért, pár alapvető funkcióért, és a plug-in rendszer kereteiért felelős, lesz egy darab általános főprogramunk (a három helyett), és lesznek a plug-in-jeink.

A fő Makefile

Makefile:

1
2
3
4
5
6
SUBDIRS:=libpnm pnm-main pnm-plugins

all clean:
    @for subdir in $(SUBDIRS); do $(MAKE) -C $$subdir $@; done

.PHONY: all clean

A libpnm két header fájllal bővül. Az egyik (pnm.h) mindössze egy "gyűjtő" header, hogy ha valaki ezt a libet használja, ne kelljen egyesével include-olnia mindent. A másik a plug-in elemeit tartalmazza: a szabványos függvény fejléceket és neveket. Minden plug-in-nek egy inicializáló, egy dolgozó és egy lezáró függvényt kell tartalmaznia. Az első az esetleges paraméterek kezelésére, konfigurálásra jó, a második végzi a kép átalakítását, a harmadik pedig takarít, ha kell.

A libpnm új és változott elemei

libpnm/Makefile:

 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
LIB:=libpnm.so
SRCS:=pnm-type.c pnm-manipulation.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=$(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall -fPIC

all: $(LIB)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(LIB): $(OBJS)
    $(CC) -shared -Wl,-soname,$@ -o $@ $^

clean:
    rm -rf $(LIB) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

libpnm/pnm.h:

1
2
3
4
5
6
7
8
#ifndef PNM_H
#define PNM_H

#include "pnm-type.h"
#include "pnm-manipulation.h"
#include "pnm-plugin.h"

#endif

libpnm/pnm-plugin.h:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#ifndef PNM_PLUGIN_H
#define PNM_PLUGIN_H

#include "pnm-type.h"

#define RGBPIC_PLUGIN_START_FNC start_rgbpic_plugin
#define RGBPIC_PLUGIN_WORK_FNC rgbpic_plugin_worker
#define RGBPIC_PLUGIN_STOP_FNC stop_rgbpic_plugin

#define RGBPIC_PLUGIN_START_FNC_NAME "start_rgbpic_plugin"
#define RGBPIC_PLUGIN_WORK_FNC_NAME "rgbpic_plugin_worker"
#define RGBPIC_PLUGIN_STOP_FNC_NAME "stop_rgbpic_plugin"

typedef int (*rgbpic_plugin_start_fnc)(const char *);
typedef rgbpic_t* (*rgbpic_plugin_work_fnc)(rgbpic_t*);
typedef int (*rgbpic_plugin_stop_fnc)();

#endif

A főprogram teljesen általános lesz: a neve alapján megkeresi a plug-in-t, inicializálja, végrahajtja, majd lezárja.

A pnm-main főprogram

pnm-main/Makefile:

 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
PROG:=pnm-main
SRCS:=pnm-main.c
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
LDFLAGS:=-rdynamic -L../libpnm $(LDFLAGS)
LDLIBS:=-ldl -lpnm $(LDLIBS)

all: $(PROG)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(PROG): $(OBJS)
    $(CC) $(CFLAGS) $(LDFLAGS) $< $(LDLIBS) -o $@

clean:
    rm -rf $(PROG) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

pnm-main/pnm-main.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#include "pnm.h"

#include <dlfcn.h>
#include <malloc.h>
#include <stdio.h>
#include <string.h>

/*
 * A main függvény.
 */
int main(int argc, char *argv[]) {
    int argi;
    void *lib_handle;
    char *error_msg;
    rgbpic_t *kep = NULL, *tmp = NULL;

    for (argi = 1; argi < argc; ++argi) {
        rgbpic_plugin_start_fnc start;
        rgbpic_plugin_work_fnc work;
        rgbpic_plugin_stop_fnc stop;

        char *next_par = argv[argi];
        char *lib_name = NULL;
        char *lib_pars = NULL;
        size_t name_size;

        for (lib_pars = next_par; *lib_pars; ++lib_pars) {
            if (*lib_pars == '=') {
                *(lib_pars++) = '\0';
                break;
            }
        }
        name_size = strlen(next_par) + 7;
        lib_name = malloc(name_size * sizeof(char));
        sprintf(lib_name, "lib%s.so", next_par);

        lib_handle = dlopen(lib_name, RTLD_LAZY);
        if (!lib_handle) {
            fprintf(stderr, "Nem sikerült megnyitni a %s fájlt!\n%s\n", lib_name, dlerror());
            free(lib_name);
            continue;
        }
        free(lib_name);

        start = dlsym(lib_handle, RGBPIC_PLUGIN_START_FNC_NAME);
        if ((error_msg = dlerror()) != NULL) {
            fprintf(stderr, "%s\n", error_msg);
            dlclose(lib_handle);
            rgbpic_delete(kep);
            return 1;
        }
        if (start(lib_pars)) {
            fprintf(stderr, "Nem sikerült elindítani a %s plugint!\n", next_par);
            dlclose(lib_handle);
            break;
        };

        work = dlsym(lib_handle, RGBPIC_PLUGIN_WORK_FNC_NAME);
        if ((error_msg = dlerror()) != NULL) {
            fprintf(stderr, "%s\n", error_msg);
            dlclose(lib_handle);
            rgbpic_delete(kep);
            return 1;
        }
        tmp = work(kep);
        if (tmp == NULL) {
            fprintf(stderr, "A %s plugin futtatása nem sikerült!\n", next_par);
            dlclose(lib_handle);
            break;
        }
        kep = tmp;

        stop = dlsym(lib_handle, RGBPIC_PLUGIN_STOP_FNC_NAME);
        if ((error_msg = dlerror()) != NULL) {
            fprintf(stderr, "%s\n", error_msg);
            dlclose(lib_handle);
            rgbpic_delete(kep);
            return 1;
        }
        if (stop(lib_pars)) {
            fprintf(stderr, "Nem sikerült lezárni a %s plugint!\n", next_par);
            dlclose(lib_handle);
            break;
        };

        dlclose(lib_handle);
    }

    rgbpic_delete(kep);
    return 0;
}

És végül megvalósítunk néhány plug-in-t (amihez használni fogjuk a libpnm már ismert függvényeit).

A pnm-plugins elemei

pnm-plugins/Makefile:

 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
SRCS:=$(wildcard *.c)
OBJS:=$(patsubst %.c,%.o,$(SRCS))
DEPS:=$(patsubst %.c,%.d,$(SRCS))
LIBS:=$(patsubst %.c,lib%.so,$(SRCS))

CPPFLAGS:=-I../libpnm $(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall -fPIC

all: $(LIBS)

$(OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(LIBS): lib%.so: %.o
    $(CC) -shared -Wl,-soname,$@ -o $@ $^

clean:
    rm -rf $(LIBS) $(OBJS) $(DEPS)

.PHONY: all clean

include $(DEPS)

pnm-plugins/fuzz.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "pnm.h"
#include <stdlib.h>
#include <string.h>
#include <time.h>

#define TRANSFERLUM(PIX,TF,TW,FF,FW,MAX) while (PIX->FF > TW && PIX->TF < MAX - FW) { PIX->FF -= TW; PIX->TF += FW; }

static void fuzz_rg(rgbpic_pixel_t *p, rgbpic_monochrome_pixel_t m) {
    TRANSFERLUM(p,red,30,green,59,m)
}

static void fuzz_rb(rgbpic_pixel_t *p, rgbpic_monochrome_pixel_t m) {
    TRANSFERLUM(p,red,30,blue,11,m)
}

static void fuzz_gr(rgbpic_pixel_t *p, rgbpic_monochrome_pixel_t m) {
    TRANSFERLUM(p,green,59,red,30,m)
}

static void fuzz_gb(rgbpic_pixel_t *p, rgbpic_monochrome_pixel_t m) {
    TRANSFERLUM(p,green,59,blue,11,m)
}

static void fuzz_br(rgbpic_pixel_t *p, rgbpic_monochrome_pixel_t m) {
    TRANSFERLUM(p,blue,11,red,30,m)
}

static void fuzz_bg(rgbpic_pixel_t *p, rgbpic_monochrome_pixel_t m) {
    TRANSFERLUM(p,blue,11,green,59,m)
}

typedef void (*fuzz_t)(rgbpic_pixel_t*, rgbpic_monochrome_pixel_t);
static const fuzz_t fuzz[] = {
    fuzz_rg,
    fuzz_rb,
    fuzz_gr,
    fuzz_gb,
    fuzz_br,
    fuzz_bg,
    NULL
};
static int iterations = 0;

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    srand(time(0));
    if (params == NULL || !strcmp("", params)) {
        iterations = 1024;
    } else {
        iterations = atoi(params);
    }
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    int fuzz_size;
    for (fuzz_size = 0; fuzz[fuzz_size]; ++fuzz_size);
    int n = pic->width * pic->height;
    while (iterations--) {
        rgbpic_picture_size_t x, y, dx, dy;
        x  = random() % pic->width;
        y  = random() % pic->height;
        dx = (pic->width / 32) + random() % (pic->width / 8);
        dy = (pic->height / 32) + random() % (pic->height / 8);
        fuzz_t f = fuzz[random() % fuzz_size];
        int idx = 0;
        for (rgbpic_picture_size_t i = y; i < y + dy && idx < n; ++i) {
            idx = rgbpic_coord(pic, x, i);
            for (rgbpic_picture_size_t j = x; j < x + dx && idx < n ; ++j, ++idx) {
                if (idx < n) {
                    f(pic->pixelvalues + idx, pic->maxpixvalue);
                }
            }
        }
    }
    return pic;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/gray.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
31
32
33
34
35
36
37
#include "pnm.h"
#include <string.h>

static void (*gray_function)(rgbpic_t *) = NULL;

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    if (params == NULL || !strcmp("", params) || !strcmp("lum", params)) {
        gray_function = rgbpic_convert_to_grey_lum;
    } else if (!strcmp("nom", params)) {
        gray_function = rgbpic_convert_to_grey_avg;
    } else if (!strcmp("red", params)) {
        gray_function = rgbpic_convert_R_to_grey;
    } else if (!strcmp("green", params)) {
        gray_function = rgbpic_convert_G_to_grey;
    } else if (!strcmp("blue", params)) {
        gray_function = rgbpic_convert_B_to_grey;
    } else if (!strcmp("binarize-lum", params)) {
        gray_function = rgbpic_convert_to_bw_lum;
    } else if (!strcmp("binarize-nom", params)) {
        gray_function = rgbpic_convert_to_bw_avg;
    } else {
        return 1;
    }
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    if (gray_function) {
        gray_function(pic);
        return pic;
    }
    return NULL;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/info.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include "pnm.h"
#include <malloc.h>
#include <stdio.h>

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    rgbpic_picture_size_t i, j;
    int szurke = 1, binary = 1;
    unsigned long long int iR = 0, iG = 0, iB = 0;
    printf("A képfájl mérete: %hdx%hd pixel\n", pic->width, pic->height);
    for (i = 0; i < pic->height; ++i) {
        for (j = 0; j < pic->width; ++j) {
            rgbpic_monochrome_pixel_t r, g, b;
            iR += r = pic->pixelvalues[rgbpic_coord(pic, j, i)].red;
            iG += g = pic->pixelvalues[rgbpic_coord(pic, j, i)].green;
            iB += b = pic->pixelvalues[rgbpic_coord(pic, j, i)].blue;
            if (r != g || r != b) {
                szurke = binary = 0;
            }
            if (r != 0 && r != pic->maxpixvalue) {
                binary = 0;
            }
        }
    }
    if (binary) {
        printf("Ez egy fekete-fehér bináris kép %llu%%-os átlagos szürke intenzitással.\n",
                    100llu * iR / (pic->width * pic->height * pic->maxpixvalue));
    } else if (szurke) {
        printf("Ez egy szürkeárnyalatos kép %llu%%-os átlagos intenzitással.\n",
                    100llu * iR / (pic->width * pic->height * pic->maxpixvalue));
    } else {
        printf("Ez egy színes kép (%llu%%, %llu%%, %llu%%)-os átlagos RGB,\n",
                    100llu * iR / (pic->width * pic->height * pic->maxpixvalue),
                    100llu * iG / (pic->width * pic->height * pic->maxpixvalue),
                    100llu * iB / (pic->width * pic->height * pic->maxpixvalue));
        printf("és %llu%%-os érték szerinti valamint %llu%%-os érzékelt szürke intenzitásokkal.\n",
                    100llu * (iR + iG + iB) / (3 * pic->width * pic->height * pic->maxpixvalue),
                    (30*iR + 59*iG + 11*iB) / (pic->width * pic->height * pic->maxpixvalue));
    }
    return pic;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/invert.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
31
#include "pnm.h"
#include <string.h>

static void (*inverter_function)(rgbpic_t *) = NULL;

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    if (params == NULL || !strcmp("", params) || !strcmp("all", params)) {
        inverter_function = rgbpic_invert;
    } else if (!strcmp("red", params)) {
        inverter_function = rgbpic_invert_R;
    } else if (!strcmp("green", params)) {
        inverter_function = rgbpic_invert_G;
    } else if (!strcmp("blue", params)) {
        inverter_function = rgbpic_invert_B;
    } else {
        return 1;
    }
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    if (inverter_function) {
        inverter_function(pic);
        return pic;
    }
    return NULL;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/load.c:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "pnm.h"
#include <malloc.h>

static const char *filename = NULL;

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    filename = params;
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    rgbpic_t *tmp = rgbpic_load(filename);
    if (tmp == NULL) {
        return NULL;
    }
    rgbpic_delete(pic);
    return pic = tmp;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/mirror.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
#include "pnm.h"
#include <string.h>

static void (*mirror_function)(rgbpic_t *) = NULL;

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    if (!strcmp("center", params)) {
        mirror_function = rgbpic_mirror_c;
    } else if (!strcmp("vertical", params)) {
        mirror_function = rgbpic_mirror_v;
    } else if (!strcmp("horizontal", params)) {
        mirror_function = rgbpic_mirror_h;
    } else {
        return 1;
    }
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    if (mirror_function) {
        mirror_function(pic);
        return pic;
    }
    return NULL;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/rotate.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
#include "pnm.h"
#include <string.h>

static void (*rotate_function)(rgbpic_t *) = NULL;

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    if (!strcmp("left", params) || !strcmp("counterclockwise", params) || !strcmp("+90", params)) {
        rotate_function = rgbpic_rotate_l;
    } else if (!strcmp("right", params) || !strcmp("clockwise", params) || !strcmp("-90", params)) {
        rotate_function = rgbpic_rotate_r;
    } else {
        return 1;
    }
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    if (rotate_function) {
        rotate_function(pic);
        return pic;
    }
    return NULL;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/save.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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include "pnm.h"
#include <malloc.h>
#include <string.h>

typedef int (*save_fnc_t)(const char*, rgbpic_t*);

static const char *filename = NULL;
static save_fnc_t savefunction = NULL;

struct pnm_t {
    const char * const option;
    const save_fnc_t function;
};

static const struct pnm_t stable[] = {
    {".pbm", rgbpic_save_PBM},
    {".pgm", rgbpic_save_PGM},
    {".ppm", rgbpic_save_PPM},
    {NULL, NULL}
};

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    const char *pnmtype = NULL;
    filename = params;
    for (pnmtype = filename; *pnmtype; ++pnmtype);
    for (--pnmtype; pnmtype > filename; --pnmtype) {
        if (*pnmtype == '.') {
            break;
        }
    }
    if (pnmtype == filename) {
        return 1;
    }
    for (const struct pnm_t *sp = stable; sp->option; ++sp) {
        if (!strcmp(sp->option, pnmtype)) {
            savefunction = sp->function;
            return 0;
        }
    }
    return 1;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    if (savefunction(filename, pic)) {
        return NULL;
    }
    return pic;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

pnm-plugins/xcolor.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
#include "pnm.h"
#include <string.h>

static void (*exchange_function)(rgbpic_t *) = NULL;

int RGBPIC_PLUGIN_START_FNC(const char *params) {
    if (!strcmp("red:green", params) || !strcmp("green:red", params)) {
        exchange_function = rgbpic_color_RG;
    } else if (!strcmp("red:blue", params) || !strcmp("blue:red", params)) {
        exchange_function = rgbpic_color_BR;
    } else if (!strcmp("green:blue", params) || !strcmp("blue:green", params)) {
        exchange_function = rgbpic_color_GB;
    } else {
        return 1;
    }
    return 0;
}

rgbpic_t* RGBPIC_PLUGIN_WORK_FNC(rgbpic_t *pic) {
    if (exchange_function) {
        exchange_function(pic);
        return pic;
    }
    return NULL;
}

int RGBPIC_PLUGIN_STOP_FNC() {
    return 0;
}

A futtatás ezek után így néz ki. Megjegyzendő, hogy a libpnm.so függvényeit a plug-in-ek (is) használják.

1
2
3
4
5
make
pushd pnm-main
LD_LIBRARY_PATH=../libpnm:../pnm-plugins:$LD_LIBRARY_PATH ./pnm-main load=../../torony-o.ppm fuzz save=fuzzed.ppm invert=green dcolor=red save=out.pgm
popd
make clean

Utolsó frissítés: 2023-02-01 11:01:06