// -*- c++ -*-
/* $Id: pixbuf.ccg,v 1.8 2006/05/11 11:40:23 murrayc Exp $ */

/* Copyright 1998-2002 The gtkmm Development Team
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include <glibmm/vectorutils.h>

#include <gdkmm/window.h>
#include <gdk/gdk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

namespace
{

static void pixbuf_destroy_data_callback(guint8* pixels, void* user_data)
{
  Gdk::Pixbuf::SlotDestroyData *const slot = static_cast<Gdk::Pixbuf::SlotDestroyData*>(user_data);
  g_return_if_fail(slot != 0);

  try
  {
    (*slot)(pixels); // invoke callback
  }
  catch(...)
  {
    Glib::exception_handlers_invoke();
  }

  delete slot;
}

} // anonymous namespace


namespace Gdk
{

Pixbuf::Pixbuf(const Glib::RefPtr<Window>& src,
               int src_x, int src_y,
               int width, int height)
:
  Object((GObject*) gdk_pixbuf_get_from_window(
    src->gobj(), src_x, src_y, width, height))
{}

Pixbuf::Pixbuf(const Cairo::RefPtr<Cairo::Surface>& src,
         int src_x, int src_y, int width, int height)
:
  Object((GObject*) gdk_pixbuf_get_from_surface(
    src->cobj(), src_x, src_y, width, height))
{}

Glib::RefPtr<Pixbuf> Pixbuf::create_from_data(const guint8* data, Colorspace colorspace,
                                              bool has_alpha, int bits_per_sample,
                                              int width, int height, int rowstride)
{
  GdkPixbuf *const pixbuf = gdk_pixbuf_new_from_data(
      data, (GdkColorspace) colorspace, has_alpha, bits_per_sample, width, height, rowstride,
      0, 0);

  return Glib::wrap(pixbuf);
}

Glib::RefPtr<Pixbuf> Pixbuf::create_from_data(const guint8* data, Colorspace colorspace,
                                              bool has_alpha, int bits_per_sample,
                                              int width, int height, int rowstride,
                                              const Pixbuf::SlotDestroyData& destroy_slot)
{
  GdkPixbuf *const pixbuf = gdk_pixbuf_new_from_data(
      data, (GdkColorspace) colorspace, has_alpha, bits_per_sample, width, height, rowstride,
      &pixbuf_destroy_data_callback, new SlotDestroyData(destroy_slot));

  return Glib::wrap(pixbuf);
}

Glib::RefPtr<Pixbuf> Pixbuf::create_from_stream(const Glib::RefPtr<Gio::InputStream>& stream)
{
  GError* gerror = 0;
  Glib::RefPtr<Pixbuf> retvalue = Glib::wrap(gdk_pixbuf_new_from_stream(const_cast<GInputStream*>(Glib::unwrap(stream)), 0, &(gerror)));

  if(gerror)
    ::Glib::Error::throw_exception(gerror);

  return retvalue;
}

Glib::RefPtr<Pixbuf> Pixbuf::create_from_stream_at_scale(const Glib::RefPtr<Gio::InputStream>& stream, int width, int height, bool preserve_aspect_ratio)
{
  GError* gerror = 0;
  Glib::RefPtr<Pixbuf> retvalue = Glib::wrap(gdk_pixbuf_new_from_stream_at_scale(const_cast<GInputStream*>(Glib::unwrap(stream)), width, height, static_cast<int>(preserve_aspect_ratio), 0, &(gerror)));

  if(gerror)
    ::Glib::Error::throw_exception(gerror);

  return retvalue;
}

void Pixbuf::save(const std::string& filename, const Glib::ustring& type)
{
  GError* gerror = 0;
  gdk_pixbuf_savev(gobj(), filename.c_str(), type.c_str(), 0, 0, &gerror);

  if(gerror)
    ::Glib::Error::throw_exception(gerror);
}


void Pixbuf::save(const std::string& filename, const Glib::ustring& type,
                  const std::vector<Glib::ustring>& option_keys,
                  const std::vector<Glib::ustring>& option_values)
{
  GError* gerror = 0;

  gdk_pixbuf_savev(
      gobj(), filename.c_str(), type.c_str(),
      const_cast<char**>(Glib::ArrayHandler<Glib::ustring>::vector_to_array(option_keys).data ()),
      const_cast<char**>(Glib::ArrayHandler<Glib::ustring>::vector_to_array(option_values).data ()),
      &gerror);

  if(gerror)
    ::Glib::Error::throw_exception(gerror);
}


void Pixbuf::save_to_buffer(gchar*& buffer, gsize& buffer_size,
                            const Glib::ustring& type)
{
  GError* gerror = 0;

  gdk_pixbuf_save_to_buffer(
      gobj(), &buffer, &buffer_size,
      type.c_str(),
      &gerror, NULL);

  if(gerror)
    ::Glib::Error::throw_exception(gerror);
}

void Pixbuf::save_to_buffer(gchar*& buffer, gsize& buffer_size,
                            const Glib::ustring& type,
                            const std::vector<Glib::ustring>& option_keys,
                            const std::vector<Glib::ustring>& option_values)
{
  GError* gerror = 0;

  gdk_pixbuf_save_to_bufferv(
      gobj(), &buffer, &buffer_size,
      type.c_str(),
      const_cast<char**>(Glib::ArrayHandler<Glib::ustring>::vector_to_array(option_keys).data ()),
      const_cast<char**>(Glib::ArrayHandler<Glib::ustring>::vector_to_array(option_values).data ()),
      &gerror);

  if(gerror)
    ::Glib::Error::throw_exception(gerror);
}

std::vector<PixbufFormat> Pixbuf::get_formats()
{
  return Glib::SListHandler<PixbufFormat, PixbufFormatTraits>::slist_to_vector(gdk_pixbuf_get_formats(), Glib::OWNERSHIP_SHALLOW);
}

} // namespace Gdk
