1pod::Prima::codecs(3) User Contributed Perl Documentationpod::Prima::codecs(3)
2
3
4
6 Prima::codecs - How to write a codec for Prima image subsystem
7
9 How to write a codec for Prima image subsystem
10
12 There are many graphical formats in the world, and yet more libraries,
13 that depend on them. Writing a codec that supports particular library
14 is a tedious task, especially if one wants many formats. Usually you
15 never want to get into internal parts, the functionality comes first,
16 and who needs all those funky options that format provides? We want to
17 load a file and to show it. Everything else comes later - if ever. So,
18 in a way to not scare you off, we start it simple.
19
20 Load
21 Define a callback function like:
22
23 static Bool
24 load( PImgCodec instance, PImgLoadFileInstance fi)
25 {
26 }
27
28 Just that function is not enough for whole mechanism to work, but
29 bindings will come later. Let us imagine we work with an imaginary
30 library libduff, that we want to load files of .duf format. [ To
31 discern imaginary code from real, imaginary will be prepended with _ -
32 like, _libduff_loadfile ]. So, we call _libduff_loadfile(), that loads
33 black-and-white, 1-bits/pixel images, where 1 is white and 0 is black.
34
35 static Bool
36 load( PImgCodec instance, PImgLoadFileInstance fi)
37 {
38 _LIBDUFF * _l = _libduff_load_file( fi-> fileName);
39 if ( !_l) return false;
40
41 // - create storage for our file
42 CImage( fi-> object)-> create_empty( fi-> object,
43 _l-> width, _l-> height, imBW);
44
45 // Prima wants images aligned to 4-bytes boundary,
46 // happily libduff has same considerations
47 memcpy( PImage( fi-> object)-> data, _l-> bits,
48 PImage( fi-> object)-> dataSize);
49
50 _libduff_close_file( _l);
51
52 return true;
53 }
54
55 Prima keeps an open handle of the file; so we can use it if libduff
56 trusts handles vs names:
57
58 {
59 _LIBDUFF * _l = _libduff_load_file_from_handle( fi-> f);
60 ...
61 // In both cases, you don't need to close the handle -
62 // however you might, it is ok:
63
64 _libduff_close_file( _l);
65 fclose( fi-> f);
66 // You just assign it to null to indicate that you've closed it
67 fi-> f = null;
68 ...
69 }
70
71 Together with load() you have to implement minimal open_load() and
72 close_load().
73
74 Simplest open_load() returns non-null pointer - it is enough to report
75 'o.k'
76
77 static void *
78 open_load( PImgCodec instance, PImgLoadFileInstance fi)
79 {
80 return (void*)1;
81 }
82
83 Its result will be available in "PImgLoadFileInstance-> instance", just
84 in case. If it was dynamically allocated, free it in close_load().
85 Dummy close_load() is doing simply nothing:
86
87 static void
88 close_load( PImgCodec instance, PImgLoadFileInstance fi)
89 {
90 }
91
92 Writing to "PImage-> data"
93 As mentioned above, Prima insists on keeping its image data in 32-bit
94 aligned scanlines. If libduff allows reading from file by scanlines, we
95 can use this possibility as well:
96
97 PImage i = ( PImage) fi-> object;
98 // note - since this notation is more convenient than
99 // PImage( fi-> object)-> , instead i-> will be used
100
101 Byte * dest = i-> data + ( _l-> height - 1) * i-> lineSize;
102 while ( _l-> height--) {
103 _libduff_read_next_scanline( _l, dest);
104 dest -= i-> lineSize;
105 }
106
107 Note that image is filled in reverse - Prima images are built like
108 classical XY-coordinate grid, where Y ascends upwards.
109
110 Here ends the simple part. You can skip down to "Registering with image
111 subsystem" part, if you want it fast.
112
114 Palette
115 Our libduff can be black-and-white in two ways - where 0 is black and 1
116 is white and vice versa. While 0B/1W is perfectly corresponding to
117 imbpp1 | imGrayScale and no palette operations are needed ( Image cares
118 automatically about these), 0W/1B is although black-and-white grayscale
119 but should be treated like general imbpp1 type.
120
121 if ( l-> _reversed_BW) {
122 i-> palette[0].r = i-> palette[0].g = i-> palette[0].b = 0xff;
123 i-> palette[1].r = i-> palette[1].g = i-> palette[1].b = 0;
124 }
125
126 NB. Image creates palette with size calculated by exponent of 2, since
127 it can't know beforehand of the actual palette size. If color palette
128 for, say, 4-bit image contains 15 of 16 possible for 4-bit image
129 colors, code like
130
131 i-> palSize = 15;
132
133 does the trick.
134
135 Data conversion
136 As mentioned before, Prima defines image scanline size to be aligned to
137 32 bits, and the formula for lineSize calculation is
138
139 lineSize = (( width * bits_per_pixel + 31) / 32) * 4;
140
141 Prima defines number of converting routines between different data
142 formats. Some of them can be applied to scanlines, and some to whole
143 image ( due sampling algorithms ). These are defined in img_conv.h, and
144 probably ones that you'll need would be "bc_format1_format2", which
145 work on scanlines and probably ibc_repad, which combines some
146 "bc_XX_XX" with byte repadding.
147
148 For those who are especially lucky, some libraries do not check between
149 machine byte format and file byte format. Prima unfortunately doesn't
150 provide easy method for determining this situation, but you have to
151 convert your data in appropriate way to keep picture worthy of its
152 name. Note the BYTEORDER symbol that is defined ( usually ) in
153 sys/types.h
154
155 Load with no data
156 If a high-level code just needs image information rather than all its
157 bits, codec can provide it in a smart way. Old code will work, but will
158 eat memory and time. A flag "PImgLoadFileInstance-> noImageData" is
159 indicating if image data is needed. On that condition, codec needs to
160 report only dimensions of the image - but the type must be set anyway.
161 Here comes full code:
162
163 static Bool
164 load( PImgCodec instance, PImgLoadFileInstance fi)
165 {
166 _LIBDUFF * _l = _libduff_load_file( fi-> fileName);
167 HV * profile = fi-> frameProperties;
168 PImage i = ( PImage) fi-> frameProperties;
169 if ( !_l) return false;
170
171 CImage( fi-> object)-> create_empty( fi-> object, 1, 1,
172 _l-> _reversed_BW ? imbpp1 : imBW);
173
174 // copy palette, if any
175 if ( _l-> _reversed_BW) {
176 i-> palette[0].r = i-> palette[0].g = i-> palette[0].b = 0xff;
177 i-> palette[1].r = i-> palette[1].g = i-> palette[1].b = 0;
178 }
179
180 if ( fi-> noImageData) {
181 // report dimensions
182 pset_i( width, _l-> width);
183 pset_i( height, _l-> height);
184 return true;
185 }
186
187 // - create storage for our file
188 CImage( fi-> object)-> create_empty( fi-> object,
189 _l-> width, _l-> height,
190 _l-> _reversed_BW ? imbpp1 : imBW);
191
192 // Prima wants images aligned to 4-bytes boundary,
193 // happily libduff has same considerations
194 memcpy( PImage( fi-> object)-> data, _l-> bits,
195 PImage( fi-> object)-> dataSize);
196
197
198 _libduff_close_file( _l);
199
200 return true;
201 }
202
203 The newly introduced macro "pset_i" is a convenience operator,
204 assigning integer (i) as a value to a hash key, given as a first
205 parameter - it becomes string literal upon the expansion. Hash used for
206 storage is a lexical of type "HV*". Code
207
208 HV * profile = fi-> frameProperties;
209 pset_i( width, _l-> width);
210
211 is a prettier way for
212
213 hv_store(
214 fi-> frameProperties,
215 "width", strlen( "width"),
216 newSViv( _l-> width),
217 0);
218
219 hv_store(), HV's and SV's along with other funny symbols are described
220 in perlguts.pod in Perl installation.
221
222 Return extra information
223 Image attributes are dimensions, type, palette and data. However, it
224 is only Prima point of view - different formats can supply number of
225 extra information, often irrelevant but sometimes useful. From perl
226 code, Image has a hash reference 'extras' on object, where comes all
227 this stuff. Codec can report also such data, storing it in
228 "PImgLoadFileInstance-> frameProperties". Data should be stored in
229 native perl format, so if you're not familiar with perlguts, you better
230 read it, especially if you want return arrays and hashes. But just in
231 simple, you can return:
232
233 1. integers: pset_i( integer, _l-> integer);
234
235 2. floats: pset_f( float, _l-> float);
236
237 3. strings: pset_c( string, _l-> charstar); - note - no malloc
238 codec from you required
239
240 4. prima objects: pset_H( Handle, _l-> primaHandle);
241
242 5. SV's: pset_sv_noinc( scalar, newSVsv(sv));
243
244 6. hashes: pset_sv_noinc( scalar, ( SV *) newHV()); - hashes
245 created through newHV() can be filled just in the same manner as
246 described here
247
248 7. arrays: pset_sv_noinc( scalar, ( SV *) newAV()); - arrays
249 (AV) are described in perlguts also, but most useful function here
250 is av_push. To push 4 values, for example, follow this code:
251
252 AV * av = newAV();
253 for ( i = 0;i < 4;i++) av_push( av, newSViv( i));
254 pset_sv_noinc( myarray, newRV_noinc(( SV *) av);
255
256 is a C equivalent to
257
258 ->{extras}-> {myarray} = [0,1,2,3];
259
260 High level code can specify if the extra information should be loaded.
261 This behavior is determined by flag "PImgLoadFileInstance->
262 loadExtras". Codec may skip this flag, the extra information will not
263 be returned, even if "PImgLoadFileInstance-> frameProperties" was
264 changed. However, it is advisable to check for the flag, just for an
265 efficiency. All keys, possibly assigned to frameProperties should be
266 enumerated for high-level code. These strings should be represented
267 into "char ** PImgCodecInfo-> loadOutput" array.
268
269 static char * loadOutput[] = {
270 "hotSpotX",
271 "hotSpotY",
272 nil
273 };
274
275 static ImgCodecInfo codec_info = {
276 ...
277 loadOutput
278 };
279
280 static void *
281 init( PImgCodecInfo * info, void * param)
282 {
283 *info = &codec_info;
284 ...
285 }
286
287 The code above is taken from codec_X11.c, where X11 bitmap can provide
288 location of hot spot, two integers, X and Y. The type of the data is
289 not specified.
290
291 Loading to icons
292 If high-level code wants an Icon instead of an Image, Prima takes care
293 for producing and-mask automatically. However, if codec knows
294 explicitly about transparency mask stored in a file, it might change
295 object in the way it fits better. Mask is stored on Icon in a "-> mask"
296 field.
297
298 a) Let us imagine, that 4-bit image always carries a transparent color
299 index, in 0-15 range. In this case, following code will create
300 desirable mask:
301
302 if ( kind_of( fi-> object, CIcon) &&
303 ( _l-> transparent >= 0) &&
304 ( _l-> transparent < PIcon( fi-> object)-> palSize)) {
305 PRGBColor p = PIcon( fi-> object)-> palette;
306 p += _l-> transparent;
307 PIcon( fi-> object)-> maskColor = ARGB( p->r, p-> g, p-> b);
308 PIcon( fi-> object)-> autoMasking = amMaskColor;
309 }
310
311 Of course,
312
313 pset_i( transparentColorIndex, _l-> transparent);
314
315 would be also helpful.
316
317 b) if explicit bit mask is given, code will be like:
318
319 if ( kind_of( fi-> object, CIcon) &&
320 ( _l-> maskData >= 0)) {
321 memcpy( PIcon( fi-> object)-> mask, _l-> maskData, _l-> maskSize);
322 PIcon( fi-> object)-> autoMasking = amNone;
323 }
324
325 Note that mask is also subject to LSB/MSB and 32-bit alignment issues.
326 Treat it as a regular imbpp1 data format.
327
328 c) A format supports transparency information, but image does not
329 contain any. In this case no action is required on the codec's part;
330 the high-level code specifies if the transparency mask is created (
331 iconUnmask field ).
332
333 open_load() and close_load()
334 open_load() and close_load() are used as brackets for load requests,
335 and although they come to full power in multiframe load requests, it is
336 very probable that correctly written codec should use them. Codec that
337 assigns "false" to "PImgCodecInfo-> canLoadMultiple" claims that it
338 cannot load those images that have index different from zero. It may
339 report total amount of frames, but still be incapable of loading them.
340 There is also a load sequence, called null-load, when no load() calls
341 are made, just open_load() and close_load(). These requests are made
342 in case codec can provide some file information without loading frames
343 at all. It can be any information, of whatever kind. It have to be
344 stored into the hash "PImgLoadFileInstance-> fileProperties", to be
345 filled once on open_load(). The only exception is
346 "PImgLoadFileInstance-> frameCount", which can be filled on
347 open_load(). Actually, frameCount could be filled on any load stage,
348 except close_load(), to make sense in frame positioning. Even single
349 frame codec is advised to fill this field, at least to tell whether
350 file is empty ( frameCount == 0) or not ( frameCount == 1). More about
351 frameCount comes into chapters dedicated to multiframe requests. For
352 strictly single-frame codecs it is therefore advised to care for
353 open_load() and close_load().
354
355 Load input
356 So far codec is expected to respond for noImageData hint only, and it
357 is possible to allow a high-level code to alter codec load behavior,
358 passing specific parameters. "PImgLoadFileInstance-> profile" is a
359 hash, that contains these parameters. The data that should be applied
360 to all frames and/or image file are set there when open_load() is
361 called. These data, plus frame-specific keys passed to every load()
362 call. However, Prima passes only those hash keys, which are returned
363 by load_defaults() function. This functions returns newly created ( by
364 calling newHV()) hash, with accepted keys and their default ( and
365 always valid ) value pairs. Example below defines speed_vs_memory
366 integer value, that should be 0, 1 or 2.
367
368 static HV *
369 load_defaults( PImgCodec c)
370 {
371 HV * profile = newHV();
372 pset_i( speed_vs_memory, 1);
373 return profile;
374 }
375 ...
376 static Bool
377 load( PImgCodec instance, PImgLoadFileInstance fi)
378 {
379 ...
380 HV * profile = fi-> profile;
381 if ( pexist( speed_vs_memory)) {
382 int speed_vs_memory = pget_i( speed_vs_memory);
383 if ( speed_vs_memory < 0 || speed_vs_memory > 2) {
384 strcpy( fi-> errbuf, "speed_vs_memory should be 0, 1 or 2");
385 return false;
386 }
387 _libduff_set_load_optimization( speed_vs_memory);
388 }
389 }
390
391 The latter code chunk can be applied to open_load() as well.
392
393 Returning an error
394 Image subsystem defines no severity gradation for codec errors. If
395 error occurs during load, codec returns false value, which is "null" on
396 open_load() and "false" on load. It is advisable to explain the error,
397 otherwise the user gets just "Loading error" string. To do so, error
398 message is to be copied to "PImgLoadFileInstance-> errbuf", which is
399 "char[256]". On an extreme severe error codec may call croak(), which
400 jumps to the closest G_EVAL block. If there is no G_EVAL blocks then
401 program aborts. This condition could also happen if codec calls some
402 Prima code that issues croak(). This condition is untrappable, - at
403 least without calling perl functions. Understanding that that behavior
404 is not acceptable, it is still under design.
405
407 In order to indicate that a codec is ready to read multiframe images,
408 it must set "PImgCodecInfo-> canLoadMultiple" flag to true. This only
409 means, that codec should respond to the "PImgLoadFileInstance-> frame"
410 field, which is integer that can be in range from 0 to
411 "PImgLoadFileInstance-> frameCount - 1". It is advised that codec
412 should change the frameCount from its original value "-1" to actual
413 one, to help Prima filter range requests before they go down to the
414 codec. The only real problem that may happen to the codec which it
415 strongly unwilling to initialize frameCount, is as follows. If a
416 loadAll request was made ( corresponding boolean
417 "PImgLoadFileInstance-> loadAll" flag is set for codec's information)
418 and frameCount is not initialized, then Prima starts loading all
419 frames, incrementing frame index until it receives an error. Assuming
420 the first error it gets is an EOF, it reports no error, so there's no
421 way for a high-level code to tell whether there was an loading error or
422 an end-of-file condition. Codec may initialize frameCount at any time
423 during open_load() or load(), even together with false return value.
424
426 Approach for handling saving requests is very similar to a load ones.
427 For the same reason and with same restrictions functions
428 save_defaults() open_save(), save() and close_save() are defined. Below
429 shown a typical saving code and highlighted differences from load. As
430 an example we'll take existing codec_X11.c, which defines extra hot
431 spot coordinates, x and y.
432
433 static HV *
434 save_defaults( PImgCodec c)
435 {
436 HV * profile = newHV();
437 pset_i( hotSpotX, 0);
438 pset_i( hotSpotY, 0);
439 return profile;
440 }
441
442 static void *
443 open_save( PImgCodec instance, PImgSaveFileInstance fi)
444 {
445 return (void*)1;
446 }
447
448 static Bool
449 save( PImgCodec instance, PImgSaveFileInstance fi)
450 {
451 PImage i = ( PImage) fi-> object;
452 Byte * l;
453 ...
454
455 fprintf( fi-> f, "#define %s_width %d\n", name, i-> w);
456 fprintf( fi-> f, "#define %s_height %d\n", name, i-> h);
457 if ( pexist( hotSpotX))
458 fprintf( fi-> f, "#define %s_x_hot %d\n", name, (int)pget_i( hotSpotX));
459 if ( pexist( hotSpotY))
460 fprintf( fi-> f, "#define %s_y_hot %d\n", name, (int)pget_i( hotSpotY));
461 fprintf( fi-> f, "static char %s_bits[] = {\n ", name);
462 ...
463 // printing of data bytes is omitted
464 }
465
466 static void
467 close_save( PImgCodec instance, PImgSaveFileInstance fi)
468 {
469 }
470
471 Save request takes into account defined supported types, that are
472 defined in "PImgCodecInfo-> saveTypes". Prima converts image to be
473 saved into one of these formats, before actual save() call takes place.
474 Another boolean flag, "PImgSaveFileInstance-> append" is summoned to
475 govern appending to or rewriting a file, but this functionality is
476 under design. Its current value is a hint, if true, for a codec not to
477 rewrite but rather append the frames to an existing file. Due to
478 increased complexity of the code, that should respond to the append
479 hint, this behavior is not required.
480
481 Codec may set two of PImgCodecInfo flags, canSave and canSaveMultiple.
482 Save requests will never be called if canSave is false, and append
483 requests along with multiframe save requests would be never invoked for
484 a codec with canSaveMultiple set to false. Scenario for a multiframe
485 save request is the same as for a load one. All the issues concerning
486 palette, data converting and saving extra information are actual,
487 however there's no corresponding flag like loadExtras - codec is
488 expected to save all information what it can extract from
489 "PImgSaveFileInstance-> objectExtras" hash.
490
492 Finally, the code have to be registered. It is not as illustrative but
493 this part better not to be oversimplified. A codec's callback
494 functions are set into ImgCodecVMT structure. Those function slots
495 that are unused should not be defined as dummies - those are already
496 defined and gathered under struct CNullImgCodecVMT. That's why all
497 functions in the illustration code were defined as static. A codec
498 have to provide some information that Prima uses to decide what codec
499 should load this particular file. If no explicit directions given,
500 Prima asks those codecs whose file extensions match to file's. init()
501 should return pointer to the filled struct, that describes codec's
502 capabilities:
503
504 // extensions to file - might be several, of course, thanks to dos...
505 static char * myext[] = { "duf", "duff", nil };
506
507 // we can work only with 1-bit/pixel
508 static int mybpp[] = {
509 imbpp1 | imGrayScale, // 1st item is a default type
510 imbpp1,
511 0 }; // Zero means end-of-list. No type has zero value.
512
513 // main structure
514 static ImgCodecInfo codec_info = {
515 "DUFF", // codec name
516 "Numb & Number, Inc.", // vendor
517 _LIBDUFF_VERS_MAJ, _LIBDUFF_VERS_MIN, // version
518 myext, // extension
519 "DUmb Format", // file type
520 "DUFF", // file short type
521 nil, // features
522 "", // module
523 true, // canLoad
524 false, // canLoadMultiple
525 false, // canSave
526 false, // canSaveMultiple
527 mybpp, // save types
528 nil, // load output
529 };
530
531 static void *
532 init( PImgCodecInfo * info, void * param)
533 {
534 *info = &codec_info;
535 return (void*)1; // just non-null, to indicate success
536 }
537
538 The result of init() is stored into "PImgCodec-> instance", and info
539 into "PImgCodec-> info". If dynamic memory was allocated for these
540 structs, it can be freed on done() invocation. Finally, the function
541 that is invoked from Prima, is the only that required to be exported,
542 is responsible for registering a codec:
543
544 void
545 apc_img_codec_duff( void )
546 {
547 struct ImgCodecVMT vmt;
548 memcpy( &vmt, &CNullImgCodecVMT, sizeof( CNullImgCodecVMT));
549 vmt. init = init;
550 vmt. open_load = open_load;
551 vmt. load = load;
552 vmt. close_load = close_load;
553 apc_img_register( &vmt, nil);
554 }
555
556 This procedure can register as many codecs as it wants to, but
557 currently Prima is designed so that one codec_XX.c file should be
558 connected to one library only.
559
560 The name of the procedure is apc_img_codec_ plus library name, that is
561 required for a compilation with Prima. File with the codec should be
562 called codec_duff.c ( is our case) and put into img directory in Prima
563 source tree. Following these rules, Prima will be assembled with
564 libduff.a ( or duff.lib, or whatever, the actual library name is system
565 dependent) - if the library is present.
566
568 Dmitry Karasik, <dmitry@karasik.eu.org>.
569
571 Prima, Prima::Image, Prima::internals, Prima::image-load
572
573
574
575perl v5.34.0 2021-07-22 pod::Prima::codecs(3)