home *** CD-ROM | disk | FTP | other *** search
-
- tutorial
- -----------------------------
-
- this tutorial intends to generally explain basic terms of image
- processing and to instruct the programming of render.library.
-
-
-
- introduction
- -----------------------------
-
- render.library provides low-level image processing. there are no
- functions implemented that interact with higher-level resources,
- such as rastports or datatypes. you may call render.library an
- image processing kernel.
-
- there's no reason for hesitation, though. i hope this tutorial
- will help you to find the thread to image processing and
- conversion. as you will see, the functions provided with
- render.library are very easy to use, once you've understood the
- basic terms, and most functions provide much more power and
- functionality than it might appear at first sight.
-
-
-
- philosophy
- -----------------------------
-
- function interfaces are kept lean and logical. first of all i
- focused on using as few definitions as possible. you will notice
- that you can use the majority of render.library's functions
- without setting up a single structure. each function was reduced
- to an absolute minimum of mandatory parameters. any argument that
- in some way could be assigned a default value to has been made
- optional and was banned to the taglists.
-
- render.library is layouted quasi object-oriented. everything is
- kept in black boxes, and objects heavily interact internally, only
- you don't know anything about the chemistry. many functions may be
- interpreted as constructors, destructors and methods, only the
- interfaces are implemented procedurally. there is nothing
- available for misguiding, insane assumptions.
-
-
- overview
- -----------------------------
-
- render.library covers the following domains:
-
- > planar to chunky conversion
-
- and vice versa. the functions in this domain are considered
- low-level, and they do not interact with higher-level
- graphics.library instances such as rastports. you have to
- take good care not to play havoc with non-standard bitmaps.
- please read the appropriate autodoc chapters very carefully.
-
- - Planar2ChunkyA()
- - Chunky2BitMapA()
-
-
- > chunky to truecolor conversion
-
- this is a simple task, and render.library serves it with
- high speed and flexibility. Amiga-specific modes (HAM6 and
- HAM8) are correctly handled even with horizontal offsets.
-
- - Chunky2RGBA()
-
-
- > palettes
-
- render.library uses a special type of color-lookup tables,
- called 'palettes'. a render.library palette is much more
- than a mere table of RGB values. all that nasty stuff is
- hidden from you throughout these functions:
-
- - CreatePaletteA()
- - ImportPaletteA()
- - ExportPaletteA()
- - SortPaletteA()
-
-
- > histograms
-
- histograms are in most cases not more than a means to an
- end. they are required for color reduction, statistics, and
- for certain low-level functions. render.library supports
- 'real' truecolor histograms with up to 24bit resolution.
-
- - CreateHistogramA()
- - AddChunkyImageA()
- - AddRGBImageA()
- - AddRGB()
- - AddHistogram()
- - CountRGB()
- - QueryHistogram()
-
-
- > color reduction (quantization)
-
- this task is everything but trivial. render.library
- uses a sophisticated algorithm that can stand the test
- with the most-elaborated implementations on the Amiga
- platform.
-
- - ExtractPaletteA()
-
-
- > truecolor to chunky conversion (rendering)
-
- this task was the initial idea for render.library, and
- render.library deserves its name. just have a look at all
- those possible taglist arguments for
-
- - RenderA()
-
-
- > chunky to chunky conversion
-
- this is similar to RenderA(), only that it accepts
- chunky bytes as input.
-
- - ConvertChunkyA()
- - CreatePenTableA()
-
-
- > mapping
-
- in addition to those fully-featured rendering and
- chunky-conversion functions, render.library offers a
- low-level conversion class called mapping-engine.
-
- - CreateMapEngineA()
- - MapChunkyArrayA()
- - MapRGBArrayA()
-
-
- > scaling
-
- render.library provides an own scaling class. its instances
- are called scaling-engines. the performance of
- non-interpolating scaling-engines comes close to CopyMem().
- Scaling-engines may be passed to RenderA() and
- ConvertChunkyA(), allowing scaling and rendering in a
- single pass.
-
- - CreateScaleEngineA()
- - ScaleA()
- - ScaleOrdinate()
-
-
- > alpha-channel
-
- this is a trivial task with render.library and can be
- achieved with these functions:
-
- - InsertAlphaChannelA()
- - ExtractAlphaChannelA()
- - ApplyAlphaChannelA()
- - MixRGBArrayA()
-
-
- > memory management
-
- render.library features a lean and simple, but yet
- effective memory management system throughout these
- functions:
-
- - CreateRMHandlerA()
- - AllocRenderMem()
- - AllocRenderVec()
-
-
-
- data types
- -----------------------------
-
- what kind of data can be processed with render.library?
- currently, there are the following formats available:
-
- > PIXFMT_0RGB_32 (ULONG)
-
- longword truecolor pixels, with the sequence
-
- 0x00rrggbb
-
- the upmost byte is not defined, and in most cases may
- contain alpha-channel information.
-
- > PIXFMT_CHUNKY_CLUT (UBYTE)
-
- chunky-byte pixels with color-lookup table. a pixel's
- actual color is not defined throughout this format.
- therefore you need a palette for most operations with
- arrays of chunky bytes. usually, the byte value
- acts directly as an index to the color-lookup table.
-
-
- for certain operations, an additional specification is required
- for the access to a color-lookup table:
-
- > COLORMODE_CLUT
-
- this mode defines 'normal' color lookup (see above). one
- byte directly acts as an index to a color-lookup table.
-
- > COLORMODE_HAM6
-
- this mode defines HAM6 color lookup. one byte does not
- necessarily reference a palette index.
-
- > COLORMODE_HAM6
-
- this mode defines HAM8 color lookup. one byte does not
- necessarily reference a palette index.
-
- remember that COLORMODE_HAM6 and COLORMODE_HAM8 still apply to
- chunky pixels, although HAM is an Amiga specific format that was
- (unfortunately) never defined for chunky bytes, but rather for
- planar data ('bitplanes').
-
-
-
- memory management
- -----------------------------
-
- since image processing is in general quite hungry for memory
- resources, render.library offers a both simple and effective
- custom memory management.
-
- you don't need to use it, but it may help you to make things
- easier and faster. under normal circumstances, you probably want
- to set up a pooled memory environment. pooled memory helps to
- avoid memory fragmentation, and it preserves the system's public
- memory lists from too much stressing. create a pooled memhandler
- as follows:
-
- memhandler = CreateRMHandler(RND_MemType, RMHTYPE_POOLED,
- TAG_DONE);
-
- * note: pooled memory is not available prior to exec v39. if
- not available, just create a 'standard' memory handler with
- RMHTYPE_PUBLIC - the default value. if your project won't
- include many large images, palettes and histograms, you
- might as well create a 'standard' memory handler.
-
- you might even live without a memhandler, since the memhandler
- argument is always optional - public memory will be used in
- this case. nevertheless, i recommend to use a memhandler. it
- helps you to easily upgrade your code to a more sophisticated
- memory management when necessary. for the use with dynamic
- histograms (see next section), a pooled memory handler is
- highly advised.
-
- you may also set up a memory manager that operates in a static
- block of memory. this is similar to the memory management of
- programs like AdPro. they grab a large block of memory at
- startup and allocate all intermediate buffers from this memory
- area. example:
-
- memhandler = CreateRMHandler(RND_MemType, RMHTYPE_PRIVATE,
- RND_MemBlock, my_memblock,
- RND_MemSize, my_memblock_size, TAG_DONE);
-
- * note: private memory management is less flexible than a
- pooled memory environment. once the static buffer is full or
- becomes fragmented, further allocations will fail. v39 exec
- pools will automatically grow and shrink to the required size.
-
- not only render.library functions may profit from a memhandler.
- your application has access to it throughout these functions:
-
- mem = AllocRenderMem(memhandler, size);
- FreeRenderMem(memhandler, mem, size);
-
- mem = AllocRenderVec(memhandler, size);
- FreeRenderVec(mem);
-
- the main idea is to allow your application and render.library to
- share a particular memory pool. this will keep the whole memory
- management lean and effective.
-
-
-
- histograms
- -----------------------------
-
- a histogram is an object that holds color information. it
- maintains a counter for each color.
-
- many functions are related to histograms. their creation is
- quite simple:
-
- histogram = CreateHistogram(RND_RMHandler, memhandler,
- RND_HSType, HSTYPE_15BIT_TURBO,
- TAG_DONE);
-
- render.library offers two different histogram schemes: the
- _TURBO types are provided with tables, the 'normal' types use
- digital trees. tabular histograms are very fast, but their
- memory consumption is determined according to 2^bits x 4
- bytes. with other words, a 12bit histogram is very small (8192
- bytes), but a 24bit histogram would require 64mb of memory. it
- would be nonsense to create a 24bit tabular histogram anyway,
- since most images do not contain all possible RGB values. that's
- why _TURBO histograms are limited to 12, 15, and 18 bit
- resolution.
-
- tree histograms are more flexible. their memory consumption is
- not predictable, because they are created dynamically. a
- drawback is that every single RGB entry in a tree histogram
- requires 20 bytes of memory. the tree type allows you to create
- 'real' histograms with full 24bit accuracy. by the way, this is
- the only way to evaluate the effective number of different
- colors in an image. (most image processing applications operate
- with 15bit histograms and tell you nonsense about the number of
- colors found.)
-
- * a 15 bit _TURBO histogram is the best choice in most cases.
- as mentioned before, most image processing applications use
- this resolution. render.library histograms are interpolated.
- this reasonably compensates the loss of information.
-
- now that we've created a histogram, what are we going to do with
- it? first of all, we have to load it with color information.
-
- success = AddRGBImage(histogram, rgb_array,
- 640, 480, NULL);
-
- in this example, a 640×480 truecolor image is added to the
- histogram. you may of course add as many pictures as you like,
- e.g. in order to record all the single frames of an animation.
- it's also possible to directly add chunky images to a histogram:
-
- success = AddChunkyImage(histogram, chunky_array, 640, 480,
- palette, NULL);
-
- (as you can see, chunky images require a palette. palettes will
- be covered by the next chapter.)
-
- * note: the return value <success> is not boolean, it's
- defined with constants. you must check for ADDH_SUCCESS, since
- adding data to histograms may fail at any time. your histogram
- won't be corrupted in that case, but if you don't check the
- return value, you can't depend on finding your histogram in a
- state that correctly represents the data you've added.
-
- you can find out about certain histogram parameters with the
- QueryHistogram() function. for instance
-
- number_of_pixels = QueryHistogram(histogram, RND_NumPixels);
- number_of_colors = QueryHistogram(histogram, RND_NumColors);
-
- another way to access histograms is the CountRGB() function. it
- returns the number of pixels recorded for a specific RGB value:
-
- pixels_represented = CountRGB(histogram, 0x00rrggbb);
-
- * note: according to current definitions, histograms can hold
- the information for 2^32 (4,28 billions) pixels. as long as no
- overflow occurs to a single entry inside the histogram, they
- may even contain 2^32 different colors. further
- histogram-related processing (e.g. quantization) is defined
- for 2^32 pixels, though.
-
-
-
- palettes
- -----------------------------
-
- most functions dealing with chunky pixels require color-lookup
- tables. in the context of render.library, they are called
- 'palettes'. palettes cover many technical details internally.
- a palette is created this way:
-
- palette = CreatePalette(NULL);
-
- this sets up a palette of 15bit resolution. these 15bit suffice
- in most situations, and they are taken into account only if some
- kind of rendering or mapping takes place. more control can be
- obtained via taglist arguments:
-
- palette = CreatePalette(RND_HSType, HSTYPE_12BIT, TAG_DONE);
-
- as you can see, a palette's resolution is specified analogously
- to a histogram's resolution.
-
- * note: palettes do not differenciate between _TURBO and
- non-TURBO types.
-
- in addition to that, you can supply CreatePalette() with a
- memhandler (which is not a bad idea at all):
-
- palette = CreatePalette(RND_RMHandler, memhandler, TAG_DONE);
-
- after all, what you get is just an empty palette ready for being
- loaded with colors. by default, ImportPaletteA() handles
- 0x00rrggbb color entries. you may as well import LoadRGB32-alike
- and LoadRGB4-type tables.
-
- ULONG colortable[5] =
- {0xff0088, 0x007532, 0x435278, 0xffffff, 0x000000};
-
- ImportPalette(palette, colortable, 5, NULL);
-
- you may import colors mulitple times and specify
- offsets:
-
- ImportPalette(palette, colortable1, 10, NULL);
- ImportPalette(palette, colortable2, 20, RND_NewPalette, FALSE,
- RND_FirstColor, 10, TAG_DONE);
-
- with RND_NewPalette set to FALSE, ImportPaletteA() merges
- color entries to a palette. the RND_FirstColor tag specifies
- the offset where to add new colors. the total number of
- colors in a palette is automatically maintained and updated.
- the only restriction is that you are neither allowed to
- import more than 256 colors, nor to import beyond the 256th
- entry. the counterpart of ImportPaletteA() is
- ExportPaletteA().
-
- another way to load a palette with colors is via
- ExtractPaletteA() - refer to the next section.
-
-
-
- quantization
- -----------------------------
-
- i've got you here, right? quantization ('color reduction') is
- extremely easy with render.library. you just have to supply a
- source histogram and a destination palette.
-
- success = ExtractPalette(histogram, palette, numcolors, NULL);
-
- this is the basic function call. it will extract the given
- number of colors from the histogram and insert it into the
- palette. refer to the autodocs for further details.
-
-
-
- rendering
- -----------------------------
-
- now for the climax of it all and to what gave render.library its
- name. let's transform a RGB array to chunky pixels:
-
- success = Render(rgbarray, width, height, chunkyarray,
- palette, tags);
-
- there are many tags available, for dithering, offsets, secondary
- conversions, HAM mode support, scaling, callback hooks, and
- more. the same applies to the chunky-byte equivalent:
-
- success = ConvertChunky(sourcearray, sourcepalette,
- width, height, destarray, destpalette, tags);
-
- no need to mention that these functions are quite fast. if
- they're still not fast enough for your purposes, refer to the
- next section.
-
-
-
- mapping
- -----------------------------
-
- you are disappointed by the speed of Render(), because it is
- only several times faster than any other software on the Amiga?
- then you have to enter the wonderful realm of mapping-engines.
- mapping-engines focus on three aspects: a) speed, b) speed, c)
- speed.
-
- * mapping-engines depend on a destination palette. The
- contents of that destination palette may change, but it must
- not be deleted prior to dependent mapping-engines.
-
- the mapping-engine may additionally depend on a histogram
- containing the color information of those pixels that are
- going to be mapped. this histogram has to be of a _TURBO
- type of the same resolution as the palette. if there's a
- histogram available that fits these specifications, you
- are strongly recommended to use it¹. this improves speed
- remarkably.
-
- mapengine = CreateMapEngine(destpalette, NULL);
-
- mapengine =
- CreateMapEngine(destpalette, RND_Histogram, histogram,
- TAG_DONE);
-
- now there are two ways of using a mapping-engine. the
- preferrable way is to pass it to RenderA() or ConvertChunkyA()
- with the RND_MapEngine tag argument. if you can manage without
- dithering, scaling, callback hooks and alike, you might as well
- use it with these functions:
-
- MapRGBArray(mapengine, rgbarray, width, height, chunkyarray,
- tags);
-
- MapChunkyArray(mapengine, sourcearray, sourcepalette, width,
- height, destarray, tags);
-
- the above functions have got very low overhead and provide
- rather primitive data transfer schemes. MapRGBArrayA() comes
- close to physical bus performance (with totally 4 memory
- accesses per pixel).
-
- * a drawback with MapRGBArrayA() is that your RGB pixels may not
- contain a trailing alphachannel-byte. it must by set to zero,
- otherwise you must use RenderA() instead.
-
- a call to a function with a mapping-engine can be quite slow
- when invoked for the first time (or whenever the destination
- palette changed). the mapping-engine's internal buffers have
- to be (re-)constructed then. that's where the optional
- histogram gets into action: only those RGB values actually
- appearing in the histogram are updated. if no histogram was
- specified with CreateMapEngineA(), a mapping-table will be
- calculated for the whole RGB space. the palette's resolution
- is a significant factor then: a 12bit RGB space holds 4096
- colors, a 15bit RGB space has got 32768 colors, and a 18bit
- RGB space contains 262144 colors. you better know what you're
- doing when creating a mapping-engine for a 18bit palette and
- do not specify a histogram.
-
- * ¹note: nothing serious happens if the supplied histogram
- does not correctly represent the data you convert with a
- mapping-engine. however, colors that are not part of the
- histogram would be mapped to nonsense pixels. unfortunately
- this applies to conversions with dithering. you should
- disable dithering when histogram-related mapping-engines are
- passed to RenderA() or ConvertChunkyA().
-