// SPDX-License-Identifier: GPL-2.0-or-later
/**
 * @file
 * Cairo drawing context with Inkscape extensions.
 *//*
 * Authors:
 *   Krzysztof Kosiński <tweenk.pl@gmail.com>
 *
 * Copyright (C) 2011 Authors
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#include "display/drawing-context.h"
#include "display/drawing-surface.h"
#include "display/cairo-utils.h"

namespace Inkscape {

using Geom::X;
using Geom::Y;

/**
 * @class DrawingContext::Save
 * RAII idiom for saving the state of DrawingContext.
 */

DrawingContext::Save::Save()
    : _dc(nullptr)
{}
DrawingContext::Save::Save(DrawingContext &dc)
    : _dc(&dc)
{
    _dc->save();
}
DrawingContext::Save::~Save()
{
    if (_dc) {
        _dc->restore();
    }
}
void DrawingContext::Save::save(DrawingContext &dc)
{
    if (_dc) {
        // TODO: it might be better to treat this occurrence as a bug
        _dc->restore();
    }
    _dc = &dc;
    _dc->save();
}

/**
 * @class DrawingContext
 * Minimal wrapper over Cairo.
 *
 * This is a wrapper over cairo_t, extended with operations that work
 * with 2Geom geometrical primitives. Some of this is probably duplicated
 * in cairo-render-context.cpp, which provides higher level operations
 * for drawing entire SPObjects when exporting.
 */

// Uses existing Cairo surface
DrawingContext::DrawingContext(cairo_t *ct, Geom::Point const &origin)
    : _ct(ct)
    , _surface(new DrawingSurface(cairo_get_group_target(ct), origin))
    , _delete_surface(true)
    , _restore_context(true)
{
    _surface->_has_context = true;
    cairo_reference(_ct);
    cairo_save(_ct);
    cairo_translate(_ct, -origin[Geom::X], -origin[Geom::Y]);
}

// Uses existing Cairo surface
DrawingContext::DrawingContext(cairo_surface_t *surface, Geom::Point const &origin)
    : _ct(nullptr)
    , _surface(new DrawingSurface(surface, origin))
    , _delete_surface(true)
    , _restore_context(false)
{
    _surface->_has_context = true;
    _ct = _surface->createRawContext();
}

DrawingContext::DrawingContext(DrawingSurface &s)
    : _ct(s.createRawContext())
    , _surface(&s)
    , _delete_surface(false)
    , _restore_context(false)
{}

DrawingContext::~DrawingContext()
{
    if (_restore_context) {
        cairo_restore(_ct);
    }
    cairo_destroy(_ct);
    _surface->_has_context = false;
    if (_delete_surface) {
        delete _surface;
    }
}

void DrawingContext::arc(Geom::Point const &center, double radius, Geom::AngleInterval const &angle)
{
    double from = angle.initialAngle();
    double to = angle.finalAngle();
    if (to > from) {
        cairo_arc(_ct, center[X], center[Y], radius, from, to);
    } else {
        cairo_arc_negative(_ct, center[X], center[Y], radius, to, from);
    }
}

// Applies transform to Cairo surface
void DrawingContext::transform(Geom::Affine const &trans) {
    ink_cairo_transform(_ct, trans);
}

void DrawingContext::path(Geom::PathVector const &pv) {
    feed_pathvector_to_cairo(_ct, pv);
}

void DrawingContext::paint(double alpha) {
    if (alpha == 1.0) cairo_paint(_ct);
    else cairo_paint_with_alpha(_ct, alpha);
}

void DrawingContext::setHairline() {
    ink_cairo_set_hairline(_ct);
}

void DrawingContext::setSource(guint32 rgba) {
    ink_cairo_set_source_rgba32(_ct, rgba);
}
void DrawingContext::setSource(DrawingSurface *s) {
    Geom::Point origin = s->origin();
    cairo_set_source_surface(_ct, s->raw(), origin[X], origin[Y]);
}
void DrawingContext::setSourceCheckerboard() {
    cairo_pattern_t *check = ink_cairo_pattern_create_checkerboard();
    cairo_set_source(_ct, check);
    cairo_pattern_destroy(check);
}

Geom::Rect DrawingContext::targetLogicalBounds() const
{
    Geom::Rect ret(_surface->area());
    return ret;
}

} // end namespace Inkscape

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
