//----------------------------------------------------------------------------//
bool WindowRenderer::contentFits() const
{
    throw InvalidRequestException("This function isn't implemented for this type of window renderer.");
}
Beispiel #2
0
//----------------------------------------------------------------------------//
float WidgetDim::getValue(const Window& wnd) const
{
    const Window* widget;

    // if target widget name is empty, then use the input window.
    if (d_widgetName.empty())
    {
        widget = &wnd;
    }
    // name not empty, so find window with required name
    else
    {
        widget = wnd.getChild(d_widgetName);
    }

    // get size of parent; required to extract pixel values
    Sizef parentSize(widget->getParentPixelSize());

    switch (d_what)
    {
        case DT_WIDTH:
            return widget->getPixelSize().d_width;
            break;

        case DT_HEIGHT:
            return widget->getPixelSize().d_height;
            break;

        case DT_X_OFFSET:
            Logger::getSingleton().logEvent("WigetDim::getValue - Nonsensical DimensionType of DT_X_OFFSET specified!  returning 0.0f");
            return 0.0f;
            break;

        case DT_Y_OFFSET:
            Logger::getSingleton().logEvent("WigetDim::getValue - Nonsensical DimensionType of DT_Y_OFFSET specified!  returning 0.0f");
            return 0.0f;
            break;

        case DT_LEFT_EDGE:
        case DT_X_POSITION:
            return CoordConverter::asAbsolute(widget->getPosition().d_x, parentSize.d_width);
            break;

        case DT_TOP_EDGE:
        case DT_Y_POSITION:
            return CoordConverter::asAbsolute(widget->getPosition().d_y, parentSize.d_height);
            break;

        case DT_RIGHT_EDGE:
            return CoordConverter::asAbsolute(widget->getArea().d_max.d_x, parentSize.d_width);
            break;

        case DT_BOTTOM_EDGE:
            return CoordConverter::asAbsolute(widget->getArea().d_max.d_y, parentSize.d_height);
            break;

        default:
            CEGUI_THROW(InvalidRequestException(
                "unknown or unsupported DimensionType encountered."));
            break;
    }
}
//----------------------------------------------------------------------------//
UDim WindowRenderer::getHeightOfAreaReservedForContentLowerBoundAsFuncOfWindowHeight() const
{
    throw InvalidRequestException("This function isn't implemented for this type of window renderer.");
}
//----------------------------------------------------------------------------//
bool WindowRenderer::contentFitsForSpecifiedWindowSize(const Sizef& /*window_size*/) const
{
    throw InvalidRequestException("This function isn't implemented for this type of window renderer.");
}
Beispiel #5
0
/*************************************************************************
	Load all resources for this scheme
*************************************************************************/
void Scheme::loadResources(void)
{
    Logger::getSingleton().logEvent((utf8*)"---- Begining resource loading for GUI scheme '" + d_name + "' ----", Informative);

    ImagesetManager& ismgr		= ImagesetManager::getSingleton();
    FontManager& fntmgr			= FontManager::getSingleton();
    WindowFactoryManager& wfmgr = WindowFactoryManager::getSingleton();
    WidgetLookManager& wlfMgr   = WidgetLookManager::getSingleton();

    std::vector<LoadableUIElement>::const_iterator	pos;

    // check imagesets
    for (pos = d_imagesets.begin(); pos != d_imagesets.end(); ++pos)
    {
        if (!ismgr.isImagesetPresent((*pos).name))
        {
            Imageset* iset = ismgr.createImageset((*pos).filename, (*pos).resourceGroup);

            // check for wrong imageset for specified name
            String realname = iset->getName();

            if (realname != (*pos).name)
            {
                ismgr.destroyImageset(iset);
                throw InvalidRequestException((utf8*)"Scheme::loadResources - The Imageset created by file '" +
                                              (*pos).filename + "' is named '" + realname + "', not '" + (*pos).name + "' as required by Scheme '" + d_name + "'.");
            }

        }

    }

    // check imagesets that are created directly from image files
    for (pos = d_imagesetsFromImages.begin(); pos != d_imagesetsFromImages.end(); ++pos)
    {
        if (!ismgr.isImagesetPresent((*pos).name))
            ismgr.createImagesetFromImageFile((*pos).name, (*pos).filename, (*pos).resourceGroup);
    }

    // check fonts
    for (pos = d_fonts.begin(); pos != d_fonts.end(); ++pos)
    {
        if (!fntmgr.isFontPresent((*pos).name))
        {
            Font* font = fntmgr.createFont((*pos).filename, (*pos).resourceGroup);

            // check for wrong font for specified name
            String realname = font->getName();

            if (realname != (*pos).name)
            {
                fntmgr.destroyFont(font);
                throw InvalidRequestException((utf8*)"Scheme::loadResources - The Font created by file '" +
                                              (*pos).filename + "' is named '" + realname + "', not '" + (*pos).name + "' as required by Scheme '" + d_name + "'.");
            }

        }

    }

    // load look'n'feels (can't actually check these, so just re-parse data; it does no harm except maybe wase a bit of time)
    for (pos = d_looknfeels.begin(); pos != d_looknfeels.end(); ++pos)
    {
        wlfMgr.parseLookNFeelSpecification((*pos).filename, (*pos).resourceGroup);
    }

    // check factories
    std::vector<UIModule>::iterator	cmod = d_widgetModules.begin();
    for (; cmod != d_widgetModules.end(); ++cmod)
    {
        // create and load dynamic module as required
        if ((*cmod).module == NULL)
        {
            (*cmod).module = new FactoryModule((*cmod).name);
        }

        // see if we should just register all factories available in the module (i.e. No factories explicitly specified)
        if ((*cmod).factories.size() == 0)
        {
            Logger::getSingleton().logEvent("No window factories specified for module '" + (*cmod).name + "' - adding all available factories...");
            (*cmod).module->registerAllFactories();
        }
        // some names were explicitly given, so only register those.
        else
        {
            std::vector<UIElementFactory>::const_iterator	elem = (*cmod).factories.begin();
            for (; elem != (*cmod).factories.end(); ++elem)
            {
                if (!wfmgr.isFactoryPresent((*elem).name))
                {
                    (*cmod).module->registerFactory((*elem).name);
                }
            }
        }
    }

    // check aliases
    std::vector<AliasMapping>::iterator alias = d_aliasMappings.begin();
    for (; alias != d_aliasMappings.end(); ++alias)
    {
        // get iterator
        WindowFactoryManager::TypeAliasIterator iter = wfmgr.getAliasIterator();

        // look for this alias
        while (!iter.isAtEnd() && (iter.getCurrentKey() != (*alias).aliasName))
            ++iter;

        // if the alias exists
        if (!iter.isAtEnd())
        {
            // if the current target type matches
            if (iter.getCurrentValue().getActiveTarget() == (*alias).targetName)
            {
                // assume this mapping is ours and skip to next alias
                continue;
            }

        }

        // create a new alias entry
        wfmgr.addWindowTypeAlias((*alias).aliasName, (*alias).targetName);
    }

    // check falagard window mappings.
    std::vector<FalagardMapping>::iterator falagard = d_falagardMappings.begin();
    for (; falagard != d_falagardMappings.end(); ++falagard)
    {
        // get iterator
        WindowFactoryManager::FalagardMappingIterator iter = wfmgr.getFalagardMappingIterator();

        // look for this mapping
        while (!iter.isAtEnd() && (iter.getCurrentKey() != (*falagard).windowName))
            ++iter;

        // if the alias exists
        if (!iter.isAtEnd())
        {
            // if the current target and looks match
            if ((iter.getCurrentValue().d_baseType == (*falagard).targetName) &&
                    (iter.getCurrentValue().d_lookName == (*falagard).lookName))
            {
                // assume this mapping is ours and skip to next
                continue;
            }
        }

        // create a new mapping entry
        wfmgr.addFalagardWindowMapping((*falagard).windowName, (*falagard).targetName, (*falagard).lookName);
    }

    Logger::getSingleton().logEvent((utf8*)"---- Resource loading for GUI scheme '" + d_name + "' completed ----", Informative);
}
//----------------------------------------------------------------------------//
float WindowRenderer::getContentHeight() const
{
    throw InvalidRequestException("This function isn't implemented for this type of window renderer.");
}
//----------------------------------------------------------------------------//
void RenderedStringImageComponent::draw(const Window* ref_wnd,
                                        std::vector<GeometryBuffer*>& geometry_buffers,
                                        const glm::vec2& position,
                                        const ColourRect* mod_colours,
                                        const Rectf* clip_rect,
                                        const float vertical_space,
                                        const float /*space_extra*/) const
{
    if (!d_image)
        return;

    Rectf dest(position.x, position.y, 0, 0);
    float y_scale = 1.0f;

    // handle formatting options
    switch (d_verticalFormatting)
    {
    case VF_BOTTOM_ALIGNED:
        dest.d_min.y += vertical_space - getPixelSize(ref_wnd).d_height;
        break;

    case VF_CENTRE_ALIGNED:
        dest.d_min.y += (vertical_space - getPixelSize(ref_wnd).d_height) / 2 ;
        break;

    case VF_STRETCHED:
        y_scale = vertical_space / getPixelSize(ref_wnd).d_height;
        break;

    case VF_TOP_ALIGNED:
        // nothing additional to do for this formatting option.
        break;

    default:
        throw InvalidRequestException(
            "unknown VerticalFormatting option specified.");
    }

    Sizef sz(d_image->getRenderedSize());
    if (d_size.d_width != 0.0)
        sz.d_width = d_size.d_width;
    if (d_size.d_height != 0.0)
        sz.d_height = d_size.d_height;
    
    sz.d_height *= y_scale;
    dest.setSize(sz);

    // apply padding to position
    dest.offset(d_padding.getPosition());

    // render the selection if needed
    if (d_selectionImage && d_selected)
    {
        const Rectf select_area(position, getPixelSize(ref_wnd));
        d_selectionImage->render(geometry_buffers, select_area, clip_rect, true, ColourRect(0xFF002FFF));
    }

    // apply modulative colours if needed.
    ColourRect final_cols(d_colours);
    if (mod_colours)
        final_cols *= *mod_colours;

    // draw the image. 
    d_image->render(geometry_buffers, dest, clip_rect, true, final_cols);
}
//----------------------------------------------------------------------------//
RenderedStringImageComponent* RenderedStringImageComponent::split(
    const Window* /*ref_wnd*/ ,float /*split_point*/, bool /*first_component*/)
{
    throw InvalidRequestException(
        "this component does not support being split.");
}
void FrameComponent::doBackgroundRender(Window& srcWindow, Rect& destRect, float base_z, const ColourRect& colours, const Rect* clipper, bool clipToDisplay) const
{
    HorizontalFormatting horzFormatting = d_horzFormatPropertyName.empty() ? d_horzFormatting :
                                          FalagardXMLHelper::stringToHorzFormat(srcWindow.getProperty(d_horzFormatPropertyName));

    VerticalFormatting vertFormatting = d_vertFormatPropertyName.empty() ? d_vertFormatting :
                                        FalagardXMLHelper::stringToVertFormat(srcWindow.getProperty(d_vertFormatPropertyName));

    uint horzTiles, vertTiles;
    float xpos, ypos;

    Size imgSz(d_frameImages[FIC_BACKGROUND]->getSize());

    // calculate initial x co-ordinate and horizontal tile count according to formatting options
    switch (horzFormatting)
    {
    case HF_STRETCHED:
        imgSz.d_width = destRect.getWidth();
        xpos = destRect.d_left;
        horzTiles = 1;
        break;

    case HF_TILED:
        xpos = destRect.d_left;
        horzTiles = (uint)((destRect.getWidth() + (imgSz.d_width - 1)) / imgSz.d_width);
        break;

    case HF_LEFT_ALIGNED:
        xpos = destRect.d_left;
        horzTiles = 1;
        break;

    case HF_CENTRE_ALIGNED:
        xpos = destRect.d_left + PixelAligned((destRect.getWidth() - imgSz.d_width) * 0.5f);
        horzTiles = 1;
        break;

    case HF_RIGHT_ALIGNED:
        xpos = destRect.d_right - imgSz.d_width;
        horzTiles = 1;
        break;

    default:
        throw InvalidRequestException("FrameComponent::doBackgroundRender - An unknown HorizontalFormatting value was specified.");
    }

    // calculate initial y co-ordinate and vertical tile count according to formatting options
    switch (vertFormatting)
    {
    case VF_STRETCHED:
        imgSz.d_height = destRect.getHeight();
        ypos = destRect.d_top;
        vertTiles = 1;
        break;

    case VF_TILED:
        ypos = destRect.d_top;
        vertTiles = (uint)((destRect.getHeight() + (imgSz.d_height - 1)) / imgSz.d_height);
        break;

    case VF_TOP_ALIGNED:
        ypos = destRect.d_top;
        vertTiles = 1;
        break;

    case VF_CENTRE_ALIGNED:
        ypos = destRect.d_top + PixelAligned((destRect.getHeight() - imgSz.d_height) * 0.5f);
        vertTiles = 1;
        break;

    case VF_BOTTOM_ALIGNED:
        ypos = destRect.d_bottom - imgSz.d_height;
        vertTiles = 1;
        break;

    default:
        throw InvalidRequestException("FrameComponent::doBackgroundRender - An unknown VerticalFormatting value was specified.");
    }

    // perform final rendering (actually is now a caching of the images which will be drawn)
    Rect finalRect;
    Rect finalClipper;
    const Rect* clippingRect;
    finalRect.d_top = ypos;
    finalRect.d_bottom = ypos + imgSz.d_height;

    for (uint row = 0; row < vertTiles; ++row)
    {
        finalRect.d_left = xpos;
        finalRect.d_right = xpos + imgSz.d_width;

        for (uint col = 0; col < horzTiles; ++col)
        {
            // use custom clipping for right and bottom edges when tiling the imagery
            if (((vertFormatting == VF_TILED) && row == vertTiles - 1) ||
                    ((horzFormatting == HF_TILED) && col == horzTiles - 1))
            {
                finalClipper = clipper ? clipper->getIntersection(destRect) : destRect;
                clippingRect = &finalClipper;
            }
            // not tiliing, or not on far edges, just used passed in clipper (if any).
            else
            {
                clippingRect = clipper;
            }

            // add image to the rendering cache for the target window.
            srcWindow.getRenderCache().cacheImage(*d_frameImages[FIC_BACKGROUND], finalRect, base_z, colours, clippingRect, clipToDisplay);

            finalRect.d_left += imgSz.d_width;
            finalRect.d_right += imgSz.d_width;
        }

        finalRect.d_top += imgSz.d_height;
        finalRect.d_bottom += imgSz.d_height;
    }
}
//----------------------------------------------------------------------------//
void MinizipResourceProvider::loadRawDataContainer(const String& filename,
                                                   RawDataContainer& output,
                                                   const String& resourceGroup)
{
    const String final_filename = getFinalFilename(filename, resourceGroup);

    if (d_pimpl->d_loadLocal && doesFileExist(final_filename))
    {
        DefaultResourceProvider::loadRawDataContainer(filename,
                                                      output,
                                                      resourceGroup);
        return;
    }

    if (d_pimpl->d_zfile == 0)
    {
        throw InvalidRequestException(
            "'" + final_filename + "' cannot be "
            "loaded because the archive has not been set");
    }

#if CEGUI_STRING_CLASS == CEGUI_STRING_CLASS_STD
    if (unzLocateFile(d_pimpl->d_zfile, final_filename.c_str(), 0) != UNZ_OK)
#elif CEGUI_STRING_CLASS == CEGUI_STRING_CLASS_UNICODE
    if (unzLocateFile(d_pimpl->d_zfile, final_filename.toUtf8String().c_str(), 0) != UNZ_OK)
#endif
    {
        throw InvalidRequestException("'" + final_filename +
            "' does not exist");
    }

    unz_file_info file_info;

    if (unzGetCurrentFileInfo(d_pimpl->d_zfile, &file_info,
                              0, 0, 0, 0, 0, 0) != UNZ_OK)
    {
        throw FileIOException("'" + final_filename +
            "' error reading file header");
    }

    if (unzOpenCurrentFile(d_pimpl->d_zfile) != Z_OK)
    {
        throw FileIOException("'" + final_filename +
            "' error opening file");
    }

    std::uint64_t size = file_info.uncompressed_size;
    std::uint8_t* buffer = new std::uint8_t[size];

    if (unzReadCurrentFile(d_pimpl->d_zfile, buffer, size) < 0)
    {
        throw FileIOException("'" + final_filename +
            "' error reading file");
    }

    if (unzCloseCurrentFile(d_pimpl->d_zfile) != UNZ_OK)
    {
        throw GenericException("'" + final_filename +
            "' error validating file");
    }

    output.setData(buffer);
    output.setSize(size);
}
//----------------------------------------------------------------------------//
RenderedStringTextComponent* RenderedStringTextComponent::split(
                                                        const Window* ref_wnd,
                                                        float split_point,
                                                        bool first_component)
{
    const Font* fnt = getEffectiveFont(ref_wnd);

    // This is checked, but should never fail, since if we had no font our
    // extent would be 0 and we would never cause a split to be needed here.
    if (!fnt)
        CEGUI_THROW(InvalidRequestException(
            "unable to split with no font set."));

    // create 'left' side of split and clone our basic configuration
    RenderedStringTextComponent* lhs = CEGUI_NEW_AO RenderedStringTextComponent();
    lhs->d_padding = d_padding;
    lhs->d_verticalFormatting = d_verticalFormatting;
    lhs->d_font = d_font;
    lhs->d_colours = d_colours;

    // calculate the 'best' place to split the text
    size_t left_len = 0;
    float left_extent = 0.0f;

    while (left_len < d_text.length())
    {
        size_t token_len = getNextTokenLength(d_text, left_len);
        // exit loop if no more valid tokens.
        if (token_len == 0)
            break;

        const float token_extent = 
            fnt->getTextExtent(d_text.substr(left_len, token_len));

        // does the next token extend past the split point?
        if (left_extent + token_extent > split_point)
        {
            // if it was the first token, split the token itself
            if (first_component && left_len == 0)
                left_len =
                    ceguimax(static_cast<size_t>(1),
                             fnt->getCharAtPixel(
                                d_text.substr(0, token_len), split_point));
            
            // left_len is now the character index at which to split the line
            break;
        }
        
        // add this token to the left side
        left_len += token_len;
        left_extent += token_extent;
    }
    
    // perform the split.
    lhs->d_text = d_text.substr(0, left_len);

    // here we're trimming leading delimiters from the substring range 
    size_t rhs_start =
        d_text.find_first_not_of(TextUtils::DefaultWrapDelimiters, left_len);
    if (rhs_start == String::npos)
        rhs_start = left_len;

    // split the selection
    if (d_selectionLength)
    {
        const size_t sel_end = d_selectionStart + d_selectionLength - 1;
        lhs->d_selectionStart = d_selectionStart;
        lhs->d_selectionLength = sel_end < left_len ? d_selectionLength : left_len - d_selectionStart;

        if (sel_end >= left_len)
        {
            d_selectionStart = 0;
            d_selectionLength -= rhs_start;
        }
        else
            setSelection(ref_wnd, 0, 0);
    }

    d_text = d_text.substr(rhs_start);

    return lhs;
}
//----------------------------------------------------------------------------//
void RenderedStringTextComponent::draw(const Window* ref_wnd,
                                       GeometryBuffer& buffer,
                                       const Vector2f& position,
                                       const ColourRect* mod_colours,
                                       const Rectf* clip_rect,
                                       const float vertical_space,
                                       const float space_extra) const
{
    const Font* fnt = getEffectiveFont(ref_wnd); 

    if (!fnt)
        return;

    Vector2f final_pos(position);
    float y_scale = 1.0f;

    // handle formatting options
    switch (d_verticalFormatting)
    {
    case VF_BOTTOM_ALIGNED:
        final_pos.d_y += vertical_space - getPixelSize(ref_wnd).d_height;
        break;

    case VF_CENTRE_ALIGNED:
        final_pos.d_y += (vertical_space - getPixelSize(ref_wnd).d_height) / 2 ;
        break;

    case VF_STRETCHED:
        y_scale = vertical_space / getPixelSize(ref_wnd).d_height;
        break;

    case VF_TOP_ALIGNED:
        // nothing additional to do for this formatting option.
        break;

    default:
        CEGUI_THROW(InvalidRequestException(
            "unknown VerticalFormatting option specified."));
    }

    // apply padding to position:
    final_pos += d_padding.getPosition();

    // apply modulative colours if needed.
    ColourRect final_cols(d_colours);
    if (mod_colours)
        final_cols *= *mod_colours;

    // render selection
    if (d_selectionImage && d_selectionLength)
    {
        float sel_start_extent = 0, sel_end_extent = 0;

        if (d_selectionStart > 0)
            sel_start_extent = fnt->getTextExtent(d_text.substr(0, d_selectionStart));

        sel_end_extent = fnt->getTextExtent(d_text.substr(0, d_selectionStart + d_selectionLength));

        Rectf sel_rect(position.d_x + sel_start_extent,
                       position.d_y,
                       position.d_x + sel_end_extent,
                       position.d_y + vertical_space);

        d_selectionImage->render(buffer, sel_rect, clip_rect, ColourRect(0xFF002FFF));
    }

    // draw the text string.
    fnt->drawText(buffer, d_text, final_pos, clip_rect, final_cols,
                  space_extra, 1.0f, y_scale);
}
//----------------------------------------------------------------------------//
void FrameComponent::renderImage(std::vector<GeometryBuffer*>& geometry_buffers, const Image* image,
                                 VerticalFormatting vertFmt,
                                 HorizontalFormatting horzFmt,
                                 Rectf& destRect, const ColourRect& colours,
                                 const Rectf* clipper, bool clip_to_display) const
{
    uint horzTiles, vertTiles;
    float xpos, ypos;

    Sizef imgSz(image->getRenderedSize());

    // calculate initial x co-ordinate and horizontal tile count according to formatting options
    switch (horzFmt)
    {
        case HF_STRETCHED:
            imgSz.d_width = destRect.getWidth();
            xpos = destRect.left();
            horzTiles = 1;
            break;

        case HF_TILED:
            xpos = destRect.left();
            horzTiles = std::abs(static_cast<int>(
                (destRect.getWidth() + (imgSz.d_width - 1)) / imgSz.d_width));
            break;

        case HF_LEFT_ALIGNED:
            xpos = destRect.left();
            horzTiles = 1;
            break;

        case HF_CENTRE_ALIGNED:
            xpos = destRect.left() + CoordConverter::alignToPixels((destRect.getWidth() - imgSz.d_width) * 0.5f);
            horzTiles = 1;
            break;

        case HF_RIGHT_ALIGNED:
            xpos = destRect.right() - imgSz.d_width;
            horzTiles = 1;
            break;

        default:
            CEGUI_THROW(InvalidRequestException(
                "An unknown HorizontalFormatting value was specified."));
    }

    // calculate initial y co-ordinate and vertical tile count according to formatting options
    switch (vertFmt)
    {
        case VF_STRETCHED:
            imgSz.d_height = destRect.getHeight();
            ypos = destRect.top();
            vertTiles = 1;
            break;

        case VF_TILED:
            ypos = destRect.top();
            vertTiles = std::abs(static_cast<int>(
                (destRect.getHeight() + (imgSz.d_height - 1)) / imgSz.d_height));
            break;

        case VF_TOP_ALIGNED:
            ypos = destRect.top();
            vertTiles = 1;
            break;

        case VF_CENTRE_ALIGNED:
            ypos = destRect.top() + CoordConverter::alignToPixels((destRect.getHeight() - imgSz.d_height) * 0.5f);
            vertTiles = 1;
            break;

        case VF_BOTTOM_ALIGNED:
            ypos = destRect.bottom() - imgSz.d_height;
            vertTiles = 1;
            break;

        default:
            CEGUI_THROW(InvalidRequestException(
                "An unknown VerticalFormatting value was specified."));
    }

    // perform final rendering (actually is now a caching of the images which will be drawn)
    Rectf finalRect;
    Rectf finalClipper;
    const Rectf* clippingRect;
    finalRect.d_min.d_y = ypos;
    finalRect.d_max.d_y = ypos + imgSz.d_height;

    for (uint row = 0; row < vertTiles; ++row)
    {
        finalRect.d_min.d_x = xpos;
        finalRect.d_max.d_x = xpos + imgSz.d_width;

        for (uint col = 0; col < horzTiles; ++col)
        {
            // use custom clipping for right and bottom edges when tiling the imagery
            if (((vertFmt == VF_TILED) && row == vertTiles - 1) ||
                ((horzFmt == HF_TILED) && col == horzTiles - 1))
            {
                finalClipper = clipper ? clipper->getIntersection(destRect) : destRect;
                clippingRect = &finalClipper;
            }
            // not tiliing, or not on far edges, just used passed in clipper (if any).
            else
                clippingRect = clipper;

            image->render(geometry_buffers, finalRect, clippingRect, !clip_to_display, colours);

            finalRect.d_min.d_x += imgSz.d_width;
            finalRect.d_max.d_x += imgSz.d_width;
        }

        finalRect.d_min.d_y += imgSz.d_height;
        finalRect.d_max.d_y += imgSz.d_height;
    }
}