Kihagyás

Modularizáció: forráskód szervezése, függvénykönyvtárak, linkelés

Adott több képmanipuláló program. Első körben ezeket szét kell választani három részre: programok, képreprezentáció, képmanipuláció. Ezután a képreprezentáció és képmanipuláció elemeit külön függvénykönyvtárakba is ki kell szervezni, majd a programokat a függvénykönyvtárak segítségével kell megvalósítani.

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

Az egyik önálló, monolit program: pnm-info

Ez a program fog egy PNM formátumú képet, és mindenféle információt ír róla a konzolra. A program teljes forrása tömörítve innen letölthető.

A program egyetlen C forrásban van megírva, ami önmagában még teljesen rendben van.

A program forráskódja

pnm-monolit-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
 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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

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

#define UNUSED(X) (void)(X)

typedef unsigned short int rgbpic_monochrome_pixel_t;
typedef unsigned short int rgbpic_picture_size_t;

typedef struct {
    rgbpic_monochrome_pixel_t red;
    rgbpic_monochrome_pixel_t green;
    rgbpic_monochrome_pixel_t blue;
} rgbpic_pixel_t;

typedef struct {
    rgbpic_monochrome_pixel_t maxpixvalue;
    rgbpic_picture_size_t width;
    rgbpic_picture_size_t height;
    rgbpic_pixel_t *pixelvalues;
} rgbpic_t;

int rgbpic_coord(rgbpic_t *pic, rgbpic_picture_size_t x, rgbpic_picture_size_t y) {
    return  y * pic->width + x;
}

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

rgbpic_t *rgbpic_load_PBM(const char *fname) {
    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;
    }
    unsigned int n, i;
    rgbpic_monochrome_pixel_t v;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++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) {
    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;
    }
    unsigned int n, i;
    rgbpic_monochrome_pixel_t v;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++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) {
    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;
    }
    unsigned int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++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;
}

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

/*
 * A main függvény.
 */
int main(int argc, char *argv[]) {
    rgbpic_t *kep;                              /* A kép (illetve a képre mutató pointer) */
    rgbpic_picture_size_t i, j;
    int szurke = 1, binary = 1;
    unsigned long long int iR = 0, iG = 0, iB = 0;
    if (argc < 2) {                                            /* Ha nincs elég paraméter */
        printf("Nincs elég paraméter!\n");
        return 1;
    }
    if (!(kep = rgbpic_load(argv[1]))) {  /* Az első paramétert input képként értelmezzük */
        printf("Nem sikerült megnyitni: %s\n", argv[1]);
        return 1;
    }
    printf("A képfájl neve: %s\n", argv[1]);
    printf("\tmérete: %hdx%hd pixel\n", kep->width, kep->height);
    for (i = 0; i < kep->height; ++i) {
        for (j = 0; j < kep->width; ++j) {
            rgbpic_monochrome_pixel_t r, g, b;
            iR += r = kep->pixelvalues[rgbpic_coord(kep, j, i)].red;
            iG += g = kep->pixelvalues[rgbpic_coord(kep, j, i)].green;
            iB += b = kep->pixelvalues[rgbpic_coord(kep, j, i)].blue;
            if (r != g || r != b) {
                szurke = binary = 0;
            }
            if (r != 0 && r != kep->maxpixvalue) {
                binary = 0;
            }
        }
    }
    if (binary) {
        printf("\tEz egy fekete-fehér bináris kép %llu%%-os átlagos szürke intenzitással.\n",
                    100llu * iR / (kep->width * kep->height * kep->maxpixvalue));
    } else if (szurke) {
        printf("\tEz egy szürkeárnyalatos kép %llu%%-os átlagos intenzitással.\n",
                    100llu * iR / (kep->width * kep->height * kep->maxpixvalue));
    } else {
        printf("\tEz egy színes kép (%llu%%, %llu%%, %llu%%)-os átlagos RGB,\n",
                    100llu * iR / (kep->width * kep->height * kep->maxpixvalue),
                    100llu * iG / (kep->width * kep->height * kep->maxpixvalue),
                    100llu * iB / (kep->width * kep->height * kep->maxpixvalue));
        printf("\tés %llu%%-os érték szerinti valamint %llu%%-os érzékelt szürke intenzitásokkal.\n",
                    100llu * (iR + iG + iB) / (3 * kep->width * kep->height * kep->maxpixvalue),
                    (30*iR + 59*iG + 11*iB) / (kep->width * kep->height * kep->maxpixvalue));
    }
    rgbpic_delete(kep);                                              /* Töröljük a képet. */
    return 0;
}

A program fordítása egyszerű, hiszen egyetlen C forrásfájlból áll.

A makefile

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
info_PROG:=pnm-info
info_SRCS:=pnm-monolit-info.c
info_OBJS:=$(patsubst %.c,%.o,$(info_SRCS))
info_DEPS:=$(patsubst %.c,%.d,$(info_SRCS))

info_CPPFLAGS:=$(CPPFLAGS)
info_CFLAGS:=$(CFLAGS) -Wall
info_LDFLAGS:=$(LDFLAGS)
info_LDLIBS:=$(LDLIBS)

all: $(info_PROG)

$(info_OBJS): %.o: %.c
    $(CC) -c $(info_CPPFLAGS) $(info_CFLAGS) $< -o $@

$(info_DEPS): %.d: %.c
    $(CC) -MM $(info_CPPFLAGS) $< -MF $@

$(info_PROG): $(info_OBJS)
    $(CC) $(info_CFLAGS) $(info_LDFLAGS) $^ $(info_LDLIBS) -o $@

clean:
    rm -rf $(info_PROG) $(info_OBJS) $(info_DEPS)

.PHONY: all clean

include $(info_DEPS)

A makefile segítségével készítsük el a programot, majd futtassuk.

1
2
3
make
./pnm-monolit-info torony-o.ppm
make clean

A másik önálló, monolit program: pnm-manipulate

Ez a program fog egy PNM formátumú képet, és a megadott kapcsolók szerint átalakítja azt. A program teljes forrása tömörítve innen letölthető.

A program egyetlen C forrásban van megírva, ami önmagában még rendben van, bár a hossza miatt lehet, hogy egy programozó már így is a szétdarabolását javasolná.

A program forráskódja

pnm-monolit-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
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

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

#define UNUSED(X) (void)(X)

typedef unsigned short int rgbpic_monochrome_pixel_t;
typedef unsigned short int rgbpic_picture_size_t;

typedef struct {
    rgbpic_monochrome_pixel_t red;
    rgbpic_monochrome_pixel_t green;
    rgbpic_monochrome_pixel_t blue;
} rgbpic_pixel_t;

typedef struct {
    rgbpic_monochrome_pixel_t maxpixvalue;
    rgbpic_picture_size_t width;
    rgbpic_picture_size_t height;
    rgbpic_pixel_t *pixelvalues;
} rgbpic_t;

typedef void (*rgbpic_manipulation_fnc_t)(rgbpic_t*);

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

rgbpic_t *rgbpic_load_PBM(const char *fname) {
    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;
    }
    unsigned int n, i;
    rgbpic_monochrome_pixel_t v;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++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) {
    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;
    }
    unsigned int n, i;
    rgbpic_monochrome_pixel_t v;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++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) {
    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;
    }
    unsigned int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++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;
}

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

void rgbpic_invert(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++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_invert_R(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].red   = pic->maxpixvalue - pic->pixelvalues[i].red;
    }
}

void rgbpic_invert_G(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].green = pic->maxpixvalue - pic->pixelvalues[i].green;
    }
}

void rgbpic_invert_B(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].blue  = pic->maxpixvalue - pic->pixelvalues[i].blue;
    }
}

void rgbpic_mirror_h(rgbpic_t *pic) {
    int i, j, l, r;
    rgbpic_pixel_t temp;
    for (i = 0; i < pic->height; ++i) {
        for (j = 0; j < pic->width / 2; ++j) {
            l = (   i  * pic->width    ) + j;
            r = ((i+1) * pic->width - 1) - j;
            temp                = pic->pixelvalues[l];
            pic->pixelvalues[l] = pic->pixelvalues[r];
            pic->pixelvalues[r] = temp;
        }
    }
}

void rgbpic_mirror_v(rgbpic_t *pic) {
    int i, j, l, r;
    rgbpic_pixel_t temp;
    for (j = 0; j < pic->width; ++j) {
        for (i = 0; i < pic->height / 2; ++i) {
            l = (                   i  * pic->width) + j;
            r = ((pic->height - 1 - i) * pic->width) + j;
            temp                = pic->pixelvalues[l];
            pic->pixelvalues[l] = pic->pixelvalues[r];
            pic->pixelvalues[r] = temp;
        }
    }
}

void rgbpic_rotate_l(rgbpic_t *pic) {
    int i, j, n;
    rgbpic_pixel_t *temp;
    n = pic->width * pic->height;
    temp = (rgbpic_pixel_t*)malloc(n * sizeof(rgbpic_pixel_t));
    if (!temp) {
        return;
    }
    for (i = 0; i < pic->height; ++i) {
        for (j = 0; j < pic->width; ++j) {
            temp[(pic->width - 1 - j) * pic->height + i] =
                    pic->pixelvalues[i * pic->width  + j];
        }
    }
    n           = pic->height;
    pic->height = pic->width;
    pic->width  = n;
    free(pic->pixelvalues);
    pic->pixelvalues = temp;
}

void rgbpic_mirror_c(rgbpic_t *pic) {
    int i, n;
    rgbpic_pixel_t temp;
    n = pic->width * pic->height;
    for (i = 0; i < n / 2; ++i) {
        temp                        = pic->pixelvalues[i];
        pic->pixelvalues[i]         = pic->pixelvalues[n - i - 1];
        pic->pixelvalues[n - i - 1] = temp;
    }
}

void rgbpic_rotate_r(rgbpic_t *pic) {
    int i, j, n;
    rgbpic_pixel_t *temp;
    n = pic->width * pic->height;
    temp = (rgbpic_pixel_t*)malloc(n * sizeof(rgbpic_pixel_t));
    if (!temp) {
        return;
    }
    for (i = 0; i < pic->height; ++i) {
        for (j = 0; j < pic->width; ++j) {
            temp[j * pic->height + pic->height - 1 - i] =
                    pic->pixelvalues[i * pic->width + j];
        }
    }
    n           = pic->height;
    pic->height = pic->width;
    pic->width  = n;
    free(pic->pixelvalues);
    pic->pixelvalues = temp;
}

void rgbpic_color_RG(rgbpic_t *pic) {
    int n, i, m;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        m                         = pic->pixelvalues[i].red;
        pic->pixelvalues[i].red   = pic->pixelvalues[i].green;
        pic->pixelvalues[i].green = m;
    }
}

void rgbpic_color_GB(rgbpic_t *pic) {
    int n, i, m;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        m                         = pic->pixelvalues[i].green;
        pic->pixelvalues[i].green = pic->pixelvalues[i].blue;
        pic->pixelvalues[i].blue  = m;
    }
}

void rgbpic_color_BR(rgbpic_t *pic) {
    int n, i, m;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        m                        = pic->pixelvalues[i].blue;
        pic->pixelvalues[i].blue = pic->pixelvalues[i].red;
        pic->pixelvalues[i].red  = m;
    }
}

void rgbpic_delete_R(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].red = 0;
    }
}

void rgbpic_delete_G(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].green = 0;
    }
}

void rgbpic_delete_B(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].blue = 0;
    }
}

void rgbpic_convert_to_grey_avg(rgbpic_t *pic) {
    unsigned int n, i;
    rgbpic_monochrome_pixel_t v;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        v = (pic->pixelvalues[i].red + pic->pixelvalues[i].green + pic->pixelvalues[i].blue) / 3;
        pic->pixelvalues[i].red   = v;
        pic->pixelvalues[i].green = v;
        pic->pixelvalues[i].blue  = v;
    }
}

rgbpic_monochrome_pixel_t rgbpic_rgb_to_gray(rgbpic_pixel_t p) {
    return (30 * p.red + 59 * p.green + 11 * p.blue) / 100;
}

void rgbpic_convert_to_grey_lum(rgbpic_t *pic) {
    unsigned int n, i;
    rgbpic_monochrome_pixel_t v;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        v = rgbpic_rgb_to_gray(pic->pixelvalues[i]);
        pic->pixelvalues[i].red   = v;
        pic->pixelvalues[i].green = v;
        pic->pixelvalues[i].blue  = v;
    }
}

void rgbpic_convert_R_to_grey(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].green = pic->pixelvalues[i].red;
        pic->pixelvalues[i].blue  = pic->pixelvalues[i].red;
    }
}

void rgbpic_convert_G_to_grey(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].red  = pic->pixelvalues[i].green;
        pic->pixelvalues[i].blue = pic->pixelvalues[i].green;
    }
}

void rgbpic_convert_B_to_grey(rgbpic_t *pic) {
    int n, i;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        pic->pixelvalues[i].red   = pic->pixelvalues[i].blue;
        pic->pixelvalues[i].green = pic->pixelvalues[i].blue;
    }
}

void rgbpic_convert_to_bw_lum(rgbpic_t *pic) {
    unsigned int n, i;
    rgbpic_monochrome_pixel_t v;
    n = pic->width * pic->height;
    for (i = 0; i < n; ++i) {
        v = (rgbpic_rgb_to_gray(pic->pixelvalues[i]) > pic->maxpixvalue / 2) * pic->maxpixvalue;
        pic->pixelvalues[i].red   = v;
        pic->pixelvalues[i].green = v;
        pic->pixelvalues[i].blue  = v;
    }
}

int rgbpic_save_PBM(const char *fname, rgbpic_t *pic) {
    FILE *file;
    unsigned int n, i;
    if (!(file = fopen(fname, "w"))) {
        return 1;
    }
    n = pic->width * pic->height;
    fprintf(file, "P1\n%hd %hd\n", pic->width, pic->height);
    for (i = 0; i < n; ++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) {
    FILE *file;
    unsigned int n, i;
    if (!(file = fopen(fname, "w"))) {
        return 1;
    }
    n = pic->width * pic->height;
    fprintf(file, "P2\n%hd %hd\n%hd\n",
            pic->width,
            pic->height,
            pic->maxpixvalue);
    for (i = 0; i < n; ++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) {
    FILE *file;
    unsigned int n, i;
    if (!(file = fopen(fname, "w"))) {
        return 1;
    }
    n = pic->width * pic->height;
    fprintf(file, "P3\n%hd %hd\n%hd\n",
            pic->width,
            pic->height,
            pic->maxpixvalue);
    for (i = 0; i < n; ++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;
}

typedef void (*modf)(rgbpic_t*);
typedef struct modparam {
    char                      *option;
    rgbpic_manipulation_fnc_t function;
} 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) */
    if (argc < 4) {                                            /* Ha nincs elég paraméter */
        printf("Nincs eleg parameter!\n");
        printf("Használat: %s <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;
    }
    if (!(kep = rgbpic_load(argv[1]))) {  /* Az első paramétert input képként értelmezzük */
        printf("Nem sikerult megnyitni: %s\n", argv[1]);
        return 1;
    }
    /*
     * A 4. paramétertől kezdve az elvégzendő műveletek kapcsolói
     * szerepelnek.
     */
    for (argi = 4; 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])) {
                p->function(kep);
                break;
            }
        }
    }
    /*
     * 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[2])) {
            /*
             * 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[3], 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[2]);
                rgbpic_delete(kep);
                return 1;
            }
            break;
        }
    }
    /* Töröljük a képet. */
    rgbpic_delete(kep);
    return 0;
}

/*
 * Némi megjegyzés a végére: a képet beolvasása során RGB formátumúra
 * konvertáljuk, és végig ezen a formátumon dolgozunk. Tehát amikor azt
 * mondjuk, hogy szürkeárnyalatos képet csinálunk belőle, a szürke
 * értékeket akkor is RGB formátumban tároljuk, és a fekete-fehérré
 * alakítással is így járunk el. Ilyen módon is igaz viszont az, hogy
 * egy szürke képet ismét szürkévé, vagy fekete-fehéret ismét fekete-
 * fehérré alakítva már semmilyen változás nem történik a képben. Tehát
 * ilyen módon nem kell megjegyeznünk, hogy az adott kép éppen milyen
 * formátumú. Kiíratásnál pedig elvégezzük a megfelelő konverziót (ha
 * kell), így a tárolt kép már a megfelelő alakú lesz mielőtt a fájlba
 * kerül.
 */

A program fordítása egyszerű, hiszen egyetlen C forrásfájlból áll.

A makefile

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
manipulate_PROG:=pnm-manipulate
manipulate_SRCS:=pnm-monolit-manipulate.c
manipulate_OBJS:=$(patsubst %.c,%.o,$(manipulate_SRCS))
manipulate_DEPS:=$(patsubst %.c,%.d,$(manipulate_SRCS))

manipulate_CPPFLAGS:=$(CPPFLAGS)
manipulate_CFLAGS:=$(CFLAGS) -Wall
manipulate_LDFLAGS:=$(LDFLAGS)
manipulate_LDLIBS:=$(LDLIBS)

all: $(manipulate_PROG)

$(manipulate_OBJS): %.o: %.c
    $(CC) -c $(manipulate_CPPFLAGS) $(manipulate_CFLAGS) $< -o $@

$(manipulate_DEPS): %.d: %.c
    $(CC) -MM $(manipulate_CPPFLAGS) $< -MF $@

$(manipulate_PROG): $(manipulate_OBJS)
    $(CC) $(manipulate_CFLAGS) $(manipulate_LDFLAGS) $^ $(manipulate_LDLIBS) -o $@

clean:
    rm -rf $(manipulate_PROG) $(manipulate_OBJS) $(manipulate_DEPS)

.PHONY: all clean

include $(manipulate_DEPS)

A makefile segítségével készítsük el a programot, majd futtassuk.

1
2
3
make
./pnm-monolit-manipulate torony-o.ppm -PPM inverted.ppm -i
make clean

Közös részek kiszervezése

Ha mindkét programra szükségünk van, akkor megtehetjük, hogy egy közös könyvtárba rakjuk őket, a makefile-jaikat pedig összegyúrjuk. A két monolit programnak viszont sok közös eleme van:

  • adattípusok
  • függvények

Nem szerencsés, hogy ezeket külön-külön többször írjuk meg (és ebbe most beleétjük azt is, hogy csak egyszer írjuk meg és utána copy-paste-tel átmásoljuk). Így ugyanis úgynevezett klónjaink, kódmásolataink lesznek, amiket hosszú távon nem olyan egyszerű kezelni (ha páldául kiderül, hogy valamit hibásan írtunk meg, több helyen kell kijavítani).

Szóval, fogjuk a forrásainkat, és szervezzük ki/át az egyes elemeket. A kiindulóállapot tömörítve innen letölthető, bár itt a Makefile már az elvárt végállapot szerint fordít.

  • Készítsünk másolatot a pnm-monolit-info.c-ről pnm-info.c néven.
  • Készítsünk másolatot a pnm-monolit-manipulate.c-ről pnm-manipulate.c néven.
  • Készítsünk egy pnm-type.h header fájlt, és mozgassuk át bele a pnm-info.c és pnm-manipulate.c forrásfájlokban is megtalálható közös típusdefiníciókat és azoknak a függvényeknek a deklarációit, amik a képeket betöltik/elmentik/létrehozzák/törlik.
  • Készítsünk egy pnm-type.c fájlt, és mozgassuk át bele a pnm-info.c és pnm-manipulate.c forrásfájlokban is megtalálható olyan függvények a definícióit, amik a képeket betöltik/elmentik/létrehozzák/törlik.
  • Készítsünk egy pnm-manipulation.h header fájlt, és mozgassuk át a pnm-manipulate.c forrásfáljból a típusdefiníciókat és azoknak a függvényeknek a deklarációit, amik a képeket manipulálják.
  • Készítsünk egy pnm-manipulation.c fájlt, és mozgassuk át a pnm-manipulate.c forrásfáljból azoknak a függvényeknek a definícióit, amik a képeket manipulálják.
  • Most akkor 2 header és 4 forrásfájlunk van.
  • Fogjuk az alábbi Makefile-t, és a segítségével próbáljuk meg elkészíteni a két programot:
    1
    make
    
A Makefile

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
30
31
32
33
34
35
36
37
38
39
40
41
info_PROG:=pnm-info
info_SRCS:=pnm-info.c
info_OBJS:=$(patsubst %.c,%.o,$(info_SRCS))
info_DEPS:=$(patsubst %.c,%.d,$(info_SRCS))

manipulate_PROG:=pnm-manipulate
manipulate_SRCS:=pnm-manipulate.c
manipulate_OBJS:=$(patsubst %.c,%.o,$(manipulate_SRCS))
manipulate_DEPS:=$(patsubst %.c,%.d,$(manipulate_SRCS))

common_SRCS:=pnm-type.c pnm-manipulation.c
common_OBJS:=$(patsubst %.c,%.o,$(common_SRCS))
common_DEPS:=$(patsubst %.c,%.d,$(common_SRCS))

CPPFLAGS:=$(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
info_LDFLAGS:=$(LDFLAGS)
info_LDLIBS:=$(LDLIBS)
manipulate_LDFLAGS:=$(LDFLAGS)
manipulate_LDLIBS:=$(LDLIBS)

all: $(info_PROG) $(manipulate_PROG)

$(info_OBJS) $(manipulate_OBJS) $(common_OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(info_DEPS) $(manipulate_DEPS) $(common_DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(info_PROG): $(info_OBJS) $(common_OBJS)
    $(CC) $(CFLAGS) $(info_LDFLAGS) $^ $(info_LDLIBS) -o $@

$(manipulate_PROG): $(manipulate_OBJS) $(common_OBJS)
    $(CC) $(CFLAGS) $(manipulate_LDFLAGS) $^ $(manipulate_LDLIBS) -o $@

clean:
    rm -rf $(info_PROG) $(info_OBJS) $(info_DEPS) $(manipulate_PROG) $(manipulate_OBJS) $(manipulate_DEPS) $(common_OBJS) $(common_DEPS)

.PHONY: all clean

include $(info_DEPS) $(manipulate_DEPS) $(common_DEPS)

  • Ez elsőre nem fog menni, mert az át/kiszervezések miatt lesznek hibák (hiányzó típusok, függvénydeklarációk). Javítsuk ezeket:
    • Include-oljuk a pnm-type.h headert a pnm-info.c-ben (sajátként, nem #include <...> hanem #include "...").
    • Include-oljuk a pnm-type.h headert a pnm-manipulation.h-ban.
    • Include-oljuk a pnm-type.h és pnm-manipulation.h header fájlokat a pnm-manipulate.c-ben.
    • Védjük le a header fájljainkat a többszörös include ellen (#ifndef XXX_H / #define XXX_H / #endif).
  • Próbáljuk ismét a make parancsot.
  • Korrigáljuk a maradék hibákat, amíg nem lesz sikeres a make futása.
  • Optimalizáljunk kicsit. Fogjuk a rgbpic_coord() és rgbpic_rgb_to_gray() függvényeket, és vigyük át őket a pnm-type.c-ből a pnm-type.h-ba, és tegyük őket static inline-ná.
Mi az a static inline?

Egyrészt ez a két függvény nagyon egyszerű, tulajdonképpen jobban járnánk, ha nem is függvényként használnánk őket, hanem az eredményüket kiszámoló kifejezéseket mindenhol behelyettesítenénk a függvényhívás helyén. (Megspórolnánk a függvényhívást előkészítő és visszatérését kezelő gépi utasításokat, és így a végrehajtás is gyorsabb lenne.) Az inline pont azt mondja a fordítónak, hogy ha úgy látja, ezt tegye meg. Ehhez viszont az kell, hogy már fordítási időben rendelkezésre álljon a függvény kódja (definíciója) is, ezért lett ez a két függvény a header-ben definiálva.

Mivel a header fájlokat több forrás is include-olja és ezeknek a függvényeknek most a definíciója van a header-ben, ha több ilyen forrásból készült object-eket szeretnénk összepakolni (márpedig pont ezt fogjuk csinálni), akkor ezek a definíciók csúnyán összevesznének a linkelésnél (még akkor is, ha a függvény inline, mert ez "csak egy ajánlás" a fordító számára, és a biztonság kedvéért megtartja a függvényt külön függvényként is). A static itt (a globális scope-ban) azt jelenti, hogy az adott elem (jelen esetben függvény) csak az adott forrásfájlból készült object-ben lesz látható. Így tehát hiába lesz benne több forrásban és így több objectben is, az összerakott object-ek ezeket "nem teszik be a közösbe", hanem megtartják maguknak (mindenki a saját definícióját), így nem lesz min összeveszni.

  • Korrigáljuk a maradék hibákat, amíg nem lesz sikeres a make futása. Az eredmény valami ilyesmi lesz:
A pnm-type headerje és forráskódja

pnm-type.h:

 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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#ifndef PNM_TYPE_H
#define PNM_TYPE_H

typedef unsigned short int rgbpic_monochrome_pixel_t;
typedef unsigned short int rgbpic_picture_size_t;

typedef struct {
    rgbpic_monochrome_pixel_t red;
    rgbpic_monochrome_pixel_t green;
    rgbpic_monochrome_pixel_t blue;
} rgbpic_pixel_t;

typedef struct {
    rgbpic_monochrome_pixel_t maxpixvalue;
    rgbpic_picture_size_t width;
    rgbpic_picture_size_t height;
    rgbpic_pixel_t *pixelvalues;
} rgbpic_t;

static inline int rgbpic_coord(rgbpic_t *pic, rgbpic_picture_size_t x, rgbpic_picture_size_t y) {
    return  y * pic->width + x;
}

static inline rgbpic_monochrome_pixel_t rgbpic_rgb_to_gray(rgbpic_pixel_t p) {
    return (30 * p.red + 59 * p.green + 11 * p.blue) / 100;
}

rgbpic_t *rgbpic_load(const char*);
rgbpic_t *rgbpic_load_PBM(const char*);
rgbpic_t *rgbpic_load_PGM(const char*);
rgbpic_t *rgbpic_load_PPM(const char*);

int rgbpic_save_PBM(const char*, rgbpic_t*);
int rgbpic_save_PGM(const char*, rgbpic_t*);
int rgbpic_save_PPM(const char*, rgbpic_t*);

rgbpic_t *rgbpic_create(rgbpic_picture_size_t width, rgbpic_picture_size_t height, rgbpic_monochrome_pixel_t max);
void rgbpic_delete(rgbpic_t*);

#endif // PNM_TYPE_H

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
/*
 * 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)

rgbpic_t *rgbpic_load(const char *fname) {
    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) {
    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) {
    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) {
    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) {
    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) {
    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) {
    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) {
    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) {
    if (pic) {
        free(pic->pixelvalues);
        free(pic);
    }
}

A pnm-manipulation headerje és forráskódja

pnm-manipulation.h:

 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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#ifndef PNM_MANIPULATION_H
#define PNM_MANIPULATION_H

#include "pnm-type.h"

typedef void (*rgbpic_manipulation_fnc_t)(rgbpic_t*);

void rgbpic_invert(rgbpic_t*);
void rgbpic_invert_R(rgbpic_t*);
void rgbpic_invert_G(rgbpic_t*);
void rgbpic_invert_B(rgbpic_t*);
void rgbpic_mirror_h(rgbpic_t*);
void rgbpic_mirror_v(rgbpic_t*);
void rgbpic_rotate_l(rgbpic_t*);
void rgbpic_mirror_c(rgbpic_t*);
void rgbpic_rotate_r(rgbpic_t*);
void rgbpic_color_RG(rgbpic_t*);
void rgbpic_color_GB(rgbpic_t*);
void rgbpic_color_BR(rgbpic_t*);
void rgbpic_delete_R(rgbpic_t*);
void rgbpic_delete_G(rgbpic_t*);
void rgbpic_delete_B(rgbpic_t*);
void rgbpic_convert_to_grey_avg(rgbpic_t*);
void rgbpic_convert_to_grey_lum(rgbpic_t*);
void rgbpic_convert_R_to_grey(rgbpic_t*);
void rgbpic_convert_G_to_grey(rgbpic_t*);
void rgbpic_convert_B_to_grey(rgbpic_t*);
void rgbpic_convert_to_bw_avg(rgbpic_t*);
void rgbpic_convert_to_bw_lum(rgbpic_t*);

#endif // PNM_MANIPULATION_H

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
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
217
218
219
220
221
222
223
224
/*
 * 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)

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_invert_R(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        pic->pixelvalues[i].red   = pic->maxpixvalue - pic->pixelvalues[i].red;
    }
}

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

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

void rgbpic_mirror_h(rgbpic_t *pic) {
    int i, j, l, r;
    rgbpic_pixel_t temp;
    for (i = 0; i < pic->height; ++i) {
        for (j = 0; j < pic->width / 2; ++j) {
            l = rgbpic_coord(pic, j, i);
            r = rgbpic_coord(pic, pic->width - j - 1, i);
            temp                = pic->pixelvalues[l];
            pic->pixelvalues[l] = pic->pixelvalues[r];
            pic->pixelvalues[r] = temp;
        }
    }
}

void rgbpic_mirror_v(rgbpic_t *pic) {
    int i, j, l, r;
    rgbpic_pixel_t temp;
    for (j = 0; j < pic->width; ++j) {
        for (i = 0; i < pic->height / 2; ++i) {
            l = rgbpic_coord(pic, j, i);
            r = rgbpic_coord(pic, j, pic->height - i - 1);
            temp                = pic->pixelvalues[l];
            pic->pixelvalues[l] = pic->pixelvalues[r];
            pic->pixelvalues[r] = temp;
        }
    }
}

void rgbpic_rotate_l(rgbpic_t *pic) {
    int i, j, n;
    rgbpic_pixel_t *temp;
    n = pic->width * pic->height;
    temp = (rgbpic_pixel_t*)malloc(n * sizeof(rgbpic_pixel_t));
    if (!temp) {
        return;
    }
    for (i = 0; i < pic->height; ++i) {
        for (j = 0; j < pic->width; ++j) {
            temp[(pic->width - 1 - j) * pic->height + i] =
                    pic->pixelvalues[rgbpic_coord(pic, j, i)];
        }
    }
    n           = pic->height;
    pic->height = pic->width;
    pic->width  = n;
    free(pic->pixelvalues);
    pic->pixelvalues = temp;
}

void rgbpic_mirror_c(rgbpic_t *pic) {
    int i, n;
    rgbpic_pixel_t temp;
    n = pic->width * pic->height;
    for (i = 0; i < n / 2; ++i) {
        temp                        = pic->pixelvalues[i];
        pic->pixelvalues[i]         = pic->pixelvalues[n - i - 1];
        pic->pixelvalues[n - i - 1] = temp;
    }
}

void rgbpic_rotate_r(rgbpic_t *pic) {
    int i, j, n;
    rgbpic_pixel_t *temp;
    n = pic->width * pic->height;
    temp = (rgbpic_pixel_t*)malloc(n * sizeof(rgbpic_pixel_t));
    if (!temp) {
        return;
    }
    for (i = 0; i < pic->height; ++i) {
        for (j = 0; j < pic->width; ++j) {
            temp[j * pic->height + pic->height - 1 - i] =
                    pic->pixelvalues[rgbpic_coord(pic, j, i)];
        }
    }
    n           = pic->height;
    pic->height = pic->width;
    pic->width  = n;
    free(pic->pixelvalues);
    pic->pixelvalues = temp;
}

void rgbpic_color_RG(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        rgbpic_monochrome_pixel_t m;
        m                         = pic->pixelvalues[i].red;
        pic->pixelvalues[i].red   = pic->pixelvalues[i].green;
        pic->pixelvalues[i].green = m;
    }
}

void rgbpic_color_GB(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        rgbpic_monochrome_pixel_t m;
        m                         = pic->pixelvalues[i].green;
        pic->pixelvalues[i].green = pic->pixelvalues[i].blue;
        pic->pixelvalues[i].blue  = m;
    }
}

void rgbpic_color_BR(rgbpic_t *pic) {
    FORALLPIXELS(pic, i) {
        rgbpic_monochrome_pixel_t m;
        m                        = pic->pixelvalues[i].blue;
        pic->pixelvalues[i].blue = pic->pixelvalues[i].red;
        pic->pixelvalues[i].red  = m;
    }
}

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_avg(rgbpic_t *pic) {
    rgbpic_monochrome_pixel_t v;
    FORALLPIXELS(pic, i) {
        v = (pic->pixelvalues[i].red + pic->pixelvalues[i].green + pic->pixelvalues[i].blue) / 3;
        pic->pixelvalues[i].red   = v;
        pic->pixelvalues[i].green = v;
        pic->pixelvalues[i].blue  = v;
    }
}

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;
    }
}

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

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

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

void rgbpic_convert_to_bw_avg(rgbpic_t *pic) {
    rgbpic_monochrome_pixel_t v;
    FORALLPIXELS(pic, i) {
        v = (2 * (pic->pixelvalues[i].red + pic->pixelvalues[i].green + pic->pixelvalues[i].blue) > 3 * pic->maxpixvalue) * pic->maxpixvalue;
        pic->pixelvalues[i].red   = v;
        pic->pixelvalues[i].green = v;
        pic->pixelvalues[i].blue  = v;
    }
}

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

A két főprogram forráskódja

pnm-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
49
50
51
52
53
54
55
56
57
58
59
60
61
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

#include "pnm-type.h"

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

/*
 * A main függvény.
 */
int main(int argc, char *argv[]) {
    rgbpic_t *kep;                              /* A kép (illetve a képre mutató pointer) */
    rgbpic_picture_size_t i, j;
    int szurke = 1, binary = 1;
    unsigned long long int iR = 0, iG = 0, iB = 0;
    if (argc < 2) {                                            /* Ha nincs elég paraméter */
        printf("Nincs elég paraméter!\n");
        return 1;
    }
    if (!(kep = rgbpic_load(argv[1]))) {  /* Az első paramétert input képként értelmezzük */
        printf("Nem sikerült megnyitni: %s\n", argv[1]);
        return 1;
    }
    printf("A képfájl neve: %s\n", argv[1]);
    printf("\tmérete: %hdx%hd pixel\n", kep->width, kep->height);
    for (i = 0; i < kep->height; ++i) {
        for (j = 0; j < kep->width; ++j) {
            rgbpic_monochrome_pixel_t r, g, b;
            iR += r = kep->pixelvalues[rgbpic_coord(kep, j, i)].red;
            iG += g = kep->pixelvalues[rgbpic_coord(kep, j, i)].green;
            iB += b = kep->pixelvalues[rgbpic_coord(kep, j, i)].blue;
            if (r != g || r != b) {
                szurke = binary = 0;
            }
            if (r != 0 && r != kep->maxpixvalue) {
                binary = 0;
            }
        }
    }
    if (binary) {
        printf("\tEz egy fekete-fehér bináris kép %llu%%-os átlagos szürke intenzitással.\n",
                    100llu * iR / (kep->width * kep->height * kep->maxpixvalue));
    } else if (szurke) {
        printf("\tEz egy szürkeárnyalatos kép %llu%%-os átlagos intenzitással.\n",
                    100llu * iR / (kep->width * kep->height * kep->maxpixvalue));
    } else {
        printf("\tEz egy színes kép (%llu%%, %llu%%, %llu%%)-os átlagos RGB,\n",
                    100llu * iR / (kep->width * kep->height * kep->maxpixvalue),
                    100llu * iG / (kep->width * kep->height * kep->maxpixvalue),
                    100llu * iB / (kep->width * kep->height * kep->maxpixvalue));
        printf("\tés %llu%%-os érték szerinti valamint %llu%%-os érzékelt szürke intenzitásokkal.\n",
                    100llu * (iR + iG + iB) / (3 * kep->width * kep->height * kep->maxpixvalue),
                    (30*iR + 59*iG + 11*iB) / (kep->width * kep->height * kep->maxpixvalue));
    }
    rgbpic_delete(kep);                                              /* Töröljük a képet. */
    return 0;
}

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
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

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

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

typedef struct modparam {
    char                      *option;
    rgbpic_manipulation_fnc_t function;
} 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) */
    if (argc < 4) {                                            /* Ha nincs elég paraméter */
        printf("Nincs eleg parameter!\n");
        printf("Használat: %s <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;
    }
    if (!(kep = rgbpic_load(argv[1]))) {  /* Az első paramétert input képként értelmezzük */
        printf("Nem sikerult megnyitni: %s\n", argv[1]);
        return 1;
    }
    /*
     * A 4. paramétertől kezdve az elvégzendő műveletek kapcsolói
     * szerepelnek.
     */
    for (argi = 4; 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])) {
                p->function(kep);
                break;
            }
        }
    }
    /*
     * 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[2])) {
            /*
             * 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[3], 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[2]);
                rgbpic_delete(kep);
                return 1;
            }
            break;
        }
    }
    /* Töröljük a képet. */
    rgbpic_delete(kep);
    return 0;
}

  • Futtassuk az elkészített programokat.
    1
    2
    3
    4
    make
    ./pnm-monolit-info ../torony-o.ppm
    ./pnm-monolit-manipulate ../torony-o.ppm -PPM inverted.ppm -i
    make clean
    

Készítsünk statikus könyvtárat

A object fájlok közvetlen linkelése helyett először készítsünk library-t a közös fájlokból az alábbi Makefile segítségével. A programok forrásai és a Makefile egyben tömörítve innen letölthető.

A Makefile

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
info_PROG:=pnm-info
info_SRCS:=pnm-info.c
info_OBJS:=$(patsubst %.c,%.o,$(info_SRCS))
info_DEPS:=$(patsubst %.c,%.d,$(info_SRCS))

manipulate_PROG:=pnm-manipulate
manipulate_SRCS:=pnm-manipulate.c
manipulate_OBJS:=$(patsubst %.c,%.o,$(manipulate_SRCS))
manipulate_DEPS:=$(patsubst %.c,%.d,$(manipulate_SRCS))

libpnm_LIB:=libpnm.a
libpnm_SRCS:=pnm-type.c pnm-manipulation.c
libpnm_OBJS:=$(patsubst %.c,%.o,$(libpnm_SRCS))
libpnm_DEPS:=$(patsubst %.c,%.d,$(libpnm_SRCS))

CPPFLAGS:=$(CPPFLAGS)
CFLAGS:=$(CFLAGS) -Wall
info_LDFLAGS:=-L. $(LDFLAGS)
info_LDLIBS:= -lpnm $(LDLIBS)
manipulate_LDFLAGS:=-L. $(LDFLAGS)
manipulate_LDLIBS:= -lpnm $(LDLIBS)
ARFLAGS:=$(ARFLAGS)

all: $(info_PROG) $(manipulate_PROG)

$(info_OBJS) $(manipulate_OBJS) $(libpnm_OBJS): %.o: %.c
    $(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@

$(info_DEPS) $(manipulate_DEPS) $(libpnm_DEPS): %.d: %.c
    $(CC) -MM $(CPPFLAGS) $< -MF $@

$(info_PROG): $(info_OBJS) $(libpnm_LIB)
    $(CC) $(CFLAGS) $(info_LDFLAGS) $< $(info_LDLIBS) -o $@

$(manipulate_PROG): $(manipulate_OBJS) $(libpnm_LIB)
    $(CC) $(CFLAGS) $(manipulate_LDFLAGS) $< $(manipulate_LDLIBS) -o $@

$(libpnm_LIB): $(libpnm_OBJS)
    $(AR) $(ARFLAGS) $@ $^

clean:
    rm -rf $(info_PROG) $(info_OBJS) $(info_DEPS) $(manipulate_PROG) $(manipulate_OBJS) $(manipulate_DEPS) $(libpnm_LIB) $(libpnm_OBJS) $(libpnm_DEPS)

.PHONY: all clean

include $(info_DEPS) $(manipulate_DEPS) $(libpnm_DEPS)

Látható, hogy most ahelyett, hogy a két programot közvetlenül az (egyes programokhoz szükséges) object fájlokból gyúrnánk össze, először az ar (archiver) program segítségével létrehozunk egy libpnm.a függvénykönyvtárat a közös részekből, majd a programok szerkesztésénél a -lpnm kapcsolóval azt mondjuk a linkernek, hogy ezt a függvénykönyvtárat is nézze át, amikor a függvények megvalósítását keresi.

A két program így is előáll, és szépen működik is.

1
2
3
4
make
./pnm-monolit-info ../torony-o.ppm
./pnm-monolit-manipulate ../torony-o.ppm -PPM inverted.ppm -i
make clean

Most nézzük meg, hogy mely függvények kerültek be a programokba amikor object-eket linkeltünk, és amikor a library-t használtunk.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
make
make -C ../03-objects
ls -l pnm-info ../03-objects/pnm-info
nm pnm-info
nm ../03-objects/pnm-info
ls -l pnm-manipulate ../03-objects/pnm-manipulate
nm pnm-manipulate
nm ../03-objects/pnm-manipulate
make clean
make -C ../03-objects clean

Látható, hogy a library-ből a linker csak azokat az object-eket szerkesztette hozzá a programokhoz, amikre valóban szükség volt, míg ha közvetlenül az object-ekből linkeltünk, akkor minden megadott object belekerült a programokba. Ezt onnan látjuk, hogy a pnm-info esetében a képátalakító függvények hiányoznak a library-s verzióból.

Mely függvények kerülnek be a programba egy library-ből?

A linker alapvetően fordítási egységeket képes "összeragasztani", ha egy objectben sok függvény van, akkor azok egyszerre kerülnek -- vagy nem kerülnek -- bele a programba. A library pedig nem más, mint ilyen object-ek gyűjteménye.

Források kiszervezése alkönyvtárakba

Ahelyett, hogy az összes forrásunk egyetlen könyvtárban lenne (ami pár fájl esetén nem akkora gond, de ha már százas nagyságrendben vannak forrásfájljaink, akkor kezelhetetlen), az egyes modulokhoz tartozó forrásokat szervezzük ki alkönyvtárakba.

A kiszervezés során a gyökérben is hagyunk egy Makefile-t, de ennek csak annyi szerepe lesz, hogy minden alkönyvtárban lefuttatja a make-et parancsot a megadott target-re. Az új hierarchia és a Makefile-ok egyben tömörítve innen tölthető le.

A központi makefile

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

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

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

all: $(LIB)

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

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

$(LIB): $(OBJS)
    $(AR) $(ARFLAGS) $@ $^

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

.PHONY: all clean

include $(DEPS)

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:=-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:=-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)

Ezeken felül hozzáadtunk még egy programot, ami szintén a pnm függvénykönyvtárat használja.

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:=-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)

pnm-fuzz/pnm-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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
/*
 * Szegedi Tudományegyetem
 * Informatikai Tanszékcsoport
 * Szoftverfejlesztés Tanszék
 */

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

#include <stdio.h>
#include <string.h>
#include <stdlib.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; }

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

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

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

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

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

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);
fuzz_t fuzz[] = {
    fuzz_rg,
    fuzz_rb,
    fuzz_gr,
    fuzz_gb,
    fuzz_br,
    fuzz_bg,
    NULL
};

/*
 * A main függvény.
 */
int main(int argc, char *argv[])
{
    rgbpic_t *kep;
    if (argc < 2) {
        printf("Nincs eleg parameter!\n");
        return 1;
    }
    if (!(kep = rgbpic_load_PPM(argv[1]))) {
        printf("Nem sikerult megnyitni: %s\n", argv[1]);
        return 1;
    }
    srand(time(0));
    int fuzz_size;
    for (fuzz_size = 0; fuzz[fuzz_size]; ++fuzz_size);
    int n = kep->width * kep->height;
    for (int iter = 1024; iter ; --iter) {
        rgbpic_picture_size_t x, y, dx, dy;
        x  = random() % kep->width;
        y  = random() % kep->height;
        dx = (kep->width / 32) + random() % (kep->width / 8);
        dy = (kep->height / 32) + random() % (kep->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(kep, x, i);
            for (rgbpic_picture_size_t j = x; j < x + dx && idx < n ; ++j, ++idx) {
                if (idx < n) {
                    f(kep->pixelvalues + idx, kep->maxpixvalue);
                }
            }
        }
    }
    if (rgbpic_save_PPM(argv[2], kep)) {
            printf("Nem sikerult elmenteni: %s\n", argv[2]);
            rgbpic_delete(kep);
            return 1;
    }
    rgbpic_delete(kep);
    return 0;
}

Fordítsunk, és próbáljuk ki az egyes programokat.

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

Teljesen statikus linkelés

A modern operációs rendszereken a linkelés kétféleképpen oldható meg: statikusan és dinamikusan (illetve vegyesen, mint látni fogjuk).

A statikus linkelés azt jelenti, hogy a linker a megfelelő függvényeket tartalmazó object-eket fizikailag hozzáragasztja a programhoz, így a program tulajdonképpen tartalmazza ezen függvények megvalósítását. Így amikor futtatás előtt a program kódja betöltődik a memóriába, ezek a függvények is betöltődnek, vagyis (ezek miatt legalábbis) semilyen extra dologra nincs szükség a futtatáshoz.

A dinamikus linkelés során ezzel szemben a linker nem pakolja bele a függvények kódját a programba. Csak azt az infót teszi bele, hogy melyik függvények vannak meghívva, és azok melyik (shared) object fájlban találhatók. Amikor az operációs rendszer betölti a programot a memóriába, akkor a linker által berakott információ alapján megkeresi a megfelelő (shared) object fájlokat, és azokat is ugyanúgy betölti, és a memóriában hozzáköti a programhoz. Így válik betöltés után teljessé és futtathatóvá a program.

Jó, de akkor mi a dinamikus linkelés előnye?

Egyelőre röviden: ha sokan használják ugyanazokat a függvényeket, akkor nincs mindenkinek "saját" példánya, hanem rendszer szinten osztoznak egy példányon. Ez helyfoglalás szempontjából eléggé előnyös tud lenni, és nem csak a háttértáron tárolt programok mérete, de az egész rendszer memóriafelhasználása is kedvezőbb lesz (erről később).

A modern operációs rendszerekre, és így az ezekre programot készítő linkerekre a dinamikus linkelés jellemző (bár verziózási és kompatibilitási problémák miatt van egy ezzel ellentétes tendencia is). Vagyis amit nem mi írtunk, csak felhasználtunk a programban, azt alapvetően dinamikusan teszi hozzá, a saját kódot pedig statikusan szerkeszti össze. (Vagyis valójában vegyes linkelés folyik.) A linkert viszont rá lehet venni, hogy teljesen statikusan linkeljen.

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

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:= -static -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:=-static -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:=-static -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ás során látunk-e különbséget a programok működésében?

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

Most akkor ellenőrizzük le, mekkorák is ezek a programok?

1
2
3
4
5
6
make
make -C ../05-static-library-subdirs
ls -l pnm-fuzz/pnm-fuzz ../05-static-library-subdirs/pnm-fuzz/pnm-fuzz
ls -l pnm-info/pnm-info ../05-static-library-subdirs/pnm-info/pnm-info
make clean
make -C ../05-static-library-subdirs clean

Látható, hogy a teljesen statikus linkelés jócskán megnöveli a program méretét, cserébe olyan rendszeren is futtatható lesz, ahol valamilyen szükséges komponens nincs előre telepítve.


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