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