Logo Search packages:      
Sourcecode: timfx version File versions  Download package

image_luma.cc

// timfx 
// Copyright 2002, Timothy M. Shead
//
// Contact: tshead@k-3d.com
//
// 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

#include "image_luma.h"
#include "kino_plugin_types.h"

#include <kino/image_transitions.h>

#include <gtkmm/adjustment.h>
#include <gtkmm/box.h>
#include <gtkmm/button.h>
#include <gtkmm/checkbutton.h>
#include <gtkmm/entry.h>
#include <gtkmm/fileselection.h>
#include <gtkmm/main.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/window.h>
#include <gdk-pixbuf/gdk-pixbuf.h>

#include <algorithm>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

namespace
{

class invert_luma
{
public:
      void operator()(kino::basic_luma<double>& RHS)
      {
            RHS.luma = 1.0 - RHS.luma;
      }
};
      
class image_luma :
      public GDKImageTransition
{
public:
      image_luma() :
            m_filepath(std::string(g_get_home_dir()) + "/"),
            m_softness(0.2),
            m_reverse(false),
            m_interlaced(true),
            m_even_field_first(true)
      {
            Gtk::Main main(0, 0);

            // Setup our user interface ...
            m_path_entry.set_text(m_filepath);

            Gtk::Button* const browse_button = new Gtk::Button("Browse ...");
            browse_button->signal_clicked().connect(sigc::mem_fun(*this, &image_luma::on_browse));

            Gtk::HBox* const file_group = new Gtk::HBox(false, 0);
            file_group->pack_start(*manage(new Gtk::Label("Image:")), false, true);
            file_group->pack_start(m_path_entry, true, true);
            file_group->pack_start(*manage(browse_button), false, true);

            m_reverse_button.add(*manage(new Gtk::Label("Reverse")));
            m_reverse_button.set_active(m_reverse);

            m_interlaced_button.add(*manage(new Gtk::Label("Interlace")));
            m_interlaced_button.set_active(m_interlaced);

            m_even_field_first_button.add(*manage(new Gtk::Label("Lower Field First")));
            m_even_field_first_button.set_active(m_even_field_first);

            m_softness_spin_button.set_adjustment(*manage(new Gtk::Adjustment(m_softness, 0.0, 1.0, 0.01, 0.1)));
            m_softness_spin_button.set_numeric(true);
            m_softness_spin_button.set_digits(2);
            m_softness_spin_button.set_wrap(false);
            m_softness_spin_button.set_snap_to_ticks(true);

            Gtk::HBox* const options_group = new Gtk::HBox(false, 0);
            options_group->pack_start(*manage(new Gtk::Label("Softness:")), false, true);
            options_group->pack_start(m_softness_spin_button, true, true);

            Gtk::VBox* const vbox = new Gtk::VBox(false, 0);
            vbox->pack_start(*manage(file_group), false, true);
            vbox->pack_start(*manage(options_group), false, true);
            vbox->pack_start(m_reverse_button, false, true);
            vbox->pack_start(m_interlaced_button, false, true);
            vbox->pack_start(m_even_field_first_button, false, true);
            vbox->show_all();

            m_window.add(*manage(vbox));
      }

      char *GetDescription( ) const
      {
            return "Image Luma"; 
      }

      void GetFrame( uint8_t *io, uint8_t *mesh, int width, int height, double position, double frame_delta, bool reverse )
      {
            if ( !m_luma.data() )
            {
                  GError *gerr = NULL;

                  // Load the luma definition from file
                  kino::raii<GdkPixbuf> raw_image( gdk_pixbuf_new_from_file( m_filepath.c_str(), &gerr ),
                        reinterpret_cast< void (*)(GdkPixbuf*) >( g_object_unref ) );
                  if ( !raw_image.get() )
                        throw("failed to load luma image from file");

                  // Scale the luma definition to the frame
                  kino::raii<GdkPixbuf> scaled_image( gdk_pixbuf_scale_simple( raw_image.get(), width, height, GDK_INTERP_HYPER ),
                        reinterpret_cast< void (*)(GdkPixbuf*) >( g_object_unref ) );

                  // Cache the image luma as doubles, normalized in the range [0, 1]
                  m_luma.reset( width, height );
                  std::copy( reinterpret_cast< kino::basic_rgb<uint8_t>* >( gdk_pixbuf_get_pixels( scaled_image.get() ) ),
                        reinterpret_cast< kino::basic_rgb<uint8_t>* >( gdk_pixbuf_get_pixels( scaled_image.get() ) +
                              height * gdk_pixbuf_get_rowstride( scaled_image.get() ) ),
                        m_luma.begin() );
                  if ( m_reverse )
                        std::for_each( m_luma.begin(), m_luma.end(), invert_luma() );
            }
      
            const kino::basic_rgb<uint8_t>* a = reinterpret_cast<kino::basic_rgb<uint8_t>*>(io);
            const kino::basic_rgb<uint8_t>* b = reinterpret_cast<kino::basic_rgb<uint8_t>*>(mesh);
            kino::basic_rgb<uint8_t>* output = reinterpret_cast<kino::basic_rgb<uint8_t>*>(io);

            for( int field = 0; field < (m_interlaced ? 2 : 1); ++field )
            {
                  // Offset the position based on which field we're looking at ...
                  const double field_position = position + ((m_even_field_first ? 1 - field : field) * frame_delta * 0.5);

                  // Scale the position so that the entire wipe will be complete (including the "soft" edge) by the time we finish ...
                  const double adjusted_position = kino::lerp(0.0, 1.0 + m_softness, field_position);

                  // For each row ...
                  for( int row = field; row < height; row += (m_interlaced ? 2 : 1) )
                  {
                        const kino::basic_rgb<uint8_t>* pixel_a = &a[row * width];
                        const kino::basic_rgb<uint8_t>* pixel_b = &b[row * width];
                        const kino::basic_luma<double>* pixel_luma = &m_luma.data()[row * width];
                        kino::basic_rgb<uint8_t>* pixel_output = &output[row * width];

                        for( int column = 0; column < width; ++column )
                        {
                              const double mix = kino::smoothstep(pixel_luma->luma, pixel_luma->luma + m_softness, adjusted_position);
                              
                              pixel_output->red = kino::lerp(pixel_a->red, pixel_b->red, mix);
                              pixel_output->green = kino::lerp(pixel_a->green, pixel_b->green, mix);
                              pixel_output->blue = kino::lerp(pixel_a->blue, pixel_b->blue, mix);
                              
                              ++pixel_a;
                              ++pixel_b;
                              ++pixel_luma;
                              ++pixel_output;
                        }
                  }
            }
      }

      void AttachWidgets(GtkBin* bin)
      {
            gtk_widget_reparent( ( GTK_BIN( m_window.gobj() ) )->child, GTK_WIDGET( bin ) );
      }

      void DetachWidgets(GtkBin* bin)
      {
            gtk_widget_reparent( ( GTK_BIN( bin ) )->child, GTK_WIDGET( m_window.gobj() ) );
      }

      void InterpretWidgets( GtkBin *bin )
      {
            m_filepath = m_path_entry.get_chars(0, -1);
            m_softness = m_softness_spin_button.get_value();
            m_reverse = m_reverse_button.get_active();
            m_interlaced = m_interlaced_button.get_active();
            m_even_field_first = m_even_field_first_button.get_active();
            
            m_luma.clear();
      }

private:
      void on_browse()
      {
            m_file_selection_dialog.set_filename(m_path_entry.get_chars(0, -1));
            m_file_selection_dialog.show();
            m_file_selection_dialog.run();
            m_path_entry.set_text(m_file_selection_dialog.get_filename());
            m_file_selection_dialog.hide();
      }
      
      std::string m_filepath;
      kino::basic_bitmap<kino::basic_luma<double> > m_luma;
      double m_softness;
      bool m_reverse;
      bool m_interlaced;
      bool m_even_field_first;

      Gtk::Window m_window;
      Gtk::Entry m_path_entry;
      Gtk::FileSelection m_file_selection_dialog;
      Gtk::SpinButton m_softness_spin_button;
      Gtk::CheckButton m_reverse_button;
      Gtk::CheckButton m_interlaced_button;
      Gtk::CheckButton m_even_field_first_button;
};

} // namespace

GDKImageTransition* image_luma_factory()
{
      return new image_luma();
}

Generated by  Doxygen 1.6.0   Back to index