#!/usr/bin/env python

#   Gimp-Python - allows the writing of Gimp plugins in Python.
#   Copyright (C) 2007 Kalman, Ferenc <fkalman@index.hu>
#
#   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.


import math, struct
from gimpfu import *

class pixel_fetcher:
        def __init__(self, drawable):
                self.col = -1
                self.row = -1
                self.img_width = drawable.width
                self.img_height = drawable.height
                self.img_bpp = drawable.bpp
                self.img_has_alpha = drawable.has_alpha
                self.tile_width = 64
                self.tile_height = 64
                self.bg_colour = '\0\0\0\0'
                self.bounds = drawable.mask_bounds
                self.drawable = drawable
                self.tile = None
        def set_bg_colour(self, r, g, b, a):
                self.bg_colour = struct.pack('BBB', r,g,b)
                if self.img_has_alpha:
                        self.bg_colour = self.bg_colour + chr(a)
        def get_pixel(self, x, y):
                sel_x1, sel_y1, sel_x2, sel_y2 = self.bounds
                if x < sel_x1 or x >= sel_x2 or y < sel_y1 or y >= sel_y2:
                        return self.bg_colour
                col = x / self.tile_width
                coloff = x % self.tile_width
                row = y / self.tile_height
                rowoff = y % self.tile_height

                if col != self.col or row != self.row or self.tile == None:
                        self.tile = self.drawable.get_tile2(FALSE, row, col)
                        if self.tile is None:
                            print("Tile is none at "+str(row)+","+str(col))
                        self.col = col
                        self.row = row
                return self.tile[coloff, rowoff]
        def set_pixel(self, x, y, pixel):
                sel_x1, sel_y1, sel_x2, sel_y2 = self.bounds
                if x < sel_x1 or x >= sel_x2 or y < sel_y1 or y >= sel_y2:
                        return
                col = x / self.tile_width
                coloff = x % self.tile_width
                row = y / self.tile_height
                rowoff = y % self.tile_height

                if col != self.col or row != self.row or self.tile == None:
                        self.tile = self.drawable.get_tile2(FALSE, row, col)
                        if self.tile is None:
                            print("Tile is none at "+str(row)+","+str(col))
                        self.col = col
                        self.row = row
                self.tile[coloff, rowoff] = pixel

class Dummy:
        pass

def python_feca_hdr(image, drawable, width1, width2, blur, levelLight, levelDark):
        self = Dummy()
        self.width = drawable.width
        self.height = drawable.height
        self.bpp = drawable.bpp
        self.has_alpha = drawable.has_alpha
        self.bounds = drawable.mask_bounds
        self.sel_x1, self.sel_y1, self.sel_x2, self.sel_y2 = \
                     drawable.mask_bounds
        self.sel_w = self.sel_x2 - self.sel_x1
        self.sel_h = self.sel_y2 - self.sel_y1
        
        gimp.tile_cache_ntiles(2 * (self.width + 63) / 64)

        if (len(image.layers) != 3):
                pdb.gimp_message("You need have exactly 3 layers (order is not important): Normal, +EV, -EV!")
                return

        pdb.gimp_image_undo_group_start(image)

        layer_normal = image.layers[2]
        layer_dark = image.layers[1]
        layer_bright = image.layers[0]
        
        layer_normal.add_alpha()

        progress = 0
        max_progress = 5
        gimp.progress_init("High Dynamic Range with Tone Mapping...")

        pfn = pixel_fetcher(layer_normal)
        pfd = pixel_fetcher(layer_dark)
        pfb = pixel_fetcher(layer_bright)
        
        cn = 0
        cd = 0
        cb = 0
        
        for row in range(self.sel_y1, self.sel_y2, 50):
            for col in range(self.sel_x1, self.sel_x2, 50):
                pixeln = pfn.get_pixel(col, row)
                pixelb = pfb.get_pixel(col, row)
                pixeld = pfd.get_pixel(col, row)
                rn = ord(pixeln[0]) + 1
                gn = ord(pixeln[1]) + 1
                bn = ord(pixeln[2]) + 1
                rd = ord(pixeld[0]) + 1
                gd = ord(pixeld[1]) + 1
                bd = ord(pixeld[2]) + 1
                rb = ord(pixelb[0]) + 1
                gb = ord(pixelb[1]) + 1
                bb = ord(pixelb[2]) + 1
                if (rn > rd): cn = cn +1 
                else: cd = cd + 1
                if (gn > gd): cn = cn +1
                else: cd = cd + 1
                if (bn > bd): cn = cn +1
                else: cd = cd + 1
                if (rn > rb): cn = cn +1
                else: cb = cb + 1
                if (gn > gb): cn = cn +1
                else: cb = cb + 1
                if (bn > bb): cn = cn +1
                else: cb = cb + 1
                if (rd > rb): cd = cd +1
                else: cb = cb + 1
                if (gd > gb): cd = cd +1
                else: cb = cb + 1
                if (bd > bb): cd = cd +1
                else: cb = cb + 1
        if (cn > cd):
            if (cn > cb):
                layer = layer_bright
                layer_bright = layer_normal
                if (cd > cb):
                    layer_normal = layer_dark
                    layer_dark = layer
                    image.lower_layer(layer_normal)
                else:
                    layer_normal = layer
                    image.lower_layer(layer_normal)
                    image.lower_layer(layer_normal)
        else:
            if (cd > cb):
                layer = layer_bright
                layer_bright = layer_dark
                if (cn > cb):
                    layer_dark = layer
                else:
                    layer_dark = layer_normal
                    layer_normal = layer
                    image.lower_layer(layer_normal)
                    image.lower_layer(layer_normal)
            else:
                layer = layer_normal
                layer_normal = layer_dark
                layer_dark = layer
                image.lower_layer(layer_normal)

        if (layer_dark.mask != None):
            pdb.gimp_layer_remove_mask(layer_dark, 1)
        if (layer_bright.mask != None):
            pdb.gimp_layer_remove_mask(layer_bright, 1)

        layer_two = layer_normal.copy()
        layer_two.name = "Dark"
        image.add_layer(layer_two, 0)
        
        progress += 1
        gimp.progress_update(float(progress) / max_progress)

        cp2 = (0,0, 255-width2,25, 255-width1,240, 255,255)
        pdb.gimp_curves_spline(layer_two, HISTOGRAM_VALUE, 8, cp2)
        pdb.gimp_drawable_set_visible(layer_two, 0)

        layer_one = layer_normal.copy()
        layer_one.name = "Bright"
        image.add_layer(layer_one, 0)

        cp1 = [0,255, width1,240, width2,25, 255,0]
        pdb.gimp_curves_spline(layer_one, HISTOGRAM_VALUE, 8, cp1)
        pdb.gimp_drawable_set_visible(layer_one, 0)

        pdb.plug_in_gauss_rle2(image, layer_two, blur, blur)

        pdb.plug_in_gauss_rle2(image, layer_one, blur, blur)

        progress += 1
        gimp.progress_update(float(progress) / max_progress)
            
        progress += 1
        gimp.progress_update(float(progress) / max_progress)

        if (layer_dark.mask == None):
            mask_dark = pdb.gimp_layer_create_mask(layer_dark, 0)
            pdb.gimp_layer_add_mask(layer_dark, mask_dark)
        else:
            mask_dark = layer_dark.mask
        if (layer_bright.mask == None):
            mask_bright = pdb.gimp_layer_create_mask(layer_bright, 0)
            pdb.gimp_layer_add_mask(layer_bright, mask_bright)
        else:
            mask_bright = layer_bright.mask
        
        progress += 1
        gimp.progress_update(float(progress) / max_progress)

        pdb.gimp_edit_copy(layer_two)
        floating_sel = pdb.gimp_edit_paste(mask_dark, 0)
        pdb.gimp_floating_sel_anchor(floating_sel)

        progress += 1
        gimp.progress_update(float(progress) / max_progress)

        pdb.gimp_edit_copy(layer_one)
        floating_sel = pdb.gimp_edit_paste(mask_bright, 0)
        pdb.gimp_floating_sel_anchor(floating_sel)

        pdb.gimp_image_remove_layer(image, layer_one)
        pdb.gimp_image_remove_layer(image, layer_two)

        cp1 = [0,0, 128,128-levelLight, 255,255]
        pdb.gimp_curves_spline(layer_bright, HISTOGRAM_VALUE, 6, cp1)

        cp1 = [0,0, 128,128+levelDark, 255,255]
        pdb.gimp_curves_spline(layer_dark, HISTOGRAM_VALUE, 6, cp1)

        pdb.gimp_image_flatten(image)
        pdb.gimp_image_undo_group_end(image)

register(
        "python_fu_feca_hdr",
        "High dynamic range with tone mapping",
        "High dynamic range with tone mapping",
        "Ferenc Kalman",
        "Ferenc Kalman",
        "2007",
        "<Image>/Python-Fu/Render/HDR tone mapping",
        "*",
        [
                (PF_SPINNER, "width1", "Extreme value width (10-50)", 40, (10, 50, 1)),
                (PF_SPINNER, "width2", "Greater width (20-100)", 50, (20, 100, 1)),
                (PF_SPINNER, "blur", "Blurring of extreme colors (0-50)", 10, (0, 50, 1)),
                (PF_SPINNER, "levelLight", "Light colors darking (-100-100)", 30, (-100, 100, 1)),
                (PF_SPINNER, "levelDark", "Dark colors lighting (-100-100)", 30, (-100, 100, 1))
        ],
        [],
        python_feca_hdr)

main()