1pod::Prima::codecs(3) User Contributed Perl Documentationpod::Prima::codecs(3)
2
3
4

NAME

6       Prima::codecs - How to write a codec for Prima image subsystem
7

DESCRIPTION

9       How to write a codec for Prima image subsystem
10

Start simple

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

Single-frame loading

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

Multiple-frame load

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 one,
413       to help Prima filter range requests before they go down to the codec.
414       The only real problem that may happen to the codec which it strongly
415       unwilling to initialize frameCount, is as follows.  If a loadAll
416       request was made ( corresponding boolean "PImgLoadFileInstance->
417       loadAll" flag is set for codec's information) and frameCount is not
418       initialized, then Prima starts loading all frames, incrementing frame
419       index until it receives an error. Assuming the first error it gets is
420       an EOF, it reports no error, so there's no way for a high-level code to
421       tell whether there was an loading error or an end-of-file condition.
422       Codec may initialize frameCount at any time during open_load() or
423       load(), even together with false return value.
424

Saving

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

Registering with image subsystem

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

AUTHOR

568       Dmitry Karasik, <dmitry@karasik.eu.org>.
569

SEE ALSO

571       Prima, Prima::Image, Prima::internals, Prima::image-load
572
573
574
575perl v5.36.0                      2023-03-20             pod::Prima::codecs(3)
Impressum