/* $Id: cpl_frameset.c,v 1.27 2007/08/20 12:13:06 scastro Exp $ * * This file is part of the ESO Common Pipeline Library * Copyright (C) 2001-2005 European Southern Observatory * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * $Author: scastro $ * $Date: 2007/08/20 12:13:06 $ * $Revision: 1.27 $ * $Name: $ */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include "cpl_frameset.h" #include "cpl_errorstate.h" /** * @defgroup cpl_frameset Frame Sets * * The module implements a container type for frames. Frames can be stored * in a frame set and retrieved, either by searching for a particular * frame tag or by sequential access. Frame sets can be created, filled and * saved to a so called `set of frames' file or loaded from such a file. * * @par Synopsis: * @code * #include * @endcode */ /**@{*/ /* * The frame set type. */ enum _cpl_frameset_cacheid { TAG, POS, ALL }; typedef enum _cpl_frameset_cacheid cpl_frameset_cacheid; struct _cpl_frameset_cache_ { cx_multimap_iterator tag; cx_multimap_iterator pos; }; typedef struct _cpl_frameset_cache_ cpl_frameset_cache; struct _cpl_frameset_ { cx_multimap *frames; cx_list *history; cpl_frameset_cache cache; }; /* * Private methods */ inline static cxbool _cpl_frameset_cache_empty(const cpl_frameset *self, cpl_frameset_cacheid id) { cxbool state; switch (id) { case TAG: state = self->cache.tag == NULL; break; case POS: state = self->cache.pos == NULL; break; case ALL: state = (self->cache.tag == NULL && self->cache.pos == NULL); break; } return state; } inline static void _cpl_frameset_cache_reset(cpl_frameset *self, cpl_frameset_cacheid id) { switch (id) { case TAG: self->cache.tag = NULL; break; case POS: self->cache.pos = NULL; break; case ALL: self->cache.tag = NULL; self->cache.pos = NULL; break; } return; } inline static void _cpl_frameset_cache_push(cpl_frameset *self, cpl_frameset_cacheid id, cx_multimap_const_iterator position) { switch (id) { case TAG: self->cache.tag = (cx_multimap_iterator)position; break; case POS: self->cache.pos = (cx_multimap_iterator)position; break; case ALL: self->cache.tag = (cx_multimap_iterator)position; self->cache.pos = (cx_multimap_iterator)position; break; } return; } inline static cx_multimap_iterator _cpl_frameset_cache_get(const cpl_frameset *self, cpl_frameset_cacheid id) { cxptr entry; switch (id) { case TAG: entry = self->cache.tag; break; case POS: entry = self->cache.pos; break; default: entry = NULL; break; } return entry; } static cxbool _cpl_frameset_compare(cxcptr s, cxcptr t) { return strcmp(s, t) < 0 ? TRUE : FALSE; } /* * Get the first frame in the set with the provided tag, or NULL if such * a frame does not exist. */ inline static cpl_frame * _cpl_frameset_get(const cpl_frameset *self, const char *tag) { cx_multimap_iterator entry; entry = cx_multimap_lower_bound(self->frames, tag); if (entry == cx_multimap_upper_bound(self->frames, tag)) return NULL; return cx_multimap_get_value(self->frames, entry); } /* * Public methods */ /** * @brief * Create a new, empty frame set. * * @return * The handle for the newly created frame set. * * The function allocates the memory for the new frame set, initialises the * set to be empty and returns a handle for it. */ cpl_frameset * cpl_frameset_new(void) { cpl_frameset *self = cx_malloc(sizeof *self); self->frames = cx_multimap_new(_cpl_frameset_compare, NULL, (cx_free_func)cpl_frame_delete); self->history = cx_list_new(); _cpl_frameset_cache_reset(self, ALL); return self; } /** * @brief * Create a copy of the given frame set. * * @param other The frame set to be copied. * * @return * A handle for the created clone. The function returns @c NULL if an * error occurs and sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter other is a NULL pointer. *
* @enderror * * The function creates a deep copy, i.e. the frame set object and its * contents, of the frame set @em other. The created copy and the original * set do not share any resources. */ cpl_frameset * cpl_frameset_duplicate(const cpl_frameset *other) { const cxchar *const _id = "cpl_frameset_duplicate"; cx_list_iterator first, last; cpl_frameset *self = NULL; if (other == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return NULL; } self = cpl_frameset_new(); first = cx_list_begin(other->history); last = cx_list_end(other->history); while (first != last) { cx_multimap_iterator position = cx_list_get(other->history, first); cpl_frame *frame = cx_multimap_get_value(other->frames, position); cpl_frame *tmp = cpl_frame_duplicate(frame); cxptr key = (cxptr)cpl_frame_get_tag(tmp); position = cx_multimap_insert(self->frames, key, tmp); cx_list_push_back(self->history, position); first = cx_list_next(other->history, first); } _cpl_frameset_cache_reset(self, ALL); return self; } /** * @brief * Destroy a frame set. * * @param self The frame set to destroy. * * @return Nothing. * * The function destroys the frame set @em self and its whole contents. */ void cpl_frameset_delete(cpl_frameset *self) { if (self) { cx_multimap_delete(self->frames); cx_list_delete(self->history); cx_free(self); } return; } /** * @brief * Get the current size of a frame set. * * @param self A frame set. * * @return * The frame set's current size, or 0 if it is empty. The function * returns 0 if an error occurs and sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The reports the current number of frames stored in the frame set * @em self. */ int cpl_frameset_get_size(const cpl_frameset *self) { const cxchar *const _id = "cpl_frameset_get_size"; if (self == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return 0; } return cx_multimap_size(self->frames); } /** * @brief * Check whether a frame set is empty. * * @param self A frame set. * * @return * The function returns 1 if the set is empty, and 0 otherwise. If an * error occurs 0 is returned and an appropriate error code is set. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The function checks if @em self contains any frames. */ int cpl_frameset_is_empty(const cpl_frameset *self) { const cxchar *const _id = "cpl_frameset_is_empty"; if (self == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return 0; } return cx_multimap_empty(self->frames); } /** * @brief * Counts the frames stored in a frame set having the given tag. * * @param self A frame set. * @param tag The frame tag. * * @return * The number of frames with tag @em tag. The function returns 0 if an * error occurs and sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self or tag is a NULL * pointer. *
* @enderror * * The function scans the frame set @em self for frames with the tag @em tag * and returns the number of frames found. */ int cpl_frameset_count_tags(const cpl_frameset *self, const char *tag) { const cxchar *const _id = "cpl_frameset_count_tags"; if (self == NULL || tag == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return 0; } return cx_multimap_count(self->frames, tag); } /** * @brief * Find a frame with the given tag in a frame set. * * @param self A frame set. * @param tag The frame tag to search for. * * @return * The handle for a frame with tag @em tag, or @c NULL if no * such frame was found. The function returns @c NULL if an error * occurs and sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self or tag is a NULL * pointer. *
* @enderror * * The function searches the frame set @em self for the frames with the * tag @em tag. If such a frame is present, a handle for it is returned. * If the set contains several frames with the tag @em tag the first * one is returned. The remaining frames with this tag can be accessed * sequentially by using @c NULL as tag when calling this function * repeatedly, since the most recent frame accessed is cached. This * cache is reset whenever the provided tag is not @c NULL. If no frame * with the tag @em tag is present in @em self or no more frames with * this tag are found the function returns @c NULL. */ const cpl_frame * cpl_frameset_find_const(const cpl_frameset *self, const char *tag) { const cxchar *const _id = "cpl_frameset_find_const"; cx_multimap_iterator pos; cx_multimap_iterator cached; cpl_frame *frame; if (self == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return NULL; } if (tag) { /* * Find the first frame with this tag in the set. Make sure that * we really found something. */ pos = cx_multimap_lower_bound(self->frames, tag); if (pos == cx_multimap_upper_bound(self->frames, tag)) { _cpl_frameset_cache_reset((cpl_frameset *)self, TAG); return NULL; } } else { cxptr last_tag; /* * Check that the cache is not empty. */ if (_cpl_frameset_cache_empty(self, TAG)) { return NULL; } cached = _cpl_frameset_cache_get(self, TAG); /* * Get the successor of the cached entry. */ last_tag = cx_multimap_get_key(self->frames, cached); pos = cx_multimap_next(self->frames, cached); if (pos == cx_multimap_upper_bound(self->frames, last_tag)) { return NULL; } } _cpl_frameset_cache_push((cpl_frameset *)self, TAG, pos); frame = cx_multimap_get_value(self->frames, pos); return frame; } /** * @brief * Find a frame with the given tag in a frame set. * * @param self A frame set. * @param tag The frame tag to search for. * * @return * The handle for a frame with tag @em tag, or @c NULL if no * such frame was found. The function returns @c NULL if an error * occurs and sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self or tag is a NULL * pointer. *
* @enderror * * The function searches the frame set @em self for the frames with the * tag @em tag. If such a frame is present, a handle for it is returned. * If the set contains several frames with the tag @em tag the first * one is returned. The remaining frames with this tag can be accessed * sequentially by using @c NULL as tag when calling this function * repeatedly, since the most recent frame accessed is cached. This * cache is reset whenever the provided tag is not @c NULL. If no frame * with the tag @em tag is present in @em self or no more frames with * this tag are found the function returns @c NULL. */ cpl_frame * cpl_frameset_find(cpl_frameset *self, const char *tag) { cpl_errorstate prestate = cpl_errorstate_get(); cpl_frame *frame = (cpl_frame *)cpl_frameset_find_const(self, tag); if (!cpl_errorstate_is_equal(prestate)) (void)cpl_error_set_where(cpl_func); return frame; } /** * @brief * Get the first frame in the given set. * * @param self A frame set. * * @return * A handle for the first frame in the set, or @c NULL if the set * is empty. The function returns @c NULL if an error occurs and * sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The function returns the first frame in the frame set @em self if it * exists. If a first frame does not exist, i.e. the frame set is empty, * @c NULL is returned. The function also updates the internal cache. * * @see cpl_frameset_get_next_const() */ const cpl_frame * cpl_frameset_get_first_const(const cpl_frameset *self) { const cxchar *const _id = "cpl_frameset_get_first_const"; cx_list_iterator first; cx_multimap_iterator position; if (self == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return NULL; } first = cx_list_begin(self->history); if (first == cx_list_end(self->history)) { return NULL; } position = cx_list_get(self->history, first); cx_assert(position != cx_multimap_end(self->frames)); _cpl_frameset_cache_push((cpl_frameset *)self, POS, position); return cx_multimap_get_value(self->frames, position); } /** * @brief * Get the first frame in the given set. * * @param self A frame set. * * @return * A handle for the first frame in the set, or @c NULL if the set * is empty. The function returns @c NULL if an error occurs and * sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The function returns the first frame in the frame set @em self if it * exists. If a first frame does not exist, i.e. the frame set is empty, * @c NULL is returned. The function also updates the internal cache. * * @see cpl_frameset_get_next() */ cpl_frame * cpl_frameset_get_first(cpl_frameset *self) { cpl_errorstate prestate = cpl_errorstate_get(); cpl_frame *frame = (cpl_frame *)cpl_frameset_get_first_const(self); if (!cpl_errorstate_is_equal(prestate)) (void)cpl_error_set_where(cpl_func); return frame; } /** * @brief * Get the next frame in the given set. * * @param self A frame set. * * @return * A handle for the next frame in a set. If there are no more * frames in the set the function returns @c NULL. The function returns * @c NULL if an error occurs and sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The function returns the next frame in the frame set @em self if it * exists and otherwise @c NULL. The function uses the internal cache * to determine the most recently accessed frame. This means that the * function only works as expected if @em self has been initialised by * a call to @b cpl_frameset_get_first_const(), and if no function updating the * internal cache was called between two subsequent calls to this * function. * * @see cpl_frameset_get_first_const() */ const cpl_frame * cpl_frameset_get_next_const(const cpl_frameset *self) { const cxchar *const _id = "cpl_frameset_get_next_const"; cx_list_iterator first; cx_list_iterator last; cx_multimap_iterator next; if (self == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return NULL; } cx_assert(_cpl_frameset_cache_get(self, POS) != NULL); first = cx_list_begin(self->history); last = cx_list_end(self->history); while (first != last) { next = cx_list_get(self->history, first); if (next == _cpl_frameset_cache_get(self, POS)) { break; } first = cx_list_next(self->history, first); } first = cx_list_next(self->history, first); if (first == cx_list_end(self->history)) { return NULL; } next = cx_list_get(self->history, first); if (next == cx_multimap_end(self->frames)) { return NULL; } _cpl_frameset_cache_push((cpl_frameset *)self, POS, next); return cx_multimap_get_value(self->frames, next); } /** * @brief * Get the next frame in the given set. * * @param self A frame set. * * @return * A handle for the next frame in a set. If there are no more * frames in the set the function returns @c NULL. The function returns * @c NULL if an error occurs and sets an appropriate error code. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The function returns the next frame in the frame set @em self if it * exists and otherwise @c NULL. The function uses the internal cache * to determine the most recently accessed frame. This means that the * function only works as expected if @em self has been initialised by * a call to @b cpl_frameset_get_first(), and if no function updating the * internal cache was called between two subsequent calls to this * function. * * @see cpl_frameset_get_first() */ cpl_frame * cpl_frameset_get_next(cpl_frameset *self) { cpl_errorstate prestate = cpl_errorstate_get(); cpl_frame *frame = (cpl_frame *)cpl_frameset_get_next_const(self); if (!cpl_errorstate_is_equal(prestate)) (void)cpl_error_set_where(cpl_func); return frame; } /** * @brief * Insert a frame into the given frame set. * * @param self A frame set. * @param frame The frame to insert. * * @return * The function returns @c CPL_ERROR_NONE on success or a CPL error * code otherwise. * * @error * * * * * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self or frame is a NULL * pointer. *
CPL_ERROR_ILLEGAL_INPUT * The parameter frame has an invalid tag. *
* @enderror * * The function adds the frame @em frame to the frame set @em self using the * frame's tag as key. * * The insertion of a frame into a frameset transfers the ownership of the * frame @em frame to the frameset @em self. This means that the frame must * not be deallocated through the pointer @em frame. * * In addition, the frame pointer returned by any member function call * returning a handle to a frameset's member frame, must not be used to * insert the returned frame into another framset without prior duplication * of this frame, and, it must not be used to modify the frames tag without * removing it from the frameset first and re-inserting it with the new * tag afterwards. */ cpl_error_code cpl_frameset_insert(cpl_frameset *self, cpl_frame *frame) { const cxchar *const _id = "cpl_frameset_insert"; const cxchar *tag = NULL; cx_multimap_iterator position; if (self == NULL || frame == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return CPL_ERROR_NULL_INPUT; } tag = cpl_frame_get_tag(frame); if (tag == NULL) { cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT); return CPL_ERROR_ILLEGAL_INPUT; } position = cx_multimap_insert(self->frames, tag, frame); cx_list_remove(self->history, position); cx_list_push_back(self->history, position); return CPL_ERROR_NONE; } /** * @brief * Erase all frames with the given tag from a frame set. * * @param self A frame set. * @param tag The tag used to locate the frames to remove. * * @return * The function returns the number of frames removed. If an error occurs * 0 is returned and an appropriate error code is set. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self or tag is a NULL * pointer. *
* @enderror * * The function searches the frame set @em self for frames having the * tag @em tag and removes them from the set. The removed frames are * destroyed. If no frame with the tag @em tag is found the function * has no effect. */ int cpl_frameset_erase(cpl_frameset *self, const char *tag) { const cxchar *const _id = "cpl_frameset_erase"; cxsize count = 0; cx_multimap_iterator first; cx_multimap_iterator last; cx_multimap_iterator position; if (self == NULL || tag == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return count; } cx_multimap_equal_range(self->frames, tag, &first, &last); position = first; while (position != last) { cx_assert(strcmp(cx_multimap_get_key(self->frames, position), tag) == 0); if (_cpl_frameset_cache_get(self, TAG) == position) { cx_multimap_iterator p; p = cx_multimap_previous(self->frames, position); if (p == cx_multimap_end(self->frames)) { p = cx_multimap_begin(self->frames); } _cpl_frameset_cache_push(self, TAG, p); } if (_cpl_frameset_cache_get(self, POS) == position) { cx_list_iterator p = cx_list_begin(self->history); while (cx_list_get(self->history, p) != position && p != cx_list_end(self->history)) { p = cx_list_next(self->history, p); } cx_assert(p != cx_list_end(self->history)); p = cx_list_previous(self->history, p); if (p == cx_list_end(self->history)) { p = cx_list_begin(self->history); } _cpl_frameset_cache_push(self, POS, cx_list_get(self->history, p)); } ++count; cx_list_remove(self->history, position); position = cx_multimap_next(self->frames, position); } cx_multimap_erase_range(self->frames, first, last); return count; } /** * @brief * Erase the given frame from a frame set. * * @param self A frame set. * @param frame The frame to remove. * * @return * The function returns @c CPL_ERROR_NONE on success or a CPL error * code otherwise. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self or frame is a NULL * pointer. *
* @enderror * * The function searches the frame set @em self for the first occurrance * of @em frame. If it is present, the frame is removed from the set and * destroyed. If frame is not present in @em self the function has no * effect. */ cpl_error_code cpl_frameset_erase_frame(cpl_frameset *self, cpl_frame *frame) { const cxchar *const _id = "cpl_frameset_erase_frame"; cx_multimap_iterator first, last; if (self == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return CPL_ERROR_NULL_INPUT; } if (!frame) { return CPL_ERROR_NONE; } first = cx_multimap_lower_bound(self->frames, cpl_frame_get_tag(frame)); last = cx_multimap_upper_bound(self->frames, cpl_frame_get_tag(frame)); while (first != last) { if (cx_multimap_get_value(self->frames, first) == frame) { if (_cpl_frameset_cache_get(self, TAG) == first) { cx_multimap_iterator p; p = cx_multimap_previous(self->frames, first); if (p == cx_multimap_end(self->frames)) { p = cx_multimap_begin(self->frames); } _cpl_frameset_cache_push(self, TAG, p); } if (_cpl_frameset_cache_get(self, POS) == first) { cx_list_iterator p = cx_list_begin(self->history); while (cx_list_get(self->history, p) != first && p != cx_list_end(self->history)) { p = cx_list_next(self->history, p); } cx_assert(p != cx_list_end(self->history)); p = cx_list_previous(self->history, p); if (p == cx_list_end(self->history)) { p = cx_list_begin(self->history); } _cpl_frameset_cache_push(self, POS, cx_list_get(self->history, p)); } cx_multimap_erase_position(self->frames, first); cx_list_remove(self->history, first); break; } first = cx_multimap_next(self->frames, first); } return CPL_ERROR_NONE; } /** * @brief * Separate a list of frames into groups, using a comparison function * * @param self Input frame set * @param compare Pointer to comparison function to use. * @param nb_labels Number of different sets or undefined on error * * @return * array of labels defining the selection or NULL on error * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self, compare or nb_labels * is a NULL pointer. *
* @enderror * * This function takes a set of frames and groups the frames that are * 'identical' together. The user provided comparison function defines what * being identical means for two frames. A label (non-negative int) is associated * to each group of identical frames, these labels are returned in an array of * length equal to the size of the frameset. * * The comparison function should be commutative, must take two frames and * return 1 if they are identical, 0 if they are different, and -1 on error. * * The number of calls to the comparison functions is O(n*m), where n is the * number of frames in the set, and m is the number of different labels found * in the set. In the worst case m equals n, and the call requires n(n-1)/2 * calls to the comparison function. If all identical frames appear together * in the list, the number of required calls is only n + O(m^2). * * The returned array must be deallocated with cpl_free(). */ int * cpl_frameset_labelise(const cpl_frameset *self, int (*compare)(const cpl_frame*, const cpl_frame*), int *nb_labels) { cxint *labels; cxint *labelsinv; cxint nframes; cxint i; cxint nlabels = 0; cxint j = 0; cxint ncomp = 0; const cpl_frame ** framelist; const cpl_frame * frame1; cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL); cpl_ensure(compare != NULL, CPL_ERROR_NULL_INPUT, NULL); cpl_ensure(nb_labels != NULL, CPL_ERROR_NULL_INPUT, NULL); nframes = cpl_frameset_get_size(self); cpl_ensure(nframes >= 1, CPL_ERROR_ILLEGAL_INPUT, NULL); labels = cx_malloc(nframes * sizeof(cxint)); labelsinv = cx_malloc(nframes * sizeof(cxint)); framelist = cx_malloc(nframes * sizeof(const cpl_frame *)); for (frame1 = cpl_frameset_get_first_const(self), i = 0; frame1 != NULL; frame1 = cpl_frameset_get_next_const(self), i++) { cxint jj; for (jj = 0; jj < nlabels; jj++, j = j == nlabels-1 ? 0 : j+1) { const cpl_frame * frame2 = framelist[j]; /* * Compare the frames i and j * - frame i is first compared to the frame which matched the * previous comparison. In this way only one comparison is * needed for frames tagged as the previous one */ const int comp = (*compare)(frame1, frame2); ncomp++; if (comp == 1) { /* Identical */ break; } else if (comp != 0) { /* Error */ cx_free(labels); cx_free(framelist); cx_free(labelsinv); cpl_ensure(0, CPL_ERROR_UNSPECIFIED, NULL); } } if (jj == nlabels) { /* Labelise the newly found type of frame */ framelist[nlabels] = frame1; labelsinv[nlabels] = i; labels[i] = nlabels++; } else { /* Identical */ labels[i] = labels[labelsinv[j]]; } } *nb_labels = nlabels; cx_free(framelist); cx_free(labelsinv); cpl_msg_debug(cpl_func, "%d frames labelized with %d labels after %d " "comparisons", nframes, nlabels, ncomp); return labels; } /** * @brief * Get a frame from a frame set. * * @param set Input frame set. * @param position The requested frame. * * @return * The function returns a handle to the frame at position @em position in * the set, or @c NULL in case an error occurs. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The function returns a handle to the frame at the index @em position in * the set. The frame position ranges from @c 0 to one less than the size of * the frame set. * * The returned frame is still owned by the frame set @em set, i.e. the * obtained frame must not be deleted through the returned handle and also * its tag must not be modified. * * As an alternative to using this function, the functions * cpl_frameset_get_first_const() and cpl_frameset_get_next_const() should be * considered, if performance is an issue. * * @see * cpl_frameset_get_size(), cpl_frameset_get_first_const(), * cpl_frameset_get_next_const() */ const cpl_frame * cpl_frameset_get_frame_const(const cpl_frameset *set, int position) { const cxchar *const _id = "cpl_frameset_get_frame_const"; cxint nbframes; cxint i; const cpl_frame *frame; if (set == NULL) { cpl_error_set(_id, CPL_ERROR_NULL_INPUT); return NULL; } /* * Get the number of frames and check ind */ nbframes = cpl_frameset_get_size(set); if (position < 0 || position >= nbframes) { cpl_error_set(_id, CPL_ERROR_ILLEGAL_INPUT); return NULL; } /* * Find the requested frame */ frame = cpl_frameset_get_first_const(set); for (i = 0; i < position; i++) { frame = cpl_frameset_get_next_const(set); } return frame; } /** * @brief * Get a frame from a frame set. * * @param set Input frame set. * @param position The requested frame. * * @return * The function returns a handle to the frame at position @em position in * the set, or @c NULL in case an error occurs. * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self is a NULL pointer. *
* @enderror * * The function returns a handle to the frame at the index @em position in * the set. The frame position ranges from @c 0 to one less than the size of * the frame set. * * The returned frame is still owned by the frame set @em set, i.e. the * obtained frame must not be deleted through the returned handle and also * its tag must not be modified. * * As an alternative to using this function, the functions * cpl_frameset_get_first() and cpl_frameset_get_next() should be considered, * if performance is an issue. * * @see * cpl_frameset_get_size(), cpl_frameset_get_first(), * cpl_frameset_get_next() */ cpl_frame * cpl_frameset_get_frame(cpl_frameset *set, int position) { cpl_errorstate prestate = cpl_errorstate_get(); cpl_frame *frame = (cpl_frame *)cpl_frameset_get_frame_const(set, position); if (!cpl_errorstate_is_equal(prestate)) (void)cpl_error_set_where(cpl_func); return frame; } /** * @brief * Extract a subset of frames from a set of frames * * @param self Input frame set * @param labels The array of labels associated to each input frame * @param desired_label The label identifying the requested frames * @note The array of labels must have (at least) the length of the frameset * * @return a pointer to a newly allocated frame set or NULL on error * * @error * * * * * *
CPL_ERROR_NULL_INPUT * The parameter self or labels is a NULL * pointer. *
* @enderror * * The returned object must be deallocated with cpl_frameset_delete() */ cpl_frameset * cpl_frameset_extract(const cpl_frameset *self, const int *labels, int desired_label) { cpl_frameset *selected = NULL; const cpl_frame *frame; cxint i; cpl_ensure(self != NULL, CPL_ERROR_NULL_INPUT, NULL); cpl_ensure(labels != NULL, CPL_ERROR_NULL_INPUT, NULL); for (frame = cpl_frameset_get_first_const(self), i = 0; frame != NULL; frame = cpl_frameset_get_next_const(self), i++) { if (labels[i] == desired_label) { /* * Duplicate frame and insert it in the selected object */ cpl_frame * framecopy = cpl_frame_duplicate(frame); if (selected == NULL) selected = cpl_frameset_new(); cpl_frameset_insert(selected, framecopy); } } return selected; } /**@}*/